Subversion Repositories Projects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
805 - 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
#if TARGET_OS_IPHONE
18
// Note: You may need to add the CFNetwork Framework to your project
19
#import <CFNetwork/CFNetwork.h>
20
#endif
21
 
22
#pragma mark Declarations
23
 
24
#define DEFAULT_PREBUFFERING YES        // Whether pre-buffering is enabled by default
25
 
26
#define READQUEUE_CAPACITY      5           // Initial capacity
27
#define WRITEQUEUE_CAPACITY 5           // Initial capacity
28
#define READALL_CHUNKSIZE       256         // Incremental increase in buffer size
29
#define WRITE_CHUNKSIZE    (1024 * 4)   // Limit on size of each write pass
30
 
31
NSString *const AsyncSocketException = @"AsyncSocketException";
32
NSString *const AsyncSocketErrorDomain = @"AsyncSocketErrorDomain";
33
 
34
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
35
// Mutex lock used by all instances of AsyncSocket, to protect getaddrinfo.
36
// Prior to Mac OS X 10.5 this method was not thread-safe.
37
static NSString *getaddrinfoLock = @"lock";
38
#endif
39
 
40
enum AsyncSocketFlags
41
{
42
        kEnablePreBuffering      = 1 <<  0,  // If set, pre-buffering is enabled
43
        kDidPassConnectMethod    = 1 <<  1,  // If set, disconnection results in delegate call
44
        kDidCompleteOpenForRead  = 1 <<  2,  // If set, open callback has been called for read stream
45
        kDidCompleteOpenForWrite = 1 <<  3,  // If set, open callback has been called for write stream
46
        kStartingReadTLS         = 1 <<  4,  // If set, we're waiting for TLS negotiation to complete
47
        kStartingWriteTLS        = 1 <<  5,  // If set, we're waiting for TLS negotiation to complete
48
        kForbidReadsWrites       = 1 <<  6,  // If set, no new reads or writes are allowed
49
        kDisconnectAfterReads    = 1 <<  7,  // If set, disconnect after no more reads are queued
50
        kDisconnectAfterWrites   = 1 <<  8,  // If set, disconnect after no more writes are queued
51
        kClosingWithError        = 1 <<  9,  // If set, the socket is being closed due to an error
52
        kDequeueReadScheduled    = 1 << 10,  // If set, a maybeDequeueRead operation is already scheduled
53
        kDequeueWriteScheduled   = 1 << 11,  // If set, a maybeDequeueWrite operation is already scheduled
54
        kSocketCanAcceptBytes    = 1 << 12,  // If set, we know socket can accept bytes. If unset, it's unknown.
55
        kSocketHasBytesAvailable = 1 << 13,  // If set, we know socket has bytes available. If unset, it's unknown.
56
};
57
 
58
@interface AsyncSocket (Private)
59
 
60
// Connecting
61
- (void)startConnectTimeout:(NSTimeInterval)timeout;
62
- (void)endConnectTimeout;
63
 
64
// Socket Implementation
65
- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr;
66
- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
67
- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
68
- (BOOL)configureSocketAndReturnError:(NSError **)errPtr;
69
- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
70
- (void)doAcceptWithSocket:(CFSocketNativeHandle)newSocket;
71
- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)err;
72
 
73
// Stream Implementation
74
- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr;
75
- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
76
- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr;
77
- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr;
78
- (BOOL)openStreamsAndReturnError:(NSError **)errPtr;
79
- (void)doStreamOpen;
80
- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr;
81
 
82
// Disconnect Implementation
83
- (void)closeWithError:(NSError *)err;
84
- (void)recoverUnreadData;
85
- (void)emptyQueues;
86
- (void)close;
87
 
88
// Errors
89
- (NSError *)getErrnoError;
90
- (NSError *)getAbortError;
91
- (NSError *)getStreamError;
92
- (NSError *)getSocketError;
93
- (NSError *)getConnectTimeoutError;
94
- (NSError *)getReadMaxedOutError;
95
- (NSError *)getReadTimeoutError;
96
- (NSError *)getWriteTimeoutError;
97
- (NSError *)errorFromCFStreamError:(CFStreamError)err;
98
 
99
// Diagnostics
100
- (BOOL)isSocketConnected;
101
- (BOOL)areStreamsConnected;
102
- (NSString *)connectedHost:(CFSocketRef)socket;
103
- (UInt16)connectedPort:(CFSocketRef)socket;
104
- (NSString *)localHost:(CFSocketRef)socket;
105
- (UInt16)localPort:(CFSocketRef)socket;
106
- (NSString *)addressHost:(CFDataRef)cfaddr;
107
- (UInt16)addressPort:(CFDataRef)cfaddr;
108
 
109
// Reading
110
- (void)doBytesAvailable;
111
- (void)completeCurrentRead;
112
- (void)endCurrentRead;
113
- (void)scheduleDequeueRead;
114
- (void)maybeDequeueRead;
115
- (void)doReadTimeout:(NSTimer *)timer;
116
 
117
// Writing
118
- (void)doSendBytes;
119
- (void)completeCurrentWrite;
120
- (void)endCurrentWrite;
121
- (void)scheduleDequeueWrite;
122
- (void)maybeDequeueWrite;
123
- (void)maybeScheduleDisconnect;
124
- (void)doWriteTimeout:(NSTimer *)timer;
125
 
126
// Run Loop
127
- (void)runLoopAddSource:(CFRunLoopSourceRef)source;
128
- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source;
129
- (void)runLoopAddTimer:(NSTimer *)timer;
130
- (void)runLoopRemoveTimer:(NSTimer *)timer;
131
- (void)runLoopUnscheduleReadStream;
132
- (void)runLoopUnscheduleWriteStream;
133
 
134
// Security
135
- (void)maybeStartTLS;
136
- (void)onTLSHandshakeSuccessful;
137
 
138
// Callbacks
139
- (void)doCFCallback:(CFSocketCallBackType)type forSocket:(CFSocketRef)sock withAddress:(NSData *)address withData:(const void *)pData;
140
- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream;
141
- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream;
142
 
143
@end
144
 
145
static void MyCFSocketCallback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
146
static void MyCFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *pInfo);
147
static void MyCFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *pInfo);
148
 
149
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
150
#pragma mark -
151
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
152
 
153
/**
154
 * The AsyncReadPacket encompasses the instructions for any given read.
155
 * The content of a read packet allows the code to determine if we're:
156
 *  - reading to a certain length
157
 *  - reading to a certain separator
158
 *  - or simply reading the first chunk of available data
159
**/
160
@interface AsyncReadPacket : NSObject
161
{
162
  @public
163
        NSMutableData *buffer;
164
        CFIndex bytesDone;
165
        NSTimeInterval timeout;
166
        CFIndex maxLength;
167
        long tag;
168
        NSData *term;
169
        BOOL readAllAvailableData;
170
}
171
- (id)initWithData:(NSMutableData *)d
172
                   timeout:(NSTimeInterval)t
173
                           tag:(long)i
174
  readAllAvailable:(BOOL)a
175
                terminator:(NSData *)e
176
                 maxLength:(CFIndex)m;
177
 
178
- (unsigned)readLengthForTerm;
179
 
180
- (unsigned)prebufferReadLengthForTerm;
181
- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes;
182
@end
183
 
184
@implementation AsyncReadPacket
185
 
186
- (id)initWithData:(NSMutableData *)d
187
                   timeout:(NSTimeInterval)t
188
                           tag:(long)i
189
  readAllAvailable:(BOOL)a
190
                terminator:(NSData *)e
191
         maxLength:(CFIndex)m
192
{
193
        if((self = [super init]))
194
        {
195
                buffer = [d retain];
196
                timeout = t;
197
                tag = i;
198
                readAllAvailableData = a;
199
                term = [e copy];
200
                bytesDone = 0;
201
                maxLength = m;
202
        }
203
        return self;
204
}
205
 
206
/**
207
 * For read packets with a set terminator, returns the safe length of data that can be read
208
 * without going over a terminator, or the maxLength.
209
 *
210
 * It is assumed the terminator has not already been read.
211
**/
212
- (unsigned)readLengthForTerm
213
{
214
        NSAssert(term != nil, @"Searching for term in data when there is no term.");
215
 
216
        // What we're going to do is look for a partial sequence of the terminator at the end of the buffer.
217
        // If a partial sequence occurs, then we must assume the next bytes to arrive will be the rest of the term,
218
        // and we can only read that amount.
219
        // Otherwise, we're safe to read the entire length of the term.
220
 
221
        unsigned result = [term length];
222
 
223
        // Shortcut when term is a single byte
224
        if(result == 1) return result;
225
 
226
        // i = index within buffer at which to check data
227
        // j = length of term to check against
228
 
229
        // Note: Beware of implicit casting rules
230
        // This could give you -1: MAX(0, (0 - [term length] + 1));
231
 
232
        CFIndex i = MAX(0, (CFIndex)(bytesDone - [term length] + 1));
233
        CFIndex j = MIN([term length] - 1, bytesDone);
234
 
235
        while(i < bytesDone)
236
        {
237
                const void *subBuffer = [buffer bytes] + i;
238
 
239
                if(memcmp(subBuffer, [term bytes], j) == 0)
240
                {
241
                        result = [term length] - j;
242
                        break;
243
                }
244
 
245
                i++;
246
                j--;
247
        }
248
 
249
        if(maxLength > 0)
250
                return MIN(result, (maxLength - bytesDone));
251
        else
252
                return result;
253
}
254
 
255
/**
256
 * Assuming pre-buffering is enabled, returns the amount of data that can be read
257
 * without going over the maxLength.
258
**/
259
- (unsigned)prebufferReadLengthForTerm
260
{
261
        if(maxLength > 0)
262
                return MIN(READALL_CHUNKSIZE, (maxLength - bytesDone));
263
        else
264
                return READALL_CHUNKSIZE;
265
}
266
 
267
/**
268
 * For read packets with a set terminator, scans the packet buffer for the term.
269
 * It is assumed the terminator had not been fully read prior to the new bytes.
270
 *
271
 * If the term is found, the number of excess bytes after the term are returned.
272
 * If the term is not found, this method will return -1.
273
 *
274
 * Note: A return value of zero means the term was found at the very end.
275
**/
276
- (CFIndex)searchForTermAfterPreBuffering:(CFIndex)numBytes
277
{
278
        NSAssert(term != nil, @"Searching for term in data when there is no term.");
279
 
280
        // We try to start the search such that the first new byte read matches up with the last byte of the term.
281
        // We continue searching forward after this until the term no longer fits into the buffer.
282
 
283
        // Note: Beware of implicit casting rules
284
        // This could give you -1: MAX(0, 1 - 1 - [term length] + 1);
285
 
286
        CFIndex i = MAX(0, (CFIndex)(bytesDone - numBytes - [term length] + 1));
287
 
288
        while(i + [term length] <= bytesDone)
289
        {
290
                const void *subBuffer = [buffer bytes] + i;
291
 
292
                if(memcmp(subBuffer, [term bytes], [term length]) == 0)
293
                {
294
                        return bytesDone - (i + [term length]);
295
                }
296
 
297
                i++;
298
        }
299
 
300
        return -1;
301
}
302
 
303
- (void)dealloc
304
{
305
        [buffer release];
306
        [term release];
307
        [super dealloc];
308
}
309
 
310
@end
311
 
312
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
313
#pragma mark -
314
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
315
 
316
/**
317
 * The AsyncWritePacket encompasses the instructions for any given write.
318
**/
319
@interface AsyncWritePacket : NSObject
320
{
321
  @public
322
        NSData *buffer;
323
        CFIndex bytesDone;
324
        long tag;
325
        NSTimeInterval timeout;
326
}
327
- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i;
328
@end
329
 
330
@implementation AsyncWritePacket
331
 
