DNS协议 TCP转发 测试驱动开发
研究A Query请求
首先,启动一个UDP监听服务,打印请求:
import * as udp from 'dgram';
interface ServerConfig {
host: string;
port: number;
}
function main(config: ServerConfig) {
var server = udp.createSocket('udp4');
server.bind({
port: config.port,
address: config.host,
}, () => {
task(server);
});
server.on('error', (err) => {
console.error(err);
});
}
main({
host: 'localhost',
port: 53,
});
function task(server: udp.Socket) {
server.on('message', (msg) => {
console.log('conn', msg);
});
}
使用dig发送一个请求
$ dig @localhost a
conn <Buffer 56 64 01 20 00 01 00 00 00 00 00 01 00 00 02 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 40 23 44 f8 9a 88 91 99>
$ dig @localhost b
conn <Buffer 6d df 01 20 00 01 00 00 00 00 00 01 01 62 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 72 b4 05 cf c0 9b 95 6b>
长度居然不相同,换个域名:
$ dig @localhost google.com
2b c3 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 3b 32 50 88 3e bd 80 ... 1 more byte>
bf 39 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 2c 3b d4 6c f2 d0 8f ... 1 more byte>
16 35 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 b7 12 05 94 3e 14 f2 ... 1 more byte>
4f 58 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 8a f3 ed fb c8 66 e5 ... 1 more byte>
g o o g l e ? c o m
研究转发
import * as udp from 'dgram';
interface ServerConfig {
host: string;
port: number;
}
function main(config: ServerConfig) {
var server = udp.createSocket('udp4');
server.bind({
port: config.port,
address: config.host,
}, () => {
task(server);
});
server.on('error', (err) => {
console.error(err);
});
}
main({
host: 'localhost',
port: 53,
});
function task(server: udp.Socket) {
server.on('message', (msg, rinfo) => {
console.log(rinfo);
printMsg(msg, 'conn');
// forward to 1.1.1.1
var nds = udp.createSocket('udp4');
nds.send(msg, 53, '1.1.1.1');
nds.on('message', (msg) => {
printMsg(msg, 'forward');
server.send(msg, rinfo.port, rinfo.address);
});
});
}
function printMsg(msg: Buffer, hint: string) {
console.log(`===============${hint}:==================`);
console.log(msg.buffer);
var le = msg.byteLength;
var str = '';
for (let i = 0; i < le; i++) {
str += String.fromCharCode(msg[i]);
}
console.log(`try read:`, str);
}
使用dig @localhost google.com
查看:
{ address: '127.0.0.1', family: 'IPv4', port: 56797, size: 51 }
===============conn:==================
ArrayBuffer {
[Uint8Contents]: <2c 3c 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 37 30 eb c9 e3 37 83 9b>,
byteLength: 51
}
try read: ,< googlecom)
70ëÉã7
===============forward:================== <== 运营商先返回一个污染的数据包
ArrayBuffer {
[Uint8Contents]: <2c 3c 85 80 00 01 00 01 00 00 00 00 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 00 3c
00 04
2e 52 ae 45>,
byteLength: 54
}
try read: ,<
googlecomgooglecom<.R®E
===============forward:================== <== 真实的数据包到达,数据还被篡改了
ArrayBuffer {
[Uint8Contents]: <2c 3c 81 80 00 01 00 01 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 c0 0c 00 01 00 01 00 00 00 87 00 04 ac d9 04 ae 00 00 29 04 d0
00 00
00 00 00 00>,
byteLength: 55
}
try read: ,<googlecomÀ
¬Ù®)Ð
转发效果:
$ dig @localhost google.com
; <<>> DiG 9.16.1-Ubuntu <<>> @localhost google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11324
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 60 IN A 46.82.174.69 <= 就是2e 52 ae 45
;; Query time: 23 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: 六 1月 30 16:17:30 CST 2021
;; MSG SIZE rcvd: 54
可否直接将udp请求使用tcp转发
tcp与udp的A query请求:
tcp
00 33 21 44 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 74 95 9e ba f5 2f 7e fe
00 33 24 ea 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 2e 00 32 09 ce 45 2b 1b
udp
90 97 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 f7 af 83 bf 5a 55 b8 9a
2e 3c 01 20 00 01 00 00 00 00 00 01 06 67 6f 6f 67 6c 65 03 63 6f 6d 00 00 01 00 01 00 00 29 10 00 00 00 00 00 00 0c 00 0a 00 08 97 97 e4 eb 82 52 f4 45
注意观察, tcp前面多了两个字节:0x00 0x33!仅此而已!
let's go!
function task(server: udp.Socket, config: ServerConfig) {
server.on('message', (msg, rinfo) => {
console.log(rinfo);
printMsg(msg, 'conn');
// forward to DNS server
var dnsServer = config.server || '1.1.1.1';
var tcp = net.createConnection({ host: dnsServer, port: 53 }, () => {
console.log('server connected...');
// add "0x00 0x33"
var tcpQuery = Buffer.alloc(2 + msg.length);
tcpQuery[0] = 0x00;
tcpQuery[1] = 0x33;
for (var i = 2; i < tcpQuery.length; i++) {
tcpQuery[i] = msg[i - 2];
}
printMsg(tcpQuery, 'forward');
tcp.write(tcpQuery);
tcp.once('data', (resp) => {
// delete "0x00 0x33"
var tcpResp = Buffer.alloc(msg.length);
for (var i = 0; i < tcpResp.length; i++) {
tcpResp[i] = resp[i + 2];
}
printMsg(tcpResp, 'resp');
server.send(tcpResp, rinfo.port, rinfo.address);
});
});
});
}
修改/etc/resolv.conf
备份软连接:
$ ls -lih /etc/resolv.conf
1048783 lrwxrwxrwx 1 root root 39 1月 22 20:39 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
$ ls /run/systemd/resolve/stub-resolv.conf
/run/systemd/resolve/stub-resolv.conf
$ echo 'nameserver 127.0.0.1' > /etc/resolv.conf
更正
使用过程中发现只有固定的测试dig google.com可以被正确处理,研究发现TCP并不是固定前两个字节,0x33等于51,是字节长度!
DNS的TCP协议与UDP协议相同,只是有一个区别-通过TCP发送的消息以网络字节顺序以16位整数作为前缀,以指定消息字节的长度。UDP不需要这样做,因为消息长度由数据报的大小决定。
See: https://stackoverflow.com/questions/41512591/dns-query-over-tcp