Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to pack a DNS request message #40

Open
selfboot opened this issue Jul 3, 2017 · 3 comments
Open

How to pack a DNS request message #40

selfboot opened this issue Jul 3, 2017 · 3 comments

Comments

@selfboot
Copy link
Owner

selfboot commented Jul 3, 2017

As issue #38 mentioned, the top level format of message is divided into 5 sections (some of which are empty in certain cases) shown below:

+---------------------+
|        Header       |
+---------------------+
|       Question      | the question for the name server
+---------------------+
|        Answer       | RRs answering the question
+---------------------+
|      Authority      | RRs pointing toward an authority
+---------------------+
|      Additional     | RRs holding additional information
+---------------------+

So, we need to fill these five section to make a DNS request message.

Header

The format string pack the DNS request header section as followers:

                                1  1  1  1  1  1
  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5    Pack Value
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      ID                       |   H: request_id
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |   BB: 1 0 (RD is set 1, others are 0)
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    QDCOUNT                    |   H: 1
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ANCOUNT                    |   H: 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    NSCOUNT                    |   H: 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    ARCOUNT                    |   H: 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Details:

  • RD is set 1 in query, it directs the name server to pursue the query recursively.
  • QDCOUNT is set 1, it specifies there is only one number entry in the question section.

In asyncdns.build_request, there are following snippet:

header = struct.pack('!HBBHHHH', request_id, 1, 0, 1, 0, 0, 0)

Format string !HBBHHHH specify that the byte order is network(= big-endian), and:

  • H: unsigned shorted - integer(2 Bytes)
  • B: unsigned char - integer(1 Bytes)

Question

The question section is used to carry the "question" in most queries, i.e., the parameters that define what is being asked. The section contains QDCOUNT (usually 1) entries, each of the following format:

                                1  1  1  1  1  1
  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5    Pack Value
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                                               |
/                     QNAME                     /
/                                               /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                     QTYPE                     |   H: qtype
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                     QCLASS                    |   H: QCLASS_IN
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

The following snippet packs the QTYPE and QCLASS para in question section.

qtype_qclass = struct.pack('!HH', qtype, QCLASS_IN)

QNAME is created by the following snippet:

def build_address(address):
    """ Convert domain name to QNAME used in the DNS Question section.
    Return a sequence of labels, where each label consists of a
    length octet followed by that number of octets.
    The domain name terminates with the zero length octet
    for the null label of the root.
    """
    address = address.strip(b'.')
    labels = address.split(b'.')
    results = []
    # Labels must be 63 characters or less.  Ref: RFC 1035
    for label in labels:
        l = len(label)
        if l > 63:
            return None
        results.append(common.chr(l))
        results.append(label)
    results.append(b'\0')
    return b''.join(results)

Ref

#37 Struct: working with binary data
#38 Data format of message used in DNS
#39 View DNS data datagram via wireshark

@selfboot selfboot changed the title How data is packed via struct module in shadowsocks How to get a DNS request pack Jul 24, 2018
@selfboot selfboot changed the title How to get a DNS request pack How to pack a DNS request message Jul 24, 2018
@selfboot
Copy link
Owner Author

selfboot commented Jul 25, 2018

Following shows a simple demo which build QNAME for domain name google.com:

google = asyncdns.build_address("google.com")
print(binascii.hexlify(google))

It prints (just as what we wish: each label consists of a length octet followed by that number of octets.)

06676f6f676c6503636f6d00

@selfboot
Copy link
Owner Author

As #39 has showed, the dns request to google.com is

Then we can build the request as follows:

dns_request = asyncdns.build_request("google.com", 1, 0x9118)
print(binascii.hexlify(dns_request))

It prints as what we analysis from the data package which is captured from wireshark when dig google.com .

91180100000100000000000006676f6f676c6503636f6d0000010001

@selfboot
Copy link
Owner Author

selfboot commented Aug 1, 2018

Function build_address use common.chr(l) to convert int to bytes.

def compat_chr(d):
    if bytes == str:
        return _chr(d)
    return bytes([d])

_chr = chr
chr = compat_chr

Python2

>>> type(chr(34))
<type 'str'>
>>> type(chr(34)) is bytes
True
>>> import binascii
>>> binascii.hexlify(chr(34))
'22'

Python3

>>> type(chr(34))
<class 'str'>
>>> binascii.hexlify(chr(34))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: a bytes-like object is required, not 'str'

>>> bytes([34])
b'"'
>>> binascii.hexlify(bytes([34]))
b'22'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant