5 // Created by Jens Alfke on 5/10/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
10 #import "TCP_Internal.h"
13 extern const CFStringRef _kCFStreamPropertySSLClientSideAuthentication; // in CFNetwork
15 static NSError* fixStreamError( NSError *error );
18 @implementation TCPStream
21 - (id) initWithConnection: (TCPConnection*)conn stream: (NSStream*)stream
25 _conn = [conn retain];
26 _stream = [stream retain];
27 _stream.delegate = self;
28 [_stream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
29 LogTo(TCPVerbose,@"%@ initialized; status=%i", self,_stream.streamStatus);
37 LogTo(TCP,@"DEALLOC %@",self);
44 - (id) propertyForKey: (CFStringRef)cfStreamProperty
46 return nil; // abstract -- overridden by TCPReader and TCPWriter
49 - (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
50 { // abstract -- overridden by TCPReader and TCPWriter
58 - (NSString*) securityLevel {return [_stream propertyForKey: NSStreamSocketSecurityLevelKey];}
60 - (NSDictionary*) SSLProperties {return [self propertyForKey: kCFStreamPropertySSLSettings];}
62 - (void) setSSLProperties: (NSDictionary*)p
64 LogTo(TCPVerbose,@"%@ SSL settings := %@",self,p);
65 [self setProperty: p forKey: kCFStreamPropertySSLSettings];
67 id clientAuth = [p objectForKey: kTCPPropertySSLClientSideAuthentication];
69 [self setProperty: clientAuth forKey: _kCFStreamPropertySSLClientSideAuthentication];
72 - (NSArray*) peerSSLCerts
75 return [self propertyForKey: kCFStreamPropertySSLPeerCertificates];
80 #pragma mark OPENING/CLOSING:
86 AssertEq(_stream.streamStatus,NSStreamStatusNotOpen);
87 LogTo(TCP,@"Opening %@",self);
95 LogTo(TCP,@"Disconnect %@",self);
96 _stream.delegate = nil;
110 LogTo(TCP,@"Closing %@",self);
111 [[self retain] autorelease]; // don't let myself be dealloced in the midst of this
112 [_conn _streamClosed: self]; // have to do this before disconnect
121 NSStreamStatus status = _stream.streamStatus;
122 return status >= NSStreamStatusOpen && status < NSStreamStatusAtEnd;
127 return NO; // abstract
133 [_conn _streamOpened: self];
149 [self _gotError: [NSError errorWithDomain: NSPOSIXErrorDomain code: ECONNRESET userInfo: nil]];
152 [_conn _streamGotEOF: self];
158 - (BOOL) _gotError: (NSError*)error
160 [_conn _stream: self gotError: fixStreamError(error)];
166 NSError *error = _stream.streamError;
168 error = [NSError errorWithDomain: NSPOSIXErrorDomain code: EIO userInfo: nil]; //fallback
169 return [self _gotError: error];
173 - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)streamEvent
175 [[self retain] autorelease];
176 switch(streamEvent) {
177 case NSStreamEventOpenCompleted:
178 LogTo(TCPVerbose,@"%@ opened",self);
181 case NSStreamEventHasBytesAvailable:
182 if( ! [_conn _streamPeerCertAvailable: self] )
184 LogTo(TCPVerbose,@"%@ can read",self);
187 case NSStreamEventHasSpaceAvailable:
188 if( ! [_conn _streamPeerCertAvailable: self] )
190 LogTo(TCPVerbose,@"%@ can write",self);
193 case NSStreamEventErrorOccurred:
194 LogTo(TCPVerbose,@"%@ got error",self);
197 case NSStreamEventEndEncountered:
198 LogTo(TCPVerbose,@"%@ got EOF",self);
202 Warn(@"%@: unknown NSStreamEvent %i",self,streamEvent);
206 // If I was previously asked to close, try again in case I'm no longer busy
217 @implementation TCPReader
220 - (TCPWriter*) writer
226 - (id) propertyForKey: (CFStringRef)cfStreamProperty
228 CFTypeRef value = CFReadStreamCopyProperty((CFReadStreamRef)_stream,cfStreamProperty);
229 return [(id)CFMakeCollectable(value) autorelease];
232 - (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
234 if( ! CFReadStreamSetProperty((CFReadStreamRef)_stream,cfStreamProperty,(CFTypeRef)value) )
235 Warn(@"%@ didn't accept property '%@'", self,cfStreamProperty);
244 static NSError* fixStreamError( NSError *error )
246 // NSStream incorrectly returns SSL errors without the correct error domain:
247 if( $equal(error.domain,@"NSUnknownErrorDomain") ) {
248 int code = error.code;
249 if( -9899 <= code && code <= -9800 ) {
250 NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
251 if( ! [userInfo objectForKey: NSLocalizedFailureReasonErrorKey] ) {
252 // look up error message:
253 NSBundle *secBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Security.framework"];
254 NSString *message = [secBundle localizedStringForKey: $sprintf(@"%i",code)
256 table: @"SecErrorMessages"];
258 if( ! userInfo ) userInfo = $mdict();
259 [userInfo setObject: message forKey: NSLocalizedFailureReasonErrorKey];
262 error = [NSError errorWithDomain: NSStreamSocketSSLErrorDomain
263 code: code userInfo: userInfo];
265 Warn(@"NSStream returned error with unknown domain: %@",error);
271 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
273 Redistribution and use in source and binary forms, with or without modification, are permitted
274 provided that the following conditions are met:
276 * Redistributions of source code must retain the above copyright notice, this list of conditions
277 and the following disclaimer.
278 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
279 and the following disclaimer in the documentation and/or other materials provided with the
282 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
283 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
284 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
285 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
286 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
287 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
288 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
289 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.