Как я могу расшифровать сообщения DNS?

Я пишу программу для получения сообщений DNS и ответить на соответствующий ответ (простой сервер DNS, который отвечает только на записи A).
но когда я получаю сообщения, это не похоже на описанный формат в 1035 RFC.
например, это DNS-запрос, сгенерированный nslookup:

'\xe1\x0c\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06google\x03com\x00\x00\x01\x00\x01'

я знаю о заголовках и битах днс, как определено в 1035 RFC, но почему он должен быть в шестнадцатеричном?
я должен рассмотреть их как шестнадцатеричные числа или их эквиваленты utf-8?
мои ответы тоже должны иметь этот формат?

1 ответ

Решение

Он выглядит как шестнадцатеричный, потому что это необработанный двоичный запрос, но вы, вероятно, пытаетесь распечатать его как строку. Это, очевидно, то, как непечатные символы отображаются тем, что вы используете для его распечатки; он избегает их как шестнадцатеричные последовательности.

Вы не интерпретируете это как "hex" или UTF-8 вообще; вам нужно интерпретировать двоичный формат, описанный в RFC. Если вы упомянете, какой язык вы используете, я (или кто-то еще) мог бы описать вам, как обрабатывать данные в двоичном формате, как этот.

Теперь перейдем к RFC 1035 и посмотрим, как интерпретировать ваш запрос вручную:

The header contains the following fields:

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      ID                       |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    QDCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ANCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    NSCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ARCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Каждая строка содержит 16 бит, то есть 12 байтов. Давайте заполним наши первые 12 байтов туда:

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                   ID = e10c                   |  \xe1 \x0c
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    | 0|  Opcode=0 | 0| 0| 1| 0| Z=0    |  RCODE=0  |  \x01 \x00
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                  QDCOUNT = 1                  |  \x00 \x01
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                  ANCOUNT = 0                  |  \x00 \x00
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                  NSCOUNT = 0                  |  \x00 \x00
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                  ARCOUNT = 0                  |  \x00 \x00
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Так. У нас есть запрос с ID = e10c (просто произвольное число, чтобы клиент мог сопоставить запросы с ответами), QR = 0 означает, что это запрос, opcode = 0 означает, что это стандартный запрос, AA и TC предназначены для ответов, RD = 1 означает, что рекурсия желательна (мы делаем рекурсивный запрос к нашему локальному серверу имен). Z зарезервирован для будущего использования, RCODE - это код ответа для ответов. QDCOUNT = 1 означает, что у нас есть 1 вопрос, все остальные - это числа записей разных типов в ответе.

Теперь мы подошли к вопросам. Каждый имеет следующий формат:

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

QNAME - это имя, о котором идет запрос. Формат - один октет, указывающий длину метки, за которой следует метка, оканчивающаяся меткой с длиной 0.

Итак, мы имеем:

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |       LEN = 6         |           g           |  \x06 g
    |           o           |           o           |  o    o
    |           g           |           l           |  g    l
    |           e           |       LEN = 3         |  e    \x03
    |           c           |           o           |  c    o
    |           m           |       LEN = 0         |  m    \x00
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                   QTYPE = 1                   |  \x00 \x01
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                  QCLASS = 1                   |  \x00 \x01
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Это указывает на то, что имя, которое мы ищем, google.com (иногда пишется как google.com., с пустой меткой в ​​конце, сделанной явной). QTYPE = 1 - это запись A (адрес IPv4). QCLASS = 1 - это запрос IN (Интернет). Так что это просит IPv4-адрес google.com.

Другие вопросы по тегам