Created
April 26, 2017 20:56
-
-
Save fikeminkel/a9c4bc4d0348527e8df3690e242038d3 to your computer and use it in GitHub Desktop.
Swift 3.1 DNS TXT record lookup
This file contains 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
import dnssd | |
struct DNSTxtRecord { | |
typealias DNSLookupHandler = ([String: String]?) -> Void | |
static func lookup(_ domainName: String, completionHandler: @escaping DNSLookupHandler) { | |
var mutableCompletionHandler = completionHandler // completionHandler needs to be mutable to be used as inout param | |
let callback: DNSServiceQueryRecordReply = { | |
(sdRef, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdlen, rdata, ttl, context) -> Void in | |
// dereference completionHandler from pointer since we can't directly capture it in a C callback | |
guard let completionHandlerPtr = context?.assumingMemoryBound(to: DNSLookupHandler.self) else { return } | |
let completionHandler = completionHandlerPtr.pointee | |
// map memory at rdata to a UInt8 pointer | |
guard let txtPtr = rdata?.assumingMemoryBound(to: UInt8.self) else { | |
completionHandler(nil) | |
return | |
} | |
// advancing pointer by 1 to skip bad character at beginning of record | |
let txt = String(cString: txtPtr.advanced(by: 1)) | |
// parse name=value txt record into dictionary | |
var record: [String: String] = [:] | |
let recordParts = txt.components(separatedBy: "=") | |
record[recordParts[0]] = recordParts[1] | |
completionHandler(record) | |
} | |
// MemoryLayout<T>.size can give us the necessary size of the struct to allocate | |
let serviceRef: UnsafeMutablePointer<DNSServiceRef?> = UnsafeMutablePointer.allocate(capacity: MemoryLayout<DNSServiceRef>.size) | |
// pass completionHandler as context object to callback so that we have a way to pass the record result back to the caller | |
DNSServiceQueryRecord(serviceRef, 0, 0, domainName, UInt16(kDNSServiceType_TXT), UInt16(kDNSServiceClass_IN), callback, &mutableCompletionHandler); | |
DNSServiceProcessResult(serviceRef.pointee) | |
DNSServiceRefDeallocate(serviceRef.pointee) | |
} | |
} |
https://gist.github.com/fikeminkel/a9c4bc4d0348527e8df3690e242038d3#file-dnstxtrecord-swift-L20
Unfortunately this code produces incorrect behaviour on concatenated TXT entries, leaving a garbage byte in place of the concatenation:
// advancing pointer by 1 to skip bad character at beginning of record
let txt = String(cString: txtPtr.advanced(by: 1))
Quoting https://kb.isc.org/docs/aa-00356: per RFC 4408 a TXT or SPF record is allowed to contain multiple strings, which should be concatenated together by the reading application
See apple/swift-async-dns-resolver#43 and apple/swift-async-dns-resolver#44
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you very much! @fikeminkel and @juanheyns both snipped worked for me.
If someone is having an issue like @ethan-gerardot where it gets stuck at
DNSServiceProcessResult
, is most likely because thedomainName
is invalid, adding a time out like @juanheyns might help.