| jens@0 |      1 | //
 | 
| jens@0 |      2 | //  TCPStream.m
 | 
| jens@0 |      3 | //  MYNetwork
 | 
| jens@0 |      4 | //
 | 
| jens@0 |      5 | //  Created by Jens Alfke on 5/10/08.
 | 
| jens@0 |      6 | //  Copyright 2008 Jens Alfke. All rights reserved.
 | 
| jens@0 |      7 | //
 | 
| jens@0 |      8 | 
 | 
| jens@0 |      9 | #import "TCPStream.h"
 | 
| jens@0 |     10 | #import "TCP_Internal.h"
 | 
| jens@7 |     11 | #import "IPAddress.h"
 | 
| jens@0 |     12 | 
 | 
| jens@1 |     13 | #import "Logging.h"
 | 
| jens@1 |     14 | #import "Test.h"
 | 
| jens@1 |     15 | 
 | 
| jens@0 |     16 | 
 | 
| jens@0 |     17 | extern const CFStringRef _kCFStreamPropertySSLClientSideAuthentication; // in CFNetwork
 | 
| jens@0 |     18 | 
 | 
| jens@0 |     19 | static NSError* fixStreamError( NSError *error );
 | 
| jens@0 |     20 | 
 | 
| jens@0 |     21 | 
 | 
| jens@0 |     22 | @implementation TCPStream
 | 
| jens@0 |     23 | 
 | 
| jens@0 |     24 | 
 | 
| jens@0 |     25 | - (id) initWithConnection: (TCPConnection*)conn stream: (NSStream*)stream
 | 
| jens@0 |     26 | {
 | 
| jens@0 |     27 |     self = [super init];
 | 
| jens@0 |     28 |     if (self != nil) {
 | 
| jens@0 |     29 |         _conn = [conn retain];
 | 
| jens@0 |     30 |         _stream = [stream retain];
 | 
| jens@0 |     31 |         _stream.delegate = self;
 | 
| jens@0 |     32 |         [_stream scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSRunLoopCommonModes];
 | 
| jens@0 |     33 |         LogTo(TCPVerbose,@"%@ initialized; status=%i", self,_stream.streamStatus);
 | 
| jens@0 |     34 |     }
 | 
| jens@0 |     35 |     return self;
 | 
| jens@0 |     36 | }
 | 
| jens@0 |     37 | 
 | 
| jens@0 |     38 | 
 | 
| jens@0 |     39 | - (void) dealloc
 | 
| jens@0 |     40 | {
 | 
| jens@0 |     41 |     LogTo(TCP,@"DEALLOC %@",self);
 | 
| jens@0 |     42 |     if( _stream )
 | 
| jens@0 |     43 |         [self disconnect];
 | 
| jens@0 |     44 |     [super dealloc];
 | 
| jens@0 |     45 | }
 | 
| jens@0 |     46 | 
 | 
| jens@0 |     47 | 
 | 
| jens@0 |     48 | - (id) propertyForKey: (CFStringRef)cfStreamProperty
 | 
| jens@0 |     49 | {
 | 
| jens@7 |     50 |     return [_stream propertyForKey: (NSString*)cfStreamProperty];
 | 
| jens@0 |     51 | }
 | 
| jens@0 |     52 | 
 | 
| jens@0 |     53 | - (void) setProperty: (id)value forKey: (CFStringRef)cfStreamProperty
 | 
| jens@7 |     54 | {
 | 
| jens@7 |     55 |     if( ! [_stream setProperty: value forKey: (NSString*)cfStreamProperty] )
 | 
| jens@7 |     56 |         Warn(@"Failed to set property %@ on %@",cfStreamProperty,self);
 | 
| jens@7 |     57 | }
 | 
| jens@7 |     58 | 
 | 
| jens@7 |     59 | 
 | 
| jens@7 |     60 | - (IPAddress*) peerAddress
 | 
