UNPKG

xep-whois

Version:
384 lines 13.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.batchWhois = exports.whois = exports.tcpWhois = exports.WhoIsParser = exports.ProxyType = void 0; const socks_1 = require("socks"); const net_1 = __importDefault(require("net")); const fetch = require('node-fetch'); var SERVERS = require('./../whois-servers.json'); var PARAMETERS = require('./../parameters.json'); var IANA_CHK_URL = 'https://www.iana.org/whois?q='; /** * 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 * @returns WhoIs server which hosts the information for the domains of the TLD */ async function findWhoIsServer(tld) { 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) { console.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 * @returns TLD */ function getTLD(domain) { var tld = ''; var 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; } console.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 || (exports.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) { var lastStr = ''; var lastField = null; var lastLetter = ''; for (var 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 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 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(); return new Promise((resolve, reject) => { try { socket.connect({ port: port, host: server }, function () { if (queryOptions != '') { socket.write(encoder.encode(`${queryOptions} ${domain}\r\n`)); } else { socket.write(encoder.encode(`${domain}\r\n`)); } }); socket.on('data', (data) => { resolve(decoder.decode(data)); }); socket.on('error', (error) => { reject(error); }); } 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!')); } 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) => { resolve(decoder.decode(data)); }); info === null || info === void 0 ? void 0 : info.socket.resume(); } }); }); } } exports.tcpWhois = tcpWhois; /** * Collect WhoIs data for the mentioned {@link domain}. Parse the reveived 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} * @returns {@link WhoIsResponse} */ async function whois(domain, parse = false, options = null) { var tld; var port = 43; var server = ''; var queryOptions; var proxy; var encoding = 'utf-8'; if (!options) { tld = getTLD(domain); proxy = null; } else { tld = options.tld ? options.tld : getTLD(domain); 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 = getWhoIsServer(tld); if (!serverData) { console.debug(`No WhoIs server found for TLD: ${tld}! Attempting IANA WhoIs database for server!`); serverData = await findWhoIsServer(tld); if (!serverData) { console.debug('WhoIs server could not be found!'); return { _raw: '', parsedData: null }; } console.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) { return { _raw: rawData, parsedData: null }; } } } catch (err) { return { _raw: '', parsedData: null }; } } exports.whois = whois; /** * 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 parallally 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) { var 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; } exports.batchWhois = batchWhois; //# sourceMappingURL=whois.js.map