From daa649feeb5b2b241f12b83d6f74cf484f877f76 Mon Sep 17 00:00:00 2001 From: bia-pain-bache Date: Wed, 23 Oct 2024 01:46:04 +0330 Subject: [PATCH] Clash Fake DNS filter, Xray DNS hosts. --- _worker.js | 500 +++++++++++++++++++++++++++----------------------- src/worker.js | 130 ++++++------- 2 files changed, 333 insertions(+), 297 deletions(-) diff --git a/_worker.js b/_worker.js index f77974799..ac2aec244 100644 --- a/_worker.js +++ b/_worker.js @@ -5020,7 +5020,7 @@ async function updateDataset(env, newSettings, resetSettings) { vlessTrojanFakeDNS: validateField("vlessTrojanFakeDNS") ?? currentSettings?.vlessTrojanFakeDNS ?? false, proxyIP: validateField("proxyIP")?.trim() ?? currentSettings?.proxyIP ?? "", outProxy: validateField("outProxy") ?? currentSettings?.outProxy ?? "", - outProxyParams: extractChainProxyParams(validateField("outProxy")) ?? currentSettings?.outProxyParams ?? "", + outProxyParams: extractChainProxyParams(validateField("outProxy")) ?? currentSettings?.outProxyParams ?? {}, cleanIPs: validateField("cleanIPs")?.replaceAll(" ", "") ?? currentSettings?.cleanIPs ?? "", enableIPv6, customCdnAddrs: validateField("customCdnAddrs")?.replaceAll(" ", "") ?? currentSettings?.customCdnAddrs ?? "", @@ -6839,7 +6839,7 @@ function renderErrorPage(message2, error, refer) { function extractChainProxyParams(chainProxy) { let configParams = {}; if (!chainProxy) - return null; + return {}; let url = new URL(chainProxy); const protocol = url.protocol.slice(0, -1); if (protocol === "vless") { @@ -6931,7 +6931,7 @@ function extractWireguardParams(warpConfigs, isWoW) { privateKey: warpConfigs[index].privateKey }; } -async function buildXrayDNS(proxySettings, outboundAddrs, domainToStaticIPs, isWorkerLess, isWarp) { +async function buildXrayDNS(proxySettings, outboundAddrs, domainToStaticIPs, isWorkerLess, isBalancer, isWarp) { const { remoteDNS, resolvedRemoteDNS, @@ -6947,47 +6947,51 @@ async function buildXrayDNS(proxySettings, outboundAddrs, domainToStaticIPs, isW bypassRussia } = proxySettings; const isBypass = bypassIran || bypassChina || bypassRussia; + const isBlock = blockAds || blockPorn; + const bypassRules = [ + { rule: bypassIran, domain: "geosite:category-ir", ip: "geoip:ir" }, + { rule: bypassChina, domain: "geosite:cn", ip: "geoip:cn" }, + { rule: bypassRussia, domain: "geosite:category-ru", ip: "geoip:ru" } + ]; + const hostsRules = [ + { rule: blockAds, host: "geosite:category-ads-all", address: ["127.0.0.1"] }, + { rule: blockAds, host: "geosite:category-ads-ir", address: ["127.0.0.1"] }, + { rule: blockPorn, host: "geosite:category-porn", address: ["127.0.0.1"] } + ]; const isFakeDNS = vlessTrojanFakeDNS && !isWarp || warpFakeDNS && isWarp; const isIPv62 = enableIPv6 && !isWarp || warpEnableIPv6 && isWarp; const outboundDomains = outboundAddrs.filter((address) => isDomain(address)); const isOutboundRule = outboundDomains.length > 0; const outboundRules = outboundDomains.map((domain) => `full:${domain}`); - const warpRemoteDNS = warpEnableIPv6 ? ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] : ["1.1.1.1", "1.0.0.1"]; - const finalRemoteDNS = isWarp ? warpRemoteDNS : isWorkerLess ? ["https://cloudflare-dns.com/dns-query"] : [remoteDNS]; - let dnsObject = { - hosts: { - "domain:googleapis.cn": ["googleapis.com"] - }, - servers: finalRemoteDNS, - queryStrategy: isIPv62 ? "UseIP" : "UseIPv4", - tag: "dns" - }; + isBalancer && outboundRules.push("full:www.gstatic.com"); + const finalRemoteDNS = isWorkerLess ? ["https://cloudflare-dns.com/dns-query"] : isWarp ? warpEnableIPv6 ? ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] : ["1.1.1.1", "1.0.0.1"] : [remoteDNS]; + const dnsHost = {}; + isBlock && hostsRules.forEach(({ rule, host, address }) => { + if (rule) + dnsHost[host] = address; + }); const staticIPs = domainToStaticIPs ? await resolveDNS(domainToStaticIPs) : void 0; if (staticIPs) - dnsObject.hosts[domainToStaticIPs] = enableIPv6 ? [...staticIPs.ipv4, ...staticIPs.ipv6] : staticIPs.ipv4; + dnsHost[domainToStaticIPs] = enableIPv6 ? [...staticIPs.ipv4, ...staticIPs.ipv6] : staticIPs.ipv4; if (resolvedRemoteDNS.server && !isWorkerLess && !isWarp) - dnsObject.hosts[resolvedRemoteDNS.server] = resolvedRemoteDNS.staticIPs; + dnsHost[resolvedRemoteDNS.server] = resolvedRemoteDNS.staticIPs; if (isWorkerLess) { - const resolvedDOH = await resolveDNS("cloudflare-dns.com"); - const resolvedCloudflare = await resolveDNS("cloudflare.com"); - const resolvedCLDomain = await resolveDNS("www.speedtest.net.cdn.cloudflare.net"); - const resolvedCFNS_1 = await resolveDNS("ben.ns.cloudflare.com"); - const resolvedCFNS_2 = await resolveDNS("lara.ns.cloudflare.com"); - dnsObject.hosts["cloudflare-dns.com"] = [ - ...resolvedDOH.ipv4, - ...resolvedCloudflare.ipv4, - ...resolvedCLDomain.ipv4, - ...resolvedCFNS_1.ipv4, - ...resolvedCFNS_2.ipv4 + const domains = ["cloudflare-dns.com", "cloudflare.com", "dash.cloudflare.com"]; + const resolved = await Promise.all(domains.map(resolveDNS)); + const hostIPv4 = resolved.flatMap((r) => r.ipv4); + const hostIPv6 = enableIPv6 ? resolved.flatMap((r) => r.ipv6) : []; + dnsHost["cloudflare-dns.com"] = [ + ...hostIPv4, + ...hostIPv6 ]; } - if (blockAds) { - dnsObject.hosts["geosite:category-ads-all"] = ["127.0.0.1"]; - dnsObject.hosts["geosite:category-ads-ir"] = ["127.0.0.1"]; - } - if (blockPorn) { - dnsObject.hosts["geosite:category-porn"] = ["127.0.0.1"]; - } + const hosts = Object.keys(dnsHost).length ? { hosts: dnsHost } : {}; + let dnsObject = { + ...hosts, + servers: finalRemoteDNS, + queryStrategy: isIPv62 ? "UseIP" : "UseIPv4", + tag: "dns" + }; isOutboundRule && dnsObject.servers.push({ address: localDNS === "localhost" ? "8.8.8.8" : localDNS, domains: outboundRules @@ -6998,9 +7002,12 @@ async function buildXrayDNS(proxySettings, outboundAddrs, domainToStaticIPs, isW expectIPs: [] }; if (!isWorkerLess && isBypass) { - bypassIran && localDNSServer.domains.push("geosite:category-ir") && localDNSServer.expectIPs.push("geoip:ir"); - bypassChina && localDNSServer.domains.push("geosite:cn") && localDNSServer.expectIPs.push("geoip:cn"); - bypassRussia && localDNSServer.domains.push("geosite:category-ru") && localDNSServer.expectIPs.push("geoip:ru"); + bypassRules.forEach(({ rule, domain, ip }) => { + if (rule) { + localDNSServer.domains.push(domain); + localDNSServer.expectIPs.push(ip); + } + }); dnsObject.servers.push(localDNSServer); } if (isFakeDNS) { @@ -7029,7 +7036,16 @@ function buildXrayRoutingRules(proxySettings, outboundAddrs, isChain, isBalancer blockPorn, blockUDP443 } = proxySettings; - const isBypass = bypassIran || bypassChina || bypassRussia || bypassLAN; + const isBlock = blockAds || blockPorn; + const isBypass = bypassIran || bypassChina || bypassRussia; + const geoRules = [ + { rule: bypassLAN, type: "direct", domain: "geosite:private", ip: "geoip:private" }, + { rule: bypassIran, type: "direct", domain: "geosite:category-ir", ip: "geoip:ir" }, + { rule: bypassChina, type: "direct", domain: "geosite:cn", ip: "geoip:cn" }, + { rule: blockAds, type: "block", domain: "geosite:category-ads-all" }, + { rule: blockAds, type: "block", domain: "geosite:category-ads-ir" }, + { rule: blockPorn, type: "block", domain: "geosite:category-porn" } + ]; const outboundDomains = outboundAddrs.filter((address) => isDomain(address)); const isOutboundRule = outboundDomains.length > 0; let rules = [ @@ -7057,22 +7073,29 @@ function buildXrayRoutingRules(proxySettings, outboundAddrs, isChain, isBalancer outboundTag: "direct", type: "field" }); - if (isBypass && !isWorkerLess) { - let ipRule = { - ip: [], - outboundTag: "direct", - type: "field" - }; - let domainRule = { - domain: [], - outboundTag: "direct", + if (isBypass || isBlock) { + const createRule = (type, outbound) => ({ + [type]: [], + outboundTag: outbound, type: "field" - }; - bypassLAN && domainRule.domain.push("geosite:private") && ipRule.ip.push("geoip:private"); - bypassIran && domainRule.domain.push("geosite:category-ir") && ipRule.ip.push("geoip:ir"); - bypassChina && domainRule.domain.push("geosite:cn") && ipRule.ip.push("geoip:cn"); - bypassRussia && domainRule.domain.push("geosite:category-ru") && ipRule.ip.push("geoip:ru"); - rules.push(domainRule, ipRule); + }); + let geositeDirectRule, geoipDirectRule; + if (!isWorkerLess) { + geositeDirectRule = createRule("domain", "direct"); + geoipDirectRule = createRule("ip", "direct"); + } + let geositeBlockRule = createRule("domain", "block"); + geoRules.forEach(({ rule, type, domain, ip }) => { + if (rule) { + if (type === "direct") { + geositeDirectRule?.domain.push(domain); + geoipDirectRule?.ip?.push(ip); + } else { + geositeBlockRule.domain.push(domain); + } + } + }); + isWorkerLess ? rules.push(geositeBlockRule) : rules.push(geositeDirectRule, geoipDirectRule, geositeBlockRule); } blockUDP443 && rules.push({ network: "udp", @@ -7080,16 +7103,6 @@ function buildXrayRoutingRules(proxySettings, outboundAddrs, isChain, isBalancer outboundTag: "block", type: "field" }); - if (blockAds || blockPorn) { - let rule = { - domain: [], - outboundTag: "block", - type: "field" - }; - blockAds && rule.domain.push("geosite:category-ads-all", "geosite:category-ads-ir"); - blockPorn && rule.domain.push("geosite:category-porn"); - rules.push(rule); - } if (isBalancer) { rules.push({ network: "tcp,udp", @@ -7450,7 +7463,7 @@ function buildXrayConfig(proxySettings, remark, isFragment, isBalancer, isChain, async function buildXrayBestPingConfig(proxySettings, totalAddresses, chainProxy, outbounds, isFragment) { const remark = isFragment ? "\u{1F4A6} BPB F - Best Ping \u{1F4A5}" : "\u{1F4A6} BPB - Best Ping \u{1F4A5}"; let config = buildXrayConfig(proxySettings, remark, isFragment, true, chainProxy, chainProxy ? "chain-2" : "prox-2"); - config.dns = await buildXrayDNS(proxySettings, totalAddresses, void 0); + config.dns = await buildXrayDNS(proxySettings, totalAddresses, void 0, false, true, false); config.routing.rules = buildXrayRoutingRules(proxySettings, totalAddresses, chainProxy, true, false); config.outbounds.unshift(...outbounds); return config; @@ -7477,7 +7490,7 @@ async function buildXrayBestFragmentConfig(proxySettings, hostName, chainProxy, "100-200" ]; let config = buildXrayConfig(proxySettings, "\u{1F4A6} BPB F - Best Fragment \u{1F60E}", true, true, chainProxy, void 0, false); - config.dns = await buildXrayDNS(proxySettings, [], hostName); + config.dns = await buildXrayDNS(proxySettings, [], hostName, false, true, false); config.routing.rules = buildXrayRoutingRules(proxySettings, [], chainProxy, true, false); const fragment = config.outbounds.shift(); let bestFragOutbounds = []; @@ -7538,7 +7551,7 @@ async function getXrayCustomConfigs(env, proxySettings, hostName, isFragment) { await env.bpb.put("proxySettings", JSON.stringify({ ...proxySettings, outProxy: "", - outProxyParams: "" + outProxyParams: {} })); } } @@ -7617,7 +7630,7 @@ async function getXrayWarpConfigs(proxySettings, warpConfigs, client) { xrayWarpOutbounds.push(proxyOutbound); xrayWoWOutbounds.push(chainOutbound); } - const dnsObject = await buildXrayDNS(proxySettings, outboundDomains, void 0, false, true); + const dnsObject = await buildXrayDNS(proxySettings, outboundDomains, void 0, false, true, true); let xrayWarpBestPing = buildXrayConfig(proxySettings, `\u{1F4A6} Warp${proIndicator}- Best Ping \u{1F680}`, false, true, false, void 0, true); xrayWarpBestPing.dns = dnsObject; xrayWarpBestPing.routing.rules = buildXrayRoutingRules(proxySettings, outboundDomains, false, true, false); @@ -7637,7 +7650,6 @@ async function buildClashDNS(proxySettings, isWarp) { enableIPv6, warpFakeDNS, warpEnableIPv6, - bypassLAN, bypassIran, bypassChina, bypassRussia @@ -7646,6 +7658,12 @@ async function buildClashDNS(proxySettings, isWarp) { let clashLocalDNS = localDNS === "localhost" ? "system" : localDNS; const isFakeDNS = vlessTrojanFakeDNS && !isWarp || warpFakeDNS && isWarp; const isIPv62 = enableIPv6 && !isWarp || warpEnableIPv6 && isWarp; + const isBypass = bypassIran || bypassChina || bypassRussia; + const bypassRules = [ + { rule: bypassIran, geosite: "category-ir" }, + { rule: bypassChina, geosite: "cn" }, + { rule: bypassRussia, geosite: "category-ru" } + ]; let dns = { "enable": true, "listen": "0.0.0.0:1053", @@ -7659,21 +7677,22 @@ async function buildClashDNS(proxySettings, isWarp) { [resolvedRemoteDNS.server]: resolvedRemoteDNS.staticIPs }; } - let geosites = []; - bypassLAN && geosites.push("private"); - bypassIran && geosites.push("category-ir"); - bypassChina && geosites.push("cn"); - bypassRussia && geosites.push("category-ru"); - if (bypassIran || bypassChina || bypassLAN || bypassRussia) { + if (isBypass) { + let geosites = []; + bypassRules.forEach(({ rule, geosite }) => { + rule && geosites.push(geosite); + }); dns["nameserver-policy"] = { [`geosite:${geosites.join(",")}`]: [clashLocalDNS], "www.gstatic.com": [clashLocalDNS] }; } - if (isFakeDNS) { - dns["enhanced-mode"] = "fake-ip"; - dns["fake-ip-range"] = "198.18.0.1/16"; - } + if (isFakeDNS) + Object.assign(dns, { + "enhanced-mode": "fake-ip", + "fake-ip-range": "198.18.0.1/16", + "fake-ip-filter": ["geosite:private"] + }); return dns; } function buildClashRoutingRules(proxySettings) { @@ -7688,18 +7707,33 @@ function buildClashRoutingRules(proxySettings) { blockPorn, blockUDP443 } = proxySettings; + const isBypass = bypassIran || bypassChina || bypassLAN || bypassRussia; + const isBlock = blockAds || blockPorn; + let geositeDirectRules = [], geoipDirectRules = [], geositeBlockRules = []; + const geoRules = [ + { rule: bypassLAN, type: "direct", geosite: "private", geoip: "private" }, + { rule: bypassIran, type: "direct", geosite: "category-ir", geoip: "ir" }, + { rule: bypassChina, type: "direct", geosite: "cn", geoip: "cn" }, + { rule: bypassRussia, type: "direct", geosite: "category-ru", geoip: "ru" }, + { rule: blockAds, type: "block", geosite: "category-ads-all" }, + { rule: blockAds, type: "block", geosite: "category-ads-ir" }, + { rule: blockPorn, type: "block", geosite: "category-porn" } + ]; + if (isBypass || isBlock) { + geoRules.forEach(({ rule, type, geosite, geoip }) => { + if (rule) { + if (type === "direct") { + geositeDirectRules.push(`GEOSITE,${geosite},DIRECT`); + geoipDirectRules.push(`GEOIP,${geoip},DIRECT,no-resolve`); + } else { + geositeBlockRules.push(`GEOSITE,${geosite},REJECT`); + } + } + }); + } localDNS !== "localhost" && rules.push(`AND,((IP-CIDR,${localDNS}/32),(DST-PORT,53)),DIRECT`); - bypassLAN && rules.push("GEOSITE,private,DIRECT"); - bypassIran && rules.push("GEOSITE,category-ir,DIRECT"); - bypassChina && rules.push("GEOSITE,cn,DIRECT"); - bypassRussia && rules.push("GEOSITE,category-ru,DIRECT"); - bypassLAN && rules.push("GEOIP,private,DIRECT,no-resolve"); - bypassIran && rules.push("GEOIP,ir,DIRECT,no-resolve"); - bypassChina && rules.push("GEOIP,cn,DIRECT,no-resolve"); - bypassRussia && rules.push("GEOIP,ru,DIRECT,no-resolve"); + rules.push(...geositeDirectRules, ...geoipDirectRules, ...geositeBlockRules); blockUDP443 && rules.push("AND,((NETWORK,udp),(DST-PORT,443)),REJECT"); - blockAds && rules.push("GEOSITE,category-ads-all,REJECT", "GEOSITE,category-ads-ir,REJECT"); - blockPorn && rules.push("GEOSITE,category-porn,REJECT"); rules.push("MATCH,\u2705 Selector"); return rules; } @@ -7911,7 +7945,7 @@ async function getClashNormalConfig(env, proxySettings, hostName) { await env.bpb.put("proxySettings", JSON.stringify({ ...proxySettings, outProxy: "", - outProxyParams: "" + outProxyParams: {} })); } } @@ -8001,6 +8035,16 @@ function buildSingBoxDNS(proxySettings, isChain, isWarp) { let fakeip; const isFakeDNS = vlessTrojanFakeDNS && !isWarp || warpFakeDNS && isWarp; const isIPv62 = enableIPv6 && !isWarp || warpEnableIPv6 && isWarp; + const geoRules = [ + { rule: bypassIran, type: "direct", ruleSet: "geosite-ir" }, + { rule: bypassChina, type: "direct", ruleSet: "geosite-cn" }, + { rule: bypassRussia, type: "direct", ruleSet: "geosite-category-ru" }, + { rule: true, type: "block", ruleSet: "geosite-malware" }, + { rule: true, type: "block", ruleSet: "geosite-phishing" }, + { rule: true, type: "block", ruleSet: "geosite-cryptominers" }, + { rule: blockAds, type: "block", ruleSet: "geosite-category-ads-all" }, + { rule: blockPorn, type: "block", ruleSet: "geosite-nsfw" } + ]; const servers = [ { address: isWarp ? "1.1.1.1" : remoteDNS, @@ -8024,30 +8068,38 @@ function buildSingBoxDNS(proxySettings, isChain, isWarp) { { outbound: "any", server: "dns-direct" + }, + { + domain: "www.gstatic.com", + server: "dns-direct" + }, + { + clash_mode: "block", + server: "dns-block" + }, + { + clash_mode: "direct", + server: "dns-direct" + }, + { + clash_mode: "global", + server: "dns-remote" } ]; - if (bypassIran || bypassChina || bypassRussia) { - let bypassRules = { - rule_set: [], - server: "dns-direct" - }; - bypassIran && bypassRules.rule_set.push("geosite-ir"); - bypassChina && bypassRules.rule_set.push("geosite-cn"); - bypassRussia && bypassRules.rule_set.push("geosite-category-ru"); - rules.push(bypassRules); - } - let blockRules = { + let bypassRule = { + rule_set: [], + server: "dns-direct" + }; + let blockRule = { disable_cache: true, - rule_set: [ - "geosite-malware", - "geosite-phishing", - "geosite-cryptominers" - ], + rule_set: [], server: "dns-block" }; - blockAds && blockRules.rule_set.push("geosite-category-ads-all"); - blockPorn && blockRules.rule_set.push("geosite-nsfw"); - rules.push(blockRules); + geoRules.forEach(({ rule, type, ruleSet }) => { + rule && type === "direct" && bypassRule.rule_set.push(ruleSet); + rule && type === "block" && blockRule.rule_set.push(ruleSet); + }); + rules.push(bypassRule, blockRule); if (isFakeDNS) { servers.push({ address: "fakeip", @@ -8090,158 +8142,140 @@ function buildSingBoxRoutingRules(proxySettings) { network: "udp", port: 53, outbound: "dns-out" + }, + { + clash_mode: "direct", + outbound: "direct" + }, + { + clash_mode: "block", + outbound: "block" + }, + { + clash_mode: "global", + outbound: "proxy" } ]; - let ruleSet = [ + const geoRules = [ { - type: "remote", - tag: "geosite-malware", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-malware.srs", - download_detour: "direct" + rule: bypassIran, + type: "direct", + ruleSet: { + geosite: "geosite-ir", + geoip: "geoip-ir", + geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-ir.srs", + geoipURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ir.srs" + } }, { - type: "remote", - tag: "geosite-phishing", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-phishing.srs", - download_detour: "direct" + rule: bypassChina, + type: "direct", + ruleSet: { + geosite: "geosite-cn", + geoip: "geoip-cn", + geositeURL: "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs", + geoipURL: "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs" + } }, { - type: "remote", - tag: "geosite-cryptominers", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-cryptominers.srs", - download_detour: "direct" + rule: bypassRussia, + type: "direct", + ruleSet: { + geosite: "geosite-category-ru", + geoip: "geoip-ru", + geositeURL: "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-ru.srs", + geoipURL: "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-ru.srs" + } }, { - type: "remote", - tag: "geoip-malware", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-malware.srs", - download_detour: "direct" + rule: true, + type: "block", + ruleSet: { + geosite: "geosite-malware", + geoip: "geoip-malware", + geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-malware.srs", + geoipURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-malware.srs" + } }, { - type: "remote", - tag: "geoip-phishing", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-phishing.srs", - download_detour: "direct" - } - ]; - if (bypassIran) { - rules.push({ - rule_set: ["geosite-ir", "geoip-ir"], - outbound: "direct" - }); - ruleSet.push( - { - type: "remote", - tag: "geosite-ir", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-ir.srs", - download_detour: "direct" - }, - { - type: "remote", - tag: "geoip-ir", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ir.srs", - download_detour: "direct" + rule: true, + type: "block", + ruleSet: { + geosite: "geosite-phishing", + geoip: "geoip-phishing", + geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-phishing.srs", + geoipURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-phishing.srs" } - ); - } - if (bypassChina) { - rules.push({ - rule_set: ["geosite-cn", "geoip-cn"], - outbound: "direct" - }); - ruleSet.push( - { - type: "remote", - tag: "geosite-cn", - format: "binary", - url: "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs", - download_detour: "direct" - }, - { - type: "remote", - tag: "geoip-cn", - format: "binary", - url: "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs", - download_detour: "direct" + }, + { + rule: true, + type: "block", + ruleSet: { + geosite: "geosite-cryptominers", + geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-cryptominers.srs" } - ); - } - if (bypassRussia) { - rules.push({ - rule_set: ["geosite-category-ru", "geoip-ru"], - outbound: "direct" - }); - ruleSet.push( - { - type: "remote", - tag: "geosite-category-ru", - format: "binary", - url: "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-category-ru.srs", - download_detour: "direct" - }, - { - type: "remote", - tag: "geoip-ru", - format: "binary", - url: "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-ru.srs", - download_detour: "direct" + }, + { + rule: blockAds, + type: "block", + ruleSet: { + geosite: "geosite-category-ads-all", + geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-category-ads-all.srs" } - ); - } + }, + { + rule: blockPorn, + type: "block", + ruleSet: { + geosite: "geosite-nsfw", + geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-nsfw.srs" + } + } + ]; bypassLAN && rules.push({ ip_is_private: true, outbound: "direct" }); + const createRule = (outbound) => ({ + rule_set: [], + outbound + }); + const routingRuleSet = { + type: "remote", + tag: "", + format: "binary", + url: "", + download_detour: "direct" + }; + let geositeDirectRule = createRule("direct"); + let geoipDirectRule = createRule("direct"); + let geositeBlockRule = createRule("block"); + let geoipBlockRule = createRule("block"); + let ruleSets = []; + geoRules.forEach(({ rule, type, ruleSet }) => { + const { geosite, geoip, geositeURL, geoipURL } = ruleSet; + if (rule) { + type === "direct" ? geositeDirectRule.rule_set.push(geosite) : geositeBlockRule.rule_set.push(geosite); + ruleSets.push({ ...routingRuleSet, tag: geosite, url: geositeURL }); + if (geoip) { + type === "direct" ? geoipDirectRule.rule_set.push(geoip) : geoipBlockRule.rule_set.push(geoip); + ruleSets.push({ ...routingRuleSet, tag: geoip, url: geoipURL }); + } + } + }); + rules.push(geositeDirectRule, geoipDirectRule, geositeBlockRule, geoipBlockRule); blockUDP443 && rules.push({ network: "udp", port: 443, protocol: "quic", outbound: "block" }); - let blockRuleSet = { - rule_set: [ - "geosite-malware", - "geosite-phishing", - "geosite-cryptominers", - "geoip-malware", - "geoip-phishing" - ], - outbound: "block" - }; - if (blockAds) { - blockRuleSet.rule_set.push("geosite-category-ads-all"); - ruleSet.push({ - type: "remote", - tag: "geosite-category-ads-all", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-category-ads-all.srs", - download_detour: "direct" - }); - } - if (blockPorn) { - blockRuleSet.rule_set.push("geosite-nsfw"); - ruleSet.push({ - type: "remote", - tag: "geosite-nsfw", - format: "binary", - url: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-nsfw.srs", - download_detour: "direct" - }); - } - rules.push(blockRuleSet); rules.push({ ip_cidr: ["224.0.0.0/3", "ff00::/8"], source_ip_cidr: ["224.0.0.0/3", "ff00::/8"], outbound: "block" }); - return { rules, rule_set: ruleSet }; + return { rules, rule_set: ruleSets }; } function buildSingBoxVLESSOutbound(proxySettings, remark, address, port, host, sni, allowInsecure, isFragment) { const { lengthMin, lengthMax, intervalMin, intervalMax, proxyIP: proxyIP2 } = proxySettings; @@ -8508,9 +8542,8 @@ async function getSingBoxCustomConfig(env, proxySettings, hostName, client, isFr await env.bpb.put("proxySettings", JSON.stringify({ ...proxySettings, outProxy: "", - outProxyParams: "" + outProxyParams: {} })); - throw new Error(error); } } let config = structuredClone(singboxConfigTemp); @@ -8860,7 +8893,7 @@ var singboxConfigTemp = { store_fakeip: true }, clash_api: { - external_controller: "0.0.0.0:9090", + external_controller: "127.0.0.1:9090", external_ui: "yacd", external_ui_download_url: "https://github.com/MetaCubeX/Yacd-meta/archive/gh-pages.zip", external_ui_download_detour: "direct", @@ -8876,6 +8909,7 @@ var clashConfigTemp = { "log-level": "info", "keep-alive-interval": 30, "unified-delay": false, + "external-controller": "127.0.0.1:9090", "dns": {}, "tun": { "enable": true, diff --git a/src/worker.js b/src/worker.js index cd75539e8..02baacc55 100644 --- a/src/worker.js +++ b/src/worker.js @@ -1228,7 +1228,7 @@ async function updateDataset (env, newSettings, resetSettings) { vlessTrojanFakeDNS: validateField('vlessTrojanFakeDNS') ?? currentSettings?.vlessTrojanFakeDNS ?? false, proxyIP: validateField('proxyIP')?.trim() ?? currentSettings?.proxyIP ?? '', outProxy: validateField('outProxy') ?? currentSettings?.outProxy ?? '', - outProxyParams: extractChainProxyParams(validateField('outProxy')) ?? currentSettings?.outProxyParams ?? '', + outProxyParams: extractChainProxyParams(validateField('outProxy')) ?? currentSettings?.outProxyParams ?? {}, cleanIPs: validateField('cleanIPs')?.replaceAll(' ', '') ?? currentSettings?.cleanIPs ?? '', enableIPv6: enableIPv6, customCdnAddrs: validateField('customCdnAddrs')?.replaceAll(' ', '') ?? currentSettings?.customCdnAddrs ?? '', @@ -3079,7 +3079,7 @@ function renderErrorPage (message, error, refer) { function extractChainProxyParams(chainProxy) { let configParams = {}; - if (!chainProxy) return null; + if (!chainProxy) return {}; let url = new URL(chainProxy); const protocol = url.protocol.slice(0, -1); if (protocol === 'vless') { @@ -3198,14 +3198,14 @@ async function buildXrayDNS (proxySettings, outboundAddrs, domainToStaticIPs, is } = proxySettings; const isBypass = bypassIran || bypassChina || bypassRussia; + const isBlock = blockAds || blockPorn; const bypassRules = [ { rule: bypassIran, domain: "geosite:category-ir", ip: "geoip:ir" }, { rule: bypassChina, domain: "geosite:cn", ip: "geoip:cn" }, { rule: bypassRussia, domain: "geosite:category-ru", ip: "geoip:ru" } ]; - const hosts = [ - { rule: true, host: "domain:googleapis.cn", address: ["googleapis.com"] }, + const hostsRules = [ { rule: blockAds, host: "geosite:category-ads-all", address: ["127.0.0.1"] }, { rule: blockAds, host: "geosite:category-ads-ir", address: ["127.0.0.1"] }, { rule: blockPorn, host: "geosite:category-porn", address: ["127.0.0.1"] } @@ -3218,15 +3218,15 @@ async function buildXrayDNS (proxySettings, outboundAddrs, domainToStaticIPs, is const outboundRules = outboundDomains.map(domain => `full:${domain}`); isBalancer && outboundRules.push("full:www.gstatic.com"); const finalRemoteDNS = isWorkerLess - ? ['https://cloudflare-dns.com/dns-query'] + ? ["https://cloudflare-dns.com/dns-query"] : isWarp ? warpEnableIPv6 - ? ['1.1.1.1', '1.0.0.1', '2606:4700:4700::1111', '2606:4700:4700::1001'] - : ['1.1.1.1', '1.0.0.1'] + ? ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] + : ["1.1.1.1", "1.0.0.1"] : [remoteDNS]; const dnsHost = {}; - hosts.forEach( ({ rule, host, address}) => { + isBlock && hostsRules.forEach( ({ rule, host, address}) => { if (rule) dnsHost[host] = address; }); @@ -3234,25 +3234,26 @@ async function buildXrayDNS (proxySettings, outboundAddrs, domainToStaticIPs, is if (staticIPs) dnsHost[domainToStaticIPs] = enableIPv6 ? [...staticIPs.ipv4, ...staticIPs.ipv6] : staticIPs.ipv4; if (resolvedRemoteDNS.server && !isWorkerLess && !isWarp) dnsHost[resolvedRemoteDNS.server] = resolvedRemoteDNS.staticIPs; if (isWorkerLess) { - const domains = ['cloudflare-dns.com', 'cloudflare.com', 'dash.cloudflare.com']; + const domains = ["cloudflare-dns.com", "cloudflare.com", "dash.cloudflare.com"]; const resolved = await Promise.all(domains.map(resolveDNS)); const hostIPv4 = resolved.flatMap(r => r.ipv4); const hostIPv6 = enableIPv6 ? resolved.flatMap(r => r.ipv6) : []; - dnsHost['cloudflare-dns.com'] = [ + dnsHost["cloudflare-dns.com"] = [ ...hostIPv4, ...hostIPv6 ]; } + const hosts = Object.keys(dnsHost).length ? { hosts: dnsHost } : {}; let dnsObject = { - hosts: dnsHost, + ...hosts, servers: finalRemoteDNS, queryStrategy: isIPv6 ? "UseIP" : "UseIPv4", tag: "dns", }; isOutboundRule && dnsObject.servers.push({ - address: localDNS === 'localhost' ? '8.8.8.8' : localDNS, + address: localDNS === 'localhost' ? "8.8.8.8" : localDNS, domains: outboundRules }); @@ -3305,12 +3306,12 @@ function buildXrayRoutingRules (proxySettings, outboundAddrs, isChain, isBalance const isBlock = blockAds || blockPorn; const isBypass = bypassIran || bypassChina || bypassRussia; const geoRules = [ - { rule: bypassLAN, type: "direct", domain: "geosite:private", ip: "geoip:private" }, - { rule: bypassIran, type: "direct", domain: "geosite:category-ir", ip: "geoip:ir" }, - { rule: bypassChina, type: "direct", domain: "geosite:cn", ip: "geoip:cn" }, - { rule: blockAds, type: "block", domain: "geosite:category-ads-all" }, - { rule: blockAds, type: "block", domain: "geosite:category-ads-ir" }, - { rule: blockPorn, type: "block", domain: "geosite:category-porn" } + { rule: bypassLAN, type: 'direct', domain: "geosite:private", ip: "geoip:private" }, + { rule: bypassIran, type: 'direct', domain: "geosite:category-ir", ip: "geoip:ir" }, + { rule: bypassChina, type: 'direct', domain: "geosite:cn", ip: "geoip:cn" }, + { rule: blockAds, type: 'block', domain: "geosite:category-ads-all" }, + { rule: blockAds, type: 'block', domain: "geosite:category-ads-ir" }, + { rule: blockPorn, type: 'block', domain: "geosite:category-porn" } ]; const outboundDomains = outboundAddrs.filter(address => isDomain(address)); const isOutboundRule = outboundDomains.length > 0; @@ -3334,7 +3335,7 @@ function buildXrayRoutingRules (proxySettings, outboundAddrs, isChain, isBalance ]; if (!isWorkerLess && (isOutboundRule || (localDNS !== 'localhost' && isBypass))) rules.push({ - ip: [localDNS === 'localhost' ? '8.8.8.8' : localDNS], + ip: [localDNS === 'localhost' ? "8.8.8.8" : localDNS], port: "53", outboundTag: "direct", type: "field" @@ -3844,7 +3845,7 @@ async function getXrayCustomConfigs(env, proxySettings, hostName, isFragment) { await env.bpb.put("proxySettings", JSON.stringify({ ...proxySettings, outProxy: '', - outProxyParams: '' + outProxyParams: {} })); } } @@ -3961,9 +3962,9 @@ async function buildClashDNS (proxySettings, isWarp) { } = proxySettings; const warpRemoteDNS = warpEnableIPv6 - ? ['1.1.1.1', '1.0.0.1', '2606:4700:4700::1111', '2606:4700:4700::1001'] - : ['1.1.1.1', '1.0.0.1']; - let clashLocalDNS = localDNS === 'localhost' ? 'system' : localDNS; + ? ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] + : ["1.1.1.1", "1.0.0.1"]; + let clashLocalDNS = localDNS === 'localhost' ? "system" : localDNS; const isFakeDNS = (vlessTrojanFakeDNS && !isWarp) || (warpFakeDNS && isWarp); const isIPv6 = (enableIPv6 && !isWarp) || (warpEnableIPv6 && isWarp); const isBypass = bypassIran || bypassChina || bypassRussia; @@ -3983,7 +3984,7 @@ async function buildClashDNS (proxySettings, isWarp) { }; if (resolvedRemoteDNS.server && !isWarp) { - dns['hosts'] = { + dns["hosts"] = { [resolvedRemoteDNS.server]: resolvedRemoteDNS.staticIPs }; } @@ -3994,16 +3995,17 @@ async function buildClashDNS (proxySettings, isWarp) { rule && geosites.push(geosite) }); - dns['nameserver-policy'] = { + dns["nameserver-policy"] = { [`geosite:${geosites.join(',')}`]: [clashLocalDNS], - 'www.gstatic.com': [clashLocalDNS] + "www.gstatic.com": [clashLocalDNS] }; } - if (isFakeDNS) { - dns["enhanced-mode"] = "fake-ip"; - dns["fake-ip-range"] = "198.18.0.1/16"; - } + if (isFakeDNS) Object.assign(dns, { + "enhanced-mode": "fake-ip", + "fake-ip-range": "198.18.0.1/16", + "fake-ip-filter": ["geosite:private"] + }); return dns; } @@ -4025,13 +4027,13 @@ function buildClashRoutingRules (proxySettings) { const isBlock = blockAds || blockPorn; let geositeDirectRules = [], geoipDirectRules = [], geositeBlockRules = []; const geoRules = [ - { rule: bypassLAN, type: "direct", geosite: "private", geoip: "private" }, - { rule: bypassIran, type: "direct", geosite: "category-ir", geoip: "ir" }, - { rule: bypassChina, type: "direct", geosite: "cn", geoip: "cn" }, - { rule: bypassRussia, type: "direct", geosite: "category-ru", geoip: "ru" }, - { rule: blockAds, type: "block", geosite: "category-ads-all" }, - { rule: blockAds, type: "block", geosite: "category-ads-ir" }, - { rule: blockPorn, type: "block", geosite: "category-porn" } + { rule: bypassLAN, type: 'direct', geosite: "private", geoip: "private" }, + { rule: bypassIran, type: 'direct', geosite: "category-ir", geoip: "ir" }, + { rule: bypassChina, type: 'direct', geosite: "cn", geoip: "cn" }, + { rule: bypassRussia, type: 'direct', geosite: "category-ru", geoip: "ru" }, + { rule: blockAds, type: 'block', geosite: "category-ads-all" }, + { rule: blockAds, type: 'block', geosite: "category-ads-ir" }, + { rule: blockPorn, type: 'block', geosite: "category-porn" } ]; if (isBypass || isBlock) { @@ -4049,8 +4051,8 @@ function buildClashRoutingRules (proxySettings) { localDNS !== 'localhost' && rules.push(`AND,((IP-CIDR,${localDNS}/32),(DST-PORT,53)),DIRECT`); rules.push(...geositeDirectRules, ...geoipDirectRules, ...geositeBlockRules); - blockUDP443 && rules.push('AND,((NETWORK,udp),(DST-PORT,443)),REJECT'); - rules.push('MATCH,✅ Selector'); + blockUDP443 && rules.push("AND,((NETWORK,udp),(DST-PORT,443)),REJECT"); + rules.push("MATCH,✅ Selector"); return rules; } @@ -4279,7 +4281,7 @@ async function getClashNormalConfig (env, proxySettings, hostName) { await env.bpb.put("proxySettings", JSON.stringify({ ...proxySettings, outProxy: '', - outProxyParams: '' + outProxyParams: {} })); } } @@ -4379,25 +4381,25 @@ function buildSingBoxDNS (proxySettings, isChain, isWarp) { const isFakeDNS = (vlessTrojanFakeDNS && !isWarp) || (warpFakeDNS && isWarp); const isIPv6 = (enableIPv6 && !isWarp) || (warpEnableIPv6 && isWarp); const geoRules = [ - { rule: bypassIran, type: "direct", ruleSet: "geosite-ir" }, - { rule: bypassChina, type: "direct", ruleSet: "geosite-cn" }, - { rule: bypassRussia, type: "direct", ruleSet: "geosite-category-ru" }, - { rule: true, type: "block", ruleSet: "geosite-malware" }, - { rule: true, type: "block", ruleSet: "geosite-phishing" }, - { rule: true, type: "block", ruleSet: "geosite-cryptominers" }, - { rule: blockAds, type: "block", ruleSet: "geosite-category-ads-all" }, - { rule: blockPorn, type: "block", ruleSet: "geosite-nsfw" } + { rule: bypassIran, type: 'direct', ruleSet: "geosite-ir" }, + { rule: bypassChina, type: 'direct', ruleSet: "geosite-cn" }, + { rule: bypassRussia, type: 'direct', ruleSet: "geosite-category-ru" }, + { rule: true, type: 'block', ruleSet: "geosite-malware" }, + { rule: true, type: 'block', ruleSet: "geosite-phishing" }, + { rule: true, type: 'block', ruleSet: "geosite-cryptominers" }, + { rule: blockAds, type: 'block', ruleSet: "geosite-category-ads-all" }, + { rule: blockPorn, type: 'block', ruleSet: "geosite-nsfw" } ]; const servers = [ { - address: isWarp ? '1.1.1.1' : remoteDNS, + address: isWarp ? "1.1.1.1" : remoteDNS, address_resolver: "dns-direct", strategy: isIPv6 ? "prefer_ipv4" : "ipv4_only", detour: isChain ? 'proxy-1' : "proxy", tag: "dns-remote" }, { - address: localDNS === 'localhost' ? 'local' : localDNS, + address: localDNS === 'localhost' ? "local" : localDNS, strategy: isIPv6 ? "prefer_ipv4" : "ipv4_only", detour: "direct", tag: "dns-direct" @@ -4513,7 +4515,7 @@ function buildSingBoxRoutingRules (proxySettings) { const geoRules = [ { rule: bypassIran, - type: "direct", + type: 'direct', ruleSet: { geosite: "geosite-ir", geoip: "geoip-ir", @@ -4523,7 +4525,7 @@ function buildSingBoxRoutingRules (proxySettings) { }, { rule: bypassChina, - type: "direct", + type: 'direct', ruleSet: { geosite: "geosite-cn", geoip: "geoip-cn", @@ -4533,7 +4535,7 @@ function buildSingBoxRoutingRules (proxySettings) { }, { rule: bypassRussia, - type: "direct", + type: 'direct', ruleSet: { geosite: "geosite-category-ru", geoip: "geoip-ru", @@ -4543,7 +4545,7 @@ function buildSingBoxRoutingRules (proxySettings) { }, { rule: true, - type: "block", + type: 'block', ruleSet: { geosite: "geosite-malware", geoip: "geoip-malware", @@ -4553,7 +4555,7 @@ function buildSingBoxRoutingRules (proxySettings) { }, { rule: true, - type: "block", + type: 'block', ruleSet: { geosite: "geosite-phishing", geoip: "geoip-phishing", @@ -4563,7 +4565,7 @@ function buildSingBoxRoutingRules (proxySettings) { }, { rule: true, - type: "block", + type: 'block', ruleSet: { geosite: "geosite-cryptominers", geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-cryptominers.srs", @@ -4571,7 +4573,7 @@ function buildSingBoxRoutingRules (proxySettings) { }, { rule: blockAds, - type: "block", + type: 'block', ruleSet: { geosite: "geosite-category-ads-all", geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-category-ads-all.srs", @@ -4579,7 +4581,7 @@ function buildSingBoxRoutingRules (proxySettings) { }, { rule: blockPorn, - type: "block", + type: 'block', ruleSet: { geosite: "geosite-nsfw", geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-nsfw.srs", @@ -4605,10 +4607,10 @@ function buildSingBoxRoutingRules (proxySettings) { download_detour: "direct" }; - let geositeDirectRule = createRule("direct"); - let geoipDirectRule = createRule("direct"); - let geositeBlockRule = createRule("block"); - let geoipBlockRule = createRule("block"); + let geositeDirectRule = createRule('direct'); + let geoipDirectRule = createRule('direct'); + let geositeBlockRule = createRule('block'); + let geoipBlockRule = createRule('block'); let ruleSets = []; geoRules.forEach(({ rule, type, ruleSet }) => { @@ -4930,9 +4932,8 @@ async function getSingBoxCustomConfig(env, proxySettings, hostName, client, isFr await env.bpb.put("proxySettings", JSON.stringify({ ...proxySettings, outProxy: '', - outProxyParams: '' + outProxyParams: {} })); - throw new Error(error); } } @@ -5319,6 +5320,7 @@ const clashConfigTemp = { "log-level": "info", "keep-alive-interval": 30, "unified-delay": false, + "external-controller": "127.0.0.1:9090", "dns": {}, "tun": { "enable": true,