| jens@7 |     61 | {
 | 
| jens@7 |     62 |     const CFSocketNativeHandle *socketPtr = [[self propertyForKey: kCFStreamPropertySocketNativeHandle] bytes];
 | 
| jens@7 |     63 |     return socketPtr ?[IPAddress addressOfSocket: *socketPtr] :nil;
 | 
| jens@0 |     64 | }
 | 
| jens@0 |     65 | 
 | 
| jens@0 |     66 | 
 | 
| jens@0 |     67 | #pragma mark -
 | 
| jens@0 |     68 | #pragma mark SSL:
 | 
| jens@0 |     69 | 
 | 
| jens@0 |     70 | 
 | 
| jens@0 |     71 | - (NSString*) securityLevel                 {return [_stream propertyForKey: NSStreamSocketSecurityLevelKey];}
 | 
| jens@0 |     72 | 
 | 
| jens@0 |     73 | - (NSDictionary*) SSLProperties             {return [self propertyForKey: kCFStreamPropertySSLSettings];}
 | 
| jens@0 |     74 | 
 | 
| jens@0 |     75 | - (void) setSSLProperties: (NSDictionary*)p
 | 
| jens@0 |     76 | {
 | 
| jens@0 |     77 |     LogTo(TCPVerbose,@"%@ SSL settings := %@",self,p);
 | 
| jens@0 |     78 |     [self setProperty: p forKey: kCFStreamPropertySSLSettings];
 | 
| jens@0 |     79 |     
 | 
| jens@0 |     80 |     id clientAuth = [p objectForKey: kTCPPropertySSLClientSideAuthentication];
 | 
| jens@0 |     81 |     if( clientAuth )
 | 
| jens@0 |     82 |         [self setProperty: clientAuth forKey: _kCFStreamPropertySSLClientSideAuthentication];
 | 
| jens@0 |     83 | }
 | 
| jens@0 |     84 | 
 | 
| jens@0 |     85 | - (NSArray*) peerSSLCerts
 | 
| jens@0 |     86 | {
 | 
| jens@0 |     87 |     Assert(self.isOpen);
 | 
| jens@0 |     88 |     return [self propertyForKey: kCFStreamPropertySSLPeerCertificates];
 | 
| jens@0 |     89 | }
 | 
| jens@0 |     90 | 
 | 
| jens@0 |     91 | 
 | 
| jens@0 |     92 | #pragma mark -
 | 
| jens@0 |     93 | #pragma mark OPENING/CLOSING:
 | 
| jens@0 |     94 | 
 | 
| jens@0 |     95 | 
 | 
| jens@0 |     96 | - (void) open
 | 
| jens@0 |     97 | {
 | 
| jens@0 |     98 |     Assert(_stream);
 | 
| jens@26 |     99 |     AssertEq(_stream.streamStatus,(NSStreamStatus)NSStreamStatusNotOpen);
 | 
| jens@0 |    100 |     LogTo(TCP,@"Opening %@",self);
 | 
| jens@0 |    101 |     [_stream open];
 | 
| jens@0 |    102 | }
 | 
| jens@0 |    103 | 
 | 
| jens@0 |    104 | 
 | 
| jens@0 |    105 | - (void) disconnect
 | 
| jens@0 |    106 | {
 | 
| jens@0 |    107 |     if( _stream ) {
 | 
| jens@0 |    108 |         LogTo(TCP,@"Disconnect %@",self);
 | 
| jens@0 |    109 |         _stream.delegate = nil;
 | 
| jens@0 |    110 |         [_stream close];
 | 
| jens@0 |    111 |         setObj(&_stream,nil);
 | 
| jens@0 |    112 |     }
 | 
| jens@18 |    113 |     if( _conn ) {
 | 
| jens@18 |    114 |         [self retain];
 | 
| jens@18 |    115 |         [_conn _streamDisconnected: self];
 | 
| jens@18 |    116 |         setObj(&_conn,nil);
 | 
| jens@18 |    117 |         [self release];
 | 
| jens@18 |    118 |     }
 | 
| jens@0 |    119 | }
 | 