332
- (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i
333
{
334
        if((self = [super init]))
335
        {
336
                buffer = [d retain];
337
                timeout = t;
338
                tag = i;
339
                bytesDone = 0;
340
        }
341
        return self;
342
}
343
 
344
- (void)dealloc
345
{
346
        [buffer release];
347
        [super dealloc];
348
}
349
 
350
@end
351
 
352
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
353
#pragma mark -
354
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
355
 
356
/**
357
 * The AsyncSpecialPacket encompasses special instructions for interruptions in the read/write queues.
358
 * This class my be altered to support more than just TLS in the future.
359
**/
360
@interface AsyncSpecialPacket : NSObject
361
{
362
  @public
363
        NSDictionary *tlsSettings;
364
}
365
- (id)initWithTLSSettings:(NSDictionary *)settings;
366
@end
367
 
368
@implementation AsyncSpecialPacket
369
 
370
- (id)initWithTLSSettings:(NSDictionary *)settings
371
{
372
        if((self = [super init]))
373
        {
374
                tlsSettings = [settings copy];
375
        }
376
        return self;
377
}
378
 
379
- (void)dealloc
380
{
381
        [tlsSettings release];
382
        [super dealloc];
383
}
384
 
385
@end
386
 
387
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
388
#pragma mark -
389
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
390
 
391
@implementation AsyncSocket
392
 
393
- (id)init
394
{
395
        return [self initWithDelegate:nil userData:0];
396
}
397
 
398
- (id)initWithDelegate:(id)delegate
399
{
400
        return [self initWithDelegate:delegate userData:0];
401
}
402
 
403
// Designated initializer.
404
- (id)initWithDelegate:(id)delegate userData:(long)userData
405
{
406
        if((self = [super init]))
407
        {
408
                theFlags = DEFAULT_PREBUFFERING ? kEnablePreBuffering : 0;
409
                theDelegate = delegate;
410
                theUserData = userData;
411
 
412
                theSocket4 = NULL;
413
                theSource4 = NULL;
414
                theSocket6 = NULL;
415
                theSource6 = NULL;
416
                theRunLoop = NULL;
417
                theReadStream = NULL;
418
                theWriteStream = NULL;
419
 
420
                theConnectTimer = nil;
421
 
422
                theReadQueue = [[NSMutableArray alloc] initWithCapacity:READQUEUE_CAPACITY];
423
                theCurrentRead = nil;
424
                theReadTimer = nil;
425
 
426
                partialReadBuffer = [[NSMutableData alloc] initWithCapacity:READALL_CHUNKSIZE];
427
 
428
                theWriteQueue = [[NSMutableArray alloc] initWithCapacity:WRITEQUEUE_CAPACITY];
429
                theCurrentWrite = nil;
430
                theWriteTimer = nil;
431
 
432
                // Socket context
433
                NSAssert(sizeof(CFSocketContext) == sizeof(CFStreamClientContext), @"CFSocketContext != CFStreamClientContext");
434
                theContext.version = 0;
435
                theContext.info = self;
436
                theContext.retain = nil;
437
                theContext.release = nil;
438
                theContext.copyDescription = nil;
439
 
440
                // Default run loop modes
441
                theRunLoopModes = [[NSArray arrayWithObject:NSDefaultRunLoopMode] retain];
442
        }
443
        return self;
444
}
445
 
446
// The socket may been initialized in a connected state and auto-released, so this should close it down cleanly.
447
- (void)dealloc
448
{
449
        [self close];
450
        [theReadQueue release];
451
        [theWriteQueue release];
452
        [theRunLoopModes release];
453
        [partialReadBuffer release];
454
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
455
        [super dealloc];
456
}
457
 
458
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
459
#pragma mark Accessors
460
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
461
 
462
- (long)userData
463
{
464
        return theUserData;
465
}
466
 
467
- (void)setUserData:(long)userData
468
{
469
        theUserData = userData;
470
}
471
 
472
- (id)delegate
473
{
474
        return theDelegate;
475
}
476
 
477
- (void)setDelegate:(id)delegate
478
{
479
        theDelegate = delegate;
480
}
481
 
482
- (BOOL)canSafelySetDelegate
483
{
484
        return ([theReadQueue count] == 0 && [theWriteQueue count] == 0 && theCurrentRead == nil && theCurrentWrite == nil);
485
}
486
 
487
- (CFSocketRef)getCFSocket
488
{
489
        if(theSocket4)
490
                return theSocket4;
491
        else
492
                return theSocket6;
493
}
494
 
495
- (CFReadStreamRef)getCFReadStream
496
{
497
        return theReadStream;
498
}
499
 
500
- (CFWriteStreamRef)getCFWriteStream
501
{
502
        return theWriteStream;
503
}
504
 
505
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
506
#pragma mark Progress
507
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
508
 
509
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
510
{
511
        // Check to make sure we're actually reading something right now,
512
        // and that the read packet isn't an AsyncSpecialPacket (upgrade to TLS).
513
        if (!theCurrentRead || ![theCurrentRead isKindOfClass:[AsyncReadPacket class]]) return NAN;
514
 
515
        // It's only possible to know the progress of our read if we're reading to a certain length
516
        // If we're reading to data, we of course have no idea when the data will arrive
517
        // If we're reading to timeout, then we have no idea when the next chunk of data will arrive.
518
        BOOL hasTotal = (theCurrentRead->readAllAvailableData == NO && theCurrentRead->term == nil);
519
 
520
        CFIndex d = theCurrentRead->bytesDone;
521
        CFIndex t = hasTotal ? [theCurrentRead->buffer length] : 0;
522
        if (tag != NULL)   *tag = theCurrentRead->tag;
523
        if (done != NULL)  *done = d;
524
        if (total != NULL) *total = t;
525
        float ratio = (float)d/(float)t;
526
        return isnan(ratio) ? 1.0F : ratio; // 0 of 0 bytes is 100% done.
527
}
528
 
529
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total
530
{
531
        // Check to make sure we're actually writing something right now,
532
        // and that the write packet isn't an AsyncSpecialPacket (upgrade to TLS).
533
        if (!theCurrentWrite || ![theCurrentWrite isKindOfClass:[AsyncWritePacket class]]) return NAN;
534
 
535
        CFIndex d = theCurrentWrite->bytesDone;
536
        CFIndex t = [theCurrentWrite->buffer length];
537
        if (tag != NULL)   *tag = theCurrentWrite->tag;
538
        if (done != NULL)  *done = d;
539
        if (total != NULL) *total = t;
540
        return (float)d/(float)t;
541
}
542
 
543
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
544
#pragma mark Run Loop
545
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
546
 
547
- (void)runLoopAddSource:(CFRunLoopSourceRef)source
548
{
549
        unsigned i, count = [theRunLoopModes count];
550
        for(i = 0; i < count; i++)
551
        {
552
                CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
553
                CFRunLoopAddSource(theRunLoop, source, runLoopMode);
554
        }
555
}
556
 
557
- (void)runLoopRemoveSource:(CFRunLoopSourceRef)source
558
{
559
        unsigned i, count = [theRunLoopModes count];
560
        for(i = 0; i < count; i++)
561
        {
562
                CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
563
                CFRunLoopRemoveSource(theRunLoop, source, runLoopMode);
564
        }
565
}
566
 
567
- (void)runLoopAddTimer:(NSTimer *)timer
568
{
569
        unsigned i, count = [theRunLoopModes count];
570
        for(i = 0; i < count; i++)
571
        {
572
                CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
573
                CFRunLoopAddTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
574
        }
575
}
576
 
577
- (void)runLoopRemoveTimer:(NSTimer *)timer
578
{
579
        unsigned i, count = [theRunLoopModes count];
580
        for(i = 0; i < count; i++)             
581
        {
582
                CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
583
                CFRunLoopRemoveTimer(theRunLoop, (CFRunLoopTimerRef)timer, runLoopMode);
584
        }
585
}
586
 
587
- (void)runLoopUnscheduleReadStream
588
{
589
        unsigned i, count = [theRunLoopModes count];
590
        for(i = 0; i < count; i++)
591
        {
592
                CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
593
                CFReadStreamUnscheduleFromRunLoop(theReadStream, theRunLoop, runLoopMode);
594
        }
595
        CFReadStreamSetClient(theReadStream, kCFStreamEventNone, NULL, NULL);
596
}
597
 
598
- (void)runLoopUnscheduleWriteStream
599
{
600
        unsigned i, count = [theRunLoopModes count];
601
        for(i = 0; i < count; i++)
602
        {
603
                CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
604
                CFWriteStreamUnscheduleFromRunLoop(theWriteStream, theRunLoop, runLoopMode);
605
        }
606
        CFWriteStreamSetClient(theWriteStream, kCFStreamEventNone, NULL, NULL);
607
}
608
 
609
 
610
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
611
#pragma mark Configuration
612
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
613
 
614
/**
615
 * See the header file for a full explanation of pre-buffering.
616
**/
617
- (void)enablePreBuffering
618
{
619
        theFlags |= kEnablePreBuffering;
620
}
621
 
622
/**
623
 * See the header file for a full explanation of this method.
624
**/
625
- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop
626
{
627
        NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
628
                         @"moveToRunLoop must be called from within the current RunLoop!");
629
 
630
        if(runLoop == nil)
631
        {
632
                return NO;
633
        }
634
        if(theRunLoop == [runLoop getCFRunLoop])
635
        {
636
                return YES;
637
        }
638
 
639
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
640
        theFlags &= ~kDequeueReadScheduled;
641
        theFlags &= ~kDequeueWriteScheduled;
642
 
643
        if(theReadStream && theWriteStream)
644
    {
645
        [self runLoopUnscheduleReadStream];
646
        [self runLoopUnscheduleWriteStream];
647
    }
648
 
649
        if(theSource4) [self runLoopRemoveSource:theSource4];
650
        if(theSource6) [self runLoopRemoveSource:theSource6];
651
 
652
        // We do not retain the timers - they get retained by the runloop when we add them as a source.
653
        // Since we're about to remove them as a source, we retain now, and release again below.
654
        [theReadTimer retain];
655
        [theWriteTimer retain];
656
 
657
        if(theReadTimer) [self runLoopRemoveTimer:theReadTimer];
658
        if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer];
659
 
660
        theRunLoop = [runLoop getCFRunLoop];
661
 
662
        if(theReadTimer) [self runLoopAddTimer:theReadTimer];
663
        if(theWriteTimer) [self runLoopAddTimer:theWriteTimer];
664
 
665
        // Release timers since we retained them above
666
        [theReadTimer release];
667
        [theWriteTimer release];
668
 
669
        if(theSource4) [self runLoopAddSource:theSource4];
670
        if(theSource6) [self runLoopAddSource:theSource6];
671
 
672
    if(theReadStream && theWriteStream)
673
        {
674
                if(![self attachStreamsToRunLoop:runLoop error:nil])
675
                {
676
                        return NO;
677
                }
678
        }
679
 
680
        [runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes];
681
        [runLoop performSelector:@selector(maybeDequeueWrite) target:self argument:nil order:0 modes:theRunLoopModes];
682
        [runLoop performSelector:@selector(maybeScheduleDisconnect) target:self argument:nil order:0 modes:theRunLoopModes];
683
 
684
        return YES;
685
}
686
 
687
/**
688
 * See the header file for a full explanation of this method.
689
**/
690
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes
691
{
692
        NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
693
                         @"setRunLoopModes must be called from within the current RunLoop!");
694
 
695
        if([runLoopModes count] == 0)
696
        {
697
                return NO;
698
        }
699
        if([theRunLoopModes isEqualToArray:runLoopModes])
700
        {
701
                return YES;
702
        }
703
 
704
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
705
        theFlags &= ~kDequeueReadScheduled;
706
        theFlags &= ~kDequeueWriteScheduled;
707
 
708
        if(theReadStream && theWriteStream)
709
    {
710
        [self runLoopUnscheduleReadStream];
711
        [self runLoopUnscheduleWriteStream];
712
    }
713
 
714
        if(theSource4) [self runLoopRemoveSource:theSource4];
715
        if(theSource6) [self runLoopRemoveSource:theSource6];
716
 
717
        // We do not retain the timers - they get retained by the runloop when we add them as a source.
718
        // Since we're about to remove them as a source, we retain now, and release again below.
719
        [theReadTimer retain];
720
        [theWriteTimer retain];
721
 
722
        if(theReadTimer) [self runLoopRemoveTimer:theReadTimer];
723
        if(theWriteTimer) [self runLoopRemoveTimer:theWriteTimer];
724
 
725
        [theRunLoopModes release];
726
        theRunLoopModes = [runLoopModes copy];
727
 
728
        if(theReadTimer) [self runLoopAddTimer:theReadTimer];
729
        if(theWriteTimer) [self runLoopAddTimer:theWriteTimer];
730
 
731
        // Release timers since we retained them above
732
        [theReadTimer release];
733
        [theWriteTimer release];
734
 
735
        if(theSource4) [self runLoopAddSource:theSource4];
736
        if(theSource6) [self runLoopAddSource:theSource6];
737
 
738
        if(theReadStream && theWriteStream)
739
        {
740
                // Note: theRunLoop variable is a CFRunLoop, and NSRunLoop is NOT toll-free bridged with CFRunLoop.
741
                // So we cannot pass theRunLoop to the method below, which is expecting a NSRunLoop parameter.
742
                // Instead we pass nil, which will result in the method properly using the current run loop.
743
 
744
                if(![self attachStreamsToRunLoop:nil error:nil])
745
                {
746
                        return NO;
747
                }
748
        }
749
 
750
        [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
751
        [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
752
        [self performSelector:@selector(maybeScheduleDisconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
753
 
754
        return YES;
755
}
756
 
757
- (NSArray *)runLoopModes
758
{
759
        return [[theRunLoopModes retain] autorelease];
760
}
761
 
762
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
763
#pragma mark Accepting
764
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
765
 
766
- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr
767
{
768
        return [self acceptOnInterface:nil port:port error:errPtr];
769
}
770
 
771
/**
772
 * To accept on a certain interface, pass the address to accept on.
773
 * To accept on any interface, pass nil or an empty string.
774
 * To accept only connections from localhost pass "localhost" or "loopback".
775
**/
776
- (BOOL)acceptOnInterface:(NSString *)interface port:(UInt16)port error:(NSError **)errPtr
777
{
778
        if (theDelegate == NULL)
779
    {
780
                [NSException raise:AsyncSocketException
781
                            format:@"Attempting to accept without a delegate. Set a delegate first."];
782
    }
783
 
784
        if (theSocket4 != NULL || theSocket6 != NULL)
785
    {
786
                [NSException raise:AsyncSocketException
787
                            format:@"Attempting to accept while connected or accepting connections. Disconnect first."];
788
    }
789
 
790
        // Set up the listen sockaddr structs if needed.
791
 
792
        NSData *address4 = nil, *address6 = nil;
793
        if(interface == nil || ([interface length] == 0))
794
        {
795
                // Accept on ANY address
796
                struct sockaddr_in nativeAddr4;
797
                nativeAddr4.sin_len         = sizeof(struct sockaddr_in);
798
                nativeAddr4.sin_family      = AF_INET;
799
                nativeAddr4.sin_port        = htons(port);
800
                nativeAddr4.sin_addr.s_addr = htonl(INADDR_ANY);
801
                memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero));
802
 
803
                struct sockaddr_in6 nativeAddr6;
804
                nativeAddr6.sin6_len       = sizeof(struct sockaddr_in6);
805
                nativeAddr6.sin6_family    = AF_INET6;
806
                nativeAddr6.sin6_port      = htons(port);
807
                nativeAddr6.sin6_flowinfo  = 0;
808
                nativeAddr6.sin6_addr      = in6addr_any;
809
                nativeAddr6.sin6_scope_id  = 0;
810
 
811
                // Wrap the native address structures for CFSocketSetAddress.
812
                address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
813
                address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
814
        }
815
        else if([interface isEqualToString:@"localhost"] || [interface isEqualToString:@"loopback"])
816
        {
817
                // Accept only on LOOPBACK address
818
                struct sockaddr_in nativeAddr4;
819
                nativeAddr4.sin_len         = sizeof(struct sockaddr_in);
820
                nativeAddr4.sin_family      = AF_INET;
821
                nativeAddr4.sin_port        = htons(port);
822
                nativeAddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
823
                memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero));
824
 
825
                struct sockaddr_in6 nativeAddr6;
826
                nativeAddr6.sin6_len       = sizeof(struct sockaddr_in6);
827
                nativeAddr6.sin6_family    = AF_INET6;
828
                nativeAddr6.sin6_port      = htons(port);
829
                nativeAddr6.sin6_flowinfo  = 0;
830
                nativeAddr6.sin6_addr      = in6addr_loopback;
831
                nativeAddr6.sin6_scope_id  = 0;
832
 
833
                // Wrap the native address structures for CFSocketSetAddress.
834
                address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
835
                address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
836
        }
837
        else
838
        {
839
                NSString *portStr = [NSString stringWithFormat:@"%hu", port];
840
 
841
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
842
                @synchronized (getaddrinfoLock)
843
#endif
844
                {
845
                        struct addrinfo hints, *res, *res0;
846
 
847
                        memset(&hints, 0, sizeof(hints));
848
                        hints.ai_family   = PF_UNSPEC;
849
                        hints.ai_socktype = SOCK_STREAM;
850
                        hints.ai_protocol = IPPROTO_TCP;
851
                        hints.ai_flags    = AI_PASSIVE;
852
 
853
                        int error = getaddrinfo([interface UTF8String], [portStr UTF8String], &hints, &res0);
854
 
855
                        if(error)
856
                        {
857
                                if(errPtr)
858
                                {
859
                                        NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
860
                                        NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
861
 
862
                                        *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
863
                                }
864
                        }
865
 
866
                        for(res = res0; res; res = res->ai_next)
867
                        {
868
                                if(!address4 && (res->ai_family == AF_INET))
869
                                {
870
                                        // Found IPv4 address
871
                                        // Wrap the native address structures for CFSocketSetAddress.
872
                                        address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
873
                                }
874
                                else if(!address6 && (res->ai_family == AF_INET6))
875
                                {
876
                                        // Found IPv6 address
877
                                        // Wrap the native address structures for CFSocketSetAddress.
878
                                        address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
879
                                }
880
                        }
881
                        freeaddrinfo(res0);
882
                }
883
 
884
                if(!address4 && !address6) return NO;
885
        }
