Last active
November 14, 2016 05:48
-
-
Save MatiMax/1a2890337e76eb8d7700 to your computer and use it in GitHub Desktop.
This Swift Playground shows how to use Core Foundation's CFHost in Swift 2 to try to produce an array of Strings containing the host name and all its aliases for a given IP address. Unfortunately, CFHostGetNames() does not supply the aliases of a host, so we're left alone with the gethostbyaddr() C function.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: # How to retrieve a host name and associated aliases from an IP address using Core Fondation's `CFHost` | |
import Cocoa | |
import XCPlayground | |
//: In order to get the callback working we use a simple class to implement the showcase. | |
class DNSResolve { | |
//: The IP address may be a Swift `String` thanks to the toll-free bridging to C strings. | |
let ip: String = "17.172.224.47" | |
//: We use an optional `CFHost` variable because CFHost neither comes with an initializer nor is conforming to the Nullable protocol. | |
var host: CFHost? | |
//: We use this array of `String`s to store the resolved host names. | |
var names: [String] = [] | |
func resolve() { | |
//: Let's set up the `sockaddr_in` C structure using the initializer. | |
var sin = sockaddr_in( | |
sin_len: UInt8(sizeof(sockaddr_in)), | |
sin_family: sa_family_t(AF_INET), | |
sin_port: in_port_t(0), | |
sin_addr: in_addr(s_addr: inet_addr(ip)), | |
sin_zero: (0,0,0,0,0,0,0,0) | |
) | |
//: Now convert the structure into a `CFData` object. | |
let data = withUnsafePointer(&sin) { ptr in | |
CFDataCreate(kCFAllocatorDefault, UnsafePointer(ptr), sizeof(sockaddr_in)) | |
} | |
//: Create the `CFHostRef` with the `CFData` object and store the retained value for later use. | |
let hostref = CFHostCreateWithAddress(kCFAllocatorDefault, data) | |
self.host = hostref.takeUnretainedValue() | |
//: For the callback to work we have to create a client context. | |
var ctx = CFHostClientContext( | |
version: 0, | |
info: unsafeBitCast(self, UnsafeMutablePointer<Void>.self), | |
retain: nil, | |
release: nil, | |
copyDescription: unsafeBitCast(0, CFAllocatorCopyDescriptionCallBack.self) | |
) | |
//: We can now set up the client for the callback using the `CFHostClientCallBack` signature for the closure. | |
CFHostSetClient(host!, { (host, infoType, error, info) in | |
let obj = unsafeBitCast(info, DNSResolve.self) | |
print("Resolving …") | |
obj.namesResolved(withError: error.memory) | |
}, &ctx) | |
//: Now schedule the runloop for the host. | |
CFHostScheduleWithRunLoop(host!, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); | |
//: Create a `CFStreamError` object for use with the info resolution using `CFHostStartInfoResolution`. | |
var error = CFStreamError() | |
//: Start the info resolution. | |
let started: Bool = CFHostStartInfoResolution(host!, .Names, &error) | |
print("Name resolution started: \(started)") | |
} | |
//: This function is attachted as `CFHostClientCallBack` in `CFHostSetClient` which should get called during the info resolution. | |
func namesResolved(withError error: CFStreamError) { | |
print("namesResolved: Resolving …") | |
//: Create a boolean pointer `DarwinBoolean` for use with the function `CFHostGetNames`. | |
var resolved: DarwinBoolean = DarwinBoolean(false) | |
//: Now get the results of the info resolution. | |
let cfNames: CFArrayRef = CFHostGetNames(host!, &resolved)!.takeUnretainedValue() | |
print("namesResolved: Names resolved: \(resolved) with error \(error.error)") | |
//: We can use cascading casts from `[AnyObject]` to a force-unwrapped `[String]`. Thank you, Swift. | |
self.names = cfNames as [AnyObject] as! [String] | |
//: **Oh dear—we see only one host name here and no aliases. Stuck again … :-(** | |
print("CFArray reports \(CFArrayGetCount(cfNames)) elements, [String] reports \(self.names.count) elements.") | |
self.listNames() | |
//: After the info resolution clean up either way. | |
CFHostSetClient(host!, nil, nil); | |
CFHostCancelInfoResolution(host!, .Names) | |
CFHostUnscheduleFromRunLoop(host!, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode) | |
} | |
func listNames() { | |
print(self.names) | |
} | |
} | |
//: OK, let's create an instance of our `DNSResolve` class and run the `resolve()` method. | |
let dnsRes = DNSResolve() | |
dnsRes.resolve() | |
//: In order to see the callback working we have to set Playground's execution to take on forever. | |
XCPSetExecutionShouldContinueIndefinitely() |
Hi all,
thanks for the comments and the feedback. Lately I was focusing more on the Linux version of Swift but I certainly will take the time to port the Gist to Swift 3. Please stay tuned.
Cheers, Mati
Hello all,
I finally had time to update the playground to Swift 3. As there was a lot of changes going on, especially with C-pointer type casting and all, I had to fight a little to get it working. So, please see the comments in the playground which point out the finesses.
Happy Swift-ing,
Mati
@acalism: Good point. In the playground it tends to work nicely for me but I definitively see the logic behind using an unretained value. I adapted the sources accordingly.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A swift 3 upgrade would be nice