| jens@0 |    120 | 
 | 
| jens@0 |    121 | 
 | 
| jens@0 |    122 | - (BOOL) close
 | 
| jens@0 |    123 | {
 | 
| jens@19 |    124 |     if( ! _shouldClose ) {
 | 
| jens@19 |    125 |         _shouldClose = YES;
 | 
| jens@19 |    126 |         LogTo(TCP,@"Request to close %@",self);
 | 
| jens@19 |    127 |     }
 | 
| jens@0 |    128 |     if( self.isBusy ) {
 | 
| jens@0 |    129 |         return NO;
 | 
| jens@0 |    130 |     } else {
 | 
| jens@18 |    131 |         [[self retain] autorelease];        // don't let myself be dealloced in the midst of this
 | 
| jens@18 |    132 |         [_conn _streamCanClose: self];
 | 
| jens@0 |    133 |         return YES;
 | 
| jens@0 |    134 |     }
 | 
| jens@0 |    135 | }
 | 
| jens@0 |    136 | 
 | 
| jens@19 |    137 | - (void) _unclose
 | 
| jens@19 |    138 | {
 | 
| jens@19 |    139 |     _shouldClose = NO;
 | 
| jens@19 |    140 | }
 | 
| jens@19 |    141 | 
 | 
| jens@0 |    142 | 
 | 
| jens@0 |    143 | - (BOOL) isOpen
 | 
| jens@0 |    144 | {
 | 
| jens@0 |    145 |     NSStreamStatus status = _stream.streamStatus;
 | 
| jens@0 |    146 |     return status >= NSStreamStatusOpen && status < NSStreamStatusAtEnd;
 | 
| jens@0 |    147 | }
 | 
| jens@0 |    148 | 
 | 
| jens@0 |    149 | - (BOOL) isBusy
 | 
| jens@0 |    150 | {
 | 
| jens@0 |    151 |     return NO;  // abstract
 | 
| jens@0 |    152 | }
 | 
| jens@0 |    153 | 
 | 
| jens@18 |    154 | - (BOOL) isActive
 | 
| jens@18 |    155 | {
 | 
| jens@18 |    156 |     return !_shouldClose || self.isBusy;
 | 
| jens@18 |    157 | }
 | 
| jens@18 |    158 | 
 | 
| jens@0 |    159 | 
 | 
| jens@0 |    160 | - (void) _opened
 | 
| jens@0 |    161 | {
 | 
| jens@0 |    162 |     [_conn _streamOpened: self];
 | 
| jens@0 |    163 | }
 | 
| jens@0 |    164 | 
 | 
| jens@0 |    165 | - (void) _canRead
 | 
| jens@0 |    166 | {
 | 
| jens@0 |    167 |     // abstract
 | 
| jens@0 |    168 | }
 | 
| jens@0 |    169 | 
 | 
| jens@0 |    170 | - (void) _canWrite
 | 
| jens@0 |    171 | {
 | 
| jens@0 |    172 |     // abstract
 | 
| jens@0 |    173 | }
 | 
| jens@0 |    174 | 
 | 
| jens@0 |    175 | - (void) _gotEOF
 | 
| jens@0 |    176 | {
 | 
| jens@18 |    177 |     [_conn _streamGotEOF: self];
 | 
| jens@0 |    178 | }
 | 
| jens@0 |    179 | 
 | 
| jens@0 |    180 | - (BOOL) _gotError: (NSError*)error
 | 
| jens@0 |    181 | {
 | 
| jens@0 |    182 |     [_conn _stream: self gotError: fixStreamError(error)];
 | 
| jens@0 |    183 |     return NO;
 | 
| jens@0 |    184 | }
 | 
| jens@0 |    185 | 
 | 
| jens@0 |    186 | - (BOOL) _gotError
 | 
