From 68400a25b7bcd817ff2a7c1f80fc7e76c26a606c Mon Sep 17 00:00:00 2001 From: Piper Pentagram Date: Tue, 8 Jul 2025 15:36:27 -0700 Subject: [PATCH 1/4] read the packets into packet structs --- include/dns_packet_buffer.h | 7 +++++-- include/dns_request.h | 5 ++++- include/udp_server.h | 2 +- src/dns_packet_buffer.c | 32 ++++++++++++++++++++++++++++++++ src/dns_request.c | 11 +++++++++++ 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/include/dns_packet_buffer.h b/include/dns_packet_buffer.h index f23287d..4539e02 100644 --- a/include/dns_packet_buffer.h +++ b/include/dns_packet_buffer.h @@ -1,13 +1,16 @@ #pragma once #include -#define PACKET_BUFFER_SIZE 512 +#define PB_SIZE 512 +#define PB_OUT_OF_BOUNDS 9999; struct DNSPacketBuffer { - char buf[PACKET_BUFFER_SIZE]; + char buf[PB_SIZE]; size_t pos; }; struct DNSPacketBuffer *new_dns_packet_buffer(); struct DNSPacketHeader *dns_header(struct DNSPacketBuffer); + +char pb_read_byte(struct DNSPacketBuffer *); diff --git a/include/dns_request.h b/include/dns_request.h index aeecf54..e5705ff 100644 --- a/include/dns_request.h +++ b/include/dns_request.h @@ -1,7 +1,10 @@ #pragma once #include "dns_packets.h" +#include "dns_packet_buffer.h" struct DNSRequest { - struct DNSPacketHeader header; + struct DNSPacketHeader *header; }; + +struct DNSRequest *new_dns_request(struct DNSPacketBuffer *pb); diff --git a/include/udp_server.h b/include/udp_server.h index cd63cfc..40276be 100644 --- a/include/udp_server.h +++ b/include/udp_server.h @@ -1,5 +1,5 @@ #pragma once -#define UDP_PORT 53 +#define UDP_PORT 1053 int start_server(); diff --git a/src/dns_packet_buffer.c b/src/dns_packet_buffer.c index 55fe986..7d7cd69 100644 --- a/src/dns_packet_buffer.c +++ b/src/dns_packet_buffer.c @@ -1,4 +1,5 @@ #include "dns_packet_buffer.h" +#include #include struct DNSPacketBuffer *new_dns_packet_buffer() @@ -12,3 +13,34 @@ struct DNSPacketHeader *dns_header(struct DNSPacketBuffer) { } + + +// read a single byte and move the cursor one byte forward +char pb_read_byte(struct DNSPacketBuffer *pb) +{ + char b = pb->buf[pb->pos]; + pb->pos++; + + if (pb->pos >= PB_SIZE) { + printf( + "WARN: pb_read_byte: advanced packetbuffer cursor pos to %lu, which is greater than the max %d", + pb->pos, + PB_SIZE + ); + } + + return b; +} + +char pb_get_byte(struct DNSPacketBuffer *pb) +{ + if (pb->pos >= PB_SIZE) { + printf( + "WARN: pb_get_byte: accessing packetbuffer cursor pos %lu, which is greater than the max %d", + pb->pos, + PB_SIZE + ); + } + + return pb->buf[pb->pos]; +} diff --git a/src/dns_request.c b/src/dns_request.c index 243502c..b960d9d 100644 --- a/src/dns_request.c +++ b/src/dns_request.c @@ -1,3 +1,14 @@ +#include + #include "dns_request.h" + +struct DNSRequest *new_dns_request(struct DNSPacketBuffer *pb) +{ + struct DNSRequest *req; + req = malloc(sizeof(struct DNSRequest)); + + req->header = (struct DNSPacketHeader *) pb->buf; + return req; +} -- 2.49.1 From 5cd390f7b1b872a74f7c5f9922c783ecd216338a Mon Sep 17 00:00:00 2001 From: Piper Pentagram Date: Fri, 18 Jul 2025 16:25:26 -0700 Subject: [PATCH 2/4] parse a DNS header! --- CMakeLists.txt | 1 + include/dns_packet_buffer.h | 4 +- include/dns_packets.h | 114 ++++++++++++++++++------------------ include/dns_request.h | 2 +- src/dns_packet_buffer.c | 6 +- src/dns_request.c | 56 +++++++++++++++++- src/udp_server.c | 39 ++++++++---- 7 files changed, 146 insertions(+), 76 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e5144e..23a4478 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 4.0) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_BUILD_TYPE Debug) project(pupdns VERSION 0.1) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/include/dns_packet_buffer.h b/include/dns_packet_buffer.h index 4539e02..5884dad 100644 --- a/include/dns_packet_buffer.h +++ b/include/dns_packet_buffer.h @@ -5,7 +5,7 @@ #define PB_OUT_OF_BOUNDS 9999; struct DNSPacketBuffer { - char buf[PB_SIZE]; + unsigned char buf[PB_SIZE]; size_t pos; }; @@ -13,4 +13,4 @@ struct DNSPacketBuffer *new_dns_packet_buffer(); struct DNSPacketHeader *dns_header(struct DNSPacketBuffer); -char pb_read_byte(struct DNSPacketBuffer *); +unsigned char pb_read_byte(struct DNSPacketBuffer *); diff --git a/include/dns_packets.h b/include/dns_packets.h index f4a1ac8..b80b66c 100644 --- a/include/dns_packets.h +++ b/include/dns_packets.h @@ -1,6 +1,7 @@ #pragma once #include +#define DNS_HEADER_BYTES 12 struct DNSPacketHeader { /* @@ -8,14 +9,34 @@ struct DNSPacketHeader { * a random ID is assigned to query packets. * Response packets must contain the same ID as the query. */ - size_t packet_id : 16; + unsigned int packet_id : 16; + + /* + * RD (Recursion Desired) + * if set by the client, the server should + * attempt to resolve the query recursively + * + * the server may not allow recursive resolution. + * (see RA / Recursion Available) + */ + unsigned int recursion_desired : 1; /* - * QR (Query/Response) - * 0 if the packet is a query - * 1 if the packet is a response + * TC (Truncated) + * if true, this message was truncated. + * usually happens when the response payload + * is larger than the max UDP packet size + * + * if set, the query may be re-issued over TCP for the full payload. */ - size_t is_response : 1; + unsigned int is_truncated : 1; + + /* + * AA (Authoritative Answer) + * + * 1 if the responding server is authoritative for the queried domain + */ + unsigned int is_authoritative : 1; /* * OPCODE (Operation Code) @@ -27,51 +48,14 @@ struct DNSPacketHeader { * * almost always will be 0 */ - size_t opcode : 4; + unsigned int opcode : 4; /* - * AA (Authoritative Answer) - * - * 1 if the responding server is authoritative for the queried domain + * QR (Query/Response) + * 0 if the packet is a query + * 1 if the packet is a response */ - size_t is_authoritative : 1; - - /* - * TC (Truncated) - * if true, this message was truncated. - * usually happens when the response payload - * is larger than the max UDP packet size - * - * if set, the query may be re-issued over TCP for the full payload. - */ - size_t is_truncated : 1; - - /* - * RD (Recursion Desired) - * if set by the client, the server should - * attempt to resolve the query recursively - * - * the server may not allow recursive resolution. - * (see RA / Recursion Available) - */ - size_t recursion_desired : 1; - - - /* - * RA (Recursion Available) - * if set by the server, incicates that the server allows - * recursive queries. - */ - size_t recursion_available : 1; - - /* - * Z (Reserved) - * in the original spec, this is reserved for future use. - * - * in later RFCs, it is used for DNSSEC queries. - * (to be implemented) - */ - size_t z : 3; + unsigned int is_response : 1; /* * RCODE (Response Code) @@ -86,31 +70,47 @@ struct DNSPacketHeader { * 5 - Refused - the nameserver refuses to perform the requested operation. for example, it may refuse a zone transfer. * 6-15 - Reserved for future use */ - size_t response_code : 4; + unsigned int response_code : 4; + + /* + * Z (Reserved) + * in the original spec, this is reserved for future use. + * + * in later RFCs, it is used for DNSSEC queries. + * (to be implemented) + */ + unsigned int z : 3; + + /* + * RA (Recursion Available) + * if set by the server, incicates that the server allows + * recursive queries. + */ + unsigned int recursion_available : 1; /* * QDCOUNT (Question Count) * the number of entries in the question section */ - size_t question_count : 16; + unsigned int question_count : 16; /* * ANCOUNT (Answer Count) * the number of entries in the answer section */ - size_t answer_count : 16; + unsigned int answer_count : 16; /* * NSCOUNT (Nameserver Count) * the number of entries in the authority records section */ - size_t ns_count : 16; + unsigned int ns_count : 16; /* * ARCOUNT (Additional Records Count) * the number of entries in the additional records section */ - size_t ar_count : 16; + unsigned int ar_count : 16; }; @@ -125,26 +125,26 @@ struct RecordPreamble { * TYPE * the record type */ - size_t type : 16; + unsigned int type : 16; /* * CLASS * specifies the class of the data in the RDATA field. * in practice, always set to 1 */ - size_t class : 16; + unsigned int class : 16; /* * TTL (Time-To-Live) * how long a record can be cached before it should be requeried. */ - size_t ttl : 32; + unsigned int ttl : 32; /* * RDLENGTH (Record Data Length) * length of the record-type-specific data. */ - size_t rd_data_length: 16; + unsigned int rd_data_length: 16; }; /* @@ -153,5 +153,5 @@ struct RecordPreamble { */ struct ARecord { struct RecordPreamble preamble; - size_t ip : 32; + unsigned int ip : 32; }; diff --git a/include/dns_request.h b/include/dns_request.h index e5705ff..393c01b 100644 --- a/include/dns_request.h +++ b/include/dns_request.h @@ -7,4 +7,4 @@ struct DNSRequest { struct DNSPacketHeader *header; }; -struct DNSRequest *new_dns_request(struct DNSPacketBuffer *pb); +struct DNSRequest *parse_dns_query(struct DNSPacketBuffer *pb); diff --git a/src/dns_packet_buffer.c b/src/dns_packet_buffer.c index 7d7cd69..eb84f12 100644 --- a/src/dns_packet_buffer.c +++ b/src/dns_packet_buffer.c @@ -16,9 +16,9 @@ struct DNSPacketHeader *dns_header(struct DNSPacketBuffer) // read a single byte and move the cursor one byte forward -char pb_read_byte(struct DNSPacketBuffer *pb) +unsigned char pb_read_byte(struct DNSPacketBuffer *pb) { - char b = pb->buf[pb->pos]; + unsigned char b = pb->buf[pb->pos]; pb->pos++; if (pb->pos >= PB_SIZE) { @@ -32,7 +32,7 @@ char pb_read_byte(struct DNSPacketBuffer *pb) return b; } -char pb_get_byte(struct DNSPacketBuffer *pb) +unsigned char pb_get_byte(struct DNSPacketBuffer *pb) { if (pb->pos >= PB_SIZE) { printf( diff --git a/src/dns_request.c b/src/dns_request.c index b960d9d..034359e 100644 --- a/src/dns_request.c +++ b/src/dns_request.c @@ -1,14 +1,66 @@ #include +#include #include "dns_request.h" +/* + * get k'th bit from byte c + * because of how the DNS spec works, this starts counting from the LEFTMOST bit. + */ +// unsigned int get_bit(unsigned char c, unsigned int k) +// { +// unsigned int flag = 1U; // flag = 0000.....00001 +// +// flag = flag << (8U-k); // flag = 0000...010...000 (shifted k positions) +// +// return c & flag; +// } +/* + * get n bits from byte c starting at position k + * because of how the DNS spec works, this starts counting from the LEFTMOST bit. + */ +// unsigned int get_bits(unsigned char c, unsigned int n, unsigned int k) +// { +// unsigned int ret = 0; +// unsigned int flag = 1U; // flag = 0000.....00001 +// +// for (int j = 0; j < n; j++) { +// unsigned int bit_pos = 8U - k + j; +// ret = ret | get_bit(c, k+j) << bit_pos; +// } +// +// return ret; +// } -struct DNSRequest *new_dns_request(struct DNSPacketBuffer *pb) +struct DNSRequest *parse_dns_query(struct DNSPacketBuffer *pb) { struct DNSRequest *req; req = malloc(sizeof(struct DNSRequest)); - req->header = (struct DNSPacketHeader *) pb->buf; + + /* + * casting the buffer directly into the packet header struct like this + * works okay, but because of the nature of the DNS protocol, it needs + * a lot of manual tweaking. + * + * the struct fields are defined in a weird order (compared to the spec) + * because C wants to read each byte from right to left, + * but DNS fields go left to right. + * + * so for each set of fields adding up to 8 bits of data, i reversed the + * order of the fields in the struct definition so that the fields end up + * with the correct bits off the wire. + * + * DNS also has multi-bit (but less than a byte) fields such as + * opcode and response code. fortunately we can skip these for now, + * since opcode is practically always zero for our purposes, + * and response_code only makes sense in the server response. + * + * but we do need to take care of all the network-order 16-bit fields. + */ + req->header->packet_id = ntohs(req->header->packet_id); + req->header->question_count = ntohs(req->header->question_count); + return req; } diff --git a/src/udp_server.c b/src/udp_server.c index 04de262..0027214 100644 --- a/src/udp_server.c +++ b/src/udp_server.c @@ -2,10 +2,10 @@ #include "dns_packet_buffer.h" #include "dns_request.h" +#include #include #include #include -#include #include #include #include @@ -37,20 +37,37 @@ int start_server() return -1; } - socklen_t len = 0; + while (true) { + socklen_t len = 0; - int n = recvfrom(sockfd, (char *)pb->buf, sizeof(pb->buf), MSG_WAITALL, 0, &len); + int n = recvfrom(sockfd, (char *)pb->buf, sizeof(pb->buf), MSG_WAITALL, 0, &len); - printf("received %d bytes\n", n); + printf("===== NEW REQUEST =====\n"); + printf("received %d bytes\n", n); - struct DNSRequest *req = new_dns_request(pb); + struct DNSRequest *req = parse_dns_query(pb); - printf("packet_id: %x\n", req->header->packet_id); - printf("is_response: %d\n", req->header->is_response); - printf("opcode: %d\n", req->header->opcode); - printf("is_authoritative: %d\n", req->header->is_authoritative); - printf("is_truncated: %d\n", req->header->is_truncated); - printf("recursion_desired: %d\n", req->header->recursion_desired); + printf("packet_id: %x\n", req->header->packet_id); + printf("is_response: %d\n", req->header->is_response); + printf("opcode: %d\n", req->header->opcode); + printf("is_authoritative: %d\n", req->header->is_authoritative); + printf("is_truncated: %d\n", req->header->is_truncated); + printf("recursion_desired: %d\n", req->header->recursion_desired); + printf("recursion_available: %d\n", req->header->recursion_available); + printf("reserved: %d\n", req->header->z); + printf("response_code: %d\n", req->header->response_code); + printf("question_count: %d\n", req->header->question_count); + printf("answer_count: %d\n", req->header->answer_count); + printf("ns_count: %d\n", req->header->ns_count); + printf("ar_count: %d\n", req->header->ar_count); + + // for (int i=0; ibuf[i]); + // } + + free(pb); + free(req); + } close(sockfd); return 0; -- 2.49.1 From 201e15348e9fb4e99bfa7b3066c02a16f0e911c4 Mon Sep 17 00:00:00 2001 From: Piper Pentagram Date: Fri, 18 Jul 2025 16:29:07 -0700 Subject: [PATCH 3/4] remove unused byte munching code --- include/dns_packet_buffer.h | 2 -- src/dns_packet_buffer.c | 36 ------------------------------------ 2 files changed, 38 deletions(-) diff --git a/include/dns_packet_buffer.h b/include/dns_packet_buffer.h index 5884dad..9457e54 100644 --- a/include/dns_packet_buffer.h +++ b/include/dns_packet_buffer.h @@ -11,6 +11,4 @@ struct DNSPacketBuffer { struct DNSPacketBuffer *new_dns_packet_buffer(); -struct DNSPacketHeader *dns_header(struct DNSPacketBuffer); - unsigned char pb_read_byte(struct DNSPacketBuffer *); diff --git a/src/dns_packet_buffer.c b/src/dns_packet_buffer.c index eb84f12..d47f1e5 100644 --- a/src/dns_packet_buffer.c +++ b/src/dns_packet_buffer.c @@ -8,39 +8,3 @@ struct DNSPacketBuffer *new_dns_packet_buffer() buf = malloc(sizeof(struct DNSPacketBuffer)); return buf; } - -struct DNSPacketHeader *dns_header(struct DNSPacketBuffer) -{ - -} - - -// read a single byte and move the cursor one byte forward -unsigned char pb_read_byte(struct DNSPacketBuffer *pb) -{ - unsigned char b = pb->buf[pb->pos]; - pb->pos++; - - if (pb->pos >= PB_SIZE) { - printf( - "WARN: pb_read_byte: advanced packetbuffer cursor pos to %lu, which is greater than the max %d", - pb->pos, - PB_SIZE - ); - } - - return b; -} - -unsigned char pb_get_byte(struct DNSPacketBuffer *pb) -{ - if (pb->pos >= PB_SIZE) { - printf( - "WARN: pb_get_byte: accessing packetbuffer cursor pos %lu, which is greater than the max %d", - pb->pos, - PB_SIZE - ); - } - - return pb->buf[pb->pos]; -} -- 2.49.1 From 2c24b6d7237a58567799d8d939f89457bd29d87e Mon Sep 17 00:00:00 2001 From: Piper Pentagram Date: Fri, 18 Jul 2025 16:33:52 -0700 Subject: [PATCH 4/4] remove more unused byte munching code --- src/dns_request.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/src/dns_request.c b/src/dns_request.c index 034359e..6e0cd57 100644 --- a/src/dns_request.c +++ b/src/dns_request.c @@ -3,36 +3,6 @@ #include "dns_request.h" -/* - * get k'th bit from byte c - * because of how the DNS spec works, this starts counting from the LEFTMOST bit. - */ -// unsigned int get_bit(unsigned char c, unsigned int k) -// { -// unsigned int flag = 1U; // flag = 0000.....00001 -// -// flag = flag << (8U-k); // flag = 0000...010...000 (shifted k positions) -// -// return c & flag; -// } - -/* - * get n bits from byte c starting at position k - * because of how the DNS spec works, this starts counting from the LEFTMOST bit. - */ -// unsigned int get_bits(unsigned char c, unsigned int n, unsigned int k) -// { -// unsigned int ret = 0; -// unsigned int flag = 1U; // flag = 0000.....00001 -// -// for (int j = 0; j < n; j++) { -// unsigned int bit_pos = 8U - k + j; -// ret = ret | get_bit(c, k+j) << bit_pos; -// } -// -// return ret; -// } - struct DNSRequest *parse_dns_query(struct DNSPacketBuffer *pb) { struct DNSRequest *req; -- 2.49.1