Bug Summary

File:Plugins/Bonjour/libezv/Simple HTTP Server/AsyncSocket.m
Location:line 1584, column 7
Description:Memory Leak
Code is compiled without garbage collection.

Annotated Source Code

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
24NSString *const AsyncSocketException = @"AsyncSocketException";
25NSString *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.
29NSString *getaddrinfoLock = @"lock";
30
31enum 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
108static void MyCFSocketCallback (CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
109static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo);
110static 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
539Failed:;
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];</