| jens@0 |    187 | {
 | 
| jens@0 |    188 |     NSError *error = _stream.streamError;
 | 
| jens@0 |    189 |     if( ! error )
 | 
| jens@0 |    190 |         error = [NSError errorWithDomain: NSPOSIXErrorDomain code: EIO userInfo: nil]; //fallback
 | 
| jens@0 |    191 |     return [self _gotError: error];
 | 
| jens@0 |    192 | }
 | 
| jens@0 |    193 | 
 | 
| jens@0 |    194 | 
 | 
| jens@0 |    195 | - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)streamEvent 
 | 
| jens@0 |    196 | {
 | 
| jens@0 |    197 |     [[self retain] autorelease];
 | 
| jens@0 |    198 |     switch(streamEvent) {
 | 
| jens@0 |    199 |         case NSStreamEventOpenCompleted:
 | 
| jens@0 |    200 |             LogTo(TCPVerbose,@"%@ opened",self);
 | 
| jens@0 |    201 |             [self _opened];
 | 
| jens@0 |    202 |             break;
 | 
| jens@0 |    203 |         case NSStreamEventHasBytesAvailable:
 | 
| jens@0 |    204 |             if( ! [_conn _streamPeerCertAvailable: self] )
 | 
| jens@0 |    205 |                 return;
 | 
| jens@0 |    206 |             LogTo(TCPVerbose,@"%@ can read",self);
 | 
| jens@0 |    207 |             [self _canRead];
 | 
| jens@0 |    208 |             break;
 | 
| jens@0 |    209 |         case NSStreamEventHasSpaceAvailable:
 | 
| jens@0 |    210 |             if( ! [_conn _streamPeerCertAvailable: self] )
 | 
| jens@0 |    211 |                 return;
 | 
| jens@0 |    212 |             LogTo(TCPVerbose,@"%@ can write",self);
 | 
| jens@0 |    213 |             [self _canWrite];
 | 
| jens@0 |    214 |             break;
 | 
| jens@0 |    215 |         case NSStreamEventErrorOccurred:
 | 
| jens@0 |    216 |             LogTo(TCPVerbose,@"%@ got error",self);
 | 
| jens@0 |    217 |             [self _gotError];
 | 
| jens@0 |    218 |             break;
 | 
| jens@0 |    219 |         case NSStreamEventEndEncountered:
 | 
| jens@0 |    220 |             LogTo(TCPVerbose,@"%@ got EOF",self);
 | 
| jens@0 |    221 |             [self _gotEOF];
 | 
| jens@0 |    222 |             break;
 | 
| jens@0 |    223 |         default:
 | 
| jens@0 |    224 |             Warn(@"%@: unknown NSStreamEvent %i",self,streamEvent);
 | 
| jens@0 |    225 |             break;
 | 
| jens@0 |    226 |     }
 | 
| jens@0 |    227 |     
 | 
| jens@0 |    228 |     // If I was previously asked to close, try again in case I'm no longer busy
 | 
| jens@0 |    229 |     if( _shouldClose )
 | 
| jens@0 |    230 |         [self close];
 | 
| jens@0 |    231 | }
 | 
| jens@0 |    232 | 
 | 
| jens@0 |    233 | 
 | 
| jens@0 |    234 | @end
 | 
| jens@0 |    235 | 
 | 
| jens@0 |    236 | 
 | 
| jens@0 |    237 | 
 | 
| jens@0 |    238 | 
 | 
| jens@0 |    239 | @implementation TCPReader
 | 
| jens@0 |    240 | 
 | 
| jens@0 |    241 | 
 | 
| jens@0 |    242 | - (TCPWriter*) writer
 | 
| jens@0 |    243 | {
 | 
| jens@0 |    244 |     return _conn.writer;
 | 
| jens@0 |    245 | }
 | 
| jens@0 |    246 | 
 | 
| jens@2 |    247 | - (NSInteger) read: (void*)dst maxLength: (NSUInteger)maxLength
 | 
