├── nac.syso
├── .gitignore
├── go.mod
├── conf.yaml
├── Dockerfile
├── nac.manifest
├── LICENSE
├── core
├── constant.go
├── intra_handler.go
├── intra_server_handler.go
├── core_handler.go
├── pool_handler.go
├── connect_handler.go
├── natok_server_handler.go
└── natok_handler.go
├── s-cert.pem
├── s-cert.key
├── go.sum
├── README.md
├── conf
└── app_config.go
├── main.go
└── grid-snake.svg
/nac.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/natokay/go-natok-cli/HEAD/nac.syso
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Eclipse artifacts, including WTP generated manifests #
2 | .classpath
3 | .project
4 |
5 | # IDEA artifacts and output dirs #
6 | *.ipr
7 | *.iws
8 | .idea
9 |
10 | *.exe
11 | vendor
12 | natok-cli
13 | *.log
14 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module natok-cli
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/kardianos/service v1.2.2
7 | github.com/sirupsen/logrus v1.9.3
8 | gopkg.in/yaml.v2 v2.4.0
9 | )
10 |
11 | require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
12 |
--------------------------------------------------------------------------------
/conf.yaml:
--------------------------------------------------------------------------------
1 | natok:
2 | server:
3 | # - host: yourdomain.com
4 | # port: 1001
5 | # access-key: 752b65f22c39e006db38078585dc4fa4
6 | - host: localhost
7 | port: 1001
8 | access-key: 752b65f22c39e006db38078585dc4fa4
9 | cert-key-path: s-cert.key
10 | cert-pem-path: s-cert.pem
11 | log-file-path: out.log
12 | log-debug-level: false
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM centos:8
2 |
3 | ENV HOME=/app/
4 |
5 | ADD natok-cli ${HOME}
6 | ADD s-cert.key ${HOME}
7 | ADD s-cert.pem ${HOME}
8 | ADD application.json ${HOME}
9 |
10 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
11 | RUN echo 'Asia/Shanghai' >/etc/timezone
12 |
13 | WORKDIR ${HOME}
14 | ENTRYPOINT ["sh","-c","./natok-cli"]
15 |
16 | # docker build -f Dockerfile -t natok-cli:0.1 .
17 | # docker run --name=natok-cli --restart=always --net=host -d natok-cli:0.1
18 |
--------------------------------------------------------------------------------
/nac.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 | natok-cli
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 代码咖啡因
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/core/constant.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import "sync"
4 |
5 | // 数据包常量
6 | const (
7 | Uint8Size = 1
8 | Uint16Size = 2
9 | Uint32Size = 4
10 | Uint64Size = 8
11 | MaxPacketSize = 4 * 1 << 20 // 最大数据包大小为最大数据包大小为 4M
12 | )
13 |
14 | // 消息类型常量
15 | const (
16 | TypeAuth = 0x01 // 验证消息以检查访问密钥是否正确
17 | typeNoAvailablePort = 0x02 // 访问密钥没有可用端口
18 | TypeConnectNatok = 0xa1 //连接到NATOK服务
19 | TypeConnectIntra = 0xa2 //连接到内部服务
20 | TypeDisconnect = 0x04 // 断开
21 | TypeTransfer = 0x05 // 数据传输
22 | TypeIsInuseKey = 0x06 // 访问秘钥已在其他客户端使用
23 | TypeHeartbeat = 0x07 // 心跳
24 | TypeDisabledAccessKey = 0x08 // 禁用的访问密钥
25 | TypeDisabledTrialClient = 0x09 // 禁用的试用客户端
26 | TypeInvalidKey = 0x10 // 无效的访问密钥
27 | HeartbeatInterval = 10 //心跳间隔时长10秒
28 | )
29 |
30 | // Counter 计数器
31 | type Counter struct {
32 | mu sync.Mutex
33 | count int
34 | }
35 |
36 | func (c *Counter) Increment() {
37 | c.mu.Lock()
38 | defer c.mu.Unlock()
39 | c.count++
40 | }
41 |
42 | func (c *Counter) Decrement() {
43 | c.mu.Lock()
44 | defer c.mu.Unlock()
45 | c.count--
46 | }
47 |
48 | func (c *Counter) GetCount() int {
49 | c.mu.Lock()
50 | defer c.mu.Unlock()
51 | return c.count
52 | }
53 |
--------------------------------------------------------------------------------
/s-cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDWTCCAkGgAwIBAgIJAK2vWakY/TwYMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV
3 | BAYTAkNOMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg
4 | Q29tcGFueSBMdGQwIBcNMjAwOTExMDgyODQ4WhgPMjEyMDA4MTgwODI4NDhaMEIx
5 | CzAJBgNVBAYTAkNOMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0Rl
6 | ZmF1bHQgQ29tcGFueSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
7 | AQC/ar8oN+k5zXRF9Lsv/TN//K0mXmB/5poSFXzZjcqeOflDpdy3iV0FDd8/7OJW
8 | CU+5f91w78JSgLC9sth6cNQbSh0/Wu4xVmOV4uTQ6VSNAyfGsA5RiUWM2StfmvIh
9 | W5is9iDGffbzcuJda4ehQP2NtjJ/QVH3Dqor0sUcMQwi92DJjJORzqwRorkF9NHx
10 | NvHY0/oSPlkUkXX/w2xPPHME88sPpURMEc0j+yV9rTGhvLxqh6d3To9KhlkaieHo
11 | j+zI0C73E10z2sHzxLHXKxRCCX+4AK57oeyi8bSSB4IxIyaw+sxgvu1LTVknfewB
12 | GmmO0jjZfGkesfXnYOjrO30rAgMBAAGjUDBOMB0GA1UdDgQWBBR31XafwrSG1VCh
13 | SIaZ6E5lHVnB9DAfBgNVHSMEGDAWgBR31XafwrSG1VChSIaZ6E5lHVnB9DAMBgNV
14 | HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQC7Ai/2bklqngszXsQTH0B3loLn
15 | DUjE8yiL/1aryat09fB0HL/LfuKESkNnNLSWqH3mKzXgOcnx5zR7T+i/83kHgtkj
16 | kDn0saaFdVPudTYiJuYhsnVKnaQwGeGfPz3deiLT8qnNgBraw2148+OhyXi4v9y9
17 | CPrGQq58SaULPxr0SJCpcSsUaNRiBYKHHYfLrBv4nbJ3VOwRhpEI8pAQ7v2UlbCN
18 | 6wW115ZkeeZiIk5NMl4garHXBM+1vm0FqLUNwRIfTK2eX4n9NnZXepgDJFJXvL6x
19 | 8e/uTN4rKAHIBBxRZ0lUxd1C0I0dTNJBrc7aCVnahlaSOc1szZvPXM/JWX1f
20 | -----END CERTIFICATE-----
21 |
--------------------------------------------------------------------------------
/core/intra_handler.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import log "github.com/sirupsen/logrus"
4 |
5 | // IntraServerHandler struct 内网服务处理
6 | type IntraServerHandler struct {
7 | Uri string
8 | AccessKey string
9 | NatokHandler *NatokHandler
10 | connectHandler *ConnectHandler
11 | }
12 |
13 | // Encode 编码消息
14 | func (s *IntraServerHandler) Encode(msg interface{}) []byte {
15 | if msg == nil {
16 | return []byte{}
17 | }
18 | return msg.([]byte)
19 | }
20 |
21 | // Decode 解码消息
22 | func (s *IntraServerHandler) Decode(buf []byte) (interface{}, int) {
23 | return buf, len(buf)
24 | }
25 |
26 | // Receive 请求接收
27 | func (s *IntraServerHandler) Receive(connHandler *ConnectHandler, data interface{}) {
28 | if conn := connHandler.ConnHandler; conn != nil {
29 | msg := Message{Type: TypeTransfer, Data: data.([]byte)}
30 | conn.Write(msg)
31 | log.Debugf("intra receive message %s", connHandler.Name)
32 | }
33 | }
34 |
35 | // Error 错误处理
36 | func (s *IntraServerHandler) Error(connHandler *ConnectHandler) {
37 | if natokHandler := connHandler.ConnHandler; natokHandler != nil {
38 | msg := Message{Type: TypeDisconnect, Uri: s.Uri}
39 | natokHandler.Write(msg)
40 | connHandler.ConnHandler = nil
41 | }
42 | }
43 |
44 | // Failure 失败处理
45 | func (s *IntraServerHandler) Failure() {
46 | msg := Message{Type: TypeDisconnect, Uri: s.Uri}
47 | s.connectHandler.Write(msg)
48 | }
49 |
--------------------------------------------------------------------------------
/s-cert.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEpAIBAAKCAQEAv2q/KDfpOc10RfS7L/0zf/ytJl5gf+aaEhV82Y3Knjn5Q6Xc
3 | t4ldBQ3fP+ziVglPuX/dcO/CUoCwvbLYenDUG0odP1ruMVZjleLk0OlUjQMnxrAO
4 | UYlFjNkrX5ryIVuYrPYgxn3283LiXWuHoUD9jbYyf0FR9w6qK9LFHDEMIvdgyYyT
5 | kc6sEaK5BfTR8Tbx2NP6Ej5ZFJF1/8NsTzxzBPPLD6VETBHNI/slfa0xoby8aoen
6 | d06PSoZZGonh6I/syNAu9xNdM9rB88Sx1ysUQgl/uACue6HsovG0kgeCMSMmsPrM
7 | YL7tS01ZJ33sARppjtI42XxpHrH152Do6zt9KwIDAQABAoIBAEFjAoeHidjf8O8Q
8 | qXy8HoKC2tb3eDlYmZrB0lMyl1szbI2KM/pSJv9Z/MAGeE5xgdVY81jn3dZ29Wjn
9 | lgFFV3828wS4WBNscjo6NnWSrvo4cLbzXwDFRofVi3ZuJHX2pxG2Rf3n+5qvzNmi
10 | qMMRw0tMSLWlp40gakrsBb8alg2/IevSSxKjRMt5HsihNbbLydR2qSGNBcmsDKQB
11 | uLzwmpgPeyhUdvBeX+99yqwJ6AJ4GpDyi1dGgbsiK/Z5F0T8AwD0ic+LCkuWFCL0
12 | K2HBx/HsfrTpj5dGcDTxxFI1JdvJWcioPYvUlDcHg98i/zwng6eCx/d3Q+jzK5w3
13 | 6/NYwrECgYEA5i4FO8Va73hAACI005wEqjJRsapI5tI4ckFLtiwFnU385GdeNJzv
14 | CGtBMRCFpEAdVKmqs+EVHKntPg4+LzfYKnsyBA9qnns7kHjP2Efqfq/ixAOZMaeX
15 | ++pPgQJ2bEszjqaXakXdykoDbr21GAM1fPBoslY9OE51p1tOGGCKMZMCgYEA1OOX
16 | Rf6r75GfQd0JnvCys5iNZwnhKvbR7UvKjRDv09fZ6/Q5HmFZldmmxq0SGWmgQzSz
17 | UvVg5i8pc4DghMzJvDtCFSGgPTLn2xNTvECB8Jwm7o1rJw39/CnamMkadLg9Cb2i
18 | g8IMn9Mml+xx1hI9hsbL38vEKpoFNKFqvO1kpQkCgYEArfeCRRZ4EB2WYYN44aY9
19 | cFTvoZPN3YZs2w22p0zGQYm75PSrIqCpmHdXojmWh/ldMau6NJGdXzie8hPZs95F
20 | JnZN6vur3XPOJPbqP9C6zl0oynTdx8We/OquhBbUYizEHsCSF+QOKOGfjoca47cp
21 | KfCZcI/1XSUPjxlXAN2WFLkCgYASnPuC8StTPOYxugO3U9AsB7CFS8XWHdJo7vF8
22 | t/hgC0VQbf/4egZ9JZSBVmx4sFWEyrzLCg040vLK2H/I3KbewEec1V3PO/4tl1kA
23 | 4pr50I1O2ip+Naj5PSeRqDOZ9OnRSjVFU9gKuUlsiw3A68NZX1Q/8u7p0qGV4m8U
24 | qaTdEQKBgQCRNs3pSvr5MBst/TpLddrBBwRxBdZHZkgCecoKC9OtWG3llcKCO939
25 | LuDR8TcTGx6cga9CJsdxF7uxViRl1ggtRe8qZUwnTWLFdobPP6Zo4VpjJhtPANwl
26 | PIm4B0GReEUSmAcOCAERMZh4n/3BYUNRu/EMgEB3XEyt28JstoNDSQ==
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
5 | github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
8 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
9 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
11 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
12 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
13 | golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
14 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
15 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
17 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
18 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
19 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
20 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
21 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
22 |
--------------------------------------------------------------------------------
/core/intra_server_handler.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "github.com/kataras/golog"
5 | )
6 |
7 | // IntraServerHandler struct 内网服务处理
8 | type IntraServerHandler struct {
9 | Uri string
10 | AccessKey string
11 | PoolHandler *PoolHandler
12 | natokHandler *ConnectHandler
13 | }
14 |
15 | // Encode 编码消息
16 | func (i *IntraServerHandler) Encode(msg interface{}) []byte {
17 | if msg == nil {
18 | return []byte{}
19 | }
20 | return msg.([]byte)
21 | }
22 |
23 | // Decode 解码消息
24 | func (i *IntraServerHandler) Decode(buf []byte) (interface{}, int) {
25 | return buf, len(buf)
26 | }
27 |
28 | // Receive 请求接收
29 | func (i *IntraServerHandler) Receive(connHandler *ConnectHandler, data interface{}) {
30 | if connHandler.ConnHandler == nil {
31 | return
32 | }
33 | msg := Message{Type: TypeTransfer, Data: data.([]byte)}
34 | connHandler.ConnHandler.Write(msg)
35 | }
36 |
37 | // Success 成功 交换连接通道
38 | func (i *IntraServerHandler) Success(connHandler *ConnectHandler) {
39 | natokHandler, err := i.PoolHandler.Pull()
40 | if err != nil {
41 | golog.Errorf("Get Connection Uri:%s error:%+v", i.Uri, err)
42 | msg := Message{Type: TypeDisconnect, Uri: i.Uri}
43 | i.natokHandler.Write(msg)
44 | connHandler.Conn.Close()
45 | } else {
46 | natokHandler.ConnHandler = connHandler
47 | connHandler.ConnHandler = natokHandler
48 |
49 | msg := Message{Type: TypeConnect, Uri: i.Uri + "@" + i.AccessKey}
50 | natokHandler.Write(msg)
51 | golog.Infof("Intranet server connect success, notify natok server:%s", msg.Uri)
52 | }
53 | }
54 |
55 | // Error 错误处理
56 | func (i *IntraServerHandler) Error(connHandler *ConnectHandler) {
57 | conn := connHandler.ConnHandler
58 | if conn != nil {
59 | msg := Message{Type: TypeDisconnect, Uri: i.Uri}
60 | conn.Write(msg)
61 | conn.ConnHandler = nil
62 | }
63 | connHandler.MsgHandler = nil
64 | }
65 |
66 | // Failure 失败处理
67 | func (i *IntraServerHandler) Failure() {
68 | msg := Message{Type: TypeDisconnect, Uri: i.Uri}
69 | i.natokHandler.Write(msg)
70 | }
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NATOK ·  
2 |
3 |
4 |
5 |

