paulc / dnslib

A Python library to encode/decode DNS wire-format packets
https://github.com/paulc/dnslib
BSD 2-Clause "Simplified" License
295 stars 84 forks source link

ID mismatch with dig #26

Closed imane0897 closed 3 years ago

imane0897 commented 3 years ago

I'm trying to use dnslib for a DNS cache server. The minimal environment is as:

import socket
from dns_resolve import *

IP = '127.0.0.1'
PORT = 8123

def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((IP, PORT))
    print('DNS is listening on {0}:{1} ...'.format(IP, PORT))

    while True:
        data, addr = sock.recvfrom(1024)
        q = DNSRecord.parse(data)
        sock.sendto(str(q).encode('utf-8'), addr)

if __name__ == '__main__':
    main()

And when I do dig in shell as:

dig @127.0.0.1 -p 8123 google.com

The reply is:

;; Warning: ID mismatch: expected ID 39351, got 15163
;; Warning: query response not set

I print q, and the ID is actually the expected ID 39351. I've tried to manually set the ID, new a DNSRecord with specific values, new a DNSHeader, restart the Python server, but I still got the warning which says expected ID xxx, got 15163. I have no idea where 15163 comes from.

PS. I also tried to change the while part to:

   while True:
        data, addr = sock.recvfrom(1024)
        sock.sendto(data, addr)

In this case, dig can get the reply with the correct ID number.

leafcoder commented 3 years ago

Actually your demo code is wrong.

Wrong demo:

    ...
    while True:
        data, addr = sock.recvfrom(1024)
        q = DNSRecord.parse(data)
        sock.sendto(str(q).encode('utf-8'), addr)  # wrong
    ....

Correct demo:

import socket
from dnslib import DNSRecord

IP = '127.0.0.1'
PORT = 8123

def send_udp(data, host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(data, (host, port))
    response, server = sock.recvfrom(8192)
    sock.close()
    return response

def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((IP, PORT))
    print('DNS is listening on {0}:{1} ...'.format(IP, PORT))

    while True:
        data, addr = sock.recvfrom(8192)
        resp = send_udp(data, '8.8.8.8', 53)
        # q = DNSRecord.parse(resp)  # add cache or do other things by dnslib
        sock.sendto(resp, addr)

if __name__ == '__main__':
    main()

By the way, your demo is not a DNS cache server. It looks like a DNS proxy server.

If you're trying to write a DNS cache server. Your can upgrade the correct demo.

imane0897 commented 3 years ago

@leafcoder Thank you!

I 'm trying to write a DNS cache server, because I need to send DNS queries to different public DNS servers according to the suffixes of the domain names.

I tested you code, it works fine. But when I uncomment the q = DNSRecord.parse(resp) line, and changed the next line to sock.sendto(str(q).encode('utf-8'), addr) I get the problem again:

;; Warning: ID mismatch: expected ID 40293, got 15163
;; Warning: query response not set
leafcoder commented 3 years ago

May be you're looking for this.

while True:
    data, addr = sock.recvfrom(8192)
    resp = send_udp(data, '8.8.8.8', 53)
    q = DNSRecord.parse(resp)
    # do something
    r = q.pack()
    sock.sendto(r, addr)
imane0897 commented 3 years ago

@leafcoder That's exactly what I need! Thanks a lot. I'll close this issue.