| jens@2 |    248 | {
 | 
| jens@2 |    249 |     NSInteger bytesRead = [(NSInputStream*)_stream read:dst maxLength: maxLength];
 | 
| jens@2 |    250 |     if( bytesRead < 0 )
 | 
| jens@2 |    251 |         [self _gotError];
 | 
| jens@2 |    252 |     return bytesRead;
 | 
| jens@2 |    253 | }
 | 
| jens@2 |    254 | 
 | 
| jens@2 |    255 | 
 | 
| jens@0 |    256 | @end
 | 
| jens@0 |    257 | 
 | 
| jens@0 |    258 | 
 | 
| jens@0 |    259 | 
 | 
| jens@0 |    260 | 
 | 
| jens@0 |    261 | static NSError* fixStreamError( NSError *error )
 | 
| jens@0 |    262 | {
 | 
| jens@0 |    263 |     // NSStream incorrectly returns SSL errors without the correct error domain:
 | 
| jens@0 |    264 |     if( $equal(error.domain,@"NSUnknownErrorDomain") ) {
 | 
| jens@0 |    265 |         int code = error.code;
 | 
| jens@0 |    266 |         if( -9899 <= code && code <= -9800 ) {
 | 
| jens@0 |    267 |             NSMutableDictionary *userInfo = error.userInfo.mutableCopy;
 | 
| jens@0 |    268 |             if( ! [userInfo objectForKey: NSLocalizedFailureReasonErrorKey] ) {
 | 
| jens@0 |    269 |                 // look up error message:
 | 
| jens@0 |    270 |                 NSBundle *secBundle = [NSBundle bundleWithPath: @"/System/Library/Frameworks/Security.framework"];
 | 
| jens@0 |    271 |                 NSString *message = [secBundle localizedStringForKey: $sprintf(@"%i",code)
 | 
| jens@0 |    272 |                                                                value: nil
 | 
| jens@0 |    273 |                                                                table: @"SecErrorMessages"];
 | 
| jens@0 |    274 |                 if( message ) {
 | 
| jens@0 |    275 |                     if( ! userInfo ) userInfo = $mdict();
 | 
| jens@0 |    276 |                     [userInfo setObject: message forKey: NSLocalizedFailureReasonErrorKey];
 | 
| jens@0 |    277 |                 }
 | 
| jens@0 |    278 |             }
 | 
| jens@0 |    279 |             error = [NSError errorWithDomain: NSStreamSocketSSLErrorDomain
 | 
| jens@0 |    280 |                                         code: code userInfo: userInfo];
 | 
| jens@0 |    281 |         } else
 | 
| jens@0 |    282 |             Warn(@"NSStream returned error with unknown domain: %@",error);
 | 
| jens@0 |    283 |     }
 | 
| jens@0 |    284 |     return error;
 | 
| jens@0 |    285 | }
 | 
| jens@0 |    286 | 
 | 
| jens@0 |    287 | /*
 | 
| jens@0 |    288 |  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
 | 
| jens@0 |    289 |  
 | 
| jens@0 |    290 |  Redistribution and use in source and binary forms, with or without modification, are permitted
 | 
| jens@0 |    291 |  provided that the following conditions are met:
 | 
| jens@0 |    292 |  
 | 
| jens@0 |    293 |  * Redistributions of source code must retain the above copyright notice, this list of conditions
 | 
| jens@0 |    294 |  and the following disclaimer.
 | 
| jens@0 |    295 |  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 | 
| jens@0 |    296 |  and the following disclaimer in the documentation and/or other materials provided with the
 | 
| jens@0 |    297 |  distribution.
 | 
| jens@0 |    298 |  
 | 
| jens@0 |    299 |  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 | 
| jens@0 |    300 |  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 | 
| jens@0 |    301 |  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
 | 
| jens@0 |    302 |  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
| jens@0 |    303 |  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 | 
| jens@0 |    304 |   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 | 
| jens@0 |    305 |  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
 | 
| jens@0 |    306 |  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
| jens@0 |    307 |  */
 |