dns2 2.0.1 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/client/doh.js +4 -8
- package/package.json +3 -2
- package/packet.js +5 -3
- package/server/dns.js +13 -10
- package/server/doh.js +62 -57
- package/server/tcp.js +8 -3
- package/server/udp.js +6 -2
- package/test/index.js +41 -1
package/README.md
CHANGED
|
@@ -145,8 +145,12 @@ server.on('request', (request, response, rinfo) => {
|
|
|
145
145
|
console.log(request.header.id, request.questions[0]);
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
+
server.on('requestError', (error) => {
|
|
149
|
+
console.log('Client sent an invalid request', error);
|
|
150
|
+
});
|
|
151
|
+
|
|
148
152
|
server.on('listening', () => {
|
|
149
|
-
console.log(server.
|
|
153
|
+
console.log(server.addresses());
|
|
150
154
|
});
|
|
151
155
|
|
|
152
156
|
server.on('close', () => {
|
|
@@ -154,7 +158,18 @@ server.on('close', () => {
|
|
|
154
158
|
});
|
|
155
159
|
|
|
156
160
|
server.listen({
|
|
157
|
-
udp:
|
|
161
|
+
// Optionally specify port, address and/or the family of socket() for udp server:
|
|
162
|
+
udp: {
|
|
163
|
+
port: 5333,
|
|
164
|
+
address: "127.0.0.1",
|
|
165
|
+
type: "udp4", // IPv4 or IPv6 (Must be either "udp4" or "udp6")
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// Optionally specify port and/or address for tcp server:
|
|
169
|
+
tcp: {
|
|
170
|
+
port: 5333,
|
|
171
|
+
address: "127.0.0.1",
|
|
172
|
+
},
|
|
158
173
|
});
|
|
159
174
|
|
|
160
175
|
// eventually
|
package/client/doh.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
const https = require('https');
|
|
2
|
-
const http = require('http');
|
|
3
1
|
const Packet = require('../packet');
|
|
4
2
|
|
|
5
|
-
const
|
|
3
|
+
const defaultGet = url => new Promise((resolve, reject) => {
|
|
6
4
|
const headers = {
|
|
7
5
|
accept: 'application/dns-message',
|
|
8
6
|
};
|
|
9
|
-
const base = url.startsWith('https') ? https : http;
|
|
7
|
+
const base = url.startsWith('https') ? require('https') : require('http');
|
|
10
8
|
const req = base.get(url, { headers }, resolve);
|
|
11
9
|
req.on('error', reject);
|
|
12
10
|
});
|
|
@@ -25,7 +23,7 @@ const readStream = stream => {
|
|
|
25
23
|
* @docs https://tools.ietf.org/html/rfc8484
|
|
26
24
|
* @param {*} param0
|
|
27
25
|
*/
|
|
28
|
-
const DOHClient = ({ dns, http } = {}) => {
|
|
26
|
+
const DOHClient = ({ dns, http, get = defaultGet } = {}) => {
|
|
29
27
|
return (name, type = 'A', cls = Packet.CLASS.IN, { clientIp, recursive = true } = {}) => {
|
|
30
28
|
const packet = new Packet();
|
|
31
29
|
// see https://github.com/song940/node-dns/issues/29
|
|
@@ -43,9 +41,7 @@ const DOHClient = ({ dns, http } = {}) => {
|
|
|
43
41
|
type : Packet.TYPE[type],
|
|
44
42
|
});
|
|
45
43
|
const query = packet.toBase64URL();
|
|
46
|
-
return Promise
|
|
47
|
-
.resolve()
|
|
48
|
-
.then(() => get(`http${http ? '' : 's'}://${dns}/dns-query?dns=${query}`))
|
|
44
|
+
return Promise.resolve(get(`http${http ? '' : 's'}://${dns}/dns-query?dns=${query}`))
|
|
49
45
|
.then(readStream)
|
|
50
46
|
.then(Packet.parse);
|
|
51
47
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dns2",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "A DNS Server and Client Implementation in Pure JavaScript with no dependencies.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"author": "Liu Song <hi@lsong.org>",
|
|
22
22
|
"contributors": [
|
|
23
23
|
"Andris Reinman <andris.reinman@gmail.com>",
|
|
24
|
-
"Eviltik <eviltik@gmail.com>"
|
|
24
|
+
"Eviltik <eviltik@gmail.com>",
|
|
25
|
+
"Martin Heidegger <martin.heidegger@gmail.com>"
|
|
25
26
|
],
|
|
26
27
|
"license": "MIT",
|
|
27
28
|
"repository": {
|
package/packet.js
CHANGED
|
@@ -514,9 +514,11 @@ Packet.Resource.AAAA = {
|
|
|
514
514
|
length -= 2;
|
|
515
515
|
parts.push(reader.read(16));
|
|
516
516
|
}
|
|
517
|
-
this.address = parts
|
|
518
|
-
|
|
519
|
-
|
|
517
|
+
this.address = parts
|
|
518
|
+
.map(part => part > 0 ? part.toString(16) : '')
|
|
519
|
+
.join(':')
|
|
520
|
+
.replace('::::', '::')
|
|
521
|
+
.replace(':::', '::');
|
|
520
522
|
return this;
|
|
521
523
|
},
|
|
522
524
|
encode: function(record, writer) {
|
package/server/dns.js
CHANGED
|
@@ -35,8 +35,10 @@ class DNSServer extends EventEmitter {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
const emitRequest = (request, send, client) => this.emit('request', request, send, client);
|
|
38
|
+
const emitRequestError = (error) => this.emit('requestError', error);
|
|
38
39
|
for (const server of servers) {
|
|
39
40
|
server.on('request', emitRequest);
|
|
41
|
+
server.on('requestError', emitRequestError);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
if (options.handle) {
|
|
@@ -59,17 +61,18 @@ class DNSServer extends EventEmitter {
|
|
|
59
61
|
return addresses;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
listen(
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
listen(options = {}) {
|
|
65
|
+
for (const serverType of Object.keys(this.servers)) {
|
|
66
|
+
const server = this.servers[serverType];
|
|
67
|
+
const serverOptions = options[serverType]; // Port or { port, address }
|
|
68
|
+
|
|
69
|
+
if (serverOptions && serverOptions.port) {
|
|
70
|
+
server.listen(serverOptions.port, serverOptions.address);
|
|
71
|
+
} else {
|
|
72
|
+
server.listen(serverOptions);
|
|
73
|
+
}
|
|
72
74
|
}
|
|
75
|
+
|
|
73
76
|
return this.listening;
|
|
74
77
|
}
|
|
75
78
|
|
package/server/doh.js
CHANGED
|
@@ -43,69 +43,74 @@ class Server extends EventEmitter {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
async handleRequest(client, res) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
// Check so the uri is correct
|
|
69
|
-
if (pathname !== '/dns-query') {
|
|
70
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
71
|
-
res.write('404 Not Found\n');
|
|
72
|
-
res.end();
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
// Make sure the requestee is requesting the correct content type
|
|
76
|
-
const contentType = headers.accept;
|
|
77
|
-
if (contentType !== 'application/dns-message') {
|
|
78
|
-
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
79
|
-
res.write('400 Bad Request: Illegal content type\n');
|
|
80
|
-
res.end();
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
let queryData;
|
|
84
|
-
if (method === 'GET') {
|
|
85
|
-
// Parse query string for the request data
|
|
86
|
-
const dns = query.get('dns');
|
|
87
|
-
if (!dns) {
|
|
88
|
-
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
89
|
-
res.write('400 Bad Request: No query defined\n');
|
|
46
|
+
try {
|
|
47
|
+
const { method, url, headers } = client;
|
|
48
|
+
const { pathname, searchParams: query } = new URL(url, 'http://unused/');
|
|
49
|
+
const { cors } = this;
|
|
50
|
+
if (cors === true) {
|
|
51
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
52
|
+
} else if (typeof cors === 'string') {
|
|
53
|
+
res.setHeader('Access-Control-Allow-Origin', cors);
|
|
54
|
+
res.setHeader('Vary', 'Origin');
|
|
55
|
+
} else if (typeof cors === 'function') {
|
|
56
|
+
const isAllowed = cors(headers.origin);
|
|
57
|
+
res.setHeader('Access-Control-Allow-Origin', isAllowed ? headers.origin : 'false');
|
|
58
|
+
res.setHeader('Vary', 'Origin');
|
|
59
|
+
}
|
|
60
|
+
// debug
|
|
61
|
+
debug('request', method, url);
|
|
62
|
+
// We are only handling get and post as reqired by rfc
|
|
63
|
+
if ((method !== 'GET' && method !== 'POST')) {
|
|
64
|
+
res.writeHead(405, { 'Content-Type': 'text/plain' });
|
|
65
|
+
res.write('405 Method not allowed\n');
|
|
90
66
|
res.end();
|
|
91
67
|
return;
|
|
92
68
|
}
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
69
|
+
// Check so the uri is correct
|
|
70
|
+
if (pathname !== '/dns-query') {
|
|
71
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
72
|
+
res.write('404 Not Found\n');
|
|
73
|
+
res.end();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Make sure the requestee is requesting the correct content type
|
|
77
|
+
const contentType = headers.accept;
|
|
78
|
+
if (contentType !== 'application/dns-message') {
|
|
96
79
|
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
97
|
-
res.write('400 Bad Request:
|
|
80
|
+
res.write('400 Bad Request: Illegal content type\n');
|
|
98
81
|
res.end();
|
|
99
82
|
return;
|
|
100
83
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
84
|
+
let queryData;
|
|
85
|
+
if (method === 'GET') {
|
|
86
|
+
// Parse query string for the request data
|
|
87
|
+
const dns = query.get('dns');
|
|
88
|
+
if (!dns) {
|
|
89
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
90
|
+
res.write('400 Bad Request: No query defined\n');
|
|
91
|
+
res.end();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Decode from Base64Url Encoding
|
|
95
|
+
const base64 = decodeBase64URL(dns);
|
|
96
|
+
if (!base64) {
|
|
97
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
98
|
+
res.write('400 Bad Request: Invalid query data\n');
|
|
99
|
+
res.end();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Decode Base64 to buffer
|
|
103
|
+
queryData = Buffer.from(base64, 'base64');
|
|
104
|
+
} else if (method === 'POST') {
|
|
105
|
+
queryData = await readStream(client);
|
|
106
|
+
}
|
|
107
|
+
// Parse DNS query and Raise event.
|
|
108
|
+
const message = Packet.parse(queryData);
|
|
109
|
+
this.emit('request', message, this.response.bind(this, res), client);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
this.emit('requestError', e);
|
|
112
|
+
res.destroy();
|
|
105
113
|
}
|
|
106
|
-
// Parse DNS query and Raise event.
|
|
107
|
-
const message = Packet.parse(queryData);
|
|
108
|
-
this.emit('request', message, this.response.bind(this, res), client);
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
/**
|
|
@@ -125,8 +130,8 @@ class Server extends EventEmitter {
|
|
|
125
130
|
* @param {*} port
|
|
126
131
|
* @returns
|
|
127
132
|
*/
|
|
128
|
-
listen(port) {
|
|
129
|
-
return this.server.listen(port || this.port);
|
|
133
|
+
listen(port, address) {
|
|
134
|
+
return this.server.listen(port || this.port, address);
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
address() {
|
package/server/tcp.js
CHANGED
|
@@ -11,9 +11,14 @@ class Server extends tcp.Server {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async handle(client) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
try {
|
|
15
|
+
const data = await Packet.readStream(client);
|
|
16
|
+
const message = Packet.parse(data);
|
|
17
|
+
this.emit('request', message, this.response.bind(this, client), client);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
this.emit('requestError', e);
|
|
20
|
+
client.destroy();
|
|
21
|
+
}
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
response(client, message) {
|
package/server/udp.js
CHANGED
|
@@ -20,8 +20,12 @@ class Server extends udp.Socket {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
handle(data, rinfo) {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
try {
|
|
24
|
+
const message = Packet.parse(data);
|
|
25
|
+
this.emit('request', message, this.response.bind(this, rinfo), rinfo);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
this.emit('requestError', e);
|
|
28
|
+
}
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
response(rinfo, message) {
|
package/test/index.js
CHANGED
|
@@ -2,6 +2,8 @@ const assert = require('assert');
|
|
|
2
2
|
const test = require('./test');
|
|
3
3
|
const { Packet, createDOHServer, createServer, TCPClient, DOHClient, UDPClient } = require('..');
|
|
4
4
|
const http = require('http');
|
|
5
|
+
const tcp = require('net');
|
|
6
|
+
const udp = require('dgram');
|
|
5
7
|
|
|
6
8
|
/* TODO: below is unused, either delete or use
|
|
7
9
|
const request = Buffer.from([
|
|
@@ -110,7 +112,7 @@ test('Packet#encode', function() {
|
|
|
110
112
|
type : Packet.TYPE.AAAA,
|
|
111
113
|
class : Packet.CLASS.IN,
|
|
112
114
|
ttl : 300,
|
|
113
|
-
address : '2001:db8
|
|
115
|
+
address : '2001:db8::ff00:42:8329',
|
|
114
116
|
});
|
|
115
117
|
|
|
116
118
|
response.answers.push({
|
|
@@ -335,6 +337,44 @@ test('server/all#simple-request', async() => {
|
|
|
335
337
|
await server.close();
|
|
336
338
|
});
|
|
337
339
|
|
|
340
|
+
test('server/all#invalid-request', async() => {
|
|
341
|
+
const server = createServer({
|
|
342
|
+
doh : true,
|
|
343
|
+
tcp : true,
|
|
344
|
+
udp : true,
|
|
345
|
+
handle : () => {},
|
|
346
|
+
});
|
|
347
|
+
const servers = await server.listen();
|
|
348
|
+
assert.ok(servers.udp.port > 1000);
|
|
349
|
+
assert.ok(servers.tcp.port > 1000);
|
|
350
|
+
assert.ok(servers.doh.port > 1000);
|
|
351
|
+
|
|
352
|
+
const errors = [];
|
|
353
|
+
server.on('requestError', (e) => {
|
|
354
|
+
errors.push(e);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const tcpSocket = tcp.connect({ port: servers.tcp.port, host: '127.0.0.1' });
|
|
358
|
+
tcpSocket.on('connect', () => tcpSocket.end('INVALID'));
|
|
359
|
+
|
|
360
|
+
const udpSocket = udp.createSocket('udp4');
|
|
361
|
+
udpSocket.send('INVALID', servers.udp.port, '127.0.0.1', () => udpSocket.close());
|
|
362
|
+
|
|
363
|
+
const dohConn = http.get(`http://127.0.0.1:${servers.doh.port}/dns-query?dns=INVALID`, {
|
|
364
|
+
headers: { accept: 'application/dns-message' },
|
|
365
|
+
}).on('error', () => {});
|
|
366
|
+
|
|
367
|
+
await Promise.all([
|
|
368
|
+
new Promise((resolve) => tcpSocket.on('close', resolve)),
|
|
369
|
+
new Promise((resolve) => udpSocket.on('close', resolve)),
|
|
370
|
+
new Promise((resolve) => dohConn.on('close', resolve)),
|
|
371
|
+
]);
|
|
372
|
+
|
|
373
|
+
assert.equal(errors.length, 3);
|
|
374
|
+
|
|
375
|
+
await server.close();
|
|
376
|
+
});
|
|
377
|
+
|
|
338
378
|
function get(url, options) {
|
|
339
379
|
return new Promise((resolve, reject) => {
|
|
340
380
|
try {
|