Skip to content

Commit 0d3921c

Browse files
authored
Add files via upload
1 parent d01e532 commit 0d3921c

19 files changed

+1759
-0
lines changed

config.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"fake_sni": "random",
3+
"full_fake_sni_length": "29-32",
4+
"fake_sni_tld": ".com",
5+
6+
"listen_host": "0.0.0.0",
7+
"listen_port": 12430,
8+
"listen_trojan_password": "domain_fronting_pass",
9+
10+
"main_gateway_host": "127.0.0.1",
11+
"main_gateway_port": 12431,
12+
"main_gateway_trojan_password": "main_gateway_pass",
13+
14+
"bypass_gateway_host": "127.0.0.1",
15+
"bypass_gateway_port": 12432,
16+
"bypass_gateway_trojan_password": "bypass_gateway_pass",
17+
18+
"issuer_certificate_path": "certificate.crt",
19+
"issuer_private_key_path": "private.key",
20+
"issuer_private_key_pass": null,
21+
22+
"instant_certificate_temp_file_path": ".instant_certificate"
23+
24+
}

controller.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import ssl
2+
import string
3+
import random
4+
import secrets
5+
from util.instant_cert_ctx import InstantCertServerSideCtx
6+
from util.proxy_protocols import struct_pure_tcp_trojan
7+
from util.tls_parser import parse_tls_client_hello
8+
from util.timer import Timer
9+
from transports.wrapped_transport import WrappedTransport
10+
from transports.tcp_stream_transport import TcpStreamTransport
11+
from tcp_relays.final_relays.bypass_relay import BypassRelay
12+
from tcp_relays.final_relays.one_repeater import OneRepeater
13+
from tcp_relays.final_relays.domain_fronter import DomainFronter
14+
from initialize import issuer_private_key_pass, issuer_private_key_path, issuer_cert_path, \
15+
main_gateway_host, \
16+
main_gateway_port, main_gateway_trojan_hashed_password, bypass_gateway_host, bypass_gateway_port, \
17+
bypass_gateway_trojan_hashed_password, local_connect_timeout, \
18+
loopback_connect_host, listen_port, upgrade_incoming_timeout, upgrade_incoming_handshake_timeout, \
19+
upgrade_incoming_shutdown_timeout, others_connect_upgrade_timeout, others_connect_upgrade_handshake_timeout, \
20+
others_connect_upgrade_shutdown_timeout, incoming_write_timeout, outgoing_write_timeout, \
21+
private_domain_front_trojan_hashed_password, fake_sni, fake_sni_tld, pure_fake_sni_length_min, \
22+
pure_fake_sni_length_max, incoming_wait_for_cancel_timeout, instant_certificate_temp_file_path
23+
24+
25+
class Controller:
26+
icc = InstantCertServerSideCtx(issuer_cert_path, issuer_private_key_path, issuer_private_key_pass,
27+
instant_certificate_temp_file_path)
28+
29+
@staticmethod
30+
def get_fake_sni():
31+
if fake_sni != "random":
32+
return fake_sni
33+
n = random.randint(pure_fake_sni_length_min, pure_fake_sni_length_max)
34+
return (''.join(secrets.choice(string.ascii_lowercase + string.digits) for _ in range(n))) + fake_sni_tld
35+
36+
@classmethod
37+
async def on_repeater_domain_front_request(cls, incoming_tp: WrappedTransport[TcpStreamTransport],
38+
incoming_parsed_tj, incoming_payload: bytes):
39+
40+
try:
41+
parsed_tls = parse_tls_client_hello(incoming_payload)
42+
except Exception:
43+
gateway_trojan = struct_pure_tcp_trojan(bypass_gateway_trojan_hashed_password,
44+
incoming_parsed_tj["remote_address_type"],
45+
incoming_parsed_tj["remote_address"],
46+
incoming_parsed_tj["remote_port"])
47+
bypass_relay = BypassRelay(incoming_tp, None, bypass_gateway_host, bypass_gateway_port,
48+
gateway_trojan, 3, incoming_payload,
49+
None, None,
50+
list(), bytearray(), bytearray(),
51+
None, None, Timer(), local_connect_timeout, incoming_write_timeout,
52+
outgoing_write_timeout)
53+
print("Non-TLS Request Bypassed", incoming_parsed_tj["remote_address_type"], incoming_parsed_tj["remote_address"],
54+
incoming_parsed_tj["remote_port"], incoming_payload)
55+
await bypass_relay.main_loop(incoming_tp)
56+
incoming_tp.sync_close()
57+
return
58+
else:
59+
gateway_trojan = struct_pure_tcp_trojan(private_domain_front_trojan_hashed_password,
60+
incoming_parsed_tj["remote_address_type"],
61+
incoming_parsed_tj["remote_address"],
62+
incoming_parsed_tj["remote_port"])
63+
one_repeater_relay = OneRepeater(incoming_tp, None, loopback_connect_host, listen_port,
64+
gateway_trojan, -1, incoming_payload,
65+
None, None,
66+
list(), bytearray(), bytearray(),
67+
None, None, Timer(), local_connect_timeout, incoming_write_timeout,
68+
outgoing_write_timeout)
69+
await one_repeater_relay.main_loop(incoming_tp)
70+
incoming_tp.sync_close()
71+
return
72+
73+
@classmethod
74+
async def on_private_domain_front_request(cls, incoming_tp: WrappedTransport[TcpStreamTransport],
75+
incoming_parsed_tj, incoming_payload: bytes):
76+
77+
try:
78+
parsed_tls = parse_tls_client_hello(incoming_payload)
79+
except Exception:
80+
print("!!!CRITICAL WARNING!!! non-tls in private.", incoming_parsed_tj, incoming_payload)
81+
incoming_tp.sync_close()
82+
return
83+
gateway_trojan = struct_pure_tcp_trojan(main_gateway_trojan_hashed_password,
84+
incoming_parsed_tj["remote_address_type"],
85+
incoming_parsed_tj["remote_address"],
86+
incoming_parsed_tj["remote_port"])
87+
upgrade_incoming_magic = {"in_alpn": parsed_tls["alpn"], "in_sni": parsed_tls["sni"], "icc": cls.icc,
88+
"chosen_alpn": -1, "wait_for_cancel_timeout": incoming_wait_for_cancel_timeout,
89+
"remote_address_type": incoming_parsed_tj["remote_address_type"],
90+
"remote_address": incoming_parsed_tj["remote_address"],
91+
"repeat_command": incoming_payload, "timeout": upgrade_incoming_timeout,
92+
"sslcontext": -1, "kwargs": {"server_hostname": None,
93+
"ssl_handshake_timeout": upgrade_incoming_handshake_timeout,
94+
"ssl_shutdown_timeout": upgrade_incoming_shutdown_timeout}}
95+
upgrade_outgoing_magic = {"sslcontext": -1, "check_hostname": False, "verify_mode": ssl.CERT_NONE,
96+
"cadata": None,
97+
"out_alpn": parsed_tls["alpn"], "timeout": others_connect_upgrade_timeout,
98+
"kwargs": {"server_hostname": cls.get_fake_sni(),
99+
"ssl_handshake_timeout": others_connect_upgrade_handshake_timeout,
100+
"ssl_shutdown_timeout": others_connect_upgrade_shutdown_timeout}}
101+
domain_front_relay = DomainFronter(incoming_tp, None, main_gateway_host, main_gateway_port,
102+
gateway_trojan, -1, None,
103+
upgrade_incoming_magic, upgrade_outgoing_magic,
104+
list(), bytearray(), bytearray(),
105+
None, None, Timer(), local_connect_timeout, incoming_write_timeout,
106+
outgoing_write_timeout)
107+
108+
await domain_front_relay.main_loop(incoming_tp)
109+
incoming_tp.sync_close()
110+
return

