高级DNS路由,为特定网站使用指定DNS解析
当前为
// ==UserScript==
// @name L站佬友专用DNS分流器
// @namespace http://tampermonkey.net/
// @license Duy
// @version 1.01
// @description 高级DNS路由,为特定网站使用指定DNS解析
// @author You
// @run-at document-start
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @connect chrome.cloudflare-dns.com
// @connect cloudflare-dns.com
// @connect linux.do
// @match https://linux.do/*
// @match http://linux.do/*
// @match https://*.linux.do/*
// @match http://*.linux.do/*
// @match https://github.com/*
// ==/UserScript==
(function() {
'use strict';
// 配置存储
const CONFIG_KEY = 'dnsRouterConfig';
const MATCH_RULES_KEY = 'dnsMatchRules';
// 默认配置 - 专门为 linux.do 添加规则
const defaultConfig = {
rules: {
'linux.do': 'https://chrome.cloudflare-dns.com/dns-query',
'github.com': 'https://chrome.cloudflare-dns.com/dns-query',
'twitter.com': 'https://chrome.cloudflare-dns.com/dns-query'
},
showUI: false
};
// 获取配置
function getConfig() {
const saved = GM_getValue(CONFIG_KEY, JSON.stringify(defaultConfig));
return JSON.parse(saved);
}
// 保存配置
function saveConfig(config) {
GM_setValue(CONFIG_KEY, JSON.stringify(config));
}
// 获取匹配规则
function getMatchRules() {
const saved = GM_getValue(MATCH_RULES_KEY, JSON.stringify([]));
return JSON.parse(saved);
}
// 保存匹配规则
function saveMatchRules(rules) {
GM_setValue(MATCH_RULES_KEY, JSON.stringify(rules));
}
// 为域名生成匹配模式
function generateMatchPatterns(domain) {
return [
`https://${domain}/*`,
`http://${domain}/*`,
`https://*.${domain}/*`,
`http://*.${domain}/*`
];
}
// 添加新的匹配规则
function addMatchRule(domain) {
const patterns = generateMatchPatterns(domain);
const existingRules = getMatchRules();
// 避免重复添加
for (const pattern of patterns) {
if (!existingRules.includes(pattern)) {
existingRules.push(pattern);
}
}
saveMatchRules(existingRules);
console.log(`[DNS Router] 为域名 ${domain} 添加匹配规则`);
// 提示用户需要重新安装脚本
showNotification(`已为 ${domain} 添加规则,请编辑脚本添加地址到// @match *以生效`, 'info', 5000);
}
// 删除匹配规则
function removeMatchRule(domain) {
const patterns = generateMatchPatterns(domain);
let existingRules = getMatchRules();
existingRules = existingRules.filter(pattern => !patterns.includes(pattern));
saveMatchRules(existingRules);
console.log(`[DNS Router] 为域名 ${domain} 移除匹配规则`);
}
let menuCommandId = null;
// DNS解析函数 - 专门处理 DoH 解析
async function resolveWithDoH(domain, dohEndpoint = 'https://chrome.cloudflare-dns.com/dns-query') {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: `${dohEndpoint}?name=${encodeURIComponent(domain)}&type=A`,
headers: {
'Accept': 'application/dns-json'
},
onload: function(response) {
try {
const data = JSON.parse(response.responseText);
if (data.Answer && data.Answer.length > 0) {
const ip = data.Answer[0].data;
console.log(`[DNS Router] ${domain} -> ${ip} via ${dohEndpoint}`);
resolve(ip);
} else {
reject(new Error(`No DNS answer for ${domain}`));
}
} catch (e) {
reject(e);
}
},
onerror: function(error) {
reject(error);
}
});
});
}
// 为 linux.do 执行实际的 DNS 重写
function setupDNSOverride() {
const config = getConfig();
const currentDomain = window.location.hostname;
console.log(`[DNS Router] 为 ${currentDomain} 设置 DNS 重写`);
// 预解析当前域名
for (const [domain, dns] of Object.entries(config.rules)) {
if (currentDomain === domain || currentDomain.endsWith('.' + domain)) {
resolveWithDoH(currentDomain, dns).then(ip => {
console.log(`[DNS Router] 预解析成功: ${currentDomain} -> ${ip}`);
}).catch(error => {
console.warn(`[DNS Router] 预解析失败: ${currentDomain}`, error);
});
break;
}
}
// 拦截资源请求
interceptResourceRequests();
}
// 拦截资源请求
function interceptResourceRequests() {
// 拦截 XMLHttpRequest
const originalXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, ...args) {
const processedUrl = processURL(url);
return originalXHROpen.call(this, method, processedUrl, ...args);
};
// 拦截 fetch 请求
const originalFetch = window.fetch;
window.fetch = function(input, init) {
if (typeof input === 'string') {
input = processURL(input);
}
return originalFetch.call(this, input, init);
};
console.log('[DNS Router] 资源请求拦截已启用');
}
// 处理 URL
function processURL(url) {
try {
const urlObj = new URL(url, window.location.href);
const config = getConfig();
// 检查 URL 的域名是否在规则中
for (const ruleDomain in config.rules) {
if (urlObj.hostname === ruleDomain || urlObj.hostname.endsWith('.' + ruleDomain)) {
console.log(`[DNS Router] 处理请求: ${urlObj.hostname}`);
break;
}
}
} catch (e) {
// URL 解析失败
}
return url;
}
// 创建UI界面
function createUI() {
const config = getConfig();
const currentDomain = window.location.hostname;
const isLinuxDo = currentDomain === 'linux.do' || currentDomain.endsWith('.linux.do');
const panel = document.createElement('div');
panel.id = 'dns-router-panel';
panel.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #2c3e50;
color: white;
padding: 20px;
border-radius: 10px;
z-index: 10000;
font-family: Arial, sans-serif;
font-size: 14px;
min-width: 400px;
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
border: 2px solid #3498db;
max-height: 80vh;
overflow-y: auto;
`;
let html = `
<div style="margin-bottom: 15px; font-weight: bold; border-bottom: 1px solid #34495e; padding-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
<span style="display: flex; align-items: center;">
<span style="color: #3498db; margin-right: 8px;">🌐</span>
DNS 路由器
</span>
<button id="close-panel" style="background: none; border: none; color: #e74c3c; font-size: 16px; cursor: pointer; padding: 0; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;">×</button>
</div>
<!-- Linux.Do 特别提示 -->
${isLinuxDo ? `
<div style="margin-bottom: 15px; padding: 12px; background: #1a5276; border-radius: 5px; border-left: 4px solid #3498db;">
<div style="font-size: 13px; font-weight: bold; color: #3498db; margin-bottom: 5px;">🚀 Linux.Do 专用解析</div>
<div style="font-size: 11px; color: #bbdefb;">
当前使用: <strong>https://chrome.cloudflare-dns.com/dns-query</strong><br>
Cloudflare DNS-over-HTTPS 加密解析
</div>
</div>
` : ''}
<!-- 当前状态 -->
<div style="margin-bottom: 20px; padding: 12px; background: #34495e; border-radius: 5px;">
<div style="font-size: 12px; color: #bdc3c7; margin-bottom: 5px;">当前域名</div>
<div style="font-size: 13px; font-weight: bold; color: #ecf0f1;">${currentDomain}</div>
<div style="font-size: 11px; color: #95a5a6; margin-top: 3px;">
DNS: ${isLinuxDo ? 'Cloudflare DoH' : '根据规则配置'}
</div>
</div>
<!-- 添加新规则 -->
<div style="margin-bottom: 20px; padding: 15px; background: #34495e; border-radius: 5px;">
<div style="font-size: 13px; font-weight: bold; margin-bottom: 10px; color: #3498db;">添加新规则</div>
<div style="display: grid; grid-template-columns: 1fr 1.5fr auto; gap: 8px; align-items: end;">
<div>
<div style="font-size: 11px; color: #bdc3c7; margin-bottom: 4px;">域名</div>
<input type="text" id="new-domain" placeholder="example.com" style="width: 100%; padding: 6px; border: 1px solid #555; background: #2c3e50; color: white; border-radius: 3px; font-size: 12px;">
</div>
<div>
<div style="font-size: 11px; color: #bdc3c7; margin-bottom: 4px;">DNS端点</div>
<input type="text" id="new-dns" placeholder="https://chrome.cloudflare-dns.com/dns-query" value="https://chrome.cloudflare-dns.com/dns-query" style="width: 100%; padding: 6px; border: 1px solid #555; background: #2c3e50; color: white; border-radius: 3px; font-size: 12px;">
</div>
<button id="add-rule" style="background: #27ae60; color: white; border: none; padding: 6px 12px; border-radius: 3px; cursor: pointer; font-size: 12px; white-space: nowrap;">添加</button>
</div>
<div style="font-size: 10px; color: #7f8c8d; margin-top: 8px;">
💡 添加后需要重新安装脚本才能在新域名上生效
</div>
</div>
<!-- 规则列表 -->
<div style="margin-bottom: 20px;">
<div style="font-size: 13px; font-weight: bold; margin-bottom: 10px; color: #3498db;">规则列表</div>
<div id="rules-list" style="max-height: 200px; overflow-y: auto;">
`;
// 显示规则列表
for (const [domain, dns] of Object.entries(config.rules)) {
const isCurrent = currentDomain === domain || currentDomain.endsWith('.' + domain);
const isDoH = dns.includes('cloudflare-dns');
const isLinuxDoRule = domain === 'linux.do';
html += `
<div class="rule-item" data-domain="${domain}" style="display: flex; justify-content: space-between; align-items: center; padding: 8px; margin-bottom: 5px; background: ${isCurrent ? (isLinuxDoRule ? '#1a237e' : '#1a5276') : '#2c3e50'}; border-radius: 5px; border-left: 4px solid ${isLinuxDoRule ? '#3498db' : (isDoH ? '#27ae60' : '#e74c3c')};">
<div style="flex: 1;">
<div style="font-size: 12px; font-weight: bold; color: ${isCurrent ? '#3498db' : '#ecf0f1'};">${domain}</div>
<div style="font-size: 10px; color: #bdc3c7; word-break: break-all;">${dns}</div>
</div>
<div style="display: flex; align-items: center; gap: 5px;">
${isLinuxDoRule ? '<span style="font-size: 8px; background: #3498db; color: white; padding: 2px 5px; border-radius: 3px;">默认</span>' : ''}
<button class="delete-rule" data-domain="${domain}" style="background: #e74c3c; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 10px;">删除</button>
</div>
</div>
`;
}
html += `
</div>
</div>
<!-- 操作按钮 -->
<div style="display: flex; gap: 10px; justify-content: flex-end; border-top: 1px solid #34495e; padding-top: 15px;">
<button id="test-dns" style="background: #e67e22; color: white; border: none; padding: 8px 15px; border-radius: 3px; cursor: pointer; font-size: 12px; display: flex; align-items: center;">
<span style="margin-right: 5px;">🔍</span>测试DNS
</button>
<button id="save-config" style="background: #27ae60; color: white; border: none; padding: 8px 15px; border-radius: 3px; cursor: pointer; font-size: 12px; display: flex; align-items: center;">
<span style="margin-right: 5px;">💾</span>保存配置
</button>
</div>
`;
panel.innerHTML = html;
document.body.appendChild(panel);
// 添加遮罩层
const overlay = document.createElement('div');
overlay.id = 'dns-router-overlay';
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 9999;
`;
document.body.appendChild(overlay);
// 事件监听
setupEventListeners(panel);
}
// 设置事件监听
function setupEventListeners(panel) {
// 关闭面板
document.getElementById('close-panel').addEventListener('click', hideUI);
document.getElementById('dns-router-overlay').addEventListener('click', hideUI);
// 添加规则
document.getElementById('add-rule').addEventListener('click', addNewRule);
// 删除规则
document.querySelectorAll('.delete-rule').forEach(btn => {
btn.addEventListener('click', function() {
const domain = this.getAttribute('data-domain');
deleteRule(domain);
});
});
// 测试DNS
document.getElementById('test-dns').addEventListener('click', testCurrentDNS);
// 保存配置
document.getElementById('save-config').addEventListener('click', function() {
showNotification('配置已保存', 'success');
setTimeout(hideUI, 1000);
});
// 回车键添加规则
document.getElementById('new-domain').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addNewRule();
}
});
}
// 添加新规则
function addNewRule() {
const domainInput = document.getElementById('new-domain');
const dnsInput = document.getElementById('new-dns');
const domain = domainInput.value.trim();
const dns = dnsInput.value.trim() || 'https://chrome.cloudflare-dns.com/dns-query';
if (!domain) {
showNotification('请输入域名', 'error');
return;
}
const config = getConfig();
config.rules[domain] = dns;
saveConfig(config);
// 添加匹配规则
addMatchRule(domain);
showNotification(`已添加规则: ${domain},请编辑脚本添加地址到// @match *以生效`, 'info', 5000);
// 清空输入框
domainInput.value = '';
dnsInput.value = 'https://chrome.cloudflare-dns.com/dns-query';
// 重新加载UI
setTimeout(() => {
hideUI();
showUI();
}, 1000);
}
// 删除规则
function deleteRule(domain) {
// 防止删除 linux.do 的专用规则
if (domain === 'linux.do') {
showNotification('不能删除 linux.do 默认规则', 'error');
return;
}
const config = getConfig();
delete config.rules[domain];
saveConfig(config);
// 移除匹配规则
removeMatchRule(domain);
showNotification(`已删除规则: ${domain}`, 'success');
// 重新加载UI
setTimeout(() => {
hideUI();
showUI();
}, 1000);
}
// 测试当前域名DNS
function testCurrentDNS() {
const currentDomain = window.location.hostname;
const config = getConfig();
let dnsEndpoint = 'https://chrome.cloudflare-dns.com/dns-query';
// 查找匹配的DNS规则
for (const [domain, dns] of Object.entries(config.rules)) {
if (currentDomain === domain || currentDomain.endsWith('.' + domain)) {
dnsEndpoint = dns;
break;
}
}
const testBtn = document.getElementById('test-dns');
const originalText = testBtn.innerHTML;
testBtn.innerHTML = '<span style="margin-right: 5px;">⏳</span>测试中...';
testBtn.disabled = true;
resolveWithDoH(currentDomain, dnsEndpoint).then(ip => {
showNotification(`✅ DNS解析成功<br>域名: ${currentDomain}<br>IP: ${ip}<br>DNS: ${dnsEndpoint}`, 'success');
testBtn.innerHTML = originalText;
}).catch(error => {
showNotification(`❌ DNS解析失败<br>域名: ${currentDomain}<br>错误: ${error.message}`, 'error');
testBtn.innerHTML = originalText;
}).finally(() => {
testBtn.disabled = false;
});
}
// 显示UI
function showUI() {
if (document.getElementById('dns-router-panel')) {
hideUI();
}
createUI();
}
// 隐藏UI
function hideUI() {
const panel = document.getElementById('dns-router-panel');
const overlay = document.getElementById('dns-router-overlay');
if (panel) panel.remove();
if (overlay) overlay.remove();
}
// 显示通知
function showNotification(message, type = 'info', duration = 4000) {
// 移除现有通知
const existing = document.getElementById('dns-router-notification');
if (existing) existing.remove();
const notification = document.createElement('div');
notification.id = 'dns-router-notification';
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'success' ? '#27ae60' : type === 'error' ? '#e74c3c' : '#3498db'};
color: white;
padding: 12px 15px;
border-radius: 5px;
z-index: 10002;
font-family: Arial, sans-serif;
font-size: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
max-width: 300px;
`;
notification.innerHTML = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, duration);
}
// 注册菜单命令
function registerMenuCommand() {
if (menuCommandId) {
GM_unregisterMenuCommand(menuCommandId);
}
menuCommandId = GM_registerMenuCommand('🚀 显示DNS路由器', function() {
showUI();
});
}
// 初始化
function init() {
registerMenuCommand();
setupDNSOverride();
console.log('Advanced DNS Router 已加载 - Linux.Do DoH 解析已启用');
// 如果配置中设置为显示UI,则自动显示
const config = getConfig();
if (config.showUI) {
setTimeout(showUI, 1000);
}
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();