Compare commits

..

No commits in common. "master" and "0.2" have entirely different histories.
master ... 0.2

23 changed files with 359 additions and 2100 deletions

View file

@ -1,34 +0,0 @@
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,30 +0,0 @@
# 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" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.21
- name: Build
run: |
go mod tidy
go build -v ./...
- name: Test
run: go test -v ./...

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -1,13 +0,0 @@
FROM golang AS build-env
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . ./
RUN go build -v -o pingtunnel
FROM debian
COPY --from=build-env /app/pingtunnel .
COPY GeoLite2-Country.mmdb .
WORKDIR ./

Binary file not shown.

View file

@ -1,78 +1,24 @@
# Pingtunnel
# pingtunnel
通过伪造ping把udp流量通过远程服务器转发到目的服务器上。用于突破某些运营商封锁UDP流量。
By forging ping, the udp traffic is forwarded to the destination server through the remote server. Used to break certain operators to block UDP traffic.
[<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/actions/workflow/status/esrrhs/pingtunnel/go.yml?branch=master">](https://github.com/esrrhs/pingtunnel/actions)
Usage:
Pingtunnel is a tool that send TCP/UDP traffic over ICMP.
pingtunnel -type server
## Note: This tool is only to be used for study and research, do not use it for illegal purposes
pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455
![image](network.jpg)
-type 服务器或者客户端
client or server
## Usage
-l 本地的地址,发到这个端口的流量将转发到服务器
Local address, traffic sent to this port will be forwarded to the server
### 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
- “-key” parameter is **int** type, only supports numbers between 0-2147483647
```
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
- “-key” parameter is **int** type, only supports numbers between 0-2147483647
#### 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>
-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 本地记录连接超时的时间,单位是秒
The time when the local record connection timed out, in seconds

View file

@ -1 +0,0 @@
theme: jekyll-theme-cayman

765
client.go
View file

@ -1,101 +1,38 @@
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"
"fmt"
"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) (*Client, error) {
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)
ipaddr, err := net.ResolveUDPAddr("udp", 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
@ -105,33 +42,16 @@ type Client struct {
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
localAddrToConnMap map[string]*ClientConn
localIdToConnMap map[string]*ClientConn
}
type ClientConn struct {
exit bool
ipaddr *net.UDPAddr
tcpaddr *net.TCPAddr
id string
activeRecvTime time.Time
activeSendTime time.Time
activeTime time.Time
close bool
fm *frame.FrameMgr
}
func (p *Client) Addr() string {
@ -154,677 +74,128 @@ 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 {
func (p *Client) Run() {
conn, err := icmp.ListenPacket("ip4:icmp", "")
if err != nil {
loggo.Error("Error listening for ICMP packets: %s", err.Error())
return err
fmt.Printf("Error listening for ICMP packets: %s\n", err.Error())
return
}
defer conn.Close()
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:
fmt.Printf("Error listening for udp packets: %s\n", err.Error())
return
}
defer listener.Close()
p.listenConn = listener
p.localAddrToConnMap = make(map[string]*ClientConn)
p.localIdToConnMap = make(map[string]*ClientConn)
go p.Accept()
recv := make(chan *Packet, 1000)
go recvICMP(*p.conn, recv)
interval := time.NewTicker(time.Second)
defer interval.Stop()
for {
select {
case <-interval.C:
p.checkTimeoutConn()
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")
fmt.Println("client waiting local accept")
bytes := make([]byte, 10240)
for !p.exit {
for {
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)
if neterr, ok := err.(*net.OpError); ok {
if neterr.Timeout() {
// Read timeout
continue
} else {
fmt.Printf("Error read udp %s\n", err)
continue
}
}
if n <= 0 {
continue
}
now := common.GetNowUpdateInSecond()
clientConn := p.getClientConnByAddr(srcaddr.String())
now := time.Now()
clientConn := p.localAddrToConnMap[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())
uuid := UniqueId()
clientConn = &ClientConn{ipaddr: srcaddr, id: uuid, activeTime: now, close: false}
p.localAddrToConnMap[srcaddr.String()] = clientConn
p.localIdToConnMap[uuid] = clientConn
fmt.Printf("client accept new local %s %s\n", 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)
clientConn.activeTime = now
sendICMP(*p.conn, p.ipaddrServer, p.targetAddr, clientConn.id, (uint32)(DATA), bytes[:n])
}
return nil
}
func (p *Client) processPacket(packet *Packet) {
if packet.my.Rproto >= 0 {
if packet.data == nil {
return
}
if packet.my.Key != (int32)(p.key) {
return
}
fmt.Printf("processPacket %s %s %d\n", packet.id, packet.src.String(), len(packet.data))
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)
clientConn := p.localIdToConnMap[packet.id]
if clientConn == nil {
loggo.Debug("processPacket no conn %s ", packet.my.Id)
p.remoteError(packet.my.Id)
fmt.Printf("processPacket no conn %s \n", packet.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)
now := time.Now()
clientConn.activeTime = now
_, err := p.listenConn.WriteToUDP(packet.data, addr)
if err != nil {
loggo.Info("WriteToUDP Error read udp %s", err)
fmt.Printf("WriteToUDP Error read udp %s\n", err)
clientConn.close = true
return
}
}
p.recvPacket++
p.recvPacketSize += (uint64)(len(packet.my.Data))
func (p *Client) Close(clientConn *ClientConn) {
if p.localIdToConnMap[clientConn.id] != nil {
delete(p.localIdToConnMap, clientConn.id)
delete(p.localAddrToConnMap, clientConn.ipaddr.String())
}
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)) {
now := time.Now()
for _, conn := range p.localIdToConnMap {
diff := now.Sub(conn.activeTime)
if diff > time.Second*(time.Duration(p.timeout)) {
conn.close = true
}
}
for id, conn := range tmp {
for id, conn := range p.localIdToConnMap {
if conn.close {
loggo.Info("close inactive conn %s %s", id, conn.ipaddr.String())
p.close(conn)
fmt.Printf("close inactive conn %s %s\n", 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,66 +3,22 @@ package main
import (
"flag"
"fmt"
"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"
"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.
通过伪造ping把udp流量通过远程服务器转发到目的服务器上用于突破某些运营商封锁UDP流量
By forging ping, the udp traffic is forwarded to the destination server through the remote server. Used to break certain operators to block 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, 参数为int类型范围从0-2147483647不可夹杂字母特殊符号
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
@ -72,198 +28,53 @@ Usage:
-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
-timeout 本地记录连接超时的时间单位是秒
The time when the local record connection timed out, in seconds
`
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" {
if (*t != "client" && *t != "server") || (*t == "client" && (len(*listen) == 0 || len(*target) == 0 || len(*server) == 0)) {
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 > pingtunnel.FRAME_MAX_ID {
fmt.Println("set tcp win to big, max = " + strconv.Itoa(pingtunnel.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)
fmt.Println("start...")
if *t == "server" {
s, err := pingtunnel.NewServer(*key, *maxconn, *max_process_thread, *max_process_buffer, *conntt)
s, err := pingtunnel.NewServer(*timeout)
if err != nil {
loggo.Error("ERROR: %s", err.Error())
fmt.Printf("ERROR: %s\n", err.Error())
return
}
loggo.Info("Server start")
err = s.Run()
fmt.Printf("Server start\n")
s.Run()
}
if *t == "client" {
fmt.Printf("type %s\n", *t)
fmt.Printf("listen %s\n", *listen)
fmt.Printf("server %s\n", *server)
fmt.Printf("target %s\n", *target)
c, err := pingtunnel.NewClient(*listen, *server, *target, *timeout)
if err != nil {
loggo.Error("Run ERROR: %s", err.Error())
fmt.Printf("ERROR: %s\n", 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 := geoip.Load(*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 := geoip.GetCountryIsoCode(taddr.IP.String())
if err != nil {
return false
}
if len(ret) <= 0 {
return false
}
return ret != *s5filter
}
c, err := pingtunnel.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(),
fmt.Printf("Client Listen %s (%s) Server %s (%s) TargetPort %s:\n", 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)
c.Run()
}
}

View file

@ -1,2 +0,0 @@
KEY=123456
SERVER=www.yourserver.com

View file

@ -1,16 +0,0 @@
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

View file

@ -1,9 +0,0 @@
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}"

View file

@ -1,8 +0,0 @@
version: "3.7"
services:
pingtunnelServer:
image: esrrhs/pingtunnel:latest
restart: always
network_mode: host
command: "./pingtunnel -type server -key ${KEY}"

View file

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

18
go.mod
View file

@ -1,18 +0,0 @@
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
View file

@ -1,29 +0,0 @@
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
View file

@ -1,228 +0,0 @@
// 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,
}

View file

@ -1,26 +0,0 @@
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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

56
pack.sh
View file

@ -1,56 +0,0 @@
#! /bin/bash
#set -x
NAME="pingtunnel"
export GO111MODULE=on
#go tool dist list
build_list=$(go tool dist list)
rm pack -rf
rm pack.zip -f
mkdir pack
go mod tidy
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"

View file

@ -1,119 +1,180 @@
package pingtunnel
import (
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/binary"
"github.com/esrrhs/gohome/common"
"github.com/esrrhs/gohome/loggo"
"github.com/golang/protobuf/proto"
"encoding/hex"
"fmt"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"io"
"net"
"sync"
"syscall"
"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) {
const (
DATA uint32 = 0xDEADBEEF
)
// An Echo represents an ICMP echo request or reply message body.
type MyMsg struct {
TYPE uint32
ID string
TARGET string
Data []byte
}
// Len implements the Len method of MessageBody interface.
func (p *MyMsg) Len(proto int) int {
if p == nil {
return 0
}
return 4 + p.LenString(p.ID) + p.LenString(p.TARGET) + len(p.Data)
}
func (p *MyMsg) LenString(s string) int {
return 2 + len(s)
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *MyMsg) Marshal(proto int) ([]byte, error) {
b := make([]byte, p.Len(proto))
binary.BigEndian.PutUint32(b[:4], uint32(p.TYPE))
id := p.MarshalString(p.ID)
copy(b[4:], id)
target := p.MarshalString(p.TARGET)
copy(b[4+p.LenString(p.ID):], target)
copy(b[4+p.LenString(p.ID)+p.LenString(p.TARGET):], p.Data)
return b, nil
}
func (p *MyMsg) MarshalString(s string) []byte {
b := make([]byte, p.LenString(s))
binary.BigEndian.PutUint16(b[:2], uint16(len(s)))
copy(b[2:], []byte(s))
return b
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *MyMsg) Unmarshal(b []byte) error {
defer func() {
recover()
}()
p.TYPE = binary.BigEndian.Uint32(b[:4])
p.ID = p.UnmarshalString(b[4:])
p.TARGET = p.UnmarshalString(b[4+p.LenString(p.ID):])
p.Data = make([]byte, len(b[4+p.LenString(p.ID)+p.LenString(p.TARGET):]))
copy(p.Data, b[4+p.LenString(p.ID)+p.LenString(p.TARGET):])
return nil
}
func (p *MyMsg) UnmarshalString(b []byte) string {
len := binary.BigEndian.Uint16(b[:2])
data := make([]byte, len)
copy(data, b[2:])
return string(data)
}
func sendICMP(conn icmp.PacketConn, server *net.IPAddr, target string, connId string, msgType uint32, data []byte) {
m := &MyMsg{
Id: connId,
Type: (int32)(msgType),
Target: target,
ID: connId,
TYPE: 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),
Type: ipv4.ICMPTypeTimestamp,
Code: 0,
Body: body,
Body: m,
}
bytes, err := msg.Marshal(nil)
if err != nil {
loggo.Error("sendICMP Marshal error %s %s", server.String(), err)
fmt.Printf("sendICMP Marshal error %s %s\n", server.String(), err)
return
}
conn.WriteTo(bytes, server)
for {
if _, err := conn.WriteTo(bytes, server); err != nil {
if neterr, ok := err.(*net.OpError); ok {
if neterr.Err == syscall.ENOBUFS {
continue
}
}
fmt.Printf("sendICMP WriteTo error %s %s\n", server.String(), err)
}
break
}
func recvICMP(workResultLock *sync.WaitGroup, exit *bool, conn icmp.PacketConn, recv chan<- *Packet) {
return
}
defer common.CrashLog()
(*workResultLock).Add(1)
defer (*workResultLock).Done()
func recvICMP(conn icmp.PacketConn, recv chan<- *Packet) {
bytes := make([]byte, 10240)
for !*exit {
for {
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)
if neterr, ok := err.(*net.OpError); ok {
if neterr.Timeout() {
// Read timeout
continue
} else {
fmt.Printf("Error read icmp message %s\n", err)
continue
}
}
}
if n <= 0 {
my := &MyMsg{
}
my.Unmarshal(bytes[4:n])
if my.TYPE != (uint32)(DATA) {
fmt.Printf("processPacket diff type %d \n", my.TYPE)
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}
recv <- &Packet{data: my.Data, id: my.ID, target: my.TARGET, src: srcaddr.(*net.IPAddr)}
}
}
type Packet struct {
my *MyMsg
data []byte
id string
target string
src *net.IPAddr
echoId int
echoSeq int
}
const (
FRAME_MAX_SIZE int = 888
FRAME_MAX_ID int = 1000000
)
func UniqueId() string {
b := make([]byte, 48)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
}
return GetMd5String(base64.URLEncoding.EncodeToString(b))
}
func GetMd5String(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}

View file

@ -2,26 +2,27 @@ package pingtunnel
import (
"fmt"
"github.com/golang/protobuf/proto"
"github.com/esrrhs/pingtunnel"
"testing"
)
func Test0001(t *testing.T) {
func Test0001(test *testing.T) {
my := &MyMsg{}
my.Id = "12345"
my.Target = "111:11"
my.Type = 12
my.Data = make([]byte, 0)
dst, _ := proto.Marshal(my)
my := &pingtunnel.MyMsg{
}
my.ID = "12345"
my.TARGET = "111:11"
my.TYPE = 12
my.Data = make([]byte, 3)
dst,_ := my.Marshal(0)
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)
my1 := &pingtunnel.MyMsg{
}
my1.Unmarshal(dst)
fmt.Println("my1 = ", my1)
my1.Unmarshal(dst[0:4])
fmt.Println("my1 = ", my1)
}

592
server.go
View file

@ -1,614 +1,148 @@
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"
"fmt"
"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
func NewServer(timeout int) (*Server, error) {
return &Server{
timeout: timeout,
}, nil
}
type Server struct {
exit bool
key int
workResultLock sync.WaitGroup
maxconn int
maxprocessthread int
maxprocessbuffer int
connecttmeout int
timeout 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
localConnMap map[string]*ServerConn
}
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
activeTime time.Time
close bool
rproto int
fm *frame.FrameMgr
tcpmode int
echoId int
echoSeq int
}
func (p *Server) Run() error {
func (p *Server) Run() {
conn, err := icmp.ListenPacket("ip4:icmp", "")
if err != nil {
loggo.Error("Error listening for ICMP packets: %s", err.Error())
return err
fmt.Printf("Error listening for ICMP packets: %s\n", err.Error())
return
}
p.conn = conn
recv := make(chan *Packet, 10000)
p.recvcontrol = make(chan int, 1)
go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv)
p.localConnMap = make(map[string]*ServerConn)
go func() {
defer common.CrashLog()
recv := make(chan *Packet, 1000)
go recvICMP(*p.conn, recv)
p.workResultLock.Add(1)
defer p.workResultLock.Done()
interval := time.NewTicker(time.Second)
defer interval.Stop()
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 {
for {
select {
case <-p.recvcontrol:
return
case <-interval.C:
p.checkTimeoutConn()
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
}
fmt.Printf("processPacket %s %s %d\n", packet.id, packet.src.String(), len(packet.data))
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
}
now := time.Now()
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
}
id := packet.id
udpConn := p.localConnMap[id]
if udpConn == nil {
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))
addr := packet.target
ipaddrTarget, err := net.ResolveUDPAddr("udp", addr)
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)
fmt.Printf("Error ResolveUDPAddr for udp addr: %s %s\n", addr, err.Error())
return
}
localConn.fm.OnRecvFrame(f)
} else {
if packet.my.Data == nil {
targetConn, err := net.DialUDP("udp", nil, ipaddrTarget)
if err != nil {
fmt.Printf("Error listening for udp packets: %s\n", err.Error())
return
}
_, err := localConn.conn.Write(packet.my.Data)
udpConn = &ServerConn{conn: targetConn, ipaddrTarget: ipaddrTarget, id: id, activeTime: now, close: false}
p.localConnMap[id] = udpConn
go p.Recv(udpConn, id, packet.src)
}
udpConn.activeTime = now
_, err := udpConn.conn.Write(packet.data)
if err != nil {
loggo.Info("WriteToUDP Error %s", err)
localConn.close = true
fmt.Printf("WriteToUDP Error %s\n", err)
udpConn.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()
fmt.Printf("server waiting target response %s -> %s %s\n", conn.ipaddrTarget.String(), conn.id, conn.conn.LocalAddr().String())
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 {
bytes := make([]byte, 10240)
for {
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)
if neterr, ok := err.(*net.OpError); ok {
if neterr.Timeout() {
// Read timeout
continue
} else {
fmt.Printf("ReadFromUDP Error read udp %s\n", err)
conn.close = true
return
}
}
}
now := common.GetNowUpdateInSecond()
conn.activeSendTime = now
now := time.Now()
conn.activeTime = 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)
sendICMP(*p.conn, src, "", id, (uint32)(DATA), bytes[:n])
}
}
func (p *Server) close(conn *ServerConn) {
if p.getServerConnById(conn.id) != nil {
conn.exit = true
if conn.conn != nil {
func (p *Server) Close(conn *ServerConn) {
if p.localConnMap[conn.id] != nil {
conn.conn.Close()
}
if conn.tcpconn != nil {
conn.tcpconn.Close()
}
p.deleteServerConn(conn.id)
delete(p.localConnMap, 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)) {
now := time.Now()
for _, conn := range p.localConnMap {
diff := now.Sub(conn.activeTime)
if diff > time.Second*(time.Duration(p.timeout)) {
conn.close = true
}
}
for id, conn := range tmp {
if conn.tcpmode > 0 {
continue
}
for id, conn := range p.localConnMap {
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)
fmt.Printf("close inactive conn %s %s\n", id, conn.ipaddrTarget.String())
p.Close(conn)
}
}
}