Implemented new close protocol with 'bye' meta-message.
5 // Created by Jens Alfke on 5/10/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
10 #import "TCP_Internal.h"
17 extern const CFStringRef _kCFStreamPropertySSLClientSideAuthentication; // in CFNetwork
19 static NSError* fixStreamError( NSError *error );
22 @implementation TCPStream
25 - (id) initWithConnection: (TCPConnection*)conn stream: (NSStream*)stream
29 _conn = [conn retain];
30 _stream = [stream retain];
31 _stream.delegate = self;
32 [_stream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
33 LogTo(TCPVerbose,@"%@ initialized; status=%i", self,_stream.streamStatus);
41 LogTo(TCP,@"DEALLOC %@",self);
48 - (id) propertyForKey: (CFStringRef)cfStreamProperty
50 return [_stream propertyForKey: (NSString*)cfStreamProperty];
53 - (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
55 if( ! [_stream setProperty: value forKey: (NSString*)cfStreamProperty] )
56 Warn(@"Failed to set property %@ on %@",cfStreamProperty,self);
60 - (IPAddress*) peerAddress
62 const CFSocketNativeHandle *socketPtr = [[self propertyForKey: kCFStreamPropertySocketNativeHandle] bytes];
63 return socketPtr ?[IPAddress addressOfSocket: *socketPtr] :nil;
71 - (NSString*) securityLevel {return [_stream propertyForKey: NSStreamSocketSecurityLevelKey];}
73 - (NSDictionary*) SSLProperties {return [self propertyForKey: kCFStreamPropertySSLSettings];}
75 - (void) setSSLProperties: (NSDictionary*)p
77 LogTo(TCPVerbose,@"%@ SSL settings := %@",self,p);
78 [self setProperty: p forKey: kCFStreamPropertySSLSettings];
80 id clientAuth = [p objectForKey: kTCPPropertySSLClientSideAuthentication];
82 [self setProperty: clientAuth forKey: _kCFStreamPropertySSLClientSideAuthentication];
85 - (NSArray*) peerSSLCerts
88 return [self propertyForKey: kCFStreamPropertySSLPeerCertificates];
93 #pragma mark OPENING/CLOSING:
99 AssertEq(_stream.streamStatus,NSStreamStatusNotOpen);
100 LogTo(TCP,@"Opening %@",self);
108 LogTo(TCP,@"Disconnect %@",self);
109 _stream.delegate = nil;
111 setObj(&_stream,nil);
115 [_conn _streamDisconnected: self];
128 LogTo(TCP,@"Request to close %@",self);
129 [[self retain] autorelease]; // don't let myself be dealloced in the midst of this
130 [_conn _streamCanClose: self];
138 NSStreamStatus status = _stream.streamStatus;
139 return status >= NSStreamStatusOpen && status < NSStreamStatusAtEnd;
144 return NO; // abstract
149 return !_shouldClose || self.isBusy;
155 [_conn _streamOpened: self];
170 [_conn _streamGotEOF: self];
173 - (BOOL) _gotError: (NSError*)error
175 [_conn _stream: self gotError: fixStreamError(error)];
181 NSError *error = _stream.streamError;
183 error = [NSError errorWithDomain: NSPOSIXErrorDomain code: EIO userInfo: nil]; //fallback
184 return [self _gotError: error];
188 - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)streamEvent
190 [[self retain] autorelease];
191 switch(streamEvent) {
192 case NSStreamEventOpenCompleted:
193 LogTo(TCPVerbose,@"%@ opened",self);
196 case NSStreamEventHasBytesAvailable:
197 if( ! [_conn _streamPeerCertAvailable: self] )
199 LogTo(TCPVerbose,@"%@ can read",self);
202 case NSStreamEventHasSpaceAvailable:
203 if( ! [_conn _streamPeerCertAvailable: self] )
205 LogTo(TCPVerbose,@"%@ can write",self);
208 case NSStreamEventErrorOccurred:
209 LogTo(TCPVerbose,@"%@ got error",self);
212 case NSStreamEventEndEncountered:
213 LogTo(TCPVerbose,@"%@ got EOF",self);
217 Warn(@"%@: unknown NSStreamEvent %i",self,streamEvent);
221 // If I was previously asked to close, try again in case I'm no longer busy
232 @implementation TCPReader
235 - (TCPWriter*) writer
240 - (NSInteger) read: (void*)dst maxLength: (NSUInteger)maxLength
242 NSInteger bytesRead = [(NSInputStream*)_stream read:dst maxLength: maxLength];
254 static NSError* fixStreamError( NSError *error )
256 // NSStream incorrectly returns SSL errors without the correct error domain:
257 if( $equal(error.domain,@"NSUnknownErrorDomain") ) {
258 int code = error.code;
259 if( -9899 <= code && code <= -9800 ) {
260 NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
261 if( ! [userInfo objectForKey: NSLocalizedFailureReasonErrorKey] ) {
262 // look up error message:
263 NSBundle *secBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Security.framework"];
264 NSString *message = [secBundle localizedStringForKey: $sprintf(@"%i",code)
266 table: @"SecErrorMessages"];
268 if( ! userInfo ) userInfo = $mdict();
269 [userInfo setObject: message forKey: NSLocalizedFailureReasonErrorKey];
272 error = [NSError errorWithDomain: NSStreamSocketSSLErrorDomain
273 code: code userInfo: userInfo];
275 Warn(@"NSStream returned error with unknown domain: %@",error);
281 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
283 Redistribution and use in source and binary forms, with or without modification, are permitted
284 provided that the following conditions are met:
286 * Redistributions of source code must retain the above copyright notice, this list of conditions
287 and the following disclaimer.
288 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
289 and the following disclaimer in the documentation and/or other materials provided with the
292 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
293 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
294 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
295 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
296 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
297 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
298 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
299 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.