I'm trying to run a shell script using posix_spawn, by trying to convert from NSArray to char array with a NULL in the last element, I end up crashing my system. Below is the original code that has no error.
NSArray *arg_array = nil;
arg_array = #[#"/bin/bash", #"/var/somefile.sh"];
char **argv = NULL;
NSInteger numargv = arg_array.count;
if (numargv) {
argv = (char **)calloc(numargv, sizeof(char*)) ;
if (argv) {
for (NSInteger i=0;i<numargv;i++) {
NSString *nsString = arg_array[i];
if (i==0){
NSArray* spliteArray = [nsString componentsSeparatedByString: #"/"];
NSString* cStringFirstArgv = [spliteArray lastObject];
nsString = cStringFirstArgv;
}
char *cString = (char *)malloc([nsString lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1); // + 1 for \0
if (cString) {
strcpy(cString, nsString.UTF8String);
argv[i] = cString;
} else {
// error
}
}
}
}
pid_t pid;
int status;
//const char *argv[] = {"bash", "/var/test.sh", NULL}; This is what I expected argv to be, a NULL in the last element
posix_spawnp(&pid, "bash", NULL, NULL, (char* const*)argv, NULL);
waitpid(pid, &status, WEXITED);
argv currently is {"bash", "/var/test.sh"}, however I trying to make it to an array like this:{"bash", "/var/test.sh", NULL} so that I could run posix_spawn successfully. Any idea how to fix this? Thanks in advance!
The following will work:
NSArray *arg_array = nil;
arg_array = #[#"/bin/bash", #"/var/somefile.sh"];
char **argv = NULL;
NSInteger numargv = arg_array.count;
argv = (char **)calloc(numargv + 1, sizeof(char*));
if (argv) {
for (NSInteger i = 0; i < numargv; i++) {
NSString *nsString = arg_array[i];
if (i == 0){
NSString* application = [[nsString componentsSeparatedByString: #"/"] lastObject];
nsString = application;
}
NSData* stringData = [nsString dataUsingEncoding:[NSString defaultCStringEncoding]];
char *cString = (char *)malloc(stringData.length + 1);
[stringData getBytes:cString length:stringData.length];
cString[stringData.length] = 0;
argv[i] = cString;
}
}
pid_t pid;
int status;
//const char *argv[] = {"bash", "/var/test.sh", NULL}; This is what I expected argv to be, a NULL in the last element
posix_spawnp(&pid, "bash", NULL, NULL, (char* const*)argv, NULL);
waitpid(pid, &status, WEXITED);
I think there were two problems in your code:
You were missing the +1 in your calloc for the NULL terminator.
You don't actually set the ending 0 for your string arguments.
how hexadecimal string to Bytes and Bytes to hexadecimal string?
I have oc code but I can not to write in swift
can u help me ?
thanks so much
+ (NSData *)convertHexToDataBytes:(NSString *)hexStr {
NSMutableData *dataBytes = [NSMutableData data];
int idx;
for (idx = 0; idx + 2 <= hexStr.length; idx += 2) {
NSRange range = NSMakeRange(idx, 2);
NSString *singleHexStr = [hexStr substringWithRange:range];
NSScanner *scanner = [NSScanner scannerWithString:singleHexStr];
unsigned int intValue;
[scanner scanHexInt:&intValue];
[dataBytes appendBytes:&intValue length:1];
}
return dataBytes;
}
+ (NSString *)convertDataBytesToHex:(NSData *)dataBytes {
if (!dataBytes || [dataBytes length] == 0) {
return #"";
}
NSMutableString *hexStr = [[NSMutableString alloc] initWithCapacity:[dataBytes length]];
[dataBytes enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
unsigned char *dataBytes = (unsigned char *)bytes;
for (NSInteger i = 0; i < byteRange.length; i ++) {
NSString *singleHexStr = [NSString stringWithFormat:#"%x", (dataBytes[i]) & 0xff];
if ([singleHexStr length] == 2) {
[hexStr appendString:singleHexStr];
} else {
[hexStr appendFormat:#"0%#", singleHexStr];
}
}
}];
return hexStr;
}
I write in swift like this
class func convertHex(toDataBytes hexStr: String?) -> Data? {
var dataBytes = Data()
var idx: Int
idx = 0
while idx + 2 <= (hexStr?.count ?? 0) {
let range = NSRange(location: idx, length: 2)
let singleHexStr = (hexStr as NSString?)?.substring(with: range)
let scanner = Scanner(string: singleHexStr ?? "")
var intValue: UInt
scanner.scanHexInt32(&UInt32(intValue))
dataBytes.append(&intValue, count: 1)
idx += 2
}
return dataBytes
}
its error says:Cannot pass immutable value as inout argument: function call returns immutable value how to fix it?
To answer your immediate question: UInt32(intValue) creates a new (constant) value whose address cannot be taken with &. So
var intValue: UInt
scanner.scanHexInt32(&UInt32(intValue))
should be
var intValue: UInt32 = 0
scanner.scanHexInt32(&intValue)
And
dataBytes.append(&intValue, count: 1)
does not compile because &intValue is a pointer to an integer, not to an UInt8. Here you can do
dataBytes.append(UInt8(intValue))
because the value is known to fit in a single byte.
Having said that, all the conversions from String to NSString are not needed. A more “Swifty” translation of that Objective-C code to Swift would be
func convertHex(toDataBytes hexStr: String) -> Data {
var dataBytes = Data()
var startPos = hexStr.startIndex
while let endPos = hexStr.index(startPos, offsetBy: 2, limitedBy: hexStr.endIndex) {
let singleHexStr = hexStr[startPos..<endPos]
let scanner = Scanner(string: String(singleHexStr))
var intValue: UInt32 = 0
scanner.scanHexInt32(&intValue)
dataBytes.append(UInt8(intValue))
startPos = endPos
}
return dataBytes
}
For an alternative approach (which includes error checking) see for example
hex/binary string conversion in Swift
I'm using DNSServiceQueryRecord to get kDNSServiceType_MX using following:
DNSServiceQueryRecord(&self->_sdRef,
kDNSServiceFlagsReturnIntermediates,
0,
srvNameCStr, //for example "google.com"
kDNSServiceType_MX,
kDNSServiceClass_IN,
QueryRecordCallback,
(__bridge void *)(self)
);
and call back is called but there is only one entry:
NSMutableData * rrData;
dns_resource_record_t * rr;
uint8_t u8;
uint16_t u16;
uint32_t u32;
assert(rdata != NULL);
assert(rdlen < 65536);
rrData = [NSMutableData data];
assert(rrData != nil);
u8 = 0;
[rrData appendBytes:&u8 length:sizeof(u8)];
u16 = htons(kDNSServiceType_MX);
[rrData appendBytes:&u16 length:sizeof(u16)];
u16 = htons(kDNSServiceClass_IN);
[rrData appendBytes:&u16 length:sizeof(u16)];
u32 = htonl(666);
[rrData appendBytes:&u32 length:sizeof(u32)];
u16 = htons(rdlen);
[rrData appendBytes:&u16 length:sizeof(u16)];
[rrData appendBytes:rdata length:rdlen];
rr = dns_parse_resource_record([rrData bytes], (uint32_t) [rrData length]);
assert(rr != NULL);
if (rr != NULL)
{
NSString *result = nil;
result = [NSString stringWithCString:rr->data.MX->name encoding:NSASCIIStringEncoding];
dns_free_resource_record(rr);
}
so this returns only one entry. How to get the list of MX? What am I doing wrong? Thanks in advance!
Ok I have found that its better to do the query with select() and the result contains all MXes.
while (dnsUpdateTimeout > 0)
{
FD_ZERO(&readfds);
FD_SET(dns_sd_fd, &readfds);
struct timeval tv;
tv.tv_sec = (time_t)dnsUpdateTimeout;
tv.tv_usec = (self.dnsUpdateTimeout - tv.tv_sec) * 1000000;
result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
if (result == 1)
{
if (FD_ISSET(dns_sd_fd, &readfds))
{
err = DNSServiceProcessResult(sdRef); // triggers callback that will process the result
if (err != kDNSServiceErr_NoError)
{
NSLog(#"There was an error reading the DNS SRV records.");
break;
}
}
}
NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:startTime];
dnsUpdateTimeout -= elapsed;
}
My server devs ask me to send them some data encoded with base64 with this rules:
big-endian byte order
no extra zero bytes
base64 string
for example:
10005000 → «mKol»
1234567890 → «SZYC0g»
I did some spaghetti code, and it's work. But maybe somebody have more elegant solution?
+ (NSString*)encodeBigEndianBase64:(uint32_t)value {
char *bytes = (char*) &value;
int len = sizeof(uint32_t);
char *reverseBytes = malloc(sizeof(char) * len);
unsigned long index = len - 1;
for (int i = 0; i < len; i++)
reverseBytes[index--] = bytes[i];
int offset = 0;
while (reverseBytes[offset] == 0) {
offset++;
}
NSData *resultData;
if (offset > 0) {
int truncatedLen = (len - offset);
char *truncateBytes = malloc(sizeof(char) * truncatedLen);
for (int i = 0; i < truncatedLen ; i++)
truncateBytes[i] = reverseBytes[i + offset];
resultData = [NSData dataWithBytes:truncateBytes length:truncatedLen];
free(truncateBytes);
} else {
resultData = [NSData dataWithBytes:reverseBytes length:len];
}
free(reverseBytes);
return [[resultData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength] stringByReplacingOccurrencesOfString:#"=" withString:#""];
}
Little bit improved solution (thanks to zaph):
+ (NSString*)encodeBigEndianBase64:(uint32_t)value {
uint32_t swappedValue = CFSwapInt32HostToBig(value);
char *swappedBytes = (char*) &swappedValue;
int len = sizeof(uint32_t);
int offset = 0;
while (swappedBytes[offset] == 0) {
offset++;
}
NSData *resultData;
if (offset > 0) {
int truncatedLen = (len - offset);
char *truncateBytes = malloc(sizeof(char) * truncatedLen);
for (int i = 0; i < truncatedLen ; i++)
truncateBytes[i] = swappedBytes[i + offset];
resultData = [NSData dataWithBytes:truncateBytes length:truncatedLen];
free(truncateBytes);
} else {
resultData = [NSData dataWithBytes:swappedBytes length:len];
}
return [[resultData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength] stringByReplacingOccurrencesOfString:#"=" withString:#""];
}
For endian conversions use htons(), htonl(), ntohs(), ntohl()
network byte order is bigendian
`htons()` // host to network short
`htonl()` // host to network ling
`ntohs()` // network to host long
`ntohl()` // network to host long
These are defined in endan.h
Also see Byte-Order Utilities Reference
I am implementing for iOS some decryption code for a message originating on a server over which I have no control. So the decryption requirements are:
Cipher Method : AES256
Cipher Mode: ECB
Padding: PKCS5Padding
Since my initial trials failed to decrypt. So I played around with some test vectors to see the code that i use was right,
This is the code that encrypts the data:
NSString+AESCrypt.h
-------------------
#import <Foundation/Foundation.h>
#import "NSData+AESCrypt.h"
#interface NSString (AESCrypt)
- (NSString *)AES256EncryptWithKey:(NSString *)key;
- (NSString *)AES256DecryptWithKey:(NSString *)key;
#end
NSString+AESCrypt.m
-------------------
#import "NSString+AESCrypt.h"
#implementation NSString (AESCrypt)
- (NSString *)AES256EncryptWithKey:(NSString *)key
{
NSData *plainData = [self dataUsingEncoding:NSUTF8StringEncoding];
NSData *encryptedData = [plainData AES256EncryptWithKey:key];
NSString *encryptedString = [encryptedData base64Encoding];
return encryptedString;
}
- (NSString *)AES256DecryptWithKey:(NSString *)key
{
NSData *encryptedData = [NSData dataWithBase64EncodedString:self];
NSData *plainData = [encryptedData AES256DecryptWithKey:key];
NSString *plainString = [[NSString alloc] initWithData:plainData encoding:NSUTF8StringEncoding];
return [plainString autorelease];
}
#end
NSData+AESCrypt.h
-------------------
#import <Foundation/Foundation.h>
#interface NSData (AESCrypt)
- (NSData *)AES256EncryptWithKey:(NSString *)key;
- (NSData *)AES256DecryptWithKey:(NSString *)key;
+ (NSData *)dataWithBase64EncodedString:(NSString *)string;
- (id)initWithBase64EncodedString:(NSString *)string;
- (NSString *)base64Encoding;
- (NSString *)base64EncodingWithLineLength:(NSUInteger)lineLength;
- (BOOL)hasPrefixBytes:(const void *)prefix length:(NSUInteger)length;
- (BOOL)hasSuffixBytes:(const void *)suffix length:(NSUInteger)length;
#end
NSData+AESCrypt.m
-------------------
#import "NSData+AESCrypt.h"
#import <CommonCrypto/CommonCryptor.h>
static char encodingTable[64] =
{
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};
#implementation NSData (AESCrypt)
- (NSData *)AES256EncryptWithKey:(NSString *)key
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode + kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted );
if( cryptStatus == kCCSuccess )
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free( buffer ); //free the buffer
return nil;
}
- (NSData *)AES256DecryptWithKey:(NSString *)key
{
// 'key' should be 32 bytes for AES256, will be null-padded otherwise
char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
bzero( keyPtr, sizeof( keyPtr ) ); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc( bufferSize );
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt( kCCDecrypt, kCCAlgorithmAES128, kCCOptionECBMode + kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES256,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesDecrypted );
if( cryptStatus == kCCSuccess )
{
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
}
free( buffer ); //free the buffer
return nil;
}
#pragma mark -
+ (NSData *)dataWithBase64EncodedString:(NSString *)string
{
return [[[NSData allocWithZone:nil] initWithBase64EncodedString:string] autorelease];
}
- (id)initWithBase64EncodedString:(NSString *)string
{
NSMutableData *mutableData = nil;
if( string )
{
unsigned long ixtext = 0;
unsigned long lentext = 0;
unsigned char ch = 0;
unsigned char inbuf[4], outbuf[3];
short i = 0, ixinbuf = 0;
BOOL flignore = NO;
BOOL flendtext = NO;
NSData *base64Data = nil;
const unsigned char *base64Bytes = nil;
// Convert the string to ASCII data.
base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];
base64Bytes = [base64Data bytes];
mutableData = [NSMutableData dataWithCapacity:base64Data.length];
lentext = base64Data.length;
while( YES )
{
if( ixtext >= lentext ) break;
ch = base64Bytes[ixtext++];
flignore = NO;
if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';
else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;
else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;
else if( ch == '+' ) ch = 62;
else if( ch == '=' ) flendtext = YES;
else if( ch == '/' ) ch = 63;
else flignore = YES;
if( ! flignore )
{
short ctcharsinbuf = 3;
BOOL flbreak = NO;
if( flendtext )
{
if( ! ixinbuf ) break;
if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;
else ctcharsinbuf = 2;
ixinbuf = 3;
flbreak = YES;
}
inbuf [ixinbuf++] = ch;
if( ixinbuf == 4 )
{
ixinbuf = 0;
outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );
outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );
outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );
for( i = 0; i < ctcharsinbuf; i++ )
[mutableData appendBytes:&outbuf[i] length:1];
}
if( flbreak ) break;
}
}
}
self = [self initWithData:mutableData];
return self;
}
#pragma mark -
- (NSString *)base64Encoding
{
return [self base64EncodingWithLineLength:0];
}
- (NSString *)base64EncodingWithLineLength:(NSUInteger)lineLength
{
const unsigned char *bytes = [self bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:self.length];
unsigned long ixtext = 0;
unsigned long lentext = self.length;
long ctremaining = 0;
unsigned char inbuf[3], outbuf[4];
unsigned short i = 0;
unsigned short charsonline = 0, ctcopy = 0;
unsigned long ix = 0;
while( YES )
{
ctremaining = lentext - ixtext;
if( ctremaining <= 0 ) break;
for( i = 0; i < 3; i++ )
{
ix = ixtext + i;
if( ix < lentext ) inbuf[i] = bytes[ix];
else inbuf [i] = 0;
}
outbuf [0] = (inbuf [0] & 0xFC) >> 2;
outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
outbuf [3] = inbuf [2] & 0x3F;
ctcopy = 4;
switch( ctremaining )
{
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}
for( i = 0; i < ctcopy; i++ )
[result appendFormat:#"%c", encodingTable[outbuf[i]]];
for( i = ctcopy; i < 4; i++ )
[result appendString:#"="];
ixtext += 3;
charsonline += 4;
if( lineLength > 0 )
{
if( charsonline >= lineLength )
{
charsonline = 0;
[result appendString:#"\n"];
}
}
}
return [NSString stringWithString:result];
}
#pragma mark -
- (BOOL)hasPrefixBytes:(const void *)prefix length:(NSUInteger)length
{
if( ! prefix || ! length || self.length < length ) return NO;
return ( memcmp( [self bytes], prefix, length ) == 0 );
}
- (BOOL)hasSuffixBytes:(const void *)suffix length:(NSUInteger)length
{
if( ! suffix || ! length || self.length < length ) return NO;
return ( memcmp( ((const char *)[self bytes] + (self.length - length)), suffix, length ) == 0 );
}
#end
I execute above function and write the resulting data to the log with this code:
NSString * _secret = #"6bc1bee22e409f96e93d7e117393172a";
NSString * _key = #"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
NSString *encryptedString = [_secret AES256EncryptWithKey:_key];
NSLog(#"Encrypted ID : %#", encryptedString);
NSString *decryptedString = [encryptedString AES256DecryptWithKey:_key];
NSLog(#"Decrypted ID : %#", decryptedString);
As from test vector, the encrypted cipher should be this:
f3eed1bdb5d2a03c064b5a7e3db181f8
Result logs:
2011-10-19 13:32:41.640 Ticket[2215:707] Encrypted ID : XWLsnTQvocXNkAqVisEgWTCPdYR6KPoIojezjN3fn/wuytQkpUZnNbzUoT4peeTK
2011-10-19 13:32:41.641 Ticket[2215:707] Decrypted ID : 6bc1bee22e409f96e93d7e117393172a
I know that this Encrypted ID is in Base64, but still even if i convert it to HEX, the actual output varies from the result.
What option am I forgetting? Is the encoding of the NSData returned something else…?
So if someone could direct me on the right path that would be great, Cheers.
I htink the problem is that the test-vector-page assumes that these printed hex-values are binaries and not text: The string "6b" does look utf-8 encoded like this: 0x3662.
for getting the correct test-string you have to encode it first. your string should start with this: #"kÁ¾â....". That String will result in the right hexa-representation if it will be ecnoded with utf-8.
You should either test your aes-encryption with NSData initialized with an hex-string or you have to decode utf-8 first to pack all that into a string. But take care: there are often symbols which cannot be represented/printed and even worse: if your tesdata or the cypher contains the Zero-Byte then this will cause some problems because it is often used as a termination-symbol in strings which normally contains only readable charachters. (dont know how this interacts with NSString)
or use the first variant to create an NSData out of a hex-string and then convert that data to an NSString