DNS
DNS [Система доменных имен: (англ. Domain Name System, аббревиатура: DNS)] — служба Интернета. Он действует как распределенная база данных, которая сопоставляет доменные имена и IP-адреса друг с другом, облегчая доступ людей в Интернет. DNS использует TCP и UDP порт 53.
Народная версия
Это доменное имя веб-сайта, переданное клиентом (например, браузер), найдите соответствующий IP-адрес в списке DNS и верните его клиенту, а затем клиент может найти соответствующий сервер в соответствии с IP-адресом и может отправить запрос к серверу.
Проще говоря: цель DNS — предоставить клиенту соответствующий IP-адрес сервера. Наконец, когда клиент взаимодействует с сервером, DNS не имеет ничего общего.
Формат DNS-пакета
Формат пакетов DNS, будь то пакет запроса или пакет ответа, возвращаемый DNS-сервером, использует унифицированный формат.
-
Header
заголовок -
Question
запрос вопрос -
Answer
отвечать -
Authority
Ответ об авторизации -
Additional
Дополнительная информация
DNS format
+--+--+--+--+--+--+--+
| Header |
+--+--+--+--+--+--+--+
| Question |
+--+--+--+--+--+--+--+
| Answer |
+--+--+--+--+--+--+--+
| Authority |
+--+--+--+--+--+--+--+
| Additional |
+--+--+--+--+--+--+--+
Заголовок заголовка
-
ID
:2
байты (16bit
), поле идентификации, клиент будет анализировать ответное сообщение DNS, возвращенное сервером, получитьID
Значение устанавливается с сообщением запросаID
Значения сравниваются, и если они совпадают, они считаются одной и той же сессией DNS. -
FLAGS
:2
байты (16bit
) поля флага. Содержит следующие свойства:-
QR
:0
Указывает сообщение запроса,1
Представляет ответное сообщение; -
opcode
: обычно значение равно0
(стандартный запрос), остальные значения1
(обратный поиск) и2
(запрос состояния сервера),[3,15]
зарезервированное значение; -
AA
: Указывает авторитетный ответ — этот бит имеет смысл только тогда, когда дан ответ, указывая, что сервер, который дал ответ, является полномочным сервером разрешения для запроса доменного имени; -
TC
: указывает на усечение (truncated) — используется для обозначения того, что сообщение длиннее допустимой длины, в результате чего оно усечено; -
RD
: указывает на необходимость рекурсии — этот бит устанавливается запросом и возвращается с тем же значением, что и в ответе. Если установлен RD, рекомендуется, чтобы сервер доменных имен выполнял рекурсивное разрешение, а поддержка рекурсивного запроса не является обязательной; -
RA
: Указывает, что рекурсия поддерживается (Recursion Available) — этот бит устанавливается или снимается в ответе, чтобы указать, поддерживает ли сервер рекурсивный запрос; -
Z
: зарезервированное значение, еще не использованное; -
RCODE
: Код ответа. Эти 4 бита устанавливаются в ответном сообщении и имеют следующие значения:-
0
: ошибок нет. -
1
: ошибка формата сообщения (Format error) - сервер не понимает запрошенное сообщение; -
2
: Сбой сервера - запрос не может быть обработан из-за сервера; -
3
: Ошибка имени — имеет значение только для авторизованных серверов разрешения доменных имен, указывая на то, что разрешенное доменное имя не существует; -
4
: Not Implemented - сервер имен не поддерживает данный тип запроса; -
5
:Refused - Сервер отказывается давать ответ из-за заданной политики, например, сервер не хочет отвечать на некоторые запросы, или сервер не хочет выполнять определенные операции (например, передачу зоны); -
[6,15]
: Зарезервированное значение, еще не используется.
-
-
-
QDCOUNT
: без знака16bit
Целое число, указывающее количество проблемных записей в сегменте запроса сообщения. -
ANCOUNT
: без знака16bit
Целое число указывает количество записей ответа в сегменте ответа сообщения. -
NSCOUNT
: без знака16bit
Целое число, указывающее количество записей авторизации в сегменте авторизации сообщения. -
ARCOUNT
: без знака16bit
Целое число, указывающее количество дополнительных записей в дополнительном сегменте сообщения.
Header format
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Поле запроса вопроса
-
QNAME
неподписанный8bit
Длина блока не ограничивается указанием имени запроса (в широком смысле: доменного имени). -
QTYPE
неподписанный16bit
Целое число, представляющее запрошенный тип протокола. -
QCLASS
неподписанный16bit
Целое число, представляющее класс запроса, например,IN
обозначает Интернет.
Question format
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ... |
| QNAME |
| ... |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QTYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Answer/Authority/Additional
Все три поля имеют одинаковый формат.
-
NAME
Доменное имя, содержащееся в записи ресурса. -
TYPE
выражатьDNS
Тип соглашения. -
CLASS
Класс, представляющий RDATA. -
TTL
4-байтовое целое число без знака, указывающее, как долго запись ресурса может кэшироваться. 0 означает, что его можно только передавать, но не кэшировать. -
RDLENGTH
2 байта целое число без знака, представляющее длину RDATA -
RDATA
Строка переменной длины для представления записи, корневой формат TYPE связан с CLASS. Например, если TYPE — A, а CLASS — IN, то RDATA — это 4-байтовый сетевой адрес ARPA.
Answer/Authority/Additional format
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NAME |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDATA |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Разбор сообщения DNS-запроса
Просто говорите и не делайте фейков. Как разобрать сообщение DNS-запроса? Давайте посмотрим на сообщение запроса DNS:
6dca 0100 0001 0000 0000 0000 0377 7777
0561 7070 6c65 0363 6f6d 0000 0100 01
ЭтоBuffer
Пример, если вы запутались после прочтения, не нервничайте, посмотрите сначала анализconsole.log
Кажется, что мир стал лучше в одно мгновение.
Ниже приведен запрос запросаwww.apple.com
Пакет DNS-запроса веб-сайта ip.
//Header
ID: <Buffer 6d ca>
FLAG: QR: 0 opcode: 0 AA: 0 TC: 0 RD: 1
RA: 0 zero: 0 recode: 0
QDCOUNT: <Buffer 00 01> ANCOUNT: <Buffer 00 00> NSCOUNT: <Buffer 00 00> ARCOUNT: <Buffer 00 00>
//QUESTION
QNAME: <Buffer 03 77 77 77 05 61 70 70 6c 65 03 63 6f 6d 00> QTYPE: <Buffer 00 01> QCLASS: <Buffer 00 01>
QUESTION STRING: www.apple.com
Парсинг сообщения запроса разбит на 2 небольших блока:
-
Header
Разбор заголовка пакета -
QUESTION
Анализ проблемы запроса
Разбор заголовка заголовка
Разберите часть заголовка.
Сначала определите размер каждого поля:
ID: 2 字节
QR: 1 bit
opcode: 4bit
AA: 1bit
TC: 1bit
RD: 1bit
RA: 1bit
Z : 3bit
RCODE: 4bit
QDCOUNT: 2 字节
ANCOUNT: 2 字节
NSCOUNT: 2 字节
ARCOUNT: 2 字节
Всего 12 байт.
Если мы оставим[3,4]
На самом деле разобрать заголовок несложно, но блокbit
должен быть правbuffer
Значение экземпляра подвергается побитовым операциям.
Таким образом, значения следующих параметров могут быть напрямую изменены сbuffer
Получено в:
var header = {};
header.id = buf.slice(0,2);
header.qdcount = buf.slice(4,6);
header.ancount = buf.slice(6,8);
header.nscount = buf.slice(8,10);
header.arcount = buf.slice(10, 12);
Трудность в том, как получить первый[3,4]
значение, сначала нужно поставитьbuffer
Байты, соответствующие экземпляру, преобразуются в2
Затем базовая строка преобразуется в число, а окончательный результат вычисляется по длине аргумента.
Первый шаг, будетbuffer
Преобразовать в двоичную строку, а затем в числовую (при условии, что сообщение DNSbuf
):
//对第3个字节转成`2`进制字符串然后转换为数值
var b = buf.slice(2,3).toString('binary', 0, 1).charCodeAt(0);
Шаг 2, выполните резку данных:
Прежде всего, вам нужно понять следующую функцию, которая представляет собой не что иное, как извлечение изoffset
начало, длинаlength
Числовые биты, преобразованные в побитовые операцииInteger
Затем возвращается номер типа.
Грубо говоря, это преобразование нужных вам бинарных данных вInteger
введите и верните.
var bitSlice = function(b, offset, length) {
return (b >>> (7-(offset+length-1))) & ~(0xff << length);
};
Обратите внимание, потому что рассматривается только один байт ===
8bit
, поэтому его можно записать как(7-(offset+length-1))
и0xff << length
. Если это не байт, то вам может понадобиться изменить число в нем7
и0xff
значение .
Демонстрация начинается:
'use strict';
var buf = Buffer.from([0x2d]);
var b = buf.toString('binary' , 0,1).charCodeAt(0);
console.log(bitSlice(b , 0, 1));//0
console.log(bitSlice(b , 1, 1));//0
console.log(bitSlice(b , 2, 1));//1
console.log(bitSlice(b , 3, 1));//0
console.log(bitSlice(b , 4, 1));//1
console.log(bitSlice(b , 5, 1));//1
console.log(bitSlice(b , 6, 1));//0
console.log(bitSlice(b , 7, 1));//1
console.log(bitSlice(b , 5, 3));//5 === 0000 0101
/**
* 16进制:0x2d
* 10进制:45
* 2进制: 0010 1101
*
* (45,0,1):45>>>7 & ~(0xff<<1)
* 45>>>7 = 0000 0000
* (0xff<<1) = 0000 0000 0000 0000 0000 0001 1111 1110 510
* ~(0xff<<1) = 1111 1111 1111 1111 1111 1110 0000 0001 -511 = -((0xff<<1)+1)
*
* 0000 0000 0000 0000 0000 0000 0000 0000 === 45>>>7
* & 1111 1111 1111 1111 1111 1110 0000 0001 === ~(0xff<<1)
* ----------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0000 = 0
*
* (45,2,1):45>>>5 & ~(0xff<<1)
* 45>>>5 = 0000 0001
* (0xff<<1) = 0000 0000 0000 0000 0000 0001 1111 1110 510
* ~(0xff<<1) = 1111 1111 1111 1111 1111 1110 0000 0001 -511 = -((0xff<<1)+1)
*
* 0000 0000 0000 0000 0000 0000 0000 0001 === 45>>>5
* & 1111 1111 1111 1111 1111 1110 0000 0001 === ~(0xff<<1)
* ----------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0001 = 1
*/
После понимания функции вышеуказанной функции вы действительно можете использовать эту функцию для получения первой части заголовка пакета DNS.[3,4]
значение в байтах.
На кончиках ваших пальцев:
//第3个字节
var b = buf.slice(2,3).toString('binary', 0, 1).charCodeAt(0);
header.qr = bitSlice(b,0,1);
header.opcode = bitSlice(b,1,4);
header.aa = bitSlice(b,5,1);
header.tc = bitSlice(b,6,1);
header.rd = bitSlice(b,7,1);
//第4个字节
b = buf.slice(3,4).toString('binary', 0, 1).charCodeAt(0);
header.ra = bitSlice(b,0,1);
header.z = bitSlice(b,1,3);
header.rcode = bitSlice(b,4,4);
Разрешение поля запроса QUESTION
В основном это имя домена запроса, тип протокола и категория.
эти 3 параметраQTYPE
иQCLASS
фиксированный2
байт,QNAME
не фиксируется.
Поэтому вам нужно обратить внимание на выборку данных, потому чтоQUESTION
информация следующаяHeader
После этого, так из12
Вернуть байты обратно:
var question = {};
question.qname = buf.slice(12, buf.length-4);
question.qtype = buf.slice(buf.length-4, buf.length-2);
question.qclass = buf.slice(buf.length-2, buf.length);
qname
используетlen+data
смешанное кодирование для0x00
конец. Каждая строка начинается с длины, за которой следует содержимое.qname
длина должна начинаться с8
Байт - это единица измерения.
Напримерwww.apple.com
(Примечание: середина.
добавляется при разборе), егоbuffer
Экземпляр представлен как:
03 77 77 77 05 61 70 70 6c 65 03 63 6f 6d 00
//约等于
3www5apple3com
То есть первый бит указывает длину, за ним следуют данные той же длины и так далее.
var domainify = function(qname) {
var parts = [];
for (var i = 0; i < qname.length && qname[i];) {
var len = qname[i] , offset = i+1;//获取每一块域名长度
parts.push(qname.slice(offset,offset+len).toString());//获取每一块域名
i = offset+len;
}
return parts.join('.');//拼凑成完整域名
};
qtype
тип соглашения.смотрите подробности
Список, соответствующий типу протокола:
ценность | тип соглашения | описывать |
---|---|---|
1 | A | IPv4-адрес |
2 | NS | сервер имен |
5 | CNAME | Каноническое имя определяет псевдоним для официального имени хоста. |
6 | SOA | Начальная авторизация отмечает начало зоны |
11 | WKS | Знаком с сетевыми службами, предоставляемыми хостом определения службы. |
12 | PTR | Указатель преобразует IP-адрес в доменное имя |
13 | HINFO | Информация о хосте дает представление об оборудовании и операционной системе, используемых хостом. |
15 | MX | Mail Exchange направляет изменения почты на почтовые серверы |
28 | AAAA | IPv6-адрес |
252 | AXFR | Передать запрос на всю зону |
255 | ANY | запрос на все записи |
qclass
Обычно 1, ссылаясь на интернет-данные.
Сценарий приложения — прокси-сервер запроса DNS
Сохраните следующий код как.js
файл, затем используйтеNode.js
Выполните, используйте машину в той же локальной сети, чтобы настроить DNS для этой машины.
Следующий код предназначен только для справки:
'use strict';
const dgram = require('dgram');
const dns = require('dns');
const fs = require('fs');
const server = dgram.createSocket('udp4');
var bitSlice = function(b, offset, length) {
return (b >>> (7-(offset+length-1))) & ~(0xff << length);
};
var domainify = function(qname) {
var parts = [];
for (var i = 0; i < qname.length && qname[i];) {
var length = qname[i];
var offset = i+1;
parts.push(qname.slice(offset,offset+length).toString());
i = offset+length;
}
return parts.join('.');
};
var parse = function(buf) {
var header = {};
var question = {};
var b = buf.slice(2,3).toString('binary', 0, 1).charCodeAt(0);
console.log('b:',b,buf.slice(2,3));
header.id = buf.slice(0,2);
header.qr = bitSlice(b,0,1);
header.opcode = bitSlice(b,1,4);
header.aa = bitSlice(b,5,1);
header.tc = bitSlice(b,6,1);
header.rd = bitSlice(b,7,1);
b = buf.slice(3,4).toString('binary', 0, 1).charCodeAt(0);
header.ra = bitSlice(b,0,1);
header.z = bitSlice(b,1,3);
header.rcode = bitSlice(b,4,4);
header.qdcount = buf.slice(4,6);
header.ancount = buf.slice(6,8);
header.nscount = buf.slice(8,10);
header.arcount = buf.slice(10, 12);
question.qname = buf.slice(12, buf.length-4);
question.qtype = buf.slice(buf.length-4, buf.length-2);
question.qclass = buf.slice(buf.length-2, buf.length);
return {header:header, question:question};
};
server.on('error' , (err)=>{
console.log(`server error: ${err.stack}`);
});
server.on('message' , (msg , rinfo)=>{
//fs.writeFile('dns.json' ,msg, {flag:'w',endcoding:'utf-8'} ,(err)=>{
// console.log(err);
//});
var query = parse(msg);
console.log('标识ID: ' ,query.header.id);
console.log('标识FLAG: ' , 'QR: ',query.header.qr , 'opcode: ',query.header.opcode , 'AA: ',query.header.aa , 'TC: ',query.header.tc,'RD: ',query.header.rd);
console.log('RA: ',query.header.ra , 'zero: ',query.header.z , 'recode: ',query.header.rcode);
console.log('QDCOUNT: ',query.header.qdcount , 'ANCOUNT: ' , query.header.ancount, 'NSCOUNT: ' , query.header.nscount,'ARCOUNT: ',query.header.arcount);
console.log('QNAME: ',query.question.qname , 'QTYPE: ', query.question.qtype ,'QCLASS: ' , query.question.qclass);
console.log('QUESTION STRING: ' ,domainify(query.question.qname));
server.close();
});
server.on('listening' , ()=>{
var address = server.address();
console.log(`server listening ${address.address}:${address.port}`);
});
server.bind({port:53,address:'8.8.8.8'});//address需要指定到你要用于进行代理的机器ip
использованная литература
doc store.Mick.UA/или Elly/net для…