4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Python3でUDPのDNSクエリーを送信し、応答をダンプ表示する

Last updated at Posted at 2019-07-18

pyDNSdump

This program sends a DNS UDP request and outputs a response.
https://github.com/NobuyukiInoue/pyDNSdump/

python pyDNSdump.py [dnsserver] [target] [recordtype]

pyDNSdump from lambda

UDPのDNSクエリーを送信するプログラムの例(サンプル)

Pythonのsocketモジュールを使用して、
UDPのDNSクエリーを送信し、応答をdump表示する例を紹介します。

メイン処理
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket

def main():
    # "or.jp" "any"
    # data_send = b'\x00\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x02or\x02jp\x00\x00\xff\x00\x01'
    # "." "ns"
    # data_send = b'\x00\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01'
    data_send = set_Header_and_Question(1, "jp", "ns")
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # send a DNS udp request.
    s.sendto(data_send, ("8.8.8.8", 53))

    # recv a DNS udp response.
    data_recv, address = s.recvfrom(8192)

    # print(data_recv)
    print("DNS Response from {0}".format(address))
    for i in range(len(data_recv)):
        if i % 16 == 0:
            print("\n{0:04x}: {1:02x}".format(i, data_recv[i]), end = "")
        else:
            print(" {0:02x}".format(data_recv[i]), end = "")

DNSクエリーのHeader Section およびQuestion Section は
RFC 1035 に準拠するようにbytes型で用意しておきます。

Transaction ID は、繰り返し連続して送信しないのであれば 0x0001 で良いでしょう。

Qestion Section については、FDQNを . で分割し、

[文字列の長さ, 1バイト文字, ..., 0x00]

といったフォーマットで連結します。

Classについては、RFC 1035RFC 2930 にて何種類か定義されていますが、IN以外はほとんど利用されておらず、IN(0x0001)を2オクテットの値としてセットしておきます。

def set_Header_and_Question(Transaction_ID, resolvstring, type):
    data = Transaction_ID.to_bytes(2, 'big')    # Transaction ID
    data += 0x0100.to_bytes(2, 'big')           # Flags
    data += 0x0001.to_bytes(2, 'big')           # Questions
    data += 0x0000.to_bytes(2, 'big')           # Answer RRS
    data += 0x0000.to_bytes(2, 'big')           # Answer RRS
    data += 0x0000.to_bytes(2, 'big')           # Additional RRS

    # Queries
    if resolvstring == ".":
        data += 0x00.to_bytes(1, 'big')
    else:
        flds = resolvstring.split(".")
        for name in flds:
            data += len(name).to_bytes(1, 'big')
            data += name.encode(encoding = 'ascii')
        data += 0x00.to_bytes(1, 'big')
    data += set_RecordType(type.upper())        # Type
    data += 0x0001.to_bytes(2, 'big')           # Class ... IN(0x0001)

    return data

レコードタイプは、"A", "NS", "CNAME", ... といった文字列で渡せるようにしていますが、

Headerセクションには整数でセットしなければならないので、
下記のような処理で2バイトの値に変換するようにします。


def set_RecordType(type):
    if type.isnumeric() == True:
        if int(type) > 0:
            return int(type).to_bytes(2, 'big')
    # Type
    if type is None:
        return 0x00ff.to_bytes(2, 'big')
    elif type == 'A':
        return 0x0001.to_bytes(2, 'big')
    elif type == 'NS':
        return 0x0002.to_bytes(2, 'big')
    elif type == 'CNAME':
        return 0x0005.to_bytes(2, 'big')
    elif type == 'SOA':
        return 0x0006.to_bytes(2, 'big')
    elif type == 'PTR':
        return 0x000c.to_bytes(2, 'big')
    elif type == 'HINFO':
        return 0x000d.to_bytes(2, 'big')
    elif type == 'MX':
        return 0x000f.to_bytes(2, 'big')
    elif type == 'TXT':
        return 0x0010.to_bytes(2, 'big')
    elif type == 'AAAA':
        return 0x001c.to_bytes(2, 'big')
    elif type == 'SRV':
        return 0x0021.to_bytes(2, 'big')
    elif type == 'DS':
        return 0x002b.to_bytes(2, 'big')
    elif type == 'RRSIG':
        return 0x002e.to_bytes(2, 'big')
    elif type == 'NSEC':
        return 0x002f.to_bytes(2, 'big')
    elif type == 'DNSKEY':
        return 0x0030.to_bytes(2, 'big')
    elif type == 'NSEC3':
        return 0x0032.to_bytes(2, 'big')
    elif type == 'NSEC3PARAM':
        return 0x0033.to_bytes(2, 'big')
    elif type == 'CAA':
        return 0x0101.to_bytes(2, 'big')
    elif type == 'ANY':
        return 0x00ff.to_bytes(2, 'big')
    else:
        return 0x00ff.to_bytes(2, 'big')
  • sample_udp_dnsquery.py の実行結果例
$ python sample_udp_dnsquery.py
DNS Response from ('8.8.8.8', 53)

