├── .gitignore
├── LICENSE
├── README-zh.md
├── README.md
├── address_pool.go
├── address_pool_test.go
├── bitop_test.go
├── config.json.example
├── conn.go
├── conn_mgr.go
├── go.mod
├── go.sum
├── http3_conn.go
├── http_server.go
├── keys
├── server.crt
└── server.key
├── local_login_check.go
├── login_check.go
├── main.go
├── packet_dispatcher.go
├── pole_packet.go
├── polevpn.service
├── polevpn_server.go
├── request_handler.go
├── restart.sh
├── router_mgr.go
├── start.sh
├── stop.sh
├── tools
├── cert.info
├── createtls.sh
└── init.sh
├── traffic_counter.go
├── tunio.go
├── users.credentials
├── utils.go
└── ws_conn.go
/.gitignore:
--------------------------------------------------------------------------------
1 | *.out
2 | *.log
3 | polevpn_server
4 | config.json
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 polevpn
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.
22 |
--------------------------------------------------------------------------------
/README-zh.md:
--------------------------------------------------------------------------------
1 | # PoleVPN a secure fast reliable VPN
2 |
3 | [English](https://github.com/polevpn/polevpn_server/blob/main/README.md)
4 |
5 | ## 企业级VPN 您值得信赖
6 | * 部署·配置方便,一键接入
7 | * 全程TLS 通信加密,安全可靠
8 | * 支持网桥功能,快速远程组网
9 | * 支持IOT 设备组网
10 | * 可以作为代理服务器(梯子)
11 | * 全客户端支持(ios android,windows,macos,linux)
12 | * 支持流量控制,限速
13 | * 支持 本地文件,http 接口,LDAP 认证方式
14 | * 代码开源
15 |
16 | ## 为什么会有PoleVPN
17 | * 疫情期间远程办公,需要有VPN,来接入公司,IDC 资源
18 | * 跨国团队,VPN 不能被GFW墙,用OpenVPN 会被墙(虽然可以通过其他隧道方式伪装)
19 | * 需要有网桥功能,路由功能,能够打通公司网络,测试环境网络,开发环境网络,方便开发人员 调试,开发
20 | * 配置不能太复杂(OpenVPN 安装,维护太复杂了)
21 |
22 | ## 文档·客户端下载
23 | * [中文文档](https://www.polevpn.com/docs)
24 | * [这里下载使用](https://www.polevpn.com/index-zh.html#download)
25 |
26 | ## 项目官网
27 | * [polevpn.com](https://polevpn.com)
28 |
29 | ## Telegram Group
30 |
31 | [Telegram Group](https://t.me/polevpn)
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # PoleVPN a secure fast reliable VPN
3 |
4 | [简体中文](https://github.com/polevpn/polevpn_server/blob/main/README-zh.md)
5 |
6 | ## Enterprise VPN you can trust
7 | * Easy deployment and configuration, one-click access
8 | * Full TLS communication encryption, safe and reliable
9 | * Support network bridge function, fast remote networking
10 | * Support IOT device networking
11 | * Can act as a proxy server
12 | * Full client support (ios android, windows, macos, linux)
13 | * Support flow control, speed limit
14 | * Support local file,http interface,LDAP authenticate method
15 |
16 | ## Why Developed PoleVPN
17 | * To work remotely during the epidemic, a VPN is required to access company and IDC resources
18 | * For multinational teams, VPN cannot be blocked by GFW, but OpenVPN will be blocked (although it can be disguised by other tunnel methods)
19 | * Need to have bridge function, routing function, can get through the company network, test environment network, development environment network, convenient for developers to debug and develop
20 | * The configuration should not be too complicated (OpenVPN installation, maintenance is too complicated)
21 |
22 | ## Documentation & Download
23 | * [Documents](https://www.polevpn.com/docs/en)
24 | * [Download](https://www.polevpn.com/index.html#download)
25 |
26 | ## Project official website
27 | * [polevpn.com](https://polevpn.com)
28 |
29 | ## Telegram group
30 | [Telegram Group](https://t.me/polevpn)
31 |
--------------------------------------------------------------------------------
/address_pool.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net"
5 | "sync"
6 | )
7 |
8 | type AddressPool struct {
9 | pool map[string]bool
10 | bindips map[string]string
11 | rbindips map[string]string
12 | mutex *sync.Mutex
13 | gw string
14 | network *net.IPNet
15 | }
16 |
17 | func NewAddressPool(cidr string, bindips map[string]string) (*AddressPool, error) {
18 |
19 | rbindips := make(map[string]string)
20 |
21 | for user, ip := range bindips {
22 | rbindips[ip] = user
23 | }
24 |
25 | _, network, err := net.ParseCIDR(cidr)
26 | if err != nil {
27 | return nil, err
28 | }
29 | networkipv4 := network.IP.To4()
30 | start := net.IPv4(networkipv4[0], networkipv4[1], networkipv4[2], networkipv4[3]).To4()
31 | var gw string
32 | pool := make(map[string]bool)
33 |
34 | n, c := network.Mask.Size()
35 | a := 1 << (c - n)
36 | for i := 1; i < a-1; i++ {
37 |
38 | if i%256 == 0 {
39 | start[2] += 1
40 | }
41 | if i%65536 == 0 {
42 | start[1] += 1
43 | }
44 | start[3] += 1
45 | if start[3] == 0 {
46 | continue
47 | }
48 | if i == 1 {
49 | gw = start.String()
50 | continue
51 | }
52 | _, ok := rbindips[start.String()]
53 | if ok {
54 | pool[start.String()] = true
55 | } else {
56 | pool[start.String()] = false
57 | }
58 |
59 | }
60 | return &AddressPool{pool: pool, mutex: &sync.Mutex{}, gw: gw, network: network, bindips: bindips, rbindips: rbindips}, nil
61 | }
62 |
63 | func (ap *AddressPool) Alloc() string {
64 |
65 | for ip, used := range ap.pool {
66 | if !used {
67 | ap.pool[ip] = true
68 | return ip
69 | }
70 | }
71 | return ""
72 | }
73 |
74 | func (ap *AddressPool) SetAllocIP(ip string) {
75 | ap.pool[ip] = true
76 | }
77 |
78 | func (ap *AddressPool) GatewayIP() string {
79 | return ap.gw
80 | }
81 |
82 | func (ap *AddressPool) GetNetwork() string {
83 | return ap.network.String()
84 | }
85 |
86 | func (ap *AddressPool) Release(ip string) {
87 | _, ok := ap.pool[ip]
88 | if ok {
89 | ap.pool[ip] = false
90 | }
91 | }
92 |
93 | func (ap *AddressPool) GetBindIP(user string) string {
94 | return ap.bindips[user]
95 | }
96 |
97 | func (ap *AddressPool) GetBindUser(ip string) string {
98 | return ap.rbindips[ip]
99 | }
100 |
101 | func (ap *AddressPool) IsAlloc(ip string) bool {
102 |
103 | v, ok := ap.pool[ip]
104 | if ok {
105 | return v
106 | }
107 | return ok
108 | }
109 |
--------------------------------------------------------------------------------
/address_pool_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net"
5 | "testing"
6 | )
7 |
8 | func TestCIDRAdress(t *testing.T) {
9 |
10 | _, net, err := net.ParseCIDR("10.8.0.0/16")
11 |
12 | if err != nil {
13 | t.Fatal(err)
14 | }
15 | t.Log(net.String())
16 | n, c := net.Mask.Size()
17 | a := 1 << (c - n)
18 | for i := 1; i < a-1; i++ {
19 |
20 | if i%2 == 0 {
21 | net.IP.To4()[2] += 1
22 | }
23 | if i%655 == 0 {
24 | net.IP.To4()[1+= 1
25 | }
26 | net.IP.To4()[ += 1
27 | if net.IP.To4()[3] == 0 {
28 | continue
29 | }
30 |
31 | t.Log(net.IP.To4())
32 | }
33 | }
34 |
35 | func TestCIDRAdressPool(t *testing.T) {
36 | pool, err := NewAddressPool("10.8.0.0/16", map[string]string{})
37 | if err != nil {
38 | t.Fatal(err)
39 | }
40 | t.Log(pool.GetNetwork())
41 | t.Log(pool.Alloc())
42 | ip := pool.Alloc()
43 | t.Log(ip)
44 | t.Log(pool.IsAlloc(ip))
45 | pool.Release(ip)
46 | t.Log(pool.IsAlloc(ip))
47 | t.Log(pool.Alloc())
48 | }
49 |
50 | func TestCIDR(t *testing.T) {
51 | ip, network, err := net.ParseCIDR("10.9.3.255/31")
52 | if ip.Strg() == network.IP.String() { gw := network.IP.To4()
53 | gw[3] = gw[3] + 1
54 | t.Log("gw=", gw)
55 | } else {
56 | t.Lo"gw=", network.IP)
57 | }
58 | t.Log(ip, network, err)
59 | }
60 |
--------------------------------------------------------------------------------
/bitop_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "testing"
4 |
5 | func TestBitOp(t *testing.T) {
6 | b := make([]byte, 1)
7 | b[0] = 0x60
8 | var a byte = 0x60
9 | a = a >> 4
10 | t.Log(a)
11 | c := b[0]
12 | c = c >> 4
13 | t.Log(c, b[0])
14 | }
15 |
--------------------------------------------------------------------------------
/config.json.example:
--------------------------------------------------------------------------------
1 | {
2 | "endpoint":{
3 | "listen":"0.0.0.0:443",
4 | "cert_file":"./keys/server.crt",
5 | "key_file":"./keys/server.key"
6 | },
7 | "network_cidr":"10.8.0.0/16",
8 | "dns":"8.8.8.8",
9 | "client_routes":["1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1"],
10 | "server_routes":[],
11 | "bind_ips":[],
12 | "up_traffic_limit":52428800,
13 | "down_traffic_limit":104857600,
14 | "auth":{
15 | "file":{
16 | "path":"users.credentials"
17 | },
18 | "http":{
19 | "url":"http://127.0.0.1/auth",
20 | "timeout":5
21 | },
22 | "ldap":{
23 | "host":"ldap://localhost",
24 | "admin_dn":"cn=admin,dc=polevpn,dc=com",
25 | "admin_pwd":"xxxxx",
26 | "user_dn":"ou=Users,dc=polevpn,dc=com"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/conn.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Conn interface {
4 | Read()
5 | Write()
6 | Send([]byte)
7 | Close(bool) error
8 | IsClosed() bool
9 | String() string
10 | }
11 |
--------------------------------------------------------------------------------
/conn_mgr.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "sync"
5 | "time"
6 |
7 | "github.com/polevpn/elog"
8 | )
9 |
10 | const (
11 | CONNECTION_TIMEOUT = 1
12 | CHECK_TIMEOUT_INTEVAL = 5
13 | )
14 |
15 | type ConnMgr struct {
16 | ip2conns map[string]Conn
17 | conn2ips map[string]string
18 | ip2actives map[string]time.Time
19 | ip2users map[string]string
20 | conn2users map[string]string
21 | mutex *sync.RWMutex
22 | addresspool *AddressPool
23 | }
24 |
25 | func NewConnMgr() *ConnMgr {
26 | cm := &ConnMgr{
27 | ip2conns: make(map[string]Conn),
28 | mutex: &sync.RWMutex{},
29 | conn2ips: make(map[string]string),
30 | ip2actives: make(map[string]time.Time),
31 | ip2users: make(map[string]string),
32 | conn2users: make(map[string]string),
33 | }
34 | go cm.CheckTimeout()
35 | return cm
36 | }
37 |
38 | func (cm *ConnMgr) CheckTimeout() {
39 | for range time.NewTicker(time.Second * CHECK_TIMEOUT_INTEVAL).C {
40 | timeNow := time.Now()
41 | iplist := make([]string, 0)
42 | cm.mutex.RLock()
43 | for ip, lastActive := range cm.ip2actives {
44 | if timeNow.Sub(lastActive) > time.Minute*CONNECTION_TIMEOUT {
45 | iplist = append(iplist, ip)
46 |
47 | }
48 | }
49 | cm.mutex.RUnlock()
50 |
51 | for _, ip := range iplist {
52 | cm.RelelaseAddress(ip)
53 | conn := cm.GetConnByIP(ip)
54 | if conn != nil {
55 | cm.DetachIPAddressFromConn(conn)
56 | cm.DetachUserFromConn(conn)
57 | conn.Close(false)
58 | }
59 | }
60 |
61 | }
62 | }
63 |
64 | func (cm *ConnMgr) SetAddressPool(addrespool *AddressPool) {
65 | cm.addresspool = addrespool
66 | }
67 |
68 | func (cm *ConnMgr) AllocAddress(conn Conn) string {
69 |
70 | cm.mutex.Lock()
71 | defer cm.mutex.Unlock()
72 |
73 | if cm.addresspool == nil {
74 | elog.Error("address pool haven't set")
75 | return ""
76 | }
77 |
78 | user := cm.conn2users[conn.String()]
79 | ip := cm.addresspool.GetBindIP(user)
80 |
81 | if ip != "" {
82 | _, ok := cm.ip2conns[ip]
83 | if ok {
84 | elog.Error("bind ip have been allocated")
85 | return ""
86 | }
87 | }
88 |
89 | if ip == "" {
90 | ip = cm.addresspool.Alloc()
91 | }
92 |
93 | if ip != "" {
94 | cm.ip2actives[ip] = time.Now()
95 | }
96 |
97 | return ip
98 | }
99 |
100 | func (cm *ConnMgr) CheckAndAllocAddress(user string, ip string) bool {
101 |
102 | cm.mutex.Lock()
103 | defer cm.mutex.Unlock()
104 |
105 | if cm.addresspool == nil {
106 | elog.Error("address pool haven't set")
107 | return false
108 | }
109 |
110 | dip := cm.addresspool.GetBindIP(user)
111 |
112 | if dip != "" {
113 | return true
114 | }
115 |
116 | if dip == "" {
117 | if !cm.addresspool.IsAlloc(ip) {
118 | cm.addresspool.SetAllocIP(ip)
119 | }
120 | }
121 |
122 | cm.ip2actives[ip] = time.Now()
123 |
124 | return true
125 | }
126 |
127 | func (cm *ConnMgr) RelelaseAddress(ip string) {
128 |
129 | cm.mutex.Lock()
130 | defer cm.mutex.Unlock()
131 |
132 | if cm.addresspool == nil {
133 | return
134 | }
135 |
136 | delete(cm.ip2actives, ip)
137 |
138 | user := cm.addresspool.GetBindUser(ip)
139 | if user != "" {
140 | return
141 | }
142 |
143 | cm.addresspool.Release(ip)
144 | }
145 |
146 | func (cm *ConnMgr) IsAllocedAddress(ip string) bool {
147 |
148 | cm.mutex.RLock()
149 | defer cm.mutex.RUnlock()
150 |
151 | if cm.addresspool == nil {
152 | return false
153 | }
154 |
155 | user := cm.addresspool.GetBindUser(ip)
156 | if user != "" {
157 | return true
158 | }
159 |
160 | return cm.addresspool.IsAlloc(ip)
161 | }
162 |
163 | func (cm *ConnMgr) GetBindUser(ip string) string {
164 |
165 | cm.mutex.RLock()
166 | defer cm.mutex.RUnlock()
167 |
168 | if cm.addresspool == nil {
169 | return ""
170 | }
171 | return cm.addresspool.GetBindUser(ip)
172 | }
173 |
174 | func (cm *ConnMgr) GetBindIP(user string) string {
175 |
176 | cm.mutex.RLock()
177 | defer cm.mutex.RUnlock()
178 |
179 | if cm.addresspool == nil {
180 | return ""
181 | }
182 | return cm.addresspool.GetBindIP(user)
183 | }
184 |
185 | func (cm *ConnMgr) UpdateConnActiveTime(conn Conn) {
186 |
187 | cm.mutex.Lock()
188 | defer cm.mutex.Unlock()
189 | ip, ok := cm.conn2ips[conn.String()]
190 | if ok {
191 | cm.ip2actives[ip] = time.Now()
192 | }
193 | }
194 |
195 | func (cm *ConnMgr) AttachIPAddressToConn(ip string, conn Conn) {
196 | cm.mutex.Lock()
197 | defer cm.mutex.Unlock()
198 | sconn, ok := cm.ip2conns[ip]
199 | if ok {
200 | delete(cm.conn2ips, sconn.String())
201 | }
202 | cm.ip2conns[ip] = conn
203 | cm.conn2ips[conn.String()] = ip
204 | }
205 |
206 | func (cm *ConnMgr) IsDetached(ip string) bool {
207 | cm.mutex.RLock()
208 | defer cm.mutex.RUnlock()
209 | _, ok := cm.ip2conns[ip]
210 | return ok
211 | }
212 |
213 | func (cm *ConnMgr) DetachIPAddressFromConn(conn Conn) {
214 | cm.mutex.Lock()
215 | defer cm.mutex.Unlock()
216 | ip, ok := cm.conn2ips[conn.String()]
217 | if ok {
218 | sconn, ok := cm.ip2conns[ip]
219 | if ok && sconn.String() == conn.String() {
220 | delete(cm.ip2conns, ip)
221 | }
222 | delete(cm.conn2ips, conn.String())
223 | }
224 | }
225 |
226 | func (cm *ConnMgr) AttachUserToConn(user string, conn Conn) {
227 | cm.mutex.Lock()
228 | defer cm.mutex.Unlock()
229 | cm.conn2users[conn.String()] = user
230 | }
231 |
232 | func (cm *ConnMgr) DetachUserFromConn(conn Conn) {
233 | cm.mutex.Lock()
234 | defer cm.mutex.Unlock()
235 | delete(cm.conn2users, conn.String())
236 | }
237 |
238 | func (cm *ConnMgr) GetConnAttachUser(conn Conn) string {
239 | cm.mutex.RLock()
240 | defer cm.mutex.RUnlock()
241 | return cm.conn2users[conn.String()]
242 | }
243 |
244 | func (cm *ConnMgr) AttachUserToIP(user string, ip string) {
245 | cm.mutex.Lock()
246 | defer cm.mutex.Unlock()
247 | cm.ip2users[ip] = user
248 | }
249 |
250 | func (cm *ConnMgr) DetachUserFromIP(ip string) {
251 | cm.mutex.Lock()
252 | defer cm.mutex.Unlock()
253 | delete(cm.ip2users, ip)
254 | }
255 |
256 | func (cm *ConnMgr) GetIPAttachUser(ip string) string {
257 | cm.mutex.RLock()
258 | defer cm.mutex.RUnlock()
259 | return cm.ip2users[ip]
260 | }
261 |
262 | func (cm *ConnMgr) GetConnByIP(ip string) Conn {
263 | cm.mutex.RLock()
264 | defer cm.mutex.RUnlock()
265 | return cm.ip2conns[ip]
266 | }
267 |
268 | func (cm *ConnMgr) GeIPByConn(conn Conn) string {
269 | cm.mutex.RLock()
270 | defer cm.mutex.RUnlock()
271 | return cm.conn2ips[conn.String()]
272 | }
273 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/polevpn/polevpn_server
2 |
3 | go 1.22
4 |
5 | toolchain go1.23.1
6 |
7 | require (
8 | github.com/go-ldap/ldap/v3 v3.4.4
9 | github.com/gorilla/websocket v1.5.0
10 | github.com/polevpn/anyvalue v1.0.6
11 | github.com/polevpn/elog v1.1.1
12 | github.com/polevpn/h3conn v1.0.20
13 | github.com/polevpn/netstack v1.10.12
14 | github.com/polevpn/water v1.0.4
15 | github.com/quic-go/quic-go v0.47.0
16 | )
17 |
18 | require (
19 | github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
20 | github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
21 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
22 | github.com/google/btree v1.0.0 // indirect
23 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
24 | github.com/kr/pretty v0.3.1 // indirect
25 | github.com/onsi/ginkgo/v2 v2.9.5 // indirect
26 | github.com/quic-go/qpack v0.5.1 // indirect
27 | github.com/rogpeppe/go-internal v1.10.0 // indirect
28 | github.com/vmihailenco/msgpack/v5 v5.0.0 // indirect
29 | github.com/vmihailenco/tagparser v0.1.2 // indirect
30 | go.uber.org/mock v0.4.0 // indirect
31 | golang.org/x/crypto v0.26.0 // indirect
32 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
33 | golang.org/x/mod v0.17.0 // indirect
34 | golang.org/x/net v0.28.0 // indirect
35 | golang.org/x/sys v0.23.0 // indirect
36 | golang.org/x/text v0.17.0 // indirect
37 | golang.org/x/time v0.5.0 // indirect
38 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
39 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
40 | gopkg.in/yaml.v2 v2.4.0 // indirect
41 | )
42 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
2 | github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
3 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
4 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
5 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
6 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
11 | github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
12 | github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
13 | github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
14 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
15 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
16 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
17 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
18 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
19 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
20 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
21 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
22 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
23 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
24 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
25 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
26 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
27 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
28 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
29 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
30 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
31 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
32 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
33 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
34 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
35 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
36 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
37 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
38 | github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
39 | github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
40 | github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
41 | github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
42 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
43 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
44 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
45 | github.com/polevpn/anyvalue v1.0.6 h1:IaAtj0ZkOfAqTd1K8rc5JvDt/qFsuivzrI4LXrYC4ic=
46 | github.com/polevpn/anyvalue v1.0.6/go.mod h1:SAmPKOT6BtaGar2NZc9u/TUZgDYBXO7lZZ9q1MU0LVk=
47 | github.com/polevpn/elog v1.1.1 h1:+Nnyaajv//fbmVextCGrcFfZXi+isprA1qQag2dcwhE=
48 | github.com/polevpn/elog v1.1.1/go.mod h1:u30sJGFE2hYV+KuEmE4Bw7Qh0J3eXS3AEXjyHtqPDjU=
49 | github.com/polevpn/h3conn v1.0.20 h1:3aP9P4MXuo5Tzws3y36ldsmku15uSpggjez5arAAQ3k=
50 | github.com/polevpn/h3conn v1.0.20/go.mod h1:Xe6Vhr5J/jd1/cbyyVUh5VrJ3fK5VRfyYL8QEg7O24s=
51 | github.com/polevpn/netstack v1.10.12 h1:IEZn6ddntOvV1xu8fbAIp4hRy+9x3UH/eCoKLVRciYA=
52 | github.com/polevpn/netstack v1.10.12/go.mod h1:2C2dP1yP9Jyu2XqHhPV0AxA9F1IbdZI1QpX8wdXAMGU=
53 | github.com/polevpn/water v1.0.4 h1:OCLCeKgdQwlh4J5GjR7zzsd8u+tJ/EN8XAZQ931V6PI=
54 | github.com/polevpn/water v1.0.4/go.mod h1:mc/yhflKH5xQ9HKP8e4Qra21Qoh2I1DXvu1vE0mGGkM=
55 | github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
56 | github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
57 | github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
58 | github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
59 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
60 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
61 | github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
62 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
63 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
64 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
65 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
66 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
67 | github.com/vmihailenco/msgpack/v5 v5.0.0 h1:nCaMMPEyfgwkGc/Y0GreJPhuvzqCqW+Ufq5lY7zLO2c=
68 | github.com/vmihailenco/msgpack/v5 v5.0.0/go.mod h1:HVxBVPUK/+fZMonk4bi1islLa8V3cfnBug0+4dykPzo=
69 | github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
70 | github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
71 | go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
72 | go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
73 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
74 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
75 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
76 | golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
77 | golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
78 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
79 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
80 | golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
81 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
82 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
83 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
84 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
85 | golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
86 | golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
87 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
88 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
89 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
90 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
91 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
92 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
93 | golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
94 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
95 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
96 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
97 | golang.org/x/sys v0.0.0-20211002104244-808efd93c36d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
98 | golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
99 | golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
100 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
101 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
102 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
103 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
104 | golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
105 | golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
106 | golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
107 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
108 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
109 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
110 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
111 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
112 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
113 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
114 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
115 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
116 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
117 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
118 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
119 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
120 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
121 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
122 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
123 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
124 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
125 |
--------------------------------------------------------------------------------
/http3_conn.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "math/rand"
5 | "time"
6 |
7 | "github.com/polevpn/elog"
8 | "github.com/polevpn/h3conn"
9 | "github.com/polevpn/netstack/tcpip/header"
10 | "github.com/polevpn/netstack/tcpip/transport/tcp"
11 | "github.com/polevpn/netstack/tcpip/transport/udp"
12 | )
13 |
14 | const (
15 | CH_H3C_WRITE_SIZE = 100
16 | )
17 |
18 | type Http3Conn struct {
19 | conn *h3conn.Conn
20 | wch chan []byte
21 | closed bool
22 | handler *RequestHandler
23 | downlimit uint64
24 | uplimit uint64
25 | tcDownStream *TrafficCounter
26 | tcUpStream *TrafficCounter
27 | }
28 |
29 | func NewHttp3Conn(conn *h3conn.Conn, downlimit uint64, uplimit uint64, handler *RequestHandler) *Http3Conn {
30 | return &Http3Conn{
31 | conn: conn,
32 | closed: false,
33 | wch: make(chan []byte, CH_H3C_WRITE_SIZE),
34 | handler: handler,
35 | downlimit: downlimit,
36 | uplimit: uplimit,
37 | tcDownStream: NewTrafficCounter(TRAFFIC_LIMIT_INTERVAL * time.Millisecond),
38 | tcUpStream: NewTrafficCounter(TRAFFIC_LIMIT_INTERVAL * time.Millisecond),
39 | }
40 | }
41 |
42 | func (h3c *Http3Conn) Close(flag bool) error {
43 | if !h3c.closed {
44 | h3c.closed = true
45 | if h3c.wch != nil {
46 | h3c.wch <- nil
47 | close(h3c.wch)
48 | }
49 | err := h3c.conn.Close()
50 | if flag {
51 | go h3c.handler.OnClosed(h3c, false)
52 | }
53 | return err
54 | }
55 | return nil
56 | }
57 |
58 | func (h3c *Http3Conn) String() string {
59 | return h3c.conn.RemoteAddr().String() + "->" + h3c.conn.LocalAddr().String()
60 | }
61 |
62 | func (h3c *Http3Conn) IsClosed() bool {
63 | return h3c.closed
64 | }
65 |
66 | func (h3c *Http3Conn) checkStreamLimit(pkt []byte, tfcounter *TrafficCounter, limit uint64) (bool, time.Duration) {
67 | bytes, ltime := tfcounter.StreamCount(uint64(len(pkt)))
68 | if bytes > limit/(1000/uint64(tfcounter.StreamCountInterval()/time.Millisecond)) {
69 | duration := ltime.Add(tfcounter.StreamCountInterval()).Sub(time.Now())
70 | if duration > 0 {
71 | drop := false
72 | if len(h3c.wch) > CH_WEBSOCKET_WRITE_SIZE*0.5 {
73 | ippkt := header.IPv4(pkt)
74 | if ippkt.Protocol() == uint8(tcp.ProtocolNumber) {
75 | n := rand.Intn(5)
76 | if n > 2 {
77 | drop = true
78 | }
79 | } else if ippkt.Protocol() == uint8(udp.ProtocolNumber) {
80 | udppkt := header.UDP(ippkt.Payload())
81 | if udppkt.DestinationPort() != 53 && udppkt.SourcePort() != 53 {
82 | drop = true
83 | }
84 | }
85 | }
86 |
87 | if drop {
88 | return true, 0
89 | } else {
90 | return true, duration
91 | }
92 | }
93 | }
94 | return false, 0
95 | }
96 |
97 | func (h3c *Http3Conn) Read() {
98 |
99 | defer func() {
100 | h3c.Close(true)
101 | }()
102 |
103 | defer PanicHandler()
104 |
105 | for {
106 |
107 | pkt, err := ReadPacket(h3c.conn)
108 |
109 | if err != nil {
110 | elog.Info(h3c.String(), " read h3conn end,status=", err)
111 | return
112 | }
113 |
114 | ppkt := PolePacket(pkt)
115 | if ppkt.Cmd() == CMD_C2S_IPDATA {
116 | //traffic limit
117 | limit, duration := h3c.checkStreamLimit(ppkt.Payload(), h3c.tcUpStream, h3c.uplimit)
118 | if limit {
119 | if duration > 0 {
120 | time.Sleep(duration)
121 | } else {
122 | continue
123 | }
124 | }
125 | }
126 | h3c.handler.OnRequest(pkt, h3c)
127 |
128 | }
129 |
130 | }
131 |
132 | func (h3c *Http3Conn) drainWriteCh() {
133 | for {
134 | select {
135 | case _, ok := <-h3c.wch:
136 | if !ok {
137 | return
138 | }
139 | default:
140 | return
141 | }
142 | }
143 | }
144 |
145 | func (h3c *Http3Conn) Write() {
146 |
147 | defer PanicHandler()
148 | defer h3c.drainWriteCh()
149 |
150 | for {
151 |
152 | pkt, ok := <-h3c.wch
153 | if !ok {
154 | elog.Error(h3c.String(), " channel closed")
155 | return
156 | }
157 | if pkt == nil {
158 | elog.Info(h3c.String(), " exit write process")
159 | return
160 | }
161 |
162 | ppkt := PolePacket(pkt)
163 | if ppkt.Cmd() == CMD_S2C_IPDATA {
164 | //traffic limit
165 | limit, duration := h3c.checkStreamLimit(ppkt.Payload(), h3c.tcDownStream, h3c.downlimit)
166 | if limit {
167 | if duration > 0 {
168 | time.Sleep(duration)
169 | } else {
170 | continue
171 | }
172 | }
173 | }
174 | _, err := h3c.conn.Write(pkt)
175 | if err != nil {
176 | elog.Error(h3c.String(), " h3conn write end status=", err)
177 | return
178 | }
179 | }
180 | }
181 |
182 | func (h3c *Http3Conn) Send(pkt []byte) {
183 | if h3c.closed {
184 | return
185 | }
186 | if h3c.wch != nil {
187 |
188 | select {
189 | case h3c.wch <- pkt:
190 | default:
191 | elog.Error(h3c.String(), " wch is full")
192 | }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/http_server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | "strings"
6 | "sync"
7 |
8 | "github.com/gorilla/websocket"
9 | "github.com/polevpn/elog"
10 | "github.com/polevpn/h3conn"
11 | "github.com/quic-go/quic-go/http3"
12 | )
13 |
14 | const (
15 | TCP_WRITE_BUFFER_SIZE = 524288
16 | TCP_READ_BUFFER_SIZE = 524288
17 | )
18 |
19 | type HttpServer struct {
20 | requestHandler *RequestHandler
21 | loginchecker LoginChecker
22 | upgrader *websocket.Upgrader
23 | uplimit uint64
24 | downlimit uint64
25 | }
26 |
27 | func NewHttpServer(uplimit uint64, downlimit uint64, requestHandler *RequestHandler) *HttpServer {
28 |
29 | upgrader := &websocket.Upgrader{
30 | CheckOrigin: func(r *http.Request) bool {
31 | return true
32 | },
33 | EnableCompression: false,
34 | }
35 |
36 | return &HttpServer{requestHandler: requestHandler, upgrader: upgrader, uplimit: uplimit, downlimit: downlimit}
37 | }
38 |
39 | func (hs *HttpServer) SetLoginCheckHandler(loginchecker LoginChecker) {
40 | hs.loginchecker = loginchecker
41 | }
42 |
43 | func (hs *HttpServer) defaultHandler(w http.ResponseWriter, r *http.Request) {
44 | hs.respError(http.StatusForbidden, w)
45 | }
46 |
47 | func (hs *HttpServer) ListenTLS(wg *sync.WaitGroup, addr string, certFile string, keyFile string) {
48 |
49 | defer wg.Done()
50 |
51 | handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
52 |
53 | if r.URL.Path == "/" {
54 | if r.ProtoAtLeast(3, 0) {
55 | hs.h3Handler(w, r)
56 | } else {
57 | hs.wsHandler(w, r)
58 | }
59 | } else {
60 | hs.defaultHandler(w, r)
61 | }
62 | })
63 | elog.Error(http3.ListenAndServeTLS(addr, certFile, keyFile, handler))
64 | }
65 |
66 | func (hs *HttpServer) respError(status int, w http.ResponseWriter) {
67 | if status == http.StatusBadRequest {
68 | w.Header().Add("Server", "nginx/1.10.3")
69 | w.WriteHeader(http.StatusBadRequest)
70 | w.Write([]byte("\n
400 Bad Request\n\n400 Bad Request
\n
nginx/1.10.3\n\n"))
71 | } else if status == http.StatusForbidden {
72 | w.Header().Add("Server", "nginx/1.10.3")
73 | w.WriteHeader(http.StatusForbidden)
74 | w.Write([]byte("\n403 Forbidden\n\n403 Forbidden
\n
nginx/1.10.3\n\n"))
75 |
76 | }
77 | }
78 |
79 | func (hs *HttpServer) h3Handler(w http.ResponseWriter, r *http.Request) {
80 |
81 | defer PanicHandler()
82 |
83 | user := r.URL.Query().Get("user")
84 | pwd := r.URL.Query().Get("pwd")
85 | ip := r.URL.Query().Get("ip")
86 | deviceType := r.URL.Query().Get("deviceType")
87 | deviceId := r.URL.Query().Get("deviceId")
88 |
89 | if user == "" || pwd == "" {
90 | hs.respError(http.StatusForbidden, w)
91 | return
92 | }
93 |
94 | elog.Infof("user:%v,ip:%v,deviceType:%v,deviceId:%v,remoteip:%v connect,xff:%v", user, ip, deviceType, deviceId, r.RemoteAddr, r.Header.Get("X-Forwarded-For"))
95 |
96 | err := hs.loginchecker.CheckLogin(user, pwd, strings.Split(r.RemoteAddr, ":")[0], deviceType, deviceId)
97 | if err != nil {
98 | elog.Errorf("user:%v,pwd:%v,ip:%v verify fail,%v", user, pwd, ip, err)
99 | hs.respError(http.StatusForbidden, w)
100 | return
101 | }
102 |
103 | if ip != "" {
104 |
105 | if !hs.requestHandler.connmgr.CheckAndAllocAddress(user, ip) {
106 | elog.Errorf("user:%v,pwd:%v,ip:%v reconnect fail,ip address not alloc to it", user, pwd, ip)
107 | hs.respError(http.StatusBadRequest, w)
108 | return
109 | }
110 |
111 | if hs.requestHandler.connmgr.GetIPAttachUser(ip) != "" && hs.requestHandler.connmgr.GetIPAttachUser(ip) != user {
112 | elog.Errorf("user:%v,pwd:%v,ip:%v reconnect fail,ip address not belong to the user", user, pwd, ip)
113 | hs.respError(http.StatusBadRequest, w)
114 | return
115 | }
116 | }
117 |
118 | conn, err := h3conn.Accept(w, r)
119 | if err != nil {
120 | elog.Error("upgrade http request to h3 fail", err)
121 | return
122 | }
123 |
124 | elog.Info("accpet new h3 conn from ", user, " ", conn.RemoteAddr().String())
125 | if hs.requestHandler == nil {
126 | elog.Error("request dispatcher haven't set")
127 | return
128 | }
129 |
130 | if hs.requestHandler != nil {
131 | h3conn := NewHttp3Conn(conn, hs.downlimit, hs.uplimit, hs.requestHandler)
132 | hs.requestHandler.OnConnection(h3conn, user, ip)
133 | go h3conn.Read()
134 | go h3conn.Write()
135 | } else {
136 | elog.Error("h3 conn handler haven't set")
137 | conn.Close()
138 | }
139 |
140 | }
141 |
142 | func (hs *HttpServer) wsHandler(w http.ResponseWriter, r *http.Request) {
143 |
144 | defer PanicHandler()
145 |
146 | user := r.URL.Query().Get("user")
147 | pwd := r.URL.Query().Get("pwd")
148 | ip := r.URL.Query().Get("ip")
149 | deviceType := r.URL.Query().Get("deviceType")
150 | deviceId := r.URL.Query().Get("deviceId")
151 |
152 | if user == "" || pwd == "" {
153 | hs.respError(http.StatusForbidden, w)
154 | return
155 | }
156 |
157 | elog.Infof("user:%v,ip:%v,deviceType:%v,deviceId:%v,remoteip:%v connect,xff:%v", user, ip, deviceType, deviceId, r.RemoteAddr, r.Header.Get("X-Forwarded-For"))
158 |
159 | err := hs.loginchecker.CheckLogin(user, pwd, strings.Split(r.RemoteAddr, ":")[0], deviceType, deviceId)
160 | if err != nil {
161 | elog.Errorf("user:%v,pwd:%v,ip:%v verify fail,%v", user, pwd, ip, err)
162 | hs.respError(http.StatusForbidden, w)
163 | return
164 | }
165 |
166 | if ip != "" {
167 | if !hs.requestHandler.connmgr.CheckAndAllocAddress(user, ip) {
168 | elog.Errorf("user:%v,pwd:%v,ip:%v reconnect fail,ip address not alloc to it", user, pwd, ip)
169 | hs.respError(http.StatusBadRequest, w)
170 | return
171 | }
172 |
173 | if hs.requestHandler.connmgr.GetIPAttachUser(ip) != "" && hs.requestHandler.connmgr.GetIPAttachUser(ip) != user {
174 | elog.Errorf("user:%v,pwd:%v,ip:%v reconnect fail,ip address not belong to the user", user, pwd, ip)
175 | hs.respError(http.StatusBadRequest, w)
176 | return
177 | }
178 | }
179 |
180 | conn, err := hs.upgrader.Upgrade(w, r, nil)
181 | if err != nil {
182 | elog.Error("upgrade http request to ws fail,", err)
183 | return
184 | }
185 |
186 | elog.Info("accpet new ws conn from ", user, " ", conn.RemoteAddr().String())
187 | if hs.requestHandler == nil {
188 | elog.Error("request dispatcher haven't set")
189 | return
190 | }
191 |
192 | if hs.requestHandler != nil {
193 | wsconn := NewWebSocketConn(conn, hs.downlimit, hs.uplimit, hs.requestHandler)
194 | hs.requestHandler.OnConnection(wsconn, user, ip)
195 | go wsconn.Read()
196 | go wsconn.Write()
197 | } else {
198 | elog.Error("ws conn handler haven't set")
199 | conn.Close()
200 | }
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/keys/server.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDxTCCAq2gAwIBAgIJANMs1UZaJPcbMA0GCSqGSIb3DQEBCwUAMHkxCzAJBgNV
3 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRMwEQYDVQQHDApDYWxpZm9ybmlh
4 | MRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQLDApBcHBsZSBJbmMuMRYwFAYD
5 | VQQDDA13d3cuYXBwbGUuY29tMB4XDTIwMTExNDEzMjQ1MloXDTIxMTExNDEzMjQ1
6 | MloweTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEzARBgNVBAcM
7 | CkNhbGlmb3JuaWExEzARBgNVBAoMCkFwcGxlIEluYy4xEzARBgNVBAsMCkFwcGxl
8 | IEluYy4xFjAUBgNVBAMMDXd3dy5hcHBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
9 | A4IBDwAwggEKAoIBAQC3sTQFB5R9bkVijp+nP7iRhaUDS5kSRVGAgRdyjq0If9VO
10 | sqQVtT3FLEPgqIwdDcth6u2MTFwCMU/xja4PMFyIcemhmzacuIX3sKjUD7d6YmvP
11 | hd4EO284FxQURpZ0FElsF1hZwHzkb8awKC7yy5sBH/Rvm0XqVS2qZtMK6656kVjb
12 | C23wPjG5orNuWeNTEy2lLNwUf4J6oY2jnxncXLMTFc+4fWfBh/xGoKy3FPEwxLHa
13 | XxKVVJX2QLgrIINyhX7q9s/fUlCmey8Mqts88WL7WS9AI3LIlToX77ABujhjmP/y
14 | XQfnZ0BtdKOJnngJNJ+tsmTS23fTyY6xYpCGEDvbAgMBAAGjUDBOMB0GA1UdDgQW
15 | BBRM/L1G4qZDfF2tXRE/XOsvA5rVojAfBgNVHSMEGDAWgBRM/L1G4qZDfF2tXRE/
16 | XOsvA5rVojAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCXic/WI7V0
17 | qvbKelisDmNZwQlP8MWmGeEtLxWzLEcKK+f5k64c2s675SUlTKfZndv5Ktlm4mQz
18 | T4VPaUEUfgYcL8g5DIDeRTTapjva2LP/YhNGZUJ5GKSpuCNgPodNwMuDn67pDuTC
19 | Hnj6XpYt/HA4W5EFArIViIJNH6qZnoh6pIrWB4/8pyIfS3ciz6IZDaJjO0NO09g8
20 | dD2LxlLbS+RXafS/GwXb6aTvHskeRR95WFU+EzOwHjac2muqyiLqiUKHpEbV0ECR
21 | y5QpetJXhmHo/hrt5n6YIziFKDuuMYSA0SvOHDHm/3szpsZOteo2GYQZ/P1fjmmX
22 | hy8pou51Wk0b
23 | -----END CERTIFICATE-----
24 |
--------------------------------------------------------------------------------
/keys/server.key:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC3sTQFB5R9bkVi
3 | jp+nP7iRhaUDS5kSRVGAgRdyjq0If9VOsqQVtT3FLEPgqIwdDcth6u2MTFwCMU/x
4 | ja4PMFyIcemhmzacuIX3sKjUD7d6YmvPhd4EO284FxQURpZ0FElsF1hZwHzkb8aw
5 | KC7yy5sBH/Rvm0XqVS2qZtMK6656kVjbC23wPjG5orNuWeNTEy2lLNwUf4J6oY2j
6 | nxncXLMTFc+4fWfBh/xGoKy3FPEwxLHaXxKVVJX2QLgrIINyhX7q9s/fUlCmey8M
7 | qts88WL7WS9AI3LIlToX77ABujhjmP/yXQfnZ0BtdKOJnngJNJ+tsmTS23fTyY6x
8 | YpCGEDvbAgMBAAECggEBAID2lNnvH24MTARMs4PdNbxdAqPUoGv2ZKj+eLLBvnOY
9 | I+6zdoWTEAHs+N6yFmSTebWOFSaJuZMEgebJnGknf99/chI2gVn4Jn3l9jGjsyHm
10 | FWDF0EBSZdwQP9WYVSq7z2xm221vuX87hlfIbmWhUnqwr5Dnh2dkU8ItnH8vRfjf
11 | /+D4+DVpCh6+4TR31A3bISyQrfAwhg4JPGjDx5wVhGsZ1zsyIsh9n7jGt9VDYvwY
12 | 4IhwTTgVFnTHyuLSaCx0nVvmodebLL2wrRpoHA3+ct1sKlqYoPZdC938/4WrkZMk
13 | x0j+isk56tfjsYlYoSYROMDMysXvnGUXg9MfbKMpNXkCgYEA3P3Hu1fAPkkWnay7
14 | AoT0BbcDlruWSOP5RxNLGuwFyOwcIq+UMCBxJuAzn5l4tR6e/UZrDafifor1OPsF
15 | NTO5+DZT78Xqvv5DMbIiYVQ25DXcQb0d13w/lAoE4LfDueqGGP6sgcNyYoSE3eIH
16 | xConvcKazom8TUEU9yr/t5ELJd0CgYEA1MrFW4HyHF5osBR2DZGB+G7gglbgrW5b
17 | QgHHwBy3Y8DMc2djqt1wO6JhRlABus1q2CP7HAsYCuPVy26X0eSn2gFYeJCqmF4u
18 | 7V+q2leAr+xshWhBDykdeANUdDcq5LMoqmsHwuqEP3C7PbZD6jAJRFfm7A9d0Fbw
19 | TGXWBAYeWRcCgYEAn/pro7iDp9NbNuu7ObirupNhWrYTtlKqu4RBA41HJsueFpIK
20 | cciagS0e0fPVCTDQocNfbjRR54+KtCQsAfafMwuJOfOJ25fPZuNwtgqz7gL7nu9b
21 | 8gm1MOML4u3FGAqp5uA+W3X0kVMjqEifnKdzu5BsZwYYtcheeAu9sNKkQlUCgYAp
22 | QHoMKF/oUgEXiY/tR3Ub1VreybraxFcar7+qpaaDxx+bi8KNoEoetXcIK4B6V1JM
23 | PoVyxU/O5KZQvsrd3OcRY0tkbB82VKPiTraPh4EiGcJcBn7+UmktF/Aqa3t8RKMw
24 | jnk9tzsWFWOnLgtyfgd94VCaGMyGFCSXmvJna0w81wKBgQCpX83oulnlsPiQXnr1
25 | WaTZ1ZxoFwQCTO5FK3qGlSOQwIzfqAEeVdb3V+WuH6e/QA0AxXgX43CeF9ypQoGa
26 | tU72Zdy8HneN/cmqemxMQyij+6qXsjxqU9Pd3V2X7Z829uyi3jx8GlbuqHoWy9Zq
27 | YS9a3Bf9XmFUrLtOt+WkuPl+ig==
28 | -----END PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/local_login_check.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "errors"
7 | "fmt"
8 | "io"
9 | "io/ioutil"
10 | "net/http"
11 | "os"
12 | "strings"
13 | "time"
14 |
15 | "github.com/go-ldap/ldap/v3"
16 | "github.com/polevpn/anyvalue"
17 | )
18 |
19 | const (
20 | MAX_PASSWORD_LENGHT = 16
21 | )
22 |
23 | type User struct {
24 | Uid uint64
25 | Email string
26 | Vip int
27 | VipExpireTime uint64
28 | LastLoginTime uint64
29 | }
30 |
31 | type LocalLoginChecker struct {
32 | }
33 |
34 | func NewLocalLoginChecker() *LocalLoginChecker {
35 | return &LocalLoginChecker{}
36 | }
37 |
38 | func (llc *LocalLoginChecker) CheckLogin(user string, pwd string, remoteIp string, deviceType string, deviceId string) error {
39 |
40 | var err error
41 |
42 | if Config.Has("auth.file") {
43 | err = llc.checkFileLogin(user, pwd)
44 | }
45 |
46 | if err == nil {
47 | return nil
48 | }
49 |
50 | if Config.Has("auth.http") {
51 | err = llc.checkHttpLogin(user, pwd, remoteIp, deviceType, deviceId)
52 | }
53 |
54 | if err == nil {
55 | return nil
56 | }
57 |
58 | if Config.Has("auth.ldap") {
59 | err = llc.checkLDAPLogin(user, pwd)
60 | }
61 |
62 | return err
63 |
64 | }
65 |
66 | func (llc *LocalLoginChecker) checkFileLogin(user string, pwd string) error {
67 |
68 | filePath := Config.Get("auth.file.path").AsStr()
69 |
70 | f, err := os.Open(filePath)
71 | if err != nil {
72 | return err
73 | }
74 | defer f.Close()
75 |
76 | br := bufio.NewReader(f)
77 | for {
78 | line, _, c := br.ReadLine()
79 | if c == io.EOF {
80 | break
81 | }
82 | userPassword := strings.Trim(string(line), "\n\r")
83 | userPasswordArr := strings.Split(userPassword, ",")
84 | if len(userPasswordArr) != 2 {
85 | continue
86 | }
87 | if userPasswordArr[0] == user && userPasswordArr[1] == pwd {
88 | return nil
89 | }
90 | }
91 | return errors.New("user or password incorrect")
92 | }
93 |
94 | func (llc *LocalLoginChecker) checkHttpLogin(user string, pwd string, remoteIp string, deviceType string, deviceId string) error {
95 |
96 | req := anyvalue.New()
97 |
98 | req.Set("user", user)
99 | req.Set("pwd", pwd)
100 | req.Set("deviceType", deviceType)
101 | req.Set("deviceId", deviceId)
102 | req.Set("remoteIp", remoteIp)
103 |
104 | data, _ := req.EncodeJson()
105 |
106 | client := http.Client{Timeout: time.Duration(Config.Get("auth.http.timeout").AsInt()) * time.Second}
107 | request, err := http.NewRequest(http.MethodPost, Config.Get("auth.http.url").AsStr(), bytes.NewReader(data))
108 |
109 | if err != nil {
110 | return err
111 | }
112 |
113 | resp, err := client.Do(request)
114 |
115 | if err != nil {
116 | return err
117 | }
118 | defer resp.Body.Close()
119 |
120 | if resp.StatusCode != http.StatusOK {
121 | data, err := ioutil.ReadAll(resp.Body)
122 | if err != nil {
123 | return nil
124 | }
125 | return errors.New(string(data))
126 | }
127 | return nil
128 | }
129 |
130 | func (llc *LocalLoginChecker) checkLDAPLogin(user string, pwd string) error {
131 |
132 | l, err := ldap.DialURL(Config.Get("auth.ldap.host").AsStr())
133 | if err != nil {
134 | return err
135 | }
136 | defer l.Close()
137 |
138 | err = l.Bind(Config.Get("auth.ldap.admin_dn").AsStr(), Config.Get("auth.ldap.admin_pwd").AsStr())
139 | if err != nil {
140 | return err
141 | }
142 |
143 | searchRequest := ldap.NewSearchRequest(
144 | Config.Get("auth.ldap.user_dn").AsStr(),
145 | ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
146 | fmt.Sprintf("(&(objectClass=organizationalPerson)(uid=%s))", user),
147 | []string{"dn"},
148 | nil,
149 | )
150 |
151 | sr, err := l.Search(searchRequest)
152 | if err != nil {
153 | return err
154 | }
155 |
156 | if len(sr.Entries) != 1 {
157 | return errors.New("User does not exist")
158 | }
159 |
160 | userdn := sr.Entries[0].DN
161 |
162 | err = l.Bind(userdn, pwd)
163 | if err != nil {
164 | return err
165 | }
166 |
167 | return nil
168 | }
169 |
--------------------------------------------------------------------------------
/login_check.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type LoginChecker interface {
4 | CheckLogin(user string, pwd string, remoteIp string, deviceType string, deviceId string) error
5 | }
6 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "os"
6 | "os/signal"
7 | "syscall"
8 |
9 | "github.com/polevpn/anyvalue"
10 | "github.com/polevpn/elog"
11 | )
12 |
13 | const (
14 | CH_TUNIO_WRITE_SIZE = 200
15 | )
16 |
17 | var Config *anyvalue.AnyValue
18 | var configPath string
19 |
20 | func init() {
21 | flag.StringVar(&configPath, "config", "./config.json", "config file path")
22 | }
23 |
24 | func signalHandler() {
25 |
26 | c := make(chan os.Signal)
27 | signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
28 | go func() {
29 | for s := range c {
30 | switch s {
31 | case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
32 | elog.Fatal("receive exit signal,exit")
33 | case syscall.SIGUSR1:
34 | case syscall.SIGUSR2:
35 | default:
36 | }
37 | }
38 | }()
39 | }
40 |
41 | func main() {
42 |
43 | flag.Parse()
44 | defer elog.Flush()
45 | signalHandler()
46 |
47 | var err error
48 |
49 | Config, err = GetConfig(configPath)
50 | if err != nil {
51 | elog.Fatal("load config fail", err)
52 | }
53 | err = NewPoleVPNServer().Start(Config)
54 | if err != nil {
55 | elog.Fatal("start polevpn server fail,", err)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packet_dispatcher.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net"
5 |
6 | "github.com/polevpn/elog"
7 | "github.com/polevpn/netstack/tcpip/header"
8 | )
9 |
10 | const (
11 | IPV4_PROTOCOL = 4
12 | IPV6_PROTOCOL = 6
13 | )
14 |
15 | type PacketDispatcher struct {
16 | connmgr *ConnMgr
17 | routermgr *RouterMgr
18 | }
19 |
20 | func NewPacketDispatcher() *PacketDispatcher {
21 |
22 | return &PacketDispatcher{}
23 | }
24 |
25 | func (p *PacketDispatcher) SetConnMgr(connmgr *ConnMgr) {
26 | p.connmgr = connmgr
27 | }
28 |
29 | func (p *PacketDispatcher) SetRouterMgr(routermgr *RouterMgr) {
30 | p.routermgr = routermgr
31 | }
32 |
33 | func (p *PacketDispatcher) Dispatch(pkt []byte) {
34 |
35 | ver := pkt[0]
36 | ver = ver >> 4
37 | if ver != IPV4_PROTOCOL {
38 | return
39 | }
40 |
41 | ipv4pkt := header.IPv4(pkt)
42 |
43 | ip := ipv4pkt.DestinationAddress().To4()
44 | ipstr := ip.String()
45 | conn := p.connmgr.GetConnByIP(ipstr)
46 |
47 | if conn == nil {
48 | gw := p.routermgr.FindRoute(net.IP(ip))
49 | conn = p.connmgr.GetConnByIP(gw)
50 | }
51 |
52 | if conn == nil {
53 | elog.Debug("connmgr can't find wsconn for ", ipstr)
54 | return
55 | }
56 | buf := make([]byte, len(pkt)+POLE_PACKET_HEADER_LEN)
57 | copy(buf[POLE_PACKET_HEADER_LEN:], pkt)
58 | resppkt := PolePacket(buf)
59 | resppkt.SetLen(uint16(len(buf)))
60 | resppkt.SetCmd(CMD_S2C_IPDATA)
61 | conn.Send(resppkt)
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/pole_packet.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "encoding/binary"
4 |
5 | const (
6 | CMD_ALLOC_IPADDR = 0x1
7 | CMD_S2C_IPDATA = 0x2
8 | CMD_C2S_IPDATA = 0x3
9 | CMD_HEART_BEAT = 0x4
10 | CMD_CLIENT_CLOSED = 0x5
11 | CMD_KICK_OUT = 0x6
12 | CMD_USER_AUTH = 0x7
13 | )
14 |
15 | const (
16 | POLE_PACKET_HEADER_LEN = 4
17 | )
18 |
19 | type PolePacket []byte
20 |
21 | func (p PolePacket) Len() uint16 {
22 | return binary.BigEndian.Uint16(p[0:2])
23 | }
24 |
25 | func (p PolePacket) Cmd() uint16 {
26 | return binary.BigEndian.Uint16(p[2:4])
27 | }
28 |
29 | func (p PolePacket) Payload() []byte {
30 | return p[POLE_PACKET_HEADER_LEN:]
31 | }
32 |
33 | func (p PolePacket) SetLen(len uint16) {
34 | binary.BigEndian.PutUint16(p[0:], len)
35 | }
36 |
37 | func (p PolePacket) SetCmd(cmd uint16) {
38 | binary.BigEndian.PutUint16(p[2:], cmd)
39 | }
40 |
--------------------------------------------------------------------------------
/polevpn.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=polevpn
3 | After=network.target
4 |
5 | [Service]
6 | Type=simple
7 | Restart=always
8 | RestartSec=5s
9 | PIDFile=/opt/polevpn_server/polevpn.pid
10 | ExecStart=/opt/polevpn_server/polevpn_server -config=/opt/polevpn_server/config.json -logPath=/opt/polevpn_server/logs
11 | ExecStop=/bin/kill -s QUIT $MAINPID
12 | PrivateTmp=true
13 |
14 | [Install]
15 | WantedBy=multi-user.target
16 |
--------------------------------------------------------------------------------
/polevpn_server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "sync"
5 |
6 | "github.com/polevpn/anyvalue"
7 | "github.com/polevpn/elog"
8 | )
9 |
10 | type PoleVPNServer struct {
11 | }
12 |
13 | func NewPoleVPNServer() *PoleVPNServer {
14 | return &PoleVPNServer{}
15 | }
16 |
17 | func (ps *PoleVPNServer) Start(config *anyvalue.AnyValue) error {
18 | var err error
19 | bindips := make(map[string]string)
20 | bindiparr := config.Get("bind_ips").AsArray()
21 | for _, bindip := range bindiparr {
22 | bindip, ok := bindip.(map[string]interface{})
23 | if ok {
24 | bindips[bindip["user"].(string)] = bindip["ip"].(string)
25 | }
26 | }
27 |
28 | addresspool, err := NewAddressPool(config.Get("network_cidr").AsStr(), bindips)
29 |
30 | if err != nil {
31 | elog.Error("new address pool,", err)
32 | return err
33 | }
34 |
35 | routermgr := NewRouterMgr()
36 | routes := config.Get("server_routes").AsArray()
37 | for _, route := range routes {
38 | route, ok := route.(map[string]interface{})
39 | if ok {
40 | routermgr.AddRoute(route["cidr"].(string), route["gw"].(string))
41 | }
42 | }
43 |
44 | connmgr := NewConnMgr()
45 |
46 | connmgr.SetAddressPool(addresspool)
47 |
48 | packetHandler := NewPacketDispatcher()
49 |
50 | packetHandler.SetConnMgr(connmgr)
51 | packetHandler.SetRouterMgr(routermgr)
52 |
53 | tunio, err := NewTunIO(CH_TUNIO_WRITE_SIZE, packetHandler)
54 |
55 | if err != nil {
56 | elog.Error("create tun fail,", err)
57 | return err
58 | }
59 |
60 | gwip := addresspool.GatewayIP()
61 |
62 | elog.Infof("set tun device ip %v", gwip)
63 | err = tunio.SetIPAddress(gwip)
64 | if err != nil {
65 | elog.Error("set tun ip address fail,", err)
66 | return err
67 | }
68 |
69 | elog.Info("enable tun device")
70 | err = tunio.Enanble()
71 | if err != nil {
72 | elog.Error("enable tun fail,", err)
73 | return err
74 | }
75 | elog.Infof("add route %v to %v", addresspool.GetNetwork(), gwip)
76 | err = tunio.AddRoute(addresspool.GetNetwork(), gwip)
77 | if err != nil {
78 | elog.Error("set tun route fail,", err)
79 | return err
80 | }
81 |
82 | tunio.StartProcess()
83 |
84 | loginchecker := NewLocalLoginChecker()
85 | requestHandler := NewRequestHandler()
86 | requestHandler.SetTunIO(tunio)
87 | requestHandler.SetConnMgr(connmgr)
88 | requestHandler.SetRouterMgr(routermgr)
89 |
90 | upstream := config.Get("up_traffic_limit").AsUint64()
91 | downstream := config.Get("down_traffic_limit").AsUint64()
92 |
93 | wg := &sync.WaitGroup{}
94 |
95 | httpServer := NewHttpServer(upstream, downstream, requestHandler)
96 | httpServer.SetLoginCheckHandler(loginchecker)
97 | wg.Add(1)
98 | go httpServer.ListenTLS(wg,
99 | config.Get("endpoint.listen").AsStr(),
100 | config.Get("endpoint.cert_file").AsStr(),
101 | config.Get("endpoint.key_file").AsStr(),
102 | )
103 | elog.Infof("listen https at %v", config.Get("endpoint.listen").AsStr())
104 |
105 | wg.Wait()
106 |
107 | return nil
108 | }
109 |
--------------------------------------------------------------------------------
/request_handler.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net"
5 |
6 | "github.com/polevpn/anyvalue"
7 | "github.com/polevpn/elog"
8 | "github.com/polevpn/netstack/tcpip/header"
9 | )
10 |
11 | type RequestHandler struct {
12 | tunio *TunIO
13 | connmgr *ConnMgr
14 | routermgr *RouterMgr
15 | }
16 |
17 | func NewRequestHandler() *RequestHandler {
18 |
19 | return &RequestHandler{}
20 | }
21 |
22 | func (r *RequestHandler) SetTunIO(tunio *TunIO) {
23 | r.tunio = tunio
24 | }
25 |
26 | func (r *RequestHandler) SetConnMgr(connmgr *ConnMgr) {
27 | r.connmgr = connmgr
28 | }
29 |
30 | func (r *RequestHandler) SetRouterMgr(routermgr *RouterMgr) {
31 | r.routermgr = routermgr
32 | }
33 |
34 | func (r *RequestHandler) OnRequest(pkt []byte, conn Conn) {
35 |
36 | ppkt := PolePacket(pkt)
37 | switch ppkt.Cmd() {
38 | case CMD_ALLOC_IPADDR:
39 | elog.Info("received alloc ip adress request from ", conn.String())
40 | r.handleAllocIPAddress(ppkt, conn)
41 | case CMD_C2S_IPDATA:
42 | r.handleC2SIPData(ppkt, conn)
43 | case CMD_HEART_BEAT:
44 | elog.Debug("received heart beat request,", conn.String())
45 | r.handleHeartBeat(ppkt, conn)
46 | case CMD_CLIENT_CLOSED:
47 | r.handleClientClose(ppkt, conn)
48 | default:
49 | elog.Error("invalid pkt cmd=", ppkt.Cmd())
50 | }
51 | }
52 |
53 | func (r *RequestHandler) OnConnection(conn Conn, user string, ip string) {
54 | if ip != "" {
55 | oldconn := r.connmgr.GetConnByIP(ip)
56 | if oldconn != nil {
57 | oldconn.Close(true)
58 | }
59 | r.connmgr.AttachIPAddressToConn(ip, conn)
60 | elog.Infof("from %v,ip:%v reconnect ok", conn.String(), ip)
61 |
62 | }
63 | r.connmgr.AttachUserToConn(user, conn)
64 |
65 | }
66 |
67 | func (r *RequestHandler) handleAllocIPAddress(pkt PolePacket, conn Conn) {
68 | av := anyvalue.New()
69 |
70 | ip := r.connmgr.AllocAddress(conn)
71 |
72 | if ip == "" {
73 | elog.Error("ip alloc fail,no more ip address")
74 | }
75 |
76 | elog.Infof("alloc ip %v to %v", ip, conn.String())
77 | av.Set("ip", ip)
78 | av.Set("dns", Config.Get("dns").AsStr())
79 | av.Set("route", Config.Get("client_routes").AsStrArr())
80 | body, _ := av.MarshalJSON()
81 | buf := make([]byte, POLE_PACKET_HEADER_LEN+len(body))
82 | copy(buf[POLE_PACKET_HEADER_LEN:], body)
83 | resppkt := PolePacket(buf)
84 | resppkt.SetLen(uint16(len(buf)))
85 | resppkt.SetCmd(pkt.Cmd())
86 | conn.Send(resppkt)
87 | if ip != "" {
88 | r.connmgr.AttachIPAddressToConn(ip, conn)
89 | r.connmgr.AttachUserToIP(r.connmgr.GetConnAttachUser(conn), ip)
90 | }
91 | }
92 |
93 | func (r *RequestHandler) handleC2SIPData(pkt PolePacket, conn Conn) {
94 |
95 | ipv4pkg := header.IPv4(pkt.Payload())
96 | dstIp := ipv4pkg.DestinationAddress().To4()
97 | dstIpStr := dstIp.String()
98 |
99 | elog.Debug("received pkt to ", dstIpStr)
100 |
101 | toconn := r.connmgr.GetConnByIP(dstIpStr)
102 |
103 | if toconn == nil {
104 | gw := r.routermgr.FindRoute(net.IP(dstIp))
105 | toconn = r.connmgr.GetConnByIP(gw)
106 | }
107 |
108 | if toconn != nil {
109 | pkt.SetCmd(CMD_S2C_IPDATA)
110 | toconn.Send(pkt)
111 | } else {
112 | if r.tunio != nil {
113 | err := r.tunio.Enqueue(pkt[POLE_PACKET_HEADER_LEN:])
114 | if err != nil {
115 | elog.Error("tunio enqueue fail,", err)
116 | }
117 | }
118 | }
119 | }
120 |
121 | func (r *RequestHandler) handleHeartBeat(pkt PolePacket, conn Conn) {
122 | buf := make([]byte, POLE_PACKET_HEADER_LEN)
123 | resppkt := PolePacket(buf)
124 | resppkt.SetLen(POLE_PACKET_HEADER_LEN)
125 | resppkt.SetCmd(CMD_HEART_BEAT)
126 | conn.Send(resppkt)
127 | r.connmgr.UpdateConnActiveTime(conn)
128 | }
129 |
130 | func (r *RequestHandler) handleClientClose(pkt PolePacket, conn Conn) {
131 | elog.Info(conn.String(), " proactive close")
132 | r.connmgr.RelelaseAddress(r.connmgr.GeIPByConn(conn))
133 | }
134 |
135 | func (r *RequestHandler) OnClosed(conn Conn, proactive bool) {
136 |
137 | elog.Info("connection closed event from ", conn.String())
138 |
139 | r.connmgr.DetachIPAddressFromConn(conn)
140 | r.connmgr.DetachUserFromConn(conn)
141 | //just process proactive close event
142 | if proactive {
143 | elog.Info(conn.String(), " proactive close")
144 | r.connmgr.RelelaseAddress(r.connmgr.GeIPByConn(conn))
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/restart.sh:
--------------------------------------------------------------------------------
1 | ./stop.sh
2 | ./start.sh
3 |
--------------------------------------------------------------------------------
/router_mgr.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net"
5 | "sort"
6 | "sync"
7 | )
8 |
9 | type RouterMgr struct {
10 | routetable map[string]string
11 | nettable map[string]*net.IPNet
12 | mutex *sync.RWMutex
13 | sortedtable []string
14 | }
15 |
16 | func NewRouterMgr() *RouterMgr {
17 | rm := &RouterMgr{
18 | routetable: make(map[string]string),
19 | mutex: &sync.RWMutex{},
20 | sortedtable: make([]string, 0),
21 | nettable: make(map[string]*net.IPNet),
22 | }
23 | return rm
24 | }
25 |
26 | func (rm *RouterMgr) AddRoute(cidr string, gw string) bool {
27 |
28 | rm.mutex.Lock()
29 | defer rm.mutex.Unlock()
30 |
31 | _, ok := rm.routetable[cidr]
32 | if ok {
33 | return false
34 | }
35 |
36 | rm.routetable[cidr] = gw
37 |
38 | var sortedtable []string
39 | for route := range rm.routetable {
40 | sortedtable = append(sortedtable, route)
41 | _, subnet, err := net.ParseCIDR(route)
42 | if err != nil {
43 | continue
44 | }
45 | rm.nettable[route] = subnet
46 | }
47 |
48 | sort.Strings(sortedtable)
49 | rm.sortedtable = sortedtable
50 |
51 | return true
52 | }
53 |
54 | func (rm *RouterMgr) GetRoute(cidr string) string {
55 |
56 | rm.mutex.RLock()
57 | defer rm.mutex.RUnlock()
58 |
59 | return rm.routetable[cidr]
60 |
61 | }
62 |
63 | func (rm *RouterMgr) DelRoute(cidr string) {
64 | rm.mutex.Lock()
65 | defer rm.mutex.Unlock()
66 |
67 | delete(rm.routetable, cidr)
68 |
69 | var sortedtable []string
70 | for route := range rm.routetable {
71 | sortedtable = append(sortedtable, route)
72 | _, subnet, err := net.ParseCIDR(route)
73 | if err != nil {
74 | continue
75 | }
76 | rm.nettable[route] = subnet
77 | }
78 |
79 | sort.Strings(sortedtable)
80 | rm.sortedtable = sortedtable
81 |
82 | }
83 |
84 | func (rm *RouterMgr) FindRoute(destIP net.IP) string {
85 |
86 | rm.mutex.RLock()
87 | defer rm.mutex.RUnlock()
88 | slen := len(rm.sortedtable)
89 |
90 | for i := slen - 1; i > -1; i-- {
91 |
92 | route := rm.sortedtable[i]
93 | subnet := rm.nettable[route]
94 | if subnet == nil {
95 | continue
96 | }
97 | gw := rm.routetable[route]
98 | find := subnet.Contains(net.IP(destIP))
99 | if find {
100 | return gw
101 | }
102 | }
103 | return ""
104 | }
105 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | nohup ./polevpn_server $* >> run.log 2>&1 &
2 |
--------------------------------------------------------------------------------
/stop.sh:
--------------------------------------------------------------------------------
1 | kill `pgrep -f "polevpn_server"`
2 |
--------------------------------------------------------------------------------
/tools/cert.info:
--------------------------------------------------------------------------------
1 | "/C=US/ST=California/L=California/O=Apple Inc./OU=Apple Inc./CN=www.apple.com"
2 |
--------------------------------------------------------------------------------
/tools/createtls.sh:
--------------------------------------------------------------------------------
1 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ../keys/server.key -out ../keys/server.crt
2 |
--------------------------------------------------------------------------------
/tools/init.sh:
--------------------------------------------------------------------------------
1 | echo 1 > /proc/sys/net/ipv4/ip_forward
2 | sudo sysctl -w net.core.rmem_max=6500000
3 | iptables -t nat -A POSTROUTING -s 10.8.0.0/16 -j MASQUERADE
4 | ufw disable
5 |
--------------------------------------------------------------------------------
/traffic_counter.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type TrafficCounter struct {
8 | IPbytes uint64
9 | IPbytesTotal uint64
10 | IPLastTime time.Time
11 | IPCountInterval time.Duration
12 | }
13 |
14 | func NewTrafficCounter(interval time.Duration) *TrafficCounter {
15 | return &TrafficCounter{
16 | IPbytes: 0,
17 | IPLastTime: time.Now(),
18 | IPbytesTotal: 0,
19 | IPCountInterval: interval,
20 | }
21 | }
22 |
23 | func (tl *TrafficCounter) StreamCount(bytes uint64) (uint64, time.Time) {
24 |
25 | now := time.Now()
26 |
27 | if now.Sub(tl.IPLastTime) > tl.IPCountInterval {
28 | tl.IPbytes = 0
29 | tl.IPLastTime = time.Now()
30 | }
31 |
32 | tl.IPbytes += bytes
33 | tl.IPbytesTotal += bytes
34 | return tl.IPbytes, tl.IPLastTime
35 | }
36 |
37 | func (tl *TrafficCounter) StreamTotalBytes() uint64 {
38 |
39 | return tl.IPbytesTotal
40 | }
41 |
42 | func (tl *TrafficCounter) StreamCountInterval() time.Duration {
43 |
44 | return tl.IPCountInterval
45 | }
46 |
--------------------------------------------------------------------------------
/tunio.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "io"
6 | "os/exec"
7 |
8 | "github.com/polevpn/elog"
9 | "github.com/polevpn/water"
10 | )
11 |
12 | type TunIO struct {
13 | ifce *water.Interface
14 | wch chan []byte
15 | mtu int
16 | handler *PacketDispatcher
17 | closed bool
18 | }
19 |
20 | func NewTunIO(size int, handler *PacketDispatcher) (*TunIO, error) {
21 |
22 | config := water.Config{
23 | DeviceType: water.TUN,
24 | }
25 | ifce, err := water.New(config)
26 | if err != nil {
27 | return nil, err
28 | }
29 | return &TunIO{
30 | ifce: ifce,
31 | wch: make(chan []byte, size),
32 | mtu: 1500,
33 | handler: handler,
34 | closed: false,
35 | }, nil
36 | }
37 |
38 | // ip addr add dev tun0 local 10.8.0.1 peer 10.8.0.1
39 | // ip route add 10.8.0.0/24 via 10.8.0.1
40 | func (t *TunIO) SetIPAddress(ip1 string) error {
41 |
42 | out, err := exec.Command("bash", "-c", "ip addr add dev "+t.ifce.Name()+" local "+ip1+" peer "+ip1).CombinedOutput()
43 |
44 | if err != nil {
45 | return errors.New(err.Error() + "," + string(out))
46 | }
47 | return nil
48 | }
49 |
50 | func (t *TunIO) Enanble() error {
51 |
52 | out, err := exec.Command("bash", "-c", "ip link set "+t.ifce.Name()+" up").CombinedOutput()
53 |
54 | if err != nil {
55 | return errors.New(err.Error() + "," + string(out))
56 | }
57 | return nil
58 | }
59 |
60 | func (t *TunIO) AddRoute(cidr string, gw string) error {
61 | out, err := exec.Command("bash", "-c", "ip route add "+cidr+" via "+gw).CombinedOutput()
62 |
63 | if err != nil {
64 | return errors.New(err.Error() + "," + string(out))
65 | }
66 |
67 | return err
68 |
69 | }
70 |
71 | func (t *TunIO) Close() error {
72 | if t.closed {
73 | return nil
74 | }
75 |
76 | if t.wch != nil {
77 | t.wch <- nil
78 | close(t.wch)
79 | }
80 | t.closed = true
81 | return t.ifce.Close()
82 | }
83 |
84 | func (t *TunIO) read() {
85 | defer func() {
86 | t.Close()
87 | }()
88 |
89 | defer PanicHandlerExit()
90 |
91 | for {
92 |
93 | pkt := make([]byte, t.mtu)
94 | n, err := t.ifce.Read(pkt)
95 | if err != nil {
96 | elog.Error("read pkg from tun fail", err)
97 | return
98 | }
99 | pkt = pkt[:n]
100 |
101 | t.handler.Dispatch(pkt)
102 | }
103 |
104 | }
105 |
106 | func (t *TunIO) write() {
107 | defer PanicHandlerExit()
108 |
109 | for {
110 | pkt, ok := <-t.wch
111 | if !ok {
112 | elog.Error("get pkt from write channel fail,maybe channel closed")
113 | return
114 | }
115 | if pkt == nil {
116 | elog.Info("exit write process")
117 | return
118 | }
119 |
120 | _, err := t.ifce.Write(pkt)
121 | if err != nil {
122 | if err == io.EOF {
123 | elog.Info("tun may be closed")
124 | } else {
125 | elog.Error("tun write error", err)
126 | }
127 | return
128 | }
129 |
130 | }
131 | }
132 |
133 | func (t *TunIO) Enqueue(pkt []byte) error {
134 | if t.wch == nil {
135 | return errors.New("write channel is nil")
136 | }
137 | t.wch <- pkt
138 | return nil
139 | }
140 |
141 | func (t *TunIO) StartProcess() {
142 | go t.read()
143 | go t.write()
144 | }
145 |
--------------------------------------------------------------------------------
/users.credentials:
--------------------------------------------------------------------------------
1 | test,test12345
2 | test2,test12345
3 |
--------------------------------------------------------------------------------
/utils.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "crypto/aes"
6 | "crypto/cipher"
7 | "encoding/binary"
8 | "errors"
9 | "io"
10 | "os"
11 | "runtime/debug"
12 | "strconv"
13 |
14 | "github.com/polevpn/anyvalue"
15 | "github.com/polevpn/elog"
16 | )
17 |
18 | var ServerAesKey = []byte{0x75, 0xf3, 0xfe, 0x63, 0x18, 0x1f, 0x5c, 0x27, 0xab, 0x7c, 0xad, 0x4d, 0x7b, 0xf2, 0x59, 0xd0}
19 |
20 | func Ph3cS7Padding(ciphertext []byte, blockSize int) []byte {
21 | padding := blockSize - len(ciphertext)%blockSize
22 | padtext := bytes.Repeat([]byte{byte(padding)}, padding)
23 | return append(ciphertext, padtext...)
24 | }
25 |
26 | func Ph3cS7UnPadding(origData []byte) []byte {
27 | length := len(origData)
28 | unpadding := int(origData[length-1])
29 | if length-unpadding <= 0 {
30 | return origData
31 | }
32 | return origData[:(length - unpadding)]
33 | }
34 |
35 | //AES加密
36 | func AesEncrypt(origData, key []byte) ([]byte, error) {
37 | block, err := aes.NewCipher(key)
38 | if err != nil {
39 | return nil, err
40 | }
41 | blockSize := block.BlockSize()
42 | origData = Ph3cS7Padding(origData, blockSize)
43 | blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
44 | crypted := make([]byte, len(origData))
45 | blockMode.CryptBlocks(crypted, origData)
46 | return crypted, nil
47 | }
48 |
49 | //AES解密
50 | func AesDecrypt(crypted, key []byte) ([]byte, error) {
51 | block, err := aes.NewCipher(key)
52 | if err != nil {
53 | return nil, err
54 | }
55 | blockSize := block.BlockSize()
56 | blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
57 | origData := make([]byte, len(crypted))
58 | blockMode.CryptBlocks(origData, crypted)
59 | origData = Ph3cS7UnPadding(origData)
60 | return origData, nil
61 | }
62 |
63 | func ReadPacket(conn io.Reader) ([]byte, error) {
64 |
65 | prefetch := make([]byte, 2)
66 |
67 | _, err := io.ReadFull(conn, prefetch)
68 |
69 | if err != nil {
70 | return nil, err
71 | }
72 |
73 | len := binary.BigEndian.Uint16(prefetch)
74 |
75 | if len < POLE_PACKET_HEADER_LEN {
76 | return nil, errors.New("invalid pkt len=" + strconv.Itoa(int(len)))
77 | }
78 |
79 | pkt := make([]byte, len)
80 | copy(pkt, prefetch)
81 |
82 | _, err = io.ReadFull(conn, pkt[2:])
83 |
84 | if err != nil {
85 | return nil, err
86 | }
87 |
88 | return pkt, nil
89 | }
90 |
91 | func GetConfig(configfile string) (*anyvalue.AnyValue, error) {
92 |
93 | f, err := os.Open(configfile)
94 | if err != nil {
95 | return nil, err
96 | }
97 | return anyvalue.NewFromJsonReader(f)
98 | }
99 |
100 | func PanicHandler() {
101 | if err := recover(); err != nil {
102 | elog.Error("Panic Exception:", err)
103 | elog.Error(string(debug.Stack()))
104 | }
105 | }
106 |
107 | func PanicHandlerExit() {
108 | if err := recover(); err != nil {
109 | elog.Error("Panic Exception:", err)
110 | elog.Error(string(debug.Stack()))
111 | elog.Error("************Program Exit************")
112 | elog.Flush()
113 | os.Exit(0)
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/ws_conn.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "math/rand"
5 | "time"
6 |
7 | "github.com/gorilla/websocket"
8 | "github.com/polevpn/elog"
9 | "github.com/polevpn/netstack/tcpip/header"
10 | "github.com/polevpn/netstack/tcpip/transport/tcp"
11 | "github.com/polevpn/netstack/tcpip/transport/udp"
12 | )
13 |
14 | const (
15 | CH_WEBSOCKET_WRITE_SIZE = 100
16 | TRAFFIC_LIMIT_INTERVAL = 10
17 | )
18 |
19 | type WebSocketConn struct {
20 | conn *websocket.Conn
21 | wch chan []byte
22 | closed bool
23 | handler *RequestHandler
24 | downlimit uint64
25 | uplimit uint64
26 | tcDownStream *TrafficCounter
27 | tcUpStream *TrafficCounter
28 | }
29 |
30 | func NewWebSocketConn(conn *websocket.Conn, downlimit uint64, uplimit uint64, handler *RequestHandler) *WebSocketConn {
31 | return &WebSocketConn{
32 | conn: conn,
33 | closed: false,
34 | wch: make(chan []byte, CH_WEBSOCKET_WRITE_SIZE),
35 | handler: handler,
36 | downlimit: downlimit,
37 | uplimit: uplimit,
38 | tcDownStream: NewTrafficCounter(TRAFFIC_LIMIT_INTERVAL * time.Millisecond),
39 | tcUpStream: NewTrafficCounter(TRAFFIC_LIMIT_INTERVAL * time.Millisecond),
40 | }
41 | }
42 |
43 | func (wsc *WebSocketConn) Close(flag bool) error {
44 | if !wsc.closed {
45 | wsc.closed = true
46 | if wsc.wch != nil {
47 | wsc.wch <- nil
48 | close(wsc.wch)
49 | }
50 | err := wsc.conn.Close()
51 | if flag {
52 | go wsc.handler.OnClosed(wsc, false)
53 | }
54 | return err
55 | }
56 | return nil
57 | }
58 |
59 | func (wsc *WebSocketConn) String() string {
60 | return wsc.conn.RemoteAddr().String() + "->" + wsc.conn.LocalAddr().String()
61 | }
62 |
63 | func (wsc *WebSocketConn) IsClosed() bool {
64 | return wsc.closed
65 | }
66 |
67 | func (wsc *WebSocketConn) checkStreamLimit(pkt []byte, tfcounter *TrafficCounter, limit uint64) (bool, time.Duration) {
68 | bytes, ltime := tfcounter.StreamCount(uint64(len(pkt)))
69 | if bytes > limit/(1000/uint64(tfcounter.StreamCountInterval()/time.Millisecond)) {
70 | duration := ltime.Add(tfcounter.StreamCountInterval()).Sub(time.Now())
71 | if duration > 0 {
72 | drop := false
73 | if len(wsc.wch) > CH_WEBSOCKET_WRITE_SIZE*0.5 {
74 | ippkt := header.IPv4(pkt)
75 | if ippkt.Protocol() == uint8(tcp.ProtocolNumber) {
76 | n := rand.Intn(5)
77 | if n > 2 {
78 | drop = true
79 | }
80 | } else if ippkt.Protocol() == uint8(udp.ProtocolNumber) {
81 | udppkt := header.UDP(ippkt.Payload())
82 | if udppkt.DestinationPort() != 53 && udppkt.SourcePort() != 53 {
83 | drop = true
84 | }
85 | }
86 | }
87 |
88 | if drop {
89 | return true, 0
90 | } else {
91 | return true, duration
92 | }
93 | }
94 | }
95 | return false, 0
96 | }
97 |
98 | func (wsc *WebSocketConn) Read() {
99 | defer func() {
100 | wsc.Close(true)
101 | }()
102 |
103 | defer PanicHandler()
104 |
105 | for {
106 | mtype, pkt, err := wsc.conn.ReadMessage()
107 | if err != nil {
108 |
109 | elog.Error(wsc.String(), " wsconn read end,status=", err)
110 | return
111 | }
112 | if mtype == websocket.BinaryMessage {
113 |
114 | ppkt := PolePacket(pkt)
115 | if ppkt.Cmd() == CMD_C2S_IPDATA {
116 | //traffic limit
117 | limit, duration := wsc.checkStreamLimit(ppkt.Payload(), wsc.tcUpStream, wsc.uplimit)
118 | if limit {
119 | if duration > 0 {
120 | time.Sleep(duration)
121 | } else {
122 | continue
123 | }
124 | }
125 | }
126 | wsc.handler.OnRequest(pkt, wsc)
127 | } else {
128 | elog.Info("ws mtype=", mtype)
129 | }
130 | }
131 |
132 | }
133 |
134 | func (wsc *WebSocketConn) drainWriteCh() {
135 | for {
136 | select {
137 | case _, ok := <-wsc.wch:
138 | if !ok {
139 | return
140 | }
141 | default:
142 | return
143 | }
144 | }
145 | }
146 |
147 | func (wsc *WebSocketConn) Write() {
148 | defer PanicHandler()
149 | defer wsc.drainWriteCh()
150 |
151 | for {
152 |
153 | pkt, ok := <-wsc.wch
154 | if !ok {
155 | elog.Error(wsc.String(), " channel closed")
156 | return
157 | }
158 | if pkt == nil {
159 | elog.Info(wsc.String(), " exit write process")
160 | return
161 | }
162 |
163 | ppkt := PolePacket(pkt)
164 | if ppkt.Cmd() == CMD_S2C_IPDATA {
165 | //traffic limit
166 | limit, duration := wsc.checkStreamLimit(ppkt.Payload(), wsc.tcDownStream, wsc.downlimit)
167 | if limit {
168 | if duration > 0 {
169 | time.Sleep(duration)
170 | } else {
171 | continue
172 | }
173 | }
174 | }
175 | err := wsc.conn.WriteMessage(websocket.BinaryMessage, pkt)
176 | if err != nil {
177 | elog.Error(wsc.String(), " wsconn write end,status=", err)
178 | return
179 | }
180 | }
181 | }
182 |
183 | func (wsc *WebSocketConn) Send(pkt []byte) {
184 | if wsc.closed {
185 | return
186 | }
187 | if wsc.wch != nil {
188 | select {
189 | case wsc.wch <- pkt:
190 | default:
191 | elog.Error(wsc.String(), " wch is full")
192 | }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------