From 6b1fb1d5a10ae7501068d7c789c8593fbe4a3f8d Mon Sep 17 00:00:00 2001 From: rfm Date: Sat, 29 Aug 2015 20:12:33 +0000 Subject: [PATCH] Safer debug logging for http request/response git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@38946 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 3 + Headers/GNUstepBase/NSData+GNUstepBase.h | 29 ++++-- Source/Additions/NSData+GNUstepBase.m | 122 ++++++++++++++++++----- Source/GSHTTPURLHandle.m | 33 ++---- 4 files changed, 130 insertions(+), 57 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7e59e7bb2..b63c48f9f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,9 @@ 2015-08-29 Richard Frith-Macdonald * Source/Additions/GSMime.m: Improve descriptions for debug. + * Source/Additions/NSData+GNUstepBase.m: Add escaped representation. + * Source/GSHTTPURLHandle.m: Use escaped text format for debug log + (and plist armored format) to ensure that we safely log binary data 2015-08-24 Richard Frith-Macdonald diff --git a/Headers/GNUstepBase/NSData+GNUstepBase.h b/Headers/GNUstepBase/NSData+GNUstepBase.h index 9cdaa06d3..8f09d1501 100644 --- a/Headers/GNUstepBase/NSData+GNUstepBase.h +++ b/Headers/GNUstepBase/NSData+GNUstepBase.h @@ -47,6 +47,18 @@ extern "C" { */ + (id) dataWithRandomBytesOfLength: (NSUInteger)length; +/** Returns an NSString object containing a backslash escaped representation + * of the receiver. + */ +- (NSString*) escapedRepresentation; + +/** + * Returns a buffer containing an ASCII backslash escaped representation + * of the receiver, (and optionally the size of the buffer excluding + * the trailing nul terminator). + */ +- (char*) escapedRepresentation: (NSUInteger*)length; + /** Returns data formed by gunzipping the contents of the receiver.
* If the receiver did not contain data produced by gzip, this method * simply returns the receiver.
@@ -70,23 +82,22 @@ extern "C" { */ - (BOOL) isGzipped; -/** - * Returns an NSString object containing an ASCII hexadecimal representation +/** Returns an NSString object containing an ASCII hexadecimal representation * of the receiver. This means that the returned object will contain * exactly twice as many characters as there are bytes as the receiver, * as each byte in the receiver is represented by two hexadecimal digits.
* The high order four bits of each byte is encoded before the low * order four bits. Capital letters 'A' to 'F' are used to represent - * values from 10 to 15.
- * If you need the hexadecimal representation as raw byte data, use code - * like - - * - * hexData = [[sourceData hexadecimalRepresentation] - * dataUsingEncoding: NSASCIIStringEncoding]; - * + * values from 10 to 15. */ - (NSString*) hexadecimalRepresentation; +/** Returns a buffer containing an ASCII string with a nul terminated + * hexadecimal representation of the receiver, (and optionally the size + * of the buffer excluding the trailing nul terminator). + */ +- (char*) hexadecimalRepresentation: (NSUInteger*)length; + /** * Initialises the receiver with the supplied string data which contains * a hexadecimal coding of the bytes. The parsing of the string is diff --git a/Source/Additions/NSData+GNUstepBase.m b/Source/Additions/NSData+GNUstepBase.m index 133945949..c0926074c 100644 --- a/Source/Additions/NSData+GNUstepBase.m +++ b/Source/Additions/NSData+GNUstepBase.m @@ -116,33 +116,107 @@ randombytes(uint8_t *buf, unsigned len) return AUTORELEASE(d); } -/** - * Returns an NSString object containing an ASCII hexadecimal representation - * of the receiver. This means that the returned object will contain - * exactly twice as many characters as there are bytes as the receiver, - * as each byte in the receiver is represented by two hexadecimal digits.
- * The high order four bits of each byte is encoded before the low - * order four bits. Capital letters 'A' to 'F' are used to represent - * values from 10 to 15.
- * If you need the hexadecimal representation as raw byte data, use code - * like - - * - * hexData = [[sourceData hexadecimalRepresentation] - * dataUsingEncoding: NSASCIIStringEncoding]; - * - */ +- (NSString*) escapedRepresentation +{ + char *buf; + NSUInteger len; + NSString *string; + + buf = [self escapedRepresentation: &len]; + string = [[NSString alloc] initWithBytesNoCopy: buf + length: len + encoding: NSASCIIStringEncoding + freeWhenDone: YES]; + return AUTORELEASE(string); +} + +- (char*) escapedRepresentation: (NSUInteger*)length +{ + const uint8_t *bytes = (const uint8_t*)[self bytes]; + uint8_t *buf; + NSUInteger count = [self length]; + NSUInteger size = count + 1; + NSUInteger index; + NSUInteger pos; + + for (index = 0; index < count; index++) + { + uint8_t b = bytes[index]; + + if ('\n' == b) size++; + else if ('\r' == b) size++; + else if ('\t' == b) size++; + else if ('\\' == b) size++; + else if (!isprint(b)) size += 3; + } + buf = (uint8_t*)malloc(size); + for (pos = index = 0; index < count; index++) + { + uint8_t b = bytes[index]; + + if ('\n' == b) + { + buf[pos++] = '\\'; + buf[pos++] = 'n'; + } + else if ('\r' == b) + { + buf[pos++] = '\\'; + buf[pos++] = 'r'; + } + else if ('\t' == b) + { + buf[pos++] = '\\'; + buf[pos++] = 't'; + } + else if ('\\' == b) + { + buf[pos++] = '\\'; + buf[pos++] = '\\'; + } + else if (!isprint(b)) + { + sprintf((char*)&buf[pos], "\\x%02x", b); + pos += 4; + } + else + { + buf[pos++] = b; + } + } + buf[pos] = '\0'; + if (0 != length) + { + *length = pos; + } + return (char*)buf; +} + - (NSString*) hexadecimalRepresentation +{ + char *buf; + NSUInteger len; + NSString *string; + + buf = [self hexadecimalRepresentation: &len]; + string = [[NSString alloc] initWithBytesNoCopy: buf + length: len + encoding: NSASCIIStringEncoding + freeWhenDone: YES]; + return AUTORELEASE(string); +} + +- (char*) hexadecimalRepresentation: (NSUInteger*)length { static const char *hexChars = "0123456789ABCDEF"; unsigned slen = [self length]; unsigned dlen = slen * 2; const unsigned char *src = (const unsigned char *)[self bytes]; - char *dst = (char*)NSZoneMalloc(NSDefaultMallocZone(), dlen); + char *dst; unsigned spos = 0; unsigned dpos = 0; - NSData *data; - NSString *string; + dst = (char*)malloc(dlen + 1); while (spos < slen) { unsigned char c = src[spos++]; @@ -150,12 +224,12 @@ randombytes(uint8_t *buf, unsigned len) dst[dpos++] = hexChars[(c >> 4) & 0x0f]; dst[dpos++] = hexChars[c & 0x0f]; } - data = [NSData allocWithZone: NSDefaultMallocZone()]; - data = [data initWithBytesNoCopy: dst length: dlen]; - string = [[NSString alloc] initWithData: data - encoding: NSASCIIStringEncoding]; - RELEASE(data); - return AUTORELEASE(string); + dst[dpos] = '\0'; + if (0 != length) + { + *length = dpos; + } + return dst; } - (NSData*) gunzipped diff --git a/Source/GSHTTPURLHandle.m b/Source/GSHTTPURLHandle.m index fbe6a38f5..eaa8682a3 100644 --- a/Source/GSHTTPURLHandle.m +++ b/Source/GSHTTPURLHandle.m @@ -43,6 +43,7 @@ #import "Foundation/NSValue.h" #import "GNUstepBase/GSMime.h" #import "GNUstepBase/GSLock.h" +#import "GNUstepBase/NSData+GNUstepBase.h" #import "GNUstepBase/NSString+GNUstepBase.h" #import "GNUstepBase/NSURL+GNUstepBase.h" #import "NSCallBacks.h" @@ -230,36 +231,20 @@ static Class sslClass = 0; static void debugRead(GSHTTPURLHandle *handle, NSData *data) { - int len = (int)[data length]; - const char *ptr = (const char*)[data bytes]; - int pos; + unsigned len = (unsigned)[data length]; + char *esc = [data escapedRepresentation: 0]; - for (pos = 0; pos < len; pos++) - { - if (0 == ptr[pos]) - { - NSLog(@"Read for %p of %d bytes - %@", handle, len, data); - return; - } - } - NSLog(@"Read for %p of %d bytes - '%*.*s'", handle, len, len, len, ptr); + NSLog(@"Read for %p of %u bytes - '%s'\n%@", handle, len, esc, data); + free(esc); } static void debugWrite(GSHTTPURLHandle *handle, NSData *data) { - int len = (int)[data length]; - const char *ptr = (const char*)[data bytes]; - int pos = len; + unsigned len = (unsigned)[data length]; + char *esc = [data escapedRepresentation: 0]; - for (pos = 0; pos < len; pos++) - { - if (0 == ptr[pos]) - { - NSLog(@"Write for %p of %d bytes - %@", handle, len, data); - return; - } - } - NSLog(@"Write for %p of %d bytes -'%*.*s'", handle, len, len, len, ptr); + NSLog(@"Write for %p of %u bytes - '%s'\n%@", handle, len, esc, data); + free(esc); } + (NSURLHandle*) cachedHandleForURL: (NSURL*)newUrl