diff --git a/api/dnsresolver.js b/api/dnsresolver.js new file mode 100644 index 00000000..f6be599e --- /dev/null +++ b/api/dnsresolver.js @@ -0,0 +1,98 @@ +// api/dnsresolver.js +import { Resolver } from 'dns'; +import { promisify } from 'util'; + +// 普通 DNS 服务器列表 +const dnsServers = { + 'Google': '8.8.8.8', + 'Cloudflare': '1.1.1.1', + 'OpenDNS': '208.67.222.222', + 'Quad9': '9.9.9.9', + 'ControlD': '76.76.2.0', + 'AdGuard': '94.140.14.14', + 'Quad 101': '101.101.101.101', + 'AliDNS': '223.5.5.5', + 'DNSPod': '119.29.29.29', + '114DNS': '114.114.114.114', + 'China Unicom': '123.123.123.123', +}; + +// DNS-over-HTTPS 服务列表 +const dohServers = { + 'Google': 'https://dns.google/resolve?', + 'Cloudflare': 'https://cloudflare-dns.com/dns-query?ct=application/dns-json&', + 'AliDNS': 'https://dns.alidns.com/resolve?', +}; + +const resolveDns = async (hostname, name, server) => { + const resolver = new Resolver(); + resolver.setServers([server]); + const resolve4Async = promisify(resolver.resolve4.bind(resolver)); + try { + const addresses = await resolve4Async(hostname); + return { [name]: addresses }; + } catch (error) { + console.log(error.message); + return { [name]: `Error: No addresses found` }; + } +}; + +const resolveDoh = async (hostname, name, url) => { + try { + const response = await fetch(`${url}name=${hostname}&type=A`, { + headers: { 'Accept': 'application/dns-json' } + }); + const data = await response.json(); + const addresses = data.Answer ? data.Answer.map(answer => answer.data) : ['Error: No addresses found']; + return { [name]: addresses }; + } catch (error) { + console.log(error.message); + return { [name]: `Error: No addresses found` }; + } +}; + +const dnsResolver = async (req, res) => { + + // 限制请求方法 + if (req.method !== 'GET') { + return res.status(405).json({ message: 'Method Not Allowed' }); + } + + // 限制只能从指定域名访问 + const allowedDomains = ['localhost', ...(process.env.ALLOWED_DOMAINS || '').split(',')]; + const referer = req.headers.referer; + if (referer) { + const domain = new URL(referer).hostname; + if (!allowedDomains.includes(domain)) { + return res.status(403).json({ error: 'Access denied' }); + } + } else { + return res.status(403).json({ error: 'What are you doing?' }); + } + + const { hostname } = req.query; + + if (!hostname) { + return res.status(400).send({ error: 'Missing hostname parameter' }); + } + + const dnsPromises = Object.entries(dnsServers).map(([name, ip]) => resolveDns(hostname, name, ip)); + const dohPromises = Object.entries(dohServers).map(([name, url]) => resolveDoh(hostname, name, url)); + + try { + // 并行执行所有 DNS 和 DoH 查询 + + const result_dns = await Promise.all(dnsPromises); + const result_doh = await Promise.all(dohPromises); + + res.json({ + hostname, + result_dns, + result_doh + }); + } catch (error) { + res.status(500).send({ error: error.message }); + } +}; + +export default dnsResolver; diff --git a/package.json b/package.json index 35b2d293..75775b56 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "svgmap": "^2.10.1", "vue": "^3.4.21", "vue-i18n": "^9.10.2", + "vue-router": "^4.3.0", "vuex": "^4.1.0" }, "devDependencies": { diff --git a/server.js b/server.js index 0165e86a..0d7f269b 100644 --- a/server.js +++ b/server.js @@ -11,6 +11,7 @@ import ipsbHandler from './api/ipsb.js'; import cfHander from './api/cfradar.js'; import recaptchaHandler from './api/recaptcha.js'; import validateConfigs from './api/configs.js'; +import dnsResolver from './api/dnsresolver.js'; dotenv.config(); @@ -27,6 +28,9 @@ app.get('/api/ipsb', ipsbHandler); app.get('/api/cfradar', cfHander); app.get('/api/recaptcha', recaptchaHandler); +// DNS Resolver +app.get('/api/dnsresolver', dnsResolver); + // 使用查询参数处理所有配置请求 app.get('/api/configs', validateConfigs); diff --git a/src/App.vue b/src/App.vue index 5b7d52d5..702f36fa 100644 --- a/src/App.vue +++ b/src/App.vue @@ -21,10 +21,8 @@ - - - + @@ -47,13 +45,11 @@ import Connectivity from './components/connectivity.vue' import WebRTC from './components/webrtc.vue' import DNSLeaks from './components/dnsleaks.vue' import SpeedTest from './components/speedtest.vue' -import GlobalLatency from './components/globallatency.vue' -import MTRtest from './components/mtrtest.vue' import Footer from './components/footer.vue' import QueryIP from './components/queryip.vue' import HelpModal from './components/help.vue' import PWA from './components/pwa.vue' -import RuleTest from './components/ruletest.vue' +import AdvancedTools from './components/advancedtools.vue' import { mappingKeys, navigateCards, keyMap } from "./shortcut.js"; import { ref, computed, watch } from 'vue'; @@ -67,6 +63,7 @@ export default { const store = useStore(); const isDarkMode = computed(() => store.state.isDarkMode); const isMobile = computed(() => store.state.isMobile); + const configs = computed(() => store.state.configs); const shouldRefreshEveryThing = computed(() => store.state.shouldRefreshEveryThing); const shouldRefresh = ref(false); @@ -78,6 +75,7 @@ export default { isDarkMode, isMobile, shouldRefresh, + configs, }; }, @@ -88,13 +86,11 @@ export default { WebRTC, DNSLeaks, SpeedTest, - GlobalLatency, - MTRtest, Footer, QueryIP, HelpModal, PWA, - RuleTest, + AdvancedTools, }, name: 'App', data() { @@ -105,7 +101,6 @@ export default { originipDataCards: [], originstunServers: [], originleakTest: [], - originRuleTests: [], alertToShow: false, alertStyle: "", alertMessage: "", @@ -209,7 +204,6 @@ export default { this.originipDataCards = JSON.parse(JSON.stringify(this.$refs.IPCheckRef.ipDataCards)); this.originstunServers = JSON.parse(JSON.stringify(this.$refs.webRTCRef.stunServers)); this.originleakTest = JSON.parse(JSON.stringify(this.$refs.dnsLeaksRef.leakTest)); - this.originRuleTests = JSON.parse(JSON.stringify(this.$refs.ruleTestRef.ruleTests)); this.infoMask(); this.alertStyle = "text-warning"; this.alertMessage = this.$t('alert.maskedInfoMessage_1'); @@ -245,9 +239,6 @@ export default { this.$refs.dnsLeaksRef.leakTest.forEach((server) => { server.ip = "12.34.56.78"; }); - this.$refs.ruleTestRef.ruleTests.forEach((test) => { - test.ip = "8.8.8.8"; - }); this.infoMaskLevel = 1; } else if (this.infoMaskLevel === 1) { this.$refs.IPCheckRef.ipDataCards.forEach((card) => { @@ -266,9 +257,6 @@ export default { this.$refs.dnsLeaksRef.leakTest.forEach((server) => { server.geo = "United States"; }); - this.$refs.ruleTestRef.ruleTests.forEach((test) => { - test.country_code = "US"; - }); this.infoMaskLevel = 2; } }, @@ -278,7 +266,6 @@ export default { this.$refs.IPCheckRef.ipDataCards = JSON.parse(JSON.stringify(this.originipDataCards)); this.$refs.webRTCRef.stunServers = JSON.parse(JSON.stringify(this.originstunServers)); this.$refs.dnsLeaksRef.leakTest = JSON.parse(JSON.stringify(this.originleakTest)); - this.$refs.ruleTestRef.ruleTests = JSON.parse(JSON.stringify(this.originRuleTests)); this.infoMaskLevel = 0; }, @@ -404,15 +391,6 @@ export default { }, description: this.$t('shortcutKeys.RefreshWebRTC'), }, - { - keys: "r", - action: () => { - this.scrollToElement("RuleTest", 80); - this.$refs.ruleTestRef.checkAllRuleTest(true); - this.$trackEvent('ShortCut', 'ShortCut', 'WebRTC'); - }, - description: this.$t('shortcutKeys.RefreshRuleTests'), - }, { keys: "d", action: () => { @@ -434,7 +412,7 @@ export default { { keys: "m", action: () => { - if (this.$refs.IPCheckRef.isEnvBingMapKey) { + if (this.configs.bingMap) { window.scrollTo({ top: 0, behavior: "smooth" }); this.$refs.IPCheckRef.toggleMaps(); }; diff --git a/src/components/advancedtools.vue b/src/components/advancedtools.vue new file mode 100644 index 00000000..deeedbc1 --- /dev/null +++ b/src/components/advancedtools.vue @@ -0,0 +1,218 @@ + + + + + \ No newline at end of file diff --git a/src/components/dnsresolver.vue b/src/components/dnsresolver.vue new file mode 100644 index 00000000..223d8b33 --- /dev/null +++ b/src/components/dnsresolver.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/src/components/ipcheck.vue b/src/components/ipcheck.vue index 5c77f622..fdd16e64 100644 --- a/src/components/ipcheck.vue +++ b/src/components/ipcheck.vue @@ -441,9 +441,9 @@ export default { // 从中国来源获取 IP 地址 getIPfromCNSource() { - this.getIPFromQQ().catch(() => { - this.getIPFromIPIP().catch(() => { - this.getIPFromTaobao(); + this.getIPFromIPIP().catch(() => { + this.getIPFromTaobao().catch(() => { + this.getIPFromQQ(); }) }); }, @@ -577,7 +577,7 @@ export default { this.fetchIPDetails(1, ip); } catch (error) { console.error("Error fetching IP from Upai:", error); - this.ipDataCards[1].ip = this.$t('ipInfos.IPv4Error'); + this.getIPFromCloudflare_CN(); // 故障转移 } }, @@ -598,7 +598,26 @@ export default { this.fetchIPDetails(1, ip); } catch (error) { console.error("Error fetching IP from IPCheck.ing:", error); - this.getIPFromUpai(); // 如果发生错误,调用 getIPFromUpai + this.getIPFromCloudflare_CN(); // 故障转移 + } + }, + + // 从 Cloudflare 中国获取 IP 地址 + async getIPFromCloudflare_CN() { + try { + const response = await fetch("https://cf-ns.com/cdn-cgi/trace"); + const data = await response.text(); + const lines = data.split("\n"); + const ipLine = lines.find((line) => line.startsWith("ip=")); + if (ipLine) { + const ip = ipLine.split("=")[1]; + this.IPArray = [...this.IPArray, ip]; + this.ipDataCards[1].source = "CF-CN"; + this.fetchIPDetails(1, ip); + } + } catch (error) { + console.error("Error fetching IP from Cloudflare:", error); + this.ipDataCards[1].ip = this.$t('ipInfos.IPv4Error'); } }, @@ -912,6 +931,10 @@ export default { this.getIPFromQQ(card); this.$trackEvent('IPCheck', 'RefreshClick', 'QQ.com'); break; + case "CF-CN": + this.getIPFromCloudflare_CN(card); + this.$trackEvent('IPCheck', 'RefreshClick', 'CF-CN'); + break; default: console.error("Undefind Source:", card.source); } diff --git a/src/components/nav.vue b/src/components/nav.vue index d410ff28..aed217b9 100644 --- a/src/components/nav.vue +++ b/src/components/nav.vue @@ -63,17 +63,11 @@ {{ $t('nav.DNSLeakTest') }} - {{ - $t('nav.RuleTest') }} {{ $t('nav.SpeedTest') }} - {{ $t('nav.PingTest') - }} - {{ $t('nav.MTRTest') }} + {{ $t('nav.AdvancedTools') }} diff --git a/src/locales/en.json b/src/locales/en.json index d4259f97..da4d090c 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -9,7 +9,27 @@ "SpeedTest": "Speed Test", "PingTest": "Global Latency", "MTRTest": "MTR Test", - "RuleTest": "Rule Test" + "RuleTest": "Rule Test", + "AdvancedTools": "Advanced Tools" + }, + "advancedtools": { + "Title": "Advanced Tools", + "Note": "Tools that are used relatively infrequently, but are very useful when performing network testing.", + "PingTestNote": "Global Ping value test", + "MTRTestNote": "Global MTR route test", + "DNSResolverNote": "Real-time multi-channel DNS resolution", + "RuleTestNote": "Check the rule settings of proxy software" + }, + "dnsresolver": { + "Title": "DNS Resolution", + "Note": "In some regions, due to political or commercial reasons, some operators may contaminate certain domain names, resulting in incorrect results when accessed directly. Using DNS resolution checks will help you inspect the resolution results of domain names from well-known DNS providers around the world. Among the built-in DNS tests, some providers are from China, whose DNS resolution results may be contaminated. Please be discerning.", + "Note2": "Please enter a URL or domain name to start resolution:", + "Placeholder": "URL or Domain Name", + "Run": "Run", + "invalidURL": "Invalid URL or Domain Name", + "fetchError": "Unable to fetch resolution results", + "Provider": "DNS Provider", + "Result": "Resolution Result" }, "ruletest": { "Title": "Rule Test", @@ -487,6 +507,24 @@ "change": "Some Improvements" } ] + }, + { + "version": "v3.6", + "date": "Mar 10, 2024", + "content": [ + { + "type": "add", + "change": "Added DNS resolution feature" + }, + { + "type": "improve", + "change": "Optimized home page structure, advanced features are now collapsible" + }, + { + "type": "improve", + "change": "Optimized program logic" + } + ] } ] } diff --git a/src/locales/fr.json b/src/locales/fr.json index b6b1cb01..d36f3133 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -9,7 +9,27 @@ "SpeedTest": "Test de vitesse", "PingTest": "Latence mondiale", "MTRTest": "Test MTR", - "RuleTest": "Test de règles" + "RuleTest": "Test de règles", + "AdvancedTools": "Outils avancés" + }, + "advancedtools": { + "Title": "Outils avancés", + "Note": "Certains outils sont utilisés relativement rarement, mais sont très utiles lors des tests de réseaux.", + "PingTestNote": "Test de la valeur Ping mondiale", + "MTRTestNote": "Test de route MTR mondial", + "DNSResolverNote": "Résolution DNS multi-canal en temps réel", + "RuleTestNote": "Vérifiez les paramètres de règles du logiciel de proxy" + }, + "dnsresolver": { + "Title": "Résolution DNS", + "Note": "Dans certaines régions, pour des raisons politiques ou commerciales, certains opérateurs peuvent contaminer certains noms de domaine, entraînant des résultats incorrects lorsqu'ils sont accédés directement. L'utilisation de vérifications de résolution DNS vous aidera à inspecter les résultats de résolution de noms de domaine auprès de fournisseurs DNS renommés dans le monde entier. Parmi les tests DNS intégrés, certains fournisseurs sont originaires de Chine, dont les résultats de résolution DNS peuvent être contaminés. Veuillez faire preuve de discernement.", + "Note2": "Veuillez entrer une URL ou un nom de domaine pour commencer la résolution :", + "Placeholder": "URL ou Nom de Domaine", + "Run": "Exécuter", + "invalidURL": "URL ou Nom de Domaine invalide", + "fetchError": "Impossible de récupérer les résultats de résolution", + "Provider": "Fournisseur DNS", + "Result": "Résultat de la Résolution" }, "ruletest": { "Title": "Test de règles", @@ -487,6 +507,24 @@ "change": "Quelques améliorations" } ] + }, + { + "version": "v3.6", + "date": "Mar 10, 2024", + "content": [ + { + "type": "add", + "change": "Ajout de la fonction de résolution DNS" + }, + { + "type": "improve", + "change": "Optimisation de la structure de la page d'accueil, les fonctionnalités avancées sont désormais repliables" + }, + { + "type": "improve", + "change": "Optimisation de la logique du programme" + } + ] } ] } diff --git a/src/locales/zh.json b/src/locales/zh.json index 8502fd87..9e6356ca 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -9,7 +9,27 @@ "SpeedTest": "网速测试", "PingTest": "全球延迟", "MTRTest": "MTR 测试", - "RuleTest": "分流测试" + "RuleTest": "分流测试", + "AdvancedTools": "高级工具" + }, + "advancedtools": { + "Title": "高级工具", + "Note": "一些使用频率相对较低,但在进行网络测试时,非常有用的工具。", + "PingTestNote": "全球的 Ping 值测试", + "MTRTestNote": "全球的 MTR 路由测试", + "DNSResolverNote": "实时多渠道 DNS 解析", + "RuleTestNote": "检查代理软件的规则设置" + }, + "dnsresolver": { + "Title": "DNS 解析", + "Note": "在一些地区,某些运营商可能会因为政治原因或者商业原因,对一些域名进行污染,以至于直接进行访问的时候,无法返回正确的结果。使用 DNS 解析检查,将帮助你从全球各个知名的 DNS 厂商里检查域名的解析结果。在检测内置的 DNS 中,有一部分服务商来自中国,其 DNS 解析的结果可能会受到污染。请注意鉴别。", + "Note2": "请输入 URL 或域名,开始进行解析:", + "Placeholder": "URL 或域名", + "Run": "运行", + "invalidURL": "无效的 URL 或域名", + "fetchError": "无法获取解析结果", + "Provider": "DNS 服务商", + "Result": "解析结果" }, "ruletest": { "Title": "分流测试", @@ -487,6 +507,24 @@ "change": "优化了部分逻辑" } ] + }, + { + "version": "v3.6", + "date": "Mar 10, 2024", + "content": [ + { + "type": "add", + "change": "添加 DNS 解析功能" + }, + { + "type": "improve", + "change": "优化主页结构,高级功能折叠展示" + }, + { + "type": "improve", + "change": "优化了程序逻辑" + } + ] } ] } diff --git a/src/main.js b/src/main.js index 9cf3266b..3e2c3cb4 100644 --- a/src/main.js +++ b/src/main.js @@ -6,6 +6,7 @@ import store from './store'; import Analytics from 'analytics'; import googleAnalytics from '@analytics/google-analytics'; import { Tooltip } from 'bootstrap'; +import router from './router'; const app = createApp(App); @@ -67,6 +68,7 @@ analytics.page(); app.use(store); app.use(i18n); +app.use(router); app.config.globalProperties.$Lang = i18n.global.locale; app.config.globalProperties.$analytics = analytics; diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 00000000..454c22fa --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,21 @@ +import { createRouter, createWebHashHistory } from 'vue-router'; + +// 路由组件的懒加载 +const MTRTest = () => import('../components/mtrtest.vue'); +const PingTest = () => import('../components/globallatency.vue'); +const RuleTest = () => import('../components/ruletest.vue'); +const DNSResolver = () => import('../components/dnsresolver.vue'); + +const routes = [ + { path: '/mtrtest', component: MTRTest }, + { path: '/pingtest', component: PingTest }, + { path: '/ruletest', component: RuleTest }, + { path: '/dnsresolver', component: DNSResolver }, +]; + +const router = createRouter({ + history: createWebHashHistory(), + routes, +}); + +export default router;