[DNS SERVER] Stage 5 (#BF2) "Write question section"

I’m stuck on Stage (#BF2), where trying to send the DNS response.

Below the output of the packet, which fails the “Type” field check in the “Question” section:

[tester::#BF2] Querying `A` record for codecrafters.io.
[your_program] ======================================
[your_program]      DNS REQUEST/RESPONSE DUMP
[your_program] ======================================
[your_program]
[your_program] [ HEADER FLAGS ]
[your_program]  ID: 1234
[your_program]  QR (Query/Response): 1 (Response)
[your_program]  OPCODE:              0 (Query)
[your_program]  RCODE:               0 (No Error (Success))
[your_program]  AA (Authoritative):  0
[your_program]  TC (Truncated):      0
[your_program]  RD (Recursion Des.): 0
[your_program]  RA (Recursion Av.):  1
[your_program]  Z  (Reserved):       0
[your_program]  AD (Auth. Data):     0
[your_program]  CD (Check Disabled): 0
[your_program]  Questions (QDCOUNT): 1
[your_program]  Answers (ANCOUNT): 0
[your_program]  Authority (NSCOUNT): 0
[your_program]  Additional (ARCOUNT): 0
[your_program]
[your_program] [ QUESTION ]
[your_program]  QNAME
                                     codecraftersio
[your_program]  QTYPE                A
[your_program]  QCLASS               INTERNET ADDRESS
[your_program] ---------------------------------
[tester::#BF2] Expected question type to be 1 got 0
[tester::#BF2] Test failed

here the code responsible to format a valid response:

void format_response(Tube *client) {
    dns_h_flags flags = {0};
    client->response.header.id = htons(1234); 
    flags.qr     = DNS_RESPONSE;
    flags.rd     = 0;
    flags.ra     = 1;
    flags.rcode  = DNS_RCODE_NO_ERROR;
    client->response.header.flags   = htons(dns_pack_flags(flags));
    client->response.header.qdcount = htons(1); 
    client->response.header.ancount = htons(0);
    client->response.header.nscount = htons(0); 
    client->response.header.arcount = htons(0);
    char formatted_qname[MAX_DOMAIN_SIZE];
    size_t qname_len = dns_encode_qname("codecrafters.io", formatted_qname);
    memcpy(client->response.question.qname, formatted_qname, qname_len);
    client->response.question.qtype  = htons(DNS_QTYPE_A);
    client->response.question.qclass = htons(DNS_IN);
}

For the sake of completeness, below is the full format of the package definition:

#pragma pack(push, 1)
typedef struct dns_header_t {
    uint16_t id;       
    uint16_t flags; 
    uint16_t qdcount;  
    uint16_t ancount;  
    uint16_t nscount;  
    uint16_t arcount;  
} dns_header;
#pragma pack(pop)

#define MAX_DOMAIN_SIZE 255
typedef struct dns_question_t {
	char qname[MAX_DOMAIN_SIZE];
	uint16_t qtype;
	uint16_t qclass;
} dns_question;

typedef struct dns_query_t {
    dns_header header;
    dns_question question;
} dns_query;

Thanks in advance for your help.

Hey @h3r0cybersec, I tried printing client.response like this:

Looks like everything after codecrafter.io is 0:

Let me know if you’d like a hand digging deeper into this!

I’m a little rusty on the C language, but I figured it out. I discovered that it’s almost never necessary to send an entire structure over the network because of the padding added by the compiler.

...
size_t format_response(Tube *client) {
    // Puntatore alla posizione corrente nel buffer di invio
    uint8_t *current_ptr = client->send_buffer;
    
    // Variabili temporanee per i valori NBO dell'header
    dns_header header_nbo = {0};
    dns_h_flags flags = {0};
    
    // --- 1. COSTRUZIONE DELL'HEADER (12 byte) ---

    // a) ID: (NBO) - Usa l'ID della richiesta
    header_nbo.id = htons(1234); // client->request.header.id

    // b) Flags: (HBO -> Pack -> NBO)
    flags.qr    = DNS_RESPONSE;
    flags.rd    = (client->request.header.flags >> 8) & 0x1; 
    flags.ra    = 1; // Recursion Available (questo è il resolver)
    flags.rcode = 0; // Successo (DNS_RCODE_NO_ERROR)
    header_nbo.flags = htons(dns_pack_flags(flags));

    // c) Contatori: (NBO)
    header_nbo.qdcount = htons(1);
    header_nbo.ancount = htons(0); // FIXME: Diventerà htons(1) si aggiunge'RR
    header_nbo.nscount = htons(0);
    header_nbo.arcount = htons(0);

    // Copia l'header (12 byte) nel buffer RAW
    memcpy(current_ptr, &header_nbo, sizeof(dns_header));
    current_ptr += sizeof(dns_header);
    
    // --- 2. COSTRUZIONE DELLA SEZIONE QUESTION ---
    
    // a) QNAME (Etichettato):
    char encoded_qname_buffer[MAX_DOMAIN_SIZE]; // Buffer locale sicuro
    
    // Usiamo il QNAME DECODIFICATO della RICHIESTA (es. "codecrafters.io")
    size_t qname_len = dns_encode_qname(client->request.question.qname, encoded_qname_buffer);
    
    // Copia il QNAME etichettato nel buffer RAW
    memcpy(current_ptr, encoded_qname_buffer, qname_len);
    current_ptr += qname_len;
    
    // b) QTYPE (NBO):
    uint16_t qtype_nbo = htons(client->request.question.qtype); // Copia QTYPE dalla richiesta
    memcpy(current_ptr, &qtype_nbo, sizeof(uint16_t));
    current_ptr += sizeof(uint16_t);

    // c) QCLASS (NBO):
    uint16_t qclass_nbo = htons(client->request.question.qclass); // Copia QCLASS dalla richiesta
    memcpy(current_ptr, &qclass_nbo, sizeof(uint16_t));
    current_ptr += sizeof(uint16_t);

    // --- 3. SEZIONE ANSWER (TODO: da aggiungere qui) ---

    // --- 4. CALCOLO DIMENSIONE FINALE E RITORNO ---
    
    // La dimensione finale è la differenza tra il puntatore finale e l'inizio del buffer
    size_t response_size = current_ptr - (uint8_t *)client->send_buffer;
    printf("DNS response size (RAW): %zu\n", response_size);
    for (size_t b = 0; b < response_size; b++) {
    	printf("%02x ", client->send_buffer[b]);
    }
    printf("\n");
    return response_size;
}
...

In practice, I copy the bytes I need into an internal buffer in the “Tube” structure and then send this correctly formatted buffer.

Thanks you for your help and long life to C language!

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.