0000: 00 01 81 80 00 01 00 08 00 00 00 00 02 6a 70 00
0010: 00 02 00 01 c0 0c 00 02 00 01 00 00 31 fb 00 08
0020: 01 66 03 64 6e 73 c0 0c c0 0c 00 02 00 01 00 00
0030: 31 fb 00 04 01 65 c0 22 c0 0c 00 02 00 01 00 00
0040: 31 fb 00 04 01 62 c0 22 c0 0c 00 02 00 01 00 00
0050: 31 fb 00 04 01 67 c0 22 c0 0c 00 02 00 01 00 00
0060: 31 fb 00 04 01 68 c0 22 c0 0c 00 02 00 01 00 00
0070: 31 fb 00 04 01 63 c0 22 c0 0c 00 02 00 01 00 00
0080: 31 fb 00 04 01 64 c0 22 c0 0c 00 02 00 01 00 00
0090: 31 fb 00 04 01 61 c0 22
  • pyDNSdump.py の実行結果例
$ python pyDNSdump.py 8.8.8.8 jp ns
============================================================================================
DNS Server    = 8.8.8.8:53
target        = jp
record type   = NS
============================================================================================
Reply from    : 8.8.8.8:53
length        : 0x0098(152) bytes.
Response time : 51.352024[ms]
============================================================================================
0000:               Header:
0000: 0001          Transaction ID:          1
0002: 8180          Flags:                   0b1000000110000000
/*
                      [bit 0]     QR      (1) ... Response
                      [bit 1-4]   OPCODE  (0) ... standard query
                      [bit 5]     AA      (0) ... Not Authoritative
                      [bit 6]     TC      (0) ... Did not Flagment
                      [bit 7]     RD      (1) ... Repeat Query
                      [bit 8]     RA      (1) ... Recursion Available is False
                      [bit 9]     Reserve (0)
                      [bit 10]    Authentic Data(0)
                      [bit 11]    Checking Disable(0)
                      [bit 12-15] RCODE   (0) ... No Error
*/
0004: 0001          Questions:               1
0006: 0008          Answer RRS:              8
0008: 0000          Authority RRS:           0
000a: 0000          Additional RRS:          0

000c:               Querys:
000c: 026a7000      Name:                    jp
0010: 0002          Type:                    NS(2)
0012: 0001          Class:                   IN(1)

0014:               Answer[0]:
0014: c00c          Name:                    jp
0016: 0002          Type:                    NS(2)
0018: 0001          Class:                   IN(1)
001a: 0000545f      Time to live:            0 day 05:59:59(21599)
001e: 0008          data_length:             8
0020: 016103646e73c00c  Name:                    a.dns[.jp]

0028:               Answer[1]:
0028: c00c          Name:                    jp
002a: 0002          Type:                    NS(2)
002c: 0001          Class:                   IN(1)
002e: 0000545f      Time to live:            0 day 05:59:59(21599)
0032: 0004          data_length:             4
0034: 0162c022      Name:                    b[.dns[.jp]]

0038:               Answer[2]:
0038: c00c          Name:                    jp
003a: 0002          Type:                    NS(2)
003c: 0001          Class:                   IN(1)
003e: 0000545f      Time to live:            0 day 05:59:59(21599)
0042: 0004          data_length:             4
0044: 0165c022      Name:                    e[.dns[.jp]]

0048:               Answer[3]:
0048: c00c          Name:                    jp
004a: 0002          Type:                    NS(2)
004c: 0001          Class:                   IN(1)
004e: 0000545f      Time to live:            0 day 05:59:59(21599)
0052: 0004          data_length:             4
0054: 0164c022      Name:                    d[.dns[.jp]]

0058:               Answer[4]:
0058: c00c          Name:                    jp
005a: 0002          Type:                    NS(2)
005c: 0001          Class:                   IN(1)
005e: 0000545f      Time to live:            0 day 05:59:59(21599)
0062: 0004          data_length:             4
0064: 0167c022      Name:                    g[.dns[.jp]]

0068:               Answer[5]:
0068: c00c          Name:                    jp
006a: 0002          Type:                    NS(2)
006c: 0001          Class:                   IN(1)
006e: 0000545f      Time to live:            0 day 05:59:59(21599)
0072: 0004          data_length:             4
0074: 0163c022      Name:                    c[.dns[.jp]]

0078:               Answer[6]:
0078: c00c          Name:                    jp
007a: 0002          Type:                    NS(2)
007c: 0001          Class:                   IN(1)
007e: 0000545f      Time to live:            0 day 05:59:59(21599)
0082: 0004          data_length:             4
0084: 0166c022      Name:                    f[.dns[.jp]]

0088:               Answer[7]:
0088: c00c          Name:                    jp
008a: 0002          Type:                    NS(2)
008c: 0001          Class:                   IN(1)
008e: 0000545f      Time to live:            0 day 05:59:59(21599)
0092: 0004          data_length:             4
0094: 0168c022      Name:                    h[.dns[.jp]]
============================================================================================
Reception is complete.
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?