dpyproxy 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. dpyproxy/__init__.py +1 -0
  2. dpyproxy/__main__.py +4 -0
  3. dpyproxy-2.2.0.dist-info/METADATA +296 -0
  4. dpyproxy-2.2.0.dist-info/RECORD +59 -0
  5. dpyproxy-2.2.0.dist-info/WHEEL +4 -0
  6. dpyproxy-2.2.0.dist-info/entry_points.txt +2 -0
  7. dpyproxy-2.2.0.dist-info/licenses/LICENSE +201 -0
  8. enumerators/DnsProxyMode.py +39 -0
  9. enumerators/DnsResolvers.py +141 -0
  10. enumerators/HttpMethod.py +17 -0
  11. enumerators/Modules.py +38 -0
  12. enumerators/Port.py +11 -0
  13. enumerators/TcpProxyMode.py +17 -0
  14. enumerators/TlsVersion.py +21 -0
  15. enumerators/__init__.py +0 -0
  16. exception/DnsException.py +7 -0
  17. exception/ParserException.py +7 -0
  18. exception/__init__.py +0 -0
  19. main.py +94 -0
  20. modules/Module.py +45 -0
  21. modules/__init__.py +0 -0
  22. modules/dns/DnsModeDeterminator.py +358 -0
  23. modules/dns/DnsModule.py +113 -0
  24. modules/dns/DnsProxy.py +277 -0
  25. modules/dns/DnsResolver.py +18 -0
  26. modules/dns/__init__.py +0 -0
  27. modules/http/HttpModule.py +69 -0
  28. modules/http/HttpStrategies.py +849 -0
  29. modules/http/HttpUtils.py +94 -0
  30. modules/http/__init__.py +0 -0
  31. modules/tls/TcpProxy.py +106 -0
  32. modules/tls/TlsModule.py +173 -0
  33. modules/tls/__init__.py +0 -0
  34. network/DomainResolver.py +472 -0
  35. network/NetworkAddress.py +10 -0
  36. network/WrappedSocket.py +97 -0
  37. network/__init__.py +0 -0
  38. network/protocols/Dns.py +62 -0
  39. network/protocols/Http.py +109 -0
  40. network/protocols/Socksv4.py +70 -0
  41. network/protocols/Socksv5.py +106 -0
  42. network/protocols/Tls.py +113 -0
  43. network/protocols/__init__.py +0 -0
  44. network/tcp/Forwarder.py +203 -0
  45. network/tcp/TcpConnectionHandler.py +264 -0
  46. network/tcp/WrappedTcpSocket.py +30 -0
  47. network/tcp/__init__.py +0 -0
  48. network/udp/__init__.py +0 -0
  49. test/Sink.py +23 -0
  50. test/__init__.py +0 -0
  51. test/test_dns.py +98 -0
  52. test/test_http.py +57 -0
  53. test/test_tls.py +63 -0
  54. util/DnsAutoModeRuntimeMeasurement.py +62 -0
  55. util/DnsReachabilityCollector.py +160 -0
  56. util/DnsResolversDomainResolver.py +36 -0
  57. util/Util.py +62 -0
  58. util/__init__.py +0 -0
  59. util/constants.py +8 -0