initialize.py

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import json
2+
import sys
3+
import os
4+
import ipaddress
5+
import traceback
6+
7+
from cryptography.hazmat.primitives import hashes
8+
9+
10+
# from util.hostname_validator import is_valid_no_ip_hostname
11+
12+
def _get_loopback_connect_host(f_listen_host):
13+
try:
14+
if (f_listen_host == "") or (f_listen_host is None) or (f_listen_host == "0.0.0.0"):
15+
return "127.0.0.1"
16+
try:
17+
if ipaddress.ip_address(f_listen_host) == ipaddress.ip_address("::"):
18+
return "::1"
19+
except ValueError:
20+
pass
21+
try:
22+
if (ipaddress.ip_address(f_listen_host[1:-1]) == ipaddress.ip_address("::")) and (f_listen_host[0] == "[") and (
23+
f_listen_host[-1] == "]"):
24+
return "::1"
25+
except ValueError:
26+
pass
27+
return f_listen_host
28+
except Exception as e:
29+
traceback.print_exc()
30+
sys.exit(repr(e))
31+
32+
33+
with open(os.path.join(os.path.dirname(sys.argv[0]), "config.json")) as f:
34+
_config = json.loads(f.read())
35+
36+
listen_host = _config["listen_host"]
37+
listen_port = _config["listen_port"]
38+
loopback_connect_host = _get_loopback_connect_host(listen_host)
39+
_df = hashes.Hash(hashes.SHA224())
40+
_df.update(_config["listen_trojan_password"].encode())
41+
repeater_domain_front_trojan_hashed_password = _df.finalize()
42+
43+
main_gateway_host = _config["main_gateway_host"]
44+
main_gateway_port = _config["main_gateway_port"]
45+
_main_gateway_h = hashes.Hash(hashes.SHA224())
46+
_main_gateway_h.update(_config["main_gateway_trojan_password"].encode())
47+
main_gateway_trojan_hashed_password = _main_gateway_h.finalize()
48+
49+
bypass_gateway_host = _config["bypass_gateway_host"]
50+
bypass_gateway_port = _config["bypass_gateway_port"]
51+
_bypass_gateway_h = hashes.Hash(hashes.SHA224())
52+
_bypass_gateway_h.update(_config["bypass_gateway_trojan_password"].encode())
53+
bypass_gateway_trojan_hashed_password = _bypass_gateway_h.finalize()
54+
55+
issuer_cert_path = _config["issuer_certificate_path"]
56+
issuer_private_key_path = _config["issuer_private_key_path"]
57+
issuer_private_key_pass = _config["issuer_private_key_pass"]
58+
59+
instant_certificate_temp_file_path = _config["instant_certificate_temp_file_path"]
60+
61+
fake_sni = _config["fake_sni"]
62+
if fake_sni != "random":
63+
# if not is_valid_no_ip_hostname(fake_sni, False, False):
64+
# sys.exit("wrong fake_sni")
65+
fake_sni_tld = ""
66+
pure_fake_sni_length_min = pure_fake_sni_length_max = -1
67+
68+
else:
69+
fake_sni_tld = _config["fake_sni_tld"]
70+
# if not is_valid_no_ip_hostname("a" + fake_sni_tld, False, False):
71+
# sys.exit("wrong fake_sni_tld")
72+
_fi_s = _config["full_fake_sni_length"].split("-")
73+
pure_fake_sni_length_min = int(_fi_s[0]) - len(fake_sni_tld)
74+
pure_fake_sni_length_max = int(_fi_s[1]) - len(fake_sni_tld)
75+
if pure_fake_sni_length_max < pure_fake_sni_length_min:
76+
sys.exit("wrong pure_fake_sni_length")
77+
if pure_fake_sni_length_min < 1:
78+
sys.exit("wrong pure_fake_sni_length")
79+
80+
#####
81+
_df2 = hashes.Hash(hashes.SHA224())
82+
_df2.update("my_private_pass".encode())
83+
private_domain_front_trojan_hashed_password = _df2.finalize()
84+
wait_for_trojan_timeout = 2
85+
local_connect_timeout = 2
86+
upgrade_incoming_timeout = 2
87+
upgrade_incoming_handshake_timeout = 2
88+
upgrade_incoming_shutdown_timeout = 2
89+
others_connect_upgrade_timeout = 30
90+
others_connect_upgrade_handshake_timeout = 30
91+
others_connect_upgrade_shutdown_timeout = 30
92+
incoming_wait_for_cancel_timeout = 30
93+
incoming_write_timeout = None
94+
outgoing_write_timeout = None
95+
96+
if len(frozenset((main_gateway_trojan_hashed_password, bypass_gateway_trojan_hashed_password,
97+
repeater_domain_front_trojan_hashed_password,
98+
private_domain_front_trojan_hashed_password))) != 4:
99+
sys.exit("trojan passwords are not unique")

