1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/TCP/TCPStream.m Fri May 23 17:37:36 2008 -0700
1.3 @@ -0,0 +1,290 @@
1.4 +//
1.5 +// TCPStream.m
1.6 +// MYNetwork
1.7 +//
1.8 +// Created by Jens Alfke on 5/10/08.
1.9 +// Copyright 2008 Jens Alfke. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "TCPStream.h"
1.13 +#import "TCP_Internal.h"
1.14 +
1.15 +
1.16 +extern const CFStringRef _kCFStreamPropertySSLClientSideAuthentication; // in CFNetwork
1.17 +
1.18 +static NSError* fixStreamError( NSError *error );
1.19 +
1.20 +
1.21 +@implementation TCPStream
1.22 +
1.23 +
1.24 +- (id) initWithConnection: (TCPConnection*)conn stream: (NSStream*)stream
1.25 +{
1.26 + self = [super init];
1.27 + if (self != nil) {
1.28 + _conn = [conn retain];
1.29 + _stream = [stream retain];
1.30 + _stream.delegate = self;
1.31 + [_stream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
1.32 + LogTo(TCPVerbose,@"%@ initialized; status=%i", self,_stream.streamStatus);
1.33 + }
1.34 + return self;
1.35 +}
1.36 +
1.37 +
1.38 +- (void) dealloc
1.39 +{
1.40 + LogTo(TCP,@"DEALLOC %@",self);
1.41 + if( _stream )
1.42 + [self disconnect];
1.43 + [super dealloc];
1.44 +}
1.45 +
1.46 +
1.47 +- (id) propertyForKey: (CFStringRef)cfStreamProperty
1.48 +{
1.49 + return nil; // abstract -- overridden by TCPReader and TCPWriter
1.50 +}
1.51 +
1.52 +- (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
1.53 +{ // abstract -- overridden by TCPReader and TCPWriter
1.54 +}
1.55 +
1.56 +
1.57 +#pragma mark -
1.58 +#pragma mark SSL:
1.59 +
1.60 +
1.61 +- (NSString*) securityLevel {return [_stream propertyForKey: NSStreamSocketSecurityLevelKey];}
1.62 +
1.63 +- (NSDictionary*) SSLProperties {return [self propertyForKey: kCFStreamPropertySSLSettings];}
1.64 +
1.65 +- (void) setSSLProperties: (NSDictionary*)p
1.66 +{
1.67 + LogTo(TCPVerbose,@"%@ SSL settings := %@",self,p);
1.68 + [self setProperty: p forKey: kCFStreamPropertySSLSettings];
1.69 +
1.70 + id clientAuth = [p objectForKey: kTCPPropertySSLClientSideAuthentication];
1.71 + if( clientAuth )
1.72 + [self setProperty: clientAuth forKey: _kCFStreamPropertySSLClientSideAuthentication];
1.73 +}
1.74 +
1.75 +- (NSArray*) peerSSLCerts
1.76 +{
1.77 + Assert(self.isOpen);
1.78 + return [self propertyForKey: kCFStreamPropertySSLPeerCertificates];
1.79 +}
1.80 +
1.81 +
1.82 +#pragma mark -
1.83 +#pragma mark OPENING/CLOSING:
1.84 +
1.85 +
1.86 +- (void) open
1.87 +{
1.88 + Assert(_stream);
1.89 + AssertEq(_stream.streamStatus,NSStreamStatusNotOpen);
1.90 + LogTo(TCP,@"Opening %@",self);
1.91 + [_stream open];
1.92 +}
1.93 +
1.94 +
1.95 +- (void) disconnect
1.96 +{
1.97 + if( _stream ) {
1.98 + LogTo(TCP,@"Disconnect %@",self);
1.99 + _stream.delegate = nil;
1.100 + [_stream close];
1.101 + setObj(&_stream,nil);
1.102 + }
1.103 + setObj(&_conn,nil);
1.104 +}
1.105 +
1.106 +
1.107 +- (BOOL) close
1.108 +{
1.109 + if( self.isBusy ) {
1.110 + _shouldClose = YES;
1.111 + return NO;
1.112 + } else {
1.113 + LogTo(TCP,@"Closing %@",self);
1.114 + [[self retain] autorelease]; // don't let myself be dealloced in the midst of this
1.115 + [_conn _streamClosed: self]; // have to do this before disconnect
1.116 + [self disconnect];
1.117 + return YES;
1.118 + }
1.119 +}
1.120 +
1.121 +
1.122 +- (BOOL) isOpen
1.123 +{
1.124 + NSStreamStatus status = _stream.streamStatus;
1.125 + return status >= NSStreamStatusOpen && status < NSStreamStatusAtEnd;
1.126 +}
1.127 +
1.128 +- (BOOL) isBusy
1.129 +{
1.130 + return NO; // abstract
1.131 +}
1.132 +
1.133 +
1.134 +- (void) _opened
1.135 +{
1.136 + [_conn _streamOpened: self];
1.137 +}
1.138 +
1.139 +- (void) _canRead
1.140 +{
1.141 + // abstract
1.142 +}
1.143 +
1.144 +- (void) _canWrite
1.145 +{
1.146 + // abstract
1.147 +}
1.148 +
1.149 +- (void) _gotEOF
1.150 +{
1.151 + if( self.isBusy )
1.152 + [self _gotError: [NSError errorWithDomain: NSPOSIXErrorDomain code: ECONNRESET userInfo: nil]];
1.153 + else {
1.154 + [self retain];
1.155 + [_conn _streamGotEOF: self];
1.156 + [self disconnect];
1.157 + [self release];
1.158 + }
1.159 +}
1.160 +
1.161 +- (BOOL) _gotError: (NSError*)error
1.162 +{
1.163 + [_conn _stream: self gotError: fixStreamError(error)];
1.164 + return NO;
1.165 +}
1.166 +
1.167 +- (BOOL) _gotError
1.168 +{
1.169 + NSError *error = _stream.streamError;
1.170 + if( ! error )
1.171 + error = [NSError errorWithDomain: NSPOSIXErrorDomain code: EIO userInfo: nil]; //fallback
1.172 + return [self _gotError: error];
1.173 +}
1.174 +
1.175 +
1.176 +- (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)streamEvent
1.177 +{
1.178 + [[self retain] autorelease];
1.179 + switch(streamEvent) {
1.180 + case NSStreamEventOpenCompleted:
1.181 + LogTo(TCPVerbose,@"%@ opened",self);
1.182 + [self _opened];
1.183 + break;
1.184 + case NSStreamEventHasBytesAvailable:
1.185 + if( ! [_conn _streamPeerCertAvailable: self] )
1.186 + return;
1.187 + LogTo(TCPVerbose,@"%@ can read",self);
1.188 + [self _canRead];
1.189 + break;
1.190 + case NSStreamEventHasSpaceAvailable:
1.191 + if( ! [_conn _streamPeerCertAvailable: self] )
1.192 + return;
1.193 + LogTo(TCPVerbose,@"%@ can write",self);
1.194 + [self _canWrite];
1.195 + break;
1.196 + case NSStreamEventErrorOccurred:
1.197 + LogTo(TCPVerbose,@"%@ got error",self);
1.198 + [self _gotError];
1.199 + break;
1.200 + case NSStreamEventEndEncountered:
1.201 + LogTo(TCPVerbose,@"%@ got EOF",self);
1.202 + [self _gotEOF];
1.203 + break;
1.204 + default:
1.205 + Warn(@"%@: unknown NSStreamEvent %i",self,streamEvent);
1.206 + break;
1.207 + }
1.208 +
1.209 + // If I was previously asked to close, try again in case I'm no longer busy
1.210 + if( _shouldClose )
1.211 + [self close];
1.212 +}
1.213 +
1.214 +
1.215 +@end
1.216 +
1.217 +
1.218 +
1.219 +
1.220 +@implementation TCPReader
1.221 +
1.222 +
1.223 +- (TCPWriter*) writer
1.224 +{
1.225 + return _conn.writer;
1.226 +}
1.227 +
1.228 +
1.229 +- (id) propertyForKey: (CFStringRef)cfStreamProperty
1.230 +{
1.231 + CFTypeRef value = CFReadStreamCopyProperty((CFReadStreamRef)_stream,cfStreamProperty);
1.232 + return [(id)CFMakeCollectable(value) autorelease];
1.233 +}
1.234 +
1.235 +- (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
1.236 +{
1.237 + if( ! CFReadStreamSetProperty((CFReadStreamRef)_stream,cfStreamProperty,(CFTypeRef)value) )
1.238 + Warn(@"%@ didn't accept property '%@'", self,cfStreamProperty);
1.239 +}
1.240 +
1.241 +
1.242 +@end
1.243 +
1.244 +
1.245 +
1.246 +
1.247 +static NSError* fixStreamError( NSError *error )
1.248 +{
1.249 + // NSStream incorrectly returns SSL errors without the correct error domain:
1.250 + if( $equal(error.domain,@"NSUnknownErrorDomain") ) {
1.251 + int code = error.code;
1.252 + if( -9899 <= code && code <= -9800 ) {
1.253 + NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
1.254 + if( ! [userInfo objectForKey: NSLocalizedFailureReasonErrorKey] ) {
1.255 + // look up error message:
1.256 + NSBundle *secBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Security.framework"];
1.257 + NSString *message = [secBundle localizedStringForKey: $sprintf(@"%i",code)
1.258 + value: nil
1.259 + table: @"SecErrorMessages"];
1.260 + if( message ) {
1.261 + if( ! userInfo ) userInfo = $mdict();
1.262 + [userInfo setObject: message forKey: NSLocalizedFailureReasonErrorKey];
1.263 + }
1.264 + }
1.265 + error = [NSError errorWithDomain: NSStreamSocketSSLErrorDomain
1.266 + code: code userInfo: userInfo];
1.267 + } else
1.268 + Warn(@"NSStream returned error with unknown domain: %@",error);
1.269 + }
1.270 + return error;
1.271 +}
1.272 +
1.273 +/*
1.274 + Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
1.275 +
1.276 + Redistribution and use in source and binary forms, with or without modification, are permitted
1.277 + provided that the following conditions are met:
1.278 +
1.279 + * Redistributions of source code must retain the above copyright notice, this list of conditions
1.280 + and the following disclaimer.
1.281 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
1.282 + and the following disclaimer in the documentation and/or other materials provided with the
1.283 + distribution.
1.284 +
1.285 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
1.286 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
1.287 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
1.288 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1.289 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1.290 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1.291 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
1.292 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.293 + */