Compare commits

...

40 commits
2.6 ... master

Author SHA1 Message Date
esrrhs
66d34ba031
Merge pull request #60 from esrrhs/esrrhs-patch-1
Update go.yml
2023-11-02 20:23:13 +08:00
esrrhs
a350629abc
Update go.yml 2023-11-02 20:22:58 +08:00
esrrhs
5d53ddeb7a update 2023-11-02 20:22:15 +08:00
esrrhs
933b646d98
Merge pull request #59 from esrrhs/dependabot/go_modules/golang.org/x/net-0.17.0
Bump golang.org/x/net from 0.8.0 to 0.17.0
2023-10-12 09:19:58 +08:00
dependabot[bot]
404ea744fe
Bump golang.org/x/net from 0.8.0 to 0.17.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.8.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.8.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-11 23:35:00 +00:00
esrrhs
b30676729c
Merge pull request #58 from jiqing112/master
更新 main.go内的 -key 说明
2023-06-19 15:34:16 +08:00
jiqing112
6ab38e9d01
更新 main.go内的 -key 说明
更新 main.go内的 -key 说明
2023-06-19 14:40:06 +08:00
esrrhs
19d00b970e
Merge pull request #57 from jiqing112/patch-4
还是空格的错误,这次应该没有了
2023-04-01 16:11:44 +08:00
jiqing112
3d001c21f0
还是空格的错误,这次应该没有了 2023-04-01 15:48:50 +08:00
esrrhs
3c2bcf9bc9
Merge pull request #56 from jiqing112/patch-3
docker命令里的空格错误
2023-04-01 15:37:50 +08:00
jiqing112
a4d7ee02d9
docker命令里的空格错误 2023-04-01 14:53:46 +08:00
esrrhs
327cf680c7
Merge pull request #55 from jiqing112/patch-2
Update README.md
2023-04-01 08:11:05 +08:00
esrrhs
cab02d012b
Merge pull request #54 from jiqing112/patch-1
Update README.md
2023-04-01 08:10:35 +08:00
jiqing112
8824c495f5
Update README.md
补充关于 -key参数的正确使用。避免使用者误会 “-key” 参数是任意类型的字符串形式的密码
2023-03-31 13:53:43 +08:00
jiqing112
1c79a8db64
Update README.md
范例的命令有个位置打错了空格,会让参数报错,无法执行。
2023-03-31 13:38:40 +08:00
esrrhs
7af41a7723 update 2023-03-18 16:43:16 +08:00
esrrhs
aa216a7d72 change 2023-03-18 13:35:27 +08:00
esrrhs
520cdd7063 move dir 2023-03-18 13:32:35 +08:00
esrrhs
f8d9ed6b5e Merge branch 'master' of https://github.com/esrrhs/pingtunnel 2023-02-22 21:45:45 +08:00
esrrhs
e816385534 upadte 2023-02-22 21:45:34 +08:00
esrrhs
4a02bd6270
Update README.md 2023-02-22 21:42:13 +08:00
esrrhs
5163a571dc add 2023-02-22 21:41:13 +08:00
esrrhs
7187a57ceb
Create docker-image.yml 2023-02-22 21:36:59 +08:00
esrrhs
5f81102814 update 2023-02-22 21:36:00 +08:00
esrrhs
94a9dc1eed
Create go.yml 2023-02-22 21:32:12 +08:00
esrrhs
fe571e8e4e add 2023-02-22 21:30:11 +08:00
esrrhs
93ab736d55 add 2023-02-22 21:29:12 +08:00
esrrhs
a1a1f0462a delete 2022-10-06 21:57:54 +08:00
esrrhs
7970b38a68 delete 2022-10-06 21:56:15 +08:00
esrrhs
8d63ad5334
Update README.md 2022-10-06 21:51:40 +08:00
esrrhs
219755bd5c
Update README.md 2022-10-06 21:42:02 +08:00
esrrhs
622f631bbd
Update README.md 2022-07-05 11:58:31 +08:00
esrrhs
4422d7c1d0
Create docker-image.yml 2022-06-14 11:45:27 +08:00
esrrhs
cb246793e6
Update README.md 2022-01-27 15:16:25 +08:00
esrrhs
cd1be4debf
Update README.md 2022-01-27 15:16:04 +08:00
esrrhs
856300a446
Update README.md 2021-12-20 10:49:26 +08:00
esrrhs
ec19b924c8
Update README.md 2021-12-20 10:49:13 +08:00
esrrhs
ef8486c4eb
Update README.md 2021-11-16 12:20:44 +08:00
benderzhao
60e4971fdf Merge branch 'master' of https://github.com/esrrhs/pingtunnel 2021-11-16 12:19:54 +08:00
benderzhao
59dc4b4f79 add 2021-11-16 12:19:44 +08:00
16 changed files with 1982 additions and 138 deletions

