From 999d8b7076dab521f60c28b790aa63a63c5e83dc Mon Sep 17 00:00:00 2001 From: 7836246 Date: Sun, 3 Mar 2024 09:18:32 +0800 Subject: [PATCH] =?UTF-8?q?:triangular=5Fflag=5Fon=5Fpost:=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=87=AA=E5=AE=9A=E4=B9=89Whois=E5=90=8E=E7=BC=80?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + package.json | 4 +- pnpm-lock.yaml | 69 +--- server/api/whois.post.ts | 2 +- server/whois/json/parameters.json | 4 + server/whois/json/whois-servers.json | 319 +++++++++++++++++ server/whois/whois.ts | 496 +++++++++++++++++++++++++++ utils/domain.ts | 4 +- 8 files changed, 829 insertions(+), 70 deletions(-) create mode 100644 server/whois/json/parameters.json create mode 100644 server/whois/json/whois-servers.json create mode 100644 server/whois/whois.ts diff --git a/README.md b/README.md index 8b70636..57bf823 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Nuxt-Whois 是一个基于 Nuxt3、Tailwind CSS 和 Xep-Whois 构建的Whois查 - **语言切换**:提供多语言支持,方便不同语言用户使用。 - **时区切换**:支持时区设置,确保时间信息的准确性。 - **Dns查询**:支持Dns查询,方便用户查看域名的Dns信息。 +- **自定义后缀**:支持自定义Whois服务器后缀,方便用户查询不同后缀的域名。 ### 环境要求 diff --git a/package.json b/package.json index 76dbfc0..fc76fde 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "@nuxtjs/tailwindcss": "^6.11.4", "@pinia/nuxt": "^0.5.1", "nuxt": "^3.10.3", + "socks": "^2.8.1", "vue": "^3.4.21", - "vue-router": "^4.3.0", - "xep-whois": "^1.0.2" + "vue-router": "^4.3.0" }, "devDependencies": { "@nuxtjs/i18n": "^8.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f23ddf4..0213601 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,15 +17,15 @@ dependencies: nuxt: specifier: ^3.10.3 version: 3.10.3(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4) + socks: + specifier: ^2.8.1 + version: 2.8.1 vue: specifier: ^3.4.21 version: 3.4.21(typescript@5.3.3) vue-router: specifier: ^4.3.0 version: 4.3.0(vue@3.4.21) - xep-whois: - specifier: ^1.0.2 - version: 1.0.2(typescript@5.3.3) devDependencies: '@nuxtjs/i18n': @@ -2146,12 +2146,6 @@ packages: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true - /@types/node@18.19.21: - resolution: {integrity: sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw==} - dependencies: - undici-types: 5.26.5 - dev: false - /@types/node@20.11.24: resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} dependencies: @@ -2990,10 +2984,6 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - /ansi-sequence-parser@1.1.1: - resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==} - dev: false - /ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -4940,10 +4930,6 @@ packages: dependencies: yallist: 4.0.0 - /lunr@2.3.9: - resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - dev: false - /magic-string-ast@0.3.0: resolution: {integrity: sha512-0shqecEPgdFpnI3AP90epXyxZy9g6CRZ+SZ7BcqFwYmtFEnZ1jpevcV5HoyVnlDS9gCnc1UIg3Rsvp3Ci7r8OA==} engines: {node: '>=16.14.0'} @@ -4987,12 +4973,6 @@ packages: transitivePeerDependencies: - supports-color - /marked@4.3.0: - resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} - engines: {node: '>= 12'} - hasBin: true - dev: false - /mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} @@ -6639,15 +6619,6 @@ packages: /shell-quote@1.8.1: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - /shiki@0.14.7: - resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} - dependencies: - ansi-sequence-parser: 1.1.1 - jsonc-parser: 3.2.1 - vscode-oniguruma: 1.7.0 - vscode-textmate: 8.0.0 - dev: false - /shiki@1.0.0-beta.3: resolution: {integrity: sha512-z7cHTNSSvwGx2DfeLwjSNLo+HcVxifgNIzLm6Ye52eXcIwNHXT0wHbhy7FDOKSKveuEHBwt9opfj3Hoc8LE1Yg==} dependencies: @@ -7124,20 +7095,6 @@ packages: mime-types: 2.1.35 dev: false - /typedoc@0.24.8(typescript@5.3.3): - resolution: {integrity: sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==} - engines: {node: '>= 14.14'} - hasBin: true - peerDependencies: - typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x - dependencies: - lunr: 2.3.9 - marked: 4.3.0 - minimatch: 9.0.3 - shiki: 0.14.7 - typescript: 5.3.3 - dev: false - /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -7621,14 +7578,6 @@ packages: dependencies: vscode-languageserver-protocol: 3.16.0 - /vscode-oniguruma@1.7.0: - resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} - dev: false - - /vscode-textmate@8.0.0: - resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} - dev: false - /vscode-uri@3.0.8: resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} @@ -7818,18 +7767,6 @@ packages: utf-8-validate: optional: true - /xep-whois@1.0.2(typescript@5.3.3): - resolution: {integrity: sha512-P3hA4+yaSgJdgYya5XtcgI7bPs+QozHpmJE7kCpp73AhI5kC7WHnbVY1+AYKwFjWiuZsBLF2RzofJFIcelgmMg==} - dependencies: - '@types/node': 18.19.21 - node-fetch: 2.7.0 - socks: 2.8.1 - typedoc: 0.24.8(typescript@5.3.3) - transitivePeerDependencies: - - encoding - - typescript - dev: false - /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} diff --git a/server/api/whois.post.ts b/server/api/whois.post.ts index 1de7adb..cea6204 100644 --- a/server/api/whois.post.ts +++ b/server/api/whois.post.ts @@ -1,4 +1,4 @@ -import {whois, WhoIsOptions} from 'xep-whois'; +import {whois} from "~/server/whois/whois"; export default defineEventHandler(async (event) => { const body = await readBody(event) diff --git a/server/whois/json/parameters.json b/server/whois/json/parameters.json new file mode 100644 index 0000000..7b1e55f --- /dev/null +++ b/server/whois/json/parameters.json @@ -0,0 +1,4 @@ +{ + "whois.denic.de" : "-T dn,ace", + "whois.nic.fr": "-V Md5.2" +} diff --git a/server/whois/json/whois-servers.json b/server/whois/json/whois-servers.json new file mode 100644 index 0000000..1530e46 --- /dev/null +++ b/server/whois/json/whois-servers.json @@ -0,0 +1,319 @@ +{ + "br.com":"whois.centralnic.net", + "cn.com":"whois.centralnic.net", + "de.com":"whois.centralnic.net", + "eu.com":"whois.centralnic.net", + "gb.com":"whois.centralnic.net", + "gb.net":"whois.centralnic.net", + "gr.com":"whois.centralnic.net", + "hu.com":"whois.centralnic.net", + "in.net":"whois.centralnic.net", + "no.com":"whois.centralnic.net", + "qc.com":"whois.centralnic.net", + "ru.com":"whois.centralnic.net", + "sa.com":"whois.centralnic.net", + "se.com":"whois.centralnic.net", + "se.net":"whois.centralnic.net", + "uk.com":"whois.centralnic.net", + "uk.net":"whois.centralnic.net", + "us.com":"whois.centralnic.net", + "uy.com":"whois.centralnic.net", + "za.com":"whois.centralnic.net", + "jpn.com":"whois.centralnic.ne", + "web.com":"whois.centralnic.ne", + "com":"whois.verisign-grs.com", + "za.net":"whois.za.net", + "net":"whois.verisign-grs.com", + "eu.org":"whois.eu.org", + "za.org":"whois.za.org", + "org":"whois.pir.org", + "llyw.cymru":"whois.nic.llyw.cymru", + "gov.scot":"whois.nic.gov.scot", + "gov.wales":"whois.nic.gov.wales", + "edu":"whois.educause.edu", + "gov":"whois.dotgov.gov", + "int":"whois.iana.org", + "e164.arpa":"whois.ripe.net", + "arpa":"whois.iana.org", + "aero":"whois.aero", + "asia":"whois.nic.asia", + "biz":"whois.nic.biz", + "cat":"whois.nic.cat", + "coop":"whois.nic.coop", + "info":"whois.afilias.net", + "jobs":"whois.nic.jobs", + "mobi":"whois.afilias.net", + "museum":"whois.nic.museum", + "name":"whois.nic.name", + "post":"whois.dotpostregistry.net", + "pro":"whois.nic.pro", + "tel":"whois.nic.tel", + "travel":"whois.nic.travel", + "xxx":"whois.nic.xxx", + "ac":"whois.nic.ac", + "ae":"whois.aeda.net.ae", + "af":"whois.nic.af", + "ag":"whois.nic.ag", + "ai":"whois.nic.ai", + "am":"whois.amnic.net", + "ar":"whois.nic.ar", + "as":"whois.nic.as", + "priv.at":"whois.nic.priv.at", + "at":"whois.nic.at", + "au":"whois.auda.org.au", + "aw":"whois.nic.aw", + "ax":"whois.ax", + "be":"whois.dns.be", + "bf":"whois.registre.bf", + "bg":"whois.register.bg", + "bh":"whois.nic.bh", + "bi":"whois1.nic.bi", + "bj":"whois.nic.bj", + "bm":"whois.afilias-srs.net", + "bn":"whois.bnnic.bn", + "bo":"whois.nic.bo", + "br":"whois.registro.br", + "by":"whois.cctld.by", + "bw":"whois.nic.net.bw", + "bz":"whois.afilias-grs.info", + "co.ca":"whois.co.ca", + "ca":"whois.cira.ca", + "cc":"ccwhois.verisign-grs.com", + "cd":"whois.nic.cd", + "ch":"whois.nic.ch", + "ci":"whois.nic.ci", + "cl":"whois.nic.cl", + "cm":"whois.netcom.cm", + "edu.cn":"whois.edu.cn", + "cn":"whois.cnnic.cn", + "uk.co":"whois.uk.co", + "co":"whois.nic.co", + "cr":"whois.nic.cr", + "cx":"whois.nic.cx", + "cz":"whois.nic.cz", + "de":"whois.denic.de", + "dk":"whois.dk-hostmaster.dk", + "dm":"whois.dmdomains.dm", + "do":"whois.nic.do", + "dz":"whois.nic.dz", + "ec":"whois.nic.ec", + "ee":"whois.tld.ee", + "eu":"whois.eu", + "fi":"whois.fi", + "fj":"www.whois.fj", + "fm":"whois.nic.fm", + "fo":"whois.nic.fo", + "fr":"whois.nic.fr", + "gd":"whois.nic.gd", + "ge":"whois.nic.ge", + "gf":"whois.mediaserv.net", + "gg":"whois.gg", + "gh":"whois.nic.gh", + "gi":"whois2.afilias-grs.net", + "gl":"whois.nic.gl", + "gp":"whois.nic.gp", + "gq":"whois.dominio.gq", + "gs":"whois.nic.gs", + "gy":"whois.registry.gy", + "hk":"whois.hkirc.hk", + "hm":"whois.registry.hm", + "hn":"whois.nic.hn", + "hr":"whois.dns.hr", + "ht":"whois.nic.ht", + "hu":"whois.nic.hu", + "id":"whois.id", + "ie":"whois.weare.ie", + "il":"whois.isoc.org.il", + "im":"whois.nic.im", + "in":"whois.registry.in", + "io":"whois.nic.io", + "iq":"whois.cmc.iq", + "ir":"whois.nic.ir", + "is":"whois.isnic.is", + "it":"whois.nic.it", + "je":"whois.je", + "jp":"whois.jprs.jp", + "ke":"whois.kenic.or.ke", + "kg":"whois.kg", + "ki":"whois.nic.ki", + "kn":"whois.nic.kn", + "kr":"whois.kr", + "kw":"whois.nic.kw", + "ky":"whois.kyregistry.ky", + "kz":"whois.nic.kz", + "la":"whois.nic.la", + "lb":"whois.lbdr.org.lb", + "lc":"whois2.afilias-grs.net", + "li":"whois.nic.li", + "lk":"whois.nic.lk", + "ls":"whois.nic.ls", + "lt":"whois.domreg.lt", + "lu":"whois.dns.lu", + "lv":"whois.nic.lv", + "ly":"whois.nic.ly", + "ma":"whois.registre.ma", + "md":"whois.nic.md", + "me":"whois.nic.me", + "mg":"whois.nic.mg", + "mk":"whois.marnet.mk", + "ml":"whois.dot.ml", + "mm":"whois.registry.gov.mm", + "mn":"whois.nic.mn", + "mq":"whois.mediaserv.net", + "mr":"whois.nic.mr", + "ms":"whois.nic.ms", + "mt":"whois.nic.org.mt", + "mu":"whois.nic.mu", + "mw":"whois.nic.mw", + "mx":"whois.mx", + "my":"whois.mynic.my", + "mz":"whois.nic.mz", + "na":"whois.na-nic.com.na", + "nc":"whois.nc", + "nf":"whois.nic.nf", + "ng":"whois.nic.net.ng", + "nl":"whois.domain-registry.nl", + "no":"whois.norid.no", + "nu":"whois.iis.nu", + "nz":"whois.irs.net.nz", + "om":"whois.registry.om", + "pe":"kero.yachay.pe", + "pf":"whois.registry.pf", + "pk":"whois.pknic.net.pk", + "co.pl":"whois.co.pl", + "pl":"whois.dns.pl", + "pm":"whois.nic.pm", + "pr":"whois.afilias-srs.net", + "ps":"whois.pnina.ps", + "pt":"whois.dns.pt", + "pw":"whois.nic.pw", + "qa":"whois.registry.qa", + "re":"whois.nic.re", + "ro":"whois.rotld.ro", + "rs":"whois.rnids.rs", + "ac.ru":"whois.free.net", + "edu.ru":"whois.informika.ru", + "com.ru":"whois.flexireg.net", + "msk.ru":"whois.flexireg.net", + "net.ru":"whois.nic.net.ru", + "nov.ru":"whois.flexireg.net", + "org.ru":"whois.nic.net.ru", + "pp.ru":"whois.nic.net.ru", + "spb.ru":"whois.flexireg.net", + "ru":"whois.tcinet.ru", + "rw":"whois.ricta.org.rw", + "sa":"whois.nic.net.sa", + "sb":"whois.nic.net.sb", + "sc":"whois2.afilias-grs.net", + "sd":"whois.sdnic.sd", + "se":"whois.iis.se", + "sg":"whois.sgnic.sg", + "sh":"whois.nic.sh", + "si":"whois.register.si", + "sk":"whois.sk-nic.sk", + "sl":"whois.nic.sl", + "sm":"whois.nic.sm", + "sn":"whois.nic.sn", + "so":"whois.nic.so", + "ss":"whois.nic.ss", + "st":"whois.nic.st", + "msk.su":"whois.flexireg.net", + "nov.su":"whois.flexireg.net", + "spb.su":"whois.flexireg.net", + "su":"whois.tcinet.ru", + "sx":"whois.sx", + "sy":"whois.tld.sy", + "tc":"whois.nic.tc", + "td":"whois.nic.td", + "tf":"whois.nic.tf", + "tg":"whois.nic.tg", + "th":"whois.thnic.co.th", + "tk":"whois.dot.tk", + "tl":"whois.nic.tl", + "tm":"whois.nic.tm", + "tn":"whois.ati.tn", + "to":"whois.tonic.to", + "tr":"whois.trabis.gov.tr", + "tv":"whois.nic.tv", + "tw":"whois.twnic.net.tw", + "tz":"whois.tznic.or.tz", + "biz.ua":"whois.biz.ua", + "co.ua":"whois.co.ua", + "pp.ua":"whois.pp.ua", + "ua":"whois.ua", + "ug":"whois.co.ug", + "ac.uk":"whois.nic.ac.uk", + "gov.uk":"whois.gov.uk", + "uk":"whois.nic.uk", + "fed.us":"whois.nic.gov", + "us":"whois.nic.us", + "uy":"whois.nic.org.uy", + "uz":"whois.cctld.uz", + "vc":"whois2.afilias-grs.net", + "ve":"whois.nic.ve", + "vg":"whois.nic.vg", + "vu":"whois.dnrs.vu", + "wf":"whois.nic.wf", + "ws":"whois.website.ws", + "yt":"whois.nic.yt", + "ac.za":"whois.ac.za", + "co.za":"whois.registry.net.za", + "gov.za":"whois.gov.za", + "net.za":"net-whois.registry.net.za", + "org.za":"org-whois.registry.net.za", + "web.za":"web-whois.registry.net.za", + "zm":"whois.zicta.zm", + "xn--2scrj9c":"whois.registry.in", + "xn--3e0b707e":"whois.kr", + "xn--3hcrj9c":"whois.registry.in", + "xn--45br5cyl":"whois.registry.in", + "xn--45brj9c":"whois.registry.in", + "xn--4dbrk0ce":"whois.isoc.org.il", + "xn--80ao21a":"whois.nic.kz", + "xn--90a3ac":"whois.rnids.rs", + "xn--90ae":"whois.imena.bg", + "xn--90ais":"whois.cctld.by", + "xn--clchc0ea0b2g2a9gcd":"whois.sgnic.sg", + "xn--d1alf":"whois.marnet.mk", + "xn--e1a4c":"whois.eu", + "xn--fiqs8s":"cwhois.cnnic.cn", + "xn--fiqz9s":"cwhois.cnnic.cn", + "xn--fpcrj9c3d":"whois.registry.in", + "xn--fzc2c9e2c":"whois.nic.lk", + "xn--gecrj9c":"whois.registry.in", + "xn--h2breg3eve":"whois.registry.in", + "xn--h2brj9c8c":"whois.registry.in", + "xn--h2brj9c":"whois.registry.in", + "xn--j1amh":"whois.dotukr.com", + "xn--j6w193g":"whois.hkirc.hk", + "xn--kprw13d":"whois.twnic.net.tw", + "xn--kpry57d":"whois.twnic.net.tw", + "xn--lgbbat1ad8j":"whois.nic.dz", + "xn--mgb9awbf":"whois.registry.om", + "xn--mgba3a4f16a":"whois.nic.ir", + "xn--mgbaam7a8h":"whois.aeda.net.ae", + "xn--mgbah1a3hjkrd":"whois.nic.mr", + "xn--mgbbh1a71e":"whois.registry.in", + "xn--mgbbh1a":"whois.registry.in", + "xn--mgberp4a5d4ar":"whois.nic.net.sa", + "xn--mgbgu82a":"whois.registry.in", + "xn--mgbtx2b":"whois.cmc.iq", + "xn--mgbx4cd0ab":"whois.mynic.my", + "xn--node":"whois.itdc.ge", + "xn--o3cw4h":"whois.thnic.co.th", + "xn--ogbpf8fl":"whois.tld.sy", + "xn--p1ai":"whois.tcinet.ru", + "xn--pgbs0dh":"whois.ati.tn", + "xn--q7ce6a":"whois.nic.la", + "xn--qxa6a":"whois.eu", + "xn--rvc1e0am3e":"whois.registry.in", + "xn--s9brj9c":"whois.registry.in", + "xn--wgbh1c":"whois.dotmasr.eg", + "xn--wgbl6a":"whois.registry.qa", + "xn--xkc2al3hye2a":"whois.nic.lk", + "xn--xkc2dl3a5ee0h":"whois.registry.in", + "xn--y9a3aq":"whois.amnic.net", + "xn--yfro4i67o":"whois.sgnic.sg", + "xn--ygbi2ammx":"whois.pnina.ps", + "xyz":"whois.nic.xyz" +} diff --git a/server/whois/whois.ts b/server/whois/whois.ts new file mode 100644 index 0000000..e5e585e --- /dev/null +++ b/server/whois/whois.ts @@ -0,0 +1,496 @@ +import { SocksClient,SocksClientOptions } from 'socks'; +import Net from 'net'; + +import serversData from '~/server/whois/json/whois-servers.json'; +import parametersData from '~/server/whois/json/parameters.json'; + +const IANA_CHK_URL = 'https://www.iana.org/whois?q='; + + +interface IServers { + [key: string]: string; +} + +const SERVERS: IServers = serversData as IServers; +const PARAMETERS: IServers = parametersData as IServers; + +/** + * 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: string): Promise { + 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: T): T { + if (Array.isArray(obj)) { + return obj.slice() as T; // Clone the array + } else if (typeof obj === 'object' && obj !== null) { + const copy: any = {}; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + copy[key] = shallowCopy(obj[key]); + } + } + return copy as T; + } 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: string): string|undefined { + 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: string): string { + 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; + } + + 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: string): string|undefined { + return PARAMETERS[server]; +} + + +/** + * Type of the proxy. Either SOCKS4 or SOCKS5 + * @enum + */ +export enum ProxyType { + /** + * SOCKS4 type of proxy + */ + SOCKS4 = 0, + /** + * SOCKS5 type of proxy + */ + SOCKS5 = 1 +} + +/** + * Proxy related data + * @interface + * + */ +export interface ProxyData { + /** + * Proxy IP + */ + ip: string, + /** + * Proxy port + */ + port: number, + /** + * Username to connect to the proxy + */ + username?: string | null, + /** + * Password to connect to the proxy + */ + password?: string | null, + /** + * {@link ProxyType} + */ + type: ProxyType +} + +/** + * WhoIs options + * @interface + */ +export interface WhoIsOptions { + /** + * TLD of the domain. If the {@link tld} is not provided (or null), then it will be automatically determined as to the given domain name + */ + tld?: string | null, + /** + * The encoding type used for WhoIs server and response. By default UTF-8 is used. + */ + encoding?: string | null, + + /**{@link ProxyData} */ + proxy?: ProxyData | null, + /** + * The WhoIs server to collect data from. If not provided, the server will automatically determined using the {@link tld} + */ + server?: string | null, + /** + * The port of the WhoIs server. By default, port 43 is used. + */ + serverPort?: number | null, + /** + * Which data needs to be extracted/parsed from the WhoIs response. + * An object can be passed which contains keys of the fields of the WhoIs response. + * A copy of the provided object will be returned with the values filled for the provided keys. + * + * The keys can have default value of empty string. However, if the WhoIs response has multiple values for the same field (eg: 'Domain Status'), + * then all the values can be collected by providing a default value of an Array([]). + * + * Following example shows an object used to collect 'Domain Name', 'Domain Status' (multiple values) and 'Registrar' from WhoIs response + * + * @example {'Domain Name': '', 'Domain Status': [], 'Registrar': ''} + */ + parseData?: Object | null +} + +/** + * Response returned from whois function. Contains the raw text from WhoIs server and parsed/fornatted WhoIs data (if parsed is true) + * + * @interface + */ +export interface WhoIsResponse { + /** + * Raw text response from WhoIs server + */ + _raw: string, + /** + * Parsed/Formatted key-value pairs of the response (if parsed is true) + */ + parsedData: any | null +} + +/** + * Parse collected raw WhoIs data + * + * @class + */ +export 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} + */ + private static iterParse(rawData: string, outputData: any) { + let lastStr = ''; + let lastField: string | null = 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 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} + */ + public static parseData(rawData: string, outputData: any | null): any { + if (!outputData) { + outputData = { + 'Domain Name': '', + 'Creation Date': '', + 'Updated Date': '', + 'Registry Expiry Date': '', + 'Domain Status': [], + "Registrar": '', + } + } + + outputData = WhoIsParser.iterParse(rawData, outputData); + + return outputData; + } + +} + + +/** + * 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 + */ +export async function tcpWhois(domain: string, queryOptions: string, server: string, port: number, encoding: string, proxy: ProxyData | null): Promise { + const decoder = new TextDecoder(encoding); + const encoder = new TextEncoder(); + + if (!proxy) { + const socket = new Net.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: SocksClientOptions = { + 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) => { + SocksClient.createConnection(options, function(err, info) { + if (err) { + reject(err); + } else { + if (!info) { + reject(new Error('No socket info received!')); + } + + if (queryOptions != '') { + info?.socket.write( + encoder.encode(`${queryOptions} ${domain}\r\n`) + ); + } else { + info?.socket.write( + encoder.encode(`${domain}\r\n`) + ); + } + + info?.socket.on('data', (data) => { + resolve(decoder.decode(data)); + }); + + info?.socket.resume(); + } + }); + }); + } +} + +/** + * 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} + */ +export async function whois(domain: string, parse: boolean = false, options: WhoIsOptions | null = null): Promise { + let tld: string; + let port = 43; + let server = ''; + let queryOptions: string; + let proxy: ProxyData | null; + let 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: any | null = 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 + }; + } +} + +/** + * 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 options.parsedData is mentioned, then it will be used to parse all the responses. + * If a proxy is mentioned in {@link options}, then the proxy will be used to collect all 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 + */ +export async function batchWhois(domains: string[], parallel: boolean = false, threads: number = 1, parse: boolean = false, options: WhoIsOptions | null = null): Promise { + let response: WhoIsResponse[] = []; + + 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; +} diff --git a/utils/domain.ts b/utils/domain.ts index 5881ccb..e4db0c1 100644 --- a/utils/domain.ts +++ b/utils/domain.ts @@ -1,6 +1,8 @@ // br.com,cn.com,de.com,eu.com,gb.com,gb.net,gr.com,hu.com,in.net,no.com,qc.com,ru.com,sa.com,se.com,se.net,uk.com,uk.net,us.com,uy.com,za.com,jpn.com,web.com,com,za.net,net,eu.org,za.org,org,llyw.cymru,gov.scot,gov.wales,edu,gov,int,e164.arpa,arpa,aero,asia,biz,cat,coop,info,jobs,mobi,museum,name,post,pro,tel,travel,xxx,ac,ae,af,ag,ai,am,ar,as,priv.at,at,au,aw,ax,be,bf,bg,bh,bi,bj,bm,bn,bo,br,by,bw,bz,co.ca,ca,cc,cd,ch,ci,cl,cm,edu.cn,cn,uk.co,co,cr,cx,cz,de,dk,dm,do,dz,ec,ee,eu,fi,fj,fm,fo,fr,gd,ge,gf,gg,gh,gi,gl,gp,gq,gs,gy,hk,hm,hn,hr,ht,hu,id,ie,il,im,in,io,iq,ir,is,it,je,jp,ke,kg,ki,kn,kr,kw,ky,kz,la,lb,lc,li,lk,ls,lt,lu,lv,ly,ma,md,me,mg,mk,ml,mm,mn,mq,mr,ms,mt,mu,mw,mx,my,mz,na,nc,nf,ng,nl,no,nu,nz,om,pe,pf,pk,co.pl,pl,pm,pr,ps,pt,pw,qa,re,ro,rs,ac.ru,edu.ru,com.ru,msk.ru,net.ru,nov.ru,org.ru,pp.ru,spb.ru,ru,rw,sa,sb,sc,sd,se,sg,sh,si,sk,sl,sm,sn,so,ss,st,msk.su,nov.su,spb.su,su,sx,sy,tc,td,tf,tg,th,tk,tl,tm,tn,to,tr,tv,tw,tz,biz.ua,co.ua,pp.ua,ua,ug,ac.uk,gov.uk,uk,fed.us,us,uy,uz,vc,ve,vg,vu,wf,ws,yt,ac.za,co.za,gov.za,net.za,org.za,web.za,zm,xn--2scrj9c,xn--3e0b707e,xn--3hcrj9c,xn--45br5cyl,xn--45brj9c,xn--4dbrk0ce,xn--80ao21a,xn--90a3ac,xn--90ae,xn--90ais,xn--clchc0ea0b2g2a9gcd,xn--d1alf,xn--e1a4c,xn--fiqs8s,xn--fiqz9s,xn--fpcrj9c3d,xn--fzc2c9e2c,xn--gecrj9c,xn--h2breg3eve,xn--h2brj9c8c,xn--h2brj9c,xn--j1amh,xn--j6w193g,xn--kprw13d,xn--kpry57d,xn--lgbbat1ad8j,xn--mgb9awbf,xn--mgba3a4f16a,xn--mgbaam7a8h,xn--mgbah1a3hjkrd,xn--mgbbh1a71e,xn--mgbbh1a,xn--mgberp4a5d4ar,xn--mgbgu82a,xn--mgbtx2b,xn--mgbx4cd0ab,xn--node,xn--o3cw4h,xn--ogbpf8fl,xn--p1ai,xn--pgbs0dh,xn--q7ce6a,xn--qxa6a,xn--rvc1e0am3e,xn--s9brj9c,xn--wgbh1c,xn--wgbl6a,xn--xkc2al3hye2a,xn--xkc2dl3a5ee0h,xn--y9a3aq,xn--yfro4i67o,xn--ygbi2ammx -export const SupportedTLDs = new Set(["br.com", "cn.com", "de.com", "eu.com", "gb.com", "gb.net", "gr.com", "hu.com", "in.net", "no.com", "qc.com", "ru.com", "sa.com", "se.com", "se.net", "uk.com", "uk.net", "us.com", "uy.com", "za.com", "jpn.com", "web.com", "com", "za.net", "net", "eu.org", "za.org", "org", "llyw.cymru", "gov.scot", "gov.wales", "edu", "gov", "int", "e164.arpa", "arpa", "aero", "asia", "biz", "cat", "coop", "info", "jobs", "mobi", "museum", "name", "post", "pro", "tel", "travel", "xxx", "ac", "ae", "af", "ag", "ai", "am", "ar", "as", "priv.at", "at", "au", "aw", "ax", "be", "bf", "bg", "bh", "bi", "bj", "bm", "bn", "bo", "br", "by", "bw", "bz", "co.ca", "ca", "cc", "cd", "ch", "ci", "cl", "cm", "edu.cn", "cn", "uk.co", "co", "cr", "cx", "cz", "de", "dk", "dm", "do", "dz", "ec", "ee", "eu", "fi", "fj", "fm", "fo", "fr", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gp", "gq", "gs", "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it", "je", "jp", "ke", "kg", "ki", "kn", "kr", "kw", "ky", "kz", "la", "lb", "lc", "li", "lk", "ls", "lt", "lu", "lv", "ly", "ma", "md", "me", "mg", "mk", "ml", "mm", "mn", "mq", "mr", "ms", "mt", "mu", "mw", "mx", "my", "mz", "na", "nc", "nf", "ng", "nl", "no", "nu", "nz", "om", "pe", "pf", "pk", "co.pl", "pl", "pm", "pr", "ps", "pt", "pw", "qa", "re", "ro", "rs", "ac.ru", "edu.ru", "com.ru", "msk.ru", "net.ru", "nov.ru", "org.ru", "pp.ru", "spb.ru", "ru", "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sk", "sl", "sm", "sn", "so", "ss", "st", "msk.su", "nov.su", "spb.su", "su", "sx", "sy", "tc", "td", "tf", "tg", "th", "tk", "tl", "tm", "tn", "to", "tr", "tv", "tw", "tz", "biz.ua", "co.ua", "pp.ua", "ua", "ug", "ac.uk", "gov.uk", "uk", "fed.us", "us", "uy", "uz", "vc", "ve", "vg", "vu", "wf", "ws", "yt", "ac.za", "co.za", "gov.za", "net.za", "org.za", "web.za", "zm", "xn--2scrj9c", "xn--3e0b707e", "xn--3hcrj9c", "xn--45br5cyl", "xn--45brj9c", "xn--4dbrk0ce", "xn--80ao21a", "xn--90a3ac", "xn--90ae", "xn--90ais", "xn--clchc0ea0b2g2a9gcd", "xn--d1alf", "xn--e1a4c", "xn--fiqs8s", "xn--fiqz9s", "xn--fpcrj9c3d", "xn--fzc2c9e2c", "xn--gecrj9c", "xn--h2breg3eve", "xn--h2brj9c8c", "xn--h2brj9c", "xn--j1amh", "xn--j6w193g", "xn--kprw13d", "xn--kpry57d", "xn--lgbbat1ad8j", "xn--mgb9awbf", "xn--mgba3a4f16a", "xn--mgbaam7a8h", "xn--mgbah1a3hjkrd", "xn--mgbbh1a71e", "xn--mgbbh1a", "xn--mgberp4a5d4ar", "xn--mgbgu82a", "xn--mgbtx2b", "xn--mgbx4cd0ab", "xn--node", "xn--o3cw4h", "xn--ogbpf8fl", "xn--p1ai", "xn--pgbs0dh", "xn--q7ce6a", "xn--qxa6a", "xn--rvc1e0am3e", "xn--s9brj9c", "xn--wgbh1c", "xn--wgbl6a", "xn--xkc2al3hye2a", "xn--xkc2dl3a5ee0h", "xn--y9a3aq", "xn--yfro4i67o", "xn--ygbi2ammx"]); +export const SupportedTLDs = new Set(["br.com", "cn.com", "de.com", "eu.com", "gb.com", "gb.net", "gr.com", "hu.com", "in.net", "no.com", "qc.com", "ru.com", "sa.com", "se.com", "se.net", "uk.com", "uk.net", "us.com", "uy.com", "za.com", "jpn.com", "web.com", "com", "za.net", "net", "eu.org", "za.org", "org", "llyw.cymru", "gov.scot", "gov.wales", "edu", "gov", "int", "e164.arpa", "arpa", "aero", "asia", "biz", "cat", "coop", "info", "jobs", "mobi", "museum", "name", "post", "pro", "tel", "travel", "xxx", "ac", "ae", "af", "ag", "ai", "am", "ar", "as", "priv.at", "at", "au", "aw", "ax", "be", "bf", "bg", "bh", "bi", "bj", "bm", "bn", "bo", "br", "by", "bw", "bz", "co.ca", "ca", "cc", "cd", "ch", "ci", "cl", "cm", "edu.cn", "cn", "uk.co", "co", "cr", "cx", "cz", "de", "dk", "dm", "do", "dz", "ec", "ee", "eu", "fi", "fj", "fm", "fo", "fr", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gp", "gq", "gs", "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it", "je", "jp", "ke", "kg", "ki", "kn", "kr", "kw", "ky", "kz", "la", "lb", "lc", "li", "lk", "ls", "lt", "lu", "lv", "ly", "ma", "md", "me", "mg", "mk", "ml", "mm", "mn", "mq", "mr", "ms", "mt", "mu", "mw", "mx", "my", "mz", "na", "nc", "nf", "ng", "nl", "no", "nu", "nz", "om", "pe", "pf", "pk", "co.pl", "pl", "pm", "pr", "ps", "pt", "pw", "qa", "re", "ro", "rs", "ac.ru", "edu.ru", "com.ru", "msk.ru", "net.ru", "nov.ru", "org.ru", "pp.ru", "spb.ru", "ru", "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sk", "sl", "sm", "sn", "so", "ss", "st", "msk.su", "nov.su", "spb.su", "su", "sx", "sy", "tc", "td", "tf", "tg", "th", "tk", "tl", "tm", "tn", "to", "tr", "tv", "tw", "tz", "biz.ua", "co.ua", "pp.ua", "ua", "ug", "ac.uk", "gov.uk", "uk", "fed.us", "us", "uy", "uz", "vc", "ve", "vg", "vu", "wf", "ws", "yt", "ac.za", "co.za", "gov.za", "net.za", "org.za", "web.za", "zm", "xn--2scrj9c", "xn--3e0b707e", "xn--3hcrj9c", "xn--45br5cyl", "xn--45brj9c", "xn--4dbrk0ce", "xn--80ao21a", "xn--90a3ac", "xn--90ae", "xn--90ais", "xn--clchc0ea0b2g2a9gcd", "xn--d1alf", "xn--e1a4c", "xn--fiqs8s", "xn--fiqz9s", "xn--fpcrj9c3d", "xn--fzc2c9e2c", "xn--gecrj9c", "xn--h2breg3eve", "xn--h2brj9c8c", "xn--h2brj9c", "xn--j1amh", "xn--j6w193g", "xn--kprw13d", "xn--kpry57d", "xn--lgbbat1ad8j", "xn--mgb9awbf", "xn--mgba3a4f16a", "xn--mgbaam7a8h", "xn--mgbah1a3hjkrd", "xn--mgbbh1a71e", "xn--mgbbh1a", "xn--mgberp4a5d4ar", "xn--mgbgu82a", "xn--mgbtx2b", "xn--mgbx4cd0ab", "xn--node", "xn--o3cw4h", "xn--ogbpf8fl", "xn--p1ai", "xn--pgbs0dh", "xn--q7ce6a", "xn--qxa6a", "xn--rvc1e0am3e", "xn--s9brj9c", "xn--wgbh1c", "xn--wgbl6a", "xn--xkc2al3hye2a", "xn--xkc2dl3a5ee0h", "xn--y9a3aq", "xn--yfro4i67o", + "xn--ygbi2ammx","xyz" +]);