From 2278df1493e064c197913e49b5d1935942d83448 Mon Sep 17 00:00:00 2001 From: daniel Date: Tue, 6 May 2025 16:57:32 -0700 Subject: initial import --- src/dns.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/dns.c (limited to 'src/dns.c') diff --git a/src/dns.c b/src/dns.c new file mode 100644 index 0000000..f16f568 --- /dev/null +++ b/src/dns.c @@ -0,0 +1,113 @@ +#include +#include +#include + +#include + +#include "dns.h" + +/* DNS packet: + +--------+-------+-----------------------------------------------+ + | Offset | Bytes | Field | + +--------+--------+----------------------------------------------+ + | 0 | 2 | Transaction ID | + | 2 | 2 | Flags | + | 4 | 2 | QDCOUNT (Number of questions) | + | 6 | 2 | ANCOUNT (Number of answers) | + | 8 | 2 | NSCOUNT (Number of authority RRs) | + | 10 | 2 | ARCOUNT (Number of additional RRs) | + +--------+--------+----------------------------------------------+ + | 12 | var | Questions (name + qtype + qclass) | + +--------+--------+----------------------------------------------+ + | ?? | var | Answers (name+type+class+TTL+RDLENGTH+RDATA) | + +--------+--------+----------------------------------------------+ + | ?? | var | Authority RRs (same format) | + +--------+--------+----------------------------------------------+ + | ?? | var | Additional RRs (same format) | + +--------+--------+----------------------------------------------+ +*/ +size_t parse_dns_udp(const uint8_t *payload, + size_t len, + struct dns_question *out, size_t max_qs) { + if (len < 12) { + // not long enough + return 0; + } + + uint16_t qdcount = ntohs(*(uint16_t *)(payload + 4)); + if (qdcount == 0) { + return 0; + } + if (qdcount > max_qs) { + qdcount = max_qs; // truncate safely + } + + const uint8_t *ptr = payload + 12; + const uint8_t *end = payload + len; + + size_t parsed = 0; + for (int q = 0; q < qdcount && ptr < end; q++) { + char name[MAX_DNS_NAME_LEN]; + int name_len = 0; + + while (ptr < end && *ptr != 0 && name_len < MAX_DNS_NAME_LEN - 1) { + uint8_t label_len = *ptr++; + + if (label_len == 0 || ptr + label_len > end) { + return 0; + } + + if (name_len > 0) { + name[name_len++] = '.'; + } + + memcpy(name + name_len, ptr, label_len); + name_len += label_len; + ptr += label_len; + } + + name[name_len] = '\0'; + ptr++; // skip null terminator + + if (ptr + 4 > end) { + return 0; + } + + uint16_t qtype = ntohs(*(uint16_t *)(ptr)); + ptr += 2; + ptr += 2; // qclass + + strncpy(out[parsed].name, name, MAX_DNS_NAME_LEN); + out[parsed].name[MAX_DNS_NAME_LEN - 1] = '\0'; + out[parsed].qtype = qtype; + + parsed++; + } + + return parsed; +} + +// resolve DNS query type to human-readable string. +// TODO static buffer may yield incorrect resultsif multiple unknown +// DNS types are encountered in a single message. +const char *dns_type_to_string(uint16_t type) { + switch(type) { + case 1: return "A"; + case 2: return "NS"; + case 5: return "CNAME"; + case 6: return "SOA"; + case 12: return "PTR"; + case 15: return "MX"; + case 16: return "TXT"; + case 28: return "AAAA"; + case 33: return "SRV"; + case 64: return "SVCB"; // RFC 9460 https://www.rfc-editor.org/rfc/rfc9460 + case 65: return "HTTPS"; // RFC 9460 https://www.rfc-editor.org/rfc/rfc9460 + case 255: return "ANY"; + default: // return TYPEXXXX if type isn't known + static char buf[16]; + snprintf(buf, sizeof(buf), "TYPE%d", type); + return buf; + } +} + -- cgit v1.2.3