py-simple-nat-tunnel 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_simple_nat_tunnel-0.1.0/LICENSE +21 -0
- py_simple_nat_tunnel-0.1.0/PKG-INFO +193 -0
- py_simple_nat_tunnel-0.1.0/README.md +163 -0
- py_simple_nat_tunnel-0.1.0/pyproject.toml +51 -0
- py_simple_nat_tunnel-0.1.0/setup.cfg +4 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/__init__.py +7 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/__main__.py +7 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/cli.py +115 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/client.py +144 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/config.py +512 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/elevate.py +37 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/protocol.py +102 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/py.typed +1 -0
- py_simple_nat_tunnel-0.1.0/src/py_frp/server.py +391 -0
- py_simple_nat_tunnel-0.1.0/src/py_simple_nat_tunnel.egg-info/PKG-INFO +193 -0
- py_simple_nat_tunnel-0.1.0/src/py_simple_nat_tunnel.egg-info/SOURCES.txt +20 -0
- py_simple_nat_tunnel-0.1.0/src/py_simple_nat_tunnel.egg-info/dependency_links.txt +1 -0
- py_simple_nat_tunnel-0.1.0/src/py_simple_nat_tunnel.egg-info/entry_points.txt +5 -0
- py_simple_nat_tunnel-0.1.0/src/py_simple_nat_tunnel.egg-info/requires.txt +4 -0
- py_simple_nat_tunnel-0.1.0/src/py_simple_nat_tunnel.egg-info/top_level.txt +1 -0
- py_simple_nat_tunnel-0.1.0/tests/test_config.py +169 -0
- py_simple_nat_tunnel-0.1.0/tests/test_integration.py +228 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GGN_2015
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-simple-nat-tunnel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A small Python TCP reverse tunnel compatible with common frp/rathole config subsets.
|
|
5
|
+
Author: GGN_2015
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/GGN-2015/py-frp
|
|
8
|
+
Project-URL: Repository, https://github.com/GGN-2015/py-frp
|
|
9
|
+
Project-URL: Issues, https://github.com/GGN-2015/py-frp/issues
|
|
10
|
+
Keywords: frp,rathole,reverse-proxy,tunnel,nat-traversal
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Internet :: Proxy Servers
|
|
23
|
+
Classifier: Topic :: System :: Networking
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: py-admin-launch>=0.1.3
|
|
28
|
+
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# py-frp
|
|
32
|
+
|
|
33
|
+
一个小型 Python 生态内网穿透工具,设计上参考 frp/rathole 的“服务端公网监听 + 客户端反向拨入”模型。当前版本专注 TCP 端口映射,支持服务端、客户端、frp 常见 TOML/INI 配置子集,以及 rathole 常见 TOML 配置子集。
|
|
34
|
+
|
|
35
|
+
## 安装
|
|
36
|
+
|
|
37
|
+
从 PyPI 安装:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install py-simple-nat-tunnel
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
从源码安装:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
开发模式安装:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
安装后会得到这些命令:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
py-frp --version
|
|
59
|
+
py-frp server -c examples/frps.toml
|
|
60
|
+
py-frp client -c examples/frpc.toml
|
|
61
|
+
py-frps -c examples/frps.toml
|
|
62
|
+
py-frpc -c examples/frpc.toml
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Windows 上如果提示 scripts 目录不在 `PATH`,也可以直接用:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
python -m py_frp server -c examples/frps.toml
|
|
69
|
+
python -m py_frp client -c examples/frpc.toml
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 快速开始
|
|
73
|
+
|
|
74
|
+
服务端公网机器:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
py-frp server -c examples/frps.toml
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
客户端内网机器:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
py-frp client -c examples/frpc.toml
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`examples/frpc.toml` 默认把客户端的 `127.0.0.1:22` 暴露到服务端的 `0.0.0.0:6000`。启动后从外部访问:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
ssh -p 6000 user@your-server
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## frp 配置子集
|
|
93
|
+
|
|
94
|
+
服务端 `frps.toml`:
|
|
95
|
+
|
|
96
|
+
```toml
|
|
97
|
+
bindPort = 7000
|
|
98
|
+
|
|
99
|
+
[auth]
|
|
100
|
+
token = "change-me"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
客户端 `frpc.toml`:
|
|
104
|
+
|
|
105
|
+
```toml
|
|
106
|
+
serverAddr = "your-server"
|
|
107
|
+
serverPort = 7000
|
|
108
|
+
|
|
109
|
+
[auth]
|
|
110
|
+
token = "change-me"
|
|
111
|
+
|
|
112
|
+
[[proxies]]
|
|
113
|
+
name = "ssh"
|
|
114
|
+
type = "tcp"
|
|
115
|
+
localIP = "127.0.0.1"
|
|
116
|
+
localPort = 22
|
|
117
|
+
remotePort = 6000
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
也支持旧版 frp INI 的 `[common]` 和 TCP proxy section:
|
|
121
|
+
|
|
122
|
+
```ini
|
|
123
|
+
[common]
|
|
124
|
+
server_addr = your-server
|
|
125
|
+
server_port = 7000
|
|
126
|
+
token = change-me
|
|
127
|
+
|
|
128
|
+
[ssh]
|
|
129
|
+
type = tcp
|
|
130
|
+
local_ip = 127.0.0.1
|
|
131
|
+
local_port = 22
|
|
132
|
+
remote_port = 6000
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## rathole 配置子集
|
|
136
|
+
|
|
137
|
+
服务端:
|
|
138
|
+
|
|
139
|
+
```toml
|
|
140
|
+
[server]
|
|
141
|
+
bind_addr = "0.0.0.0:2333"
|
|
142
|
+
default_token = "change-me"
|
|
143
|
+
|
|
144
|
+
[server.services.ssh]
|
|
145
|
+
bind_addr = "0.0.0.0:6000"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
客户端:
|
|
149
|
+
|
|
150
|
+
```toml
|
|
151
|
+
[client]
|
|
152
|
+
remote_addr = "your-server:2333"
|
|
153
|
+
default_token = "change-me"
|
|
154
|
+
|
|
155
|
+
[client.services.ssh]
|
|
156
|
+
local_addr = "127.0.0.1:22"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 提权
|
|
160
|
+
|
|
161
|
+
服务端启动时如果配置里有小于 1024 的监听端口,默认会通过 `py-admin-launch` 尝试提权一次。也可以显式控制:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
py-frp server -c frps.toml --elevate
|
|
165
|
+
py-frp server -c frps.toml --no-auto-elevate
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
实现上会在重启命令后追加内部标记,保证最多只尝试一次,不会循环弹出提权请求。
|
|
169
|
+
|
|
170
|
+
## 当前范围
|
|
171
|
+
|
|
172
|
+
已支持:
|
|
173
|
+
|
|
174
|
+
- TCP 反向端口映射
|
|
175
|
+
- 多服务/多 proxy
|
|
176
|
+
- token 鉴权
|
|
177
|
+
- frp TOML 和 legacy INI 的常见 TCP 配置
|
|
178
|
+
- rathole TOML 的常见 TCP 配置
|
|
179
|
+
- `pip install .` 一键安装和 console scripts
|
|
180
|
+
|
|
181
|
+
暂不支持:
|
|
182
|
+
|
|
183
|
+
- UDP
|
|
184
|
+
- HTTP/HTTPS vhost
|
|
185
|
+
- frp 插件、负载均衡、STCP/XTCP
|
|
186
|
+
- rathole Noise/TLS 加密传输
|
|
187
|
+
- 多路复用;当前每个公网连接会建立一条独立 tunnel TCP
|
|
188
|
+
|
|
189
|
+
## 测试
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
python -m unittest discover -s tests -v
|
|
193
|
+
```
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# py-frp
|
|
2
|
+
|
|
3
|
+
一个小型 Python 生态内网穿透工具,设计上参考 frp/rathole 的“服务端公网监听 + 客户端反向拨入”模型。当前版本专注 TCP 端口映射,支持服务端、客户端、frp 常见 TOML/INI 配置子集,以及 rathole 常见 TOML 配置子集。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
从 PyPI 安装:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install py-simple-nat-tunnel
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
从源码安装:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install .
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
开发模式安装:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install -e .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
安装后会得到这些命令:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
py-frp --version
|
|
29
|
+
py-frp server -c examples/frps.toml
|
|
30
|
+
py-frp client -c examples/frpc.toml
|
|
31
|
+
py-frps -c examples/frps.toml
|
|
32
|
+
py-frpc -c examples/frpc.toml
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Windows 上如果提示 scripts 目录不在 `PATH`,也可以直接用:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
python -m py_frp server -c examples/frps.toml
|
|
39
|
+
python -m py_frp client -c examples/frpc.toml
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 快速开始
|
|
43
|
+
|
|
44
|
+
服务端公网机器:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
py-frp server -c examples/frps.toml
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
客户端内网机器:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
py-frp client -c examples/frpc.toml
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`examples/frpc.toml` 默认把客户端的 `127.0.0.1:22` 暴露到服务端的 `0.0.0.0:6000`。启动后从外部访问:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ssh -p 6000 user@your-server
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## frp 配置子集
|
|
63
|
+
|
|
64
|
+
服务端 `frps.toml`:
|
|
65
|
+
|
|
66
|
+
```toml
|
|
67
|
+
bindPort = 7000
|
|
68
|
+
|
|
69
|
+
[auth]
|
|
70
|
+
token = "change-me"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
客户端 `frpc.toml`:
|
|
74
|
+
|
|
75
|
+
```toml
|
|
76
|
+
serverAddr = "your-server"
|
|
77
|
+
serverPort = 7000
|
|
78
|
+
|
|
79
|
+
[auth]
|
|
80
|
+
token = "change-me"
|
|
81
|
+
|
|
82
|
+
[[proxies]]
|
|
83
|
+
name = "ssh"
|
|
84
|
+
type = "tcp"
|
|
85
|
+
localIP = "127.0.0.1"
|
|
86
|
+
localPort = 22
|
|
87
|
+
remotePort = 6000
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
也支持旧版 frp INI 的 `[common]` 和 TCP proxy section:
|
|
91
|
+
|
|
92
|
+
```ini
|
|
93
|
+
[common]
|
|
94
|
+
server_addr = your-server
|
|
95
|
+
server_port = 7000
|
|
96
|
+
token = change-me
|
|
97
|
+
|
|
98
|
+
[ssh]
|
|
99
|
+
type = tcp
|
|
100
|
+
local_ip = 127.0.0.1
|
|
101
|
+
local_port = 22
|
|
102
|
+
remote_port = 6000
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## rathole 配置子集
|
|
106
|
+
|
|
107
|
+
服务端:
|
|
108
|
+
|
|
109
|
+
```toml
|
|
110
|
+
[server]
|
|
111
|
+
bind_addr = "0.0.0.0:2333"
|
|
112
|
+
default_token = "change-me"
|
|
113
|
+
|
|
114
|
+
[server.services.ssh]
|
|
115
|
+
bind_addr = "0.0.0.0:6000"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
客户端:
|
|
119
|
+
|
|
120
|
+
```toml
|
|
121
|
+
[client]
|
|
122
|
+
remote_addr = "your-server:2333"
|
|
123
|
+
default_token = "change-me"
|
|
124
|
+
|
|
125
|
+
[client.services.ssh]
|
|
126
|
+
local_addr = "127.0.0.1:22"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 提权
|
|
130
|
+
|
|
131
|
+
服务端启动时如果配置里有小于 1024 的监听端口,默认会通过 `py-admin-launch` 尝试提权一次。也可以显式控制:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
py-frp server -c frps.toml --elevate
|
|
135
|
+
py-frp server -c frps.toml --no-auto-elevate
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
实现上会在重启命令后追加内部标记,保证最多只尝试一次,不会循环弹出提权请求。
|
|
139
|
+
|
|
140
|
+
## 当前范围
|
|
141
|
+
|
|
142
|
+
已支持:
|
|
143
|
+
|
|
144
|
+
- TCP 反向端口映射
|
|
145
|
+
- 多服务/多 proxy
|
|
146
|
+
- token 鉴权
|
|
147
|
+
- frp TOML 和 legacy INI 的常见 TCP 配置
|
|
148
|
+
- rathole TOML 的常见 TCP 配置
|
|
149
|
+
- `pip install .` 一键安装和 console scripts
|
|
150
|
+
|
|
151
|
+
暂不支持:
|
|
152
|
+
|
|
153
|
+
- UDP
|
|
154
|
+
- HTTP/HTTPS vhost
|
|
155
|
+
- frp 插件、负载均衡、STCP/XTCP
|
|
156
|
+
- rathole Noise/TLS 加密传输
|
|
157
|
+
- 多路复用;当前每个公网连接会建立一条独立 tunnel TCP
|
|
158
|
+
|
|
159
|
+
## 测试
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
python -m unittest discover -s tests -v
|
|
163
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "py-simple-nat-tunnel"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A small Python TCP reverse tunnel compatible with common frp/rathole config subsets."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "GGN_2015" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["frp", "rathole", "reverse-proxy", "tunnel", "nat-traversal"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Framework :: AsyncIO",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Topic :: Internet :: Proxy Servers",
|
|
29
|
+
"Topic :: System :: Networking"
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"py-admin-launch>=0.1.3",
|
|
33
|
+
"tomli>=2.0.1; python_version < '3.11'"
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://github.com/GGN-2015/py-frp"
|
|
38
|
+
Repository = "https://github.com/GGN-2015/py-frp"
|
|
39
|
+
Issues = "https://github.com/GGN-2015/py-frp/issues"
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
py-frp = "py_frp.cli:main"
|
|
43
|
+
pyfrp = "py_frp.cli:main"
|
|
44
|
+
py-frps = "py_frp.cli:server_main"
|
|
45
|
+
py-frpc = "py_frp.cli:client_main"
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.packages.find]
|
|
48
|
+
where = ["src"]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.package-data]
|
|
51
|
+
py_frp = ["py.typed"]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import asyncio
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
|
|
9
|
+
from . import __version__
|
|
10
|
+
from .client import Client
|
|
11
|
+
from .config import (
|
|
12
|
+
ConfigError,
|
|
13
|
+
describe_client_config,
|
|
14
|
+
describe_server_config,
|
|
15
|
+
load_client_config,
|
|
16
|
+
load_server_config,
|
|
17
|
+
)
|
|
18
|
+
from .elevate import ElevationError, is_admin, relaunch_once, should_elevate_server
|
|
19
|
+
from .server import Server
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
LOGGER = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
26
|
+
effective_argv = list(sys.argv[1:] if argv is None else argv)
|
|
27
|
+
parser = build_parser()
|
|
28
|
+
args = parser.parse_args(effective_argv)
|
|
29
|
+
_configure_logging(args.log_level)
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
if args.command == "server":
|
|
33
|
+
return _run_server_command(args, effective_argv)
|
|
34
|
+
if args.command == "client":
|
|
35
|
+
return _run_client_command(args)
|
|
36
|
+
except KeyboardInterrupt:
|
|
37
|
+
return 130
|
|
38
|
+
except ConfigError as exc:
|
|
39
|
+
LOGGER.error("configuration error: %s", exc)
|
|
40
|
+
return 2
|
|
41
|
+
except ElevationError as exc:
|
|
42
|
+
LOGGER.error("elevation failed: %s", exc)
|
|
43
|
+
return 1
|
|
44
|
+
return 2
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def server_main(argv: Sequence[str] | None = None) -> int:
|
|
48
|
+
return main(["server", *(sys.argv[1:] if argv is None else argv)])
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def client_main(argv: Sequence[str] | None = None) -> int:
|
|
52
|
+
return main(["client", *(sys.argv[1:] if argv is None else argv)])
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
56
|
+
parser = argparse.ArgumentParser(
|
|
57
|
+
prog="py-frp",
|
|
58
|
+
description="A small Python TCP reverse tunnel.",
|
|
59
|
+
)
|
|
60
|
+
parser.add_argument("--version", action="version", version=f"py-frp {__version__}")
|
|
61
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
62
|
+
|
|
63
|
+
server = subparsers.add_parser("server", aliases=["frps"], help="run the public server")
|
|
64
|
+
_add_runtime_options(server)
|
|
65
|
+
server.add_argument("-c", "--config", required=True, help="server config path")
|
|
66
|
+
server.add_argument(
|
|
67
|
+
"--elevate",
|
|
68
|
+
action="store_true",
|
|
69
|
+
help="relaunch as administrator/root once before binding ports",
|
|
70
|
+
)
|
|
71
|
+
server.add_argument(
|
|
72
|
+
"--auto-elevate",
|
|
73
|
+
action=argparse.BooleanOptionalAction,
|
|
74
|
+
default=True,
|
|
75
|
+
help="auto elevate once when configured listen ports are privileged",
|
|
76
|
+
)
|
|
77
|
+
server.add_argument("--elevation-attempted", action="store_true", help=argparse.SUPPRESS)
|
|
78
|
+
|
|
79
|
+
client = subparsers.add_parser("client", aliases=["frpc"], help="run the private client")
|
|
80
|
+
_add_runtime_options(client)
|
|
81
|
+
client.add_argument("-c", "--config", required=True, help="client config path")
|
|
82
|
+
|
|
83
|
+
return parser
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _add_runtime_options(parser: argparse.ArgumentParser) -> None:
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--log-level",
|
|
89
|
+
default="INFO",
|
|
90
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
91
|
+
help="logging level",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _run_server_command(args: argparse.Namespace, effective_argv: Sequence[str]) -> int:
|
|
96
|
+
config = load_server_config(args.config)
|
|
97
|
+
LOGGER.info("loaded server config: %s", describe_server_config(config))
|
|
98
|
+
needs_elevation = args.elevate or (args.auto_elevate and should_elevate_server(config))
|
|
99
|
+
if needs_elevation and not args.elevation_attempted and not is_admin():
|
|
100
|
+
LOGGER.info("relaunching with administrator/root privileges")
|
|
101
|
+
return relaunch_once(effective_argv)
|
|
102
|
+
return asyncio.run(Server(config).serve_forever())
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _run_client_command(args: argparse.Namespace) -> int:
|
|
106
|
+
config = load_client_config(args.config)
|
|
107
|
+
LOGGER.info("loaded client config: %s", describe_client_config(config))
|
|
108
|
+
return asyncio.run(Client(config).run())
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _configure_logging(level: str) -> None:
|
|
112
|
+
logging.basicConfig(
|
|
113
|
+
level=getattr(logging, level),
|
|
114
|
+
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
|
115
|
+
)
|