DNS-Paket in Python lesen

Nov 29 2020

Diese Frage wurde bereits gestellt, aber die Frage wurde nie vollständig beantwortet und stammt aus dem Jahr 2013. Ich verwende Python-Sockets, um DNS-Pakete zu beobachten. Sie sehen folgendermaßen aus:

b'\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'

Bei der Untersuchung der Grundlagen von DNS-Paketen stellte ich fest, dass sie wie folgt strukturiert sind:

QR | OpCode | AA | TC | RD | RA | Z | AD | CD | RCODE

Ich habe das Paket dann in ASCII dekodiert:

>> str = b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'
>> print(str.decode("ascii"))
wwwgoogletagmanagercom

Dies gibt nur eine einzelne Zeichenfolge mit dem Namen der Adresse zurück und keine anderen Informationen wie oben angegeben. Wo sind die restlichen Daten wie QR und OpCode? Dekodiere ich es falsch?

Um klar zu sein, ich möchte keine externe Bibliothek verwenden und mein Ziel ist es zu verstehen, wie DNS-Pakete strukturiert sind und wie sie dekodiert werden. Mir sind Bibliotheken wie dnslibund bekannt scapy.

Antworten

2 frankr6591 Nov 29 2020 at 21:50

Ich bin kein Socket-Experte. Aus der Referenz - Der DNS-Header besteht aus Bits, nicht aus Bytes. Sie müssen ihn also als Bits analysieren. Verwenden Sie Bytes und Maskenbits. Siehe Beispiel unten. Es ist nicht sicher, was der Inhalt des Headers hdr [12:] ist?

Hier ist ein Beispielcode, der auf der obigen Spezifikation basiert:

def DNStoDict(hdr):
    '''
    Parse QNAME by using length (byte) +data sequence -- final length=0 signifies end of QNAME
    Refer to https://stackoverflow.com/questions/34841206/why-is-the-content-of-qname-field-not-the-original-domain-in-a-dns-message

    1) DNS knows nothing of URLs. DNS is older than the concept of a URL.

    2) Because that's how DNS's wire format works. What you see is the 
       domain name www.mydomain.com, encoded in the DNS binary format. 
       Length+data is a very common way of storing strings in general.
    '''

        # Build DNS dictionary of values... include QNAME
    l = len(hdr)
    argSize = hdr[10]*256+hdr[11]
    dnsDict = dict(ID     = hdr[0]*256+hdr[1],
                   QR     = bool(hdr[2] & int('10000000', 2)),
                   Opcode =     (hdr[2] & int('01111000', 2))>>3,
                   AA     = bool(hdr[2] & int('00000100', 2)),
                   TC     = bool(hdr[2] & int('00000010', 2)),
                   RD     = bool(hdr[2] & int('00000001', 2)),
                   RA     = bool(hdr[3] & int('10000000', 2)),
                   Z      = bool(hdr[3] & int('01000000', 2)),
                   AD     = bool(hdr[3] & int('00100000', 2)),
                   CD     = bool(hdr[3] & int('00010000', 2)),
                   RCode  = bool(hdr[3] & int('00001111', 2)),
                   QDCOUNT = hdr[4]*256+hdr[5],
                   ANCOUNT = hdr[6]*256+hdr[7],
                   NSCOUNT = hdr[8]*256+hdr[9],
                   ARCOUNT = argSize,
                   QTYPE   = hdr[l-4]*256+hdr[l-3],
                   QCLASS   = hdr[l-2]*256+hdr[l-2])

    # Parse QNAME
    n = 12
    mx = len(hdr)
    qname = ''

    while n < mx:
        try:
            qname += hdr[n:n+argSize].decode() + '.'

            n += argSize
            argSize = int(hdr[n])
            n += 1
            if argSize == 0 : 
                break
        except Exception as err:
            print("Parse Error", err, n, qname)
            break
    dnsDict['QNAME'] = qname[:-1]
    return dnsDict

# Sample DNS Packet Data 
hdr = b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'


# Parse out the QNAME
dnsDict = DNStoDict(hdr)


print("\n DNS PACKET dictionary")
print(dnsDict)

AUSGABE:

DNS PACKET-Wörterbuch {'ID': 257, 'QR': False, 'Opcode': 0, 'AA': False, 'TC': False, 'RD': False, 'RA': False, 'Z': Falsch, 'AD': Falsch, 'CD': Falsch, 'RCode': Falsch, 'QDCOUNT': 0, 'ANCOUNT': 0, 'NSCOUNT': 0, 'ARCOUNT': 3, 'QTYPE': 1, 'QCLASS': 0, 'QNAME': 'www.googletagmanager.com'}

Pyhon-Bit-Manipulation

Beziehen auf

  1. https://wiki.python.org/moin/BitManipulation
  2. http://www.java2s.com/Tutorials/Python/Data_Types/How_to_create_integer_in_Python_octal_binary_hexadecimal_and_long_integer.htm

Ein Byte ( b'xxxx') steht für 4 Bytes. Jedes Byte besteht aus 8 Bits

0000 0000 - 0 0000 0001 - 1 0000 0010 - 2 0000 0100 - 4 0000 1000 - 8 0001 0000 - 16 0010 0000 - 32 0100 0000 - 64 1000 0000 - 128 1111 1111 - 255 (128 + 64 + 32 + 16 + 8 + 4 + 2 + 1)

In Python konvertiert das Format int ('00000111', 2) ein Array von Strings ['0' / '1'] mit Modulo 2 (Bits). Dies gibt den Wert 7 modulo 10 zurück.

Referenz-DNS-Header: https://www2.cs.duke.edu/courses/fall16/compsci356/DNS/DNS-primer.pdf http://www.networksorcery.com/enp/protocol/dns.htm