dns2 2.0.0 → 2.0.3

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/server/dns.js ADDED
@@ -0,0 +1,94 @@
1
+ const EventEmitter = require('events');
2
+ const DOHServer = require('./doh');
3
+ const TCPServer = require('./tcp');
4
+ const UDPServer = require('./udp');
5
+
6
+ class DNSServer extends EventEmitter {
7
+ constructor(options = {}) {
8
+ super();
9
+ this.servers = {};
10
+ if (options.doh) {
11
+ this.servers.doh = (new DOHServer(options.doh))
12
+ .on('error', error => this.emit('error', error, 'doh'));
13
+ }
14
+ if (options.tcp) {
15
+ this.servers.tcp = (new TCPServer())
16
+ .on('error', error => this.emit('error', error, 'tcp'));
17
+ }
18
+ if (options.udp) {
19
+ this.servers.udp = (new UDPServer(typeof options.udp === 'object' ? options.udp : undefined))
20
+ .on('error', error => this.emit('error', error, 'udp'));
21
+ }
22
+ const servers = Object.values(this.servers);
23
+ this.closed = Promise.all(
24
+ servers.map(server => new Promise(resolve => server.once('close', resolve))),
25
+ ).then(() => {
26
+ this.emit('close');
27
+ });
28
+
29
+ this.listening = Promise.all(
30
+ servers.map(server => new Promise(resolve => server.once('listening', resolve))),
31
+ ).then(() => {
32
+ const addresses = this.addresses();
33
+ this.emit('listening', addresses);
34
+ return addresses;
35
+ });
36
+
37
+ const emitRequest = (request, send, client) => this.emit('request', request, send, client);
38
+ const emitRequestError = (error) => this.emit('requestError', error);
39
+ for (const server of servers) {
40
+ server.on('request', emitRequest);
41
+ server.on('requestError', emitRequestError);
42
+ }
43
+
44
+ if (options.handle) {
45
+ this.on('request', options.handle.bind(options));
46
+ }
47
+ }
48
+
49
+ addresses() {
50
+ const addresses = {};
51
+ const { udp, tcp, doh } = this.servers;
52
+ if (udp) {
53
+ addresses.udp = udp.address();
54
+ }
55
+ if (tcp) {
56
+ addresses.tcp = tcp.address();
57
+ }
58
+ if (doh) {
59
+ addresses.doh = doh.address();
60
+ }
61
+ return addresses;
62
+ }
63
+
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
+ }
74
+ }
75
+
76
+ return this.listening;
77
+ }
78
+
79
+ close() {
80
+ const { doh, udp, tcp } = this.servers;
81
+ if (udp) {
82
+ udp.close();
83
+ }
84
+ if (tcp) {
85
+ tcp.close();
86
+ }
87
+ if (doh) {
88
+ doh.close();
89
+ }
90
+ return this.closed;
91
+ }
92
+ }
93
+
94
+ module.exports = DNSServer;
package/server/doh.js CHANGED
@@ -1,143 +1,146 @@
1
- const http = require('http');
2
- const https = require('https');
3
- const fs = require('fs');
4
- const url = require('url');
5
- const EventEmitter = require("events");
6
- const Packet = require('../packet');
7
-
8
- class Server extends EventEmitter {
9
- constructor(options) {
10
- super();
11
-
12
- // Set defaults and then check what the cat brought in
13
- var port = 80
14
- var sslport = 443;
15
- var cert = '';
16
- var key = '';
17
- if (typeof options === 'object') {
18
- if (options.http) port = options.port;
19
- if (options.sslport) sslport = options.sslport;
20
- if (options.cert) cert = options.cert;
21
- if (options.key) key = options.key;
22
- }
23
-
24
- //If no cert or key specified make a futile atempt of reading default names
25
- if (cert=='') cert = fs.readFileSync(path.join(__dirname,"server.crt"));
26
- if (key=='') key = fs.readFileSync(path.join(__dirname,"secret.key"));
27
-
28
- // if http port is specified create http server. However DoH needs SSL so a proxy must be infront.
29
- if (port!=0)
30
- var httpOptions = {
31
- };
32
- var httpServer = http.createServer(httpOptions).listen(port, function(){
33
- this.emit('listening', "http", port);
34
- });
35
- httpServer.on("request", this.handleRequest.bind(this))
36
-
37
- // if SSL-port, key and cert is specified then create an SSL endpoint
38
- if (sslport!=0&&key!=''&&cert!='')
39
- var httpsOptions = {
40
- key: key,
41
- cert: cert
42
- };
43
- var httpsServer = https.createServer(httpsOptions).listen(sslport, function(){
44
- this.emit('listening', "https", sslport);
45
- });
46
- httpsServer.on("request", this.handleRequest.bind(this))
47
- };
48
-
49
- handleRequest(req,res) {
50
-
51
- // We are only handling get and post as reqired by rfc
52
- const method = req.method;
53
- if ((method!="GET" && method!="POST")) {
54
- res.writeHead(405, {"Content-Type": "text/plain"});
55
- res.write("405 Method not allowed\n");
56
- res.end();
57
- return;
58
- }
59
-
60
- // Check so the uri is correct
61
- const uri = url.parse(req.url).pathname;
62
- if (uri!="/dns-query") {
63
- res.writeHead(404, {"Content-Type": "text/plain"});
64
- res.write("404 Not Found\n");
65
- res.end();
66
- return;
67
- }
68
-
69
- // Make sure the requestee is requesting the correct content type
70
- const contentType = req.headers['accept'];
71
- if (contentType!="application/dns-message") {
72
- res.writeHead(400, {"Content-Type": "text/plain"});
73
- res.write("400 Bad Request: Illegal content type\n");
74
- res.end();
75
- return;
76
- }
77
-
78
- var queryData = {};
79
-
80
- if (method == 'GET') {
81
- //Parse query string for the request data
82
- const queryObject = url.parse(req.url,true).query;
83
- if (!queryObject.dns) {
84
- res.writeHead(400, {"Content-Type": "text/plain"});
85
- res.write("400 Bad Request: No query defined\n");
86
- res.end();
87
- return;
88
- }
89
-
90
- //Decode from Base64Url Encoding
91
- var queryData = queryObject.dns.replace(/-/g, '+').replace(/_/g, '/');
92
- var pad = queryData.length % 4;
93
- if(pad) {
94
- if(pad === 1) {
95
- res.writeHead(400, {"Content-Type": "text/plain"});
96
- res.write("400 Bad Request: Invalid query data\n");
97
- res.end();
98
- return;
99
- }
100
- queryData += new Array(5-pad).join('=');
101
- }
102
-
103
- //Decode Base64 to buffer
104
- queryData = Buffer.from(queryData, 'base64');
105
- const message = Packet.parse(queryData);
106
-
107
- //Raise event
108
- this.emit('request', message, this.response.bind(this, res), req);
109
-
110
- } else {
111
-
112
- // Collect request from client and..
113
- collectRequestData.then((request)=>{
114
- const message = Packet.parse(request);
115
-
116
- //then raise the event
117
- this.emit('request', message, this.response.bind(this, res), req);
118
- });
119
- }
120
-
121
- };
122
-
123
- // Send of the response to the client
124
- response(res, message) {
125
- res.setHeader('Content-Type', 'application/dns-message');
126
- res.writeHead(200);
127
- res.end(message.toBuffer());
128
- };
129
-
130
- // Wait for the request body to be received
131
- async collectRequestData(request) {
132
- let body = '';
133
- request.on('data', chunk => {
134
- body += chunk;
135
- });
136
- request.on('end', () => {
137
- callback(body);
138
- });
139
- };
140
-
141
- }
142
-
143
- module.exports = Server;
1
+ const http = require('http');
2
+ const https = require('https');
3
+ const { URL } = require('url');
4
+ const Packet = require('../packet');
5
+ const EventEmitter = require('events');
6
+ const { debuglog } = require('util');
7
+
8
+ const debug = debuglog('dns2-server');
9
+
10
+ const decodeBase64URL = str => {
11
+ let queryData = str
12
+ .replace(/-/g, '+')
13
+ .replace(/_/g, '/');
14
+ const pad = queryData.length % 4;
15
+ if (pad === 1) return;
16
+ if (pad) {
17
+ queryData += new Array(5 - pad).join('=');
18
+ }
19
+ return queryData;
20
+ };
21
+
22
+ const readStream = stream => new Promise((resolve, reject) => {
23
+ let buffer = '';
24
+ stream
25
+ .on('error', reject)
26
+ .on('data', chunk => { buffer += chunk; })
27
+ .on('end', () => resolve(buffer));
28
+ });
29
+
30
+ class Server extends EventEmitter {
31
+ constructor(options) {
32
+ super();
33
+ const { ssl } = Object.assign(this, { cors: true }, options);
34
+ this.server = (ssl ? https.createServer(options) : http.createServer())
35
+ .on('request', this.handleRequest.bind(this))
36
+ .on('listening', () => this.emit('listening', this.address()))
37
+ .on('error', error => this.emit('error', error))
38
+ .on('close', () => {
39
+ this.server.removeAllListeners();
40
+ this.emit('close');
41
+ });
42
+ return this;
43
+ }
44
+
45
+ async handleRequest(client, res) {
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');
66
+ res.end();
67
+ return;
68
+ }
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') {
79
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
80
+ res.write('400 Bad Request: Illegal content type\n');
81
+ res.end();
82
+ return;
83
+ }
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();
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Send of the response to the client
118
+ * @param {*} res
119
+ * @param {*} message
120
+ */
121
+ response(res, message) {
122
+ debug('response');
123
+ res.setHeader('Content-Type', 'application/dns-message');
124
+ res.writeHead(200);
125
+ res.end(message.toBuffer());
126
+ }
127
+
128
+ /**
129
+ * listen
130
+ * @param {*} port
131
+ * @returns
132
+ */
133
+ listen(port, address) {
134
+ return this.server.listen(port || this.port, address);
135
+ }
136
+
137
+ address() {
138
+ return this.server.address();
139
+ }
140
+
141
+ close() {
142
+ return this.server.close();
143
+ }
144
+ }
145
+
146
+ module.exports = Server;
package/server/index.js CHANGED
@@ -1,24 +1,31 @@
1
- const UDPServer = require('./udp');
2
- const TCPServer = require('./tcp');
3
- const DOHServer = require('./doh');
4
-
5
- const createUDPServer = options => {
6
- return new UDPServer(options);
7
- };
8
-
9
- const createTCPServer = options => {
10
- return new TCPServer(options);
11
- };
12
-
13
- const createDOHServer = options => {
14
- return new DOHServer(options);
15
- }
16
-
17
- module.exports = {
18
- UDPServer,
19
- TCPServer,
20
- DOHServer,
21
- createTCPServer,
22
- createUDPServer,
23
- createDOHServer
24
- };
1
+ const UDPServer = require('./udp');
2
+ const TCPServer = require('./tcp');
3
+ const DOHServer = require('./doh');
4
+ const DNSServer = require('./dns');
5
+
6
+ const createUDPServer = options => {
7
+ return new UDPServer(options);
8
+ };
9
+
10
+ const createTCPServer = options => {
11
+ return new TCPServer(options);
12
+ };
13
+
14
+ const createDOHServer = options => {
15
+ return new DOHServer(options);
16
+ };
17
+
18
+ const createServer = options => {
19
+ return new DNSServer(options);
20
+ };
21
+
22
+ module.exports = {
23
+ UDPServer,
24
+ TCPServer,
25
+ DOHServer,
26
+ DNSServer,
27
+ createTCPServer,
28
+ createUDPServer,
29
+ createDOHServer,
30
+ createServer,
31
+ };
package/server/tcp.js CHANGED
@@ -1,26 +1,34 @@
1
- const tcp = require('net');
2
- const Packet = require('../packet');
3
-
4
- class Server extends tcp.Server {
5
- constructor(options) {
6
- super();
7
- if (typeof options === 'function') {
8
- this.on('request', options);
9
- }
10
- this.on('connection', this.handle.bind(this));
11
- }
12
- async handle(client) {
13
- const data = await Packet.readStream(client);
14
- const message = Packet.parse(data);
15
- this.emit('request', message, this.response.bind(this, client), client);
16
- }
17
- response(client, message) {
18
- if (message instanceof Packet)
19
- message = message.toBuffer();
20
- const len = Buffer.alloc(2);
21
- len.writeUInt16BE(message.length);
22
- client.end(Buffer.concat([len, message]));
23
- }
24
- }
25
-
26
- module.exports = Server;
1
+ const tcp = require('net');
2
+ const Packet = require('../packet');
3
+
4
+ class Server extends tcp.Server {
5
+ constructor(options) {
6
+ super();
7
+ if (typeof options === 'function') {
8
+ this.on('request', options);
9
+ }
10
+ this.on('connection', this.handle.bind(this));
11
+ }
12
+
13
+ async handle(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
+ }
22
+ }
23
+
24
+ response(client, message) {
25
+ if (message instanceof Packet) {
26
+ message = message.toBuffer();
27
+ }
28
+ const len = Buffer.alloc(2);
29
+ len.writeUInt16BE(message.length);
30
+ client.end(Buffer.concat([ len, message ]));
31
+ }
32
+ }
33
+
34
+ module.exports = Server;
package/server/udp.js CHANGED
@@ -1,41 +1,47 @@
1
- const udp = require('dgram');
2
- const Packet = require('../packet');
3
-
4
- /**
5
- * [Server description]
6
- * @docs https://tools.ietf.org/html/rfc1034
7
- * @docs https://tools.ietf.org/html/rfc1035
8
- */
9
- class Server extends udp.Socket {
10
- constructor(options) {
11
- let type = 'udp4';
12
- if (typeof options === 'object') {
13
- type = options.type;
14
- }
15
- super(type);
16
- if (typeof options === 'function') {
17
- this.on('request', options);
18
- }
19
- this.on('message', this.handle.bind(this));
20
- }
21
- handle(data, rinfo) {
22
- const message = Packet.parse(data);
23
- this.emit('request', message, this.response.bind(this, rinfo), rinfo);
24
- }
25
- response(rinfo, message) {
26
- if (message instanceof Packet)
27
- message = message.toBuffer();
28
- return new Promise((resolve, reject) => {
29
- this.send(message, rinfo.port, rinfo.address, err => {
30
- if (err) return reject(err);
31
- resolve(message);
32
- });
33
- });
34
- }
35
- listen(port, address) {
36
- return new Promise(resolve =>
37
- this.bind(port, address, resolve));
38
- }
39
- }
40
-
41
- module.exports = Server;
1
+ const udp = require('dgram');
2
+ const Packet = require('../packet');
3
+
4
+ /**
5
+ * [Server description]
6
+ * @docs https://tools.ietf.org/html/rfc1034
7
+ * @docs https://tools.ietf.org/html/rfc1035
8
+ */
9
+ class Server extends udp.Socket {
10
+ constructor(options) {
11
+ let type = 'udp4';
12
+ if (typeof options === 'object') {
13
+ type = options.type;
14
+ }
15
+ super(type);
16
+ if (typeof options === 'function') {
17
+ this.on('request', options);
18
+ }
19
+ this.on('message', this.handle.bind(this));
20
+ }
21
+
22
+ handle(data, 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
+ }
29
+ }
30
+
31
+ response(rinfo, message) {
32
+ if (message instanceof Packet) { message = message.toBuffer(); }
33
+ return new Promise((resolve, reject) => {
34
+ this.send(message, rinfo.port, rinfo.address, err => {
35
+ if (err) return reject(err);
36
+ resolve(message);
37
+ });
38
+ });
39
+ }
40
+
41
+ listen(port, address) {
42
+ return new Promise(resolve =>
43
+ this.bind(port, address, resolve));
44
+ }
45
+ }
46
+
47
+ module.exports = Server;