diff --git a/src/examples/unittests.pac b/src/examples/unittests.pac index 8d3b43f..2f3769c 100644 --- a/src/examples/unittests.pac +++ b/src/examples/unittests.pac @@ -1,16 +1,17 @@ function FindProxyForURL(url, host) { host = host.toLowerCase(); + url = url.toLowerCase(); + if ( + isInNet(host, "93.184.0.0", "255.255.0.0") + || localHostOrDomainIs(host, "example.net") + || dnsResolve(host) === "192.0.0.170" + || dnsResolve(host) === "127.0.0.1" + ) { return "PROXY netmask.example.com"; } if ( localHostOrDomainIs(host, "example.com") || localHostOrDomainIs(host, "foo.example.com") || localHostOrDomainIs(host, "bar.example.com") ) { return "PROXY company.example.com"; } - if ( - isInNet(host, "93.184.0.0", "255.255.0.0") - || localHostOrDomainIs(host, "example.net") - || isInNet(host, "192.0.0.170", "255.255.255.255") - || isInNet(host, "127.0.0.1", "255.255.255.255") - ) { return "PROXY netmask.example.com"; } if ( dnsDomainIs(host, ".example.com") ) { return "DIRECT"; } diff --git a/src/pypacer/helpers.py b/src/pypacer/helpers.py index 469b8b8..e7209ee 100644 --- a/src/pypacer/helpers.py +++ b/src/pypacer/helpers.py @@ -5,14 +5,56 @@ location = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) +def is_ipaddress(ip) -> bool: + try: + ipaddress.ip_address(ip) + return True + except ValueError: + return False + + +def is_network(ip) -> bool: + try: + ipaddress.ip_network(ip) + return True + except ValueError: + return False + + +def is_hostname(hostname): + if len(hostname) > 255 or not hostname: + return False + hostname = hostname.rstrip(".") if hostname.endswith(".") else hostname + hostname = hostname.lstrip(".") if hostname.startswith(".") else hostname + regex = r"^((?!-)[\w-]" + "{1,63}(? str: - if re.fullmatch(r"[0-9.]+", target) and target.count(".") == 3: - return "IP4" - elif re.fullmatch(r"[0-9./]+", target): - return "NET_MASK" - elif target.startswith("."): - return "HOSTS" - return "HOST" + if is_ipaddress(target): + return "IP" + elif is_network(target): + return "NETWORK" + elif is_hostname(target): + if target.startswith("."): + return "HOSTS" + return "HOST" + elif is_regex(target): + return "REGEX" + return "STRING" def sort_by_rating(proxy): @@ -20,8 +62,3 @@ def sort_by_rating(proxy): for target in proxy.targets: rating += target.rating return rating - - -def compute_netmask(netmask) -> list[str]: - ip4_network = ipaddress.ip_network(netmask) - return ip4_network.with_netmask.split("/") diff --git a/src/pypacer/helpers_test.py b/src/pypacer/helpers_test.py index 662f3b9..83ba570 100644 --- a/src/pypacer/helpers_test.py +++ b/src/pypacer/helpers_test.py @@ -1,22 +1,55 @@ import unittest -from pypacer.helpers import get_target_type, sort_by_rating, compute_netmask +from pypacer.helpers import get_target_type, sort_by_rating, is_ipaddress, is_network, \ + is_hostname, is_regex from pypacer.pypacerconfig import PyPacerConfig class TestHelpers(unittest.TestCase): + def test_is_ipaddress(self): + self.assertTrue(is_ipaddress("10.11.12.13")) + self.assertFalse(is_ipaddress("10.11.12.13.14")) + self.assertFalse(is_ipaddress("example.com")) + self.assertTrue(is_ipaddress("2001:0db8:85a3:08d3:1319:8a2e:0370:7344")) + + def test_is_network(self): + self.assertTrue(is_network("10.11.12.13/32")) + self.assertFalse(is_network("10.11.12.13.14")) + self.assertFalse(is_network("example.com")) + self.assertTrue(is_network("2001:0db8:85a3:08d3::/64")) + def test_target_type_ip_mask(self): - self.assertEqual(get_target_type("127.0.0.0/24"), "NET_MASK") + self.assertEqual(get_target_type("127.0.0.0/24"), "NETWORK") def test_target_type_ip(self): - self.assertEqual(get_target_type("127.0.0.1"), "IP4") + self.assertEqual(get_target_type("127.0.0.1"), "IP") def test_target_type_hosts(self): self.assertEqual(get_target_type(".example.com"), "HOSTS") def test_target_type_host(self): self.assertEqual(get_target_type("example.com"), "HOST") + self.assertEqual(get_target_type("example.com."), "HOST") + + def test_target_type_regex(self): + self.assertEqual(get_target_type("/10./"), "REGEX") + + def test_target_type_string(self): + self.assertEqual(get_target_type("10."), "STRING") + + def test_is_hostname(self): + self.assertTrue(is_hostname("example.com")) + self.assertTrue(is_hostname("foo.example.com")) + self.assertTrue(is_hostname("foo.bar.example")) + self.assertTrue(is_hostname("test-123.foo.bar.example-de")) + self.assertTrue(is_hostname("exämple.com")) + self.assertTrue(is_hostname(".exämple.com")) + self.assertTrue(is_hostname(".exämple.com.")) + self.assertTrue(is_hostname("exämple.com.")) + + def test_is_regex(self): + self.assertTrue(is_regex("/10./")) def test_sort_by_rating(self): config = {"proxies": {"A": {"route": "A", "targets": [".example.com"]}, @@ -25,10 +58,6 @@ def test_sort_by_rating(self): config.validate() proxies = [p for p in config.proxies.values()] self.assertEqual(proxies[0].route, "A") - self.assertEqual(proxies[0].targets[0].rating, 2) + self.assertEqual(proxies[0].targets[0].rating, 1) proxies.sort(key=sort_by_rating) self.assertEqual(proxies[0].route, "B") - - def test_compute_netmask(self): - self.assertEqual(compute_netmask("99.77.128.0/18"), ["99.77.128.0", "255.255.192.0"]) - self.assertEqual(compute_netmask("93.184.0.0/16"), ["93.184.0.0", "255.255.0.0"]) diff --git a/src/pypacer/pypacerconfig.py b/src/pypacer/pypacerconfig.py index 34e638d..42cec1c 100644 --- a/src/pypacer/pypacerconfig.py +++ b/src/pypacer/pypacerconfig.py @@ -1,6 +1,7 @@ +import ipaddress from dataclasses import dataclass, field -from pypacer.helpers import get_target_type, compute_netmask +from pypacer.helpers import get_target_type class Target: @@ -9,18 +10,14 @@ def __init__(self, target: str): self.rating = 0 self.type = get_target_type(target) self.netmask = None - if self.type == "NET_MASK": - self.netmask = compute_netmask(self.target) - elif self.type == "IP4": - self.netmask = compute_netmask(f"{self.target}/32") + if self.type == "NETWORK": + self.netmask = ipaddress.ip_network(self.target).with_netmask.split("/") def recognize_overlaps(self, targets: list): if self.type == "HOSTS": for target in targets: if target.type == "HOST" and target.target.endswith(self.target): - self.rating = target.rating + 2 - if self.type == "NET_MASK": - self.rating = self.rating + 1 + self.rating = target.rating + 1 @dataclass diff --git a/src/pypacer/template.js.jinja b/src/pypacer/template.js.jinja index 33d1e76..664887d 100644 --- a/src/pypacer/template.js.jinja +++ b/src/pypacer/template.js.jinja @@ -1,12 +1,15 @@ function FindProxyForURL(url, host) { host = host.toLowerCase(); + url = url.toLowerCase(); {%- for proxy in proxies %} if ( {%- for target in proxy.targets %} {% if loop.index > 1 %}|| {% else %} {% endif -%} {%- if target.type == "HOST" -%}localHostOrDomainIs(host, "{{ target.target }}") {%- elif target.type == "HOSTS" -%}dnsDomainIs(host, "{{ target.target }}") - {%- elif target.type in ("NET_MASK", "IP4") -%}isInNet(host, "{{ target.netmask[0] }}", "{{ target.netmask[1] }}") + {%- elif target.type == "NETWORK" -%}isInNet(host, "{{ target.netmask[0] }}", "{{ target.netmask[1] }}") + {%- elif target.type == "REGEX" -%}shExpMatch(url, "*{{ target.target }}*") + {%- elif target.type == "IP" -%}dnsResolve(host) === "{{ target.target }}" {%- else -%}host === "{{ target.target }}" {%- endif -%} {%- endfor %}