@@ -0,0 +1,141 @@
1
+ from enum import Enum
2
+
3
+
4
+ class DnsResolvers(Enum):
5
+ """
6
+ Ip addresses of public DNS resolvers.
7
+ """
8
+
9
+ # Initial list https://en.wikipedia.org/wiki/Public_recursive_name_server
10
+
11
+ # Adguard https://adguard-dns.io/en/public-dns.html
12
+ ADGUARD_1 = ("94.140.14.14", "dns.adguard-dns.com", "/dns-query")
13
+ ADGUARD_2 = ("94.140.15.15", "dns.adguard-dns.com", "/dns-query")
14
+ ADGUARD_FAMILY_1 = ("94.140.14.15", "family.adguard-dns.com", "/dns-query")
15
+ ADGUARD_FAMILY_2 = ("94.140.15.16", "family.adguard-dns.com", "/dns-query")
16
+ ADGUARD_UNFILTERED_1 = ("94.140.14.140", "unfiltered.adguard-dns.com", "/dns-query")
17
+ ADGUARD_UNFILTERED_2 = ("94.140.14.141", "unfiltered.adguard-dns.com", "/dns-query")
18
+
19
+ # CleanBrowsing https://cleanbrowsing.org/filters
20
+ CLEAN_BROWSING_FAMILY_1 = ("185.228.168.168", "family-filter-dns.cleanbrowsing.org", "/doh/family-filter/")
21
+ CLEAN_BROWSING_FAMILY_2 = ("185.228.169.168", "family-filter-dns.cleanbrowsing.org", "/doh/family-filter/")
22
+ CLEAN_BROWSING_ADULT_1 = ("185.228.168.10", "adult-filter-dns.cleanbrowsing.org", "/doh/adult-filter/")
23
+ CLEAN_BROWSING_ADULT_2 = ("185.228.169.11", "adult-filter-dns.cleanbrowsing.org", "/doh/adult-filter/")
24
+ CLEAN_BROWSING_SECURITY_1 = ("185.228.168.9", "security-filter-dns.cleanbrowsing.org", "/doh/security-filter/")
25
+ CLEAN_BROWSING_SECURITY_2 = ("185.228.169.9", "security-filter-dns.cleanbrowsing.org", "/doh/security-filter/")
26
+
27
+ # Cloudflare https://developers.cloudflare.com/1.1.1.1/ip-addresses/
28
+ CLOUDFLARE_1 = ("1.1.1.1", "one.one.one.one", "/dns-query")
29
+ CLOUDFLARE_2 = ("1.0.0.1", "1dot1dot1dot1.cloudflare-dns.com", "/dns-query")
30
+ CLOUDFLARE_SECURITY_1 = ("1.1.1.2", "security.cloudflare-dns.com", "/dns-query")
31
+ CLOUDFLARE_SECURITY_2 = ("1.0.0.2", "security.cloudflare-dns.com", "/dns-query")
32
+ CLOUDFLARE_FAMILY_1 = ("1.1.1.3", "family.cloudflare-dns.com", "/dns-query")
33
+ CLOUDFLARE_FAMILY_2 = ("1.0.0.3", "family.cloudflare-dns.com", "/dns-query")
34
+ CLOUDFLARE_MOZILLA_1 = ("162.159.61.4", "mozilla.cloudflare-dns.com", "/dns-query")
35
+ CLOUDFLARE_MOZILLA_2 = ("172.64.41.4", "mozilla.cloudflare-dns.com", "/dns-query")
36
+ CLOUDFLARE_CHROMIUM_1 = ("172.64.41.3", "chrome.cloudflare-dns.com", "/dns-query")
37
+ CLOUDFLARE_CHROMIUM_2 = ("162.159.61.3", "chrome.cloudflare-dns.com", "/dns-query")
38
+
39
+ # https://developers.google.com/speed/public-dns
40
+ GOOGLE_1 = ("8.8.8.8", "dns.google", "/dns-query")
41
+ GOOGLE_2 = ("8.8.4.4", "dns.google", "/dns-query")
42
+
43
+ # Gcore https://gcore.com/public-dns
44
+ G_CORE_1 = ("95.85.95.85", "", "/dns-query")
45
+ G_CORE_2 = ("2.56.220.2", "", "/dns-query")
46
+
47
+ # Mullvad https://mullvad.net/en/help/dns-over-https-and-dns-over-tls
48
+ MULLVAD = ("194.242.2.2", "dns.mullvad.net", "/dns-query")
49
+ MULLVAD_ADBLOCK = ("194.242.2.3", "adblock.dns.mullvad.net", "/dns-query")
50
+ MULLVAD_BASE = ("194.242.2.4", "base.dns.mullvad.net", "/dns-query")
51
+ MULLVAD_EXTENDED = ("194.242.2.5", "extended.dns.mullvad.net", "/dns-query")
52
+ MULLVAD_FAMILY = ("194.242.2.6", "family.dns.mullvad.net", "/dns-query")
53
+ MULLVAD_ALL = ("194.242.2.9", "all.dns.mullvad.net", "/dns-query")
54
+
55
+ # Cisco https://umbrella.cisco.com/blog/enhancing-support-dns-encryption-with-dns-over-https
56
+ CISCO_1 = ("208.67.222.222", "dns.opendns.com", "/dns-query")
57
+ CISCO_2 = ("208.67.220.220", "dns.umbrella.com", "/dns-query")
58
+ CISCO_FAMILY_1 = ("208.67.222.123", "familyshield.opendns.com", "/dns-query")
59
+ CISCO_FAMILY_2 = ("208.67.220.123", "familyshield.opendns.com", "/dns-query")
60
+ CISCO_SANDBOX_1 = ("208.67.222.2", "sandbox.opendns.com", "/dns-query")
61
+ CISCO_SANDBOX_2 = ("208.67.220.2", "sandbox.opendns.com", "/dns-query")
62
+ CISCO_CHROMIUM = ("146.112.41.2", "doh.opendns.com", "/dns-query")
63
+
64
+ # Quad 9 https://www.quad9.net/support/faq/
65
+ QUAD_9_1 = ("9.9.9.9", "dns.quad9.net", "/dns-query")
66
+ QUAD_9_2 = ("149.112.112.112", "dns.quad9.net", "/dns-query")
67
+ QUAD_9_EDNS_1 = ("9.9.9.11", "dns11.quad9.net", "/dns-query")
68
+ QUAD_9_EDNS_2 = ("149.112.112.11", "dns11.quad9.net", "/dns-query")
69
+ QUAD_9_UNSECURED_1 = ("9.9.9.10", "dns10.quad9.net", "/dns-query")
70
+ QUAD_9_UNSECURED_2 = ("149.112.112.10", "dns10.quad9.net", "/dns-query")
71
+
72
+ # wikimedia https://meta.wikimedia.org/wiki/Wikimedia_DNS
73
+ WIKIMEDIA = ("185.71.138.138", "wikimedia-dns.org", "/dns-query")
74
+
75
+ # Yandex https://dns.yandex.com/
76
+ YANDEX_1 = ("77.88.8.8", "common.dot.dns.yandex.net", "/dns-query")
77
+ YANDEX_2 = ("77.88.8.1", "common.dot.dns.yandex.net", "/dns-query")
78
+ YANDEX_SAFE_1 = ("77.88.8.88", "safe.dot.dns.yandex.net", "/dns-query")
79
+ YANDEX_SAFE_2 = ("77.88.8.2", "safe.dot.dns.yandex.net", "/dns-query")
80
+ YANDEX_FAMILY_1 = ("77.88.8.7", "family.dot.dns.yandex.net", "/dns-query")
81
+ YANDEX_FAMILY_2 = ("77.88.8.3", "family.dot.dns.yandex.net", "/dns-query")
82
+
83
+ # NextDNS https://nextdns.io/ Firefox and Chromium use both
84
+ NEXTDNS_1 = ("194.45.101.249", "dns.nextdns.io", "/dns-query")
85
+ NEXTDNS_2 = ("217.146.22.163", "dns.nextdns.io", "/dns-query")
86
+
87
+ def __new__(cls, ip, hostname, path):
88
+ obj = object.__new__(cls)
89
+ obj._value_ = ip # The actual enum value is the IP address
90
+ obj.hostname = hostname # Store hostname
91
+ obj.path = path # Store path
92
+ return obj
93
+
94
+ def __str__(self):
95
+ return f"{self.name} ({self.value}): {self.hostname}"
96
+
97
+ def is_default(self):
98
+ """
99
+ Returns true if we consider the DNS server to be default. DpyProxy favors "default" DNS resolvers in its AUTO
100
+ selection mode.
101
+ """
102
+ return self in [
103
+ DnsResolvers.ADGUARD_UNFILTERED_1,
104
+ DnsResolvers.ADGUARD_UNFILTERED_2,
105
+ DnsResolvers.CLOUDFLARE_1,
106
+ DnsResolvers.CLOUDFLARE_2,
107
+ DnsResolvers.GOOGLE_1,
108
+ DnsResolvers.GOOGLE_2,
109
+ DnsResolvers.G_CORE_1,
110
+ DnsResolvers.G_CORE_2,
111
+ DnsResolvers.MULLVAD,
112
+ DnsResolvers.CISCO_1,
113
+ DnsResolvers.CISCO_2,
114
+ DnsResolvers.QUAD_9_UNSECURED_1,
115
+ DnsResolvers.QUAD_9_UNSECURED_2,
116
+ DnsResolvers.YANDEX_1,
117
+ DnsResolvers.YANDEX_2,
118
+ DnsResolvers.NEXTDNS_1,
119
+ DnsResolvers.NEXTDNS_2,
120
+ DnsResolvers.CISCO_CHROMIUM,
121
+ ]
122
+
123
+ def is_family(self):
124
+ """
125
+ Returns true if the resolver filters content it considers adult. DpyProxy favors resolvers that do not apply
126
+ family filters in its AUTO selection mode.
127
+ """
128
+ return self in [
129
+ DnsResolvers.ADGUARD_FAMILY_1,
130
+ DnsResolvers.ADGUARD_FAMILY_2,
131
+ DnsResolvers.CLEAN_BROWSING_FAMILY_1,
132
+ DnsResolvers.CLEAN_BROWSING_FAMILY_1,
133
+ DnsResolvers.CLOUDFLARE_FAMILY_1,
134
+ DnsResolvers.CLOUDFLARE_FAMILY_2,
135
+ DnsResolvers.MULLVAD_FAMILY,
136
+ DnsResolvers.MULLVAD_ALL,
137
+ DnsResolvers.CISCO_FAMILY_1,
138
+ DnsResolvers.CISCO_FAMILY_2,
139
+ DnsResolvers.YANDEX_FAMILY_1,
140
+ DnsResolvers.YANDEX_FAMILY_2,
141
+ ]
@@ -0,0 +1,17 @@
1
+ from enum import Enum
2
+
3
+
4
+ class HttpMethod(Enum):
5
+ GET = "GET"
6
+ HEAD = "HEAD"
7
+ OPTIONS = "OPTIONS"
8
+ TRACE = "TRACE"
9
+ PUT = "PUT"
10
+ POST = "POST"
11
+ DELETE = "DELETE"
12
+ PATCH = "PATCH"
13
+ CONNECT = "CONNECT"
14
+
15
+ @staticmethod
16
+ def all():
17
+ return [e.value for e in HttpMethod]
enumerators/Modules.py ADDED
@@ -0,0 +1,38 @@
1
+ from argparse import ArgumentParser
2
+ from enum import Enum
3
+
4
+ from modules.dns.DnsModule import DnsModule
5
+ from modules.http.HttpModule import HttpModule
6
+ from modules.tls.TlsModule import TlsModule
7
+
8
+
9
+ class Modules(Enum):
10
+ """
11
+ All optional modules the proxy supports
12
+ """
13
+
14
+ TLS = "TLS"
15
+ DNS = "DNS"
16
+ HTTP = "HTTP"
17
+
18
+ def create_module(self, parser: ArgumentParser):
19
+ """
20
+ Created a new Module object based the enumerator type.
21
+ """
22
+ if self == Modules.TLS:
23
+ return TlsModule(parser)
24
+ elif self == Modules.DNS:
25
+ return DnsModule(parser)
26
+ elif self == Modules.HTTP:
27
+ return HttpModule(parser)
28
+
29
+ def get_class(self):
30
+ """
31
+ Returns the class of the module.
32
+ """
33
+ if self == Modules.TLS:
34
+ return TlsModule
35
+ elif self == Modules.DNS:
36
+ return DnsModule
37
+ elif self == Modules.HTTP:
38
+ return HttpModule
enumerators/Port.py ADDED
@@ -0,0 +1,11 @@
1
+ from enum import Enum
2
+
3
+
4
+ class Port(Enum):
5
+ """
6
+ Default protocol ports
7
+ """
8
+
9
+ DNS = 53
10
+ DOT = 853
11
+ DOH = 443
@@ -0,0 +1,17 @@
1
+ from enum import Enum
2
+
3
+
4
+ class TcpProxyMode(Enum):
5
+ """
6
+ Modes the proxy can operate in
7
+ """
8
+
9
+ HTTP = "HTTP"
10
+ HTTPS = "HTTPS"
11
+ SNI = "SNI"
12
+ SOCKSv4 = "SOCKSv4"
13
+ SOCKSv4a = "SOCKSv4a"
14
+ SOCKSv5 = "SOCKSv5"
15
+
16
+ def __str__(self):
17
+ return self.name
@@ -0,0 +1,21 @@
1
+ from enum import Enum
2
+
3
+
4
+ class TlsVersion(Enum):
5
+ """
6
+ Various TLS versions that can be used to overwrite the TLS version in the TLS record.
7
+ """
8
+
9
+ DEFAULT = "XXXX"
10
+ TLS10 = "0301"
11
+ TLS11 = "0302"
12
+ TLS12 = "0303"
13
+ TLS13_DRAFT_28 = "7F1C"
14
+ TLS13 = "0304"
15
+ SSL3 = "0300"
16
+ SSL2 = "0302"
17
+ INVALID_SMALLER = "0000"
18
+ INVALID_BIGGER = "2020"
19
+
20
+ def __str__(self):
21
+ return f"{self.name}: {self.value}"
File without changes
@@ -0,0 +1,7 @@
1
+ class DnsException(Exception):
2
+ """
3
+ For exceptions during the dns resolution
4
+ """
5
+
6
+ def __init__(self, *args, **kwargs):
7
+ super().__init__(args, kwargs)
@@ -0,0 +1,7 @@
1
+ class ParserException(Exception):
2
+ """
3
+ For exceptions during the parsing process
4
+ """
5
+
6
+ def __init__(self, *args, **kwargs):
7
+ super().__init__(args, kwargs)
exception/__init__.py ADDED
File without changes
main.py ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ import logging
5
+ import sys
6
+ import threading
7
+ import time
8
+ from argparse import ArgumentParser
9
+
10
+ from enumerators.Modules import Modules
11
+ from modules.dns.DnsModule import DnsModule
12
+ from modules.Module import Module
13
+ from modules.tls.TlsModule import TlsModule
14
+
15
+
16
+ def extract_activated_modules(parser: ArgumentParser) -> list[Module]:
17
+
18
+ def list_of_modules(arg):
19
+ if arg is None or len(arg) == 0:
20
+ return []
21
+ return list(map(lambda x: Modules.__getitem__(x), arg.split(",")))
22
+
23
+ general = parser.add_argument_group("Standard options")
24
+
25
+ general.add_argument("-h", "--help", action="help", help="Show this help message and exit")
26
+
27
+ general.add_argument("--debug", default=False, action=argparse.BooleanOptionalAction, help="Turns on debugging")
28
+
29
+ general.add_argument(
30
+ "--disabled_modules",
31
+ type=list_of_modules,
32
+ # choices=Modules,
33
+ default=[Modules.HTTP],
34
+ help="List of proxy modules to disable. By default, only the HTTP module is disabled.",
35
+ )
36
+
37
+ # only parse arguments of basic module to determine used modules
38
+ args = parser.parse_known_args()[0]
39
+
40
+ # change logging
41
+ if args.debug:
42
+ logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
43
+ else:
44
+ logging.basicConfig(stream=sys.stderr, level=logging.INFO)
45
+
46
+ # crate and set enabled modules
47
+ return list(map(lambda x: x.create_module(parser), [x for x in Modules if x not in args.disabled_modules]))
48
+
49
+
50
+ def main():
51
+ """
52
+ Starts the proxy with all enabled modules.
53
+ """
54
+ # initialize argumentParser
55
+ parser = argparse.ArgumentParser(
56
+ description="Proxy for circumventing DPI-based censorship.", usage="%(prog)s [options]", add_help=False
57
+ )
58
+
59
+ # parse options of other modules
60
+ for otherModule in Modules:
61
+ otherModule.get_class().register_parameters(parser)
62
+
63
+ activated_modules = extract_activated_modules(parser)
64
+
65
+ parsed_args = parser.parse_args()
66
+
67
+ for otherModule in activated_modules:
68
+ otherModule.extract_parameters(parsed_args)
69
+
70
+ # if tls module and DNS module are running provide dns server to tls module
71
+ dns_module = next((mod for mod in activated_modules if isinstance(mod, DnsModule)), None)
72
+ tls_module = next((mod for mod in activated_modules if isinstance(mod, TlsModule)), None)
73
+
74
+ if dns_module and tls_module:
75
+ logging.info("DNS Module and TLS module found. Setting DNS server for TLS Module")
76
+ tls_module.set_dns_server(dns_module.server_address)
77
+ tls_module.extract_parameters(parsed_args)
78
+
79
+ # start modules
80
+ for otherModule in activated_modules:
81
+ threading.Thread(target=otherModule.start).start()
82
+
83
+ try:
84
+ while True:
85
+ time.sleep(1000)
86
+ except KeyboardInterrupt:
87
+ logging.info("Received Keyboard Interrupt. Cancelling modules and exiting!")
88
+ for otherModule in activated_modules:
89
+ otherModule.stop()
90
+ sys.exit(0)
91
+
92
+
93
+ if __name__ == "__main__":
94
+ main()
modules/Module.py ADDED
@@ -0,0 +1,45 @@
1
+ from abc import ABC, abstractmethod
2
+ from argparse import ArgumentParser, Namespace
3
+
4
+
5
+ class Module(ABC):
6
+ """
7
+ Abstract class for a module. A module abstracts certain proxy functionality such as TLS alterations or DNS
8
+ alterations.
9
+ They can be started and stopped independently.
10
+ """
11
+
12
+ def __init__(self, parser: ArgumentParser):
13
+ self.parser = parser
14
+
15
+ @staticmethod
16
+ @abstractmethod
17
+ def register_parameters(parser: ArgumentParser):
18
+ """
19
+ Registers the module's CLI parameters.
20
+ """
21
+ pass
22
+
23
+ @abstractmethod
24
+ def extract_parameters(self, arguments: Namespace):
25
+ """
26
+ Extracts the module's registered CLI parameters from ArgumentParser's output.
27
+ """
28
+
29
+ @abstractmethod
30
+ def start(
31
+ self,
32
+ ):
33
+ """
34
+ Starts the module. Without calling this function the module should not do anything. Should only be called after
35
+ registering and extracting the module's CLI parameters.
36
+ """
37
+ pass
38
+
39
+ @abstractmethod
40
+ def stop(self):
41
+ """
42
+ Stops the module from accepting any future connections. Running connections are continued.
43
+ Does nothing if the module has not been started before.
44
+ """
45
+ pass
modules/__init__.py ADDED
File without changes