Quickly search for matching IP network segments, support for any number of proxy configurations.
This commit is contained in:
parent
fef35b21af
commit
7d686975b9
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,7 +1,4 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
china.txt
|
auto-proxy*.txt
|
||||||
china6.txt
|
*-rules-*.txt
|
||||||
gfwlist.txt
|
|
||||||
auto-proxy.txt
|
|
||||||
domain-rules-*.txt
|
|
||||||
proxy.pac
|
proxy.pac
|
||||||
|
40
README.md
40
README.md
@ -8,25 +8,46 @@
|
|||||||
|
|
||||||
项目包含一些示例配置文件:
|
项目包含一些示例配置文件:
|
||||||
|
|
||||||
|
- `auto-proxy.txt.example`
|
||||||
- `domain-rules-blocked.txt.example`
|
- `domain-rules-blocked.txt.example`
|
||||||
- `domain-rules-direct.txt.example`
|
- `domain-rules-direct.txt.example`
|
||||||
- `domain-rules-proxy.txt.example`
|
- `domain-rules-proxy.txt.example`
|
||||||
|
- `ipv4-rules-direct.txt.example`
|
||||||
|
- `ipv6-rules-direct.txt.example`
|
||||||
|
|
||||||
要使用这些文件,去掉 `.example` 扩展名。每个文件代表不同的代理行为:
|
要使用这些文件,去掉 `.example` 扩展名。每个文件代表不同的代理行为:
|
||||||
|
|
||||||
|
- **Auto-Proxy 配置的规则**:把规则添加到 `auto-proxy.txt` 中,将会按照规则来访问网站。
|
||||||
|
|
||||||
|
所有以 `auto-proxy` 开头,并且以 `.txt` 结尾的文件都按 Auto-Proxy 规则来解析。如果你有多个 Auto-Proxy 规则,可以保存为多个文件,例如 `auto-proxy-1.txt`、`auto-proxy-2.txt` 等等。
|
||||||
|
|
||||||
|
**注意**:当前,会忽略 Auto-Proxy 中的 URL 的匹配规则,仅仅处理域名规则。
|
||||||
|
|
||||||
- **Blocked**:添加到 `domain-rules-blocked.txt` 中的域名将被阻止访问。
|
- **Blocked**:添加到 `domain-rules-blocked.txt` 中的域名将被阻止访问。
|
||||||
- **Direct**:添加到 `domain-rules-direct.txt` 中的域名将绕过代理,直接连接。
|
- **Direct**:
|
||||||
|
- 添加到 `domain-rules-direct.txt` 中的域名将绕过代理,直接连接。
|
||||||
|
- 添加到 `ipv4-rules-direct.txt` 中的 IPv4 网络段(CIDR格式)将绕过代理,直接连接。
|
||||||
|
- 添加到 `ipv6-rules-direct.txt` 中的 IPv6 网络段(CIDR格式)将绕过代理,直接连接。
|
||||||
- **Proxy**:添加到 `domain-rules-proxy.txt` 中的域名将使用默认代理。
|
- **Proxy**:添加到 `domain-rules-proxy.txt` 中的域名将使用默认代理。
|
||||||
|
|
||||||
将你的域名添加到合适的文件中,每个域名一行。以 `#` 开头的行被视为注释。例如:
|
将你的域名或者IP网络段添加到合适的文件中,每个域名一行。以 `#` 开头的行被视为注释。例如:
|
||||||
|
|
||||||
|
文件 domain-rules-direct.txt 中添加的域名将会绕过代理直接连接
|
||||||
```
|
```
|
||||||
# 直连域名
|
# 直连域名
|
||||||
google.com
|
google.com
|
||||||
example.org
|
example.org
|
||||||
```
|
```
|
||||||
|
|
||||||
你也可以创建自己的自定义规则文件,文件名应遵循 `domain-rules-<rule_name>.txt` 的格式。例如,`domain-rules-companyProxy.txt` 将使该文件中的所有域名使用 `proxy.pac` 中定义的 `companyProxy` 设置。
|
文件 ipv4-rules-direct.txt 中添加的网络段将会绕过代理直接连接
|
||||||
|
```
|
||||||
|
# 直连的 IPv4 网络段
|
||||||
|
192.168.0.0/16
|
||||||
|
114.114.114.114/32 # 如果要添加一个特定 IP 地址,请追加 /32 到 IP 地址后面
|
||||||
|
```
|
||||||
|
|
||||||
|
你也可以创建自己的自定义规则文件,文件名应遵循 `<domain|ipv4|ipv6>-rules-<rule_name>.txt` 的格式。例如,`domain-rules-companyProxy.txt` 将使该文件中的所有域名使用 `proxy.pac` 中定义的 `companyProxy` 设置。`ipv4-rules-block.txt` 将不可访问文件中的所有网络段。
|
||||||
|
|
||||||
|
|
||||||
2. **生成 `proxy.pac` 文件**
|
2. **生成 `proxy.pac` 文件**
|
||||||
|
|
||||||
@ -38,7 +59,16 @@
|
|||||||
|
|
||||||
在项目根目录中会自动生成 `proxy.pac` 文件。
|
在项目根目录中会自动生成 `proxy.pac` 文件。
|
||||||
|
|
||||||
3. **代理配置**
|
3. **默认的规则来源**
|
||||||
|
构建脚本 [`build.sh`](./build.sh) 默认会下载以下文件,但不会覆盖已有的同名文件:
|
||||||
|
|
||||||
|
- `auto-proxy.txt`
|
||||||
|
- `ipv4-rules-direct.txt`
|
||||||
|
- `ipv6-rules-direct.txt`
|
||||||
|
|
||||||
|
如果你不需要 Auto-Proxy 的规则或者 IP 网络段的规则,请创建同名的空文件即可忽略下载。
|
||||||
|
|
||||||
|
4. **代理配置**
|
||||||
|
|
||||||
生成的 `proxy.pac` 文件使用以下默认的代理配置(注意默认代理服务器是 `SOCKS5 127.0.0.1:1080`):
|
生成的 `proxy.pac` 文件使用以下默认的代理配置(注意默认代理服务器是 `SOCKS5 127.0.0.1:1080`):
|
||||||
|
|
||||||
@ -54,7 +84,7 @@
|
|||||||
|
|
||||||
你可以在生成 `proxy.pac` 后修改这些值,或者直接在原始脚本 `proxy.js` 中进行自定义,以便使用不同的默认设置。请根据实际环境和需求调整这些代理设置。
|
你可以在生成 `proxy.pac` 后修改这些值,或者直接在原始脚本 `proxy.js` 中进行自定义,以便使用不同的默认设置。请根据实际环境和需求调整这些代理设置。
|
||||||
|
|
||||||
4. **测试**
|
5. **测试**
|
||||||
|
|
||||||
如果安装了 Node.js,可以使用以下命令运行测试以验证配置:
|
如果安装了 Node.js,可以使用以下命令运行测试以验证配置:
|
||||||
|
|
||||||
|
1
auto-proxy.txt.example
Normal file
1
auto-proxy.txt.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
@@||example.com
|
213
build.sh
213
build.sh
@ -13,39 +13,44 @@ fi
|
|||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
declare -A files
|
declare -A files
|
||||||
files[gfwlist.txt]=https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt
|
files[auto-proxy.txt]=https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt
|
||||||
files[china.txt]=https://gaoyifan.github.io/china-operator-ip/china.txt
|
files[ipv4-rules-direct.txt]=https://gaoyifan.github.io/china-operator-ip/china.txt
|
||||||
files[china6.txt]=https://gaoyifan.github.io/china-operator-ip/china6.txt
|
files[ipv6-rules-direct.txt]=https://gaoyifan.github.io/china-operator-ip/china6.txt
|
||||||
|
|
||||||
|
declare -a files_to_be_deleted=()
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
local retval="$?"
|
local retval="$?"
|
||||||
if [[ -n "${del_file_on_exit-}" && -f "${del_file_on_exit}" ]]; then
|
local file
|
||||||
echo "Remove ${del_file_on_exit}"
|
for file in "${files_to_be_deleted[@]}"; do
|
||||||
rm "${del_file_on_exit}"
|
if [[ -f "${file}" ]]; then
|
||||||
exit "$retval"
|
echo "Remove $file"
|
||||||
fi
|
rm -v "${file}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit "$retval"
|
||||||
} >&2
|
} >&2
|
||||||
trap cleanup EXIT ERR SIGINT
|
trap cleanup EXIT ERR SIGINT
|
||||||
|
|
||||||
for f in "${!files[@]}"; do
|
for f in "${!files[@]}"; do
|
||||||
del_file_on_exit="$f"
|
|
||||||
url="${files[$f]}"
|
url="${files[$f]}"
|
||||||
if [[ ! -f "$f" ]]; then
|
if [[ ! -f "$f" ]]; then
|
||||||
|
files_to_be_deleted=("$f" "${f}.tmp")
|
||||||
if command -v wget &>/dev/null; then
|
if command -v wget &>/dev/null; then
|
||||||
wget -O "$f" "$url"
|
wget -O "${f}.tmp" "$url"
|
||||||
elif command -v curl &>/dev/null; then
|
elif command -v curl &>/dev/null; then
|
||||||
curl --output "$f" "$url"
|
curl --output "${f}.tmp" "$url"
|
||||||
else
|
else
|
||||||
echo "Error: please install wget or curl."
|
echo "Error: please install wget or curl."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [[ "$f" = auto-proxy.txt ]]; then
|
||||||
|
base64 -d <"${f}.tmp" > "$f"
|
||||||
|
else
|
||||||
|
mv "${f}.tmp" "$f"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
files_to_be_deleted=()
|
||||||
del_file_on_exit=auto-proxy.txt
|
|
||||||
[ auto-proxy.txt -nt gfwlist.txt ] || base64 -d <gfwlist.txt >auto-proxy.txt
|
|
||||||
|
|
||||||
del_file_on_exit=
|
|
||||||
|
|
||||||
domain_segments() {
|
domain_segments() {
|
||||||
local domain="$1"
|
local domain="$1"
|
||||||
@ -65,18 +70,6 @@ if [[ "$actual" = "$expected" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi >&2
|
fi >&2
|
||||||
|
|
||||||
ipv6_to_array_format() {
|
|
||||||
while IFS= read -r line; do
|
|
||||||
local ipv6="${line%%/*}"
|
|
||||||
local prefix="${line##*/}"
|
|
||||||
|
|
||||||
local expanded_ipv6=$(expand_ipv6 "$ipv6")
|
|
||||||
local full_hex="${expanded_ipv6//:/}"
|
|
||||||
|
|
||||||
echo " [0x${full_hex:0:16}n, 0x${full_hex:16:16}n, ${prefix}], // ${line}"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
expand_ipv6() {
|
expand_ipv6() {
|
||||||
local ipv6="$1"
|
local ipv6="$1"
|
||||||
local full_ipv6=""
|
local full_ipv6=""
|
||||||
@ -110,9 +103,7 @@ generate_pac() {
|
|||||||
|
|
||||||
declare -A domain_rules
|
declare -A domain_rules
|
||||||
local file rule domain
|
local file rule domain
|
||||||
for file in *.txt.example; do
|
|
||||||
[ -f "${file%.example}" ] || cp -v "${file}" "${file%.example}"
|
|
||||||
done >&2
|
|
||||||
for file in domain-rules-*.txt; do
|
for file in domain-rules-*.txt; do
|
||||||
rule="${file#domain-rules-}"
|
rule="${file#domain-rules-}"
|
||||||
rule="${rule%.txt}"
|
rule="${rule%.txt}"
|
||||||
@ -123,62 +114,63 @@ generate_pac() {
|
|||||||
domain_rules["$domain"]="$rule";
|
domain_rules["$domain"]="$rule";
|
||||||
done < "$file"
|
done < "$file"
|
||||||
done
|
done
|
||||||
|
for file in auto-proxy*.txt; do
|
||||||
local line item rule parent_rule
|
local line item rule parent_rule
|
||||||
while read -r line; do
|
while read -r line; do
|
||||||
echo "$line" >&2
|
echo "$line" >&2
|
||||||
rule=
|
rule=
|
||||||
case "$line" in
|
case "$line" in
|
||||||
(/*)
|
(/*)
|
||||||
echo "Skip regrex rul: $line"
|
echo "Skip regrex rul: $line"
|
||||||
;;
|
;;
|
||||||
(\!*|\[*)
|
(\!*|\[*)
|
||||||
: comment
|
: comment
|
||||||
;;
|
;;
|
||||||
(@@\|\|*)
|
(@@\|\|*)
|
||||||
line="${line#@}"
|
line="${line#@}"
|
||||||
;&
|
;&
|
||||||
(@@\|*)
|
(@@\|*)
|
||||||
line="${line#@?|}"
|
line="${line#@?|}"
|
||||||
line="${line#*://}"
|
line="${line#*://}"
|
||||||
line="${line%%\%2F*}"
|
line="${line%%\%2F*}"
|
||||||
domain="${line%%/*}"
|
domain="${line%%/*}"
|
||||||
rule=direct
|
rule=direct
|
||||||
echo "==> direct access: $domain"
|
echo "==> direct access: $domain"
|
||||||
;;
|
;;
|
||||||
(\|\|*)
|
(\|\|*)
|
||||||
line="${line#|}"
|
line="${line#|}"
|
||||||
;&
|
;&
|
||||||
(\|*)
|
(\|*)
|
||||||
line="${line#|}"
|
line="${line#|}"
|
||||||
line="${line#*://}"
|
line="${line#*://}"
|
||||||
;&
|
;&
|
||||||
([.a-z0-9]*)
|
([.a-z0-9]*)
|
||||||
line="${line#.}"
|
line="${line#.}"
|
||||||
line="${line%%\%2F*}"
|
line="${line%%\%2F*}"
|
||||||
domain="${line%%/*}"
|
domain="${line%%/*}"
|
||||||
domain="${domain#*\**.}"
|
domain="${domain#*\**.}"
|
||||||
rule=proxy
|
rule=proxy
|
||||||
echo "==> proxy access: $domain"
|
echo "==> proxy access: $domain"
|
||||||
;;
|
;;
|
||||||
(*)
|
(*)
|
||||||
[[ "$line" =~ ^[[:space:]]*$ ]] ||
|
[[ "$line" =~ ^[[:space:]]*$ ]] ||
|
||||||
echo "Skip: $line"
|
echo "Skip: $line"
|
||||||
;;
|
;;
|
||||||
esac >&2
|
esac >&2
|
||||||
[[ -n "${rule}" ]] || continue
|
[[ -n "${rule}" ]] || continue
|
||||||
[[ -z "${domain_rules[$domain]-}" ]] || continue
|
[[ -z "${domain_rules[$domain]-}" ]] || continue
|
||||||
parent_rule=
|
parent_rule=
|
||||||
for item in $(domain_segments "$domain"); do
|
for item in $(domain_segments "$domain"); do
|
||||||
if [[ -n "${domain_rules[$item]-}" ]]; then
|
if [[ -n "${domain_rules[$item]-}" ]]; then
|
||||||
parent_rule="${domain_rules[$item]}"
|
parent_rule="${domain_rules[$item]}"
|
||||||
break
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -z "$parent_rule" ]] || [[ "$parent_rule" != "$rule" ]]; then
|
||||||
|
domain_rules["$domain"]="$rule"
|
||||||
fi
|
fi
|
||||||
done
|
done < <(sed '/URL Keywords/,/^!/d' "$file")
|
||||||
if [[ -z "$parent_rule" ]] || [[ "$parent_rule" != "$rule" ]]; then
|
done
|
||||||
domain_rules["$domain"]="$rule"
|
|
||||||
fi
|
|
||||||
done < <(sed '/URL Keywords/,/^!/d' auto-proxy.txt)
|
|
||||||
|
|
||||||
local domain rule parent_rule
|
local domain rule parent_rule
|
||||||
declare -a segments
|
declare -a segments
|
||||||
@ -196,30 +188,61 @@ generate_pac() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
sed -n '1,/ begin of ipv4 networks$/p' "$jsfile"
|
sed -n '1,/ begin of ipv4 networks$/p' "$jsfile"
|
||||||
cat china.txt |
|
for file in ipv4-rules-*.txt; do
|
||||||
while read -r line; do
|
rule="${file#ipv?-rules-}"
|
||||||
|
rule="${rule%%.*}"
|
||||||
|
[[ "$rule" = @(blocked|direct|proxy) ]] || rule="\"$rule\""
|
||||||
|
while IFS= read -r line; do
|
||||||
|
line="${line%%#*}"
|
||||||
|
line="${line// }"
|
||||||
|
echo "$rule: $line" >&2
|
||||||
while IFS=/ read ip prefix; do
|
while IFS=/ read ip prefix; do
|
||||||
while IFS=. read n1 n2 n3 n4; do
|
while IFS=. read n1 n2 n3 n4; do
|
||||||
printf " [0x%02x%02x%02x%02x, %s], // %s\n" "${n1:-0}" "${n2:-0}" "${n3:-0}" "${n4:-0}" "$prefix" "$line"
|
printf " [0x%02x%02x%02x%02x, %s, %s], // %s\n" "${n1:-0}" "${n2:-0}" "${n3:-0}" "${n4:-0}" "$prefix" "$rule" "$line"
|
||||||
done <<< "$ip"
|
done <<< "$ip"
|
||||||
done <<< "${line}";
|
done <<< "${line}";
|
||||||
done
|
done < "$file"
|
||||||
|
done | sort -n
|
||||||
sed -n '/ end of ipv4 networks$/,/ begin of ipv6 networks$/p' "$jsfile"
|
sed -n '/ end of ipv4 networks$/,/ begin of ipv6 networks$/p' "$jsfile"
|
||||||
ipv6_to_array_format <china6.txt
|
for file in ipv6-rules-*.txt; do
|
||||||
|
rule="${file#ipv?-rules-}"
|
||||||
|
rule="${rule%%.*}"
|
||||||
|
[[ "$rule" = @(blocked|direct|proxy) ]] || rule="\"$rule\""
|
||||||
|
while IFS= read -r line; do
|
||||||
|
line="${line%%#*}"
|
||||||
|
line="${line// }"
|
||||||
|
echo "$rule: $line" >&2
|
||||||
|
local ipv6="${line%%/*}"
|
||||||
|
local prefix="${line##*/}"
|
||||||
|
|
||||||
|
local expanded_ipv6=$(expand_ipv6 "$ipv6")
|
||||||
|
local full_hex="${expanded_ipv6//:/}"
|
||||||
|
|
||||||
|
echo " [0x${full_hex:0:16}n, 0x${full_hex:16:16}n, ${prefix}, ${rule}], // ${line}"
|
||||||
|
done < "$file"
|
||||||
|
done | sort
|
||||||
sed -n '/ end of ipv6 networks$/,/ begin of proxy rules$/p' "$jsfile"
|
sed -n '/ end of ipv6 networks$/,/ begin of proxy rules$/p' "$jsfile"
|
||||||
local domain
|
local domain
|
||||||
for domain in "${!domain_rules[@]}"; do
|
for domain in "${!domain_rules[@]}"; do
|
||||||
rule="${domain_rules[$domain]}"
|
rule="${domain_rules[$domain]}"
|
||||||
[[ "$rule" = @(blocked|direct|proxy) ]] || rule="\"$rule\""
|
[[ "$rule" = @(blocked|direct|proxy) ]] || rule="\"$rule\""
|
||||||
printf " \"%s\": %s,\n" "$domain" "$rule"
|
printf " \"%s\": %s,\n" "$domain" "$rule"
|
||||||
done | sort
|
done | sort -n
|
||||||
sed -n '/ end of proxy rules$/,$p' "$jsfile"
|
sed -n '/ end of proxy rules$/,$p' "$jsfile"
|
||||||
}
|
}
|
||||||
|
|
||||||
del_file_on_exit=proxy.pac
|
is_up_to_date=true
|
||||||
generate_pac "./proxy.js" > proxy.pac
|
files_to_be_deleted=(proxy.pac)
|
||||||
|
shopt -s nullglob
|
||||||
|
for file in "$0" *.js *.txt; do
|
||||||
|
if [ "$file" -nt proxy.pac ]; then
|
||||||
|
is_up_to_date=false
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
"$is_up_to_date" || generate_pac "./proxy.js" > proxy.pac
|
||||||
|
|
||||||
if command -v node &>/dev/null; then
|
if command -v node &>/dev/null; then
|
||||||
node proxy.pac test
|
node proxy.pac test
|
||||||
fi
|
fi
|
||||||
del_file_on_exit=
|
files_to_be_deleted=()
|
||||||
|
4
ipv4-rules-direct.txt.example
Normal file
4
ipv4-rules-direct.txt.example
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
10.0.0.0/8
|
||||||
|
100.64.0.0/10
|
||||||
|
172.16.0.0/32
|
||||||
|
192.168.0.0/16
|
1
ipv6-rules-direct.txt.example
Normal file
1
ipv6-rules-direct.txt.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
2001:db8::/32
|
378
proxy.js
378
proxy.js
@ -21,10 +21,6 @@ function isIpAddress(host) {
|
|||||||
return ipv4Pattern.test(host) || ipv6Pattern.test(host);
|
return ipv4Pattern.test(host) || ipv6Pattern.test(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInDirectAccessNetwork(ip) {
|
|
||||||
return !!findMatchingNetwork(ip, directAccessIPv4Networks, directAccessIPv6Networks);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ipToNumber(ip) {
|
function ipToNumber(ip) {
|
||||||
const parts = ip.split('.');
|
const parts = ip.split('.');
|
||||||
return parts.reduce((acc, part) => (acc << 8) + parseInt(part, 10), 0);
|
return parts.reduce((acc, part) => (acc << 8) + parseInt(part, 10), 0);
|
||||||
@ -65,172 +61,21 @@ function twoNumbersToIpv6(high, low) {
|
|||||||
return parts.join(':').replace(/(:0{1,4}){2,}/, '::');
|
return parts.join(':').replace(/(:0{1,4}){2,}/, '::');
|
||||||
}
|
}
|
||||||
|
|
||||||
function findMatchingNetwork(ip, networks4, networks6) {
|
const ipv4NetworkRules = [
|
||||||
if (ip.includes('.')) { // IPv4
|
[0x7F000000, 8 , direct], // 127.0.0.0/8 (Loopback)
|
||||||
const ipNumber = ipToNumber(ip);
|
[0xA9FE0000, 16, direct], // 169.254.0.0/16 (Link Local)
|
||||||
for (let [network, prefix] of networks4) {
|
[0x64400000, 10, direct], // 100.64.0.0/10 (Carrier-grade NAT)
|
||||||
mask = subnetMaks32[prefix]
|
|
||||||
if ((ipNumber & mask) === (network & mask)) {
|
|
||||||
return [network, prefix];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // IPv6
|
|
||||||
const [ipHigh, ipLow] = ipv6ToTwoNumbers(ip);
|
|
||||||
for (let [networkHigh, networkLow, mask] of networks6) {
|
|
||||||
if (mask>64) {
|
|
||||||
maskHigh = 0xffffffffffffffffn;
|
|
||||||
maskLow = subnetMaks64[mask-64];
|
|
||||||
} else {
|
|
||||||
maskHigh = subnetMaks64[mask];
|
|
||||||
maskLow = 0x0000000000000000n;
|
|
||||||
}
|
|
||||||
if (((ipHigh & maskHigh) === (networkHigh & maskHigh)) &&
|
|
||||||
((ipLow & maskLow) === (networkLow & maskLow))) {
|
|
||||||
return [networkHigh, networkLow, mask];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function printMatchingNetwork(ip, networks4, networks6) {
|
|
||||||
const matchedNetwork = findMatchingNetwork(ip, networks4, networks6);
|
|
||||||
if (matchedNetwork) {
|
|
||||||
if (ip.includes('.')) { // IPv4
|
|
||||||
const [network, prefixLength] = matchedNetwork;
|
|
||||||
return `${numberToIp(network)}/${prefixLength}`;
|
|
||||||
} else { // IPv6
|
|
||||||
const [networkHigh, networkLow, prefixLength] = matchedNetwork;
|
|
||||||
return `${twoNumbersToIpv6(networkHigh, networkLow)}/${prefixLength}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const subnetMaks32 = [
|
|
||||||
0x00000000, // 0
|
|
||||||
0x80000000, // 1
|
|
||||||
0xc0000000, // 2
|
|
||||||
0xe0000000, // 3
|
|
||||||
0xf0000000, // 4
|
|
||||||
0xf8000000, // 5
|
|
||||||
0xfc000000, // 6
|
|
||||||
0xfe000000, // 7
|
|
||||||
0xff000000, // 8
|
|
||||||
0xff800000, // 9
|
|
||||||
0xffc00000, // 10
|
|
||||||
0xffe00000, // 11
|
|
||||||
0xfff00000, // 12
|
|
||||||
0xfff80000, // 13
|
|
||||||
0xfffc0000, // 14
|
|
||||||
0xfffe0000, // 15
|
|
||||||
0xffff0000, // 16
|
|
||||||
0xffff8000, // 17
|
|
||||||
0xffffc000, // 18
|
|
||||||
0xffffe000, // 19
|
|
||||||
0xfffff000, // 20
|
|
||||||
0xfffff800, // 21
|
|
||||||
0xfffffc00, // 22
|
|
||||||
0xfffffe00, // 23
|
|
||||||
0xffffff00, // 24
|
|
||||||
0xffffff80, // 25
|
|
||||||
0xffffffc0, // 26
|
|
||||||
0xffffffe0, // 27
|
|
||||||
0xfffffff0, // 28
|
|
||||||
0xfffffff8, // 29
|
|
||||||
0xfffffffc, // 30
|
|
||||||
0xfffffffe, // 31
|
|
||||||
0xffffffff, // 32
|
|
||||||
];
|
|
||||||
|
|
||||||
const subnetMaks64 = [
|
|
||||||
0x0000000000000000n, // 0
|
|
||||||
0x8000000000000000n, // 1
|
|
||||||
0xc000000000000000n, // 2
|
|
||||||
0xe000000000000000n, // 3
|
|
||||||
0xf000000000000000n, // 4
|
|
||||||
0xf800000000000000n, // 5
|
|
||||||
0xfc00000000000000n, // 6
|
|
||||||
0xfe00000000000000n, // 7
|
|
||||||
0xff00000000000000n, // 8
|
|
||||||
0xff80000000000000n, // 9
|
|
||||||
0xffc0000000000000n, // 10
|
|
||||||
0xffe0000000000000n, // 11
|
|
||||||
0xfff0000000000000n, // 12
|
|
||||||
0xfff8000000000000n, // 13
|
|
||||||
0xfffc000000000000n, // 14
|
|
||||||
0xfffe000000000000n, // 15
|
|
||||||
0xffff000000000000n, // 16
|
|
||||||
0xffff800000000000n, // 17
|
|
||||||
0xffffc00000000000n, // 18
|
|
||||||
0xffffe00000000000n, // 19
|
|
||||||
0xfffff00000000000n, // 20
|
|
||||||
0xfffff80000000000n, // 21
|
|
||||||
0xfffffc0000000000n, // 22
|
|
||||||
0xfffffe0000000000n, // 23
|
|
||||||
0xffffff0000000000n, // 24
|
|
||||||
0xffffff8000000000n, // 25
|
|
||||||
0xffffffc000000000n, // 26
|
|
||||||
0xffffffe000000000n, // 27
|
|
||||||
0xfffffff000000000n, // 28
|
|
||||||
0xfffffff800000000n, // 29
|
|
||||||
0xfffffffc00000000n, // 30
|
|
||||||
0xfffffffe00000000n, // 31
|
|
||||||
0xffffffff00000000n, // 32
|
|
||||||
0xffffffff80000000n, // 33
|
|
||||||
0xffffffffc0000000n, // 34
|
|
||||||
0xffffffffe0000000n, // 35
|
|
||||||
0xfffffffff0000000n, // 36
|
|
||||||
0xfffffffff8000000n, // 37
|
|
||||||
0xfffffffffc000000n, // 38
|
|
||||||
0xfffffffffe000000n, // 39
|
|
||||||
0xffffffffff000000n, // 40
|
|
||||||
0xffffffffff800000n, // 41
|
|
||||||
0xffffffffffc00000n, // 42
|
|
||||||
0xffffffffffe00000n, // 43
|
|
||||||
0xfffffffffff00000n, // 44
|
|
||||||
0xfffffffffff80000n, // 45
|
|
||||||
0xfffffffffffc0000n, // 46
|
|
||||||
0xfffffffffffe0000n, // 47
|
|
||||||
0xffffffffffff0000n, // 48
|
|
||||||
0xffffffffffff8000n, // 49
|
|
||||||
0xffffffffffffc000n, // 50
|
|
||||||
0xffffffffffffe000n, // 51
|
|
||||||
0xfffffffffffff000n, // 52
|
|
||||||
0xfffffffffffff800n, // 53
|
|
||||||
0xfffffffffffffc00n, // 54
|
|
||||||
0xfffffffffffffe00n, // 55
|
|
||||||
0xffffffffffffff00n, // 56
|
|
||||||
0xffffffffffffff80n, // 57
|
|
||||||
0xffffffffffffffc0n, // 58
|
|
||||||
0xffffffffffffffe0n, // 59
|
|
||||||
0xfffffffffffffff0n, // 60
|
|
||||||
0xfffffffffffffff8n, // 61
|
|
||||||
0xfffffffffffffffcn, // 62
|
|
||||||
0xfffffffffffffffen, // 63
|
|
||||||
0xffffffffffffffffn, // 64
|
|
||||||
];
|
|
||||||
|
|
||||||
const directAccessIPv4Networks = [
|
|
||||||
[0xC0A80000, 16], // 192.168.0.0/16
|
|
||||||
[0x0A000000, 8], // 10.0.0.0/8
|
|
||||||
[0xAC100000, 12], // 172.16.0.0/12
|
|
||||||
[0x7F000000, 8], // 127.0.0.0/8 (Loopback)
|
|
||||||
[0xA9FE0000, 16], // 169.254.0.0/16 (Link Local)
|
|
||||||
[0x64400000, 10], // 100.64.0.0/10 (Carrier-grade NAT)
|
|
||||||
// begin of ipv4 networks
|
// begin of ipv4 networks
|
||||||
// end of ipv4 networks
|
// end of ipv4 networks
|
||||||
];
|
];
|
||||||
|
|
||||||
const directAccessIPv6Networks = [
|
const ipv6NetworkRules = [
|
||||||
[0x0000000000000000n, 0x0000000000000000n, 128], // ::/128 (Unspecified Address)
|
[0x0000000000000000n, 0x0000000000000000n, 128, direct], // ::/128 (Unspecified Address)
|
||||||
[0x0000000000000000n, 0x0000000000000001n, 128], // ::1/128 (Loopback Address)
|
[0x0000000000000000n, 0x0000000000000001n, 128, direct], // ::1/128 (Loopback Address)
|
||||||
[0x20010db800000000n, 0x0000000000000000n, 32], // 2001:db8::/32 (Documentation Address)
|
[0xfc00000000000000n, 0x0000000000000000n, 7 , direct], // fc00::/7 (Unique Local Address)
|
||||||
[0xfc00000000000000n, 0x0000000000000000n, 7], // fc00::/7 (Unique Local Address)
|
[0xff00000000000000n, 0x0000000000000000n, 8 , direct], // ff00::/8 (Multicast Address)
|
||||||
[0xff00000000000000n, 0x0000000000000000n, 8], // ff00::/8 (Multicast Address)
|
[0x2001000000000000n, 0x0000000000000000n, 16 , direct], // 2001::/16 (Teredo Address)
|
||||||
[0x2001000000000000n, 0x0000000000000000n, 16], // 2001::/16 (Teredo Address)
|
[0xfe80000000000000n, 0x0000000000000000n, 10 , direct], // fe80::/10 (Link-Local Address)
|
||||||
[0xfe80000000000000n, 0x0000000000000000n, 10], // fe80::/10 (Link-Local Address)
|
|
||||||
// begin of ipv6 networks
|
// begin of ipv6 networks
|
||||||
// end of ipv6 networks
|
// end of ipv6 networks
|
||||||
];
|
];
|
||||||
@ -244,10 +89,194 @@ const proxyRules = {
|
|||||||
// end of proxy rules
|
// end of proxy rules
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IPv4TrieNode {
|
||||||
|
constructor() {
|
||||||
|
this.children = [null, null]; // 0 and 1
|
||||||
|
this.isEnd = false;
|
||||||
|
this.network = null;
|
||||||
|
this.prefix = null;
|
||||||
|
this.action = direct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IPv4PrefixTrie {
|
||||||
|
constructor() {
|
||||||
|
this.root = new IPv4TrieNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildTrieFromData(data) {
|
||||||
|
const trie = new IPv4PrefixTrie();
|
||||||
|
for (const [network, prefix, action] of data) {
|
||||||
|
let node = trie.root;
|
||||||
|
node = IPv4PrefixTrie._insertBits(node, network, prefix);
|
||||||
|
node.isEnd = true;
|
||||||
|
node.network = network;
|
||||||
|
node.prefix = prefix;
|
||||||
|
node.action = action
|
||||||
|
}
|
||||||
|
return trie;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _insertBits(node, value, bits) {
|
||||||
|
let mask = 0x80000000;
|
||||||
|
for (let i = 0; i < bits; i++) {
|
||||||
|
const bitIndex = ((value & mask) !== 0 ? 1 : 0);
|
||||||
|
mask = mask >>> 1;
|
||||||
|
|
||||||
|
if (!node.children[bitIndex]) {
|
||||||
|
node.children[bitIndex] = new IPv4TrieNode();
|
||||||
|
}
|
||||||
|
node = node.children[bitIndex];
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
search(ip) {
|
||||||
|
let node = this.root;
|
||||||
|
let lastMatch = null;
|
||||||
|
|
||||||
|
node = IPv4PrefixTrie._searchBits(node, ip, 32, (matchedNode) => {
|
||||||
|
if (matchedNode.isEnd) {
|
||||||
|
lastMatch = matchedNode;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return lastMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _searchBits(node, value, bits, callback) {
|
||||||
|
let mask = 0x80000000;
|
||||||
|
for (let i = 0; i < bits; i++) {
|
||||||
|
const bitIndex = ((value & mask) !== 0 ? 1 : 0);
|
||||||
|
mask = mask >>> 1;
|
||||||
|
|
||||||
|
if (!node.children[bitIndex]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
node = node.children[bitIndex];
|
||||||
|
callback(node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IPv6TrieNode {
|
||||||
|
constructor() {
|
||||||
|
this.children = [null, null]; // 0 and 1
|
||||||
|
this.isEnd = false;
|
||||||
|
this.networkHigh = null;
|
||||||
|
this.networkLow = null;
|
||||||
|
this.prefix = null;
|
||||||
|
this.action = direct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IPv6PrefixTrie {
|
||||||
|
constructor() {
|
||||||
|
this.root = new IPv6TrieNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildTrieFromData(data) {
|
||||||
|
const trie = new IPv6PrefixTrie();
|
||||||
|
for (const [networkHigh, networkLow, prefix, action] of data) {
|
||||||
|
let node = trie.root;
|
||||||
|
node = IPv6PrefixTrie._insertBits(node, networkHigh, Math.min(prefix, 64));
|
||||||
|
if (prefix > 64) {
|
||||||
|
node = IPv6PrefixTrie._insertBits(node, networkLow, prefix - 64);
|
||||||
|
}
|
||||||
|
node.isEnd = true;
|
||||||
|
node.networkHigh = networkHigh;
|
||||||
|
node.networkLow = networkLow;
|
||||||
|
node.prefix = prefix;
|
||||||
|
node.action = action;
|
||||||
|
}
|
||||||
|
return trie;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _insertBits(node, value, bits) {
|
||||||
|
let mask = 0x8000000000000000n;
|
||||||
|
for (let i = 0; i < bits; i++) {
|
||||||
|
const bitIndex = ((value & mask) !== 0n ? 1 : 0);
|
||||||
|
mask = mask >> 1n;
|
||||||
|
|
||||||
|
if (!node.children[bitIndex]) {
|
||||||
|
node.children[bitIndex] = new IPv6TrieNode();
|
||||||
|
}
|
||||||
|
node = node.children[bitIndex];
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
search(ipHigh, ipLow) {
|
||||||
|
let node = this.root;
|
||||||
|
let lastMatch = null;
|
||||||
|
|
||||||
|
node = IPv6PrefixTrie._searchBits(node, ipHigh, 64, (matchedNode) => {
|
||||||
|
if (matchedNode.isEnd) {
|
||||||
|
lastMatch = matchedNode;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
node = IPv6PrefixTrie._searchBits(node, ipLow, 64, (matchedNode) => {
|
||||||
|
if (matchedNode.isEnd) {
|
||||||
|
lastMatch = matchedNode;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
static _searchBits(node, value, bits, callback) {
|
||||||
|
let mask = 0x8000000000000000n;
|
||||||
|
for (let i = 0; i < bits; i++) {
|
||||||
|
const bitIndex = ((value & mask) !== 0n ? 1 : 0);
|
||||||
|
mask = mask >> 1n;
|
||||||
|
|
||||||
|
if (!node.children[bitIndex]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
node = node.children[bitIndex];
|
||||||
|
callback(node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ipv4Trie = IPv4PrefixTrie.buildTrieFromData(ipv4NetworkRules);
|
||||||
|
const ipv6Trie = IPv6PrefixTrie.buildTrieFromData(ipv6NetworkRules);
|
||||||
|
|
||||||
|
function findMatchingNetwork(ip, networks4, networks6) {
|
||||||
|
if (ip.includes('.')) { // IPv4
|
||||||
|
const ipNumber = ipToNumber(ip);
|
||||||
|
return ipv4Trie.search(ipNumber);
|
||||||
|
} else { // IPv6
|
||||||
|
const [ipHigh, ipLow] = ipv6ToTwoNumbers(ip);
|
||||||
|
return ipv6Trie.search(ipHigh, ipLow);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printMatchingNetwork(ip, networks4, networks6) {
|
||||||
|
const matchedNetwork = findMatchingNetwork(ip, networks4, networks6);
|
||||||
|
if (matchedNetwork) {
|
||||||
|
if (ip.includes('.')) { // IPv4
|
||||||
|
const trie = matchedNetwork;
|
||||||
|
return `${numberToIp(trie.network)}/${trie.prefix}`;
|
||||||
|
} else { // IPv6
|
||||||
|
const trie = matchedNetwork;
|
||||||
|
return `${twoNumbersToIpv6(trie.networkHigh, trie.networkLow)}/${trie.prefix}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
function FindProxyForURL(url, host) {
|
function FindProxyForURL(url, host) {
|
||||||
if (isIpAddress(host)) {
|
if (isIpAddress(host)) {
|
||||||
if(isInDirectAccessNetwork(host)) {
|
const match = findMatchingNetwork(host);
|
||||||
return DIRECT;
|
if(match) {
|
||||||
|
return proxyBehaviors[match.action] || default_behavior;
|
||||||
} else {
|
} else {
|
||||||
var action = proxyRules[host];
|
var action = proxyRules[host];
|
||||||
if (action !== undefined) {
|
if (action !== undefined) {
|
||||||
@ -274,15 +303,16 @@ function FindProxyForURL(url, host) {
|
|||||||
} else if(typeof dnsResolve == 'function') {
|
} else if(typeof dnsResolve == 'function') {
|
||||||
remote_ip = dnsResolve(host);
|
remote_ip = dnsResolve(host);
|
||||||
}
|
}
|
||||||
if(remote_ip !== undefined && isInDirectAccessNetwork(remote_ip)) {
|
if(remote_ip !== undefined) {
|
||||||
return DIRECT
|
const match = findMatchingNetwork(remote_ip);
|
||||||
|
if (match) return proxyBehaviors[match.action] || default_behavior;
|
||||||
}
|
}
|
||||||
return default_behavior;
|
return default_behavior;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof process !== 'undefined' && process.argv.includes('test')) {
|
if (typeof process !== 'undefined' && process.argv.includes('test')) {
|
||||||
function assertNetwork(ip, expected) {
|
function assertNetwork(ip, expected) {
|
||||||
const result = printMatchingNetwork(ip, directAccessIPv4Networks, directAccessIPv6Networks);
|
const result = printMatchingNetwork(ip, ipv4NetworkRules, ipv6NetworkRules);
|
||||||
if (result === expected) {
|
if (result === expected) {
|
||||||
console.log(`OK: Test for ${ip} passed.`);
|
console.log(`OK: Test for ${ip} passed.`);
|
||||||
} else {
|
} else {
|
||||||
@ -316,15 +346,13 @@ if (typeof process !== 'undefined' && process.argv.includes('test')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function runTests() {
|
function runTests() {
|
||||||
assertNetwork("192.168.1.10", "192.168.0.0/16");
|
assertNetwork("127.234.168.10", "127.0.0.0/8");
|
||||||
assertNetwork("1.1.1.1", null);
|
assertNetwork("1.1.1.1", null);
|
||||||
assertNetwork("172.19.1.1", "172.16.0.0/12");
|
|
||||||
assertNetwork("2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:db8::/32");
|
|
||||||
assertNetwork("fe80::f0:c6b3:c766:9b1e", "fe80::/10");
|
assertNetwork("fe80::f0:c6b3:c766:9b1e", "fe80::/10");
|
||||||
assertVisitHostWithProxy("com.google");
|
assertVisitHostWithProxy("com.google");
|
||||||
assertVisitHostWithProxy("domains.google");
|
assertVisitHostWithProxy("domains.google");
|
||||||
assertHostWithDefaultAction("www.not-google");
|
assertHostWithDefaultAction("www.not-google");
|
||||||
assertDirectHost("10.3.4.5");
|
assertDirectHost("127.3.4.5");
|
||||||
assertDirectHost("114.114.114.114");
|
assertDirectHost("114.114.114.114");
|
||||||
assertBlockedHost("www.whitehouse.com");
|
assertBlockedHost("www.whitehouse.com");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user