Spaces:
Running
Running
File size: 34,067 Bytes
5c2ed06 |
1 2 3 4 5 6 7 8 |
{
"version": 3,
"sources": ["../../server/ip-tools.ts"],
"sourcesContent": ["/**\n * IP Tools\n * Pokemon Showdown - http://pokemonshowdown.com/\n *\n * IPTools file has various tools for IP parsing and IP-based blocking.\n *\n * These include DNSBLs: DNS-based blackhole lists, which list IPs known for\n * running proxies, spamming, or other abuse.\n *\n * We also maintain our own database of datacenter IP ranges (usually\n * proxies). These are taken from https://github.com/client9/ipcat\n * but include our own database as well.\n *\n * @license MIT\n */\n\nconst BLOCKLISTS = ['sbl.spamhaus.org', 'rbl.efnetrbl.org'];\nconst HOSTS_FILE = 'config/hosts.csv';\nconst PROXIES_FILE = 'config/proxies.csv';\n\nimport * as dns from 'dns';\nimport { FS, Net, Utils } from '../lib';\n\nexport interface AddressRange {\n\tminIP: number;\n\tmaxIP: number;\n\thost?: string;\n}\n\nfunction removeNohost(hostname: string) {\n\t// Convert from old domain.tld.type-nohost format to new domain.tld?/type format\n\tif (hostname?.includes('-nohost')) {\n\t\tconst parts = hostname.split('.');\n\t\tconst suffix = parts.pop();\n\t\treturn `${parts.join('.')}?/${suffix?.replace('-nohost', '')}`;\n\t}\n\treturn hostname;\n}\n\nexport const IPTools = new class {\n\treadonly dnsblCache = new Map<string, string | null>([\n\t\t['127.0.0.1', null],\n\t]);\n\n\treadonly connectionTestCache = new Map<string, boolean>();\n\n\treadonly ipRegex = /^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/;\n\treadonly ipRangeRegex = /^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]|\\*)){0,2}\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]|\\*)$/;\n\treadonly hostRegex = /^.+\\..{2,}$/;\n\n\tasync lookup(ip: string) {\n\t\tconst [dnsbl, host] = await Promise.all([\n\t\t\tIPTools.queryDnsbl(ip),\n\t\t\tIPTools.getHost(ip),\n\t\t]);\n\t\tconst shortHost = this.shortenHost(host);\n\t\tconst hostType = this.getHostType(shortHost, ip);\n\t\treturn { dnsbl, host, shortHost, hostType };\n\t}\n\n\tqueryDnsblLoop(ip: string, callback: (val: string | null) => void, reversedIpDot: string, index: number) {\n\t\tif (index >= BLOCKLISTS.length) {\n\t\t\t// not in any blocklist\n\t\t\tIPTools.dnsblCache.set(ip, null);\n\t\t\tcallback(null);\n\t\t\treturn;\n\t\t}\n\t\tconst blocklist = BLOCKLISTS[index];\n\t\tdns.lookup(reversedIpDot + blocklist, 4, (err, res) => {\n\t\t\tif (!err) {\n\t\t\t\t// blocked\n\t\t\t\tIPTools.dnsblCache.set(ip, blocklist);\n\t\t\t\tcallback(blocklist);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// not blocked, try next blocklist\n\t\t\tIPTools.queryDnsblLoop(ip, callback, reversedIpDot, index + 1);\n\t\t});\n\t}\n\n\t/**\n\t * IPTools.queryDnsbl(ip, callback)\n\t *\n\t * Calls callb\n\t * ack(blocklist), where blocklist is the blocklist domain\n\t * if the passed IP is in a blocklist, or null if the IP is not in\n\t * any blocklist.\n\t *\n\t * Return value matches isBlocked when treated as a boolean.\n\t */\n\tqueryDnsbl(ip: string) {\n\t\tif (!Config.dnsbl) return Promise.resolve(null);\n\t\tif (IPTools.dnsblCache.has(ip)) {\n\t\t\treturn Promise.resolve(IPTools.dnsblCache.get(ip) || null);\n\t\t}\n\t\tconst reversedIpDot = ip.split('.').reverse().join('.') + '.';\n\t\treturn new Promise<string | null>((resolve, reject) => {\n\t\t\tIPTools.queryDnsblLoop(ip, resolve, reversedIpDot, 0);\n\t\t});\n\t}\n\n\t/*********************************************************\n\t * IP parsing\n\t *********************************************************/\n\n\tipToNumber(ip: string) {\n\t\tip = ip.trim();\n\t\tif (ip.includes(':') && !ip.includes('.')) {\n\t\t\t// IPv6, which PS does not support\n\t\t\treturn null;\n\t\t}\n\t\tif (ip.startsWith('::ffff:')) ip = ip.slice(7);\n\t\telse if (ip.startsWith('::')) ip = ip.slice(2);\n\t\tlet num = 0;\n\t\tconst parts = ip.split('.');\n\t\tif (parts.length !== 4) return null;\n\t\tfor (const part of parts) {\n\t\t\tnum *= 256;\n\n\t\t\tconst partAsInt = Utils.parseExactInt(part);\n\t\t\tif (isNaN(partAsInt) || partAsInt < 0 || partAsInt > 255) return null;\n\t\t\tnum += partAsInt;\n\t\t}\n\t\treturn num;\n\t}\n\n\tnumberToIP(num: number) {\n\t\tconst ipParts: string[] = [];\n\t\tif (num < 0 || num >= 256 ** 4 || num !== Math.trunc(num)) return null;\n\t\twhile (num) {\n\t\t\tconst part = num % 256;\n\t\t\tnum = (num - part) / 256;\n\t\t\tipParts.unshift(part.toString());\n\t\t}\n\t\twhile (ipParts.length < 4) ipParts.unshift('0');\n\t\tif (ipParts.length !== 4) return null;\n\t\treturn ipParts.join('.');\n\t}\n\n\tgetCidrRange(cidr: string): AddressRange | null {\n\t\tif (!cidr) return null;\n\t\tconst index = cidr.indexOf('/');\n\t\tif (index <= 0) {\n\t\t\tconst ip = IPTools.ipToNumber(cidr);\n\t\t\tif (ip === null) return null;\n\t\t\treturn { minIP: ip, maxIP: ip };\n\t\t}\n\t\tconst low = IPTools.ipToNumber(cidr.slice(0, index));\n\t\tconst bits = Utils.parseExactInt(cidr.slice(index + 1));\n\t\t// fun fact: IPTools fails if bits <= 1 because JavaScript\n\t\t// does << with signed int32s.\n\t\tif (low === null || !bits || bits < 2 || bits > 32) return null;\n\t\tconst high = low + (1 << (32 - bits)) - 1;\n\t\treturn { minIP: low, maxIP: high };\n\t}\n\t/** Is this an IP range supported by `stringToRange`? Note that exact IPs are also valid IP ranges. */\n\tisValidRange(range: string): boolean {\n\t\treturn IPTools.stringToRange(range) !== null;\n\t}\n\tstringToRange(this: void, range: string | null): AddressRange | null {\n\t\tif (!range) return null;\n\t\tif (range.endsWith('*')) {\n\t\t\tconst parts = range.replace('.*', '').split('.');\n\t\t\tif (parts.length > 3) return null;\n\t\t\tconst [a, b, c] = parts;\n\t\t\tconst minIP = IPTools.ipToNumber(`${a || '0'}.${b || '0'}.${c || '0'}.0`);\n\t\t\tconst maxIP = IPTools.ipToNumber(`${a || '255'}.${b || '255'}.${c || '255'}.255`);\n\t\t\tif (minIP === null || maxIP === null) return null;\n\t\t\treturn { minIP, maxIP };\n\t\t}\n\t\tconst index = range.indexOf('-');\n\t\tif (index <= 0) {\n\t\t\tif (range.includes('/')) return IPTools.getCidrRange(range);\n\t\t\tconst ip = IPTools.ipToNumber(range);\n\t\t\tif (ip === null) return null;\n\n\t\t\treturn { maxIP: ip, minIP: ip };\n\t\t}\n\t\tconst minIP = IPTools.ipToNumber(range.slice(0, index));\n\t\tconst maxIP = IPTools.ipToNumber(range.slice(index + 1));\n\n\t\tif (minIP === null || maxIP === null || maxIP < minIP) return null;\n\t\treturn { minIP, maxIP };\n\t}\n\trangeToString(range: AddressRange, sep = '-') {\n\t\treturn `${this.numberToIP(range.minIP)}${sep}${this.numberToIP(range.maxIP)}`;\n\t}\n\n\t/******************************\n\t * Range management functions *\n\t ******************************/\n\n\tcheckPattern(patterns: AddressRange[], num: number | null) {\n\t\tif (num === null) return false;\n\t\tfor (const pattern of patterns) {\n\t\t\tif (num >= pattern.minIP && num <= pattern.maxIP) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns a checker function for the passed IP range or array of\n\t * ranges. The checker function returns true if its passed IP is\n\t * in the range.\n\t */\n\tchecker(rangeString: string | string[]): (ip: string) => boolean {\n\t\tif (!rangeString?.length) return () => false;\n\t\tlet ranges: AddressRange[] = [];\n\t\tif (typeof rangeString === 'string') {\n\t\t\tconst rangePatterns = IPTools.stringToRange(rangeString);\n\t\t\tif (rangePatterns) ranges = [rangePatterns];\n\t\t} else {\n\t\t\tranges = rangeString.map(IPTools.stringToRange).filter(x => x) as AddressRange[];\n\t\t}\n\t\treturn (ip: string) => {\n\t\t\tconst ipNumber = IPTools.ipToNumber(ip);\n\t\t\treturn IPTools.checkPattern(ranges, ipNumber);\n\t\t};\n\t}\n\n\t/**\n\t * Proxy and host management functions\n\t */\n\tranges: (AddressRange & { host: string })[] = [];\n\tsingleIPOpenProxies = new Set<string>();\n\ttorProxyIps = new Set<string>();\n\tproxyHosts = new Set<string>();\n\tresidentialHosts = new Set<string>();\n\tmobileHosts = new Set<string>();\n\tasync loadHostsAndRanges() {\n\t\tconst data = await FS(HOSTS_FILE).readIfExists() + await FS(PROXIES_FILE).readIfExists();\n\t\t// Strip carriage returns for Windows compatibility\n\t\tconst rows = data.split('\\n').map(row => row.replace('\\r', ''));\n\t\tconst ranges = [];\n\t\tfor (const row of rows) {\n\t\t\tif (!row) continue;\n\t\t\tlet [type, hostOrLowIP, highIP, host] = row.split(',');\n\t\t\tif (!hostOrLowIP) continue;\n\t\t\t// Handle legacy data format\n\t\t\thost = removeNohost(host);\n\t\t\thostOrLowIP = removeNohost(hostOrLowIP);\n\n\t\t\tswitch (type) {\n\t\t\tcase 'IP':\n\t\t\t\tIPTools.singleIPOpenProxies.add(hostOrLowIP);\n\t\t\t\tbreak;\n\t\t\tcase 'HOST':\n\t\t\t\tIPTools.proxyHosts.add(hostOrLowIP);\n\t\t\t\tbreak;\n\t\t\tcase 'RESIDENTIAL':\n\t\t\t\tIPTools.residentialHosts.add(hostOrLowIP);\n\t\t\t\tbreak;\n\t\t\tcase 'MOBILE':\n\t\t\t\tIPTools.mobileHosts.add(hostOrLowIP);\n\t\t\t\tbreak;\n\t\t\tcase 'RANGE':\n\t\t\t\tif (!host) continue;\n\n\t\t\t\tconst minIP = IPTools.ipToNumber(hostOrLowIP);\n\t\t\t\tif (minIP === null) {\n\t\t\t\t\tMonitor.error(`Bad IP address in host or proxy file: '${hostOrLowIP}'`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst maxIP = IPTools.ipToNumber(highIP);\n\t\t\t\tif (maxIP === null) {\n\t\t\t\t\tMonitor.error(`Bad IP address in host or proxy file: '${highIP}'`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst range = { host: IPTools.urlToHost(host), maxIP, minIP };\n\t\t\t\tif (range.maxIP < range.minIP) throw new Error(`Bad range at ${hostOrLowIP}.`);\n\t\t\t\tranges.push(range);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tIPTools.ranges = ranges;\n\t\tIPTools.sortRanges();\n\t}\n\n\tsaveHostsAndRanges() {\n\t\tlet hostsData = '';\n\t\tlet proxiesData = '';\n\t\tfor (const ip of IPTools.singleIPOpenProxies) {\n\t\t\tproxiesData += `IP,${ip}\\n`;\n\t\t}\n\t\tfor (const host of IPTools.proxyHosts) {\n\t\t\tproxiesData += `HOST,${host}\\n`;\n\t\t}\n\t\tfor (const host of IPTools.residentialHosts) {\n\t\t\thostsData += `RESIDENTIAL,${host}\\n`;\n\t\t}\n\t\tfor (const host of IPTools.mobileHosts) {\n\t\t\thostsData += `MOBILE,${host}\\n`;\n\t\t}\n\t\tIPTools.sortRanges();\n\t\tfor (const range of IPTools.ranges) {\n\t\t\tconst data = `RANGE,${IPTools.rangeToString(range, ',')}${range.host ? `,${range.host}` : ``}\\n`;\n\t\t\tif (range.host?.endsWith('/proxy')) {\n\t\t\t\tproxiesData += data;\n\t\t\t} else {\n\t\t\t\thostsData += data;\n\t\t\t}\n\t\t}\n\t\tvoid FS(HOSTS_FILE).write(hostsData);\n\t\tvoid FS(PROXIES_FILE).write(proxiesData);\n\t}\n\n\taddOpenProxies(ips: string[]) {\n\t\tfor (const ip of ips) {\n\t\t\tIPTools.singleIPOpenProxies.add(ip);\n\t\t}\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\taddProxyHosts(hosts: string[]) {\n\t\tfor (const host of hosts) {\n\t\t\tIPTools.proxyHosts.add(host);\n\t\t}\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\taddMobileHosts(hosts: string[]) {\n\t\tfor (const host of hosts) {\n\t\t\tIPTools.mobileHosts.add(host);\n\t\t}\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\taddResidentialHosts(hosts: string[]) {\n\t\tfor (const host of hosts) {\n\t\t\tIPTools.residentialHosts.add(host);\n\t\t}\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\tremoveOpenProxies(ips: string[]) {\n\t\tfor (const ip of ips) {\n\t\t\tIPTools.singleIPOpenProxies.delete(ip);\n\t\t}\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\tremoveResidentialHosts(hosts: string[]) {\n\t\tfor (const host of hosts) {\n\t\t\tIPTools.residentialHosts.delete(host);\n\t\t}\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\tremoveProxyHosts(hosts: string[]) {\n\t\tfor (const host of hosts) {\n\t\t\tIPTools.proxyHosts.delete(host);\n\t\t}\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\tremoveMobileHosts(hosts: string[]) {\n\t\tfor (const host of hosts) {\n\t\t\tIPTools.mobileHosts.delete(host);\n\t\t}\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\trangeIntersects(a: AddressRange, b: AddressRange) {\n\t\ttry {\n\t\t\tthis.checkRangeConflicts(a, [b]);\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tcheckRangeConflicts(insertion: AddressRange, sortedRanges: AddressRange[], widen?: boolean) {\n\t\tif (insertion.maxIP < insertion.minIP) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid data for address range ${IPTools.rangeToString(insertion)} (${insertion.host})`\n\t\t\t);\n\t\t}\n\n\t\tlet iMin = 0;\n\t\tlet iMax = sortedRanges.length;\n\t\twhile (iMin < iMax) {\n\t\t\tconst i = Math.floor((iMax + iMin) / 2);\n\t\t\tif (insertion.minIP > sortedRanges[i].minIP) {\n\t\t\t\tiMin = i + 1;\n\t\t\t} else {\n\t\t\t\tiMax = i;\n\t\t\t}\n\t\t}\n\t\tif (iMin < sortedRanges.length) {\n\t\t\tconst next = sortedRanges[iMin];\n\t\t\tif (insertion.minIP === next.minIP && insertion.maxIP === next.maxIP) {\n\t\t\t\tthrow new Error(`The address range ${IPTools.rangeToString(insertion)} (${insertion.host}) already exists`);\n\t\t\t}\n\t\t\tif (insertion.minIP <= next.minIP && insertion.maxIP >= next.maxIP) {\n\t\t\t\tif (widen) {\n\t\t\t\t\tif (sortedRanges[iMin + 1]?.minIP <= insertion.maxIP) {\n\t\t\t\t\t\tthrow new Error(\"You can only widen one address range at a time.\");\n\t\t\t\t\t}\n\t\t\t\t\treturn iMin;\n\t\t\t\t}\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Too wide: ${IPTools.rangeToString(insertion)} (${insertion.host})\\n` +\n\t\t\t\t\t`Intersects with: ${IPTools.rangeToString(next)} (${next.host})`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (insertion.maxIP >= next.minIP) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Could not insert: ${IPTools.rangeToString(insertion)} ${insertion.host}\\n` +\n\t\t\t\t\t`Intersects with: ${IPTools.rangeToString(next)} (${next.host})`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (iMin > 0) {\n\t\t\tconst prev = sortedRanges[iMin - 1];\n\t\t\tif (insertion.minIP >= prev.minIP && insertion.maxIP <= prev.maxIP) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Too narrow: ${IPTools.rangeToString(insertion)} (${insertion.host})\\n` +\n\t\t\t\t\t`Intersects with: ${IPTools.rangeToString(prev)} (${prev.host})`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (insertion.minIP <= prev.maxIP) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Could not insert: ${IPTools.rangeToString(insertion)} (${insertion.host})\\n` +\n\t\t\t\t\t`Intersects with: ${IPTools.rangeToString(prev)} (${prev.host})`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*********************************************************\n\t * Range handling functions\n\t *********************************************************/\n\n\turlToHost(url: string) {\n\t\tif (url.startsWith('http://')) url = url.slice(7);\n\t\tif (url.startsWith('https://')) url = url.slice(8);\n\t\tif (url.startsWith('www.')) url = url.slice(4);\n\t\tconst slashIndex = url.indexOf('/');\n\t\tif (slashIndex > 0 && url[slashIndex - 1] !== '?') url = url.slice(0, slashIndex);\n\t\treturn url;\n\t}\n\n\tsortRanges() {\n\t\tUtils.sortBy(IPTools.ranges, range => range.minIP);\n\t}\n\n\tgetRange(minIP: number, maxIP: number) {\n\t\tfor (const range of IPTools.ranges) {\n\t\t\tif (range.minIP === minIP && range.maxIP === maxIP) return range;\n\t\t}\n\t}\n\n\taddRange(range: AddressRange & { host: string }) {\n\t\tif (IPTools.getRange(range.minIP, range.maxIP)) {\n\t\t\tIPTools.removeRange(range.minIP, range.maxIP);\n\t\t}\n\t\tIPTools.ranges.push(range);\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\tremoveRange(minIP: number, maxIP: number) {\n\t\tIPTools.ranges = IPTools.ranges.filter(dc => dc.minIP !== minIP || dc.maxIP !== maxIP);\n\t\treturn IPTools.saveHostsAndRanges();\n\t}\n\n\t/**\n\t * Will not reject; IPs with no RDNS entry will resolve to\n\t * '[byte1].[byte2]?/unknown'.\n\t */\n\tgetHost(ip: string) {\n\t\treturn new Promise<string>(resolve => {\n\t\t\tif (!ip) {\n\t\t\t\tresolve('');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst ipNumber = IPTools.ipToNumber(ip);\n\t\t\tif (ipNumber === null) throw new Error(`Bad IP address: '${ip}'`);\n\t\t\tfor (const range of IPTools.ranges) {\n\t\t\t\tif (ipNumber >= range.minIP && ipNumber <= range.maxIP) {\n\t\t\t\t\tresolve(range.host);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tdns.reverse(ip, (err, hosts) => {\n\t\t\t\tif (err) {\n\t\t\t\t\tresolve(`${ip.split('.').slice(0, 2).join('.')}?/unknown`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!hosts?.[0]) {\n\t\t\t\t\tif (ip.startsWith('50.')) {\n\t\t\t\t\t\tresolve('comcast.net?/res');\n\t\t\t\t\t} else if (ipNumber >= telstraRange.minIP && ipNumber <= telstraRange.maxIP) {\n\t\t\t\t\t\tresolve(telstraRange.host);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.testConnection(ip, result => {\n\t\t\t\t\t\t\tif (result) {\n\t\t\t\t\t\t\t\tresolve(`${ip.split('.').slice(0, 2).join('.')}?/proxy`);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tresolve(`${ip.split('.').slice(0, 2).join('.')}?/unknown`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresolve(hosts[0]);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Does this IP respond to port 80? In theory, proxies are likely to\n\t * respond, while residential connections are likely to reject connections.\n\t *\n\t * Callback is guaranteed to be called exactly once, within a 1000ms\n\t * timeout.\n\t */\n\ttestConnection(ip: string, callback: (result: boolean) => void) {\n\t\tconst cachedValue = this.connectionTestCache.get(ip);\n\t\tif (cachedValue !== undefined) {\n\t\t\treturn callback(cachedValue);\n\t\t}\n\n\t\t// Node.js's documentation does not make this easy to write. I discovered\n\t\t// this behavior by manual testing:\n\n\t\t// A successful connection emits 'connect', which you should react to\n\t\t// with socket.destroy(), which emits 'close'.\n\n\t\t// Some IPs instantly reject connections, emitting 'error' followed\n\t\t// immediately by 'close'.\n\n\t\t// Some IPs just never respond, leaving you to time out. Node will\n\t\t// emit the 'timeout' event, but not actually do anything else, leaving\n\t\t// you to manually use socket.destroy(), which emits 'close'\n\n\t\tlet connected = false;\n\t\tconst socket = require('net').createConnection({\n\t\t\tport: 80,\n\t\t\thost: ip,\n\t\t\ttimeout: 1000,\n\t\t}, () => {\n\t\t\tconnected = true;\n\t\t\tthis.connectionTestCache.set(ip, true);\n\t\t\tsocket.destroy();\n\t\t\treturn callback(true);\n\t\t});\n\t\tsocket.on('error', () => {});\n\t\tsocket.on('timeout', () => socket.destroy());\n\t\tsocket.on('close', () => {\n\t\t\tif (!connected) {\n\t\t\t\tthis.connectionTestCache.set(ip, false);\n\t\t\t\treturn callback(false);\n\t\t\t}\n\t\t});\n\t}\n\n\tshortenHost(host: string) {\n\t\tif (host.split('.').pop()?.includes('/')) return host; // It has a suffix, e.g. leaseweb.com?/proxy\n\t\tlet dotLoc = host.lastIndexOf('.');\n\t\tconst tld = host.slice(dotLoc);\n\t\tif (tld === '.uk' || tld === '.au' || tld === '.br') dotLoc = host.lastIndexOf('.', dotLoc - 1);\n\t\tdotLoc = host.lastIndexOf('.', dotLoc - 1);\n\t\treturn host.slice(dotLoc + 1);\n\t}\n\n\t/**\n\t * Host types:\n\t * - 'res' - normal residential ISP\n\t * - 'shared' - like res, but shared among many people: bans will have collateral damage\n\t * - 'mobile' - like res, but unstable IP (IP bans don't work)\n\t * - 'proxy' - datacenters, VPNs, proxy services, other untrustworthy sources\n\t * (note that bots will usually be hosted on these)\n\t * - 'res?' - likely res, but host not specifically whitelisted\n\t * - 'unknown' - no rdns entry, treat with suspicion\n\t */\n\tgetHostType(host: string, ip: string) {\n\t\tif (Punishments.isSharedIp(ip)) {\n\t\t\treturn 'shared';\n\t\t}\n\t\tif (this.singleIPOpenProxies.has(ip) || this.torProxyIps.has(ip)) {\n\t\t\t// single-IP open proxies\n\t\t\treturn 'proxy';\n\t\t}\n\n\t\tif (/^he\\.net(\\?|)\\/proxy$/.test(host)) {\n\t\t\t// Known to only be VPN services\n\t\t\tif (['74.82.60.', '72.52.87.', '65.49.126.'].some(range => ip.startsWith(range))) {\n\t\t\t\treturn 'proxy';\n\t\t\t}\n\t\t\t// Hurricane Electric has an annoying habit of having residential\n\t\t\t// internet and datacenters on the same IP ranges - we get a lot of\n\t\t\t// legitimate users as well as spammers on VPNs from HE.\n\n\t\t\t// This splits the difference and treats it like any other unknown IP.\n\t\t\treturn 'unknown';\n\t\t}\n\t\t// There were previously special cases for\n\t\t// 'digitalocean.proxy-nohost', 'servihosting.es.proxy-nohost'\n\t\t// DO is commonly used to host bots; I don't know who whitelisted\n\t\t// servihosting but I assume for a similar reason. This isn't actually\n\t\t// tenable; any service that can host bots can and does also host proxies.\n\t\tif (this.proxyHosts.has(host) || host.endsWith('/proxy')) {\n\t\t\treturn 'proxy';\n\t\t}\n\t\tif (this.residentialHosts.has(host) || host.endsWith('/res')) {\n\t\t\treturn 'res';\n\t\t}\n\t\tif (this.mobileHosts.has(host) || host.endsWith('/mobile')) {\n\t\t\treturn 'mobile';\n\t\t}\n\t\tif (/^ip-[0-9]+-[0-9]+-[0-9]+\\.net$/.test(host) || /^ip-[0-9]+-[0-9]+-[0-9]+\\.eu$/.test(host)) {\n\t\t\t// OVH\n\t\t\treturn 'proxy';\n\t\t}\n\n\t\tif (host.endsWith('/unknown')) {\n\t\t\t// rdns entry doesn't exist, and IP doesn't respond to a probe on port 80\n\t\t\treturn 'unknown';\n\t\t}\n\n\t\t// rdns entry exists but is unrecognized\n\t\treturn 'res?';\n\t}\n\tasync updateTorRanges() {\n\t\ttry {\n\t\t\tconst raw = await Net('https://check.torproject.org/torbulkexitlist').get();\n\t\t\tconst torIps = raw.split('\\n');\n\t\t\tfor (const ip of torIps) {\n\t\t\t\tif (this.ipRegex.test(ip)) {\n\t\t\t\t\tthis.torProxyIps.add(ip);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t}\n};\n\nconst telstraRange: AddressRange & { host: string } = {\n\tminIP: IPTools.ipToNumber(\"101.160.0.0\")!,\n\tmaxIP: IPTools.ipToNumber(\"101.191.255.255\")!,\n\thost: 'telstra.net?/res',\n};\n\nexport default IPTools;\n\nvoid IPTools.updateTorRanges();\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBA,UAAqB;AACrB,iBAA+B;AArB/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,MAAM,aAAa,CAAC,oBAAoB,kBAAkB;AAC1D,MAAM,aAAa;AACnB,MAAM,eAAe;AAWrB,SAAS,aAAa,UAAkB;AAEvC,MAAI,UAAU,SAAS,SAAS,GAAG;AAClC,UAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,UAAM,SAAS,MAAM,IAAI;AACzB,WAAO,GAAG,MAAM,KAAK,GAAG,MAAM,QAAQ,QAAQ,WAAW,EAAE;AAAA,EAC5D;AACA,SAAO;AACR;AAEO,MAAM,UAAU,IAAI,MAAM;AAAA,EAAN;AAC1B,SAAS,aAAa,oBAAI,IAA2B;AAAA,MACpD,CAAC,aAAa,IAAI;AAAA,IACnB,CAAC;AAED,SAAS,sBAAsB,oBAAI,IAAqB;AAExD,SAAS,UAAU;AACnB,SAAS,eAAe;AACxB,SAAS,YAAY;AAiLrB;AAAA;AAAA;AAAA,kBAA8C,CAAC;AAC/C,+BAAsB,oBAAI,IAAY;AACtC,uBAAc,oBAAI,IAAY;AAC9B,sBAAa,oBAAI,IAAY;AAC7B,4BAAmB,oBAAI,IAAY;AACnC,uBAAc,oBAAI,IAAY;AAAA;AAAA,EApL9B,MAAM,OAAO,IAAY;AACxB,UAAM,CAAC,OAAO,IAAI,IAAI,MAAM,QAAQ,IAAI;AAAA,MACvC,QAAQ,WAAW,EAAE;AAAA,MACrB,QAAQ,QAAQ,EAAE;AAAA,IACnB,CAAC;AACD,UAAM,YAAY,KAAK,YAAY,IAAI;AACvC,UAAM,WAAW,KAAK,YAAY,WAAW,EAAE;AAC/C,WAAO,EAAE,OAAO,MAAM,WAAW,SAAS;AAAA,EAC3C;AAAA,EAEA,eAAe,IAAY,UAAwC,eAAuB,OAAe;AACxG,QAAI,SAAS,WAAW,QAAQ;AAE/B,cAAQ,WAAW,IAAI,IAAI,IAAI;AAC/B,eAAS,IAAI;AACb;AAAA,IACD;AACA,UAAM,YAAY,WAAW,KAAK;AAClC,QAAI,OAAO,gBAAgB,WAAW,GAAG,CAAC,KAAK,QAAQ;AACtD,UAAI,CAAC,KAAK;AAET,gBAAQ,WAAW,IAAI,IAAI,SAAS;AACpC,iBAAS,SAAS;AAClB;AAAA,MACD;AAEA,cAAQ,eAAe,IAAI,UAAU,eAAe,QAAQ,CAAC;AAAA,IAC9D,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,WAAW,IAAY;AACtB,QAAI,CAAC,OAAO;AAAO,aAAO,QAAQ,QAAQ,IAAI;AAC9C,QAAI,QAAQ,WAAW,IAAI,EAAE,GAAG;AAC/B,aAAO,QAAQ,QAAQ,QAAQ,WAAW,IAAI,EAAE,KAAK,IAAI;AAAA,IAC1D;AACA,UAAM,gBAAgB,GAAG,MAAM,GAAG,EAAE,QAAQ,EAAE,KAAK,GAAG,IAAI;AAC1D,WAAO,IAAI,QAAuB,CAAC,SAAS,WAAW;AACtD,cAAQ,eAAe,IAAI,SAAS,eAAe,CAAC;AAAA,IACrD,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,IAAY;AACtB,SAAK,GAAG,KAAK;AACb,QAAI,GAAG,SAAS,GAAG,KAAK,CAAC,GAAG,SAAS,GAAG,GAAG;AAE1C,aAAO;AAAA,IACR;AACA,QAAI,GAAG,WAAW,SAAS;AAAG,WAAK,GAAG,MAAM,CAAC;AAAA,aACpC,GAAG,WAAW,IAAI;AAAG,WAAK,GAAG,MAAM,CAAC;AAC7C,QAAI,MAAM;AACV,UAAM,QAAQ,GAAG,MAAM,GAAG;AAC1B,QAAI,MAAM,WAAW;AAAG,aAAO;AAC/B,eAAW,QAAQ,OAAO;AACzB,aAAO;AAEP,YAAM,YAAY,iBAAM,cAAc,IAAI;AAC1C,UAAI,MAAM,SAAS,KAAK,YAAY,KAAK,YAAY;AAAK,eAAO;AACjE,aAAO;AAAA,IACR;AACA,WAAO;AAAA,EACR;AAAA,EAEA,WAAW,KAAa;AACvB,UAAM,UAAoB,CAAC;AAC3B,QAAI,MAAM,KAAK,OAAO,OAAO,KAAK,QAAQ,KAAK,MAAM,GAAG;AAAG,aAAO;AAClE,WAAO,KAAK;AACX,YAAM,OAAO,MAAM;AACnB,aAAO,MAAM,QAAQ;AACrB,cAAQ,QAAQ,KAAK,SAAS,CAAC;AAAA,IAChC;AACA,WAAO,QAAQ,SAAS;AAAG,cAAQ,QAAQ,GAAG;AAC9C,QAAI,QAAQ,WAAW;AAAG,aAAO;AACjC,WAAO,QAAQ,KAAK,GAAG;AAAA,EACxB;AAAA,EAEA,aAAa,MAAmC;AAC/C,QAAI,CAAC;AAAM,aAAO;AAClB,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,SAAS,GAAG;AACf,YAAM,KAAK,QAAQ,WAAW,IAAI;AAClC,UAAI,OAAO;AAAM,eAAO;AACxB,aAAO,EAAE,OAAO,IAAI,OAAO,GAAG;AAAA,IAC/B;AACA,UAAM,MAAM,QAAQ,WAAW,KAAK,MAAM,GAAG,KAAK,CAAC;AACnD,UAAM,OAAO,iBAAM,cAAc,KAAK,MAAM,QAAQ,CAAC,CAAC;AAGtD,QAAI,QAAQ,QAAQ,CAAC,QAAQ,OAAO,KAAK,OAAO;AAAI,aAAO;AAC3D,UAAM,OAAO,OAAO,KAAM,KAAK,QAAS;AACxC,WAAO,EAAE,OAAO,KAAK,OAAO,KAAK;AAAA,EAClC;AAAA;AAAA,EAEA,aAAa,OAAwB;AACpC,WAAO,QAAQ,cAAc,KAAK,MAAM;AAAA,EACzC;AAAA,EACA,cAA0B,OAA2C;AACpE,QAAI,CAAC;AAAO,aAAO;AACnB,QAAI,MAAM,SAAS,GAAG,GAAG;AACxB,YAAM,QAAQ,MAAM,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG;AAC/C,UAAI,MAAM,SAAS;AAAG,eAAO;AAC7B,YAAM,CAAC,GAAG,GAAG,CAAC,IAAI;AAClB,YAAMA,SAAQ,QAAQ,WAAW,GAAG,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO;AACxE,YAAMC,SAAQ,QAAQ,WAAW,GAAG,KAAK,SAAS,KAAK,SAAS,KAAK,WAAW;AAChF,UAAID,WAAU,QAAQC,WAAU;AAAM,eAAO;AAC7C,aAAO,EAAE,OAAAD,QAAO,OAAAC,OAAM;AAAA,IACvB;AACA,UAAM,QAAQ,MAAM,QAAQ,GAAG;AAC/B,QAAI,SAAS,GAAG;AACf,UAAI,MAAM,SAAS,GAAG;AAAG,eAAO,QAAQ,aAAa,KAAK;AAC1D,YAAM,KAAK,QAAQ,WAAW,KAAK;AACnC,UAAI,OAAO;AAAM,eAAO;AAExB,aAAO,EAAE,OAAO,IAAI,OAAO,GAAG;AAAA,IAC/B;AACA,UAAM,QAAQ,QAAQ,WAAW,MAAM,MAAM,GAAG,KAAK,CAAC;AACtD,UAAM,QAAQ,QAAQ,WAAW,MAAM,MAAM,QAAQ,CAAC,CAAC;AAEvD,QAAI,UAAU,QAAQ,UAAU,QAAQ,QAAQ;AAAO,aAAO;AAC9D,WAAO,EAAE,OAAO,MAAM;AAAA,EACvB;AAAA,EACA,cAAc,OAAqB,MAAM,KAAK;AAC7C,WAAO,GAAG,KAAK,WAAW,MAAM,KAAK,IAAI,MAAM,KAAK,WAAW,MAAM,KAAK;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,UAA0B,KAAoB;AAC1D,QAAI,QAAQ;AAAM,aAAO;AACzB,eAAW,WAAW,UAAU;AAC/B,UAAI,OAAO,QAAQ,SAAS,OAAO,QAAQ,OAAO;AACjD,eAAO;AAAA,MACR;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,aAAyD;AAChE,QAAI,CAAC,aAAa;AAAQ,aAAO,MAAM;AACvC,QAAI,SAAyB,CAAC;AAC9B,QAAI,OAAO,gBAAgB,UAAU;AACpC,YAAM,gBAAgB,QAAQ,cAAc,WAAW;AACvD,UAAI;AAAe,iBAAS,CAAC,aAAa;AAAA,IAC3C,OAAO;AACN,eAAS,YAAY,IAAI,QAAQ,aAAa,EAAE,OAAO,OAAK,CAAC;AAAA,IAC9D;AACA,WAAO,CAAC,OAAe;AACtB,YAAM,WAAW,QAAQ,WAAW,EAAE;AACtC,aAAO,QAAQ,aAAa,QAAQ,QAAQ;AAAA,IAC7C;AAAA,EACD;AAAA,EAWA,MAAM,qBAAqB;AAC1B,UAAM,OAAO,UAAM,eAAG,UAAU,EAAE,aAAa,IAAI,UAAM,eAAG,YAAY,EAAE,aAAa;AAEvF,UAAM,OAAO,KAAK,MAAM,IAAI,EAAE,IAAI,SAAO,IAAI,QAAQ,MAAM,EAAE,CAAC;AAC9D,UAAM,SAAS,CAAC;AAChB,eAAW,OAAO,MAAM;AACvB,UAAI,CAAC;AAAK;AACV,UAAI,CAAC,MAAM,aAAa,QAAQ,IAAI,IAAI,IAAI,MAAM,GAAG;AACrD,UAAI,CAAC;AAAa;AAElB,aAAO,aAAa,IAAI;AACxB,oBAAc,aAAa,WAAW;AAEtC,cAAQ,MAAM;AAAA,QACd,KAAK;AACJ,kBAAQ,oBAAoB,IAAI,WAAW;AAC3C;AAAA,QACD,KAAK;AACJ,kBAAQ,WAAW,IAAI,WAAW;AAClC;AAAA,QACD,KAAK;AACJ,kBAAQ,iBAAiB,IAAI,WAAW;AACxC;AAAA,QACD,KAAK;AACJ,kBAAQ,YAAY,IAAI,WAAW;AACnC;AAAA,QACD,KAAK;AACJ,cAAI,CAAC;AAAM;AAEX,gBAAM,QAAQ,QAAQ,WAAW,WAAW;AAC5C,cAAI,UAAU,MAAM;AACnB,oBAAQ,MAAM,0CAA0C,cAAc;AACtE;AAAA,UACD;AACA,gBAAM,QAAQ,QAAQ,WAAW,MAAM;AACvC,cAAI,UAAU,MAAM;AACnB,oBAAQ,MAAM,0CAA0C,SAAS;AACjE;AAAA,UACD;AAEA,gBAAM,QAAQ,EAAE,MAAM,QAAQ,UAAU,IAAI,GAAG,OAAO,MAAM;AAC5D,cAAI,MAAM,QAAQ,MAAM;AAAO,kBAAM,IAAI,MAAM,gBAAgB,cAAc;AAC7E,iBAAO,KAAK,KAAK;AACjB;AAAA,MACD;AAAA,IACD;AACA,YAAQ,SAAS;AACjB,YAAQ,WAAW;AAAA,EACpB;AAAA,EAEA,qBAAqB;AACpB,QAAI,YAAY;AAChB,QAAI,cAAc;AAClB,eAAW,MAAM,QAAQ,qBAAqB;AAC7C,qBAAe,MAAM;AAAA;AAAA,IACtB;AACA,eAAW,QAAQ,QAAQ,YAAY;AACtC,qBAAe,QAAQ;AAAA;AAAA,IACxB;AACA,eAAW,QAAQ,QAAQ,kBAAkB;AAC5C,mBAAa,eAAe;AAAA;AAAA,IAC7B;AACA,eAAW,QAAQ,QAAQ,aAAa;AACvC,mBAAa,UAAU;AAAA;AAAA,IACxB;AACA,YAAQ,WAAW;AACnB,eAAW,SAAS,QAAQ,QAAQ;AACnC,YAAM,OAAO,SAAS,QAAQ,cAAc,OAAO,GAAG,IAAI,MAAM,OAAO,IAAI,MAAM,SAAS;AAAA;AAC1F,UAAI,MAAM,MAAM,SAAS,QAAQ,GAAG;AACnC,uBAAe;AAAA,MAChB,OAAO;AACN,qBAAa;AAAA,MACd;AAAA,IACD;AACA,aAAK,eAAG,UAAU,EAAE,MAAM,SAAS;AACnC,aAAK,eAAG,YAAY,EAAE,MAAM,WAAW;AAAA,EACxC;AAAA,EAEA,eAAe,KAAe;AAC7B,eAAW,MAAM,KAAK;AACrB,cAAQ,oBAAoB,IAAI,EAAE;AAAA,IACnC;AACA,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,cAAc,OAAiB;AAC9B,eAAW,QAAQ,OAAO;AACzB,cAAQ,WAAW,IAAI,IAAI;AAAA,IAC5B;AACA,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,eAAe,OAAiB;AAC/B,eAAW,QAAQ,OAAO;AACzB,cAAQ,YAAY,IAAI,IAAI;AAAA,IAC7B;AACA,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,oBAAoB,OAAiB;AACpC,eAAW,QAAQ,OAAO;AACzB,cAAQ,iBAAiB,IAAI,IAAI;AAAA,IAClC;AACA,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,kBAAkB,KAAe;AAChC,eAAW,MAAM,KAAK;AACrB,cAAQ,oBAAoB,OAAO,EAAE;AAAA,IACtC;AACA,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,uBAAuB,OAAiB;AACvC,eAAW,QAAQ,OAAO;AACzB,cAAQ,iBAAiB,OAAO,IAAI;AAAA,IACrC;AACA,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,iBAAiB,OAAiB;AACjC,eAAW,QAAQ,OAAO;AACzB,cAAQ,WAAW,OAAO,IAAI;AAAA,IAC/B;AACA,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,kBAAkB,OAAiB;AAClC,eAAW,QAAQ,OAAO;AACzB,cAAQ,YAAY,OAAO,IAAI;AAAA,IAChC;AACA,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,gBAAgB,GAAiB,GAAiB;AACjD,QAAI;AACH,WAAK,oBAAoB,GAAG,CAAC,CAAC,CAAC;AAAA,IAChC,QAAE;AACD,aAAO;AAAA,IACR;AACA,WAAO;AAAA,EACR;AAAA,EAEA,oBAAoB,WAAyB,cAA8B,OAAiB;AAC3F,QAAI,UAAU,QAAQ,UAAU,OAAO;AACtC,YAAM,IAAI;AAAA,QACT,kCAAkC,QAAQ,cAAc,SAAS,MAAM,UAAU;AAAA,MAClF;AAAA,IACD;AAEA,QAAI,OAAO;AACX,QAAI,OAAO,aAAa;AACxB,WAAO,OAAO,MAAM;AACnB,YAAM,IAAI,KAAK,OAAO,OAAO,QAAQ,CAAC;AACtC,UAAI,UAAU,QAAQ,aAAa,CAAC,EAAE,OAAO;AAC5C,eAAO,IAAI;AAAA,MACZ,OAAO;AACN,eAAO;AAAA,MACR;AAAA,IACD;AACA,QAAI,OAAO,aAAa,QAAQ;AAC/B,YAAM,OAAO,aAAa,IAAI;AAC9B,UAAI,UAAU,UAAU,KAAK,SAAS,UAAU,UAAU,KAAK,OAAO;AACrE,cAAM,IAAI,MAAM,qBAAqB,QAAQ,cAAc,SAAS,MAAM,UAAU,sBAAsB;AAAA,MAC3G;AACA,UAAI,UAAU,SAAS,KAAK,SAAS,UAAU,SAAS,KAAK,OAAO;AACnE,YAAI,OAAO;AACV,cAAI,aAAa,OAAO,CAAC,GAAG,SAAS,UAAU,OAAO;AACrD,kBAAM,IAAI,MAAM,iDAAiD;AAAA,UAClE;AACA,iBAAO;AAAA,QACR;AACA,cAAM,IAAI;AAAA,UACT,aAAa,QAAQ,cAAc,SAAS,MAAM,UAAU;AAAA,mBACxC,QAAQ,cAAc,IAAI,MAAM,KAAK;AAAA,QAC1D;AAAA,MACD;AACA,UAAI,UAAU,SAAS,KAAK,OAAO;AAClC,cAAM,IAAI;AAAA,UACT,qBAAqB,QAAQ,cAAc,SAAS,KAAK,UAAU;AAAA,mBAC/C,QAAQ,cAAc,IAAI,MAAM,KAAK;AAAA,QAC1D;AAAA,MACD;AAAA,IACD;AACA,QAAI,OAAO,GAAG;AACb,YAAM,OAAO,aAAa,OAAO,CAAC;AAClC,UAAI,UAAU,SAAS,KAAK,SAAS,UAAU,SAAS,KAAK,OAAO;AACnE,cAAM,IAAI;AAAA,UACT,eAAe,QAAQ,cAAc,SAAS,MAAM,UAAU;AAAA,mBAC1C,QAAQ,cAAc,IAAI,MAAM,KAAK;AAAA,QAC1D;AAAA,MACD;AACA,UAAI,UAAU,SAAS,KAAK,OAAO;AAClC,cAAM,IAAI;AAAA,UACT,qBAAqB,QAAQ,cAAc,SAAS,MAAM,UAAU;AAAA,mBAChD,QAAQ,cAAc,IAAI,MAAM,KAAK;AAAA,QAC1D;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,KAAa;AACtB,QAAI,IAAI,WAAW,SAAS;AAAG,YAAM,IAAI,MAAM,CAAC;AAChD,QAAI,IAAI,WAAW,UAAU;AAAG,YAAM,IAAI,MAAM,CAAC;AACjD,QAAI,IAAI,WAAW,MAAM;AAAG,YAAM,IAAI,MAAM,CAAC;AAC7C,UAAM,aAAa,IAAI,QAAQ,GAAG;AAClC,QAAI,aAAa,KAAK,IAAI,aAAa,CAAC,MAAM;AAAK,YAAM,IAAI,MAAM,GAAG,UAAU;AAChF,WAAO;AAAA,EACR;AAAA,EAEA,aAAa;AACZ,qBAAM,OAAO,QAAQ,QAAQ,WAAS,MAAM,KAAK;AAAA,EAClD;AAAA,EAEA,SAAS,OAAe,OAAe;AACtC,eAAW,SAAS,QAAQ,QAAQ;AACnC,UAAI,MAAM,UAAU,SAAS,MAAM,UAAU;AAAO,eAAO;AAAA,IAC5D;AAAA,EACD;AAAA,EAEA,SAAS,OAAwC;AAChD,QAAI,QAAQ,SAAS,MAAM,OAAO,MAAM,KAAK,GAAG;AAC/C,cAAQ,YAAY,MAAM,OAAO,MAAM,KAAK;AAAA,IAC7C;AACA,YAAQ,OAAO,KAAK,KAAK;AACzB,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA,EAEA,YAAY,OAAe,OAAe;AACzC,YAAQ,SAAS,QAAQ,OAAO,OAAO,QAAM,GAAG,UAAU,SAAS,GAAG,UAAU,KAAK;AACrF,WAAO,QAAQ,mBAAmB;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,IAAY;AACnB,WAAO,IAAI,QAAgB,aAAW;AACrC,UAAI,CAAC,IAAI;AACR,gBAAQ,EAAE;AACV;AAAA,MACD;AAEA,YAAM,WAAW,QAAQ,WAAW,EAAE;AACtC,UAAI,aAAa;AAAM,cAAM,IAAI,MAAM,oBAAoB,KAAK;AAChE,iBAAW,SAAS,QAAQ,QAAQ;AACnC,YAAI,YAAY,MAAM,SAAS,YAAY,MAAM,OAAO;AACvD,kBAAQ,MAAM,IAAI;AAClB;AAAA,QACD;AAAA,MACD;AACA,UAAI,QAAQ,IAAI,CAAC,KAAK,UAAU;AAC/B,YAAI,KAAK;AACR,kBAAQ,GAAG,GAAG,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,YAAY;AACzD;AAAA,QACD;AACA,YAAI,CAAC,QAAQ,CAAC,GAAG;AAChB,cAAI,GAAG,WAAW,KAAK,GAAG;AACzB,oBAAQ,kBAAkB;AAAA,UAC3B,WAAW,YAAY,aAAa,SAAS,YAAY,aAAa,OAAO;AAC5E,oBAAQ,aAAa,IAAI;AAAA,UAC1B,OAAO;AACN,iBAAK,eAAe,IAAI,YAAU;AACjC,kBAAI,QAAQ;AACX,wBAAQ,GAAG,GAAG,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU;AAAA,cACxD,OAAO;AACN,wBAAQ,GAAG,GAAG,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,YAAY;AAAA,cAC1D;AAAA,YACD,CAAC;AAAA,UACF;AAAA,QACD,OAAO;AACN,kBAAQ,MAAM,CAAC,CAAC;AAAA,QACjB;AAAA,MACD,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,eAAe,IAAY,UAAqC;AAC/D,UAAM,cAAc,KAAK,oBAAoB,IAAI,EAAE;AACnD,QAAI,gBAAgB,QAAW;AAC9B,aAAO,SAAS,WAAW;AAAA,IAC5B;AAeA,QAAI,YAAY;AAChB,UAAM,SAAS,QAAQ,KAAK,EAAE,iBAAiB;AAAA,MAC9C,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IACV,GAAG,MAAM;AACR,kBAAY;AACZ,WAAK,oBAAoB,IAAI,IAAI,IAAI;AACrC,aAAO,QAAQ;AACf,aAAO,SAAS,IAAI;AAAA,IACrB,CAAC;AACD,WAAO,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AAC3B,WAAO,GAAG,WAAW,MAAM,OAAO,QAAQ,CAAC;AAC3C,WAAO,GAAG,SAAS,MAAM;AACxB,UAAI,CAAC,WAAW;AACf,aAAK,oBAAoB,IAAI,IAAI,KAAK;AACtC,eAAO,SAAS,KAAK;AAAA,MACtB;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAEA,YAAY,MAAc;AACzB,QAAI,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,SAAS,GAAG;AAAG,aAAO;AACjD,QAAI,SAAS,KAAK,YAAY,GAAG;AACjC,UAAM,MAAM,KAAK,MAAM,MAAM;AAC7B,QAAI,QAAQ,SAAS,QAAQ,SAAS,QAAQ;AAAO,eAAS,KAAK,YAAY,KAAK,SAAS,CAAC;AAC9F,aAAS,KAAK,YAAY,KAAK,SAAS,CAAC;AACzC,WAAO,KAAK,MAAM,SAAS,CAAC;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,YAAY,MAAc,IAAY;AACrC,QAAI,YAAY,WAAW,EAAE,GAAG;AAC/B,aAAO;AAAA,IACR;AACA,QAAI,KAAK,oBAAoB,IAAI,EAAE,KAAK,KAAK,YAAY,IAAI,EAAE,GAAG;AAEjE,aAAO;AAAA,IACR;AAEA,QAAI,wBAAwB,KAAK,IAAI,GAAG;AAEvC,UAAI,CAAC,aAAa,aAAa,YAAY,EAAE,KAAK,WAAS,GAAG,WAAW,KAAK,CAAC,GAAG;AACjF,eAAO;AAAA,MACR;AAMA,aAAO;AAAA,IACR;AAMA,QAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,SAAS,QAAQ,GAAG;AACzD,aAAO;AAAA,IACR;AACA,QAAI,KAAK,iBAAiB,IAAI,IAAI,KAAK,KAAK,SAAS,MAAM,GAAG;AAC7D,aAAO;AAAA,IACR;AACA,QAAI,KAAK,YAAY,IAAI,IAAI,KAAK,KAAK,SAAS,SAAS,GAAG;AAC3D,aAAO;AAAA,IACR;AACA,QAAI,iCAAiC,KAAK,IAAI,KAAK,gCAAgC,KAAK,IAAI,GAAG;AAE9F,aAAO;AAAA,IACR;AAEA,QAAI,KAAK,SAAS,UAAU,GAAG;AAE9B,aAAO;AAAA,IACR;AAGA,WAAO;AAAA,EACR;AAAA,EACA,MAAM,kBAAkB;AACvB,QAAI;AACH,YAAM,MAAM,UAAM,gBAAI,8CAA8C,EAAE,IAAI;AAC1E,YAAM,SAAS,IAAI,MAAM,IAAI;AAC7B,iBAAW,MAAM,QAAQ;AACxB,YAAI,KAAK,QAAQ,KAAK,EAAE,GAAG;AAC1B,eAAK,YAAY,IAAI,EAAE;AAAA,QACxB;AAAA,MACD;AAAA,IACD,QAAE;AAAA,IAAO;AAAA,EACV;AACD;AAEA,MAAM,eAAgD;AAAA,EACrD,OAAO,QAAQ,WAAW,aAAa;AAAA,EACvC,OAAO,QAAQ,WAAW,iBAAiB;AAAA,EAC3C,MAAM;AACP;AAEA,IAAO,mBAAQ;AAEf,KAAK,QAAQ,gBAAgB;",
"names": ["minIP", "maxIP"]
}
|