Lire le paquet DNS en python

Nov 29 2020

Cette question avait déjà été posée mais la question n'a jamais été entièrement traitée et date de 2013. J'utilise des sockets python pour observer les paquets DNS, ils se présentent comme suit:

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

En recherchant les principes fondamentaux des paquets DNS, j'ai trouvé qu'ils sont structurés comme suit:

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

J'ai ensuite décodé le paquet en ASCII:

>> 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

Cela ne renvoie qu'une seule chaîne avec le nom de l'adresse, et pas d'autres informations comme spécifié ci-dessus. Où est le reste des données, comme le QR et l'OpCode? Suis-je en train de le décoder incorrectement?

Pour être clair, je ne souhaite pas utiliser de bibliothèque externe et mon objectif est de comprendre comment les paquets DNS sont structurés et comment les décoder; Je connais des bibliothèques telles que dnslibet scapy.

Réponses

2 frankr6591 Nov 29 2020 at 21:50

Je ne suis pas un expert en socket. From reference - L'en-tête DNS est composé de bits et non d'octets ... vous devez donc l'analyser sous forme de bits. Utilisez des octets et des bits de masque. Voir l'exemple ci-dessous. Il ne sait pas quel est le contenu de l'en-tête hdr [12:]?

Voici un exemple de code basé sur les spécifications ci-dessus:

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)

PRODUCTION:

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

Manipulation de bits Pyhon

Faire référence à

  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

Un octet ( b'xxxx') représente 4 octets. Chaque octet est composé de 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)

En python, le format int ('00000111', 2) est de convertir un tableau de chaînes ['0' / '1'] en utilisant modulo 2 (bits). Cela renvoie la valeur 7 modulo 10.

En-tête DNS de référence: https://www2.cs.duke.edu/courses/fall16/compsci356/DNS/DNS-primer.pdf http://www.networksorcery.com/enp/protocol/dns.htm