34
.github/workflows/docker-image.yml vendored Normal file
View file

@ -0,0 +1,34 @@
name: Docker Image CI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
push: true
tags: esrrhs/pingtunnel:latest

View file

@ -1,29 +1,30 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
name: Go
on:
push:
branches: [ master ]
branches: [ "master" ]
pull_request:
branches: [ master ]
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.21
- name: Get dependencies
run: |
GO111MODULE=off go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: GO111MODULE=off go build -v ./...
- name: Build
run: |
go mod tidy
go build -v ./...
- name: Test
run: go test -v ./...

View file

@ -1,10 +1,13 @@
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
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . ./
RUN go build -v -o pingtunnel
FROM debian
COPY --from=build-env /go/bin/pingtunnel .
COPY --from=build-env /app/pingtunnel .
COPY GeoLite2-Country.mmdb .
WORKDIR ./

View file

@ -6,61 +6,62 @@
[<img src="https://img.shields.io/github/v/release/esrrhs/pingtunnel">](https://github.com/esrrhs/pingtunnel/releases)
[<img src="https://img.shields.io/github/downloads/esrrhs/pingtunnel/total">](https://github.com/esrrhs/pingtunnel/releases)
[<img src="https://img.shields.io/docker/pulls/esrrhs/pingtunnel">](https://hub.docker.com/repository/docker/esrrhs/pingtunnel)
[<img src="https://img.shields.io/github/workflow/status/esrrhs/pingtunnel/Go">](https://github.com/esrrhs/pingtunnel/actions)
[<img src="https://img.shields.io/github/actions/workflow/status/esrrhs/pingtunnel/go.yml?branch=master">](https://github.com/esrrhs/pingtunnel/actions)
pingtunnel 是把 tcp/udp/sock5 流量伪装成 icmp 流量进行转发的工具
Pingtunnel is a tool that send TCP/UDP traffic over ICMP.
[Readme EN](./README_EN.md)
## 注意:本工具只是用作学习研究,请勿用于非法用途!!!
## 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
- 首先准备好一个具有公网 ip 的服务器,假定域名或者公网 ip 是www.yourserver.com
- 从[releases](https://github.com/esrrhs/pingtunnel/releases)下载对应的安装包,如 pingtunnel_linux64.zip然后解压以**root**权限执行
- 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
- “-key” parameter is **int** type, only supports numbers between 0-2147483647
```
sudo wget (最新release的下载链接)
sudo wget (link of latest release)
sudo unzip pingtunnel_linux64.zip
sudo ./pingtunnel -type server
```
- (可选)关闭系统默认的 ping
- (Optional) Disable system default ping
```
echo 1 >/proc/sys/net/ipv4/icmp_echo_ignore_all
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
```
### 安装客户端
### Install the client
- 从[releases](https://github.com/esrrhs/pingtunnel/releases)下载对应的安装包,如 pingtunnel_windows64.zip解压
- 然后用**管理员权限**运行,不同的转发功能所对应的命令如下
- 如果看到有 ping pong 的 log说明连接正常
- 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
- “-key” parameter is **int** type, only supports numbers between 0-2147483647
#### 转发 sock5
#### Forward sock5
```
pingtunnel.exe -type client -l :4455 -s www.yourserver.com -sock5 1
```
#### 转发 tcp
#### Forward tcp
```
pingtunnel.exe -type client -l :4455 -s www.yourserver.com -t www.yourserver.com:4455 -tcp 1
```
#### 转发 udp
#### Forward udp
```
pingtunnel.exe -type client -l :4455 -s www.yourserver.com -t www.yourserver.com:4455
```
### Docker
也可直接用docker启动更方便。参数同上
### 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
@ -70,10 +71,8 @@ docker run --name pingtunnel-server -d --privileged --network host --restart=alw
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>
下载 centos 镜像 [centos mirror](http://centos.s.uw.edu/centos/8.4.2105/isos/x86_64/CentOS-8.4.2105-x86_64-dvd1.iso),对比如下
| | wget | ss | kcp | pingtunnel |
| ------ | -------- | -------- | ------- | ---------- |
| 阿里云 | 26.6KB/s | 31.8KB/s | 606KB/s | 5.64MB/s |

View file

@ -1,77 +0,0 @@
# 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/downloads/esrrhs/pingtunnel/total">](https://github.com/esrrhs/pingtunnel/releases)
[<img src="https://img.shields.io/docker/pulls/esrrhs/pingtunnel">](https://hub.docker.com/repository/docker/esrrhs/pingtunnel)
[<img src="https://img.shields.io/github/workflow/status/esrrhs/pingtunnel/Go">](https://github.com/esrrhs/pingtunnel/actions)
Pingtunnel is a tool that advertises tcp/udp/sock5 traffic as icmp traffic for forwarding.
## 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
```
## Test
download the centos image [centos mirror](http://centos.s.uw.edu/centos/8.4.2105/isos/x86_64/CentOS-8.4.2105-x86_64-dvd1.iso)
| | wget | ss | kcp | pingtunnel |
| ------------ | -------- | -------- | ------- | ---------- |
| AlibabaCloud | 26.6KB/s | 31.8KB/s | 606KB/s | 5.64MB/s |

830
client.go Normal file
View file

@ -0,0 +1,830 @@
package pingtunnel
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
}
}

View file

@ -3,10 +3,10 @@ package main
import (
"flag"
"fmt"
"github.com/esrrhs/go-engine/src/common"
"github.com/esrrhs/go-engine/src/geoip"
"github.com/esrrhs/go-engine/src/loggo"
"github.com/esrrhs/go-engine/src/pingtunnel"
"github.com/esrrhs/gohome/common"
"github.com/esrrhs/gohome/geoip"
"github.com/esrrhs/gohome/loggo"
"github.com/esrrhs/pingtunnel"
"net"
"net/http"
_ "net/http/pprof"
@ -37,7 +37,7 @@ Usage:
服务器参数server param:
-key 设置的密码默认0
-key 设置的纯数字密码默认0, 参数为int类型范围从0-2147483647不可夹杂字母特殊符号
Set password, default 0
-nolog 不写日志文件只打印标准输出默认0

1
gen.bat Normal file
View file

@ -0,0 +1 @@
protoc --go_out=. *.proto

18
go.mod Normal file
View file

@ -0,0 +1,18 @@
module github.com/esrrhs/pingtunnel
go 1.18
require (
github.com/esrrhs/gohome v0.0.0-20231102120537-c519efbde976
github.com/golang/protobuf v1.5.3
golang.org/x/net v0.17.0
)
require (
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/oschwald/geoip2-golang v1.9.0 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
golang.org/x/sys v0.13.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)

29
go.sum Normal file
View file

@ -0,0 +1,29 @@
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/esrrhs/gohome v0.0.0-20231102120537-c519efbde976 h1:av0d/lRou1Z5cxdSQFwtVcqJjokFI5pJyyr63iAuYis=
github.com/esrrhs/gohome v0.0.0-20231102120537-c519efbde976/go.mod h1:S5fYcOFy4nUPnkYg7D9hIp+SwBR9kCBiOYmWVW42Yhs=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

228
msg.pb.go Normal file
View file

@ -0,0 +1,228 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: msg.proto
package pingtunnel
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
View file

@ -0,0 +1,26 @@
syntax = "proto3";
option go_package = "pingtunnel";
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;
}

12
pack.sh
View file

@ -2,7 +2,7 @@
#set -x
NAME="pingtunnel"
export GO111MODULE=off
export GO111MODULE=on
#go tool dist list
build_list=$(go tool dist list)
@ -11,15 +11,7 @@ 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
go mod tidy
for line in $build_list; do
os=$(echo "$line" | awk -F"/" '{print $1}')

119
pingtunnel.go Normal file
View file

@ -0,0 +1,119 @@
package pingtunnel
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
View file

@ -0,0 +1,27 @@
package pingtunnel
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
View file

@ -0,0 +1,614 @@
package pingtunnel
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)
}
}
}