| File: | Plugins/Bonjour/libezv/Simple HTTP Server/AsyncSocket.m |
| Location: | line 1584, column 7 |
| Description: | Memory Leak |
| Code is compiled without garbage collection. |
| 1 | // |
| 2 | // AsyncSocket.m |
| 3 | // |
| 4 | // This class is in the public domain. |
| 5 | // Originally created by Dustin Voss on Wed Jan 29 2003. |
| 6 | // Updated and maintained by Deusty Designs and the Mac development community. |
| 7 | // |
| 8 | // http://code.google.com/p/cocoaasyncsocket/ |
| 9 | // |
| 10 | |
| 11 | #import "AsyncSocket.h" |
| 12 | #import <sys/socket.h> |
| 13 | #import <netinet/in.h> |
| 14 | #import <arpa/inet.h> |
| 15 | #import <netdb.h> |
| 16 | |
| 17 | #pragma mark Declarations |
| 18 | |
| 19 | #define READQUEUE_CAPACITY 5 // Initial capacity |
| 20 | #define WRITEQUEUE_CAPACITY 5 // Initial capacity |
| 21 | #define READALL_CHUNKSIZE 256 // Incremental increase in buffer size |
| 22 | #define WRITE_CHUNKSIZE (1024 * 4) // Limit on size of each write pass |
| 23 | |
| 24 | NSString *const AsyncSocketException = @"AsyncSocketException"; |
| 25 | NSString *const AsyncSocketErrorDomain = @"AsyncSocketErrorDomain"; |
| 26 | |
| 27 | // This is a mutex lock used by all instances of AsyncSocket, to protect getaddrinfo. |
| 28 | // The man page says it is not thread-safe. |
| 29 | NSString *getaddrinfoLock = @"lock"; |
| 30 | |
| 31 | enum AsyncSocketFlags |
| 32 | { |
| 33 | kDidCallConnectDeleg = 0x01, // If set, connect delegate has been called. |
| 34 | kDidPassConnectMethod = 0x02, // If set, disconnection results in delegate call. |
| 35 | kForbidReadsWrites = 0x04, // If set, no new reads or writes are allowed. |
| 36 | kDisconnectSoon = 0x08 // If set, disconnect as soon as nothing is queued. |
| 37 | }; |
| 38 | |
| 39 | @interface AsyncSocket (Private) |
| 40 | |
| 41 | // Socket Implementation |
| 42 | - (CFSocketRef) createAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr; |
| 43 | - (BOOL) createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr; |
| 44 | - (BOOL) attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr; |
| 45 | - (BOOL) configureSocketAndReturnError:(NSError **)errPtr; |
| 46 | - (BOOL) connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; |
| 47 | - (void) doAcceptWithSocket:(CFSocketNativeHandle)newSocket; |
| 48 | - (void) doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)err; |
| 49 | |
| 50 | // Stream Implementation |
| 51 | - (BOOL) createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr; |
| 52 | - (BOOL) createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr; |
| 53 | - (BOOL) attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr; |
| 54 | - (BOOL) configureStreamsAndReturnError:(NSError **)errPtr; |
| 55 | - (BOOL) openStreamsAndReturnError:(NSError **)errPtr; |
| 56 | - (void) doStreamOpen; |
| 57 | - (BOOL) setSocketFromStreamsAndReturnError:(NSError **)errPtr; |
| 58 | |
| 59 | // Disconnect Implementation |
| 60 | - (void) closeWithError:(NSError *)err; |
| 61 | - (void) recoverUnreadData; |
| 62 | - (void) emptyQueues; |
| 63 | - (void) close; |
| 64 | |
| 65 | // Errors |
| 66 | - (NSError *) getErrnoError; |
| 67 | - (NSError *) getAbortError; |
| 68 | - (NSError *) getStreamError; |
| 69 | - (NSError *) getSocketError; |
| 70 | - (NSError *) getReadTimeoutError; |
| 71 | - (NSError *) getWriteTimeoutError; |
| 72 | - (NSError *) errorFromCFStreamError:(CFStreamError)err; |
| 73 | |
| 74 | // Diagnostics |
| 75 | - (BOOL) isSocketConnected; |
| 76 | - (BOOL) areStreamsConnected; |
| 77 | - (NSString *) connectedHost: (CFSocketRef)socket; |
| 78 | - (UInt16) connectedPort: (CFSocketRef)socket; |
| 79 | - (NSString *) localHost: (CFSocketRef)socket; |
| 80 | - (UInt16) localPort: (CFSocketRef)socket; |
| 81 | - (NSString *) addressHost: (CFDataRef)cfaddr; |
| 82 | - (UInt16) addressPort: (CFDataRef)cfaddr; |
| 83 | |
| 84 | // Reading |
| 85 | - (void) doBytesAvailable; |
| 86 | - (void) completeCurrentRead; |
| 87 | - (void) endCurrentRead; |
| 88 | - (void) scheduleDequeueRead; |
| 89 | - (void) maybeDequeueRead; |
| 90 | - (void) doReadTimeout:(NSTimer *)timer; |
| 91 | |
| 92 | // Writing |
| 93 | - (void) doSendBytes; |
| 94 | - (void) completeCurrentWrite; |
| 95 | - (void) endCurrentWrite; |
| 96 | - (void) scheduleDequeueWrite; |
| 97 | - (void) maybeDequeueWrite; |
| 98 | - (void) maybeScheduleDisconnect; |
| 99 | - (void) doWriteTimeout:(NSTimer *)timer; |
| 100 | |
| 101 | // Callbacks |
| 102 | - (void) doCFCallback:(CFSocketCallBackType)type forSocket:(CFSocketRef)sock withAddress:(NSData *)address withData:(const void *)pData; |
| 103 | - (void) doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream; |
| 104 | - (void) doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream; |
| 105 | |
| 106 | @end |
| 107 | |
| 108 | static void MyCFSocketCallback (CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *); |
| 109 | static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo); |
| 110 | static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo); |
| 111 | |
| 112 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 113 | #pragma mark - |
| 114 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 115 | |
| 116 | /** |
| 117 | * The AsyncReadPacket encompasses the instructions for a current read. |
| 118 | * The content of a read packet allows the code to determine if we're: |
| 119 | * reading to a certain length, reading to a certain separator, or simply reading the first chunk of data. |
| 120 | **/ |
| 121 | @interface AsyncReadPacket : NSObject |
| 122 | { |
| 123 | @public |
| 124 | NSMutableData *buffer; |
| 125 | CFIndex bytesDone; |
| 126 | NSTimeInterval timeout; |
| 127 | long tag; |
| 128 | NSData *term; |
| 129 | BOOL readAllAvailableData; |
| 130 | } |
| 131 | - (id)initWithData:(NSMutableData *)d |
| 132 | timeout:(NSTimeInterval)t |
| 133 | tag:(long)i |
| 134 | readAllAvailable:(BOOL)a |
| 135 | terminator:(NSData *)e |
| 136 | bufferOffset:(CFIndex)b; |
| 137 | |
| 138 | - (void)dealloc; |
| 139 | @end |
| 140 | |
| 141 | @implementation AsyncReadPacket |
| 142 | |
| 143 | - (id)initWithData:(NSMutableData *)d |
| 144 | timeout:(NSTimeInterval)t |
| 145 | tag:(long)i |
| 146 | readAllAvailable:(BOOL)a |
| 147 | terminator:(NSData *)e |
| 148 | bufferOffset:(CFIndex)b |
| 149 | { |
| 150 | if(self = [super init]) |
| 151 | { |
| 152 | buffer = [d retain]; |
| 153 | timeout = t; |
| 154 | tag = i; |
| 155 | readAllAvailableData = a; |
| 156 | term = [e copy]; |
| 157 | bytesDone = b; |
| 158 | } |
| 159 | return self; |
| 160 | } |
| 161 | |
| 162 | - (void)dealloc |
| 163 | { |
| 164 | [buffer release]; |
| 165 | [term release]; |
| 166 | [super dealloc]; |
| 167 | } |
| 168 | |
| 169 | @end |
| 170 | |
| 171 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 172 | #pragma mark - |
| 173 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 174 | |
| 175 | @interface AsyncWritePacket : NSObject |
| 176 | { |
| 177 | @public |
| 178 | NSData *buffer; |
| 179 | CFIndex bytesDone; |
| 180 | long tag; |
| 181 | NSTimeInterval timeout; |
| 182 | } |
| 183 | - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i; |
| 184 | - (void)dealloc; |
| 185 | @end |
| 186 | |
| 187 | @implementation AsyncWritePacket |
| 188 | |
| 189 | - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i; |
| 190 | { |
| 191 | if(self = [super init]) |
| 192 | { |
| 193 | buffer = [d retain]; |
| 194 | timeout = t; |
| 195 | tag = i; |
| 196 | bytesDone = 0; |
| 197 | } |
| 198 | return self; |
| 199 | } |
| 200 | |
| 201 | - (void)dealloc |
| 202 | { |
| 203 | [buffer release]; |
| 204 | [super dealloc]; |
| 205 | } |
| 206 | |
| 207 | @end |
| 208 | |
| 209 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 210 | #pragma mark - |
| 211 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 212 | |
| 213 | @implementation AsyncSocket |
| 214 | |
| 215 | - (id) init |
| 216 | { |
| 217 | return [self initWithDelegate:nil0 userData:0]; |
| 218 | } |
| 219 | |
| 220 | - (id) initWithDelegate:(id)delegate |
| 221 | { |
| 222 | return [self initWithDelegate:delegate userData:0]; |
| 223 | } |
| 224 | |
| 225 | // Designated initializer. |
| 226 | - (id) initWithDelegate:(id)delegate userData:(long)userData |
| 227 | { |
| 228 | self = [super init]; |
| 229 | |
| 230 | theFlags = 0x00; |
| 231 | theDelegate = delegate; |
| 232 | theUserData = userData; |
| 233 | |
| 234 | theSocket = NULL( ( void * ) 0 ); |
| 235 | theSource = NULL( ( void * ) 0 ); |
| 236 | theSocket6 = NULL( ( void * ) 0 ); |
| 237 | theSource6 = NULL( ( void * ) 0 ); |
| 238 | theRunLoop = NULL( ( void * ) 0 ); |
| 239 | theReadStream = NULL( ( void * ) 0 ); |
| 240 | theWriteStream = NULL( ( void * ) 0 ); |
| 241 | |
| 242 | theReadQueue = [[NSMutableArray alloc] initWithCapacity:READQUEUE_CAPACITY5]; |
| 243 | theCurrentRead = nil0; |
| 244 | theReadTimer = nil0; |
| 245 | |
| 246 | partialReadBuffer = nil0; |
| 247 | |
| 248 | theWriteQueue = [[NSMutableArray alloc] initWithCapacity:WRITEQUEUE_CAPACITY5]; |
| 249 | theCurrentWrite = nil0; |
| 250 | theWriteTimer = nil0; |
| 251 | |
| 252 | // Socket context |
| 253 | NSAssertdo { if ( ! ( ( sizeof ( CFSocketContext ) == sizeof ( CFStreamClientContext ) ) ) ) { [ [ NSAssertionHandler currentHandler ] handleFailureInMethod : _cmd object : self file : [ NSString stringWithCString : "/Users/ryan/Projects/Adium/Plugins/Bonjour/libezv/Simple HTTP Server/AsyncSocket.m" ] lineNumber : 253 description : ( ( @ "CFSocketContext and CFStreamClientContext aren't the same size anymore. Contact the developer." ) ) , ( 0 ) , ( 0 ) , ( 0 ) , ( 0 ) , ( 0 ) ] ; } } while ( 0 ) (sizeof(CFSocketContext) == sizeof(CFStreamClientContext), @"CFSocketContext and CFStreamClientContext aren't the same size anymore. Contact the developer."); |
| 254 | theContext.version = 0; |
| 255 | theContext.info = self; |
| 256 | theContext.retain = nil0; |
| 257 | theContext.release = nil0; |
| 258 | theContext.copyDescription = nil0; |
| 259 | |
| 260 | return self; |
| 261 | } |
| 262 | |
| 263 | // The socket may been initialized in a connected state and auto-released, so this should close it down cleanly. |
| 264 | - (void) dealloc |
| 265 | { |
| 266 | [self close]; |
| 267 | [theReadQueue release]; |
| 268 | [theWriteQueue release]; |
| 269 | [NSObject cancelPreviousPerformRequestsWithTarget:theDelegate selector:@selector(onSocketDidDisconnect:) object:self]; |
| 270 | [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 271 | [super dealloc]; |
| 272 | } |
| 273 | |
| 274 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 275 | #pragma mark Accessors |
| 276 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 277 | |
| 278 | - (long) userData |
| 279 | { |
| 280 | return theUserData; |
| 281 | } |
| 282 | |
| 283 | - (void) setUserData:(long)userData |
| 284 | { |
| 285 | theUserData = userData; |
| 286 | } |
| 287 | |
| 288 | - (id) delegate |
| 289 | { |
| 290 | return theDelegate; |
| 291 | } |
| 292 | |
| 293 | - (void) setDelegate:(id)delegate |
| 294 | { |
| 295 | theDelegate = delegate; |
| 296 | } |
| 297 | |
| 298 | - (BOOL) canSafelySetDelegate |
| 299 | { |
| 300 | return ([theReadQueue count] == 0 && [theWriteQueue count] == 0 && theCurrentRead == nil0 && theCurrentWrite == nil0); |
| 301 | } |
| 302 | |
| 303 | - (CFSocketRef) getCFSocket |
| 304 | { |
| 305 | if(theSocket) |
| 306 | return theSocket; |
| 307 | else |
| 308 | return theSocket6; |
| 309 | } |
| 310 | |
| 311 | - (CFReadStreamRef) getCFReadStream |
| 312 | { |
| 313 | return theReadStream; |
| 314 | } |
| 315 | |
| 316 | - (CFWriteStreamRef) getCFWriteStream |
| 317 | { |
| 318 | return theWriteStream; |
| 319 | } |
| 320 | |
| 321 | - (float) progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total |
| 322 | { |
| 323 | // Check to make sure we're actually reading something right now |
| 324 | if (!theCurrentRead) return NAN__builtin_nanf ( "0x7fc00000" ); |
| 325 | |
| 326 | // It's only possible to know the progress of our read if we're reading to a certain length |
| 327 | // If we're reading to data, we of course have no idea when the data will arrive |
| 328 | // If we're reading to timeout, then we have no idea when the next chunk of data will arrive. |
| 329 | BOOL hasTotal = (theCurrentRead->readAllAvailableData == NO( BOOL ) 0 && theCurrentRead->term == nil0); |
| 330 | |
| 331 | CFIndex d = theCurrentRead->bytesDone; |
| 332 | CFIndex t = hasTotal ? [theCurrentRead->buffer length] : 0; |
| 333 | if (tag != NULL( ( void * ) 0 )) *tag = theCurrentRead->tag; |
| 334 | if (done != NULL( ( void * ) 0 )) *done = d; |
| 335 | if (total != NULL( ( void * ) 0 )) *total = t; |
| 336 | float ratio = (float)d/(float)t; |
| 337 | return isnan( sizeof ( ratio ) == sizeof ( float ) ? __inline_isnanf ( ( float ) ( ratio ) ) : sizeof ( ratio ) == sizeof ( double ) ? __inline_isnand ( ( double ) ( ratio ) ) : __inline_isnan ( ( long double ) ( ratio ) ) )(ratio) ? 1.0 : ratio; // 0 of 0 bytes is 100% done. |
| 338 | } |
| 339 | |
| 340 | - (float) progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total |
| 341 | { |
| 342 | if (!theCurrentWrite) return NAN__builtin_nanf ( "0x7fc00000" ); |
| 343 | CFIndex d = theCurrentWrite->bytesDone; |
| 344 | CFIndex t = [theCurrentWrite->buffer length]; |
| 345 | if (tag != NULL( ( void * ) 0 )) *tag = theCurrentWrite->tag; |
| 346 | if (done != NULL( ( void * ) 0 )) *done = d; |
| 347 | if (total != NULL( ( void * ) 0 )) *total = t; |
| 348 | return (float)d/(float)t; |
| 349 | } |
| 350 | |
| 351 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 352 | #pragma mark Class Methods |
| 353 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 354 | |
| 355 | // Return line separators. |
| 356 | + (NSData *) CRLFData |
| 357 | { return [NSData dataWithBytes:"\x0D\x0A" length:2]; } |
| 358 | |
| 359 | + (NSData *) CRData |
| 360 | { return [NSData dataWithBytes:"\x0D" length:1]; } |
| 361 | |
| 362 | + (NSData *) LFData |
| 363 | { return [NSData dataWithBytes:"\x0A" length:1]; } |
| 364 | |
| 365 | + (NSData *) ZeroData |
| 366 | { return [NSData dataWithBytes:"" length:1]; } |
| 367 | |
| 368 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 369 | #pragma mark Connection |
| 370 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 371 | |
| 372 | - (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr |
| 373 | { |
| 374 | return [self acceptOnAddress:nil0 port:port error:errPtr]; |
| 375 | } |
| 376 | |
| 377 | /** |
| 378 | * To accept on a certain address, pass the address to accept on. |
| 379 | * To accept on any address, pass nil or an empty string. |
| 380 | * To accept only connections from localhost pass "localhost" or "loopback". |
| 381 | **/ |
| 382 | - (BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr |
| 383 | { |
| 384 | if (theDelegate == NULL( ( void * ) 0 )) |
| 385 | [NSException raise:AsyncSocketException format:@"Attempting to accept without a delegate. Set a delegate first."]; |
| 386 | |
| 387 | if (theSocket != NULL( ( void * ) 0 ) || theSocket6 != NULL( ( void * ) 0 )) |
| 388 | [NSException raise:AsyncSocketException format:@"Attempting to accept while connected or accepting connections. Disconnect first."]; |
| 389 | |
| 390 | // Set up the listen sockaddr structs if needed. |
| 391 | |
| 392 | NSData *address = nil0, *address6 = nil0; |
| 393 | if(hostaddr == nil0 || ([hostaddr length] == 0)) |
| 394 | { |
| 395 | // Accept on ANY address |
| 396 | struct sockaddr_in nativeAddr; |
| 397 | nativeAddr.sin_len = sizeof(struct sockaddr_in); |
| 398 | nativeAddr.sin_family = AF_INET2; |
| 399 | nativeAddr.sin_port = htons( __builtin_constant_p ( port ) ? ( ( uint16_t ) ( ( ( ( uint16_t ) ( port ) & 0xff00 ) >> 8 ) | ( ( ( uint16_t ) ( port ) & 0x00ff ) << 8 ) ) ) : _OSSwapInt16 ( port ) )(port); |
| 400 | nativeAddr.sin_addr.s_addr = htonl( __builtin_constant_p ( ( u_int32_t ) 0x00000000 ) ? ( ( uint32_t ) ( ( ( ( uint32_t ) ( ( u_int32_t ) 0x00000000 ) & 0xff000000 ) >> 24 ) | ( ( ( uint32_t ) ( ( u_int32_t ) 0x00000000 ) & 0x00ff0000 ) >> 8 ) | ( ( ( uint32_t ) ( ( u_int32_t ) 0x00000000 ) & 0x0000ff00 ) << 8 ) | ( ( ( uint32_t ) ( ( u_int32_t ) 0x00000000 ) & 0x000000ff ) << 24 ) ) ) : _OSSwapInt32 ( ( u_int32_t ) 0x00000000 ) )(INADDR_ANY); |
| 401 | memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero)); |
| 402 | |
| 403 | struct sockaddr_in6 nativeAddr6; |
| 404 | nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); |
| 405 | nativeAddr6.sin6_family = AF_INET630; |
| 406 | nativeAddr6.sin6_port = htons( __builtin_constant_p ( port ) ? ( ( uint16_t ) ( ( ( ( uint16_t ) ( port ) & 0xff00 ) >> 8 ) | ( ( ( uint16_t ) ( port ) & 0x00ff ) << 8 ) ) ) : _OSSwapInt16 ( port ) )(port); |
| 407 | nativeAddr6.sin6_flowinfo = 0; |
| 408 | nativeAddr6.sin6_addr = in6addr_any; |
| 409 | nativeAddr6.sin6_scope_id = 0; |
| 410 | |
| 411 | // Wrap the native address structures for CFSocketSetAddress. |
| 412 | address = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)]; |
| 413 | address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; |
| 414 | } |
| 415 | else if([hostaddr isEqualToString:@"localhost"] || [hostaddr isEqualToString:@"loopback"]) |
| 416 | { |
| 417 | // Accept only on LOOPBACK address |
| 418 | struct sockaddr_in nativeAddr; |
| 419 | nativeAddr.sin_len = sizeof(struct sockaddr_in); |
| 420 | nativeAddr.sin_family = AF_INET2; |
| 421 | nativeAddr.sin_port = htons( __builtin_constant_p ( port ) ? ( ( uint16_t ) ( ( ( ( uint16_t ) ( port ) & 0xff00 ) >> 8 ) | ( ( ( uint16_t ) ( port ) & 0x00ff ) << 8 ) ) ) : _OSSwapInt16 ( port ) )(port); |
| 422 | nativeAddr.sin_addr.s_addr = htonl( __builtin_constant_p ( ( u_int32_t ) 0x7f000001 ) ? ( ( uint32_t ) ( ( ( ( uint32_t ) ( ( u_int32_t ) 0x7f000001 ) & 0xff000000 ) >> 24 ) | ( ( ( uint32_t ) ( ( u_int32_t ) 0x7f000001 ) & 0x00ff0000 ) >> 8 ) | ( ( ( uint32_t ) ( ( u_int32_t ) 0x7f000001 ) & 0x0000ff00 ) << 8 ) | ( ( ( uint32_t ) ( ( u_int32_t ) 0x7f000001 ) & 0x000000ff ) << 24 ) ) ) : _OSSwapInt32 ( ( u_int32_t ) 0x7f000001 ) )(INADDR_LOOPBACK); |
| 423 | memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero)); |
| 424 | |
| 425 | struct sockaddr_in6 nativeAddr6; |
| 426 | nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); |
| 427 | nativeAddr6.sin6_family = AF_INET630; |
| 428 | nativeAddr6.sin6_port = htons( __builtin_constant_p ( port ) ? ( ( uint16_t ) ( ( ( ( uint16_t ) ( port ) & 0xff00 ) >> 8 ) | ( ( ( uint16_t ) ( port ) & 0x00ff ) << 8 ) ) ) : _OSSwapInt16 ( port ) )(port); |
| 429 | nativeAddr6.sin6_flowinfo = 0; |
| 430 | nativeAddr6.sin6_addr = in6addr_loopback; |
| 431 | nativeAddr6.sin6_scope_id = 0; |
| 432 | |
| 433 | // Wrap the native address structures for CFSocketSetAddress. |
| 434 | address = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)]; |
| 435 | address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; |
| 436 | } |
| 437 | else |
| 438 | { |
| 439 | NSString *portStr = [NSString stringWithFormat:@"%hu", port]; |
| 440 | |
| 441 | @synchronized (getaddrinfoLock) |
| 442 | { |
| 443 | struct addrinfo hints, *res, *res0; |
| 444 | |
| 445 | memset(&hints, 0, sizeof(hints)); |
| 446 | hints.ai_family = PF_UNSPEC0; |
| 447 | hints.ai_socktype = SOCK_STREAM1; |
| 448 | hints.ai_protocol = IPPROTO_TCP6; |
| 449 | hints.ai_flags = AI_PASSIVE0x00000001; |
| 450 | |
| 451 | int error = getaddrinfo([hostaddr UTF8String], [portStr UTF8String], &hints, &res0); |
| 452 | |
| 453 | if(error) |
| 454 | { |
| 455 | NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding]; |
| 456 | NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; |
| 457 | |
| 458 | if(errPtr) *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info]; |
| 459 | } |
| 460 | |
| 461 | for(res = res0; res; res = res->ai_next) |
| 462 | { |
| 463 | if(!address && (res->ai_family == AF_INET2)) |
| 464 | { |
| 465 | // Found IPv4 address |
| 466 | // Wrap the native address structures for CFSocketSetAddress. |
| 467 | address = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; |
| 468 | } |
| 469 | else if(!address6 && (res->ai_family == AF_INET630)) |
| 470 | { |
| 471 | // Found IPv6 address |
| 472 | // Wrap the native address structures for CFSocketSetAddress. |
| 473 | address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; |
| 474 | } |
| 475 | } |
| 476 | freeaddrinfo(res0); |
| 477 | } |
| 478 | |
| 479 | if(!address && !address6) return NO( BOOL ) 0; |
| 480 | } |
| 481 | |
| 482 | // Create the sockets. |
| 483 | |
| 484 | if (address) |
| 485 | { |
| 486 | theSocket = [self createAcceptSocketForAddress:address error:errPtr]; |
| 487 | if (theSocket == NULL( ( void * ) 0 )) goto Failed; |
| 488 | } |
| 489 | |
| 490 | if (address6) |
| 491 | { |
| 492 | theSocket6 = [self createAcceptSocketForAddress:address6 error:errPtr]; |
| 493 | if (theSocket6 == NULL( ( void * ) 0 )) goto Failed; |
| 494 | } |
| 495 | |
| 496 | // Attach the sockets to the run loop so that callback methods work |
| 497 | |
| 498 | [self attachSocketsToRunLoop:nil0 error:nil0]; |
| 499 | |
| 500 | // Set the SO_REUSEADDR flags. |
| 501 | |
| 502 | int reuseOn = 1; |
| 503 | if (theSocket) setsockopt(CFSocketGetNative(theSocket), SOL_SOCKET0xffff, SO_REUSEADDR0x0004, &reuseOn, sizeof(reuseOn)); |
| 504 | if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET0xffff, SO_REUSEADDR0x0004, &reuseOn, sizeof(reuseOn)); |
| 505 | |
| 506 | // Set the local bindings which causes the sockets to start listening. |
| 507 | |
| 508 | CFSocketError err; |
| 509 | if (theSocket) |
| 510 | { |
| 511 | err = CFSocketSetAddress (theSocket, (CFDataRef)address); |
| 512 | if (err != kCFSocketSuccess) goto Failed; |
| 513 | |
| 514 | //NSLog(@"theSocket4: %hu", [self localPort:theSocket]); |
| 515 | } |
| 516 | |
| 517 | if(port == 0 && theSocket && theSocket6) |
| 518 | { |
| 519 | // The user has passed in port 0, which means he wants to allow the kernel to choose the port for them |
| 520 | // However, the kernel will choose a different port for both theSocket and theSocket6 |
| 521 | // So we grab the port the kernel choose for theSocket, and set it as the port for theSocket6 |
| 522 | UInt16 chosenPort = [self localPort:theSocket]; |
| 523 | |
| 524 | struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes]; |
| 525 | pSockAddr6->sin6_port = htons( __builtin_constant_p ( chosenPort ) ? ( ( uint16_t ) ( ( ( ( uint16_t ) ( chosenPort ) & 0xff00 ) >> 8 ) | ( ( ( uint16_t ) ( chosenPort ) & 0x00ff ) << 8 ) ) ) : _OSSwapInt16 ( chosenPort ) )(chosenPort); |
| 526 | } |
| 527 | |
| 528 | if (theSocket6) |
| 529 | { |
| 530 | err = CFSocketSetAddress (theSocket6, (CFDataRef)address6); |
| 531 | if (err != kCFSocketSuccess) goto Failed; |
| 532 | |
| 533 | //NSLog(@"theSocket6: %hu", [self localPort:theSocket6]); |
| 534 | } |
| 535 | |
| 536 | theFlags |= kDidPassConnectMethod; |
| 537 | return YES( BOOL ) 1; |
| 538 | |
| 539 | Failed:; |
| 540 | if (errPtr) *errPtr = [self getSocketError]; |
| 541 | return NO( BOOL ) 0; |
| 542 | } |
| 543 | |
| 544 | /** |
| 545 | * This method creates an initial CFReadStream and CFWriteStream to the given host on the given port. |
| 546 | * The connection is then opened, and the corresponding CFSocket will be extracted after the connection succeeds. |
| 547 | * |
| 548 | * Thus the delegate will have access to the CFReadStream and CFWriteStream prior to connection, |
| 549 | * specifically in the onSocketWillConnect: method. |
| 550 | **/ |
| 551 | - (BOOL)connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr |
| 552 | { |
| 553 | if(theDelegate == NULL( ( void * ) 0 )) |
| 554 | { |
| 555 | NSString *message = @"Attempting to connect without a delegate. Set a delegate first."; |
| 556 | [NSException raise:AsyncSocketException format:message]; |
| 557 | } |
| 558 | |
| 559 | if(theSocket != NULL( ( void * ) 0 ) || theSocket6 != NULL( ( void * ) 0 )) |
| 560 | { |
| 561 | NSString *message = @"Attempting to connect while connected or accepting connections. Disconnect first."; |
| 562 | [NSException raise:AsyncSocketException format:message]; |
| 563 | } |
| 564 | |
| 565 | BOOL pass = YES( BOOL ) 1; |
| 566 | |
| 567 | if(pass && ![self createStreamsToHost:hostname onPort:port error:errPtr]) pass = NO( BOOL ) 0; |
| 568 | if(pass && ![self attachStreamsToRunLoop:nil0 error:errPtr]) pass = NO( BOOL ) 0; |
| 569 | if(pass && ![self configureStreamsAndReturnError:errPtr]) pass = NO( BOOL ) 0; |
| 570 | if(pass && ![self openStreamsAndReturnError:errPtr]) pass = NO( BOOL ) 0; |
| 571 | |
| 572 | if(pass) |
| 573 | theFlags |= kDidPassConnectMethod; |
| 574 | else |
| 575 | [self close]; |
| 576 | |
| 577 | return pass; |
| 578 | } |
| 579 | |
| 580 | /** |
| 581 | * This method creates an initial CFSocket to the given address. |
| 582 | * The connection is then opened, and the corresponding CFReadStream and CFWriteStream will be |
| 583 | * created from the low-level sockets after the connection succeeds. |
| 584 | * |
| 585 | * Thus the delegate will have access to the CFSocket and CFSocketNativeHandle (BSD socket) prior to connection, |
| 586 | * specifically in the onSocketWillConnect: method. |
| 587 | * |
| 588 | * Note: The NSData parameter is expected to be a sockaddr structure. For example, an NSData object returned from |
| 589 | * NSNetservice addresses method. |
| 590 | * If you have an existing struct sockaddr you can convert it to an NSData object like so: |
| 591 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; |
| 592 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; |
| 593 | **/ |
| 594 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr |
| 595 | { |
| 596 | if (theDelegate == NULL( ( void * ) 0 )) |
| 597 | { |
| 598 | NSString *message = @"Attempting to connect without a delegate. Set a delegate first."; |
| 599 | [NSException raise:AsyncSocketException format:message]; |
| 600 | } |
| 601 | |
| 602 | if (theSocket != NULL( ( void * ) 0 ) || theSocket6 != NULL( ( void * ) 0 )) |
| 603 | { |
| 604 | NSString *message = @"Attempting to connect while connected or accepting connections. Disconnect first."; |
| 605 | [NSException raise:AsyncSocketException format:message]; |
| 606 | } |
| 607 | |
| 608 | BOOL pass = YES( BOOL ) 1; |
| 609 | |
| 610 | if(pass && ![self createSocketForAddress:remoteAddr error:errPtr]) pass = NO( BOOL ) 0; |
| 611 | if(pass && ![self attachSocketsToRunLoop:nil0 error:errPtr]) pass = NO( BOOL ) 0; |
| 612 | if(pass && ![self configureSocketAndReturnError:errPtr]) pass = NO( BOOL ) 0; |
| 613 | if(pass && ![self connectSocketToAddress:remoteAddr error:errPtr]) pass = NO( BOOL ) 0; |
| 614 | |
| 615 | if(pass) |
| 616 | theFlags |= kDidPassConnectMethod; |
| 617 | else |
| 618 | [self close]; |
| 619 | |
| 620 | return pass; |
| 621 | } |
| 622 | |
| 623 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 624 | #pragma mark Socket Implementation: |
| 625 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 626 | |
| 627 | /** |
| 628 | * Creates the accept sockets. |
| 629 | * Returns true if either IPv4 or IPv6 is created. |
| 630 | * If either is missing, an error is returned (even though the method may return true). |
| 631 | **/ |
| 632 | - (CFSocketRef)createAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr |
| 633 | { |
| 634 | struct sockaddr *pSockAddr = (struct sockaddr *)[addr bytes]; |
| 635 | int addressFamily = pSockAddr->sa_family; |
| 636 | |
| 637 | CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, |
| 638 | addressFamily, |
| 639 | SOCK_STREAM1, |
| 640 | 0, |
| 641 | kCFSocketAcceptCallBack, // Callback flags |
| 642 | (CFSocketCallBack)&MyCFSocketCallback, // Callback method |
| 643 | &theContext); |
| 644 | |
| 645 | if(socket == NULL( ( void * ) 0 )) |
| 646 | { |
| 647 | if(errPtr) *errPtr = [self getSocketError]; |
| 648 | } |
| 649 | |
| 650 | return socket; |
| 651 | } |
| 652 | |
| 653 | - (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr |
| 654 | { |
| 655 | struct sockaddr *pSockAddr = (struct sockaddr *)[remoteAddr bytes]; |
| 656 | |
| 657 | if(pSockAddr->sa_family == AF_INET2) |
| 658 | { |
| 659 | theSocket = CFSocketCreate(NULL( ( void * ) 0 ), // Default allocator |
| 660 | PF_INET2, // Protocol Family |
| 661 | SOCK_STREAM1, // Socket Type |
| 662 | IPPROTO_TCP6, // Protocol |
| 663 | kCFSocketConnectCallBack, // Callback flags |
| 664 | (CFSocketCallBack)&MyCFSocketCallback, // Callback method |
| 665 | &theContext); // Socket Context |
| 666 | |
| 667 | if(theSocket == NULL( ( void * ) 0 )) |
| 668 | { |
| 669 | if (errPtr) *errPtr = [self getSocketError]; |
| 670 | return NO( BOOL ) 0; |
| 671 | } |
| 672 | } |
| 673 | else if(pSockAddr->sa_family == AF_INET630) |
| 674 | { |
| 675 | theSocket6 = CFSocketCreate(NULL( ( void * ) 0 ), // Default allocator |
| 676 | PF_INET630, // Protocol Family |
| 677 | SOCK_STREAM1, // Socket Type |
| 678 | IPPROTO_TCP6, // Protocol |
| 679 | kCFSocketConnectCallBack, // Callback flags |
| 680 | (CFSocketCallBack)&MyCFSocketCallback, // Callback method |
| 681 | &theContext); // Socket Context |
| 682 | |
| 683 | if(theSocket6 == NULL( ( void * ) 0 )) |
| 684 | { |
| 685 | if (errPtr) *errPtr = [self getSocketError]; |
| 686 | return NO( BOOL ) 0; |
| 687 | } |
| 688 | } |
| 689 | else |
| 690 | { |
| 691 | if (errPtr) *errPtr = [self getSocketError]; |
| 692 | return NO( BOOL ) 0; |
| 693 | } |
| 694 | |
| 695 | return YES( BOOL ) 1; |
| 696 | } |
| 697 | |
| 698 | /** |
| 699 | * Adds the CFSocket's to the run-loop so that callbacks will work properly. |
| 700 | **/ |
| 701 | - (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr |
| 702 | { |
| 703 | // Get the CFRunLoop to which the socket should be attached. |
| 704 | theRunLoop = (runLoop == nil0) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop]; |
| 705 | |
| 706 | if(theSocket) |
| 707 | { |
| 708 | theSource = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket, 0); |
| 709 | CFRunLoopAddSource (theRunLoop, theSource, kCFRunLoopDefaultMode); |
| 710 | } |
| 711 | |
| 712 | if(theSocket6) |
| 713 | { |
| 714 | theSource6 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket6, 0); |
| 715 | CFRunLoopAddSource (theRunLoop, theSource6, kCFRunLoopDefaultMode); |
| 716 | } |
| 717 | |
| 718 | return YES( BOOL ) 1; |
| 719 | } |
| 720 | |
| 721 | /** |
| 722 | * Allows the delegate method to configure the CFSocket or CFNativeSocket as desired before we connect. |
| 723 | * Note that the CFReadStream and CFWriteStream will not be available until after the connection is opened. |
| 724 | **/ |
| 725 | - (BOOL)configureSocketAndReturnError:(NSError **)errPtr |
| 726 | { |
| 727 | // Call the delegate method for further configuration. |
| 728 | if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)]) |
| 729 | { |
| 730 | if([theDelegate onSocketWillConnect:self] == NO( BOOL ) 0) |
| 731 | { |
| 732 | if (errPtr) *errPtr = [self getAbortError]; |
| 733 | return NO( BOOL ) 0; |
| 734 | } |
| 735 | } |
| 736 | return YES( BOOL ) 1; |
| 737 | } |
| 738 | |
| 739 | - (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr |
| 740 | { |
| 741 | // Start connecting to the given address in the background |
| 742 | // The MyCFSocketCallback method will be called when the connection succeeds or fails |
| 743 | if(theSocket) |
| 744 | { |
| 745 | CFSocketError err = CFSocketConnectToAddress(theSocket, (CFDataRef)remoteAddr, -1); |
| 746 | if(err != kCFSocketSuccess) |
| 747 | { |
| 748 | if (errPtr) *errPtr = [self getSocketError]; |
| 749 | return NO( BOOL ) 0; |
| 750 | } |
| 751 | } |
| 752 | else if(theSocket6) |
| 753 | { |
| 754 | CFSocketError err = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, -1); |
| 755 | if(err != kCFSocketSuccess) |
| 756 | { |
| 757 | if (errPtr) *errPtr = [self getSocketError]; |
| 758 | return NO( BOOL ) 0; |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | return YES( BOOL ) 1; |
| 763 | } |
| 764 | |
| 765 | /** |
| 766 | * Attempt to make the new socket. |
| 767 | * If an error occurs, ignore this event. |
| 768 | **/ |
| 769 | - (void)doAcceptWithSocket:(CFSocketNativeHandle)newNative |
| 770 | { |
| 771 | AsyncSocket *newSocket = [[[AsyncSocket alloc] initWithDelegate:theDelegate] autorelease]; |
| 772 | if(newSocket) |
| 773 | { |
| 774 | NSRunLoop *runLoop = nil0; |
| 775 | if ([theDelegate respondsToSelector:@selector(onSocket:didAcceptNewSocket:)]) |
| 776 | [theDelegate onSocket:self didAcceptNewSocket:newSocket]; |
| 777 | |
| 778 | if ([theDelegate respondsToSelector:@selector(onSocket:wantsRunLoopForNewSocket:)]) |
| 779 | runLoop = [theDelegate onSocket:self wantsRunLoopForNewSocket:newSocket]; |
| 780 | |
| 781 | BOOL pass = YES( BOOL ) 1; |
| 782 | |
| 783 | if(pass && ![newSocket createStreamsFromNative:newNative error:nil0]) pass = NO( BOOL ) 0; |
| 784 | if(pass && ![newSocket attachStreamsToRunLoop:runLoop error:nil0]) pass = NO( BOOL ) 0; |
| 785 | if(pass && ![newSocket configureStreamsAndReturnError:nil0]) pass = NO( BOOL ) 0; |
| 786 | if(pass && ![newSocket openStreamsAndReturnError:nil0]) pass = NO( BOOL ) 0; |
| 787 | |
| 788 | if(pass) |
| 789 | newSocket->theFlags |= kDidPassConnectMethod; |
| 790 | else { |
| 791 | // No NSError, but errors will still get logged from the above functions. |
| 792 | [newSocket close]; |
| 793 | } |
| 794 | |
| 795 | } |
| 796 | } |
| 797 | |
| 798 | /** |
| 799 | * Description forthcoming... |
| 800 | **/ |
| 801 | - (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)socketError |
| 802 | { |
| 803 | NSParameterAssertdo { if ( ! ( ( ( sock == theSocket ) || ( sock == theSocket6 ) ) ) ) { [ [ NSAssertionHandler currentHandler ] handleFailureInMethod : _cmd object : self file : [ NSString stringWithCString : "/Users/ryan/Projects/Adium/Plugins/Bonjour/libezv/Simple HTTP Server/AsyncSocket.m" ] lineNumber : 803 description : ( @ "Invalid parameter not satisfying: %s" ) , ( "(sock == theSocket) || (sock == theSocket6)" ) , ( 0 ) , ( 0 ) , ( 0 ) , ( 0 ) ] ; } } while ( 0 ) ((sock == theSocket) || (sock == theSocket6)); |
| 804 | |
| 805 | if(socketError == kCFSocketTimeout || socketError == kCFSocketError) |
| 806 | { |
| 807 | [self closeWithError:[self getSocketError]]; |
| 808 | return; |
| 809 | } |
| 810 | |
| 811 | // Get the underlying native (BSD) socket |
| 812 | CFSocketNativeHandle nativeSocket = CFSocketGetNative(sock); |
| 813 | |
| 814 | // Setup the socket so that invalidating the socket will not close the native socket |
| 815 | CFSocketSetSocketFlags(sock, 0); |
| 816 | |
| 817 | // Invalidate and release the CFSocket - All we need from here on out is the nativeSocket |
| 818 | // Note: If we don't invalidate the socket (leaving the native socket open) |
| 819 | // then theReadStream and theWriteStream won't function properly. |
| 820 | // Specifically, their callbacks won't work, with the expection of kCFStreamEventOpenCompleted. |
| 821 | // I'm not entirely sure why this is, but I'm guessing that events on the socket fire to the CFSocket we created, |
| 822 | // as opposed to the CFReadStream/CFWriteStream. |
| 823 | |
| 824 | CFSocketInvalidate(sock); |
| 825 | CFRelease(sock); |
| 826 | theSocket = NULL( ( void * ) 0 ); |
| 827 | theSocket6 = NULL( ( void * ) 0 ); |
| 828 | |
| 829 | NSError *err; |
| 830 | BOOL pass = YES( BOOL ) 1; |
| 831 | |
| 832 | if(pass && ![self createStreamsFromNative:nativeSocket error:&err]) pass = NO( BOOL ) 0; |
| 833 | if(pass && ![self attachStreamsToRunLoop:nil0 error:&err]) pass = NO( BOOL ) 0; |
| 834 | if(pass && ![self openStreamsAndReturnError:&err]) pass = NO( BOOL ) 0; |
| 835 | |
| 836 | if(!pass) |
| 837 | { |
| 838 | [self closeWithError:err]; |
| 839 | } |
| 840 | } |
| 841 | |
| 842 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 843 | #pragma mark Stream Implementation: |
| 844 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 845 | |
| 846 | /** |
| 847 | * Creates the CFReadStream and CFWriteStream from the given native socket. |
| 848 | * The CFSocket may be extracted from either stream after the streams have been opened. |
| 849 | * |
| 850 | * Note: The given native socket must already be connected! |
| 851 | **/ |
| 852 | - (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr |
| 853 | { |
| 854 | // Create the socket & streams. |
| 855 | CFStreamCreatePairWithSocket(kCFAllocatorDefault, native, &theReadStream, &theWriteStream); |
| 856 | if (theReadStream == NULL( ( void * ) 0 ) || theWriteStream == NULL( ( void * ) 0 )) |
| 857 | { |
| 858 | NSError *err = [self getStreamError]; |
| 859 | NSLog (@"AsyncSocket %p couldn't create streams from accepted socket: %@", self, err); |
| 860 | if (errPtr) *errPtr = err; |
| 861 | return NO( BOOL ) 0; |
| 862 | } |
| 863 | |
| 864 | // Ensure the CF & BSD socket is closed when the streams are closed. |
| 865 | CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); |
| 866 | CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); |
| 867 | |
| 868 | return YES( BOOL ) 1; |
| 869 | } |
| 870 | |
| 871 | /** |
| 872 | * Creates the CFReadStream and CFWriteStream from the given hostname and port number. |
| 873 | * The CFSocket may be extracted from either stream after the streams have been opened. |
| 874 | **/ |
| 875 | - (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr |
| 876 | { |
| 877 | // Create the socket & streams. |
| 878 | CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)hostname, port, &theReadStream, &theWriteStream); |
| 879 | if (theReadStream == NULL( ( void * ) 0 ) || theWriteStream == NULL( ( void * ) 0 )) |
| 880 | { |
| 881 | if (errPtr) *errPtr = [self getStreamError]; |
| 882 | return NO( BOOL ) 0; |
| 883 | } |
| 884 | |
| 885 | // Ensure the CF & BSD socket is closed when the streams are closed. |
| 886 | CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); |
| 887 | CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue); |
| 888 | |
| 889 | return YES( BOOL ) 1; |
| 890 | } |
| 891 | |
| 892 | - (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr |
| 893 | { |
| 894 | // Get the CFRunLoop to which the socket should be attached. |
| 895 | theRunLoop = (runLoop == nil0) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop]; |
| 896 | |
| 897 | // Make read stream non-blocking. |
| 898 | if (!CFReadStreamSetClient (theReadStream, |
| 899 | kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered | kCFStreamEventOpenCompleted, |
| 900 | (CFReadStreamClientCallBack)&MyCFReadStreamCallback, |
| 901 | (CFStreamClientContext *)(&theContext) )) |
| 902 | { |
| 903 | NSError *err = [self getStreamError]; |
| 904 | |
| 905 | NSLog (@"AsyncSocket %p couldn't attach read stream to run-loop,", self); |
| 906 | NSLog (@"Error: %@", err); |
| 907 | |
| 908 | if (errPtr) *errPtr = err; |
| 909 | return NO( BOOL ) 0; |
| 910 | } |
| 911 | CFReadStreamScheduleWithRunLoop (theReadStream, theRunLoop, kCFRunLoopDefaultMode); |
| 912 | |
| 913 | // Make write stream non-blocking. |
| 914 | if (!CFWriteStreamSetClient (theWriteStream, |
| 915 | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered | kCFStreamEventOpenCompleted, |
| 916 | (CFWriteStreamClientCallBack)&MyCFWriteStreamCallback, |
| 917 | (CFStreamClientContext *)(&theContext) )) |
| 918 | { |
| 919 | NSError *err = [self getStreamError]; |
| 920 | |
| 921 | NSLog (@"AsyncSocket %p couldn't attach write stream to run-loop,", self); |
| 922 | NSLog (@"Error: %@", err); |
| 923 | |
| 924 | if (errPtr) *errPtr = err; |
| 925 | return NO( BOOL ) 0; |
| 926 | |
| 927 | } |
| 928 | CFWriteStreamScheduleWithRunLoop (theWriteStream, theRunLoop, kCFRunLoopDefaultMode); |
| 929 | |
| 930 | return YES( BOOL ) 1; |
| 931 | } |
| 932 | |
| 933 | /** |
| 934 | * Allows the delegate method to configure the CFReadStream and/or CFWriteStream as desired before we connect. |
| 935 | * Note that the CFSocket and CFNativeSocket will not be available until after the connection is opened. |
| 936 | **/ |
| 937 | - (BOOL)configureStreamsAndReturnError:(NSError **)errPtr |
| 938 | { |
| 939 | // Call the delegate method for further configuration. |
| 940 | if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)]) |
| 941 | { |
| 942 | if([theDelegate onSocketWillConnect:self] == NO( BOOL ) 0) |
| 943 | { |
| 944 | NSError *err = [self getAbortError]; |
| 945 | if (errPtr) *errPtr = err; |
| 946 | return NO( BOOL ) 0; |
| 947 | } |
| 948 | } |
| 949 | return YES( BOOL ) 1; |
| 950 | } |
| 951 | |
| 952 | - (BOOL)openStreamsAndReturnError:(NSError **)errPtr |
| 953 | { |
| 954 | BOOL pass = YES( BOOL ) 1; |
| 955 | |
| 956 | if(pass && !CFReadStreamOpen (theReadStream)) |
| 957 | { |
| 958 | NSLog (@"AsyncSocket %p couldn't open read stream,", self); |
| 959 | pass = NO( BOOL ) 0; |
| 960 | } |
| 961 | |
| 962 | if(pass && !CFWriteStreamOpen (theWriteStream)) |
| 963 | { |
| 964 | NSLog (@"AsyncSocket %p couldn't open write stream,", self); |
| 965 | pass = NO( BOOL ) 0; |
| 966 | } |
| 967 | |
| 968 | if(!pass) |
| 969 | { |
| 970 | NSError *err = [self getStreamError]; |