├── .gitignore ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── examples ├── __init__.py ├── example.py ├── example1.py ├── example2.py ├── example3.py ├── example4.py ├── example5.py ├── example6.py └── files │ └── SSL │ ├── C.pem │ ├── CA.pem │ ├── CAK.pem │ └── CK.pem ├── requirements.txt ├── setup.py └── twunnel3 ├── __init__.py ├── local_proxy_server.py ├── local_proxy_server__https.py ├── local_proxy_server__socks4.py ├── local_proxy_server__socks5.py ├── local_proxy_server__ssl.py ├── logger.py ├── proxy_server.py ├── proxy_server__https.py ├── proxy_server__socks4.py ├── proxy_server__socks5.py ├── remote_proxy_server.py └── remote_proxy_server__ssl.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Jeroen Van Steirteghem 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.rst 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | init: 2 | pip install -r requirements.txt --use-mirrors 3 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Twunnel 2 | ======= 3 | 4 | A HTTPS/SOCKS4/SOCKS5 tunnel for AsyncIO. 5 | 6 | Supports: 7 | 8 | - TCP 9 | - TCP over SSL 10 | 11 | Examples 12 | -------- 13 | 14 | - https://github.com/jvansteirteghem/twunnel3/tree/master/examples 15 | 16 | - Example 1: A TCP tunnel. 17 | - Example 2: A HTTPS TCP tunnel. 18 | - Example 3: A SOCKS4 TCP tunnel. 19 | - Example 4: A SOCKS5 TCP tunnel. 20 | - Example 5: A HTTPS TCP, SOCKS4 TCP, SOCKS5 TCP tunnel. 21 | - Example 6: A SOCKS5 TCP over SSL tunnel. 22 | 23 | License 24 | ------- 25 | 26 | Uses the `MIT`_ license. 27 | 28 | 29 | .. _MIT: http://opensource.org/licenses/MIT 30 | -------------------------------------------------------------------------------- /examples/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jvansteirteghem/twunnel3/717ad40e114cfe63fa366465643ecb9f16ea6df9/examples/__init__.py -------------------------------------------------------------------------------- /examples/example.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import twunnel3.logger 3 | import twunnel3.proxy_server 4 | 5 | class Protocol(asyncio.Protocol): 6 | def __init__(self): 7 | twunnel3.logger.trace("trace: Protocol.__init__") 8 | 9 | self.request = b"" 10 | self.response = b"" 11 | 12 | def connection_made(self, transport): 13 | twunnel3.logger.trace("trace: Protocol.connection_made") 14 | 15 | self.transport = transport 16 | 17 | self.request = b"HEAD / HTTP/1.1\r\n" 18 | 19 | if self.factory.port == 80 or self.factory.port == 443: 20 | self.request = self.request + b"Host: " + self.factory.address.encode() + b"\r\n" 21 | else: 22 | self.request = self.request + b"Host: " + self.factory.address.encode() + b":" + str(self.factory.port).encode() + b"\r\n" 23 | 24 | self.request = self.request + b"\r\n" 25 | 26 | twunnel3.logger.info("request: " + self.request.decode()) 27 | 28 | self.transport.write(self.request) 29 | 30 | def connection_lost(self, exception): 31 | twunnel3.logger.trace("trace: Protocol.connection_lost") 32 | 33 | self.transport = None 34 | 35 | def data_received(self, data): 36 | twunnel3.logger.trace("trace: Protocol.data_received") 37 | 38 | self.response = self.response + data 39 | 40 | i = self.response.find(b"\r\n\r\n") 41 | 42 | if i == -1: 43 | return 44 | 45 | twunnel3.logger.info("response: " + self.response.decode()) 46 | 47 | self.transport.close() 48 | 49 | class ProtocolFactory(object): 50 | def __init__(self): 51 | twunnel3.logger.trace("trace: ProtocolFactory.__init__") 52 | 53 | self.address = "" 54 | self.port = 0 55 | 56 | def __call__(self): 57 | twunnel3.logger.trace("trace: ProtocolFactory.__call__") 58 | 59 | protocol = Protocol() 60 | protocol.factory = self 61 | return protocol 62 | 63 | def create_connection(configuration, ssl=False): 64 | factory = ProtocolFactory() 65 | factory.address = "www.google.com" 66 | 67 | if ssl == False: 68 | factory.port = 80 69 | else: 70 | factory.port = 443 71 | 72 | tunnel = twunnel3.proxy_server.create_tunnel(configuration) 73 | asyncio.async(tunnel.create_connection(factory, address=factory.address, port=factory.port, ssl=ssl)) -------------------------------------------------------------------------------- /examples/example1.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | sys.path.insert(0, os.path.abspath("..")) 4 | 5 | import asyncio 6 | from twunnel3 import local_proxy_server, logger 7 | from examples import example 8 | 9 | configuration = \ 10 | { 11 | "LOGGER": 12 | { 13 | "LEVEL": 3 14 | } 15 | } 16 | 17 | logger.configure(configuration) 18 | 19 | loop = asyncio.get_event_loop() 20 | 21 | configuration = \ 22 | { 23 | "PROXY_SERVERS": 24 | [ 25 | ] 26 | } 27 | 28 | loop.call_later(5, example.create_connection, configuration) 29 | loop.call_later(10, example.create_connection, configuration, True) 30 | loop.call_later(15, loop.stop) 31 | loop.run_forever() -------------------------------------------------------------------------------- /examples/example2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | sys.path.insert(0, os.path.abspath("..")) 4 | 5 | import asyncio 6 | from twunnel3 import local_proxy_server, logger 7 | from examples import example 8 | 9 | configuration = \ 10 | { 11 | "LOGGER": 12 | { 13 | "LEVEL": 3 14 | } 15 | } 16 | 17 | logger.configure(configuration) 18 | 19 | loop = asyncio.get_event_loop() 20 | 21 | configuration = \ 22 | { 23 | "PROXY_SERVERS": [], 24 | "LOCAL_PROXY_SERVER": 25 | { 26 | "TYPE": "HTTPS", 27 | "ADDRESS": "127.0.0.1", 28 | "PORT": 8080 29 | } 30 | } 31 | 32 | https_server = loop.run_until_complete(local_proxy_server.create_server(configuration)) 33 | 34 | configuration = \ 35 | { 36 | "PROXY_SERVERS": 37 | [ 38 | { 39 | "TYPE": "HTTPS", 40 | "ADDRESS": "127.0.0.1", 41 | "PORT": 8080, 42 | "ACCOUNT": 43 | { 44 | "NAME": "", 45 | "PASSWORD": "" 46 | } 47 | } 48 | ] 49 | } 50 | 51 | loop.call_later(5, example.create_connection, configuration) 52 | loop.call_later(10, example.create_connection, configuration, True) 53 | loop.call_later(15, https_server.close) 54 | loop.call_later(20, loop.stop) 55 | loop.run_forever() -------------------------------------------------------------------------------- /examples/example3.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | sys.path.insert(0, os.path.abspath("..")) 4 | 5 | import asyncio 6 | from twunnel3 import local_proxy_server, logger 7 | from examples import example 8 | 9 | configuration = \ 10 | { 11 | "LOGGER": 12 | { 13 | "LEVEL": 3 14 | } 15 | } 16 | 17 | logger.configure(configuration) 18 | 19 | loop = asyncio.get_event_loop() 20 | 21 | configuration = \ 22 | { 23 | "PROXY_SERVERS": [], 24 | "LOCAL_PROXY_SERVER": 25 | { 26 | "TYPE": "SOCKS4", 27 | "ADDRESS": "127.0.0.1", 28 | "PORT": 8080 29 | } 30 | } 31 | 32 | socks4_server = loop.run_until_complete(local_proxy_server.create_server(configuration)) 33 | 34 | configuration = \ 35 | { 36 | "PROXY_SERVERS": 37 | [ 38 | { 39 | "TYPE": "SOCKS4", 40 | "ADDRESS": "127.0.0.1", 41 | "PORT": 8080, 42 | "ACCOUNT": 43 | { 44 | "NAME": "" 45 | } 46 | } 47 | ] 48 | } 49 | 50 | loop.call_later(5, example.create_connection, configuration) 51 | loop.call_later(10, example.create_connection, configuration, True) 52 | loop.call_later(15, socks4_server.close) 53 | loop.call_later(20, loop.stop) 54 | loop.run_forever() -------------------------------------------------------------------------------- /examples/example4.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | sys.path.insert(0, os.path.abspath("..")) 4 | 5 | import asyncio 6 | from twunnel3 import local_proxy_server, logger 7 | from examples import example 8 | 9 | configuration = \ 10 | { 11 | "LOGGER": 12 | { 13 | "LEVEL": 3 14 | } 15 | } 16 | 17 | logger.configure(configuration) 18 | 19 | loop = asyncio.get_event_loop() 20 | 21 | configuration = \ 22 | { 23 | "PROXY_SERVERS": [], 24 | "LOCAL_PROXY_SERVER": 25 | { 26 | "TYPE": "SOCKS5", 27 | "ADDRESS": "127.0.0.1", 28 | "PORT": 8080, 29 | "ACCOUNTS": 30 | [ 31 | { 32 | "NAME": "", 33 | "PASSWORD": "" 34 | } 35 | ] 36 | } 37 | } 38 | 39 | socks5_server = loop.run_until_complete(local_proxy_server.create_server(configuration)) 40 | 41 | configuration = \ 42 | { 43 | "PROXY_SERVERS": 44 | [ 45 | { 46 | "TYPE": "SOCKS5", 47 | "ADDRESS": "127.0.0.1", 48 | "PORT": 8080, 49 | "ACCOUNT": 50 | { 51 | "NAME": "", 52 | "PASSWORD": "" 53 | } 54 | } 55 | ] 56 | } 57 | 58 | loop.call_later(5, example.create_connection, configuration) 59 | loop.call_later(10, example.create_connection, configuration, True) 60 | loop.call_later(15, socks5_server.close) 61 | loop.call_later(20, loop.stop) 62 | loop.run_forever() -------------------------------------------------------------------------------- /examples/example5.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | sys.path.insert(0, os.path.abspath("..")) 4 | 5 | import asyncio 6 | from twunnel3 import local_proxy_server, logger 7 | from examples import example 8 | 9 | configuration = \ 10 | { 11 | "LOGGER": 12 | { 13 | "LEVEL": 3 14 | } 15 | } 16 | 17 | logger.configure(configuration) 18 | 19 | loop = asyncio.get_event_loop() 20 | 21 | configuration = \ 22 | { 23 | "PROXY_SERVERS": [], 24 | "LOCAL_PROXY_SERVER": 25 | { 26 | "TYPE": "HTTPS", 27 | "ADDRESS": "127.0.0.1", 28 | "PORT": 8080 29 | } 30 | } 31 | 32 | https_server = loop.run_until_complete(local_proxy_server.create_server(configuration)) 33 | 34 | configuration = \ 35 | { 36 | "PROXY_SERVERS": [], 37 | "LOCAL_PROXY_SERVER": 38 | { 39 | "TYPE": "SOCKS4", 40 | "ADDRESS": "127.0.0.1", 41 | "PORT": 8081 42 | } 43 | } 44 | 45 | socks4_server = loop.run_until_complete(local_proxy_server.create_server(configuration)) 46 | 47 | configuration = \ 48 | { 49 | "PROXY_SERVERS": [], 50 | "LOCAL_PROXY_SERVER": 51 | { 52 | "TYPE": "SOCKS5", 53 | "ADDRESS": "127.0.0.1", 54 | "PORT": 8082, 55 | "ACCOUNTS": 56 | [ 57 | { 58 | "NAME": "", 59 | "PASSWORD": "" 60 | } 61 | ] 62 | } 63 | } 64 | 65 | socks5_server = loop.run_until_complete(local_proxy_server.create_server(configuration)) 66 | 67 | configuration = \ 68 | { 69 | "PROXY_SERVERS": 70 | [ 71 | { 72 | "TYPE": "HTTPS", 73 | "ADDRESS": "127.0.0.1", 74 | "PORT": 8080, 75 | "ACCOUNT": 76 | { 77 | "NAME": "", 78 | "PASSWORD": "" 79 | } 80 | }, 81 | { 82 | "TYPE": "SOCKS4", 83 | "ADDRESS": "127.0.0.1", 84 | "PORT": 8081, 85 | "ACCOUNT": 86 | { 87 | "NAME": "" 88 | } 89 | }, 90 | { 91 | "TYPE": "SOCKS5", 92 | "ADDRESS": "127.0.0.1", 93 | "PORT": 8082, 94 | "ACCOUNT": 95 | { 96 | "NAME": "", 97 | "PASSWORD": "" 98 | } 99 | } 100 | ] 101 | } 102 | 103 | loop.call_later(5, example.create_connection, configuration) 104 | loop.call_later(10, example.create_connection, configuration, True) 105 | loop.call_later(15, socks5_server.close) 106 | loop.call_later(20, socks4_server.close) 107 | loop.call_later(25, https_server.close) 108 | loop.call_later(30, loop.stop) 109 | loop.run_forever() -------------------------------------------------------------------------------- /examples/example6.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | sys.path.insert(0, os.path.abspath("..")) 4 | 5 | import asyncio 6 | from twunnel3 import local_proxy_server, logger, remote_proxy_server 7 | from examples import example 8 | 9 | configuration = \ 10 | { 11 | "LOGGER": 12 | { 13 | "LEVEL": 3 14 | } 15 | } 16 | 17 | logger.configure(configuration) 18 | 19 | loop = asyncio.get_event_loop() 20 | 21 | configuration = \ 22 | { 23 | "PROXY_SERVERS": [], 24 | "REMOTE_PROXY_SERVER": 25 | { 26 | "TYPE": "SSL", 27 | "ADDRESS": "127.0.0.1", 28 | "PORT": 8443, 29 | "CERTIFICATE": 30 | { 31 | "FILE": "files/SSL/C.pem", 32 | "KEY": 33 | { 34 | "FILE": "files/SSL/CK.pem" 35 | } 36 | }, 37 | "ACCOUNTS": 38 | [ 39 | { 40 | "NAME": "", 41 | "PASSWORD": "" 42 | } 43 | ] 44 | } 45 | } 46 | 47 | ssl_server = loop.run_until_complete(remote_proxy_server.create_server(configuration)) 48 | 49 | configuration = \ 50 | { 51 | "PROXY_SERVERS": [], 52 | "LOCAL_PROXY_SERVER": 53 | { 54 | "TYPE": "SOCKS5", 55 | "ADDRESS": "127.0.0.1", 56 | "PORT": 8080, 57 | "ACCOUNTS": 58 | [ 59 | { 60 | "NAME": "", 61 | "PASSWORD": "" 62 | } 63 | ] 64 | }, 65 | "REMOTE_PROXY_SERVERS": 66 | [ 67 | { 68 | "TYPE": "SSL", 69 | "ADDRESS": "127.0.0.1", 70 | "PORT": 8443, 71 | "CERTIFICATE": 72 | { 73 | "AUTHORITY": 74 | { 75 | "FILE": "files/SSL/CA.pem" 76 | }, 77 | "ADDRESS": "" 78 | }, 79 | "ACCOUNT": 80 | { 81 | "NAME": "", 82 | "PASSWORD": "" 83 | } 84 | } 85 | ] 86 | } 87 | 88 | socks5_server = loop.run_until_complete(local_proxy_server.create_server(configuration)) 89 | 90 | configuration = \ 91 | { 92 | "PROXY_SERVERS": 93 | [ 94 | { 95 | "TYPE": "SOCKS5", 96 | "ADDRESS": "127.0.0.1", 97 | "PORT": 8080, 98 | "ACCOUNT": 99 | { 100 | "NAME": "", 101 | "PASSWORD": "" 102 | } 103 | } 104 | ] 105 | } 106 | 107 | loop.call_later(5, example.create_connection, configuration) 108 | loop.call_later(10, example.create_connection, configuration, True) 109 | loop.call_later(15, socks5_server.close) 110 | loop.call_later(20, ssl_server.close) 111 | loop.call_later(25, loop.stop) 112 | loop.run_forever() -------------------------------------------------------------------------------- /examples/files/SSL/C.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDDTCCAfWgAwIBAwIGDKZ5XYUoMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAMT 3 | AkNBMB4XDTE0MDEyODEzMDAyN1oXDTE5MDEyNzEzMDAyN1owFDESMBAGA1UEAxMJ 4 | MTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5pzb9Xg6 5 | JX2qznSOzcil6UrPRkeU+XhrC0A9UI8LEQCqYtZCqhUop2g3W0w+1lXLEq6KvS91 6 | MvJP2/V6bp49VIepz3ZhLBt4sgjGWnIXAFcrguW5HjEo+EyyVtfGexOE9y9mQZOM 7 | QNurEegKqc+A/Bda9XY2u3bC+qQ3BUcIKDlQKsgTn/qJo7EpSDt1DovCeqbgs7tT 8 | iZTNGPUpw5rGDSpfH1Acnqkb07SoCGjBjAkk4aeJy5iUFYOGWVXO/wafcl++I+3s 9 | ClwQFbBK4SDbhINvgt1j1M88coVikKy8PhbrMDaZH3Gv/m1UoWlPmGvbh9lpAE+G 10 | N31DtFsiVtdpcwIDAQABo2wwajAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSYpdJa 11 | hCJcFntTkuQY8+TOhscNgjAfBgNVHSMEGDAWgBRzdLGl3k/kp60RF5pgAWZESnVi 12 | rTAaBgNVHREEEzARhwR/AAABgglsb2NhbGhvc3QwDQYJKoZIhvcNAQEFBQADggEB 13 | ABZJPaXqCp+otKIWKs20zOPIcqc38S0N2r4mi8EM1mGLxvPKiJs8J9l8CFMHe5pd 14 | 7RameVPkywp+599BpFypBJQAXwWIhAOGNm+/9VZeFcIq5koH3v2uHBh/p7iKpKrn 15 | v/hFbNQDZUYEF3J8eUFElyDLRxZYDf8qES13A2xvOtsAgDi/1lyIGRnfNy6A5SMl 16 | +e2X/cMU1XRsMFSSeo6vZqByV20GNZJ0k8lIXcqL+TX6K64QO/weOPc20pIYyhd/ 17 | OATKSl1VT7nezV2tbBl0IIS05dW04Mv4BzLrM4JPFftsEoEV0pPOoJV1wZ+nUJkC 18 | GpYGXUsQXVJv+n8BTAfJYCg= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /examples/files/SSL/CA.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC7TCCAdWgAwIBAwIGDKZ5XUi6MA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAMT 3 | AkNBMB4XDTE0MDEyODEzMDAyNloXDTE5MDEyNzEzMDAyNlowDTELMAkGA1UEAxMC 4 | Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlmv+ZCrjnTAbio/Hs 5 | GEctscBpcdw7QnpvBKufNplXKsng2UQ9BRqQnJbU6hQN0Oeg35B6cfmykgDYSbzU 6 | dUxLLOdHApb8WLqmp0mqJDph+rXwqohAKuzIKPk6pfyO4aNmhJ0lBAm1hlr6o0Xk 7 | D0a8dErhpEjCZ0Mzqyczc298iwW6j2s6qKL+66Frwr/+o4RIxAdnW9HgFK55GLDZ 8 | M7g3CiRX54INOX2Ha8a6ROqv/tpCin06RJ5/U+r65Z8e0IWTGZMweEp1hlhVUDKj 9 | bGf9Ykx2XE/PmtdEGQdwT4/S7wJrkLYmpXsKl61ZtSaJbWteX7d9kE2ZYsyoP7yZ 10 | jp5rAgMBAAGjUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHN0saXeT+Sn 11 | rREXmmABZkRKdWKtMB8GA1UdIwQYMBaAFHN0saXeT+SnrREXmmABZkRKdWKtMA0G 12 | CSqGSIb3DQEBBQUAA4IBAQAhoXSccw3kf1Wonk7rrO104ps6QXChwARSPshDICF2 13 | etSyUmoHMVGiW19eIEukqSgLLnLsrCdug2p31MlaiQrqrW/RQ7xEHKWyLVesX5OY 14 | ccqfDaaeFLEi4OADu3auUc7E3e3l7EVPS1pWWHfG2G8dFsofWK5wfRUdOJ61OjVf 15 | mN5MilwVvBqVnAF5M+jlUnU3VSrAHHrVopB5VGIad/JOoC7j3bC508h788NeVVPJ 16 | hc/cC17vuSpgRvQAXIY2sXRSXYFejLbMcucoVvbbbptTJ39RYi275wcxmcG9E4+S 17 | R7d+rEEefDtJrHgKq+Z8asXhZMOnE6qiI0Gp+P/t2TEs 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /examples/files/SSL/CAK.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA5Zr/mQq450wG4qPx7BhHLbHAaXHcO0J6bwSrnzaZVyrJ4NlE 3 | PQUakJyW1OoUDdDnoN+QenH5spIA2Em81HVMSyznRwKW/Fi6pqdJqiQ6Yfq18KqI 4 | QCrsyCj5OqX8juGjZoSdJQQJtYZa+qNF5A9GvHRK4aRIwmdDM6snM3NvfIsFuo9r 5 | Oqii/uuha8K//qOESMQHZ1vR4BSueRiw2TO4NwokV+eCDTl9h2vGukTqr/7aQop9 6 | OkSef1Pq+uWfHtCFkxmTMHhKdYZYVVAyo2xn/WJMdlxPz5rXRBkHcE+P0u8Ca5C2 7 | JqV7CpetWbUmiW1rXl+3fZBNmWLMqD+8mY6eawIDAQABAoIBAQCRzZC8DbwHXRcO 8 | Ow+4Xl5s3PCTxTMWCowXDb8yNoi/erOB7lZP7XrGFDoP8KlVcLB2TyG/L7thsyeI 9 | MaMQw3/0SQqA5Fse2fWFOLwlqCXO7Fc3AgPNw7RjEunZXWh5Dz4lLtdflyXXOP9z 10 | I9WREkZYC/01No7hrJNXLzuQh6BnU+i+TeXruNV/7fIoqv0dXP7b7UMsLVUjV5Os 11 | XBvRIx5UcCGeYN1P9DcCJUZ68ywHTlaMY85CpaY6ZiDFVund7h8YA4QnLoRxZgBx 12 | XeRgI17of+XCX32kcn3JHX1OqxEHDS9R5wrECy49RiCOpUDhdIvv3XX1izqjtFrp 13 | AkBu0msJAoGBAP7Q2Xhubr1VWdtEw7Sbd+y0iXcbYTz0AIkMKt+WuTTO9Yd54crJ 14 | u3JsGmgolgB+pMOgLkuRM0PLDtDcFvHrYe06NuC/Lu9AuQqT236pjNFVcixxk0m0 15 | lAx6k2wpZBnCCFr6bi8qPkolfC3bKGuKSZkLo0GeVXgq0ZEtDMGRErSXAoGBAOas 16 | KBQwhNyu0eawaPQMDFxEsot3HXIO9WCL3YikRdlQCcxiGDiSaSQyHnzmYH9tZI8S 17 | pPXS2SNXhit210sWjsda2BQtP6oBXjF1EQptPK629F/B05LDJ2eyoyLxlhBqHl1J 18 | flNenZd1n+NWh8NaGNheDu6z3JzVeqjJxnfnnLtNAoGBAJY+qpt58c2tL6g76XDq 19 | YlGtOYiDGliIPnCQPg9LnbRnexoAYYPiYqXa288ibx+njJrULbfcy29joT7BJr5J 20 | 3Zm0w3rM/22Oqgx0le0fJ6qeSQ2vgfrpgInDFH2oAh+16ptKIgGpVMT/STL4j+Ko 21 | zTByTjZ4l0idZqfE+kp/sZnNAoGBALaYtKk2txWR9WotXwdAulAXuVYZFmABY0/n 22 | p5fQodIiP39gb1fo3f3fBq9MFjsItz9iPN6y51vIntatHkO8OY2pZb6jCvBiYNO5 23 | naGpUw3imZNrdTWJ8hluGSFCJ84akqq50JviG9GXNNfa2WFUYrJz0lI2Yn5rpssR 24 | 3l9wImtJAoGBAPaSL9XVNRB3eVGsMU87iIri052bRFEwDjCgRDRE+GSzdKsrm5kv 25 | /VXK9wCBwvlW5XOW4FeLAg6eDWTAJ/sK3fmG8zh4qJ9TEPILz7xa+AJgioxPE7DE 26 | WBCRRKw6ABpigGM3jhTeVXrdRROL0EmXOKr8W+PyhDJMit9R/5DavI2k 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /examples/files/SSL/CK.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA5pzb9Xg6JX2qznSOzcil6UrPRkeU+XhrC0A9UI8LEQCqYtZC 3 | qhUop2g3W0w+1lXLEq6KvS91MvJP2/V6bp49VIepz3ZhLBt4sgjGWnIXAFcrguW5 4 | HjEo+EyyVtfGexOE9y9mQZOMQNurEegKqc+A/Bda9XY2u3bC+qQ3BUcIKDlQKsgT 5 | n/qJo7EpSDt1DovCeqbgs7tTiZTNGPUpw5rGDSpfH1Acnqkb07SoCGjBjAkk4aeJ 6 | y5iUFYOGWVXO/wafcl++I+3sClwQFbBK4SDbhINvgt1j1M88coVikKy8PhbrMDaZ 7 | H3Gv/m1UoWlPmGvbh9lpAE+GN31DtFsiVtdpcwIDAQABAoIBAQCiPC295cO6RksY 8 | jQU9ne+iTL++6HLUgjOEKcA44Tje0EIN4D/r2ZDNHNL8xonaTWbckLAH4dAodDFc 9 | Q7jLA0WQJxR+YACCgB0L2oJ4dJTb53cz0hrGM71n2MUJtSjrwtKi9tSQQ5XCtwcF 10 | ROHjjphmQKP71RsyDJ2kqEwQyWTirgEXq/8qqaalrbAeovyFem852YBMhBgWcHi0 11 | +/FfDBgYYuqC7vRv8W6qA8HeyZC4OTVpPU+RsLGCaETYDQrHDldkHTXoZNEcOlCT 12 | qx1YVDrM18K6De+E5g9MbY3BxZsExeBw+8xfN1eLAa3DzbiKvumoD7ki7DbPiOIB 13 | X1AFEhcJAoGBAP3GUAcQ2uBdyVFGwwJIMhxRip0WB0yi3YfOz/q4XxGM0i44oXmu 14 | mKozn0V466BURzfpPURkUNGmLVW3KxN7k37JP0/mq3jCU+3tHkshZoYIXxtjXHw0 15 | ZM1vkgak7bMWbcA4V6A/nUcmGpLAfRBTuB2Qtb2/sB4pl5Ve8EBlLAqvAoGBAOii 16 | jSqRIRB1rvQa+QWrmSkjUKg3h1kMU2KZVmqLhD+OezBzwjOasdHQqnuG+tj90puH 17 | WxSV4Z/7FwGVwfOOA/rW+esGF03j6sRhtSszX3/IpyBveos/+G1/n2i9ae7fZSGf 18 | xHfIfnUgE9qVsbzylUGgn4AsFmHdVhsbR9ab8W59AoGAToJysazqOe1p4cYlgGg7 19 | cH1dh7mP6oqcRG6b8zHk33YUdA47Dn0QZ99uj7yYENLh8Y8r0CPEyQdDETiIkA90 20 | Ew5JkvuG4uYjtwDblVyw7hha7AsySo6e/NtebsXJb9UpgQUtPrtPefp00B/S0SFF 21 | XYguYZSS9NHjPmfPpexm0qsCgYEAr7dIZaSlkucxNRTCj/Pjhl00aTs4MpzKxykH 22 | SwcC53bZuelWAOCR++dfPlrgUtK2J9//ffcQK2TfJcVWcH8OpoOG1m0NYToMdDVR 23 | QMZXFhAbnLZXm6LAJQVHBTg9Mb7vhoxFPCzmx9Vo6aw0SoH5F08Z6xhh953Vkjfy 24 | 9TlpDYkCgYACrEJXd2dAYM1wrWKCzhAXM3KnuzwCtbyIYqXD6Y+JMseLfqM73Tl5 25 | lzN1+6PCKdCmMUvdVVGXmkoDPJvJFQb0C8FMrQa9op5xmtF8ob5BZXyBzSCPoOWH 26 | hjnXklr6xHLMItV9Us2V4p0/63r+ALQFH+uLkXInLjAxng2zbn2jdg== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asyncio -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | 7 | try: 8 | from setuptools import setup 9 | except ImportError: 10 | from distutils.core import setup 11 | 12 | if sys.argv[-1] == 'publish': 13 | os.system('python setup.py sdist upload') 14 | sys.exit() 15 | 16 | 17 | with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as f: 18 | readme = f.read() 19 | 20 | packages = [ 21 | 'twunnel3', 22 | ] 23 | 24 | package_data = { 25 | } 26 | 27 | requires = [ 28 | 'asyncio' 29 | ] 30 | 31 | classifiers=[ 32 | 'Programming Language :: Python', 33 | 'Programming Language :: Python :: 3', 34 | 'Programming Language :: Python :: 3.3', 35 | ] 36 | 37 | setup( 38 | name='twunnel3', 39 | version='0.4.0', 40 | description='A HTTPS/SOCKS4/SOCKS5 tunnel for AsyncIO.', 41 | long_description=readme, 42 | packages=packages, 43 | package_data=package_data, 44 | install_requires=requires, 45 | author='Jeroen Van Steirteghem', 46 | author_email='jeroen.vansteirteghem@gmail.com', 47 | url='https://github.com/jvansteirteghem/twunnel3', 48 | license='MIT', 49 | classifiers=classifiers, 50 | ) 51 | -------------------------------------------------------------------------------- /twunnel3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jvansteirteghem/twunnel3/717ad40e114cfe63fa366465643ecb9f16ea6df9/twunnel3/__init__.py -------------------------------------------------------------------------------- /twunnel3/local_proxy_server.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import twunnel3.logger 6 | import twunnel3.proxy_server 7 | 8 | def set_default_configuration(configuration, keys): 9 | twunnel3.proxy_server.set_default_configuration(configuration, keys) 10 | 11 | if "LOCAL_PROXY_SERVER" in keys: 12 | configuration.setdefault("LOCAL_PROXY_SERVER", {}) 13 | configuration["LOCAL_PROXY_SERVER"].setdefault("TYPE", "") 14 | if configuration["LOCAL_PROXY_SERVER"]["TYPE"] == "HTTPS": 15 | configuration["LOCAL_PROXY_SERVER"].setdefault("ADDRESS", "") 16 | configuration["LOCAL_PROXY_SERVER"].setdefault("PORT", 0) 17 | else: 18 | if configuration["LOCAL_PROXY_SERVER"]["TYPE"] == "SOCKS4": 19 | configuration["LOCAL_PROXY_SERVER"].setdefault("ADDRESS", "") 20 | configuration["LOCAL_PROXY_SERVER"].setdefault("PORT", 0) 21 | else: 22 | if configuration["LOCAL_PROXY_SERVER"]["TYPE"] == "SOCKS5": 23 | configuration["LOCAL_PROXY_SERVER"].setdefault("ADDRESS", "") 24 | configuration["LOCAL_PROXY_SERVER"].setdefault("PORT", 0) 25 | configuration["LOCAL_PROXY_SERVER"].setdefault("ACCOUNTS", []) 26 | i = 0 27 | while i < len(configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"]): 28 | configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"][i].setdefault("NAME", "") 29 | configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"][i].setdefault("PASSWORD", "") 30 | i = i + 1 31 | 32 | if "REMOTE_PROXY_SERVERS" in keys: 33 | configuration.setdefault("REMOTE_PROXY_SERVERS", []) 34 | i = 0 35 | while i < len(configuration["REMOTE_PROXY_SERVERS"]): 36 | configuration["REMOTE_PROXY_SERVERS"][i].setdefault("TYPE", "") 37 | if configuration["REMOTE_PROXY_SERVERS"][i]["TYPE"] == "SSL": 38 | configuration["REMOTE_PROXY_SERVERS"][i].setdefault("ADDRESS", "") 39 | configuration["REMOTE_PROXY_SERVERS"][i].setdefault("PORT", 0) 40 | configuration["REMOTE_PROXY_SERVERS"][i].setdefault("CERTIFICATE", {}) 41 | configuration["REMOTE_PROXY_SERVERS"][i]["CERTIFICATE"].setdefault("AUTHORITY", {}) 42 | configuration["REMOTE_PROXY_SERVERS"][i]["CERTIFICATE"]["AUTHORITY"].setdefault("FILE", "") 43 | configuration["REMOTE_PROXY_SERVERS"][i]["CERTIFICATE"].setdefault("ADDRESS", "") 44 | configuration["REMOTE_PROXY_SERVERS"][i].setdefault("ACCOUNT", {}) 45 | configuration["REMOTE_PROXY_SERVERS"][i]["ACCOUNT"].setdefault("NAME", "") 46 | configuration["REMOTE_PROXY_SERVERS"][i]["ACCOUNT"].setdefault("PASSWORD", "") 47 | i = i + 1 48 | 49 | def create_server(configuration): 50 | set_default_configuration(configuration, ["PROXY_SERVERS", "LOCAL_PROXY_SERVER", "REMOTE_PROXY_SERVERS"]) 51 | 52 | output_protocol_connection_manager = OutputProtocolConnectionManager(configuration) 53 | 54 | if configuration["LOCAL_PROXY_SERVER"]["TYPE"] == "HTTPS": 55 | from twunnel3.local_proxy_server__https import create_https_server 56 | 57 | return create_https_server(configuration, output_protocol_connection_manager) 58 | else: 59 | if configuration["LOCAL_PROXY_SERVER"]["TYPE"] == "SOCKS4": 60 | from twunnel3.local_proxy_server__socks4 import create_socks4_server 61 | 62 | return create_socks4_server(configuration, output_protocol_connection_manager) 63 | else: 64 | if configuration["LOCAL_PROXY_SERVER"]["TYPE"] == "SOCKS5": 65 | from twunnel3.local_proxy_server__socks5 import create_socks5_server 66 | 67 | return create_socks5_server(configuration, output_protocol_connection_manager) 68 | else: 69 | return None 70 | 71 | class OutputProtocol(asyncio.Protocol): 72 | def __init__(self): 73 | twunnel3.logger.trace("OutputProtocol.__init__") 74 | 75 | self.input_protocol = None 76 | self.connection_state = 0 77 | self.transport = None 78 | 79 | def connection_made(self, transport): 80 | twunnel3.logger.trace("OutputProtocol.connection_made") 81 | 82 | self.transport = transport 83 | 84 | self.connection_state = 1 85 | 86 | self.input_protocol.output_protocol__connection_made(self.transport) 87 | 88 | def connection_lost(self, exception): 89 | twunnel3.logger.trace("OutputProtocol.connection_lost") 90 | 91 | self.connection_state = 2 92 | 93 | self.input_protocol.output_protocol__connection_lost(exception) 94 | 95 | self.transport = None 96 | 97 | def data_received(self, data): 98 | twunnel3.logger.trace("OutputProtocol.data_received") 99 | 100 | self.input_protocol.output_protocol__data_received(data) 101 | 102 | def input_protocol__connection_made(self, transport): 103 | twunnel3.logger.trace("OutputProtocol.input_protocol__connection_made") 104 | 105 | def input_protocol__connection_lost(self, exception): 106 | twunnel3.logger.trace("OutputProtocol.input_protocol__connection_lost") 107 | 108 | if self.connection_state == 1: 109 | self.transport.close() 110 | 111 | def input_protocol__data_received(self, data): 112 | twunnel3.logger.trace("OutputProtocol.input_protocol__data_received") 113 | 114 | if self.connection_state == 1: 115 | self.transport.write(data) 116 | 117 | def pause_writing(self): 118 | twunnel3.logger.trace("OutputProtocol.pause_reading") 119 | 120 | if self.connection_state == 1: 121 | self.transport.pause_reading() 122 | 123 | def resume_writing(self): 124 | twunnel3.logger.trace("OutputProtocol.resume_writing") 125 | 126 | if self.connection_state == 1: 127 | self.transport.resume_reading() 128 | 129 | class OutputProtocolFactory(object): 130 | def __init__(self, input_protocol): 131 | twunnel3.logger.trace("OutputProtocolFactory.__init__") 132 | 133 | self.input_protocol = input_protocol 134 | 135 | def __call__(self): 136 | twunnel3.logger.trace("OutputProtocolFactory.__call__") 137 | 138 | output_protocol = OutputProtocol() 139 | output_protocol.input_protocol = self.input_protocol 140 | output_protocol.input_protocol.output_protocol = output_protocol 141 | return output_protocol 142 | 143 | class OutputProtocolConnection(object): 144 | def __init__(self, configuration): 145 | twunnel3.logger.trace("OutputProtocolConnection.__init__") 146 | 147 | self.configuration = configuration 148 | 149 | def connect(self, remote_address, remote_port, input_protocol): 150 | twunnel3.logger.trace("OutputProtocolConnection.connect") 151 | 152 | output_protocol_factory = OutputProtocolFactory(input_protocol) 153 | 154 | tunnel = twunnel3.proxy_server.create_tunnel(self.configuration) 155 | asyncio.async(tunnel.create_connection(output_protocol_factory, address=remote_address, port=remote_port)) 156 | 157 | class OutputProtocolConnectionManager(object): 158 | def __init__(self, configuration): 159 | twunnel3.logger.trace("OutputProtocolConnectionManager.__init__") 160 | 161 | self.configuration = configuration 162 | self.i = -1 163 | 164 | self.output_protocol_connections = [] 165 | 166 | if len(self.configuration["REMOTE_PROXY_SERVERS"]) == 0: 167 | configuration = {} 168 | configuration["PROXY_SERVERS"] = self.configuration["PROXY_SERVERS"] 169 | configuration["LOCAL_PROXY_SERVER"] = self.configuration["LOCAL_PROXY_SERVER"] 170 | 171 | output_protocol_connection = OutputProtocolConnection(configuration) 172 | self.output_protocol_connections.append(output_protocol_connection) 173 | else: 174 | i = 0 175 | while i < len(self.configuration["REMOTE_PROXY_SERVERS"]): 176 | configuration = {} 177 | configuration["PROXY_SERVERS"] = self.configuration["PROXY_SERVERS"] 178 | configuration["LOCAL_PROXY_SERVER"] = self.configuration["LOCAL_PROXY_SERVER"] 179 | configuration["REMOTE_PROXY_SERVER"] = self.configuration["REMOTE_PROXY_SERVERS"][i] 180 | 181 | output_protocol_connection_class = self.get_output_protocol_connection_class(configuration["REMOTE_PROXY_SERVER"]["TYPE"]) 182 | 183 | if output_protocol_connection_class is not None: 184 | output_protocol_connection = output_protocol_connection_class(configuration) 185 | self.output_protocol_connections.append(output_protocol_connection) 186 | 187 | i = i + 1 188 | 189 | def connect(self, remote_address, remote_port, input_protocol): 190 | twunnel3.logger.trace("OutputProtocolConnectionManager.connect") 191 | 192 | self.i = self.i + 1 193 | if self.i >= len(self.output_protocol_connections): 194 | self.i = 0 195 | 196 | output_protocol_connection = self.output_protocol_connections[self.i] 197 | output_protocol_connection.connect(remote_address, remote_port, input_protocol) 198 | 199 | def get_output_protocol_connection_class(self, type): 200 | twunnel3.logger.trace("OutputProtocolConnectionManager.get_output_protocol_connection_class") 201 | 202 | if type == "SSL": 203 | from twunnel3.local_proxy_server__ssl import SSLOutputProtocolConnection 204 | 205 | return SSLOutputProtocolConnection 206 | else: 207 | return None -------------------------------------------------------------------------------- /twunnel3/local_proxy_server__https.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import twunnel3.logger 6 | 7 | class HTTPSInputProtocol(asyncio.Protocol): 8 | def __init__(self): 9 | twunnel3.logger.trace("HTTPSInputProtocol.__init__") 10 | 11 | self.configuration = None 12 | self.output_protocol_connection_manager = None 13 | self.output_protocol = None 14 | self.remote_address = "" 15 | self.remote_port = 0 16 | self.connection_state = 0 17 | self.data = b"" 18 | self.data_state = 0 19 | self.transport = None 20 | 21 | def connection_made(self, transport): 22 | twunnel3.logger.trace("HTTPSInputProtocol.connection_made") 23 | 24 | self.transport = transport 25 | 26 | self.connection_state = 1 27 | 28 | def connection_lost(self, exception): 29 | twunnel3.logger.trace("HTTPSInputProtocol.connection_lost") 30 | 31 | self.connection_state = 2 32 | 33 | if self.output_protocol is not None: 34 | self.output_protocol.input_protocol__connection_lost(exception) 35 | 36 | self.transport = None 37 | 38 | def data_received(self, data): 39 | twunnel3.logger.trace("HTTPSInputProtocol.data_received") 40 | 41 | self.data = self.data + data 42 | if self.data_state == 0: 43 | if self.process_data_state0(): 44 | return 45 | if self.data_state == 1: 46 | if self.process_data_state1(): 47 | return 48 | 49 | def process_data_state0(self): 50 | twunnel3.logger.trace("HTTPSInputProtocol.process_data_state0") 51 | 52 | data = self.data 53 | 54 | i = data.find(b"\r\n\r\n") 55 | 56 | if i == -1: 57 | return True 58 | 59 | i = i + 4 60 | 61 | request = data[:i] 62 | 63 | data = data[i:] 64 | 65 | self.data = data 66 | 67 | request_lines = request.split(b"\r\n") 68 | request_line = request_lines[0].split(b" ", 2) 69 | 70 | if len(request_line) != 3: 71 | response = b"HTTP/1.1 400 Bad Request\r\n" 72 | response = response + b"\r\n" 73 | 74 | self.transport.write(response) 75 | self.transport.close() 76 | 77 | return True 78 | 79 | request_method = request_line[0].upper() 80 | request_uri = request_line[1] 81 | request_version = request_line[2].upper() 82 | 83 | if request_method == b"CONNECT": 84 | address = b"" 85 | port = 0 86 | 87 | i1 = request_uri.find(b"[") 88 | i2 = request_uri.find(b"]") 89 | i3 = request_uri.rfind(b":") 90 | 91 | if i3 > i2: 92 | address = request_uri[:i3] 93 | port = int(request_uri[i3 + 1:]) 94 | else: 95 | address = request_uri 96 | port = 443 97 | 98 | if i2 > i1: 99 | address = address[i1 + 1:i2] 100 | 101 | self.remote_address = address.decode() 102 | self.remote_port = port 103 | 104 | twunnel3.logger.debug("remote_address: " + self.remote_address) 105 | twunnel3.logger.debug("remote_port: " + str(self.remote_port)) 106 | 107 | self.output_protocol_connection_manager.connect(self.remote_address, self.remote_port, self) 108 | 109 | return True 110 | else: 111 | response = b"HTTP/1.1 405 Method Not Allowed\r\n" 112 | response = response + b"Allow: CONNECT\r\n" 113 | response = response + b"\r\n" 114 | 115 | self.transport.write(response) 116 | self.transport.close() 117 | 118 | return True 119 | 120 | def process_data_state1(self): 121 | twunnel3.logger.trace("HTTPSInputProtocol.process_data_state1") 122 | 123 | self.output_protocol.input_protocol__data_received(self.data) 124 | 125 | self.data = b"" 126 | 127 | return True 128 | 129 | def output_protocol__connection_made(self, transport): 130 | twunnel3.logger.trace("HTTPSInputProtocol.output_protocol__connection_made") 131 | 132 | if self.connection_state == 1: 133 | response = b"HTTP/1.1 200 OK\r\n" 134 | response = response + b"\r\n" 135 | 136 | self.transport.write(response) 137 | 138 | self.output_protocol.input_protocol__connection_made(self.transport) 139 | if len(self.data) > 0: 140 | self.output_protocol.input_protocol__data_received(self.data) 141 | 142 | self.data = b"" 143 | self.data_state = 1 144 | else: 145 | if self.connection_state == 2: 146 | self.output_protocol.input_protocol__connection_lost(None) 147 | 148 | def output_protocol__connection_lost(self, exception): 149 | twunnel3.logger.trace("HTTPSInputProtocol.output_protocol__connection_lost") 150 | 151 | if self.connection_state == 1: 152 | if self.data_state == 1: 153 | self.transport.close() 154 | else: 155 | response = b"HTTP/1.1 404 Not Found\r\n" 156 | response = response + b"\r\n" 157 | 158 | self.transport.write(response) 159 | self.transport.close() 160 | else: 161 | if self.connection_state == 2: 162 | self.output_protocol.input_protocol__connection_lost(None) 163 | 164 | def output_protocol__data_received(self, data): 165 | twunnel3.logger.trace("HTTPSInputProtocol.output_protocol__data_received") 166 | 167 | if self.connection_state == 1: 168 | self.transport.write(data) 169 | else: 170 | if self.connection_state == 2: 171 | self.output_protocol.input_protocol__connection_lost(None) 172 | 173 | def pause_writing(self): 174 | twunnel3.logger.trace("HTTPSInputProtocol.pause_reading") 175 | 176 | if self.connection_state == 1: 177 | self.transport.pause_reading() 178 | 179 | def resume_writing(self): 180 | twunnel3.logger.trace("HTTPSInputProtocol.resume_writing") 181 | 182 | if self.connection_state == 1: 183 | self.transport.resume_reading() 184 | 185 | class HTTPSInputProtocolFactory(object): 186 | protocol = HTTPSInputProtocol 187 | 188 | def __init__(self, configuration, output_protocol_connection_manager): 189 | twunnel3.logger.trace("HTTPSInputProtocolFactory.__init__") 190 | 191 | self.configuration = configuration 192 | self.output_protocol_connection_manager = output_protocol_connection_manager 193 | 194 | def __call__(self): 195 | twunnel3.logger.trace("HTTPSInputProtocolFactory.__call__") 196 | 197 | input_protocol = HTTPSInputProtocol() 198 | input_protocol.configuration = self.configuration 199 | input_protocol.output_protocol_connection_manager = self.output_protocol_connection_manager 200 | return input_protocol 201 | 202 | def create_https_server(configuration, output_protocol_connection_manager): 203 | input_protocol_factory = HTTPSInputProtocolFactory(configuration, output_protocol_connection_manager) 204 | return asyncio.get_event_loop().create_server(input_protocol_factory, host=configuration["LOCAL_PROXY_SERVER"]["ADDRESS"], port=configuration["LOCAL_PROXY_SERVER"]["PORT"]) -------------------------------------------------------------------------------- /twunnel3/local_proxy_server__socks4.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import socket 6 | import struct 7 | import twunnel3.logger 8 | 9 | class SOCKS4InputProtocol(asyncio.Protocol): 10 | def __init__(self): 11 | twunnel3.logger.trace("SOCKS4InputProtocol.__init__") 12 | 13 | self.configuration = None 14 | self.output_protocol_connection_manager = None 15 | self.output_protocol = None 16 | self.remote_address = "" 17 | self.remote_port = 0 18 | self.connection_state = 0 19 | self.data = b"" 20 | self.data_state = 0 21 | self.transport = None 22 | 23 | def connection_made(self, transport): 24 | twunnel3.logger.trace("SOCKS4InputProtocol.connection_made") 25 | 26 | self.transport = transport 27 | 28 | self.connection_state = 1 29 | 30 | def connection_lost(self, exception): 31 | twunnel3.logger.trace("SOCKS4InputProtocol.connection_lost") 32 | 33 | self.connection_state = 2 34 | 35 | if self.output_protocol is not None: 36 | self.output_protocol.input_protocol__connection_lost(exception) 37 | 38 | self.transport = None 39 | 40 | def data_received(self, data): 41 | twunnel3.logger.trace("SOCKS4InputProtocol.data_received") 42 | 43 | self.data = self.data + data 44 | if self.data_state == 0: 45 | if self.process_data_state0(): 46 | return 47 | if self.data_state == 1: 48 | if self.process_data_state1(): 49 | return 50 | 51 | def process_data_state0(self): 52 | twunnel3.logger.trace("SOCKS4InputProtocol.process_data_state0") 53 | 54 | data = self.data 55 | 56 | if len(data) < 8: 57 | return True 58 | 59 | version, method, port, address = struct.unpack("!BBHI", data[:8]) 60 | 61 | data = data[8:] 62 | 63 | address_type = 0x01 64 | if address >= 1 and address <= 255: 65 | address_type = 0x03 66 | 67 | self.remote_port = port 68 | 69 | if address_type == 0x01: 70 | address = struct.pack("!I", address) 71 | address = socket.inet_ntop(socket.AF_INET, address) 72 | 73 | self.remote_address = address 74 | 75 | if b"\x00" not in data: 76 | return True 77 | 78 | name, data = data.split(b"\x00", 1) 79 | 80 | if address_type == 0x03: 81 | if b"\x00" not in data: 82 | return True 83 | 84 | address, data = data.split(b"\x00", 1) 85 | 86 | self.remote_address = address.decode() 87 | 88 | self.data = data 89 | 90 | twunnel3.logger.debug("remote_address: " + self.remote_address) 91 | twunnel3.logger.debug("remote_port: " + str(self.remote_port)) 92 | 93 | if method == 0x01: 94 | self.output_protocol_connection_manager.connect(self.remote_address, self.remote_port, self) 95 | 96 | return True 97 | else: 98 | response = struct.pack("!BBHI", 0x00, 0x5b, 0, 0) 99 | 100 | self.transport.write(response) 101 | self.transport.close() 102 | 103 | return True 104 | 105 | def process_data_state1(self): 106 | twunnel3.logger.trace("SOCKS4InputProtocol.process_data_state1") 107 | 108 | self.output_protocol.input_protocol__data_received(self.data) 109 | 110 | self.data = b"" 111 | 112 | return True 113 | 114 | def output_protocol__connection_made(self, transport): 115 | twunnel3.logger.trace("SOCKS4InputProtocol.output_protocol__connection_made") 116 | 117 | if self.connection_state == 1: 118 | response = struct.pack("!BBHI", 0x00, 0x5a, 0, 0) 119 | 120 | self.transport.write(response) 121 | 122 | self.output_protocol.input_protocol__connection_made(self.transport) 123 | if len(self.data) > 0: 124 | self.output_protocol.input_protocol__data_received(self.data) 125 | 126 | self.data = b"" 127 | self.data_state = 1 128 | else: 129 | if self.connection_state == 2: 130 | self.output_protocol.input_protocol__connection_lost(None) 131 | 132 | def output_protocol__connection_lost(self, exception): 133 | twunnel3.logger.trace("SOCKS4InputProtocol.output_protocol__connection_lost") 134 | 135 | if self.connection_state == 1: 136 | if self.data_state != 1: 137 | response = struct.pack("!BBHI", 0x00, 0x5b, 0, 0) 138 | 139 | self.transport.write(response) 140 | self.transport.close() 141 | else: 142 | self.transport.close() 143 | else: 144 | if self.connection_state == 2: 145 | self.output_protocol.input_protocol__connection_lost(None) 146 | 147 | def output_protocol__data_received(self, data): 148 | twunnel3.logger.trace("SOCKS4InputProtocol.output_protocol__data_received") 149 | 150 | if self.connection_state == 1: 151 | self.transport.write(data) 152 | else: 153 | if self.connection_state == 2: 154 | self.output_protocol.input_protocol__connection_lost(None) 155 | 156 | def pause_writing(self): 157 | twunnel3.logger.trace("SOCKS4InputProtocol.pause_reading") 158 | 159 | if self.connection_state == 1: 160 | self.transport.pause_reading() 161 | 162 | def resume_writing(self): 163 | twunnel3.logger.trace("SOCKS4InputProtocol.resume_writing") 164 | 165 | if self.connection_state == 1: 166 | self.transport.resume_reading() 167 | 168 | class SOCKS4InputProtocolFactory(object): 169 | def __init__(self, configuration, output_protocol_connection_manager): 170 | twunnel3.logger.trace("SOCKS4InputProtocolFactory.__init__") 171 | 172 | self.configuration = configuration 173 | self.output_protocol_connection_manager = output_protocol_connection_manager 174 | 175 | def __call__(self): 176 | twunnel3.logger.trace("SOCKS4InputProtocolFactory.__call__") 177 | 178 | input_protocol = SOCKS4InputProtocol() 179 | input_protocol.configuration = self.configuration 180 | input_protocol.output_protocol_connection_manager = self.output_protocol_connection_manager 181 | return input_protocol 182 | 183 | def create_socks4_server(configuration, output_protocol_connection_manager): 184 | input_protocol_factory = SOCKS4InputProtocolFactory(configuration, output_protocol_connection_manager) 185 | return asyncio.get_event_loop().create_server(input_protocol_factory, host=configuration["LOCAL_PROXY_SERVER"]["ADDRESS"], port=configuration["LOCAL_PROXY_SERVER"]["PORT"]) -------------------------------------------------------------------------------- /twunnel3/local_proxy_server__socks5.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import socket 6 | import struct 7 | import twunnel3.logger 8 | 9 | class SOCKS5InputProtocol(asyncio.Protocol): 10 | def __init__(self): 11 | twunnel3.logger.trace("SOCKS5InputProtocol.__init__") 12 | 13 | self.configuration = None 14 | self.output_protocol_connection_manager = None 15 | self.output_protocol = None 16 | self.remote_address = "" 17 | self.remote_port = 0 18 | self.connection_state = 0 19 | self.data = b"" 20 | self.data_state = 0 21 | self.transport = None 22 | 23 | def connection_made(self, transport): 24 | twunnel3.logger.trace("SOCKS5InputProtocol.connection_made") 25 | 26 | self.transport = transport 27 | 28 | self.connection_state = 1 29 | 30 | def connection_lost(self, exception): 31 | twunnel3.logger.trace("SOCKS5InputProtocol.connection_lost") 32 | 33 | self.connection_state = 2 34 | 35 | if self.output_protocol is not None: 36 | self.output_protocol.input_protocol__connection_lost(exception) 37 | 38 | self.transport = None 39 | 40 | def data_received(self, data): 41 | twunnel3.logger.trace("SOCKS5InputProtocol.data_received") 42 | 43 | self.data = self.data + data 44 | if self.data_state == 0: 45 | if self.process_data_state0(): 46 | return 47 | if self.data_state == 1: 48 | if self.process_data_state1(): 49 | return 50 | if self.data_state == 2: 51 | if self.process_data_state2(): 52 | return 53 | if self.data_state == 3: 54 | if self.process_data_state3(): 55 | return 56 | 57 | def process_data_state0(self): 58 | twunnel3.logger.trace("SOCKS5InputProtocol.process_data_state0") 59 | 60 | data = self.data 61 | 62 | if len(data) < 2: 63 | return True 64 | 65 | version, number_of_methods = struct.unpack("!BB", data[:2]) 66 | 67 | data = data[2:] 68 | 69 | if len(data) < number_of_methods: 70 | return True 71 | 72 | methods = struct.unpack("!%dB" % number_of_methods, data[:number_of_methods]) 73 | 74 | data = data[number_of_methods:] 75 | 76 | self.data = data 77 | 78 | supported_methods = [] 79 | if len(self.configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"]) == 0: 80 | supported_methods.append(0x00) 81 | else: 82 | supported_methods.append(0x02) 83 | 84 | for supported_method in supported_methods: 85 | if supported_method in methods: 86 | if supported_method == 0x00: 87 | response = struct.pack("!BB", 0x05, 0x00) 88 | 89 | self.transport.write(response) 90 | 91 | self.data_state = 2 92 | 93 | return False 94 | else: 95 | if supported_method == 0x02: 96 | response = struct.pack("!BB", 0x05, 0x02) 97 | 98 | self.transport.write(response) 99 | 100 | self.data_state = 1 101 | 102 | return True 103 | 104 | response = struct.pack("!BB", 0x05, 0xFF) 105 | 106 | self.transport.write(response) 107 | self.transport.close() 108 | 109 | return True 110 | 111 | def process_data_state1(self): 112 | twunnel3.logger.trace("SOCKS5InputProtocol.process_data_state1") 113 | 114 | data = self.data 115 | 116 | if len(data) < 2: 117 | return True 118 | 119 | version, name_length = struct.unpack("!BB", data[:2]) 120 | 121 | data = data[2:] 122 | 123 | if len(data) < name_length: 124 | return True 125 | 126 | name, = struct.unpack("!%ds" % name_length, data[:name_length]) 127 | 128 | data = data[name_length:] 129 | 130 | if len(data) < 1: 131 | return True 132 | 133 | password_length, = struct.unpack("!B", data[:1]) 134 | 135 | data = data[1:] 136 | 137 | if len(data) < password_length: 138 | return True 139 | 140 | password, = struct.unpack("!%ds" % password_length, data[:password_length]) 141 | 142 | data = data[password_length:] 143 | 144 | self.data = data 145 | 146 | i = 0 147 | while i < len(self.configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"]): 148 | if self.configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"][i]["NAME"].encode() == name: 149 | if self.configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"][i]["PASSWORD"].encode() == password: 150 | response = struct.pack("!BB", 0x05, 0x00) 151 | 152 | self.transport.write(response) 153 | 154 | self.data_state = 2 155 | 156 | return True 157 | 158 | response = struct.pack("!BB", 0x05, 0x01) 159 | 160 | self.transport.write(response) 161 | self.transport.close() 162 | 163 | return True 164 | 165 | i = i + 1 166 | 167 | response = struct.pack("!BB", 0x05, 0x01) 168 | 169 | self.transport.write(response) 170 | self.transport.close() 171 | 172 | return True 173 | 174 | def process_data_state2(self): 175 | twunnel3.logger.trace("SOCKS5InputProtocol.process_data_state2") 176 | 177 | data = self.data 178 | 179 | if len(data) < 4: 180 | return True 181 | 182 | version, method, reserved, address_type = struct.unpack("!BBBB", data[:4]) 183 | 184 | data = data[4:] 185 | 186 | if address_type == 0x01: 187 | if len(data) < 4: 188 | return True 189 | 190 | address, = struct.unpack("!I", data[:4]) 191 | address = struct.pack("!I", address) 192 | address = socket.inet_ntop(socket.AF_INET, address) 193 | 194 | self.remote_address = address 195 | 196 | data = data[4:] 197 | else: 198 | if address_type == 0x03: 199 | if len(data) < 1: 200 | return True 201 | 202 | address_length, = struct.unpack("!B", data[:1]) 203 | 204 | data = data[1:] 205 | 206 | if len(data) < address_length: 207 | return True 208 | 209 | address, = struct.unpack("!%ds" % address_length, data[:address_length]) 210 | 211 | self.remote_address = address.decode() 212 | 213 | data = data[address_length:] 214 | else: 215 | if address_type == 0x04: 216 | if len(data) < 16: 217 | return True 218 | 219 | address1, address2, address3, address4 = struct.unpack("!IIII", data[:16]) 220 | address = struct.pack("!IIII", address1, address2, address3, address4) 221 | address = socket.inet_ntop(socket.AF_INET6, address) 222 | 223 | self.remote_address = address 224 | 225 | data = data[16:] 226 | 227 | if len(data) < 2: 228 | return True 229 | 230 | port, = struct.unpack("!H", data[:2]) 231 | 232 | self.remote_port = port 233 | 234 | data = data[2:] 235 | 236 | self.data = data 237 | 238 | twunnel3.logger.debug("remote_address: " + self.remote_address) 239 | twunnel3.logger.debug("remote_port: " + str(self.remote_port)) 240 | 241 | if method == 0x01: 242 | self.output_protocol_connection_manager.connect(self.remote_address, self.remote_port, self) 243 | 244 | return True 245 | else: 246 | response = struct.pack("!BBBBIH", 0x05, 0x07, 0x00, 0x01, 0, 0) 247 | 248 | self.transport.write(response) 249 | self.transport.close() 250 | 251 | return True 252 | 253 | def process_data_state3(self): 254 | twunnel3.logger.trace("SOCKS5InputProtocol.process_data_state3") 255 | 256 | self.output_protocol.input_protocol__data_received(self.data) 257 | 258 | self.data = b"" 259 | 260 | return True 261 | 262 | def output_protocol__connection_made(self, transport): 263 | twunnel3.logger.trace("SOCKS5InputProtocol.output_protocol__connection_made") 264 | 265 | if self.connection_state == 1: 266 | response = struct.pack("!BBBBIH", 0x05, 0x00, 0x00, 0x01, 0, 0) 267 | 268 | self.transport.write(response) 269 | 270 | self.output_protocol.input_protocol__connection_made(self.transport) 271 | if len(self.data) > 0: 272 | self.output_protocol.input_protocol__data_received(self.data) 273 | 274 | self.data = b"" 275 | self.data_state = 3 276 | else: 277 | if self.connection_state == 2: 278 | self.output_protocol.input_protocol__connection_lost(None) 279 | 280 | def output_protocol__connection_lost(self, exception): 281 | twunnel3.logger.trace("SOCKS5InputProtocol.output_protocol__connection_lost") 282 | 283 | if self.connection_state == 1: 284 | if self.data_state != 3: 285 | response = struct.pack("!BBBBIH", 0x05, 0x05, 0x00, 0x01, 0, 0) 286 | 287 | self.transport.write(response) 288 | self.transport.close() 289 | else: 290 | self.transport.close() 291 | else: 292 | if self.connection_state == 2: 293 | self.output_protocol.input_protocol__connection_lost(None) 294 | 295 | def output_protocol__data_received(self, data): 296 | twunnel3.logger.trace("SOCKS5InputProtocol.output_protocol__data_received") 297 | 298 | if self.connection_state == 1: 299 | self.transport.write(data) 300 | else: 301 | if self.connection_state == 2: 302 | self.output_protocol.input_protocol__connection_lost(None) 303 | 304 | def pause_writing(self): 305 | twunnel3.logger.trace("SOCKS5InputProtocol.pause_reading") 306 | 307 | if self.connection_state == 1: 308 | self.transport.pause_reading() 309 | 310 | def resume_writing(self): 311 | twunnel3.logger.trace("SOCKS5InputProtocol.resume_writing") 312 | 313 | if self.connection_state == 1: 314 | self.transport.resume_reading() 315 | 316 | class SOCKS5InputProtocolFactory(object): 317 | def __init__(self, configuration, output_protocol_connection_manager): 318 | twunnel3.logger.trace("SOCKS5InputProtocolFactory.__init__") 319 | 320 | self.configuration = configuration 321 | self.output_protocol_connection_manager = output_protocol_connection_manager 322 | 323 | def __call__(self): 324 | twunnel3.logger.trace("SOCKS5InputProtocolFactory.__call__") 325 | 326 | input_protocol = SOCKS5InputProtocol() 327 | input_protocol.configuration = self.configuration 328 | input_protocol.output_protocol_connection_manager = self.output_protocol_connection_manager 329 | return input_protocol 330 | 331 | def create_socks5_server(configuration, output_protocol_connection_manager): 332 | input_protocol_factory = SOCKS5InputProtocolFactory(configuration, output_protocol_connection_manager) 333 | return asyncio.get_event_loop().create_server(input_protocol_factory, host=configuration["LOCAL_PROXY_SERVER"]["ADDRESS"], port=configuration["LOCAL_PROXY_SERVER"]["PORT"]) -------------------------------------------------------------------------------- /twunnel3/local_proxy_server__ssl.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import ssl as _ssl 6 | import twunnel3.local_proxy_server 7 | import twunnel3.logger 8 | import twunnel3.proxy_server 9 | import twunnel3.proxy_server__socks5 10 | 11 | class SSLOutputProtocolConnection(object): 12 | def __init__(self, configuration): 13 | twunnel3.logger.trace("SSLOutputProtocolConnection.__init__") 14 | 15 | self.configuration = configuration 16 | 17 | def connect(self, remote_address, remote_port, input_protocol): 18 | twunnel3.logger.trace("SSLOutputProtocolConnection.connect") 19 | 20 | configuration = {} 21 | configuration["PROXY_SERVER"] = {} 22 | configuration["PROXY_SERVER"]["TYPE"] = "SOCKS5" 23 | configuration["PROXY_SERVER"]["ADDRESS"] = self.configuration["REMOTE_PROXY_SERVER"]["ADDRESS"] 24 | configuration["PROXY_SERVER"]["PORT"] = self.configuration["REMOTE_PROXY_SERVER"]["PORT"] 25 | configuration["PROXY_SERVER"]["ACCOUNT"] = {} 26 | configuration["PROXY_SERVER"]["ACCOUNT"]["NAME"] = self.configuration["REMOTE_PROXY_SERVER"]["ACCOUNT"]["NAME"] 27 | configuration["PROXY_SERVER"]["ACCOUNT"]["PASSWORD"] = self.configuration["REMOTE_PROXY_SERVER"]["ACCOUNT"]["PASSWORD"] 28 | 29 | output_protocol_factory = twunnel3.local_proxy_server.OutputProtocolFactory(input_protocol) 30 | 31 | tunnel_output_protocol_factory = twunnel3.proxy_server__socks5.SOCKS5TunnelOutputProtocolFactory(configuration, remote_address, remote_port) 32 | tunnel_protocol_factory = twunnel3.proxy_server.TunnelProtocolFactory(tunnel_output_protocol_factory, output_protocol_factory, None, None) 33 | 34 | ssl = _ssl.SSLContext(_ssl.PROTOCOL_SSLv23) 35 | ssl.options |= _ssl.OP_NO_SSLv2 36 | ssl.set_default_verify_paths() 37 | ssl.verify_mode = _ssl.CERT_REQUIRED 38 | if self.configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"]["AUTHORITY"]["FILE"] != "": 39 | ssl.load_verify_locations(cafile=self.configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"]["AUTHORITY"]["FILE"]) 40 | 41 | ssl_address = self.configuration["REMOTE_PROXY_SERVER"]["ADDRESS"] 42 | if self.configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"]["ADDRESS"] != "": 43 | ssl_address = self.configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"]["ADDRESS"] 44 | 45 | tunnel = twunnel3.proxy_server.create_tunnel(self.configuration) 46 | asyncio.async(tunnel.create_connection(tunnel_protocol_factory, address=self.configuration["REMOTE_PROXY_SERVER"]["ADDRESS"], port=self.configuration["REMOTE_PROXY_SERVER"]["PORT"], ssl=ssl, ssl_address=ssl_address)) -------------------------------------------------------------------------------- /twunnel3/logger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import time 5 | import datetime 6 | 7 | class LoggerMessage(object): 8 | def __init__(self): 9 | self.time = 0 10 | self.level = 0 11 | self.text = "" 12 | 13 | class Logger(object): 14 | def __init__(self, configuration): 15 | self.configuration = configuration 16 | 17 | def log(self, message_level, message_text, *message_text_arguments): 18 | if message_level <= self.configuration["LOGGER"]["LEVEL"]: 19 | message = LoggerMessage() 20 | message.time = int(round(time.time() * 1000)) 21 | message.level = message_level 22 | message.text = message_text.format(*message_text_arguments) 23 | 24 | self.print_message(message) 25 | 26 | def print_message(self, message): 27 | print(self.format_message(message)) 28 | 29 | def format_message(self, message): 30 | return "{0} - {1} - {2}".format(self.format_message_time(message.time), self.format_message_level(message.level), self.format_message_text(message.text)) 31 | 32 | def format_message_time(self, message_time): 33 | return datetime.datetime.utcfromtimestamp(message_time / 1000).strftime("%Y-%m-%d %H:%M:%S.%f") 34 | 35 | def format_message_level(self, message_level): 36 | if message_level == 1: 37 | return "ERROR" 38 | elif message_level == 2: 39 | return "WARNING" 40 | elif message_level == 3: 41 | return "INFO" 42 | elif message_level == 4: 43 | return "DEBUG" 44 | elif message_level == 5: 45 | return "TRACE" 46 | else: 47 | return "" 48 | 49 | def format_message_text(self, message_text): 50 | return message_text 51 | 52 | default_logger = None 53 | default_logger_class = Logger 54 | 55 | def get_default_logger_class(): 56 | global default_logger_class 57 | 58 | return default_logger_class 59 | 60 | def set_default_logger_class(logger_class): 61 | global default_logger_class 62 | 63 | default_logger_class = logger_class 64 | 65 | def set_default_configuration(configuration, keys): 66 | if "LOGGER" in keys: 67 | configuration.setdefault("LOGGER", {}) 68 | configuration["LOGGER"].setdefault("LEVEL", 0) 69 | 70 | def configure(configuration): 71 | global default_logger, default_logger_class 72 | 73 | set_default_configuration(configuration, ["LOGGER"]) 74 | 75 | default_logger = default_logger_class(configuration) 76 | 77 | def log(message_level, message_text, *message_text_arguments): 78 | global default_logger 79 | 80 | if default_logger is None: 81 | configuration = {} 82 | configure(configuration) 83 | 84 | default_logger.log(message_level, message_text, *message_text_arguments) 85 | 86 | def error(message_text, *message_text_arguments): 87 | log(1, message_text, *message_text_arguments) 88 | 89 | def warning(message_text, *message_text_arguments): 90 | log(2, message_text, *message_text_arguments) 91 | 92 | def info(message_text, *message_text_arguments): 93 | log(3, message_text, *message_text_arguments) 94 | 95 | def debug(message_text, *message_text_arguments): 96 | log(4, message_text, *message_text_arguments) 97 | 98 | def trace(message_text, *message_text_arguments): 99 | log(5, message_text, *message_text_arguments) -------------------------------------------------------------------------------- /twunnel3/proxy_server.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import socket 6 | import twunnel3.logger 7 | 8 | def is_ipv4_address(address): 9 | try: 10 | socket.inet_pton(socket.AF_INET, address) 11 | except socket.error: 12 | return False 13 | return True 14 | 15 | def is_ipv6_address(address): 16 | try: 17 | socket.inet_pton(socket.AF_INET6, address) 18 | except socket.error: 19 | return False 20 | return True 21 | 22 | def set_default_configuration(configuration, keys): 23 | if "PROXY_SERVERS" in keys: 24 | configuration.setdefault("PROXY_SERVERS", []) 25 | i = 0 26 | while i < len(configuration["PROXY_SERVERS"]): 27 | configuration["PROXY_SERVERS"][i].setdefault("TYPE", "") 28 | if configuration["PROXY_SERVERS"][i]["TYPE"] == "HTTPS": 29 | configuration["PROXY_SERVERS"][i].setdefault("ADDRESS", "") 30 | configuration["PROXY_SERVERS"][i].setdefault("PORT", 0) 31 | configuration["PROXY_SERVERS"][i].setdefault("ACCOUNT", {}) 32 | configuration["PROXY_SERVERS"][i]["ACCOUNT"].setdefault("NAME", "") 33 | configuration["PROXY_SERVERS"][i]["ACCOUNT"].setdefault("PASSWORD", "") 34 | else: 35 | if configuration["PROXY_SERVERS"][i]["TYPE"] == "SOCKS4": 36 | configuration["PROXY_SERVERS"][i].setdefault("ADDRESS", "") 37 | configuration["PROXY_SERVERS"][i].setdefault("PORT", 0) 38 | configuration["PROXY_SERVERS"][i].setdefault("ACCOUNT", {}) 39 | configuration["PROXY_SERVERS"][i]["ACCOUNT"].setdefault("NAME", "") 40 | else: 41 | if configuration["PROXY_SERVERS"][i]["TYPE"] == "SOCKS5": 42 | configuration["PROXY_SERVERS"][i].setdefault("ADDRESS", "") 43 | configuration["PROXY_SERVERS"][i].setdefault("PORT", 0) 44 | configuration["PROXY_SERVERS"][i].setdefault("ACCOUNT", {}) 45 | configuration["PROXY_SERVERS"][i]["ACCOUNT"].setdefault("NAME", "") 46 | configuration["PROXY_SERVERS"][i]["ACCOUNT"].setdefault("PASSWORD", "") 47 | i = i + 1 48 | 49 | class TunnelProtocol(asyncio.Protocol): 50 | def __init__(self): 51 | twunnel3.logger.trace("TunnelProtocol.__init__") 52 | 53 | self.data = b"" 54 | self.factory = None 55 | self.transport = None 56 | 57 | def connection_made(self, transport): 58 | twunnel3.logger.trace("TunnelProtocol.connection_made") 59 | 60 | self.transport = transport 61 | 62 | if self.factory.tunnel_output_protocol is None: 63 | self.factory.tunnel_output_protocol_factory.tunnel_protocol = self 64 | self.factory.tunnel_output_protocol = self.factory.tunnel_output_protocol_factory() 65 | self.factory.tunnel_output_protocol.connection_made(self.transport) 66 | else: 67 | if self.factory.output_protocol is None: 68 | self.factory.tunnel_output_protocol = None 69 | 70 | self.factory.output_protocol = self.factory.output_protocol_factory() 71 | self.factory.output_protocol.connection_made(self.transport) 72 | 73 | if len(self.data) > 0: 74 | self.factory.output_protocol.data_received(self.data) 75 | 76 | self.data = b"" 77 | 78 | def connection_lost(self, exception): 79 | twunnel3.logger.trace("TunnelProtocol.connection_lost") 80 | 81 | if self.factory.tunnel_output_protocol is not None: 82 | self.factory.tunnel_output_protocol.connection_lost(exception) 83 | else: 84 | if self.factory.output_protocol is not None: 85 | self.factory.output_protocol.connection_lost(exception) 86 | 87 | self.transport = None 88 | 89 | def data_received(self, data): 90 | twunnel3.logger.trace("TunnelProtocol.data_received") 91 | 92 | if self.factory.tunnel_output_protocol is not None: 93 | self.factory.tunnel_output_protocol.data_received(data) 94 | else: 95 | if self.factory.output_protocol is not None: 96 | self.factory.output_protocol.data_received(data) 97 | 98 | def tunnel_output_protocol__connection_made(self, transport, data): 99 | twunnel3.logger.trace("TunnelProtocol.tunnel_output_protocol__connection_made") 100 | 101 | self.data = data 102 | 103 | asyncio.async(asyncio.get_event_loop().create_connection(lambda: self, sock=self.transport.get_extra_info("socket"), ssl=self.factory.ssl, server_hostname=self.factory.ssl_address)) 104 | 105 | class TunnelProtocolFactory(object): 106 | def __init__(self, tunnel_output_protocol_factory, output_protocol_factory, ssl, ssl_address): 107 | twunnel3.logger.trace("TunnelProtocolFactory.__init__") 108 | 109 | self.tunnel_output_protocol = None 110 | self.tunnel_output_protocol_factory = tunnel_output_protocol_factory 111 | self.output_protocol = None 112 | self.output_protocol_factory = output_protocol_factory 113 | self.ssl = ssl 114 | self.ssl_address = ssl_address 115 | 116 | def __call__(self): 117 | twunnel3.logger.trace("TunnelProtocolFactory.__call__") 118 | 119 | protocol = TunnelProtocol() 120 | protocol.factory = self 121 | return protocol 122 | 123 | class Tunnel(object): 124 | def __init__(self, configuration): 125 | twunnel3.logger.trace("Tunnel.__init__") 126 | 127 | self.configuration = configuration 128 | 129 | def create_connection(self, output_protocol_factory, address=None, port=None, *, local_address=None, local_port=None, address_family=0, address_protocol=0, address_flags=0, ssl=None, ssl_address=None): 130 | twunnel3.logger.trace("Tunnel.create_connection") 131 | 132 | local_address_port = None 133 | if local_address is not None or local_port is not None: 134 | local_address_port = (local_address, local_port) 135 | 136 | if ssl and not ssl_address: 137 | ssl_address = address 138 | 139 | if len(self.configuration["PROXY_SERVERS"]) == 0: 140 | return asyncio.get_event_loop().create_connection(output_protocol_factory, host=address, port=port, local_addr=local_address_port, family=address_family, proto=address_protocol, flags=address_flags, ssl=ssl, server_hostname=ssl_address) 141 | else: 142 | i = len(self.configuration["PROXY_SERVERS"]) 143 | 144 | configuration = {} 145 | configuration["PROXY_SERVER"] = self.configuration["PROXY_SERVERS"][i - 1] 146 | 147 | tunnel_output_protocol_factory_class = self.get_tunnel_output_protocol_factory_class(configuration["PROXY_SERVER"]["TYPE"]) 148 | tunnel_output_protocol_factory = tunnel_output_protocol_factory_class(configuration, address, port) 149 | 150 | tunnel_protocol_factory = TunnelProtocolFactory(tunnel_output_protocol_factory, output_protocol_factory, ssl, ssl_address) 151 | 152 | i = i - 1 153 | 154 | while i > 0: 155 | configuration = {} 156 | configuration["PROXY_SERVER"] = self.configuration["PROXY_SERVERS"][i - 1] 157 | 158 | tunnel_output_protocol_factory_class = self.get_tunnel_output_protocol_factory_class(configuration["PROXY_SERVER"]["TYPE"]) 159 | tunnel_output_protocol_factory = tunnel_output_protocol_factory_class(configuration, self.configuration["PROXY_SERVERS"][i]["ADDRESS"], self.configuration["PROXY_SERVERS"][i]["PORT"]) 160 | 161 | tunnel_protocol_factory = TunnelProtocolFactory(tunnel_output_protocol_factory, tunnel_protocol_factory, None, None) 162 | 163 | i = i - 1 164 | 165 | return asyncio.get_event_loop().create_connection(tunnel_protocol_factory, host=self.configuration["PROXY_SERVERS"][i]["ADDRESS"], port=self.configuration["PROXY_SERVERS"][i]["PORT"], local_addr=local_address_port, family=address_family, proto=address_protocol, flags=address_flags) 166 | 167 | def get_tunnel_output_protocol_factory_class(self, type): 168 | twunnel3.logger.trace("Tunnel.get_tunnel_output_protocol_factory_class") 169 | 170 | if type == "HTTPS": 171 | from twunnel3.proxy_server__https import HTTPSTunnelOutputProtocolFactory 172 | 173 | return HTTPSTunnelOutputProtocolFactory 174 | else: 175 | if type == "SOCKS4": 176 | from twunnel3.proxy_server__socks4 import SOCKS4TunnelOutputProtocolFactory 177 | 178 | return SOCKS4TunnelOutputProtocolFactory 179 | else: 180 | if type == "SOCKS5": 181 | from twunnel3.proxy_server__socks5 import SOCKS5TunnelOutputProtocolFactory 182 | 183 | return SOCKS5TunnelOutputProtocolFactory 184 | else: 185 | return None 186 | 187 | default_tunnel_class = Tunnel 188 | 189 | def get_default_tunnel_class(): 190 | global default_tunnel_class 191 | 192 | return default_tunnel_class 193 | 194 | def set_default_tunnel_class(tunnel_class): 195 | global default_tunnel_class 196 | 197 | default_tunnel_class = tunnel_class 198 | 199 | def create_tunnel(configuration): 200 | set_default_configuration(configuration, ["PROXY_SERVERS"]) 201 | 202 | tunnel_class = get_default_tunnel_class() 203 | tunnel = tunnel_class(configuration) 204 | 205 | return tunnel -------------------------------------------------------------------------------- /twunnel3/proxy_server__https.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import base64 6 | import twunnel3.logger 7 | 8 | from twunnel3.proxy_server import is_ipv4_address, is_ipv6_address 9 | 10 | class HTTPSTunnelOutputProtocol(asyncio.Protocol): 11 | def __init__(self): 12 | twunnel3.logger.trace("HTTPSTunnelOutputProtocol.__init__") 13 | 14 | self.data = b"" 15 | self.data_state = 0 16 | self.factory = None 17 | self.transport = None 18 | 19 | def connection_made(self, transport): 20 | twunnel3.logger.trace("HTTPSTunnelOutputProtocol.connection_made") 21 | 22 | self.transport = transport 23 | 24 | request = b"CONNECT " 25 | 26 | if is_ipv6_address(self.factory.address) == True: 27 | request = request + b"[" + self.factory.address.encode() + b"]:" + str(self.factory.port).encode() 28 | else: 29 | request = request + self.factory.address.encode() + b":" + str(self.factory.port).encode() 30 | 31 | request = request + b" HTTP/1.1\r\n" 32 | 33 | if self.factory.configuration["PROXY_SERVER"]["ACCOUNT"]["NAME"].encode() != b"": 34 | request = request + b"Proxy-Authorization: Basic " + base64.standard_b64encode(self.factory.configuration["PROXY_SERVER"]["ACCOUNT"]["NAME"].encode() + b":" + self.factory.configuration["PROXY_SERVER"]["ACCOUNT"]["PASSWORD"].encode()) + b"\r\n" 35 | 36 | request = request + b"\r\n" 37 | 38 | self.transport.write(request) 39 | 40 | def connection_lost(self, exception): 41 | twunnel3.logger.trace("HTTPSTunnelOutputProtocol.connection_lost") 42 | 43 | self.transport = None 44 | 45 | def data_received(self, data): 46 | twunnel3.logger.trace("HTTPSTunnelOutputProtocol.data_received") 47 | 48 | self.data = self.data + data 49 | if self.data_state == 0: 50 | if self.process_data_state0(): 51 | return 52 | 53 | def process_data_state0(self): 54 | twunnel3.logger.trace("HTTPSTunnelOutputProtocol.process_data_state0") 55 | 56 | data = self.data 57 | 58 | i = data.find(b"\r\n\r\n") 59 | 60 | if i == -1: 61 | return True 62 | 63 | i = i + 4 64 | 65 | response = data[:i] 66 | 67 | data = data[i:] 68 | 69 | response_lines = response.split(b"\r\n") 70 | response_line = response_lines[0].split(b" ", 2) 71 | 72 | if len(response_line) != 3: 73 | self.transport.close() 74 | 75 | return True 76 | 77 | response_version = response_line[0].upper() 78 | response_status = response_line[1] 79 | response_status_message = response_line[2] 80 | 81 | if response_status != b"200": 82 | self.transport.close() 83 | 84 | return True 85 | 86 | self.factory.tunnel_protocol.tunnel_output_protocol__connection_made(self.transport, data) 87 | 88 | self.data = b"" 89 | 90 | return True 91 | 92 | class HTTPSTunnelOutputProtocolFactory(object): 93 | def __init__(self, configuration, address, port): 94 | twunnel3.logger.trace("HTTPSTunnelOutputProtocolFactory.__init__") 95 | 96 | self.configuration = configuration 97 | self.address = address 98 | self.port = port 99 | self.tunnel_protocol = None 100 | 101 | def __call__(self): 102 | twunnel3.logger.trace("HTTPSTunnelOutputProtocolFactory.__call__") 103 | 104 | protocol = HTTPSTunnelOutputProtocol() 105 | protocol.factory = self 106 | return protocol -------------------------------------------------------------------------------- /twunnel3/proxy_server__socks4.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import socket 6 | import struct 7 | import twunnel3.logger 8 | 9 | from twunnel3.proxy_server import is_ipv4_address, is_ipv6_address 10 | 11 | class SOCKS4TunnelOutputProtocol(asyncio.Protocol): 12 | def __init__(self): 13 | twunnel3.logger.trace("SOCKS4TunnelOutputProtocol.__init__") 14 | 15 | self.data = b"" 16 | self.data_state = 0 17 | self.factory = None 18 | self.transport = None 19 | 20 | def connection_made(self, transport): 21 | twunnel3.logger.trace("SOCKS4TunnelOutputProtocol.connection_made") 22 | 23 | self.transport = transport 24 | 25 | address_type = 0x03 26 | if is_ipv4_address(self.factory.address) == True: 27 | address_type = 0x01 28 | 29 | request = struct.pack("!BB", 0x04, 0x01) 30 | 31 | port = self.factory.port 32 | 33 | request = request + struct.pack("!H", port) 34 | 35 | address = 0 36 | if address_type == 0x01: 37 | address = self.factory.address 38 | address = socket.inet_pton(socket.AF_INET, address) 39 | address, = struct.unpack("!I", address) 40 | else: 41 | if address_type == 0x03: 42 | address = 1 43 | 44 | request = request + struct.pack("!I", address) 45 | 46 | name = self.factory.configuration["PROXY_SERVER"]["ACCOUNT"]["NAME"].encode() 47 | name = name + b"\x00" 48 | name_length = len(name) 49 | 50 | request = request + struct.pack("!%ds" % name_length, name) 51 | 52 | if address_type == 0x03: 53 | address = self.factory.address.encode() 54 | address = address + b"\x00" 55 | address_length = len(address) 56 | 57 | request = request + struct.pack("!%ds" % address_length, address) 58 | 59 | self.transport.write(request) 60 | 61 | self.data_state = 0 62 | 63 | def connection_lost(self, exception): 64 | twunnel3.logger.trace("SOCKS4TunnelOutputProtocol.connection_lost") 65 | 66 | self.transport = None 67 | 68 | def data_received(self, data): 69 | twunnel3.logger.trace("SOCKS4TunnelOutputProtocol.data_received") 70 | 71 | self.data = self.data + data 72 | if self.data_state == 0: 73 | if self.process_data_state0(): 74 | return 75 | 76 | def process_data_state0(self): 77 | twunnel3.logger.trace("SOCKS4TunnelOutputProtocol.process_data_state0") 78 | 79 | data = self.data 80 | 81 | if len(data) < 8: 82 | return True 83 | 84 | status, = struct.unpack("!B", data[1:2]) 85 | 86 | data = data[8:] 87 | 88 | if status != 0x5a: 89 | self.transport.close() 90 | 91 | return True 92 | 93 | self.factory.tunnel_protocol.tunnel_output_protocol__connection_made(self.transport, data) 94 | 95 | self.data = b"" 96 | 97 | return True 98 | 99 | class SOCKS4TunnelOutputProtocolFactory(object): 100 | def __init__(self, configuration, address, port): 101 | twunnel3.logger.trace("SOCKS4TunnelOutputProtocolFactory.__init__") 102 | 103 | self.configuration = configuration 104 | self.address = address 105 | self.port = port 106 | self.tunnel_protocol = None 107 | 108 | def __call__(self): 109 | twunnel3.logger.trace("SOCKS4TunnelOutputProtocolFactory.__call__") 110 | 111 | protocol = SOCKS4TunnelOutputProtocol() 112 | protocol.factory = self 113 | return protocol -------------------------------------------------------------------------------- /twunnel3/proxy_server__socks5.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import socket 6 | import struct 7 | import twunnel3.logger 8 | 9 | from twunnel3.proxy_server import is_ipv4_address, is_ipv6_address 10 | 11 | class SOCKS5TunnelOutputProtocol(asyncio.Protocol): 12 | def __init__(self): 13 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocol.__init__") 14 | 15 | self.data = b"" 16 | self.data_state = 0 17 | self.factory = None 18 | self.transport = None 19 | 20 | def connection_made(self, transport): 21 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocol.connection_made") 22 | 23 | self.transport = transport 24 | 25 | request = struct.pack("!BBBB", 0x05, 0x02, 0x00, 0x02) 26 | 27 | self.transport.write(request) 28 | 29 | self.data_state = 0 30 | 31 | def connection_lost(self, exception): 32 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocol.connection_lost") 33 | 34 | self.transport = None 35 | 36 | def data_received(self, data): 37 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocol.data_received") 38 | 39 | self.data = self.data + data 40 | if self.data_state == 0: 41 | if self.process_data_state0(): 42 | return 43 | if self.data_state == 1: 44 | if self.process_data_state1(): 45 | return 46 | if self.data_state == 2: 47 | if self.process_data_state2(): 48 | return 49 | if self.data_state == 3: 50 | if self.process_data_state3(): 51 | return 52 | 53 | def process_data_state0(self): 54 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocol.process_data_state0") 55 | 56 | data = self.data 57 | 58 | if len(data) < 2: 59 | return True 60 | 61 | version, method = struct.unpack("!BB", data[:2]) 62 | 63 | data = data[2:] 64 | 65 | self.data = data 66 | 67 | if method == 0x00: 68 | self.data_state = 2 69 | 70 | return False 71 | else: 72 | if method == 0x02: 73 | name = self.factory.configuration["PROXY_SERVER"]["ACCOUNT"]["NAME"].encode() 74 | name_length = len(name) 75 | 76 | password = self.factory.configuration["PROXY_SERVER"]["ACCOUNT"]["PASSWORD"].encode() 77 | password_length = len(password) 78 | 79 | request = struct.pack("!B", 0x01) 80 | request = request + struct.pack("!B%ds" % name_length, name_length, name) 81 | request = request + struct.pack("!B%ds" % password_length, password_length, password) 82 | 83 | self.transport.write(request) 84 | 85 | self.data_state = 1 86 | 87 | return True 88 | else: 89 | self.transport.close() 90 | 91 | return True 92 | 93 | def process_data_state1(self): 94 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocol.process_data_state1") 95 | 96 | data = self.data 97 | 98 | if len(data) < 2: 99 | return True 100 | 101 | version, status = struct.unpack("!BB", data[:2]) 102 | 103 | data = data[2:] 104 | 105 | self.data = data 106 | 107 | if status != 0x00: 108 | self.transport.close() 109 | 110 | return True 111 | 112 | self.data_state = 2 113 | 114 | return False 115 | 116 | def process_data_state2(self): 117 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocol.process_data_state2") 118 | 119 | address_type = 0x03 120 | if is_ipv4_address(self.factory.address) == True: 121 | address_type = 0x01 122 | else: 123 | if is_ipv6_address(self.factory.address) == True: 124 | address_type = 0x04 125 | 126 | request = struct.pack("!BBB", 0x05, 0x01, 0x00) 127 | 128 | if address_type == 0x01: 129 | address = self.factory.address 130 | address = socket.inet_pton(socket.AF_INET, address) 131 | address, = struct.unpack("!I", address) 132 | 133 | request = request + struct.pack("!BI", 0x01, address) 134 | else: 135 | if address_type == 0x03: 136 | address = self.factory.address.encode() 137 | address_length = len(address) 138 | 139 | request = request + struct.pack("!BB%ds" % address_length, 0x03, address_length, address) 140 | else: 141 | if address_type == 0x04: 142 | address = self.factory.address 143 | address = socket.inet_pton(socket.AF_INET6, address) 144 | address1, address2, address3, address4 = struct.unpack("!IIII", address) 145 | 146 | request = request + struct.pack("!BIIII", 0x04, address1, address2, address3, address4) 147 | 148 | port = self.factory.port 149 | 150 | request = request + struct.pack("!H", port) 151 | 152 | self.transport.write(request) 153 | 154 | self.data_state = 3 155 | 156 | return True 157 | 158 | def process_data_state3(self): 159 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocol.process_data_state3") 160 | 161 | data = self.data 162 | 163 | if len(data) < 4: 164 | return True 165 | 166 | version, status, reserved, address_type = struct.unpack("!BBBB", data[:4]) 167 | 168 | data = data[4:] 169 | 170 | if status != 0x00: 171 | self.transport.close() 172 | 173 | return True 174 | 175 | if address_type == 0x01: 176 | if len(data) < 4: 177 | return True 178 | 179 | address, = struct.unpack("!I", data[:4]) 180 | address = struct.pack("!I", address) 181 | address = socket.inet_ntop(socket.AF_INET, address) 182 | 183 | data = data[4:] 184 | else: 185 | if address_type == 0x03: 186 | if len(data) < 1: 187 | return True 188 | 189 | address_length, = struct.unpack("!B", data[:1]) 190 | 191 | data = data[1:] 192 | 193 | if len(data) < address_length: 194 | return True 195 | 196 | address, = struct.unpack("!%ds" % address_length, data[:address_length]) 197 | 198 | data = data[address_length:] 199 | else: 200 | if address_type == 0x04: 201 | if len(data) < 16: 202 | return True 203 | 204 | address1, address2, address3, address4 = struct.unpack("!IIII", data[:16]) 205 | address = struct.pack("!IIII", address1, address2, address3, address4) 206 | address = socket.inet_ntop(socket.AF_INET6, address) 207 | 208 | data = data[16:] 209 | 210 | if len(data) < 2: 211 | return True 212 | 213 | port, = struct.unpack("!H", data[:2]) 214 | 215 | data = data[2:] 216 | 217 | self.factory.tunnel_protocol.tunnel_output_protocol__connection_made(self.transport, data) 218 | 219 | self.data = b"" 220 | 221 | return True 222 | 223 | class SOCKS5TunnelOutputProtocolFactory(object): 224 | def __init__(self, configuration, address, port): 225 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocolFactory.__init__") 226 | 227 | self.configuration = configuration 228 | self.address = address 229 | self.port = port 230 | self.tunnel_protocol = None 231 | 232 | def __call__(self): 233 | twunnel3.logger.trace("SOCKS5TunnelOutputProtocolFactory.__call__") 234 | 235 | protocol = SOCKS5TunnelOutputProtocol() 236 | protocol.factory = self 237 | return protocol -------------------------------------------------------------------------------- /twunnel3/remote_proxy_server.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import twunnel3.proxy_server 5 | 6 | def set_default_configuration(configuration, keys): 7 | twunnel3.proxy_server.set_default_configuration(configuration, keys) 8 | 9 | if "REMOTE_PROXY_SERVER" in keys: 10 | configuration.setdefault("REMOTE_PROXY_SERVER", {}) 11 | configuration["REMOTE_PROXY_SERVER"].setdefault("TYPE", "") 12 | if configuration["REMOTE_PROXY_SERVER"]["TYPE"] == "SSL": 13 | configuration["REMOTE_PROXY_SERVER"].setdefault("ADDRESS", "") 14 | configuration["REMOTE_PROXY_SERVER"].setdefault("PORT", 0) 15 | configuration["REMOTE_PROXY_SERVER"].setdefault("CERTIFICATE", {}) 16 | configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"].setdefault("FILE", "") 17 | configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"].setdefault("KEY", {}) 18 | configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"]["KEY"].setdefault("FILE", "") 19 | configuration["REMOTE_PROXY_SERVER"].setdefault("ACCOUNTS", []) 20 | i = 0 21 | while i < len(configuration["REMOTE_PROXY_SERVER"]["ACCOUNTS"]): 22 | configuration["REMOTE_PROXY_SERVER"]["ACCOUNTS"][i].setdefault("NAME", "") 23 | configuration["REMOTE_PROXY_SERVER"]["ACCOUNTS"][i].setdefault("PASSWORD", "") 24 | i = i + 1 25 | 26 | def create_server(configuration): 27 | set_default_configuration(configuration, ["PROXY_SERVERS", "REMOTE_PROXY_SERVER"]) 28 | 29 | if configuration["REMOTE_PROXY_SERVER"]["TYPE"] == "SSL": 30 | from twunnel3.remote_proxy_server__ssl import create_ssl_server 31 | 32 | return create_ssl_server(configuration) 33 | else: 34 | return None -------------------------------------------------------------------------------- /twunnel3/remote_proxy_server__ssl.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Jeroen Van Steirteghem 2 | # See LICENSE 3 | 4 | import asyncio 5 | import ssl as _ssl 6 | import twunnel3.local_proxy_server 7 | import twunnel3.local_proxy_server__socks5 8 | import twunnel3.logger 9 | import twunnel3.proxy_server 10 | 11 | class SSLInputProtocolFactory(twunnel3.local_proxy_server__socks5.SOCKS5InputProtocolFactory): 12 | def __init__(self, configuration): 13 | twunnel3.logger.trace("SSLInputProtocolFactory.__init__") 14 | 15 | self.configuration = configuration 16 | 17 | configuration = {} 18 | configuration["PROXY_SERVERS"] = self.configuration["PROXY_SERVERS"] 19 | configuration["LOCAL_PROXY_SERVER"] = {} 20 | configuration["LOCAL_PROXY_SERVER"]["TYPE"] = "SOCKS5" 21 | configuration["LOCAL_PROXY_SERVER"]["ADDRESS"] = self.configuration["REMOTE_PROXY_SERVER"]["ADDRESS"] 22 | configuration["LOCAL_PROXY_SERVER"]["PORT"] = self.configuration["REMOTE_PROXY_SERVER"]["PORT"] 23 | configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"] = [] 24 | i = 0 25 | while i < len(self.configuration["REMOTE_PROXY_SERVER"]["ACCOUNTS"]): 26 | configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"].append({}) 27 | configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"][i]["NAME"] = self.configuration["REMOTE_PROXY_SERVER"]["ACCOUNTS"][i]["NAME"] 28 | configuration["LOCAL_PROXY_SERVER"]["ACCOUNTS"][i]["PASSWORD"] = self.configuration["REMOTE_PROXY_SERVER"]["ACCOUNTS"][i]["PASSWORD"] 29 | i = i + 1 30 | configuration["REMOTE_PROXY_SERVERS"] = [] 31 | 32 | output_protocol_connection_manager = twunnel3.local_proxy_server.OutputProtocolConnectionManager(configuration) 33 | 34 | twunnel3.local_proxy_server__socks5.SOCKS5InputProtocolFactory.__init__(self, configuration, output_protocol_connection_manager) 35 | 36 | def create_ssl_server(configuration): 37 | input_protocol_factory = SSLInputProtocolFactory(configuration) 38 | 39 | ssl = _ssl.SSLContext(_ssl.PROTOCOL_SSLv23) 40 | ssl.options |= _ssl.OP_NO_SSLv2 41 | ssl.load_cert_chain(configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"]["FILE"], keyfile=configuration["REMOTE_PROXY_SERVER"]["CERTIFICATE"]["KEY"]["FILE"]) 42 | 43 | return asyncio.get_event_loop().create_server(input_protocol_factory, host=configuration["REMOTE_PROXY_SERVER"]["ADDRESS"], port=configuration["REMOTE_PROXY_SERVER"]["PORT"], ssl=ssl) --------------------------------------------------------------------------------