xep-whois
Version:
A lightweight WHOIS client
408 lines • 14.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WhoIsParser = exports.ProxyType = void 0;
exports.tcpWhois = tcpWhois;
exports.whois = whois;
exports.batchWhois = batchWhois;
const socks_1 = require("socks");
const net_1 = __importDefault(require("net"));
const SERVERS = require('./../whois-servers.json');
const PARAMETERS = require('./../parameters.json');
const IANA_CHK_URL = 'https://www.iana.org/whois?q=';
const loggerLocal = {
info(...data) {
console.info(data);
},
debug(...data) {
console.debug(data);
},
error(...data) {
console.error(data);
}
};
/**
* Find the WhoIs server for the TLD from IANA WhoIs service. The TLD is be searched and the HTML response is parsed to extract the WhoIs server
*
* @param tld TLD of the domain
* @param logger
* @returns WhoIs server which hosts the information for the domains of the TLD
*/
async function findWhoIsServer(tld, logger) {
const chkURL = IANA_CHK_URL + tld;
try {
const res = await fetch(chkURL);
if (res.ok) {
const body = await res.text();
const server = body.match(/whois:\s+(.*)\s+/);
if (server) {
return server[1];
}
}
}
catch (err) {
logger.error('Error in getting WhoIs server data from IANA', err);
}
return '';
}
/**
* Copy an Object with its values
*
* @param obj Object which needs to be copied
* @returns A copy of the object
*/
function shallowCopy(obj) {
if (Array.isArray(obj)) {
return obj.slice(); // Clone the array
}
else if (typeof obj === 'object' && obj !== null) {
const copy = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
copy[key] = shallowCopy(obj[key]);
}
}
return copy;
}
else {
return obj; // For primitive values, return as is
}
}
/**
* Get whois server of the tld from servers list
*
* @param tld TLD of the domain
* @returns WhoIs server which hosts the information for the domains of the TLD
*/
function getWhoIsServer(tld) {
return SERVERS[tld];
}
/**
* Extract TLD from domain name.
* If the TLD is in whois-servers.json file, then the TLD is returned.
* If TLD is not found within the file, then determined by taking the last element after splitting the domain name from '.'
*
* @param domain Domain name
* @param logger
* @returns TLD
*/
function getTLD(domain, logger) {
let tld = '';
let domainStr = domain;
while (true) {
const domainData = domainStr.split('.');
if (domainData.length < 2) {
break;
}
const tldCheck = domainData.slice(1).join('.');
const server = SERVERS[tldCheck];
if (server) {
tld = tldCheck;
break;
}
domainStr = tldCheck;
}
if (tld != '') {
return tld;
}
logger.debug('TLD is not found in server list. Returning last element after split as TLD!');
const domainData = domain.split('.');
return domainData[domainData.length - 1];
}
// get whois query parameters if exist on parameters.json for whois server
function getParameters(server) {
return PARAMETERS[server];
}
/**
* Type of the proxy. Either SOCKS4 or SOCKS5
* @enum
*/
var ProxyType;
(function (ProxyType) {
/**
* SOCKS4 type of proxy
*/
ProxyType[ProxyType["SOCKS4"] = 0] = "SOCKS4";
/**
* SOCKS5 type of proxy
*/
ProxyType[ProxyType["SOCKS5"] = 1] = "SOCKS5";
})(ProxyType || (exports.ProxyType = ProxyType = {}));
/**
* Parse collected raw WhoIs data
*
* @class
*/
class WhoIsParser {
/**
* Iterated through the complete text and returns extracted values
*
* @param rawData raw text from WhoIs server
* @param outputData Data which needs to be extracted from the raw text (key/value pairs). Keys are used to extract from raw text and values are filled.
* @returns Filled {@link outputData}
*/
static iterParse(rawData, outputData) {
let lastStr = '';
let lastField = null;
let lastLetter = '';
for (let i = 0; i < rawData.length; i++) {
let letter = rawData[i];
if (letter == '\n' || (lastLetter == ':' && letter == ' ')) {
if (lastStr.trim() in outputData) {
lastField = lastStr.trim();
}
else if (lastField !== null) {
let x = lastStr.trim();
if (x != '') {
let obj = outputData[lastField];
if (Array.isArray(obj)) {
obj.push(x);
}
else {
outputData[lastField] = x;
}
lastField = null;
}
}
lastStr = '';
}
else if (letter != ':') {
lastStr = lastStr + letter;
}
lastLetter = letter;
if (lastStr == 'Record maintained by' || lastStr == '>>>') {
break;
}
}
return outputData;
}
/**
* Parse the raw WhoIs text and return extracted values
*
* @param rawData raw text from WhoIs server
* @param outputData Data which needs to be extracted from the raw text (key/value pairs). Keys are used to extract from raw text and values are filled.
* @returns Filled {@link outputData}
*/
static parseData(rawData, outputData) {
if (!outputData) {
outputData = {
'Domain Name': '',
'Creation Date': '',
'Updated Date': '',
'Registry Expiry Date': '',
'Domain Status': [],
"Registrar": '',
};
}
outputData = WhoIsParser.iterParse(rawData, outputData);
return outputData;
}
}
exports.WhoIsParser = WhoIsParser;
/**
* Connects to the provided {@link server}:{@link port} through TCP (through a proxy if a proxy is given), run the WhoIs query and returns the response
*
* @param domain Domain name
* @param queryOptions Query options which can be used with the specific WhoIs server to get the complete response
* @param server WhoIs server
* @param port WhoIs server port
* @param encoding Encoding used by the WhoIs server
* @param proxy {@link ProxyData}
* @returns The {string} WhoIs response for the query. Empty string is returned for errors
*/
async function tcpWhois(domain, queryOptions, server, port, encoding, proxy) {
const decoder = new TextDecoder(encoding);
const encoder = new TextEncoder();
if (!proxy) {
const socket = new net_1.default.Socket();
let response = '';
return new Promise((resolve, reject) => {
try {
socket.connect({ port: port, host: server, noDelay: false }, function () {
if (queryOptions != '') {
socket.write(encoder.encode(`${queryOptions} ${domain}\r\n`));
}
else {
socket.write(encoder.encode(`${domain}\r\n`));
}
});
socket.on('data', (data) => {
response = response + decoder.decode(data);
});
socket.on('error', (error) => {
reject(error);
});
socket.on('end', () => {
resolve(response);
});
}
catch (e) {
reject(e);
}
});
}
else {
const options = {
proxy: {
host: proxy.ip,
port: proxy.port,
type: proxy.type == ProxyType.SOCKS5 ? 5 : 4
},
command: 'connect',
destination: {
host: server,
port: port
}
};
if (proxy.username && proxy.password) {
options.proxy.userId = proxy.username;
options.proxy.password = proxy.password;
}
return new Promise((resolve, reject) => {
socks_1.SocksClient.createConnection(options, function (err, info) {
if (err) {
reject(err);
}
else {
if (!info) {
reject(new Error('No socket info received!'));
}
let response = '';
if (queryOptions != '') {
info === null || info === void 0 ? void 0 : info.socket.write(encoder.encode(`${queryOptions} ${domain}\r\n`));
}
else {
info === null || info === void 0 ? void 0 : info.socket.write(encoder.encode(`${domain}\r\n`));
}
info === null || info === void 0 ? void 0 : info.socket.on('data', (data) => {
response = response + decoder.decode(data);
});
info === null || info === void 0 ? void 0 : info.socket.on('end', () => {
resolve(response);
});
info === null || info === void 0 ? void 0 : info.socket.resume();
}
});
});
}
}
/**
* Collect WhoIs data for the mentioned {@link domain}. Parse the received response if {@link parse} is true, accordingly.
*
* @param domain Domain name
* @param parse Whether the raw text needs to be parsed/formatted or not
* @param options {@link WhoIsOptions}
* @param logger
* @returns {@link WhoIsResponse}
*/
async function whois(domain, parse = false, options = null, logger = loggerLocal) {
let tld;
let port = 43;
let server = '';
let queryOptions;
let proxy;
let encoding = 'utf-8';
const isIp = domain.match(/^(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])){3}$/);
if (!options) {
tld = isIp ? domain : getTLD(domain, logger);
proxy = null;
}
else {
tld = options.tld ? options.tld : (isIp ? domain : getTLD(domain, logger));
encoding = options.encoding ? options.encoding : 'utf-8';
proxy = options.proxy ? options.proxy : null;
server = options.server ? options.server : '';
port = options.serverPort ? options.serverPort : 43;
}
if (server == '') {
let serverData = isIp ? undefined : getWhoIsServer(tld);
if (!serverData) {
logger.debug(`No WhoIs server found for TLD: ${tld}! Attempting IANA WhoIs database for server!`);
serverData = await findWhoIsServer(tld, logger);
if (!serverData) {
logger.debug('WhoIs server could not be found!');
return {
_raw: '',
parsedData: null
};
}
logger.debug(`WhoIs sever found for ${tld}: ${server}`);
}
server = serverData;
}
const qOptions = getParameters(server);
queryOptions = qOptions ? qOptions : '';
try {
let rawData = await tcpWhois(domain, queryOptions, server, port, encoding, proxy);
if (!parse) {
return {
_raw: rawData,
parsedData: null
};
}
else {
let outputData = null;
if (options && options.parseData) {
outputData = shallowCopy(options.parseData);
}
try {
const parsedData = WhoIsParser.parseData(rawData, outputData);
return {
_raw: rawData,
parsedData: parsedData
};
}
catch (err) {
logger.info(err);
return {
_raw: rawData,
parsedData: null
};
}
}
}
catch (err) {
logger.info(err);
return {
_raw: '',
parsedData: null
};
}
}
/**
* Collects (and parse/format if set to be true) for the provided {@link domains}. If {@link parallel} is set to be true, multiple threads will be used to batch process the domains according to {@link threads} mentioned.
* If <i>options.parsedData</i> is mentioned, then it will be used to parse <b>all</b> the responses.
* If a proxy is mentioned in {@link options}, then the proxy will be used to collect <b>all</b> the WhoIs data.
*
* @param domains Domains Names
* @param parallel Whether data should be collected in parallel or not
* @param threads Batch size (for parallel processing)
* @param parse Whether the raw text needs to be parsed/formatted or not
* @param options {@link WhoIsOptions}
* @returns Array of {@link WhoIsResponse} for all the domains. Order is not guaranteed
*/
async function batchWhois(domains, parallel = false, threads = 1, parse = false, options = null) {
let response = [];
if (parallel) {
if (threads > domains.length) {
threads = domains.length;
}
for (let i = 0; i < domains.length; i += threads) {
const batch = domains.slice(i, i + threads);
let resp = await Promise.all(batch.map(async (domain) => {
return await whois(domain, parse, options);
}));
response = response.concat(resp);
}
}
else {
for (let i = 0; i < domains.length; i++) {
const res = await whois(domains[i], parse, options);
response.push(res);
}
}
return response;
}
//# sourceMappingURL=whois.js.map