В предыдущей статье я объяснял как парсить пакеты DNS запросов, но сам не могу летать(вход в самолет). В этой статье в основном объясняется с точки зрения DNS-сервера, как создать ответное сообщение и вернуть его клиенту.
В этом предложении вы можете указать DNS-сервер в созданииresponse
Проблема, которую необходимо решить при ответе на сообщение.
- тип дейтаграммы DNS
Buffer
? - Как создать буфер в 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
),Этоrdata
4 байта как раз достаточно для хранения следующего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;
беззнаковый целочисленный класс
если ты виделBuffer
API или использовать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;
};