PythonでDNSパケットを読み取る

Nov 29 2020

この質問は以前に尋ねられたことがありますが、質問は完全には解決されておらず、2013年のものです。私はPythonソケットを使用してDNSパケットを監視していますが、次のように表示されます。

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

DNSパケットの基本を調べたところ、次のように構成されていることがわかりました。

QR | オペコード| AA | TC | RD | RA | Z | AD | CD | RCODE

次に、パケットを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

これは、アドレスの名前を持つ単一の文字列のみを返し、上記で指定された他の情報は返しません。QRやOpCodeなどの残りのデータはどこにありますか?正しくデコードしていませんか?

明確にするために、私は外部ライブラリを使用したくありません。私の目的は、DNSパケットがどのように構造化され、どのようにデコードされるかを理解することです。dnslibやなどのライブラリを知っていますscapy

回答

2 frankr6591 Nov 29 2020 at 21:50

私はソケットの専門家ではありません。参照から-DNSヘッダーはバイトではなくビットで構成されているため、ビットとして解析する必要があります。バイトとマスクビットを使用します。以下のサンプルを参照してください。ヘッダーhdr [12:]の内容がわかりませんか?

上記の仕様に基づくサンプルコードは次のとおりです。

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)

出力:

DNSパケット辞書{'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'}

Pyhonビット操作

参照する

  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

バイト(b'xxxx')は4バイトを表します。各バイトは8ビットで構成されています

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)

Pythonでは、int( '00000111'、2)の形式は、モジュロ2(ビット)を使用して文字列の配列['0' / '1']を変換します。これは、10を法とする値7を返します。

参照DNSヘッダー: https://www2.cs.duke.edu/courses/fall16/compsci356/DNS/DNS-primer.pdf http://www.networksorcery.com/enp/protocol/dns.htm