Skip to content

Commit

Permalink
Added custom routing rules.
Browse files Browse the repository at this point in the history
  • Loading branch information
bia-pain-bache committed Nov 14, 2024
1 parent 4ef63fb commit ffbe3ef
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 80 deletions.
57 changes: 48 additions & 9 deletions src/cores-configs/clash.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getConfigAddresses, extractWireguardParams, generateRemark, randomUpperCase, getRandomPath, isIPv6 } from './helpers';
import { getConfigAddresses, extractWireguardParams, generateRemark, randomUpperCase, getRandomPath, isIPv6, isIPv4 } from './helpers';
import { initializeParams, userID, trojanPassword, hostName, defaultHttpsPorts } from "../helpers/init";
import { getDataset } from '../kv/handlers';
import { renderErrorPage } from '../pages/error';
Expand All @@ -15,14 +15,17 @@ async function buildClashDNS (proxySettings, isChain, isWarp) {
warpEnableIPv6,
bypassIran,
bypassChina,
bypassRussia
bypassRussia,
customBypassRules,
customBlockRules
} = 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"];
const isFakeDNS = (vlessTrojanFakeDNS && !isWarp) || (warpFakeDNS && isWarp);
const isIPv6 = (enableIPv6 && !isWarp) || (warpEnableIPv6 && isWarp);
const customBypassRulesDomains = customBypassRules.split(',').filter(address => isDomain(address));
const isBypass = bypassIran || bypassChina || bypassRussia;
const bypassRules = [
{ rule: bypassIran, geosite: "ir" },
Expand Down Expand Up @@ -62,6 +65,13 @@ async function buildClashDNS (proxySettings, isChain, isWarp) {
};
}

customBypassRulesDomains.forEach( domain => {
dns["nameserver-policy"] = {
...dns["nameserver-policy"],
[`+.${domain}`]: [`${localDNS}#DIRECT`]
};
});

