/* |
File: main.m |
Contains: Trivial test program for the SimplePing class. |
Written by: DTS |
Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. |
("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or |
redistribution of this Apple software constitutes acceptance of |
these terms. If you do not agree with these terms, please do |
not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following |
terms, and subject to these terms, Apple grants you a personal, |
non-exclusive license, under Apple's copyrights in this |
original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or |
without modifications, in source and/or binary forms; provided |
that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the |
following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks |
or logos of Apple Inc. may be used to endorse or promote |
products derived from the Apple Software without specific prior |
written permission from Apple. Except as expressly stated in |
this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or |
by other works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. |
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING |
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, |
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING |
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, |
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY |
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY |
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR |
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF |
SUCH DAMAGE. |
*/ |
#include "SimplePing.h" |
#include <sys/socket.h> |
#include <netdb.h> |
#pragma mark * Utilities |
static NSString * DisplayAddressForAddress(NSData * address) |
// Returns a dotted decimal string for the specified address (a (struct sockaddr) |
// within the address NSData). |
{ |
int err; |
NSString * result; |
char hostStr[NI_MAXHOST]; |
result = nil; |
if (address != nil) { |
err = getnameinfo([address bytes], (socklen_t) [address length], hostStr, sizeof(hostStr), NULL, 0, NI_NUMERICHOST); |
if (err == 0) { |
result = [NSString stringWithCString:hostStr encoding:NSASCIIStringEncoding]; |
assert(result != nil); |
} |
} |
return result; |
} |
@interface Main : NSObject <SimplePingDelegate> |
{ |
SimplePing * _pinger; |
NSTimer * _sendTimer; |
BOOL _done; |
} |
- (void)runWithHostName:(NSString *)hostName; |
@end |
@interface Main () |
@property (nonatomic, retain, readwrite) SimplePing * pinger; |
@property (nonatomic, retain, readwrite) NSTimer * sendTimer; |
@end |
@implementation Main |
- (void)dealloc |
{ |
[self->_pinger stop]; |
[self->_pinger release]; |
[self->_sendTimer invalidate]; |
[self->_sendTimer release]; |
[super dealloc]; |
} |
@synthesize pinger = _pinger; |
@synthesize sendTimer = _sendTimer; |
- (NSString *)_shortErrorFromError:(NSError *)error |
// Given an NSError, returns a short error string that we can print, handling |
// some special cases along the way. |
{ |
NSString * result; |
NSNumber * failureNum; |
int failure; |
const char * failureStr; |
assert(error != nil); |
result = nil; |
// Handle DNS errors as a special case. |
if ( [[error domain] isEqual:(NSString *)kCFErrorDomainCFNetwork] && ([error code] == kCFHostErrorUnknown) ) { |
failureNum = [[error userInfo] objectForKey:(id)kCFGetAddrInfoFailureKey]; |
if ( [failureNum isKindOfClass:[NSNumber class]] ) { |
failure = [failureNum intValue]; |
if (failure != 0) { |
failureStr = gai_strerror(failure); |
if (failureStr != NULL) { |
result = [NSString stringWithUTF8String:failureStr]; |
assert(result != nil); |
} |
} |
} |
} |
// Otherwise try various properties of the error object. |
if (result == nil) { |
result = [error localizedFailureReason]; |
} |
if (result == nil) { |
result = [error localizedDescription]; |
} |
if (result == nil) { |
result = [error description]; |
} |
assert(result != nil); |
return result; |
} |
- (void)runWithHostName:(NSString *)hostName |
// The Objective-C 'main' for this program. It creates a SimplePing object |
// and runs the runloop sending pings and printing the results. |
{ |
assert(self.pinger == nil); |
self.pinger = [SimplePing simplePingWithHostName:hostName]; |
assert(self.pinger != nil); |
self.pinger.delegate = self; |
[self.pinger start]; |
do { |
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; |
} while (self.pinger != nil); |
} |
- (void)sendPing |
// Called to send a ping, both directly (as soon as the SimplePing object starts up) |
// and via a timer (to continue sending pings periodically). |
{ |
assert(self.pinger != nil); |
[self.pinger sendPingWithData:nil]; |
} |
- (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address |
// A SimplePing delegate callback method. We respond to the startup by sending a |
// ping immediately and starting a timer to continue sending them every second. |
{ |
#pragma unused(pinger) |
assert(pinger == self.pinger); |
assert(address != nil); |
NSLog(@"pinging %@", DisplayAddressForAddress(address)); |
// Send the first ping straight away. |
[self sendPing]; |
// And start a timer to send the subsequent pings. |
assert(self.sendTimer == nil); |
self.sendTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(sendPing) userInfo:nil repeats:YES]; |
} |
- (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error |
// A SimplePing delegate callback method. We shut down our timer and the |
// SimplePing object itself, which causes the runloop code to exit. |
{ |
#pragma unused(pinger) |
assert(pinger == self.pinger); |
#pragma unused(error) |
NSLog(@"failed: %@", [self _shortErrorFromError:error]); |
[self.sendTimer invalidate]; |
self.sendTimer = nil; |
// No need to call -stop. The pinger will stop itself in this case. |
// We do however want to nil out pinger so that the runloop stops. |
self.pinger = nil; |
} |
- (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet |
// A SimplePing delegate callback method. We just log the send. |
{ |
#pragma unused(pinger) |
assert(pinger == self.pinger); |
#pragma unused(packet) |
NSLog(@"#%u sent", (unsigned int) OSSwapBigToHostInt16(((const ICMPHeader *) [packet bytes])->sequenceNumber) ); |
} |
- (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet error:(NSError *)error |
// A SimplePing delegate callback method. We just log the failure. |
{ |
#pragma unused(pinger) |
assert(pinger == self.pinger); |
#pragma unused(packet) |
#pragma unused(error) |
NSLog(@"#%u send failed: %@", (unsigned int) OSSwapBigToHostInt16(((const ICMPHeader *) [packet bytes])->sequenceNumber), [self _shortErrorFromError:error]); |
} |
- (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet |
// A SimplePing delegate callback method. We just log the reception of a ping response. |
{ |
#pragma unused(pinger) |
assert(pinger == self.pinger); |
#pragma unused(packet) |
NSLog(@"#%u received", (unsigned int) OSSwapBigToHostInt16([SimplePing icmpInPacket:packet]->sequenceNumber) ); |
} |
- (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet |
// A SimplePing delegate callback method. We just log the receive. |
{ |
const ICMPHeader * icmpPtr; |
#pragma unused(pinger) |
assert(pinger == self.pinger); |
#pragma unused(packet) |
icmpPtr = [SimplePing icmpInPacket:packet]; |
if (icmpPtr != NULL) { |
NSLog(@"#%u unexpected ICMP type=%u, code=%u, identifier=%u", (unsigned int) OSSwapBigToHostInt16(icmpPtr->sequenceNumber), (unsigned int) icmpPtr->type, (unsigned int) icmpPtr->code, (unsigned int) OSSwapBigToHostInt16(icmpPtr->identifier) ); |
} else { |
NSLog(@"unexpected packet size=%zu", (size_t) [packet length]); |
} |
} |
@end |
int main(int argc, char* argv[]) |
{ |
#pragma unused(argc) |
#pragma unused(argv) |
NSAutoreleasePool * pool; |
Main * mainObj; |
pool = [[NSAutoreleasePool alloc] init]; |
assert(pool != nil); |
if (argc == 2) { |
mainObj = [[[Main alloc] init] autorelease]; |
assert(mainObj != nil); |
[mainObj runWithHostName:[NSString stringWithUTF8String:argv[1]]]; |
} else { |
fprintf(stderr, "usage: %s host\n", getprogname()); |
} |
[pool drain]; |
// On success we loop forever in -runWithHostName:, so the only way to |
// get here is if something went wrong. |
return EXIT_FAILURE; |
} |
Last updated: 2010-04-06