main.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# @patterniha
2+
import asyncio
3+
import sys
4+
import traceback
5+
from transports.abstract_transport_manager import WrappedTransport
6+
from transports.tcp_stream_transport import TcpStreamTransport, TransportType
7+
from util.proxy_protocols import parse_trojan_protocol
8+
from initialize import listen_host, listen_port, wait_for_trojan_timeout, \
9+
repeater_domain_front_trojan_hashed_password, private_domain_front_trojan_hashed_password
10+
from controller import Controller
11+
12+
13+
async def trojan_handler(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
14+
try:
15+
incoming_tp = WrappedTransport[TcpStreamTransport](TcpStreamTransport(TransportType.incoming, reader, writer))
16+
await asyncio.sleep(0.015625)
17+
try:
18+
trojan_data = await incoming_tp.read(wait_for_trojan_timeout)
19+
except Exception as e:
20+
# print("WARNING READ TROJAN FAILED:", repr(e))
21+
incoming_tp.sync_close()
22+
return
23+
except asyncio.CancelledError:
24+
print("WARNING READ TROJAN CANCELLED")
25+
incoming_tp.sync_close()
26+
return
27+
try:
28+
incoming_parsed_tj = parse_trojan_protocol(trojan_data)
29+
except Exception as e:
30+
print("WARNING No Trojan Request!, Connection Closed", repr(e), trojan_data)
31+
incoming_tp.sync_close()
32+
return
33+
34+
incoming_payload = trojan_data[incoming_parsed_tj["payload_index"]:]
35+
if incoming_parsed_tj["transport_protocol"] != "tcp":
36+
print("WARNING UDP Request Is Not Supported, Connection Closed", incoming_parsed_tj, incoming_payload)
37+
incoming_tp.sync_close()
38+
return
39+
incoming_trojan_hashed_password = incoming_parsed_tj["trojan_hashed_password"]
40+
if incoming_trojan_hashed_password == private_domain_front_trojan_hashed_password:
41+
await Controller.on_private_domain_front_request(incoming_tp, incoming_parsed_tj, incoming_payload)
42+
incoming_tp.sync_close()
43+
return
44+
elif incoming_trojan_hashed_password == repeater_domain_front_trojan_hashed_password:
45+
await Controller.on_repeater_domain_front_request(incoming_tp, incoming_parsed_tj, incoming_payload)
46+
incoming_tp.sync_close()
47+
return
48+
else:
49+
print("WARNING Unauthenticated Request!, Connection Closed", incoming_parsed_tj, incoming_payload)
50+
incoming_tp.sync_close()
51+
return
52+
except Exception as e:
53+
traceback.print_exc()
54+
sys.exit(repr(e))
55+
except asyncio.CancelledError:
56+
sys.exit("cancel not allowed!")
57+
58+
59+
async def main():
60+
server = await asyncio.start_server(
61+
trojan_handler, listen_host, listen_port, limit=32767)
62+
async with server:
63+
print("MMDF started. listening on ", str(listen_host) + ":" + str(listen_port))
64+
await server.serve_forever()
65+
66+
67+
asyncio.run(main())
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from tcp_relays.swiss_knife_relay import SwissKnifeRelay
2+
3+
4+
class BypassRelay(SwissKnifeRelay):
5+
6+
def _init(self):
7+
pass
8+
9+
async def incoming_main_loop_read_data_event(self, data: bytes) -> None:
10+
await self.outgoing_write(data)
11+
12+
async def outgoing_main_loop_read_data_event(self, data: bytes) -> None:
13+
await self.incoming_write(data)
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import asyncio
2+
import ssl
3+
from util.proxy_protocols import parse_trojan_protocol
4+
5+
from tcp_relays.final_relays.bypass_relay import BypassRelay
6+
7+
8+
class DomainFronter(BypassRelay):
9+
10+
async def incoming_upgrade(self, timeout,
11+
sslcontext, *,
12+
server_hostname, ssl_handshake_timeout, ssl_shutdown_timeout):
13+
self.relay_correct_usage_check()
14+
try:
15+
to_return = await self.incoming_tp.upgrade(timeout,
16+
sslcontext, server_hostname=server_hostname,
17+
ssl_handshake_timeout=ssl_handshake_timeout,
18+
ssl_shutdown_timeout=ssl_shutdown_timeout)
19+
20+
except ssl.SSLError as e:
21+
p_tj = parse_trojan_protocol(self.gateway_trojan)
22+
print("INBOUND SSL ERROR: ", repr(e), "sni: ", self.upgrade_incoming_magic["in_sni"], "fake_sni: ",
23+
self.upgrade_outgoing_magic["kwargs"]["server_hostname"], "alpn: ",
24+
self.upgrade_incoming_magic["in_alpn"], "selected_alpn: ", self.upgrade_incoming_magic["chosen_alpn"],
25+
"address_type: ", p_tj["remote_address_type"],
26+
"address: ", p_tj["remote_address"], "port: ", p_tj["remote_port"], "client_hello: ",
27+
self.upgrade_incoming_magic["repeat_command"])
28+
self.relay_close()
29+
except Exception as e:
30+
self.relay_close()
31+
except asyncio.CancelledError:
32+
self.relay_close()
33+
else:
34+
self.relay_correct_usage_check()
35+
return to_return
36+
37+
async def outgoing_upgrade(self, timeout,
38+
sslcontext, *,
39+
server_hostname, ssl_handshake_timeout, ssl_shutdown_timeout):
40+
self.relay_correct_usage_check()
41+
try:
42+
to_return = await self.outgoing_tp.upgrade(timeout,
43+
sslcontext, server_hostname=server_hostname,
44+
ssl_handshake_timeout=ssl_handshake_timeout,
45+
ssl_shutdown_timeout=ssl_shutdown_timeout)
46+
47+
except ssl.SSLError as e:
48+
p_tj = parse_trojan_protocol(self.gateway_trojan)
49+
print("OUTBOUND SSL ERROR: ", repr(e), "sni: ", self.upgrade_incoming_magic["in_sni"], "fake_sni: ",
50+
self.upgrade_outgoing_magic["kwargs"]["server_hostname"], "alpn: ",
51+
self.upgrade_incoming_magic["in_alpn"],
52+
"address_type: ", p_tj["remote_address_type"],
53+
"address: ", p_tj["remote_address"], "port: ", p_tj["remote_port"])
54+
self.relay_close()
55+
except Exception as e:
56+
self.relay_close()
57+
except asyncio.CancelledError:
58+
self.relay_close()
59+
else:
60+
self.relay_correct_usage_check()
61+
return to_return

0 commit comments

Comments
 (0)