Dealing with multiple queries has been very challening for me. I haven’t been able to make it work with tthe following timeout error I get on #YC9 (after #GT1 passes the test)
remote: [tester::#YC9] Running tests for Stage #YC9 (Parse compressed packet)
remote: [tester::#YC9] Starting DNS server on 127.0.0.1:2053
remote: [tester::#YC9] Running program
remote: [tester::#YC9] DNS resolver listening on 127.0.0.1:5354
remote: [tester::#YC9] Connecting to 127.0.0.1:2053 using UDP
remote: [tester::#YC9] Querying the following in the same request (Messages with >> prefix are part of this log)
remote: [tester::#YC9] >> ;abc.longassdomainname.com. IN A
remote: [tester::#YC9] >> ;def.longassdomainname.com. IN A
remote: [tester::#YC9] Sending Request: (Messages with >>> prefix are part of this log)
remote: [tester::#YC9] >>> ;; opcode: QUERY, status: NOERROR, id: 38476
remote: [tester::#YC9] >>> ;; flags: rd; QUERY: 2, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
remote: [tester::#YC9] >>>
remote: [tester::#YC9] >>> ;; QUESTION SECTION:
remote: [tester::#YC9] >>> ;abc.longassdomainname.com. IN A
remote: [tester::#YC9] >>> ;def.longassdomainname.com. IN A
remote: [tester::#YC9] >>>
remote: [tester::#YC9] DNS query failed: read udp 127.0.0.1:39437->127.0.0.1:2053: i/o timeout.
remote: [tester::#YC9] If you are seeing this after a while then it is likely that your server is not responding with appropriate id
remote: [tester::#YC9] Test failed
remote: [tester::#YC9] Terminating program
remote: [tester::#YC9] Shutting down DNS resolver server...
remote: [tester::#YC9] Program terminated successfully
This is my code. How can I improve it so I don’t get that timeout error?
import * as dgram from "dgram";
import { DNSMessageHeader } from "./dnsMessageHeader";
const args = process.argv.slice(2);
const resolverArg = args.find(arg => arg === args[1]);
const [resolverIp, resolverPortStr] = resolverArg?.split(':') ?? [];
const udpSocket = dgram.createSocket("udp4");
const forwardSocket = dgram.createSocket("udp4");
udpSocket.bind(2053, "127.0.0.1");
function parseDNSQuestion(data: Buffer, offset: number): [Buffer, number] {
const labels = [];
let currentOffset = offset;
while (true) {
const length = data[currentOffset];
if (length === 0) {
labels.push(Buffer.from([0]));
currentOffset++;
break;
}
if ((length & 0xc0) === 0xc0) {
const pointer = ((length & 0x3f) << 8) | data[currentOffset + 1];
const [compressedLabel] = parseDNSQuestion(data, pointer);
labels.push(compressedLabel.slice(0, -5));
currentOffset += 2;
const typeClass = data.slice(currentOffset, currentOffset + 4);
currentOffset += 4;
return [Buffer.concat([...labels, Buffer.from([0]), typeClass]), currentOffset];
}
labels.push(data.slice(currentOffset, currentOffset + length + 1));
currentOffset += length + 1;
}
const typeClass = data.slice(currentOffset, currentOffset + 4);
currentOffset += 4;
return [Buffer.concat([...labels, typeClass]), currentOffset];
}
udpSocket.on("message", (data: Buffer, remoteInfo: dgram.RemoteInfo) => {
const queryId = data.readUInt16BE(0);
const qdcount = data.readUInt16BE(4);
if (resolverArg) {
if (qdcount > 1) {
let offset = 12;
const responses: Buffer[] = [];
for (let i = 0; i < qdcount; i++) {
const [question, newOffset] = parseDNSQuestion(data, offset);
const singleHeader = Buffer.from([
...data.slice(0, 2),
data[2], data[3],
0x00, 0x01,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00
]);
const singlePacket = Buffer.concat([singleHeader, question]);
forwardSocket.send(singlePacket, Number(resolverPortStr), resolverIp);
forwardSocket.once("message", (response) => {
responses.push(response);
if (responses.length === qdcount) {
const combinedHeader = Buffer.from([
...data.slice(0, 2),
responses[0][2], responses[0][3],
0x00, qdcount,
0x00, qdcount,
0x00, 0x00,
0x00, 0x00
]);
const questions = [];
const answers = [];
for (let j = 0; j < responses.length; j++) {
const resp = responses[j];
const [question, qOffset] = parseDNSQuestion(data, 12 + (j * 16));
questions.push(question);
const answer = resp.slice(qOffset);
answers.push(answer);
}
const finalResponse = Buffer.concat([
combinedHeader,
...questions,
...answers
]);
udpSocket.send(finalResponse, remoteInfo.port, remoteInfo.address);
}
});
offset = newOffset;
}
} else {
forwardSocket.send(data, Number(resolverPortStr), resolverIp);
forwardSocket.once("message", (response) => {
udpSocket.send(response, remoteInfo.port, remoteInfo.address);
});
}
return;
}
const header = new DNSMessageHeader();
header.setId(queryId);
header.setQR(1);
header.setQDCount(qdcount);
header.setANCount(qdcount);
header.setRD(1);
let offset = 12;
const questions = [];
const answers = [];
for (let i = 0; i < qdcount; i++) {
const [question, newOffset] = parseDNSQuestion(data, offset);
questions.push(question);
const answer = Buffer.concat([
question.slice(0, -4),
Buffer.from([
0x00, 0x01,
0x00, 0x01,
0x00, 0x00, 0x00, 0x3c,
0x00, 0x04,
151, 101, 65, 140
])
]);
answers.push(answer);
offset = newOffset;
}
const response = Buffer.concat([header.toBuffer(), ...questions, ...answers]);
udpSocket.send(response, remoteInfo.port, remoteInfo.address);
});