1
- # README.md
1
+ # PortForward
2
2
3
- ------------------------------
4
-
5
- ### 0x00 前言
3
+ ## 0x00 前言
6
4
` PortForward ` 是使用 Golang 进行开发的端口转发工具,解决在某些场景下 ` 内外网无法互通 ` 的问题。
7
5
8
6
` PortForward ` 的功能如下:
29
27
10 . issue
30
28
11 . contributions
31
29
32
- ### 0x01 使用说明
30
+ ## 0x01 使用说明
33
31
** 1.使用**
34
32
35
33
Usage:
54
52
./build.sh
55
53
56
54
57
- ### 0x02 工作场景
55
+ ## 0x02 工作场景
58
56
这里列举了一些 ` PortForward ` 的工作场景,如下:
59
57
60
58
** 2.1 简单模式**
76
74
</div >
77
75
78
76
79
- ### 0x03 源码结构
77
+ ## 0x03 源码结构
80
78
81
79
.
82
80
├── CHANGELOG
91
89
└── udp.go // udp layer
92
90
93
91
94
- ### 0x04 逻辑结构
92
+ ## 0x04 逻辑结构
95
93
` PortForward ` 支持 ` TCP ` , ` UDP ` 协议层的端口转发,代码抽象后逻辑结构框架如下:
96
94
<div align =" center " >
97
95
<img src =" ./Images/portforward_framework.png " width =" 500 " >
98
96
</br >[ 图4.整体框架]
99
97
</div >
100
98
101
99
102
- ### 0x05 端口转发的实现
100
+ ## 0x05 端口转发的实现
103
101
端口转发程序作为网络传输的中间人,无非就是将两端的 socket 对象进行联通,数据就可以通过这条「链路」进行传输了。
104
102
105
103
按照这样的思路,我们从需求开始分析和抽象,可以这么认为:无论是作为 ` tcp ` 还是 ` udp ` 运行,无论是作为 ` connect ` 还是 ` listen ` 运行,最终都将获得两个 socket,其中一个连向原服务,另一个与客户端连接;最终将这两端的 socket 联通即可实现端口转发。
106
104
107
105
在 Golang 中我们采用了 ` io.Copy() ` 来联通两个 socket,但是 ` io.Copy ` 必须要求对象实现了 ` io.Reader/io.Writer ` 接口,` tcp ` 的 socket 可以直接支持,而 ` udp ` 的 socket 需要我们进行封装。
108
106
109
107
110
- ### 0x06 udp的knock报文
108
+ ## 0x06 udp的knock报文
111
109
在 ` udp ` 的 ` connect ` 模式下,我们在连接服务器成功后,立即发送了一个 ` knock ` 报文,如下:
112
110
113
111
conn, err := net.DialTimeout("udp", ...
116
114
其作用是通知远程 ` udp ` 服务器我们已经连上了(` udp ` 创建连接后,仅在本地操作系统层进行了注册,只有当发送一个报文到对端后,远程服务器才能感知到新连接),当我们在 ` udp ` 的 ` conn-conn ` 模式下运行时,这个报文是必须的。
117
115
118
116
119
- ### 0x07 udp的超时设置
117
+ ## 0x07 udp的超时设置
120
118
在 ` udp ` 的实现中,我们为所有的 ` udp ` 连接 socket 对象都设置了超时时间(` tcp ` 中不需要),这是因为在 ` udp ` 中,socket 对象无法感知对端退出,如果不设置超时时间,将会一直在 ` conn.Read() ` 阻塞下去。
121
119
122
120
我们设置了 ` udp ` 超时时间为 60 秒,当 60 秒无数据传输,本次建立的虚拟通信链路将销毁,端口转发程序将重新创建新的通信链路。
123
121
124
122
125
- ### 0x08 listen-listen的超时设置
123
+ ## 0x08 listen-listen的超时设置
126
124
对于 ` listen-listen ` 模式,需要等待两端的客户端都连上端口转发程序后,才能将两个 socket 进行联通。
127
125
128
126
为此我们在此处设置了 120 秒的超时时间,也就是说当其中一端有客户端连接后,另一端在 120 秒内没有连接,我们就销毁这个未成功建立的通信链路;用户重新连接即可。
129
127
130
128
> 如果没有这个超时,可能某些场景遗留了某个连接,将造成后续的通信链路错位。
131
129
132
130
133
- ### 0x09 多通路的实现
131
+ ## 0x09 多通路的实现
134
132
多通路可以支持同时发起多个连接,这里我们以 ` tcp ` 作为例子来说明。为了处理这种情况,我们的处理方式是:
135
133
136
134
1 . ` listen-conn ` : 每当 listen 服务器接收到新连接后,与远端创建新的连接,并将两个 socket 进行联通。
140
138
> 我们在 ` udp ` 中也加入了多通路的支持,和 ` tcp ` 基本类似,但由于 ` udp ` 是无连接的,我们不能像 ` tcp ` 直接联通两个 socket 对象。我们在 ` udp listen ` 服务器中维护了一个临时表,使用 ` ip:port ` 作为标志,以描述各个通信链路的联通情况,依据此进行流量的分发。
141
139
142
140
143
- ### 0x0A issue
141
+ ## 0x0A issue
144
142
** 1.udp的映射表未清空**
145
143
udp多通路中的映射表没有对无效数据进行清理,长时间运行可能造成内存占用
146
144
@@ -162,7 +160,7 @@ udp多通路中的映射表没有对无效数据进行清理,长时间运行
162
160
这种多端口通信我们也是无法处理的,因为在目前多通路的实现下,上述流程中第 2 步过后,对于端口转发程序来说,后续的报文无法确定转发给 69 端口还是 61234 端口。
163
161
164
162
165
- ### 0x0B contributions
163
+ ## 0x0B contributions
166
164
167
165
[ r0oike@knownsec 404] ( https://github.com/r0oike )
168
166
[ fenix@knownsec 404] ( https://github.com/13ph03nix )
0 commit comments