├── .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("\n400 Bad Request\n\n

400 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\n

403 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 | --------------------------------------------------------------------------------