pending-dns 1.1.1 → 1.2.0
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 +1 -1
- package/lib/api-server.js +16 -18
- package/lib/cached-resolver.js +1 -1
- package/lib/certs.js +4 -6
- package/lib/dns-handler.js +39 -13
- package/lib/dns-server.js +2 -2
- package/lib/dns-tcp-server.js +1 -3
- package/lib/health-worker.js +4 -6
- package/lib/public-server.js +7 -15
- package/lib/tools.js +1 -1
- package/lib/zone-store.js +3 -4
- package/package.json +23 -21
package/README.md
CHANGED
package/lib/api-server.js
CHANGED
|
@@ -18,28 +18,26 @@ const hostnameSchema = Joi.string().hostname({
|
|
|
18
18
|
minDomainSegments: 1
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
const subdomainValidator = opts => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
21
|
+
const subdomainValidator = opts => (value, helpers) => {
|
|
22
|
+
if (!value) {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
let valueToCheck = value;
|
|
27
|
+
if (opts.allowUnderscore) {
|
|
28
|
+
valueToCheck = valueToCheck.replace(/\b_/g, 'x');
|
|
29
|
+
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
if (opts.allowWildcard) {
|
|
32
|
+
valueToCheck = valueToCheck.replace(/^\*\./, 'x.');
|
|
33
|
+
}
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
let result = hostnameSchema.validate(valueToCheck);
|
|
36
|
+
if (result.error) {
|
|
37
|
+
return helpers.error('any.invalid');
|
|
38
|
+
}
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
};
|
|
40
|
+
return value;
|
|
43
41
|
};
|
|
44
42
|
|
|
45
43
|
const recordScheme = Joi.object({
|
package/lib/cached-resolver.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const config = require('wild-config');
|
|
4
4
|
const db = require('./db');
|
|
5
|
-
const punycode = require('punycode');
|
|
5
|
+
const punycode = require('punycode/');
|
|
6
6
|
const { Resolver } = require('dns').promises;
|
|
7
7
|
const resolver = new Resolver();
|
|
8
8
|
const logger = require('./logger').child({ component: 'cached-resolver' });
|
package/lib/certs.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const config = require('wild-config');
|
|
4
4
|
const pkg = require('../package.json');
|
|
5
5
|
const db = require('./db');
|
|
6
|
-
const punycode = require('punycode');
|
|
6
|
+
const punycode = require('punycode/');
|
|
7
7
|
const { zoneStore } = require('./zone-store');
|
|
8
8
|
const crypto = require('crypto');
|
|
9
9
|
const { checkNSStatus, normalizeDomain } = require('./tools');
|
|
@@ -34,11 +34,9 @@ const acme = ACME.create({
|
|
|
34
34
|
},
|
|
35
35
|
dns01(query) {
|
|
36
36
|
return localResolver.resolveTxt(query.dnsHost).then(records => ({
|
|
37
|
-
answer: records.map(rr => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
41
|
-
})
|
|
37
|
+
answer: records.map(rr => ({
|
|
38
|
+
data: rr
|
|
39
|
+
}))
|
|
42
40
|
}));
|
|
43
41
|
}
|
|
44
42
|
});
|
package/lib/dns-handler.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const config = require('wild-config');
|
|
4
4
|
const dns2 = require('dns2');
|
|
5
|
-
const punycode = require('punycode');
|
|
5
|
+
const punycode = require('punycode/');
|
|
6
6
|
const { zoneStore } = require('./zone-store');
|
|
7
7
|
const cachedResolver = require('./cached-resolver');
|
|
8
8
|
const ipaddr = require('ipaddr.js');
|
|
9
9
|
const logger = require('./logger').child({ component: 'dns-handler' });
|
|
10
10
|
const { normalizeDomain } = require('./tools');
|
|
11
|
+
const { v4: uuidv4 } = require('uuid');
|
|
11
12
|
|
|
12
13
|
// Split long string values into character chunks
|
|
13
14
|
const formatTXTData = data => {
|
|
@@ -19,11 +20,7 @@ const formatTXTData = data => {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
// Helps to convert DNS type integer into a string (0x01 -> 'A')
|
|
22
|
-
const reversedTypes = new Map(
|
|
23
|
-
Object.keys(dns2.Packet.TYPE).map(key => {
|
|
24
|
-
return [dns2.Packet.TYPE[key], key];
|
|
25
|
-
})
|
|
26
|
-
);
|
|
23
|
+
const reversedTypes = new Map(Object.keys(dns2.Packet.TYPE).map(key => [dns2.Packet.TYPE[key], key]));
|
|
27
24
|
|
|
28
25
|
const shuffle = array => {
|
|
29
26
|
let currentIndex = array.length,
|
|
@@ -89,7 +86,7 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
89
86
|
let dnsEntries = (
|
|
90
87
|
await Promise.all(
|
|
91
88
|
Array.from(types).map(async type => {
|
|
92
|
-
let records = await zoneStore.resolve(domain, type,
|
|
89
|
+
let records = await zoneStore.resolve(domain, type, false);
|
|
93
90
|
if (records && records.length > 1) {
|
|
94
91
|
switch (type) {
|
|
95
92
|
case 'A':
|
|
@@ -187,7 +184,13 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
187
184
|
break;
|
|
188
185
|
}
|
|
189
186
|
} catch (err) {
|
|
190
|
-
logger.error({
|
|
187
|
+
logger.error({
|
|
188
|
+
msg: 'Failed resolving ANAME',
|
|
189
|
+
id: response._id,
|
|
190
|
+
domain: dnsEntry.domain,
|
|
191
|
+
type: questionTypeStr,
|
|
192
|
+
err
|
|
193
|
+
});
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
[].concat(value || []).forEach(value => {
|
|
@@ -258,7 +261,12 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
258
261
|
try {
|
|
259
262
|
entry.domain = punycode.toASCII(entry.domain);
|
|
260
263
|
} catch (err) {
|
|
261
|
-
logger.error({
|
|
264
|
+
logger.error({
|
|
265
|
+
msg: 'Failed to punycode',
|
|
266
|
+
id: response._id,
|
|
267
|
+
domain: entry.domain,
|
|
268
|
+
err
|
|
269
|
+
});
|
|
262
270
|
}
|
|
263
271
|
break;
|
|
264
272
|
|
|
@@ -267,7 +275,12 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
267
275
|
try {
|
|
268
276
|
entry.ns = punycode.toASCII(entry.ns);
|
|
269
277
|
} catch (err) {
|
|
270
|
-
logger.error({
|
|
278
|
+
logger.error({
|
|
279
|
+
msg: 'Failed to punycode',
|
|
280
|
+
id: response._id,
|
|
281
|
+
domain: entry.ns,
|
|
282
|
+
err
|
|
283
|
+
});
|
|
271
284
|
}
|
|
272
285
|
break;
|
|
273
286
|
|
|
@@ -284,7 +297,12 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
284
297
|
try {
|
|
285
298
|
entry.exchange = punycode.toASCII(entry.exchange);
|
|
286
299
|
} catch (err) {
|
|
287
|
-
logger.error({
|
|
300
|
+
logger.error({
|
|
301
|
+
msg: 'Failed to punycode',
|
|
302
|
+
id: response._id,
|
|
303
|
+
domain: entry.exchange,
|
|
304
|
+
err
|
|
305
|
+
});
|
|
288
306
|
}
|
|
289
307
|
break;
|
|
290
308
|
|
|
@@ -316,6 +334,7 @@ const processQuestion = async (response, question, domain, depth) => {
|
|
|
316
334
|
const dnsHandler = async request => {
|
|
317
335
|
let startTime = Date.now();
|
|
318
336
|
const response = new dns2.Packet(request);
|
|
337
|
+
request._id = response._id = uuidv4();
|
|
319
338
|
|
|
320
339
|
response.header.qr = 1;
|
|
321
340
|
response.header.aa = 1;
|
|
@@ -323,18 +342,25 @@ const dnsHandler = async request => {
|
|
|
323
342
|
await Promise.all(
|
|
324
343
|
request.questions.map(question => {
|
|
325
344
|
logger.info({
|
|
345
|
+
id: request._id,
|
|
326
346
|
msg: 'DNS query',
|
|
327
347
|
type: request.source.type,
|
|
328
348
|
port: request.source.port,
|
|
329
349
|
address: request.source.address,
|
|
330
|
-
|
|
350
|
+
question: question.name,
|
|
331
351
|
rr: reversedTypes.get(question.type) || question.type
|
|
332
352
|
});
|
|
333
353
|
return processQuestion(response, question);
|
|
334
354
|
})
|
|
335
355
|
);
|
|
336
356
|
|
|
337
|
-
logger.info({
|
|
357
|
+
logger.info({
|
|
358
|
+
msg: 'DNS response',
|
|
359
|
+
id: request._id,
|
|
360
|
+
dnsTime: Date.now() - startTime,
|
|
361
|
+
questions: request.questions,
|
|
362
|
+
answers: response.answers
|
|
363
|
+
});
|
|
338
364
|
|
|
339
365
|
// normalize answers for the DNS library
|
|
340
366
|
response.answers.forEach(answer => {
|
package/lib/dns-server.js
CHANGED
|
@@ -15,7 +15,7 @@ const SUPPORTED_TYPES = new Set(
|
|
|
15
15
|
|
|
16
16
|
const init = async () => {
|
|
17
17
|
// create UDP server
|
|
18
|
-
createDNSUdpServer(
|
|
18
|
+
createDNSUdpServer((request, send) => {
|
|
19
19
|
// filter out unsupported requests (eg. EDNS)
|
|
20
20
|
if (request.additionals && request.additionals.length) {
|
|
21
21
|
request.additionals = request.additionals.filter(additional => SUPPORTED_TYPES.has(additional.type));
|
|
@@ -43,7 +43,7 @@ const init = async () => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
// create TCP server
|
|
46
|
-
createDNSTcpServer(
|
|
46
|
+
createDNSTcpServer((request, send) => {
|
|
47
47
|
// filter out unsupported requests (eg. EDNS)
|
|
48
48
|
if (request.additionals && request.additionals.length) {
|
|
49
49
|
request.additionals = request.additionals.filter(additional => SUPPORTED_TYPES.has(additional.type));
|
package/lib/dns-tcp-server.js
CHANGED
|
@@ -52,9 +52,7 @@ class DNSTcpServer extends EventEmitter {
|
|
|
52
52
|
port: socket.remotePort,
|
|
53
53
|
address: socket.remoteAddress
|
|
54
54
|
};
|
|
55
|
-
this.emit('request', request, message =>
|
|
56
|
-
return this.send(socket, message);
|
|
57
|
-
});
|
|
55
|
+
this.emit('request', request, message => this.send(socket, message));
|
|
58
56
|
};
|
|
59
57
|
|
|
60
58
|
socket.on('readable', () => {
|
package/lib/health-worker.js
CHANGED
|
@@ -10,8 +10,8 @@ const tls = require('tls');
|
|
|
10
10
|
const http = require('http');
|
|
11
11
|
const https = require('https');
|
|
12
12
|
|
|
13
|
-
const tcpHealthCheck = async url =>
|
|
14
|
-
|
|
13
|
+
const tcpHealthCheck = async url =>
|
|
14
|
+
new Promise((resolve, reject) => {
|
|
15
15
|
let connectTimeout;
|
|
16
16
|
|
|
17
17
|
let conn;
|
|
@@ -57,10 +57,9 @@ const tcpHealthCheck = async url => {
|
|
|
57
57
|
client.on('timeout', onTimeout);
|
|
58
58
|
client.setTimeout(config.health.ttl);
|
|
59
59
|
});
|
|
60
|
-
};
|
|
61
60
|
|
|
62
|
-
const httpHealthCheck = async url =>
|
|
63
|
-
|
|
61
|
+
const httpHealthCheck = async url =>
|
|
62
|
+
new Promise((resolve, reject) => {
|
|
64
63
|
let connectTimeout;
|
|
65
64
|
|
|
66
65
|
let conn;
|
|
@@ -111,7 +110,6 @@ const httpHealthCheck = async url => {
|
|
|
111
110
|
client.on('timeout', onTimeout);
|
|
112
111
|
client.setTimeout(config.health.ttl);
|
|
113
112
|
});
|
|
114
|
-
};
|
|
115
113
|
|
|
116
114
|
const healthCheck = async target => {
|
|
117
115
|
try {
|
package/lib/public-server.js
CHANGED
|
@@ -17,7 +17,7 @@ const httpProxy = require('http-proxy');
|
|
|
17
17
|
|
|
18
18
|
const proxyServer = httpProxy.createProxyServer({});
|
|
19
19
|
|
|
20
|
-
proxyServer.on('proxyReq',
|
|
20
|
+
proxyServer.on('proxyReq', (proxyReq, req /*, res, options*/) => {
|
|
21
21
|
proxyReq.setHeader('X-Forwarded-Proto', req.proto);
|
|
22
22
|
proxyReq.setHeader('X-Connecting-IP', req.ip);
|
|
23
23
|
proxyReq.setHeader('X-CDN-Loop', 'PendingDNS');
|
|
@@ -181,11 +181,7 @@ const handler = async (req, res) => {
|
|
|
181
181
|
const url = new URL(req.url, `${req.proto}://${domain}/`);
|
|
182
182
|
const route = url.pathname;
|
|
183
183
|
|
|
184
|
-
const target =
|
|
185
|
-
records &&
|
|
186
|
-
records.find(rr => {
|
|
187
|
-
return rr && rr.type === 'URL' && rr.value && rr.value.length;
|
|
188
|
-
});
|
|
184
|
+
const target = records && records.find(rr => rr && rr.type === 'URL' && rr.value && rr.value.length);
|
|
189
185
|
|
|
190
186
|
if (target) {
|
|
191
187
|
// redirect to target URL
|
|
@@ -238,8 +234,8 @@ const handler = async (req, res) => {
|
|
|
238
234
|
);
|
|
239
235
|
};
|
|
240
236
|
|
|
241
|
-
const setupHttps = () =>
|
|
242
|
-
|
|
237
|
+
const setupHttps = () =>
|
|
238
|
+
new Promise((resolve, reject) => {
|
|
243
239
|
const server = https.createServer(
|
|
244
240
|
{
|
|
245
241
|
key: defaultKey,
|
|
@@ -249,9 +245,7 @@ const setupHttps = () => {
|
|
|
249
245
|
allowHTTP1: true,
|
|
250
246
|
SNICallback(servername, cb) {
|
|
251
247
|
getSNIContext(servername)
|
|
252
|
-
.then(ctx =>
|
|
253
|
-
return cb(null, ctx || defaultCtx);
|
|
254
|
-
})
|
|
248
|
+
.then(ctx => cb(null, ctx || defaultCtx))
|
|
255
249
|
.catch(err => {
|
|
256
250
|
logger.error({ msg: 'SNI failed', servername, err });
|
|
257
251
|
return cb(null, defaultCtx);
|
|
@@ -325,10 +319,9 @@ const setupHttps = () => {
|
|
|
325
319
|
reject(err);
|
|
326
320
|
});
|
|
327
321
|
});
|
|
328
|
-
};
|
|
329
322
|
|
|
330
|
-
const setupHttp = () =>
|
|
331
|
-
|
|
323
|
+
const setupHttp = () =>
|
|
324
|
+
new Promise((resolve, reject) => {
|
|
332
325
|
const server = http.createServer((req, res) => {
|
|
333
326
|
req.proto = 'http';
|
|
334
327
|
middleware(req, res);
|
|
@@ -362,7 +355,6 @@ const setupHttp = () => {
|
|
|
362
355
|
reject(err);
|
|
363
356
|
});
|
|
364
357
|
});
|
|
365
|
-
};
|
|
366
358
|
|
|
367
359
|
const init = async () => {
|
|
368
360
|
await Promise.all([setupHttps(), setupHttp()]);
|
package/lib/tools.js
CHANGED
package/lib/zone-store.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const punycode = require('punycode');
|
|
3
|
+
const punycode = require('punycode/');
|
|
4
4
|
const shortid = require('shortid');
|
|
5
5
|
const db = require('./db');
|
|
6
6
|
const logger = require('./logger').child({ component: 'zone-store' });
|
|
@@ -196,6 +196,7 @@ class ZoneStore {
|
|
|
196
196
|
if (short) {
|
|
197
197
|
return {
|
|
198
198
|
id: this.getFullId(name, type, hid),
|
|
199
|
+
zone: zoneDomain,
|
|
199
200
|
domain,
|
|
200
201
|
type,
|
|
201
202
|
value: JSON.parse(value)
|
|
@@ -225,9 +226,7 @@ class ZoneStore {
|
|
|
225
226
|
|
|
226
227
|
return Object.keys(record)
|
|
227
228
|
.map(key => [key, record[key]])
|
|
228
|
-
.sort((a, b) =>
|
|
229
|
-
return a[1].localeCompare(b[1]);
|
|
230
|
-
})
|
|
229
|
+
.sort((a, b) => a[1].localeCompare(b[1]))
|
|
231
230
|
.map(entry => {
|
|
232
231
|
let hid = entry[0];
|
|
233
232
|
let value = entry[1];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pending-dns",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Lightweight API driven DNS server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,34 +25,36 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/postalsys/pending-dns#readme",
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"eslint": "
|
|
28
|
+
"eslint": "8.18.0",
|
|
29
29
|
"eslint-config-nodemailer": "1.2.0",
|
|
30
|
-
"eslint-config-prettier": "
|
|
31
|
-
"grunt": "1.
|
|
32
|
-
"grunt-cli": "1.3
|
|
33
|
-
"grunt-eslint": "
|
|
30
|
+
"eslint-config-prettier": "8.5.0",
|
|
31
|
+
"grunt": "1.5.3",
|
|
32
|
+
"grunt-cli": "1.4.3",
|
|
33
|
+
"grunt-eslint": "24.0.0",
|
|
34
34
|
"npm-license-crawler": "0.2.1"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@fidm/x509": "1.2.1",
|
|
38
|
-
"@hapi/boom": "
|
|
39
|
-
"@hapi/hapi": "
|
|
40
|
-
"@hapi/inert": "6.0.
|
|
38
|
+
"@hapi/boom": "10.0.0",
|
|
39
|
+
"@hapi/hapi": "20.2.2",
|
|
40
|
+
"@hapi/inert": "6.0.5",
|
|
41
41
|
"@hapi/joi": "17.1.1",
|
|
42
|
-
"@hapi/vision": "6.
|
|
43
|
-
"@root/acme": "3.0
|
|
42
|
+
"@hapi/vision": "6.1.0",
|
|
43
|
+
"@root/acme": "3.1.0",
|
|
44
44
|
"@root/csr": "0.8.1",
|
|
45
|
-
"dns2": "
|
|
46
|
-
"hapi-pino": "
|
|
47
|
-
"hapi-swagger": "
|
|
45
|
+
"dns2": "2.0.2",
|
|
46
|
+
"hapi-pino": "10.1.0",
|
|
47
|
+
"hapi-swagger": "14.5.5",
|
|
48
48
|
"http-proxy": "1.18.1",
|
|
49
|
-
"ioredfour": "1.0
|
|
50
|
-
"ioredis": "
|
|
51
|
-
"ipaddr.js": "
|
|
52
|
-
"node-rsa": "1.
|
|
49
|
+
"ioredfour": "1.2.0-ioredis-06",
|
|
50
|
+
"ioredis": "5.0.6",
|
|
51
|
+
"ipaddr.js": "2.0.1",
|
|
52
|
+
"node-rsa": "1.1.1",
|
|
53
53
|
"pem-jwk": "2.0.0",
|
|
54
|
-
"pino": "
|
|
55
|
-
"
|
|
56
|
-
"
|
|
54
|
+
"pino": "8.1.0",
|
|
55
|
+
"punycode": "^2.1.1",
|
|
56
|
+
"shortid": "2.2.16",
|
|
57
|
+
"uuid": "^8.3.2",
|
|
58
|
+
"wild-config": "1.6.1"
|
|
57
59
|
}
|
|
58
60
|
}
|