886
 
887
        // Create the sockets.
888
 
889
        if (address4)
890
        {
891
                theSocket4 = [self newAcceptSocketForAddress:address4 error:errPtr];
892
                if (theSocket4 == NULL) goto Failed;
893
        }
894
 
895
        if (address6)
896
        {
897
                theSocket6 = [self newAcceptSocketForAddress:address6 error:errPtr];
898
 
899
                // Note: The iPhone doesn't currently support IPv6
900
 
901
#if !TARGET_OS_IPHONE
902
                if (theSocket6 == NULL) goto Failed;
903
#endif
904
        }
905
 
906
        // Attach the sockets to the run loop so that callback methods work
907
 
908
        [self attachSocketsToRunLoop:nil error:nil];
909
 
910
        // Set the SO_REUSEADDR flags.
911
 
912
        int reuseOn = 1;
913
        if (theSocket4) setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
914
        if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
915
 
916
        // Set the local bindings which causes the sockets to start listening.
917
 
918
        CFSocketError err;
919
        if (theSocket4)
920
        {
921
                err = CFSocketSetAddress (theSocket4, (CFDataRef)address4);
922
                if (err != kCFSocketSuccess) goto Failed;
923
 
924
                //NSLog(@"theSocket4: %hu", [self localPort:theSocket4]);
925
        }
926
 
927
        if(port == 0 && theSocket4 && theSocket6)
928
        {
929
                // The user has passed in port 0, which means he wants to allow the kernel to choose the port for them
930
                // However, the kernel will choose a different port for both theSocket4 and theSocket6
931
                // So we grab the port the kernel choose for theSocket4, and set it as the port for theSocket6
932
                UInt16 chosenPort = [self localPort:theSocket4];
933
 
934
                struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)[address6 bytes];
935
                pSockAddr6->sin6_port = htons(chosenPort);
936
    }
937
 
938
        if (theSocket6)
939
        {
940
                err = CFSocketSetAddress (theSocket6, (CFDataRef)address6);
941
                if (err != kCFSocketSuccess) goto Failed;
942
 
943
                //NSLog(@"theSocket6: %hu", [self localPort:theSocket6]);
944
        }
945
 
946
        theFlags |= kDidPassConnectMethod;
947
        return YES;
948
 
949
Failed:
950
        if(errPtr) *errPtr = [self getSocketError];
951
        if(theSocket4 != NULL)
952
        {
953
                CFSocketInvalidate(theSocket4);
954
                CFRelease(theSocket4);
955
                theSocket4 = NULL;
956
        }
957
        if(theSocket6 != NULL)
958
        {
959
                CFSocketInvalidate(theSocket6);
960
                CFRelease(theSocket6);
961
                theSocket6 = NULL;
962
        }
963
        return NO;
964
}
965
 
966
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
967
#pragma mark Connecting
968
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
969
 
970
- (BOOL)connectToHost:(NSString*)hostname onPort:(UInt16)port error:(NSError **)errPtr
971
{
972
        return [self connectToHost:hostname onPort:port withTimeout:-1 error:errPtr];
973
}
974
 
975
/**
976
 * This method creates an initial CFReadStream and CFWriteStream to the given host on the given port.
977
 * The connection is then opened, and the corresponding CFSocket will be extracted after the connection succeeds.
978
 *
979
 * Thus the delegate will have access to the CFReadStream and CFWriteStream prior to connection,
980
 * specifically in the onSocketWillConnect: method.
981
**/
982
- (BOOL)connectToHost:(NSString *)hostname
983
                           onPort:(UInt16)port
984
                  withTimeout:(NSTimeInterval)timeout
985
                                error:(NSError **)errPtr
986
{
987
        if(theDelegate == NULL)
988
        {
989
                [NSException raise:AsyncSocketException
990
                            format:@"Attempting to connect without a delegate. Set a delegate first."];
991
        }
992
 
993
        if(theSocket4 != NULL || theSocket6 != NULL)
994
        {
995
                [NSException raise:AsyncSocketException
996
                            format:@"Attempting to connect while connected or accepting connections. Disconnect first."];
997
        }
998
 
999
        if(![self createStreamsToHost:hostname onPort:port error:errPtr]) goto Failed;
1000
        if(![self attachStreamsToRunLoop:nil error:errPtr])               goto Failed;
1001
        if(![self configureStreamsAndReturnError:errPtr])                 goto Failed;
1002
        if(![self openStreamsAndReturnError:errPtr])                      goto Failed;
1003
 
1004
        [self startConnectTimeout:timeout];
1005
        theFlags |= kDidPassConnectMethod;
1006
 
1007
        return YES;
1008
 
1009
Failed:
1010
        [self close];
1011
        return NO;
1012
}
1013
 
1014
- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
1015
{
1016
        return [self connectToAddress:remoteAddr withTimeout:-1 error:errPtr];
1017
}
1018
 
1019
/**
1020
 * This method creates an initial CFSocket to the given address.
1021
 * The connection is then opened, and the corresponding CFReadStream and CFWriteStream will be
1022
 * created from the low-level sockets after the connection succeeds.
1023
 *
1024
 * Thus the delegate will have access to the CFSocket and CFSocketNativeHandle (BSD socket) prior to connection,
1025
 * specifically in the onSocketWillConnect: method.
1026
 *
1027
 * Note: The NSData parameter is expected to be a sockaddr structure. For example, an NSData object returned from
1028
 * NSNetservice addresses method.
1029
 * If you have an existing struct sockaddr you can convert it to an NSData object like so:
1030
 * struct sockaddr sa  -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
1031
 * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
1032
**/
1033
- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr
1034
{
1035
        if (theDelegate == NULL)
1036
        {
1037
                [NSException raise:AsyncSocketException
1038
                            format:@"Attempting to connect without a delegate. Set a delegate first."];
1039
        }
1040
 
1041
        if (theSocket4 != NULL || theSocket6 != NULL)
1042
        {
1043
                [NSException raise:AsyncSocketException
1044
                            format:@"Attempting to connect while connected or accepting connections. Disconnect first."];
1045
        }
1046
 
1047
        if(![self createSocketForAddress:remoteAddr error:errPtr])   goto Failed;
1048
        if(![self attachSocketsToRunLoop:nil error:errPtr])          goto Failed;
1049
        if(![self configureSocketAndReturnError:errPtr])             goto Failed;
1050
        if(![self connectSocketToAddress:remoteAddr error:errPtr])   goto Failed;
1051
 
1052
        [self startConnectTimeout:timeout];
1053
        theFlags |= kDidPassConnectMethod;
1054
 
1055
        return YES;
1056
 
1057
Failed:
1058
        [self close];
1059
        return NO;
1060
}
1061
 
1062
- (void)startConnectTimeout:(NSTimeInterval)timeout
1063
{
1064
        if(timeout >= 0.0)
1065
        {
1066
                theConnectTimer = [NSTimer timerWithTimeInterval:timeout
1067
                                                                                              target:self
1068
                                                                                            selector:@selector(doConnectTimeout:)
1069
                                                                                            userInfo:nil
1070
                                                                                             repeats:NO];
1071
                [self runLoopAddTimer:theConnectTimer];
1072
        }
1073
}
1074
 
1075
- (void)endConnectTimeout
1076
{
1077
        [theConnectTimer invalidate];
1078
        theConnectTimer = nil;
1079
}
1080
 
1081
- (void)doConnectTimeout:(NSTimer *)timer
1082
{
1083
        [self endConnectTimeout];
1084
        [self closeWithError:[self getConnectTimeoutError]];
1085
}
1086
 
1087
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1088
#pragma mark Socket Implementation
1089
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1090
 
1091
/**
1092
 * Creates the accept sockets.
1093
 * Returns true if either IPv4 or IPv6 is created.
1094
 * If either is missing, an error is returned (even though the method may return true).
1095
**/
1096
- (CFSocketRef)newAcceptSocketForAddress:(NSData *)addr error:(NSError **)errPtr
1097
{
1098
        struct sockaddr *pSockAddr = (struct sockaddr *)[addr bytes];
1099
        int addressFamily = pSockAddr->sa_family;
1100
 
1101
        CFSocketRef theSocket = CFSocketCreate(kCFAllocatorDefault,
1102
                                               addressFamily,
1103
                                               SOCK_STREAM,
1104
                                               0,
1105
                                               kCFSocketAcceptCallBack,                // Callback flags
1106
                                               (CFSocketCallBack)&MyCFSocketCallback,  // Callback method
1107
                                               &theContext);
1108
 
1109
        if(theSocket == NULL)
1110
        {
1111
                if(errPtr) *errPtr = [self getSocketError];
1112
        }
1113
 
1114
        return theSocket;
1115
}
1116
 
1117
- (BOOL)createSocketForAddress:(NSData *)remoteAddr error:(NSError **)errPtr
1118
{
1119
        struct sockaddr *pSockAddr = (struct sockaddr *)[remoteAddr bytes];
1120
 
1121
        if(pSockAddr->sa_family == AF_INET)
1122
        {
1123
                theSocket4 = CFSocketCreate(NULL,                                   // Default allocator
1124
                                            PF_INET,                                // Protocol Family
1125
                                            SOCK_STREAM,                            // Socket Type
1126
                                            IPPROTO_TCP,                            // Protocol
1127
                                            kCFSocketConnectCallBack,               // Callback flags
1128
                                            (CFSocketCallBack)&MyCFSocketCallback,  // Callback method
1129
                                            &theContext);                           // Socket Context
1130
 
1131
                if(theSocket4 == NULL)
1132
                {
1133
                        if (errPtr) *errPtr = [self getSocketError];
1134
                        return NO;
1135
                }
1136
        }
1137
        else if(pSockAddr->sa_family == AF_INET6)
1138
        {
1139
                theSocket6 = CFSocketCreate(NULL,                                   // Default allocator
1140
                                                                    PF_INET6,                               // Protocol Family
1141
                                                                    SOCK_STREAM,                            // Socket Type
1142
                                                                    IPPROTO_TCP,                            // Protocol
1143
                                                                    kCFSocketConnectCallBack,               // Callback flags
1144
                                                                    (CFSocketCallBack)&MyCFSocketCallback,  // Callback method
1145
                                                                    &theContext);                           // Socket Context
1146
 
1147
                if(theSocket6 == NULL)
1148
                {
1149
                        if (errPtr) *errPtr = [self getSocketError];
1150
                        return NO;
1151
                }
1152
        }
1153
        else
1154
        {
1155
                if (errPtr) *errPtr = [self getSocketError];
1156
                return NO;
1157
        }
1158
 
1159
        return YES;
1160
}
1161
 
1162
/**
1163
 * Adds the CFSocket's to the run-loop so that callbacks will work properly.
1164
**/
1165
- (BOOL)attachSocketsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
1166
{      
1167
        // Get the CFRunLoop to which the socket should be attached.
1168
        theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
1169
 
1170
        if(theSocket4)
1171
        {
1172
                theSource4 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket4, 0);
1173
        [self runLoopAddSource:theSource4];
1174
        }
1175
 
1176
        if(theSocket6)
1177
        {
1178
                theSource6 = CFSocketCreateRunLoopSource (kCFAllocatorDefault, theSocket6, 0);
1179
        [self runLoopAddSource:theSource6];
1180
        }
1181
 
1182
        return YES;
1183
}
1184
 
1185
/**
1186
 * Allows the delegate method to configure the CFSocket or CFNativeSocket as desired before we connect.
1187
 * Note that the CFReadStream and CFWriteStream will not be available until after the connection is opened.
1188
**/
1189
- (BOOL)configureSocketAndReturnError:(NSError **)errPtr
1190
{
1191
        // Call the delegate method for further configuration.
1192
        if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
1193
        {
1194
                if([theDelegate onSocketWillConnect:self] == NO)
1195
                {
1196
                        if (errPtr) *errPtr = [self getAbortError];
1197
                        return NO;
1198
                }
1199
        }
1200
        return YES;
1201
}
1202
 
1203
- (BOOL)connectSocketToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
1204
{
1205
        // Start connecting to the given address in the background
1206
        // The MyCFSocketCallback method will be called when the connection succeeds or fails
1207
        if(theSocket4)
1208
        {
1209
                CFSocketError err = CFSocketConnectToAddress(theSocket4, (CFDataRef)remoteAddr, -1);
1210
                if(err != kCFSocketSuccess)
1211
                {
1212
                        if (errPtr) *errPtr = [self getSocketError];
1213
                        return NO;
1214
                }
1215
        }
1216
        else if(theSocket6)
1217
        {
1218
                CFSocketError err = CFSocketConnectToAddress(theSocket6, (CFDataRef)remoteAddr, -1);
1219
                if(err != kCFSocketSuccess)
1220
                {
1221
                        if (errPtr) *errPtr = [self getSocketError];
1222
                        return NO;
1223
                }
1224
        }
1225
 
1226
        return YES;
1227
}
1228
 
1229
/**
1230
 * Attempt to make the new socket.
1231
 * If an error occurs, ignore this event.
1232
**/
1233
- (void)doAcceptWithSocket:(CFSocketNativeHandle)newNative
1234
{
1235
        // New socket inherits same delegate and run loop modes.
1236
        // Note: We use [self class] to support subclassing AsyncSocket.
1237
        AsyncSocket *newSocket = [[[[self class] alloc] initWithDelegate:theDelegate] autorelease];
1238
        [newSocket setRunLoopModes:theRunLoopModes];
1239
 
1240
        if(newSocket)
1241
        {
1242
                if ([theDelegate respondsToSelector:@selector(onSocket:didAcceptNewSocket:)])
1243
                        [theDelegate onSocket:self didAcceptNewSocket:newSocket];
1244
 
1245
                NSRunLoop *runLoop = nil;
1246
                if ([theDelegate respondsToSelector:@selector(onSocket:wantsRunLoopForNewSocket:)])
1247
                        runLoop = [theDelegate onSocket:self wantsRunLoopForNewSocket:newSocket];
1248
 
1249
                BOOL pass = YES;
1250
 
1251
                if(pass && ![newSocket createStreamsFromNative:newNative error:nil]) pass = NO;
1252
                if(pass && ![newSocket attachStreamsToRunLoop:runLoop error:nil])    pass = NO;
1253
                if(pass && ![newSocket configureStreamsAndReturnError:nil])          pass = NO;
1254
                if(pass && ![newSocket openStreamsAndReturnError:nil])               pass = NO;
1255
 
1256
                if(pass)
1257
                        newSocket->theFlags |= kDidPassConnectMethod;
1258
                else {
1259
                        // No NSError, but errors will still get logged from the above functions.
1260
                        [newSocket close];
1261
                }
1262
 
1263
        }
1264
}
1265
 