6 |
7 |
8 |
9 |
10 | - 🌱 natok是一个将局域网内个人服务代理到公网可访问的内网穿透工具,基于tcp协议、支持udp协议,支持任何tcp上层协议(列如:http、https、ssh、telnet、data base、remote desktop....)。
11 | - 🤔 目前市面上提供类似服务的有:花生壳、natapp、ngrok等等。当然,这些工具都很优秀!但是免费提供的服务都很有限,想要有比较好的体验都需要支付一定的套餐费用,由于数据包会流经第三方,因此总归有些不太友好。
12 | - ⚡ natok-server与natok-cli都基于GO语言开发,几乎不存在并发问题。运行时的内存开销也很低,一般在几十M左右。所以很推荐自主搭建服务!
13 |
14 |
15 | **natok-cli的相关配置:conf.yaml**
16 | ```yaml
17 | natok:
18 | server:
19 | - host: natok1.cn #服务器地址:域名 或者 ip
20 | port: 1001 #服务器端口:可自定义
21 | #客户端访问密钥,从natok-server的web页面中C端列表里获取
22 | access-key: 74a7a42fcdc4ccb6c8641ce543fe2e07
23 | - host: natok2.cn
24 | port: 1001
25 | access-key: 74a7a42fcdc4ccb6c8641ce543fe2e07
26 | cert-key-path: s-cert.key #TSL加密密钥,可自己指定。注:需与server端保持一致
27 | cert-pem-path: s-cert.pem #TSL加密证书,可自己指定。注:需与server端保持一致
28 | log-file-path: out.log #程序日志输出配置
29 | ```
30 |
31 | - windows系统启动: 双击 natok-cli.exe
32 | ```powershell
33 | # 注册服务,自动提取管理员权限:
34 | natok-cli.exe install
35 | # 卸载服务,自动提取管理员权限:
36 | natok-cli.exe uninstall
37 | # 启停服务,自动提取管理员权限:
38 | natok-cli.exe start/stop
39 | # 启停服务,终端管理员权限
40 | net start/stop natok-cli
41 | ```
42 | - Linux系统启动:
43 | ```shell
44 | # 授予natok-cli可执权限
45 | chmod 755 natok-cli
46 | # 启动应用
47 | nohup ./natok-cli > /dev/null 2>&1 &
48 | ```
49 |
50 | ---
51 | **Go 1.13 及以上(推荐)**
52 | ```shell
53 | # 配置 GOPROXY 环境变量
54 | go env -w GO111MODULE=on
55 | go env -w GOPROXY=https://goproxy.io,direct
56 | ```
57 |
58 | 构建natok-cli可执行程序
59 |
60 | ```shell
61 | # 克隆项目
62 | git clone https://github.com/natokay/go-natok-cli.git
63 |
64 | # 进入项目目录
65 | cd go-natok-cli
66 |
67 | # 更新/下载依赖
68 | go mod tidy
69 | go mod vendor
70 |
71 | # 设置目标可执行程序操作系统构架,包括 386,amd64,arm
72 | go env -w GOARCH=amd64
73 |
74 | # 设置可执行程序运行操作系统,支持 darwin,freebsd,linux,windows
75 | go env -w GOOS=windows
76 |
77 | # golang windows 程序获取管理员权限(UAC)
78 | rsrc -manifest nac.manifest -o nac.syso
79 |
80 | # cd到main.go目录,打包命令
81 | go build
82 |
83 | # 启动程序
84 | ./natok-cli.exe
85 | ```
86 |
87 | ## 版本描述
88 | **natok:1.0.0**
89 | natok-cli与natok-server网络代理通信基本功能实现。
90 |
91 | **natok:1.1.0**
92 | natok-cli与natok-server支持windows平台注册为服务运行,可支持开机自启,保证服务畅通。
93 |
94 | **natok:1.2.0**
95 | natok-cli可与多个natok-server保持连接,支持从多个不同的natok-server来访问natok-cli,以实现更快及更优的网络通信。
96 |
97 | **natok:1.3.0**
98 | natok-cli与natok-server可支持udp网络代理。
99 |
--------------------------------------------------------------------------------
/core/core_handler.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | log "github.com/sirupsen/logrus"
5 | "net"
6 | "runtime/debug"
7 | "time"
8 | )
9 |
10 | // Message struct 消息体对象
11 | type Message struct {
12 | Type byte // 消息类型
13 | Serial string // 消息序列
14 | Net string // 网络类型
15 | Uri string // 消息头
16 | Data []byte // 消息体
17 | }
18 |
19 | // MsgHandler interface 消息处理接口
20 | type MsgHandler interface {
21 | Error(*ConnectHandler) //出错
22 | Encode(interface{}) []byte //编码
23 | Decode([]byte) (interface{}, int) //解码
24 | Receive(*ConnectHandler, interface{}) //接收
25 | }
26 |
27 | // ConnectHandler struct 通道链接载体
28 | type ConnectHandler struct {
29 | Name string //通道名称
30 | ReadTime time.Time //读取时间
31 | WriteTime time.Time //写入时间
32 | Active bool //是否活跃
33 | ReadBuf []byte //读取的内容
34 | Conn net.Conn //连接通道
35 | MsgHandler MsgHandler //消息句柄
36 | ConnHandler *ConnectHandler //连接句柄
37 | }
38 |
39 | // Write 消息写入
40 | func (c *ConnectHandler) Write(msg interface{}) {
41 | if c.MsgHandler == nil {
42 | return
43 | }
44 | data := c.MsgHandler.Encode(msg)
45 | c.WriteTime = time.Now()
46 | _, _ = c.Conn.Write(data)
47 | }
48 |
49 | // Listen 连接请求监听
50 | func (c *ConnectHandler) Listen() {
51 | defer func() {
52 | if err := recover(); err != nil {
53 | c.MsgHandler.Error(c)
54 | log.Warnf("Warn: %+v", err)
55 | debug.PrintStack()
56 | }
57 | }()
58 |
59 | if c.Conn == nil {
60 | return
61 | }
62 |
63 | c.Active = true
64 | c.ReadTime = time.Now()
65 |
66 | for c.Active {
67 | // 最大缓冲4M
68 | if c.ReadBuf != nil && len(c.ReadBuf) > MaxPacketSize {
69 | log.Error("Warn: This conn is error ! Packet max than 4M !")
70 | _ = c.Conn.Close()
71 | }
72 |
73 | // 最大包64kb
74 | buf := make([]byte, 1024*64)
75 | n, err := c.Conn.Read(buf)
76 | if err != nil || n == 0 {
77 | log.Errorf("Error: %+v", err)
78 | c.Active = false
79 | c.MsgHandler.Error(c)
80 | return
81 | }
82 |
83 | c.ReadTime = time.Now()
84 | if c.ReadBuf == nil {
85 | c.ReadBuf = buf[0:n]
86 | } else {
87 | c.ReadBuf = append(c.ReadBuf, buf[0:n]...)
88 | }
89 |
90 | for {
91 | msg, n := c.MsgHandler.Decode(c.ReadBuf)
92 | if msg == nil {
93 | break
94 | }
95 | c.MsgHandler.Receive(c, msg)
96 | c.ReadBuf = c.ReadBuf[n:]
97 | if len(c.ReadBuf) == 0 {
98 | break
99 | }
100 | }
101 |
102 | if len(c.ReadBuf) > 0 {
103 | buf := make([]byte, len(c.ReadBuf))
104 | copy(buf, c.ReadBuf)
105 | c.ReadBuf = buf
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/core/pool_handler.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "crypto/tls"
5 | "github.com/kataras/golog"
6 | "net"
7 | "sync"
8 | )
9 |
10 | // NatokPool interface NATOK连接池处理接口
11 | type NatokPool interface {
12 | Add(*PoolHandler) (*ConnectHandler, error) //添加进连接池
13 | Remove(*ConnectHandler) //移除出连接池
14 | IsActive(*ConnectHandler) bool //连接是否活跃
15 | }
16 |
17 | type ConnPooler struct {
18 | Addr string
19 | Conf *tls.Config
20 | }
21 |
22 | // PoolHandler struct
23 | type PoolHandler struct {
24 | Mu sync.Mutex //锁
25 | Pool NatokPool //池
26 | Conns []*ConnectHandler //连接
27 | }
28 |
29 | // GetConn 获取连接
30 | func (p *PoolHandler) GetConn() (*ConnectHandler, error) {
31 | p.Mu.Lock()
32 | defer p.Mu.Unlock()
33 |
34 | if len(p.Conns) == 0 {
35 | return nil, nil
36 | }
37 | conn := p.Conns[len(p.Conns)-1]
38 | p.Conns = p.Conns[:len(p.Conns)-1]
39 | if p.Pool.IsActive(conn) {
40 | return conn, nil
41 | } else {
42 | return nil, nil
43 | }
44 | }
45 |
46 | // Push 放入连接池
47 | func (p *PoolHandler) Push(conn *ConnectHandler) {
48 | p.Mu.Lock()
49 | defer p.Mu.Unlock()
50 | p.Conns = append(p.Conns, conn)
51 | }
52 |
53 | // Pull 从连接池取出
54 | func (p *PoolHandler) Pull() (*ConnectHandler, error) {
55 | for {
56 | if len(p.Conns) == 0 {
57 | conn, err := p.Pool.Add(p)
58 | if err != nil {
59 | return nil, err
60 | }
61 | return conn, nil
62 | } else {
63 | conn, err := p.GetConn()
64 | if conn != nil {
65 | return conn, err
66 | }
67 | }
68 | }
69 | }
70 |
71 | // Add 添加进连接池
72 | func (p *ConnPooler) Add(pool *PoolHandler) (*ConnectHandler, error) {
73 | var conn net.Conn
74 | var err error
75 |
76 | if p.Conf != nil {
77 | conn, err = tls.Dial("tcp", p.Addr, p.Conf)
78 | } else {
79 | conn, err = net.Dial("tcp", p.Addr)
80 | }
81 |
82 | if err != nil {
83 | golog.Errorf("Error:+v%", err)
84 | return nil, err
85 | }
86 |
87 | natokServerHandler := &NatokServerHandler{PoolHandler: pool}
88 | connHandler := &ConnectHandler{
89 | Active: true,
90 | Conn: conn,
91 | MsgHandler: interface{}(natokServerHandler).(MsgHandler),
92 | }
93 | natokServerHandler.ConnHandler = connHandler
94 | natokServerHandler.HeartBeat()
95 | go func() {
96 | connHandler.Listen(conn, natokServerHandler)
97 | }()
98 | return connHandler, nil
99 | }
100 |
101 | // Remove 移除出连接池
102 | func (p *ConnPooler) Remove(connHandler *ConnectHandler) {
103 | connHandler.Conn.Close()
104 | }
105 |
106 | // IsActive 连接是否活跃
107 | func (p *ConnPooler) IsActive(connHandler *ConnectHandler) bool {
108 | return connHandler.Active
109 | }
110 |
--------------------------------------------------------------------------------
/core/connect_handler.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "github.com/kataras/golog"
5 | "net"
6 | "runtime/debug"
7 | "time"
8 | )
9 |
10 | // MsgHandler interface 消息处理接口
11 | type MsgHandler interface {
12 | Success(*ConnectHandler) //成功
13 | Error(*ConnectHandler) //出错
14 | Encode(interface{}) []byte //编码
15 | Decode([]byte) (interface{}, int) //解码
16 | Receive(*ConnectHandler, interface{}) //接收
17 | }
18 |
19 | // Message struct 消息体对象
20 | type Message struct {
21 | Type byte
22 | SerialNum uint64
23 | Uri string
24 | Data []byte
25 | }
26 |
27 | // ConnectHandler struct 通道链接载体
28 | type ConnectHandler struct {
29 | ReadTime int64 //读取时间
30 | WriteTime int64 //写入时间
31 | Active bool //是否活跃
32 | ReadBuf []byte //读取的内容
33 | Conn net.Conn //连接通道
34 | MsgHandler MsgHandler //消息句柄
35 | ConnHandler *ConnectHandler //连接句柄
36 | }
37 |
38 | // Write 消息写入
39 | func (c *ConnectHandler) Write(msg interface{}) {
40 | if c.MsgHandler == nil {
41 | return
42 | }
43 | data := c.MsgHandler.Encode(msg)
44 | c.WriteTime = time.Now().Unix()
45 | c.Conn.Write(data)
46 | }
47 |
48 | // Listen 连接请求监听
49 | func (c *ConnectHandler) Listen(conn net.Conn, msgHandler interface{}) {
50 | defer func() {
51 | if err := recover(); err != nil {
52 | c.MsgHandler.Error(c)
53 | golog.Warnf("Warn: %+v", err)
54 | debug.PrintStack()
55 | }
56 | }()
57 |
58 | if conn == nil {
59 | return
60 | }
61 |
62 | c.Conn = conn
63 | c.Active = true
64 | c.ReadTime = time.Now().Unix()
65 | c.MsgHandler = msgHandler.(MsgHandler)
66 | c.MsgHandler.Success(c)
67 |
68 | for {
69 | buf := make([]byte, 1024*64)
70 | if c.ReadBuf != nil && len(c.ReadBuf) > MaxPacketSize {
71 | golog.Error("Warn: This conn is error ! Packet max than 4M !")
72 | c.Conn.Close()
73 | }
74 |
75 | n, err := c.Conn.Read(buf)
76 | if err != nil || n == 0 {
77 | golog.Errorf("Error:%v", err)
78 | //debug.PrintStack()
79 | c.Active = false
80 | c.MsgHandler.Error(c)
81 | break
82 | }
83 |
84 | c.ReadTime = time.Now().Unix()
85 | if c.ReadBuf == nil {
86 | c.ReadBuf = buf[0:n]
87 | } else {
88 | c.ReadBuf = append(c.ReadBuf, buf[0:n]...)
89 | }
90 |
91 | for {
92 | msg, n := c.MsgHandler.Decode(c.ReadBuf)
93 | if msg == nil {
94 | break
95 | }
96 | c.MsgHandler.Receive(c, msg)
97 | c.ReadBuf = c.ReadBuf[n:]
98 | if len(c.ReadBuf) == 0 {
99 | break
100 | }
101 | }
102 |
103 | if len(c.ReadBuf) > 0 {
104 | buf := make([]byte, len(c.ReadBuf))
105 | copy(buf, c.ReadBuf)
106 | c.ReadBuf = buf
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/conf/app_config.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | log "github.com/sirupsen/logrus"
5 | "gopkg.in/yaml.v2"
6 | "io"
7 | "os"
8 | "path/filepath"
9 | "regexp"
10 | "runtime"
11 | "strings"
12 | )
13 |
14 | var AppConf *AppConfig
15 |
16 | // AppConfig 应用配置
17 | type AppConfig struct {
18 | Natok Natok `yaml:"natok"`
19 | }
20 |
21 | type Natok struct {
22 | Server []Server `yaml:"server"` //服务器端
23 | CertKeyPath string `yaml:"cert-key-path"` //密钥路径
24 | CertPemPath string `yaml:"cert-pem-path"` //证书路径
25 | LogFilePath string `yaml:"log-file-path"` //日志路径
26 | LogDebugLevel bool `yaml:"log-debug-level"` //Debug日志
27 | }
28 |
29 | // Server NATOK服务配置
30 | type Server struct {
31 | InetHost string `yaml:"host"` // 服务器地址
32 | InetPort int `yaml:"port"` // 服务器端口
33 | AccessKey string `yaml:"access-key"` //访问秘钥
34 |
35 | }
36 |
37 | // AppConfig Load 加载配置
38 | func init() {
39 | baseDir := getCurrentAbPath()
40 | // 读取文件内容
41 | file, err := os.ReadFile(baseDir + "conf.yaml")
42 | if err != nil {
43 | log.Error(err)
44 | panic(err)
45 | }
46 | // 利用json转换为AppConfig
47 | appConfig := new(AppConfig)
48 | err = yaml.Unmarshal(file, appConfig)
49 | if err != nil {
50 | log.Error(err)
51 | panic(err)
52 | }
53 | conf := &appConfig.Natok
54 | compile, err := regexp.Compile("^/|^\\\\|^[a-zA-Z]:")
55 | // 密钥文件
56 | if conf.CertKeyPath != "" && !compile.MatchString(conf.CertKeyPath) {
57 | log.Infof("%s -> %s", conf.CertKeyPath, baseDir+conf.CertKeyPath)
58 | conf.CertKeyPath = baseDir + conf.CertKeyPath
59 | }
60 | // 证书文件
61 | if conf.CertPemPath != "" && !compile.MatchString(conf.CertPemPath) {
62 | log.Infof("%s -> %s", conf.CertPemPath, baseDir+conf.CertPemPath)
63 | conf.CertPemPath = baseDir + conf.CertPemPath
64 | }
65 |
66 | // 日志记录配置
67 | log.SetLevel(log.InfoLevel)
68 | log.SetFormatter(&log.TextFormatter{
69 | FullTimestamp: true,
70 | ForceColors: true,
71 | TimestampFormat: "2006-01-02 15:04:05.000",
72 | })
73 | // 在输出日志中添加文件名和方法信息
74 | if appConfig.Natok.LogDebugLevel {
75 | log.SetReportCaller(true)
76 | log.SetLevel(log.DebugLevel)
77 | }
78 | // 日志记录输出文件
79 | if conf.LogFilePath != "" && !compile.MatchString(conf.LogFilePath) {
80 | log.Infof("%s -> %s", conf.LogFilePath, baseDir+conf.LogFilePath)
81 | conf.LogFilePath = baseDir + conf.LogFilePath
82 | logFile, err := os.OpenFile(conf.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
83 | if err != nil {
84 | log.Fatal(err)
85 | } else {
86 | // 组合一下即可,os.Stdout代表标准输出流
87 | multiWriter := io.MultiWriter(logFile, os.Stdout)
88 | log.SetOutput(multiWriter)
89 | }
90 | }
91 | AppConf = appConfig
92 | }
93 |
94 | // 最终方案-全兼容
95 | func getCurrentAbPath() string {
96 | dir := getCurrentAbPathByExecutable()
97 | if strings.Contains(dir, getTmpDir()) {
98 | dir = getCurrentAbPathByCaller()
99 | }
100 | return dir + "/"
101 | }
102 |
103 | // 获取系统临时目录,兼容go run
104 | func getTmpDir() string {
105 | dir := os.Getenv("TEMP")
106 | if dir == "" {
107 | dir = os.Getenv("TMP")
108 | }
109 | res, _ := filepath.EvalSymlinks(dir)
110 | return res
111 | }
112 |
113 | // 获取当前执行文件绝对路径
114 | func getCurrentAbPathByExecutable() string {
115 | exePath, err := os.Executable()
116 | if err != nil {
117 | log.Fatal(err)
118 | }
119 | res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))
120 | return res
121 | }
122 |
123 | // 获取当前执行文件绝对路径(go run)
124 | func getCurrentAbPathByCaller() string {
125 | var abPath string
126 | if _, filename, _, ok := runtime.Caller(0); ok {
127 | if lst := strings.LastIndex(filename, "/conf"); lst != -1 {
128 | abPath = filename[0:lst]
129 | }
130 | }
131 | return abPath
132 | }
133 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/tls"
5 | "crypto/x509"
6 | "github.com/kardianos/service"
7 | log "github.com/sirupsen/logrus"
8 | "natok-cli/conf"
9 | "natok-cli/core"
10 | "net"
11 | "os"
12 | "strconv"
13 | "time"
14 | )
15 |
16 | type Program struct{}
17 |
18 | func (p *Program) Start(s service.Service) error {
19 | go p.run()
20 | return nil
21 | }
22 |
23 | func (p *Program) run() {
24 | log.Info("Started natok client service")
25 | Start()
26 | }
27 |
28 | func (p *Program) Stop(s service.Service) error {
29 | log.Info("Stop natok client service")
30 | return nil
31 | }
32 |
33 | // 程序入口
34 | func main() {
35 | svcConfig := &service.Config{
36 | Name: "natok-cli",
37 | DisplayName: "Natok Client Service",
38 | Description: "Go语言实现的内网代理客户端服务",
39 | }
40 |
41 | prg := &Program{}
42 | s, err := service.New(prg, svcConfig)
43 | if err != nil {
44 | log.Fatal(err)
45 | }
46 |
47 | if len(os.Args) > 1 {
48 | if os.Args[1] == "install" {
49 | if se := s.Install(); se != nil {
50 | log.Errorf("Service installation failed. %+v", se)
51 | } else {
52 | log.Info("Service installed")
53 | }
54 | return
55 | }
56 | if os.Args[1] == "uninstall" {
57 | if se := s.Uninstall(); se != nil {
58 | log.Errorf("Service uninstall failed. %+v", se)
59 | } else {
60 | log.Info("Service uninstalled")
61 | }
62 | return
63 | }
64 | if os.Args[1] == "start" {
65 | if se := s.Start(); se != nil {
66 | log.Errorf("Service start failed. %+v", se)
67 | } else {
68 | log.Info("Service startup completed")
69 | }
70 | return
71 | }
72 | if os.Args[1] == "restart" {
73 | if se := s.Restart(); se != nil {
74 | log.Errorf("Service restart failed. %+v", se)
75 | } else {
76 | log.Info("Service restart completed")
77 | }
78 | return
79 | }
80 | if os.Args[1] == "stop" {
81 | if se := s.Stop(); se != nil {
82 | log.Errorf("Service stop failed. %+v", se)
83 | } else {
84 | log.Info("Service stop completed")
85 | }
86 | return
87 | }
88 | }
89 |
90 | if err = s.Run(); err != nil {
91 | log.Fatal(err)
92 | }
93 | }
94 |
95 | // Start 启动主服务
96 | func Start() {
97 | doRun := func(server conf.Server) {
98 | addr := server.InetHost + ":" + strconv.Itoa(server.InetPort)
99 | tlsConfig := TlsConfig()
100 | poolHandler := &core.NatokHandler{
101 | Conf: &core.NatokConnConfig{
102 | Addr: addr,
103 | Conf: tlsConfig,
104 | },
105 | Conns: make([]*core.ConnectHandler, 0, 10),
106 | }
107 |
108 | connHandler := &core.ConnectHandler{Name: "Main"}
109 |
110 | for {
111 | connHandler.Conn = Connect(addr, tlsConfig)
112 | natokServerHandler := &core.NatokServerHandler{
113 | AccessKey: server.AccessKey,
114 | NatokHandler: poolHandler,
115 | ConnHandler: connHandler,
116 | }
117 | connHandler.MsgHandler = natokServerHandler
118 |
119 | natokServerHandler.HeartBeat()
120 | natokServerHandler.Auth()
121 | connHandler.Listen()
122 | }
123 | }
124 | // 调用
125 | for idx, server := range conf.AppConf.Natok.Server {
126 | go func(ser conf.Server) { doRun(ser) }(server)
127 | log.Infof("Listen: %d, %s", idx+1, server.InetHost)
128 | }
129 | }
130 |
131 | // Connect 向NATOK-SERVER发起连接
132 | func Connect(addr string, conf *tls.Config) net.Conn {
133 | retry := 0
134 | for {
135 | var conn net.Conn
136 | var err error
137 |
138 | if conf != nil {
139 | conn, err = tls.Dial("tcp", addr, conf)
140 | } else {
141 | conn, err = net.Dial("tcp", addr)
142 | }
143 |
144 | if err != nil {
145 | retry += 1
146 | if retry > 1000000 {
147 | retry = 1
148 | }
149 | if retry%30 == 0 {
150 | log.Warnf("Connection to natok server exception! retry: %d Addr: %s, Error: %+v", retry, addr, err)
151 | }
152 | time.Sleep(time.Second * 2)
153 | continue
154 | }
155 |
156 | return conn
157 | }
158 | }
159 |
160 | // TlsConfig TSL协议配置
161 | func TlsConfig() *tls.Config {
162 | tlsConf := conf.AppConf.Natok
163 | cert, err := tls.LoadX509KeyPair(tlsConf.CertPemPath, tlsConf.CertKeyPath)
164 | if err != nil {
165 | log.Error(err)
166 | }
167 | certBytes, err := os.ReadFile(tlsConf.CertPemPath)
168 | if err != nil {
169 | log.Fatal("Unable to read cert.pem")
170 | }
171 | clientCertPool := x509.NewCertPool()
172 | ok := clientCertPool.AppendCertsFromPEM(certBytes)
173 | if !ok {
174 | log.Fatal("failed to parse root certificate")
175 | }
176 | return &tls.Config{
177 | RootCAs: clientCertPool,
178 | Certificates: []tls.Certificate{cert},
179 | InsecureSkipVerify: true,
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/core/natok_server_handler.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "crypto/tls"
5 | "encoding/binary"
6 | "github.com/kataras/golog"
7 | "net"
8 | "os"
9 | "time"
10 | )
11 |
12 | // NatokServerHandler struct NATOK服务处理
13 | type NatokServerHandler struct {
14 | Chan chan struct{}
15 | AccessKey string
16 | PoolHandler *PoolHandler
17 | ConnHandler *ConnectHandler
18 | }
19 |
20 | // NatokConfig struct 地址配置
21 | type NatokConfig struct {
22 | Addr string
23 | Conf *tls.Config
24 | }
25 |
26 | // Encode 编码消息
27 | func (n *NatokServerHandler) Encode(inMsg interface{}) []byte {
28 | if inMsg == nil {
29 | return []byte{}
30 | }
31 | msg := inMsg.(Message)
32 | buf := make([]byte, 8)
33 | binary.BigEndian.PutUint64(buf, msg.SerialNum)
34 |
35 | uriBytes := []byte(msg.Uri)
36 | bodyLen := TypeSize + SerialNumSize + UriLenSize + len(uriBytes) + len(msg.Data)
37 |
38 | data := make([]byte, HeaderSize, bodyLen+HeaderSize)
39 | binary.BigEndian.PutUint32(data, uint32(bodyLen))
40 |
41 | data = append(data, msg.Type)
42 | data = append(data, buf...)
43 | data = append(data, byte(len(uriBytes)))
44 | data = append(data, uriBytes...)
45 | data = append(data, msg.Data...)
46 | return data
47 | }
48 |
49 | // Decode 解码消息
50 | func (n *NatokServerHandler) Decode(buf []byte) (interface{}, int) {
51 | HeaderBytes := buf[0:HeaderSize]
52 | HeaderLen := binary.BigEndian.Uint32(HeaderBytes)
53 |
54 | if uint32(len(buf)) < HeaderLen+HeaderSize {
55 | return nil, 0
56 | }
57 |
58 | num := int(HeaderLen + HeaderSize)
59 | body := buf[HeaderSize:num]
60 |
61 | uriLen := uint8(body[SerialNumSize+TypeSize])
62 | msg := Message{
63 | Type: body[0],
64 | SerialNum: binary.BigEndian.Uint64(body[TypeSize : SerialNumSize+TypeSize]),
65 | Uri: string(body[SerialNumSize+TypeSize+UriLenSize : SerialNumSize+TypeSize+UriLenSize+uriLen]),
66 | Data: body[SerialNumSize+TypeSize+UriLenSize+uriLen:],
67 | }
68 | return msg, num
69 | }
70 |
71 | // Receive 请求接收
72 | func (n *NatokServerHandler) Receive(connHandler *ConnectHandler, msgData interface{}) {
73 | msg := msgData.(Message)
74 | //golog.Println("Received connect message:", msg.Uri, "=>", string(msg.Data))
75 | switch msg.Type {
76 | case TypeConnect:
77 | go func() {
78 | intraServerHandler := &IntraServerHandler{
79 | Uri: msg.Uri,
80 | natokHandler: connHandler,
81 | AccessKey: n.AccessKey,
82 | PoolHandler: n.PoolHandler,
83 | }
84 | addr := string(msg.Data)
85 | conn, err := net.Dial("tcp", addr)
86 | if err != nil {
87 | golog.Errorf("Failed to connect intranet server! %+v", err)
88 | intraServerHandler.Failure()
89 | } else {
90 | connectHandler := &ConnectHandler{Conn: conn}
91 | connectHandler.Listen(conn, intraServerHandler)
92 | }
93 | }()
94 | case TypeTransfer:
95 | if connHandler.ConnHandler != nil {
96 | connHandler.ConnHandler.Write(msg.Data)
97 | }
98 | case TypeDisconnect:
99 | if connHandler.ConnHandler != nil {
100 | connHandler.ConnHandler.Conn.Close()
101 | connHandler.ConnHandler = nil
102 | }
103 | if n.AccessKey == "" {
104 | n.PoolHandler.Push(connHandler)
105 | }
106 | case typeNoAvailablePort:
107 | golog.Warn("There are no available ports for the natok access key.")
108 | case TypeDisabledAccessKey:
109 | golog.Info("Natok access key is disabled.")
110 | n.Close(connHandler)
111 | os.Exit(1)
112 | case TypeInvalidKey:
113 | golog.Info("Natok access key is not valid.")
114 | n.Close(connHandler)
115 | os.Exit(1)
116 | case TypeIsInuseKey:
117 | golog.Info("Natok access key is in use by other natok client.")
118 | golog.Info("If you want to have exclusive natok service")
119 | golog.Info("please visit 'www.natok.cn' for more details.")
120 | n.Close(connHandler)
121 | os.Exit(1)
122 | case TypeDisabledTrialClient:
123 | golog.Info("Your natok client is overuse.")
124 | golog.Info("The trial natok access key can only be used for 20 minutes in 24 hours.")
125 | golog.Info("If you want to have exclusive natok service")
126 | golog.Info("please visit 'www.natok.cn' for more details.")
127 | n.Close(connHandler)
128 | os.Exit(1)
129 | }
130 | }
131 |
132 | // Success 认证成功
133 | func (n *NatokServerHandler) Success(connHandler *ConnectHandler) {
134 | if n.AccessKey == "" {
135 | return
136 | }
137 | msg := Message{Type: TypeAuth, Uri: n.AccessKey}
138 | connHandler.Write(msg)
139 | }
140 |
141 | // Error 错误处理
142 | func (n *NatokServerHandler) Error(connHandler *ConnectHandler) {
143 | if n.Chan != nil {
144 | close(n.Chan)
145 | }
146 | handler := connHandler.ConnHandler
147 | if handler != nil {
148 | if handler.Conn != nil {
149 | handler.Conn.Close()
150 | handler.Conn = nil
151 | }
152 | handler.ConnHandler = nil
153 | }
154 | connHandler.ConnHandler = nil
155 | connHandler.MsgHandler = nil
156 | time.Sleep(time.Second * 3)
157 | }
158 |
159 | // Close 关闭连接通道
160 | func (n *NatokServerHandler) Close(connHandler *ConnectHandler) {
161 | if n.Chan != nil {
162 | close(n.Chan)
163 | }
164 | if connHandler.ConnHandler != nil {
165 | if connHandler.ConnHandler.Conn != nil {
166 | connHandler.ConnHandler.Conn.Close()
167 | connHandler.ConnHandler.Conn = nil
168 | connHandler.ConnHandler.ConnHandler = nil
169 | }
170 | connHandler.ConnHandler = nil
171 | //handler.Conn.Close()
172 | }
173 | if n.ConnHandler != nil && n.ConnHandler.Conn != nil {
174 | n.ConnHandler.Conn.Close()
175 | n.ConnHandler.Conn = nil
176 | }
177 | n.ConnHandler = nil
178 | connHandler.MsgHandler = nil
179 | }
180 |
181 | // HeartBeat 发送心跳包 -> NATOK-SERVER
182 | func (n *NatokServerHandler) HeartBeat() {
183 | n.Chan = make(chan struct{})
184 | go func() {
185 | for {
186 | select {
187 | case <-time.After(time.Second * HeartbeatInterval):
188 | if time.Now().Unix()-n.ConnHandler.ReadTime >= 2*TypeHeartbeat {
189 | golog.Error("Natok connection timeout")
190 | if n.ConnHandler != nil && n.ConnHandler.Conn != nil {
191 | n.ConnHandler.Conn.Close()
192 | }
193 | return
194 | }
195 | msg := Message{Type: TypeHeartbeat}
196 | n.ConnHandler.Write(msg)
197 | case <-n.Chan:
198 | return
199 | }
200 | }
201 | }()
202 | }
203 |
--------------------------------------------------------------------------------
/core/natok_handler.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "crypto/tls"
5 | "encoding/binary"
6 | "fmt"
7 | log "github.com/sirupsen/logrus"
8 | "net"
9 | "os"
10 | "time"
11 | )
12 |
13 | // NatokServerHandler struct NATOK服务处理
14 | type NatokServerHandler struct {
15 | Chan chan struct{}
16 | AccessKey string //密钥
17 | source string //来源
18 | target string //目标
19 | NatokHandler *NatokHandler
20 | ConnHandler *ConnectHandler
21 | }
22 |
23 | // NatokConfig struct 地址配置
24 | type NatokConfig struct {
25 | Addr string
26 | Conf *tls.Config
27 | }
28 |
29 | // NatokHandler struct // Natok句柄
30 | type NatokHandler struct {
31 | count Counter //数量
32 | Conf *NatokConnConfig //配置
33 | Conns []*ConnectHandler //连接
34 | }
35 |
36 | type NatokConnConfig struct {
37 | Addr string
38 | Conf *tls.Config
39 | }
40 |
41 | // Get 获取连接
42 | func (p *NatokConnConfig) Get() (*ConnectHandler, error) {
43 | var conn net.Conn
44 | var err error
45 | retries := 5
46 | dialer := net.Dialer{Timeout: 2 * time.Second}
47 | for tries := 0; tries <= retries; tries++ {
48 | if p.Conf != nil {
49 | conn, err = tls.DialWithDialer(&dialer, "tcp", p.Addr, p.Conf)
50 | } else {
51 | conn, err = dialer.Dial("tcp", p.Addr)
52 | }
53 | if err == nil {
54 | break
55 | }
56 | if tries > 0 {
57 | log.Warnf("Connect natok-server failed, Retries: %d/%d, Error: %+v", tries, retries, err)
58 | }
59 | time.Sleep(200 * time.Millisecond)
60 | }
61 | // 如果未连接成功
62 | if err != nil {
63 | log.Errorf("Connect natok-server failed, Error: %+v", err)
64 | return nil, err
65 | }
66 | connHandler := &ConnectHandler{
67 | Name: "natok-server-子集",
68 | Active: true,
69 | Conn: conn,
70 | }
71 | return connHandler, nil
72 | }
73 |
74 | // Encode 编码消息
75 | func (s *NatokServerHandler) Encode(inMsg interface{}) []byte {
76 | if inMsg == nil {
77 | return []byte{}
78 | }
79 | msg := inMsg.(Message)
80 | serialBytes := []byte(msg.Serial)
81 | netBytes := []byte(msg.Net)
82 | UriBytes := []byte(msg.Uri)
83 | // byte=Uint8Size,3个string=Uint8Size*3,+data
84 | dataLen := Uint8Size + Uint8Size*3 + len(serialBytes) + len(netBytes) + len(UriBytes) + len(msg.Data)
85 | data := make([]byte, Uint32Size, Uint32Size+dataLen)
86 | binary.BigEndian.PutUint32(data, uint32(dataLen))
87 |
88 | data = append(data, msg.Type)
89 | data = append(data, byte(len(serialBytes)))
90 | data = append(data, byte(len(netBytes)))
91 | data = append(data, byte(len(UriBytes)))
92 | data = append(data, serialBytes...)
93 | data = append(data, netBytes...)
94 | data = append(data, UriBytes...)
95 | data = append(data, msg.Data...)
96 | return data
97 | }
98 |
99 | // Decode 解码消息
100 | func (s *NatokServerHandler) Decode(buf []byte) (interface{}, int) {
101 | headerBytes := buf[0:Uint32Size]
102 | headerLen := binary.BigEndian.Uint32(headerBytes)
103 | // 来自客户端的包,校验完整性。
104 | if uint32(len(buf)) < headerLen+Uint32Size {
105 | return nil, 0
106 | }
107 |
108 | head := int(Uint32Size + headerLen)
109 | body := buf[Uint32Size:head]
110 | serialLen := int(body[Uint8Size])
111 | netLen := int(body[Uint8Size*2])
112 | uriLen := int(body[Uint8Size*3])
113 | msg := Message{
114 | Type: body[0],
115 | Serial: string(body[Uint8Size*4 : Uint8Size*4+serialLen]),
116 | Net: string(body[Uint8Size*4+serialLen : Uint8Size*4+serialLen+netLen]),
117 | Uri: string(body[Uint8Size*4+serialLen+netLen : Uint8Size*4+serialLen+netLen+uriLen]),
118 | Data: body[Uint8Size*4+serialLen+netLen+uriLen:],
119 | }
120 | return msg, head
121 | }
122 |
123 | // Receive 请求接收
124 | func (s *NatokServerHandler) Receive(connHandler *ConnectHandler, msgData interface{}) {
125 | msg := msgData.(Message)
126 | //log.Println("Received connect message:", msg.Uri, "=>", string(msg.Data))
127 | switch msg.Type {
128 | // 连接到natok服务
129 | case TypeConnectNatok:
130 | go func() {
131 | log.Debugf("1-1 ===== From natok server message: %s %s", msg.Serial, string(msg.Data))
132 | if natokHandler, err := s.NatokHandler.Conf.Get(); err == nil {
133 | natokServerHandler := &NatokServerHandler{
134 | AccessKey: s.AccessKey,
135 | ConnHandler: natokHandler,
136 | }
137 | log.Debugf("1-2 =====Connect natok, Listen natok server message: %s %s", msg.Serial, string(msg.Data))
138 | natokHandler.MsgHandler = natokServerHandler
139 | natokServerHandler.HeartBeat()
140 | natokHandler.Write(Message{Type: TypeConnectNatok, Serial: msg.Serial, Uri: s.AccessKey})
141 | natokHandler.Listen()
142 | log.Debugf("1-3 =====Disconnect natok, Listen natok server message: %s %s", msg.Serial, string(msg.Data))
143 | } else {
144 | log.Errorf("1-e =====Connect natok server failed, Message: %s %s, Error: %+v", msg.Serial, string(msg.Data), err)
145 | }
146 | }()
147 | // 连接到内部服务
148 | case TypeConnectIntra:
149 | go func() {
150 | network := msg.Net
151 | addr := string(msg.Data)
152 | s.source = fmt.Sprintf("%s://%s", network, addr)
153 | s.target = msg.Uri
154 | sprintf := fmt.Sprintf("%s %s -> %s", msg.Serial, s.source, s.target)
155 | log.Debugf("2-1 ===== From natok server message: %s", sprintf)
156 | if conn, err := net.Dial(network, addr); err == nil {
157 | intraHandler := &ConnectHandler{Name: network + addr, Conn: conn, Active: true, ConnHandler: connHandler}
158 | intraHandler.MsgHandler = &IntraServerHandler{
159 | Uri: msg.Uri,
160 | AccessKey: s.AccessKey,
161 | connectHandler: connHandler,
162 | }
163 | connHandler.ConnHandler = intraHandler
164 | connHandler.Write(Message{Type: TypeConnectIntra, Serial: msg.Serial, Uri: s.AccessKey})
165 | log.Debugf("2-2 =====Connect intranet, Listen natok server message: %s", sprintf)
166 | intraHandler.Listen()
167 | log.Debugf("2-3 =====Disconnect intranet, Listen natok server message: %s", sprintf)
168 | } else {
169 | log.Errorf("2-e =====Connect intranet server failed, Message: %s, Error: %+v", sprintf, err)
170 | }
171 | }()
172 | // 传输数据 - 转发内部服务
173 | case TypeTransfer:
174 | sprintf := fmt.Sprintf("%s %s -> %s", msg.Serial, s.source, s.target)
175 | log.Debugf("3-1 =====TypeTransfer natok server message: %s", sprintf)
176 | if conn := connHandler.ConnHandler; conn != nil {
177 | log.Debugf("3-2 =====TypeTransfer intranet server message: %s", sprintf)
178 | conn.Write(msg.Data)
179 | }
180 | // 关闭连接 - 断开内部服务
181 | case TypeDisconnect:
182 | sprintf := fmt.Sprintf("%s %s -> %s", msg.Serial, s.source, s.target)
183 | log.Debugf("4-1 =====TypeDisconnect natok server message: %s", sprintf)
184 | if conn := connHandler.ConnHandler; conn != nil {
185 | _ = conn.Conn.Close()
186 | connHandler.ConnHandler = nil
187 | }
188 | case typeNoAvailablePort:
189 | log.Warnf("Natok access key %s no available ports.", msg.Uri)
190 | case TypeDisabledAccessKey:
191 | log.Warnf("Natok access key %s is disabled.", msg.Uri)
192 | case TypeInvalidKey:
193 | log.Errorf("Natok access key %s is not valid.", msg.Uri)
194 | s.Close(connHandler)
195 | os.Exit(1)
196 | case TypeIsInuseKey:
197 | log.Warnf("Natok access key %s is in use by other natok client.", msg.Uri)
198 | s.Close(connHandler)
199 | os.Exit(1)
200 | case TypeDisabledTrialClient:
201 | log.Infof("Natok access key %s is overuse.", msg.Uri)
202 | s.Close(connHandler)
203 | os.Exit(1)
204 | }
205 | }
206 |
207 | // Auth 认证成功
208 | func (s *NatokServerHandler) Auth() {
209 | if s.AccessKey == "" {
210 | return
211 | }
212 | msg := Message{Type: TypeAuth, Serial: "1", Net: "tcp", Uri: s.AccessKey, Data: []byte("8888")}
213 | s.ConnHandler.Write(msg)
214 | }
215 |
216 | // Error 错误处理
217 | func (s *NatokServerHandler) Error(connHandler *ConnectHandler) {
218 | if s.Chan != nil {
219 | close(s.Chan)
220 | }
221 | intraHandler := connHandler.ConnHandler
222 | if intraHandler != nil {
223 | if conn := intraHandler.Conn; conn != nil {
224 | _ = conn.Close()
225 | }
226 | intraHandler.ConnHandler = nil
227 | }
228 | connHandler.ConnHandler = nil
229 | connHandler.MsgHandler = nil
230 | time.Sleep(time.Second * 3)
231 | }
232 |
233 | // Close 关闭连接通道
234 | func (s *NatokServerHandler) Close(connHandler *ConnectHandler) {
235 | if s.Chan != nil {
236 | close(s.Chan)
237 | }
238 | if intraHandler := connHandler.ConnHandler; intraHandler != nil {
239 | if intraHandler.Conn != nil {
240 | intraHandler.Active = false
241 | _ = intraHandler.Conn.Close()
242 | intraHandler.Conn = nil
243 | intraHandler.ConnHandler = nil
244 | }
245 | connHandler.ConnHandler = nil
246 | }
247 | if connHandler.Conn != nil {
248 | connHandler.Active = false
249 | _ = connHandler.Conn.Close()
250 | connHandler.Conn = nil
251 | }
252 | s.ConnHandler = nil
253 | connHandler.MsgHandler = nil
254 | }
255 |
256 | // HeartBeat 发送心跳包 -> NATOK-SERVER
257 | func (s *NatokServerHandler) HeartBeat() {
258 | s.Chan = make(chan struct{})
259 | go func() {
260 | for {
261 | now := time.Now()
262 | select {
263 | case <-time.After(10 * time.Second):
264 | // 若通道在30s内未收到过数据,则发送一次心跳包。
265 | if now.Sub(s.ConnHandler.ReadTime) >= 30*time.Second {
266 | msg := Message{Type: TypeHeartbeat, Uri: s.AccessKey}
267 | s.ConnHandler.Write(msg)
268 | }
269 | case <-s.Chan:
270 | return
271 | }
272 | }
273 | }()
274 | }
275 |
--------------------------------------------------------------------------------
/grid-snake.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------