1
1
#!/usr/bin/env python3
2
+ # Copyright 2024 rev1si0n ([email protected] ). All rights reserved.
3
+ #
4
+ # Distributed under MIT license.
5
+ # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
2
6
#encoding=utf-8
3
7
import os
8
+ import re
4
9
import sys
5
10
import time
11
+ import logging
6
12
import subprocess
7
13
import argparse
8
14
import uuid
15
+ import asyncio
16
+ import threading
17
+ import dns .message
18
+ import dns .query
19
+ import httpx
9
20
10
21
from socket import *
11
22
from random import randint
12
23
from multiprocessing import Process
24
+ from urllib .parse import urlparse
25
+ from functools import partial
13
26
14
27
from mitmproxy .certs import CertStore
15
28
from mitmproxy .tools .main import mitmweb as web
@@ -36,15 +49,87 @@ def cleanup(*args, **kwargs):
36
49
os ._exit (0 )
37
50
38
51
52
+ def is_doh (server ):
53
+ u = urlparse (server )
54
+ return u .scheme in ("http" , "https" )
55
+
56
+
57
+ def fmt_rdns (dns , lport ):
58
+ return "reverse:dns://{}@{}" .format (dns , lport )
59
+
60
+
61
+ class DOHProxiedProtocol (asyncio .Protocol ):
62
+ def __init__ (self , loop , server , proxy ):
63
+ self .server = server
64
+ log ("using DOH: {}" .format (server ))
65
+ self .client = httpx .Client (proxies = proxy )
66
+ self .loop = loop
67
+ def datagram_received (self , pkt , addr ):
68
+ self .loop .create_task (self .handle (pkt , addr ))
69
+ def connection_made (self , transport ):
70
+ self .transport = transport
71
+ async def handle (self , pkt , addr ):
72
+ res = await self .loop .run_in_executor (None ,
73
+ self .dns_query , pkt )
74
+ self .transport .sendto (res , addr )
75
+ def dns_query (self , pkt ):
76
+ res = dns .message .from_wire (pkt )
77
+ res = dns .query .https (res , self .server ,
78
+ session = self .client )
79
+ return res .to_wire ()
80
+ @classmethod
81
+ def start (cls , * args , ** kwargs ):
82
+ dns = threading .Thread (target = cls ._start ,
83
+ args = args , kwargs = kwargs )
84
+ dns .daemon = True
85
+ dns .start ()
86
+ @classmethod
87
+ def _start (cls , bind , port , upstream , proxy = None ):
88
+ loop = asyncio .new_event_loop ()
89
+ factory = partial (cls , loop , upstream , proxy )
90
+ coro = loop .create_datagram_endpoint (factory ,
91
+ local_addr = (bind , port ),
92
+ reuse_port = True )
93
+ loop .run_until_complete (coro )
94
+ loop .run_forever ()
95
+
96
+
97
+ def setup_dns_upstream (args ):
98
+ port = randint (28080 , 58080 )
99
+ dns = "{}:{}" .format ("127.0.0.1" , port )
100
+ DOHProxiedProtocol .start (
101
+ "127.0.0.1" ,
102
+ port ,
103
+ args .dns ,
104
+ args .upstream )
105
+ args .dns = fmt_rdns (dns , proxy )
106
+
107
+
39
108
def add_server (command , spec ):
40
109
spec and command .append ("--mode" )
41
110
spec and command .append (spec )
42
111
43
112
113
+ def add_upstream (args , ext ):
114
+ u = urlparse (args .upstream )
115
+ upstream = "upstream:{}://{}:{}" .format (u .scheme ,
116
+ u .hostname ,
117
+ u .port )
118
+ args .mode = upstream
119
+ cred = "{}:{}" .format (u .username , u .password )
120
+ u .username and ext .append ("--upstream-auth" )
121
+ u .username and ext .append (cred )
122
+
123
+
44
124
def log (* args ):
45
125
print (time .ctime (), * args )
46
126
47
127
128
+ def die (* args ):
129
+ print (time .ctime (), * args )
130
+ sys .exit (1 )
131
+
132
+
48
133
def adb (* args ):
49
134
command = ["adb" ]
50
135
if serial is not None :
@@ -103,15 +188,12 @@ def get_default_interface_ip(target):
103
188
lamda = int (os .environ .get ("PORT" ,
104
189
65000 ))
105
190
106
- def dnsopt (dns ):
107
- return "reverse:dns://{}@{}" .format (dns , proxy )
108
191
argp .add_argument ("device" , nargs = 1 )
109
- argp .add_argument ("-m" , "--mode" , default = "regular" )
192
+ mod = argp .add_mutually_exclusive_group (required = False )
193
+ mod .add_argument ("-m" , "--mode" , default = "regular" )
194
+ mod .add_argument ("--upstream" , type = str , default = None )
110
195
argp .add_argument ("--serial" , type = str , default = None )
111
- dns = argp .add_mutually_exclusive_group (required = False )
112
- dns .add_argument ("--dns" , type = dnsopt , nargs = "?" ,
113
- const = "1.1.1.1" )
114
- dns .add_argument ("--nameserver" , type = str , default = "" )
196
+ argp .add_argument ("--dns" , type = str , default = None )
115
197
args , extras = argp .parse_known_args ()
116
198
serial = args .serial
117
199
host = args .device [0 ]
@@ -127,17 +209,30 @@ def dnsopt(dns):
127
209
128
210
if cert :
129
211
log ("ssl:" , cert )
212
+ if args .upstream :
213
+ add_upstream (args , extras )
130
214
if usb and args .dns :
131
- log ("dns mitm not available over usb" )
132
- sys .exit (1 )
133
- if usb and (forward (lamda , lamda ).wait () != 0 or \
134
- reverse (proxy , proxy ).wait () != 0 ):
135
- log ("forward failed" )
136
- sys .exit (1 )
215
+ die ("dns mitm not available in USB mode" )
216
+ if usb and args .upstream :
217
+ log ("dns will not sent via upstream in USB mode" )
218
+ if args .upstream and not args .dns :
219
+ die ("dns must be set in upstream mode" )
220
+ if args .upstream and args .dns and not is_doh (args .dns ):
221
+ die ("dns must be DOH in upstream mode" )
222
+ if usb and forward (lamda , lamda ).wait () != 0 :
223
+ die ("adb forward failed" )
224
+ if usb and reverse (proxy , proxy ).wait () != 0 :
225
+ die ("adb forward failed" )
226
+ if not args .upstream and args .dns and not is_doh (args .dns ):
227
+ args .dns = fmt_rdns (args .dns , proxy )
228
+ if args .dns and is_doh (args .dns ):
229
+ setup_dns_upstream (args )
230
+
137
231
138
232
# 创建设备实例
139
233
d = Device (host , port = lamda ,
140
234
certificate = cert )
235
+ logger .setLevel (logging .WARN )
141
236
142
237
# 拼接证书文件路径
143
238
DIR = os .path .expanduser (CONF_DIR )
@@ -150,8 +245,8 @@ def dnsopt(dns):
150
245
# 初始化 proxy 配置
151
246
profile = GproxyProfile ()
152
247
profile .type = GproxyType .HTTP_CONNECT
153
- profile .nameserver = args . nameserver
154
- if not usb and args .dns :
248
+ profile .nameserver = "1.1.1.1"
249
+ if not usb and ( args .upstream or args . dns ) :
155
250
profile .nameserver = "{}:{}" .format (server , proxy )
156
251
profile .drop_udp = True
157
252
0 commit comments