1266
/**
1267
 * Description forthcoming...
1268
**/
1269
- (void)doSocketOpen:(CFSocketRef)sock withCFSocketError:(CFSocketError)socketError
1270
{
1271
        NSParameterAssert ((sock == theSocket4) || (sock == theSocket6));
1272
 
1273
        if(socketError == kCFSocketTimeout || socketError == kCFSocketError)
1274
        {
1275
                [self closeWithError:[self getSocketError]];
1276
                return;
1277
        }
1278
 
1279
        // Get the underlying native (BSD) socket
1280
        CFSocketNativeHandle nativeSocket = CFSocketGetNative(sock);
1281
 
1282
        // Setup the socket so that invalidating the socket will not close the native socket
1283
        CFSocketSetSocketFlags(sock, 0);
1284
 
1285
        // Invalidate and release the CFSocket - All we need from here on out is the nativeSocket
1286
        // Note: If we don't invalidate the socket (leaving the native socket open)
1287
        // then theReadStream and theWriteStream won't function properly.
1288
        // Specifically, their callbacks won't work, with the exception of kCFStreamEventOpenCompleted.
1289
        // I'm not entirely sure why this is, but I'm guessing that events on the socket fire to the CFSocket we created,
1290
        // as opposed to the CFReadStream/CFWriteStream.
1291
 
1292
        CFSocketInvalidate(sock);
1293
        CFRelease(sock);
1294
        theSocket4 = NULL;
1295
        theSocket6 = NULL;
1296
 
1297
        NSError *err;
1298
        BOOL pass = YES;
1299
 
1300
        if(pass && ![self createStreamsFromNative:nativeSocket error:&err]) pass = NO;
1301
        if(pass && ![self attachStreamsToRunLoop:nil error:&err])           pass = NO;
1302
        if(pass && ![self openStreamsAndReturnError:&err])                  pass = NO;
1303
 
1304
        if(!pass)
1305
        {
1306
                [self closeWithError:err];
1307
        }
1308
}
1309
 
1310
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1311
#pragma mark Stream Implementation
1312
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1313
 
1314
/**
1315
 * Creates the CFReadStream and CFWriteStream from the given native socket.
1316
 * The CFSocket may be extracted from either stream after the streams have been opened.
1317
 *
1318
 * Note: The given native socket must already be connected!
1319
**/
1320
- (BOOL)createStreamsFromNative:(CFSocketNativeHandle)native error:(NSError **)errPtr
1321
{
1322
        // Create the socket & streams.
1323
        CFStreamCreatePairWithSocket(kCFAllocatorDefault, native, &theReadStream, &theWriteStream);
1324
        if (theReadStream == NULL || theWriteStream == NULL)
1325
        {
1326
                NSError *err = [self getStreamError];
1327
 
1328
                NSLog (@"AsyncSocket %p couldn't create streams from accepted socket: %@", self, err);
1329
 
1330
                if (errPtr) *errPtr = err;
1331
                return NO;
1332
        }
1333
 
1334
        // Ensure the CF & BSD socket is closed when the streams are closed.
1335
        CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
1336
        CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
1337
 
1338
        return YES;
1339
}
1340
 
1341
/**
1342
 * Creates the CFReadStream and CFWriteStream from the given hostname and port number.
1343
 * The CFSocket may be extracted from either stream after the streams have been opened.
1344
**/
1345
- (BOOL)createStreamsToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr
1346
{
1347
        // Create the socket & streams.
1348
        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)hostname, port, &theReadStream, &theWriteStream);
1349
        if (theReadStream == NULL || theWriteStream == NULL)
1350
        {
1351
                if (errPtr) *errPtr = [self getStreamError];
1352
                return NO;
1353
        }
1354
 
1355
        // Ensure the CF & BSD socket is closed when the streams are closed.
1356
        CFReadStreamSetProperty(theReadStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
1357
        CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
1358
 
1359
        return YES;
1360
}
1361
 
1362
- (BOOL)attachStreamsToRunLoop:(NSRunLoop *)runLoop error:(NSError **)errPtr
1363
{
1364
        // Get the CFRunLoop to which the socket should be attached.
1365
        theRunLoop = (runLoop == nil) ? CFRunLoopGetCurrent() : [runLoop getCFRunLoop];
1366
 
1367
        // Setup read stream callbacks
1368
 
1369
        CFOptionFlags readStreamEvents = kCFStreamEventHasBytesAvailable |
1370
                                         kCFStreamEventErrorOccurred     |
1371
                                         kCFStreamEventEndEncountered    |
1372
                                         kCFStreamEventOpenCompleted;
1373
 
1374
        if (!CFReadStreamSetClient(theReadStream,
1375
                                                           readStreamEvents,
1376
                                                           (CFReadStreamClientCallBack)&MyCFReadStreamCallback,
1377
                                                           (CFStreamClientContext *)(&theContext)))
1378
        {
1379
                NSError *err = [self getStreamError];
1380
 
1381
                NSLog (@"AsyncSocket %p couldn't attach read stream to run-loop,", self);
1382
                NSLog (@"Error: %@", err);
1383
 
1384
                if (errPtr) *errPtr = err;
1385
                return NO;
1386
        }
1387
 
1388
        // Setup write stream callbacks
1389
 
1390
        CFOptionFlags writeStreamEvents = kCFStreamEventCanAcceptBytes |
1391
                                          kCFStreamEventErrorOccurred  |
1392
                                          kCFStreamEventEndEncountered |
1393
                                          kCFStreamEventOpenCompleted;
1394
 
1395
        if (!CFWriteStreamSetClient (theWriteStream,
1396
                                                                 writeStreamEvents,
1397
                                                                 (CFWriteStreamClientCallBack)&MyCFWriteStreamCallback,
1398
                                                                 (CFStreamClientContext *)(&theContext)))
1399
        {
1400
                NSError *err = [self getStreamError];
1401
 
1402
                NSLog (@"AsyncSocket %p couldn't attach write stream to run-loop,", self);
1403
                NSLog (@"Error: %@", err);
1404
 
1405
                if (errPtr) *errPtr = err;
1406
                return NO;
1407
        }
1408
 
1409
        // Add read and write streams to run loop
1410
 
1411
        unsigned i, count = [theRunLoopModes count];
1412
        for(i = 0; i < count; i++)
1413
        {
1414
                CFStringRef runLoopMode = (CFStringRef)[theRunLoopModes objectAtIndex:i];
1415
                CFReadStreamScheduleWithRunLoop(theReadStream, theRunLoop, runLoopMode);
1416
                CFWriteStreamScheduleWithRunLoop(theWriteStream, theRunLoop, runLoopMode);
1417
        }
1418
 
1419
        return YES;
1420
}
1421
 
1422
/**
1423
 * Allows the delegate method to configure the CFReadStream and/or CFWriteStream as desired before we connect.
1424
 * Note that the CFSocket and CFNativeSocket will not be available until after the connection is opened.
1425
**/
1426
- (BOOL)configureStreamsAndReturnError:(NSError **)errPtr
1427
{
1428
        // Call the delegate method for further configuration.
1429
        if([theDelegate respondsToSelector:@selector(onSocketWillConnect:)])
1430
        {
1431
                if([theDelegate onSocketWillConnect:self] == NO)
1432
                {
1433
                        if (errPtr) *errPtr = [self getAbortError];
1434
                        return NO;
1435
                }
1436
        }
1437
        return YES;
1438
}
1439
 
1440
- (BOOL)openStreamsAndReturnError:(NSError **)errPtr
1441
{
1442
        BOOL pass = YES;
1443
 
1444
        if(pass && !CFReadStreamOpen (theReadStream))
1445
        {
1446
                NSLog (@"AsyncSocket %p couldn't open read stream,", self);
1447
                pass = NO;
1448
        }
1449
 
1450
        if(pass && !CFWriteStreamOpen (theWriteStream))
1451
        {
1452
                NSLog (@"AsyncSocket %p couldn't open write stream,", self);
1453
                pass = NO;
1454
        }
1455
 
1456
        if(!pass)
1457
        {
1458
                if (errPtr) *errPtr = [self getStreamError];
1459
        }
1460
 
1461
        return pass;
1462
}
1463
 
1464
/**
1465
 * Called when read or write streams open.
1466
 * When the socket is connected and both streams are open, consider the AsyncSocket instance to be ready.
1467
**/
1468
- (void)doStreamOpen
1469
{
1470
        NSError *err = nil;
1471
        if ((theFlags & kDidCompleteOpenForRead) && (theFlags & kDidCompleteOpenForWrite))
1472
        {
1473
                // Get the socket.
1474
                if (![self setSocketFromStreamsAndReturnError: &err])
1475
                {
1476
                        NSLog (@"AsyncSocket %p couldn't get socket from streams, %@. Disconnecting.", self, err);
1477
                        [self closeWithError:err];
1478
                        return;
1479
                }
1480
 
1481
        // Stop the connection attempt timeout timer
1482
                [self endConnectTimeout];
1483
 
1484
                if ([theDelegate respondsToSelector:@selector(onSocket:didConnectToHost:port:)])
1485
                {
1486
                        [theDelegate onSocket:self didConnectToHost:[self connectedHost] port:[self connectedPort]];
1487
                }
1488
 
1489
                // Immediately deal with any already-queued requests.
1490
                [self maybeDequeueRead];
1491
                [self maybeDequeueWrite];
1492
        }
1493
}
1494
 
1495
- (BOOL)setSocketFromStreamsAndReturnError:(NSError **)errPtr
1496
{
1497
        // Get the CFSocketNativeHandle from theReadStream
1498
        CFSocketNativeHandle native;
1499
        CFDataRef nativeProp = CFReadStreamCopyProperty(theReadStream, kCFStreamPropertySocketNativeHandle);
1500
        if(nativeProp == NULL)
1501
        {
1502
                if (errPtr) *errPtr = [self getStreamError];
1503
                return NO;
1504
        }
1505
 
1506
        CFDataGetBytes(nativeProp, CFRangeMake(0, CFDataGetLength(nativeProp)), (UInt8 *)&native);
1507
        CFRelease(nativeProp);
1508
 
1509
        CFSocketRef theSocket = CFSocketCreateWithNative(kCFAllocatorDefault, native, 0, NULL, NULL);
1510
        if(theSocket == NULL)
1511
        {
1512
                if (errPtr) *errPtr = [self getSocketError];
1513
                return NO;
1514
        }
1515
 
1516
        // Determine whether the connection was IPv4 or IPv6
1517
        CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket);
1518
        if(peeraddr == NULL)
1519
        {
1520
                NSLog(@"AsyncSocket couldn't determine IP version of socket");
1521
 
1522
                CFRelease(theSocket);
1523
 
1524
                if (errPtr) *errPtr = [self getSocketError];
1525
                return NO;
1526
        }
1527
        struct sockaddr *sa = (struct sockaddr *)CFDataGetBytePtr(peeraddr);
1528
 
1529
        if(sa->sa_family == AF_INET)
1530
        {
1531
                theSocket4 = theSocket;
1532
        }
1533
        else
1534
        {
1535
                theSocket6 = theSocket;
1536
        }
1537
 
1538
        CFRelease(peeraddr);
1539
 
1540
        return YES;
1541
}
1542
 
1543
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1544
#pragma mark Disconnect Implementation
1545
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1546
 
1547
// Sends error message and disconnects
1548
- (void)closeWithError:(NSError *)err
1549
{
1550
        theFlags |= kClosingWithError;
1551
 
1552
        if (theFlags & kDidPassConnectMethod)
1553
        {
1554
                // Try to salvage what data we can.
1555
                [self recoverUnreadData];
1556
 
1557
                // Let the delegate know, so it can try to recover if it likes.
1558
                if ([theDelegate respondsToSelector:@selector(onSocket:willDisconnectWithError:)])
1559
                {
1560
                        [theDelegate onSocket:self willDisconnectWithError:err];
1561
                }
1562
        }
1563
        [self close];
1564
}
1565
 
1566
// Prepare partially read data for recovery.
1567
- (void)recoverUnreadData
1568
{
1569
        if(theCurrentRead != nil)
1570
        {
1571
                // We never finished the current read.
1572
                // Check to see if it's a normal read packet (not AsyncSpecialPacket) and if it had read anything yet.
1573
 
1574
                if(([theCurrentRead isKindOfClass:[AsyncReadPacket class]]) && (theCurrentRead->bytesDone > 0))
1575
                {
1576
                        // We need to move its data into the front of the partial read buffer.
1577
 
1578
                        [partialReadBuffer replaceBytesInRange:NSMakeRange(0, 0)
1579
                                                                                 withBytes:[theCurrentRead->buffer bytes]
1580
                                                                                        length:theCurrentRead->bytesDone];
1581
                }
1582
        }
1583
 
1584
        [self emptyQueues];
1585
}
1586
 
1587
- (void)emptyQueues
1588
{
1589
        if (theCurrentRead != nil)      [self endCurrentRead];
1590
        if (theCurrentWrite != nil)     [self endCurrentWrite];
1591
 
1592
        [theReadQueue removeAllObjects];
1593
        [theWriteQueue removeAllObjects];
1594
 
1595
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueRead) object:nil];
1596
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueWrite) object:nil];
1597
 
1598
        theFlags &= ~kDequeueReadScheduled;
1599
        theFlags &= ~kDequeueWriteScheduled;
1600
}
1601
 
1602
/**
1603
 * Disconnects. This is called for both error and clean disconnections.
1604
**/
1605
- (void)close
1606
{
1607
        // Empty queues
1608
        [self emptyQueues];
1609
 
1610
        // Clear partialReadBuffer (pre-buffer and also unreadData buffer in case of error)
1611
        [partialReadBuffer replaceBytesInRange:NSMakeRange(0, [partialReadBuffer length]) withBytes:NULL length:0];
1612
 
1613
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(disconnect) object:nil];
1614
 
1615
        // Stop the connection attempt timeout timer
1616
        if (theConnectTimer != nil)
1617
        {
1618
                [self endConnectTimeout];
1619
        }
1620
 
1621
        // Close streams.
1622
        if (theReadStream != NULL)