if (isFakeDNS) Object.assign(dns, {
"enhanced-mode": "fake-ip",
"fake-ip-range": "198.18.0.1/16",
Expand All @@ -79,17 +89,22 @@ function buildClashRoutingRules (proxySettings) {
bypassRussia,
blockAds,
blockPorn,
blockUDP443
blockUDP443,
customBypassRules,
customBlockRules
} = proxySettings;

const customBypassRulesTotal = customBypassRules ? customBypassRules.split(',') : [];
const customBlockRulesTotal = customBlockRules ? customBlockRules.split(',') : [];
const geoRules = [
{
rule: bypassLAN,
type: 'direct',
noResolve: true,
ruleProvider: {
format: "yaml",
geosite: "private",
geoip: "private-cidr",
geoip: "private-cidr",
geositeURL: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/private.yaml",
geoipURL: "https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geoip/private.yaml"
}
Expand All @@ -100,7 +115,7 @@ function buildClashRoutingRules (proxySettings) {
ruleProvider: {
format: "text",
geosite: "ir",
geoip: "ir-cidr",
geoip: "ir-cidr",
geositeURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-clash-rules/release/ir.txt",
geoipURL: "https://raw.githubusercontent.com/Chocolate4U/Iran-clash-rules/release/ircidr.txt"
}
Expand Down Expand Up @@ -188,24 +203,48 @@ function buildClashRoutingRules (proxySettings) {
}
}

const rules = [], ruleProviders = {};
geoRules.forEach( ({ rule, type, ruleProvider }) => {
const directDomainRules = [], directIPRules = [], blockDomainRules = [], blockIPRules = [], ruleProviders = {};
geoRules.forEach( ({ rule, type, ruleProvider, noResolve }) => {
const { geosite, geoip, geositeURL, geoipURL, format } = ruleProvider;
if (rule) {
if (geosite) {
rules.push(`RULE-SET,${geosite},${type === 'direct' ? 'DIRECT' : 'REJECT'}`);
const targetRules = type === 'direct' ? directDomainRules : blockDomainRules;
targetRules.push(`RULE-SET,${geosite},${type === 'direct' ? 'DIRECT' : 'REJECT'}`);
const ruleProvider = buildRuleProvider(geosite, format, 'domain', geositeURL);
Object.assign(ruleProviders, ruleProvider);
}

if (geoip) {
rules.push(`RULE-SET,${geoip},${type === 'direct' ? 'DIRECT' : 'REJECT'}`);
const targetRules = type === 'direct' ? directIPRules : blockIPRules;
targetRules.push(`RULE-SET,${geoip},${type === 'direct' ? 'DIRECT' : 'REJECT'}${noResolve ? ',no-resolve' : ''}`);
const ruleProvider = buildRuleProvider(geoip, format, 'ipcidr', geoipURL);
Object.assign(ruleProviders, ruleProvider);
}
}
});

const generateRule = (address, action) => {
if (isDomain(address)) {
return `DOMAIN-SUFFIX,${address},${action}`;
} else {
const type = isIPv4(address) ? 'IP-CIDR' : 'IP-CIDR6';
const ip = isIPv6(address) ? address.replace(/\[|\]/g, '') : address;
const cidr = address.includes('/') ? '' : isIPv4(address) ? '/32' : '/128';
return `${type},${ip}${cidr},${action},no-resolve`;
}
};

[...customBypassRulesTotal, ...customBlockRulesTotal].forEach((address, index) => {
const isDirectRule = index < customBypassRulesTotal.length;
const action = isDirectRule ? 'DIRECT' : 'REJECT';
const targetRules = isDirectRule
? isDomain(address) ? directDomainRules : directIPRules
: isDomain(address) ? blockDomainRules : blockIPRules;

targetRules.push(generateRule(address, action));
});

const rules = [...directDomainRules, ...directIPRules, ...blockDomainRules, ...blockIPRules];
blockUDP443 && rules.push("AND,((NETWORK,udp),(DST-PORT,443)),REJECT");
rules.push("MATCH,✅ Selector");
return { rules, ruleProviders };
Expand Down
4 changes: 2 additions & 2 deletions src/cores-configs/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ export function base64ToDecimal (base64) {
}

export function isIPv4(address) {
const ipv4Pattern = /^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const ipv4Pattern = /^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/([0-9]|[1-2][0-9]|3[0-2]))?$/;
return ipv4Pattern.test(address);
}

export function isIPv6(address) {
const ipv6Pattern = /^\[(?:(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}|(?:[a-fA-F0-9]{1,4}:){1,7}:|::(?:[a-fA-F0-9]{1,4}:){0,7}|(?:[a-fA-F0-9]{1,4}:){1,6}:[a-fA-F0-9]{1,4}|(?:[a-fA-F0-9]{1,4}:){1,5}(?::[a-fA-F0-9]{1,4}){1,2}|(?:[a-fA-F0-9]{1,4}:){1,4}(?::[a-fA-F0-9]{1,4}){1,3}|(?:[a-fA-F0-9]{1,4}:){1,3}(?::[a-fA-F0-9]{1,4}){1,4}|(?:[a-fA-F0-9]{1,4}:){1,2}(?::[a-fA-F0-9]{1,4}){1,5}|[a-fA-F0-9]{1,4}:(?::[a-fA-F0-9]{1,4}){1,6})\]$/;
const ipv6Pattern = /^\[(?:(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}|(?:[a-fA-F0-9]{1,4}:){1,7}:|::(?:[a-fA-F0-9]{1,4}:){0,7}|(?:[a-fA-F0-9]{1,4}:){1,6}:[a-fA-F0-9]{1,4}|(?:[a-fA-F0-9]{1,4}:){1,5}(?::[a-fA-F0-9]{1,4}){1,2}|(?:[a-fA-F0-9]{1,4}:){1,4}(?::[a-fA-F0-9]{1,4}){1,3}|(?:[a-fA-F0-9]{1,4}:){1,3}(?::[a-fA-F0-9]{1,4}){1,4}|(?:[a-fA-F0-9]{1,4}:){1,2}(?::[a-fA-F0-9]{1,4}){1,5}|[a-fA-F0-9]{1,4}:(?::[a-fA-F0-9]{1,4}){1,6})\](?:\/(1[0-1][0-9]|12[0-8]|[0-9]?[0-9]))?$/;
return ipv6Pattern.test(address);
}
110 changes: 85 additions & 25 deletions src/cores-configs/sing-box.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getConfigAddresses, extractWireguardParams, generateRemark, randomUpperCase, getRandomPath } from './helpers';
import { getConfigAddresses, extractWireguardParams, generateRemark, randomUpperCase, getRandomPath, isIPv6 } from './helpers';
import { initializeParams, userID, trojanPassword, hostName, defaultHttpsPorts } from "../helpers/init";
import { renderErrorPage } from '../pages/error';
import { getDataset } from '../kv/handlers';
Expand All @@ -16,12 +16,16 @@ function buildSingBoxDNS (proxySettings, outboundAddrs, isWarp, remoteDNSDetour)
bypassChina,
bypassRussia,
blockAds,
blockPorn
blockPorn,
customBypassRules,
customBlockRules
} = proxySettings;

let fakeip;
const isFakeDNS = (vlessTrojanFakeDNS && !isWarp) || (warpFakeDNS && isWarp);
const isIPv6 = (enableIPv6 && !isWarp) || (warpEnableIPv6 && isWarp);
const customBypassRulesDomains = customBypassRules.split(',').filter(address => isDomain(address));
const customBlockRulesDomains = customBlockRules.split(',').filter(address => isDomain(address));
const geoRules = [
{ rule: bypassIran, type: 'direct', geosite: "geosite-ir", geoip: "geoip-ir" },
{ rule: bypassChina, type: 'direct', geosite: "geosite-cn", geoip: "geoip-cn" },
Expand Down Expand Up @@ -100,6 +104,30 @@ function buildSingBoxDNS (proxySettings, outboundAddrs, isWarp, remoteDNSDetour)
});

rules.push(blockRule);
const createRule = (server) => ({
domain_suffix: [],
server
});

let domainDirectRule, domainBlockRule;
if (customBypassRulesDomains.length) {
domainDirectRule = createRule("dns-direct");
customBypassRulesDomains.forEach( domain => {
domainDirectRule.domain_suffix.push(domain);
});

rules.push(domainDirectRule);
}

if (customBlockRulesDomains.length) {
domainBlockRule = createRule("dns-block");
customBlockRulesDomains.forEach( domain => {
domainBlockRule.domain_suffix.push(domain);
});

rules.push(domainBlockRule);
}

if (isFakeDNS) {
servers.push({
address: "fakeip",
Expand Down Expand Up @@ -135,11 +163,14 @@ function buildSingBoxRoutingRules (proxySettings) {
bypassRussia,
blockAds,
blockPorn,
blockUDP443
blockUDP443,
customBypassRules,
customBlockRules
} = proxySettings;

const isBypass = bypassIran || bypassChina || bypassRussia;
const rules = [
const customBypassRulesTotal = customBypassRules ? customBypassRules.split(',') : [];
const customBlockRulesTotal = customBlockRules ? customBlockRules.split(',') : [];
const defaultRules = [
{
type: "logical",
mode: "or",
Expand Down Expand Up @@ -241,13 +272,14 @@ function buildSingBoxRoutingRules (proxySettings) {
},
];

bypassLAN && rules.push({
const directDomainRules = [], directIPRules = [], blockDomainRules = [], blockIPRules = [], ruleSets = [];
bypassLAN && directIPRules.push({
ip_is_private: true,
outbound: "direct"
});

const createRule = (outbound) => ({
rule_set: [],
const createRule = (rule, outbound) => ({
[rule]: [],
outbound
});

Expand All @@ -259,36 +291,64 @@ function buildSingBoxRoutingRules (proxySettings) {
download_detour: "direct"
};

const directRule = createRule('direct');;
const blockRule = createRule('block');
const ruleSets = [];
const directDomainRule = createRule('rule_set', 'direct');;
const directIPRule = createRule('rule_set', 'direct');;
const blockDomainRule = createRule('rule_set', 'block');
const blockIPRule = createRule('rule_set', 'block');

geoRules.forEach(({ rule, type, ruleSet }) => {
if (!rule) return;
const { geosite, geoip, geositeURL, geoipURL } = ruleSet;
if (rule) {
if (type === 'direct') {
directRule.rule_set.unshift(geosite);
directRule.rule_set.push(geoip);
} else {
blockRule.rule_set.unshift(geosite);
geoip && blockRule.rule_set.push(geoip);
}
ruleSets.push({...routingRuleSet, tag: geosite, url: geositeURL});
geoip && ruleSets.push({...routingRuleSet, tag: geoip, url: geoipURL});
}
const isDirect = type === 'direct';
const domainRule = isDirect ? directDomainRule : blockDomainRule;
const ipRule = isDirect ? directIPRule : blockIPRule;

domainRule.rule_set.push(geosite);
ruleSets.push({ ...routingRuleSet, tag: geosite, url: geositeURL });
if (geoip) {
ipRule.rule_set.push(geoip);
ruleSets.push({ ...routingRuleSet, tag: geoip, url: geoipURL });
}
});

isBypass && rules.push(directRule);
rules.push(blockRule);
const pushRuleIfNotEmpty = (rule, targetArray) => {
if (rule.rule_set?.length || rule.domain_suffix?.length || rule.ip_cidr?.length) {
targetArray.push(rule);
}
};

pushRuleIfNotEmpty(directDomainRule, directDomainRules);
pushRuleIfNotEmpty(directIPRule, directIPRules);
pushRuleIfNotEmpty(blockDomainRule, blockDomainRules);
pushRuleIfNotEmpty(blockIPRule, blockIPRules);

const processRules = (addresses, action) => {
const domainRule = createRule('domain_suffix', action);
const ipRule = createRule('ip_cidr', action);
addresses.forEach(address => {
if (isDomain(address)) {
domainRule.domain_suffix.push(address);
} else {
const ip = isIPv6(address) ? address.replace(/\[|\]/g, '') : address;
ipRule.ip_cidr.push(ip);
}
});

pushRuleIfNotEmpty(domainRule, action === 'direct' ? directDomainRules : blockDomainRules);
pushRuleIfNotEmpty(ipRule, action === 'direct' ? directIPRules : blockIPRules);
};

customBypassRulesTotal.length && processRules(customBypassRulesTotal, 'direct');
customBlockRulesTotal.length && processRules(customBlockRulesTotal, 'block');
const rules = [...defaultRules, ...directDomainRules, ...directIPRules, ...blockDomainRules, ...blockIPRules];
blockUDP443 && rules.push({
network: "udp",
port: 443,
protocol: "quic",
outbound: "block"
});

return {rules: rules, rule_set: ruleSets};
return {rules, rule_set: ruleSets};
}

function buildSingBoxVLESSOutbound (proxySettings, remark, address, port, host, sni, allowInsecure, isFragment) {
Expand Down
Loading

0 comments on commit ffbe3ef

Please sign in to comment.