1 | 'use strict';
|
2 | const {promisify} = require('util');
|
3 | const dns = require('dns');
|
4 | const net = require('net');
|
5 | const arrify = require('arrify');
|
6 | const got = require('got');
|
7 | const isPortReachable = require('is-port-reachable');
|
8 | const pAny = require('p-any');
|
9 | const pTimeout = require('p-timeout');
|
10 | const prependHttp = require('prepend-http');
|
11 | const routerIps = require('router-ips');
|
12 | const URL = require('url-parse');
|
13 |
|
14 | const dnsLookupP = promisify(dns.lookup);
|
15 |
|
16 | const checkHttp = async url => {
|
17 | let response;
|
18 | try {
|
19 | response = await got(url, {rejectUnauthorized: false});
|
20 | } catch (_) {
|
21 | return false;
|
22 | }
|
23 |
|
24 | if (response.headers && response.headers.location) {
|
25 | const url = new URL(response.headers.location);
|
26 | const hostname = url.hostname.replace(/^\[/, '').replace(/\]$/, '');
|
27 | return !routerIps.has(hostname);
|
28 | }
|
29 |
|
30 | return true;
|
31 | };
|
32 |
|
33 | const getAddress = async hostname => net.isIP(hostname) ? hostname : (await dnsLookupP(hostname)).address;
|
34 |
|
35 | const isTargetReachable = async target => {
|
36 | const url = new URL(prependHttp(target));
|
37 |
|
38 | if (!url.port) {
|
39 | url.port = url.protocol === 'http:' ? 80 : 443;
|
40 | }
|
41 |
|
42 | let address;
|
43 | try {
|
44 | address = await getAddress(url.hostname);
|
45 | } catch (_) {
|
46 | return false;
|
47 | }
|
48 |
|
49 | if (!address || routerIps.has(address)) {
|
50 | return false;
|
51 | }
|
52 |
|
53 | if ([80, 443].includes(url.port)) {
|
54 | return checkHttp(url.toString());
|
55 | }
|
56 |
|
57 | return isPortReachable(url.port, {host: address});
|
58 | };
|
59 |
|
60 | module.exports = async (destinations, options) => {
|
61 | options = {...options};
|
62 | options.timeout = typeof options.timeout === 'number' ? options.timeout : 5000;
|
63 |
|
64 | const promise = pAny(arrify(destinations).map(isTargetReachable));
|
65 | return pTimeout(promise, options.timeout).catch(() => false);
|
66 | };
|