Skip to content

Instantly share code, notes, and snippets.

@fikeminkel
Created April 26, 2017 20:56
Show Gist options
  • Save fikeminkel/a9c4bc4d0348527e8df3690e242038d3 to your computer and use it in GitHub Desktop.
Save fikeminkel/a9c4bc4d0348527e8df3690e242038d3 to your computer and use it in GitHub Desktop.
Swift 3.1 DNS TXT record lookup
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)
}
}
@eclair4151
Copy link

@mosen or anyone looking for an example parsing SRV records, heres an example I found that worked very well for me: https://github.com/jamf/NoMAD-2/blob/main/NoMAD/SRVLookups/SRVResolver.swift

@rgkobashi
Copy link

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 the domainName is invalid, adding a time out like @juanheyns might help.

@vanyasem
Copy link

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