mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
Every handler under api/ used to open with the same 4-line referer gate:
const referer = req.headers.referer;
if (!refererCheck(referer)) {
return res.status(403).json({...});
}
Replace that with `app.use('/api', requireReferer)` in backend-server.js
(from common/guards.js, landed in the previous commit). The middleware
fires before any handler runs, so by the time execution reaches a
handler, the referer is guaranteed to be on the allow-list. 16 handlers
each lose ~4 lines + one import.
api/configs.js is a special case — it still READS req.headers.referer
to classify whether the caller is the canonical ipcheck.ing deployment
("originalSite") or a self-hosted fork. No longer rejects, just reads.
Tests updated:
- tests/api-handlers.test.js: drop the 24-case "handlers reject missing
referer" parametrized suite. Its concern moved to middleware; covered
by tests/guards.test.js.
- tests/api-smoke.test.js: drop the 4 "rejects without referer" cases
for configs / google-map / maxmind / dns-resolver for the same
reason. Non-referer assertions (method gating, invalid params,
successful config read) stay.
Net -208 lines vs pre-commit; 208 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
138 lines
4.6 KiB
JavaScript
138 lines
4.6 KiB
JavaScript
// 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&',
|
|
'AdGuard': 'https://dns.adguard.com/resolve?',
|
|
'AliDNS': 'https://dns.alidns.com/resolve?',
|
|
};
|
|
|
|
const resolveDns = async (hostname, type, name, server) => {
|
|
const resolver = new Resolver();
|
|
resolver.setServers([server]);
|
|
const resolve4Async = promisify(resolver.resolve4.bind(resolver));
|
|
const resolve6Async = promisify(resolver.resolve6.bind(resolver));
|
|
const resolveTxtAsync = promisify(resolver.resolveTxt.bind(resolver));
|
|
const resolveCnameAsync = promisify(resolver.resolveCname.bind(resolver));
|
|
const resolveNSAsync = promisify(resolver.resolveNs.bind(resolver));
|
|
const resolveMXAsync = promisify(resolver.resolveMx.bind(resolver));
|
|
try {
|
|
let addresses;
|
|
|
|
// 根据传入的 type 参数选择不同的解析方法
|
|
switch (type) {
|
|
case 'A':
|
|
addresses = await resolve4Async(hostname);
|
|
break;
|
|
case 'AAAA':
|
|
addresses = await resolve6Async(hostname);
|
|
break;
|
|
case 'TXT':
|
|
addresses = await resolveTxtAsync(hostname);
|
|
// TXT 记录解析的结果是一个二维数组,这里进行扁平化处理
|
|
addresses = addresses.flat();
|
|
break;
|
|
case 'CNAME':
|
|
addresses = await resolveCnameAsync(hostname);
|
|
break;
|
|
case 'NS':
|
|
addresses = await resolveNSAsync(hostname);
|
|
break;
|
|
case 'MX':
|
|
addresses = await resolveMXAsync(hostname);
|
|
addresses = addresses.map(item => `${item.priority} ${item.exchange}.`)
|
|
.join(', ');
|
|
break;
|
|
default:
|
|
throw new Error('Unsupported type');
|
|
}
|
|
|
|
if (addresses.length === 0 || addresses === '' || addresses === null) {
|
|
return { [name]: `N/A` };
|
|
}
|
|
|
|
return { [name]: addresses };
|
|
} catch (error) {
|
|
console.log(error.message);
|
|
return { [name]: `N/A` };
|
|
}
|
|
};
|
|
|
|
const resolveDoh = async (hostname, type, name, url) => {
|
|
try {
|
|
const response = await fetch(`${url}name=${hostname}&type=${type}`, {
|
|
headers: { 'Accept': 'application/dns-json' }
|
|
});
|
|
const data = await response.json();
|
|
const addresses = data.Answer ? data.Answer.map(answer => answer.data) : ['N/A'];
|
|
if (addresses.length === 0 || addresses === '' || addresses === null) {
|
|
return { [name]: `N/A` };
|
|
}
|
|
return { [name]: addresses };
|
|
} catch (error) {
|
|
console.log(error.message);
|
|
return { [name]: `N/A` };
|
|
}
|
|
};
|
|
|
|
const dnsResolver = async (req, res) => {
|
|
|
|
// 限制请求方法 — defensive; app.get() in backend-server.js already gates method,
|
|
// but a dedicated smoke test asserts this 405 branch directly against the handler.
|
|
if (req.method !== 'GET') {
|
|
return res.status(405).json({ message: 'Method Not Allowed' });
|
|
}
|
|
|
|
const { hostname, type } = req.query;
|
|
|
|
if (typeof hostname !== 'string') {
|
|
return res.status(400).send({ error: 'Hostname parameter must be a string' });
|
|
}
|
|
|
|
if (!hostname) {
|
|
return res.status(400).send({ error: 'Missing hostname parameter' });
|
|
}
|
|
|
|
if (!hostname.includes('.')) {
|
|
return res.status(400).send({ error: 'Invalid hostname' });
|
|
}
|
|
|
|
const dnsPromises = Object.entries(dnsServers).map(([name, ip]) => resolveDns(hostname, type, name, ip));
|
|
const dohPromises = Object.entries(dohServers).map(([name, url]) => resolveDoh(hostname, type, 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;
|