1623
        {
1624
        [self runLoopUnscheduleReadStream];
1625
                CFReadStreamClose(theReadStream);
1626
                CFRelease(theReadStream);
1627
                theReadStream = NULL;
1628
        }
1629
        if (theWriteStream != NULL)
1630
        {
1631
        [self runLoopUnscheduleWriteStream];
1632
                CFWriteStreamClose(theWriteStream);
1633
                CFRelease(theWriteStream);
1634
                theWriteStream = NULL;
1635
        }
1636
 
1637
        // Close sockets.
1638
        if (theSocket4 != NULL)
1639
        {
1640
                CFSocketInvalidate (theSocket4);
1641
                CFRelease (theSocket4);
1642
                theSocket4 = NULL;
1643
        }
1644
        if (theSocket6 != NULL)
1645
        {
1646
                CFSocketInvalidate (theSocket6);
1647
                CFRelease (theSocket6);
1648
                theSocket6 = NULL;
1649
        }
1650
    if (theSource4 != NULL)
1651
    {
1652
        [self runLoopRemoveSource:theSource4];
1653
                CFRelease (theSource4);
1654
                theSource4 = NULL;
1655
        }
1656
        if (theSource6 != NULL)
1657
        {
1658
        [self runLoopRemoveSource:theSource6];
1659
                CFRelease (theSource6);
1660
                theSource6 = NULL;
1661
        }
1662
        theRunLoop = NULL;
1663
 
1664
        // If the client has passed the connect/accept method, then the connection has at least begun.
1665
        // Notify delegate that it is now ending.
1666
        BOOL shouldCallDelegate = (theFlags & kDidPassConnectMethod);
1667
 
1668
        // Clear all flags (except the pre-buffering flag, which should remain as is)
1669
        theFlags &= kEnablePreBuffering;
1670
 
1671
        if (shouldCallDelegate)
1672
        {
1673
                if ([theDelegate respondsToSelector: @selector(onSocketDidDisconnect:)])
1674
                {
1675
                        [theDelegate onSocketDidDisconnect:self];
1676
                }
1677
        }
1678
 
1679
        // Do not access any instance variables after calling onSocketDidDisconnect.
1680
        // This gives the delegate freedom to release us without returning here and crashing.
1681
}
1682
 
1683
/**
1684
 * Disconnects immediately. Any pending reads or writes are dropped.
1685
**/
1686
- (void)disconnect
1687
{
1688
        [self close];
1689
}
1690
 
1691
/**
1692
 * Diconnects after all pending reads have completed.
1693
**/
1694
- (void)disconnectAfterReading
1695
{
1696
        theFlags |= (kForbidReadsWrites | kDisconnectAfterReads);
1697
 
1698
        [self maybeScheduleDisconnect];
1699
}
1700
 
1701
/**
1702
 * Disconnects after all pending writes have completed.
1703
**/
1704
- (void)disconnectAfterWriting
1705
{
1706
        theFlags |= (kForbidReadsWrites | kDisconnectAfterWrites);
1707
 
1708
        [self maybeScheduleDisconnect];
1709
}
1710
 
1711
/**
1712
 * Disconnects after all pending reads and writes have completed.
1713
**/
1714
- (void)disconnectAfterReadingAndWriting
1715
{
1716
        theFlags |= (kForbidReadsWrites | kDisconnectAfterReads | kDisconnectAfterWrites);
1717
 
1718
        [self maybeScheduleDisconnect];
1719
}
1720
 
1721
/**
1722
 * Schedules a call to disconnect if possible.
1723
 * That is, if all writes have completed, and we're set to disconnect after writing,
1724
 * or if all reads have completed, and we're set to disconnect after reading.
1725
**/
1726
- (void)maybeScheduleDisconnect
1727
{
1728
        BOOL shouldDisconnect = NO;
1729
 
1730
        if(theFlags & kDisconnectAfterReads)
1731
        {
1732
                if(([theReadQueue count] == 0) && (theCurrentRead == nil))
1733
                {
1734
                        if(theFlags & kDisconnectAfterWrites)
1735
                        {
1736
                                if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
1737
                                {
1738
                                        shouldDisconnect = YES;
1739
                                }
1740
                        }
1741
                        else
1742
                        {
1743
                                shouldDisconnect = YES;
1744
                        }
1745
                }
1746
        }
1747
        else if(theFlags & kDisconnectAfterWrites)
1748
        {
1749
                if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
1750
                {
1751
                        shouldDisconnect = YES;
1752
                }
1753
        }
1754
 
1755
        if(shouldDisconnect)
1756
        {
1757
                [self performSelector:@selector(disconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
1758
        }
1759
}
1760
 
1761
/**
1762
 * In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
1763
 * any data that's left on the socket.
1764
**/
1765
- (NSData *)unreadData
1766
{
1767
        // Ensure this method will only return data in the event of an error
1768
        if(!(theFlags & kClosingWithError)) return nil;
1769
 
1770
        if(theReadStream == NULL) return nil;
1771
 
1772
        CFIndex totalBytesRead = [partialReadBuffer length];
1773
        BOOL error = NO;
1774
        while(!error && CFReadStreamHasBytesAvailable(theReadStream))
1775
        {
1776
                [partialReadBuffer increaseLengthBy:READALL_CHUNKSIZE];
1777
 
1778
                // Number of bytes to read is space left in packet buffer.
1779
                CFIndex bytesToRead = [partialReadBuffer length] - totalBytesRead;
1780
 
1781
                // Read data into packet buffer
1782
                UInt8 *packetbuf = (UInt8 *)( [partialReadBuffer mutableBytes] + totalBytesRead );
1783
                CFIndex bytesRead = CFReadStreamRead(theReadStream, packetbuf, bytesToRead);
1784
 
1785
                // Check results
1786
                if(bytesRead < 0)
1787
                {
1788
                        error = YES;
1789
                }
1790
                else
1791
                {
1792
                        totalBytesRead += bytesRead;
1793
                }
1794
        }
1795
 
1796
        [partialReadBuffer setLength:totalBytesRead];
1797
 
1798
        return partialReadBuffer;
1799
}
1800
 
1801
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1802
#pragma mark Errors
1803
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1804
 
1805
/**
1806
 * Returns a standard error object for the current errno value.
1807
 * Errno is used for low-level BSD socket errors.
1808
**/
1809
- (NSError *)getErrnoError
1810
{
1811
        NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)];
1812
        NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey];
1813
 
1814
        return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo];
1815
}
1816
 
1817
/**
1818
 * Returns a standard error message for a CFSocket error.
1819
 * Unfortunately, CFSocket offers no feedback on its errors.
1820
**/
1821
- (NSError *)getSocketError
1822
{
1823
        NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCFSocketError",
1824
                                                                                                                 @"AsyncSocket", [NSBundle mainBundle],
1825
                                                                                                                 @"General CFSocket error", nil);
1826
 
1827
        NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1828
 
1829
        return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCFSocketError userInfo:info];
1830
}
1831
 
1832
- (NSError *)getStreamError
1833
{
1834
        CFStreamError err;
1835
        if (theReadStream != NULL)
1836
        {
1837
                err = CFReadStreamGetError (theReadStream);
1838
                if (err.error != 0) return [self errorFromCFStreamError: err];
1839
        }
1840
 
1841
        if (theWriteStream != NULL)
1842
        {
1843
                err = CFWriteStreamGetError (theWriteStream);
1844
                if (err.error != 0) return [self errorFromCFStreamError: err];
1845
        }
1846
 
1847
        return nil;
1848
}
1849
 
1850
/**
1851
 * Returns a standard AsyncSocket abort error.
1852
**/
1853
- (NSError *)getAbortError
1854
{
1855
        NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketCanceledError",
1856
                                                                                                                 @"AsyncSocket", [NSBundle mainBundle],
1857
                                                                                                                 @"Connection canceled", nil);
1858
 
1859
        NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1860
 
1861
        return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketCanceledError userInfo:info];
1862
}
1863
 
1864
/**
1865
 * Returns a standard AsyncSocket connect timeout error.
1866
**/
1867
- (NSError *)getConnectTimeoutError
1868
{
1869
        NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketConnectTimeoutError",
1870
                                                                                                                 @"AsyncSocket", [NSBundle mainBundle],
1871
                                                                                                                 @"Attempt to connect to host timed out", nil);
1872
 
1873
        NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1874
 
1875
        return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketConnectTimeoutError userInfo:info];
1876
}
1877
 
1878
/**
1879
 * Returns a standard AsyncSocket maxed out error.
1880
**/
1881
- (NSError *)getReadMaxedOutError
1882
{
1883
        NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadMaxedOutError",
1884
                                                                                                                 @"AsyncSocket", [NSBundle mainBundle],
1885
                                                                                                                 @"Read operation reached set maximum length", nil);
1886
 
1887
        NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1888
 
1889
        return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadMaxedOutError userInfo:info];
1890
}
1891
 
1892
/**
1893
 * Returns a standard AsyncSocket read timeout error.
1894
**/
1895
- (NSError *)getReadTimeoutError
1896
{
1897
        NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketReadTimeoutError",
1898
                                                                                                                 @"AsyncSocket", [NSBundle mainBundle],
1899
                                                                                                                 @"Read operation timed out", nil);
1900
 
1901
        NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1902
 
1903
        return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketReadTimeoutError userInfo:info];
1904
}
1905
 
1906
/**
1907
 * Returns a standard AsyncSocket write timeout error.
1908
**/
1909
- (NSError *)getWriteTimeoutError
1910
{
1911
        NSString *errMsg = NSLocalizedStringWithDefaultValue(@"AsyncSocketWriteTimeoutError",
1912
                                                                                                                 @"AsyncSocket", [NSBundle mainBundle],
1913
                                                                                                                 @"Write operation timed out", nil);
1914
 
1915
        NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
1916
 
1917
        return [NSError errorWithDomain:AsyncSocketErrorDomain code:AsyncSocketWriteTimeoutError userInfo:info];
1918
}
1919
 
1920
- (NSError *)errorFromCFStreamError:(CFStreamError)err
1921
{
1922
        if (err.domain == 0 && err.error == 0) return nil;
1923
 
1924
        // Can't use switch; these constants aren't int literals.
1925
        NSString *domain = @"CFStreamError (unlisted domain)";
1926
        NSString *message = nil;
1927
 
1928
        if(err.domain == kCFStreamErrorDomainPOSIX) {
1929
                domain = NSPOSIXErrorDomain;
1930
        }
1931
        else if(err.domain == kCFStreamErrorDomainMacOSStatus) {
1932
                domain = NSOSStatusErrorDomain;
1933
        }
1934
        else if(err.domain == kCFStreamErrorDomainMach) {
1935
                domain = NSMachErrorDomain;
1936
        }
1937
        else if(err.domain == kCFStreamErrorDomainNetDB)
1938
        {
1939
                domain = @"kCFStreamErrorDomainNetDB";
1940
                message = [NSString stringWithCString:gai_strerror(err.error) encoding:NSASCIIStringEncoding];
1941
        }
1942
        else if(err.domain == kCFStreamErrorDomainNetServices) {
1943
                domain = @"kCFStreamErrorDomainNetServices";
1944
        }
1945
        else if(err.domain == kCFStreamErrorDomainSOCKS) {
1946
                domain = @"kCFStreamErrorDomainSOCKS";
1947
        }
1948
        else if(err.domain == kCFStreamErrorDomainSystemConfiguration) {
1949
                domain = @"kCFStreamErrorDomainSystemConfiguration";
1950
        }
1951
        else if(err.domain == kCFStreamErrorDomainSSL) {
1952
                domain = @"kCFStreamErrorDomainSSL";
1953
        }
1954
 
1955
        NSDictionary *info = nil;
1956
        if(message != nil)
1957
        {
1958
                info = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
1959
        }
1960
        return [NSError errorWithDomain:domain code:err.error userInfo:info];
1961
}
1962
 
1963
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1964
#pragma mark Diagnostics
1965
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1966
 
1967
- (BOOL)isConnected
1968
{
1969
        return [self isSocketConnected] && [self areStreamsConnected];
1970
}
1971
 
1972
- (NSString *)connectedHost
1973
{
1974
        if(theSocket4)
1975
                return [self connectedHost:theSocket4];
1976
        else
1977
                return [self connectedHost:theSocket6];
1978
}
1979
 
1980
- (UInt16)connectedPort
1981
{
1982
        if(theSocket4)
1983
                return [self connectedPort:theSocket4];
1984
        else
1985
                return [self connectedPort:theSocket6];
1986
}
1987
 
1988
- (NSString *)localHost
1989
{
1990
        if(theSocket4)
1991
                return [self localHost:theSocket4];
1992
        else
1993
                return [self localHost:theSocket6];
1994
}
1995
 
1996
- (UInt16)localPort
1997
{
1998
        if(theSocket4)
1999
                return [self localPort:theSocket4];
2000
        else
2001
                return [self localPort:theSocket6];
2002
}
2003
 
2004
- (NSString *)connectedHost:(CFSocketRef)theSocket
2005
{
2006
        if (theSocket == NULL) return nil;
2007
 
2008
        CFDataRef peeraddr;
2009
        NSString *peerstr = nil;
2010
 
2011
        if((peeraddr = CFSocketCopyPeerAddress(theSocket)))
2012
        {
2013
                peerstr = [self addressHost:peeraddr];
2014
                CFRelease (peeraddr);
2015
        }
2016
 
2017
        return peerstr;
2018
}
2019
 
2020
- (UInt16)connectedPort:(CFSocketRef)theSocket
2021
{
2022
        if (theSocket == NULL) return 0;
2023
 
2024
        CFDataRef peeraddr;
2025
        UInt16 peerport = 0;
2026
 
2027
        if((peeraddr = CFSocketCopyPeerAddress(theSocket)))
2028
        {
2029
                peerport = [self addressPort:peeraddr];
2030
                CFRelease (peeraddr);
2031
        }
2032
 
2033
        return peerport;
2034
}
2035
 
2036
- (NSString *)localHost:(CFSocketRef)theSocket
2037
{
2038
        if (theSocket == NULL) return nil;
2039
 
2040
        CFDataRef selfaddr;
2041
        NSString *selfstr = nil;
2042
 
2043
        if((selfaddr = CFSocketCopyAddress(theSocket)))
2044
        {
2045
                selfstr = [self addressHost:selfaddr];
2046
                CFRelease (selfaddr);
2047
        }
2048
 
2049
        return selfstr;
2050
}
2051
 
2052
- (UInt16)localPort:(CFSocketRef)theSocket
2053
{
2054
        if (theSocket == NULL) return 0;
2055
 
2056
        CFDataRef selfaddr;
2057
        UInt16 selfport = 0;
2058
 
2059
        if ((selfaddr = CFSocketCopyAddress(theSocket)))
2060
        {
2061
                selfport = [self addressPort:selfaddr];
2062
                CFRelease (selfaddr);
2063
        }
2064
 
2065
        return selfport;
2066
}
2067
 
