mirror of
https://github.com/avwo/whistle.git
synced 2026-10-19 07:01:44 +08:00
164 lines
4.0 KiB
JavaScript
164 lines
4.0 KiB
JavaScript
var dns = require('dns');
|
|
var net = require('net');
|
|
var request = require('../util/http-mgr').request;
|
|
var config = require('../config');
|
|
|
|
var dnsCacheTime = parseInt(config.dnsCache, 10);
|
|
var dnsServer = config.dnsServer;
|
|
var dnsOverHttps = config.dnsOverHttps;
|
|
var resolve6 = config.resolve6;
|
|
var dnsOptional = config.dnsOptional;
|
|
var dnsCache = {};
|
|
var callbacks = {};
|
|
var TIMEOUT = 10000;
|
|
var CACHE_TIME = dnsCacheTime >= 0 ? dnsCacheTime : 60000;
|
|
var MAX_CACHE_TIME = Math.max(CACHE_TIME * 3, 600000);
|
|
var IPV6_OPTIONS = {family: 6};
|
|
|
|
if (dnsOverHttps) {
|
|
dnsOverHttps += (dnsOverHttps.indexOf('?') === -1 ? '?' : '&') + 'name=';
|
|
}
|
|
|
|
function getIpFromAnswer(data) {
|
|
var index = data.length - 1;
|
|
var ip = data[index] && data[index].data;
|
|
if (net.isIP(ip)) {
|
|
return ip;
|
|
}
|
|
while (index-- > 0) {
|
|
ip = data[index] && data[index].data;
|
|
if (net.isIP(ip)) {
|
|
return ip;
|
|
}
|
|
}
|
|
}
|
|
|
|
function lookDnsOverHttps(hostname, callback) {
|
|
request(
|
|
{
|
|
url: dnsOverHttps + hostname,
|
|
rejectUnauthorized: config.rejectUnauthorized
|
|
},
|
|
function (err, data) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
try {
|
|
data = JSON.parse(data);
|
|
data = data && data.Answer;
|
|
return callback(null, data && getIpFromAnswer(data));
|
|
} catch (e) {
|
|
err = data || e;
|
|
}
|
|
return callback(err || 'DNS Over HTTPS Look Failed.');
|
|
}
|
|
);
|
|
}
|
|
|
|
function lookupDNS(hostname, callback) {
|
|
if (net.isIP(hostname)) {
|
|
return callback(null, hostname);
|
|
}
|
|
var list = callbacks[hostname];
|
|
if (list) {
|
|
if (callback) {
|
|
list.push(callback);
|
|
}
|
|
return;
|
|
}
|
|
callbacks[hostname] = list = [];
|
|
if (callback) {
|
|
list.push(callback);
|
|
}
|
|
var done;
|
|
var timer;
|
|
var optional = dnsOptional;
|
|
var handleDns = function (fn) {
|
|
timer = setTimeout(function () {
|
|
execCallback(new Error('Timeout'));
|
|
}, TIMEOUT);
|
|
if (!fn) {
|
|
if (dnsServer) {
|
|
fn = resolve6 ? 'resolve6' : 'resolve4';
|
|
} else {
|
|
fn = 'lookup';
|
|
}
|
|
}
|
|
var handleCallback = function (err, ip, type) {
|
|
clearTimeout(timer);
|
|
if (err) {
|
|
execCallback(err);
|
|
} else {
|
|
ip = Array.isArray(ip) ? ip[0] : ip;
|
|
if (!ip && optional) {
|
|
execCallback(true);
|
|
} else {
|
|
execCallback(null, ip || getDefaultIp(type));
|
|
}
|
|
}
|
|
};
|
|
try {
|
|
if (dnsOverHttps) {
|
|
lookDnsOverHttps(hostname, handleCallback);
|
|
} else if (config.ipv6Only && fn === 'lookup') {
|
|
dns[fn](hostname, IPV6_OPTIONS, handleCallback);
|
|
} else {
|
|
dns[fn](hostname, handleCallback);
|
|
}
|
|
} catch (err) {
|
|
//如果断网,可能直接抛异常,https代理没有用到error-handler
|
|
execCallback(err);
|
|
}
|
|
};
|
|
function execCallback(err, ip) {
|
|
clearTimeout(timer);
|
|
if (!err) {
|
|
dnsCache[hostname] = {
|
|
ip: ip,
|
|
hostname: hostname,
|
|
ipv6Only: config.ipv6Only,
|
|
time: Date.now()
|
|
};
|
|
} else if (optional) {
|
|
optional = false;
|
|
return handleDns('lookup');
|
|
}
|
|
if (done) {
|
|
return;
|
|
}
|
|
done = true;
|
|
var host = dnsCache[hostname];
|
|
delete callbacks[hostname];
|
|
list.forEach(function (callback) {
|
|
callback(err, host && host.ip);
|
|
});
|
|
}
|
|
handleDns();
|
|
}
|
|
|
|
function getDefaultIp(type) {
|
|
return resolve6 || type == 6 ? '0:0:0:0:0:0:0:1' : '127.0.0.1';
|
|
}
|
|
|
|
function checkCacheData(host) {
|
|
return host && (config.ipv6Only ? host.ipv6Only : !host.ipv6Only) ? host: null;
|
|
}
|
|
|
|
module.exports = function lookup(hostname, callback, allowDnsCache) {
|
|
var host = allowDnsCache ? dnsCache[hostname] : null;
|
|
var cacheTime;
|
|
if (checkCacheData(host)) {
|
|
cacheTime = Date.now() - host.time;
|
|
}
|
|
if (host && cacheTime < MAX_CACHE_TIME) {
|
|
callback(null, host.ip);
|
|
if (cacheTime > CACHE_TIME) {
|
|
lookupDNS(host.hostname);
|
|
}
|
|
return host.ip;
|
|
}
|
|
lookupDNS(hostname, function (err, ip) {
|
|
err ? lookupDNS(hostname, callback) : callback(err, ip);
|
|
});
|
|
};
|