Подробное объяснение ответного сообщения DNS

Node.js задняя часть сервер DNS
Подробное объяснение ответного сообщения DNS

В предыдущей статье я объяснял как парсить пакеты DNS запросов, но сам не могу летать(вход в самолет). В этой статье в основном объясняется с точки зрения DNS-сервера, как создать ответное сообщение и вернуть его клиенту.

В этом предложении вы можете указать DNS-сервер в созданииresponseПроблема, которую необходимо решить при ответе на сообщение.

  • тип дейтаграммы DNSBuffer?
  • Как создать буфер в Node.js?
  • При нормальных обстоятельствах, могут ли строки и числа, с которыми мы работаем, быть преобразованы вBuffer?
  • BufferМожно ли создатьresponseЗначение параметра указанного типа ответного сообщения?
  • responseответное сообщение сrequestВ чем разница между сообщениями запроса?

Кстати говоря, вы заметили это? теперь, когдаdnsзапрос иdnsВсе ответы готовы, так можно ли написать прокси-сервер dns самостоятельно?

ответ:Yes.

Затем мы переходим к этому последнему шагу,responseСоздание ответных сообщений.

Формат ответного сообщения DNS

responseответное сообщение иrequestФормат сообщения запроса тот же. Отличие в том, что значения параметров разные.

Подробное объяснение параметров ответа

  • Headerзаголовок
  • Questionзапрос вопрос
  • Answerотвечать
  • AuthorityОтвет авторизации
  • AdditionalДополнительная информация
  DNS format

  +--+--+--+--+--+--+--+
  |        Header      |
  +--+--+--+--+--+--+--+
  |      Question      |
  +--+--+--+--+--+--+--+
  |      Answer        |
  +--+--+--+--+--+--+--+
  |      Authority     |
  +--+--+--+--+--+--+--+
  |      Additional    |
  +--+--+--+--+--+--+--+

Заголовок заголовка

Описание недвижимости:

  • Идентификатор запроса клиента используется для обеспечения того, чтобы он мог правильно определить, какое ответное сообщение является запросом, когда он получает сообщение, возвращенное DNS-сервером. Таким образом, полный DNS-запрос и ответ внутриrequestа такжеresponseизIDДолжен быть последовательным.
  • header.qr = 1, указывая на ответное сообщение
  • header.ancount, это включает запись записи ответа, поэтому в соответствии с полем ответаAnswerрассчитать.
  var response = {};
  var header = response.header = {};

  header.id = request.header.id;//id相同,视为一个dns请求
  
  header.qr = 1;    //响应报文
  header.opcode = 0;//标准查询
  header.rd = 1;
  header.ra = 0;
  
  header.z = 0;
  header.rcode = 0;//没有错误

  header.qdcount = 1;
  header.nscount = 0;
  header.arcount = 0;
  header.ancount = 1;//这里answer为一个,所以设置为1.如果有多个answer那么就要考虑多个answer

Данные запроса вопроса

Верните данные запроса как есть.

  var question = response.question = {};
  question.qname = request.question.qname;
  question.qtype = request.question.qtype;
  question.qclass = request.question.qclass;

Данные ответного сообщения ответа

Содержимое этой части представляет собой дейтаграмму, которую должен вернуть DNS-сервер.

RDDATAдля поля данных.

nameДля доменного имени длина не фиксирована.

Формат:

  Answer format

    0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    NAME                       |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    TYPE                       |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    CLASS                      |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    TTL                        |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    RDLENGTH                   |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    RDATA                      |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

  var answer = {};

  answer.name = request.question.qname;
  answer.type = 1;
  answer.class = 1;
  answer.ttl = ttl || 1;//报文有效跳数
  answer.rdlength = 4;
  answer.rdata = rdata;//数据记录

rdataхранитсяipАдрес, ip должен быть преобразован клиентом, чтобы быть распознанным:

  var numify = function(ip) {
      ip = ip.split('.').map(function(n) {
          return parseInt(n, 10);
      });

      var result = 0;
      var base = 1;

      for (var i = ip.length-1; i >= 0; i--) {
          result += ip[i]*base;
          base *= 256;
      }
      return result;
  };

rdataда4байт,ipадрес из.После разрезания он состоит из 4 сегментов чисел, и каждый сегмент данных не превышает2^8 === 256--- байт (8bit),Этоrdata4 байта как раз достаточно для хранения следующегоipадрес. Теперь вопрос заключается в том, как сохранить данные IP-адреса в 4 байта, а также обеспечить, чтобы клиент мог их распознать. Очень просто хранить по байтам и просто извлекать по байтам.4байт ровно один32bitДлина целого числа.

Итак, приведенный выше расчетresultизfor(...)Цикл состоит в том, чтобы сохранить ip вrdataпрочь.

На самом деле, вы также можете использовать следующий метод для расчетаresult:

  result = ip[0]*(1<<24) + ip[1]*(1<<16) + ip[2]*(1<<8) + ip[3];

Полномочия/Дополнительные данные

Запросы, обрабатываемые сами по себе, не имеют ответов авторизации и дополнительных данных.

Ответное сообщение типа буфера

После получения всех данных ответа, которые вы хотите, следующим шагом будет преобразование этих данных во что-то, что клиент может проанализировать.BufferТипы.

