I’m stuck on Stage #YC9.
I have written code to parse the compressed question and given response accordingly. I am able to parse the question and response but when I run codecrafters test
, it gives error saying: ‘buffer size too small.’
Here are my logs:
[tester::#YC9] Running tests for Stage #YC9 (Parse compressed packet)
[tester::#YC9] Starting DNS server on 127.0.0.1:2053
[tester::#YC9] Running program
[tester::#YC9] DNS resolver listening on 127.0.0.1:5354
[tester::#YC9] Connecting to 127.0.0.1:2053 using UDP
[your_program] 2024-11-17 20:37:35 | line: 12 | app.logger/main | INFO: Logs from your program will appear here!
[tester::#YC9] Querying the following in the same request (Messages with >> prefix are part of this log)
[tester::#YC9] >> ;abc.longassdomainname.com. IN A
[tester::#YC9] >> ;def.longassdomainname.com. IN A
[tester::#YC9] Sending Request: (Messages with >>> prefix are part of this log)
[tester::#YC9] >>> ;; opcode: QUERY, status: NOERROR, id: 33217
[tester::#YC9] >>> ;; flags: rd; QUERY: 2, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
[tester::#YC9] >>>
[tester::#YC9] >>> ;; QUESTION SECTION:
[tester::#YC9] >>> ;abc.longassdomainname.com. IN A
[tester::#YC9] >>> ;def.longassdomainname.com. IN A
[tester::#YC9] >>>
[your_program] 2024-11-17 20:37:36 | line: 22 | app.logger/main | DEBUG: incoming raw data: b'\xd0\xed\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x0ccodecrafters\x02io\x00\x00\x01\x00\x01'
[your_program] 2024-11-17 20:37:36 | line: 24 | app.logger/main | DEBUG: parsed header dict: (53485, 256, 1, 0, 0, 0)
[your_program] 2024-11-17 20:37:36 | line: 26 | app.logger/main | DEBUG: parsed questions: [{'qname': 'codecrafters.io', 'qtype': 1, 'qclass': 1}]
[your_program] 2024-11-17 20:37:36 | line: 28 | app.logger/main | DEBUG: parsed flags: {'qr': 0, 'opcode': 0, 'aa': 0, 'tc': 0, 'rd': 1, 'ra': 0, 'z': 0, 'rcode': 0}
[your_program] 2024-11-17 20:37:36 | line: 72 | app.logger/main | DEBUG: response header: {'id': 53485, 'qr': 1, 'opcode': 0, 'aa': 0, 'tc': 0, 'rd': 1, 'ra': 0, 'z': 0, 'rcode': 0, 'qdcount': 1, 'ancount': 1, 'nscount': 0, 'arcount': 0}
[your_program] 2024-11-17 20:37:36 | line: 73 | app.logger/main | DEBUG: response questions: [{'qname': 'codecrafters.io', 'qtype': 1, 'qclass': 1}]
[your_program] 2024-11-17 20:37:36 | line: 74 | app.logger/main | DEBUG: response answers: [{'name': 'codecrafters.io', 'type': 1, 'domain_class': 1, 'ttl': 60, 'rdlength': 4, 'rdata': '8.8.8.8'}]
[your_program] 2024-11-17 20:37:36 | line: 76 | app.logger/main | DEBUG: response bytes: b'\xd0\xed\x81\x00\x00\x01\x00\x01\x00\x00\x00\x00\x0ccodecrafters\x02io\x00\x00\x01\x00\x01\x0ccodecrafters\x02io\x00\x00\x01\x00\x01\x00\x00\x00<\x00\x048.8.8.8'
[your_program] 2024-11-17 20:37:36 | line: 22 | app.logger/main | DEBUG: incoming raw data: b'\x81\xc1\x01\x00\x00\x02\x00\x00\x00\x00\x00\x00\x03abc\x11longassdomainname\x03com\x00\x00\x01\x00\x01\x03def\xc0\x10\x00\x01\x00\x01'
[your_program] 2024-11-17 20:37:36 | line: 24 | app.logger/main | DEBUG: parsed header dict: (33217, 256, 2, 0, 0, 0)
[your_program] 2024-11-17 20:37:36 | line: 26 | app.logger/main | DEBUG: parsed questions: [{'qname': 'abc.longassdomainname.com', 'qtype': 1, 'qclass': 1}, {'qname': 'def.longassdomainname', 'qtype': 1, 'qclass': 1}]
[your_program] 2024-11-17 20:37:36 | line: 28 | app.logger/main | DEBUG: parsed flags: {'qr': 0, 'opcode': 0, 'aa': 0, 'tc': 0, 'rd': 1, 'ra': 0, 'z': 0, 'rcode': 0}
[your_program] 2024-11-17 20:37:36 | line: 72 | app.logger/main | DEBUG: response header: {'id': 33217, 'qr': 1, 'opcode': 0, 'aa': 0, 'tc': 0, 'rd': 1, 'ra': 0, 'z': 0, 'rcode': 0, 'qdcount': 2, 'ancount': 2, 'nscount': 0, 'arcount': 0}
[your_program] 2024-11-17 20:37:36 | line: 73 | app.logger/main | DEBUG: response questions: [{'qname': 'abc.longassdomainname.com', 'qtype': 1, 'qclass': 1}, {'qname': 'def.longassdomainname', 'qtype': 1, 'qclass': 1}]
[tester::#YC9] DNS query failed: dns: buffer size too small.
[tester::#YC9] If you are seeing this after a while then it is likely that your server is not responding with appropriate id
[tester::#YC9] Test failed
[tester::#YC9] Terminating program
[tester::#YC9] Shutting down DNS resolver server...
[your_program] 2024-11-17 20:37:36 | line: 74 | app.logger/main | DEBUG: response answers: [{'name': 'abc.longassdomainname.com', 'type': 1, 'domain_class': 1, 'ttl': 60, 'rdlength': 4, 'rdata': '8.8.8.8'}, {'name': 'def.longassdomainname', 'type': 1, 'domain_class': 1, 'ttl': 60, 'rdlength': 4, 'rdata': '9.9.9.9'}]
[your_program] 2024-11-17 20:37:36 | line: 76 | app.logger/main | DEBUG: response bytes: b'\x81\xc1\x81\x00\x00\x02\x00\x02\x00\x00\x00\x00\x03abc\x11longassdomainname\x03com\x00\x00\x01\x00\x01\x03def\x11longassdomainname\x00\x00\x01\x00\x01\x03abc\x11longassdomainname\x03com\x00\x00\x01\x00\x01\x00\x00\x00<\x00\x048.8.8.8\x03def\x11longassdomainname\x00\x00\x01\x00\x01\x00\x00\x00<\x00\x049.9.9.9'
[tester::#YC9] Program terminated successfully
And here’s a snippet of my code (question parser):
import struct
class Question:
def __init__(self, qname, qtype, qclass):
self.qname: str = qname
self.qtype: int = qtype
self.qclass: int = qclass
def to_bytes(self):
return self.__encode_qname(self.qname) + struct.pack(
">HH", self.qtype, self.qclass
)
def __encode_qname(self, qname):
parts = qname.split(".")
result = b""
for part in parts:
result += struct.pack("B", len(part)) + part.encode("ascii")
return result + b"\x00"
def __parse_qname(self, data, start, parsed_qname={}):
"""
I will parse the byte data and retur the qname with the next start position
"""
parts = []
while True:
length = data[start]
if length == 0:
break
if (0xC0 & length) == 0xC0:
pointer = ((length & 0x3F) << 8) | data[start + 1]
if parsed_qname.get(pointer, None):
parts.append(parsed_qname[pointer])
else:
qname, _ = self.__parse_qname(Question, data, pointer, parsed_qname)
parts.append(qname)
start += 1
break
label = data[start + 1 : start + 1 + length]
parts.append(data[start + 1 : start + 1 + length].decode("ascii"))
parsed_qname[start] = label.decode("ascii")
start += 1 + length
return ".".join(parts), start + 1, parsed_qname
def __parse_questions(self, data, start=12, parsed_qname={}, questions=[]):
if start >= len(data):
return questions
qname, start, new_parsed_qname = self.__parse_qname(
Question, data=data, start=start, parsed_qname=parsed_qname
)
qtype, qclass = struct.unpack(">HH", data[start : start + 4])
questions.append(Question(qname, qtype, qclass))
return self.__parse_questions(
Question, data, start + 4, {**parsed_qname, **new_parsed_qname}, questions
)
@staticmethod
def from_bytes(data):
# qname, start = Question.__parse_qname(Question, data, 0)
# qtype, qclass = struct.unpack(">HH", data[start : start + 4])
# return (qname, qtype, qclass)
return Question.__parse_questions(
Question, data=data, parsed_qname={}, questions=[]
)