2068
- (NSString *)addressHost:(CFDataRef)cfaddr
2069
{
2070
        if (cfaddr == NULL) return nil;
2071
 
2072
        char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
2073
        struct sockaddr *pSockAddr = (struct sockaddr *) CFDataGetBytePtr (cfaddr);
2074
        struct sockaddr_in  *pSockAddrV4 = (struct sockaddr_in  *)pSockAddr;
2075
        struct sockaddr_in6 *pSockAddrV6 = (struct sockaddr_in6 *)pSockAddr;
2076
 
2077
        const void *pAddr = (pSockAddr->sa_family == AF_INET) ?
2078
                                                        (void *)(&(pSockAddrV4->sin_addr)) :
2079
                                                        (void *)(&(pSockAddrV6->sin6_addr));
2080
 
2081
        const char *pStr = inet_ntop (pSockAddr->sa_family, pAddr, addrBuf, sizeof(addrBuf));
2082
        if (pStr == NULL) [NSException raise: NSInternalInconsistencyException
2083
                                                                  format: @"Cannot convert address to string."];
2084
 
2085
        return [NSString stringWithCString:pStr encoding:NSASCIIStringEncoding];
2086
}
2087
 
2088
- (UInt16)addressPort:(CFDataRef)cfaddr
2089
{
2090
        if (cfaddr == NULL) return 0;
2091
 
2092
        struct sockaddr_in *pAddr = (struct sockaddr_in *) CFDataGetBytePtr (cfaddr);
2093
        return ntohs (pAddr->sin_port);
2094
}
2095
 
2096
- (NSData *)connectedAddress
2097
{
2098
    CFSocketRef theSocket;
2099
 
2100
    if (theSocket4)
2101
        theSocket = theSocket4;
2102
    else
2103
        theSocket = theSocket6;
2104
 
2105
    if (theSocket == NULL) return nil;
2106
 
2107
        CFDataRef peeraddr = CFSocketCopyPeerAddress(theSocket);
2108
 
2109
    if (peeraddr == NULL) return nil;
2110
 
2111
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
2112
    NSData *result = [NSData dataWithBytes:CFDataGetBytePtr(peeraddr) length:CFDataGetLength(peeraddr)];
2113
    CFRelease(peeraddr);
2114
    return result;
2115
#else
2116
    return [(NSData *)NSMakeCollectable(peeraddr) autorelease];
2117
#endif
2118
}
2119
 
2120
- (NSData *)localAddress
2121
{
2122
    CFSocketRef theSocket;
2123
 
2124
    if (theSocket4)
2125
        theSocket = theSocket4;
2126
    else
2127
        theSocket = theSocket6;
2128
 
2129
    if (theSocket == NULL) return nil;
2130
 
2131
    CFDataRef selfaddr = CFSocketCopyAddress(theSocket);
2132
 
2133
    if (selfaddr == NULL) return nil;
2134
 
2135
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
2136
    NSData *result = [NSData dataWithBytes:CFDataGetBytePtr(selfaddr) length:CFDataGetLength(selfaddr)];
2137
    CFRelease(selfaddr);
2138
    return result;
2139
#else
2140
    return [(NSData *)NSMakeCollectable(selfaddr) autorelease];
2141
#endif
2142
}
2143
 
2144
- (BOOL)isIPv4
2145
{
2146
        return (theSocket4 != NULL);
2147
}
2148
 
2149
- (BOOL)isIPv6
2150
{
2151
        return (theSocket6 != NULL);
2152
}
2153
 
2154
- (BOOL)isSocketConnected
2155
{
2156
        if(theSocket4 != NULL)
2157
                return CFSocketIsValid(theSocket4);
2158
        else if(theSocket6 != NULL)
2159
                return CFSocketIsValid(theSocket6);
2160
        else
2161
                return NO;
2162
}
2163
 
2164
- (BOOL)areStreamsConnected
2165
{
2166
        CFStreamStatus s;
2167
 
2168
        if (theReadStream != NULL)
2169
        {
2170
                s = CFReadStreamGetStatus (theReadStream);
2171
                if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusReading || s == kCFStreamStatusError) )
2172
                        return NO;
2173
        }
2174
        else return NO;
2175
 
2176
        if (theWriteStream != NULL)
2177
        {
2178
                s = CFWriteStreamGetStatus (theWriteStream);
2179
                if ( !(s == kCFStreamStatusOpen || s == kCFStreamStatusWriting || s == kCFStreamStatusError) )
2180
                        return NO;
2181
        }
2182
        else return NO;
2183
 
2184
        return YES;
2185
}
2186
 
2187
- (NSString *)description
2188
{
2189
        static const char *statstr[] = {"not open","opening","open","reading","writing","at end","closed","has error"};
2190
        CFStreamStatus rs = (theReadStream != NULL) ? CFReadStreamGetStatus(theReadStream) : 0;
2191
        CFStreamStatus ws = (theWriteStream != NULL) ? CFWriteStreamGetStatus(theWriteStream) : 0;
2192
 
2193
        NSString *peerstr, *selfstr;
2194
        CFDataRef peeraddr4 = NULL, peeraddr6 = NULL, selfaddr4 = NULL, selfaddr6 = NULL;
2195
 
2196
        if (theSocket4 || theSocket6)
2197
        {
2198
                if (theSocket4) peeraddr4 = CFSocketCopyPeerAddress(theSocket4);
2199
                if (theSocket6) peeraddr6 = CFSocketCopyPeerAddress(theSocket6);
2200
 
2201
                if(theSocket4 && theSocket6)
2202
                {
2203
                        peerstr = [NSString stringWithFormat: @"%@/%@ %u", 
2204
                                           [self addressHost:peeraddr4], [self addressHost:peeraddr6], [self addressPort:peeraddr4]];
2205
                }
2206
                else if(theSocket4)
2207
                {
2208
                        peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr4], [self addressPort:peeraddr4]];
2209
                }
2210
                else
2211
                {
2212
                        peerstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:peeraddr6], [self addressPort:peeraddr6]];
2213
                }
2214
 
2215
                if(peeraddr4) CFRelease(peeraddr4);
2216
                if(peeraddr6) CFRelease(peeraddr6);
2217
                peeraddr4 = NULL;
2218
                peeraddr6 = NULL;
2219
        }
2220
        else peerstr = @"nowhere";
2221
 
2222
        if (theSocket4 || theSocket6)
2223
        {
2224
                if (theSocket4) selfaddr4 = CFSocketCopyAddress (theSocket4);
2225
                if (theSocket6) selfaddr6 = CFSocketCopyAddress (theSocket6);
2226
 
2227
                if (theSocket4 && theSocket6)
2228
                {
2229
                        selfstr = [NSString stringWithFormat: @"%@/%@ %u",
2230
                                           [self addressHost:selfaddr4], [self addressHost:selfaddr6], [self addressPort:selfaddr4]];
2231
                }
2232
                else if (theSocket4)
2233
                {
2234
                        selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr4], [self addressPort:selfaddr4]];
2235
                }
2236
                else
2237
                {
2238
                        selfstr = [NSString stringWithFormat: @"%@ %u", [self addressHost:selfaddr6], [self addressPort:selfaddr6]];
2239
                }
2240
 
2241
                if(selfaddr4) CFRelease(selfaddr4);
2242
                if(selfaddr6) CFRelease(selfaddr6);
2243
                selfaddr4 = NULL;
2244
                selfaddr6 = NULL;
2245
        }
2246
        else selfstr = @"nowhere";
2247
 
2248
        NSMutableString *ms = [[NSMutableString alloc] initWithCapacity:150];
2249
 
2250
        [ms appendString:[NSString stringWithFormat:@"<AsyncSocket %p", self]];
2251
        [ms appendString:[NSString stringWithFormat:@" local %@ remote %@ ", selfstr, peerstr]];
2252
 
2253
        unsigned readQueueCount  = (unsigned)[theReadQueue count];
2254
        unsigned writeQueueCount = (unsigned)[theWriteQueue count];
2255
 
2256
        [ms appendString:[NSString stringWithFormat:@"has queued %u reads %u writes, ", readQueueCount, writeQueueCount]];
2257
 
2258
        if (theCurrentRead == nil)
2259
                [ms appendString: @"no current read, "];
2260
        else
2261
        {
2262
                int percentDone;
2263
                if ([theCurrentRead->buffer length] != 0)
2264
                        percentDone = (float)theCurrentRead->bytesDone /
2265
                                                  (float)[theCurrentRead->buffer length] * 100.0F;
2266
                else
2267
                        percentDone = 100.0F;
2268
 
2269
                [ms appendString: [NSString stringWithFormat:@"currently read %u bytes (%d%% done), ",
2270
                        (unsigned int)[theCurrentRead->buffer length],
2271
                        theCurrentRead->bytesDone ? percentDone : 0]];
2272
        }
2273
 
2274
        if (theCurrentWrite == nil)
2275
                [ms appendString: @"no current write, "];
2276
        else
2277
        {
2278
                int percentDone;
2279
                if ([theCurrentWrite->buffer length] != 0)
2280
                        percentDone = (float)theCurrentWrite->bytesDone /
2281
                                                  (float)[theCurrentWrite->buffer length] * 100.0F;
2282
                else
2283
                        percentDone = 100.0F;
2284
 
2285
                [ms appendString: [NSString stringWithFormat:@"currently written %u (%d%%), ",
2286
                        (unsigned int)[theCurrentWrite->buffer length],
2287
                        theCurrentWrite->bytesDone ? percentDone : 0]];
2288
        }
2289
 
2290
        [ms appendString:[NSString stringWithFormat:@"read stream %p %s, ", theReadStream, statstr[rs]]];
2291
        [ms appendString:[NSString stringWithFormat:@"write stream %p %s", theWriteStream, statstr[ws]]];
2292
 
2293
        if(theFlags & kDisconnectAfterReads)
2294
        {
2295
                if(theFlags & kDisconnectAfterWrites)
2296
                        [ms appendString: @", will disconnect after reads & writes"];
2297
                else
2298
                        [ms appendString: @", will disconnect after reads"];
2299
        }
2300
        else if(theFlags & kDisconnectAfterWrites)
2301
        {
2302
                [ms appendString: @", will disconnect after writes"];
2303
        }
2304
 
2305
        if (![self isConnected]) [ms appendString: @", not connected"];
2306
 
2307
        [ms appendString:@">"];
2308
 
2309
        return [ms autorelease];
2310
}
2311
 
2312
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2313
#pragma mark Reading
2314
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2315
 
2316
- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag
2317
{
2318
        if(length == 0) return;
2319
        if(theFlags & kForbidReadsWrites) return;
2320
 
2321
        NSMutableData *buffer = [[NSMutableData alloc] initWithLength:length];
2322
        AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
2323
                                                                                                                        timeout:timeout
2324
                                                                                                                                tag:tag
2325
                                                                                                   readAllAvailable:NO
2326
                                                                                                                 terminator:nil
2327
                                                                                                                  maxLength:length];
2328
 
2329
        [theReadQueue addObject:packet];
2330
        [self scheduleDequeueRead];
2331
 
2332
        [packet release];
2333
        [buffer release];
2334
}
2335
 
2336
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
2337
{
2338
        [self readDataToData:data withTimeout:timeout maxLength:-1 tag:tag];
2339
}
2340
 
2341
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag
2342
{
2343
        if(data == nil || [data length] == 0) return;
2344
        if(length >= 0 && length < [data length]) return;
2345
        if(theFlags & kForbidReadsWrites) return;
2346
 
2347
        NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
2348
        AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
2349
                                                                                                                        timeout:timeout
2350
                                                                                                                                tag:tag
2351
                                                                                                   readAllAvailable:NO
2352
                                                                                                                 terminator:data
2353
                                                                                                                  maxLength:length];
2354
 
2355
        [theReadQueue addObject:packet];
2356
        [self scheduleDequeueRead];
2357
 
2358
        [packet release];
2359
        [buffer release];
2360
}
2361
 
2362
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
2363
{
2364
        if (theFlags & kForbidReadsWrites) return;
2365
 
2366
        NSMutableData *buffer = [[NSMutableData alloc] initWithLength:0];
2367
        AsyncReadPacket *packet = [[AsyncReadPacket alloc] initWithData:buffer
2368
                                                                                                                        timeout:timeout
2369
                                                                                                                                tag:tag
2370
                                                                                                   readAllAvailable:YES
2371
                                                                                                                 terminator:nil
2372
                                                                                                                  maxLength:-1];
2373
 
2374
        [theReadQueue addObject:packet];
2375
        [self scheduleDequeueRead];
2376
 
2377
        [packet release];
2378
        [buffer release];
2379
}
2380
 
