diff --git a/ChangeLog b/ChangeLog index 79198a3df..0ab7abe37 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2008-01-07 Richard Frith-Macdonald + + * Source\GSSocketStream.h: simplify socket address handling + * Source\GSSocketStream.m: ditto + more work on socks + * Source\GSStream.h: minor tweaks + * Source\GSStream.m:minor tweaks + * Source\unix\NSStream.m: simplified socket address handling + * Source\GSNetwork.h: add macro to get socket address length + * Source\GSHTTPAuthentication.m: Handle arg classh check in init + * Source\NSURLRequest.m: ditto + * Source\NSURLProtocol.m: attempt to add support for authentication + 2008-01-07 Fred Kiefer * Source/NSFileManager.m (-fileSystemAttributesAtPath:): Correct diff --git a/Source/GSHTTPAuthentication.m b/Source/GSHTTPAuthentication.m index 13f5651e5..9c3ac9624 100644 --- a/Source/GSHTTPAuthentication.m +++ b/Source/GSHTTPAuthentication.m @@ -150,7 +150,7 @@ static GSMimeParser *mimeParser = nil; + (NSURLProtectionSpace*) protectionSpaceForAuthentication: (NSString*)auth requestURL: (NSURL*)URL; { - if (auth != nil) + if ([auth isKindOfClass: [NSString class]] == YES) { NSString *method = nil; NSURLProtectionSpace *space; diff --git a/Source/GSNetwork.h b/Source/GSNetwork.h index 56e182b64..fe1c1e058 100644 --- a/Source/GSNetwork.h +++ b/Source/GSNetwork.h @@ -50,6 +50,11 @@ #define BADSOCKET(X) ((X) == INVALID_SOCKET) #define GSNETERROR WSAGetLastError() #define GSWOULDBLOCK (GSNETERROR == WSAEWOULDBLOCK || GSNETERROR == WSAEINPROGRESS) +#ifdef AF_INET6 +#define SOCKLEN(X) ((X->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) +#else +#define SOCKLEN(X) sizeof(struct sockaddr_in) +#endif #else @@ -66,6 +71,7 @@ #endif #define SOCKET int /* Socket type */ +#define SOCKLEN(X) (X->sa_len) #define BADSOCKET(X) ((X) < 0) #define GSNETERROR errno #define GSWOULDBLOCK (errno == EINPROGRESS) diff --git a/Source/GSSocketStream.h b/Source/GSSocketStream.h index aba34bd0c..6aa7755fe 100644 --- a/Source/GSSocketStream.h +++ b/Source/GSSocketStream.h @@ -37,6 +37,7 @@ BOOL _closing; /* Must close on next failure. */\ SOCKET _sock; /* Needed for ms-windows. */\ id _handler; /* TLS/SOCKS handler. */\ + struct sockaddr *_address; /* Socket address info. */\ } /* The semi-abstract GSSocketStream class is not intended to be subclassed @@ -48,7 +49,12 @@ SOCKIVARS /** * get the sockaddr */ -- (struct sockaddr*) _peerAddr; +- (struct sockaddr*) _address; + +/** + * set the sockaddr + */ +- (void) _setAddress: (struct sockaddr*)address; /** * setter for closing flag ... the remote end has stopped either sending @@ -82,11 +88,6 @@ SOCKIVARS */ - (SOCKET) _sock; -/** - * Get the length of the socket addr - */ -- (socklen_t) _sockLen; - @end /** @@ -98,7 +99,8 @@ SOCKIVARS SOCKIVARS @end @interface GSSocketInputStream (AddedBehaviors) -- (struct sockaddr*) _peerAddr; +- (struct sockaddr*) _address; +- (void) _setAddress: (struct sockaddr*)address; - (int) _read: (uint8_t *)buffer maxLength: (unsigned int)len; - (void) _setClosing: (BOOL)passive; - (void) _setHandler: (id)h; @@ -106,14 +108,9 @@ SOCKIVARS - (void) _setSibling: (GSSocketStream*)sibling; - (void) _setSock: (SOCKET)sock; - (SOCKET) _sock; -- (socklen_t) _sockLen; @end @interface GSInetInputStream : GSSocketInputStream -{ - @private - struct sockaddr_in _peerAddr; -} /** * the designated initializer @@ -124,12 +121,6 @@ SOCKIVARS @interface GSInet6InputStream : GSSocketInputStream -{ - @private -#if defined(AF_INET6) - struct sockaddr_in6 _peerAddr; -#endif -} /** * the designated initializer @@ -147,22 +138,18 @@ SOCKIVARS SOCKIVARS @end @interface GSSocketOutputStream (AddedBehaviors) -- (struct sockaddr*) _peerAddr; +- (struct sockaddr*) _address; +- (void) _setAddress: (struct sockaddr*)address; - (void) _setClosing: (BOOL)passive; - (void) _setHandler: (id)h; - (void) _setPassive: (BOOL)passive; - (void) _setSibling: (GSSocketStream*)sibling; - (void) _setSock: (SOCKET)sock; - (SOCKET) _sock; -- (socklen_t) _sockLen; - (int) _write: (const uint8_t *)buffer maxLength: (unsigned int)len; @end @interface GSInetOutputStream : GSSocketOutputStream -{ - @private - struct sockaddr_in _peerAddr; -} /** * the designated initializer @@ -172,12 +159,6 @@ SOCKIVARS @end @interface GSInet6OutputStream : GSSocketOutputStream -{ - @private -#if defined(AF_INET6) - struct sockaddr_in6 _peerAddr; -#endif -} /** * the designated initializer @@ -208,37 +189,22 @@ SOCKIVARS */ - (Class) _outputStreamClass; -/** - * Return the sockaddr for this server - */ -- (struct sockaddr*) _serverAddr; - @end @interface GSSocketServerStream (AddedBehaviors) -- (struct sockaddr*) _peerAddr; +- (struct sockaddr*) _address; +- (void) _setAddress: (struct sockaddr*)address; - (void) _setClosing: (BOOL)passive; - (void) _setHandler: (id)h; - (void) _setPassive: (BOOL)passive; - (void) _setSibling: (GSSocketStream*)sibling; - (void) _setSock: (SOCKET)sock; - (SOCKET) _sock; -- (socklen_t) _sockLen; @end @interface GSInetServerStream : GSSocketServerStream -{ - @private - struct sockaddr_in _serverAddr; -} @end @interface GSInet6ServerStream : GSSocketServerStream -{ - @private -#if defined(AF_INET6) - struct sockaddr_in6 _serverAddr; -#endif -} @end #endif diff --git a/Source/GSSocketStream.m b/Source/GSSocketStream.m index cd28f7b2a..162f4a677 100644 --- a/Source/GSSocketStream.m +++ b/Source/GSSocketStream.m @@ -56,16 +56,16 @@ */ @interface GSStreamHandler : NSObject { - GSSocketInputStream *input; // Not retained - GSSocketOutputStream *output; // Not retained + GSSocketInputStream *istream; // Not retained + GSSocketOutputStream *ostream; // Not retained BOOL initialised; BOOL handshake; BOOL active; } - (id) initWithInput: (GSSocketInputStream*)i output: (GSSocketOutputStream*)o; -- (GSSocketInputStream*) input; -- (GSSocketOutputStream*) output; +- (GSSocketInputStream*) istream; +- (GSSocketOutputStream*) ostream; - (void) bye; /* Close down the handled session. */ - (BOOL) handshake; /* A handshake/hello is in progress. */ @@ -96,19 +96,19 @@ - (id) initWithInput: (GSSocketInputStream*)i output: (GSSocketOutputStream*)o { - input = i; - output = o; + istream = i; + ostream = o; return self; } -- (GSSocketInputStream*) input +- (GSSocketInputStream*) istream { - return input; + return istream; } -- (GSSocketOutputStream*) output +- (GSSocketOutputStream*) ostream { - return output; + return ostream; } - (int) read: (uint8_t *)buffer maxLength: (unsigned int)len @@ -184,14 +184,14 @@ GSTLSPull(gnutls_transport_ptr_t handle, void *buffer, size_t len) ssize_t result; GSTLS *tls = (GSTLS*)handle; - result = [[tls input] _read: buffer maxLength: len]; + result = [[tls istream] _read: buffer maxLength: len]; if (result < 0) { int e; - if ([[tls input] streamStatus] == NSStreamStatusError) + if ([[tls istream] streamStatus] == NSStreamStatusError) { - e = [[[(GSTLS*)handle input] streamError] code]; + e = [[[(GSTLS*)handle istream] streamError] code]; } else { @@ -211,14 +211,14 @@ GSTLSPush(gnutls_transport_ptr_t handle, const void *buffer, size_t len) ssize_t result; GSTLS *tls = (GSTLS*)handle; - result = [[tls output] _write: buffer maxLength: len]; + result = [[tls ostream] _write: buffer maxLength: len]; if (result < 0) { int e; - if ([[tls output] streamStatus] == NSStreamStatusError) + if ([[tls ostream] streamStatus] == NSStreamStatusError) { - e = [[[tls output] streamError] code]; + e = [[[tls ostream] streamError] code]; } else { @@ -418,14 +418,14 @@ static gnutls_anon_client_credentials_t anoncred; return self; } -- (GSSocketInputStream*) input +- (GSSocketInputStream*) istream { - return input; + return istream; } -- (GSSocketOutputStream*) output +- (GSSocketOutputStream*) ostream { - return output; + return ostream; } - (int) read: (uint8_t *)buffer maxLength: (unsigned int)len @@ -442,8 +442,8 @@ static gnutls_anon_client_credentials_t anoncred; [self hello]; /* try to complete the handshake */ if (handshake == NO) { - [input _sendEvent: NSStreamEventOpenCompleted]; - [output _sendEvent: NSStreamEventOpenCompleted]; + [istream _sendEvent: NSStreamEventOpenCompleted]; + [ostream _sendEvent: NSStreamEventOpenCompleted]; } } } @@ -484,44 +484,126 @@ static NSString * const GSSOCKSAckAuth = @"GSSOCKSAckAuth"; static NSString * const GSSOCKSSendConn = @"GSSOCKSSendConn"; static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; -@interface GSSOCKS : NSObject +@interface GSSOCKS : GSStreamHandler { - NSString *state; - NSString *addr; - int port; + NSString *state; /* Not retained */ + NSString *address; + NSString *port; int roffset; int woffset; int rwant; unsigned char rbuffer[128]; - NSInputStream *istream; - NSOutputStream *ostream; } -- (NSString*) addr; -- (id) initToAddr: (NSString*)_addr port: (int)_port; -- (int) port; -- (NSString*) stream: (NSStream*)stream SOCKSEvent: (NSStreamEvent)event; +- (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event; @end @implementation GSSOCKS -- (NSString*) addr +- (void) bye { - return addr; + if (handshake == YES) + { + NSString *tls; + GSTLS *t = nil; + + handshake = NO; + tls = [ostream propertyForKey: NSStreamSocketSecurityLevelKey]; + if (tls == nil && istream != nil) + { + tls = [istream propertyForKey: NSStreamSocketSecurityLevelKey]; + if (tls != nil) + { + [ostream setProperty: tls forKey: NSStreamSocketSecurityLevelKey]; + } + } + if (tls != nil) + { + t = [[GSTLS alloc] initWithInput: istream output: ostream]; + } + if (t == nil) + { + GSSocketInputStream *is = RETAIN(istream); + GSSocketOutputStream *os = RETAIN(ostream); + + /* No TLS required ... simply remove SOCKS handler from streams + * and let the streams know that the open has completed. + * NB. Removing SOCKS handle from streams may cause it to be + * deallocated, so we work with local variables to hold the + * streams long enough to send events to them. + */ + [is _setHandler: nil]; + [os _setHandler: nil]; + [is _sendEvent: NSStreamEventOpenCompleted]; + [os _sendEvent: NSStreamEventOpenCompleted]; + RELEASE(is); + RELEASE(os); + } + else + { + /* Replace SOCKS handler wth TLS handler and start TLS handshake. + */ + [istream _setHandler: t]; + [ostream _setHandler: t]; + [t hello]; + RELEASE(t); + } + } } -- (id) initToAddr: (NSString*)_addr port: (int)_port +- (void) dealloc { - ASSIGNCOPY(addr, _addr); - port = _port; - state = GSSOCKSOfferAuth; + RELEASE(address); + RELEASE(port); + [super dealloc]; +} + +- (void) hello +{ + if (handshake == NO) + { + handshake = YES; + /* Now send self an event to say we can write, to kick off the + * handshake with the SOCKS server. + */ + [self stream: ostream handleEvent: NSStreamEventHasSpaceAvailable]; + } +} + +- (id) initWithInput: (GSSocketInputStream*)i + output: (GSSocketOutputStream*)o +{ + if ((self = [super initWithInput: i output: o]) != nil) + { + if ([istream isKindOfClass: [GSInetInputStream class]] == NO) + { + NSLog(@"Attempt to use SOCKS with non-INET stream ignored"); + DESTROY(self); + } +#if defined(AF_INET6) + else if ([istream isKindOfClass: [GSInet6InputStream class]] == YES) + { + GSOnceMLog(@"INET6 not supported with SOCKS yet..."); + DESTROY(self); + } +#endif /* AF_INET6 */ + else + { + struct sockaddr_in *addr = (struct sockaddr_in*)[istream _address]; + + address = [[NSString alloc] initWithUTF8String: + (char*)inet_ntoa(addr->sin_addr)]; + port = [[NSString alloc] initWithFormat: @"%d", + (int)GSSwapBigI16ToHost(addr->sin_port)]; + } + } return self; } -- (int) port +- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len { - return port; + return [istream _read: buffer maxLength: len]; } -- (NSString*) stream: (NSStream*)stream SOCKSEvent: (NSStreamEvent)event +- (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event { NSString *error = nil; NSDictionary *conf; @@ -532,7 +614,8 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; || [stream streamStatus] == NSStreamStatusError || [stream streamStatus] == NSStreamStatusClosed) { - return @"SOCKS errur during negotiation"; + [self bye]; + return; } conf = [stream propertyForKey: NSStreamSOCKSProxyConfigurationKey]; @@ -574,12 +657,8 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; want = 3; } - result = [ostream write: buf + woffset maxLength: 4 - woffset]; - if (result == 0) - { - error = @"end-of-file during SOCKS negotiation"; - } - else if (result > 0) + result = [ostream _write: buf + woffset maxLength: 4 - woffset]; + if (result > 0) { woffset += result; if (woffset == want) @@ -594,7 +673,7 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; { int result; - result = [istream read: rbuffer + roffset maxLength: 2 - roffset]; + result = [istream _read: rbuffer + roffset maxLength: 2 - roffset]; if (result == 0) { error = @"SOCKS end-of-file during negotiation"; @@ -652,7 +731,8 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; memcpy(buf + 2, [u bytes], ul); buf[ul + 2] = pl; memcpy(buf + ul + 3, [p bytes], pl); - result = [ostream write: buf + woffset maxLength: want - woffset]; + result = [ostream _write: buf + woffset + maxLength: want - woffset]; if (result == 0) { error = @"SOCKS end-of-file during negotiation"; @@ -672,7 +752,7 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; { int result; - result = [istream read: rbuffer + roffset maxLength: 2 - roffset]; + result = [istream _read: rbuffer + roffset maxLength: 2 - roffset]; if (result == 0) { error = @"SOCKS end-of-file during negotiation"; @@ -719,7 +799,7 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; buf[1] = 1; // Connect command buf[2] = 0; // Reserved buf[3] = 1; // Address type (IPV4) - ptr = [addr lossyCString]; + ptr = [address UTF8String]; buf[4] = atoi(ptr); while (isdigit(*ptr)) ptr++; @@ -733,10 +813,11 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; ptr++; ptr++; buf[7] = atoi(ptr); - buf[8] = ((port & 0xff00) >> 8); - buf[9] = (port & 0xff); + result = [port intValue]; + buf[8] = ((result & 0xff00) >> 8); + buf[9] = (result & 0xff); - result = [ostream write: buf + woffset maxLength: want - woffset]; + result = [ostream _write: buf + woffset maxLength: want - woffset]; if (result == 0) { error = @"SOCKS end-of-file during negotiation"; @@ -756,7 +837,7 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; { int result; - result = [istream read: rbuffer + roffset maxLength: rwant - roffset]; + result = [istream _read: rbuffer + roffset maxLength: rwant - roffset]; if (result == 0) { error = @"SOCKS end-of-file during negotiation"; @@ -861,8 +942,22 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; a = [NSString stringWithUTF8String: (const char*)buf]; } - ASSIGN(addr, a); - port = rbuffer[rwant-1] * 256 * rbuffer[rwant-2]; + + [istream setProperty: a + forKey: GSStreamRemoteAddressKey]; + [ostream setProperty: a + forKey: GSStreamRemoteAddressKey]; + a = [NSString stringWithFormat: @"%d", + rbuffer[rwant-1] * 256 * rbuffer[rwant-2]]; + [istream setProperty: a + forKey: GSStreamRemotePortKey]; + [ostream setProperty: a + forKey: GSStreamRemotePortKey]; + /* Return immediately after calling -bye as it + * will cause this instance to be deallocted. + */ + [self bye]; + return; } } } @@ -870,7 +965,29 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; } } - return error; + if ([error length] > 0) + { + NSError *theError; + + theError = [NSError errorWithDomain: NSCocoaErrorDomain + code: 0 + userInfo: [NSDictionary dictionaryWithObject: error + forKey: NSLocalizedDescriptionKey]]; + if ([istream streamStatus] != NSStreamStatusError) + { + [istream _recordError: theError]; + } + if ([ostream streamStatus] != NSStreamStatusError) + { + [ostream _recordError: theError]; + } + [self bye]; + } +} + +- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len +{ + return [ostream _write: buffer maxLength: len]; } @end @@ -930,6 +1047,10 @@ setNonBlocking(SOCKET fd) [_sibling _setSibling: nil]; _sibling = nil; DESTROY(_handler); + if (_address != 0) + { + NSZoneFree(NSDefaultMallocZone(), _address); + } [super dealloc]; } @@ -953,10 +1074,9 @@ setNonBlocking(SOCKET fd) return self; } -- (struct sockaddr*) _peerAddr +- (struct sockaddr*) _address { - [self subclassResponsibility: _cmd]; - return NULL; + return (struct sockaddr*)_address; } - (int) _read: (uint8_t *)buffer maxLength: (unsigned int)len @@ -988,6 +1108,21 @@ setNonBlocking(SOCKET fd) } } +- (void) _setAddress: (struct sockaddr*)address +{ + if (_address != 0 && SOCKLEN(_address) != SOCKLEN(address)) + { + NSZoneFree(NSDefaultMallocZone(), _address); + _address = 0; + } + if (_address == 0) + { + _address = (struct sockaddr*) + NSZoneMalloc(NSDefaultMallocZone(), SOCKLEN(address)); + } + memcpy(_address, address, SOCKLEN(address)); +} + - (void) _setLoopID: (void *)ref { #if !defined(__MINGW32__) @@ -1038,12 +1173,6 @@ setNonBlocking(SOCKET fd) return _sock; } -- (socklen_t) _sockLen -{ - [self subclassResponsibility: _cmd]; - return 0; -} - - (int) _write: (const uint8_t *)buffer maxLength: (unsigned int)len { [self subclassResponsibility: _cmd]; @@ -1080,7 +1209,7 @@ setNonBlocking(SOCKET fd) { int result; - result = connect([self _sock], [self _peerAddr], [self _sockLen]); + result = connect([self _sock], _address, SOCKLEN(_address)); if (socketError(result)) { if (!socketWouldBlock()) @@ -1523,7 +1652,7 @@ setNonBlocking(SOCKET fd) { int result; - result = connect([self _sock], [self _peerAddr], [self _sockLen]); + result = connect([self _sock], _address, SOCKLEN(_address)); if (socketError(result)) { if (!socketWouldBlock()) @@ -1859,12 +1988,6 @@ setNonBlocking(SOCKET fd) return Nil; } -- (struct sockaddr*) _serverAddr -{ - [self subclassResponsibility: _cmd]; - return 0; -} - #define SOCKET_BACKLOG 256 - (void) open @@ -1885,7 +2008,7 @@ setNonBlocking(SOCKET fd) (char *)&status, sizeof(status)); #endif - bindReturn = bind([self _sock], [self _serverAddr], [self _sockLen]); + bindReturn = bind([self _sock], _address, SOCKLEN(_address)); if (socketError(bindReturn)) { [self _recordError]; @@ -1934,9 +2057,12 @@ setNonBlocking(SOCKET fd) { GSSocketStream *ins = AUTORELEASE([[self _inputStreamClass] new]); GSSocketStream *outs = AUTORELEASE([[self _outputStreamClass] new]); - socklen_t len = [ins _sockLen]; - int acceptReturn = accept([self _sock], [ins _peerAddr], &len); + uint8_t buf[BUFSIZ]; + struct sockaddr *addr = (struct sockaddr*)buf; + socklen_t len = sizeof(buf); + int acceptReturn; + acceptReturn = accept([self _sock], addr, &len); _events &= ~NSStreamEventHasBytesAvailable; if (socketError(acceptReturn)) { // test for real error @@ -1953,7 +2079,8 @@ setNonBlocking(SOCKET fd) [ins _setPassive: YES]; [outs _setPassive: YES]; // copy the addr to outs - memcpy([outs _peerAddr], [ins _peerAddr], len); + [ins _setAddress: addr]; + [outs _setAddress: addr]; [ins _setSock: acceptReturn]; [outs _setSock: acceptReturn]; } @@ -2097,16 +2224,6 @@ static id propertyForInet6Stream(int descriptor, NSString *key) @implementation GSInetInputStream -- (socklen_t) _sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) _peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - - (id) initToAddr: (NSString*)addr port: (int)port { int ptonReturn; @@ -2114,24 +2231,30 @@ static id propertyForInet6Stream(int descriptor, NSString *key) if ((self = [super init]) != nil) { - _peerAddr.sin_family = AF_INET; - _peerAddr.sin_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); + struct sockaddr_in peer; + + peer.sin_family = AF_INET; + peer.sin_port = GSSwapHostI16ToBig(port); + ptonReturn = inet_pton(AF_INET, addr_c, &peer.sin_addr); if (ptonReturn == 0) // error { DESTROY(self); } + else + { + [self _setAddress: (struct sockaddr*)&peer]; + } } return self; } - (id) propertyForKey: (NSString *)key { - id result = propertyForInet4Stream((intptr_t)_loopID, key); + id result = [super propertyForKey: key]; if (result == nil) { - result = [super propertyForKey: key]; + result = propertyForInet4Stream((intptr_t)_loopID, key); } return result; } @@ -2140,15 +2263,6 @@ static id propertyForInet6Stream(int descriptor, NSString *key) @implementation GSInet6InputStream #if defined(AF_INET6) -- (socklen_t) _sockLen -{ - return sizeof(struct sockaddr_in6); -} - -- (struct sockaddr*) _peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - (id) initToAddr: (NSString*)addr port: (int)port { @@ -2157,24 +2271,30 @@ static id propertyForInet6Stream(int descriptor, NSString *key) if ((self = [super init]) != nil) { - _peerAddr.sin6_family = AF_INET6; - _peerAddr.sin6_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); + struct sockaddr_in6 peer; + + peer.sin6_family = AF_INET6; + peer.sin6_port = GSSwapHostI16ToBig(port); + ptonReturn = inet_pton(AF_INET6, addr_c, &peer.sin6_addr); if (ptonReturn == 0) // error { DESTROY(self); } + else + { + [self _setAddress: (struct sockaddr*)&peer]; + } } return self; } - (id) propertyForKey: (NSString *)key { - id result = propertyForInet6Stream((intptr_t)_loopID, key); + id result = [super propertyForKey: key]; if (result == nil) { - result = [super propertyForKey: key]; + result = propertyForInet6Stream((intptr_t)_loopID, key); } return result; } @@ -2190,16 +2310,6 @@ static id propertyForInet6Stream(int descriptor, NSString *key) @implementation GSInetOutputStream -- (socklen_t) _sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) _peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - - (id) initToAddr: (NSString*)addr port: (int)port { int ptonReturn; @@ -2207,24 +2317,30 @@ static id propertyForInet6Stream(int descriptor, NSString *key) if ((self = [super init]) != nil) { - _peerAddr.sin_family = AF_INET; - _peerAddr.sin_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); + struct sockaddr_in peer; + + peer.sin_family = AF_INET; + peer.sin_port = GSSwapHostI16ToBig(port); + ptonReturn = inet_pton(AF_INET, addr_c, &peer.sin_addr); if (ptonReturn == 0) // error { DESTROY(self); } + else + { + [self _setAddress: (struct sockaddr*)&peer]; + } } return self; } - (id) propertyForKey: (NSString *)key { - id result = propertyForInet4Stream((intptr_t)_loopID, key); + id result = [super propertyForKey: key]; if (result == nil) { - result = [super propertyForKey: key]; + result = propertyForInet4Stream((intptr_t)_loopID, key); } return result; } @@ -2233,15 +2349,6 @@ static id propertyForInet6Stream(int descriptor, NSString *key) @implementation GSInet6OutputStream #if defined(AF_INET6) -- (socklen_t) _sockLen -{ - return sizeof(struct sockaddr_in6); -} - -- (struct sockaddr*) _peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - (id) initToAddr: (NSString*)addr port: (int)port { @@ -2250,24 +2357,30 @@ static id propertyForInet6Stream(int descriptor, NSString *key) if ((self = [super init]) != nil) { - _peerAddr.sin6_family = AF_INET6; - _peerAddr.sin6_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); + struct sockaddr_in6 peer; + + peer.sin6_family = AF_INET6; + peer.sin6_port = GSSwapHostI16ToBig(port); + ptonReturn = inet_pton(AF_INET6, addr_c, &peer.sin6_addr); if (ptonReturn == 0) // error { DESTROY(self); } + else + { + [self _setAddress: (struct sockaddr*)&peer]; + } } return self; } - (id) propertyForKey: (NSString *)key { - id result = propertyForInet6Stream((intptr_t)_loopID, key); + id result = [super propertyForKey: key]; if (result == nil) { - result = [super propertyForKey: key]; + result = propertyForInet6Stream((intptr_t)_loopID, key); } return result; } @@ -2293,30 +2406,21 @@ static id propertyForInet6Stream(int descriptor, NSString *key) return [GSInetOutputStream class]; } -- (socklen_t) _sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) _serverAddr -{ - return (struct sockaddr*)&_serverAddr; -} - - (id) initToAddr: (NSString*)addr port: (int)port { if ((self = [super init]) != nil) { int ptonReturn; const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + struct sockaddr_in addr; - _serverAddr.sin_family = AF_INET; - _serverAddr.sin_port = GSSwapHostI16ToBig(port); + addr.sin_family = AF_INET; + addr.sin_port = GSSwapHostI16ToBig(port); if (addr_c == 0) { addr_c = "0.0.0.0"; /* Bind on all addresses */ } - ptonReturn = inet_pton(AF_INET, addr_c, &(_serverAddr.sin_addr)); + ptonReturn = inet_pton(AF_INET, addr_c, &addr.sin_addr); if (ptonReturn == 0) // error { DESTROY(self); @@ -2332,6 +2436,7 @@ static id propertyForInet6Stream(int descriptor, NSString *key) } else { + [self _setAddress: (struct sockaddr*)&addr]; [self _setSock: s]; } } @@ -2353,30 +2458,21 @@ static id propertyForInet6Stream(int descriptor, NSString *key) return [GSInet6OutputStream class]; } -- (socklen_t) _sockLen -{ - return sizeof(struct sockaddr_in6); -} - -- (struct sockaddr*) _serverAddr -{ - return (struct sockaddr*)&_serverAddr; -} - - (id) initToAddr: (NSString*)addr port: (int)port { if ([super init] != nil) { int ptonReturn; const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + struct sockaddr_in6 addr; - _serverAddr.sin6_family = AF_INET6; - _serverAddr.sin6_port = GSSwapHostI16ToBig(port); + addr.sin6_family = AF_INET6; + addr.sin6_port = GSSwapHostI16ToBig(port); if (addr_c == 0) { addr_c = "0:0:0:0:0:0:0:0"; /* Bind on all addresses */ } - ptonReturn = inet_pton(AF_INET6, addr_c, &(_serverAddr.sin6_addr)); + ptonReturn = inet_pton(AF_INET6, addr_c, &addr.sin6_addr); if (ptonReturn == 0) // error { DESTROY(self); @@ -2392,6 +2488,7 @@ static id propertyForInet6Stream(int descriptor, NSString *key) } else { + [self _setAddress: (struct sockaddr*)&addr]; [self _setSock: s]; } } diff --git a/Source/GSStream.h b/Source/GSStream.h index bf02ffe7f..a38268352 100644 --- a/Source/GSStream.h +++ b/Source/GSStream.h @@ -137,6 +137,7 @@ IVARS * record an error based on errno */ - (void) _recordError; +- (void) _recordError: (NSError*)anError; /** * say whether there is unhandled data for the stream. diff --git a/Source/GSStream.m b/Source/GSStream.m index 11b66e3cd..7bf8a144b 100644 --- a/Source/GSStream.m +++ b/Source/GSStream.m @@ -335,6 +335,11 @@ static RunLoopEventType typeForStream(NSStream *aStream) { } +- (void) _recordError: (NSError*)anError +{ + return; +} + - (void) _schedule { } @@ -381,8 +386,13 @@ static RunLoopEventType typeForStream(NSStream *aStream) NSError *theError; theError = [NSError _last]; -// NSLog(@"%@ - %@", self, theError); - ASSIGN(_lastError, theError); + [self _recordError: theError]; +} + +- (void) _recordError: (NSError*)anError +{ +// NSLog(@"%@ - %@", self, anError); + ASSIGN(_lastError, anError); _currentStatus = NSStreamStatusError; } diff --git a/Source/NSURLProtocol.m b/Source/NSURLProtocol.m index 64e5724d6..afd44158a 100644 --- a/Source/NSURLProtocol.m +++ b/Source/NSURLProtocol.m @@ -43,6 +43,7 @@ @end @interface _NSHTTPURLProtocol : NSURLProtocol + { GSMimeParser *_parser; // Parser handling incoming data unsigned _parseOffset; // Bytes of body loaded in parser. @@ -55,6 +56,9 @@ BOOL _debug; BOOL _isLoading; BOOL _shouldClose; + NSURLAuthenticationChallenge *_challenge; + NSURLCredential *_credential; + NSHTTPURLResponse *_response; } @end @@ -295,6 +299,23 @@ static NSURLProtocol *placeholder = nil; return request; } +- (void) cancelAuthenticationChallenge: (NSURLAuthenticationChallenge*)c +{ + if (c == _challenge) + { + DESTROY(_challenge); // We should cancel the download + } +} + +- (void) continueWithoutCredentialForAuthenticationChallenge: + (NSURLAuthenticationChallenge*)c +{ + if (c == _challenge) + { + DESTROY(_credential); // We download the challenge page + } +} + - (void) _didInitializeOutputStream: (NSOutputStream*)stream { return; @@ -304,6 +325,9 @@ static NSURLProtocol *placeholder = nil; { [_parser release]; // received headers [_body release]; // for sending the body + [_response release]; + [_credential release]; + [_credential release]; [super dealloc]; } @@ -348,6 +372,7 @@ static NSURLProtocol *placeholder = nil; return; } + _statusCode = 0; /* No status returned yet. */ _isLoading = YES; _complete = NO; _debug = NO; @@ -357,7 +382,6 @@ static NSURLProtocol *placeholder = nil; */ if ([[[this->request URL] path] length] == 0) { - NSURLRequest *request; NSString *s = [[this->request URL] absoluteString]; NSURL *url; @@ -374,8 +398,7 @@ static NSURLProtocol *placeholder = nil; s = [s stringByAppendingString: @"/"]; } url = [NSURL URLWithString: s]; - request = [NSURLRequest requestWithURL: url]; - if (request == nil) + if (url == nil) { NSError *e; @@ -388,6 +411,10 @@ static NSURLProtocol *placeholder = nil; } else { + NSMutableURLRequest *request; + + request = [this->request mutableCopy]; + [request setURL: url]; [this->client URLProtocol: self wasRedirectedToRequest: request redirectResponse: nil]; @@ -546,7 +573,6 @@ static NSURLProtocol *placeholder = nil; if (wasInHeaders == YES && isInHeaders == NO) { - NSHTTPURLResponse *response; GSMimeHeader *info; NSString *enc; int len = -1; @@ -585,13 +611,14 @@ static NSURLProtocol *placeholder = nil; enc = [[document headerNamed: @"transfer-encoding"] value]; } - response = [[NSHTTPURLResponse alloc] initWithURL: [this->request URL] - MIMEType: nil - expectedContentLength: len - textEncodingName: nil]; - [response _setStatusCode: _statusCode text: s]; + _response = [[NSHTTPURLResponse alloc] + initWithURL: [this->request URL] + MIMEType: nil + expectedContentLength: len + textEncodingName: nil]; + [_response _setStatusCode: _statusCode text: s]; [document deleteHeaderNamed: @"http"]; - [response _setHeaders: [document allHeaders]]; + [_response _setHeaders: [document allHeaders]]; if (_statusCode == 204 || _statusCode == 304) { @@ -606,17 +633,18 @@ static NSURLProtocol *placeholder = nil; _complete = YES; // Had EOF ... terminate } - /* Check for a redirect. - */ - s = [[document headerNamed: @"location"] value]; - if ([s length] > 0) + if (_statusCode == 401) { - NSURLRequest *request; - NSURL *url; + /* This is an authentication challenge, so we keep reading + * until the challenge is complete, then try to deal with it. + */ + } + else if ((s = [[document headerNamed: @"location"] value]) != nil) + { + NSURL *url; url = [NSURL URLWithString: s]; - request = [NSURLRequest requestWithURL: url]; - if (request == nil) + if (url == nil) { NSError *e; @@ -629,9 +657,13 @@ static NSURLProtocol *placeholder = nil; } else { + NSMutableURLRequest *request; + + request = [this->request mutableCopy]; + [request setURL: url]; [this->client URLProtocol: self wasRedirectedToRequest: request - redirectResponse: response]; + redirectResponse: _response]; } } else @@ -660,13 +692,173 @@ static NSURLProtocol *placeholder = nil; } } [this->client URLProtocol: self - didReceiveResponse: response + didReceiveResponse: _response cacheStoragePolicy: policy]; } } if (_complete == YES) { + if (_statusCode == 401) + { + NSURLProtectionSpace *space; + NSString *hdr; + NSURL *url; + int failures = 0; + + /* This was an authentication challenge. + */ + hdr = [[document headerNamed: @"WWW-Authenticate"] value]; + url = [this->request URL]; + space = [GSHTTPAuthentication + protectionSpaceForAuthentication: hdr requestURL: url]; + DESTROY(_credential); + if (space != nil) + { + /* Create credential from user and password + * stored in the URL. + * Returns nil if we have no username or password. + */ + _credential = [[NSURLCredential alloc] + initWithUser: [url user] + password: [url password] + persistence: NSURLCredentialPersistenceForSession]; + if (_credential == nil) + { + /* No credential from the URL, so we try using the + * default credential for the protection space. + */ + ASSIGN(_credential, + [[NSURLCredentialStorage sharedCredentialStorage] + defaultCredentialForProtectionSpace: space]); + } + } + + if (_challenge != nil) + { + /* The failure count is incremented if we have just + * tried a request in the same protection space. + */ + if (YES == [[_challenge protectionSpace] isEqual: space]) + { + failures = [_challenge previousFailureCount] + 1; + } + } + else if ([this->request valueForHTTPHeaderField:@"Authorization"]) + { + /* Our request had an authorization header, so we should + * count that as a failure or we wouldn't have been + * challenged. + */ + failures = 1; + } + DESTROY(_challenge); + + _challenge = [[NSURLAuthenticationChallenge alloc] + initWithProtectionSpace: space + proposedCredential: _credential + previousFailureCount: failures + failureResponse: _response + error: nil + sender: self]; + + /* Allow the client to control the credential we send + * or whether we actually send at all. + */ + [this->client URLProtocol: self + didReceiveAuthenticationChallenge: _challenge]; + + if (_challenge == nil) + { + NSError *e; + + /* The client cancelled the authentication challenge + * so we must cancel the download. + */ + e = [NSError errorWithDomain: @"Authentication cancelled" + code: 0 + userInfo: nil]; + [self stopLoading]; + [this->client URLProtocol: self + didFailWithError: e]; + } + else + { + NSString *auth = nil; + + if (_credential != nil) + { + GSHTTPAuthentication *authentication; + + /* Get information about basic or + * digest authentication. + */ + authentication = [GSHTTPAuthentication + authenticationWithCredential: _credential + inProtectionSpace: space]; + + /* Generate authentication header value for the + * authentication type in the challenge. + */ + auth = [authentication + authorizationForAuthentication: hdr + method: [this->request HTTPMethod] + path: [url path]]; + } + + if (auth == nil) + { + NSURLCacheStoragePolicy policy; + + /* We have no authentication credentials so we + * treat this as a download of the challenge page. + */ + + /* Tell the client that we have a response and how + * it should be cached. + */ + policy = [this->request cachePolicy]; + if (policy == NSURLRequestUseProtocolCachePolicy) + { + if ([self isKindOfClass: [_NSHTTPSURLProtocol class]]) + { + /* For HTTPS we should not allow caching unless + * the request explicitly wants it. + */ + policy = NSURLCacheStorageNotAllowed; + } + else + { + /* For HTTP we allow caching unless the request + * specifically denies it. + */ + policy = NSURLCacheStorageAllowed; + } + } + [this->client URLProtocol: self + didReceiveResponse: _response + cacheStoragePolicy: policy]; + /* Fall through to code providing page data. + */ + } + else + { + NSMutableURLRequest *request; + + /* To answer the authentication challenge, + * we must retry with a modified request and + * with the cached response cleared. + */ + request = [this->request mutableCopy]; + [request setValue: auth + forHTTPHeaderField: @"Authorization"]; + [self stopLoading]; + DESTROY(this->cachedResponse); + [self startLoading]; + } + } + } + [self _unschedule]; if (_shouldClose == YES) { @@ -676,88 +868,6 @@ static NSURLProtocol *placeholder = nil; DESTROY(this->output); } -#if 0 - /* - * Retrieve essential keys from document - */ - if (_statusCode == 401 && self->challenged < 2) - { - GSMimeHeader *ah; - - self->challenged++; // Prevent repeated challenge/auth - if ((ah = [document headerNamed: @"WWW-Authenticate"]) != nil) - { - NSURLProtectionSpace *space; - NSString *ac; - GSHTTPAuthentication *authentication; - NSString *method; - NSString *auth; - - ac = [ah value]; - space = [GSHTTPAuthentication - protectionSpaceForAuthentication: ac requestURL: url]; - if (space == nil) - { - authentication = nil; - } - else - { - NSURLCredential *cred; - - /* - * Create credential from user and password - * stored in the URL. - * Returns nil if we have no username or password. - */ - cred = [[NSURLCredential alloc] - initWithUser: [url user] - password: [url password] - persistence: NSURLCredentialPersistenceForSession]; - - if (cred == nil) - { - authentication = nil; - } - else - { - /* - * Get the digest object and ask it for a header - * to use for authorisation. - * Returns nil if we have no credential. - */ - authentication = [GSHTTPAuthentication - authenticationWithCredential: cred - inProtectionSpace: space]; - RELEASE(cred); - } - } - - method = [request objectForKey: GSHTTPPropertyMethodKey]; - if (method == nil) - { - if ([wData length] > 0) - { - method = @"POST"; - } - else - { - method = @"GET"; - } - } - - auth = [authentication authorizationForAuthentication: ac - method: method - path: [url path]]; - if (auth != nil) - { - [self writeProperty: auth forKey: @"Authorization"]; - [self _tryLoadInBackground: u]; - return; // Retrying. - } - } - } -#endif - /* * Tell superclass that we have successfully loaded the data * (as long as we haven't had the load terminated by the client). @@ -787,7 +897,7 @@ static NSURLProtocol *placeholder = nil; } } } - else if (_isLoading == YES) + else if (_isLoading == YES && _statusCode != 401) { /* * Report partial data if possible. @@ -1074,6 +1184,14 @@ static NSURLProtocol *placeholder = nil; [this->client URLProtocol: self didFailWithError: [stream streamError]]; } +- (void) useCredential: (NSURLCredential*)credential + forAuthenticationChallenge: (NSURLAuthenticationChallenge*)challenge +{ + if (challenge == _challenge) + { + ASSIGN(_credential, credential); + } +} @end @implementation _NSHTTPSURLProtocol diff --git a/Source/NSURLRequest.m b/Source/NSURLRequest.m index fcce7814f..6e8603dec 100644 --- a/Source/NSURLRequest.m +++ b/Source/NSURLRequest.m @@ -183,7 +183,11 @@ typedef struct { cachePolicy: (NSURLRequestCachePolicy)cachePolicy timeoutInterval: (NSTimeInterval)timeoutInterval { - if ((self = [super init]) != nil) + if ([URL isKindOfClass: [NSURL class]] == NO) + { + DESTROY(self); + } + else if ((self = [super init]) != nil) { this->URL = RETAIN(URL); this->cachePolicy = cachePolicy; diff --git a/Source/unix/NSStream.m b/Source/unix/NSStream.m index 1ce0ec141..ae6dae1ea 100644 --- a/Source/unix/NSStream.m +++ b/Source/unix/NSStream.m @@ -49,11 +49,6 @@ @end @interface GSLocalInputStream : GSSocketInputStream -{ - @private - struct sockaddr_un _peerAddr; -} - /** * the designated initializer */ @@ -73,11 +68,6 @@ @end @interface GSLocalOutputStream : GSSocketOutputStream -{ - @private - struct sockaddr_un _peerAddr; -} - /** * the designated initializer */ @@ -218,29 +208,22 @@ if ((self = [super init]) != nil) { - _peerAddr.sun_family = AF_LOCAL; - if (strlen(real_addr)>sizeof(_peerAddr.sun_path)-1) // too long + struct sockaddr_un peer; + + peer.sun_family = AF_LOCAL; + if (strlen(real_addr) > sizeof(peer.sun_path)-1) // too long { DESTROY(self); } else { - strncpy(_peerAddr.sun_path, real_addr, sizeof(_peerAddr.sun_path)-1); + strncpy(peer.sun_path, real_addr, sizeof(peer.sun_path)-1); + [self _setAddress: (struct sockaddr)&peer]; } } return self; } -- (struct sockaddr*) _peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - -- (socklen_t) _sockLen -{ - return sizeof(struct sockaddr_un); -} - @end @implementation GSFileOutputStream @@ -364,29 +347,22 @@ if ((self = [super init]) != nil) { - _peerAddr.sun_family = AF_LOCAL; - if (strlen(real_addr) > sizeof(_peerAddr.sun_path)-1) // too long + struct sockaddr_un peer; + + peer.sun_family = AF_LOCAL; + if (strlen(real_addr) > sizeof(peer.sun_path)-1) // too long { DESTROY(self); } else { - strncpy(_peerAddr.sun_path, real_addr, sizeof(_peerAddr.sun_path)-1); + strncpy(peer.sun_path, real_addr, sizeof(peer.sun_path)-1); + [self _setAddress: (struct sockaddr*)&peer]; } } return self; } -- (struct sockaddr*) _peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_un); -} - @end @implementation NSStream @@ -657,23 +633,14 @@ return [GSLocalOutputStream class]; } -- (socklen_t) _sockLen -{ - return sizeof(struct sockaddr_un); -} - -- (struct sockaddr*) _serverAddr -{ - return (struct sockaddr*)&_serverAddr; -} - - (id) initToAddr: (NSString*)addr { if ((self = [super init]) != nil) { const char* real_addr = [addr fileSystemRepresentation]; + struct sockaddr_un addr; - if (strlen(real_addr) > sizeof(_serverAddr.sun_path)-1) + if (strlen(real_addr) > sizeof(addr.sun_path)-1) { DESTROY(self); } @@ -681,7 +648,7 @@ { SOCKET s; - _serverAddr.sun_family = AF_LOCAL; + addr.sun_family = AF_LOCAL; s = socket(AF_LOCAL, SOCK_STREAM, 0); if (s < 0) { @@ -690,8 +657,8 @@ else { [(GSSocketStream*)self _setSock: s]; - strncpy(_serverAddr.sun_path, real_addr, - sizeof(_serverAddr.sun_path)-1); + strncpy(addr.sun_path, real_addr, sizeof(addr.sun_path)-1); + [self _setAddress: (struct sockaddr)&addr]; } } }