Strange test #YC9, C++

I’m stuck(???) on Stage #YC9.

There is a very strange thing I found for me. Some days ago, I successfully passed test #YC9 about parsing compressed packets in DNS server challenge. Today, I started doing next stage, #GT1, about forwarding messages and noticed, that my code passes #GT1 but doesn’t pass #YC9. After some hours of debugging, I found that problem is in parsing compressed question. Also, I found that my program breaks at pointer octet, and I logged it. It looks like this: 11000000, so the pointer is 0 and programs goes to the begging of the buffer, thinking that it is a label sequence.

I rolled back my code to the last commit that passes previous stage (#YC9), and the programs had broken again! In my logs below you can see that octet at 47 index is 11000000, and if you calculate octets, you will see that exactly this octet must be a pointer (12 to header + 1 + 3 + 1 + 17 + 1 + 3 + 1 + 2 + 2 +1 + 3 = 47, so 46th octet is the f, last octet of the def label). It goes after label, it is not ASCII symbol (so there is no doubts about this octet is not a part of label) and it begins with two 1.

In newer code (not as below) I found myself believing that I don’t modify the buffer I parsing by marking every buffer pointer by const, so the buffer is exactly how it was read.

So my question is: other people (and me, lol) passed this test. Can someone check him test? Am I doing something wrong?

Here are my logs (previous tests have been passed):

[33m[tester::#YC9] [94mRunning tests for Stage #YC9 (Parse compressed packet)
[33m[tester::#YC9] [94mStarting DNS server on 127.0.0.1:2053
[33m[tester::#YC9] [94mConnecting to 127.0.0.1:2053 using UDP
[33m[your_program] Logs from your program will appear here!
[33m[your_program] 
[33m[your_program] Request question labels (1):
[33m[your_program] codecrafters.io.
[33m[your_program] Written 64 bytes
[33m[tester::#YC9] [94mQuerying the following in the same request (Messages with >> prefix are part of this log)
[33m[tester::#YC9] [94m>> ;abc.longassdomainname.com.	IN	 A
[33m[tester::#YC9] [94m>> ;def.longassdomainname.com.	IN	 A
[33m[tester::#YC9] [91mDNS query failed: dns: bad rdata.
[33m[tester::#YC9] [91mIf you are seeing this after a while then it is likely that your server is not responding with appropriate id
[33m[tester::#YC9] [91mTest failed (try setting 'debug: true' in your codecrafters.yml to see more details)
[33m[your_program] 
[33m[your_program] BYTE: 47
[33m[your_program] BITSET: 11000000
[33m[your_program] PTR: 0
[33m[your_program] Request question labels (2):
[33m[your_program] abc.longassdomainname.com.
[33m[your_program] def.�

BYTE and so on was written when my code faced pointer byte. Below, you can see my parsing code:


inline bool byte_get_bit(std::byte byte, char bit) {
    std::byte mask = std::byte{1} << bit;
    return (byte & mask) != std::byte{0};
}


DNS::dns_message DNS::dns_message::parse_from_buffer(void *buffer) {
    dns_message result {};

    std::byte *byte_buf = reinterpret_cast<std::byte*>(buffer);

    result.packet_id = ntohs(reinterpret_cast<std::uint16_t*>(byte_buf)[0]);
    result.flag_qr = byte_get_bit(byte_buf[2], 7);
    result.opcode = std::to_integer<std::uint8_t>((byte_buf[2] & std::byte{0b0111'1000}) >> 3);
    result.flag_aa = byte_get_bit(byte_buf[2], 2);
    result.flag_tc = byte_get_bit(byte_buf[2], 1);
    result.flag_rd = byte_get_bit(byte_buf[2], 0);
    result.flag_ra = byte_get_bit(byte_buf[3], 7);
    result.reserved = std::to_integer<std::uint8_t>((byte_buf[3] & std::byte{0b0111'0000}) >> 4);
    result.rcode = std::to_integer<std::uint8_t>(byte_buf[3] & std::byte{0b0000'1111});

    std::uint16_t qdcount = ntohs(reinterpret_cast<std::uint16_t*>(byte_buf+4)[0]);
    std::uint16_t ancount = ntohs(reinterpret_cast<std::uint16_t*>(byte_buf+6)[0]); // to parse answers
    result.nscount_ = ntohs(reinterpret_cast<std::uint16_t*>(byte_buf+8)[0]);
    result.arcount_ = ntohs(reinterpret_cast<std::uint16_t*>(byte_buf+10)[0]);

    std::byte *read_byte = byte_buf + 12;
    for (int i = 0; i < qdcount; ++i) {
        std::byte *old_read_byte_compressed;
        bool read_compressed = false;
        dns_question q;

        bool first_write = true;
        while (*read_byte != std::byte{0}) { // WARNING: memory issue if invalid packet format
            if ((*read_byte & std::byte{0xC0}) == std::byte{0xC0}) {
                std::uint8_t ptr = std::to_integer<std::uint8_t>(*read_byte & std::byte{0x3F});

                if (first_write) {
                    std::cout << "BYTE: " << read_byte - byte_buf << std::endl;
                    std::cout << "BITSET: " << std::bitset<8>((std::uint8_t)*read_byte) << std::endl;
                    std::cout << "PTR: " << (int)ptr << std::endl;
                    first_write = false;
                }

                old_read_byte_compressed = read_byte;
                read_byte = byte_buf + ptr;
                read_compressed = true;
            } 

            int label_size = std::to_integer<int>(*read_byte++);
            std::string label(label_size, '\0');
            for (char &ch : label) ch = std::to_integer<int>(*read_byte++);
            q.labels.push_back(label);
        }

        if (read_compressed) read_byte = old_read_byte_compressed;

        ++read_byte; // skip zero octet or pointer octet

        q.qtype = ntohs(reinterpret_cast<std::uint16_t*>(read_byte)[0]);
        read_byte += 2;

        q.qclass = ntohs(reinterpret_cast<std::uint16_t*>(read_byte)[0]);
        read_byte += 2;

        result.questions.push_back(q);
    }

    return result;
}

It is used simply like this:

        // (In while)
        // Receive data
        bytesRead = recvfrom(udpSocket, buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr*>(&clientAddress), &clientAddrLen);
        if (bytesRead == -1) {
            perror("Error receiving data");
            break;
        }

        DNS::dns_message request = DNS::dns_message::parse_from_buffer(buffer);

I feel that there must be something simple, but… I can’t figure it out.

Hi @Germenzi, thanks for highlighting the issue! We’ll investigate it and get back to you as soon as possible.