2381
/**
2382
 * Puts a maybeDequeueRead on the run loop.
2383
 * An assumption here is that selectors will be performed consecutively within their priority.
2384
**/
2385
- (void)scheduleDequeueRead
2386
{
2387
        if((theFlags & kDequeueReadScheduled) == 0)
2388
        {
2389
                theFlags |= kDequeueReadScheduled;
2390
                [self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
2391
        }
2392
}
2393
 
2394
/**
2395
 * This method starts a new read, if needed.
2396
 * It is called when a user requests a read,
2397
 * or when a stream opens that may have requested reads sitting in the queue, etc.
2398
**/
2399
- (void)maybeDequeueRead
2400
{
2401
        // Unset the flag indicating a call to this method is scheduled
2402
        theFlags &= ~kDequeueReadScheduled;
2403
 
2404
        // If we're not currently processing a read AND we have an available read stream
2405
        if((theCurrentRead == nil) && (theReadStream != NULL))
2406
        {
2407
                if([theReadQueue count] > 0)
2408
                {
2409
                        // Dequeue the next object in the write queue
2410
                        theCurrentRead = [[theReadQueue objectAtIndex:0] retain];
2411
                        [theReadQueue removeObjectAtIndex:0];
2412
 
2413
                        if([theCurrentRead isKindOfClass:[AsyncSpecialPacket class]])
2414
                        {
2415
                                // Attempt to start TLS
2416
                                theFlags |= kStartingReadTLS;
2417
 
2418
                                // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are both set
2419
                                [self maybeStartTLS];
2420
                        }
2421
                        else
2422
                        {
2423
                                // Start time-out timer
2424
                                if(theCurrentRead->timeout >= 0.0)
2425
                                {
2426
                                        theReadTimer = [NSTimer timerWithTimeInterval:theCurrentRead->timeout
2427
                                                                                                                   target:self
2428
                                                                                                                 selector:@selector(doReadTimeout:)
2429
                                                                                                                 userInfo:nil
2430
                                                                                                                  repeats:NO];
2431
                                        [self runLoopAddTimer:theReadTimer];
2432
                                }
2433
 
2434
                                // Immediately read, if possible
2435
                                [self doBytesAvailable];
2436
                        }
2437
                }
2438
                else if(theFlags & kDisconnectAfterReads)
2439
                {
2440
                        if(theFlags & kDisconnectAfterWrites)
2441
                        {
2442
                                if(([theWriteQueue count] == 0) && (theCurrentWrite == nil))
2443
                                {
2444
                                        [self disconnect];
2445
                                }
2446
                        }
2447
                        else
2448
                        {
2449
                                [self disconnect];
2450
                        }
2451
                }
2452
        }
2453
}
2454
 
2455
/**
2456
 * Call this method in doBytesAvailable instead of CFReadStreamHasBytesAvailable().
2457
 * This method supports pre-buffering properly as well as the kSocketHasBytesAvailable flag.
2458
**/
2459
- (BOOL)hasBytesAvailable
2460
{
2461
        if ((theFlags & kSocketHasBytesAvailable) || ([partialReadBuffer length] > 0))
2462
        {
2463
                return YES;
2464
        }
2465
        else
2466
        {
2467
                return CFReadStreamHasBytesAvailable(theReadStream);
2468
        }
2469
}
2470
 
2471
/**
2472
 * Call this method in doBytesAvailable instead of CFReadStreamRead().
2473
 * This method support pre-buffering properly.
2474
**/
2475
- (CFIndex)readIntoBuffer:(UInt8 *)buffer maxLength:(CFIndex)length
2476
{
2477
        if([partialReadBuffer length] > 0)
2478
        {
2479
                // Determine the maximum amount of data to read
2480
                CFIndex bytesToRead = MIN(length, [partialReadBuffer length]);
2481
 
2482
                // Copy the bytes from the buffer
2483
                memcpy(buffer, [partialReadBuffer bytes], bytesToRead);
2484
 
2485
                // Remove the copied bytes from the buffer
2486
                [partialReadBuffer replaceBytesInRange:NSMakeRange(0, bytesToRead) withBytes:NULL length:0];
2487
 
2488
                return bytesToRead;
2489
        }
2490
        else
2491
        {
2492
                // Unset the "has-bytes-available" flag
2493
                theFlags &= ~kSocketHasBytesAvailable;
2494
 
2495
                return CFReadStreamRead(theReadStream, buffer, length);
2496
        }
2497
}
2498
 
2499
/**
2500
 * This method is called when a new read is taken from the read queue or when new data becomes available on the stream.
2501
**/
2502
- (void)doBytesAvailable
2503
{
2504
        // If data is available on the stream, but there is no read request, then we don't need to process the data yet.
2505
        // Also, if there is a read request, but no read stream setup yet, we can't process any data yet.
2506
        if((theCurrentRead != nil) && (theReadStream != NULL))
2507
        {
2508
                // Note: This method is not called if theCurrentRead is an AsyncSpecialPacket (startTLS packet)
2509
 
2510
                CFIndex totalBytesRead = 0;
2511
 
2512
                BOOL done = NO;
2513
                BOOL socketError = NO;
2514
                BOOL maxoutError = NO;
2515
 
2516
                while(!done && !socketError && !maxoutError && [self hasBytesAvailable])
2517
                {
2518
                        BOOL didPreBuffer = NO;
2519
 
2520
                        // There are 3 types of read packets:
2521
                        //
2522
                        // 1) Read a specific length of data.
2523
                        // 2) Read all available data.
2524
                        // 3) Read up to a particular terminator.
2525
 
2526
                        if(theCurrentRead->readAllAvailableData == YES)
2527
                        {
2528
                                // We're reading all available data.
2529
                                //
2530
                                // Make sure there is at least READALL_CHUNKSIZE bytes available.
2531
                                // We don't want to increase the buffer any more than this or we'll waste space.
2532
                                // With prebuffering it's possible to read in a small chunk on the first read.
2533
 
2534
                                unsigned buffInc = READALL_CHUNKSIZE - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
2535
                                [theCurrentRead->buffer increaseLengthBy:buffInc];
2536
                        }
2537
                        else if(theCurrentRead->term != nil)
2538
                        {
2539
                                // We're reading up to a terminator.
2540
                                //
2541
                                // We may only want to read a few bytes.
2542
                                // Just enough to ensure we don't go past our term or over our max limit.
2543
                                // Unless pre-buffering is enabled, in which case we may want to read in a larger chunk.
2544
 
2545
                                // If we already have data pre-buffered, we obviously don't want to pre-buffer it again.
2546
                                // So in this case we'll just read as usual.
2547
 
2548
                                if(([partialReadBuffer length] > 0) || !(theFlags & kEnablePreBuffering))
2549
                                {
2550
                                        unsigned maxToRead = [theCurrentRead readLengthForTerm];
2551
 
2552
                                        unsigned bufInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
2553
                                        [theCurrentRead->buffer increaseLengthBy:bufInc];
2554
                                }
2555
                                else
2556
                                {
2557
                                        didPreBuffer = YES;
2558
                                        unsigned maxToRead = [theCurrentRead prebufferReadLengthForTerm];
2559
 
2560
                                        unsigned buffInc = maxToRead - ([theCurrentRead->buffer length] - theCurrentRead->bytesDone);
2561
                                        [theCurrentRead->buffer increaseLengthBy:buffInc];
2562
 
2563
                                }
2564
                        }
2565
 
2566
                        // Number of bytes to read is space left in packet buffer.
2567
                        CFIndex bytesToRead = [theCurrentRead->buffer length] - theCurrentRead->bytesDone;
2568
 
2569
                        // Read data into packet buffer
2570
                        UInt8 *subBuffer = (UInt8 *)([theCurrentRead->buffer mutableBytes] + theCurrentRead->bytesDone);
2571
                        CFIndex bytesRead = [self readIntoBuffer:subBuffer maxLength:bytesToRead];
2572
 
2573
                        // Check results
2574
                        if(bytesRead < 0)
2575
                        {
2576
                                socketError = YES;
2577
                        }
2578
                        else
2579
                        {
2580
                                // Update total amount read for the current read
2581
                                theCurrentRead->bytesDone += bytesRead;
2582
 
2583
                                // Update total amount read in this method invocation
2584
                                totalBytesRead += bytesRead;
2585
                        }
2586
 
2587
                        // Is packet done?
2588
                        if(theCurrentRead->readAllAvailableData != YES)
2589
                        {
2590
                                if(theCurrentRead->term != nil)
2591
                                {
2592
                                        if(didPreBuffer)
2593
                                        {
2594
                                                // Search for the terminating sequence within the big chunk we just read.
2595
                                                CFIndex overflow = [theCurrentRead searchForTermAfterPreBuffering:bytesRead];
2596
 
2597
                                                if(overflow > 0)
2598
                                                {
2599
                                                        // Copy excess data into partialReadBuffer
2600
                                                        NSMutableData *buffer = theCurrentRead->buffer;
2601
                                                        const void *overflowBuffer = [buffer bytes] + theCurrentRead->bytesDone - overflow;
2602
 
2603
                                                        [partialReadBuffer appendBytes:overflowBuffer length:overflow];
2604
 
2605
                                                        // Update the bytesDone variable.
2606
                                                        // Note: The completeCurrentRead method will trim the buffer for us.
2607
                                                        theCurrentRead->bytesDone -= overflow;
2608
                                                }
2609
 
2610
                                                done = (overflow >= 0);
2611
                                        }
2612
                                        else
2613
                                        {
2614
                                                // Search for the terminating sequence at the end of the buffer
2615
                                                int termlen = [theCurrentRead->term length];
2616
                                                if(theCurrentRead->bytesDone >= termlen)
2617
                                                {
2618
                                                        const void *buf = [theCurrentRead->buffer bytes] + (theCurrentRead->bytesDone - termlen);
2619
                                                        const void *seq = [theCurrentRead->term bytes];
2620
                                                        done = (memcmp (buf, seq, termlen) == 0);
2621
                                                }
2622
                                        }
2623
 
2624
                                        if(!done && theCurrentRead->maxLength >= 0 && theCurrentRead->bytesDone >= theCurrentRead->maxLength)
2625
                                        {
2626
                                                // There's a set maxLength, and we've reached that maxLength without completing the read
2627
                                                maxoutError = YES;
2628
                                        }
2629
                                }
2630
                                else
2631
                                {
2632
                                        // Done when (sized) buffer is full.
2633
                                        done = ([theCurrentRead->buffer length] == theCurrentRead->bytesDone);
2634
                                }
2635
                        }
2636
                        // else readAllAvailable doesn't end until all readable is read.
2637
                }
2638
 
2639
                if(theCurrentRead->readAllAvailableData && theCurrentRead->bytesDone > 0)
2640
                {
2641
                        // Ran out of bytes, so the "read-all-available-data" type packet is done
2642
                        done = YES;
2643
                }
2644
 
2645
                if(done)
2646
                {
2647
                        [self completeCurrentRead];
2648
                        if (!socketError) [self scheduleDequeueRead];
2649
                }
2650
                else if(totalBytesRead > 0)
2651
                {
2652
                        // We're not done with the readToLength or readToData yet, but we have read in some bytes
2653
                        if ([theDelegate respondsToSelector:@selector(onSocket:didReadPartialDataOfLength:tag:)])
2654
                        {
2655
                                [theDelegate onSocket:self didReadPartialDataOfLength:totalBytesRead tag:theCurrentRead->tag];
2656
                        }
2657
                }
2658
 
2659
                if(socketError)
2660
                {
2661
                        CFStreamError err = CFReadStreamGetError(theReadStream);
2662
                        [self closeWithError:[self errorFromCFStreamError:err]];
2663
                        return;
2664
                }
2665
                if(maxoutError)
2666
                {
2667
                        [self closeWithError:[self getReadMaxedOutError]];
2668
                        return;
2669
                }
2670
        }
2671
}
2672
 
2673
// Ends current read and calls delegate.
2674
- (void)completeCurrentRead
2675
{
2676
        NSAssert(theCurrentRead, @"Trying to complete current read when there is no current read.");
2677
 
2678
        [theCurrentRead->buffer setLength:theCurrentRead->bytesDone];
2679
        if([theDelegate respondsToSelector:@selector(onSocket:didReadData:withTag:)])
2680
        {
2681
                [theDelegate onSocket:self didReadData:theCurrentRead->buffer withTag:theCurrentRead->tag];
2682
        }
2683
 
2684
        if (theCurrentRead != nil) [self endCurrentRead]; // Caller may have disconnected.
2685
}
2686
 
2687
// Ends current read.
2688
- (void)endCurrentRead
2689
{
2690
        NSAssert(theCurrentRead, @"Trying to end current read when there is no current read.");
2691
 
2692
        [theReadTimer invalidate];
2693
        theReadTimer = nil;
2694
 
2695
        [theCurrentRead release];
2696
        theCurrentRead = nil;
2697
}
2698
 
2699
- (void)doReadTimeout:(NSTimer *)timer
2700
{
2701
        NSTimeInterval timeoutExtension = 0.0;
2702
 
2703
        if([theDelegate respondsToSelector:@selector(onSocket:shouldTimeoutReadWithTag:elapsed:bytesDone:)])
2704
        {
2705
                timeoutExtension = [theDelegate onSocket:self shouldTimeoutReadWithTag:theCurrentRead->tag
2706
                                                                               elapsed:theCurrentRead->timeout
2707
                                                                             bytesDone:theCurrentRead->bytesDone];
2708
        }
2709
 
2710
        if(timeoutExtension > 0.0)
2711
        {
2712
                theCurrentRead->timeout += timeoutExtension;
2713
 
2714
                theReadTimer = [NSTimer timerWithTimeInterval:timeoutExtension
2715
                                                                                           target:self
2716
                                                                                         selector:@selector(doReadTimeout:)
2717
                                                                                         userInfo:nil
2718
                                                                                          repeats:NO];
2719
                [self runLoopAddTimer:theReadTimer];
2720
        }
2721
        else
2722
        {
2723
                // Do not call endCurrentRead here.
2724
                // We must allow the delegate access to any partial read in the unreadData method.
2725
 
2726
                [self closeWithError:[self getReadTimeoutError]];
2727
        }
2728
}
2729
 
2730
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2731
#pragma mark Writing
2732
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2733
 
2734
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
2735
{
2736
        if (data == nil || [data length] == 0) return;
2737
        if (theFlags & kForbidReadsWrites) return;
2738
 
2739
        AsyncWritePacket *packet = [[AsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];
2740
 
2741
        [theWriteQueue addObject:packet];
2742
        [self scheduleDequeueWrite];
2743
 
2744
        [packet release];
2745
}
2746
 
2747
- (void)scheduleDequeueWrite
2748
{
2749
        if((theFlags & kDequeueWriteScheduled) == 0)
2750
        {
2751
                theFlags |= kDequeueWriteScheduled;
2752
                [self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
2753
        }
2754
}
2755
 
2756
/**
2757
 * Conditionally starts a new write.
2758
 *
2759
 * IF there is not another write in process
2760
 * AND there is a write queued
2761
 * AND we have a write stream available
2762
 *
2763
 * This method also handles auto-disconnect post read/write completion.
2764
**/
2765
- (void)maybeDequeueWrite
2766
{
2767
        // Unset the flag indicating a call to this method is scheduled
2768
        theFlags &= ~kDequeueWriteScheduled;
2769
 
2770
        // If we're not currently processing a write AND we have an available write stream
2771
        if((theCurrentWrite == nil) && (theWriteStream != NULL))
2772
        {
2773
                if([theWriteQueue count] > 0)
2774
                {
2775
                        // Dequeue the next object in the write queue
2776
                        theCurrentWrite = [[theWriteQueue objectAtIndex:0] retain];
2777
                        [theWriteQueue removeObjectAtIndex:0];
2778
 
2779
                        if([theCurrentWrite isKindOfClass:[AsyncSpecialPacket class]])
2780
                        {
2781
                                // Attempt to start TLS
2782
                                theFlags |= kStartingWriteTLS;
2783
 
2784
                                // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are both set
2785
                                [self maybeStartTLS];
2786
                        }
2787
                        else
2788
                        {
2789
                                // Start time-out timer
2790
                                if(theCurrentWrite->timeout >= 0.0)
2791
                                {
2792
                                        theWriteTimer = [NSTimer timerWithTimeInterval:theCurrentWrite->timeout
2793
                                                                                                                        target:self
2794
                                                                                                                  selector:@selector(doWriteTimeout:)
2795
                                                                                                                  userInfo:nil
2796
                                                                                                                   repeats:NO];
2797
                                        [self runLoopAddTimer:theWriteTimer];
2798
                                }
2799
 
2800
                                // Immediately write, if possible
2801
                                [self doSendBytes];
2802
                        }
2803
                }
2804
                else if(theFlags & kDisconnectAfterWrites)
2805
                {
2806
                        if(theFlags & kDisconnectAfterReads)
2807
                        {
2808
                                if(([theReadQueue count] == 0) && (theCurrentRead == nil))
2809
                                {
2810
                                        [self disconnect];
2811
                                }
2812
                        }
2813
                        else
2814
                        {
2815
                                [self disconnect];
2816
                        }
2817
                }
2818
        }
2819
}
2820
 
2821
/**
2822
 * Call this method in doSendBytes instead of CFWriteStreamCanAcceptBytes().
2823
 * This method supports the kSocketCanAcceptBytes flag.
2824
**/
2825
- (BOOL)canAcceptBytes
2826
{
2827
        if (theFlags & kSocketCanAcceptBytes)
2828
        {
2829
                return YES;
2830
        }
2831
        else
2832
        {
2833
                return CFWriteStreamCanAcceptBytes(theWriteStream);
2834
        }
2835
}
2836
 
2837
- (void)doSendBytes
2838
{
2839
        if((theCurrentWrite != nil) && (theWriteStream != NULL))
2840
        {
2841
                // Note: This method is not called if theCurrentWrite is an AsyncSpecialPacket (startTLS packet)
2842
 
2843
                CFIndex totalBytesWritten = 0;
2844
 
2845
                BOOL done = NO;
2846
                BOOL error = NO;
2847
 
2848
                while (!done && !error && [self canAcceptBytes])
2849
                {
2850
                        // Figure out what to write.
2851
                        CFIndex bytesRemaining = [theCurrentWrite->buffer length] - theCurrentWrite->bytesDone;
2852
                        CFIndex bytesToWrite = (bytesRemaining < WRITE_CHUNKSIZE) ? bytesRemaining : WRITE_CHUNKSIZE;
2853
                        UInt8 *writestart = (UInt8 *)([theCurrentWrite->buffer bytes] + theCurrentWrite->bytesDone);
2854
 
2855
                        // Write.
2856
                        CFIndex bytesWritten = CFWriteStreamWrite(theWriteStream, writestart, bytesToWrite);
2857
 
2858
                        // Unset the "can accept bytes" flag
2859
                        theFlags &= ~kSocketCanAcceptBytes;
2860
 
2861
                        // Check results
2862
                        if (bytesWritten < 0)
2863
                        {
2864
                                error = YES;
2865
                        }
2866
                        else
2867
                        {
2868
                                // Update total amount read for the current write
2869
                                theCurrentWrite->bytesDone += bytesWritten;
2870
 
2871
                                // Update total amount written in this method invocation
2872
                                totalBytesWritten += bytesWritten;
2873
 
2874
                                // Is packet done?
2875
                                done = ([theCurrentWrite->buffer length] == theCurrentWrite->bytesDone);
2876
                        }
2877
                }
2878
 
2879
                if(done)
2880
                {
2881
                        [self completeCurrentWrite];
2882
                        [self scheduleDequeueWrite];
2883
                }
2884
                else if(error)
2885
                {
2886
                        CFStreamError err = CFWriteStreamGetError(theWriteStream);
2887
                        [self closeWithError:[self errorFromCFStreamError:err]];
2888
                        return;
2889
                }
2890
                else
2891
                {
2892
                        // We're not done with the entire write, but we have written some bytes
2893
                        if ([theDelegate respondsToSelector:@selector(onSocket:didWritePartialDataOfLength:tag:)])
2894
                        {
2895
                                [theDelegate onSocket:self didWritePartialDataOfLength:totalBytesWritten tag:theCurrentWrite->tag];
2896
                        }
2897
                }
2898
        }
2899
}
2900
 
2901
// Ends current write and calls delegate.
2902
- (void)completeCurrentWrite
2903
{
2904
        NSAssert(theCurrentWrite, @"Trying to complete current write when there is no current write.");
2905
 
2906
        if ([theDelegate respondsToSelector:@selector(onSocket:didWriteDataWithTag:)])
2907
        {
2908
                [theDelegate onSocket:self didWriteDataWithTag:theCurrentWrite->tag];
2909
        }
2910
 
2911
        if (theCurrentWrite != nil) [self endCurrentWrite]; // Caller may have disconnected.
2912
}
2913
 
2914
// Ends current write.
2915
- (void)endCurrentWrite
2916
{
2917
        NSAssert(theCurrentWrite, @"Trying to complete current write when there is no current write.");
2918
 
2919
        [theWriteTimer invalidate];
2920
        theWriteTimer = nil;
2921
 
2922
        [theCurrentWrite release];
2923
        theCurrentWrite = nil;
2924
}
2925
 
2926
- (void)doWriteTimeout:(NSTimer *)timer
2927
{
2928
        NSTimeInterval timeoutExtension = 0.0;
2929
 
2930
        if([theDelegate respondsToSelector:@selector(onSocket:shouldTimeoutWriteWithTag:elapsed:bytesDone:)])
2931
        {
2932
                timeoutExtension = [theDelegate onSocket:self shouldTimeoutWriteWithTag:theCurrentWrite->tag
2933
                                                                                elapsed:theCurrentWrite->timeout
2934
                                                                              bytesDone:theCurrentWrite->bytesDone];
2935
        }
2936
 
2937
        if(timeoutExtension > 0.0)
2938
        {
2939
                theCurrentWrite->timeout += timeoutExtension;
2940
 
2941
                theWriteTimer = [NSTimer timerWithTimeInterval:timeoutExtension
2942
                                                        target:self
2943
                                                      selector:@selector(doWriteTimeout:)
2944
                                                      userInfo:nil
2945
                                                       repeats:NO];
2946
                [self runLoopAddTimer:theWriteTimer];
2947
        }
2948
        else
2949
        {
2950
                [self closeWithError:[self getWriteTimeoutError]];
2951
        }
2952
}
2953
 
2954
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2955
#pragma mark Security
2956
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2957
 
2958
- (void)startTLS:(NSDictionary *)tlsSettings
2959
{
2960
        if(tlsSettings == nil)
2961
    {
2962
        // Passing nil/NULL to CFReadStreamSetProperty will appear to work the same as passing an empty dictionary,
2963
        // but causes problems if we later try to fetch the remote host's certificate.
2964
        //
2965
        // To be exact, it causes the following to return NULL instead of the normal result:
2966
        // CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates)
2967
        //
2968
        // So we use an empty dictionary instead, which works perfectly.
2969
 
2970
        tlsSettings = [NSDictionary dictionary];
2971
    }
2972
 
2973
        AsyncSpecialPacket *packet = [[AsyncSpecialPacket alloc] initWithTLSSettings:tlsSettings];
2974
 
2975
        [theReadQueue addObject:packet];
2976
        [self scheduleDequeueRead];
2977
 
2978
        [theWriteQueue addObject:packet];
2979
        [self scheduleDequeueWrite];
2980
 
2981
        [packet release];
2982
}
2983
 
2984
- (void)maybeStartTLS
2985
{
2986
        // We can't start TLS until:
2987
        // - All queued reads prior to the user calling StartTLS are complete
2988
        // - All queued writes prior to the user calling StartTLS are complete
2989
        //
2990
        // We'll know these conditions are met when both kStartingReadTLS and kStartingWriteTLS are set
2991
 
2992
        if((theFlags & kStartingReadTLS) && (theFlags & kStartingWriteTLS))
2993
        {
2994
                AsyncSpecialPacket *tlsPacket = (AsyncSpecialPacket *)theCurrentRead;
2995
 
2996
                BOOL didStartOnReadStream = CFReadStreamSetProperty(theReadStream, kCFStreamPropertySSLSettings,
2997
                                                                                                                   (CFDictionaryRef)tlsPacket->tlsSettings);
2998
                BOOL didStartOnWriteStream = CFWriteStreamSetProperty(theWriteStream, kCFStreamPropertySSLSettings,
2999
                                                                                                                         (CFDictionaryRef)tlsPacket->tlsSettings);
3000
 
3001
                if(!didStartOnReadStream || !didStartOnWriteStream)
3002
                {
3003
            [self closeWithError:[self getSocketError]];
3004
                }
3005
        }
3006
}
3007
 
3008
- (void)onTLSHandshakeSuccessful
3009
{
3010
        if((theFlags & kStartingReadTLS) && (theFlags & kStartingWriteTLS))
3011
        {
3012
                theFlags &= ~kStartingReadTLS;
3013
                theFlags &= ~kStartingWriteTLS;
3014
 
3015
                if([theDelegate respondsToSelector:@selector(onSocketDidSecure:)])
3016
                {
3017
                        [theDelegate onSocketDidSecure:self];
3018
                }
3019
 
3020
                [self endCurrentRead];
3021
                [self endCurrentWrite];
3022
 
3023
                [self scheduleDequeueRead];
3024
                [self scheduleDequeueWrite];
3025
        }
3026
}
3027
 
3028
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3029
#pragma mark CF Callbacks
3030
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3031
 
3032
- (void)doCFSocketCallback:(CFSocketCallBackType)type
3033
                                 forSocket:(CFSocketRef)sock
3034
                           withAddress:(NSData *)address
3035
                                  withData:(const void *)pData
3036
{
3037
        NSParameterAssert ((sock == theSocket4) || (sock == theSocket6));
3038
 
3039
        switch (type)
3040
        {
3041
                case kCFSocketConnectCallBack:
3042
                        // The data argument is either NULL or a pointer to an SInt32 error code, if the connect failed.
3043
                        if(pData)
3044
                                [self doSocketOpen:sock withCFSocketError:kCFSocketError];
3045
                        else
3046
                                [self doSocketOpen:sock withCFSocketError:kCFSocketSuccess];
3047
                        break;
3048
                case kCFSocketAcceptCallBack:
3049
                        [self doAcceptWithSocket: *((CFSocketNativeHandle *)pData)];
3050
                        break;
3051
                default:
3052
                        NSLog (@"AsyncSocket %p received unexpected CFSocketCallBackType %d.", self, type);
3053
                        break;
3054
        }
3055
}
3056
 
3057
- (void)doCFReadStreamCallback:(CFStreamEventType)type forStream:(CFReadStreamRef)stream
3058
{
3059
        NSParameterAssert(theReadStream != NULL);
3060
 
3061
        CFStreamError err;
3062
        switch (type)
3063
        {
3064
                case kCFStreamEventOpenCompleted:
3065
                        theFlags |= kDidCompleteOpenForRead;
3066
                        [self doStreamOpen];
3067
                        break;
3068
                case kCFStreamEventHasBytesAvailable:
3069
                        if(theFlags & kStartingReadTLS) {
3070
                                [self onTLSHandshakeSuccessful];
3071
                        }
3072
                        else {
3073
                                theFlags |= kSocketHasBytesAvailable;
3074
                                [self doBytesAvailable];
3075
                        }
3076
                        break;
3077
                case kCFStreamEventErrorOccurred:
3078
                case kCFStreamEventEndEncountered:
3079
                        err = CFReadStreamGetError (theReadStream);
3080
                        [self closeWithError: [self errorFromCFStreamError:err]];
3081
                        break;
3082
                default:
3083
                        NSLog (@"AsyncSocket %p received unexpected CFReadStream callback, CFStreamEventType %d.", self, type);
3084
        }
3085
}
3086
 
3087
- (void)doCFWriteStreamCallback:(CFStreamEventType)type forStream:(CFWriteStreamRef)stream
3088
{
3089
        NSParameterAssert(theWriteStream != NULL);
3090
 
3091
        CFStreamError err;
3092
        switch (type)
3093
        {
3094
                case kCFStreamEventOpenCompleted:
3095
                        theFlags |= kDidCompleteOpenForWrite;
3096
                        [self doStreamOpen];
3097
                        break;
3098
                case kCFStreamEventCanAcceptBytes:
3099
                        if(theFlags & kStartingWriteTLS) {
3100
                                [self onTLSHandshakeSuccessful];
3101
                        }
3102
                        else {
3103
                                theFlags |= kSocketCanAcceptBytes;
3104
                                [self doSendBytes];
3105
                        }
3106
                        break;
3107
                case kCFStreamEventErrorOccurred:
3108
                case kCFStreamEventEndEncountered:
3109
                        err = CFWriteStreamGetError (theWriteStream);
3110
                        [self closeWithError: [self errorFromCFStreamError:err]];
3111
                        break;
3112
                default:
3113
                        NSLog (@"AsyncSocket %p received unexpected CFWriteStream callback, CFStreamEventType %d.", self, type);
3114
        }
3115
}
3116
 
3117
/**
3118
 * This is the callback we setup for CFSocket.
3119
 * This method does nothing but forward the call to it's Objective-C counterpart
3120
**/
3121
static void MyCFSocketCallback (CFSocketRef sref, CFSocketCallBackType type, CFDataRef address, const void *pData, void *pInfo)
3122
{
3123
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3124
 
3125
        AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
3126
        [theSocket doCFSocketCallback:type forSocket:sref withAddress:(NSData *)address withData:pData];
3127
 
3128
        [pool release];
3129
}
3130
 
3131
/**
3132
 * This is the callback we setup for CFReadStream.
3133
 * This method does nothing but forward the call to it's Objective-C counterpart
3134
**/
3135
static void MyCFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo)
3136
{
3137
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3138
 
3139
        AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
3140
        [theSocket doCFReadStreamCallback:type forStream:stream];
3141
 
3142
        [pool release];
3143
}
3144
 
3145
/**
3146
 * This is the callback we setup for CFWriteStream.
3147
 * This method does nothing but forward the call to it's Objective-C counterpart
3148
**/
3149
static void MyCFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo)
3150
{
3151
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3152
 
3153
        AsyncSocket *theSocket = [[(AsyncSocket *)pInfo retain] autorelease];
3154
        [theSocket doCFWriteStreamCallback:type forStream:stream];
3155
 
3156
        [pool release];
3157
}
3158
 
3159
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3160
#pragma mark Class Methods
3161
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3162
 
3163
// Return line separators.
3164
+ (NSData *)CRLFData
3165
{
3166
        return [NSData dataWithBytes:"\x0D\x0A" length:2];
3167
}
3168
 
3169
+ (NSData *)CRData
3170
{
3171
        return [NSData dataWithBytes:"\x0D" length:1];
3172
}
3173
 
3174
+ (NSData *)LFData
3175
{
3176
        return [NSData dataWithBytes:"\x0A" length:1];
3177
}
3178
 
3179
+ (NSData *)ZeroData
3180
{
3181
        return [NSData dataWithBytes:"" length:1];
3182
}
3183
 
3184
@end