add
This commit is contained in:
parent
a1a1f0462a
commit
93ab736d55
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, build with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
FROM golang AS build-env
|
||||||
|
|
||||||
|
RUN GO111MODULE=off go get -u github.com/esrrhs/pingtunnel
|
||||||
|
RUN GO111MODULE=off go get -u github.com/esrrhs/pingtunnel/...
|
||||||
|
RUN GO111MODULE=off go install github.com/esrrhs/pingtunnel
|
||||||
|
|
||||||
|
FROM debian
|
||||||
|
COPY --from=build-env /go/bin/pingtunnel .
|
||||||
|
COPY GeoLite2-Country.mmdb .
|
||||||
|
WORKDIR ./
|
BIN
GeoLite2-Country.mmdb
Normal file
BIN
GeoLite2-Country.mmdb
Normal file
Binary file not shown.
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 zhao xin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
73
README.md
Normal file
73
README.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# Pingtunnel
|
||||||
|
|
||||||
|
[<img src="https://img.shields.io/github/license/esrrhs/pingtunnel">](https://github.com/esrrhs/pingtunnel)
|
||||||
|
[<img src="https://img.shields.io/github/languages/top/esrrhs/pingtunnel">](https://github.com/esrrhs/pingtunnel)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/esrrhs/pingtunnel)](https://goreportcard.com/report/github.com/esrrhs/pingtunnel)
|
||||||
|
[<img src="https://img.shields.io/github/v/release/esrrhs/pingtunnel">](https://github.com/esrrhs/pingtunnel/releases)
|
||||||
|
[<img src="https://img.shields.io/github/workflow/status/esrrhs/pingtunnel/Go">](https://github.com/esrrhs/pingtunnel/actions)
|
||||||
|
|
||||||
|
Pingtunnel is a tool that send TCP/UDP traffic over ICMP.
|
||||||
|
|
||||||
|
## Note: This tool is only to be used for study and research, do not use it for illegal purposes
|
||||||
|
|
||||||
|
![image](network.jpg)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Install server
|
||||||
|
|
||||||
|
- First prepare a server with a public IP, such as EC2 on AWS, assuming the domain name or public IP is www.yourserver.com
|
||||||
|
- Download the corresponding installation package from [releases](https://github.com/esrrhs/pingtunnel/releases), such as pingtunnel_linux64.zip, then decompress and execute with **root** privileges
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo wget (link of latest release)
|
||||||
|
sudo unzip pingtunnel_linux64.zip
|
||||||
|
sudo ./pingtunnel -type server
|
||||||
|
```
|
||||||
|
|
||||||
|
- (Optional) Disable system default ping
|
||||||
|
|
||||||
|
```
|
||||||
|
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install the client
|
||||||
|
|
||||||
|
- Download the corresponding installation package from [releases](https://github.com/esrrhs/pingtunnel/releases), such as pingtunnel_windows64.zip, and decompress it
|
||||||
|
- Then run with **administrator** privileges. The commands corresponding to different forwarding functions are as follows.
|
||||||
|
- If you see a log of ping pong, the connection is normal
|
||||||
|
|
||||||
|
#### Forward sock5
|
||||||
|
|
||||||
|
```
|
||||||
|
pingtunnel.exe -type client -l: 4455 -s www.yourserver.com -sock5 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Forward tcp
|
||||||
|
|
||||||
|
```
|
||||||
|
pingtunnel.exe -type client -l: 4455 -s www.yourserver.com -t www.yourserver.com:4455 -tcp 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Forward udp
|
||||||
|
|
||||||
|
```
|
||||||
|
pingtunnel.exe -type client -l: 4455 -s www.yourserver.com -t www.yourserver.com:4455
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use Docker
|
||||||
|
It can also be started directly with docker, which is more convenient. Same parameters as above
|
||||||
|
- server:
|
||||||
|
```
|
||||||
|
docker run --name pingtunnel-server -d --privileged --network host --restart=always esrrhs/pingtunnel ./pingtunnel -type server -key 123456
|
||||||
|
```
|
||||||
|
- client:
|
||||||
|
```
|
||||||
|
docker run --name pingtunnel-client -d --restart=always -p 1080: 1080 esrrhs/pingtunnel ./pingtunnel -type client -l: 1080 -s www.yourserver.com -sock5 1 -key 123456
|
||||||
|
```
|
||||||
|
|
||||||
|
## Thanks for free JetBrains Open Source license
|
||||||
|
|
||||||
|
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" height="200"/></a>
|
||||||
|
|
||||||
|
|
1
_config.yml
Normal file
1
_config.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
theme: jekyll-theme-cayman
|
830
client.go
Normal file
830
client.go
Normal file
@ -0,0 +1,830 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/esrrhs/gohome/common"
|
||||||
|
"github.com/esrrhs/gohome/frame"
|
||||||
|
"github.com/esrrhs/gohome/loggo"
|
||||||
|
"github.com/esrrhs/gohome/network"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SEND_PROTO int = 8
|
||||||
|
RECV_PROTO int = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewClient(addr string, server string, target string, timeout int, key int,
|
||||||
|
tcpmode int, tcpmode_buffersize int, tcpmode_maxwin int, tcpmode_resend_timems int, tcpmode_compress int,
|
||||||
|
tcpmode_stat int, open_sock5 int, maxconn int, sock5_filter *func(addr string) bool) (*Client, error) {
|
||||||
|
|
||||||
|
var ipaddr *net.UDPAddr
|
||||||
|
var tcpaddr *net.TCPAddr
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if tcpmode > 0 {
|
||||||
|
tcpaddr, err = net.ResolveTCPAddr("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ipaddr, err = net.ResolveUDPAddr("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipaddrServer, err := net.ResolveIPAddr("ip", server)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
return &Client{
|
||||||
|
exit: false,
|
||||||
|
rtt: 0,
|
||||||
|
id: rand.Intn(math.MaxInt16),
|
||||||
|
ipaddr: ipaddr,
|
||||||
|
tcpaddr: tcpaddr,
|
||||||
|
addr: addr,
|
||||||
|
ipaddrServer: ipaddrServer,
|
||||||
|
addrServer: server,
|
||||||
|
targetAddr: target,
|
||||||
|
timeout: timeout,
|
||||||
|
key: key,
|
||||||
|
tcpmode: tcpmode,
|
||||||
|
tcpmode_buffersize: tcpmode_buffersize,
|
||||||
|
tcpmode_maxwin: tcpmode_maxwin,
|
||||||
|
tcpmode_resend_timems: tcpmode_resend_timems,
|
||||||
|
tcpmode_compress: tcpmode_compress,
|
||||||
|
tcpmode_stat: tcpmode_stat,
|
||||||
|
open_sock5: open_sock5,
|
||||||
|
maxconn: maxconn,
|
||||||
|
pongTime: time.Now(),
|
||||||
|
sock5_filter: sock5_filter,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
exit bool
|
||||||
|
rtt time.Duration
|
||||||
|
workResultLock sync.WaitGroup
|
||||||
|
maxconn int
|
||||||
|
|
||||||
|
id int
|
||||||
|
sequence int
|
||||||
|
|
||||||
|
timeout int
|
||||||
|
sproto int
|
||||||
|
rproto int
|
||||||
|
key int
|
||||||
|
tcpmode int
|
||||||
|
tcpmode_buffersize int
|
||||||
|
tcpmode_maxwin int
|
||||||
|
tcpmode_resend_timems int
|
||||||
|
tcpmode_compress int
|
||||||
|
tcpmode_stat int
|
||||||
|
|
||||||
|
open_sock5 int
|
||||||
|
sock5_filter *func(addr string) bool
|
||||||
|
|
||||||
|
ipaddr *net.UDPAddr
|
||||||
|
tcpaddr *net.TCPAddr
|
||||||
|
addr string
|
||||||
|
|
||||||
|
ipaddrServer *net.IPAddr
|
||||||
|
addrServer string
|
||||||
|
|
||||||
|
targetAddr string
|
||||||
|
|
||||||
|
conn *icmp.PacketConn
|
||||||
|
listenConn *net.UDPConn
|
||||||
|
tcplistenConn *net.TCPListener
|
||||||
|
|
||||||
|
localAddrToConnMap sync.Map
|
||||||
|
localIdToConnMap sync.Map
|
||||||
|
|
||||||
|
sendPacket uint64
|
||||||
|
recvPacket uint64
|
||||||
|
sendPacketSize uint64
|
||||||
|
recvPacketSize uint64
|
||||||
|
localAddrToConnMapSize int
|
||||||
|
localIdToConnMapSize int
|
||||||
|
|
||||||
|
recvcontrol chan int
|
||||||
|
|
||||||
|
pongTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConn struct {
|
||||||
|
exit bool
|
||||||
|
ipaddr *net.UDPAddr
|
||||||
|
tcpaddr *net.TCPAddr
|
||||||
|
id string
|
||||||
|
activeRecvTime time.Time
|
||||||
|
activeSendTime time.Time
|
||||||
|
close bool
|
||||||
|
|
||||||
|
fm *frame.FrameMgr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) Addr() string {
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) IPAddr() *net.UDPAddr {
|
||||||
|
return p.ipaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) TargetAddr() string {
|
||||||
|
return p.targetAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) ServerIPAddr() *net.IPAddr {
|
||||||
|
return p.ipaddrServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) ServerAddr() string {
|
||||||
|
return p.addrServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) RTT() time.Duration {
|
||||||
|
return p.rtt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) RecvPacketSize() uint64 {
|
||||||
|
return p.recvPacketSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) SendPacketSize() uint64 {
|
||||||
|
return p.sendPacketSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) RecvPacket() uint64 {
|
||||||
|
return p.recvPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) SendPacket() uint64 {
|
||||||
|
return p.sendPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) LocalIdToConnMapSize() int {
|
||||||
|
return p.localIdToConnMapSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) LocalAddrToConnMapSize() int {
|
||||||
|
return p.localAddrToConnMapSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) Run() error {
|
||||||
|
|
||||||
|
conn, err := icmp.ListenPacket("ip4:icmp", "")
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Error listening for ICMP packets: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.conn = conn
|
||||||
|
|
||||||
|
if p.tcpmode > 0 {
|
||||||
|
tcplistenConn, err := net.ListenTCP("tcp", p.tcpaddr)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Error listening for tcp packets: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.tcplistenConn = tcplistenConn
|
||||||
|
} else {
|
||||||
|
listener, err := net.ListenUDP("udp", p.ipaddr)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Error listening for udp packets: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.listenConn = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.tcpmode > 0 {
|
||||||
|
go p.AcceptTcp()
|
||||||
|
} else {
|
||||||
|
go p.Accept()
|
||||||
|
}
|
||||||
|
|
||||||
|
recv := make(chan *Packet, 10000)
|
||||||
|
p.recvcontrol = make(chan int, 1)
|
||||||
|
go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
for !p.exit {
|
||||||
|
p.checkTimeoutConn()
|
||||||
|
p.ping()
|
||||||
|
p.showNet()
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
for !p.exit {
|
||||||
|
p.updateServerAddr()
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
for !p.exit {
|
||||||
|
select {
|
||||||
|
case <-p.recvcontrol:
|
||||||
|
return
|
||||||
|
case r := <-recv:
|
||||||
|
p.processPacket(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) Stop() {
|
||||||
|
p.exit = true
|
||||||
|
p.recvcontrol <- 1
|
||||||
|
p.workResultLock.Wait()
|
||||||
|
p.conn.Close()
|
||||||
|
if p.tcplistenConn != nil {
|
||||||
|
p.tcplistenConn.Close()
|
||||||
|
}
|
||||||
|
if p.listenConn != nil {
|
||||||
|
p.listenConn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) AcceptTcp() error {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
loggo.Info("client waiting local accept tcp")
|
||||||
|
|
||||||
|
for !p.exit {
|
||||||
|
p.tcplistenConn.SetDeadline(time.Now().Add(time.Millisecond * 1000))
|
||||||
|
|
||||||
|
conn, err := p.tcplistenConn.AcceptTCP()
|
||||||
|
if err != nil {
|
||||||
|
nerr, ok := err.(net.Error)
|
||||||
|
if !ok || !nerr.Timeout() {
|
||||||
|
loggo.Info("Error accept tcp %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn != nil {
|
||||||
|
if p.open_sock5 > 0 {
|
||||||
|
go p.AcceptSock5Conn(conn)
|
||||||
|
} else {
|
||||||
|
go p.AcceptTcpConn(conn, p.targetAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) AcceptTcpConn(conn *net.TCPConn, targetAddr string) {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
tcpsrcaddr := conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
|
||||||
|
if p.maxconn > 0 && p.localIdToConnMapSize >= p.maxconn {
|
||||||
|
loggo.Info("too many connections %d, client accept new local tcp fail %s", p.localIdToConnMapSize, tcpsrcaddr.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid := common.UniqueId()
|
||||||
|
|
||||||
|
fm := frame.NewFrameMgr(FRAME_MAX_SIZE, FRAME_MAX_ID, p.tcpmode_buffersize, p.tcpmode_maxwin, p.tcpmode_resend_timems, p.tcpmode_compress, p.tcpmode_stat)
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
clientConn := &ClientConn{exit: false, tcpaddr: tcpsrcaddr, id: uuid, activeRecvTime: now, activeSendTime: now, close: false,
|
||||||
|
fm: fm}
|
||||||
|
p.addClientConn(uuid, tcpsrcaddr.String(), clientConn)
|
||||||
|
loggo.Info("client accept new local tcp %s %s", uuid, tcpsrcaddr.String())
|
||||||
|
|
||||||
|
loggo.Info("start connect remote tcp %s %s", uuid, tcpsrcaddr.String())
|
||||||
|
clientConn.fm.Connect()
|
||||||
|
startConnectTime := common.GetNowUpdateInSecond()
|
||||||
|
for !p.exit && !clientConn.exit {
|
||||||
|
if clientConn.fm.IsConnected() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
clientConn.fm.Update()
|
||||||
|
sendlist := clientConn.fm.GetSendList()
|
||||||
|
for e := sendlist.Front(); e != nil; e = e.Next() {
|
||||||
|
f := e.Value.(*frame.Frame)
|
||||||
|
mb, _ := clientConn.fm.MarshalFrame(f)
|
||||||
|
p.sequence++
|
||||||
|
sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb,
|
||||||
|
SEND_PROTO, RECV_PROTO, p.key,
|
||||||
|
p.tcpmode, p.tcpmode_buffersize, p.tcpmode_maxwin, p.tcpmode_resend_timems, p.tcpmode_compress, p.tcpmode_stat,
|
||||||
|
p.timeout)
|
||||||
|
p.sendPacket++
|
||||||
|
p.sendPacketSize += (uint64)(len(mb))
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
diffclose := now.Sub(startConnectTime)
|
||||||
|
if diffclose > time.Second*5 {
|
||||||
|
loggo.Info("can not connect remote tcp %s %s", uuid, tcpsrcaddr.String())
|
||||||
|
p.close(clientConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !clientConn.exit {
|
||||||
|
loggo.Info("connected remote tcp %s %s", uuid, tcpsrcaddr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := make([]byte, 10240)
|
||||||
|
|
||||||
|
tcpActiveRecvTime := common.GetNowUpdateInSecond()
|
||||||
|
tcpActiveSendTime := common.GetNowUpdateInSecond()
|
||||||
|
|
||||||
|
for !p.exit && !clientConn.exit {
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
sleep := true
|
||||||
|
|
||||||
|
left := common.MinOfInt(clientConn.fm.GetSendBufferLeft(), len(bytes))
|
||||||
|
if left > 0 {
|
||||||
|
conn.SetReadDeadline(time.Now().Add(time.Millisecond * 1))
|
||||||
|
n, err := conn.Read(bytes[0:left])
|
||||||
|
if err != nil {
|
||||||
|
nerr, ok := err.(net.Error)
|
||||||
|
if !ok || !nerr.Timeout() {
|
||||||
|
loggo.Info("Error read tcp %s %s %s", uuid, tcpsrcaddr.String(), err)
|
||||||
|
clientConn.fm.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
sleep = false
|
||||||
|
clientConn.fm.WriteSendBuffer(bytes[:n])
|
||||||
|
tcpActiveRecvTime = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConn.fm.Update()
|
||||||
|
|
||||||
|
sendlist := clientConn.fm.GetSendList()
|
||||||
|
if sendlist.Len() > 0 {
|
||||||
|
sleep = false
|
||||||
|
clientConn.activeSendTime = now
|
||||||
|
for e := sendlist.Front(); e != nil; e = e.Next() {
|
||||||
|
f := e.Value.(*frame.Frame)
|
||||||
|
mb, err := clientConn.fm.MarshalFrame(f)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Error tcp Marshal %s %s %s", uuid, tcpsrcaddr.String(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.sequence++
|
||||||
|
sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb,
|
||||||
|
SEND_PROTO, RECV_PROTO, p.key,
|
||||||
|
p.tcpmode, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
p.sendPacket++
|
||||||
|
p.sendPacketSize += (uint64)(len(mb))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientConn.fm.GetRecvBufferSize() > 0 {
|
||||||
|
sleep = false
|
||||||
|
rr := clientConn.fm.GetRecvReadLineBuffer()
|
||||||
|
conn.SetWriteDeadline(time.Now().Add(time.Millisecond * 1))
|
||||||
|
n, err := conn.Write(rr)
|
||||||
|
if err != nil {
|
||||||
|
nerr, ok := err.(net.Error)
|
||||||
|
if !ok || !nerr.Timeout() {
|
||||||
|
loggo.Info("Error write tcp %s %s %s", uuid, tcpsrcaddr.String(), err)
|
||||||
|
clientConn.fm.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
clientConn.fm.SkipRecvBuffer(n)
|
||||||
|
tcpActiveSendTime = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sleep {
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
diffrecv := now.Sub(clientConn.activeRecvTime)
|
||||||
|
diffsend := now.Sub(clientConn.activeSendTime)
|
||||||
|
tcpdiffrecv := now.Sub(tcpActiveRecvTime)
|
||||||
|
tcpdiffsend := now.Sub(tcpActiveSendTime)
|
||||||
|
if diffrecv > time.Second*(time.Duration(p.timeout)) || diffsend > time.Second*(time.Duration(p.timeout)) ||
|
||||||
|
(tcpdiffrecv > time.Second*(time.Duration(p.timeout)) && tcpdiffsend > time.Second*(time.Duration(p.timeout))) {
|
||||||
|
loggo.Info("close inactive conn %s %s", clientConn.id, clientConn.tcpaddr.String())
|
||||||
|
clientConn.fm.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientConn.fm.IsRemoteClosed() {
|
||||||
|
loggo.Info("closed by remote conn %s %s", clientConn.id, clientConn.tcpaddr.String())
|
||||||
|
clientConn.fm.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConn.fm.Close()
|
||||||
|
|
||||||
|
startCloseTime := common.GetNowUpdateInSecond()
|
||||||
|
for !p.exit && !clientConn.exit {
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
|
||||||
|
clientConn.fm.Update()
|
||||||
|
|
||||||
|
sendlist := clientConn.fm.GetSendList()
|
||||||
|
for e := sendlist.Front(); e != nil; e = e.Next() {
|
||||||
|
f := e.Value.(*frame.Frame)
|
||||||
|
mb, _ := clientConn.fm.MarshalFrame(f)
|
||||||
|
p.sequence++
|
||||||
|
sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb,
|
||||||
|
SEND_PROTO, RECV_PROTO, p.key,
|
||||||
|
p.tcpmode, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
p.sendPacket++
|
||||||
|
p.sendPacketSize += (uint64)(len(mb))
|
||||||
|
}
|
||||||
|
|
||||||
|
nodatarecv := true
|
||||||
|
if clientConn.fm.GetRecvBufferSize() > 0 {
|
||||||
|
rr := clientConn.fm.GetRecvReadLineBuffer()
|
||||||
|
conn.SetWriteDeadline(time.Now().Add(time.Millisecond * 100))
|
||||||
|
n, _ := conn.Write(rr)
|
||||||
|
if n > 0 {
|
||||||
|
clientConn.fm.SkipRecvBuffer(n)
|
||||||
|
nodatarecv = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diffclose := now.Sub(startCloseTime)
|
||||||
|
if diffclose > time.Second*60 {
|
||||||
|
loggo.Info("close conn had timeout %s %s", clientConn.id, clientConn.tcpaddr.String())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteclosed := clientConn.fm.IsRemoteClosed()
|
||||||
|
if remoteclosed && nodatarecv {
|
||||||
|
loggo.Info("remote conn had closed %s %s", clientConn.id, clientConn.tcpaddr.String())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
loggo.Info("close tcp conn %s %s", clientConn.id, clientConn.tcpaddr.String())
|
||||||
|
conn.Close()
|
||||||
|
p.close(clientConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) Accept() error {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
loggo.Info("client waiting local accept udp")
|
||||||
|
|
||||||
|
bytes := make([]byte, 10240)
|
||||||
|
|
||||||
|
for !p.exit {
|
||||||
|
p.listenConn.SetReadDeadline(time.Now().Add(time.Millisecond * 100))
|
||||||
|
n, srcaddr, err := p.listenConn.ReadFromUDP(bytes)
|
||||||
|
if err != nil {
|
||||||
|
nerr, ok := err.(net.Error)
|
||||||
|
if !ok || !nerr.Timeout() {
|
||||||
|
loggo.Info("Error read udp %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
clientConn := p.getClientConnByAddr(srcaddr.String())
|
||||||
|
if clientConn == nil {
|
||||||
|
if p.maxconn > 0 && p.localIdToConnMapSize >= p.maxconn {
|
||||||
|
loggo.Info("too many connections %d, client accept new local udp fail %s", p.localIdToConnMapSize, srcaddr.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uuid := common.UniqueId()
|
||||||
|
clientConn = &ClientConn{exit: false, ipaddr: srcaddr, id: uuid, activeRecvTime: now, activeSendTime: now, close: false}
|
||||||
|
p.addClientConn(uuid, srcaddr.String(), clientConn)
|
||||||
|
loggo.Info("client accept new local udp %s %s", uuid, srcaddr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConn.activeSendTime = now
|
||||||
|
sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, p.targetAddr, clientConn.id, (uint32)(MyMsg_DATA), bytes[:n],
|
||||||
|
SEND_PROTO, RECV_PROTO, p.key,
|
||||||
|
p.tcpmode, 0, 0, 0, 0, 0,
|
||||||
|
p.timeout)
|
||||||
|
|
||||||
|
p.sequence++
|
||||||
|
|
||||||
|
p.sendPacket++
|
||||||
|
p.sendPacketSize += (uint64)(n)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) processPacket(packet *Packet) {
|
||||||
|
|
||||||
|
if packet.my.Rproto >= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.my.Key != (int32)(p.key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.echoId != p.id {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.my.Type == (int32)(MyMsg_PING) {
|
||||||
|
t := time.Time{}
|
||||||
|
t.UnmarshalBinary(packet.my.Data)
|
||||||
|
now := time.Now()
|
||||||
|
d := now.Sub(t)
|
||||||
|
loggo.Info("pong from %s %s", packet.src.String(), d.String())
|
||||||
|
p.rtt = d
|
||||||
|
p.pongTime = now
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.my.Type == (int32)(MyMsg_KICK) {
|
||||||
|
clientConn := p.getClientConnById(packet.my.Id)
|
||||||
|
if clientConn != nil {
|
||||||
|
p.close(clientConn)
|
||||||
|
loggo.Info("remote kick local %s", packet.my.Id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loggo.Debug("processPacket %s %s %d", packet.my.Id, packet.src.String(), len(packet.my.Data))
|
||||||
|
|
||||||
|
clientConn := p.getClientConnById(packet.my.Id)
|
||||||
|
if clientConn == nil {
|
||||||
|
loggo.Debug("processPacket no conn %s ", packet.my.Id)
|
||||||
|
p.remoteError(packet.my.Id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
clientConn.activeRecvTime = now
|
||||||
|
|
||||||
|
if p.tcpmode > 0 {
|
||||||
|
f := &frame.Frame{}
|
||||||
|
err := proto.Unmarshal(packet.my.Data, f)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Unmarshal tcp Error %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConn.fm.OnRecvFrame(f)
|
||||||
|
} else {
|
||||||
|
if packet.my.Data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addr := clientConn.ipaddr
|
||||||
|
_, err := p.listenConn.WriteToUDP(packet.my.Data, addr)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Info("WriteToUDP Error read udp %s", err)
|
||||||
|
clientConn.close = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.recvPacket++
|
||||||
|
p.recvPacketSize += (uint64)(len(packet.my.Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) close(clientConn *ClientConn) {
|
||||||
|
clientConn.exit = true
|
||||||
|
p.deleteClientConn(clientConn.id, clientConn.ipaddr.String())
|
||||||
|
p.deleteClientConn(clientConn.id, clientConn.tcpaddr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) checkTimeoutConn() {
|
||||||
|
|
||||||
|
if p.tcpmode > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := make(map[string]*ClientConn)
|
||||||
|
p.localIdToConnMap.Range(func(key, value interface{}) bool {
|
||||||
|
id := key.(string)
|
||||||
|
clientConn := value.(*ClientConn)
|
||||||
|
tmp[id] = clientConn
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
for _, conn := range tmp {
|
||||||
|
diffrecv := now.Sub(conn.activeRecvTime)
|
||||||
|
diffsend := now.Sub(conn.activeSendTime)
|
||||||
|
if diffrecv > time.Second*(time.Duration(p.timeout)) || diffsend > time.Second*(time.Duration(p.timeout)) {
|
||||||
|
conn.close = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, conn := range tmp {
|
||||||
|
if conn.close {
|
||||||
|
loggo.Info("close inactive conn %s %s", id, conn.ipaddr.String())
|
||||||
|
p.close(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) ping() {
|
||||||
|
now := time.Now()
|
||||||
|
b, _ := now.MarshalBinary()
|
||||||
|
sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, "", "", (uint32)(MyMsg_PING), b,
|
||||||
|
SEND_PROTO, RECV_PROTO, p.key,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
loggo.Info("ping %s %s %d %d %d %d", p.addrServer, now.String(), p.sproto, p.rproto, p.id, p.sequence)
|
||||||
|
p.sequence++
|
||||||
|
if now.Sub(p.pongTime) > time.Second*3 {
|
||||||
|
p.rtt = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) showNet() {
|
||||||
|
p.localAddrToConnMapSize = 0
|
||||||
|
p.localIdToConnMap.Range(func(key, value interface{}) bool {
|
||||||
|
p.localAddrToConnMapSize++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
p.localIdToConnMapSize = 0
|
||||||
|
p.localIdToConnMap.Range(func(key, value interface{}) bool {
|
||||||
|
p.localIdToConnMapSize++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
loggo.Info("send %dPacket/s %dKB/s recv %dPacket/s %dKB/s %d/%dConnections",
|
||||||
|
p.sendPacket, p.sendPacketSize/1024, p.recvPacket, p.recvPacketSize/1024, p.localAddrToConnMapSize, p.localIdToConnMapSize)
|
||||||
|
p.sendPacket = 0
|
||||||
|
p.recvPacket = 0
|
||||||
|
p.sendPacketSize = 0
|
||||||
|
p.recvPacketSize = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) AcceptSock5Conn(conn *net.TCPConn) {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
var err error = nil
|
||||||
|
if err = network.Sock5HandshakeBy(conn, "", ""); err != nil {
|
||||||
|
loggo.Error("socks handshake: %s", err)
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, addr, err := network.Sock5GetRequest(conn)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("error getting request: %s", err)
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Sending connection established message immediately to client.
|
||||||
|
// This some round trip time for creating socks connection with the client.
|
||||||
|
// But if connection failed, the client will get connection reset error.
|
||||||
|
_, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43})
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("send connection confirmation: %s", err)
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loggo.Info("accept new sock5 conn: %s", addr)
|
||||||
|
|
||||||
|
if p.sock5_filter == nil {
|
||||||
|
p.AcceptTcpConn(conn, addr)
|
||||||
|
} else {
|
||||||
|
if (*p.sock5_filter)(addr) {
|
||||||
|
p.AcceptTcpConn(conn, addr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.AcceptDirectTcpConn(conn, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) addClientConn(uuid string, addr string, clientConn *ClientConn) {
|
||||||
|
|
||||||
|
p.localAddrToConnMap.Store(addr, clientConn)
|
||||||
|
p.localIdToConnMap.Store(uuid, clientConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) getClientConnByAddr(addr string) *ClientConn {
|
||||||
|
ret, ok := p.localAddrToConnMap.Load(addr)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ret.(*ClientConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) getClientConnById(uuid string) *ClientConn {
|
||||||
|
ret, ok := p.localIdToConnMap.Load(uuid)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ret.(*ClientConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) deleteClientConn(uuid string, addr string) {
|
||||||
|
p.localIdToConnMap.Delete(uuid)
|
||||||
|
p.localAddrToConnMap.Delete(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) remoteError(uuid string) {
|
||||||
|
sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, "", uuid, (uint32)(MyMsg_KICK), []byte{},
|
||||||
|
SEND_PROTO, RECV_PROTO, p.key,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) AcceptDirectTcpConn(conn *net.TCPConn, targetAddr string) {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
tcpsrcaddr := conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
|
||||||
|
loggo.Info("client accept new direct local tcp %s %s", tcpsrcaddr.String(), targetAddr)
|
||||||
|
|
||||||
|
tcpaddrTarget, err := net.ResolveTCPAddr("tcp", targetAddr)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Info("direct local tcp ResolveTCPAddr fail: %s %s", targetAddr, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetconn, err := net.DialTCP("tcp", nil, tcpaddrTarget)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Info("direct local tcp DialTCP fail: %s %s", targetAddr, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go p.transfer(conn, targetconn, conn.RemoteAddr().String(), targetconn.RemoteAddr().String())
|
||||||
|
go p.transfer(targetconn, conn, targetconn.RemoteAddr().String(), conn.RemoteAddr().String())
|
||||||
|
|
||||||
|
loggo.Info("client accept new direct local tcp ok %s %s", tcpsrcaddr.String(), targetAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) transfer(destination io.WriteCloser, source io.ReadCloser, dst string, src string) {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
defer destination.Close()
|
||||||
|
defer source.Close()
|
||||||
|
loggo.Info("client begin transfer from %s -> %s", src, dst)
|
||||||
|
io.Copy(destination, source)
|
||||||
|
loggo.Info("client end transfer from %s -> %s", src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Client) updateServerAddr() {
|
||||||
|
ipaddrServer, err := net.ResolveIPAddr("ip", p.addrServer)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.ipaddrServer.String() != ipaddrServer.String() {
|
||||||
|
p.ipaddrServer = ipaddrServer
|
||||||
|
}
|
||||||
|
}
|
52
country.go
Normal file
52
country.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/esrrhs/gohome/common"
|
||||||
|
"github.com/oschwald/geoip2-golang"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var gdb *geoip2.Reader
|
||||||
|
|
||||||
|
func LoadGeoDB(file string) error {
|
||||||
|
|
||||||
|
if len(file) <= 0 {
|
||||||
|
file = common.GetDataDir() + "/geoip/" + "GeoLite2-Country.mmdb"
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := geoip2.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gdb = db
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCountryIsoCode(ipaddr string) (string, error) {
|
||||||
|
|
||||||
|
ip := net.ParseIP(ipaddr)
|
||||||
|
if ip == nil {
|
||||||
|
return "", errors.New("ip " + ipaddr + " ParseIP nil")
|
||||||
|
}
|
||||||
|
record, err := gdb.City(ip)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return record.Country.IsoCode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCountryName(ipaddr string) (string, error) {
|
||||||
|
|
||||||
|
ip := net.ParseIP(ipaddr)
|
||||||
|
if ip == nil {
|
||||||
|
return "", errors.New("ip " + ipaddr + "ParseIP nil")
|
||||||
|
}
|
||||||
|
record, err := gdb.City(ip)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return record.Country.Names["en"], nil
|
||||||
|
}
|
16
country_test.go
Normal file
16
country_test.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
Load("./GeoLite2-Country.mmdb")
|
||||||
|
|
||||||
|
fmt.Println(GetCountryIsoCode("39.106.101.133"))
|
||||||
|
fmt.Println(GetCountryIsoCode(""))
|
||||||
|
fmt.Println(GetCountryIsoCode("aa"))
|
||||||
|
fmt.Println(GetCountryIsoCode("39.106.101.133:14234"))
|
||||||
|
fmt.Println(GetCountryIsoCode("192.168.1.121"))
|
||||||
|
}
|
2
docker-compose/.env
Normal file
2
docker-compose/.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
KEY=123456
|
||||||
|
SERVER=www.yourserver.com
|
16
docker-compose/Readme.md
Normal file
16
docker-compose/Readme.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Deploy with docker-compose
|
||||||
|
===========================
|
||||||
|
**First** edit `.env` file in this directory to your appropriate value.
|
||||||
|
|
||||||
|
**Then** run stack with these commands:
|
||||||
|
|
||||||
|
- in the server
|
||||||
|
```
|
||||||
|
docker-compose -f server.yml up -d
|
||||||
|
```
|
||||||
|
- in client machine
|
||||||
|
```
|
||||||
|
docker-compose -f client.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**Now** use socks5 proxy at port `1080` of your client machine
|
9
docker-compose/client.yml
Normal file
9
docker-compose/client.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
version: "3.7"
|
||||||
|
|
||||||
|
services:
|
||||||
|
pingtunnelServer:
|
||||||
|
image: esrrhs/pingtunnel:latest
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 1080:1080
|
||||||
|
command: "./pingtunnel -type client -l 0.0.0.0:1080 -s ${SERVER} -sock5 1 -key ${KEY}"
|
8
docker-compose/server.yml
Normal file
8
docker-compose/server.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
version: "3.7"
|
||||||
|
|
||||||
|
services:
|
||||||
|
pingtunnelServer:
|
||||||
|
image: esrrhs/pingtunnel:latest
|
||||||
|
restart: always
|
||||||
|
network_mode: host
|
||||||
|
command: "./pingtunnel -type server -key ${KEY}"
|
18
go.mod
Normal file
18
go.mod
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module github.com/esrrhs/pingtunnul
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/esrrhs/gohome v0.0.0-20230222132228-8bb1d3e2ecc4
|
||||||
|
github.com/golang/protobuf v1.5.2
|
||||||
|
github.com/oschwald/geoip2-golang v1.8.0
|
||||||
|
golang.org/x/net v0.7.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
||||||
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
|
)
|
267
main.go
Normal file
267
main.go
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/esrrhs/gohome/common"
|
||||||
|
"github.com/esrrhs/gohome/loggo"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
_ "net/http/pprof"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var usage = `
|
||||||
|
通过伪造ping,把tcp/udp/sock5流量通过远程服务器转发到目的服务器上。用于突破某些运营商封锁TCP/UDP流量。
|
||||||
|
By forging ping, the tcp/udp/sock5 traffic is forwarded to the destination server through the remote server. Used to break certain operators to block TCP/UDP traffic.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
// server
|
||||||
|
pingtunnel -type server
|
||||||
|
|
||||||
|
// client, Forward udp
|
||||||
|
pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455
|
||||||
|
|
||||||
|
// client, Forward tcp
|
||||||
|
pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 -tcp 1
|
||||||
|
|
||||||
|
// client, Forward sock5, implicitly open tcp, so no target server is needed
|
||||||
|
pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -sock5 1
|
||||||
|
|
||||||
|
-type 服务器或者客户端
|
||||||
|
client or server
|
||||||
|
|
||||||
|
服务器参数server param:
|
||||||
|
|
||||||
|
-key 设置的密码,默认0
|
||||||
|
Set password, default 0
|
||||||
|
|
||||||
|
-nolog 不写日志文件,只打印标准输出,默认0
|
||||||
|
Do not write log files, only print standard output, default 0 is off
|
||||||
|
|
||||||
|
-noprint 不打印屏幕输出,默认0
|
||||||
|
Do not print standard output, default 0 is off
|
||||||
|
|
||||||
|
-loglevel 日志文件等级,默认info
|
||||||
|
log level, default is info
|
||||||
|
|
||||||
|
-maxconn 最大连接数,默认0,不受限制
|
||||||
|
the max num of connections, default 0 is no limit
|
||||||
|
|
||||||
|
-maxprt server最大处理线程数,默认100
|
||||||
|
max process thread in server, default 100
|
||||||
|
|
||||||
|
-maxprb server最大处理线程buffer数,默认1000
|
||||||
|
max process thread's buffer in server, default 1000
|
||||||
|
|
||||||
|
-conntt server发起连接到目标地址的超时时间,默认1000ms
|
||||||
|
The timeout period for the server to initiate a connection to the destination address. The default is 1000ms.
|
||||||
|
|
||||||
|
客户端参数client param:
|
||||||
|
|
||||||
|
-l 本地的地址,发到这个端口的流量将转发到服务器
|
||||||
|
Local address, traffic sent to this port will be forwarded to the server
|
||||||
|
|
||||||
|
-s 服务器的地址,流量将通过隧道转发到这个服务器
|
||||||
|
The address of the server, the traffic will be forwarded to this server through the tunnel
|
||||||
|
|
||||||
|
-t 远端服务器转发的目的地址,流量将转发到这个地址
|
||||||
|
Destination address forwarded by the remote server, traffic will be forwarded to this address
|
||||||
|
|
||||||
|
-timeout 本地记录连接超时的时间,单位是秒,默认60s
|
||||||
|
The time when the local record connection timed out, in seconds, 60 seconds by default
|
||||||
|
|
||||||
|
-key 设置的密码,默认0
|
||||||
|
Set password, default 0
|
||||||
|
|
||||||
|
-tcp 设置是否转发tcp,默认0
|
||||||
|
Set the switch to forward tcp, the default is 0
|
||||||
|
|
||||||
|
-tcp_bs tcp的发送接收缓冲区大小,默认1MB
|
||||||
|
Tcp send and receive buffer size, default 1MB
|
||||||
|
|
||||||
|
-tcp_mw tcp的最大窗口,默认20000
|
||||||
|
The maximum window of tcp, the default is 20000
|
||||||
|
|
||||||
|
-tcp_rst tcp的超时发送时间,默认400ms
|
||||||
|
Tcp timeout resend time, default 400ms
|
||||||
|
|
||||||
|
-tcp_gz 当数据包超过这个大小,tcp将压缩数据,0表示不压缩,默认0
|
||||||
|
Tcp will compress data when the packet exceeds this size, 0 means no compression, default 0
|
||||||
|
|
||||||
|
-tcp_stat 打印tcp的监控,默认0
|
||||||
|
Print tcp connection statistic, default 0 is off
|
||||||
|
|
||||||
|
-nolog 不写日志文件,只打印标准输出,默认0
|
||||||
|
Do not write log files, only print standard output, default 0 is off
|
||||||
|
|
||||||
|
-noprint 不打印屏幕输出,默认0
|
||||||
|
Do not print standard output, default 0 is off
|
||||||
|
|
||||||
|
-loglevel 日志文件等级,默认info
|
||||||
|
log level, default is info
|
||||||
|
|
||||||
|
-sock5 开启sock5转发,默认0
|
||||||
|
Turn on sock5 forwarding, default 0 is off
|
||||||
|
|
||||||
|
-profile 在指定端口开启性能检测,默认0不开启
|
||||||
|
Enable performance detection on the specified port. The default 0 is not enabled.
|
||||||
|
|
||||||
|
-s5filter sock5模式设置转发过滤,默认全转发,设置CN代表CN地区的直连不转发
|
||||||
|
Set the forwarding filter in the sock5 mode. The default is full forwarding. For example, setting the CN indicates that the Chinese address is not forwarded.
|
||||||
|
|
||||||
|
-s5ftfile sock5模式转发过滤的数据文件,默认读取当前目录的GeoLite2-Country.mmdb
|
||||||
|
The data file in sock5 filter mode, the default reading of the current directory GeoLite2-Country.mmdb
|
||||||
|
`
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
t := flag.String("type", "", "client or server")
|
||||||
|
listen := flag.String("l", "", "listen addr")
|
||||||
|
target := flag.String("t", "", "target addr")
|
||||||
|
server := flag.String("s", "", "server addr")
|
||||||
|
timeout := flag.Int("timeout", 60, "conn timeout")
|
||||||
|
key := flag.Int("key", 0, "key")
|
||||||
|
tcpmode := flag.Int("tcp", 0, "tcp mode")
|
||||||
|
tcpmode_buffersize := flag.Int("tcp_bs", 1*1024*1024, "tcp mode buffer size")
|
||||||
|
tcpmode_maxwin := flag.Int("tcp_mw", 20000, "tcp mode max win")
|
||||||
|
tcpmode_resend_timems := flag.Int("tcp_rst", 400, "tcp mode resend time ms")
|
||||||
|
tcpmode_compress := flag.Int("tcp_gz", 0, "tcp data compress")
|
||||||
|
nolog := flag.Int("nolog", 0, "write log file")
|
||||||
|
noprint := flag.Int("noprint", 0, "print stdout")
|
||||||
|
tcpmode_stat := flag.Int("tcp_stat", 0, "print tcp stat")
|
||||||
|
loglevel := flag.String("loglevel", "info", "log level")
|
||||||
|
open_sock5 := flag.Int("sock5", 0, "sock5 mode")
|
||||||
|
maxconn := flag.Int("maxconn", 0, "max num of connections")
|
||||||
|
max_process_thread := flag.Int("maxprt", 100, "max process thread in server")
|
||||||
|
max_process_buffer := flag.Int("maxprb", 1000, "max process thread's buffer in server")
|
||||||
|
profile := flag.Int("profile", 0, "open profile")
|
||||||
|
conntt := flag.Int("conntt", 1000, "the connect call's timeout")
|
||||||
|
s5filter := flag.String("s5filter", "", "sock5 filter")
|
||||||
|
s5ftfile := flag.String("s5ftfile", "GeoLite2-Country.mmdb", "sock5 filter file")
|
||||||
|
flag.Usage = func() {
|
||||||
|
fmt.Printf(usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *t != "client" && *t != "server" {
|
||||||
|
flag.Usage()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *t == "client" {
|
||||||
|
if len(*listen) == 0 || len(*server) == 0 {
|
||||||
|
flag.Usage()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *open_sock5 == 0 && len(*target) == 0 {
|
||||||
|
flag.Usage()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *open_sock5 != 0 {
|
||||||
|
*tcpmode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if *tcpmode_maxwin*10 > FRAME_MAX_ID {
|
||||||
|
fmt.Println("set tcp win to big, max = " + strconv.Itoa(FRAME_MAX_ID/10))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
level := loggo.LEVEL_INFO
|
||||||
|
if loggo.NameToLevel(*loglevel) >= 0 {
|
||||||
|
level = loggo.NameToLevel(*loglevel)
|
||||||
|
}
|
||||||
|
loggo.Ini(loggo.Config{
|
||||||
|
Level: level,
|
||||||
|
Prefix: "pingtunnel",
|
||||||
|
MaxDay: 3,
|
||||||
|
NoLogFile: *nolog > 0,
|
||||||
|
NoPrint: *noprint > 0,
|
||||||
|
})
|
||||||
|
loggo.Info("start...")
|
||||||
|
loggo.Info("key %d", *key)
|
||||||
|
|
||||||
|
if *t == "server" {
|
||||||
|
s, err := NewServer(*key, *maxconn, *max_process_thread, *max_process_buffer, *conntt)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("ERROR: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loggo.Info("Server start")
|
||||||
|
err = s.Run()
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Run ERROR: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if *t == "client" {
|
||||||
|
|
||||||
|
loggo.Info("type %s", *t)
|
||||||
|
loggo.Info("listen %s", *listen)
|
||||||
|
loggo.Info("server %s", *server)
|
||||||
|
loggo.Info("target %s", *target)
|
||||||
|
|
||||||
|
if *tcpmode == 0 {
|
||||||
|
*tcpmode_buffersize = 0
|
||||||
|
*tcpmode_maxwin = 0
|
||||||
|
*tcpmode_resend_timems = 0
|
||||||
|
*tcpmode_compress = 0
|
||||||
|
*tcpmode_stat = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(*s5filter) > 0 {
|
||||||
|
err := LoadGeoDB(*s5ftfile)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Load Sock5 ip file ERROR: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filter := func(addr string) bool {
|
||||||
|
if len(*s5filter) <= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
taddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := GetCountryIsoCode(taddr.IP.String())
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(ret) <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ret != *s5filter
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := NewClient(*listen, *server, *target, *timeout, *key,
|
||||||
|
*tcpmode, *tcpmode_buffersize, *tcpmode_maxwin, *tcpmode_resend_timems, *tcpmode_compress,
|
||||||
|
*tcpmode_stat, *open_sock5, *maxconn, &filter)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("ERROR: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loggo.Info("Client Listen %s (%s) Server %s (%s) TargetPort %s:", c.Addr(), c.IPAddr(),
|
||||||
|
c.ServerAddr(), c.ServerIPAddr(), c.TargetAddr())
|
||||||
|
err = c.Run()
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Run ERROR: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *profile > 0 {
|
||||||
|
go http.ListenAndServe("0.0.0.0:"+strconv.Itoa(*profile), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Hour)
|
||||||
|
}
|
||||||
|
}
|
228
msg.pb.go
Normal file
228
msg.pb.go
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: msg.proto
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
|
type MyMsg_TYPE int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MyMsg_DATA MyMsg_TYPE = 0
|
||||||
|
MyMsg_PING MyMsg_TYPE = 1
|
||||||
|
MyMsg_KICK MyMsg_TYPE = 2
|
||||||
|
MyMsg_MAGIC MyMsg_TYPE = 57005
|
||||||
|
)
|
||||||
|
|
||||||
|
var MyMsg_TYPE_name = map[int32]string{
|
||||||
|
0: "DATA",
|
||||||
|
1: "PING",
|
||||||
|
2: "KICK",
|
||||||
|
57005: "MAGIC",
|
||||||
|
}
|
||||||
|
|
||||||
|
var MyMsg_TYPE_value = map[string]int32{
|
||||||
|
"DATA": 0,
|
||||||
|
"PING": 1,
|
||||||
|
"KICK": 2,
|
||||||
|
"MAGIC": 57005,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x MyMsg_TYPE) String() string {
|
||||||
|
return proto.EnumName(MyMsg_TYPE_name, int32(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (MyMsg_TYPE) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_c06e4cca6c2cc899, []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyMsg struct {
|
||||||
|
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
|
Type int32 `protobuf:"varint,2,opt,name=type,proto3" json:"type,omitempty"`
|
||||||
|
Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"`
|
||||||
|
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
|
||||||
|
Rproto int32 `protobuf:"zigzag32,5,opt,name=rproto,proto3" json:"rproto,omitempty"`
|
||||||
|
Magic int32 `protobuf:"zigzag32,6,opt,name=magic,proto3" json:"magic,omitempty"`
|
||||||
|
Key int32 `protobuf:"zigzag32,7,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
|
Timeout int32 `protobuf:"varint,8,opt,name=timeout,proto3" json:"timeout,omitempty"`
|
||||||
|
Tcpmode int32 `protobuf:"varint,9,opt,name=tcpmode,proto3" json:"tcpmode,omitempty"`
|
||||||
|
TcpmodeBuffersize int32 `protobuf:"varint,10,opt,name=tcpmode_buffersize,json=tcpmodeBuffersize,proto3" json:"tcpmode_buffersize,omitempty"`
|
||||||
|
TcpmodeMaxwin int32 `protobuf:"varint,11,opt,name=tcpmode_maxwin,json=tcpmodeMaxwin,proto3" json:"tcpmode_maxwin,omitempty"`
|
||||||
|
TcpmodeResendTimems int32 `protobuf:"varint,12,opt,name=tcpmode_resend_timems,json=tcpmodeResendTimems,proto3" json:"tcpmode_resend_timems,omitempty"`
|
||||||
|
TcpmodeCompress int32 `protobuf:"varint,13,opt,name=tcpmode_compress,json=tcpmodeCompress,proto3" json:"tcpmode_compress,omitempty"`
|
||||||
|
TcpmodeStat int32 `protobuf:"varint,14,opt,name=tcpmode_stat,json=tcpmodeStat,proto3" json:"tcpmode_stat,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) Reset() { *m = MyMsg{} }
|
||||||
|
func (m *MyMsg) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*MyMsg) ProtoMessage() {}
|
||||||
|
func (*MyMsg) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_c06e4cca6c2cc899, []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_MyMsg.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *MyMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_MyMsg.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (m *MyMsg) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_MyMsg.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *MyMsg) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_MyMsg.Size(m)
|
||||||
|
}
|
||||||
|
func (m *MyMsg) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_MyMsg.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_MyMsg proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *MyMsg) GetId() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Id
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetType() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Type
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetTarget() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Target
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetData() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetRproto() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Rproto
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetMagic() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Magic
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetKey() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Key
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetTimeout() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Timeout
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetTcpmode() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Tcpmode
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetTcpmodeBuffersize() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.TcpmodeBuffersize
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetTcpmodeMaxwin() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.TcpmodeMaxwin
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetTcpmodeResendTimems() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.TcpmodeResendTimems
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetTcpmodeCompress() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.TcpmodeCompress
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MyMsg) GetTcpmodeStat() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.TcpmodeStat
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterEnum("MyMsg_TYPE", MyMsg_TYPE_name, MyMsg_TYPE_value)
|
||||||
|
proto.RegisterType((*MyMsg)(nil), "MyMsg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("msg.proto", fileDescriptor_c06e4cca6c2cc899) }
|
||||||
|
|
||||||
|
var fileDescriptor_c06e4cca6c2cc899 = []byte{
|
||||||
|
// 342 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x3c, 0x90, 0xdb, 0x6a, 0xe2, 0x50,
|
||||||
|
0x14, 0x86, 0x27, 0x27, 0x0f, 0xcb, 0xe8, 0xc4, 0x35, 0x07, 0xd6, 0x65, 0x46, 0x18, 0xc8, 0x5c,
|
||||||
|
0xcc, 0xc0, 0xb4, 0x4f, 0xa0, 0xb6, 0x88, 0x48, 0x8a, 0xa4, 0xde, 0xb4, 0x37, 0x12, 0xcd, 0x36,
|
||||||
|
0x84, 0x36, 0x07, 0xb2, 0xb7, 0xb4, 0xf6, 0x9d, 0xfa, 0x08, 0x7d, 0x8d, 0x3e, 0x4f, 0xc9, 0x72,
|
||||||
|
0xa7, 0x77, 0xff, 0xff, 0x7f, 0x5f, 0xc8, 0x62, 0x43, 0x3f, 0x97, 0xe9, 0xbf, 0xaa, 0x2e, 0x55,
|
||||||
|
0x39, 0x79, 0xb7, 0xc0, 0x09, 0x4f, 0xa1, 0x4c, 0x71, 0x04, 0x66, 0x96, 0x90, 0xe1, 0x1b, 0x41,
|
||||||
|
0x3f, 0x32, 0xb3, 0x04, 0x11, 0x6c, 0x75, 0xaa, 0x04, 0x99, 0xbe, 0x11, 0x38, 0x11, 0x67, 0xfc,
|
||||||
|
0x09, 0x1d, 0x15, 0xd7, 0xa9, 0x50, 0x64, 0xb1, 0xa7, 0x5b, 0xe3, 0x26, 0xb1, 0x8a, 0xc9, 0xf6,
|
||||||
|
0x8d, 0xc0, 0x8d, 0x38, 0x37, 0x6e, 0xcd, 0xff, 0x20, 0xc7, 0x37, 0x82, 0x71, 0xa4, 0x1b, 0x7e,
|
||||||
|
0x07, 0x27, 0x8f, 0xd3, 0x6c, 0x4f, 0x1d, 0x9e, 0xcf, 0x05, 0x3d, 0xb0, 0x1e, 0xc4, 0x89, 0xba,
|
||||||
|
0xbc, 0x35, 0x11, 0x09, 0xba, 0x2a, 0xcb, 0x45, 0x79, 0x54, 0xd4, 0xe3, 0x13, 0xda, 0xca, 0x64,
|
||||||
|
0x5f, 0xe5, 0x65, 0x22, 0xa8, 0xaf, 0xc9, 0xb9, 0xe2, 0x5f, 0x40, 0x1d, 0xb7, 0xbb, 0xe3, 0xe1,
|
||||||
|
0x20, 0x6a, 0x99, 0xbd, 0x08, 0x02, 0x96, 0xc6, 0x9a, 0xcc, 0x3e, 0x01, 0xfe, 0x86, 0x51, 0xab,
|
||||||
|
0xe7, 0xf1, 0xf3, 0x53, 0x56, 0xd0, 0x80, 0xd5, 0xa1, 0x5e, 0x43, 0x1e, 0xf1, 0x02, 0x7e, 0xb4,
|
||||||
|
0x5a, 0x2d, 0xa4, 0x28, 0x92, 0x6d, 0x73, 0x49, 0x2e, 0xc9, 0x65, 0xfb, 0x9b, 0x86, 0x11, 0xb3,
|
||||||
|
0x0d, 0x23, 0xfc, 0x03, 0x5e, 0xfb, 0xcd, 0xbe, 0xcc, 0xab, 0x5a, 0x48, 0x49, 0x43, 0xd6, 0xbf,
|
||||||
|
0xea, 0x7d, 0xae, 0x67, 0xfc, 0x05, 0x6e, 0xab, 0x4a, 0x15, 0x2b, 0x1a, 0xb1, 0x36, 0xd0, 0xdb,
|
||||||
|
0xad, 0x8a, 0xd5, 0xe4, 0x3f, 0xd8, 0x9b, 0xbb, 0xf5, 0x35, 0xf6, 0xc0, 0xbe, 0x9a, 0x6e, 0xa6,
|
||||||
|
0xde, 0x97, 0x26, 0xad, 0x97, 0x37, 0x0b, 0xcf, 0x68, 0xd2, 0x6a, 0x39, 0x5f, 0x79, 0x26, 0x0e,
|
||||||
|
0xc0, 0x09, 0xa7, 0x8b, 0xe5, 0xdc, 0x7b, 0x7d, 0xb3, 0x66, 0xee, 0x3d, 0x54, 0x59, 0x91, 0xaa,
|
||||||
|
0x63, 0x51, 0x88, 0xc7, 0x5d, 0x87, 0xdf, 0xfe, 0xf2, 0x23, 0x00, 0x00, 0xff, 0xff, 0x59, 0xbc,
|
||||||
|
0x55, 0x76, 0xfa, 0x01, 0x00, 0x00,
|
||||||
|
}
|
26
msg.proto
Normal file
26
msg.proto
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
option go_package = "main";
|
||||||
|
|
||||||
|
message MyMsg {
|
||||||
|
enum TYPE {
|
||||||
|
DATA = 0;
|
||||||
|
PING = 1;
|
||||||
|
KICK = 2;
|
||||||
|
MAGIC = 0xdead;
|
||||||
|
}
|
||||||
|
|
||||||
|
string id = 1;
|
||||||
|
int32 type = 2;
|
||||||
|
string target = 3;
|
||||||
|
bytes data = 4;
|
||||||
|
sint32 rproto = 5;
|
||||||
|
sint32 magic = 6;
|
||||||
|
sint32 key = 7;
|
||||||
|
int32 timeout = 8;
|
||||||
|
int32 tcpmode = 9;
|
||||||
|
int32 tcpmode_buffersize = 10;
|
||||||
|
int32 tcpmode_maxwin = 11;
|
||||||
|
int32 tcpmode_resend_timems = 12;
|
||||||
|
int32 tcpmode_compress = 13;
|
||||||
|
int32 tcpmode_stat = 14;
|
||||||
|
}
|
BIN
network.jpg
Normal file
BIN
network.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
64
pack.sh
Normal file
64
pack.sh
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
#set -x
|
||||||
|
NAME="pingtunnel"
|
||||||
|
|
||||||
|
export GO111MODULE=off
|
||||||
|
|
||||||
|
#go tool dist list
|
||||||
|
build_list=$(go tool dist list)
|
||||||
|
|
||||||
|
rm pack -rf
|
||||||
|
rm pack.zip -f
|
||||||
|
mkdir pack
|
||||||
|
|
||||||
|
go get -u -v github.com/esrrhs/pingtunnel/...
|
||||||
|
last=`pwd`
|
||||||
|
cd $GOPATH/src/golang.org/x
|
||||||
|
for dir in `ls`; do
|
||||||
|
cd $dir
|
||||||
|
git pull
|
||||||
|
cd ..
|
||||||
|
done
|
||||||
|
cd $last
|
||||||
|
|
||||||
|
for line in $build_list; do
|
||||||
|
os=$(echo "$line" | awk -F"/" '{print $1}')
|
||||||
|
arch=$(echo "$line" | awk -F"/" '{print $2}')
|
||||||
|
echo "os="$os" arch="$arch" start build"
|
||||||
|
if [ $os == "android" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ $os == "ios" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ $arch == "wasm" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags="-s -w"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "os="$os" arch="$arch" build fail"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ $os = "windows" ]; then
|
||||||
|
zip ${NAME}_"${os}"_"${arch}"".zip" $NAME".exe"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "os="$os" arch="$arch" zip fail"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mv ${NAME}_"${os}"_"${arch}"".zip" pack/
|
||||||
|
rm $NAME".exe" -f
|
||||||
|
else
|
||||||
|
zip ${NAME}_"${os}"_"${arch}"".zip" $NAME
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "os="$os" arch="$arch" zip fail"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mv ${NAME}_"${os}"_"${arch}"".zip" pack/
|
||||||
|
rm $NAME -f
|
||||||
|
fi
|
||||||
|
echo "os="$os" arch="$arch" done build"
|
||||||
|
done
|
||||||
|
|
||||||
|
zip pack.zip pack/ -r
|
||||||
|
|
||||||
|
echo "all done"
|
119
pingtunnel.go
Normal file
119
pingtunnel.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"github.com/esrrhs/gohome/common"
|
||||||
|
"github.com/esrrhs/gohome/loggo"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sendICMP(id int, sequence int, conn icmp.PacketConn, server *net.IPAddr, target string,
|
||||||
|
connId string, msgType uint32, data []byte, sproto int, rproto int, key int,
|
||||||
|
tcpmode int, tcpmode_buffer_size int, tcpmode_maxwin int, tcpmode_resend_time int, tcpmode_compress int, tcpmode_stat int,
|
||||||
|
timeout int) {
|
||||||
|
|
||||||
|
m := &MyMsg{
|
||||||
|
Id: connId,
|
||||||
|
Type: (int32)(msgType),
|
||||||
|
Target: target,
|
||||||
|
Data: data,
|
||||||
|
Rproto: (int32)(rproto),
|
||||||
|
Key: (int32)(key),
|
||||||
|
Tcpmode: (int32)(tcpmode),
|
||||||
|
TcpmodeBuffersize: (int32)(tcpmode_buffer_size),
|
||||||
|
TcpmodeMaxwin: (int32)(tcpmode_maxwin),
|
||||||
|
TcpmodeResendTimems: (int32)(tcpmode_resend_time),
|
||||||
|
TcpmodeCompress: (int32)(tcpmode_compress),
|
||||||
|
TcpmodeStat: (int32)(tcpmode_stat),
|
||||||
|
Timeout: (int32)(timeout),
|
||||||
|
Magic: (int32)(MyMsg_MAGIC),
|
||||||
|
}
|
||||||
|
|
||||||
|
mb, err := proto.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("sendICMP Marshal MyMsg error %s %s", server.String(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body := &icmp.Echo{
|
||||||
|
ID: id,
|
||||||
|
Seq: sequence,
|
||||||
|
Data: mb,
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &icmp.Message{
|
||||||
|
Type: (ipv4.ICMPType)(sproto),
|
||||||
|
Code: 0,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := msg.Marshal(nil)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("sendICMP Marshal error %s %s", server.String(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.WriteTo(bytes, server)
|
||||||
|
}
|
||||||
|
|
||||||
|
func recvICMP(workResultLock *sync.WaitGroup, exit *bool, conn icmp.PacketConn, recv chan<- *Packet) {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
(*workResultLock).Add(1)
|
||||||
|
defer (*workResultLock).Done()
|
||||||
|
|
||||||
|
bytes := make([]byte, 10240)
|
||||||
|
for !*exit {
|
||||||
|
conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100))
|
||||||
|
n, srcaddr, err := conn.ReadFrom(bytes)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
nerr, ok := err.(net.Error)
|
||||||
|
if !ok || !nerr.Timeout() {
|
||||||
|
loggo.Info("Error read icmp message %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
echoId := int(binary.BigEndian.Uint16(bytes[4:6]))
|
||||||
|
echoSeq := int(binary.BigEndian.Uint16(bytes[6:8]))
|
||||||
|
|
||||||
|
my := &MyMsg{}
|
||||||
|
err = proto.Unmarshal(bytes[8:n], my)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Debug("Unmarshal MyMsg error: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if my.Magic != (int32)(MyMsg_MAGIC) {
|
||||||
|
loggo.Debug("processPacket data invalid %s", my.Id)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
recv <- &Packet{my: my,
|
||||||
|
src: srcaddr.(*net.IPAddr),
|
||||||
|
echoId: echoId, echoSeq: echoSeq}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Packet struct {
|
||||||
|
my *MyMsg
|
||||||
|
src *net.IPAddr
|
||||||
|
echoId int
|
||||||
|
echoSeq int
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
FRAME_MAX_SIZE int = 888
|
||||||
|
FRAME_MAX_ID int = 1000000
|
||||||
|
)
|
27
pingtunnel_test.go
Normal file
27
pingtunnel_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test0001(t *testing.T) {
|
||||||
|
|
||||||
|
my := &MyMsg{}
|
||||||
|
my.Id = "12345"
|
||||||
|
my.Target = "111:11"
|
||||||
|
my.Type = 12
|
||||||
|
my.Data = make([]byte, 0)
|
||||||
|
dst, _ := proto.Marshal(my)
|
||||||
|
fmt.Println("dst = ", dst)
|
||||||
|
|
||||||
|
my1 := &MyMsg{}
|
||||||
|
proto.Unmarshal(dst, my1)
|
||||||
|
fmt.Println("my1 = ", my1)
|
||||||
|
fmt.Println("my1.Data = ", my1.Data)
|
||||||
|
|
||||||
|
proto.Unmarshal(dst[0:4], my1)
|
||||||
|
fmt.Println("my1 = ", my1)
|
||||||
|
|
||||||
|
}
|
614
server.go
Normal file
614
server.go
Normal file
@ -0,0 +1,614 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/esrrhs/gohome/common"
|
||||||
|
"github.com/esrrhs/gohome/frame"
|
||||||
|
"github.com/esrrhs/gohome/loggo"
|
||||||
|
"github.com/esrrhs/gohome/threadpool"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServer(key int, maxconn int, maxprocessthread int, maxprocessbuffer int, connecttmeout int) (*Server, error) {
|
||||||
|
s := &Server{
|
||||||
|
exit: false,
|
||||||
|
key: key,
|
||||||
|
maxconn: maxconn,
|
||||||
|
maxprocessthread: maxprocessthread,
|
||||||
|
maxprocessbuffer: maxprocessbuffer,
|
||||||
|
connecttmeout: connecttmeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
if maxprocessthread > 0 {
|
||||||
|
s.processtp = threadpool.NewThreadPool(maxprocessthread, maxprocessbuffer, func(v interface{}) {
|
||||||
|
packet := v.(*Packet)
|
||||||
|
s.processDataPacket(packet)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
exit bool
|
||||||
|
key int
|
||||||
|
workResultLock sync.WaitGroup
|
||||||
|
maxconn int
|
||||||
|
maxprocessthread int
|
||||||
|
maxprocessbuffer int
|
||||||
|
connecttmeout int
|
||||||
|
|
||||||
|
conn *icmp.PacketConn
|
||||||
|
|
||||||
|
localConnMap sync.Map
|
||||||
|
connErrorMap sync.Map
|
||||||
|
|
||||||
|
sendPacket uint64
|
||||||
|
recvPacket uint64
|
||||||
|
sendPacketSize uint64
|
||||||
|
recvPacketSize uint64
|
||||||
|
localConnMapSize int
|
||||||
|
|
||||||
|
processtp *threadpool.ThreadPool
|
||||||
|
recvcontrol chan int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConn struct {
|
||||||
|
exit bool
|
||||||
|
timeout int
|
||||||
|
ipaddrTarget *net.UDPAddr
|
||||||
|
conn *net.UDPConn
|
||||||
|
tcpaddrTarget *net.TCPAddr
|
||||||
|
tcpconn *net.TCPConn
|
||||||
|
id string
|
||||||
|
activeRecvTime time.Time
|
||||||
|
activeSendTime time.Time
|
||||||
|
close bool
|
||||||
|
rproto int
|
||||||
|
fm *frame.FrameMgr
|
||||||
|
tcpmode int
|
||||||
|
echoId int
|
||||||
|
echoSeq int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) Run() error {
|
||||||
|
|
||||||
|
conn, err := icmp.ListenPacket("ip4:icmp", "")
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Error listening for ICMP packets: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.conn = conn
|
||||||
|
|
||||||
|
recv := make(chan *Packet, 10000)
|
||||||
|
p.recvcontrol = make(chan int, 1)
|
||||||
|
go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
for !p.exit {
|
||||||
|
p.checkTimeoutConn()
|
||||||
|
p.showNet()
|
||||||
|
p.updateConnError()
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
for !p.exit {
|
||||||
|
select {
|
||||||
|
case <-p.recvcontrol:
|
||||||
|
return
|
||||||
|
case r := <-recv:
|
||||||
|
p.processPacket(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) Stop() {
|
||||||
|
p.exit = true
|
||||||
|
p.recvcontrol <- 1
|
||||||
|
p.workResultLock.Wait()
|
||||||
|
p.processtp.Stop()
|
||||||
|
p.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) processPacket(packet *Packet) {
|
||||||
|
|
||||||
|
if packet.my.Key != (int32)(p.key) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.my.Type == (int32)(MyMsg_PING) {
|
||||||
|
t := time.Time{}
|
||||||
|
t.UnmarshalBinary(packet.my.Data)
|
||||||
|
loggo.Info("ping from %s %s %d %d %d", packet.src.String(), t.String(), packet.my.Rproto, packet.echoId, packet.echoSeq)
|
||||||
|
sendICMP(packet.echoId, packet.echoSeq, *p.conn, packet.src, "", "", (uint32)(MyMsg_PING), packet.my.Data,
|
||||||
|
(int)(packet.my.Rproto), -1, p.key,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.my.Type == (int32)(MyMsg_KICK) {
|
||||||
|
localConn := p.getServerConnById(packet.my.Id)
|
||||||
|
if localConn != nil {
|
||||||
|
p.close(localConn)
|
||||||
|
loggo.Info("remote kick local %s", packet.my.Id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.maxprocessthread > 0 {
|
||||||
|
p.processtp.AddJob((int)(common.HashString(packet.my.Id)), packet)
|
||||||
|
} else {
|
||||||
|
p.processDataPacket(packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) processDataPacketNewConn(id string, packet *Packet) *ServerConn {
|
||||||
|
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
|
||||||
|
loggo.Info("start add new connect %s %s", id, packet.my.Target)
|
||||||
|
|
||||||
|
if p.maxconn > 0 && p.localConnMapSize >= p.maxconn {
|
||||||
|
loggo.Info("too many connections %d, server connected target fail %s", p.localConnMapSize, packet.my.Target)
|
||||||
|
p.remoteError(packet.echoId, packet.echoSeq, id, (int)(packet.my.Rproto), packet.src)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := packet.my.Target
|
||||||
|
if p.isConnError(addr) {
|
||||||
|
loggo.Info("addr connect Error before: %s %s", id, addr)
|
||||||
|
p.remoteError(packet.echoId, packet.echoSeq, id, (int)(packet.my.Rproto), packet.src)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if packet.my.Tcpmode > 0 {
|
||||||
|
|
||||||
|
c, err := net.DialTimeout("tcp", addr, time.Millisecond*time.Duration(p.connecttmeout))
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Error listening for tcp packets: %s %s", id, err.Error())
|
||||||
|
p.remoteError(packet.echoId, packet.echoSeq, id, (int)(packet.my.Rproto), packet.src)
|
||||||
|
p.addConnError(addr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
targetConn := c.(*net.TCPConn)
|
||||||
|
ipaddrTarget := targetConn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
|
||||||
|
fm := frame.NewFrameMgr(FRAME_MAX_SIZE, FRAME_MAX_ID, (int)(packet.my.TcpmodeBuffersize), (int)(packet.my.TcpmodeMaxwin), (int)(packet.my.TcpmodeResendTimems), (int)(packet.my.TcpmodeCompress),
|
||||||
|
(int)(packet.my.TcpmodeStat))
|
||||||
|
|
||||||
|
localConn := &ServerConn{exit: false, timeout: (int)(packet.my.Timeout), tcpconn: targetConn, tcpaddrTarget: ipaddrTarget, id: id, activeRecvTime: now, activeSendTime: now, close: false,
|
||||||
|
rproto: (int)(packet.my.Rproto), fm: fm, tcpmode: (int)(packet.my.Tcpmode)}
|
||||||
|
|
||||||
|
p.addServerConn(id, localConn)
|
||||||
|
|
||||||
|
go p.RecvTCP(localConn, id, packet.src)
|
||||||
|
return localConn
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
c, err := net.DialTimeout("udp", addr, time.Millisecond*time.Duration(p.connecttmeout))
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Error listening for udp packets: %s %s", id, err.Error())
|
||||||
|
p.remoteError(packet.echoId, packet.echoSeq, id, (int)(packet.my.Rproto), packet.src)
|
||||||
|
p.addConnError(addr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
targetConn := c.(*net.UDPConn)
|
||||||
|
ipaddrTarget := targetConn.RemoteAddr().(*net.UDPAddr)
|
||||||
|
|
||||||
|
localConn := &ServerConn{exit: false, timeout: (int)(packet.my.Timeout), conn: targetConn, ipaddrTarget: ipaddrTarget, id: id, activeRecvTime: now, activeSendTime: now, close: false,
|
||||||
|
rproto: (int)(packet.my.Rproto), tcpmode: (int)(packet.my.Tcpmode)}
|
||||||
|
|
||||||
|
p.addServerConn(id, localConn)
|
||||||
|
|
||||||
|
go p.Recv(localConn, id, packet.src)
|
||||||
|
|
||||||
|
return localConn
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) processDataPacket(packet *Packet) {
|
||||||
|
|
||||||
|
loggo.Debug("processPacket %s %s %d", packet.my.Id, packet.src.String(), len(packet.my.Data))
|
||||||
|
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
|
||||||
|
id := packet.my.Id
|
||||||
|
localConn := p.getServerConnById(id)
|
||||||
|
if localConn == nil {
|
||||||
|
localConn = p.processDataPacketNewConn(id, packet)
|
||||||
|
if localConn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localConn.activeRecvTime = now
|
||||||
|
localConn.echoId = packet.echoId
|
||||||
|
localConn.echoSeq = packet.echoSeq
|
||||||
|
|
||||||
|
if packet.my.Type == (int32)(MyMsg_DATA) {
|
||||||
|
|
||||||
|
if packet.my.Tcpmode > 0 {
|
||||||
|
f := &frame.Frame{}
|
||||||
|
err := proto.Unmarshal(packet.my.Data, f)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Unmarshal tcp Error %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
localConn.fm.OnRecvFrame(f)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if packet.my.Data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err := localConn.conn.Write(packet.my.Data)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Info("WriteToUDP Error %s", err)
|
||||||
|
localConn.close = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.recvPacket++
|
||||||
|
p.recvPacketSize += (uint64)(len(packet.my.Data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) RecvTCP(conn *ServerConn, id string, src *net.IPAddr) {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
loggo.Info("server waiting target response %s -> %s %s", conn.tcpaddrTarget.String(), conn.id, conn.tcpconn.LocalAddr().String())
|
||||||
|
|
||||||
|
loggo.Info("start wait remote connect tcp %s %s", conn.id, conn.tcpaddrTarget.String())
|
||||||
|
startConnectTime := common.GetNowUpdateInSecond()
|
||||||
|
for !p.exit && !conn.exit {
|
||||||
|
if conn.fm.IsConnected() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
conn.fm.Update()
|
||||||
|
sendlist := conn.fm.GetSendList()
|
||||||
|
for e := sendlist.Front(); e != nil; e = e.Next() {
|
||||||
|
f := e.Value.(*frame.Frame)
|
||||||
|
mb, _ := conn.fm.MarshalFrame(f)
|
||||||
|
sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb,
|
||||||
|
conn.rproto, -1, p.key,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
p.sendPacket++
|
||||||
|
p.sendPacketSize += (uint64)(len(mb))
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
diffclose := now.Sub(startConnectTime)
|
||||||
|
if diffclose > time.Second*5 {
|
||||||
|
loggo.Info("can not connect remote tcp %s %s", conn.id, conn.tcpaddrTarget.String())
|
||||||
|
p.close(conn)
|
||||||
|
p.remoteError(conn.echoId, conn.echoSeq, id, conn.rproto, src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !conn.exit {
|
||||||
|
loggo.Info("remote connected tcp %s %s", conn.id, conn.tcpaddrTarget.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := make([]byte, 10240)
|
||||||
|
|
||||||
|
tcpActiveRecvTime := common.GetNowUpdateInSecond()
|
||||||
|
tcpActiveSendTime := common.GetNowUpdateInSecond()
|
||||||
|
|
||||||
|
for !p.exit && !conn.exit {
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
sleep := true
|
||||||
|
|
||||||
|
left := common.MinOfInt(conn.fm.GetSendBufferLeft(), len(bytes))
|
||||||
|
if left > 0 {
|
||||||
|
conn.tcpconn.SetReadDeadline(time.Now().Add(time.Millisecond * 1))
|
||||||
|
n, err := conn.tcpconn.Read(bytes[0:left])
|
||||||
|
if err != nil {
|
||||||
|
nerr, ok := err.(net.Error)
|
||||||
|
if !ok || !nerr.Timeout() {
|
||||||
|
loggo.Info("Error read tcp %s %s %s", conn.id, conn.tcpaddrTarget.String(), err)
|
||||||
|
conn.fm.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
sleep = false
|
||||||
|
conn.fm.WriteSendBuffer(bytes[:n])
|
||||||
|
tcpActiveRecvTime = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.fm.Update()
|
||||||
|
|
||||||
|
sendlist := conn.fm.GetSendList()
|
||||||
|
if sendlist.Len() > 0 {
|
||||||
|
sleep = false
|
||||||
|
conn.activeSendTime = now
|
||||||
|
for e := sendlist.Front(); e != nil; e = e.Next() {
|
||||||
|
f := e.Value.(*frame.Frame)
|
||||||
|
mb, err := conn.fm.MarshalFrame(f)
|
||||||
|
if err != nil {
|
||||||
|
loggo.Error("Error tcp Marshal %s %s %s", conn.id, conn.tcpaddrTarget.String(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb,
|
||||||
|
conn.rproto, -1, p.key,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
p.sendPacket++
|
||||||
|
p.sendPacketSize += (uint64)(len(mb))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.fm.GetRecvBufferSize() > 0 {
|
||||||
|
sleep = false
|
||||||
|
rr := conn.fm.GetRecvReadLineBuffer()
|
||||||
|
conn.tcpconn.SetWriteDeadline(time.Now().Add(time.Millisecond * 1))
|
||||||
|
n, err := conn.tcpconn.Write(rr)
|
||||||
|
if err != nil {
|
||||||
|
nerr, ok := err.(net.Error)
|
||||||
|
if !ok || !nerr.Timeout() {
|
||||||
|
loggo.Info("Error write tcp %s %s %s", conn.id, conn.tcpaddrTarget.String(), err)
|
||||||
|
conn.fm.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
conn.fm.SkipRecvBuffer(n)
|
||||||
|
tcpActiveSendTime = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sleep {
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
diffrecv := now.Sub(conn.activeRecvTime)
|
||||||
|
diffsend := now.Sub(conn.activeSendTime)
|
||||||
|
tcpdiffrecv := now.Sub(tcpActiveRecvTime)
|
||||||
|
tcpdiffsend := now.Sub(tcpActiveSendTime)
|
||||||
|
if diffrecv > time.Second*(time.Duration(conn.timeout)) || diffsend > time.Second*(time.Duration(conn.timeout)) ||
|
||||||
|
(tcpdiffrecv > time.Second*(time.Duration(conn.timeout)) && tcpdiffsend > time.Second*(time.Duration(conn.timeout))) {
|
||||||
|
loggo.Info("close inactive conn %s %s", conn.id, conn.tcpaddrTarget.String())
|
||||||
|
conn.fm.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.fm.IsRemoteClosed() {
|
||||||
|
loggo.Info("closed by remote conn %s %s", conn.id, conn.tcpaddrTarget.String())
|
||||||
|
conn.fm.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.fm.Close()
|
||||||
|
|
||||||
|
startCloseTime := common.GetNowUpdateInSecond()
|
||||||
|
for !p.exit && !conn.exit {
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
|
||||||
|
conn.fm.Update()
|
||||||
|
|
||||||
|
sendlist := conn.fm.GetSendList()
|
||||||
|
for e := sendlist.Front(); e != nil; e = e.Next() {
|
||||||
|
f := e.Value.(*frame.Frame)
|
||||||
|
mb, _ := conn.fm.MarshalFrame(f)
|
||||||
|
sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb,
|
||||||
|
conn.rproto, -1, p.key,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
p.sendPacket++
|
||||||
|
p.sendPacketSize += (uint64)(len(mb))
|
||||||
|
}
|
||||||
|
|
||||||
|
nodatarecv := true
|
||||||
|
if conn.fm.GetRecvBufferSize() > 0 {
|
||||||
|
rr := conn.fm.GetRecvReadLineBuffer()
|
||||||
|
conn.tcpconn.SetWriteDeadline(time.Now().Add(time.Millisecond * 100))
|
||||||
|
n, _ := conn.tcpconn.Write(rr)
|
||||||
|
if n > 0 {
|
||||||
|
conn.fm.SkipRecvBuffer(n)
|
||||||
|
nodatarecv = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diffclose := now.Sub(startCloseTime)
|
||||||
|
if diffclose > time.Second*60 {
|
||||||
|
loggo.Info("close conn had timeout %s %s", conn.id, conn.tcpaddrTarget.String())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteclosed := conn.fm.IsRemoteClosed()
|
||||||
|
if remoteclosed && nodatarecv {
|
||||||
|
loggo.Info("remote conn had closed %s %s", conn.id, conn.tcpaddrTarget.String())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
loggo.Info("close tcp conn %s %s", conn.id, conn.tcpaddrTarget.String())
|
||||||
|
p.close(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) Recv(conn *ServerConn, id string, src *net.IPAddr) {
|
||||||
|
|
||||||
|
defer common.CrashLog()
|
||||||
|
|
||||||
|
p.workResultLock.Add(1)
|
||||||
|
defer p.workResultLock.Done()
|
||||||
|
|
||||||
|
loggo.Info("server waiting target response %s -> %s %s", conn.ipaddrTarget.String(), conn.id, conn.conn.LocalAddr().String())
|
||||||
|
|
||||||
|
bytes := make([]byte, 2000)
|
||||||
|
|
||||||
|
for !p.exit {
|
||||||
|
|
||||||
|
conn.conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100))
|
||||||
|
n, _, err := conn.conn.ReadFromUDP(bytes)
|
||||||
|
if err != nil {
|
||||||
|
nerr, ok := err.(net.Error)
|
||||||
|
if !ok || !nerr.Timeout() {
|
||||||
|
loggo.Info("ReadFromUDP Error read udp %s", err)
|
||||||
|
conn.close = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
conn.activeSendTime = now
|
||||||
|
|
||||||
|
sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), bytes[:n],
|
||||||
|
conn.rproto, -1, p.key,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
|
||||||
|
p.sendPacket++
|
||||||
|
p.sendPacketSize += (uint64)(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) close(conn *ServerConn) {
|
||||||
|
if p.getServerConnById(conn.id) != nil {
|
||||||
|
conn.exit = true
|
||||||
|
if conn.conn != nil {
|
||||||
|
conn.conn.Close()
|
||||||
|
}
|
||||||
|
if conn.tcpconn != nil {
|
||||||
|
conn.tcpconn.Close()
|
||||||
|
}
|
||||||
|
p.deleteServerConn(conn.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) checkTimeoutConn() {
|
||||||
|
|
||||||
|
tmp := make(map[string]*ServerConn)
|
||||||
|
p.localConnMap.Range(func(key, value interface{}) bool {
|
||||||
|
id := key.(string)
|
||||||
|
serverConn := value.(*ServerConn)
|
||||||
|
tmp[id] = serverConn
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
for _, conn := range tmp {
|
||||||
|
if conn.tcpmode > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
diffrecv := now.Sub(conn.activeRecvTime)
|
||||||
|
diffsend := now.Sub(conn.activeSendTime)
|
||||||
|
if diffrecv > time.Second*(time.Duration(conn.timeout)) || diffsend > time.Second*(time.Duration(conn.timeout)) {
|
||||||
|
conn.close = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, conn := range tmp {
|
||||||
|
if conn.tcpmode > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if conn.close {
|
||||||
|
loggo.Info("close inactive conn %s %s", id, conn.ipaddrTarget.String())
|
||||||
|
p.close(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) showNet() {
|
||||||
|
p.localConnMapSize = 0
|
||||||
|
p.localConnMap.Range(func(key, value interface{}) bool {
|
||||||
|
p.localConnMapSize++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
loggo.Info("send %dPacket/s %dKB/s recv %dPacket/s %dKB/s %dConnections",
|
||||||
|
p.sendPacket, p.sendPacketSize/1024, p.recvPacket, p.recvPacketSize/1024, p.localConnMapSize)
|
||||||
|
p.sendPacket = 0
|
||||||
|
p.recvPacket = 0
|
||||||
|
p.sendPacketSize = 0
|
||||||
|
p.recvPacketSize = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) addServerConn(uuid string, serverConn *ServerConn) {
|
||||||
|
p.localConnMap.Store(uuid, serverConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) getServerConnById(uuid string) *ServerConn {
|
||||||
|
ret, ok := p.localConnMap.Load(uuid)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ret.(*ServerConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) deleteServerConn(uuid string) {
|
||||||
|
p.localConnMap.Delete(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) remoteError(echoId int, echoSeq int, uuid string, rprpto int, src *net.IPAddr) {
|
||||||
|
sendICMP(echoId, echoSeq, *p.conn, src, "", uuid, (uint32)(MyMsg_KICK), []byte{},
|
||||||
|
rprpto, -1, p.key,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) addConnError(addr string) {
|
||||||
|
_, ok := p.connErrorMap.Load(addr)
|
||||||
|
if !ok {
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
p.connErrorMap.Store(addr, now)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) isConnError(addr string) bool {
|
||||||
|
_, ok := p.connErrorMap.Load(addr)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Server) updateConnError() {
|
||||||
|
|
||||||
|
tmp := make(map[string]time.Time)
|
||||||
|
p.connErrorMap.Range(func(key, value interface{}) bool {
|
||||||
|
id := key.(string)
|
||||||
|
t := value.(time.Time)
|
||||||
|
tmp[id] = t
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
now := common.GetNowUpdateInSecond()
|
||||||
|
for id, t := range tmp {
|
||||||
|
diff := now.Sub(t)
|
||||||
|
if diff > time.Second*5 {
|
||||||
|
p.connErrorMap.Delete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user