Работа этого шага точно такая же, какrequestРазбор пакета запроса работает как раз наоборот. Приведенные выше данные объединены вresponseДанные формата ответного сообщения.

Длина буфера определяется

вернуть абзацBufferсообщение, вы должны сначала создать определенную длинуBuffer.

По полевым анализам, кромеQuestion.qnameполя иAnswer.nameДлина поля не фиксирована, и другие поля могут быть вычислены.

Внося данные, вы можете получить то, что нужно создатьBufferразмер.

  len = Header + Question + Answer
      = 12 + (Question.qname.length+4) + (Answer.name.length + 14)
      = 30 + Question.qname.length + Answer.name.length

Определите, что нужно создатьBufferДлина экземпляра30 + Question.qname.length + Answer.name.lengthПосле этого можно выполнить преобразование параметров.

Преобразование параметров экземпляра буфера

responseДанные условно делятся на 3 категории:

  • Обычный полный байтовый класс
  • Категория, которая должна быть побитово объединена в байт
  • беззнаковый целочисленный класс

Обычный полный байтовый класс

Часто это лучший способ справиться сcopyПросто приходи.

использоватьbuf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])функция копирования.

например копироватьheader.id:

  header.id.copy(buf,0,0,2);

Таким образом, другие параметры могут быть преобразованы один за другим.

Категория, которая должна быть побитово объединена в байт

Этот основной номер предназначен дляHeaderПервый[3,4]байт. Должно получиться, что данные этих 2-х байт различаются по длине бит, и теперь их нужно собрать в полные байты.

Первое, что необходимо определить, это длину байта, а также значение по умолчанию, а затем битовый оператор.

1byte = 8bit

Значение по умолчанию:0 = 0x00

оператор:

  &: 不行,因为任何数&0 == 0
  |: ok ,任何数 | 0 都等于这个数

пройти через|Вы можете получить желаемый результат:

  buf[2] = 0x00 | header.qr << 7 | header.opcode << 3 | header.aa << 2 | header.tc << 1 | header.rd;
  buf[3] = 0x00 | header.ra << 7 | header.z << 4 | header.rcode;

беззнаковый целочисленный класс

если ты виделBufferAPI или использоватьBufferсозданныйbufБеззнаковое целое, тогда эту проблему можно легко решить.

buf.writeUInt16BE(value, offset[, noAssert])а такжеbuf.writeUInt32BE(value, offset[, noAssert]), сразу видно, что нужно создать16немного, один32немного.

 buf.writeUInt16BE(header.ancount, 6);
 buf.writeUInt32BE(answer.rdata, len-4);

Сценарии применения

КромеAnswerданныеttlПакетное эффективное количество переходов иrdata, вам действительно нужно получить его из других мест. Другие данные в основном могут быть рассчитаны или получены изrequestполучено в.

Если вы инкапсулируете его как функцию, вам нужно только передать(request,ttl,rdata)Вот и все.

Следующий код предназначен только для справки:

  var responseBuffer = function(response){
      var buf = Buffer.alloc(30+response.question.qname.length +response.answer.name.length) ,
          offset = response.question.qname.length;

      response.header.id.copy(buf,0,0,2);

      buf[2] = 0x00 | response.header.qr << 7 | response.header.opcode << 3 | response.header.aa << 2 | response.header.tc << 1 | response.header.rd;
      buf[3] = 0x00 | response.header.ra << 7 | response.header.z << 4 | response.header.rcode;

      buf.writeUInt16BE(response.header.qdcount, 4);
      buf.writeUInt16BE(response.header.ancount, 6);
      buf.writeUInt16BE(response.header.nscount, 8);
      buf.writeUInt16BE(response.header.arcount, 10);

      response.question.qname.copy(buf,12);
      response.question.qtype.copy(buf,12+offset,0,2);
      response.question.qclass.copy(buf,14+offset,0,2);

      offset += 16;
      response.answer.name.copy(buf,offset);

      offset += response.answer.name.length;
      buf.writeUInt16BE(response.answer.type , offset);
      buf.writeUInt16BE(response.answer.class , offset+2);
      buf.writeUInt32BE(response.answer.ttl , offset+4);
      buf.writeUInt16BE(response.answer.rdlength , offset+8);
      buf.writeUInt32BE(response.answer.rdata , offset+10);

      return buf;
  };

  var response = function(request , ttl , rdata){
      var response = {};
      response.header = {};
      response.question = {};
      response.answer = resolve(request.question.qname , ttl , rdata);

      response.header.id = request.header.id;

      response.header.qr = 1;
      response.header.opcode = 0;
      response.header.aa = 0;
      response.header.tc = 0;
      response.header.rd = 1;
      response.header.ra = 0;
      response.header.z = 0;
      response.header.rcode = 0;
      response.header.qdcount = 1;
      response.header.ancount = 1;
      response.header.nscount = 0;
      response.header.arcount = 0;

      response.question.qname = request.question.qname;
      response.question.qtype = request.question.qtype;
      response.question.qclass = request.question.qclass;

      return responseBuffer(response);

  };
  var resolve = function(qname , ttl , rdata){
      var answer = {};

      answer.name = qname;
      answer.type = 1;
      answer.class = 1;
      answer.ttl = ttl;
      answer.rdlength = 4;
      answer.rdata = rdata;

      return answer;
  };

использованная литература

GitHub.com/проблема в том/из…