Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b30523d

Browse files
committed
CFHostSample: Version 3.0, 2017年03月14日
Rewritten in Swift. The main program is a simple command line tool that accepts parameters, creates query objects, runs them, and prints the results. The various query classes are each a simple wrapper around CFHost; each has extensive comments explaining how to use the class and how the class works internally. Signed-off-by: Liu Lantao <liulantao@gmail.com>
1 parent c0557ce commit b30523d

File tree

11 files changed

+1925
-0
lines changed

11 files changed

+1925
-0
lines changed

‎CFHostSample/CFHostSample.xcodeproj/project.pbxproj‎

Lines changed: 422 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
Copyright (C) 2017 Apple Inc. All Rights Reserved.
3+
See LICENSE.txt for this sample’s licensing information
4+
5+
Abstract:
6+
Resolves a DNS name to a list of IP addresses.
7+
*/
8+
9+
@import Foundation;
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
@protocol QHostAddressQueryDelegate;
14+
15+
/// This class uses CFHost to query a DNS name for its addresses. To do this:
16+
///
17+
/// 1. Create the `QHostAddressQuery` object with the name in question.
18+
///
19+
/// 2. Set a delegate.
20+
///
21+
/// 3. Call `start()`.
22+
///
23+
/// 4. Wait for `-hostAddressQuery:didCompleteWithAddresses:` or `-hostAddressQuery:didCompleteWithError:`
24+
/// to be called.
25+
///
26+
/// CFHost, and hence this class, is run loop based. The class remembers the run loop on which you
27+
/// call `start()` and delivers the delegate callbacks on that run loop.
28+
29+
@interface QHostAddressQuery : NSObject
30+
31+
/// Creates an instance to query the specified DNS name for its addresses.
32+
///
33+
/// - Parameter name: The DNS name to query.
34+
35+
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
36+
37+
- (instancetype)init NS_UNAVAILABLE;
38+
39+
/// The DNS name to query.
40+
41+
@property (nonatomic, copy, readonly) NSString * name;
42+
43+
/// You must set this to learn about the results of your query.
44+
45+
@property (nonatomic, weak, readwrite) id<QHostAddressQueryDelegate> delegate;
46+
47+
/// Starts the query process.
48+
///
49+
/// The query remembers the thread that called this method and calls any delegate
50+
/// callbacks on that thread.
51+
///
52+
/// - Important: For the query to make progress, this thread must run its run loop in
53+
/// the default run loop mode.
54+
///
55+
/// - Warning: It is an error to start a query that's running.
56+
57+
- (void)start;
58+
59+
/// Cancels a running query.
60+
///
61+
/// If you successfully cancel a query, no delegate callback for that query will be
62+
/// called.
63+
///
64+
/// If the query is running, you must call this from the thread that called `start()`.
65+
///
66+
/// - Note: It is acceptable to call this on a query that's not running; it does nothing
67+
// in that case.
68+
69+
- (void)cancel;
70+
71+
@end
72+
73+
/// The delegate protocol for the HostAddressQuery class.
74+
75+
@protocol QHostAddressQueryDelegate <NSObject>
76+
77+
@required
78+
79+
/// Called when the query completes successfully.
80+
///
81+
/// This is called on the same thread that called `start()`.
82+
///
83+
/// - Parameters:
84+
/// - addresses: The addresses for the DNS name. This has some important properties:
85+
/// - It will not be empty.
86+
/// - Each element is an `NSData` value that contains some flavour of `sockaddr`
87+
/// - It can contain any combination of IPv4 and IPv6 addresses
88+
/// - The addresses are sorted, with the most preferred first
89+
/// - query: The query that completed.
90+
91+
- (void)hostAddressQuery:(QHostAddressQuery *)query didCompleteWithAddresses:(NSArray<NSData *> *)addresses;
92+
93+
/// Called when the query completes with an error.
94+
///
95+
/// This is called on the same thread that called `start()`.
96+
///
97+
/// - Parameters:
98+
/// - error: An error describing the failure.
99+
/// - query: The query that completed.
100+
///
101+
/// - Important: In most cases the error will be in domain `kCFErrorDomainCFNetwork`
102+
/// with a code of `kCFHostErrorUnknown` (aka `CFNetworkErrors.cfHostErrorUnknown`),
103+
/// and the user info dictionary will contain an element with the `kCFGetAddrInfoFailureKey`
104+
/// key whose value is an NSNumber containing an `EAI_XXX` value (from `<netdb.h>`).
105+
106+
- (void)hostAddressQuery:(QHostAddressQuery *)query didCompleteWithError:(NSError *)error;
107+
108+
@end
109+
110+
NS_ASSUME_NONNULL_END
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
Copyright (C) 2017 Apple Inc. All Rights Reserved.
3+
See LICENSE.txt for this sample’s licensing information
4+
5+
Abstract:
6+
Resolves a DNS name to a list of IP addresses.
7+
*/
8+
9+
#import "QHostAddressQuery.h"
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
@interface QHostAddressQuery ()
14+
15+
/// The underlying CFHost object that does the resolution.
16+
17+
@property (nonatomic, strong, readwrite, nullable) CFHostRef host __attribute__ (( NSObject ));
18+
19+
/// The run loop on which the CFHost object is scheduled; this is set in `start()`
20+
/// and cleared when the query stops (either via `cancel()` or by completing).
21+
22+
@property (nonatomic, strong, readwrite, nullable) NSRunLoop * targetRunLoop;
23+
24+
@end
25+
26+
NS_ASSUME_NONNULL_END
27+
28+
@implementation QHostAddressQuery
29+
30+
- (instancetype)initWithName:(NSString *)name {
31+
NSParameterAssert(name != nil);
32+
self = [super init];
33+
if (self != nil) {
34+
self->_name = [name copy];
35+
self->_host = CFHostCreateWithName(nil, (__bridge CFStringRef) self->_name);
36+
}
37+
return self;
38+
}
39+
40+
- (instancetype)init {
41+
abort();
42+
}
43+
44+
- (void)start {
45+
NSParameterAssert(self.targetRunLoop == nil);
46+
self.targetRunLoop = NSRunLoop.currentRunLoop;
47+
48+
CFHostClientContext context = {
49+
.info = (void *) CFBridgingRetain(self)
50+
};
51+
BOOL success = CFHostSetClient(self.host, HostClientCallBack, &context) != false;
52+
assert(success);
53+
CFHostScheduleWithRunLoop(self.host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
54+
55+
CFStreamError streamError;
56+
success = CFHostStartInfoResolution(self.host, kCFHostAddresses, &streamError) != false;
57+
if ( ! success ) {
58+
[self stopWithStreamError:&streamError notify:NO];
59+
}
60+
}
61+
62+
/// Our CFHost callback function for our host; this just extracts the object from the `info`
63+
/// pointer and calls methods on it.
64+
65+
static void HostClientCallBack(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError * __nullable error, void * __nullable info) {
66+
#pragma unused(theHost)
67+
#pragma unused(typeInfo)
68+
69+
QHostAddressQuery * obj = (__bridge QHostAddressQuery *) info;
70+
assert([obj isKindOfClass:[QHostAddressQuery class]]);
71+
72+
if ( (error == NULL) || ( (error->domain == 0) && (error->error == 0) ) ) {
73+
[obj stopWithStreamError:NULL notify:YES];
74+
} else {
75+
[obj stopWithStreamError:error notify:YES];
76+
}
77+
}
78+
79+
/// Stops the query with the supplied error, notifying the delegate if `notify` is true.
80+
81+
- (void)stopWithStreamError:(nullable const CFStreamError *)streamError notify:(BOOL)notify {
82+
NSError * error = nil;
83+
84+
if (streamError != NULL) {
85+
// Convert a CFStreamError to a NSError. This is less than ideal. I only handle a
86+
// limited number of error domains, and I can't use a switch statement because
87+
// some of the `kCFStreamErrorDomainXxx` values are not a constant. Wouldn't it be
88+
// nice if there was a public API to do this mapping <rdar://problem/5845848>
89+
// or a CFHost API that used CFError <rdar://problem/6016542>.
90+
if (streamError->domain == kCFStreamErrorDomainPOSIX) {
91+
error = [NSError errorWithDomain:NSPOSIXErrorDomain code:streamError->error userInfo:nil];
92+
} else if (streamError->domain == kCFStreamErrorDomainMacOSStatus) {
93+
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:streamError->error userInfo:nil];
94+
} else if (streamError->domain == kCFStreamErrorDomainNetServices) {
95+
error = [NSError errorWithDomain:(__bridge NSString *) kCFErrorDomainCFNetwork code:streamError->error userInfo:nil];
96+
} else if (streamError->domain == kCFStreamErrorDomainNetDB) {
97+
error = [NSError errorWithDomain:(__bridge NSString *) kCFErrorDomainCFNetwork code:kCFHostErrorUnknown userInfo:@{
98+
(__bridge NSString *) kCFGetAddrInfoFailureKey: @(streamError->error)
99+
}];
100+
} else {
101+
// If it's something we don't understand, we just assume it comes from
102+
// CFNetwork.
103+
error = [NSError errorWithDomain:(__bridge NSString *) kCFErrorDomainCFNetwork code:streamError->error userInfo:nil];
104+
}
105+
}
106+
107+
[self stopWithError:error notify:notify];
108+
}
109+
110+
/// Stops the query with the supplied error, notifying the delegate if `notify` is true.
111+
112+
- (void)stopWithError:(nullable NSError *)error notify:(BOOL)notify {
113+
NSParameterAssert(NSRunLoop.currentRunLoop == self.targetRunLoop);
114+
self.targetRunLoop = nil;
115+
116+
CFHostSetClient(self.host, nil, nil);
117+
CFHostUnscheduleFromRunLoop(self.host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
118+
CFHostCancelInfoResolution(self.host, kCFHostAddresses);
119+
CFRelease( (__bridge CFTypeRef) self );
120+
121+
id<QHostAddressQueryDelegate> strongDelegate = self.delegate;
122+
if (notify && (strongDelegate != nil) ) {
123+
if (error != nil) {
124+
[strongDelegate hostAddressQuery:self didCompleteWithError:error];
125+
} else {
126+
NSArray<NSData *> * addresses = (__bridge NSArray<NSData *> *) CFHostGetAddressing(self.host, NULL);
127+
[strongDelegate hostAddressQuery:self didCompleteWithAddresses:addresses];
128+
}
129+
}
130+
}
131+
132+
- (void)cancel {
133+
if (self.targetRunLoop != nil) {
134+
[self stopWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil] notify:NO];
135+
}
136+
}
137+
138+
@end
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
Copyright (C) 2017 Apple Inc. All Rights Reserved.
3+
See LICENSE.txt for this sample’s licensing information
4+
5+
Abstract:
6+
Resolves an IP address to a list of DNS names.
7+
*/
8+
9+
@import Foundation;
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
@protocol QHostNameQueryDelegate;
14+
15+
/// This class uses CFHost to query an IP address for its DNS names. To do this:
16+
///
17+
/// 1. Create the `QHostNameQuery` object with the name in question.
18+
///
19+
/// 2. Set a delegate.
20+
///
21+
/// 3. Call `start()`.
22+
///
23+
/// 4. Wait for `-hostNameQuery:didCompleteWithNames:` or `-hostNameQuery:didCompleteWithError:`
24+
/// to be called.
25+
///
26+
/// CFHost, and hence this class, is run loop based. The class remembers the run loop on which you
27+
/// call `start()` and delivers the delegate callbacks on that run loop.
28+
///
29+
/// - Important: Reverse DNS queries are notoriously unreliable. Specifically, you must not
30+
/// assume that:
31+
///
32+
/// - Every IP address has a valid reverse DNS name
33+
/// - The reverse DNS name is unique
34+
/// - There's any correlation between the forward and reverse DNS mappings
35+
///
36+
/// Unless you have domain specific knowledge (for example, you're working in an enterprise
37+
/// environment where you know how the DNS is set up), reverse DNS queries are generally not
38+
/// useful for anything other than logging.
39+
40+
@interface QHostNameQuery : NSObject
41+
42+
/// Creates an instance to query the specified IP address for its DNS name.
43+
///
44+
/// - Parameter address: The IP address to query, as a `NSData` value containing some flavour of `sockaddr`.
45+
46+
- (instancetype)initWithAddress:(NSData *)address NS_DESIGNATED_INITIALIZER;
47+
48+
- (instancetype)init NS_UNAVAILABLE;
49+
50+
/// The IP address to query, as a `NSData` value containing some flavour of `sockaddr`.
51+
52+
@property (nonatomic, copy, readonly) NSData * address;
53+
54+
/// You must set this to learn about the results of your query.
55+
56+
@property (nonatomic, weak, readwrite) id<QHostNameQueryDelegate> delegate;
57+
58+
/// Starts the query process.
59+
///
60+
/// The query remembers the thread that called this method and calls any delegate
61+
/// callbacks on that thread.
62+
///
63+
/// - Important: For the query to make progress, this thread must run its run loop in
64+
/// the default run loop mode.
65+
///
66+
/// - Warning: It is an error to start a query that's running.
67+
68+
- (void)start;
69+
70+
/// Cancels a running query.
71+
///
72+
/// If you successfully cancel a query, no delegate callback for that query will be
73+
/// called.
74+
///
75+
/// If the query is running, you must call this from the thread that called `start()`.
76+
///
77+
/// - Note: It is acceptable to call this on a query that's not running; it does nothing
78+
// in that case.
79+
80+
- (void)cancel;
81+
82+
@end
83+
84+
/// The delegate protocol for the HostNameQuery class.
85+
86+
@protocol QHostNameQueryDelegate <NSObject>
87+
88+
@required
89+
90+
/// Called when the query completes successfully.
91+
///
92+
/// This is called on the same thread that called `start()`.
93+
///
94+
/// - Parameters:
95+
/// - names: The DNS names for the IP address.
96+
/// - query: The query that completed.
97+
98+
- (void)hostNameQuery:(QHostNameQuery *)query didCompleteWithNames:(NSArray<NSString *> *)names;
99+
100+
/// Called when the query completes with an error.
101+
///
102+
/// This is called on the same thread that called `start()`.
103+
///
104+
/// - Parameters:
105+
/// - error: An error describing the failure.
106+
/// - query: The query that completed.
107+
///
108+
/// - Important: In most cases the error will be in domain `kCFErrorDomainCFNetwork`
109+
/// with a code of `kCFHostErrorUnknown` (aka `CFNetworkErrors.cfHostErrorUnknown`),
110+
/// and the user info dictionary will contain an element with the `kCFGetAddrInfoFailureKey`
111+
/// key whose value is an NSNumber containing an `EAI_XXX` value (from `<netdb.h>`).
112+
113+
- (void)hostNameQuery:(QHostNameQuery *)query didCompleteWithError:(NSError *)error;
114+
115+
@end
116+
117+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /