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 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.address());
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: 5333
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 get = url => new Promise((resolve, reject) => {
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.1",
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.map(function(part) {
518
- return part > 0 ? part.toString(16) : '';
519
- }).join(':');
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(ports = {}) {
63
- const { udp, tcp, doh } = this.servers;
64
- if (udp) {
65
- udp.listen(ports.udp);
66
- }
67
- if (tcp) {
68
- tcp.listen(ports.tcp);
69
- }
70
- if (doh) {
71
- doh.listen(ports.doh);
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
- const { method, url, headers } = client;
47
- const { pathname, searchParams: query } = new URL(url, 'http://unused/');
48
- const { cors } = this;
49
- if (cors === true) {
50
- res.setHeader('Access-Control-Allow-Origin', '*');
51
- } else if (typeof cors === 'string') {
52
- res.setHeader('Access-Control-Allow-Origin', cors);
53
- res.setHeader('Vary', 'Origin');
54
- } else if (typeof cors === 'function') {
55
- const isAllowed = cors(headers.origin);
56
- res.setHeader('Access-Control-Allow-Origin', isAllowed ? headers.origin : 'false');
57
- res.setHeader('Vary', 'Origin');
58
- }
59
- // debug
60
- debug('request', method, url);
61
- // We are only handling get and post as reqired by rfc
62
- if ((method !== 'GET' && method !== 'POST')) {
63
- res.writeHead(405, { 'Content-Type': 'text/plain' });
64
- res.write('405 Method not allowed\n');
65
- res.end();
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
- // Decode from Base64Url Encoding
94
- const base64 = decodeBase64URL(dns);
95
- if (!base64) {
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: Invalid query data\n');
80
+ res.write('400 Bad Request: Illegal content type\n');
98
81
  res.end();
99
82
  return;
100
83
  }
101
- // Decode Base64 to buffer
102
- queryData = Buffer.from(base64, 'base64');
103
- } else if (method === 'POST') {
104
- queryData = await readStream(client);
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
- const data = await Packet.readStream(client);
15
- const message = Packet.parse(data);
16
- this.emit('request', message, this.response.bind(this, client), client);
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
- const message = Packet.parse(data);
24
- this.emit('request', message, this.response.bind(this, rinfo), rinfo);
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::::ff00:42:8329',
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 {