返回 oh-my-ppt
no-proxy.ts
根目录 / src / main / utils / no-proxy.ts
1 /**
2 * NO_PROXY 风格的内网直连匹配:解析逗号分隔的绕过列表,
3 * 支持域名(含子域名)、IP、IPv4 CIDR 网段与端口限定。
4 */
5
6 export interface NoProxyEntry {
7 hostname: string
8 port: number | null
9 cidr: { address: number; bits: number } | null
10 }
11
12 const parseIpv4 = (value: string): number | null => {
13 const parts = value.split('.')
14 if (parts.length !== 4) return null
15 let result = 0
16 for (const part of parts) {
17 if (!/^\d{1,3}$/.test(part)) return null
18 const octet = Number(part)
19 if (octet > 255) return null
20 result = result * 256 + octet
21 }
22 return result
23 }
24
25 const parseCidr = (value: string): { address: number; bits: number } | null => {
26 const [rawAddress, rawBits] = value.split('/')
27 if (rawAddress === undefined) return null
28 const address = parseIpv4(rawAddress)
29 if (address === null) return null
30 const bits = rawBits === undefined ? 32 : Number(rawBits)
31 if (!Number.isInteger(bits) || bits < 0 || bits > 32) return null
32 return { address, bits }
33 }
34
35 const inCidr = (host: number, cidr: { address: number; bits: number }): boolean => {
36 if (cidr.bits === 0) return true
37 const mask = cidr.bits === 32 ? 0xffffffff : ~(0xffffffff >>> cidr.bits)
38 return (host & mask) >>> 0 === (cidr.address & mask) >>> 0
39 }
40
41 export const parseNoProxyEntries = (value: string | null | undefined): NoProxyEntry[] => {
42 if (typeof value !== 'string') return []
43 return value
44 .split(/[,\s]+/)
45 .map((item) => item.trim())
46 // 通配符 * 由 containsNoProxyWildcard 单独处理,不进入逐条匹配
47 .filter((item) => item.length > 0 && item !== '*')
48 .map<NoProxyEntry>((item) => {
49 const lower = item.toLowerCase()
50 const portMatch = lower.match(/^(.+):(\d+)$/)
51 const rawHost = (portMatch ? portMatch[1] : lower).replace(/^\*\./, '').replace(/^\./, '')
52 if (!rawHost) return { hostname: '', port: null, cidr: null }
53 return {
54 hostname: rawHost,
55 port: portMatch ? Number(portMatch[2]) : null,
56 cidr: rawHost.includes('/') ? parseCidr(rawHost) : null
57 }
58 })
59 .filter((entry) => entry.hostname.length > 0)
60 }
61
62 export const matchesNoProxy = (
63 entries: NoProxyEntry[],
64 hostname: string,
65 port: number
66 ): boolean => {
67 const host = hostname.toLowerCase().replace(/^\[|\]$/g, '')
68 if (!host) return false
69 for (const entry of entries) {
70 if (entry.port !== null && entry.port !== port) continue
71 if (entry.cidr) {
72 const hostAddress = parseIpv4(host)
73 if (hostAddress !== null && inCidr(hostAddress, entry.cidr)) return true
74 continue
75 }
76 if (host === entry.hostname) return true
77 if (host.endsWith(`.${entry.hostname}`)) return true
78 }
79 return false
80 }
81
82 export const containsNoProxyWildcard = (value: string | null | undefined): boolean => {
83 if (typeof value !== 'string') return false
84 return value
85 .split(/[,\s]+/)
86 .map((item) => item.trim())
87 .some((item) => item === '*')
88 }
89
89 lines TYPESCRIPT