@onekeyfe/react-native-ping 1.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,944 @@
1
+ //
2
+ // GBPing.m
3
+ //
4
+
5
+ #import "GBPing.h"
6
+ #import "LHDefinition.h"
7
+
8
+ #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
9
+ #import <CFNetwork/CFNetwork.h>
10
+ #else
11
+ #import <CoreServices/CoreServices.h>
12
+ #endif
13
+
14
+ #import "ICMPHeader.h"
15
+
16
+ #include <sys/socket.h>
17
+ #include <netinet/in.h>
18
+
19
+ #include <stdio.h>
20
+ #include <stdlib.h>
21
+ #include <unistd.h>
22
+ #include <errno.h>
23
+ #include <string.h>
24
+ #include <sys/types.h>
25
+ #include <arpa/inet.h>
26
+ #include <netdb.h>
27
+
28
+ static NSTimeInterval const kPendingPingsCleanupGrace = 1.0;
29
+
30
+ static NSUInteger const kDefaultPayloadSize = 56;
31
+ static NSUInteger const kDefaultTTL = 49;
32
+ static NSTimeInterval const kDefaultPingPeriod = 1.0;
33
+ static NSTimeInterval const kDefaultTimeout = 2.0;
34
+
35
+ @interface GBPing ()
36
+
37
+ @property (assign, atomic) int socket;
38
+ @property (strong, nonatomic) NSData *hostAddress;
39
+ @property (strong, nonatomic) NSString *hostAddressString;
40
+ @property (assign, nonatomic) uint16_t identifier;
41
+
42
+ @property (assign, atomic, readwrite) BOOL isPinging;
43
+ @property (assign, atomic, readwrite) BOOL isReady;
44
+ @property (assign, nonatomic) NSUInteger nextSequenceNumber;
45
+ @property (strong, atomic) NSMutableDictionary *pendingPings;
46
+ @property (strong, nonatomic) NSMutableDictionary *timeoutTimers;
47
+
48
+ @property (strong, nonatomic) dispatch_queue_t setupQueue;
49
+
50
+ @property (assign, atomic) BOOL isStopped;
51
+
52
+ @property (copy, atomic) void (^ successBlock)(GBPingSummary *summary);
53
+ @property (copy, atomic) void (^ failBlock)(NSError *summary);
54
+
55
+ @end
56
+
57
+ @implementation GBPing
58
+ {
59
+ NSUInteger _payloadSize;
60
+ NSUInteger _ttl;
61
+ NSTimeInterval _timeout;
62
+ NSTimeInterval _pingPeriod;
63
+ }
64
+
65
+ #pragma mark - custom acc
66
+
67
+ - (void)setTimeout:(NSTimeInterval)timeout
68
+ {
69
+ @synchronized(self) {
70
+ if (self.isPinging) {
71
+ if (self.debug) {
72
+ NSLog(@"GBPing: can't set timeout while pinger is running.");
73
+ }
74
+ } else {
75
+ _timeout = timeout;
76
+ }
77
+ }
78
+ }
79
+
80
+ - (NSTimeInterval)timeout
81
+ {
82
+ @synchronized(self) {
83
+ if (!_timeout) {
84
+ return kDefaultTimeout;
85
+ } else {
86
+ return _timeout;
87
+ }
88
+ }
89
+ }
90
+
91
+ - (void)setTtl:(NSUInteger)ttl
92
+ {
93
+ @synchronized(self) {
94
+ if (self.isPinging) {
95
+ if (self.debug) {
96
+ NSLog(@"GBPing: can't set ttl while pinger is running.");
97
+ }
98
+ } else {
99
+ _ttl = ttl;
100
+ }
101
+ }
102
+ }
103
+
104
+ - (NSUInteger)ttl
105
+ {
106
+ @synchronized(self) {
107
+ if (!_ttl) {
108
+ return kDefaultTTL;
109
+ } else {
110
+ return _ttl;
111
+ }
112
+ }
113
+ }
114
+
115
+ - (void)setPayloadSize:(NSUInteger)payloadSize
116
+ {
117
+ @synchronized(self) {
118
+ if (self.isPinging) {
119
+ if (self.debug) {
120
+ NSLog(@"GBPing: can't set payload size while pinger is running.");
121
+ }
122
+ } else {
123
+ _payloadSize = payloadSize;
124
+ }
125
+ }
126
+ }
127
+
128
+ - (NSUInteger)payloadSize
129
+ {
130
+ @synchronized(self) {
131
+ if (!_payloadSize) {
132
+ return kDefaultPayloadSize;
133
+ } else {
134
+ return _payloadSize;
135
+ }
136
+ }
137
+ }
138
+
139
+ - (void)setPingPeriod:(NSTimeInterval)pingPeriod
140
+ {
141
+ @synchronized(self) {
142
+ if (self.isPinging) {
143
+ if (self.debug) {
144
+ NSLog(@"GBPing: can't set pingPeriod while pinger is running.");
145
+ }
146
+ } else {
147
+ _pingPeriod = pingPeriod;
148
+ }
149
+ }
150
+ }
151
+
152
+ - (NSTimeInterval)pingPeriod
153
+ {
154
+ @synchronized(self) {
155
+ if (!_pingPeriod) {
156
+ return (NSTimeInterval)kDefaultPingPeriod;
157
+ } else {
158
+ return _pingPeriod;
159
+ }
160
+ }
161
+ }
162
+
163
+ #pragma mark - core pinging methods
164
+
165
+ - (void)setupWithBlock:(StartupCallback)callback
166
+ {
167
+ //error out of its already setup
168
+ if (self.isReady) {
169
+ if (self.debug) {
170
+ NSLog(@"GBPing: Can't setup, already setup.");
171
+ }
172
+
173
+ //notify about error and return
174
+ dispatch_async(dispatch_get_main_queue(), ^{
175
+ DEFINE_NSError(runningError, PingUtil_Message_PreviousPingIsStillRunning)
176
+ callback(NO, runningError);
177
+ });
178
+ return;
179
+ }
180
+
181
+ //error out if no host is set
182
+ if (!self.host) {
183
+ if (self.debug) {
184
+ NSLog(@"GBPing: set host before attempting to start.");
185
+ }
186
+
187
+ //notify about error and return
188
+ dispatch_async(dispatch_get_main_queue(), ^{
189
+ DEFINE_NSError(hostError, PingUtil_Message_HostErrorNotSetHost)
190
+ callback(NO, hostError);
191
+ });
192
+ return;
193
+ }
194
+
195
+ //set up data structs
196
+ self.nextSequenceNumber = 0;
197
+ self.pendingPings = [[NSMutableDictionary alloc] init];
198
+ self.timeoutTimers = [[NSMutableDictionary alloc] init];
199
+
200
+ dispatch_async(self.setupQueue, ^{
201
+ CFStreamError streamError;
202
+ BOOL success;
203
+
204
+ CFHostRef hostRef = CFHostCreateWithName(NULL, (__bridge CFStringRef)self.host);
205
+
206
+ /*
207
+ * CFHostCreateWithName will return a null result in certain cases.
208
+ * CFHostStartInfoResolution will return YES if _hostRef is null.
209
+ */
210
+ if (hostRef) {
211
+ success = CFHostStartInfoResolution(hostRef, kCFHostAddresses, &streamError);
212
+ } else {
213
+ success = NO;
214
+ }
215
+
216
+ if (!success) {
217
+ //construct an error
218
+ NSDictionary *userInfo;
219
+ NSError *error;
220
+
221
+ if (hostRef && streamError.domain == kCFStreamErrorDomainNetDB) {
222
+ userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
223
+ [NSNumber numberWithInteger:streamError.error], kCFGetAddrInfoFailureKey,
224
+ nil
225
+ ];
226
+ } else {
227
+ userInfo = nil;
228
+ }
229
+ error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorUnknown userInfo:userInfo];
230
+
231
+ //clean up so far
232
+ [self stop];
233
+
234
+ //notify about error and return
235
+ dispatch_async(dispatch_get_main_queue(), ^{
236
+ DEFINE_NSError(hostUnknowError, PingUtil_Message_HostErrorUnknown)
237
+ callback(NO, hostUnknowError);
238
+ });
239
+
240
+ //just incase
241
+ if (hostRef) {
242
+ CFRelease(hostRef);
243
+ }
244
+ return;
245
+ }
246
+
247
+ //get the first IPv4 or IPv6 address
248
+ Boolean resolved;
249
+ NSArray *addresses = (__bridge NSArray *)CFHostGetAddressing(hostRef, &resolved);
250
+ if (resolved && (addresses != nil)) {
251
+ resolved = false;
252
+ for (NSData *address in addresses) {
253
+ const struct sockaddr *anAddrPtr = (const struct sockaddr *)[address bytes];
254
+
255
+ if ([address length] >= sizeof(struct sockaddr) &&
256
+ (anAddrPtr->sa_family == AF_INET || anAddrPtr->sa_family == AF_INET6)) {
257
+ resolved = true;
258
+ self.hostAddress = address;
259
+ struct sockaddr_in *sin = (struct sockaddr_in *)anAddrPtr;
260
+ char str[INET6_ADDRSTRLEN];
261
+ inet_ntop(anAddrPtr->sa_family, &(sin->sin_addr), str, INET6_ADDRSTRLEN);
262
+ self.hostAddressString = [[NSString alloc] initWithUTF8String:str];
263
+ break;
264
+ }
265
+ }
266
+ }
267
+
268
+ //we can stop host resolution now
269
+ if (hostRef) {
270
+ CFRelease(hostRef);
271
+ }
272
+
273
+ //if an error occurred during resolution
274
+ if (!resolved) {
275
+ //stop
276
+ [self stop];
277
+
278
+ //notify about error and return
279
+ dispatch_async(dispatch_get_main_queue(), ^{
280
+ DEFINE_NSError(hostError, PingUtil_Message_HostErrorHostNotFound)
281
+ callback(NO, hostError);
282
+ });
283
+ return;
284
+ }
285
+
286
+ //set up socket
287
+ int err = 0;
288
+ switch (self.hostAddressFamily) {
289
+ case AF_INET: {
290
+ self.socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
291
+ if (self.socket < 0) {
292
+ err = errno;
293
+ }
294
+ } break;
295
+ case AF_INET6: {
296
+ self.socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
297
+ if (self.socket < 0) {
298
+ err = errno;
299
+ }
300
+ } break;
301
+ default: {
302
+ err = EPROTONOSUPPORT;
303
+ } break;
304
+ }
305
+
306
+ //couldnt setup socket
307
+ if (err) {
308
+ //clean up so far
309
+ [self stop];
310
+
311
+ //notify about error and close
312
+ dispatch_async(dispatch_get_main_queue(), ^{
313
+ DEFINE_NSError(unknownError, PingUtil_Message_Unknown)
314
+ callback(NO, unknownError);
315
+ });
316
+ return;
317
+ }
318
+
319
+ //set ttl on the socket
320
+ if (self.ttl) {
321
+ u_char ttlForSockOpt = (u_char)self.ttl;
322
+ setsockopt(self.socket, IPPROTO_IP, IP_TTL, &ttlForSockOpt, sizeof(NSUInteger));
323
+ }
324
+
325
+ //we are ready now
326
+ self.isReady = YES;
327
+
328
+ //notify that we are ready
329
+ dispatch_async(dispatch_get_main_queue(), ^{
330
+ callback(YES, nil);
331
+ });
332
+ });
333
+
334
+ self.isStopped = NO;
335
+ }
336
+
337
+ - (void)startPingingWithBlock:(void (^)(GBPingSummary *))successBlock fail:(void (^)(NSError *))failBlock
338
+ {
339
+ if (self.isReady && !self.isPinging) {
340
+ self.successBlock = [successBlock copy];
341
+ self.failBlock = [failBlock copy];
342
+ //go into infinite listenloop on a new thread (listenThread)
343
+ NSThread *listenThread = [[NSThread alloc] initWithTarget:self selector:@selector(listenLoop) object:nil];
344
+ listenThread.name = @"listenThread";
345
+
346
+ //set up loop that sends packets on a new thread (sendThread)
347
+ NSThread *sendThread = [[NSThread alloc] initWithTarget:self selector:@selector(sendLoop) object:nil];
348
+ sendThread.name = @"sendThread";
349
+
350
+ //we're pinging now
351
+ self.isPinging = YES;
352
+ [listenThread start];
353
+ [sendThread start];
354
+ }
355
+ }
356
+
357
+ - (void)listenLoop
358
+ {
359
+ @autoreleasepool {
360
+ while (self.isPinging)
361
+ [self listenOnce];
362
+ }
363
+ }
364
+
365
+ - (void)listenOnce
366
+ {
367
+ int err;
368
+ struct sockaddr_storage addr;
369
+ socklen_t addrLen;
370
+ ssize_t bytesRead;
371
+ void *buffer;
372
+ enum { kBufferSize = 65535 };
373
+
374
+ buffer = malloc(kBufferSize);
375
+ assert(buffer);
376
+
377
+ //set socket timeout
378
+ struct timeval tv;
379
+ tv.tv_sec = self.timeout / 1000;
380
+ tv.tv_usec = 0;
381
+ if (setsockopt(self.socket, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv)) < 0) {
382
+ NSLog(@"Set Timeput Error");
383
+ }
384
+
385
+ //read the data.
386
+ addrLen = sizeof(addr);
387
+ bytesRead = recvfrom(self.socket, buffer, kBufferSize, 0, (struct sockaddr *)&addr, &addrLen);
388
+ err = 0;
389
+ if (bytesRead < 0) {
390
+ err = errno;
391
+ }
392
+
393
+ //process the data we read.
394
+ if (bytesRead > 0) {
395
+ char hoststr[INET6_ADDRSTRLEN];
396
+ struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
397
+ inet_ntop(sin->sin_family, &(sin->sin_addr), hoststr, INET6_ADDRSTRLEN);
398
+ NSString *host = [[NSString alloc] initWithUTF8String:hoststr];
399
+
400
+ if ([host isEqualToString:self.hostAddressString]) { // only make sense where received packet comes from expected source
401
+ NSDate *receiveDate = [NSDate date];
402
+ NSMutableData *packet;
403
+
404
+ packet = [NSMutableData dataWithBytes:buffer length:(NSUInteger)bytesRead];
405
+ assert(packet);
406
+
407
+ //complete the ping summary
408
+ const struct ICMPHeader *headerPointer;
409
+
410
+ if (sin->sin_family == AF_INET) {
411
+ headerPointer = [[self class] icmp4InPacket:packet];
412
+ } else {
413
+ headerPointer = (const struct ICMPHeader *)[packet bytes];
414
+ }
415
+
416
+ NSUInteger seqNo = (NSUInteger)OSSwapBigToHostInt16(headerPointer->sequenceNumber);
417
+ NSNumber *key = @(seqNo);
418
+ GBPingSummary *pingSummary = [(GBPingSummary *)self.pendingPings[key] copy];
419
+
420
+ if (pingSummary) {
421
+ if ([self isValidPingResponsePacket:packet]) {
422
+ //override the source address (we might have sent to google.com and 172.123.213.192 replied)
423
+ pingSummary.receiveDate = receiveDate;
424
+ // IP can't be read from header for ICMPv6
425
+ if (sin->sin_family == AF_INET) {
426
+ pingSummary.host = [[self class] sourceAddressInPacket:packet];
427
+
428
+ //set ttl from response (different servers may respond with different ttls)
429
+ const struct IPHeader *ipPtr;
430
+ if ([packet length] >= sizeof(IPHeader)) {
431
+ ipPtr = (const IPHeader *)[packet bytes];
432
+ pingSummary.ttl = ipPtr->timeToLive;
433
+ }
434
+ }
435
+
436
+ pingSummary.status = GBPingStatusSuccess;
437
+
438
+ //invalidate the timeouttimer
439
+ NSTimer *timer = self.timeoutTimers[key];
440
+ [timer invalidate];
441
+ [self.timeoutTimers removeObjectForKey:key];
442
+
443
+ if (self.successBlock) {
444
+ self.successBlock([pingSummary copy]);
445
+ }
446
+ if (self.delegate && [self.delegate respondsToSelector:@selector(ping:didReceiveReplyWithSummary:)]) {
447
+ dispatch_async(dispatch_get_main_queue(), ^{
448
+ //notify delegate
449
+ [self.delegate ping:self didReceiveReplyWithSummary:[pingSummary copy]];
450
+ });
451
+ }
452
+ } else {
453
+ pingSummary.status = GBPingStatusFail;
454
+
455
+ if (self.delegate && [self.delegate respondsToSelector:@selector(ping:didReceiveUnexpectedReplyWithSummary:)]) {
456
+ dispatch_async(dispatch_get_main_queue(), ^{
457
+ [self.delegate ping:self didReceiveUnexpectedReplyWithSummary:[pingSummary copy]];
458
+ });
459
+ }
460
+ }
461
+ }
462
+ }
463
+ } else {
464
+ //we failed to read the data, so shut everything down.
465
+ if (err == 0) {
466
+ err = EPIPE;
467
+ }
468
+
469
+ @synchronized(self) {
470
+ if (!self.isStopped) {
471
+ if (self.failBlock) {
472
+ DEFINE_NSError(unknownError, PingUtil_Message_Unknown)
473
+ _failBlock(unknownError);
474
+ }
475
+ if (self.delegate && [self.delegate respondsToSelector:@selector(ping:didFailWithError:)]) {
476
+ dispatch_async(dispatch_get_main_queue(), ^{
477
+ [self.delegate ping:self didFailWithError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]];
478
+ });
479
+ }
480
+ }
481
+ }
482
+
483
+ //stop the whole thing
484
+ [self stop];
485
+ }
486
+
487
+ free(buffer);
488
+ }
489
+
490
+ - (void)sendLoop
491
+ {
492
+ @autoreleasepool {
493
+ while (self.isPinging) {
494
+ [self sendPing];
495
+
496
+ NSTimeInterval runUntil = CFAbsoluteTimeGetCurrent() + self.pingPeriod;
497
+ NSTimeInterval time = 0;
498
+ while (runUntil > time) {
499
+ NSDate *runUntilDate = [NSDate dateWithTimeIntervalSinceReferenceDate:runUntil];
500
+ [[NSRunLoop currentRunLoop] runUntilDate:runUntilDate];
501
+
502
+ time = CFAbsoluteTimeGetCurrent();
503
+ }
504
+ }
505
+ }
506
+ }
507
+
508
+ - (void)sendPing
509
+ {
510
+ if (self.isPinging) {
511
+ int err;
512
+ NSData *packet;
513
+ ssize_t bytesSent;
514
+
515
+ // Construct the ping packet.
516
+ NSData *payload = [self generateDataWithLength:(self.payloadSize)];
517
+
518
+ switch (self.hostAddressFamily) {
519
+ case AF_INET: {
520
+ packet = [self pingPacketWithType:kICMPv4TypeEchoRequest payload:payload requiresChecksum:YES];
521
+ } break;
522
+ case AF_INET6: {
523
+ packet = [self pingPacketWithType:kICMPv6TypeEchoRequest payload:payload requiresChecksum:NO];
524
+ } break;
525
+ default: {
526
+ assert(NO);
527
+ } break;
528
+ }
529
+
530
+ // this is our ping summary
531
+ GBPingSummary *newPingSummary = [GBPingSummary new];
532
+
533
+ // Send the packet.
534
+ if (self.socket == 0) {
535
+ bytesSent = -1;
536
+ err = EBADF;
537
+ } else {
538
+ //record the send date
539
+ NSDate *sendDate = [NSDate date];
540
+
541
+ //construct ping summary, as much as it can
542
+ newPingSummary.sequenceNumber = self.nextSequenceNumber;
543
+ newPingSummary.host = self.host;
544
+ newPingSummary.sendDate = sendDate;
545
+ newPingSummary.ttl = self.ttl;
546
+ newPingSummary.payloadSize = self.payloadSize;
547
+ newPingSummary.status = GBPingStatusPending;
548
+
549
+ //add it to pending pings
550
+ NSNumber *key = @(self.nextSequenceNumber);
551
+ self.pendingPings[key] = newPingSummary;
552
+
553
+ //increment sequence number
554
+ self.nextSequenceNumber += 1;
555
+
556
+ //we create a copy, this one will be passed out to other threads
557
+ GBPingSummary *pingSummaryCopy = [newPingSummary copy];
558
+
559
+ //we need to clean up our list of pending pings, and we do that after the timeout has elapsed (+ some grace period)
560
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((self.timeout + kPendingPingsCleanupGrace) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
561
+ //remove the ping from the pending list
562
+ [self.pendingPings removeObjectForKey:key];
563
+ });
564
+
565
+ //add a timeout timer
566
+ //add a timeout timer
567
+ NSTimer *timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:self.timeout
568
+ target:[NSBlockOperation blockOperationWithBlock:^{
569
+ newPingSummary.status = GBPingStatusFail;
570
+
571
+ //notify about the failure
572
+ if (self.delegate && [self.delegate respondsToSelector:@selector(ping:didTimeoutWithSummary:)]) {
573
+ dispatch_async(dispatch_get_main_queue(), ^{
574
+ [self.delegate ping:self didTimeoutWithSummary:pingSummaryCopy];
575
+ });
576
+ }
577
+
578
+ //remove the timer itself from the timers list
579
+ //lm make sure that the timer list doesnt grow and these removals actually work... try logging the count of the timeoutTimers when stopping the pinger
580
+ [self.timeoutTimers removeObjectForKey:key];
581
+ }]
582
+ selector:@selector(main)
583
+ userInfo:nil
584
+ repeats:NO];
585
+ [[NSRunLoop mainRunLoop] addTimer:timeoutTimer forMode:NSRunLoopCommonModes];
586
+
587
+ //keep a local ref to it
588
+ if (self.timeoutTimers) {
589
+ self.timeoutTimers[key] = timeoutTimer;
590
+ }
591
+
592
+ //notify delegate about this
593
+ if (self.delegate && [self.delegate respondsToSelector:@selector(ping:didSendPingWithSummary:)]) {
594
+ dispatch_async(dispatch_get_main_queue(), ^{
595
+ [self.delegate ping:self didSendPingWithSummary:pingSummaryCopy];
596
+ });
597
+ }
598
+
599
+ bytesSent = sendto(
600
+ self.socket,
601
+ [packet bytes],
602
+ [packet length],
603
+ 0,
604
+ (struct sockaddr *)[self.hostAddress bytes],
605
+ (socklen_t)[self.hostAddress length]
606
+ );
607
+ err = 0;
608
+ if (bytesSent < 0) {
609
+ err = errno;
610
+ }
611
+ }
612
+
613
+ // This is after the sending
614
+
615
+ //successfully sent
616
+ if ((bytesSent > 0) && (((NSUInteger)bytesSent) == [packet length])) {
617
+ //noop, we already notified delegate about sending of the ping
618
+ }
619
+ //failed to send
620
+ else {
621
+ //complete the error
622
+ if (err == 0) {
623
+ err = ENOBUFS; // This is not a hugely descriptor error, alas.
624
+ }
625
+
626
+ //little log
627
+ if (self.debug) {
628
+ NSLog(@"GBPing: failed to send packet with error code: %d", err);
629
+ }
630
+
631
+ //change status
632
+ newPingSummary.status = GBPingStatusFail;
633
+
634
+ GBPingSummary *pingSummaryCopyAfterFailure = [newPingSummary copy];
635
+
636
+ if (self.failBlock) {
637
+ DEFINE_NSError(unknownError, PingUtil_Message_Unknown)
638
+ self.failBlock(unknownError);
639
+ }
640
+ //notify delegate
641
+ if (self.delegate && [self.delegate respondsToSelector:@selector(ping:didFailToSendPingWithSummary:error:)]) {
642
+ dispatch_async(dispatch_get_main_queue(), ^{
643
+ [self.delegate ping:self didFailToSendPingWithSummary:pingSummaryCopyAfterFailure error:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]];
644
+ });
645
+ }
646
+ }
647
+ }
648
+ }
649
+
650
+ - (void)stop
651
+ {
652
+ @synchronized(self) {
653
+ if (!self.isStopped) {
654
+ self.isPinging = NO;
655
+
656
+ self.isReady = NO;
657
+
658
+ //destroy listenThread by closing socket (listenThread)
659
+ if (self.socket) {
660
+ close(self.socket);
661
+ self.socket = 0;
662
+ }
663
+
664
+ //destroy host
665
+ self.hostAddress = nil;
666
+
667
+ //clean up pendingpings
668
+ [self.pendingPings removeAllObjects];
669
+ self.pendingPings = nil;
670
+ for (NSNumber *key in [self.timeoutTimers copy]) {
671
+ NSTimer *timer = self.timeoutTimers[key];
672
+ [timer invalidate];
673
+ }
674
+
675
+ //clean up timeouttimers
676
+ [self.timeoutTimers removeAllObjects];
677
+ self.timeoutTimers = nil;
678
+
679
+ //reset seq number
680
+ self.nextSequenceNumber = 0;
681
+
682
+ self.isStopped = YES;
683
+ }
684
+ }
685
+ }
686
+
687
+ #pragma mark - util
688
+
689
+ static uint16_t in_cksum(const void *buffer, size_t bufferLen)
690
+ // This is the standard BSD checksum code, modified to use modern types.
691
+ {
692
+ size_t bytesLeft;
693
+ int32_t sum;
694
+ const uint16_t *cursor;
695
+ union {
696
+ uint16_t us;
697
+ uint8_t uc[2];
698
+ } last;
699
+ uint16_t answer;
700
+
701
+ bytesLeft = bufferLen;
702
+ sum = 0;
703
+ cursor = buffer;
704
+
705
+ /*
706
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
707
+ * sequential 16 bit words to it, and at the end, fold back all the
708
+ * carry bits from the top 16 bits into the lower 16 bits.
709
+ */
710
+ while (bytesLeft > 1) {
711
+ sum += *cursor;
712
+ cursor += 1;
713
+ bytesLeft -= 2;
714
+ }
715
+
716
+ /* mop up an odd byte, if necessary */
717
+ if (bytesLeft == 1) {
718
+ last.uc[0] = *(const uint8_t *)cursor;
719
+ last.uc[1] = 0;
720
+ sum += last.us;
721
+ }
722
+
723
+ /* add back carry outs from top 16 bits to low 16 bits */
724
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
725
+ sum += (sum >> 16); /* add carry */
726
+ answer = (uint16_t) ~sum; /* truncate to 16 bits */
727
+
728
+ return answer;
729
+ }
730
+
731
+ + (NSString *)sourceAddressInPacket:(NSData *)packet
732
+ {
733
+ // Returns the source address of the IP packet
734
+
735
+ const struct IPHeader *ipPtr;
736
+ const uint8_t *sourceAddress;
737
+
738
+ if ([packet length] >= sizeof(IPHeader)) {
739
+ ipPtr = (const IPHeader *)[packet bytes];
740
+
741
+ sourceAddress = ipPtr->sourceAddress;//dont need to swap byte order those cuz theyre the smallest atomic unit (1 byte)
742
+ NSString *ipString = [NSString stringWithFormat:@"%d.%d.%d.%d", sourceAddress[0], sourceAddress[1], sourceAddress[2], sourceAddress[3]];
743
+
744
+ return ipString;
745
+ } else return nil;
746
+ }
747
+
748
+ + (NSUInteger)icmp4HeaderOffsetInPacket:(NSData *)packet
749
+ // Returns the offset of the ICMPHeader within an IP packet.
750
+ {
751
+ NSUInteger result;
752
+ const struct IPHeader *ipPtr;
753
+ size_t ipHeaderLength;
754
+
755
+ result = NSNotFound;
756
+ if ([packet length] >= (sizeof(IPHeader) + sizeof(ICMPHeader))) {
757
+ ipPtr = (const IPHeader *)[packet bytes];
758
+ assert((ipPtr->versionAndHeaderLength & 0xF0) == 0x40); // IPv4
759
+ assert(ipPtr->protocol == 1); // ICMP
760
+ ipHeaderLength = (ipPtr->versionAndHeaderLength & 0x0F) * sizeof(uint32_t);
761
+ if ([packet length] >= (ipHeaderLength + sizeof(ICMPHeader))) {
762
+ result = ipHeaderLength;
763
+ }
764
+ }
765
+ return result;
766
+ }
767
+
768
+ + (const struct ICMPHeader *)icmp4InPacket:(NSData *)packet
769
+ // See comment in header.
770
+ {
771
+ const struct ICMPHeader *result;
772
+ NSUInteger icmpHeaderOffset;
773
+
774
+ result = nil;
775
+ icmpHeaderOffset = [self icmp4HeaderOffsetInPacket:packet];
776
+ if (icmpHeaderOffset != NSNotFound) {
777
+ result = (const struct ICMPHeader *)(((const uint8_t *)[packet bytes]) + icmpHeaderOffset);
778
+ }
779
+ return result;
780
+ }
781
+
782
+ - (BOOL)isValidPingResponsePacket:(NSMutableData *)packet
783
+ {
784
+ BOOL result;
785
+
786
+ switch (self.hostAddressFamily) {
787
+ case AF_INET: {
788
+ result = [self isValidPing4ResponsePacket:packet];
789
+ } break;
790
+ case AF_INET6: {
791
+ result = [self isValidPing6ResponsePacket:packet];
792
+ } break;
793
+ default: {
794
+ assert(NO);
795
+ result = NO;
796
+ } break;
797
+ }
798
+ return result;
799
+ }
800
+
801
+ - (BOOL)isValidPing4ResponsePacket:(NSMutableData *)packet
802
+ // Returns true if the packet looks like a valid ping response packet destined
803
+ // for us.
804
+ {
805
+ BOOL result;
806
+ NSUInteger icmpHeaderOffset;
807
+ ICMPHeader *icmpPtr;
808
+ uint16_t receivedChecksum;
809
+ uint16_t calculatedChecksum;
810
+
811
+ result = NO;
812
+
813
+ icmpHeaderOffset = [[self class] icmp4HeaderOffsetInPacket:packet];
814
+ if (icmpHeaderOffset != NSNotFound) {
815
+ icmpPtr = (struct ICMPHeader *)(((uint8_t *)[packet mutableBytes]) + icmpHeaderOffset);
816
+
817
+ receivedChecksum = icmpPtr->checksum;
818
+ icmpPtr->checksum = 0;
819
+ calculatedChecksum = in_cksum(icmpPtr, [packet length] - icmpHeaderOffset);
820
+ icmpPtr->checksum = receivedChecksum;
821
+
822
+ if (receivedChecksum == calculatedChecksum) {
823
+ if ( (icmpPtr->type == kICMPv4TypeEchoReply) && (icmpPtr->code == 0) ) {
824
+ if (OSSwapBigToHostInt16(icmpPtr->identifier) == self.identifier) {
825
+ if (OSSwapBigToHostInt16(icmpPtr->sequenceNumber) < self.nextSequenceNumber) {
826
+ result = YES;
827
+ }
828
+ }
829
+ }
830
+ }
831
+ }
832
+
833
+ // NSLog(@"valid: %@, type: %d", _b(result), icmpPtr->type);
834
+
835
+ return result;
836
+ }
837
+
838
+ - (BOOL)isValidPing6ResponsePacket:(NSMutableData *)packet
839
+ // Returns true if the IPv6 packet looks like a valid ping response packet destined
840
+ // for us.
841
+ {
842
+ BOOL result;
843
+ const ICMPHeader *icmpPtr;
844
+
845
+ result = NO;
846
+
847
+ if (packet.length >= sizeof(*icmpPtr)) {
848
+ icmpPtr = packet.bytes;
849
+
850
+ if ( (icmpPtr->type == kICMPv6TypeEchoReply) && (icmpPtr->code == 0) ) {
851
+ if (OSSwapBigToHostInt16(icmpPtr->identifier) == self.identifier) {
852
+ if (OSSwapBigToHostInt16(icmpPtr->sequenceNumber) < self.nextSequenceNumber) {
853
+ result = YES;
854
+ }
855
+ }
856
+ }
857
+ }
858
+
859
+ return result;
860
+ }
861
+
862
+ - (NSData *)generateDataWithLength:(NSUInteger)length
863
+ {
864
+ //create a buffer full of 7's of specified length
865
+ char tempBuffer[length];
866
+ memset(tempBuffer, 7, length);
867
+
868
+ return [[NSData alloc] initWithBytes:tempBuffer length:length];
869
+ }
870
+
871
+ - (void)_invokeTimeoutCallback:(NSTimer *)timer
872
+ {
873
+ dispatch_block_t callback = timer.userInfo;
874
+ if (callback) {
875
+ callback();
876
+ }
877
+ }
878
+
879
+ - (NSData *)pingPacketWithType:(uint8_t)type payload:(NSData *)payload requiresChecksum:(BOOL)requiresChecksum
880
+ {
881
+ NSMutableData *packet;
882
+ ICMPHeader *icmpPtr;
883
+
884
+ packet = [NSMutableData dataWithLength:sizeof(*icmpPtr) + payload.length];
885
+ assert(packet != nil);
886
+
887
+ icmpPtr = packet.mutableBytes;
888
+ icmpPtr->type = type;
889
+ icmpPtr->code = 0;
890
+ icmpPtr->checksum = 0;
891
+ icmpPtr->identifier = OSSwapHostToBigInt16(self.identifier);
892
+ icmpPtr->sequenceNumber = OSSwapHostToBigInt16(self.nextSequenceNumber);
893
+ memcpy(&icmpPtr[1], [payload bytes], [payload length]);
894
+
895
+ if (requiresChecksum) {
896
+ // The IP checksum routine returns a 16-bit number that's already in correct byte order
897
+ // (due to wacky 1's complement maths), so we just put it into the packet as a 16-bit unit.
898
+
899
+ icmpPtr->checksum = in_cksum(packet.bytes, packet.length);
900
+ }
901
+
902
+ return packet;
903
+ }
904
+
905
+ - (sa_family_t)hostAddressFamily
906
+ {
907
+ sa_family_t result;
908
+
909
+ result = AF_UNSPEC;
910
+ if ( (self.hostAddress != nil) && (self.hostAddress.length >= sizeof(struct sockaddr)) ) {
911
+ result = ((const struct sockaddr *)self.hostAddress.bytes)->sa_family;
912
+ }
913
+ return result;
914
+ }
915
+
916
+ #pragma mark - memory
917
+
918
+ - (id)init
919
+ {
920
+ if (self = [super init]) {
921
+ self.setupQueue = dispatch_queue_create("GBPing setup queue", 0);
922
+ self.isStopped = YES;
923
+ self.identifier = arc4random();
924
+ }
925
+
926
+ return self;
927
+ }
928
+
929
+ - (void)dealloc
930
+ {
931
+ self.delegate = nil;
932
+ self.host = nil;
933
+ self.timeoutTimers = nil;
934
+ self.pendingPings = nil;
935
+ self.hostAddress = nil;
936
+
937
+ //clean up socket to be sure
938
+ if (self.socket) {
939
+ close(self.socket);
940
+ self.socket = 0;
941
+ }
942
+ }
943
+
944
+ @end