Parse DNS Packet requests and responses

Are there any libraries I can use to parse DNS packet requests and responses? Otherwise, I'll have to implement my own.

I would check https://swiftpackageindex.com when looking for existing libraries. A quick search reveals there are at least a few DNS related packages listed there, whether they will suit your needs though I can't say.

1 Like

If you’re working on Apple platforms there’s always <dns_util.h> and specifically the dns_parse_packet function.

This is a low-level C API, so let us know if you need help calling it from Swift.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

That's perfect! How can I add the header file and use it within a Swift Xcode project? Also pardon if I'm misunderstanding, but is there a way to convert a dns_reply_t object into a character buffer to send out through the wire?

What I want to be able to do is:

  1. Parse a DNS request
  2. Construct a DNS Response and convert it into Swift's Foundation Data object so that I can send it through the network

How can I add the header file and use it within a Swift Xcode project?

If you’re building non-library code, you can simply include <dns_util.h> in your bridging header. If you’re building library code, you’ll need to build a wrapper module.

Oh, and the functions are exported by the libresolv.tbd stub library.

Also pardon if I'm misunderstanding, but is there a way to convert a
dns_reply_t object into a character buffer to send out through the
wire?

No. The API is for parsing, not rendering, DNS packets.

As an example, this code:

let packet: [UInt8] = [
    0x21, 0xD9, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x01, 0x07, 0x65, 0x78, 0x61,
    0x6D, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D,
    0x00, 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00,
    0x01, 0x00, 0x01, 0x00, 0x00, 0xB2, 0x3E, 0x00,
    0x04, 0x5D, 0xB8, 0xD8, 0x22, 0x00, 0x00, 0x29,
    0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
]
let replyQ = packet.withUnsafeBytes { buf -> UnsafeMutablePointer<dns_reply_t>? in
    // Many C APIs, including this one, use `char` values for raw bytes.  On
    // Apple platforms `char` is equivalent to `SInt8`.  However, on the
    // Swift side we typically use `UInt8` for raw bytes and thus so we have
    // to do an ugly cast.
    let base = buf.baseAddress!.assumingMemoryBound(to: Int8.self)
    return dns_parse_packet(base, UInt32(buf.count))
}
guard let reply = replyQ else { fatalError() }
// The 0xffff should be a combinary of `DNS_PRINT_` flags but there’s no
// ‘all’ flag so I’m taking a shortcut.
dns_print_reply(reply, stdout, 0xffff)

prints this:

Xid: 8665
QR: Reply
Server: -nil-
Opcode: Standard
AA: Non-Authoritative
TC: Non-Truncated
RD: Recursion desired
RA: Recursion available
Rcode: No error
Question (1):
example.com IN A    
Answer (1):
example.com IN A     45630 93.184.216.34
Authority (0):
Additional records (1):
 ?? OPT   0 0  ()

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

Hello Quinn, can this library be used in the case where I have multiple answers in my reply. If so how could I obtain them?

dns_parse_packet returns a dns_reply_t structure which has this definition:

typedef struct
{
    …
	dns_header_t *header;
    …
    dns_resource_record_t **answer;
    …
} dns_reply_t;

The header contains an ancount field that tells you how many entries there are in the answer array. So, to expand on my example above:

let packet: [UInt8] = [
    0x47, 0x49, 0x81, 0x80, 0x00, 0x01, 0x00, 0x04,
    0x00, 0x00, 0x00, 0x01, 0x03, 0x77, 0x77, 0x77,
    0x05, 0x61, 0x70, 0x70, 0x6C, 0x65, 0x03, 0x63,
    0x6F, 0x6D, 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0,
    0x0C, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x06,
    0x30, 0x00, 0x1B, 0x03, 0x77, 0x77, 0x77, 0x05,
    0x61, 0x70, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F,
    0x6D, 0x07, 0x65, 0x64, 0x67, 0x65, 0x6B, 0x65,
    0x79, 0x03, 0x6E, 0x65, 0x74, 0x00, 0xC0, 0x2B,
    0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x38, 0x58,
    0x00, 0x32, 0x03, 0x77, 0x77, 0x77, 0x05, 0x61,
    0x70, 0x70, 0x6C, 0x65, 0x03, 0x63, 0x6F, 0x6D,
    0x07, 0x65, 0x64, 0x67, 0x65, 0x6B, 0x65, 0x79,
    0x03, 0x6E, 0x65, 0x74, 0x0B, 0x67, 0x6C, 0x6F,
    0x62, 0x61, 0x6C, 0x72, 0x65, 0x64, 0x69, 0x72,
    0x06, 0x61, 0x6B, 0x61, 0x64, 0x6E, 0x73, 0x03,
    0x6E, 0x65, 0x74, 0x00, 0xC0, 0x52, 0x00, 0x05,
    0x00, 0x01, 0x00, 0x00, 0x0D, 0xBA, 0x00, 0x1B,
    0x05, 0x65, 0x36, 0x38, 0x35, 0x38, 0x04, 0x64,
    0x73, 0x63, 0x78, 0x0A, 0x61, 0x6B, 0x61, 0x6D,
    0x61, 0x69, 0x65, 0x64, 0x67, 0x65, 0x03, 0x6E,
    0x65, 0x74, 0x00, 0xC0, 0x90, 0x00, 0x01, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x02,
    0x14, 0x5E, 0xB1, 0x00, 0x00, 0x29, 0x04, 0xD0,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
… packet parsing code as above …
let answers = UnsafeBufferPointer(start: reply.pointee.answer, count: Int(reply.pointee.header.pointee.ancount))
for a in answers {
    print(a!.pointee)
}

which prints:

dns_resource_record_t(name: …, dnstype: 5, dnsclass: 1, ttl: 1584, data: …)
dns_resource_record_t(name: …, dnstype: 5, dnsclass: 1, ttl: 14424, data: …)
dns_resource_record_t(name: …, dnstype: 5, dnsclass: 1, ttl: 3514, data: …)
dns_resource_record_t(name: …, dnstype: 1, dnsclass: 1, ttl: 5, data: …)

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

Terms of Service

Privacy Policy

Cookie Policy