├── .gitignore ├── BUG ├── LICENSE ├── Makefile ├── README.en.md ├── README.md ├── TODO ├── admin └── admin.go ├── auth ├── auth.go └── auth.sql ├── client.go ├── common ├── cache.go ├── cache_test.go ├── common.go └── servercommon.go ├── doc └── docker.md ├── dogtunnel ├── ikcp ├── ikcp.go ├── ikcp_h.go ├── ikcp_test.go └── ikcp_test_h.go ├── keys ├── demo ├── server.crt └── server.key ├── nat ├── connection.go ├── gather.go ├── nat.go └── stun │ └── stun.go ├── release.sh ├── scripts ├── doghole.sh ├── install_linux.sh └── install_ubuntu.sh ├── server.go ├── start.sh └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | dtunnel 2 | dtunnel_s 3 | /bin/ -------------------------------------------------------------------------------- /BUG: -------------------------------------------------------------------------------- 1 | 2014-01-06:某些网络环境(A,B),要求穿透方向必须是A向B发起,否则会穿透失败。所以必须实现重试机制,双方互换顺序进行穿透,由tcp server控制(fixed) 2 | 2014-01-19:对外ip有多个的网络环境,提供手工指明ip列表和stun服务辅助检测ip的机制进行穿透(fixed) 3 | 2014-01-29:对于nat更改监听端口的情况没有处理,导致穿透失败(fixed) 4 | 2014-01-31:网络不好的情况下,重试机制出现问题,导致收发包混乱,并且超时机制也处理错误,断开连接后仍在尝试发送数据(fixed) 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2015 vzex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @make release 3 | release: 4 | make mac 5 | make linux 6 | make win 7 | make pi 8 | mac: 9 | make client 10 | make server 11 | linux: 12 | make client_linux 13 | make server_linux 14 | pi: 15 | make client_pi 16 | make server_pi 17 | win: 18 | make client_win 19 | make server_win 20 | debug: 21 | make client_debug 22 | make server_debug 23 | 24 | client_debug: 25 | @go build -gcflags "-N -l" -o dtunnel_d client.go 26 | server_debug: 27 | @go build -gcflags "-N -l" -o dtunnel_s_d server.go 28 | 29 | 30 | client: 31 | @go build -ldflags "-s -w" -o bin/dtunnel client.go 32 | client_linux: 33 | @GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/linux/dtunnel client.go 34 | client_pi: 35 | @GOOS=linux GOARCH=arm go build -ldflags "-s -w" -o bin/pi/dtunnel client.go 36 | client_win: 37 | @GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o bin/windows/dtunnel.exe client.go 38 | 39 | 40 | server: 41 | @go build -ldflags "-s -w" -o bin/dtunnel_s server.go 42 | server_linux: 43 | @GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/linux/dtunnel_s server.go 44 | server_pi: 45 | @GOOS=linux GOARCH=arm go build -ldflags "-s -w" -o bin/pi/dtunnel_s server.go 46 | server_win: 47 | @GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o bin/windows/dtunnel_s.exe server.go 48 | 49 | clean: 50 | @rm -rf bin/* 51 | .PHONY: all debug release client_debug server_debug client server clean 52 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # Dog Tunnel 2 | ## Introduction 3 | 4 | kcp-based p2p port mapping tool that supports SOCKS5 proxy 5 | 6 | ## Compiling 7 | Installation dependencies 8 | 9 | go get github.com/go-sql-driver/mysql 10 | 11 | go get github.com/klauspost/reedsolomon 12 | 13 | go get github.com/cznic/zappy 14 | 15 | Compiler 16 | go get -u -d github.com/vzex/dog-tunnel && cd $GOPATH/src/github.com/vzex/dog-tunnel/ && git checkout master && make 17 | 18 | (Windows users modify the path by themselves) 19 | 20 | ## server setup 21 | 22 | The compiled program has two dtunnel_s, dtunnel 23 | 24 | dtunnel_s is the server dtunnel is the client 25 | 26 | The use of dtunnel reference official website http://dog-tunnel.tk (Note: The official website because it hangs on a vps unreliable expires to stop the renewals, so no longer provide the official p2p server, after the binary version will Posted in github 27 | 28 | When dtunnel_s starts, it will listen to a TCP port and set it with -addr. If you need -ssl (default is false), then you must specify -cert to load the ssl certificate. Then the client connection must also open the -ssl switch (default is true). 29 | -addrudp is the auxiliary udp port for p2p hole punching. It can increase the success rate of hole punching. The corresponding dtunnel parameter -buster specifies the same ip and port. 30 | 31 | dtunnel_s supports remote interface management. If necessary, specify ip:port with -admin, for example -admin 127.0.0.1:1234 32 | 33 | List of supported commands 34 | ``` 35 | http://127.0.0.1:1234/admin?cmd=servers List all reg users 36 | http://127.0.0.1:1234/admin?cmd=sessions&server=a list all links to a session 37 | http://127.0.0.1:1234/admin?admin?cmd=kicksession&server=a&session=1 Kick off the client with session number 1 (link end) 38 | http://127.0.0.1:1234/admin?cmd=kickserver&server=a Kick off reg a client 39 | http://127.0.0.1:1234/admin?cmd=broadcast&type=s&msg=test&quit=true broadcast message, type(s:reg, c:link, a: all clients), msg message content, quit Provincial parameters, non-empty broadcast after the client is kicked out) 40 | 41 | http://127.0.0.1:1234/admin?cmd=usersetting (user management related api, need to connect mysql) 42 | Configure mysql need to use auth/auth.sql table statement, create the database dogtunnel before the construction of the table 43 | Connection mysql need to add -dbhost -dbuser -dbpass parameters in the startup parameters, after adding mysql must pass -key to log in to the server 44 | The use of mysql please learn on your own 45 | There are multiple subcommands under usersetting (directly spelled above main api) 46 | &action=list&limita=0&limitb=10, pagination lists user information 47 | &action=limit&user=aaa&size=10000 Limit user aaa's c/s mode traffic to 10k (daily) 48 | &action=add&user=aaa&passwd=1111&type=admin Add user aaa, password 1111, type type (admin administrator (highest privilege), black blacklist, super advanced user, type not pass default normal user), return key user dtunnel - Key parameter 49 | &action=get&user=aaa Returns aaa's user information 50 | &action=del&user=aaa delete aaa 51 | &action=key&user=aaa Returns aaa's new key 52 | &action=set&user=aaa&type=super&serven=10&sessionn=100&pipen=10&sameip=10, which restricts the function of aaa account. The type specified by the type has a default set of configurations, and can also specify servern (the maximum number of registered names), sessionn (The upper limit of the number of sessions that each registered server can connect to), pipen (up to several p2p pipes per session), and single ip (configured to the same number of ip configurable services) 53 | 54 | ``` 55 | ## Thanks 56 | 57 | [netroby] (https://github.com/netroby) 58 | 59 | ## License 60 | 61 | [MIT License](LICENSE) 62 | 63 | ## Credits 64 | ![Welcome donate with Alipay && Welcome to Alipay for this project] (https://raw.githubusercontent.com/vzex/dog-tunnel/udpVersion/dog-tunnel.png) 65 | 66 | Author: vzex@163.com -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dog Tunnel(狗洞) 2 | [README in English](https://github.com/f06ybeast/dog-tunnel/blob/master/README.en.md) 3 | ## Introduction 4 | 5 | 基于kcp的p2p端口映射工具,同时支持socks5代理 6 | 7 | ## 编译 8 | 安装依赖 9 | 10 | go get github.com/go-sql-driver/mysql 11 | 12 | go get github.com/klauspost/reedsolomon 13 | 14 | go get github.com/cznic/zappy 15 | 16 | 编译程序 17 | go get -u -d github.com/vzex/dog-tunnel && cd $GOPATH/src/github.com/vzex/dog-tunnel/ && git checkout master && make 18 | 19 | (windows用户自行修改路径) 20 | 21 | ## 服务器搭建 22 | 23 | 编译出来的程序有两个dtunnel_s, dtunnel 24 | 25 | dtunnel_s 为服务端 dtunnel 为客户端 26 | 27 | dtunnel 的用法参考官网 http://dog-tunnel.tk(注:官网因为挂在一个不靠谱的vps上到期就停止续费了,所以不再提供官方的p2p服务器了,之后的二进制版本会发布在github中) 28 | 29 | dtunnel_s 启动时会监听一个tcp端口,通过-addr设置,如果需要-ssl(默认是false),那么要指定-cert加载ssl证书,之后客户端连接也要打开-ssl开关(默认是true的) 30 | -addrudp 是p2p打洞的辅助udp端口,能提高打洞成功率,对应dtunnel参数-buster指定同样的ip和端口 31 | 32 | dtunnel_s 支持远程接口管理,如果需要,可通过-admin 指定ip:端口,比如-admin 127.0.0.1:1234 33 | 34 | 支持的命令列表 35 | ``` 36 | http://127.0.0.1:1234/admin?cmd=servers 列出所有reg的用户 37 | http://127.0.0.1:1234/admin?cmd=sessions&server=a 列出所有link到a的会话 38 | http://127.0.0.1:1234/admin?admin?cmd=kicksession&server=a&session=1 踢掉会话号为1的客户端(link端) 39 | http://127.0.0.1:1234/admin?cmd=kickserver&server=a 踢掉reg a的客户端 40 | http://127.0.0.1:1234/admin?cmd=broadcast&type=s&msg=test&quit=true 广播消息,type(s:reg端,c:link端,a:所有客户端),msg消息内容,quit(缺省参数,非空则广播后客户端被踢掉) 41 | 42 | http://127.0.0.1:1234/admin?cmd=usersetting (用户管理相关api,需要连接mysql) 43 | 配置mysql需要用到auth/auth.sql 建表语句,建表前请先创建数据库dogtunnel 44 | 连接mysql需要在启动参数中添加 -dbhost -dbuser -dbpass 参数,加了mysql之后就必须通过-key才能登录服务器 45 | mysql的使用方法请自行学习 46 | usersetting下面有多个子命令(直接拼在上面主api后面) 47 | &action=list&limita=0&limitb=10,分页列出用户信息 48 | &action=limit&user=aaa&size=10000 限制用户aaa的c/s模式流量上线为10k(每日) 49 | &action=add&user=aaa&passwd=1111&type=admin 添加用户aaa,密码1111,类型type(admin管理员(最高权限),black黑名单,super高级用户,type不传默认普通用户),返回的key用户dtunnel 的-key参数 50 | &action=get&user=aaa 返回aaa的用户信息 51 | &action=del&user=aaa 删除aaa 52 | &action=key&user=aaa 返回aaa的新key 53 | &action=set&user=aaa&type=super&serven=10&sessionn=100&pipen=10&sameip=10,对aaa的账号做功能限制,type指定的类型有默认的几套配置,也可以通过指定servern(可以注册的名字数上限),sessionn(每个注册的服务器可以连接的会话数上限),pipen(每个会话最多支持几条p2p管道),sameip(同ip可注册服务数上限)来单独指定配置 54 | 55 | ``` 56 | ## Thanks 57 | 58 | [netroby](https://github.com/netroby) 59 | 60 | ## License 61 | 62 | [MIT License](LICENSE) 63 | 64 | ## Credits 65 | ![Welcome donate with Alipay && 欢迎使用支付宝对该项目进行捐赠](https://raw.githubusercontent.com/vzex/dog-tunnel/udpVersion/dog-tunnel.png) 66 | 67 | author: vzex@163.com 68 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | (%100)udp发包ack确认,失败自动重发,发包顺序简单校验 2 | (%100)客户端穿透失败自动换向重连 3 | (%80)服务端管理端口,广播,踢人,关服,限制人数,限制ip,限制服务名 4 | (%100)版本校验 5 | (%100)udp穿透失败后切换成c/s模式 6 | (%100)客户端会话支持打多个洞,备用洞用于内嵌shell功能 7 | (%80)账号数据存储,登录认证,登录次数,流量,时长限制(go-sql-driver) 8 | (%80)配置文件支持(账号密码读取,json file) 9 | (%100)客户端会话支持sock5 proxy 10 | (%0)nat类型检测 11 | (%0)新的分级日志(factorlog) 12 | 13 | -------------------------------------------------------------------------------- /admin/admin.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/json" 6 | "fmt" 7 | "net" 8 | "net/http" 9 | "strconv" 10 | 11 | "github.com/vzex/dog-tunnel/auth" 12 | "github.com/vzex/dog-tunnel/common" 13 | ) 14 | 15 | var g_AdminCommands map[string]cmdHandler 16 | 17 | type cmdHandler func(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) 18 | 19 | func addAdminCmd(cmd string, callback cmdHandler) { 20 | g_AdminCommands[cmd] = callback 21 | } 22 | 23 | func initAdminCmds() { 24 | g_AdminCommands = make(map[string]cmdHandler) 25 | addAdminCmd("servers", _adminGetServers) 26 | addAdminCmd("sessions", _adminGetSession) 27 | addAdminCmd("kicksession", _adminKickSession) 28 | addAdminCmd("kickserver", _adminKickServer) 29 | 30 | addAdminCmd("broadcast", _adminBroadcast) 31 | addAdminCmd("setglobal", _adminSetGlobal) 32 | addAdminCmd("getglobal", _adminGetGlobal) 33 | 34 | addAdminCmd("usersetting", _adminUserSetting) 35 | } 36 | 37 | func InitAdminPort(addr, certFile, keyFile string) error { 38 | initAdminCmds() 39 | mux := http.NewServeMux() 40 | mux.HandleFunc("/admin", adminHandler) 41 | server := &http.Server{Addr: addr, Handler: mux} 42 | listener, err := net.Listen("tcp", addr) 43 | if err != nil { 44 | return err 45 | } 46 | if certFile != "" && keyFile != "" { 47 | config := &tls.Config{} 48 | config.NextProtos = []string{"http/1.1"} 49 | var err error 50 | config.Certificates = make([]tls.Certificate, 1) 51 | config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) 52 | if err != nil { 53 | return err 54 | } 55 | tlsListener := tls.NewListener(listener, config) 56 | go server.Serve(tlsListener) 57 | } else { 58 | go server.Serve(listener) 59 | } 60 | return nil 61 | } 62 | 63 | type handlerResult struct { 64 | Code int 65 | Msg string 66 | } 67 | 68 | func adminHandler(w http.ResponseWriter, r *http.Request) { 69 | command := r.FormValue("cmd") 70 | if command != "" { 71 | handler, bHave := g_AdminCommands[command] 72 | if bHave { 73 | result, bOk := handler(w, r) 74 | if bOk { 75 | res, _ := json.Marshal(handlerResult{Code: 200, Msg: result}) 76 | w.Write([]byte(res)) 77 | } else { 78 | res, _ := json.Marshal(handlerResult{Code: 201, Msg: result}) 79 | w.Write([]byte(res)) 80 | } 81 | return 82 | } 83 | } 84 | res, _ := json.Marshal(handlerResult{Code: 202, Msg: "invalid command"}) 85 | w.Write([]byte(res)) 86 | } 87 | 88 | func _adminKickServer(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) { 89 | server := r.FormValue("server") 90 | if server == "" { 91 | result = "please spec server" 92 | bSuccess = false 93 | return 94 | } 95 | conn, bHave := common.ServerName2Conn[server] 96 | if bHave { 97 | common.Write(conn, "0", "showandquit", "admin kick you out") 98 | result = "kick server ok" 99 | } else { 100 | bSuccess = false 101 | result = "donnot have this serverName" 102 | return 103 | } 104 | bSuccess = true 105 | return 106 | } 107 | 108 | func _adminGetServers(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) { 109 | arr := make(map[string]([]string)) 110 | for _, server := range common.Conn2ClientInfo { 111 | if server.IsServer { 112 | _arr, bHave := arr[server.UserName] 113 | if bHave { 114 | arr[server.UserName] = append(_arr, server.ServerName) 115 | arr[server.UserName] = append(arr[server.UserName], server.Conn.RemoteAddr().String()) 116 | } else { 117 | arr[server.UserName] = []string{server.ServerName, server.Conn.RemoteAddr().String()} 118 | } 119 | } 120 | } 121 | _result, _ := json.Marshal(arr) 122 | result = string(_result) 123 | bSuccess = true 124 | return 125 | } 126 | 127 | func _adminKickSession(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) { 128 | server := r.FormValue("server") 129 | session := r.FormValue("session") 130 | if server == "" || session == "" { 131 | result = "please spec server and session" 132 | bSuccess = false 133 | return 134 | } 135 | conn, bHave := common.ServerName2Conn[server] 136 | if bHave { 137 | server, bHave2 := common.Conn2ClientInfo[conn] 138 | if bHave2 { 139 | session, bHave := server.Id2Session[session] 140 | if bHave { 141 | if session.ClientA != conn { 142 | common.Write(session.ClientA, "0", "showandquit", "admin kick you out") 143 | } else if session.ClientB != conn { 144 | common.Write(session.ClientB, "0", "showandquit", "admin kick you out") 145 | } 146 | result = "kick session ok" 147 | } else { 148 | result = "no need kick" 149 | } 150 | } else { 151 | bSuccess = false 152 | result = "donnot have this conn" 153 | } 154 | } else { 155 | bSuccess = false 156 | result = "donnot have this serverName" 157 | return 158 | } 159 | bSuccess = true 160 | return 161 | } 162 | 163 | func _adminGetSession(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) { 164 | server := r.FormValue("server") 165 | if server == "" { 166 | result = "please spec server" 167 | bSuccess = false 168 | return 169 | } 170 | arr := []string{} 171 | conn, bHave := common.ServerName2Conn[server] 172 | if bHave { 173 | server, bHave2 := common.Conn2ClientInfo[conn] 174 | if bHave2 { 175 | for _, session := range server.Id2Session { 176 | arr = append(arr, session.String()) 177 | } 178 | } else { 179 | bSuccess = false 180 | result = "donnot have this conn" 181 | } 182 | } else { 183 | bSuccess = false 184 | result = "donnot have this serverName" 185 | return 186 | } 187 | _result, _ := json.Marshal(arr) 188 | result = string(_result) 189 | bSuccess = true 190 | return 191 | } 192 | 193 | func _adminBroadcast(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) { 194 | msgtype := r.FormValue("type") 195 | if msgtype == "" { 196 | result = "please spec type" 197 | bSuccess = false 198 | return 199 | } 200 | msg := r.FormValue("msg") 201 | quit := r.FormValue("quit") 202 | cmd := "show" 203 | if quit != "" { 204 | cmd = "showandquit" 205 | } 206 | n := 0 207 | for conn, info := range common.Conn2ClientInfo { 208 | hit := false 209 | if msgtype == "s" && info.IsServer { 210 | hit = true 211 | } else if msgtype == "c" && !info.IsServer { 212 | hit = true 213 | } else if msgtype == "a" { 214 | hit = true 215 | } 216 | if hit { 217 | common.Write(conn, "0", cmd, msg) 218 | n++ 219 | } 220 | } 221 | result = fmt.Sprintf("%d", n) 222 | bSuccess = true 223 | return 224 | } 225 | 226 | func _adminSetGlobal(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) { 227 | key := r.FormValue("key") 228 | //value := r.FormValue("value") 229 | if key == "" { 230 | result = "please spec key" 231 | bSuccess = false 232 | return 233 | } 234 | result = "" 235 | bSuccess = true 236 | return 237 | } 238 | 239 | func _adminGetGlobal(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) { 240 | key := r.FormValue("key") 241 | if key == "" { 242 | result = "please spec key" 243 | bSuccess = false 244 | return 245 | } 246 | result = "" 247 | bSuccess = true 248 | return 249 | } 250 | 251 | func _adminUserSetting(w http.ResponseWriter, r *http.Request) (result string, bSuccess bool) { 252 | action := r.FormValue("action") 253 | key := r.FormValue("user") 254 | if key == "" && action != "list" { 255 | result = "please spec user" 256 | bSuccess = false 257 | return 258 | } 259 | switch action { 260 | case "limit": 261 | n := r.FormValue("size") 262 | size, _ := strconv.Atoi(n) 263 | 264 | user, err := auth.GetUser(key) 265 | if err != nil { 266 | result = err.Error() 267 | bSuccess = false 268 | return 269 | } else { 270 | user.LimitDataSize = size 271 | err := auth.UpdateUser(key, user) 272 | if err == nil { 273 | bSuccess = true 274 | } else { 275 | result = err.Error() 276 | bSuccess = false 277 | } 278 | return 279 | } 280 | case "add": 281 | passwd := r.FormValue("passwd") 282 | if passwd == "" { 283 | result = "please spec passwd" 284 | bSuccess = false 285 | return 286 | } 287 | _usertype := auth.UserType_Normal 288 | usertype := r.FormValue("type") 289 | maxOnlineServerNum := auth.DefaultMaxOnlineServerNum 290 | maxSessionNum := auth.DefaultMaxSessionNum 291 | maxPipeNum := auth.DefaultMaxPipeNum 292 | maxSameIPServers := auth.DefaultMaxSameIPServers 293 | switch usertype { 294 | case "black": 295 | _usertype = auth.UserType_BlackList 296 | case "super": 297 | _usertype = auth.UserType_Super 298 | maxOnlineServerNum = 10 299 | maxSessionNum = 10 300 | maxPipeNum = 10 301 | maxSameIPServers = 10 302 | case "admin": 303 | _usertype = auth.UserType_Admin 304 | } 305 | 306 | user := &auth.User{UserName: key, Passwd: common.HashPasswd(common.Md5(passwd)), UserType: _usertype, LastLoginTime: 0, LastLogoutTime: 0, MaxOnlineServerNum: maxOnlineServerNum, MaxSessionNum: maxSessionNum, MaxPipeNum: maxPipeNum, MaxSameIPServers: maxSameIPServers, TodayCSModeData: 0, LimitDataSize: 0} 307 | key, err := auth.AddUser(key, user) 308 | if err != nil { 309 | result = err.Error() 310 | bSuccess = false 311 | return 312 | } else { 313 | result = key 314 | bSuccess = true 315 | return 316 | } 317 | case "list": 318 | limita := r.FormValue("limita") 319 | limitb := r.FormValue("limitb") 320 | if limita == "" || limitb == "" { 321 | result = "please limita and limitb" 322 | bSuccess = false 323 | return 324 | } 325 | arr := auth.GetUserNameList(limita, limitb) 326 | res, _ := json.Marshal(arr) 327 | result = string(res) 328 | bSuccess = true 329 | return 330 | case "get": 331 | user, err := auth.GetUser(key) 332 | if err != nil { 333 | result = err.Error() 334 | bSuccess = false 335 | return 336 | } else { 337 | if user == nil { 338 | result = "donnot have this user" 339 | bSuccess = false 340 | return 341 | } 342 | 343 | res, _ := json.Marshal(user) 344 | result = string(res) 345 | bSuccess = true 346 | return 347 | } 348 | case "del": 349 | bHave, err := auth.DelUser(key) 350 | if err != nil { 351 | result = err.Error() 352 | bSuccess = false 353 | return 354 | } else { 355 | if !bHave { 356 | result = "donnot have this user" 357 | } 358 | bSuccess = true 359 | return 360 | } 361 | case "key": 362 | _key := auth.GenUserKey(key) 363 | if _key == "" { 364 | result = "gen user key fail" 365 | bSuccess = false 366 | return 367 | } 368 | err := auth.UpdateUserKey(key, _key) 369 | if err == nil { 370 | result = _key 371 | bSuccess = true 372 | } else { 373 | result = err.Error() 374 | bSuccess = false 375 | } 376 | return 377 | case "set": 378 | _usertype := -1 379 | maxOnlineServerNum := -1 380 | maxSessionNum := -1 381 | maxPipeNum := -1 382 | maxSameIPServers := -1 383 | pass := "" 384 | passwd := r.FormValue("passwd") 385 | if passwd != "" { 386 | pass = common.HashPasswd(common.Md5(passwd)) 387 | } 388 | usertype := r.FormValue("type") 389 | if usertype != "" { 390 | switch usertype { 391 | case "black": 392 | _usertype = auth.UserType_BlackList 393 | case "super": 394 | _usertype = auth.UserType_Super 395 | maxOnlineServerNum = 10 396 | maxSessionNum = 10 397 | maxPipeNum = 10 398 | case "normal": 399 | _usertype = auth.UserType_Normal 400 | maxOnlineServerNum = auth.DefaultMaxOnlineServerNum 401 | maxSessionNum = auth.DefaultMaxSessionNum 402 | maxPipeNum = auth.DefaultMaxPipeNum 403 | maxSameIPServers = auth.DefaultMaxSameIPServers 404 | case "admin": 405 | _usertype = auth.UserType_Admin 406 | } 407 | } 408 | servern := r.FormValue("serven") 409 | if servern != "" { 410 | maxOnlineServerNum, _ = strconv.Atoi(servern) 411 | } 412 | sessionn := r.FormValue("sessionn") 413 | if sessionn != "" { 414 | maxSessionNum, _ = strconv.Atoi(sessionn) 415 | } 416 | pipen := r.FormValue("pipen") 417 | if pipen != "" { 418 | maxPipeNum, _ = strconv.Atoi(pipen) 419 | } 420 | ipn := r.FormValue("sameip") 421 | if ipn != "" { 422 | maxSameIPServers, _ = strconv.Atoi(ipn) 423 | } 424 | user, err := auth.GetUser(key) 425 | if err != nil { 426 | result = err.Error() 427 | bSuccess = false 428 | return 429 | } else { 430 | if _usertype != -1 { 431 | user.UserType = _usertype 432 | } 433 | if maxOnlineServerNum != -1 { 434 | user.MaxOnlineServerNum = maxOnlineServerNum 435 | } 436 | if maxSessionNum != -1 { 437 | user.MaxSessionNum = maxSessionNum 438 | } 439 | if maxPipeNum != -1 { 440 | user.MaxPipeNum = maxPipeNum 441 | } 442 | if maxSameIPServers != -1 { 443 | user.MaxSameIPServers = maxSameIPServers 444 | } 445 | if pass != "" { 446 | user.Passwd = pass 447 | } 448 | err := auth.UpdateUser(key, user) 449 | if err == nil { 450 | bSuccess = true 451 | } else { 452 | result = err.Error() 453 | bSuccess = false 454 | } 455 | return 456 | } 457 | default: 458 | result = "invalid action" 459 | bSuccess = false 460 | return 461 | } 462 | result = "" 463 | bSuccess = true 464 | return 465 | } 466 | -------------------------------------------------------------------------------- /auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "fmt" 7 | "log" 8 | "reflect" 9 | "time" 10 | 11 | _ "github.com/go-sql-driver/mysql" 12 | "github.com/vzex/dog-tunnel/common" 13 | ) 14 | 15 | const ( 16 | UserType_Normal = iota 17 | UserType_BlackList 18 | UserType_Super 19 | UserType_Admin 20 | 21 | CacheTime = 30 22 | 23 | DefaultMaxOnlineServerNum = 2 24 | DefaultMaxSessionNum = 2 25 | DefaultMaxPipeNum = 2 26 | DefaultMaxSameIPServers = 2 27 | DefaultMaxCSModeData = 10000000 //bytes 28 | ) 29 | 30 | type User struct { 31 | //save to database 32 | UserName string 33 | Passwd string 34 | UserType int 35 | AuthKey string 36 | 37 | LastLoginTime int64 38 | LastLogoutTime int64 39 | MaxOnlineServerNum int 40 | MaxSessionNum int 41 | MaxPipeNum int 42 | MaxSameIPServers int 43 | TodayCSModeData int 44 | LimitDataSize int `stop:"true"` 45 | //donnot save 46 | lastProcessTime int64 47 | //for cache 48 | overTime int64 49 | cacheTime int64 50 | } 51 | 52 | func (u *User) CheckType() bool { 53 | if u.UserType == UserType_BlackList { 54 | return false 55 | } 56 | return true 57 | } 58 | 59 | func (u *User) OnLogin() { 60 | u.LastLoginTime = time.Now().Unix() 61 | u.lastProcessTime = u.LastLoginTime 62 | log.Println("OnLogin", u.lastProcessTime) 63 | } 64 | 65 | func (u *User) OnLogout() { 66 | u.LastLogoutTime = time.Now().Unix() 67 | updateUserDB(u) 68 | } 69 | 70 | func (u *User) CheckIpLimit(ip string) bool { 71 | if u.UserType == UserType_BlackList { 72 | return false 73 | } 74 | if u.UserType == UserType_Admin { 75 | return true 76 | } 77 | if common.GetOnlineServiceNumByNameAndIP(u.UserName, ip) >= u.MaxSameIPServers { 78 | return false 79 | } 80 | 81 | return true 82 | } 83 | 84 | func (u *User) CheckOnlineServiceNum() bool { 85 | if u.UserType == UserType_BlackList { 86 | return false 87 | } 88 | if u.UserType == UserType_Admin { 89 | return true 90 | } 91 | if common.GetOnlineServiceNumByName(u.UserName) >= u.MaxOnlineServerNum { 92 | return false 93 | } 94 | return true 95 | } 96 | 97 | func (u *User) CheckPipeNum(n int) bool { 98 | if u.UserType == UserType_BlackList { 99 | return false 100 | } 101 | if u.UserType == UserType_Admin { 102 | return true 103 | } 104 | if n > u.MaxPipeNum { 105 | return false 106 | } 107 | return true 108 | } 109 | 110 | func (u *User) CheckSessionNum(n int) bool { 111 | if u.UserType == UserType_BlackList { 112 | return false 113 | } 114 | if u.UserType == UserType_Admin { 115 | return true 116 | } 117 | if n >= u.MaxSessionNum { 118 | return false 119 | } 120 | return true 121 | } 122 | 123 | func (u *User) UpdateCSMode(size int) bool { 124 | if u.UserType == UserType_BlackList { 125 | return false 126 | } 127 | if u.UserType == UserType_Admin { 128 | return true 129 | } 130 | old := time.Unix(u.lastProcessTime, 0) 131 | now := time.Now() 132 | if now.Year() == old.Year() && now.YearDay() == old.YearDay() { 133 | u.TodayCSModeData += size 134 | } else { 135 | u.TodayCSModeData = size 136 | } 137 | u.lastProcessTime = now.Unix() 138 | n := DefaultMaxCSModeData 139 | if u.LimitDataSize > 0 { 140 | n = u.LimitDataSize 141 | } 142 | if u.TodayCSModeData > n { 143 | return false 144 | } 145 | return true 146 | } 147 | 148 | func (u *User) IsAlive() bool { 149 | return time.Now().Unix() < u.overTime 150 | } 151 | 152 | func (u *User) SetCacheTime(t int64) { 153 | if t >= 0 { 154 | u.cacheTime = t 155 | } else { 156 | t = u.cacheTime 157 | } 158 | u.overTime = t + time.Now().Unix() 159 | } 160 | 161 | func (u *User) DeInit() { 162 | updateUserDB(u) 163 | //log.Println("remove user from cache", u.UserName) 164 | } 165 | 166 | var g_Name2Users map[string]*User 167 | var g_Database *sql.DB 168 | var g_QueryUserStmt *sql.Stmt 169 | var g_QueryUserNameByKeyStmt *sql.Stmt 170 | var g_DelUserStmt *sql.Stmt 171 | var g_AddUserStmt *sql.Stmt 172 | var g_UpdateUserStmt *sql.Stmt 173 | 174 | func Init(user, passwd, host string) error { 175 | g_Name2Users = make(map[string]*User) 176 | var err error 177 | g_Database, err = sql.Open("mysql", user+":"+passwd+"@tcp("+host+")/dogtunnel") 178 | if err != nil { 179 | return err 180 | } 181 | g_QueryUserStmt, err = g_Database.Prepare("SELECT UserName, Passwd, UserType, AuthKey, LastLoginTime, LastLogoutTime, MaxOnlineServerNum, MaxSessionNum, MaxPipeNum, MaxSameIPServers, TodayCSModeData, LimitDataSize FROM users WHERE UserName = ?") 182 | if err != nil { 183 | return err 184 | } 185 | g_QueryUserNameByKeyStmt, err = g_Database.Prepare("SELECT UserName FROM users WHERE AuthKey = ?") 186 | if err != nil { 187 | return err 188 | } 189 | g_DelUserStmt, err = g_Database.Prepare("DELETE FROM users where UserName = ?") 190 | if err != nil { 191 | return err 192 | } 193 | g_AddUserStmt, err = g_Database.Prepare("INSERT INTO users (UserName, Passwd, UserType, AuthKey, LastLoginTime, LastLogoutTime, MaxOnlineServerNum, MaxSessionNum, MaxPipeNum, MaxSameIPServers, TodayCSModeData, LimitDataSize) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") 194 | if err != nil { 195 | return err 196 | } 197 | g_UpdateUserStmt, err = g_Database.Prepare("UPDATE users SET Passwd = ?, UserType = ?, AuthKey = ?, LastLoginTime = ? , LastLogoutTime = ?, MaxOnlineServerNum = ?, MaxSessionNum = ?, MaxPipeNum = ?, MaxSameIPServers = ?, TodayCSModeData = ?, LimitDataSize = ? WHERE UserName = ?") 198 | if err != nil { 199 | return err 200 | } 201 | go func() { 202 | c := time.Tick(time.Minute * 15) 203 | for _ = range c { 204 | g_Database.Ping() 205 | } 206 | }() 207 | return nil 208 | } 209 | 210 | func DeInit() { 211 | if g_Database != nil { 212 | g_Database.Close() 213 | g_Database = nil 214 | } 215 | } 216 | 217 | func preScan(struc interface{}) []interface{} { 218 | s := reflect.ValueOf(struc).Elem() 219 | s2 := reflect.TypeOf(struc).Elem() 220 | length := s.NumField() 221 | onerow := make([]interface{}, 0) 222 | for i := 0; i < length; i++ { 223 | onerow = append(onerow, s.Field(i).Addr().Interface()) 224 | if s2.Field(i).Tag.Get("stop") != "" { 225 | break 226 | } 227 | } 228 | return onerow 229 | } 230 | 231 | var g_UserKey2Name map[string]string 232 | 233 | func GetUserByKey(key string) (*User, error) { 234 | if g_UserKey2Name == nil { 235 | g_UserKey2Name = make(map[string]string) 236 | } 237 | var err error 238 | name, bHave := g_UserKey2Name[key] 239 | if !bHave { 240 | err = g_QueryUserNameByKeyStmt.QueryRow(key).Scan(&name) 241 | //fmt.Printf("load from db %+v\n", _user) 242 | if err == nil { 243 | g_UserKey2Name[key] = name 244 | } 245 | if err == sql.ErrNoRows { 246 | g_UserKey2Name[key] = "" 247 | err = nil 248 | } 249 | } 250 | if name != "" { 251 | return GetUser(name) 252 | } 253 | return nil, err 254 | } 255 | 256 | func GetUser(name string) (*User, error) { 257 | cache := common.GetCacheContainer("user") 258 | var user *User = nil 259 | var err error 260 | info := cache.GetCache(name) 261 | if info == nil { 262 | _user := &User{} 263 | row := preScan(_user) 264 | err = g_QueryUserStmt.QueryRow(name).Scan(row...) 265 | //fmt.Printf("load from db %+v,%d,%v\n", _user, _user.TodayCSModeData, err) 266 | if err == nil { 267 | user = _user 268 | cache.AddCache(name, _user, CacheTime) 269 | user.OnLogin() 270 | } 271 | if err == sql.ErrNoRows { 272 | err = nil 273 | } 274 | } else { 275 | user = info.(*User) 276 | user.SetCacheTime(-1) 277 | } 278 | return user, err 279 | } 280 | 281 | func DelUser(name string) (bool, error) { 282 | user, err := GetUser(name) 283 | if user != nil { 284 | if g_UserKey2Name != nil { 285 | delete(g_UserKey2Name, user.AuthKey) 286 | } 287 | } 288 | cache := common.GetCacheContainer("user") 289 | cache.DelCache(name) 290 | result, err := g_DelUserStmt.Exec(name) 291 | n, _ := result.RowsAffected() 292 | return n > 0, err 293 | } 294 | 295 | func updateUserDB(user *User) error { 296 | row := preScan(user) 297 | _row := append(row[1:], row[0]) 298 | _, _err := g_UpdateUserStmt.Exec(_row...) 299 | if _err != nil { 300 | return _err 301 | } 302 | return nil 303 | } 304 | 305 | func UpdateUser(name string, user *User) error { 306 | cache := common.GetCacheContainer("user") 307 | info := cache.GetCache(name) 308 | if info != nil { 309 | cache.UpdateCache(name, user) 310 | return nil 311 | } else { 312 | return updateUserDB(user) 313 | } 314 | } 315 | 316 | var authBaseId int = 1 317 | var staticKey string = "admin vzex" 318 | 319 | func genUserKey(name string) string { 320 | authBaseId++ 321 | return common.Md5(fmt.Sprintf("%d%.0f%s%s", authBaseId, time.Now().Unix(), name, staticKey)) 322 | } 323 | 324 | func UpdateUserKey(name, key string) error { 325 | if g_UserKey2Name == nil { 326 | g_UserKey2Name = make(map[string]string) 327 | } 328 | user, err := GetUser(name) 329 | if err != nil { 330 | return err 331 | } 332 | 333 | if user != nil { 334 | if user.AuthKey == key { 335 | return nil 336 | } 337 | g_UserKey2Name[user.AuthKey] = "" 338 | } else { 339 | return errors.New("no user") 340 | } 341 | g_UserKey2Name[key] = name 342 | user.AuthKey = key 343 | err = UpdateUser(name, user) 344 | return err 345 | } 346 | 347 | func GenUserKey(name string) string { 348 | if g_UserKey2Name == nil { 349 | g_UserKey2Name = make(map[string]string) 350 | } 351 | for i := 0; i < 10; i++ { 352 | key := genUserKey(name) 353 | _, bHave := g_UserKey2Name[key] 354 | if !bHave { 355 | return key 356 | } 357 | } 358 | return "" 359 | } 360 | 361 | func AddUser(name string, user *User) (string, error) { 362 | old, err := GetUser(name) 363 | if old != nil { 364 | return "", errors.New("already have user") 365 | } 366 | if err != nil { 367 | return "", err 368 | } 369 | key := GenUserKey(name) 370 | if key == "" { 371 | return "", errors.New("gen user key fail") 372 | } 373 | user.AuthKey = key 374 | row := preScan(user) 375 | _, _err := g_AddUserStmt.Exec(row...) 376 | if _err != nil { 377 | return "", _err 378 | } 379 | if g_UserKey2Name == nil { 380 | g_UserKey2Name = make(map[string]string) 381 | } 382 | g_UserKey2Name[user.AuthKey] = name 383 | return user.AuthKey, nil 384 | } 385 | 386 | func GetUserNameList(limita, limitb string) []string { 387 | names := []string{} 388 | rows, err := g_Database.Query("select UserName from users limit " + limita + "," + limitb) 389 | if err != nil { 390 | return names 391 | } 392 | for rows.Next() { 393 | var name string 394 | if err := rows.Scan(&name); err == nil { 395 | names = append(names, name) 396 | } 397 | } 398 | return names 399 | } 400 | 401 | /* 402 | func main() { 403 | err := Init("dog", "dog") 404 | if err != nil { 405 | panic(err) 406 | } 407 | defer DeInit() 408 | user, _err := GetUser("vzex") 409 | user, _err = GetUser("vzex") 410 | user, _err = GetUser("vzex") 411 | if _err == nil { 412 | fmt.Printf("%+v\n", *user) 413 | } else { 414 | panic(_err) 415 | } 416 | time.Sleep(time.Second * 31) 417 | user, _err = GetUser("vzex") 418 | time.Sleep(time.Second * 50) 419 | } 420 | */ 421 | -------------------------------------------------------------------------------- /auth/auth.sql: -------------------------------------------------------------------------------- 1 | use dogtunnel; 2 | 3 | DROP TABLE IF EXISTS users; 4 | CREATE TABLE users( 5 | UserName varchar(50) NOT NULL, 6 | Passwd varchar(50) NOT NULL, 7 | UserType tinyint NOT NULL DEFAULT 0, 8 | AuthKey varchar(40) NOT NULL DEFAULT "", 9 | LastLoginTime int(11) NOT NULL DEFAULT 0, 10 | LastLogoutTime int(11) NOT NULL DEFAULT 0, 11 | MaxOnlineServerNum int NOT NULL DEFAULT 2, 12 | MaxSessionNum int NOT NULL DEFAULT 2, 13 | MaxPipeNum int NOT NULL DEFAULT 2, 14 | MaxSameIPServers int NOT NULL DEFAULT 2, 15 | TodayCSModeData int NOT NULL DEFAULT 0, 16 | LimitDataSize int NOT NULL DEFAULT 0, 17 | PRIMARY KEY (UserName), 18 | UNIQUE KEY (AuthKey) 19 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 20 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/tls" 8 | "encoding/json" 9 | "flag" 10 | "fmt" 11 | "io/ioutil" 12 | "log" 13 | "math/rand" 14 | "net" 15 | "os" 16 | "os/signal" 17 | "os/user" 18 | "path" 19 | "strconv" 20 | "strings" 21 | "sync" 22 | "syscall" 23 | "time" 24 | 25 | "github.com/vzex/dog-tunnel/common" 26 | "github.com/vzex/dog-tunnel/nat" 27 | ) 28 | 29 | var accessKey = flag.String("key", "", "please get an accesskey") 30 | var clientKey = flag.String("clientkey", "", "when other client linkt to the reg client, need clientkey, or empty") 31 | 32 | var serverAddr = flag.String("remote", "127.0.0.1:8000", "connect remote server") 33 | var serverBustAddr = flag.String("buster", "127.0.0.1:8018", "MakeHole server") 34 | 35 | var addInitAddr = flag.String("addip", "127.0.0.1", "addip for bust,xx.xx.xx.xx;xx.xx.xx.xx;") 36 | var pipeNum = flag.Int("pipen", 1, "pipe num for transmission") 37 | var kcpSettings = flag.String("kcp", "", "k1:v1;k2:v2;... k in (nodelay, resend, nc, snd, rcv, mtu),two sides should use the same setting") 38 | var dataShards = flag.Int("ds", 0, "dataShards for fec, only available in p2p mode, two sides should be same") 39 | var parityShards = flag.Int("ps", 0, "parityShards for fec, only available in p2p mode, two sides should be same") 40 | 41 | var serveName = flag.String("reg", "", "reg the name for client link, must assign reg or link") 42 | 43 | var linkName = flag.String("link", "", "name for link, must assign reg or link") 44 | var localAddr = flag.String("local", "", "addr for listen or connect(value \"socks5\" means tcp socks5 proxy for reg),depends on link or reg") 45 | var bVerbose = flag.Bool("v", false, "verbose mode") 46 | var delayTime = flag.Int("delay", 2, "if bust fail, try to make some delay seconds") 47 | var clientMode = flag.Int("mode", 0, "connect mode:0 if p2p fail, use c/s mode;1 just p2p mode;2 just c/s mode") 48 | var bUseSSL = flag.Bool("ssl", true, "use ssl") 49 | var bShowVersion = flag.Bool("version", false, "show version") 50 | var bLoadSettingFromFile = flag.Bool("f", false, "load setting from file(~/.dtunnel)") 51 | var bEncrypt = flag.Bool("encrypt", false, "p2p mode encrypt") 52 | var dnsCacheNum = flag.Int("dnscache", 0, "if > 0, dns will cache xx minutes") 53 | 54 | var aesKey *cipher.Block 55 | 56 | var remoteConn net.Conn 57 | var clientType = -1 58 | 59 | type dnsInfo struct { 60 | Ip string 61 | Status string 62 | Queue []*dnsQueryReq 63 | overTime, cacheTime int64 64 | } 65 | 66 | func (u *dnsInfo) IsAlive() bool { 67 | return time.Now().Unix() < u.overTime 68 | } 69 | 70 | func (u *dnsInfo) GetCacheTime() int64 { 71 | return u.overTime 72 | } 73 | 74 | func (u *dnsInfo) SetCacheTime(t int64) { 75 | if t >= 0 { 76 | u.cacheTime = t 77 | } else { 78 | t = u.cacheTime 79 | } 80 | u.overTime = t + time.Now().Unix() 81 | } 82 | func (u *dnsInfo) DeInit() {} 83 | 84 | var g_ClientMap map[string]*Client 85 | var g_ClientMapKey map[string]*cipher.Block 86 | var g_Id2UDPSession map[string]*UDPMakeSession 87 | var markName = "" 88 | var bForceQuit = false 89 | 90 | func isCommonSessionId(id string) bool { 91 | return id == "common" 92 | } 93 | 94 | func handleResponse(conn net.Conn, clientId string, action string, content string) { 95 | //log.Println("got", clientId, action) 96 | switch action { 97 | case "aeskey": 98 | fmt.Println("init aeskey for client", clientId, content) 99 | block, _ := aes.NewCipher([]byte(content)) 100 | g_ClientMapKey[clientId] = &block 101 | case "show": 102 | fmt.Println(time.Now().Format("2006-01-02 15:04:05"), content) 103 | case "showandretry": 104 | fmt.Println(time.Now().Format("2006-01-02 15:04:05"), content) 105 | remoteConn.Close() 106 | case "showandquit": 107 | fmt.Println(time.Now().Format("2006-01-02 15:04:05"), content) 108 | remoteConn.Close() 109 | bForceQuit = true 110 | case "clientquit": 111 | client := g_ClientMap[clientId] 112 | log.Println("clientquit!!!", clientId, client) 113 | if client != nil { 114 | client.Quit() 115 | } 116 | case "remove_udpsession": 117 | log.Println("server force remove udpsession", clientId) 118 | delete(g_Id2UDPSession, clientId) 119 | case "query_addrlist_a": 120 | outip := content 121 | arr := strings.Split(clientId, "-") 122 | id := arr[0] 123 | sessionId := arr[1] 124 | pipeType := arr[2] 125 | g_Id2UDPSession[id] = &UDPMakeSession{id: id, sessionId: sessionId, pipeType: pipeType} 126 | go g_Id2UDPSession[id].reportAddrList(true, outip) 127 | case "query_addrlist_b": 128 | arr := strings.Split(clientId, "-") 129 | id := arr[0] 130 | sessionId := arr[1] 131 | pipeType := arr[2] 132 | g_Id2UDPSession[id] = &UDPMakeSession{id: id, sessionId: sessionId, pipeType: pipeType} 133 | go g_Id2UDPSession[id].reportAddrList(false, content) 134 | case "tell_bust_a": 135 | session, bHave := g_Id2UDPSession[clientId] 136 | if bHave { 137 | go session.beginMakeHole(content) 138 | } 139 | case "tell_bust_b": 140 | session, bHave := g_Id2UDPSession[clientId] 141 | if bHave { 142 | go session.beginMakeHole("") 143 | } 144 | case "csmode_c_tunnel_close": 145 | log.Println("receive close msg from server") 146 | arr := strings.Split(clientId, "-") 147 | clientId = arr[0] 148 | sessionId := arr[1] 149 | client, bHave := g_ClientMap[clientId] 150 | if bHave { 151 | client.removeSession(sessionId) 152 | } 153 | case "csmode_s_tunnel_close": 154 | arr := strings.Split(clientId, "-") 155 | clientId = arr[0] 156 | sessionId := arr[1] 157 | client, bHave := g_ClientMap[clientId] 158 | if bHave { 159 | client.removeSession(sessionId) 160 | } 161 | case "csmode_s_tunnel_open": 162 | oriId := clientId 163 | arr := strings.Split(oriId, "-") 164 | clientId = arr[0] 165 | sessionId := arr[1] 166 | client, bHave := g_ClientMap[clientId] 167 | if !bHave { 168 | client = &Client{id: clientId, pipes: make(map[int]net.Conn), engine: nil, buster: true, sessions: make(map[string]*clientSession), ready: true, bUdp: false} 169 | client.pipes[0] = remoteConn 170 | g_ClientMap[clientId] = client 171 | } else { 172 | client.pipes[0] = remoteConn 173 | client.ready = true 174 | client.bUdp = false 175 | } 176 | //log.Println("client init csmode", clientId, sessionId) 177 | if *localAddr != "socks5" { 178 | s_conn, err := net.DialTimeout("tcp", *localAddr, 10*time.Second) 179 | if err != nil { 180 | log.Println("connect to local server fail:", err.Error()) 181 | msg := "cannot connect to bind addr" + *localAddr 182 | common.Write(remoteConn, clientId, "tunnel_error", msg) 183 | //remoteConn.Close() 184 | return 185 | } else { 186 | client.sessionLock.Lock() 187 | client.sessions[sessionId] = &clientSession{pipe: remoteConn, localConn: s_conn} 188 | client.sessionLock.Unlock() 189 | go handleLocalPortResponse(client, oriId) 190 | } 191 | } else { 192 | client.sessionLock.Lock() 193 | client.sessions[sessionId] = &clientSession{pipe: remoteConn, localConn: nil, status: "init", recvMsg: ""} 194 | client.sessionLock.Unlock() 195 | } 196 | case "csmode_c_begin": 197 | client, bHave := g_ClientMap[clientId] 198 | if !bHave { 199 | client = &Client{id: clientId, pipes: make(map[int]net.Conn), engine: nil, buster: false, sessions: make(map[string]*clientSession), ready: true, bUdp: false} 200 | client.pipes[0] = remoteConn 201 | g_ClientMap[clientId] = client 202 | } else { 203 | client.pipes[0] = remoteConn 204 | client.ready = true 205 | client.bUdp = false 206 | } 207 | if client.MultiListen() { 208 | common.Write(remoteConn, clientId, "makeholeok", "csmode") 209 | } 210 | case "csmode_msg_c": 211 | oriId := clientId 212 | arr := strings.Split(clientId, "-") 213 | clientId = arr[0] 214 | sessionId := arr[1] 215 | client, bHave := g_ClientMap[clientId] 216 | if bHave { 217 | session := client.getSession(sessionId) 218 | if session != nil && session.localConn != nil { 219 | session.localConn.Write([]byte(content)) 220 | } else if session != nil && *localAddr == "socks5" { 221 | session.processSockProxy(client, oriId, content, func() { 222 | if len(session.recvMsg) > 0 && session.localConn != nil { 223 | session.localConn.Write([]byte(session.recvMsg)) 224 | } 225 | }) 226 | } 227 | } 228 | case "csmode_msg_s": 229 | arr := strings.Split(clientId, "-") 230 | clientId = arr[0] 231 | sessionId := arr[1] 232 | client, bHave := g_ClientMap[clientId] 233 | if bHave { 234 | session := client.getSession(sessionId) 235 | if session != nil && session.localConn != nil { 236 | session.localConn.Write([]byte(content)) 237 | } else { 238 | //log.Println("cs:cannot tunnel msg", sessionId) 239 | } 240 | } 241 | } 242 | } 243 | 244 | type UDPMakeSession struct { 245 | id string 246 | sessionId string 247 | buster bool 248 | engine *nat.AttemptEngine 249 | delay int 250 | pipeType string 251 | } 252 | 253 | func (session *UDPMakeSession) beginMakeHole(content string) { 254 | engine := session.engine 255 | if engine == nil { 256 | return 257 | } 258 | addrList := content 259 | if session.buster { 260 | engine.SetOtherAddrList(addrList) 261 | } 262 | log.Println("begin bust", session.id, session.sessionId, session.buster) 263 | if clientType == 1 && !session.buster { 264 | log.Println("retry bust!") 265 | } 266 | report := func() { 267 | if session.buster { 268 | if session.delay > 0 { 269 | log.Println("try to delay", session.delay, "seconds") 270 | time.Sleep(time.Duration(session.delay) * time.Second) 271 | } 272 | go common.Write(remoteConn, session.id, "success_bust_a", "") 273 | } 274 | } 275 | oldSession := session 276 | var aesBlock *cipher.Block 277 | if clientType == 1 { 278 | aesBlock = aesKey 279 | } else { 280 | aesBlock, _ = g_ClientMapKey[session.sessionId] 281 | } 282 | var conn net.Conn 283 | var err error 284 | if aesBlock == nil { 285 | conn, err = engine.GetConn(report, nil, nil) 286 | } else { 287 | conn, err = engine.GetConn(report, func(s []byte) []byte { 288 | if aesBlock == nil { 289 | return s 290 | } else { 291 | padLen := aes.BlockSize - (len(s) % aes.BlockSize) 292 | for i := 0; i < padLen; i++ { 293 | s = append(s, byte(padLen)) 294 | } 295 | srcLen := len(s) 296 | encryptText := make([]byte, srcLen+aes.BlockSize) 297 | iv := encryptText[srcLen:] 298 | for i := 0; i < len(iv); i++ { 299 | iv[i] = byte(i) 300 | } 301 | mode := cipher.NewCBCEncrypter(*aesBlock, iv) 302 | mode.CryptBlocks(encryptText[:srcLen], s) 303 | return encryptText 304 | } 305 | }, func(s []byte) []byte { 306 | if aesBlock == nil { 307 | return s 308 | } else { 309 | if len(s) < aes.BlockSize*2 || len(s)%aes.BlockSize != 0 { 310 | return []byte{} 311 | } 312 | srcLen := len(s) - aes.BlockSize 313 | decryptText := make([]byte, srcLen) 314 | iv := s[srcLen:] 315 | mode := cipher.NewCBCDecrypter(*aesBlock, iv) 316 | mode.CryptBlocks(decryptText, s[:srcLen]) 317 | paddingLen := int(decryptText[srcLen-1]) 318 | if paddingLen > 16 { 319 | return []byte{} 320 | } 321 | return decryptText[:srcLen-paddingLen] 322 | } 323 | }) 324 | } 325 | session, _bHave := g_Id2UDPSession[session.id] 326 | if session != oldSession { 327 | return 328 | } 329 | if !_bHave { 330 | return 331 | } 332 | delete(g_Id2UDPSession, session.id) 333 | if err == nil { 334 | if !session.buster { 335 | common.Write(remoteConn, session.id, "makeholeok", "") 336 | } 337 | client, bHave := g_ClientMap[session.sessionId] 338 | if !bHave { 339 | client = &Client{id: session.sessionId, engine: session.engine, buster: session.buster, ready: true, bUdp: true, sessions: make(map[string]*clientSession), specPipes: make(map[string]net.Conn), pipes: make(map[int]net.Conn)} 340 | g_ClientMap[session.sessionId] = client 341 | } 342 | if isCommonSessionId(session.pipeType) { 343 | size := len(client.pipes) 344 | client.pipes[size] = conn 345 | go client.Run(size, "") 346 | log.Println("add common session", session.buster, session.sessionId, session.id) 347 | if clientType == 1 { 348 | if len(client.pipes) == *pipeNum { 349 | client.MultiListen() 350 | } 351 | } 352 | } else { 353 | client.specPipes[session.pipeType] = conn 354 | go client.Run(-1, session.pipeType) 355 | log.Println("add session for", session.pipeType) 356 | } 357 | } else { 358 | delete(g_ClientMap, session.sessionId) 359 | delete(g_ClientMapKey, session.sessionId) 360 | log.Println("cannot connect", err.Error()) 361 | if !session.buster && err.Error() != "quit" { 362 | common.Write(remoteConn, session.id, "makeholefail", "") 363 | } 364 | } 365 | } 366 | 367 | func getKcpSetting() *nat.KcpSetting { 368 | setting := nat.DefaultKcpSetting() 369 | //bSetResend := false 370 | if *kcpSettings != "" { 371 | arr := strings.Split(*kcpSettings, ";") 372 | for _, v := range arr { 373 | _arr := strings.Split(v, ":") 374 | if len(_arr) == 2 { 375 | k := _arr[0] 376 | var val int32 377 | var _val int 378 | _val, _ = strconv.Atoi(_arr[1]) 379 | val = int32(_val) 380 | 381 | switch k { 382 | case "nodelay": 383 | setting.Nodelay = val 384 | case "resend": 385 | setting.Resend = val 386 | //bSetResend = true 387 | case "nc": 388 | setting.Nc = val 389 | case "snd": 390 | setting.Sndwnd = val 391 | case "rcv": 392 | setting.Rcvwnd = val 393 | case "mtu": 394 | setting.Mtu = val 395 | } 396 | } 397 | } 398 | } 399 | //setting.Xor = *xorData 400 | /* 401 | if *dataShards > 0 && *parityShards > 0 { 402 | if !bSetResend { 403 | setting.Resend = 0 404 | println("resend default to 0 in fec mode") 405 | } 406 | }*/ 407 | return setting 408 | } 409 | 410 | func (session *UDPMakeSession) reportAddrList(buster bool, outip string) { 411 | id := session.id 412 | var otherAddrList string 413 | if !buster { 414 | arr := strings.SplitN(outip, ":", 2) 415 | outip, otherAddrList = arr[0], arr[1] 416 | } else { 417 | arr := strings.SplitN(outip, ":", 2) 418 | var delayTime string 419 | outip, delayTime = arr[0], arr[1] 420 | session.delay, _ = strconv.Atoi(delayTime) 421 | if session.delay < 0 { 422 | session.delay = 0 423 | } 424 | } 425 | outip += ";" + *addInitAddr 426 | _id, _ := strconv.Atoi(id) 427 | engine, err := nat.Init(outip, buster, _id, *serverBustAddr) 428 | engine.Kcp = getKcpSetting() 429 | engine.D = *dataShards 430 | engine.P = *parityShards 431 | if err != nil { 432 | println("init error", err.Error()) 433 | disconnect() 434 | return 435 | } 436 | session.engine = engine 437 | session.buster = buster 438 | if !buster { 439 | engine.SetOtherAddrList(otherAddrList) 440 | } 441 | addrList := engine.GetAddrList() 442 | println("addrList", addrList) 443 | common.Write(remoteConn, id, "report_addrlist", addrList) 444 | } 445 | 446 | type fileSetting struct { 447 | Key string 448 | } 449 | 450 | func saveSettings(info fileSetting) error { 451 | clientInfoStr, err := json.Marshal(info) 452 | if err != nil { 453 | return err 454 | } 455 | user, err := user.Current() 456 | if err != nil { 457 | return err 458 | } 459 | filePath := path.Join(user.HomeDir, ".dtunnel") 460 | 461 | return ioutil.WriteFile(filePath, clientInfoStr, 0700) 462 | } 463 | 464 | func loadSettings(info *fileSetting) error { 465 | user, err := user.Current() 466 | if err != nil { 467 | return err 468 | } 469 | filePath := path.Join(user.HomeDir, ".dtunnel") 470 | content, err := ioutil.ReadFile(filePath) 471 | if err != nil { 472 | return err 473 | } 474 | err = json.Unmarshal([]byte(content), info) 475 | if err != nil { 476 | return err 477 | } 478 | return nil 479 | } 480 | 481 | func main() { 482 | flag.Parse() 483 | checkDns = make(chan *dnsQueryReq) 484 | checkDnsRes = make(chan *dnsQueryBack) 485 | go dnsLoop() 486 | if *bShowVersion { 487 | fmt.Printf("%.2f\n", common.Version) 488 | return 489 | } 490 | if !*bVerbose { 491 | log.SetOutput(ioutil.Discard) 492 | } 493 | if *dataShards < 0 || *dataShards >= 128 { 494 | println("-ds should in [0-127]") 495 | return 496 | } 497 | if *parityShards < 0 || *parityShards >= 128 { 498 | println("-ds should in [0-127]") 499 | return 500 | } 501 | if *serveName == "" && *linkName == "" { 502 | println("you must assign reg or link") 503 | return 504 | } 505 | if *serveName != "" && *linkName != "" { 506 | println("you must assign reg or link, not both of them") 507 | return 508 | } 509 | if *localAddr == "" { 510 | println("you must assign the local addr") 511 | return 512 | } 513 | if *serveName != "" { 514 | clientType = 0 515 | } else { 516 | clientType = 1 517 | } 518 | if *bEncrypt { 519 | if clientType != 1 { 520 | println("only link size need encrypt") 521 | return 522 | } 523 | } 524 | go func() { 525 | c := make(chan os.Signal, 1) 526 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 527 | n := 0 528 | for { 529 | <-c 530 | log.Println("received signal,shutdown") 531 | bForceQuit = true 532 | if remoteConn != nil { 533 | remoteConn.Close() 534 | } 535 | n++ 536 | if n > 5 { 537 | log.Println("force shutdown") 538 | os.Exit(-1) 539 | } 540 | } 541 | }() 542 | 543 | loop := func() bool { 544 | if bForceQuit { 545 | return true 546 | } 547 | g_ClientMap = make(map[string]*Client) 548 | g_ClientMapKey = make(map[string]*cipher.Block) 549 | g_Id2UDPSession = make(map[string]*UDPMakeSession) 550 | //var err error 551 | if *bUseSSL { 552 | _remoteConn, err := tls.Dial("tcp", *serverAddr, &tls.Config{InsecureSkipVerify: true}) 553 | if err != nil { 554 | println("connect remote err:" + err.Error()) 555 | return false 556 | } 557 | remoteConn = net.Conn(_remoteConn) 558 | } else { 559 | _remoteConn, err := net.DialTimeout("tcp", *serverAddr, 10*time.Second) 560 | if err != nil { 561 | println("connect remote err:" + err.Error()) 562 | return false 563 | } 564 | remoteConn = _remoteConn 565 | } 566 | println("connect to server succeed") 567 | go connect() 568 | q := make(chan bool) 569 | go func() { 570 | c := time.NewTicker(time.Second * 10) 571 | out: 572 | for { 573 | select { 574 | case <-c.C: 575 | if remoteConn != nil { 576 | common.Write(remoteConn, "-1", "ping", "") 577 | } 578 | case <-q: 579 | break out 580 | } 581 | } 582 | c.Stop() 583 | }() 584 | 585 | common.Read(remoteConn, handleResponse) 586 | q <- true 587 | for clientId, client := range g_ClientMap { 588 | log.Println("client shutdown", clientId) 589 | client.Quit() 590 | } 591 | 592 | for _, session := range g_Id2UDPSession { 593 | if session.engine != nil { 594 | session.engine.Fail() 595 | } 596 | } 597 | if remoteConn != nil { 598 | remoteConn.Close() 599 | } 600 | if bForceQuit { 601 | return true 602 | } 603 | return false 604 | } 605 | if clientType == 0 { 606 | for { 607 | if loop() { 608 | break 609 | } 610 | time.Sleep(10 * time.Second) 611 | } 612 | } else { 613 | loop() 614 | } 615 | log.Println("service shutdown") 616 | } 617 | 618 | func connect() { 619 | if *pipeNum <= 0 { 620 | *pipeNum = 1 621 | } 622 | clientInfo := common.ClientSetting{Version: common.Version, Delay: *delayTime, Mode: *clientMode, PipeNum: *pipeNum, AccessKey: *accessKey, ClientKey: *clientKey, AesKey: ""} 623 | if *bEncrypt { 624 | clientInfo.AesKey = string([]byte(fmt.Sprintf("asd4%d%d", int32(time.Now().Unix()), (rand.Intn(100000) + 100)))[:16]) 625 | log.Println("debug aeskey", clientInfo.AesKey) 626 | key, _ := aes.NewCipher([]byte(clientInfo.AesKey)) 627 | aesKey = &key 628 | } 629 | if *bLoadSettingFromFile { 630 | var setting fileSetting 631 | err := loadSettings(&setting) 632 | if err == nil { 633 | clientInfo.AccessKey = setting.Key 634 | } else { 635 | log.Println("load setting fail", err.Error()) 636 | } 637 | } else { 638 | if clientInfo.AccessKey != "" { 639 | var setting = fileSetting{Key: clientInfo.AccessKey} 640 | err := saveSettings(setting) 641 | if err != nil { 642 | log.Println("save setting error", err.Error()) 643 | } else { 644 | println("save setting ok, nexttime please use -f to replace -key") 645 | } 646 | } 647 | } 648 | if clientType == 0 { 649 | markName = *serveName 650 | clientInfo.ClientType = "reg" 651 | } else if clientType == 1 { 652 | markName = *linkName 653 | clientInfo.ClientType = "link" 654 | } else { 655 | println("no clienttype!") 656 | } 657 | clientInfo.Name = markName 658 | clientInfoStr, err := json.Marshal(clientInfo) 659 | if err != nil { 660 | println("encode args error") 661 | } 662 | log.Println("init client", string(clientInfoStr)) 663 | common.Write(remoteConn, "0", "init", string(clientInfoStr)) 664 | } 665 | 666 | func disconnect() { 667 | if remoteConn != nil { 668 | remoteConn.Close() 669 | remoteConn = nil 670 | } 671 | } 672 | 673 | type clientSession struct { 674 | pipe net.Conn 675 | localConn net.Conn 676 | status string 677 | recvMsg string 678 | extra uint8 679 | } 680 | 681 | func (session *clientSession) processSockProxy(sc *Client, sessionId, content string, callback func()) { 682 | pipe := session.pipe 683 | session.recvMsg += content 684 | bytes := []byte(session.recvMsg) 685 | size := len(bytes) 686 | //log.Println("recv msg-====", len(session.recvMsg), session.recvMsg, session.status, sessionId) 687 | switch session.status { 688 | case "init": 689 | if session.localConn != nil { 690 | session.localConn.Close() 691 | session.localConn = nil 692 | } 693 | if size < 2 { 694 | //println("wait init") 695 | return 696 | } 697 | var _, nmethod uint8 = bytes[0], bytes[1] 698 | //println("version", version, nmethod) 699 | session.status = "version" 700 | session.recvMsg = string(bytes[2:]) 701 | session.extra = nmethod 702 | case "version": 703 | if uint8(size) < session.extra { 704 | //println("wait version") 705 | return 706 | } 707 | var send = []uint8{5, 0} 708 | go common.Write(pipe, sessionId, "tunnel_msg_s", string(send)) 709 | session.status = "hello" 710 | session.recvMsg = string(bytes[session.extra:]) 711 | session.extra = 0 712 | //log.Println("now", len(session.recvMsg)) 713 | case "hello": 714 | var hello reqMsg 715 | bOk, tail := hello.read(bytes) 716 | if bOk { 717 | go func() { 718 | var ansmsg ansMsg 719 | url := hello.url 720 | var s_conn net.Conn 721 | var err error 722 | if *dnsCacheNum > 0 && hello.atyp == 3 { 723 | host := string(hello.dst_addr[1 : 1+hello.dst_addr[0]]) 724 | resChan := make(chan *dnsQueryRes) 725 | checkDns <- &dnsQueryReq{c: resChan, host: host, port: int(hello.dst_port2), reqtype: hello.reqtype, url: url} 726 | res := <-resChan 727 | s_conn = res.conn 728 | err = res.err 729 | if res.ip != "" { 730 | url = net.JoinHostPort(res.ip, fmt.Sprintf("%d", hello.dst_port2)) 731 | } 732 | } 733 | if s_conn == nil && err == nil { 734 | s_conn, err = net.DialTimeout(hello.reqtype, url, 30*time.Second) 735 | } 736 | if err != nil { 737 | log.Println("connect to local server fail:", err.Error()) 738 | ansmsg.gen(&hello, 4) 739 | go common.Write(pipe, sessionId, "tunnel_msg_s", string(ansmsg.buf[:ansmsg.mlen])) 740 | return 741 | } else { 742 | session.localConn = s_conn 743 | go handleLocalPortResponse(sc, sessionId) 744 | ansmsg.gen(&hello, 0) 745 | go common.Write(pipe, sessionId, "tunnel_msg_s", string(ansmsg.buf[:ansmsg.mlen])) 746 | session.status = "ok" 747 | session.recvMsg = string(tail) 748 | callback() 749 | return 750 | } 751 | }() 752 | } else { 753 | //log.Println("wait hello") 754 | } 755 | return 756 | case "ok": 757 | return 758 | } 759 | session.processSockProxy(sc, sessionId, "", callback) 760 | } 761 | 762 | var checkDns chan *dnsQueryReq 763 | var checkDnsRes chan *dnsQueryBack 764 | 765 | type dnsQueryReq struct { 766 | c chan *dnsQueryRes 767 | host string 768 | port int 769 | reqtype string 770 | url string 771 | } 772 | 773 | type dnsQueryBack struct { 774 | host string 775 | status string 776 | conn net.Conn 777 | err error 778 | } 779 | 780 | type dnsQueryRes struct { 781 | conn net.Conn 782 | err error 783 | ip string 784 | } 785 | 786 | func dnsLoop() { 787 | for { 788 | select { 789 | case info := <-checkDns: 790 | cache := common.GetCacheContainer("dns") 791 | cacheInfo := cache.GetCache(info.host) 792 | if cacheInfo == nil { 793 | cache.AddCache(info.host, &dnsInfo{Queue: []*dnsQueryReq{info}, Status: "querying"}, int64(*dnsCacheNum*60)) 794 | go func() { 795 | back := &dnsQueryBack{host: info.host} 796 | //log.Println("try dial", info.url) 797 | s_conn, err := net.DialTimeout(info.reqtype, info.url, 30*time.Second) 798 | //log.Println("try dial", info.url, "ok") 799 | if err != nil { 800 | back.status = "queryfail" 801 | back.err = err 802 | } else { 803 | back.status = "queryok" 804 | back.conn = s_conn 805 | } 806 | checkDnsRes <- back 807 | }() 808 | } else { 809 | _cacheInfo := cacheInfo.(*dnsInfo) 810 | //log.Println("on trigger", info.host, _cacheInfo.GetCacheTime(), len(_cacheInfo.Queue)) 811 | switch _cacheInfo.Status { 812 | case "querying": 813 | _cacheInfo.Queue = append(_cacheInfo.Queue, info) 814 | //cache.UpdateCache(info.host, _cacheInfo) 815 | cacheInfo.SetCacheTime(-1) 816 | case "queryok": 817 | cacheInfo.SetCacheTime(-1) 818 | go func() { 819 | info.c <- &dnsQueryRes{ip: _cacheInfo.Ip} 820 | }() 821 | } 822 | //url = cacheInfo.(*dnsInfo).Ip + fmt.Sprintf(":%d", info.port) 823 | } 824 | case info := <-checkDnsRes: 825 | cache := common.GetCacheContainer("dns") 826 | cacheInfo := cache.GetCache(info.host) 827 | if cacheInfo != nil { 828 | _cacheInfo := cacheInfo.(*dnsInfo) 829 | _cacheInfo.Status = info.status 830 | switch info.status { 831 | case "queryfail": 832 | for _, _info := range _cacheInfo.Queue { 833 | c := _info.c 834 | go func() { 835 | c <- &dnsQueryRes{err: info.err} 836 | }() 837 | } 838 | cache.DelCache(info.host) 839 | case "queryok": 840 | log.Println("add host", info.host, "to dns cache") 841 | _cacheInfo.Ip, _, _ = net.SplitHostPort(info.conn.RemoteAddr().String()) 842 | _cacheInfo.SetCacheTime(-1) 843 | //log.Println("process the queue of host", info.host, len(_cacheInfo.Queue)) 844 | conn := info.conn 845 | for _, _info := range _cacheInfo.Queue { 846 | c := _info.c 847 | go func() { 848 | c <- &dnsQueryRes{ip: _cacheInfo.Ip, conn: conn} 849 | }() 850 | conn = nil 851 | } 852 | _cacheInfo.Queue = []*dnsQueryReq{} 853 | } 854 | //cache.UpdateCache(info.host, _cacheInfo) 855 | } 856 | } 857 | } 858 | } 859 | 860 | type ansMsg struct { 861 | ver uint8 862 | rep uint8 863 | rsv uint8 864 | atyp uint8 865 | buf [300]uint8 866 | mlen uint16 867 | } 868 | 869 | func (msg *ansMsg) gen(req *reqMsg, rep uint8) { 870 | msg.ver = 5 871 | msg.rep = rep //rfc1928 872 | msg.rsv = 0 873 | msg.atyp = 1 874 | 875 | msg.buf[0], msg.buf[1], msg.buf[2], msg.buf[3] = msg.ver, msg.rep, msg.rsv, msg.atyp 876 | for i := 5; i < 11; i++ { 877 | msg.buf[i] = 0 878 | } 879 | msg.mlen = 10 880 | } 881 | 882 | type reqMsg struct { 883 | ver uint8 // socks v5: 0x05 884 | cmd uint8 // CONNECT: 0x01, BIND:0x02, UDP ASSOCIATE: 0x03 885 | rsv uint8 //RESERVED 886 | atyp uint8 //IP V4 addr: 0x01, DOMANNAME: 0x03, IP V6 addr: 0x04 887 | dst_addr [255]byte // 888 | dst_port [2]uint8 // 889 | dst_port2 uint16 // 890 | 891 | reqtype string 892 | url string 893 | } 894 | 895 | func (msg *reqMsg) read(bytes []byte) (bool, []byte) { 896 | size := len(bytes) 897 | if size < 4 { 898 | return false, bytes 899 | } 900 | buf := bytes[0:4] 901 | 902 | msg.ver, msg.cmd, msg.rsv, msg.atyp = buf[0], buf[1], buf[2], buf[3] 903 | //println("test", msg.ver, msg.cmd, msg.rsv, msg.atyp) 904 | 905 | if 5 != msg.ver || 0 != msg.rsv { 906 | log.Println("Request Message VER or RSV error!") 907 | return false, bytes[4:] 908 | } 909 | buf = bytes[4:] 910 | size = len(buf) 911 | switch msg.atyp { 912 | case 1: //ip v4 913 | if size < 4 { 914 | return false, buf 915 | } 916 | copy(msg.dst_addr[:4], buf[:4]) 917 | buf = buf[4:] 918 | size = len(buf) 919 | case 4: 920 | if size < 16 { 921 | return false, buf 922 | } 923 | copy(msg.dst_addr[:16], buf[:16]) 924 | buf = buf[16:] 925 | size = len(buf) 926 | case 3: 927 | if size < 1 { 928 | return false, buf 929 | } 930 | copy(msg.dst_addr[:1], buf[:1]) 931 | buf = buf[1:] 932 | size = len(buf) 933 | if size < int(msg.dst_addr[0]) { 934 | return false, buf 935 | } 936 | copy(msg.dst_addr[1:], buf[:int(msg.dst_addr[0])]) 937 | buf = buf[int(msg.dst_addr[0]):] 938 | size = len(buf) 939 | } 940 | if size < 2 { 941 | return false, buf 942 | } 943 | copy(msg.dst_port[:], buf[:2]) 944 | msg.dst_port2 = (uint16(msg.dst_port[0]) << 8) + uint16(msg.dst_port[1]) 945 | 946 | switch msg.cmd { 947 | case 1: 948 | msg.reqtype = "tcp" 949 | case 2: 950 | log.Println("BIND") 951 | case 3: 952 | msg.reqtype = "udp" 953 | } 954 | switch msg.atyp { 955 | case 1: // ipv4 956 | msg.url = fmt.Sprintf("%d.%d.%d.%d:%d", msg.dst_addr[0], msg.dst_addr[1], msg.dst_addr[2], msg.dst_addr[3], msg.dst_port2) 957 | case 3: //DOMANNAME 958 | msg.url = net.JoinHostPort(string(msg.dst_addr[1:1+msg.dst_addr[0]]), fmt.Sprintf("%d", msg.dst_port2)) 959 | case 4: //ipv6 960 | msg.url = fmt.Sprintf("[%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x]:%d", msg.dst_addr[0], msg.dst_addr[1], msg.dst_addr[2], msg.dst_addr[3], 961 | msg.dst_addr[4], msg.dst_addr[5], msg.dst_addr[6], msg.dst_addr[7], 962 | msg.dst_addr[8], msg.dst_addr[9], msg.dst_addr[10], msg.dst_addr[11], 963 | msg.dst_addr[12], msg.dst_addr[13], msg.dst_addr[14], msg.dst_addr[15], 964 | msg.dst_port2) 965 | } 966 | log.Println(msg.reqtype, msg.url, msg.atyp, msg.dst_port2) 967 | return true, buf[2:] 968 | } 969 | 970 | type Client struct { 971 | id string 972 | buster bool 973 | engine *nat.AttemptEngine 974 | pipes map[int]net.Conn // client for pipes 975 | specPipes map[string]net.Conn // client for pipes 976 | sessions map[string]*clientSession // session to pipeid 977 | sessionLock sync.RWMutex 978 | ready bool 979 | bUdp bool 980 | } 981 | 982 | // pipe : client to client 983 | // local : client to local apps 984 | func (sc *Client) getSession(sessionId string) *clientSession { 985 | sc.sessionLock.RLock() 986 | session, _ := sc.sessions[sessionId] 987 | sc.sessionLock.RUnlock() 988 | return session 989 | } 990 | 991 | func (sc *Client) removeSession(sessionId string) bool { 992 | if clientType == 1 { 993 | common.RmId("udp", sessionId) 994 | } 995 | sc.sessionLock.RLock() 996 | session, bHave := sc.sessions[sessionId] 997 | sc.sessionLock.RUnlock() 998 | if bHave { 999 | if session.localConn != nil { 1000 | session.localConn.Close() 1001 | } 1002 | sc.sessionLock.Lock() 1003 | delete(sc.sessions, sessionId) 1004 | sc.sessionLock.Unlock() 1005 | //log.Println("client", sc.id, "remove session", sessionId) 1006 | return true 1007 | } 1008 | return false 1009 | } 1010 | 1011 | func (sc *Client) OnTunnelRecv(pipe net.Conn, sessionId string, action string, content string) { 1012 | //println("recv p2p tunnel", sessionId, action, content) 1013 | session := sc.getSession(sessionId) 1014 | var conn net.Conn 1015 | if session != nil { 1016 | conn = session.localConn 1017 | } 1018 | switch action { 1019 | case "tunnel_error": 1020 | if conn != nil { 1021 | conn.Write([]byte(content)) 1022 | log.Println("tunnel error", content, sessionId) 1023 | } 1024 | sc.removeSession(sessionId) 1025 | //case "serve_begin": 1026 | case "tunnel_msg_s": 1027 | if conn != nil { 1028 | //println("tunnel msg", sessionId, len(content)) 1029 | conn.Write([]byte(content)) 1030 | } else { 1031 | //log.Println("cannot tunnel msg", sessionId) 1032 | } 1033 | case "tunnel_close_s": 1034 | sc.removeSession(sessionId) 1035 | case "ping", "pingback": 1036 | //log.Println("recv", action) 1037 | if action == "ping" { 1038 | common.Write(pipe, sessionId, "pingback", "") 1039 | } 1040 | case "tunnel_msg_c": 1041 | if conn != nil { 1042 | //log.Println("tunnel", len(content), sessionId) 1043 | conn.Write([]byte(content)) 1044 | } else if *localAddr == "socks5" { 1045 | if session == nil { 1046 | return 1047 | } 1048 | session.processSockProxy(sc, sessionId, content, func() { 1049 | sc.OnTunnelRecv(pipe, sessionId, action, session.recvMsg) 1050 | }) 1051 | } 1052 | case "tunnel_close": 1053 | sc.removeSession(sessionId) 1054 | case "tunnel_open": 1055 | if clientType == 0 { 1056 | if *localAddr != "socks5" { 1057 | s_conn, err := net.DialTimeout("tcp", *localAddr, 10*time.Second) 1058 | if err != nil { 1059 | log.Println("connect to local server fail:", err.Error()) 1060 | msg := "cannot connect to bind addr" + *localAddr 1061 | common.Write(pipe, sessionId, "tunnel_error", msg) 1062 | //remoteConn.Close() 1063 | return 1064 | } else { 1065 | sc.sessionLock.Lock() 1066 | sc.sessions[sessionId] = &clientSession{pipe: pipe, localConn: s_conn} 1067 | sc.sessionLock.Unlock() 1068 | go handleLocalPortResponse(sc, sessionId) 1069 | } 1070 | } else { 1071 | sc.sessionLock.Lock() 1072 | sc.sessions[sessionId] = &clientSession{pipe: pipe, localConn: nil, status: "init", recvMsg: ""} 1073 | sc.sessionLock.Unlock() 1074 | } 1075 | } 1076 | } 1077 | } 1078 | 1079 | func (sc *Client) Quit() { 1080 | log.Println("client quit", sc.id) 1081 | delete(g_ClientMap, sc.id) 1082 | delete(g_ClientMapKey, sc.id) 1083 | for id, _ := range sc.sessions { 1084 | sc.removeSession(id) 1085 | } 1086 | for _, pipe := range sc.pipes { 1087 | if pipe != remoteConn { 1088 | pipe.Close() 1089 | } 1090 | } 1091 | if sc.engine != nil { 1092 | sc.engine.Fail() 1093 | } 1094 | } 1095 | 1096 | ///////////////////////multi pipe support 1097 | var g_LocalConn net.Conn 1098 | 1099 | func (sc *Client) MultiListen() bool { 1100 | if g_LocalConn == nil { 1101 | g_LocalConn, err := net.Listen("tcp", *localAddr) 1102 | if err != nil { 1103 | log.Println("cannot listen addr:" + err.Error()) 1104 | if remoteConn != nil { 1105 | remoteConn.Close() 1106 | } 1107 | return false 1108 | } 1109 | go func() { 1110 | quit := false 1111 | ping := time.NewTicker(time.Second) 1112 | go func() { 1113 | out: 1114 | for { 1115 | select { 1116 | case <-ping.C: 1117 | if quit { 1118 | break out 1119 | } 1120 | for _, pipe := range sc.pipes { 1121 | common.Write(pipe, "-1", "ping", "") 1122 | } 1123 | } 1124 | } 1125 | }() 1126 | for { 1127 | conn, err := g_LocalConn.Accept() 1128 | if err != nil { 1129 | continue 1130 | } 1131 | sessionId := common.GetId("udp") 1132 | pipe := sc.getOnePipe() 1133 | if pipe == nil { 1134 | log.Println("cannot get pipe for client") 1135 | if remoteConn != nil { 1136 | remoteConn.Close() 1137 | } 1138 | return 1139 | } 1140 | sc.sessionLock.Lock() 1141 | sc.sessions[sessionId] = &clientSession{pipe: pipe, localConn: conn} 1142 | sc.sessionLock.Unlock() 1143 | log.Println("client", sc.id, "create session", sessionId) 1144 | go handleLocalServerResponse(sc, sessionId) 1145 | } 1146 | quit = true 1147 | ping.Stop() 1148 | }() 1149 | mode := "p2p mode" 1150 | if !sc.bUdp { 1151 | mode = "c/s mode" 1152 | delete(g_ClientMapKey, sc.id) 1153 | } 1154 | println("service start success,please connect", *localAddr, mode) 1155 | } 1156 | return true 1157 | } 1158 | 1159 | func (sc *Client) getOnePipe() net.Conn { 1160 | tmp := []int{} 1161 | for id, _ := range sc.pipes { 1162 | tmp = append(tmp, id) 1163 | } 1164 | size := len(tmp) 1165 | if size == 0 { 1166 | return nil 1167 | } 1168 | index := rand.Intn(size) 1169 | log.Println("choose pipe for ", sc.id, ",", index, "of", size) 1170 | hitId := tmp[index] 1171 | pipe, _ := sc.pipes[hitId] 1172 | return pipe 1173 | } 1174 | 1175 | ///////////////////////multi pipe support 1176 | 1177 | func (sc *Client) Run(index int, specPipe string) { 1178 | var pipe net.Conn 1179 | if index >= 0 { 1180 | pipe = sc.pipes[index] 1181 | } else { 1182 | pipe = sc.specPipes[specPipe] 1183 | } 1184 | if pipe == nil { 1185 | return 1186 | } 1187 | go func() { 1188 | callback := func(conn net.Conn, sessionId, action, content string) { 1189 | if sc != nil { 1190 | sc.OnTunnelRecv(conn, sessionId, action, content) 1191 | } 1192 | } 1193 | common.Read(pipe, callback) 1194 | log.Println("client end read", index) 1195 | if index >= 0 { 1196 | delete(sc.pipes, index) 1197 | if clientType == 1 { 1198 | if len(sc.pipes) == 0 { 1199 | if remoteConn != nil { 1200 | remoteConn.Close() 1201 | } 1202 | } 1203 | } 1204 | } else { 1205 | delete(sc.specPipes, specPipe) 1206 | } 1207 | }() 1208 | } 1209 | 1210 | func (sc *Client) LocalAddr() net.Addr { return nil } 1211 | func (sc *Client) Close() error { return nil } 1212 | func (sc *Client) RemoteAddr() net.Addr { return nil } 1213 | func (sc *Client) SetDeadline(t time.Time) error { return nil } 1214 | func (sc *Client) SetReadDeadline(t time.Time) error { return nil } 1215 | func (sc *Client) SetWriteDeadline(t time.Time) error { return nil } 1216 | 1217 | func handleLocalPortResponse(client *Client, id string) { 1218 | sessionId := id 1219 | if !client.bUdp { 1220 | arr := strings.Split(id, "-") 1221 | sessionId = arr[1] 1222 | } 1223 | session := client.getSession(sessionId) 1224 | if session == nil { 1225 | return 1226 | } 1227 | conn := session.localConn 1228 | if conn == nil { 1229 | return 1230 | } 1231 | arr := make([]byte, nat.SendBuffSize) 1232 | reader := bufio.NewReader(conn) 1233 | for { 1234 | size, err := reader.Read(arr) 1235 | if err != nil { 1236 | break 1237 | } 1238 | if common.Write(session.pipe, id, "tunnel_msg_s", string(arr[0:size])) != nil { 1239 | break 1240 | } 1241 | } 1242 | // log.Println("handlerlocal down") 1243 | if client.removeSession(sessionId) { 1244 | common.Write(session.pipe, id, "tunnel_close_s", "") 1245 | } 1246 | } 1247 | 1248 | func handleLocalServerResponse(client *Client, sessionId string) { 1249 | session := client.getSession(sessionId) 1250 | if session == nil { 1251 | return 1252 | } 1253 | pipe := session.pipe 1254 | if pipe == nil { 1255 | return 1256 | } 1257 | conn := session.localConn 1258 | common.Write(pipe, sessionId, "tunnel_open", "") 1259 | arr := make([]byte, nat.SendBuffSize) 1260 | reader := bufio.NewReader(conn) 1261 | for { 1262 | size, err := reader.Read(arr) 1263 | if err != nil { 1264 | break 1265 | } 1266 | if common.Write(pipe, sessionId, "tunnel_msg_c", string(arr[0:size])) != nil { 1267 | break 1268 | } 1269 | } 1270 | common.Write(pipe, sessionId, "tunnel_close", "") 1271 | client.removeSession(sessionId) 1272 | } 1273 | -------------------------------------------------------------------------------- /common/cache.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type cache interface { 8 | SetCacheTime(int64) //if arg < 0, just update alive time 9 | IsAlive() bool 10 | DeInit() 11 | } 12 | 13 | type cacheContainer struct { 14 | c map[string]cache 15 | } 16 | 17 | func (container *cacheContainer) UpdateCache(key string, c cache) { 18 | container.c[key] = c 19 | } 20 | 21 | func (container *cacheContainer) AddCache(key string, c cache, cacheTime int64) { 22 | container.DelCache(key) 23 | container.c[key] = c 24 | c.SetCacheTime(cacheTime) 25 | } 26 | 27 | func (container *cacheContainer) GetCache(key string) cache { 28 | v, bHave := container.c[key] 29 | if bHave { 30 | if v.IsAlive() { 31 | return v 32 | } else { 33 | container.DelCache(key) 34 | } 35 | } 36 | return nil 37 | } 38 | 39 | func (container *cacheContainer) DelCache(key string) bool { 40 | v, bHave := container.c[key] 41 | if bHave { 42 | v.DeInit() 43 | delete(container.c, key) 44 | return true 45 | } 46 | return false 47 | } 48 | 49 | func (container *cacheContainer) DelAllCache() { 50 | for key, _ := range container.c { 51 | container.DelCache(key) 52 | } 53 | } 54 | 55 | var g_CacheMgr map[string]*cacheContainer 56 | 57 | func init() { 58 | g_CacheMgr = make(map[string]*cacheContainer) 59 | go func() { 60 | c := time.Tick(time.Second * 30) 61 | for _ = range c { 62 | for k, cache := range g_CacheMgr { 63 | for key, info := range cache.c { 64 | if !info.IsAlive() { 65 | info.DeInit() 66 | delete(cache.c, key) 67 | } 68 | } 69 | if len(cache.c) == 0 { 70 | delete(g_CacheMgr, k) 71 | } 72 | } 73 | } 74 | }() 75 | } 76 | 77 | func GetCacheContainer(key string) *cacheContainer { 78 | c, bHave := g_CacheMgr[key] 79 | if bHave { 80 | return c 81 | } 82 | c = &cacheContainer{c: make(map[string]cache)} 83 | g_CacheMgr[key] = c 84 | return c 85 | } 86 | 87 | func DelCacheContainer(key string) { 88 | c, bHave := g_CacheMgr[key] 89 | if bHave { 90 | c.DelAllCache() 91 | } 92 | delete(g_CacheMgr, key) 93 | } 94 | 95 | func DelAllCacheContainer() { 96 | for key, c := range g_CacheMgr { 97 | c.DelAllCache() 98 | delete(g_CacheMgr, key) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /common/cache_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "testing" 4 | 5 | type m struct { 6 | T int 7 | w int 8 | } 9 | 10 | func (*m) IsAlive() bool { return true } 11 | func (*m) SetCacheTime(t int64) {} 12 | func (*m) DeInit() { println("deinit") } 13 | 14 | func TestCache(t *testing.T) { 15 | cache := GetCacheContainer("test") 16 | cache2 := GetCacheContainer("test2") 17 | println("addcache", "mmnn", "aabb") 18 | cache.AddCache("mm", &m{1, 2}, 0) 19 | cache.AddCache("nn", &m{3, 4}, 0) 20 | cache2.AddCache("aa", &m{1, 2}, 0) 21 | cache2.AddCache("bb", &m{3, 4}, 0) 22 | a := cache.GetCache("mm") 23 | b := cache.GetCache("mm") 24 | b.(*m).T = 12 25 | if (a.(*m)).T != 12 { 26 | t.Error("cache value copied!!") 27 | } 28 | if cache2.GetCache("novalue") != nil { 29 | t.Error("cache should be nil!!") 30 | } 31 | println("delcache", "mmnn", "aabb") 32 | DelAllCacheContainer() 33 | } 34 | -------------------------------------------------------------------------------- /common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/md5" 7 | "encoding/binary" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "log" 12 | "net" 13 | "strconv" 14 | ) 15 | 16 | const Version = 0.80 17 | 18 | type ClientSetting struct { 19 | AccessKey string 20 | ClientKey string 21 | 22 | Name string 23 | ClientType string 24 | Version float32 25 | Delay int 26 | Mode int 27 | PipeNum int 28 | AesKey string 29 | } 30 | 31 | var encodingData []byte = []byte("bertdfvuifu4359c") 32 | var encodingLen int = 16 33 | var headerLen int = 4 34 | 35 | func init() { 36 | encodingLen = len(encodingData) 37 | headerLen = binary.Size(uint32(1)) 38 | } 39 | 40 | func Xor(s string) string { 41 | n := len(s) 42 | if n == 0 { 43 | return "" 44 | } 45 | r := make([]byte, n) 46 | for i := 0; i < n; i++ { 47 | r[i] = s[i] ^ encodingData[i%encodingLen] 48 | } 49 | return string(r) 50 | } 51 | 52 | func Write(conn net.Conn, id string, action string, content string) error { 53 | if conn == nil { 54 | return nil 55 | } 56 | l1 := len(id) 57 | action = Xor(action) 58 | l2 := len(action) 59 | l3 := len(content) 60 | var buf bytes.Buffer 61 | binary.Write(&buf, binary.LittleEndian, uint32(l1)) 62 | binary.Write(&buf, binary.LittleEndian, uint32(l2)) 63 | binary.Write(&buf, binary.LittleEndian, uint32(l3)) 64 | binary.Write(&buf, binary.LittleEndian, []byte(id)) 65 | binary.Write(&buf, binary.LittleEndian, []byte(action)) 66 | binary.Write(&buf, binary.LittleEndian, []byte(content)) 67 | _, err := conn.Write(buf.Bytes()) 68 | //println("write!!!", old, l1, l2, l3) 69 | if err != nil { 70 | println("write err", err.Error()) 71 | } 72 | return err 73 | } 74 | 75 | type ReadCallBack func(conn net.Conn, id string, action string, arg string) 76 | type ReadUDPCallBack func(conn *net.UDPConn, addr *net.UDPAddr, id string, action string, arg string) 77 | 78 | func Read(conn net.Conn, callback ReadCallBack) { 79 | scanner := bufio.NewScanner(conn) 80 | split := func(data []byte, atEOF bool) (adv int, token []byte, err error) { 81 | l := len(data) 82 | if l < headerLen*3 { 83 | return 0, nil, nil 84 | } 85 | if l > 1048576 { //1024*1024=1048576 86 | conn.Close() 87 | log.Println("invalid query!") 88 | return 0, nil, errors.New("too large data!") 89 | } 90 | var l1, l2, l3 uint32 91 | buf := bytes.NewReader(data) 92 | binary.Read(buf, binary.LittleEndian, &l1) 93 | binary.Read(buf, binary.LittleEndian, &l2) 94 | binary.Read(buf, binary.LittleEndian, &l3) 95 | tail := l - headerLen*3 96 | lhead := l1 + l2 + l3 97 | if lhead > 1048576 { 98 | conn.Close() 99 | log.Println("invalid query2!") 100 | return 0, nil, errors.New("too large data!") 101 | } 102 | if uint32(tail) < lhead { 103 | return 0, nil, nil 104 | } 105 | id := make([]byte, l1) 106 | action := make([]byte, l2) 107 | content := make([]byte, l3) 108 | binary.Read(buf, binary.LittleEndian, &id) 109 | binary.Read(buf, binary.LittleEndian, &action) 110 | binary.Read(buf, binary.LittleEndian, &content) 111 | callback(conn, string(id), Xor(string(action)), string(content)) 112 | //println("read11!!", l1,l2, l3,string(id), Xor(string(action)), string(content)) 113 | return headerLen*3 + int(l1+l2+l3), []byte{}, nil 114 | } 115 | scanner.Split(split) 116 | for scanner.Scan() { 117 | } 118 | if scanner.Err() != nil { 119 | println(scanner.Err().Error()) 120 | } 121 | } 122 | 123 | func Md5(msg string) string { 124 | h := md5.New() 125 | io.WriteString(h, msg) 126 | return fmt.Sprintf("%x", h.Sum(nil)) 127 | } 128 | 129 | func HashPasswd(pass string) string { 130 | return Md5(pass + "testzc222sf") 131 | } 132 | 133 | type _reuseTbl struct { 134 | tbl map[string]bool 135 | } 136 | 137 | var currIdMap map[string]int 138 | var reuseTbl map[string]*_reuseTbl 139 | 140 | func GetId(name string) string { 141 | if reuseTbl != nil { 142 | tbl, bHave := reuseTbl[name] 143 | if bHave { 144 | if len(tbl.tbl) > 0 { 145 | for key := range tbl.tbl { 146 | delete(tbl.tbl, key) 147 | // println("got old id", key) 148 | return key 149 | } 150 | } 151 | } 152 | } 153 | if currIdMap == nil { 154 | currIdMap = make(map[string]int) 155 | currIdMap[name] = 0 156 | } 157 | currIdMap[name]++ 158 | // println("gen new id", currIdMap[name]) 159 | return strconv.Itoa(currIdMap[name]) 160 | } 161 | 162 | func RmId(name, id string) { 163 | return 164 | if currIdMap == nil { 165 | currIdMap = make(map[string]int) 166 | currIdMap[name] = 0 167 | } 168 | n, err := strconv.Atoi(id) 169 | if err != nil { 170 | return 171 | } 172 | if n > currIdMap[name] { 173 | return 174 | } 175 | if reuseTbl == nil { 176 | reuseTbl = make(map[string]*_reuseTbl) 177 | } 178 | tbl, bHave := reuseTbl[name] 179 | if !bHave { 180 | reuseTbl[name] = &_reuseTbl{tbl: make(map[string]bool)} 181 | tbl = reuseTbl[name] 182 | } 183 | tbl.tbl[id] = true 184 | // println("can reuse ", name, id) 185 | } 186 | 187 | func Id_test(name string) { 188 | id1 := GetId(name) 189 | id2 := GetId(name) 190 | id3 := GetId(name) 191 | id4 := GetId(name) 192 | 193 | RmId(name, id2) 194 | RmId(name, id4) 195 | println(GetId(name)) 196 | println(GetId(name)) 197 | RmId(name, id1) 198 | println(GetId(name)) 199 | RmId(name, id3) 200 | println(GetId(name)) 201 | println(GetId(name)) 202 | } 203 | -------------------------------------------------------------------------------- /common/servercommon.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | type ClientInfo struct { 12 | Conn net.Conn 13 | ClientMap map[net.Conn]*Session 14 | Id2Session map[string]*Session 15 | 16 | UserName string 17 | ClientKey string 18 | 19 | ResponseTime int64 20 | 21 | Quit chan bool 22 | 23 | IsServer bool 24 | ServerName string // is serverName != "", this client is a server! 25 | Id2MakeSession map[string]*UDPMakeSession 26 | } 27 | 28 | type UDPMakeSession struct { 29 | Id string 30 | CreateTime int64 31 | ClientA net.Conn 32 | ClientB net.Conn 33 | SessionId string 34 | PipeType string 35 | Status string 36 | ServerName string 37 | Quit chan bool 38 | } 39 | 40 | type Session struct { 41 | Id string 42 | ClientA net.Conn 43 | ClientB net.Conn 44 | Status string 45 | OverTime int64 46 | Method string 47 | Setting ClientSetting 48 | MakeHoleResponseN int 49 | MakeHoleHasFail bool 50 | Quit chan bool 51 | } 52 | 53 | func (session *Session) String() string { 54 | return fmt.Sprintf("%s|delay:%d|status:%s|method:%s|ClientA:%s|ClientB:%s|Pipes:%d/%d", session.Id, session.Setting.Delay, session.Status, session.Method, session.ClientA.RemoteAddr().String(), session.ClientB.RemoteAddr().String(), session.MakeHoleResponseN, session.Setting.PipeNum) 55 | } 56 | 57 | type AdminInfo struct { 58 | Conn net.Conn 59 | } 60 | 61 | func (session *Session) RestartSession(ServerName string) { 62 | log.Println("restart session", session.Id) 63 | session.Method = "restart" 64 | session.Quit <- true 65 | tmp := session.ClientA 66 | session.ClientA = session.ClientB 67 | session.ClientB = tmp 68 | session.MakeHoleResponseN = 0 69 | session.MakeHoleHasFail = false 70 | n := session.Setting.PipeNum 71 | session.StartSession(n, ServerName, session.Id) 72 | } 73 | 74 | func (session *Session) Down() { 75 | if session.Quit != nil { 76 | close(session.Quit) 77 | session.Quit = nil 78 | } 79 | session.Status = "down" 80 | } 81 | 82 | func (session *Session) StartCSMode() { 83 | //make sure ClientA and ClientB not exchanged 84 | session.Method = "cs" 85 | clientConn := session.ClientA 86 | session.Status = "csmode_begin" 87 | Write(clientConn, session.Id, "csmode_c_begin", "") 88 | session.Loop() 89 | } 90 | 91 | func (session *Session) Loop() { 92 | go func() { 93 | checkChan := time.NewTicker(10 * time.Second) 94 | out: 95 | for { 96 | select { 97 | case <-checkChan.C: 98 | //println("check lop session status", session.status) 99 | if time.Now().Unix() > session.OverTime { 100 | if session.Status != "ok" { 101 | if session.Method == "udp" || session.Method == "cs" { 102 | session.ClientA.Close() 103 | } else { 104 | session.ClientB.Close() 105 | } 106 | } 107 | } 108 | case <-session.Quit: 109 | log.Println("session loop quit", session.Id) 110 | break out 111 | } 112 | } 113 | checkChan.Stop() 114 | }() 115 | } 116 | 117 | func (session *Session) StartSession(n int, ServerName, sessionId string) { 118 | if n > 10 { 119 | if session.Method == "udp" || session.Method == "cs" { 120 | Write(session.ClientA, "0", "showandquit", "pipen cannot larger than "+strconv.Itoa(10)) 121 | } else { 122 | Write(session.ClientB, "0", "showandquit", "pipen cannot larger than "+strconv.Itoa(10)) 123 | } 124 | } 125 | for i := 0; i < n; i++ { 126 | session.startUdpSession(ServerName, sessionId, "common") 127 | } 128 | //session.startUdpSession(ServerName, sessionId, "file") 129 | session.OverTime = time.Now().Add(60 * time.Second).Unix() 130 | session.Loop() 131 | } 132 | 133 | func (session *Session) startUdpSession(ServerName, sessionId, pipeType string) { 134 | udpSessionId := GetId("makehole") 135 | log.Println("start session", session.Id, session.Setting.Mode, ServerName, udpSessionId) 136 | udpSession := &UDPMakeSession{CreateTime: time.Now().Unix(), Id: udpSessionId, ClientA: session.ClientA, ClientB: session.ClientB, SessionId: sessionId, PipeType: pipeType, ServerName: ServerName, Status: "init", Quit: make(chan bool)} 137 | GetClientInfoByName(ServerName, func(server *ClientInfo) { 138 | server.Id2MakeSession[udpSession.Id] = udpSession 139 | }, func() {}) 140 | udpSession.BeginMakeHole(0, "") 141 | udpSession.Loop() 142 | } 143 | 144 | func (s *ClientInfo) GetSession(conn net.Conn) *Session { 145 | session, bHave := s.ClientMap[conn] 146 | if bHave { 147 | return session 148 | } else { 149 | return nil 150 | } 151 | } 152 | 153 | func (s *ClientInfo) AddClient(conn net.Conn, clientInfo ClientSetting) { 154 | id := GetId(s.ServerName) 155 | s.ClientMap[conn] = &Session{ClientA: conn, ClientB: s.Conn, Method: "udp", OverTime: 0, Status: "init", Id: id, Setting: clientInfo, Quit: make(chan bool), MakeHoleResponseN: 0, MakeHoleHasFail: false} 156 | s.Id2Session[id] = s.ClientMap[conn] 157 | if s.ClientMap[conn].Setting.Mode == 2 { 158 | s.ClientMap[conn].StartCSMode() 159 | } else { 160 | if clientInfo.AesKey != "" { 161 | Write(s.Conn, id, "aeskey", clientInfo.AesKey) 162 | } 163 | n := clientInfo.PipeNum 164 | s.ClientMap[conn].StartSession(n, s.ServerName, id) 165 | } 166 | } 167 | 168 | func (s *ClientInfo) Loop() { 169 | go func() { 170 | checkChan := time.NewTicker(10 * time.Second) 171 | out: 172 | for { 173 | select { 174 | case <-checkChan.C: 175 | if time.Now().Unix()-s.ResponseTime > 1800 { 176 | log.Println("timeout,client loop quit", s.Conn.RemoteAddr().String()) 177 | break out 178 | } 179 | case <-s.Quit: 180 | break out 181 | } 182 | } 183 | checkChan.Stop() 184 | s.Conn.Close() 185 | }() 186 | } 187 | 188 | func (s *ClientInfo) DelClient(conn net.Conn) string { 189 | session, bHave := s.ClientMap[conn] 190 | if bHave { 191 | Write(conn, "0", "showandquit", "server kick you out") 192 | id := session.Id 193 | session.Down() 194 | log.Println("remove client session", id) 195 | delete(s.Id2Session, id) 196 | delete(s.ClientMap, conn) 197 | RmId(s.ServerName, id) 198 | return id 199 | } 200 | return "" 201 | } 202 | 203 | func (udpsession *UDPMakeSession) Remove(bTimeout bool) { 204 | if bTimeout { 205 | log.Println("timeout,remove udpsession", udpsession.Id) 206 | } else { 207 | //log.Println("remove udpsession", udpsession.Id) 208 | } 209 | close(udpsession.Quit) 210 | GetClientInfoByName(udpsession.ServerName, func(server *ClientInfo) { 211 | delete(server.Id2MakeSession, udpsession.Id) 212 | session, bHave := server.Id2Session[udpsession.SessionId] 213 | if bHave && bTimeout { 214 | Write(session.ClientA, udpsession.Id, "remove_udpsession", "") 215 | Write(session.ClientB, udpsession.Id, "remove_udpsession", "") 216 | } 217 | }, func() {}) 218 | RmId("makehole", udpsession.Id) 219 | } 220 | 221 | func (udpsession *UDPMakeSession) Loop() { 222 | go func() { 223 | checkChan := time.NewTicker(10 * time.Second) 224 | out: 225 | for { 226 | select { 227 | case <-checkChan.C: 228 | if time.Now().Unix()-udpsession.CreateTime > 120 { 229 | udpsession.Remove(true) 230 | break out 231 | } 232 | case <-udpsession.Quit: 233 | break out 234 | } 235 | } 236 | checkChan.Stop() 237 | }() 238 | } 239 | 240 | func (udpsession *UDPMakeSession) BeginMakeHole(step int, content string) { 241 | var session *Session = nil 242 | GetClientInfoByName(udpsession.ServerName, func(server *ClientInfo) { 243 | session = server.Id2Session[udpsession.SessionId] 244 | }, func() {}) 245 | if session != nil && session.Method == "cs" { 246 | return 247 | } 248 | id := udpsession.Id 249 | ClientA := udpsession.ClientA 250 | ClientB := udpsession.ClientB 251 | if step == 0 { 252 | log.Println("===>>tell a to report addrlist", ClientA.RemoteAddr().String(), udpsession.ServerName, udpsession.Id) 253 | delay := 0 254 | if session != nil { 255 | delay = session.Setting.Delay 256 | } 257 | Write(ClientA, id+"-"+udpsession.SessionId+"-"+udpsession.PipeType, "query_addrlist_a", ClientA.RemoteAddr().(*net.TCPAddr).IP.String()+":"+strconv.Itoa(delay)) 258 | if session != nil { 259 | session.Status = "tella" 260 | } 261 | udpsession.Status = "tella" 262 | } else if step == 1 { 263 | if udpsession.Status == "tella" { 264 | udpsession.Status = "atellb" 265 | if session != nil { 266 | session.Status = "atellb" 267 | } 268 | log.Println("===>>tell b to report addlist,give b the a's addrlist", ClientB.RemoteAddr().String(), udpsession.ServerName, udpsession.Id) 269 | Write(ClientB, id+"-"+udpsession.SessionId+"-"+udpsession.PipeType, "query_addrlist_b", ClientB.RemoteAddr().(*net.TCPAddr).IP.String()+":"+content) 270 | } else if udpsession.Status == "atellb" { 271 | udpsession.Status = "bust_start_a" 272 | if session != nil { 273 | session.Status = "bust_start_a" 274 | } 275 | log.Println("=====>>tell a the b 's addrlist, and a start bust", ClientA.RemoteAddr().String(), udpsession.ServerName, udpsession.Id) 276 | Write(ClientA, id, "tell_bust_a", content) 277 | } 278 | } else if step == 2 { 279 | udpsession.Status = "bust_start_b" 280 | if session != nil { 281 | session.Status = "bust_start_b" 282 | } 283 | log.Println("=====>>tell b start bust", ClientB.RemoteAddr().String(), udpsession.ServerName, udpsession.Id) 284 | Write(ClientB, id, "tell_bust_b", content) 285 | } 286 | } 287 | 288 | func GetServerInfoByConn(conn net.Conn, cb_ok func(*ClientInfo), cb_fail func()) { 289 | info, bHave := Conn2ClientInfo[conn] 290 | if bHave { 291 | if info.IsServer { 292 | cb_ok(info) 293 | } else { 294 | ServerName := info.ServerName 295 | GetClientInfoByName(ServerName, cb_ok, cb_fail) 296 | } 297 | } else { 298 | cb_fail() 299 | } 300 | } 301 | func GetClientInfoByConn(conn net.Conn, cb_ok func(*ClientInfo), cb_fail func()) { 302 | info, bHave := Conn2ClientInfo[conn] 303 | if bHave { 304 | cb_ok(info) 305 | } else { 306 | cb_fail() 307 | } 308 | } 309 | func GetClientInfoByName(ServerName string, cb_ok func(*ClientInfo), cb_fail func()) { 310 | conn, bHave := ServerName2Conn[ServerName] 311 | if bHave { 312 | GetClientInfoByConn(conn, cb_ok, cb_fail) 313 | return 314 | } else { 315 | cb_fail() 316 | } 317 | } 318 | 319 | func GetOnlineServiceNumByNameAndIP(userName, ip string) int { 320 | size := 0 321 | for _, info := range Conn2ClientInfo { 322 | if info.IsServer && info.UserName == userName && (ip == info.Conn.RemoteAddr().(*net.TCPAddr).IP.String()) { 323 | size++ 324 | } 325 | } 326 | return size 327 | } 328 | 329 | func GetOnlineServiceNumByName(userName string) int { 330 | size := 0 331 | for _, info := range Conn2ClientInfo { 332 | if info.IsServer && info.UserName == userName { 333 | size++ 334 | } 335 | } 336 | return size 337 | } 338 | 339 | var ServerName2Conn map[string]net.Conn 340 | var Conn2ClientInfo map[net.Conn]*ClientInfo 341 | var Conn2Admin map[net.Conn]*AdminInfo 342 | -------------------------------------------------------------------------------- /doc/docker.md: -------------------------------------------------------------------------------- 1 | # Run dog tunnel with docker container 2 | 3 | First things, you need make sure you have installed docker on your linux environment. 4 | Following instruction was testing under ubuntu 14.04 or above. 5 | 6 | You need install docker on both your server side and client side 7 | 8 | ## server side 9 | 10 | 11 | ``` 12 | docker run -d --restart=always --name=dog-tunnel-server -p 0.0.0.0:8443:8443/udp netroby/alpine-dog-tunnel /usr/bin/dtunnel_lite -service 0.0.0.0:8443 -auth verystrongpassword2 13 | ``` 14 | 15 | ## client side 16 | 17 | Replace your service ip with your.remote.server, and run this command. 18 | 19 | ``` 20 | docker run -d --restart=always --name=dog-tunnel-client -p 0.0.0.0:8070:8070 netroby/alpine-dog-tunnel /usr/bin/dtunnel_lite -service your.remote.server:8443 -local :8070 -auth verystrongpassword2 21 | ``` 22 | If every thing ok, you will have socks5 proxy service listen on your local 0.0.0.0:8070 23 | -------------------------------------------------------------------------------- /dogtunnel: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASE_DIR=`dirname $0` 4 | if [ -L $0 ] 5 | then 6 | BASE_DIR=`dirname $(readlink $0)` 7 | fi 8 | cd $BASE_DIR 9 | go run client.go -remote dog-tunnel.tk:8008 -key test -ssl -local :8888 -delay 2 -stun stun.l.google.com:19302 $@ 10 | -------------------------------------------------------------------------------- /ikcp/ikcp.go: -------------------------------------------------------------------------------- 1 | package ikcp 2 | 3 | import "container/list" 4 | import "encoding/binary" 5 | 6 | //===================================================================== 7 | // 8 | // KCP - A Better ARQ Protocol Implementation 9 | // skywind3000 (at) gmail.com, 2010-2011 10 | // 11 | // Features: 12 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. 13 | // + Maximum RTT reduce three times vs tcp. 14 | // + Lightweight, distributed as a single source file. 15 | // 16 | //===================================================================== 17 | 18 | //===================================================================== 19 | // KCP BASIC 20 | //===================================================================== 21 | const IKCP_RTO_NDL uint32 = 30 // no delay min rto 22 | const IKCP_RTO_MIN uint32 = 100 // normal min rto 23 | const IKCP_RTO_DEF uint32 = 200 24 | const IKCP_RTO_MAX uint32 = 60000 25 | const IKCP_CMD_PUSH uint32 = 81 // cmd: push data 26 | const IKCP_CMD_ACK uint32 = 82 // cmd: ack 27 | const IKCP_CMD_WASK uint32 = 83 // cmd: window probe (ask) 28 | const IKCP_CMD_WINS uint32 = 84 // cmd: window size (tell) 29 | const IKCP_ASK_SEND uint32 = 1 // need to send IKCP_CMD_WASK 30 | const IKCP_ASK_TELL uint32 = 2 // need to send IKCP_CMD_WINS 31 | const IKCP_WND_SND uint32 = 32 32 | const IKCP_WND_RCV uint32 = 32 33 | const IKCP_MTU_DEF uint32 = 1400 34 | const IKCP_ACK_FAST uint32 = 3 35 | const IKCP_INTERVAL uint32 = 100 36 | const IKCP_OVERHEAD uint32 = 24 37 | const IKCP_DEADLINK uint32 = 10 38 | const IKCP_THRESH_INIT uint32 = 2 39 | const IKCP_THRESH_MIN uint32 = 2 40 | const IKCP_PROBE_INIT uint32 = 7000 // 7 secs to probe window size 41 | const IKCP_PROBE_LIMIT uint32 = 120000 // up to 120 secs to probe window 42 | 43 | //--------------------------------------------------------------------- 44 | // encode / decode 45 | //--------------------------------------------------------------------- 46 | 47 | /* encode 8 bits unsigned int */ 48 | func ikcp_encode8u(p []byte, c byte) []byte { 49 | p[0] = c 50 | return p[1:] 51 | } 52 | 53 | /* decode 8 bits unsigned int */ 54 | func ikcp_decode8u(p []byte, c *byte) []byte { 55 | *c = p[0] 56 | return p[1:] 57 | } 58 | 59 | /* encode 16 bits unsigned int (lsb) */ 60 | func ikcp_encode16u(p []byte, w uint16) []byte { 61 | binary.LittleEndian.PutUint16(p, w) 62 | return p[2:] 63 | } 64 | 65 | /* decode 16 bits unsigned int (lsb) */ 66 | func ikcp_decode16u(p []byte, w *uint16) []byte { 67 | *w = binary.LittleEndian.Uint16(p) 68 | return p[2:] 69 | } 70 | 71 | /* encode 32 bits unsigned int (lsb) */ 72 | func ikcp_encode32u(p []byte, l uint32) []byte { 73 | binary.LittleEndian.PutUint32(p, l) 74 | return p[4:] 75 | } 76 | 77 | /* decode 32 bits unsigned int (lsb) */ 78 | func ikcp_decode32u(p []byte, l *uint32) []byte { 79 | *l = binary.LittleEndian.Uint32(p) 80 | return p[4:] 81 | } 82 | 83 | func _imin_(a, b uint32) uint32 { 84 | if a <= b { 85 | return a 86 | } else { 87 | return b 88 | } 89 | } 90 | 91 | func _imax_(a, b uint32) uint32 { 92 | if a >= b { 93 | return a 94 | } else { 95 | return b 96 | } 97 | } 98 | 99 | func _ibound_(lower, middle, upper uint32) uint32 { 100 | return _imin_(_imax_(lower, middle), upper) 101 | } 102 | 103 | func _itimediff(later, earlier uint32) int32 { 104 | return ((int32)(later - earlier)) 105 | } 106 | 107 | //--------------------------------------------------------------------- 108 | // manage segment 109 | //--------------------------------------------------------------------- 110 | type IKCPSEG struct { 111 | conv uint32 112 | cmd uint32 113 | frg uint32 114 | wnd uint32 115 | ts uint32 116 | sn uint32 117 | una uint32 118 | _len uint32 119 | resendts uint32 120 | rto uint32 121 | fastack uint32 122 | xmit uint32 123 | data []byte //1 size 124 | } 125 | 126 | /* 127 | static void* (*ikcp_malloc_hook)(size_t) = nil 128 | static void (*ikcp_free_hook)(void *) = nil 129 | 130 | // internal malloc 131 | static void* ikcp_malloc(size_t size) { 132 | if (ikcp_malloc_hook) 133 | return ikcp_malloc_hook(size) 134 | return malloc(size) 135 | } 136 | 137 | // internal free 138 | static void ikcp_free(void *ptr) { 139 | if (ikcp_free_hook) { 140 | ikcp_free_hook(ptr) 141 | } else { 142 | free(ptr) 143 | } 144 | } 145 | // redefine allocator 146 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)) 147 | { 148 | ikcp_malloc_hook = new_malloc 149 | ikcp_free_hook = new_free 150 | } 151 | 152 | // allocate a new kcp segment 153 | */ 154 | func ikcp_segment_new(kcp *Ikcpcb, size int32) *IKCPSEG { 155 | newInfo := &IKCPSEG{} 156 | newInfo.data = make([]byte, size) 157 | return newInfo 158 | } 159 | 160 | // delete a segment 161 | 162 | // write log 163 | func Ikcp_log(kcp *Ikcpcb, mask int32, head string, args ...interface{}) { 164 | //if ((mask & kcp.logmask) == 0 || kcp.writelog == 0) { return } 165 | //fmt.Printf(head, args...) 166 | } 167 | 168 | // check log mask 169 | func ikcp_canlog(kcp *Ikcpcb, mask int32) int32 { 170 | if (mask&kcp.logmask) == 0 || kcp.writelog == nil { 171 | return 0 172 | } 173 | return 1 174 | } 175 | 176 | // output segment 177 | func ikcp_output(kcp *Ikcpcb, data []byte, size int32) int32 { 178 | if ikcp_canlog(kcp, IKCP_LOG_OUTPUT) != 0 { 179 | Ikcp_log(kcp, IKCP_LOG_OUTPUT, "[RO] %ld bytes", int32(size)) 180 | } 181 | if size == 0 { 182 | return 0 183 | } 184 | return kcp.Output(data, size, kcp, kcp.user) 185 | } 186 | 187 | //--------------------------------------------------------------------- 188 | // create a new kcpcb 189 | //--------------------------------------------------------------------- 190 | func Ikcp_create(conv uint32, user interface{}) *Ikcpcb { 191 | kcp := &Ikcpcb{} 192 | kcp.conv = conv 193 | kcp.user = user 194 | kcp.snd_una = 0 195 | kcp.snd_nxt = 0 196 | kcp.rcv_nxt = 0 197 | kcp.ts_recent = 0 198 | kcp.ts_lastack = 0 199 | kcp.ts_probe = 0 200 | kcp.probe_wait = 0 201 | kcp.snd_wnd = IKCP_WND_SND 202 | kcp.rcv_wnd = IKCP_WND_RCV 203 | kcp.rmt_wnd = IKCP_WND_RCV 204 | kcp.cwnd = 0 205 | kcp.incr = 0 206 | kcp.probe = 0 207 | kcp.mtu = IKCP_MTU_DEF 208 | kcp.mss = kcp.mtu - IKCP_OVERHEAD 209 | 210 | kcp.buffer = make([]byte, (kcp.mtu+IKCP_OVERHEAD)*3) 211 | if kcp.buffer == nil { 212 | return nil 213 | } 214 | 215 | kcp.snd_queue = list.New() 216 | kcp.rcv_queue = list.New() 217 | kcp.snd_buf = list.New() 218 | kcp.rcv_buf = list.New() 219 | kcp.nrcv_buf = 0 220 | kcp.nsnd_buf = 0 221 | kcp.nrcv_que = 0 222 | kcp.nsnd_que = 0 223 | kcp.state = 0 224 | kcp.acklist = nil 225 | kcp.ackblock = 0 226 | kcp.ackcount = 0 227 | kcp.rx_srtt = 0 228 | kcp.rx_rttval = 0 229 | kcp.rx_rto = IKCP_RTO_DEF 230 | kcp.rx_minrto = IKCP_RTO_MIN 231 | kcp.current = 0 232 | kcp.interval = IKCP_INTERVAL 233 | kcp.ts_flush = IKCP_INTERVAL 234 | kcp.nodelay = 0 235 | kcp.updated = 0 236 | kcp.logmask = 0 237 | kcp.ssthresh = IKCP_THRESH_INIT 238 | kcp.fastresend = 0 239 | kcp.nocwnd = 0 240 | kcp.xmit = 0 241 | kcp.dead_link = IKCP_DEADLINK 242 | kcp.Output = nil 243 | kcp.writelog = nil 244 | 245 | return kcp 246 | } 247 | 248 | //--------------------------------------------------------------------- 249 | // release a new kcpcb 250 | //--------------------------------------------------------------------- 251 | func Ikcp_release(kcp *Ikcpcb) { 252 | if kcp != nil { 253 | kcp.nrcv_buf = 0 254 | kcp.nsnd_buf = 0 255 | kcp.nrcv_que = 0 256 | kcp.nsnd_que = 0 257 | kcp.ackcount = 0 258 | kcp.buffer = nil 259 | kcp.acklist = nil 260 | } 261 | } 262 | 263 | //--------------------------------------------------------------------- 264 | // recv data 265 | //--------------------------------------------------------------------- 266 | func Ikcp_recv(kcp *Ikcpcb, buffer []byte, _len int32) int32 { 267 | ispeek := 1 268 | if _len >= 0 { 269 | ispeek = 0 270 | } 271 | var peeksize int32 272 | _recover := 0 273 | var seg *IKCPSEG 274 | 275 | if kcp.rcv_queue.Len() == 0 { 276 | return -1 277 | } 278 | 279 | if _len < 0 { 280 | _len = -_len 281 | } 282 | 283 | peeksize = Ikcp_peeksize(kcp) 284 | 285 | if peeksize < 0 { 286 | return -2 287 | } 288 | 289 | if peeksize > _len { 290 | return -3 291 | } 292 | 293 | if kcp.nrcv_que >= kcp.rcv_wnd { 294 | _recover = 1 295 | } 296 | 297 | //if kcp.user[0] == 0 { 298 | //fmt.Println("have!!!!") 299 | //} 300 | // merge fragment 301 | _len = 0 302 | for p := kcp.rcv_queue.Front(); p != nil; { 303 | var fragment int32 304 | seg = p.Value.(*IKCPSEG) 305 | 306 | if len(buffer) > 0 { 307 | copy(buffer, seg.data[:seg._len]) 308 | buffer = buffer[seg._len:] 309 | } 310 | 311 | _len += int32(seg._len) 312 | fragment = int32(seg.frg) 313 | 314 | if ikcp_canlog(kcp, IKCP_LOG_RECV) != 0 { 315 | Ikcp_log(kcp, IKCP_LOG_RECV, "recv sn=", seg.sn, seg._len, kcp.user) 316 | } 317 | 318 | if ispeek == 0 { 319 | q := p.Next() 320 | kcp.rcv_queue.Remove(p) 321 | p = q 322 | kcp.nrcv_que-- 323 | //if kcp.user[0] == 0 { 324 | //fmt.Println("remove from recvqueue", kcp.rcv_queue.Len(), kcp.user, "rcv q:", kcp.nrcv_que) 325 | //} 326 | } else { 327 | p = p.Next() 328 | } 329 | 330 | if fragment == 0 { 331 | break 332 | } 333 | } 334 | // move available data from rcv_buf . rcv_queue 335 | for p := kcp.rcv_buf.Front(); p != nil; { 336 | seg := p.Value.(*IKCPSEG) 337 | if seg.sn == kcp.rcv_nxt && kcp.nrcv_que < kcp.rcv_wnd { 338 | q := p.Next() 339 | kcp.rcv_buf.Remove(p) 340 | p = q 341 | kcp.nrcv_buf-- 342 | kcp.rcv_queue.PushBack(seg) 343 | kcp.nrcv_que++ 344 | //if kcp.user[0] == 0 { 345 | //fmt.Println("insert from recvqueue", kcp.rcv_queue.Len(), kcp.user, "rcv q:", kcp.nrcv_que) 346 | //} 347 | kcp.rcv_nxt++ 348 | } else { 349 | break 350 | } 351 | } 352 | 353 | // fast _recover 354 | if kcp.nrcv_que < kcp.rcv_wnd && _recover != 0 { 355 | // ready to send back IKCP_CMD_WINS in Ikcp_flush 356 | // tell remote my window size 357 | kcp.probe |= IKCP_ASK_TELL 358 | } 359 | 360 | return _len 361 | } 362 | 363 | //--------------------------------------------------------------------- 364 | // send data 365 | //--------------------------------------------------------------------- 366 | func Ikcp_peeksize(kcp *Ikcpcb) int32 { 367 | length := 0 368 | 369 | if kcp.rcv_queue.Len() == 0 { 370 | return -1 371 | } 372 | 373 | seg := kcp.rcv_queue.Front().Value.(*IKCPSEG) 374 | if seg.frg == 0 { 375 | return int32(seg._len) 376 | } 377 | 378 | if kcp.nrcv_que < seg.frg+1 { 379 | return -1 380 | } 381 | 382 | for p := kcp.rcv_queue.Front(); p != nil; p = p.Next() { 383 | seg = p.Value.(*IKCPSEG) 384 | length += int(seg._len) 385 | if seg.frg == 0 { 386 | break 387 | } 388 | } 389 | 390 | return int32(length) 391 | } 392 | 393 | //--------------------------------------------------------------------- 394 | // send data 395 | //--------------------------------------------------------------------- 396 | func Ikcp_send(kcp *Ikcpcb, buffer []byte, _len int) int { 397 | var seg *IKCPSEG 398 | var count, i int32 399 | 400 | if _len < 0 { 401 | return -1 402 | } 403 | 404 | if _len <= int(kcp.mss) { 405 | count = 1 406 | } else { 407 | count = (int32(_len) + int32(kcp.mss) - 1) / int32(kcp.mss) 408 | } 409 | 410 | if count > 255 { 411 | return -2 412 | } 413 | 414 | if count == 0 { 415 | count = 1 416 | } 417 | 418 | // fragment 419 | for i = 0; i < count; i++ { 420 | size := int32(kcp.mss) 421 | if _len <= int(kcp.mss) { 422 | size = int32(_len) 423 | } 424 | seg = ikcp_segment_new(kcp, size) 425 | if seg == nil { 426 | return -2 427 | } 428 | if buffer != nil && _len > 0 { 429 | copy(seg.data, buffer[:size]) 430 | } 431 | seg._len = uint32(size) 432 | seg.frg = uint32(count - i - 1) 433 | kcp.snd_queue.PushBack(seg) 434 | //if kcp.user[0] == 0 { 435 | //fmt.Println(kcp.user, "send", kcp.snd_queue.Len()) 436 | //} 437 | kcp.nsnd_que++ 438 | if buffer != nil { 439 | buffer = buffer[size:] 440 | } 441 | _len -= int(size) 442 | } 443 | 444 | return 0 445 | } 446 | 447 | //--------------------------------------------------------------------- 448 | // parse ack 449 | //--------------------------------------------------------------------- 450 | func Ikcp_update_ack(kcp *Ikcpcb, rtt int32) { 451 | rto := 0 452 | if kcp.rx_srtt == 0 { 453 | kcp.rx_srtt = uint32(rtt) 454 | kcp.rx_rttval = uint32(rtt) / 2 455 | } else { 456 | delta := rtt - int32(kcp.rx_srtt) 457 | if delta < 0 { 458 | delta = -delta 459 | } 460 | kcp.rx_rttval = (3*kcp.rx_rttval + uint32(delta)) / 4 461 | kcp.rx_srtt = (7*kcp.rx_srtt + uint32(rtt)) / 8 462 | if kcp.rx_srtt < 1 { 463 | kcp.rx_srtt = 1 464 | } 465 | } 466 | rto = int(kcp.rx_srtt + _imax_(kcp.interval, 4*kcp.rx_rttval)) 467 | kcp.rx_rto = _ibound_(kcp.rx_minrto, uint32(rto), IKCP_RTO_MAX) 468 | } 469 | 470 | func ikcp_shrink_buf(kcp *Ikcpcb) { 471 | if kcp.snd_buf.Len() > 0 { 472 | p := kcp.snd_buf.Front() 473 | seg := p.Value.(*IKCPSEG) 474 | kcp.snd_una = seg.sn 475 | //if kcp.user[0] == 0 { 476 | //println("set snd_una:", seg.sn) 477 | //} 478 | } else { 479 | kcp.snd_una = kcp.snd_nxt 480 | //if kcp.user[0] == 0 { 481 | //println("set2 snd_una:", kcp.snd_nxt) 482 | //} 483 | } 484 | } 485 | 486 | func ikcp_parse_ack(kcp *Ikcpcb, sn uint32) { 487 | if _itimediff(sn, kcp.snd_una) < 0 || _itimediff(sn, kcp.snd_nxt) >= 0 { 488 | // //fmt.Printf("wi %d,%d %d,%d\n", sn, kcp.snd_una, sn, kcp.snd_nxt) 489 | return 490 | } 491 | 492 | for p := kcp.snd_buf.Front(); p != nil; p = p.Next() { 493 | seg := p.Value.(*IKCPSEG) 494 | if sn == seg.sn { 495 | kcp.snd_buf.Remove(p) 496 | kcp.nsnd_buf-- 497 | break 498 | } 499 | if _itimediff(sn, seg.sn) < 0 { 500 | break 501 | } 502 | } 503 | } 504 | 505 | func ikcp_parse_fastack(kcp *Ikcpcb, sn uint32) { 506 | if _itimediff(sn, kcp.snd_una) < 0 || _itimediff(sn, kcp.snd_nxt) >= 0 { 507 | return 508 | } 509 | 510 | for p := kcp.snd_buf.Front(); p != nil; p = p.Next() { 511 | seg := p.Value.(*IKCPSEG) 512 | if _itimediff(sn, seg.sn) < 0 { 513 | break 514 | } else if sn != seg.sn { 515 | seg.fastack++ 516 | } 517 | } 518 | } 519 | 520 | func ikcp_parse_una(kcp *Ikcpcb, una uint32) { 521 | for p := kcp.snd_buf.Front(); p != nil; { 522 | seg := p.Value.(*IKCPSEG) 523 | if _itimediff(una, seg.sn) > 0 { 524 | q := p.Next() 525 | kcp.snd_buf.Remove(p) 526 | p = q 527 | kcp.nsnd_buf-- 528 | } else { 529 | break 530 | } 531 | } 532 | } 533 | 534 | //--------------------------------------------------------------------- 535 | // ack append 536 | //--------------------------------------------------------------------- 537 | func ikcp_ack_push(kcp *Ikcpcb, sn, ts uint32) { 538 | newsize := kcp.ackcount + 1 539 | 540 | if newsize > kcp.ackblock { 541 | var acklist []uint32 542 | var newblock int32 543 | 544 | for newblock = 8; uint32(newblock) < newsize; newblock <<= 1 { 545 | } 546 | acklist = make([]uint32, newblock*2) 547 | if kcp.acklist != nil { 548 | for x := 0; uint32(x) < kcp.ackcount; x++ { 549 | acklist[x*2+0] = kcp.acklist[x*2+0] 550 | acklist[x*2+1] = kcp.acklist[x*2+1] 551 | } 552 | } 553 | kcp.acklist = acklist 554 | kcp.ackblock = uint32(newblock) 555 | } 556 | 557 | ptr := kcp.acklist[kcp.ackcount*2:] 558 | ptr[0] = sn 559 | ptr[1] = ts 560 | kcp.ackcount++ 561 | } 562 | 563 | func ikcp_ack_get(kcp *Ikcpcb, p int32, sn, ts *uint32) { 564 | if sn != nil { 565 | *sn = kcp.acklist[p*2+0] 566 | } 567 | if ts != nil { 568 | *ts = kcp.acklist[p*2+1] 569 | } 570 | } 571 | 572 | //--------------------------------------------------------------------- 573 | // parse data 574 | //--------------------------------------------------------------------- 575 | func ikcp_parse_data(kcp *Ikcpcb, newseg *IKCPSEG) { 576 | var p *list.Element 577 | sn := newseg.sn 578 | repeat := 0 579 | if _itimediff(sn, kcp.rcv_nxt+kcp.rcv_wnd) >= 0 || 580 | _itimediff(sn, kcp.rcv_nxt) < 0 { 581 | return 582 | } 583 | 584 | for p = kcp.rcv_buf.Back(); p != nil; p = p.Prev() { 585 | seg := p.Value.(*IKCPSEG) 586 | if seg.sn == sn { 587 | repeat = 1 588 | break 589 | } 590 | if _itimediff(sn, seg.sn) > 0 { 591 | break 592 | } 593 | } 594 | 595 | if repeat == 0 { 596 | if p == nil { 597 | kcp.rcv_buf.PushFront(newseg) 598 | } else { 599 | kcp.rcv_buf.InsertAfter(newseg, p) 600 | } 601 | kcp.nrcv_buf++ 602 | } else { 603 | } 604 | for p = kcp.rcv_buf.Front(); p != nil; { 605 | seg := p.Value.(*IKCPSEG) 606 | if seg.sn == kcp.rcv_nxt && kcp.nrcv_que < kcp.rcv_wnd { 607 | q := p.Next() 608 | kcp.rcv_buf.Remove(p) 609 | p = q 610 | kcp.nrcv_buf-- 611 | kcp.rcv_queue.PushBack(seg) 612 | //if kcp.user[0] == 0 { 613 | //fmt.Println("insert from recvqueue2", kcp.rcv_queue.Len(), kcp.user) 614 | //} 615 | kcp.nrcv_que++ 616 | kcp.rcv_nxt++ 617 | } else { 618 | break 619 | } 620 | } 621 | //println("inputok!!!", kcp.nrcv_buf, kcp.nrcv_que, repeat, kcp.rcv_nxt, sn) 622 | } 623 | 624 | //--------------------------------------------------------------------- 625 | // input data 626 | //--------------------------------------------------------------------- 627 | func Ikcp_input(kcp *Ikcpcb, data []byte, size int) int { 628 | una := kcp.snd_una 629 | var maxack uint32 = 0 630 | flag := 0 631 | if ikcp_canlog(kcp, IKCP_LOG_INPUT) != 0 { 632 | Ikcp_log(kcp, IKCP_LOG_INPUT, "[RI] %d bytes", size) 633 | } 634 | 635 | if data == nil || size < 24 { 636 | return 0 637 | } 638 | 639 | for { 640 | var ts, sn, _len, una, conv uint32 641 | var wnd uint16 642 | var cmd, frg uint8 643 | var seg *IKCPSEG 644 | 645 | if size < int(IKCP_OVERHEAD) { 646 | break 647 | } 648 | 649 | data = ikcp_decode32u(data, &conv) 650 | if conv != kcp.conv { 651 | return -1 652 | } 653 | 654 | data = ikcp_decode8u(data, &cmd) 655 | data = ikcp_decode8u(data, &frg) 656 | data = ikcp_decode16u(data, &wnd) 657 | data = ikcp_decode32u(data, &ts) 658 | data = ikcp_decode32u(data, &sn) 659 | data = ikcp_decode32u(data, &una) 660 | data = ikcp_decode32u(data, &_len) 661 | 662 | size -= int(IKCP_OVERHEAD) 663 | 664 | if uint32(size) < uint32(_len) { 665 | return -2 666 | } 667 | 668 | if cmd != uint8(IKCP_CMD_PUSH) && cmd != uint8(IKCP_CMD_ACK) && 669 | cmd != uint8(IKCP_CMD_WASK) && cmd != uint8(IKCP_CMD_WINS) { 670 | return -3 671 | } 672 | 673 | kcp.rmt_wnd = uint32(wnd) 674 | ikcp_parse_una(kcp, una) 675 | ikcp_shrink_buf(kcp) 676 | 677 | if cmd == uint8(IKCP_CMD_ACK) { 678 | if _itimediff(kcp.current, ts) >= 0 { 679 | Ikcp_update_ack(kcp, _itimediff(kcp.current, ts)) 680 | } 681 | ikcp_parse_ack(kcp, sn) 682 | ikcp_shrink_buf(kcp) 683 | if flag == 0 { 684 | flag = 1 685 | maxack = sn 686 | } else { 687 | if _itimediff(sn, maxack) > 0 { 688 | maxack = sn 689 | } 690 | } 691 | /* 692 | log.Printf( 693 | "input ack: sn=%lu rtt=%ld rto=%ld", sn, 694 | uint32(_itimediff(kcp.current, ts)), 695 | uint32(kcp.rx_rto))*/ 696 | } else if cmd == uint8(IKCP_CMD_PUSH) { 697 | /* 698 | log.Printf( 699 | "input psh: sn=%lu ts=%lu", sn, ts)*/ 700 | if _itimediff(sn, kcp.rcv_nxt+kcp.rcv_wnd) < 0 { 701 | ikcp_ack_push(kcp, sn, ts) 702 | if _itimediff(sn, kcp.rcv_nxt) >= 0 { 703 | seg = ikcp_segment_new(kcp, int32(_len)) 704 | seg.conv = conv 705 | seg.cmd = uint32(cmd) 706 | seg.frg = uint32(frg) 707 | seg.wnd = uint32(wnd) 708 | seg.ts = ts 709 | seg.sn = sn 710 | seg.una = una 711 | seg._len = _len 712 | 713 | if _len > 0 { 714 | copy(seg.data, data[:_len]) 715 | } 716 | 717 | ikcp_parse_data(kcp, seg) 718 | } 719 | } 720 | } else if cmd == uint8(IKCP_CMD_WASK) { 721 | // ready to send back IKCP_CMD_WINS in Ikcp_flush 722 | // tell remote my window size 723 | kcp.probe |= IKCP_ASK_TELL 724 | if ikcp_canlog(kcp, IKCP_LOG_IN_PROBE) != 0 { 725 | Ikcp_log(kcp, IKCP_LOG_IN_PROBE, "input probe") 726 | } 727 | } else if cmd == uint8(IKCP_CMD_WINS) { 728 | // do nothing 729 | if ikcp_canlog(kcp, IKCP_LOG_IN_WIN) != 0 { 730 | Ikcp_log(kcp, IKCP_LOG_IN_WIN, 731 | "input wins: %lu", uint32(wnd)) 732 | } 733 | } else { 734 | return -3 735 | } 736 | 737 | data = data[_len:] 738 | size -= int(_len) 739 | } 740 | 741 | if flag != 0 { 742 | ikcp_parse_fastack(kcp, maxack) 743 | } 744 | 745 | if _itimediff(kcp.snd_una, una) > 0 { 746 | if kcp.cwnd < kcp.rmt_wnd { 747 | mss := kcp.mss 748 | if kcp.cwnd < kcp.ssthresh { 749 | kcp.cwnd++ 750 | kcp.incr += mss 751 | } else { 752 | if kcp.incr < mss { 753 | kcp.incr = mss 754 | } 755 | kcp.incr += (mss*mss)/kcp.incr + (mss / 16) 756 | if (kcp.cwnd+1)*mss <= kcp.incr { 757 | kcp.cwnd++ 758 | } 759 | } 760 | if kcp.cwnd > kcp.rmt_wnd { 761 | kcp.cwnd = kcp.rmt_wnd 762 | kcp.incr = kcp.rmt_wnd * mss 763 | } 764 | } 765 | } 766 | 767 | return 0 768 | } 769 | 770 | //--------------------------------------------------------------------- 771 | // ikcp_encode_seg 772 | //--------------------------------------------------------------------- 773 | func ikcp_encode_seg(ptr []byte, seg *IKCPSEG) []byte { 774 | ptr = ikcp_encode32u(ptr, seg.conv) 775 | ptr = ikcp_encode8u(ptr, uint8(seg.cmd)) 776 | ptr = ikcp_encode8u(ptr, uint8(seg.frg)) 777 | ptr = ikcp_encode16u(ptr, uint16(seg.wnd)) 778 | ptr = ikcp_encode32u(ptr, seg.ts) 779 | ptr = ikcp_encode32u(ptr, seg.sn) 780 | ptr = ikcp_encode32u(ptr, seg.una) 781 | ptr = ikcp_encode32u(ptr, seg._len) 782 | return ptr 783 | } 784 | 785 | func ikcp_wnd_unused(kcp *Ikcpcb) int32 { 786 | if kcp.nrcv_que < kcp.rcv_wnd { 787 | return int32(kcp.rcv_wnd - kcp.nrcv_que) 788 | } 789 | return 0 790 | } 791 | 792 | //--------------------------------------------------------------------- 793 | // Ikcp_flush 794 | //--------------------------------------------------------------------- 795 | func Ikcp_flush(kcp *Ikcpcb) { 796 | current := kcp.current 797 | buffer := kcp.buffer 798 | ptr := buffer 799 | var count, size, i int32 800 | var resent, cwnd uint32 801 | var rtomin uint32 802 | change := 0 803 | lost := 0 804 | var seg IKCPSEG 805 | 806 | // 'Ikcp_update' haven't been called. 807 | if kcp.updated == 0 { 808 | return 809 | } 810 | 811 | seg.conv = kcp.conv 812 | seg.cmd = IKCP_CMD_ACK 813 | seg.frg = 0 814 | seg.wnd = uint32(ikcp_wnd_unused(kcp)) 815 | seg.una = kcp.rcv_nxt 816 | seg._len = 0 817 | seg.sn = 0 818 | seg.ts = 0 819 | 820 | // flush acknowledges 821 | size = 0 822 | count = int32(kcp.ackcount) 823 | for i = 0; i < count; i++ { 824 | //size = int32(ptr - buffer) 825 | if size+int32(IKCP_OVERHEAD) > int32(kcp.mtu) { 826 | ikcp_output(kcp, buffer, size) 827 | ptr = buffer 828 | size = 0 829 | } 830 | ikcp_ack_get(kcp, i, &seg.sn, &seg.ts) 831 | ptr = ikcp_encode_seg(ptr, &seg) 832 | size += 24 833 | } 834 | 835 | kcp.ackcount = 0 836 | 837 | // probe window size (if remote window size equals zero) 838 | if kcp.rmt_wnd == 0 { 839 | if kcp.probe_wait == 0 { 840 | kcp.probe_wait = IKCP_PROBE_INIT 841 | kcp.ts_probe = kcp.current + kcp.probe_wait 842 | } else { 843 | if _itimediff(kcp.current, kcp.ts_probe) >= 0 { 844 | if kcp.probe_wait < IKCP_PROBE_INIT { 845 | kcp.probe_wait = IKCP_PROBE_INIT 846 | } 847 | kcp.probe_wait += kcp.probe_wait / 2 848 | if kcp.probe_wait > IKCP_PROBE_LIMIT { 849 | kcp.probe_wait = IKCP_PROBE_LIMIT 850 | } 851 | kcp.ts_probe = kcp.current + kcp.probe_wait 852 | kcp.probe |= IKCP_ASK_SEND 853 | } 854 | } 855 | } else { 856 | kcp.ts_probe = 0 857 | kcp.probe_wait = 0 858 | } 859 | 860 | // flush window probing commands 861 | if (kcp.probe & IKCP_ASK_SEND) != 0 { 862 | seg.cmd = IKCP_CMD_WASK 863 | if size+int32(IKCP_OVERHEAD) > int32(kcp.mtu) { 864 | ikcp_output(kcp, buffer, size) 865 | ptr = buffer 866 | size = 0 867 | } 868 | ptr = ikcp_encode_seg(ptr, &seg) 869 | size += 24 870 | } 871 | 872 | // flush window probing commands 873 | if (kcp.probe & IKCP_ASK_TELL) != 0 { 874 | seg.cmd = IKCP_CMD_WINS 875 | if size+int32(IKCP_OVERHEAD) > int32(kcp.mtu) { 876 | ikcp_output(kcp, buffer, size) 877 | ptr = buffer 878 | size = 0 879 | } 880 | ptr = ikcp_encode_seg(ptr, &seg) 881 | size += 24 882 | } 883 | 884 | kcp.probe = 0 885 | 886 | // calculate window size 887 | cwnd = _imin_(kcp.snd_wnd, kcp.rmt_wnd) 888 | if kcp.nocwnd == 0 { 889 | cwnd = _imin_(kcp.cwnd, cwnd) 890 | } 891 | 892 | // move data from snd_queue to snd_buf 893 | ////println("check",kcp.snd_queue.Len()) 894 | for p := kcp.snd_queue.Front(); p != nil; { 895 | ////println("debug check:", t, p.Next(), kcp.snd_nxt, kcp.snd_una, cwnd, _itimediff(kcp.snd_nxt, kcp.snd_una + cwnd)) 896 | ////fmt.Printf("timediff %d,%d,%d,%d\n", kcp.snd_nxt, kcp.snd_una, cwnd, _itimediff(kcp.snd_nxt, kcp.snd_una + cwnd)); 897 | if _itimediff(kcp.snd_nxt, kcp.snd_una+cwnd) >= 0 { 898 | //if kcp.user[0] == 0 { 899 | ////fmt.Println("=======", kcp.snd_nxt, kcp.snd_una, cwnd) 900 | //} 901 | break 902 | } 903 | newseg := p.Value.(*IKCPSEG) 904 | q := p.Next() 905 | kcp.snd_queue.Remove(p) 906 | p = q 907 | kcp.snd_buf.PushBack(newseg) 908 | //if kcp.user[0] == 0 { 909 | //println("debug check2:", t, kcp.snd_queue.Len(), kcp.snd_buf.Len(), kcp.nsnd_que) 910 | //} 911 | kcp.nsnd_que-- 912 | kcp.nsnd_buf++ 913 | 914 | newseg.conv = kcp.conv 915 | newseg.cmd = IKCP_CMD_PUSH 916 | newseg.wnd = seg.wnd 917 | newseg.ts = current 918 | newseg.sn = kcp.snd_nxt 919 | kcp.snd_nxt++ 920 | newseg.una = kcp.rcv_nxt 921 | newseg.resendts = current 922 | newseg.rto = kcp.rx_rto 923 | newseg.fastack = 0 924 | newseg.xmit = 0 925 | } 926 | 927 | // calculate resent 928 | resent = uint32(kcp.fastresend) 929 | if kcp.fastresend <= 0 { 930 | resent = 0xffffffff 931 | } 932 | rtomin = (kcp.rx_rto >> 3) 933 | if kcp.nodelay != 0 { 934 | rtomin = 0 935 | } 936 | 937 | // flush data segments 938 | for p := kcp.snd_buf.Front(); p != nil; p = p.Next() { 939 | ////println("debug loop", a, kcp.snd_buf.Len()) 940 | segment := p.Value.(*IKCPSEG) 941 | needsend := 0 942 | if segment.xmit == 0 { 943 | needsend = 1 944 | segment.xmit++ 945 | segment.rto = kcp.rx_rto 946 | segment.resendts = current + segment.rto + rtomin 947 | } else if _itimediff(current, segment.resendts) >= 0 { 948 | needsend = 1 949 | segment.xmit++ 950 | kcp.xmit++ 951 | if kcp.nodelay == 0 { 952 | segment.rto += kcp.rx_rto 953 | } else { 954 | segment.rto += kcp.rx_rto / 2 955 | } 956 | segment.resendts = current + segment.rto 957 | lost = 1 958 | } else if segment.fastack >= resent { 959 | needsend = 1 960 | segment.xmit++ 961 | segment.fastack = 0 962 | segment.resendts = current + segment.rto 963 | change++ 964 | } 965 | if needsend != 0 { 966 | var need int32 967 | segment.ts = current 968 | segment.wnd = seg.wnd 969 | segment.una = kcp.rcv_nxt 970 | 971 | need = int32(IKCP_OVERHEAD + segment._len) 972 | 973 | ////fmt.Printf("vzex:need send%d, %d,%d,%d\n", kcp.nsnd_buf, size, need, kcp.mtu) 974 | if size+need > int32(kcp.mtu) { 975 | // //fmt.Printf("trigger!\n"); 976 | ikcp_output(kcp, buffer, size) 977 | ptr = buffer 978 | size = 0 979 | } 980 | 981 | ptr = ikcp_encode_seg(ptr, segment) 982 | size += 24 983 | 984 | if segment._len > 0 { 985 | copy(ptr, segment.data[:segment._len]) 986 | ptr = ptr[segment._len:] 987 | size += int32(segment._len) 988 | } 989 | 990 | if segment.xmit >= kcp.dead_link { 991 | kcp.state = 0 992 | } 993 | } 994 | } 995 | 996 | // flash remain segments 997 | if size > 0 { 998 | ikcp_output(kcp, buffer, size) 999 | } 1000 | 1001 | // update ssthresh 1002 | if change != 0 { 1003 | inflight := kcp.snd_nxt - kcp.snd_una 1004 | kcp.ssthresh = inflight / 2 1005 | if kcp.ssthresh < IKCP_THRESH_MIN { 1006 | kcp.ssthresh = IKCP_THRESH_MIN 1007 | } 1008 | kcp.cwnd = kcp.ssthresh + resent 1009 | kcp.incr = kcp.cwnd * kcp.mss 1010 | } 1011 | 1012 | if lost != 0 { 1013 | kcp.ssthresh = cwnd / 2 1014 | if kcp.ssthresh < IKCP_THRESH_MIN { 1015 | kcp.ssthresh = IKCP_THRESH_MIN 1016 | } 1017 | kcp.cwnd = 1 1018 | kcp.incr = kcp.mss 1019 | } 1020 | 1021 | if kcp.cwnd < 1 { 1022 | kcp.cwnd = 1 1023 | kcp.incr = kcp.mss 1024 | } 1025 | } 1026 | 1027 | //--------------------------------------------------------------------- 1028 | // input update 1029 | //--------------------------------------------------------------------- 1030 | func Ikcp_update(kcp *Ikcpcb, current uint32) { 1031 | var slap int32 1032 | 1033 | kcp.current = current 1034 | 1035 | if kcp.updated == 0 { 1036 | kcp.updated = 1 1037 | kcp.ts_flush = kcp.current 1038 | } 1039 | 1040 | slap = _itimediff(kcp.current, kcp.ts_flush) 1041 | 1042 | if slap >= 10000 || slap < -10000 { 1043 | kcp.ts_flush = kcp.current 1044 | slap = 0 1045 | } 1046 | 1047 | if slap >= 0 { 1048 | kcp.ts_flush += kcp.interval 1049 | if _itimediff(kcp.current, kcp.ts_flush) >= 0 { 1050 | kcp.ts_flush = kcp.current + kcp.interval 1051 | } 1052 | Ikcp_flush(kcp) 1053 | } 1054 | } 1055 | 1056 | func Ikcp_check(kcp *Ikcpcb, current uint32) uint32 { 1057 | ts_flush := kcp.ts_flush 1058 | tm_flush := 0x7fffffff 1059 | tm_packet := 0x7fffffff 1060 | minimal := 0 1061 | if kcp.updated == 0 { 1062 | return current 1063 | } 1064 | 1065 | if _itimediff(current, ts_flush) >= 10000 || 1066 | _itimediff(current, ts_flush) < -10000 { 1067 | ts_flush = current 1068 | } 1069 | 1070 | if _itimediff(current, ts_flush) >= 0 { 1071 | return current 1072 | } 1073 | 1074 | tm_flush = int(_itimediff(ts_flush, current)) 1075 | 1076 | for p := kcp.snd_buf.Front(); p != nil; p = p.Next() { 1077 | seg := p.Value.(*IKCPSEG) 1078 | diff := _itimediff(seg.resendts, current) 1079 | if diff <= 0 { 1080 | return current 1081 | } 1082 | if diff < int32(tm_packet) { 1083 | tm_packet = int(diff) 1084 | } 1085 | } 1086 | 1087 | minimal = int(tm_packet) 1088 | if tm_packet >= tm_flush { 1089 | minimal = int(tm_flush) 1090 | } 1091 | if uint32(minimal) >= kcp.interval { 1092 | minimal = int(kcp.interval) 1093 | } 1094 | 1095 | return current + uint32(minimal) 1096 | } 1097 | 1098 | func Ikcp_setmtu(kcp *Ikcpcb, mtu int32) int32 { 1099 | if mtu < 50 || mtu < int32(IKCP_OVERHEAD) { 1100 | return -1 1101 | } 1102 | buffer := make([]byte, (uint32(mtu)+IKCP_OVERHEAD)*3) 1103 | if buffer == nil { 1104 | return -2 1105 | } 1106 | kcp.mtu = uint32(mtu) 1107 | kcp.mss = kcp.mtu - IKCP_OVERHEAD 1108 | kcp.buffer = buffer 1109 | return 0 1110 | } 1111 | 1112 | func ikcp_interval(kcp *Ikcpcb, interval int32) int32 { 1113 | if interval > 5000 { 1114 | interval = 5000 1115 | } else if interval < 10 { 1116 | interval = 10 1117 | } 1118 | kcp.interval = uint32(interval) 1119 | return 0 1120 | } 1121 | 1122 | func Ikcp_nodelay(kcp *Ikcpcb, nodelay, interval, resend, nc int32) int32 { 1123 | if nodelay >= 0 { 1124 | kcp.nodelay = uint32(nodelay) 1125 | if nodelay != 0 { 1126 | kcp.rx_minrto = IKCP_RTO_NDL 1127 | } else { 1128 | kcp.rx_minrto = IKCP_RTO_MIN 1129 | } 1130 | } 1131 | if interval >= 0 { 1132 | if interval > 5000 { 1133 | interval = 5000 1134 | } else if interval < 10 { 1135 | interval = 10 1136 | } 1137 | kcp.interval = uint32(interval) 1138 | } 1139 | if resend >= 0 { 1140 | kcp.fastresend = resend 1141 | } 1142 | if nc >= 0 { 1143 | kcp.nocwnd = nc 1144 | } 1145 | return 0 1146 | } 1147 | 1148 | func Ikcp_wndsize(kcp *Ikcpcb, sndwnd, rcvwnd int32) int32 { 1149 | if kcp != nil { 1150 | if sndwnd > 0 { 1151 | kcp.snd_wnd = uint32(sndwnd) 1152 | } 1153 | if rcvwnd > 0 { 1154 | kcp.rcv_wnd = uint32(rcvwnd) 1155 | } 1156 | } 1157 | return 0 1158 | } 1159 | 1160 | func Ikcp_waitsnd(kcp *Ikcpcb) int32 { 1161 | return int32(kcp.nsnd_buf + kcp.nsnd_que) 1162 | } 1163 | -------------------------------------------------------------------------------- /ikcp/ikcp_h.go: -------------------------------------------------------------------------------- 1 | package ikcp 2 | import "container/list" 3 | //===================================================================== 4 | // 5 | // KCP - A Better ARQ Protocol Implementation 6 | // skywind3000 (at) gmail.com, 2010-2011 7 | // 8 | // Features: 9 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. 10 | // + Maximum RTT reduce three times vs tcp. 11 | // + Lightweight, distributed as a single source file. 12 | // 13 | //===================================================================== 14 | //--------------------------------------------------------------------- 15 | // IKCPCB 16 | //--------------------------------------------------------------------- 17 | type IKCPCB struct { 18 | conv, mtu, mss, state uint32 19 | snd_una, snd_nxt, rcv_nxt uint32 20 | ts_recent, ts_lastack, ssthresh uint32 21 | rx_rttval, rx_srtt, rx_rto, rx_minrto uint32 22 | snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe uint32 23 | current, interval, ts_flush, xmit uint32 24 | nrcv_buf, nsnd_buf uint32 25 | nrcv_que, nsnd_que uint32 26 | nodelay, updated uint32 27 | ts_probe, probe_wait uint32 28 | dead_link, incr uint32 29 | snd_queue, rcv_queue, snd_buf, rcv_buf *list.List 30 | acklist []uint32 31 | ackcount uint32 32 | ackblock uint32 33 | user interface{} 34 | buffer []byte 35 | fastresend int32 36 | nocwnd int32 37 | logmask int32 38 | writelog func (log []byte, kcp *Ikcpcb, user []byte) 39 | 40 | Output func (buf []byte, _len int32, kcp *Ikcpcb, user interface{}) (int32) 41 | } 42 | 43 | 44 | type Ikcpcb struct {IKCPCB} 45 | 46 | const IKCP_LOG_OUTPUT = 1 47 | const IKCP_LOG_INPUT = 2 48 | const IKCP_LOG_SEND = 4 49 | const IKCP_LOG_RECV = 8 50 | const IKCP_LOG_IN_DATA = 16 51 | const IKCP_LOG_IN_ACK = 32 52 | const IKCP_LOG_IN_PROBE = 64 53 | const IKCP_LOG_IN_WIN = 128 54 | const IKCP_LOG_OUT_DATA =256 55 | const IKCP_LOG_OUT_ACK = 512 56 | const IKCP_LOG_OUT_PROBE = 1024 57 | const IKCP_LOG_OUT_WINS = 2048 58 | -------------------------------------------------------------------------------- /ikcp/ikcp_test.go: -------------------------------------------------------------------------------- 1 | package ikcp 2 | import "encoding/binary" 3 | import "bytes" 4 | import "time" 5 | import "fmt" 6 | import "testing" 7 | //===================================================================== 8 | //===================================================================== 9 | 10 | // 模拟网络 11 | var vnet *LatencySimulator 12 | 13 | // 模拟网络:模拟发送一个 udp包 14 | func udp_output(buf []byte, _len int32, kcp *Ikcpcb, user interface{}) int32 { 15 | arr := (user).([]byte) 16 | var id uint32 = uint32(arr[0]) 17 | //println("send!!!!", id, _len) 18 | if vnet.send(int(id), buf, int(_len)) != 1 { 19 | //println("wocao !!!", id, _len) 20 | } 21 | return 0 22 | } 23 | 24 | // 测试用例 25 | func test(mode int) { 26 | // 创建模拟网络:丢包率10%,Rtt 60ms~125ms 27 | vnet = &LatencySimulator{} 28 | vnet.Init(10, 60, 125, 1000) 29 | 30 | // 创建两个端点的 kcp对象,第一个参数 conv是会话编号,同一个会话需要相同 31 | // 最后一个是 user参数,用来传递标识 32 | a := []byte {0} 33 | b := []byte {1} 34 | kcp1 := Ikcp_create(0x11223344, a) 35 | kcp2 := Ikcp_create(0x11223344, b) 36 | 37 | // 设置kcp的下层输出,这里为 udp_output,模拟udp网络输出函数 38 | kcp1.Output = udp_output 39 | kcp2.Output = udp_output 40 | 41 | current := uint32(iclock()) 42 | slap := current + 20 43 | index := 0 44 | next := 0 45 | var sumrtt uint32 = 0 46 | count := 0 47 | maxrtt := 0 48 | 49 | // 配置窗口大小:平均延迟200ms,每20ms发送一个包, 50 | // 而考虑到丢包重发,设置最大收发窗口为128 51 | Ikcp_wndsize(kcp1, 128, 128) 52 | Ikcp_wndsize(kcp2, 128, 128) 53 | 54 | // 判断测试用例的模式 55 | if (mode == 0) { 56 | // 默认模式 57 | Ikcp_nodelay(kcp1, 0, 10, 0, 0) 58 | Ikcp_nodelay(kcp2, 0, 10, 0, 0) 59 | } else if (mode == 1) { 60 | // 普通模式,关闭流控等 61 | Ikcp_nodelay(kcp1, 0, 10, 0, 1) 62 | Ikcp_nodelay(kcp2, 0, 10, 0, 1) 63 | } else { 64 | // 启动快速模式 65 | // 第二个参数 nodelay-启用以后若干常规加速将启动 66 | // 第三个参数 interval为内部处理时钟,默认设置为 10ms 67 | // 第四个参数 resend为快速重传指标,设置为2 68 | // 第五个参数 为是否禁用常规流控,这里禁止 69 | Ikcp_nodelay(kcp1, 1, 10, 2, 1) 70 | Ikcp_nodelay(kcp2, 1, 10, 2, 1) 71 | } 72 | 73 | 74 | var buffer []byte = make([]byte, 2000) 75 | var hr int32 76 | 77 | ts1 := iclock() 78 | 79 | for { 80 | time.Sleep(100* time.Millisecond) 81 | current = uint32(iclock()) 82 | Ikcp_update(kcp1,uint32(iclock())) 83 | Ikcp_update(kcp2, uint32(iclock())) 84 | 85 | // 每隔 20ms,kcp1发送数据 86 | for ; current >= slap; slap += 20 { 87 | buf := new(bytes.Buffer) 88 | binary.Write(buf, binary.LittleEndian, uint32(index)) 89 | index++ 90 | binary.Write(buf, binary.LittleEndian, uint64(current)) 91 | // 发送上层协议包 92 | Ikcp_send(kcp1, buf.Bytes(), 8) 93 | //println("now", iclock()) 94 | } 95 | 96 | // 处理虚拟网络:检测是否有udp包从p1->p2 97 | for { 98 | hr = vnet.recv(1, buffer, 2000) 99 | if (hr < 0) { 100 | break 101 | } 102 | // 如果 p2收到udp,则作为下层协议输入到kcp2 103 | Ikcp_input(kcp2, buffer, int(hr)) 104 | } 105 | 106 | // 处理虚拟网络:检测是否有udp包从p2->p1 107 | for { 108 | hr = vnet.recv(0, buffer, 2000) 109 | if (hr < 0) { break } 110 | // 如果 p1收到udp,则作为下层协议输入到kcp1 111 | Ikcp_input(kcp1, buffer, int(hr)) 112 | //println("@@@@", hr, r) 113 | } 114 | 115 | // kcp2接收到任何包都返回回去 116 | for { 117 | hr = Ikcp_recv(kcp2, buffer, 10) 118 | // 没有收到包就退出 119 | if (hr < 0) { break } 120 | // 如果收到包就回射 121 | buf := bytes.NewReader(buffer) 122 | var sn uint32 123 | binary.Read(buf, binary.LittleEndian, &sn) 124 | Ikcp_send(kcp2, buffer, int(hr)) 125 | } 126 | 127 | // kcp1收到kcp2的回射数据 128 | for { 129 | hr = Ikcp_recv(kcp1, buffer, 10) 130 | buf := bytes.NewReader(buffer) 131 | // 没有收到包就退出 132 | if (hr < 0) { break } 133 | var sn uint32 134 | var ts, rtt uint32 135 | binary.Read(buf, binary.LittleEndian, &sn) 136 | binary.Read(buf, binary.LittleEndian, &ts) 137 | rtt = uint32(current) - ts 138 | 139 | if (sn != uint32(next)) { 140 | // 如果收到的包不连续 141 | //for i:=0;i<8 ;i++ { 142 | //println("---", i, buffer[i]) 143 | //} 144 | println("ERROR sn ", count, "<->", next, sn) 145 | return 146 | } 147 | 148 | next++ 149 | sumrtt += rtt 150 | count++ 151 | if (rtt > uint32(maxrtt)) { maxrtt = int(rtt) } 152 | 153 | println("[RECV] mode=", mode, " sn=",sn, " rtt=", rtt) 154 | } 155 | if (next > 100) { break } 156 | } 157 | 158 | ts1 = iclock() - ts1 159 | 160 | names := []string{ "default", "normal", "fast" } 161 | fmt.Printf("%s mode result (%dms):\n", names[mode], ts1) 162 | fmt.Printf("avgrtt=%d maxrtt=%d\n", int(sumrtt / uint32(count)), maxrtt) 163 | } 164 | 165 | func TestNetwork(t *testing.T) { 166 | test(0); // 默认模式,类似 TCP:正常模式,无快速重传,常规流控 167 | test(1); // 普通模式,关闭流控等 168 | test(2); // 快速模式,所有开关都打开,且关闭流控 169 | } 170 | 171 | /* 172 | default mode result (20917ms): 173 | avgrtt=740 maxrtt=1507 174 | 175 | normal mode result (20131ms): 176 | avgrtt=156 maxrtt=571 177 | 178 | fast mode result (20207ms): 179 | avgrtt=138 maxrtt=392 180 | */ 181 | 182 | -------------------------------------------------------------------------------- /ikcp/ikcp_test_h.go: -------------------------------------------------------------------------------- 1 | package ikcp 2 | import "container/list" 3 | import "math/rand" 4 | import "time" 5 | 6 | func iclock() int32 { 7 | return int32((time.Now().UnixNano()/1000000) & 0xffffffff) 8 | } 9 | type DelayPacket struct { 10 | _ptr []byte 11 | _size int 12 | _ts int32 13 | } 14 | 15 | func (p *DelayPacket) Init(size int, src []byte) { 16 | p._ptr = make([]byte, size) 17 | p._size = size 18 | copy(p._ptr, src[:size]) 19 | } 20 | 21 | func (p *DelayPacket) ptr() []byte { return p._ptr } 22 | func (p *DelayPacket) size() int { return p._size } 23 | func (p *DelayPacket) ts() int32 { return p._ts } 24 | func (p *DelayPacket) setts(ts int32) { p._ts = ts} 25 | 26 | type DelayTunnel struct {*list.List} 27 | type Random *rand.Rand 28 | type LatencySimulator struct { 29 | current int32 30 | lostrate, rttmin, rttmax, nmax int 31 | p12 DelayTunnel 32 | p21 DelayTunnel 33 | r12 *rand.Rand 34 | r21 *rand.Rand 35 | } 36 | 37 | // lostrate: 往返一周丢包率的百分比,默认 10% 38 | // rttmin:rtt最小值,默认 60 39 | // rttmax:rtt最大值,默认 125 40 | //func (p *LatencySimulator)Init(int lostrate = 10, int rttmin = 60, int rttmax = 125, int nmax = 1000): 41 | func (p *LatencySimulator)Init(lostrate, rttmin ,rttmax, nmax int) { 42 | p.r12 = rand.New(rand.NewSource(9)) 43 | p.r21 = rand.New(rand.NewSource(99)) 44 | p.p12 = DelayTunnel{list.New()} 45 | p.p21 = DelayTunnel{list.New()} 46 | p.current = iclock() 47 | p.lostrate = lostrate / 2; // 上面数据是往返丢包率,单程除以2 48 | p.rttmin = rttmin / 2 49 | p.rttmax = rttmax / 2 50 | p.nmax = nmax 51 | } 52 | // 发送数据 53 | // peer - 端点0/1,从0发送,从1接收;从1发送从0接收 54 | func (p *LatencySimulator) send(peer int, data []byte, size int) int { 55 | rnd := 0 56 | if (peer == 0) { 57 | rnd = p.r12.Intn(100) 58 | } else { 59 | rnd = p.r21.Intn(100) 60 | } 61 | //println("!!!!!!!!!!!!!!!!!!!!", rnd, p.lostrate, peer) 62 | if (rnd < p.lostrate) { return 0} 63 | pkt := &DelayPacket{} 64 | pkt.Init(size, data) 65 | p.current = iclock() 66 | delay := p.rttmin 67 | if (p.rttmax > p.rttmin) { 68 | delay += rand.Int() % (p.rttmax - p.rttmin) 69 | } 70 | pkt.setts(p.current + int32(delay)) 71 | if (peer == 0) { 72 | p.p12.PushBack(pkt) 73 | } else { 74 | p.p21.PushBack(pkt) 75 | } 76 | return 1 77 | } 78 | 79 | // 接收数据 80 | func (p *LatencySimulator)recv(peer int, data []byte, maxsize int) int32 { 81 | var it *list.Element 82 | if (peer == 0) { 83 | it = p.p21.Front() 84 | if (p.p21.Len() == 0){ return -1} 85 | } else { 86 | it = p.p12.Front() 87 | if (p.p12.Len() == 0){ return -1} 88 | } 89 | pkt := it.Value.(*DelayPacket) 90 | p.current = iclock() 91 | if (p.current < pkt.ts()) { return -2 } 92 | if (maxsize < pkt.size()) { return -3 } 93 | if (peer == 0) { 94 | p.p21.Remove(it) 95 | } else { 96 | p.p12.Remove(it) 97 | } 98 | maxsize = pkt.size() 99 | copy(data, pkt.ptr()[:maxsize]) 100 | return int32(maxsize) 101 | } 102 | -------------------------------------------------------------------------------- /keys/demo: -------------------------------------------------------------------------------- 1 | # server 2 | go run server.go -ssl -cert keys/server.crt -key keys/server.key 3 | # client A 4 | go run client.go -reg v -local :22 -ssl 5 | # client B 6 | go run client.go -link v -local :8888 -ssl 7 | -------------------------------------------------------------------------------- /keys/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdjCCAl4CCQDCMrV4+C2NPTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJD 3 | TjERMA8GA1UECBMIWmhlSmlhbmcxETAPBgNVBAcTCEhhbmdaaG91MQ0wCwYDVQQK 4 | EwROb25lMQ0wCwYDVQQLEwROb25lMQ0wCwYDVQQDEwR2emV4MRswGQYJKoZIhvcN 5 | AQkBFgx2emV4QDE2My5jb20wHhcNMTQwMTAyMTY1NTQxWhcNMjMxMjMxMTY1NTQx 6 | WjB9MQswCQYDVQQGEwJDTjERMA8GA1UECBMIWmhlSmlhbmcxETAPBgNVBAcTCEhh 7 | bmdaaG91MQ0wCwYDVQQKEwROb25lMQ0wCwYDVQQLEwROb25lMQ0wCwYDVQQDEwR2 8 | emV4MRswGQYJKoZIhvcNAQkBFgx2emV4QDE2My5jb20wggEiMA0GCSqGSIb3DQEB 9 | AQUAA4IBDwAwggEKAoIBAQCd9TJzRMsSWfgfulF97a76kP5uQc/StVQZr34DSbMp 10 | ueFj6P1zG2TmEdIUFVh1Y34hAhhPzYSAdPWnpPjvQZ5pQFkr1b701qkOTJ4dYSc0 11 | cKv9/VxFYL0OmBaBPlNsIFJbEepEFww9zi3BGpSq1VyHXKA0quU/ToqfQaaKitR8 12 | U+ESgSxClogJxBFf2CnWhtHsMDUzizOTlH/CbZoYF8ezXTTN7MgvfJe4RT4BT6/i 13 | jwdjXJ/dm/eJY30VG7Eow9BVgG2yRH2blBd6EnzuyMwXKlBn78PYeX0VNrls2LP/ 14 | owFR86XFi0ZNKI0fN443APNEHndisH5npCVmc5yjfVnFAgMBAAEwDQYJKoZIhvcN 15 | AQEFBQADggEBABDxL6txncq0dMt2viMxeqmSOvKNjfSZcDvGOhJKFE5YRVntd0yf 16 | vHtLJIrzABapCsMoL+nCo01i2DRrOzhb8KOHrLDcp0WWB2/YUAg/bWgmoLXhwQ8L 17 | 1DOxF+6wQXpFPcWr9RybAmDReat0SO8pTHT4C+el/qW9iJZG0OZJ/vQQ7FETug6G 18 | UBenSyjWZsZM3D3rJdOUQZzUDc3HfNRgl11TJW9/79y1HT5UJysibgDFv083m3uN 19 | g9qMVhs3ORI6+TSUoRM3KIejvKPpl78j17taTAra4V3XC2ua01nT1WPw4wWy7U/p 20 | zwzaPKQqHZG8y+P1CqQ9l6C5pUWbtoinWwU= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /keys/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAnfUyc0TLEln4H7pRfe2u+pD+bkHP0rVUGa9+A0mzKbnhY+j9 3 | cxtk5hHSFBVYdWN+IQIYT82EgHT1p6T470GeaUBZK9W+9NapDkyeHWEnNHCr/f1c 4 | RWC9DpgWgT5TbCBSWxHqRBcMPc4twRqUqtVch1ygNKrlP06Kn0GmiorUfFPhEoEs 5 | QpaICcQRX9gp1obR7DA1M4szk5R/wm2aGBfHs100zezIL3yXuEU+AU+v4o8HY1yf 6 | 3Zv3iWN9FRuxKMPQVYBtskR9m5QXehJ87sjMFypQZ+/D2Hl9FTa5bNiz/6MBUfOl 7 | xYtGTSiNHzeONwDzRB53YrB+Z6QlZnOco31ZxQIDAQABAoIBAC94E2w+nURrkzx9 8 | sqXEexRD5KJf1sO8yIRbj+QPDu+YM9rhluEzt1PIwThV3dqRRsNGrOW2nn44J+b0 9 | lfbY4nFA7EtXFebgZ7erYMz1eGGzBozyyfrzgn1TSVgX0dwprnY94QgLYRtlzaw9 10 | f7YDHqZzOJX7UZ9/y/rmMNVruUWPOqeQmf/YqL0aOK2AAu/+J5zB9nRJ7xHXsfDs 11 | 1HxgCiiLB/ubIMGt4g6RHr1JW61hXbSp+RP2x4xmXY0RSrIGOklxmcxVJM17kRWL 12 | 5DtQ6TqB0vL5or9WuwDMOtjJ6kKa31QmFv4X/e7c9JxzWZPDducquH1OtgRal9by 13 | e5/fjYECgYEAzu11eY3rx8fK2bqU7OwXSNMPVdtT4LaWjz0qqK04jn2yGXVnZMbC 14 | 3qk1+WASKp/bz9oZIiTCPJY32Re8Xa0Z8jC2UjQI/zs+nkk+DMJxZi34qIwdBAUs 15 | kSsU1YDVZ/uNN8Ne2jHVSFhB+q1bSqYn00zAUnXHMdITzWtX9+HSxaUCgYEAw2rK 16 | c+EmvxdJsTVkF35rVEmas7gjUJQWhaDLce2gk/6hpnhxeGdaM87l6rCaWrUwFvmp 17 | 16usEugSNCW74GSHF+zSEnkjSgmNjalWMHdaL4Vam29bxGauXWHXzhkAuP/R6VZb 18 | xSqe14KBN/9zV4eUclPrhHLyRtaSsZ9jaY7XSaECgYEAjdmeVEuDvBor5oHPIXLe 19 | q19z/ckygLLqFxs5KRRF1vg3pNgknqLaZ0Tb6S2AhpO7XYsG+67zMBFSC5IH4LeP 20 | N8VI1qUxHbYBQQS4corArekJspEKOv1JBeLgj1OX94Yr0nQti6npqYRHBo7cLBom 21 | 5uSQWna+rFp0lLbnQNgfYrkCgYBBNu9PUrEqjmwCXo4DpSXOYC3nkij+6BzIBYw/ 22 | fgzbtttdgc5kXyFkLsTd2If6g4MD99etaNYpDReeHYWgcgv/eQH2NkUHnT6HKx7f 23 | V8VaHIfLXZNjbNz9vvxVBFUQaBc2ZqKDnyCZTS0wceidKlro+Iadkfq2lz8nVcjI 24 | XmoMYQKBgQC9+51iodDemCmlMNnOU7e41dagrTiPCpuVvNm/AuWA5B/JKXWDatnV 25 | hnJG7PFwlcaeuqvteG2fQ3LVniuiSSXWrUmsE4mH4aqNj+1anop6FzejyNJeQEmM 26 | ielf2r1Ao0TKQJKWSnuPLDC3YuSU6GJZDBYYg+aEUxl/1tyE25J5UA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /nat/connection.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import "github.com/vzex/dog-tunnel/ikcp" 4 | import "github.com/klauspost/reedsolomon" 5 | import "github.com/vzex/zappy" 6 | 7 | import ( 8 | "errors" 9 | "flag" 10 | "log" 11 | //"math/rand" 12 | "net" 13 | "time" 14 | ) 15 | 16 | const ( 17 | Ping byte = 1 18 | Data byte = 2 19 | 20 | FecData byte = 1 21 | FecOver byte = 2 22 | ) 23 | 24 | var bDebug = flag.Bool("debug", false, "whether show nat pipe debug msg") 25 | var bCompress = flag.Bool("compress", false, "whether compress data, two sides should be same") 26 | 27 | const dataLimit = 4000 28 | const SendBuffSize = 2000 //after kcp_send , 3k+, after compress, 3k+ * 1.5+ ,,this size is limited by bufio.Scanner 29 | const CacheBuffSize = SendBuffSize * 1.5 30 | 31 | func debug(args ...interface{}) { 32 | if *bDebug { 33 | log.Println(args...) 34 | } 35 | } 36 | func iclock() int32 { 37 | return int32((time.Now().UnixNano() / 1000000) & 0xffffffff) 38 | } 39 | 40 | func udp_output(buf []byte, _len int32, kcp *ikcp.Ikcpcb, user interface{}) int32 { 41 | //debug("write!!!", _len) 42 | c := user.(*Conn) 43 | c.output(buf[:_len]) 44 | return 0 45 | } 46 | 47 | type cache struct { 48 | b []byte 49 | l int 50 | c chan int 51 | } 52 | 53 | type fecInfo struct { 54 | bytes [][]byte 55 | overTime int64 56 | } 57 | 58 | type Conn struct { 59 | conn *net.UDPConn 60 | local, remote net.Addr 61 | closed bool 62 | quit chan bool 63 | sendChan chan string 64 | checkCanWrite chan chan bool 65 | readChan chan cache 66 | kcp *ikcp.Ikcpcb 67 | 68 | tmp []byte 69 | tmp2 []byte 70 | encode, decode func([]byte) []byte 71 | overTime int64 72 | 73 | fecDataShards int 74 | fecParityShards int 75 | fecW *reedsolomon.Encoder 76 | fecR *reedsolomon.Encoder 77 | fecRCacheTbl map[uint]*fecInfo 78 | fecWCacheTbl *fecInfo 79 | fecWriteId uint //uint16 80 | fecSendC uint 81 | fecSendL int 82 | fecRecvId uint 83 | 84 | compressCache []byte 85 | } 86 | 87 | type KcpSetting struct { 88 | Nodelay int32 89 | Interval int32 //not for set 90 | Resend int32 91 | Nc int32 92 | 93 | Sndwnd int32 94 | Rcvwnd int32 95 | 96 | Mtu int32 97 | 98 | //Xor string 99 | } 100 | 101 | func DefaultKcpSetting() *KcpSetting { 102 | return &KcpSetting{Nodelay: 1, Interval: 10, Resend: 2, Nc: 1, Sndwnd: 1024, Rcvwnd: 1024, Mtu: 1400} 103 | } 104 | 105 | func newConn(sock *net.UDPConn, local, remote net.Addr, id int) *Conn { 106 | sock.SetDeadline(time.Time{}) 107 | conn := &Conn{conn: sock, local: local, remote: remote, closed: false, quit: make(chan bool), tmp: make([]byte, CacheBuffSize*2), tmp2: make([]byte, CacheBuffSize), sendChan: make(chan string, 10), checkCanWrite: make(chan chan bool), readChan: make(chan cache), overTime: time.Now().Unix() + 30, fecWriteId: 0, fecSendC: 0} 108 | debug("create", id) 109 | conn.kcp = ikcp.Ikcp_create(uint32(id), conn) 110 | conn.kcp.Output = udp_output 111 | conn.SetKcp(DefaultKcpSetting()) 112 | if *bCompress { 113 | conn.compressCache = make([]byte, CacheBuffSize*2) 114 | } 115 | return conn 116 | } 117 | 118 | func (c *Conn) SetKcp(setting *KcpSetting) { 119 | ikcp.Ikcp_wndsize(c.kcp, setting.Sndwnd, setting.Rcvwnd) 120 | ikcp.Ikcp_nodelay(c.kcp, setting.Nodelay, setting.Interval, setting.Resend, setting.Nc) 121 | ikcp.Ikcp_setmtu(c.kcp, setting.Mtu) 122 | } 123 | func (c *Conn) SetFec(DataShards, ParityShards int) (er error) { 124 | c.fecDataShards = DataShards 125 | c.fecParityShards = ParityShards 126 | var fec reedsolomon.Encoder 127 | fec, er = reedsolomon.New(DataShards, ParityShards) 128 | if er != nil { 129 | return 130 | } 131 | c.fecR = &fec 132 | fec, er = reedsolomon.New(DataShards, ParityShards) 133 | if er == nil { 134 | c.fecRCacheTbl = make(map[uint]*fecInfo) 135 | c.fecWCacheTbl = nil 136 | c.fecW = &fec 137 | } else { 138 | c.fecR = nil 139 | } 140 | return 141 | } 142 | 143 | func (c *Conn) Run() { 144 | go c.onUpdate() 145 | } 146 | 147 | func (c *Conn) onUpdate() { 148 | recvChan := make(chan []byte) 149 | go func() { 150 | for { 151 | n, addr, err := c.conn.ReadFrom(c.tmp) 152 | //debug("want read!", n, addr, err) 153 | // Generic non-address related errors. 154 | if addr == nil && err != nil { 155 | if err.(net.Error).Timeout() { 156 | continue 157 | } else { 158 | break 159 | } 160 | } 161 | var b []byte 162 | if *bCompress { 163 | _b, _er := zappy.Decode(nil, c.tmp[:n]) 164 | if _er != nil { 165 | log.Println("decompress fail", _er.Error()) 166 | go c.Close() 167 | } 168 | //log.Println("decompress", len(_b), n) 169 | b = _b 170 | } else { 171 | b = make([]byte, n) 172 | copy(b, c.tmp[:n]) 173 | } 174 | select { 175 | case recvChan <- b: 176 | case <-c.quit: 177 | return 178 | } 179 | } 180 | }() 181 | ping := make(chan struct{}) 182 | pingC := 0 183 | 184 | updateChan := time.NewTicker(20 * time.Millisecond) 185 | waitList := [](chan bool){} 186 | recoverChan := make(chan bool) 187 | var waitRecvCache *cache 188 | go func() { 189 | select { 190 | case ping <- struct{}{}: 191 | case <-c.quit: 192 | } 193 | 194 | }() 195 | processRecv := func() { 196 | if waitRecvCache != nil { 197 | ca := *waitRecvCache 198 | const buffSize = CacheBuffSize 199 | for { 200 | hr := ikcp.Ikcp_recv(c.kcp, c.tmp2, buffSize) 201 | 202 | if hr > 0 { 203 | action := c.tmp2[0] 204 | if action == Data { 205 | waitRecvCache = nil 206 | copy(ca.b, c.tmp2[1:hr]) 207 | hr-- 208 | if c.decode != nil { 209 | d := c.decode(ca.b[:hr]) 210 | copy(ca.b, d) 211 | hr = int32(len(d)) 212 | } 213 | select { 214 | case ca.c <- int(hr): 215 | case <-c.quit: 216 | } 217 | } else { 218 | continue 219 | } 220 | } else { 221 | } 222 | break 223 | } 224 | } 225 | } 226 | out: 227 | for { 228 | select { 229 | case <-ping: 230 | pingC++ 231 | if pingC >= 4 { 232 | pingC = 0 233 | go c.Ping() 234 | if c.fecR != nil { 235 | curr := time.Now().Unix() 236 | for id, info := range c.fecRCacheTbl { 237 | if curr >= info.overTime { 238 | delete(c.fecRCacheTbl, id) 239 | if c.fecRecvId <= id { 240 | c.fecRecvId = id + 1 241 | } 242 | //log.Println("timeout after del", id, len(c.fecRCacheTbl)) 243 | } 244 | } 245 | } 246 | } 247 | if time.Now().Unix() > c.overTime { 248 | log.Println("overtime close", c.LocalAddr().String(), c.RemoteAddr().String()) 249 | go c.Close() 250 | } else { 251 | time.AfterFunc(300*time.Millisecond, func() { 252 | select { 253 | case ping <- struct{}{}: 254 | case <-c.quit: 255 | } 256 | }) 257 | } 258 | case cache := <-c.readChan: 259 | for { 260 | const buffSize = CacheBuffSize 261 | hr := ikcp.Ikcp_recv(c.kcp, c.tmp2, buffSize) 262 | if hr > 0 { 263 | action := c.tmp2[0] 264 | if action == Data { 265 | copy(cache.b, c.tmp2[1:hr]) 266 | hr-- 267 | if c.decode != nil { 268 | d := c.decode(cache.b[:hr]) 269 | copy(cache.b, d) 270 | hr = int32(len(d)) 271 | } 272 | select { 273 | case cache.c <- int(hr): 274 | case <-c.quit: 275 | } 276 | } else { 277 | continue 278 | } 279 | } else { 280 | waitRecvCache = &cache 281 | } 282 | break 283 | } 284 | case b := <-recvChan: 285 | c.overTime = time.Now().Unix() + 30 286 | if c.fecR != nil { 287 | if len(b) <= 7 { 288 | break 289 | } 290 | id := uint(int(b[2]) | (int(b[3]) << 8) | (int(b[4]) << 16) | (int(b[5]) << 24)) 291 | var seq = uint(b[6]) 292 | _len := int(b[0]) | (int(b[1]) << 8) 293 | //log.Println("recv chan", len(b), _len, id, seq, c.fecRecvId) 294 | if id < c.fecRecvId { 295 | //log.Println("drop id for noneed", id, seq) 296 | break 297 | } 298 | if seq < uint(c.fecDataShards) { 299 | ikcp.Ikcp_input(c.kcp, b[7:], _len) 300 | //log.Println("direct input udp", id, seq, _len) 301 | } 302 | if seq >= uint(c.fecDataShards+c.fecParityShards) { 303 | log.Println("-ds and -ps must be equal on both sides") 304 | go c.Close() 305 | break 306 | } 307 | tbl, have := c.fecRCacheTbl[id] 308 | if !have { 309 | tbl = &fecInfo{make([][]byte, c.fecDataShards+c.fecParityShards), time.Now().Unix() + 15} 310 | c.fecRCacheTbl[id] = tbl 311 | } 312 | if tbl.bytes[seq] != nil { 313 | //dup, drop 314 | break 315 | } else { 316 | tbl.bytes[seq] = b 317 | } 318 | count := 0 319 | reaL := 0 320 | for _, v := range tbl.bytes { 321 | if v != nil { 322 | count++ 323 | if reaL < len(v) { 324 | reaL = len(v) 325 | } 326 | } 327 | } 328 | if count >= c.fecDataShards { 329 | markTbl := make(map[int]bool, len(tbl.bytes)) 330 | for _seq, _b := range tbl.bytes { 331 | if _b != nil { 332 | markTbl[_seq] = true 333 | } 334 | } 335 | bNeedRebuild := false 336 | for i, v := range tbl.bytes { 337 | if i >= c.fecDataShards { 338 | bNeedRebuild = true 339 | } 340 | if v != nil { 341 | if len(v) < reaL { 342 | _b := make([]byte, reaL) 343 | copy(_b, v) 344 | tbl.bytes[i] = _b 345 | } 346 | } 347 | } 348 | if bNeedRebuild { 349 | er := (*c.fecR).Reconstruct(tbl.bytes) 350 | if er != nil { 351 | //log.Println("Reconstruct fail", er.Error()) 352 | } else { 353 | //log.Println("Reconstruct ok, input", id) 354 | for i := 0; i < c.fecDataShards; i++ { 355 | if _, have := markTbl[i]; !have { 356 | _len := int(tbl.bytes[i][0]) | (int(tbl.bytes[i][1]) << 8) 357 | ikcp.Ikcp_input(c.kcp, tbl.bytes[i][7:], int(_len)) 358 | //log.Println("fec input for mark ok", i, id, _len) 359 | } 360 | } 361 | } 362 | } 363 | delete(c.fecRCacheTbl, id) 364 | //log.Println("after del", id, len(c.fecRCacheTbl)) 365 | if c.fecRecvId <= id { 366 | c.fecRecvId = id + 1 367 | } 368 | } 369 | } else { 370 | ikcp.Ikcp_input(c.kcp, b, len(b)) 371 | } 372 | processRecv() 373 | case <-recoverChan: 374 | for _, r := range waitList { 375 | log.Println("recover writing data") 376 | select { 377 | case r <- true: 378 | case <-c.quit: 379 | } 380 | } 381 | waitList = [](chan bool){} 382 | case s := <-c.checkCanWrite: 383 | if !c.closed { 384 | if ikcp.Ikcp_waitsnd(c.kcp) > dataLimit { 385 | log.Println("wait for data limit") 386 | waitList = append(waitList, s) 387 | var f func() 388 | f = func() { 389 | n := ikcp.Ikcp_waitsnd(c.kcp) 390 | if n <= dataLimit/2 { 391 | select { 392 | case <-c.quit: 393 | log.Println("recover writing data quit") 394 | case recoverChan <- true: 395 | } 396 | } else { 397 | time.AfterFunc(40*time.Millisecond, f) 398 | } 399 | } 400 | time.AfterFunc(20*time.Millisecond, f) 401 | log.Println("wait for data limitover") 402 | } else { 403 | select { 404 | case s <- true: 405 | case <-c.quit: 406 | } 407 | } 408 | } 409 | case s := <-c.sendChan: 410 | b := []byte(s) 411 | ikcp.Ikcp_send(c.kcp, b, len(b)) 412 | case <-updateChan.C: 413 | if c.closed { 414 | break out 415 | } 416 | ikcp.Ikcp_update(c.kcp, uint32(iclock())) 417 | case <-c.quit: 418 | break out 419 | } 420 | } 421 | updateChan.Stop() 422 | } 423 | func (c *Conn) Read(b []byte) (int, error) { 424 | if !c.closed { 425 | var n = 0 426 | wc := cache{b, 0, make(chan int)} 427 | select { 428 | case c.readChan <- wc: 429 | select { 430 | case n = <-wc.c: 431 | case <-c.quit: 432 | n = 0 433 | } 434 | case <-c.quit: 435 | n = 0 436 | } 437 | return n, nil 438 | } 439 | return 0, errors.New("force quit") 440 | } 441 | 442 | func (c *Conn) writeTo(b []byte) { 443 | if *bCompress { 444 | enc, er := zappy.Encode(c.compressCache, b) 445 | if er != nil { 446 | log.Println("compress error", er.Error()) 447 | go c.Close() 448 | return 449 | } 450 | //log.Println("compress", len(b), len(enc)) 451 | c.conn.WriteTo(enc, c.remote) 452 | } else { 453 | c.conn.WriteTo(b, c.remote) 454 | } 455 | } 456 | func (c *Conn) output(b []byte) { 457 | if c.fecW == nil { 458 | c.writeTo(b) 459 | } else { 460 | id := c.fecWriteId 461 | c.fecSendC++ 462 | 463 | info := c.fecWCacheTbl 464 | if info == nil { 465 | info = &fecInfo{make([][]byte, c.fecDataShards+c.fecParityShards), time.Now().Unix() + 15} 466 | c.fecWCacheTbl = info 467 | } 468 | _b := make([]byte, len(b)+7) 469 | _len := len(b) 470 | _b[0] = byte(_len & 0xff) 471 | _b[1] = byte((_len >> 8) & 0xff) 472 | _b[2] = byte(id & 0xff) 473 | _b[3] = byte((id >> 8) & 0xff) 474 | _b[4] = byte((id >> 16) & 0xff) 475 | _b[5] = byte((id >> 32) & 0xff) 476 | _b[6] = byte(c.fecSendC - 1) 477 | copy(_b[7:], b) 478 | info.bytes[c.fecSendC-1] = _b 479 | if c.fecSendL < len(_b) { 480 | c.fecSendL = len(_b) 481 | } 482 | //if rand.Intn(100) > 20 { 483 | c.writeTo(_b) 484 | //log.Println("output udp id", id, _len, len(_b), _b[6]) 485 | //} 486 | if c.fecSendC >= uint(c.fecDataShards) { 487 | for i := 0; i < c.fecDataShards; i++ { 488 | if c.fecSendL > len(info.bytes[i]) { 489 | __b := make([]byte, c.fecSendL) 490 | copy(__b, info.bytes[i]) 491 | info.bytes[i] = __b 492 | } 493 | } 494 | for i := 0; i < c.fecParityShards; i++ { 495 | info.bytes[i+c.fecDataShards] = make([]byte, c.fecSendL) 496 | } 497 | er := (*c.fecW).Encode(info.bytes) 498 | if er != nil { 499 | log.Println("encode err", er.Error()) 500 | go c.Close() 501 | return 502 | } 503 | for i := c.fecDataShards; i < c.fecDataShards+c.fecParityShards; i++ { 504 | _info := info.bytes[i] 505 | //if rand.Intn(100) > 20 { 506 | c.writeTo(_info) 507 | //_len := int(_info[0]) | (int(_info[1]) << 8) 508 | //log.Println("output udp id fec", id, i, _len, len(_info)) 509 | //} 510 | } 511 | c.fecWCacheTbl = nil 512 | c.fecSendC = 0 513 | c.fecSendL = 0 514 | c.fecWriteId++ 515 | //log.Println("flush id", id) 516 | } 517 | //log.Println("output sn", c.fecWriteId, c.fecSendC, _len) 518 | } 519 | } 520 | 521 | // type 0, check, 1 msg 522 | func (c *Conn) Write(b []byte) (int, error) { 523 | if c.closed { 524 | return 0, errors.New("eof") 525 | } 526 | 527 | if c.encode != nil { 528 | b = c.encode(b) 529 | } 530 | sendL := len(b) 531 | if sendL == 0 { 532 | return 0, nil 533 | } 534 | //log.Println("try write", sendL) 535 | wc := make(chan bool) 536 | select { 537 | case c.checkCanWrite <- wc: 538 | select { 539 | case <-wc: 540 | data := make([]byte, sendL+1) 541 | data[0] = Data 542 | copy(data[1:], b) 543 | c.sendChan <- string(data) 544 | case <-c.quit: 545 | } 546 | case <-c.quit: 547 | } 548 | return sendL, nil 549 | } 550 | 551 | func (c *Conn) Ping() (int, error) { 552 | if c.closed { 553 | return 0, errors.New("eof") 554 | } 555 | wc := make(chan bool) 556 | select { 557 | case c.checkCanWrite <- wc: 558 | select { 559 | case <-wc: 560 | data := []byte{Ping} 561 | c.sendChan <- string(data) 562 | case <-c.quit: 563 | } 564 | case <-c.quit: 565 | } 566 | return 1, nil 567 | } 568 | func (c *Conn) Close() error { 569 | if !c.closed { 570 | c.closed = true 571 | } 572 | if c.quit != nil { 573 | close(c.quit) 574 | c.quit = nil 575 | } 576 | return c.conn.Close() 577 | } 578 | 579 | func (c *Conn) LocalAddr() net.Addr { 580 | return c.local 581 | } 582 | 583 | func (c *Conn) RemoteAddr() net.Addr { 584 | return c.remote 585 | } 586 | 587 | func (c *Conn) SetDeadline(t time.Time) error { 588 | return c.conn.SetDeadline(t) 589 | } 590 | 591 | func (c *Conn) SetReadDeadline(t time.Time) error { 592 | return c.conn.SetReadDeadline(t) 593 | } 594 | 595 | func (c *Conn) SetWriteDeadline(t time.Time) error { 596 | return c.conn.SetWriteDeadline(t) 597 | } 598 | 599 | func (c *Conn) SetCrypt(encode, decode func([]byte) []byte) { 600 | c.encode = encode 601 | c.decode = decode 602 | } 603 | -------------------------------------------------------------------------------- /nat/gather.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | var lanNets = []*net.IPNet{ 13 | {net.IPv4(10, 0, 0, 0), net.CIDRMask(8, 32)}, 14 | {net.IPv4(172, 16, 0, 0), net.CIDRMask(12, 32)}, 15 | {net.IPv4(192, 168, 0, 0), net.CIDRMask(16, 32)}, 16 | {net.ParseIP("fc00"), net.CIDRMask(7, 128)}, 17 | } 18 | 19 | type candidate struct { 20 | Addr *net.UDPAddr 21 | } 22 | 23 | func (c candidate) String() string { 24 | return fmt.Sprintf("%v", c.Addr) 25 | } 26 | 27 | func (c candidate) Equal(c2 candidate) bool { 28 | return c.Addr.IP.Equal(c2.Addr.IP) && c.Addr.Port == c2.Addr.Port 29 | } 30 | 31 | func pruneDups(cs []candidate) []candidate { 32 | ret := make([]candidate, 0, len(cs)) 33 | for _, c := range cs { 34 | unique := true 35 | for _, c2 := range ret { 36 | if c.Equal(c2) { 37 | unique = false 38 | break 39 | } 40 | } 41 | if unique { 42 | ret = append(ret, c) 43 | } 44 | } 45 | return ret 46 | } 47 | 48 | func GatherCandidates(sock *net.UDPConn, outIpList string, udpAddr string) ([]candidate, error) { 49 | laddr := sock.LocalAddr().(*net.UDPAddr) 50 | ret := []candidate{} 51 | switch { 52 | case laddr.IP.IsLoopback(): 53 | return nil, errors.New("Connecting over loopback not supported") 54 | case laddr.IP.IsUnspecified(): 55 | addrs, err := net.InterfaceAddrs() 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | for _, addr := range addrs { 61 | ip, ok := addr.(*net.IPNet) 62 | if ok && ip.IP.IsGlobalUnicast() { 63 | ret = append(ret, candidate{&net.UDPAddr{IP: ip.IP, Port: laddr.Port}}) 64 | } 65 | } 66 | default: 67 | ret = append(ret, candidate{laddr}) 68 | } 69 | 70 | addip := func(ipStr string, port int) { 71 | ip := net.ParseIP(ipStr) 72 | if port == 0 { 73 | port = laddr.Port 74 | } 75 | bHave := false 76 | for _, info := range ret { 77 | if info.Addr.IP.Equal(ip) && info.Addr.Port == port { 78 | bHave = true 79 | break 80 | } 81 | } 82 | if !bHave { 83 | ret = append(ret, candidate{&net.UDPAddr{IP: ip, Port: port}}) 84 | } 85 | } 86 | 87 | if udpAddr != "" { 88 | addr, err := net.ResolveUDPAddr("udp", udpAddr) 89 | if err != nil { 90 | fmt.Println("Can't resolve udp address: ", err) 91 | return nil, err 92 | } 93 | p2pAddr := "" 94 | 95 | for i := 0; i < 5; i++ { 96 | sock.WriteToUDP([]byte("makehole"), addr) 97 | buf := make([]byte, 100) 98 | sock.SetReadDeadline(time.Now().Add(time.Duration(1) * time.Second)) 99 | n, _, err := sock.ReadFromUDP(buf) 100 | if err != nil { 101 | fmt.Println("Can't ReadFromUDP: ", err, addr.String()) 102 | continue 103 | } else { 104 | p2pAddr = string(buf[0:n]) 105 | fmt.Println("read: ", p2pAddr) 106 | break 107 | } 108 | } 109 | 110 | addLen := len(p2pAddr) 111 | if addLen > 0 { 112 | tmparr := strings.Split(p2pAddr, ":") 113 | 114 | var strip string 115 | var strport string 116 | strip, strport = tmparr[0], tmparr[1] 117 | ip := net.ParseIP(strip) 118 | port, _ := strconv.Atoi(strport) 119 | ret = append(ret, candidate{&net.UDPAddr{IP: ip, Port: port}}) 120 | } 121 | } 122 | arr := strings.Split(outIpList, ";") 123 | 124 | for _, ip := range arr { 125 | addip(ip, 0) 126 | } 127 | 128 | /* for _, info := range ret { 129 | log.Println("init ip:", info.Addr.String()) 130 | }*/ 131 | return ret, nil 132 | } 133 | -------------------------------------------------------------------------------- /nat/nat.go: -------------------------------------------------------------------------------- 1 | package nat 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "strings" 7 | "time" 8 | 9 | "github.com/vzex/dog-tunnel/nat/stun" 10 | ) 11 | 12 | func Init(outIpList string, buster bool, id int, udpAddr string) (*AttemptEngine, error) { 13 | sock, err := net.ListenUDP("udp", &net.UDPAddr{}) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | engine := &AttemptEngine{sock: sock, buster: buster, id: id} 19 | if err := engine.init(outIpList, udpAddr); err != nil { 20 | return nil, err 21 | } 22 | return engine, nil 23 | } 24 | 25 | type attempt struct { 26 | candidate 27 | tid []byte 28 | timeout time.Time 29 | success bool // did we get a STUN response from this addr 30 | chosen bool // Has this channel been picked for the connection? 31 | localaddr net.Addr 32 | } 33 | 34 | type AttemptEngine struct { 35 | id int 36 | buster bool 37 | sock *net.UDPConn 38 | attempts []attempt 39 | local_attempts []attempt 40 | p2pconn net.Conn 41 | otherReady bool 42 | status string 43 | Kcp *KcpSetting 44 | D, P int 45 | } 46 | 47 | const probeTimeout = 500 * time.Millisecond 48 | const probeInterval = 100 * time.Millisecond 49 | const decisionTime = 2 * time.Second 50 | 51 | func (e *AttemptEngine) SetOtherAddrList(addrList string) { 52 | arr := strings.Split(addrList, "\n") 53 | e.attempts = make([]attempt, 0) 54 | for _, addrStr := range arr { 55 | if addrStr != "" { 56 | addr, err := net.ResolveUDPAddr("udp", addrStr) 57 | if err != nil { 58 | debug("resolve udp addr err", err.Error()) 59 | } else { 60 | e.attempts = append(e.attempts, attempt{candidate: candidate{Addr: addr}}) 61 | } 62 | } 63 | } 64 | } 65 | 66 | func (e *AttemptEngine) GetAddrList() string { 67 | tmp := "" 68 | for _, attempt := range e.local_attempts { 69 | tmp += attempt.Addr.String() + "\n" 70 | } 71 | return tmp 72 | } 73 | 74 | func (e *AttemptEngine) Fail() { 75 | e.status = "quit" 76 | if e.sock != nil { 77 | //debug("close udp sock") 78 | e.sock.Close() 79 | } 80 | } 81 | 82 | func (e *AttemptEngine) GetConn(f func(), encode, decode func([]byte) []byte) (net.Conn, error) { 83 | var conn net.Conn 84 | var err error 85 | if conn, err = e.run(f, encode, decode); err != nil { 86 | return nil, err 87 | } 88 | return conn, nil 89 | } 90 | 91 | func (e *AttemptEngine) init(outIpList string, udpAddr string) error { 92 | candidates, err := GatherCandidates(e.sock, outIpList, udpAddr) 93 | if err != nil { 94 | return err 95 | } 96 | e.local_attempts = make([]attempt, len(candidates)) 97 | for i := range candidates { 98 | e.local_attempts[i].candidate = candidates[i] 99 | debug("init addr", candidates[i].Addr.String()) 100 | } 101 | 102 | e.sock.SetWriteDeadline(time.Time{}) 103 | 104 | return nil 105 | } 106 | 107 | func (e *AttemptEngine) xmit() (time.Time, error) { 108 | now := time.Now() 109 | var ret time.Time 110 | var err error 111 | 112 | if e.p2pconn != nil { 113 | return ret, nil 114 | } 115 | 116 | for i := range e.attempts { 117 | if e.attempts[i].timeout.Before(now) { 118 | e.attempts[i].timeout = time.Now().Add(probeTimeout) 119 | e.attempts[i].tid, err = stun.RandomTid() 120 | if err != nil { 121 | return time.Time{}, err 122 | } 123 | packet, err := stun.BindRequest(e.attempts[i].tid, e.attempts[i].Addr, nil, false, e.attempts[i].chosen) 124 | if err != nil { 125 | return time.Time{}, err 126 | } 127 | //debug("===send", i,e.attempts[i].Addr.String()) 128 | e.sock.WriteToUDP(packet, e.attempts[i].Addr) 129 | 130 | for j := range e.local_attempts { 131 | if e.local_attempts[j].success { 132 | packet, err := stun.BindRequest(e.attempts[i].tid, e.attempts[i].Addr, nil, false, e.attempts[i].chosen) 133 | if err != nil { 134 | return time.Time{}, err 135 | } 136 | //debug("===send local", i,e.local_attempts[j].localaddr.String()) 137 | e.sock.WriteToUDP(packet, e.local_attempts[j].localaddr.(*net.UDPAddr)) 138 | } 139 | } 140 | } 141 | if ret.IsZero() || e.attempts[i].timeout.Before(ret) { 142 | ret = e.attempts[i].timeout 143 | } 144 | } 145 | return ret, nil 146 | } 147 | 148 | func (e *AttemptEngine) read() error { 149 | if e.status == "over" { 150 | return nil 151 | } 152 | buf := make([]byte, 512) 153 | n, from, err := e.sock.ReadFromUDP(buf) 154 | //println("read", n, from, err) 155 | if err != nil { 156 | if neterr, ok := err.(net.Error); ok && neterr.Timeout() { 157 | return nil 158 | } 159 | return err 160 | } 161 | if string(buf[0:n]) == "makeholeover" { 162 | if e.status == "wait" { 163 | debug("wait client !!!!!!! close") 164 | e.status = "over" 165 | e.sock.WriteToUDP([]byte("makeholeover2"), from) 166 | } 167 | return nil 168 | } 169 | 170 | if string(buf[0:n]) == "makeholeover2" { 171 | if e.status == "wait" { 172 | debug("wait server !!!!!!! close") 173 | e.status = "over" 174 | } 175 | return nil 176 | } 177 | 178 | //debug("========", string(buf[0:n])) 179 | packet, err := stun.ParsePacket(buf[:n], nil) 180 | if err != nil { 181 | return nil 182 | } 183 | 184 | if packet.Method != stun.MethodBinding { 185 | return nil 186 | } 187 | 188 | validAddr := packet.Addr 189 | for i := range e.local_attempts { 190 | my_local_addr := e.local_attempts[i].Addr 191 | //debug("check local",i, validAddr.String(), packet.Class, from.String(), my_local_addr.String()) 192 | if validAddr.String() == my_local_addr.String() { 193 | e.local_attempts[i].localaddr = from 194 | e.local_attempts[i].success = true 195 | //debug("find the addr from request", packet.Class, from.String()) 196 | if packet.Class == stun.ClassRequest { 197 | for j := range e.attempts { 198 | my_remote_addr := e.attempts[j].Addr 199 | response, err := stun.BindResponse(packet.Tid[:], my_remote_addr, nil, false) 200 | if err != nil { 201 | return nil 202 | } 203 | debug("write to succ", from.String(), j, my_remote_addr.String()) 204 | e.sock.WriteToUDP(response, from) 205 | } 206 | } else if packet.Class == stun.ClassSuccess { 207 | if e.p2pconn == nil { 208 | debug("make conn success", from.String(), e.local_attempts[i].localaddr.String()) 209 | e.p2pconn = newConn(e.sock, e.local_attempts[i].Addr, e.local_attempts[i].localaddr, e.id) 210 | if e.Kcp != nil { 211 | e.p2pconn.(*Conn).SetKcp(e.Kcp) 212 | } 213 | if e.D > 0 && e.P > 0 { 214 | e.p2pconn.(*Conn).SetFec(e.D, e.P) 215 | } 216 | for j := range e.attempts { 217 | my_remote_addr := e.attempts[j].Addr 218 | response, err := stun.InformReady(packet.Tid[:], my_remote_addr, nil) 219 | if err != nil { 220 | return nil 221 | } 222 | debug("write to ready", from.String(), j, my_remote_addr.String()) 223 | e.sock.WriteToUDP(response, from) 224 | } 225 | } 226 | } else if packet.Class == stun.ClassIndication { 227 | debug("recv other ready") 228 | e.otherReady = true 229 | /* for j := range e.attempts { 230 | debug("write !!!!!!", from.String(),j) 231 | e.sock.WriteToUDP([]byte("wocao,okokokook1!!"), from) 232 | }*/ 233 | } else if packet.Class == stun.ClassError { 234 | // debug("!!!!!!!!!!!!!") 235 | } 236 | } 237 | } 238 | 239 | return nil 240 | } 241 | 242 | func (e *AttemptEngine) run(f func(), encode, decode func([]byte) []byte) (net.Conn, error) { 243 | bInform := false 244 | beginTime := time.Now().Unix() 245 | for { 246 | if time.Now().Unix()-beginTime > 10 { 247 | e.status = "fail" 248 | } 249 | if e.status == "fail" || e.status == "quit" { 250 | break 251 | } 252 | timeout, err := e.xmit() 253 | if err != nil { 254 | return nil, err 255 | } 256 | if !bInform { 257 | f() 258 | bInform = true 259 | beginTime = time.Now().Unix() 260 | } 261 | e.sock.SetReadDeadline(timeout) 262 | if err = e.read(); err != nil { 263 | //return nil, err 264 | } 265 | if e.p2pconn != nil && e.otherReady { 266 | if e.buster && e.status == "" { 267 | debug("write final!!!!!!") 268 | e.sock.WriteToUDP([]byte("makeholeover"), e.p2pconn.RemoteAddr().(*net.UDPAddr)) 269 | } 270 | if e.status != "over" { 271 | e.status = "wait" 272 | } 273 | } 274 | if e.status == "over" { 275 | e.p2pconn.(*Conn).SetCrypt(encode, decode) 276 | e.p2pconn.(*Conn).Run() 277 | return e.p2pconn, nil 278 | } 279 | } 280 | 281 | return nil, errors.New(e.status) 282 | } 283 | -------------------------------------------------------------------------------- /nat/stun/stun.go: -------------------------------------------------------------------------------- 1 | // Package stun implements a subset of the Session Traversal Utilities 2 | // for NAT (STUN) protocol, described in RFC 5389. Notably absent is 3 | // support for long-term credentials. 4 | package stun 5 | 6 | import ( 7 | "bytes" 8 | "crypto/hmac" 9 | "crypto/sha1" 10 | "crypto/rand" 11 | "encoding/binary" 12 | "errors" 13 | "fmt" 14 | "hash/crc32" 15 | "io" 16 | "net" 17 | ) 18 | 19 | type Class uint8 20 | type Method uint16 21 | 22 | // Possible classes and methods for a STUN packet. 23 | const ( 24 | ClassRequest = iota 25 | ClassIndication 26 | ClassSuccess 27 | ClassError 28 | MethodBinding = 1 29 | ) 30 | 31 | // A Packet presents select information about a STUN packet. 32 | type Packet struct { 33 | Class Class 34 | Method Method 35 | Tid [12]byte 36 | Addr *net.UDPAddr 37 | HasMac bool 38 | Software string 39 | UseCandidate bool 40 | 41 | Error *PacketError 42 | Alternate *net.UDPAddr 43 | } 44 | 45 | func RandomTid() ([]byte, error) { 46 | ret := make([]byte, 12) 47 | _, err := io.ReadFull(rand.Reader, ret) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return ret, nil 52 | } 53 | 54 | func InformReady(tid []byte, addr *net.UDPAddr, macKey []byte) ([]byte, error) { 55 | if len(tid) != 12 { 56 | panic("Wrong length for tid") 57 | } 58 | var hdr header 59 | hdr.TypeCode = typeCode(ClassIndication, MethodBinding) 60 | hdr.Magic = magic 61 | copy(hdr.Tid[:], tid) 62 | 63 | var buf bytes.Buffer 64 | if addr != nil { 65 | ip := addr.IP.To4() 66 | family := 1 67 | if ip == nil { 68 | ip = addr.IP 69 | family++ 70 | } 71 | 72 | binary.Write(&buf, binary.BigEndian, []uint16{ 73 | attrAddress, 74 | uint16(4 + len(ip)), 75 | uint16(family), 76 | uint16(addr.Port)}) 77 | buf.Write(ip) 78 | } 79 | return buildPacket(hdr, buf.Bytes(), macKey, false) 80 | } 81 | // BindRequest constructs and returns a Binding Request STUN packet. 82 | // 83 | // tid must be 12 bytes long. If a macKey is provided, the returned 84 | // packet is signed. 85 | func BindRequest(tid []byte, addr *net.UDPAddr, macKey []byte, compat bool, useCandidate bool) ([]byte, error) { 86 | if len(tid) != 12 { 87 | panic("Wrong length for tid") 88 | } 89 | var hdr header 90 | hdr.TypeCode = typeCode(ClassRequest, MethodBinding) 91 | hdr.Magic = magic 92 | copy(hdr.Tid[:], tid) 93 | 94 | var buf bytes.Buffer 95 | if useCandidate { 96 | binary.Write(&buf, binary.BigEndian, []uint16{ 97 | attrUseCandidate, 98 | uint16(0)}) 99 | } 100 | 101 | if addr != nil { 102 | ip := addr.IP.To4() 103 | family := 1 104 | if ip == nil { 105 | ip = addr.IP 106 | family++ 107 | } 108 | 109 | binary.Write(&buf, binary.BigEndian, []uint16{ 110 | attrAddress, 111 | uint16(4 + len(ip)), 112 | uint16(family), 113 | uint16(addr.Port)}) 114 | buf.Write(ip) 115 | } 116 | return buildPacket(hdr, buf.Bytes(), macKey, compat) 117 | } 118 | 119 | // BindResponse constructs and returns a Binding Success STUN packet. 120 | // 121 | // tid must be 12 bytes long. If a macKey is provided, the returned 122 | // packet is signed. 123 | func BindResponse(tid []byte, addr *net.UDPAddr, macKey []byte, compat bool) ([]byte, error) { 124 | if len(tid) != 12 { 125 | panic("Wrong length for tid") 126 | } 127 | var hdr header 128 | hdr.TypeCode = typeCode(ClassSuccess, MethodBinding) 129 | hdr.Magic = magic 130 | copy(hdr.Tid[:], tid) 131 | 132 | ip := addr.IP.To4() 133 | family := 1 134 | if ip == nil { 135 | ip = addr.IP 136 | family++ 137 | } 138 | 139 | var buf bytes.Buffer 140 | binary.Write(&buf, binary.BigEndian, []uint16{ 141 | attrXorAddress, 142 | uint16(4 + len(ip)), 143 | uint16(family), 144 | uint16(addr.Port ^ magic>>16)}) 145 | buf.Write(ip) 146 | 147 | attrs := buf.Bytes() 148 | for i := range magicBytes { 149 | attrs[8+i] ^= magicBytes[i] 150 | } 151 | for i := range attrs[12:] { 152 | attrs[12+i] ^= tid[i] 153 | } 154 | return buildPacket(hdr, attrs, macKey, compat) 155 | } 156 | 157 | // ParsePacket parses a byte slice as a STUN packet. 158 | // 159 | // If a macKey is provided, only packets correctly signed with that 160 | // key will be accepted. If no macKey is provided, only unsigned 161 | // packets will be accepted. 162 | func ParsePacket(raw []byte, macKey []byte) (*Packet, error) { 163 | var hdr header 164 | if err := binary.Read(bytes.NewBuffer(raw[:headerLen]), binary.BigEndian, &hdr); err != nil { 165 | return nil, err 166 | } 167 | 168 | // Initial sanity checks: verify initial bits, magic, length and 169 | // optional fingerprint. 170 | if hdr.TypeCode&0xC000 != 0 || int(hdr.Length+20) != len(raw) || hdr.Magic != magic { 171 | return nil, MalformedPacket{} 172 | } 173 | if hdr.Length >= fpLen { 174 | if present, valid := checkFp(raw); present { 175 | if !valid { 176 | return nil, MalformedPacket{} 177 | } 178 | raw = raw[:len(raw)-fpLen] 179 | } 180 | } 181 | 182 | pkt := &Packet{ 183 | Class: typeCodeClass(hdr.TypeCode), 184 | Method: typeCodeMethod(hdr.TypeCode), 185 | Tid: hdr.Tid, 186 | } 187 | 188 | attrReader := bytes.NewBuffer(raw[headerLen:]) 189 | var haveXor bool 190 | for { 191 | if attrReader.Len() == 0 { 192 | break 193 | } 194 | 195 | var ahdr attrHeader 196 | if err := binary.Read(attrReader, binary.BigEndian, &ahdr); err != nil { 197 | return nil, err 198 | } 199 | if ahdr.Length > 500 || attrReader.Len() < int(ahdr.Length) { 200 | return nil, MalformedPacket{} 201 | } 202 | value := attrReader.Next(int(ahdr.Length)) 203 | if ahdr.Length%4 != 0 { 204 | attrReader.Next(int(4 - ahdr.Length%4)) 205 | } 206 | 207 | switch ahdr.Type { 208 | case attrAddress: 209 | if !haveXor { 210 | ip, port, err := parseAddress(value) 211 | if err != nil { 212 | return nil, err 213 | } 214 | pkt.Addr = &net.UDPAddr{IP:ip, Port:port} 215 | } 216 | case attrXorAddress: 217 | ip, port, err := parseAddress(value) 218 | if err != nil { 219 | return nil, err 220 | } 221 | for i := range ip { 222 | ip[i] ^= raw[4+i] 223 | } 224 | port ^= int(binary.BigEndian.Uint16(raw[4:])) 225 | pkt.Addr = &net.UDPAddr{IP:ip, Port:port} 226 | haveXor = true 227 | case attrUseCandidate: 228 | pkt.UseCandidate = true 229 | 230 | case attrFingerprint: 231 | return nil, MalformedPacket{} 232 | case attrIntegrity: 233 | if len(macKey) == 0 { 234 | return nil, UnverifiableMac{} 235 | } 236 | tocheck := raw[:len(raw)-attrReader.Len()-macLen] 237 | binary.BigEndian.PutUint16(tocheck[2:4], uint16(len(tocheck)+macLen-headerLen)) 238 | macer := hmac.New(sha1.New, macKey) 239 | if _, err := macer.Write(tocheck); err != nil { 240 | return nil, err 241 | } 242 | mac := make([]byte, 0, 20) 243 | mac = macer.Sum(mac) 244 | if !bytes.Equal(mac, value) { 245 | return nil, BadMac{} 246 | } 247 | pkt.HasMac = true 248 | return pkt, nil 249 | 250 | case attrErrCode: 251 | code := uint16(value[2])*100 + uint16(value[3]) 252 | reason := string(value[4:]) 253 | pkt.Error = &PacketError{code, reason} 254 | case attrUnknownAttrs: 255 | // Ignored 256 | case attrSoftware: 257 | pkt.Software = string(value) 258 | case attrAlternate: 259 | ip, port, err := parseAddress(value) 260 | if err != nil { 261 | return nil, err 262 | } 263 | pkt.Alternate = &net.UDPAddr{IP:ip, Port:port} 264 | 265 | case attrUsername: 266 | case attrRealm: 267 | case attrNonce: 268 | return nil, errors.New("Unsupported STUN attribute") 269 | } 270 | } 271 | 272 | if len(macKey) > 0 { 273 | return nil, MissingMac{} 274 | } 275 | return pkt, nil 276 | } 277 | 278 | // A MalformedPacket error is returned by ParsePacket when it 279 | // encounters structural malformations in the STUN packet. 280 | // 281 | // On a network endpoing where STUN coexists with another protocol, 282 | // this error can be used to differentiate STUN and non-STUN traffic. 283 | type MalformedPacket struct{} 284 | 285 | func (m MalformedPacket) Error() string { 286 | return "Malformed STUN packet" 287 | } 288 | 289 | // A BadMac error is returned by ParsePacket when a structurally sound 290 | // STUN packet is received with a signature not matching the provided 291 | // macKey. 292 | type BadMac struct{} 293 | 294 | func (b BadMac) Error() string { 295 | return "Incorrect MAC on packet" 296 | } 297 | 298 | // A MissingMac error is returned by ParsePacket when it receives a 299 | // valid but unsigned STUN packet where it expected a signed packet. 300 | type MissingMac struct{} 301 | 302 | func (m MissingMac) Error() string { 303 | return "MAC expected but missing" 304 | } 305 | 306 | // An UnverifiableMac error is returned by ParsePacket when it 307 | // encounters a valid and signed STUN packet, and no macKey was 308 | // provided. 309 | type UnverifiableMac struct{} 310 | 311 | func (u UnverifiableMac) Error() string { 312 | return "MAC found but no key given" 313 | } 314 | 315 | // A PacketError describes an error returned by a STUN server. 316 | type PacketError struct { 317 | Code uint16 318 | Reason string 319 | } 320 | 321 | func (p PacketError) Error() string { 322 | var genericErr string 323 | switch p.Code { 324 | case errTryAlternate: 325 | genericErr = "Try Alternate" 326 | case errBadRequest: 327 | genericErr = "Bad Request" 328 | case errUnauthorized: 329 | genericErr = "Unauthorized" 330 | case errUnknownAttribute: 331 | genericErr = "Unknown Attribute(s)" 332 | case errStaleNonce: 333 | genericErr = "Stale Nonce" 334 | case errServerInternal: 335 | genericErr = "Internal Server Error" 336 | default: 337 | genericErr = fmt.Sprintf("Error %d", p.Code) 338 | } 339 | if len(p.Reason) == 0 { 340 | return genericErr 341 | } 342 | return fmt.Sprintf("%s: %s", genericErr, p.Reason) 343 | } 344 | 345 | func buildPacket(hdr header, attributes, macKey []byte, compat bool) ([]byte, error) { 346 | var buf bytes.Buffer 347 | 348 | if len(macKey) > 0 { 349 | hdr.Length = uint16(len(attributes) + macLen) 350 | 351 | macer := hmac.New(sha1.New, macKey) 352 | if err := binary.Write(macer, binary.BigEndian, hdr); err != nil { 353 | return nil, err 354 | } 355 | if _, err := macer.Write(attributes); err != nil { 356 | return nil, err 357 | } 358 | 359 | if err := binary.Write(&buf, binary.BigEndian, attrHeader{attrIntegrity, 20}); err != nil { 360 | return nil, err 361 | } 362 | attributes = append(attributes, macer.Sum(buf.Bytes())...) 363 | buf.Reset() 364 | } 365 | hdr.Length = uint16(len(attributes)) 366 | if !compat { 367 | hdr.Length += fpLen 368 | } 369 | if err := binary.Write(&buf, binary.BigEndian, hdr); err != nil { 370 | return nil, err 371 | } 372 | buf.Write(attributes) 373 | if !compat { 374 | var fp fpAttr 375 | fp.Type = attrFingerprint 376 | fp.Length = 4 377 | fp.Crc = crc32.ChecksumIEEE(buf.Bytes()) ^ 0x5354554e 378 | if err := binary.Write(&buf, binary.BigEndian, fp); err != nil { 379 | return nil, err 380 | } 381 | } 382 | return buf.Bytes(), nil 383 | } 384 | 385 | func parseAddress(raw []byte) (net.IP, int, error) { 386 | if len(raw) != 8 && len(raw) != 20 { 387 | return nil, 0, MalformedPacket{} 388 | } 389 | var family int 390 | switch int(raw[1]) { 391 | case 1: 392 | family = 4 393 | case 2: 394 | family = 16 395 | default: 396 | return nil, 0, MalformedPacket{} 397 | } 398 | port := binary.BigEndian.Uint16(raw[2:]) 399 | ip := make([]byte, len(raw[4:])) 400 | copy(ip, raw[4:]) 401 | if len(ip) != family { 402 | return nil, 0, MalformedPacket{} 403 | } 404 | return net.IP(ip), int(port), nil 405 | } 406 | 407 | func checkFp(raw []byte) (present, valid bool) { 408 | split := len(raw) - fpLen 409 | var fp fpAttr 410 | if err := binary.Read(bytes.NewBuffer(raw[split:]), binary.BigEndian, &fp); err != nil { 411 | return false, false 412 | } 413 | if fp.Type != attrFingerprint || fp.Length != 4 { 414 | return false, false 415 | } 416 | if fp.Crc != (crc32.ChecksumIEEE(raw[:split]) ^ 0x5354554e) { 417 | return true, false 418 | } 419 | return true, true 420 | } 421 | 422 | func typeCode(class uint8, method uint16) uint16 { 423 | return method<<2&0xFE00 | uint16(class)&2<<7 | method<<1&0x00E0 | uint16(class)&1<<4 | method&0xF 424 | } 425 | 426 | func typeCodeClass(typeCode uint16) Class { 427 | return Class(typeCode>>4&1 | typeCode>>7&2) 428 | } 429 | 430 | func typeCodeMethod(typeCode uint16) Method { 431 | return Method(typeCode&0xF | typeCode&0xE0>>1 | typeCode&0xFE00>>2) 432 | } 433 | 434 | // Parsing structs 435 | type header struct { 436 | TypeCode uint16 437 | Length uint16 438 | Magic uint32 439 | Tid [12]byte 440 | } 441 | 442 | type attrHeader struct { 443 | Type uint16 444 | Length uint16 445 | } 446 | 447 | type fpAttr struct { 448 | attrHeader 449 | Crc uint32 450 | } 451 | 452 | // Constants 453 | 454 | const ( 455 | magic = 0x2112a442 456 | headerLen = 20 457 | fpLen = 8 458 | macLen = 24 459 | ) 460 | 461 | var magicBytes = []byte{0x21, 0x12, 0xa4, 0x42} 462 | 463 | const ( 464 | // Comprehension required 465 | attrAddress = 0x01 // 466 | attrUsername = 0x06 // 467 | attrIntegrity = 0x08 // 468 | attrErrCode = 0x09 // 469 | attrUnknownAttrs = 0x0A // 470 | attrRealm = 0x14 // 471 | attrNonce = 0x15 // 472 | attrXorAddress = 0x20 // 473 | attrUseCandidate = 0x25 // 474 | 475 | // Comprehension optional 476 | attrSoftware = 0x8022 // 477 | attrAlternate = 0x8023 // 478 | attrFingerprint = 0x8028 // 479 | ) 480 | 481 | const ( 482 | errTryAlternate = 300 483 | errBadRequest = 400 484 | errUnauthorized = 401 485 | errUnknownAttribute = 420 486 | errStaleNonce = 438 487 | errServerInternal = 500 488 | ) 489 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | version=$1 3 | if [ $# -eq 0 ] 4 | then 5 | echo "Please input version, like \"./release.sh 0.60\"" 6 | exit 7 | fi 8 | rm -f dtunnel_*$1.tgz 9 | echo "Build ReleaseFile for version $version" 10 | 11 | echo "build linux_amd64" 12 | GOOS=linux GOARCH=amd64 make 13 | tar zcvf dtunnel_linux_x64_$1.tgz dtunnel dtunnel_s 14 | echo "build linux_386" 15 | GOOS=linux GOARCH=386 make 16 | tar zcvf dtunnel_linux_x86_$1.tgz dtunnel dtunnel_s 17 | echo "build mac_x64" 18 | GOOS=darwin GOARCH=amd64 make 19 | tar zcvf dtunnel_mac_x64_$1.tgz dtunnel dtunnel_s 20 | echo "build win32" 21 | GOOS=windows GOARCH=386 make && mv dtunnel dtunnel.exe && mv dtunnel_s dtunnel_s.exe 22 | tar zcvf dtunnel_win32_$1.tgz dtunnel.exe dtunnel_s.exe 23 | echo "build linux_arm" 24 | GOOS=linux GOARCH=arm make 25 | tar zcvf dtunnel_linux_arm_$1.tgz dtunnel dtunnel_s 26 | echo "build linux_mips" 27 | GOOS=linux GOARCH=mips make 28 | tar zcvf dtunnel_linux_mips_$1.tgz dtunnel dtunnel_s 29 | echo "build linux_mipsle" 30 | GOOS=linux GOARCH=mipsle make 31 | tar zcvf dtunnel_linux_mipsle_$1.tgz dtunnel dtunnel_s 32 | rm -f dtunnel dtunnel.exe dtunnel_s dtunnel_s.exe 33 | echo "Build Over" 34 | ls -l dtunnel_*$1.tgz 35 | -------------------------------------------------------------------------------- /scripts/doghole.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # move dtunnel to $HOME/bin/dtunnel 3 | # echo "mypassword" > $HOME/.ssh/pw 4 | 5 | cat $HOME/.ssh/pw | sudo -S killall -9 dtunnel 6 | sleep 2 7 | 8 | 9 | # the server side 10 | 11 | cat $HOME/.ssh/pw | sudo -S nice -n -10 $HOME/bin/dtunnel --reg node1.domain.com -local socks5 -clientkey verylongpasswd & 12 | 13 | # the client side 14 | 15 | cat $HOME/.ssh/pw | sudo -S nice -n -10 $HOME/bin/dtunnel --link node1.domain.com -local :7070 -clientkey verylongpasswd & 16 | -------------------------------------------------------------------------------- /scripts/install_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # need run as root 3 | cd ~ 4 | yum install -y tar 5 | mkdir /root/goworkspace 6 | wget https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz 7 | tar zxvf go1.4.2.linux-amd64.tar.gz 8 | echo 'export GOROOT=/root/go' >> .bashrc 9 | echo 'export GOPATH=/root/goworkspace' >> .bashrc 10 | echo 'export PATH="/root/go/bin:/root/goworkspace/bin:"$PATH' >> .bashrc 11 | source ~/.bashrc 12 | yum install -y git 13 | git clone https://github.com/vzex/dog-tunnel.git 14 | cd dog-tunnel 15 | go get github.com/go-sql-driver/mysql 16 | make 17 | mv dtunnel /usr/bin/dtunnel 18 | -------------------------------------------------------------------------------- /scripts/install_ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # need run as root 3 | cd ~ 4 | apt-get update && apt-get dist-upgrade -y && echo 'Upgrade system ok' 5 | apt-get install tar wget curl git make gcc build-essential -y && echo 'package installed' 6 | mkdir /root/goworkspace 7 | wget https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz 8 | tar zxvf go1.4.2.linux-amd64.tar.gz 9 | echo 'export GOROOT=/root/go' >> .bashrc 10 | echo 'export GOPATH=/root/goworkspace' >> .bashrc 11 | echo 'export PATH="/root/go/bin:/root/goworkspace/bin:"$PATH' >> .bashrc 12 | source ~/.bashrc 13 | git clone https://github.com/vzex/dog-tunnel.git 14 | cd dog-tunnel 15 | go get github.com/go-sql-driver/mysql 16 | make 17 | mv dtunnel /usr/bin/dtunnel 18 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/json" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net" 10 | "os" 11 | "os/signal" 12 | "strconv" 13 | "strings" 14 | "syscall" 15 | "time" 16 | 17 | "github.com/vzex/dog-tunnel/admin" 18 | "github.com/vzex/dog-tunnel/auth" 19 | "github.com/vzex/dog-tunnel/common" 20 | ) 21 | 22 | var listenAddr = flag.String("addr", "0.0.0.0:8000", "server addr") 23 | var listenAddrUDP = flag.String("addrudp", "0.0.0.0:8018", "udp server addr") 24 | var bUseSSL = flag.Bool("ssl", false, "use ssl") 25 | var bUseHttps = flag.Bool("https", false, "use https") 26 | var certFile = flag.String("cert", "", "cert file") 27 | var keyFile = flag.String("key", "", "key file") 28 | 29 | var adminAddr = flag.String("admin", "", "admin addr") 30 | var bShowVersion = flag.Bool("version", false, "show version") 31 | var bReplaceReg = flag.Bool("replace", false, "if dup name registered, kick out the previous one, default is false") 32 | 33 | var db_user = flag.String("dbuser", "", "db user") 34 | var db_pass = flag.String("dbpass", "", "db password") 35 | var db_host = flag.String("dbhost", "", "db host") 36 | 37 | var bUseDB = false 38 | 39 | func handleClient(conn net.Conn) { 40 | common.Conn2ClientInfo[conn] = &common.ClientInfo{Conn: conn, ClientMap: make(map[net.Conn]*common.Session), Id2Session: make(map[string]*common.Session), IsServer: false, Quit: make(chan bool), ResponseTime: time.Now().Unix()} 41 | log.Println("client linked success", conn.RemoteAddr().String()) 42 | common.Conn2ClientInfo[conn].Loop() 43 | common.Read(conn, handleResponse) 44 | client, bHave := common.Conn2ClientInfo[conn] 45 | if bHave { 46 | close(client.Quit) 47 | if client.IsServer { 48 | for conn, session := range client.ClientMap { 49 | conn.Close() 50 | common.RmId(client.ServerName, session.Id) 51 | } 52 | delete(common.ServerName2Conn, client.ServerName) 53 | log.Println("unregister service Name", client.ServerName) 54 | if bUseDB { 55 | user, _ := auth.GetUser(client.UserName) 56 | if user != nil { 57 | user.OnLogout() 58 | } 59 | } 60 | } else { 61 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 62 | id := server.DelClient(conn) 63 | log.Println("send quit") 64 | common.Write(server.Conn, id, "clientquit", "") 65 | }, func() {}) 66 | } 67 | delete(common.Conn2ClientInfo, conn) 68 | } 69 | conn.Close() 70 | log.Println("client disconnected", conn.RemoteAddr().String()) 71 | } 72 | 73 | func udphandleClient(conn *net.UDPConn) { 74 | 75 | for { 76 | 77 | data := make([]byte, 1024) 78 | 79 | _, remoteAddr, err := conn.ReadFromUDP(data) 80 | if err != nil { 81 | log.Println("failed to read UDP msg because of ", err.Error()) 82 | break 83 | } 84 | 85 | conn.WriteToUDP([]byte(remoteAddr.String()), remoteAddr) 86 | } 87 | } 88 | 89 | func handleResponse(conn net.Conn, id string, action string, content string) { 90 | //log.Println("got", id, action, content) 91 | common.GetClientInfoByConn(conn, func(client *common.ClientInfo) { 92 | client.ResponseTime = time.Now().Unix() 93 | }, func() { 94 | }) 95 | switch action { 96 | case "init": 97 | clientInfoStr := content 98 | var clientInfo common.ClientSetting 99 | err := json.Unmarshal([]byte(clientInfoStr), &clientInfo) 100 | if err != nil { 101 | log.Println("error decode clientinfo, kick out", conn.RemoteAddr().String()) 102 | common.Write(conn, "0", "showandquit", "server decode clientInfo error") 103 | return 104 | } 105 | if common.Version != clientInfo.Version { 106 | s_version := fmt.Sprintf("%.2f", common.Version) 107 | c_version := fmt.Sprintf("%.2f", clientInfo.Version) 108 | log.Println("version not eq", conn.RemoteAddr().String(), s_version, c_version) 109 | common.Write(conn, "0", "showandquit", "client version:"+c_version+" not eq with server:"+s_version) 110 | return 111 | } 112 | ServerName := clientInfo.Name 113 | if clientInfo.ClientType == "reg" { 114 | var user *auth.User 115 | if bUseDB { 116 | if clientInfo.AccessKey == "" { 117 | user, _ = auth.GetUser("test") 118 | } else { 119 | user, _ = auth.GetUserByKey(clientInfo.AccessKey) 120 | } 121 | } else { 122 | user = &auth.User{UserType: auth.UserType_Admin} 123 | } 124 | //fmt.Printf("%+v\n", user) 125 | if user == nil { 126 | common.Write(conn, "0", "showandquit", "invalid user accessKey:"+clientInfo.AccessKey+"!!!") 127 | return 128 | } 129 | if !user.CheckOnlineServiceNum() { 130 | common.Write(conn, "0", "showandquit", "online service num cannot overstep "+strconv.Itoa(user.MaxOnlineServerNum)) 131 | return 132 | } 133 | if !user.CheckIpLimit(conn.RemoteAddr().(*net.TCPAddr).IP.String()) { 134 | common.Write(conn, "0", "showandquit", "ip limit service num cannot overstep "+strconv.Itoa(user.MaxSameIPServers)) 135 | return 136 | } 137 | f := func() { 138 | common.ServerName2Conn[ServerName] = conn 139 | common.GetClientInfoByConn(conn, func(info *common.ClientInfo) { 140 | info.ServerName = ServerName 141 | info.IsServer = true 142 | info.Id2MakeSession = make(map[string]*common.UDPMakeSession) 143 | info.UserName = user.UserName 144 | info.ClientKey = clientInfo.ClientKey 145 | }, func() {}) 146 | log.Println("client reg service success", conn.RemoteAddr().String(), user.UserName, ServerName) 147 | common.Write(conn, "0", "show", "register service ok, user:"+user.UserName) 148 | } 149 | common.GetClientInfoByName(ServerName, func(server *common.ClientInfo) { 150 | if *bReplaceReg { 151 | _conn := server.Conn 152 | close(server.Quit) 153 | for conn, session := range server.ClientMap { 154 | conn.Close() 155 | common.RmId(server.ServerName, session.Id) 156 | } 157 | delete(common.ServerName2Conn, server.ServerName) 158 | log.Println("force unregister service Name", server.ServerName) 159 | if bUseDB { 160 | user, _ := auth.GetUser(server.UserName) 161 | if user != nil { 162 | user.OnLogout() 163 | } 164 | } 165 | delete(common.Conn2ClientInfo, _conn) 166 | common.Write(_conn, "0", "showandquit", "some one kick you out") 167 | _conn.Close() 168 | f() 169 | } else { 170 | common.Write(conn, "0", "showandretry", "already have the ServerName!") 171 | } 172 | }, f) 173 | } else if clientInfo.ClientType == "link" { 174 | if clientInfo.Mode < 0 || clientInfo.Mode > 2 { 175 | clientInfo.Mode = 0 176 | } 177 | ServerName := clientInfo.Name 178 | bAuth := true 179 | common.GetClientInfoByName(ServerName, func(info *common.ClientInfo) { 180 | var user *auth.User 181 | if bUseDB { 182 | user, _ = auth.GetUser(info.UserName) 183 | } else { 184 | user = &auth.User{UserType: auth.UserType_Admin} 185 | } 186 | //fmt.Printf("%+v\n", user) 187 | if user == nil { 188 | common.Write(conn, "0", "showandquit", "invalid user:"+info.UserName+"!!!") 189 | bAuth = false 190 | return 191 | } 192 | if info.ClientKey != clientInfo.ClientKey { 193 | common.Write(conn, "0", "showandquit", "clientkey invalid!!!") 194 | bAuth = false 195 | return 196 | } 197 | if !user.CheckSessionNum(len(info.ClientMap)) { 198 | common.Write(conn, "0", "showandquit", "session numcannot overstep "+strconv.Itoa(len(info.ClientMap))) 199 | bAuth = false 200 | return 201 | } 202 | if !user.CheckPipeNum(clientInfo.PipeNum) { 203 | common.Write(conn, "0", "showandquit", "pipenum cannot overstep "+strconv.Itoa(user.MaxPipeNum)) 204 | bAuth = false 205 | return 206 | } 207 | }, func() { 208 | common.Write(conn, "0", "showandquit", "serverName invalid!!!") 209 | bAuth = false 210 | }) 211 | if !bAuth { 212 | return 213 | } 214 | common.GetClientInfoByConn(conn, func(client *common.ClientInfo) { 215 | client.ServerName = ServerName 216 | }, func() { 217 | }) 218 | common.GetClientInfoByName(ServerName, func(server *common.ClientInfo) { 219 | log.Println("client link service success", conn.RemoteAddr().String(), ServerName) 220 | server.AddClient(conn, clientInfo) 221 | }, func() { 222 | common.Write(conn, "0", "showandquit", "donnt have this service name") 223 | }) 224 | } 225 | case "tunnel_error": 226 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 227 | log.Println("<<=====tunnel_error", server.ServerName, conn.RemoteAddr().String()) 228 | session, bHave := server.Id2Session[id] 229 | if bHave { 230 | session.Status = "fail" 231 | common.Write(session.ClientA, "0", "showandquit", content) 232 | server.DelClient(session.ClientA) 233 | } 234 | }, func() { 235 | }) 236 | case "makeholefail": 237 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 238 | udpsession, bHave := server.Id2MakeSession[id] 239 | if bHave { 240 | log.Println("<<=====make hole fail", conn.RemoteAddr().String(), udpsession.ServerName, udpsession.SessionId, id) 241 | sessionId := udpsession.SessionId 242 | session, _bHave := server.Id2Session[sessionId] 243 | if _bHave { 244 | session.Status = "fail" 245 | session.MakeHoleResponseN++ 246 | session.MakeHoleHasFail = true 247 | if session.MakeHoleResponseN == session.Setting.PipeNum { 248 | if session.Method == "udp" { 249 | session.RestartSession(server.ServerName) 250 | } else if session.Method == "restart" { 251 | if session.Setting.Mode == 0 { 252 | tmp := session.ClientA 253 | session.ClientA = session.ClientB 254 | session.ClientB = tmp 255 | session.StartCSMode() 256 | } else { 257 | server.DelClient(session.ClientB) 258 | } 259 | } else { 260 | server.DelClient(session.ClientA) 261 | } 262 | } 263 | } 264 | udpsession.Remove(false) 265 | } 266 | }, func() { 267 | }) 268 | case "makeholeok": 269 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 270 | if content == "csmode" { 271 | session, _bHave := server.Id2Session[id] 272 | if _bHave { 273 | log.Println("<<=====make hole ok", conn.RemoteAddr().String(), server.ServerName, session.Id) 274 | session.Status = "ok" 275 | session.MakeHoleResponseN++ 276 | } 277 | } 278 | udpsession, bHave := server.Id2MakeSession[id] 279 | if bHave { 280 | log.Println("<<=====make hole ok", conn.RemoteAddr().String(), udpsession.ServerName, udpsession.SessionId, id) 281 | sessionId := udpsession.SessionId 282 | session, _bHave := server.Id2Session[sessionId] 283 | if _bHave { 284 | session.MakeHoleResponseN++ 285 | if session.MakeHoleResponseN == session.Setting.PipeNum { 286 | if !session.MakeHoleHasFail { 287 | session.Status = "ok" 288 | } 289 | } 290 | } 291 | udpsession.Remove(false) 292 | } 293 | }, func() { 294 | }) 295 | case "report_addrlist": 296 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 297 | udpsession, bHave := server.Id2MakeSession[id] 298 | //log.Println("test", udpsession, id, server.ServerName) 299 | if bHave { 300 | log.Println("<<===report addr list ok", conn.RemoteAddr().String(), udpsession.ServerName, udpsession.Id) 301 | udpsession.BeginMakeHole(1, content) 302 | } 303 | }, func() { 304 | }) 305 | case "success_bust_a": 306 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 307 | udpsession, bHave := server.Id2MakeSession[id] 308 | if bHave { 309 | log.Println("<<=====success_bust_a", conn.RemoteAddr().String(), udpsession.ServerName, udpsession.SessionId, id) 310 | udpsession.BeginMakeHole(2, content) 311 | } 312 | }, func() { 313 | }) 314 | // for c/s mode 315 | case "tunnel_close": 316 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 317 | session := server.GetSession(conn) 318 | if session != nil { 319 | common.Write(session.ClientB, session.Id+"-"+id, "csmode_s_tunnel_close", content) 320 | } else { 321 | println("no session") 322 | } 323 | }, func() { 324 | }) 325 | case "tunnel_open": 326 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 327 | session := server.GetSession(conn) 328 | if session != nil { 329 | common.Write(session.ClientB, session.Id+"-"+id, "csmode_s_tunnel_open", content) 330 | } else { 331 | println("no session") 332 | } 333 | }, func() { 334 | }) 335 | case "tunnel_msg_c": 336 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 337 | var user *auth.User 338 | if bUseDB { 339 | user, _ = auth.GetUser(server.UserName) 340 | } else { 341 | user = &auth.User{UserType: auth.UserType_Admin} 342 | } 343 | if user == nil { 344 | common.Write(conn, "0", "showandquit", "cannot get userinfo of this service "+server.UserName) 345 | return 346 | } 347 | if !user.UpdateCSMode(len(content)) { 348 | common.Write(conn, "0", "showandquit", "reach today's csmode data limit") 349 | return 350 | } 351 | session := server.GetSession(conn) 352 | if session != nil { 353 | common.Write(session.ClientB, session.Id+"-"+id, "csmode_msg_c", content) 354 | } else { 355 | println("no session") 356 | } 357 | }, func() { 358 | }) 359 | case "tunnel_msg_s": 360 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 361 | var user *auth.User 362 | if bUseDB { 363 | user, _ = auth.GetUser(server.UserName) 364 | } else { 365 | user = &auth.User{UserType: auth.UserType_Admin} 366 | } 367 | if user == nil { 368 | common.Write(conn, "0", "showandquit", "cannot get userinfo of this service"+server.UserName) 369 | return 370 | } 371 | if !user.UpdateCSMode(len(content)) { 372 | common.Write(conn, "0", "show", "reach today's csmode data limit") 373 | return 374 | } 375 | arr := strings.Split(id, "-") 376 | clientId := arr[0] 377 | session, bHave := server.Id2Session[clientId] 378 | if bHave { 379 | common.Write(session.ClientA, id, "csmode_msg_s", content) 380 | } else { 381 | println("no session") 382 | } 383 | }, func() { 384 | }) 385 | case "tunnel_close_s": 386 | common.GetServerInfoByConn(conn, func(server *common.ClientInfo) { 387 | arr := strings.Split(id, "-") 388 | clientId := arr[0] 389 | session, bHave := server.Id2Session[clientId] 390 | if bHave { 391 | common.Write(session.ClientA, id, "csmode_c_tunnel_close", content) 392 | } else { 393 | println("no session") 394 | } 395 | }, func() { 396 | }) 397 | } 398 | } 399 | 400 | var err error 401 | var g_Master net.Listener 402 | 403 | func main() { 404 | flag.Parse() 405 | if *bShowVersion { 406 | fmt.Printf("%.2f\n", common.Version) 407 | return 408 | } 409 | common.Conn2ClientInfo = make(map[net.Conn]*common.ClientInfo) 410 | common.ServerName2Conn = make(map[string]net.Conn) 411 | common.Conn2Admin = make(map[net.Conn]*common.AdminInfo) 412 | listener, err := net.Listen("tcp", *listenAddr) 413 | if err != nil { 414 | log.Println("cannot listen addr:" + err.Error()) 415 | return 416 | } 417 | if *bUseSSL { 418 | config := &tls.Config{} 419 | config.Certificates = make([]tls.Certificate, 1) 420 | config.Certificates[0], err = tls.LoadX509KeyPair(*certFile, *keyFile) 421 | if err != nil { 422 | log.Println("load key file error", err.Error()) 423 | return 424 | } 425 | g_Master = tls.NewListener(listener, config) 426 | } else { 427 | g_Master = listener 428 | } 429 | go func() { 430 | for { 431 | conn, err := g_Master.Accept() 432 | if err != nil { 433 | continue 434 | } 435 | go handleClient(conn) 436 | } 437 | }() 438 | 439 | udpaddr, err := net.ResolveUDPAddr("udp", *listenAddrUDP) 440 | if err != nil { 441 | log.Println("Can't resolve address: ", err) 442 | return 443 | } 444 | 445 | udpconn, err := net.ListenUDP("udp", udpaddr) 446 | if err != nil { 447 | log.Println("Error UDP listening:", err) 448 | return 449 | } 450 | 451 | log.Println("listenAdd: ", *listenAddrUDP) 452 | 453 | defer udpconn.Close() 454 | 455 | go udphandleClient(udpconn) 456 | if *db_host != "" { 457 | err = auth.Init(*db_user, *db_pass, *db_host) 458 | if err != nil { 459 | log.Println("mysql client fail", err.Error()) 460 | return 461 | } 462 | defer auth.DeInit() 463 | bUseDB = true 464 | } 465 | log.Println("master start success") 466 | if *adminAddr != "" { 467 | cert, key := "", "" 468 | if *bUseHttps { 469 | cert, key = *certFile, *keyFile 470 | } 471 | err := admin.InitAdminPort(*adminAddr, cert, key) 472 | if err != nil { 473 | log.Println("admin service start fail", err.Error()) 474 | return 475 | } 476 | log.Println("admin service start success") 477 | } 478 | c := make(chan os.Signal, 1) 479 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 480 | <-c 481 | log.Println("received signal,shutdown") 482 | shutdown() 483 | } 484 | 485 | func shutdown() { 486 | for conn, client := range common.Conn2ClientInfo { 487 | if !client.IsServer { 488 | log.Println("shutdown client", client.ServerName) 489 | common.Write(conn, "0", "showandquit", "server shutdown") 490 | } else { 491 | log.Println("unregister service Name", client.ServerName) 492 | if bUseDB { 493 | user, _ := auth.GetUser(client.UserName) 494 | if user != nil { 495 | user.OnLogout() 496 | } 497 | } 498 | //donnot showandquit,because client_server need to reconnect 499 | } 500 | } 501 | } 502 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | nohup ./dtunnel_s -ssl -admin :8009 -dbuser dog -dbpass dog -cert keys/server.crt -key keys/server.key -https -addr :8008 > log.txt 2>&1 & 3 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # first run server,make client,and reg a,then run this bash to test 3 | declare -i i=0 4 | declare -i maxN=30 5 | 6 | while [ $i -lt $maxN ] 7 | do 8 | declare -i j=$i+10000 9 | ./dtunnel -link a -local :$j -pipen 4 -v > test_$j.log 2>&1 & 10 | i=$i+1 11 | done 12 | #maybe longer 13 | sleep 5 14 | declare -i n=`grep "service start success" test*.log|wc -l` 15 | if [ $n -eq $maxN ] 16 | then 17 | echo "test ok" 18 | else 19 | cat test*.log 20 | fi 21 | ps aux|grep "link a"|grep -v grep|awk '{print $2}'|xargs kill 22 | rm test*.log 23 | --------------------------------------------------------------------------------