CFHostSampleObjC/main.m
/*  | 
Copyright (C) 2017 Apple Inc. All Rights Reserved.  | 
See LICENSE.txt for this sample’s licensing information  | 
Abstract:  | 
Command line tool main.  | 
*/  | 
@import Foundation;  | 
#import "QHostAddressQuery.h"  | 
#import "QHostNameQuery.h"  | 
#include <netdb.h>  | 
// MARK: - utilities  | 
/// Returns a string for the supplied error, formatted to make sense as command line output.  | 
///  | 
/// - Parameter error: The error to format.  | 
/// - Returns: A string for that error, formatted to make sense as command line output.  | 
static NSString * _Nonnull stringForError(NSError * _Nonnull error) { | 
    if ([error.domain isEqual:(__bridge NSString *)kCFErrorDomainCFNetwork]) { | 
NSNumber * code = error.userInfo[(__bridge NSString *) kCFGetAddrInfoFailureKey];  | 
// We deliberately don't call `gai_strerror` here because this is a developer-  | 
// oriented tool and we want to show the symbolic name of the error.  | 
        switch (code.intValue) { | 
case EAI_ADDRFAMILY: return @"EAI_ADDRFAMILY";  | 
case EAI_AGAIN: return @"EAI_AGAIN";  | 
case EAI_BADFLAGS: return @"EAI_BADFLAGS";  | 
case EAI_FAIL: return @"EAI_FAIL";  | 
case EAI_FAMILY: return @"EAI_FAMILY";  | 
case EAI_MEMORY: return @"EAI_MEMORY";  | 
case EAI_NODATA: return @"EAI_NODATA";  | 
case EAI_NONAME: return @"EAI_NONAME";  | 
case EAI_SERVICE: return @"EAI_SERVICE";  | 
case EAI_SOCKTYPE: return @"EAI_SOCKTYPE";  | 
case EAI_SYSTEM: return @"EAI_SYSTEM";  | 
case EAI_BADHINTS: return @"EAI_BADHINTS";  | 
case EAI_PROTOCOL: return @"EAI_PROTOCOL";  | 
case EAI_OVERFLOW: return @"EAI_OVERFLOW";  | 
default:  | 
break;  | 
}  | 
}  | 
return error.description;  | 
}  | 
/// Converts an IP address to its string equivalent.  | 
///  | 
/// This does not hit the network and thus won't fail.  | 
///  | 
/// - Parameter address: An IP address, as a `NSData` value containing some flavour of `sockaddr`.  | 
/// - Returns: The string equivalent of that IP address.  | 
static NSString * _Nonnull numericStringForAddress(NSData * _Nonnull address) { | 
char name[NI_MAXHOST];  | 
BOOL success = getnameinfo(address.bytes, (socklen_t) address.length, name, sizeof(name), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) == 0;  | 
    if ( ! success ) { | 
return @"?";  | 
}  | 
return @(name);  | 
}  | 
/// Converts an IP address string to an IP address.  | 
///  | 
/// This does not hit the network but can fail if the string is not a valid IP address.  | 
///  | 
/// - Parameter numeric: An IP address string.  | 
/// - Returns: An IP address, as an `NSData` value containing some flavour of `sockaddr`, or nil.  | 
static NSData * _Nullable addressForNumericString(NSString * _Nonnull numericString) { | 
    struct addrinfo hints = { | 
.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV  | 
};  | 
struct addrinfo * addrList;  | 
BOOL success = getaddrinfo(numericString.UTF8String, NULL, &hints, &addrList) == 0;  | 
    if ( ! success ) { | 
return nil;  | 
}  | 
const struct addrinfo * cursor = addrList;  | 
NSData * result = [NSData dataWithBytes:cursor->ai_addr length:cursor->ai_addrlen];  | 
freeaddrinfo(addrList);  | 
return result;  | 
}  | 
// MARK: - main object  | 
/// The main object, which is instantiated and run by the main function.  | 
NS_ASSUME_NONNULL_BEGIN  | 
@interface MainObj : NSObject <QHostAddressQueryDelegate, QHostNameQueryDelegate>  | 
/// The name-to-address queries to run.  | 
@property (nonatomic, strong, readonly) NSMutableArray<QHostAddressQuery *> * addressQueries;  | 
/// The address-to-name queries to run.  | 
@property (nonatomic, strong, readonly) NSMutableArray<QHostNameQuery *> * nameQueries;  | 
/// The total number of queries to run.  | 
@property (nonatomic, assign, readonly) NSInteger queryCount;  | 
/// Runs the queries, not returning until they're all finished.  | 
- (void)run;  | 
@end  | 
@interface MainObj ()  | 
/// The number of queries that have finished.  | 
@property (nonatomic, assign, readwrite) NSInteger finishedQueryCount;  | 
@end  | 
NS_ASSUME_NONNULL_END  | 
@implementation MainObj  | 
- (instancetype)init { | 
self = [super init];  | 
    if (self != nil) { | 
self->_addressQueries = [[NSMutableArray alloc] init];  | 
self->_nameQueries = [[NSMutableArray alloc] init];  | 
}  | 
return self;  | 
}  | 
- (NSInteger)queryCount { | 
return (NSInteger) (self.addressQueries.count + self.nameQueries.count);  | 
}  | 
- (void)run { | 
    for (QHostAddressQuery * q in self.addressQueries) { | 
q.delegate = self;  | 
[q start];  | 
}  | 
    for (QHostNameQuery * q in self.nameQueries) { | 
q.delegate = self;  | 
[q start];  | 
}  | 
    while (self.finishedQueryCount != self.queryCount) { | 
[NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];  | 
}  | 
}  | 
- (void)hostAddressQuery:(QHostAddressQuery *)query didCompleteWithAddresses:(NSArray<NSData *> *)addresses { | 
NSMutableArray<NSString *> * stringAddresses = [[NSMutableArray alloc] init];  | 
    for (NSData * address in addresses) { | 
[stringAddresses addObject: numericStringForAddress(address) ];  | 
}  | 
NSString * addressList = [stringAddresses componentsJoinedByString:@", "];  | 
fprintf(stdout, "%s -> %s\n", query.name.UTF8String, addressList.UTF8String);  | 
self.finishedQueryCount += 1;  | 
}  | 
- (void)hostAddressQuery:(QHostAddressQuery *)query didCompleteWithError:(NSError *)error { | 
fprintf(stdout, "%s -> %s\n", query.name.UTF8String, stringForError(error).UTF8String);  | 
self.finishedQueryCount += 1;  | 
}  | 
- (void)hostNameQuery:(QHostNameQuery *)query didCompleteWithNames:(NSArray<NSString *> *)names { | 
NSString * addressString = numericStringForAddress(query.address);  | 
NSString * nameList = [names componentsJoinedByString:@", "];  | 
fprintf(stdout, "%s -> %s\n", addressString.UTF8String, nameList.UTF8String);  | 
self.finishedQueryCount += 1;  | 
}  | 
- (void)hostNameQuery:(QHostNameQuery *)query didCompleteWithError:(NSError *)error { | 
NSString * addressString = numericStringForAddress(query.address);  | 
fprintf(stdout, "%s -> %s\n", addressString.UTF8String, stringForError(error).UTF8String);  | 
self.finishedQueryCount += 1;  | 
}  | 
@end  | 
// MARK: - main function  | 
static void usage() { | 
fprintf(stderr, "usage: %s -h apple.com\n", getprogname());  | 
fprintf(stderr, " %s -a 17.172.224.47\n", getprogname());  | 
exit(EXIT_FAILURE);  | 
}  | 
int main(int argc, char **argv) { | 
// Parse any options.  | 
MainObj * m = [[MainObj alloc] init];  | 
int opt;  | 
    do { | 
opt = getopt(argc, argv, "h:a:");  | 
        switch (opt) { | 
            case -1: { | 
// do nothing  | 
} break;  | 
            case 'h': { | 
NSString * name = @(optarg);  | 
                if (name.length == 0) { | 
usage();  | 
}  | 
[m.addressQueries addObject: [[QHostAddressQuery alloc] initWithName:name] ];  | 
} break;  | 
            case 'a': { | 
NSData * address = addressForNumericString(@(optarg));  | 
                if (address == nil) { | 
usage();  | 
}  | 
[m.nameQueries addObject: [[QHostNameQuery alloc] initWithAddress:address] ];  | 
} break;  | 
            default: { | 
usage();  | 
} break;  | 
}  | 
} while (opt != -1);  | 
// Check for inconsistencies and then run.  | 
    if (m.queryCount == 0) { | 
usage(); // nothing to do  | 
}  | 
    if (optind != argc) { | 
usage(); // extra stuff an the command line  | 
}  | 
[m run];  | 
}  | 
Copyright © 2017 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2017-03-14