├── Dockerfile ├── common ├── net.go ├── time.go ├── error.go ├── alg.go ├── file.go ├── math.go └── string.go ├── go.mod ├── .gitignore ├── pingtunnel ├── msg.proto ├── pingtunnel.go ├── msg.pb.go ├── server.go └── client.go ├── frame ├── frame.proto ├── frame.pb.go └── framemgr.go ├── .github └── workflows │ ├── build-docker-image.yml │ └── release.yml ├── geoip └── country.go ├── LICENSE ├── .goreleaser.yml ├── go.sum ├── threadpool └── threadpool.go ├── network ├── socks5_server.go └── socks5_client.go ├── rbuffergo ├── robuffergo.go └── rbuffergo.go ├── README.md ├── README_EN.md ├── loggo └── loggo.go ├── cmd └── pingtunnel │ └── main.go ├── install.sh └── termcolor └── color.go /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | LABEL maintainer="cxjava " 3 | 4 | RUN set -ex \ 5 | && apk add --no-cache tzdata openssl ca-certificates \ 6 | && wget -O - https://raw.githubusercontent.com/cxjava/pingtunnel/main/install.sh | sh -s -- -b /usr/local/bin 7 | 8 | ENTRYPOINT ["/usr/local/bin/pingtunnel"] 9 | -------------------------------------------------------------------------------- /common/net.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | func GetOutboundIP() (net.IP, error) { 8 | conn, err := net.Dial("udp", "8.8.8.8:80") 9 | if err != nil { 10 | return nil, err 11 | } 12 | defer conn.Close() 13 | 14 | localAddr := conn.LocalAddr().(*net.UDPAddr) 15 | 16 | return localAddr.IP, nil 17 | } 18 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cxjava/pingtunnel 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/golang/protobuf v1.5.2 7 | github.com/google/uuid v1.3.0 8 | github.com/oschwald/geoip2-golang v1.8.0 9 | golang.org/x/net v0.6.0 10 | ) 11 | 12 | require ( 13 | github.com/oschwald/maxminddb-golang v1.10.0 // indirect 14 | golang.org/x/sys v0.5.0 // indirect 15 | google.golang.org/protobuf v1.28.1 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | dist/ 26 | cmd/pingtunnel/pingtunnel 27 | .vscode/ 28 | vendor/* 29 | *.un~ 30 | .idea 31 | *.iml 32 | *.log 33 | -------------------------------------------------------------------------------- /common/time.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var gnowsecond time.Time 8 | 9 | func init() { 10 | gnowsecond = time.Now() 11 | go updateNowInSecond() 12 | } 13 | 14 | func GetNowUpdateInSecond() time.Time { 15 | return gnowsecond 16 | } 17 | 18 | func updateNowInSecond() { 19 | defer CrashLog() 20 | 21 | for { 22 | gnowsecond = time.Now() 23 | time.Sleep(time.Second) 24 | } 25 | } 26 | 27 | func Elapsed(f func(d time.Duration)) func() { 28 | start := time.Now() 29 | return func() { 30 | f(time.Since(start)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pingtunnel/msg.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "pingtunnel"; 3 | 4 | message MyMsg { 5 | enum TYPE { 6 | DATA = 0; 7 | PING = 1; 8 | KICK = 2; 9 | MAGIC = 0xdead; 10 | } 11 | 12 | string id = 1; 13 | int32 type = 2; 14 | string target = 3; 15 | bytes data = 4; 16 | sint32 rproto = 5; 17 | sint32 magic = 6; 18 | sint32 key = 7; 19 | int32 timeout = 8; 20 | int32 tcpmode = 9; 21 | int32 tcpmode_buffersize = 10; 22 | int32 tcpmode_maxwin = 11; 23 | int32 tcpmode_resend_timems = 12; 24 | int32 tcpmode_compress = 13; 25 | int32 tcpmode_stat = 14; 26 | } 27 | -------------------------------------------------------------------------------- /frame/frame.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "frame"; 3 | 4 | message FrameData { 5 | enum TYPE { 6 | USER_DATA = 0; 7 | CONN = 1; 8 | CONNRSP = 2; 9 | CLOSE = 3; 10 | HB = 4; 11 | } 12 | int32 type = 1; 13 | bytes data = 2; 14 | bool compress = 3; 15 | } 16 | 17 | message Frame { 18 | enum TYPE { 19 | DATA = 0; 20 | REQ = 1; 21 | ACK = 2; 22 | PING = 3; 23 | PONG = 4; 24 | } 25 | 26 | int32 type = 1; 27 | bool resend = 2; 28 | int64 sendtime = 3; 29 | int32 id = 4; 30 | FrameData data = 5; 31 | repeated int32 dataid = 6; 32 | bool acked = 7; 33 | } 34 | -------------------------------------------------------------------------------- /common/error.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/cxjava/pingtunnel/loggo" 7 | "runtime" 8 | ) 9 | 10 | func CrashLog() { 11 | if r := recover(); r != nil { 12 | var err error 13 | switch x := r.(type) { 14 | case string: 15 | err = errors.New(x) 16 | case error: 17 | err = x 18 | default: 19 | err = errors.New("Unknown panic") 20 | } 21 | if err != nil { 22 | loggo.Error("crash %s \n%s", err, DumpStacks()) 23 | } 24 | panic(err) 25 | } 26 | } 27 | 28 | func DumpStacks() string { 29 | buf := make([]byte, 16384) 30 | buf = buf[:runtime.Stack(buf, true)] 31 | return fmt.Sprintf("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) 32 | } 33 | -------------------------------------------------------------------------------- /common/alg.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "bytes" 5 | "compress/zlib" 6 | "crypto/rc4" 7 | "github.com/google/uuid" 8 | "io" 9 | ) 10 | 11 | func CompressData(src []byte) []byte { 12 | var b bytes.Buffer 13 | w := zlib.NewWriter(&b) 14 | w.Write(src) 15 | w.Close() 16 | return b.Bytes() 17 | } 18 | 19 | func DeCompressData(src []byte) ([]byte, error) { 20 | b := bytes.NewReader(src) 21 | r, err := zlib.NewReader(b) 22 | if err != nil { 23 | return nil, err 24 | } 25 | var out bytes.Buffer 26 | io.Copy(&out, r) 27 | r.Close() 28 | return out.Bytes(), nil 29 | } 30 | 31 | func Rc4(key string, src []byte) ([]byte, error) { 32 | c, err := rc4.NewCipher([]byte(key)) 33 | if err != nil { 34 | return nil, err 35 | } 36 | dst := make([]byte, len(src)) 37 | c.XORKeyStream(dst, src) 38 | return dst, nil 39 | } 40 | 41 | func Guid() string { 42 | return uuid.New().String() 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/build-docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker Images 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v3 11 | - name: Set up QEMU 12 | uses: docker/setup-qemu-action@v2 13 | - name: Setup Docker Buildx 14 | uses: docker/setup-buildx-action@v2 15 | - name: Login to GitHub Container Registry 16 | uses: docker/login-action@v2 17 | with: 18 | registry: ghcr.io 19 | username: ${{ github.repository_owner }} 20 | password: ${{ secrets.GITHUB_TOKEN }} 21 | - name: Build and release Docker images 22 | uses: docker/build-push-action@v4 23 | with: 24 | # platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 25 | platforms: linux/amd64,linux/arm64 26 | tags: ghcr.io/${{ github.repository_owner }}/pingtunnel:latest 27 | push: true 28 | -------------------------------------------------------------------------------- /geoip/country.go: -------------------------------------------------------------------------------- 1 | package geoip 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | 7 | "github.com/oschwald/geoip2-golang" 8 | ) 9 | 10 | var gdb *geoip2.Reader 11 | 12 | func Load(file string) error { 13 | 14 | if len(file) <= 0 { 15 | file = "./GeoLite2-Country.mmdb" 16 | } 17 | 18 | db, err := geoip2.Open(file) 19 | if err != nil { 20 | return err 21 | } 22 | gdb = db 23 | return nil 24 | } 25 | 26 | func GetCountryIsoCode(ipaddr string) (string, error) { 27 | 28 | ip := net.ParseIP(ipaddr) 29 | if ip == nil { 30 | return "", errors.New("ip " + ipaddr + " ParseIP nil") 31 | } 32 | record, err := gdb.City(ip) 33 | if err != nil { 34 | return "", err 35 | } 36 | 37 | return record.Country.IsoCode, nil 38 | } 39 | 40 | func GetCountryName(ipaddr string) (string, error) { 41 | 42 | ip := net.ParseIP(ipaddr) 43 | if ip == nil { 44 | return "", errors.New("ip " + ipaddr + "ParseIP nil") 45 | } 46 | record, err := gdb.City(ip) 47 | if err != nil { 48 | return "", err 49 | } 50 | 51 | return record.Country.Names["en"], nil 52 | } 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 zhao xin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /common/file.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | ) 7 | 8 | func LoadJson(filename string, conf interface{}) error { 9 | err := loadJson(filename, conf) 10 | if err != nil { 11 | err := loadJson(filename+".back", conf) 12 | if err != nil { 13 | return err 14 | } 15 | } 16 | return nil 17 | } 18 | 19 | func loadJson(filename string, conf interface{}) error { 20 | file, err := os.Open(filename) 21 | if err != nil { 22 | return err 23 | } 24 | defer file.Close() 25 | decoder := json.NewDecoder(file) 26 | err = decoder.Decode(conf) 27 | if err != nil { 28 | return err 29 | } 30 | return nil 31 | } 32 | 33 | func SaveJson(filename string, conf interface{}) error { 34 | err1 := saveJson(filename, conf) 35 | err2 := saveJson(filename+".back", conf) 36 | if err1 != nil { 37 | return err1 38 | } 39 | if err2 != nil { 40 | return err2 41 | } 42 | return nil 43 | } 44 | 45 | func saveJson(filename string, conf interface{}) error { 46 | str, err := json.MarshalIndent(conf, "", " ") 47 | if err != nil { 48 | return err 49 | } 50 | jsonFile, err := os.OpenFile(filename, 51 | os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) 52 | if err != nil { 53 | return err 54 | } 55 | jsonFile.Write(str) 56 | jsonFile.Close() 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | builds: 5 | - env: 6 | - CGO_ENABLED=0 7 | main: ./cmd/pingtunnel/main.go 8 | ldflags: 9 | - "-s -w" 10 | targets: 11 | - darwin_amd64 12 | - darwin_arm64 13 | - linux_386 14 | - linux_amd64 15 | - linux_amd64_v3 16 | # - linux_arm_5 17 | - linux_arm_6 18 | - linux_arm_7 19 | - linux_arm64 20 | # - linux_mips_softfloat 21 | # - linux_mips_hardfloat 22 | # - linux_mipsle_softfloat 23 | # - linux_mipsle_hardfloat 24 | # - linux_mips64 25 | # - linux_mips64le 26 | # - linux_s390x 27 | # - linux_riscv64 28 | # - freebsd_386 29 | # - freebsd_amd64 30 | - windows_386 31 | - windows_amd64 32 | - windows_amd64_v3 33 | - windows_arm64 34 | 35 | archives: 36 | - wrap_in_directory: true 37 | format: tar.gz 38 | format_overrides: 39 | - goos: windows 40 | format: zip 41 | replacements: 42 | amd64: 64-bit 43 | 386: 32-bit 44 | arm: ARM 45 | arm64: ARM64 46 | darwin: macOS 47 | linux: Linux 48 | windows: Windows 49 | openbsd: OpenBSD 50 | netbsd: NetBSD 51 | freebsd: FreeBSD 52 | checksum: 53 | name_template: "checksums.txt" 54 | release: 55 | draft: true 56 | prerelease: auto 57 | name_template: "{{.ProjectName}}-v{{.Version}}-{{.Date}}" -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: GoReleaser 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | bump_type: 7 | description: 'Bump tag type: major, minor, patch. default: minor' 8 | required: true 9 | default: 'minor' 10 | 11 | permissions: write-all 12 | 13 | jobs: 14 | goreleaser: 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 30 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-go@v3 20 | with: 21 | go-version: "1.20" 22 | - name: Bump tag version 23 | id: bumpTag 24 | uses: anothrNick/github-tag-action@1.61.0 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | WITH_V: true 28 | DRY_RUN: true 29 | INITIAL_VERSION: "2.9.1" 30 | DEFAULT_BUMP: "${{ github.event.inputs.bump_type }}" 31 | - name: Bump tag locally 32 | run: git tag ${{ steps.bumpTag.outputs.new_tag }} 33 | 34 | - name: Set environment variables 35 | run: | 36 | echo "GOLANG_VERSION=$(go version)" >> $GITHUB_ENV 37 | echo "BUILT_BY=$(whoami)@$(hostname)" >> $GITHUB_ENV 38 | 39 | - uses: goreleaser/goreleaser-action@v4 40 | with: 41 | distribution: goreleaser 42 | version: latest 43 | args: release --clean 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | GOLANG_VERSION: ${{ env.GOLANG_VERSION }} 47 | BUILT_BY: ${{ env.BUILT_BY }} -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 3 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 4 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 5 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 6 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 7 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 8 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 9 | github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= 10 | github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= 11 | github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= 12 | github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= 13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 15 | golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= 16 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 17 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 18 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 19 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 20 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 21 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 22 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 23 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 24 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 25 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 26 | -------------------------------------------------------------------------------- /threadpool/threadpool.go: -------------------------------------------------------------------------------- 1 | package threadpool 2 | 3 | import ( 4 | "github.com/cxjava/pingtunnel/common" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type ThreadPool struct { 10 | workResultLock sync.WaitGroup 11 | max int 12 | exef func(interface{}) 13 | ca []chan interface{} 14 | control chan int 15 | stat ThreadPoolStat 16 | } 17 | 18 | type ThreadPoolStat struct { 19 | Datalen []int 20 | Pushnum []int 21 | Processnum []int 22 | } 23 | 24 | func NewThreadPool(max int, buffer int, exef func(interface{})) *ThreadPool { 25 | ca := make([]chan interface{}, max) 26 | control := make(chan int, max) 27 | for index, _ := range ca { 28 | ca[index] = make(chan interface{}, buffer) 29 | } 30 | 31 | stat := ThreadPoolStat{} 32 | stat.Datalen = make([]int, max) 33 | stat.Pushnum = make([]int, max) 34 | stat.Processnum = make([]int, max) 35 | 36 | tp := &ThreadPool{max: max, exef: exef, ca: ca, control: control, stat: stat} 37 | 38 | for index, _ := range ca { 39 | go tp.run(index) 40 | } 41 | 42 | return tp 43 | } 44 | 45 | func (tp *ThreadPool) AddJob(hash int, v interface{}) { 46 | tp.ca[common.AbsInt(hash)%len(tp.ca)] <- v 47 | tp.stat.Pushnum[common.AbsInt(hash)%len(tp.ca)]++ 48 | } 49 | 50 | func (tp *ThreadPool) AddJobTimeout(hash int, v interface{}, timeoutms int) bool { 51 | select { 52 | case tp.ca[common.AbsInt(hash)%len(tp.ca)] <- v: 53 | tp.stat.Pushnum[common.AbsInt(hash)%len(tp.ca)]++ 54 | return true 55 | case <-time.After(time.Duration(timeoutms) * time.Millisecond): 56 | return false 57 | } 58 | } 59 | 60 | func (tp *ThreadPool) Stop() { 61 | for i := 0; i < tp.max; i++ { 62 | tp.control <- i 63 | } 64 | tp.workResultLock.Wait() 65 | } 66 | 67 | func (tp *ThreadPool) run(index int) { 68 | defer common.CrashLog() 69 | 70 | tp.workResultLock.Add(1) 71 | defer tp.workResultLock.Done() 72 | 73 | for { 74 | select { 75 | case <-tp.control: 76 | return 77 | case v := <-tp.ca[index]: 78 | tp.exef(v) 79 | tp.stat.Processnum[index]++ 80 | } 81 | } 82 | } 83 | 84 | func (tp *ThreadPool) GetStat() ThreadPoolStat { 85 | for index, _ := range tp.ca { 86 | tp.stat.Datalen[index] = len(tp.ca[index]) 87 | } 88 | return tp.stat 89 | } 90 | 91 | func (tp *ThreadPool) ResetStat() { 92 | for index, _ := range tp.ca { 93 | tp.stat.Pushnum[index] = 0 94 | tp.stat.Processnum[index] = 0 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /common/math.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/base64" 6 | "hash/fnv" 7 | "io" 8 | "math" 9 | mrand "math/rand" 10 | "time" 11 | ) 12 | 13 | func init() { 14 | mrand.Seed(time.Now().UnixNano()) 15 | } 16 | 17 | func MinOfInt(vars ...int) int { 18 | min := vars[0] 19 | 20 | for _, i := range vars { 21 | if min > i { 22 | min = i 23 | } 24 | } 25 | 26 | return min 27 | } 28 | 29 | func MaxOfInt(vars ...int) int { 30 | max := vars[0] 31 | 32 | for _, i := range vars { 33 | if max < i { 34 | max = i 35 | } 36 | } 37 | 38 | return max 39 | } 40 | 41 | func MinOfInt64(vars ...int64) int64 { 42 | min := vars[0] 43 | 44 | for _, i := range vars { 45 | if min > i { 46 | min = i 47 | } 48 | } 49 | 50 | return min 51 | } 52 | 53 | func MaxOfInt64(vars ...int64) int64 { 54 | max := vars[0] 55 | 56 | for _, i := range vars { 57 | if max < i { 58 | max = i 59 | } 60 | } 61 | 62 | return max 63 | } 64 | 65 | func AbsInt(v int) int { 66 | if v > 0 { 67 | return v 68 | } 69 | return -v 70 | } 71 | 72 | func AbsInt32(v int32) int32 { 73 | if v > 0 { 74 | return v 75 | } 76 | return -v 77 | } 78 | 79 | func AbsInt64(v int64) int64 { 80 | if v > 0 { 81 | return v 82 | } 83 | return -v 84 | } 85 | 86 | func HashString(s string) uint32 { 87 | h := fnv.New32a() 88 | h.Write([]byte(s)) 89 | return h.Sum32() 90 | } 91 | 92 | func UniqueId() string { 93 | b := make([]byte, 48) 94 | 95 | if _, err := io.ReadFull(rand.Reader, b); err != nil { 96 | return "" 97 | } 98 | return GetMd5String(base64.URLEncoding.EncodeToString(b)) 99 | } 100 | 101 | func RandInt31n(n int) int32 { 102 | ret := mrand.Int31n((int32)(n)) 103 | return int32(ret) 104 | } 105 | 106 | func RandInt() int32 { 107 | ret := mrand.Int() 108 | return int32(ret) 109 | } 110 | 111 | func Shuffle(n int, swap func(i, j int)) { 112 | mrand.Shuffle(n, swap) 113 | } 114 | 115 | func MAKEINT64(high int32, low int32) int64 { 116 | return (int64)(((int64)(low)) | ((int64)((int32)(high)))<<32) 117 | } 118 | func HIINT32(I int64) int32 { 119 | return (int32)(((int64)(I) >> 32) & 0xFFFFFFFF) 120 | } 121 | func LOINT32(l int64) int32 { 122 | return (int32)(l) 123 | } 124 | 125 | func MAKEINT32(high int16, low int16) int32 { 126 | return (int32)(((int32)(low)) | ((int32)((int16)(high)))<<16) 127 | } 128 | func HIINT16(I int32) int16 { 129 | return (int16)(((int32)(I) >> 16) & 0xFFFF) 130 | } 131 | func LOINT16(l int32) int16 { 132 | return (int16)(l) 133 | } 134 | 135 | func IsInt(r float64) bool { 136 | return (r - math.Floor(r)) == 0 137 | } 138 | 139 | func ArrayContainInt(a []int, f int) bool { 140 | 141 | for _, i := range a { 142 | if f == i { 143 | return true 144 | } 145 | } 146 | 147 | return false 148 | } 149 | 150 | func ArrayContainString(a []string, f string) bool { 151 | 152 | for _, i := range a { 153 | if f == i { 154 | return true 155 | } 156 | } 157 | 158 | return false 159 | } 160 | 161 | func SafeDivide(a int64, b int64) int64 { 162 | if b == 0 { 163 | return 0 164 | } 165 | return a / b 166 | } 167 | -------------------------------------------------------------------------------- /pingtunnel/pingtunnel.go: -------------------------------------------------------------------------------- 1 | package pingtunnel 2 | 3 | import ( 4 | "encoding/binary" 5 | "github.com/cxjava/pingtunnel/common" 6 | "github.com/cxjava/pingtunnel/loggo" 7 | "github.com/golang/protobuf/proto" 8 | "golang.org/x/net/icmp" 9 | "golang.org/x/net/ipv4" 10 | "net" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | func sendICMP(id int, sequence int, conn icmp.PacketConn, server *net.IPAddr, target string, 16 | connId string, msgType uint32, data []byte, sproto int, rproto int, key int, 17 | tcpmode int, tcpmode_buffer_size int, tcpmode_maxwin int, tcpmode_resend_time int, tcpmode_compress int, tcpmode_stat int, 18 | timeout int) { 19 | 20 | m := &MyMsg{ 21 | Id: connId, 22 | Type: (int32)(msgType), 23 | Target: target, 24 | Data: data, 25 | Rproto: (int32)(rproto), 26 | Key: (int32)(key), 27 | Tcpmode: (int32)(tcpmode), 28 | TcpmodeBuffersize: (int32)(tcpmode_buffer_size), 29 | TcpmodeMaxwin: (int32)(tcpmode_maxwin), 30 | TcpmodeResendTimems: (int32)(tcpmode_resend_time), 31 | TcpmodeCompress: (int32)(tcpmode_compress), 32 | TcpmodeStat: (int32)(tcpmode_stat), 33 | Timeout: (int32)(timeout), 34 | Magic: (int32)(MyMsg_MAGIC), 35 | } 36 | 37 | mb, err := proto.Marshal(m) 38 | if err != nil { 39 | loggo.Error("sendICMP Marshal MyMsg error %s %s", server.String(), err) 40 | return 41 | } 42 | 43 | body := &icmp.Echo{ 44 | ID: id, 45 | Seq: sequence, 46 | Data: mb, 47 | } 48 | 49 | msg := &icmp.Message{ 50 | Type: (ipv4.ICMPType)(sproto), 51 | Code: 0, 52 | Body: body, 53 | } 54 | 55 | bytes, err := msg.Marshal(nil) 56 | if err != nil { 57 | loggo.Error("sendICMP Marshal error %s %s", server.String(), err) 58 | return 59 | } 60 | 61 | conn.WriteTo(bytes, server) 62 | } 63 | 64 | func recvICMP(workResultLock *sync.WaitGroup, exit *bool, conn icmp.PacketConn, recv chan<- *Packet) { 65 | 66 | defer common.CrashLog() 67 | 68 | (*workResultLock).Add(1) 69 | defer (*workResultLock).Done() 70 | 71 | bytes := make([]byte, 10240) 72 | for !*exit { 73 | conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) 74 | n, srcaddr, err := conn.ReadFrom(bytes) 75 | 76 | if err != nil { 77 | nerr, ok := err.(net.Error) 78 | if !ok || !nerr.Timeout() { 79 | loggo.Info("Error read icmp message %s", err) 80 | continue 81 | } 82 | } 83 | 84 | if n <= 0 { 85 | continue 86 | } 87 | 88 | echoId := int(binary.BigEndian.Uint16(bytes[4:6])) 89 | echoSeq := int(binary.BigEndian.Uint16(bytes[6:8])) 90 | 91 | my := &MyMsg{} 92 | err = proto.Unmarshal(bytes[8:n], my) 93 | if err != nil { 94 | loggo.Debug("Unmarshal MyMsg error: %s", err) 95 | continue 96 | } 97 | 98 | if my.Magic != (int32)(MyMsg_MAGIC) { 99 | loggo.Debug("processPacket data invalid %s", my.Id) 100 | continue 101 | } 102 | 103 | recv <- &Packet{my: my, 104 | src: srcaddr.(*net.IPAddr), 105 | echoId: echoId, echoSeq: echoSeq} 106 | } 107 | } 108 | 109 | type Packet struct { 110 | my *MyMsg 111 | src *net.IPAddr 112 | echoId int 113 | echoSeq int 114 | } 115 | 116 | const ( 117 | FRAME_MAX_SIZE int = 888 118 | FRAME_MAX_ID int = 1000000 119 | ) 120 | -------------------------------------------------------------------------------- /network/socks5_server.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "io" 7 | "net" 8 | "strconv" 9 | ) 10 | 11 | var ( 12 | errAddrType = errors.New("socks addr type not supported") 13 | errVer = errors.New("socks version not supported") 14 | errMethod = errors.New("socks only support 1 method now") 15 | errAuthExtraData = errors.New("socks authentication get extra data") 16 | errReqExtraData = errors.New("socks request get extra data") 17 | errCmd = errors.New("socks command not supported") 18 | ) 19 | 20 | const ( 21 | socksCmdConnect = 1 22 | ) 23 | 24 | func Sock5HandshakeBy(conn net.Conn) (err error) { 25 | const ( 26 | idVer = 0 27 | idNmethod = 1 28 | ) 29 | // version identification and method selection message in theory can have 30 | // at most 256 methods, plus version and nmethod field in total 258 bytes 31 | // the current rfc defines only 3 authentication methods (plus 2 reserved), 32 | // so it won't be such long in practice 33 | 34 | buf := make([]byte, 258) 35 | 36 | var n int 37 | // make sure we get the nmethod field 38 | if n, err = io.ReadAtLeast(conn, buf, idNmethod+1); err != nil { 39 | return 40 | } 41 | if buf[idVer] != socksVer5 { 42 | return errVer 43 | } 44 | nmethod := int(buf[idNmethod]) 45 | msgLen := nmethod + 2 46 | if n == msgLen { // handshake done, common case 47 | // do nothing, jump directly to send confirmation 48 | } else if n < msgLen { // has more methods to read, rare case 49 | if _, err = io.ReadFull(conn, buf[n:msgLen]); err != nil { 50 | return 51 | } 52 | } else { // error, should not get extra data 53 | return errAuthExtraData 54 | } 55 | // send confirmation: version 5, no authentication required 56 | _, err = conn.Write([]byte{socksVer5, 0}) 57 | return 58 | } 59 | 60 | func Sock5GetRequest(conn net.Conn) (rawaddr []byte, host string, err error) { 61 | const ( 62 | idVer = 0 63 | idCmd = 1 64 | idType = 3 // address type index 65 | idIP0 = 4 // ip address start index 66 | idDmLen = 4 // domain address length index 67 | idDm0 = 5 // domain address start index 68 | 69 | typeIPv4 = 1 // type is ipv4 address 70 | typeDm = 3 // type is domain address 71 | typeIPv6 = 4 // type is ipv6 address 72 | 73 | lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port 74 | lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port 75 | lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen 76 | ) 77 | // refer to getRequest in server.go for why set buffer size to 263 78 | buf := make([]byte, 263) 79 | var n int 80 | // read till we get possible domain length field 81 | if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { 82 | return 83 | } 84 | // check version and cmd 85 | if buf[idVer] != socksVer5 { 86 | err = errVer 87 | return 88 | } 89 | if buf[idCmd] != socksCmdConnect { 90 | err = errCmd 91 | return 92 | } 93 | 94 | reqLen := -1 95 | switch buf[idType] { 96 | case typeIPv4: 97 | reqLen = lenIPv4 98 | case typeIPv6: 99 | reqLen = lenIPv6 100 | case typeDm: 101 | reqLen = int(buf[idDmLen]) + lenDmBase 102 | default: 103 | err = errAddrType 104 | return 105 | } 106 | 107 | if n == reqLen { 108 | // common case, do nothing 109 | } else if n < reqLen { // rare case 110 | if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { 111 | return 112 | } 113 | } else { 114 | err = errReqExtraData 115 | return 116 | } 117 | 118 | rawaddr = buf[idType:reqLen] 119 | 120 | switch buf[idType] { 121 | case typeIPv4: 122 | host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() 123 | case typeIPv6: 124 | host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() 125 | case typeDm: 126 | host = string(buf[idDm0 : idDm0+buf[idDmLen]]) 127 | } 128 | port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) 129 | host = net.JoinHostPort(host, strconv.Itoa(int(port))) 130 | 131 | return 132 | } 133 | -------------------------------------------------------------------------------- /rbuffergo/robuffergo.go: -------------------------------------------------------------------------------- 1 | package rbuffergo 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | /* 9 | type: [1] 10 | iter: begin(2) end(8) 11 | | | 12 | data: _ _ 1 2 3 * 5 * _ _ _ 13 | buffer: _ _ _ _ _ _ _ _ _ _ _ 14 | index: 0 1 2 3 4 5 6 7 8 9 10 15 | type: [2] 16 | iter: end(2) begin(7) 17 | | | 18 | data: 7 8 _ _ _ _ _ 4 * 5 6 19 | buffer: _ _ _ _ _ _ _ _ _ _ _ 20 | index: 0 1 2 3 4 5 6 7 8 9 10 21 | type: [3] 22 | iter: begin(4),end(4) 23 | | 24 | data: _ _ _ _ _ _ _ _ _ _ _ 25 | buffer: _ _ _ _ _ _ _ _ _ _ _ 26 | index: 0 1 2 3 4 5 6 7 8 9 10 27 | type: [4] 28 | iter: begin(4),end(4) 29 | | | 30 | data: 10 * * 13 3 4 5 * 7 8 * 31 | buffer: _ _ _ _ _ _ _ _ _ _ _ 32 | index: 0 1 2 3 4 5 6 7 8 9 10 33 | */ 34 | 35 | type ROBuffergo struct { 36 | buffer []interface{} 37 | flag []bool 38 | id []int 39 | len int 40 | maxid int 41 | begin int 42 | size int 43 | } 44 | 45 | func NewROBuffer(len int, startid int, maxid int) *ROBuffergo { 46 | if startid >= maxid { 47 | return nil 48 | } 49 | if len >= maxid { 50 | return nil 51 | } 52 | buffer := &ROBuffergo{} 53 | buffer.buffer = make([]interface{}, len) 54 | buffer.flag = make([]bool, len) 55 | buffer.id = make([]int, len) 56 | buffer.len = len 57 | buffer.maxid = maxid 58 | for i, _ := range buffer.id { 59 | buffer.id[i] = startid + i 60 | } 61 | return buffer 62 | } 63 | 64 | func (b *ROBuffergo) Get(id int) (error, data interface{}) { 65 | if b.begin >= len(b.flag) { 66 | return fmt.Errorf("not init"), nil 67 | } 68 | cur := b.id[b.begin] 69 | if id >= b.maxid { 70 | return fmt.Errorf("id out of range %d %d %d", id, cur, b.maxid), nil 71 | } 72 | 73 | index := 0 74 | if id < cur { 75 | index = (b.begin + (id + b.maxid - cur)) % b.len 76 | } else { 77 | index = (b.begin + (id - cur)) % b.len 78 | } 79 | 80 | if b.id[index] != id { 81 | return fmt.Errorf("set id error %d %d %d", id, b.id[index], index), nil 82 | } 83 | 84 | if !b.flag[index] { 85 | return nil, nil 86 | } 87 | 88 | if b.buffer[index] == nil { 89 | return errors.New("data is nil"), nil 90 | } 91 | 92 | return nil, b.buffer[index] 93 | } 94 | 95 | func (b *ROBuffergo) Set(id int, data interface{}) error { 96 | if data == nil { 97 | return fmt.Errorf("data nil %d ", id) 98 | } 99 | if b.begin >= len(b.flag) { 100 | return fmt.Errorf("not init") 101 | } 102 | cur := b.id[b.begin] 103 | if id >= b.maxid { 104 | return fmt.Errorf("id out of range %d %d %d", id, cur, b.maxid) 105 | } 106 | 107 | index := 0 108 | if id < cur { 109 | index = (b.begin + (id + b.maxid - cur)) % b.len 110 | } else { 111 | index = (b.begin + (id - cur)) % b.len 112 | } 113 | 114 | if b.id[index] != id { 115 | return fmt.Errorf("set id error %d %d %d", id, b.id[index], index) 116 | } 117 | b.buffer[index] = data 118 | if !b.flag[index] { 119 | b.size++ 120 | } 121 | b.flag[index] = true 122 | return nil 123 | } 124 | 125 | func (b *ROBuffergo) Front() (error, interface{}) { 126 | if b.begin >= len(b.flag) { 127 | return errors.New("no init"), nil 128 | } 129 | if !b.flag[b.begin] { 130 | return errors.New("no front data"), nil 131 | } 132 | if b.buffer[b.begin] == nil { 133 | return errors.New("front data is nil"), nil 134 | } 135 | return nil, b.buffer[b.begin] 136 | } 137 | 138 | func (b *ROBuffergo) PopFront() error { 139 | if !b.flag[b.begin] { 140 | return errors.New("no front data") 141 | } 142 | if b.buffer[b.begin] == nil { 143 | return errors.New("front data is nil") 144 | } 145 | old := b.begin 146 | b.begin++ 147 | if b.begin >= b.len { 148 | b.begin = 0 149 | } 150 | cur := b.id[b.begin] 151 | b.buffer[old] = nil 152 | b.flag[old] = false 153 | b.id[old] = cur + b.len - 1 154 | if b.id[old] >= b.maxid { 155 | b.id[old] %= b.maxid 156 | } 157 | b.size-- 158 | return nil 159 | } 160 | 161 | func (b *ROBuffergo) Size() int { 162 | return b.size 163 | } 164 | 165 | func (b *ROBuffergo) Full() bool { 166 | return b.size == b.len 167 | } 168 | 169 | func (b *ROBuffergo) Empty() bool { 170 | return b.size <= 0 171 | } 172 | 173 | type ROBuffergoInter struct { 174 | startindex int 175 | index int 176 | Value interface{} 177 | b *ROBuffergo 178 | } 179 | 180 | func (b *ROBuffergo) FrontInter() *ROBuffergoInter { 181 | if b.begin >= len(b.flag) { 182 | return nil 183 | } 184 | if !b.flag[b.begin] { 185 | return nil 186 | } 187 | return &ROBuffergoInter{ 188 | startindex: b.begin, 189 | index: b.begin, 190 | Value: b.buffer[b.begin], 191 | b: b, 192 | } 193 | } 194 | 195 | func (bi *ROBuffergoInter) Next() *ROBuffergoInter { 196 | for { 197 | bi.index++ 198 | if bi.index >= bi.b.len { 199 | bi.index %= bi.b.len 200 | } 201 | if bi.index == bi.startindex { 202 | return nil 203 | } 204 | if bi.b.flag[bi.index] { 205 | break 206 | } 207 | } 208 | bi.Value = bi.b.buffer[bi.index] 209 | return bi 210 | } 211 | -------------------------------------------------------------------------------- /common/string.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "fmt" 7 | "math" 8 | "reflect" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | const gcharset = "abcdefghijklmnopqrstuvwxyz" + 14 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 15 | 16 | func IntArrayToString(a []int, delim string) string { 17 | var ret string 18 | for _, s := range a { 19 | ret += strconv.Itoa(s) + delim 20 | } 21 | return ret 22 | } 23 | 24 | func Int32ArrayToString(a []int32, delim string) string { 25 | var ret string 26 | for _, s := range a { 27 | ret += strconv.Itoa((int)(s)) + delim 28 | } 29 | return ret 30 | } 31 | 32 | func Int64ArrayToString(a []int64, delim string) string { 33 | var ret string 34 | for _, s := range a { 35 | ret += strconv.Itoa((int)(s)) + delim 36 | } 37 | return ret 38 | } 39 | 40 | func GetMd5String(s string) string { 41 | h := md5.New() 42 | h.Write([]byte(s)) 43 | return hex.EncodeToString(h.Sum(nil)) 44 | } 45 | 46 | func RandStr(l int) string { 47 | b := make([]byte, l) 48 | for i := range b { 49 | b[i] = gcharset[RandInt31n(len(gcharset))] 50 | } 51 | return string(b) 52 | } 53 | 54 | type StrTableLine struct { 55 | cols []string 56 | } 57 | 58 | type StrTable struct { 59 | header []string 60 | lines []StrTableLine 61 | } 62 | 63 | func (s *StrTable) AddHeader(h string) { 64 | s.header = append(s.header, h) 65 | } 66 | 67 | func (s *StrTable) AddLine(l StrTableLine) { 68 | s.lines = append(s.lines, l) 69 | } 70 | 71 | func (s *StrTableLine) AddData(d string) { 72 | s.cols = append(s.cols, d) 73 | } 74 | 75 | func (s *StrTable) String(prefix string) string { 76 | 77 | if len(s.header) <= 0 { 78 | return "" 79 | } 80 | 81 | colmax := make([]int, 0) 82 | for _, s := range s.header { 83 | colmax = append(colmax, len(s)) 84 | } 85 | 86 | totalcol := 0 87 | for i := 0; i < len(colmax); i++ { 88 | max := colmax[i] 89 | for _, sl := range s.lines { 90 | if i < len(sl.cols) { 91 | max = MaxOfInt(max, len(sl.cols[i])) 92 | } 93 | } 94 | colmax[i] = max 95 | totalcol += max 96 | } 97 | totalcol += len(colmax) + 1 98 | 99 | /* 100 | ----------- 101 | | a | b | 102 | ----------- 103 | | 1 | 2 | 104 | ----------- 105 | */ 106 | 107 | ret := prefix 108 | ret += strings.Repeat("-", totalcol) + "\n" + prefix 109 | for i, h := range s.header { 110 | ret += "|" + WrapString(h, colmax[i]) 111 | } 112 | ret += "|" + "\n" + prefix 113 | 114 | for _, l := range s.lines { 115 | ret += strings.Repeat("-", totalcol) + "\n" + prefix 116 | for i, d := range l.cols { 117 | ret += "|" + WrapString(d, colmax[i]) 118 | } 119 | for i := len(l.cols); i < len(colmax); i++ { 120 | ret += "|" + WrapString("", colmax[i]) 121 | } 122 | ret += "|" + "\n" + prefix 123 | } 124 | 125 | ret += strings.Repeat("-", totalcol) + "\n" 126 | 127 | return ret 128 | } 129 | 130 | func (s *StrTable) FromStruct(v interface{}, use func(name string) bool) { 131 | ss := reflect.ValueOf(v).Elem() 132 | typeOfT := ss.Type() 133 | 134 | for i := 0; i < ss.NumField(); i++ { 135 | name := typeOfT.Field(i).Name 136 | if use != nil { 137 | if !use(name) { 138 | continue 139 | } 140 | } 141 | s.AddHeader(name) 142 | } 143 | } 144 | 145 | func (s *StrTableLine) FromStruct(st *StrTable, v interface{}, trans func(name string, v interface{}) interface{}) { 146 | ss := reflect.ValueOf(v).Elem() 147 | typeOfT := ss.Type() 148 | 149 | for i := 0; i < ss.NumField(); i++ { 150 | f := ss.Field(i) 151 | name := typeOfT.Field(i).Name 152 | 153 | if !ArrayContainString(st.header, name) { 154 | continue 155 | } 156 | 157 | v := f.Interface() 158 | if trans != nil { 159 | v = trans(name, f.Interface()) 160 | } 161 | if v != nil { 162 | str := fmt.Sprintf("%v", v) 163 | s.AddData(str) 164 | } else { 165 | s.AddData("") 166 | } 167 | } 168 | } 169 | 170 | func WrapString(s string, n int) string { 171 | if n <= len(s) { 172 | return s 173 | } 174 | l := (n - len(s)) / 2 175 | r := (n - len(s)) - l 176 | return strings.Repeat(" ", l) + s + strings.Repeat(" ", r) 177 | } 178 | 179 | func StructToTable(v interface{}) string { 180 | t := StrTable{} 181 | tl := StrTableLine{} 182 | t.FromStruct(v, nil) 183 | tl.FromStruct(&t, v, nil) 184 | t.AddLine(tl) 185 | return t.String("") 186 | } 187 | 188 | const num2char string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 189 | const ( 190 | LITTLE_LETTERS = 36 191 | FULL_LETTERS = 62 192 | ) 193 | 194 | func NumToHex(num, n int) string { 195 | num_str := "" 196 | for num != 0 { 197 | yu := num % n 198 | num_str = string(num2char[yu]) + num_str 199 | num = num / n 200 | } 201 | return num_str 202 | } 203 | 204 | func Hex2Num(str string, n int) int { 205 | v := 0.0 206 | length := len(str) 207 | for i := 0; i < length; i++ { 208 | s := string(str[i]) 209 | index := strings.Index(num2char, s) 210 | v += float64(index) * math.Pow(float64(n), float64(length-1-i)) 211 | } 212 | return int(v) 213 | } 214 | -------------------------------------------------------------------------------- /network/socks5_client.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "net" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | const ( 13 | socksVer5 = 5 14 | socks5AuthNone = 0 15 | socks5Connect = 1 16 | Socks5AtypIP4 = 1 17 | Socks5AtypDomain = 3 18 | Socks5AtypIP6 = 4 19 | ) 20 | 21 | var socks5Errors = []string{ 22 | "", 23 | "general failure", 24 | "connection forbidden", 25 | "network unreachable", 26 | "host unreachable", 27 | "connection refused", 28 | "TTL expired", 29 | "command not supported", 30 | "address type not supported", 31 | } 32 | 33 | func Sock5Handshake(conn *net.TCPConn, timeoutms int) (err error) { 34 | 35 | buf := make([]byte, 0) 36 | buf = append(buf, socksVer5) 37 | buf = append(buf, 1) 38 | buf = append(buf, socks5AuthNone) 39 | 40 | if timeoutms > 0 { 41 | conn.SetDeadline(time.Now().Add(time.Duration(timeoutms) * time.Millisecond)) 42 | } 43 | if _, err := conn.Write(buf); err != nil { 44 | return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + conn.RemoteAddr().String() + ": " + err.Error()) 45 | } 46 | 47 | if timeoutms > 0 { 48 | conn.SetDeadline(time.Now().Add(time.Duration(timeoutms) * time.Millisecond)) 49 | } 50 | if _, err := io.ReadFull(conn, buf[:2]); err != nil { 51 | return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + conn.RemoteAddr().String() + ": " + err.Error()) 52 | } 53 | if buf[0] != 5 { 54 | return errors.New("proxy: SOCKS5 proxy at " + conn.RemoteAddr().String() + " has unexpected version " + strconv.Itoa(int(buf[0]))) 55 | } 56 | if buf[1] != 0 { 57 | return errors.New("proxy: SOCKS5 proxy at " + conn.RemoteAddr().String() + " requires authentication") 58 | } 59 | 60 | return nil 61 | } 62 | 63 | func Sock5SetRequest(conn *net.TCPConn, host string, port int, timeoutms int) (err error) { 64 | 65 | buf := make([]byte, 0) 66 | 67 | buf = buf[:0] 68 | buf = append(buf, socksVer5, socks5Connect, 0 /* reserved */) 69 | if ip := net.ParseIP(host); ip != nil { 70 | if ip4 := ip.To4(); ip4 != nil { 71 | buf = append(buf, Socks5AtypIP4) 72 | ip = ip4 73 | } else { 74 | buf = append(buf, Socks5AtypIP6) 75 | } 76 | buf = append(buf, ip...) 77 | } else { 78 | if len(host) > 255 { 79 | err = errors.New("proxy: destination hostname too long: " + host) 80 | return 81 | } 82 | buf = append(buf, Socks5AtypDomain) 83 | buf = append(buf, byte(len(host))) 84 | buf = append(buf, host...) 85 | } 86 | buf = append(buf, byte(port>>8), byte(port)) 87 | 88 | if timeoutms > 0 { 89 | conn.SetDeadline(time.Now().Add(time.Duration(timeoutms) * time.Millisecond)) 90 | } 91 | if _, err = conn.Write(buf); err != nil { 92 | return errors.New("proxy: failed to write connect request to SOCKS5 proxy: " + err.Error()) 93 | } 94 | 95 | if timeoutms > 0 { 96 | conn.SetDeadline(time.Now().Add(time.Duration(timeoutms) * time.Millisecond)) 97 | } 98 | if _, err = io.ReadFull(conn, buf[:4]); err != nil { 99 | return errors.New("proxy: failed to read connect reply from SOCKS5 proxy: " + err.Error()) 100 | } 101 | 102 | failure := "unknown error" 103 | if int(buf[1]) < len(socks5Errors) { 104 | failure = socks5Errors[buf[1]] 105 | } 106 | 107 | if len(failure) > 0 { 108 | err = errors.New("proxy: SOCKS5 proxy failed to connect: " + failure) 109 | return 110 | } 111 | 112 | if timeoutms > 0 { 113 | conn.SetDeadline(time.Now().Add(time.Duration(timeoutms) * time.Millisecond)) 114 | } 115 | hostType := buf[3] 116 | _, err = readSocksHost(conn, hostType) 117 | if err != nil { 118 | return fmt.Errorf("proxy: invalid request: fail to read dst host: %s", err) 119 | } 120 | 121 | if timeoutms > 0 { 122 | conn.SetDeadline(time.Now().Add(time.Duration(timeoutms) * time.Millisecond)) 123 | } 124 | _, err = readSocksPort(conn) 125 | if err != nil { 126 | return fmt.Errorf("proxy: invalid request: fail to read dst port: %s", err) 127 | } 128 | 129 | return nil 130 | } 131 | 132 | func ntohs(data [2]byte) uint16 { 133 | return uint16(data[0])<<8 | uint16(data[1])<<0 134 | } 135 | 136 | func readSocksIPv4Host(r io.Reader) (host string, err error) { 137 | var buf [4]byte 138 | _, err = io.ReadFull(r, buf[:]) 139 | if err != nil { 140 | return 141 | } 142 | 143 | var ip net.IP = buf[:] 144 | host = ip.String() 145 | return 146 | } 147 | 148 | func readSocksIPv6Host(r io.Reader) (host string, err error) { 149 | var buf [16]byte 150 | _, err = io.ReadFull(r, buf[:]) 151 | if err != nil { 152 | return 153 | } 154 | 155 | var ip net.IP = buf[:] 156 | host = ip.String() 157 | return 158 | } 159 | 160 | func readSocksDomainHost(r io.Reader) (host string, err error) { 161 | var buf [0x200]byte 162 | _, err = r.Read(buf[0:1]) 163 | if err != nil { 164 | return 165 | } 166 | length := buf[0] 167 | _, err = io.ReadFull(r, buf[1:1+length]) 168 | if err != nil { 169 | return 170 | } 171 | host = string(buf[1 : 1+length]) 172 | return 173 | } 174 | 175 | func readSocksHost(r io.Reader, hostType byte) (string, error) { 176 | switch hostType { 177 | case Socks5AtypIP4: 178 | return readSocksIPv4Host(r) 179 | case Socks5AtypIP6: 180 | return readSocksIPv6Host(r) 181 | case Socks5AtypDomain: 182 | return readSocksDomainHost(r) 183 | default: 184 | return string(""), fmt.Errorf("Unknown address type 0x%02x ", hostType) 185 | } 186 | } 187 | 188 | func readSocksPort(r io.Reader) (port uint16, err error) { 189 | var buf [2]byte 190 | _, err = io.ReadFull(r, buf[:]) 191 | if err != nil { 192 | return 193 | } 194 | 195 | port = ntohs(buf) 196 | return 197 | } 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pingtunnel 2 | 3 | [](https://github.com/cxjava/pingtunnel) 4 | [](https://github.com/cxjava/pingtunnel) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/cxjava/pingtunnel)](https://goreportcard.com/report/github.com/cxjava/pingtunnel) 6 | [](https://github.com/cxjava/pingtunnel/releases) 7 | [](https://github.com/cxjava/pingtunnel/releases) 8 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a200bca59d1b4ca7a9c2cdb564508b47)](https://www.codacy.com/manual/cxjava/pingtunnel?utm_source=github.com&utm_medium=referral&utm_content=cxjava/pingtunnel&utm_campaign=Badge_Grade) 9 | 10 | pingtunnel是把tcp/udp/sock5流量伪装成icmp流量进行转发的工具。用于突破网络封锁,或是绕过WIFI网络的登陆验证,或是在某些网络加快网络传输速度。 11 | 12 | [Readme EN](./README_EN.md) 13 | 14 | **注意:本工具只是用作学习研究,请勿用于非法用途!** 15 | 16 | # 功能 17 | * 某些服务器的tcp、udp流量被禁止,可以通过pingtunnel绕过。 18 | * 某些场合如学校、咖啡厅、机场,需要登录跳转验证,可以通过pingtunnel绕过。 19 | * 某些网络,tcp、udp传输很慢,可以通过pingtunnel加速网络。 20 | 21 | # 使用 22 | ### 安装服务端 23 | * 首先准备好一个具有公网ip的服务器,如AWS上的EC2,假定域名或者公网ip是www.yourserver.com 24 | * 从[releases](https://github.com/cxjava/pingtunnel/releases)下载对应的安装包,如pingtunnel_linux64.zip,然后解压,以**root**权限执行 25 | ``` 26 | sudo wget (最新release的下载链接) 27 | sudo unzip pingtunnel_linux64.zip 28 | sudo ./pingtunnel -type server 29 | ``` 30 | * 或者用`install.sh`来安装: 31 | ```shell 32 | #安装到当前目录的./bin下 33 | sudo wget -O - https://cdn.jsdelivr.net/gh/cxjava/pingtunnel/install.sh | sh 34 | #安装到/usr/local/bin 35 | sudo wget -O - https://cdn.jsdelivr.net/gh/cxjava/pingtunnel/install.sh | sh -s -- -b /usr/local/bin 36 | ``` 37 | * (可选)关闭系统默认的ping 38 | ``` 39 | echo 1 >/proc/sys/net/ipv4/icmp_echo_ignore_all 40 | ``` 41 | ### 安装GUI客户端(新手推荐) 42 | * 从[pingtunnel-qt](https://github.com/esrrhs/pingtunnel-qt)下载qt的gui版本 43 | * 双击exe运行,修改server(如www.yourserver.com)、listen port(如1080),勾上sock5,其他设置默认即可,然后点击*GO* 44 | * 一切正常,界面上会有ping值显示,然后可点击X隐藏到状态栏 45 | * 设置浏览器的sock5代理到127.0.0.1:1080,如果连不上网,出现socks version not supported错误日志,说明浏览器的代理不是socks5代理。如果提示非安全连接,说明dns有问题,勾上浏览器的【使用socks5代理DNS查询】,或者参考[yellowdns](https://github.com/esrrhs/yellowdns) 46 | 47 | ### 安装客户端(高玩推荐) 48 | * 从[releases](https://github.com/esrrhs/pingtunnel/releases)下载对应的安装包,如pingtunnel_windows64.zip,解压 49 | * 然后用**管理员权限**运行,不同的转发功能所对应的命令如下 50 | * 如果看到有ping pong的log,说明连接正常 51 | ##### 转发sock5 52 | ``` 53 | pingtunnel.exe -type client -l :4455 -s www.yourserver.com -sock5 1 54 | ``` 55 | ##### 转发tcp 56 | ``` 57 | pingtunnel.exe -type client -l :4455 -s www.yourserver.com -t www.yourserver.com:4455 -tcp 1 58 | ``` 59 | ##### 转发udp 60 | ``` 61 | pingtunnel.exe -type client -l :4455 -s www.yourserver.com -t www.yourserver.com:4455 62 | ``` 63 | 64 | ### Docker 65 | server: 66 | ``` 67 | docker run --name pingtunnel-server -d --privileged --network host --restart=always esrrhs/pingtunnel ./pingtunnel -type server -key 123456 68 | ``` 69 | client: 70 | ``` 71 | 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 72 | ``` 73 | 74 | # 效果 75 | 测试pingtunnel的加速效果,服务器位于bandwagon北美,客户端位于中国大陆的阿里云。 76 | 77 | 下载centos镜像 [centos mirror](http://mirror.calgah.com/centos/8/isos/x86_64/CentOS-8.1.1911-x86_64-dvd1.iso) 78 | 直接wget、通过shadowsocks wget、通过kcptun wget、通过pingtunnel wget的结果如下。 79 | 80 | | | wget | shaowsocks | kcptun | pingtunnel | 81 | |--------------|----------|------------|------------|------------| 82 | | 阿里云 | 26.6KB/s | 31.8KB/s | 606KB/s |5.64MB/s| 83 | 84 | 可以看到加速效果基本上**200倍**。 85 | 86 | # 下载 87 | cmd: https://github.com/cxjava/pingtunnel/releases 88 | 89 | QT GUI: https://github.com/esrrhs/pingtunnel-qt 90 | 91 | # Stargazers over time 92 | 93 | [![Stargazers over time](https://starchart.cc/cxjava/pingtunnel.svg)](https://starchart.cc/cxjava/pingtunnel) 94 | 95 | # 其他 96 | 可用于路由器上,参考[yellowsocks](https://github.com/esrrhs/yellowsocks)的使用 97 | 98 | # Usage 99 | 通过伪造ping,把tcp/udp/sock5流量通过远程服务器转发到目的服务器上。用于突破某些运营商封锁TCP/UDP流量。 100 | 101 | Usage: 102 | 103 | // server 104 | pingtunnel -type server 105 | 106 | // client, Forward udp 107 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 108 | 109 | // client, Forward tcp 110 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 -tcp 1 111 | 112 | // client, Forward sock5, implicitly open tcp, so no target server is needed 113 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -sock5 1 114 | 115 | -type 服务器或者客户端 116 | 117 | 服务器参数: 118 | 119 | -key 设置的密码,默认0 120 | 121 | -nolog 不写日志文件,只打印标准输出,默认0 122 | 123 | -noprint 不打印屏幕输出,默认0 124 | 125 | -loglevel 日志文件等级,默认info 126 | 127 | -maxconn 最大连接数,默认0,不受限制 128 | 129 | -maxprt server最大处理线程数,默认100 130 | 131 | -maxprb server最大处理线程buffer数,默认1000 132 | 133 | -conntt server发起连接到目标地址的超时时间,默认1000ms 134 | 135 | 客户端参数: 136 | 137 | -l 本地的地址,发到这个端口的流量将转发到服务器 138 | 139 | -s 服务器的地址,流量将通过隧道转发到这个服务器 140 | 141 | -t 远端服务器转发的目的地址,流量将转发到这个地址 142 | 143 | -timeout 本地记录连接超时的时间,单位是秒,默认60s 144 | 145 | -key 设置的密码,默认0 146 | 147 | -tcp 设置是否转发tcp,默认0 148 | 149 | -tcp_bs tcp的发送接收缓冲区大小,默认1MB 150 | 151 | -tcp_mw tcp的最大窗口,默认10000 152 | 153 | -tcp_rst tcp的超时发送时间,默认400ms 154 | 155 | -tcp_gz 当数据包超过这个大小,tcp将压缩数据,0表示不压缩,默认0 156 | 157 | -tcp_stat 打印tcp的监控,默认0 158 | 159 | -nolog 不写日志文件,只打印标准输出,默认0 160 | 161 | -noprint 不打印屏幕输出,默认0 162 | 163 | -loglevel 日志文件等级,默认info 164 | 165 | -sock5 开启sock5转发,默认0 166 | 167 | -profile 在指定端口开启性能检测,默认0不开启 168 | 169 | -s5filter sock5模式设置转发过滤,默认全转发,设置CN代表CN地区的直连不转发 170 | 171 | -s5ftfile sock5模式转发过滤的数据文件,默认读取当前目录的GeoLite2-Country.mmdb 172 | 173 | -------------------------------------------------------------------------------- /rbuffergo/rbuffergo.go: -------------------------------------------------------------------------------- 1 | package rbuffergo 2 | 3 | import "sync" 4 | 5 | /* 6 | type: [1] 7 | iter: begin(2) end(8) 8 | | | 9 | data: _ _ * * * * * * _ _ _ 10 | buffer: _ _ _ _ _ _ _ _ _ _ _ 11 | index: 0 1 2 3 4 5 6 7 8 9 10 12 | type: [2] 13 | iter: end(2) begin(7) 14 | | | 15 | data: * * _ _ _ _ _ * * * * 16 | buffer: _ _ _ _ _ _ _ _ _ _ _ 17 | index: 0 1 2 3 4 5 6 7 8 9 10 18 | type: [3] 19 | iter: begin(4),end(4) 20 | | 21 | data: _ _ _ _ _ _ _ _ _ _ _ 22 | buffer: _ _ _ _ _ _ _ _ _ _ _ 23 | index: 0 1 2 3 4 5 6 7 8 9 10 24 | type: [4] 25 | iter: begin(4),end(4) 26 | | | 27 | data: * * * * * * * * * * * 28 | buffer: _ _ _ _ _ _ _ _ _ _ _ 29 | index: 0 1 2 3 4 5 6 7 8 9 10 30 | */ 31 | 32 | type RBuffergo struct { 33 | buffer []byte 34 | datasize int 35 | begin int 36 | end int 37 | storeDatasize int 38 | storeBegin int 39 | storeEnd int 40 | lock sync.Locker 41 | } 42 | 43 | func New(len int, lock bool) *RBuffergo { 44 | buffer := &RBuffergo{} 45 | buffer.buffer = make([]byte, len) 46 | if lock { 47 | buffer.lock = &sync.Mutex{} 48 | } 49 | return buffer 50 | } 51 | 52 | func (b *RBuffergo) CanWrite(size int) bool { 53 | if b.lock != nil { 54 | b.lock.Lock() 55 | defer b.lock.Unlock() 56 | } 57 | return b.datasize+size <= len(b.buffer) 58 | } 59 | 60 | func (b *RBuffergo) SkipWrite(size int) { 61 | if b.lock != nil { 62 | b.lock.Lock() 63 | defer b.lock.Unlock() 64 | } 65 | 66 | if !(b.datasize+size <= len(b.buffer)) { 67 | return 68 | } 69 | 70 | b.datasize += size 71 | b.end += size 72 | if b.end >= len(b.buffer) { 73 | b.end -= len(b.buffer) 74 | } 75 | } 76 | 77 | func (b *RBuffergo) Write(data []byte) bool { 78 | if b.lock != nil { 79 | b.lock.Lock() 80 | defer b.lock.Unlock() 81 | } 82 | 83 | if !(b.datasize+len(data) <= len(b.buffer)) { 84 | return false 85 | } 86 | // [1][3] 87 | if b.end >= b.begin { 88 | // 能装下 89 | if len(b.buffer)-b.end >= len(data) { 90 | copy(b.buffer[b.end:], data) 91 | } else { 92 | copy(b.buffer[b.end:], data[0:len(b.buffer)-b.end]) 93 | copy(b.buffer, data[len(b.buffer)-b.end:]) 94 | } 95 | } else /*[2]*/ { 96 | copy(b.buffer[b.end:], data) 97 | } 98 | 99 | b.datasize += len(data) 100 | b.end += len(data) 101 | if b.end >= len(b.buffer) { 102 | b.end -= len(b.buffer) 103 | } 104 | 105 | return true 106 | } 107 | 108 | func (b *RBuffergo) CanRead(size int) bool { 109 | if b.lock != nil { 110 | b.lock.Lock() 111 | defer b.lock.Unlock() 112 | } 113 | 114 | return b.datasize >= size 115 | } 116 | 117 | func (b *RBuffergo) SkipRead(size int) { 118 | if b.lock != nil { 119 | b.lock.Lock() 120 | defer b.lock.Unlock() 121 | } 122 | 123 | if !(b.datasize >= size) { 124 | return 125 | } 126 | 127 | b.datasize -= size 128 | b.begin += size 129 | if b.begin >= len(b.buffer) { 130 | b.begin -= len(b.buffer) 131 | } 132 | 133 | if b.lock == nil { 134 | if b.datasize == 0 { 135 | b.begin = 0 136 | b.end = 0 137 | } 138 | } 139 | } 140 | 141 | func (b *RBuffergo) Read(data []byte) bool { 142 | if b.lock != nil { 143 | b.lock.Lock() 144 | defer b.lock.Unlock() 145 | } 146 | 147 | if !(b.datasize >= len(data)) { 148 | return false 149 | } 150 | 151 | // [2][4] 152 | if b.begin >= b.end { 153 | // 能读完 154 | if len(b.buffer)-b.begin >= len(data) { 155 | copy(data, b.buffer[b.begin:]) 156 | } else { 157 | copy(data[0:len(b.buffer)-b.begin], b.buffer[b.begin:]) 158 | copy(data[len(b.buffer)-b.begin:], b.buffer) 159 | } 160 | } else /* [1]*/ { 161 | copy(data, b.buffer[b.begin:]) 162 | } 163 | 164 | b.datasize -= len(data) 165 | b.begin += len(data) 166 | if b.begin >= len(b.buffer) { 167 | b.begin -= len(b.buffer) 168 | } 169 | 170 | if b.lock == nil { 171 | if b.datasize == 0 { 172 | b.begin = 0 173 | b.end = 0 174 | } 175 | } 176 | 177 | return true 178 | } 179 | 180 | func (b *RBuffergo) Store() { 181 | if b.lock != nil { 182 | b.lock.Lock() 183 | defer b.lock.Unlock() 184 | } 185 | 186 | b.storeDatasize = b.datasize 187 | b.storeBegin = b.begin 188 | b.storeEnd = b.end 189 | } 190 | 191 | func (b *RBuffergo) Restore() { 192 | if b.lock != nil { 193 | b.lock.Lock() 194 | defer b.lock.Unlock() 195 | } 196 | 197 | b.datasize = b.storeDatasize 198 | b.begin = b.storeBegin 199 | b.end = b.storeEnd 200 | } 201 | 202 | func (b *RBuffergo) Clear() { 203 | if b.lock != nil { 204 | b.lock.Lock() 205 | defer b.lock.Unlock() 206 | } 207 | 208 | b.datasize = 0 209 | b.begin = 0 210 | b.end = 0 211 | } 212 | 213 | func (b *RBuffergo) Size() int { 214 | if b.lock != nil { 215 | b.lock.Lock() 216 | defer b.lock.Unlock() 217 | } 218 | 219 | return b.datasize 220 | } 221 | 222 | func (b *RBuffergo) Capacity() int { 223 | return len(b.buffer) 224 | } 225 | 226 | func (b *RBuffergo) Empty() bool { 227 | if b.lock != nil { 228 | b.lock.Lock() 229 | defer b.lock.Unlock() 230 | } 231 | 232 | return b.datasize == 0 233 | } 234 | 235 | func (b *RBuffergo) Full() bool { 236 | if b.lock != nil { 237 | b.lock.Lock() 238 | defer b.lock.Unlock() 239 | } 240 | 241 | return b.datasize == len(b.buffer) 242 | } 243 | 244 | func (b *RBuffergo) GetReadLineBuffer() []byte { 245 | if b.lock != nil { 246 | b.lock.Lock() 247 | defer b.lock.Unlock() 248 | } 249 | 250 | if b.datasize < len(b.buffer)-b.begin { 251 | return b.buffer[b.begin : b.begin+b.datasize] 252 | } else { 253 | return b.buffer[b.begin : b.begin+len(b.buffer)-b.begin] 254 | } 255 | } 256 | 257 | func (b *RBuffergo) GetWriteLineBuffer() []byte { 258 | if b.lock != nil { 259 | b.lock.Lock() 260 | defer b.lock.Unlock() 261 | } 262 | 263 | if len(b.buffer)-b.datasize < len(b.buffer)-b.end { 264 | return b.buffer[b.end : b.end+len(b.buffer)-b.datasize] 265 | } else { 266 | return b.buffer[b.end : b.end+len(b.buffer)-b.end] 267 | } 268 | } 269 | 270 | func (b *RBuffergo) GetBuffer() []byte { 271 | return b.buffer 272 | } 273 | -------------------------------------------------------------------------------- /pingtunnel/msg.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: msg.proto 3 | 4 | package pingtunnel 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | math "math" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the proto package it is being compiled against. 19 | // A compilation error at this line likely means your copy of the 20 | // proto package needs to be updated. 21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 22 | 23 | type MyMsg_TYPE int32 24 | 25 | const ( 26 | MyMsg_DATA MyMsg_TYPE = 0 27 | MyMsg_PING MyMsg_TYPE = 1 28 | MyMsg_KICK MyMsg_TYPE = 2 29 | MyMsg_MAGIC MyMsg_TYPE = 57005 30 | ) 31 | 32 | var MyMsg_TYPE_name = map[int32]string{ 33 | 0: "DATA", 34 | 1: "PING", 35 | 2: "KICK", 36 | 57005: "MAGIC", 37 | } 38 | 39 | var MyMsg_TYPE_value = map[string]int32{ 40 | "DATA": 0, 41 | "PING": 1, 42 | "KICK": 2, 43 | "MAGIC": 57005, 44 | } 45 | 46 | func (x MyMsg_TYPE) String() string { 47 | return proto.EnumName(MyMsg_TYPE_name, int32(x)) 48 | } 49 | 50 | func (MyMsg_TYPE) EnumDescriptor() ([]byte, []int) { 51 | return fileDescriptor_c06e4cca6c2cc899, []int{0, 0} 52 | } 53 | 54 | type MyMsg struct { 55 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 56 | Type int32 `protobuf:"varint,2,opt,name=type,proto3" json:"type,omitempty"` 57 | Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` 58 | Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` 59 | Rproto int32 `protobuf:"zigzag32,5,opt,name=rproto,proto3" json:"rproto,omitempty"` 60 | Magic int32 `protobuf:"zigzag32,6,opt,name=magic,proto3" json:"magic,omitempty"` 61 | Key int32 `protobuf:"zigzag32,7,opt,name=key,proto3" json:"key,omitempty"` 62 | Timeout int32 `protobuf:"varint,8,opt,name=timeout,proto3" json:"timeout,omitempty"` 63 | Tcpmode int32 `protobuf:"varint,9,opt,name=tcpmode,proto3" json:"tcpmode,omitempty"` 64 | TcpmodeBuffersize int32 `protobuf:"varint,10,opt,name=tcpmode_buffersize,json=tcpmodeBuffersize,proto3" json:"tcpmode_buffersize,omitempty"` 65 | TcpmodeMaxwin int32 `protobuf:"varint,11,opt,name=tcpmode_maxwin,json=tcpmodeMaxwin,proto3" json:"tcpmode_maxwin,omitempty"` 66 | TcpmodeResendTimems int32 `protobuf:"varint,12,opt,name=tcpmode_resend_timems,json=tcpmodeResendTimems,proto3" json:"tcpmode_resend_timems,omitempty"` 67 | TcpmodeCompress int32 `protobuf:"varint,13,opt,name=tcpmode_compress,json=tcpmodeCompress,proto3" json:"tcpmode_compress,omitempty"` 68 | TcpmodeStat int32 `protobuf:"varint,14,opt,name=tcpmode_stat,json=tcpmodeStat,proto3" json:"tcpmode_stat,omitempty"` 69 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 70 | XXX_unrecognized []byte `json:"-"` 71 | XXX_sizecache int32 `json:"-"` 72 | } 73 | 74 | func (m *MyMsg) Reset() { *m = MyMsg{} } 75 | func (m *MyMsg) String() string { return proto.CompactTextString(m) } 76 | func (*MyMsg) ProtoMessage() {} 77 | func (*MyMsg) Descriptor() ([]byte, []int) { 78 | return fileDescriptor_c06e4cca6c2cc899, []int{0} 79 | } 80 | 81 | func (m *MyMsg) XXX_Unmarshal(b []byte) error { 82 | return xxx_messageInfo_MyMsg.Unmarshal(m, b) 83 | } 84 | func (m *MyMsg) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 85 | return xxx_messageInfo_MyMsg.Marshal(b, m, deterministic) 86 | } 87 | func (m *MyMsg) XXX_Merge(src proto.Message) { 88 | xxx_messageInfo_MyMsg.Merge(m, src) 89 | } 90 | func (m *MyMsg) XXX_Size() int { 91 | return xxx_messageInfo_MyMsg.Size(m) 92 | } 93 | func (m *MyMsg) XXX_DiscardUnknown() { 94 | xxx_messageInfo_MyMsg.DiscardUnknown(m) 95 | } 96 | 97 | var xxx_messageInfo_MyMsg proto.InternalMessageInfo 98 | 99 | func (m *MyMsg) GetId() string { 100 | if m != nil { 101 | return m.Id 102 | } 103 | return "" 104 | } 105 | 106 | func (m *MyMsg) GetType() int32 { 107 | if m != nil { 108 | return m.Type 109 | } 110 | return 0 111 | } 112 | 113 | func (m *MyMsg) GetTarget() string { 114 | if m != nil { 115 | return m.Target 116 | } 117 | return "" 118 | } 119 | 120 | func (m *MyMsg) GetData() []byte { 121 | if m != nil { 122 | return m.Data 123 | } 124 | return nil 125 | } 126 | 127 | func (m *MyMsg) GetRproto() int32 { 128 | if m != nil { 129 | return m.Rproto 130 | } 131 | return 0 132 | } 133 | 134 | func (m *MyMsg) GetMagic() int32 { 135 | if m != nil { 136 | return m.Magic 137 | } 138 | return 0 139 | } 140 | 141 | func (m *MyMsg) GetKey() int32 { 142 | if m != nil { 143 | return m.Key 144 | } 145 | return 0 146 | } 147 | 148 | func (m *MyMsg) GetTimeout() int32 { 149 | if m != nil { 150 | return m.Timeout 151 | } 152 | return 0 153 | } 154 | 155 | func (m *MyMsg) GetTcpmode() int32 { 156 | if m != nil { 157 | return m.Tcpmode 158 | } 159 | return 0 160 | } 161 | 162 | func (m *MyMsg) GetTcpmodeBuffersize() int32 { 163 | if m != nil { 164 | return m.TcpmodeBuffersize 165 | } 166 | return 0 167 | } 168 | 169 | func (m *MyMsg) GetTcpmodeMaxwin() int32 { 170 | if m != nil { 171 | return m.TcpmodeMaxwin 172 | } 173 | return 0 174 | } 175 | 176 | func (m *MyMsg) GetTcpmodeResendTimems() int32 { 177 | if m != nil { 178 | return m.TcpmodeResendTimems 179 | } 180 | return 0 181 | } 182 | 183 | func (m *MyMsg) GetTcpmodeCompress() int32 { 184 | if m != nil { 185 | return m.TcpmodeCompress 186 | } 187 | return 0 188 | } 189 | 190 | func (m *MyMsg) GetTcpmodeStat() int32 { 191 | if m != nil { 192 | return m.TcpmodeStat 193 | } 194 | return 0 195 | } 196 | 197 | func init() { 198 | proto.RegisterEnum("MyMsg_TYPE", MyMsg_TYPE_name, MyMsg_TYPE_value) 199 | proto.RegisterType((*MyMsg)(nil), "MyMsg") 200 | } 201 | 202 | func init() { proto.RegisterFile("msg.proto", fileDescriptor_c06e4cca6c2cc899) } 203 | 204 | var fileDescriptor_c06e4cca6c2cc899 = []byte{ 205 | // 342 bytes of a gzipped FileDescriptorProto 206 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x3c, 0x90, 0xdb, 0x6a, 0xe2, 0x50, 207 | 0x14, 0x86, 0x27, 0x27, 0x0f, 0xcb, 0xe8, 0xc4, 0x35, 0x07, 0xd6, 0x65, 0x46, 0x18, 0xc8, 0x5c, 208 | 0xcc, 0xc0, 0xb4, 0x4f, 0xa0, 0xb6, 0x88, 0x48, 0x8a, 0xa4, 0xde, 0xb4, 0x37, 0x12, 0xcd, 0x36, 209 | 0x84, 0x36, 0x07, 0xb2, 0xb7, 0xb4, 0xf6, 0x9d, 0xfa, 0x08, 0x7d, 0x8d, 0x3e, 0x4f, 0xc9, 0x72, 210 | 0xa7, 0x77, 0xff, 0xff, 0x7f, 0x5f, 0xc8, 0x62, 0x43, 0x3f, 0x97, 0xe9, 0xbf, 0xaa, 0x2e, 0x55, 211 | 0x39, 0x79, 0xb7, 0xc0, 0x09, 0x4f, 0xa1, 0x4c, 0x71, 0x04, 0x66, 0x96, 0x90, 0xe1, 0x1b, 0x41, 212 | 0x3f, 0x32, 0xb3, 0x04, 0x11, 0x6c, 0x75, 0xaa, 0x04, 0x99, 0xbe, 0x11, 0x38, 0x11, 0x67, 0xfc, 213 | 0x09, 0x1d, 0x15, 0xd7, 0xa9, 0x50, 0x64, 0xb1, 0xa7, 0x5b, 0xe3, 0x26, 0xb1, 0x8a, 0xc9, 0xf6, 214 | 0x8d, 0xc0, 0x8d, 0x38, 0x37, 0x6e, 0xcd, 0xff, 0x20, 0xc7, 0x37, 0x82, 0x71, 0xa4, 0x1b, 0x7e, 215 | 0x07, 0x27, 0x8f, 0xd3, 0x6c, 0x4f, 0x1d, 0x9e, 0xcf, 0x05, 0x3d, 0xb0, 0x1e, 0xc4, 0x89, 0xba, 216 | 0xbc, 0x35, 0x11, 0x09, 0xba, 0x2a, 0xcb, 0x45, 0x79, 0x54, 0xd4, 0xe3, 0x13, 0xda, 0xca, 0x64, 217 | 0x5f, 0xe5, 0x65, 0x22, 0xa8, 0xaf, 0xc9, 0xb9, 0xe2, 0x5f, 0x40, 0x1d, 0xb7, 0xbb, 0xe3, 0xe1, 218 | 0x20, 0x6a, 0x99, 0xbd, 0x08, 0x02, 0x96, 0xc6, 0x9a, 0xcc, 0x3e, 0x01, 0xfe, 0x86, 0x51, 0xab, 219 | 0xe7, 0xf1, 0xf3, 0x53, 0x56, 0xd0, 0x80, 0xd5, 0xa1, 0x5e, 0x43, 0x1e, 0xf1, 0x02, 0x7e, 0xb4, 220 | 0x5a, 0x2d, 0xa4, 0x28, 0x92, 0x6d, 0x73, 0x49, 0x2e, 0xc9, 0x65, 0xfb, 0x9b, 0x86, 0x11, 0xb3, 221 | 0x0d, 0x23, 0xfc, 0x03, 0x5e, 0xfb, 0xcd, 0xbe, 0xcc, 0xab, 0x5a, 0x48, 0x49, 0x43, 0xd6, 0xbf, 222 | 0xea, 0x7d, 0xae, 0x67, 0xfc, 0x05, 0x6e, 0xab, 0x4a, 0x15, 0x2b, 0x1a, 0xb1, 0x36, 0xd0, 0xdb, 223 | 0xad, 0x8a, 0xd5, 0xe4, 0x3f, 0xd8, 0x9b, 0xbb, 0xf5, 0x35, 0xf6, 0xc0, 0xbe, 0x9a, 0x6e, 0xa6, 224 | 0xde, 0x97, 0x26, 0xad, 0x97, 0x37, 0x0b, 0xcf, 0x68, 0xd2, 0x6a, 0x39, 0x5f, 0x79, 0x26, 0x0e, 225 | 0xc0, 0x09, 0xa7, 0x8b, 0xe5, 0xdc, 0x7b, 0x7d, 0xb3, 0x66, 0xee, 0x3d, 0x54, 0x59, 0x91, 0xaa, 226 | 0x63, 0x51, 0x88, 0xc7, 0x5d, 0x87, 0xdf, 0xfe, 0xf2, 0x23, 0x00, 0x00, 0xff, 0xff, 0x59, 0xbc, 227 | 0x55, 0x76, 0xfa, 0x01, 0x00, 0x00, 228 | } 229 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # Pingtunnel 2 | 3 | [](https://github.com/esrrhs/pingtunnel) 4 | [](https://github.com/esrrhs/pingtunnel) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/esrrhs/pingtunnel)](https://goreportcard.com/report/github.com/esrrhs/pingtunnel) 6 | [](https://github.com/esrrhs/pingtunnel/releases) 7 | [](https://github.com/esrrhs/pingtunnel/releases) 8 | [](https://hub.docker.com/repository/docker/esrrhs/pingtunnel) 9 | [](https://github.com/esrrhs/pingtunnel/actions) 10 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a200bca59d1b4ca7a9c2cdb564508b47)](https://www.codacy.com/manual/esrrhs/pingtunnel?utm_source=github.com&utm_medium=referral&utm_content=esrrhs/pingtunnel&utm_campaign=Badge_Grade) 11 | 12 | Pingtunnel is a tool that advertises tcp/udp/sock5 traffic as icmp traffic for forwarding. Used to break through the network blockade, or to bypass the WIFI network login verification, or speed up network transmission speed on some networks. 13 | 14 | 15 | # Why use this 16 | * TCP and UDP traffic of some servers are banned and can be bypassed by pingtunnel. 17 | * In some occasions, such as schools, cafes, and airports, login jump authentication is required, which can be bypassed by pingtunnel. 18 | * In some networks, TCP/UDP transmission is very slow. You can speed up the network through pingtunnel. 19 | # Use 20 | ### Install server 21 | * First prepare a server with a public IP, such as EC2 on AWS, assuming the domain name or public IP is www.yourserver.com 22 | * 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 23 | ``` 24 | sudo wget (link of latest release) 25 | sudo unzip pingtunnel_linux64.zip 26 | sudo ./pingtunnel -type server 27 | ``` 28 | * (Optional) Disable system default ping 29 | ``` 30 | echo 1> / proc / sys / net / ipv4 / icmp_echo_ignore_all 31 | ``` 32 | ### Install GUI client (recommended by novices) 33 | * Download the gui version of qt from [pingtunnel-qt](https://github.com/esrrhs/pingtunnel-qt) 34 | * Double-click the exe to run, modify the server (such as www.yourserver.com), listen port (such as 1080), tick sock5, other settings can be default, and then click *GO* 35 | * Everything is normal, there will be a ping value on the interface, and then you can click X to hide it in the status bar 36 | * Set the browser's sock5 proxy to 127.0.0.1:1080, If you do not connect to the Internet, a socks version not supported error log appears, indicating that the browser's proxy is not a socks5 proxy.If it prompts a non-secure connection, it means there is a problem with dns. Check "Use socks5 proxy DNS query" on the browser, or refer to [yellowdns](https://github.com/esrrhs/yellowdns) 37 | 38 | 39 | ### Install the client (recommended for high play) 40 | * Download the corresponding installation package from [releases](https://github.com/esrrhs/pingtunnel/releases), such as pingtunnel_windows64.zip, and decompress it 41 | * Then run with **administrator** privileges. The commands corresponding to different forwarding functions are as follows. 42 | * If you see a log of ping pong, the connection is normal 43 | ##### Forward sock5 44 | ``` 45 | pingtunnel.exe -type client -l: 4455 -s www.yourserver.com -sock5 1 46 | ``` 47 | ##### Forward tcp 48 | ``` 49 | pingtunnel.exe -type client -l: 4455 -s www.yourserver.com -t www.yourserver.com:4455 -tcp 1 50 | ``` 51 | ##### Forward udp 52 | ``` 53 | pingtunnel.exe -type client -l: 4455 -s www.yourserver.com -t www.yourserver.com:4455 54 | ``` 55 | 56 | ### Use Docker 57 | server: 58 | ``` 59 | docker run --name pingtunnel-server -d --privileged --network host --restart = always esrrhs / pingtunnel ./pingtunnel -type server -key 123456 60 | ``` 61 | client: 62 | ``` 63 | 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 64 | ``` 65 | 66 | # Test 67 | Test the acceleration effect of pingtunnel. The server is located in bandwagon North America and the client is located in AlibabaCloud mainland China. 68 | 69 | download the centos image [centos mirror](http://mirror.calgah.com/centos/8/isos/x86_64/CentOS-8.1.1911-x86_64-dvd1.iso) , the results of direct wget, shadowsocks wget, kcptun wget, and pingtunnel wget are as follows. 70 | 71 | | | wget | shaowsocks | kcptun | pingtunnel | 72 | |--------------|----------|------------|------------|------------| 73 | | AlibabaCloud | 26.6KB/s | 31.8KB/s | 606KB/s |5.64MB/s| 74 | 75 | the acceleration effect is basically **200 times**. 76 | 77 | # Download 78 | cmd: https://github.com/esrrhs/pingtunnel/releases 79 | 80 | QT GUI: https://github.com/esrrhs/pingtunnel-qt 81 | 82 | # Stargazers over time 83 | 84 | [![Stargazers over time](https://starchart.cc/esrrhs/pingtunnel.svg)](https://starchart.cc/esrrhs/pingtunnel) 85 | 86 | # Other 87 | Can be used on routers, refer to the use of [yellowsocks](https://github.com/esrrhs/yellowsocks) 88 | 89 | # Usage 90 | 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. 91 | 92 | Usage: 93 | 94 | // server 95 | pingtunnel -type server 96 | 97 | // client, Forward udp 98 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 99 | 100 | // client, Forward tcp 101 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 -tcp 1 102 | 103 | // client, Forward sock5, implicitly open tcp, so no target server is needed 104 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -sock5 1 105 | 106 | -type client or server 107 | 108 | server param: 109 | 110 | -key Set password, default 0 111 | 112 | -nolog Do not write log files, only print standard output, default 0 is off 113 | 114 | -noprint Do not print standard output, default 0 is off 115 | 116 | -loglevel log level, default is info 117 | 118 | -maxconn the max num of connections, default 0 is no limit 119 | 120 | -maxprt max process thread in server, default 100 121 | 122 | -maxprb max process thread's buffer in server, default 1000 123 | 124 | -conntt The timeout period for the server to initiate a connection to the destination address. The default is 1000ms. 125 | 126 | client param: 127 | 128 | -l Local address, traffic sent to this port will be forwarded to the server 129 | 130 | -s The address of the server, the traffic will be forwarded to this server through the tunnel 131 | 132 | -t Destination address forwarded by the remote server, traffic will be forwarded to this address 133 | 134 | -timeout The time when the local record connection timed out, in seconds, 60 seconds by default 135 | 136 | -key Set password, default 0 137 | 138 | -tcp Set the switch to forward tcp, the default is 0 139 | 140 | -tcp_bs Tcp send and receive buffer size, default 1MB 141 | 142 | -tcp_mw The maximum window of tcp, the default is 10000 143 | 144 | -tcp_rst Tcp timeout resend time, default 400ms 145 | 146 | -tcp_gz Tcp will compress data when the packet exceeds this size, 0 means no compression, default 0 147 | 148 | -tcp_stat Print tcp connection statistic, default 0 is off 149 | 150 | -nolog Do not write log files, only print standard output, default 0 is off 151 | 152 | -noprint Do not print standard output, default 0 is off 153 | 154 | -loglevel log level, default is info 155 | 156 | -sock5 Turn on sock5 forwarding, default 0 is off 157 | 158 | -profile Enable performance detection on the specified port. The default 0 is not enabled. 159 | 160 | -s5filter 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. 161 | 162 | -s5ftfile The data file in sock5 filter mode, the default reading of the current directory GeoLite2-Country.mmdb 163 | -------------------------------------------------------------------------------- /loggo/loggo.go: -------------------------------------------------------------------------------- 1 | package loggo 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/cxjava/pingtunnel/termcolor" 7 | "io/ioutil" 8 | "os" 9 | "runtime" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | const ( 15 | LEVEL_DEBUG = iota 16 | LEVEL_INFO 17 | LEVEL_WARN 18 | LEVEL_ERROR 19 | ) 20 | 21 | type Config struct { 22 | Level int 23 | Prefix string 24 | MaxDay int 25 | NoLogFile bool 26 | NoPrint bool 27 | NoLogColor bool 28 | } 29 | 30 | var gConfig Config 31 | var gInited bool 32 | 33 | func init() { 34 | gConfig.Prefix = "default" 35 | gConfig.MaxDay = 1 36 | gConfig.Level = LEVEL_DEBUG 37 | } 38 | 39 | func Ini(config Config) { 40 | initbefore := false 41 | if gInited { 42 | fmt.Println("loggo had ini before " + gConfig.Prefix) 43 | initbefore = true 44 | } 45 | 46 | gConfig = config 47 | if gConfig.Prefix == "" { 48 | panic("log prefix is empty") 49 | } 50 | 51 | if strings.Contains(gConfig.Prefix, "_") { 52 | panic("log prefix contain _") 53 | } 54 | 55 | Warn("loggo Ini") 56 | 57 | gInited = true 58 | if !initbefore { 59 | go loopCheck(gConfig) 60 | } 61 | } 62 | 63 | func Debug(format string, a ...interface{}) { 64 | if gConfig.Level <= LEVEL_DEBUG { 65 | str := genLog(LEVEL_DEBUG, format, a...) 66 | if !gConfig.NoLogFile { 67 | file := openLog(LEVEL_DEBUG) 68 | defer file.Close() 69 | file.WriteString(str) 70 | } 71 | if !gConfig.NoPrint { 72 | if !gConfig.NoLogColor { 73 | fmt.Print(termcolor.FgString(str, 0, 0, 255)) 74 | } else { 75 | fmt.Print(str) 76 | } 77 | } 78 | } 79 | } 80 | 81 | func Info(format string, a ...interface{}) { 82 | if gConfig.Level <= LEVEL_INFO { 83 | str := genLog(LEVEL_INFO, format, a...) 84 | if !gConfig.NoLogFile { 85 | file := openLog(LEVEL_INFO) 86 | defer file.Close() 87 | file.WriteString(str) 88 | } 89 | if gConfig.Level <= LEVEL_DEBUG { 90 | if !gConfig.NoLogFile { 91 | file1 := openLog(LEVEL_DEBUG) 92 | defer file1.Close() 93 | file1.WriteString(str) 94 | } 95 | } 96 | if !gConfig.NoPrint { 97 | if !gConfig.NoLogColor { 98 | fmt.Print(termcolor.FgString(str, 0, 255, 0)) 99 | } else { 100 | fmt.Print(str) 101 | } 102 | } 103 | } 104 | } 105 | 106 | func Warn(format string, a ...interface{}) { 107 | if gConfig.Level <= LEVEL_WARN { 108 | str := genLog(LEVEL_WARN, format, a...) 109 | if !gConfig.NoLogFile { 110 | file := openLog(LEVEL_WARN) 111 | defer file.Close() 112 | file.WriteString(str) 113 | } 114 | if gConfig.Level <= LEVEL_INFO { 115 | if !gConfig.NoLogFile { 116 | file1 := openLog(LEVEL_INFO) 117 | defer file1.Close() 118 | file1.WriteString(str) 119 | } 120 | } 121 | if gConfig.Level <= LEVEL_DEBUG { 122 | if !gConfig.NoLogFile { 123 | file2 := openLog(LEVEL_DEBUG) 124 | defer file2.Close() 125 | file2.WriteString(str) 126 | } 127 | } 128 | if !gConfig.NoPrint { 129 | if !gConfig.NoLogColor { 130 | fmt.Print(termcolor.FgString(str, 255, 255, 0)) 131 | } else { 132 | fmt.Print(str) 133 | } 134 | } 135 | } 136 | } 137 | 138 | func Error(format string, a ...interface{}) { 139 | if gConfig.Level <= LEVEL_ERROR { 140 | str := genLog(LEVEL_ERROR, format, a...) 141 | if !gConfig.NoLogFile { 142 | file := openLog(LEVEL_ERROR) 143 | defer file.Close() 144 | file.WriteString(str) 145 | } 146 | if gConfig.Level <= LEVEL_WARN { 147 | if !gConfig.NoLogFile { 148 | file0 := openLog(LEVEL_WARN) 149 | defer file0.Close() 150 | file0.WriteString(str) 151 | } 152 | } 153 | if gConfig.Level <= LEVEL_INFO { 154 | if !gConfig.NoLogFile { 155 | file1 := openLog(LEVEL_INFO) 156 | defer file1.Close() 157 | file1.WriteString(str) 158 | } 159 | } 160 | if gConfig.Level <= LEVEL_DEBUG { 161 | if !gConfig.NoLogFile { 162 | file2 := openLog(LEVEL_DEBUG) 163 | defer file2.Close() 164 | file2.WriteString(str) 165 | } 166 | } 167 | if !gConfig.NoPrint { 168 | if !gConfig.NoLogColor { 169 | fmt.Print(termcolor.FgString(str, 255, 0, 0)) 170 | } else { 171 | fmt.Print(str) 172 | } 173 | } 174 | } 175 | } 176 | 177 | func genLog(level int, format string, a ...interface{}) string { 178 | file, funcName, line := getFunc() 179 | t := time.Now().Format(time.RFC3339Nano) 180 | str := fmt.Sprintf(format, a...) 181 | ret := fmt.Sprintf("[%v] [%v] [%v:%v] [%v] %v\n", levelName(level), t, file, line, funcName, str) 182 | return ret 183 | } 184 | 185 | func getFunc() (string, string, int) { 186 | // Ask runtime.Callers for up to 5 pcs, including runtime.Callers itself. 187 | pc := make([]uintptr, 5) 188 | n := runtime.Callers(0, pc) 189 | if n == 0 { 190 | // No pcs available. Stop now. 191 | // This can happen if the first argument to runtime.Callers is large. 192 | return "NIL", "NIL", 0 193 | } 194 | 195 | pc = pc[:n] // pass only valid pcs to runtime.CallersFrames 196 | frames := runtime.CallersFrames(pc) 197 | 198 | n = 5 199 | 200 | // Loop to get frames. 201 | // A fixed number of pcs can expand to an indefinite number of Frames. 202 | for i := 0; i < n; i++ { 203 | frame, more := frames.Next() 204 | if i == n-1 { 205 | return frame.File, frame.Function, frame.Line 206 | } 207 | if !more { 208 | break 209 | } 210 | } 211 | return "NIL", "NIL", 0 212 | } 213 | 214 | func levelName(level int) string { 215 | switch level { 216 | case LEVEL_DEBUG: 217 | return "DEBUG" 218 | case LEVEL_INFO: 219 | return "INFO" 220 | case LEVEL_WARN: 221 | return "WARN" 222 | case LEVEL_ERROR: 223 | return "ERROR" 224 | } 225 | return "NIL" 226 | } 227 | 228 | func NameToLevel(name string) int { 229 | switch strings.ToUpper(name) { 230 | case "DEBUG": 231 | return LEVEL_DEBUG 232 | case "INFO": 233 | return LEVEL_INFO 234 | case "WARN": 235 | return LEVEL_WARN 236 | case "ERROR": 237 | return LEVEL_ERROR 238 | } 239 | return -1 240 | } 241 | 242 | func openLog(level int) os.File { 243 | date := time.Now().Format("2006-01-02") 244 | fileName := gConfig.Prefix + "_" + levelName(level) + "_" + date + ".log" 245 | f, e := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModePerm) 246 | if e != nil { 247 | panic(e) 248 | } 249 | return *f 250 | } 251 | 252 | func checkDate(config Config) { 253 | now := time.Now().Format("2006-01-02") 254 | nowt, _ := time.Parse("2006-01-02", now) 255 | nowunix := nowt.Unix() 256 | 257 | files, err := ioutil.ReadDir("./") 258 | if err != nil { 259 | Error("loggo checkDate ReadDir fail %v ", err) 260 | return 261 | } 262 | 263 | for _, f := range files { 264 | 265 | if f == nil || f.IsDir() { 266 | continue 267 | } 268 | 269 | if !strings.HasSuffix(f.Name(), ".log") { 270 | continue 271 | } 272 | 273 | strs := strings.Split(f.Name(), "_") 274 | if strs == nil || len(strs) < 3 { 275 | continue 276 | } 277 | 278 | if strs[0] != config.Prefix { 279 | continue 280 | } 281 | 282 | date := strs[2] 283 | date = strings.TrimRight(date, ".log") 284 | 285 | t, e := time.Parse("2006-01-02", date) 286 | if e != nil { 287 | Error("loggo delete Parse file fail %v %v %v", f.Name(), date, err) 288 | continue 289 | } 290 | tunix := t.Unix() 291 | if nowunix-tunix > int64(config.MaxDay)*24*3600 { 292 | err := os.Remove(f.Name()) 293 | if e != nil { 294 | Error("loggo delete log file fail %v %v", f.Name(), err) 295 | continue 296 | } 297 | } 298 | } 299 | } 300 | 301 | func crashLog() { 302 | if r := recover(); r != nil { 303 | var err error 304 | switch x := r.(type) { 305 | case string: 306 | err = errors.New(x) 307 | case error: 308 | err = x 309 | default: 310 | err = errors.New("Unknown panic") 311 | } 312 | if err != nil { 313 | Error("crash %s \n%s", err, dumpStacks()) 314 | } 315 | panic(err) 316 | } 317 | } 318 | 319 | func dumpStacks() string { 320 | buf := make([]byte, 16384) 321 | buf = buf[:runtime.Stack(buf, true)] 322 | return fmt.Sprintf("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) 323 | } 324 | 325 | func loopCheck(config Config) { 326 | defer crashLog() 327 | for { 328 | checkDate(config) 329 | time.Sleep(time.Hour) 330 | } 331 | } 332 | 333 | func IsDebug() bool { 334 | return gConfig.Level <= LEVEL_DEBUG 335 | } 336 | func IsInfo() bool { 337 | return gConfig.Level <= LEVEL_INFO 338 | } 339 | func IsWarn() bool { 340 | return gConfig.Level <= LEVEL_WARN 341 | } 342 | func IsError() bool { 343 | return gConfig.Level <= LEVEL_ERROR 344 | } 345 | -------------------------------------------------------------------------------- /frame/frame.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: frame.proto 3 | 4 | package frame 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | math "math" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the proto package it is being compiled against. 19 | // A compilation error at this line likely means your copy of the 20 | // proto package needs to be updated. 21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 22 | 23 | type FrameData_TYPE int32 24 | 25 | const ( 26 | FrameData_USER_DATA FrameData_TYPE = 0 27 | FrameData_CONN FrameData_TYPE = 1 28 | FrameData_CONNRSP FrameData_TYPE = 2 29 | FrameData_CLOSE FrameData_TYPE = 3 30 | FrameData_HB FrameData_TYPE = 4 31 | ) 32 | 33 | var FrameData_TYPE_name = map[int32]string{ 34 | 0: "USER_DATA", 35 | 1: "CONN", 36 | 2: "CONNRSP", 37 | 3: "CLOSE", 38 | 4: "HB", 39 | } 40 | 41 | var FrameData_TYPE_value = map[string]int32{ 42 | "USER_DATA": 0, 43 | "CONN": 1, 44 | "CONNRSP": 2, 45 | "CLOSE": 3, 46 | "HB": 4, 47 | } 48 | 49 | func (x FrameData_TYPE) String() string { 50 | return proto.EnumName(FrameData_TYPE_name, int32(x)) 51 | } 52 | 53 | func (FrameData_TYPE) EnumDescriptor() ([]byte, []int) { 54 | return fileDescriptor_5379e2b825e15002, []int{0, 0} 55 | } 56 | 57 | type Frame_TYPE int32 58 | 59 | const ( 60 | Frame_DATA Frame_TYPE = 0 61 | Frame_REQ Frame_TYPE = 1 62 | Frame_ACK Frame_TYPE = 2 63 | Frame_PING Frame_TYPE = 3 64 | Frame_PONG Frame_TYPE = 4 65 | ) 66 | 67 | var Frame_TYPE_name = map[int32]string{ 68 | 0: "DATA", 69 | 1: "REQ", 70 | 2: "ACK", 71 | 3: "PING", 72 | 4: "PONG", 73 | } 74 | 75 | var Frame_TYPE_value = map[string]int32{ 76 | "DATA": 0, 77 | "REQ": 1, 78 | "ACK": 2, 79 | "PING": 3, 80 | "PONG": 4, 81 | } 82 | 83 | func (x Frame_TYPE) String() string { 84 | return proto.EnumName(Frame_TYPE_name, int32(x)) 85 | } 86 | 87 | func (Frame_TYPE) EnumDescriptor() ([]byte, []int) { 88 | return fileDescriptor_5379e2b825e15002, []int{1, 0} 89 | } 90 | 91 | type FrameData struct { 92 | Type int32 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"` 93 | Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` 94 | Compress bool `protobuf:"varint,3,opt,name=compress,proto3" json:"compress,omitempty"` 95 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 96 | XXX_unrecognized []byte `json:"-"` 97 | XXX_sizecache int32 `json:"-"` 98 | } 99 | 100 | func (m *FrameData) Reset() { *m = FrameData{} } 101 | func (m *FrameData) String() string { return proto.CompactTextString(m) } 102 | func (*FrameData) ProtoMessage() {} 103 | func (*FrameData) Descriptor() ([]byte, []int) { 104 | return fileDescriptor_5379e2b825e15002, []int{0} 105 | } 106 | 107 | func (m *FrameData) XXX_Unmarshal(b []byte) error { 108 | return xxx_messageInfo_FrameData.Unmarshal(m, b) 109 | } 110 | func (m *FrameData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 111 | return xxx_messageInfo_FrameData.Marshal(b, m, deterministic) 112 | } 113 | func (m *FrameData) XXX_Merge(src proto.Message) { 114 | xxx_messageInfo_FrameData.Merge(m, src) 115 | } 116 | func (m *FrameData) XXX_Size() int { 117 | return xxx_messageInfo_FrameData.Size(m) 118 | } 119 | func (m *FrameData) XXX_DiscardUnknown() { 120 | xxx_messageInfo_FrameData.DiscardUnknown(m) 121 | } 122 | 123 | var xxx_messageInfo_FrameData proto.InternalMessageInfo 124 | 125 | func (m *FrameData) GetType() int32 { 126 | if m != nil { 127 | return m.Type 128 | } 129 | return 0 130 | } 131 | 132 | func (m *FrameData) GetData() []byte { 133 | if m != nil { 134 | return m.Data 135 | } 136 | return nil 137 | } 138 | 139 | func (m *FrameData) GetCompress() bool { 140 | if m != nil { 141 | return m.Compress 142 | } 143 | return false 144 | } 145 | 146 | type Frame struct { 147 | Type int32 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"` 148 | Resend bool `protobuf:"varint,2,opt,name=resend,proto3" json:"resend,omitempty"` 149 | Sendtime int64 `protobuf:"varint,3,opt,name=sendtime,proto3" json:"sendtime,omitempty"` 150 | Id int32 `protobuf:"varint,4,opt,name=id,proto3" json:"id,omitempty"` 151 | Data *FrameData `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` 152 | Dataid []int32 `protobuf:"varint,6,rep,packed,name=dataid,proto3" json:"dataid,omitempty"` 153 | Acked bool `protobuf:"varint,7,opt,name=acked,proto3" json:"acked,omitempty"` 154 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 155 | XXX_unrecognized []byte `json:"-"` 156 | XXX_sizecache int32 `json:"-"` 157 | } 158 | 159 | func (m *Frame) Reset() { *m = Frame{} } 160 | func (m *Frame) String() string { return proto.CompactTextString(m) } 161 | func (*Frame) ProtoMessage() {} 162 | func (*Frame) Descriptor() ([]byte, []int) { 163 | return fileDescriptor_5379e2b825e15002, []int{1} 164 | } 165 | 166 | func (m *Frame) XXX_Unmarshal(b []byte) error { 167 | return xxx_messageInfo_Frame.Unmarshal(m, b) 168 | } 169 | func (m *Frame) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 170 | return xxx_messageInfo_Frame.Marshal(b, m, deterministic) 171 | } 172 | func (m *Frame) XXX_Merge(src proto.Message) { 173 | xxx_messageInfo_Frame.Merge(m, src) 174 | } 175 | func (m *Frame) XXX_Size() int { 176 | return xxx_messageInfo_Frame.Size(m) 177 | } 178 | func (m *Frame) XXX_DiscardUnknown() { 179 | xxx_messageInfo_Frame.DiscardUnknown(m) 180 | } 181 | 182 | var xxx_messageInfo_Frame proto.InternalMessageInfo 183 | 184 | func (m *Frame) GetType() int32 { 185 | if m != nil { 186 | return m.Type 187 | } 188 | return 0 189 | } 190 | 191 | func (m *Frame) GetResend() bool { 192 | if m != nil { 193 | return m.Resend 194 | } 195 | return false 196 | } 197 | 198 | func (m *Frame) GetSendtime() int64 { 199 | if m != nil { 200 | return m.Sendtime 201 | } 202 | return 0 203 | } 204 | 205 | func (m *Frame) GetId() int32 { 206 | if m != nil { 207 | return m.Id 208 | } 209 | return 0 210 | } 211 | 212 | func (m *Frame) GetData() *FrameData { 213 | if m != nil { 214 | return m.Data 215 | } 216 | return nil 217 | } 218 | 219 | func (m *Frame) GetDataid() []int32 { 220 | if m != nil { 221 | return m.Dataid 222 | } 223 | return nil 224 | } 225 | 226 | func (m *Frame) GetAcked() bool { 227 | if m != nil { 228 | return m.Acked 229 | } 230 | return false 231 | } 232 | 233 | func init() { 234 | proto.RegisterEnum("FrameData_TYPE", FrameData_TYPE_name, FrameData_TYPE_value) 235 | proto.RegisterEnum("Frame_TYPE", Frame_TYPE_name, Frame_TYPE_value) 236 | proto.RegisterType((*FrameData)(nil), "FrameData") 237 | proto.RegisterType((*Frame)(nil), "Frame") 238 | } 239 | 240 | func init() { proto.RegisterFile("frame.proto", fileDescriptor_5379e2b825e15002) } 241 | 242 | var fileDescriptor_5379e2b825e15002 = []byte{ 243 | // 299 bytes of a gzipped FileDescriptorProto 244 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xcd, 0x4a, 0xf4, 0x30, 245 | 0x14, 0x86, 0xbf, 0xfc, 0xb5, 0x9d, 0x33, 0x9f, 0x12, 0x82, 0x0c, 0xc1, 0x85, 0x94, 0xae, 0xba, 246 | 0x9a, 0x85, 0x82, 0x5b, 0x99, 0x9f, 0x3a, 0x8a, 0xd2, 0x19, 0x33, 0xe3, 0x42, 0x37, 0x12, 0x27, 247 | 0x11, 0x8a, 0x8c, 0x2d, 0x6d, 0x37, 0xde, 0x85, 0x37, 0xe9, 0x7d, 0x48, 0x62, 0xe8, 0xca, 0xd5, 248 | 0x79, 0x9f, 0x16, 0x92, 0xe7, 0xcd, 0x81, 0xf1, 0x5b, 0xab, 0x0f, 0x76, 0xda, 0xb4, 0x75, 0x5f, 249 | 0x67, 0x5f, 0x08, 0x46, 0xd7, 0x8e, 0x97, 0xba, 0xd7, 0x42, 0x00, 0xed, 0x3f, 0x1b, 0x2b, 0x51, 250 | 0x8a, 0x72, 0xa6, 0x7c, 0x76, 0xdf, 0x8c, 0xee, 0xb5, 0xc4, 0x29, 0xca, 0xff, 0x2b, 0x9f, 0xc5, 251 | 0x29, 0x24, 0xfb, 0xfa, 0xd0, 0xb4, 0xb6, 0xeb, 0x24, 0x49, 0x51, 0x9e, 0xa8, 0x81, 0xb3, 0x2b, 252 | 0xa0, 0xbb, 0xa7, 0x4d, 0x21, 0x8e, 0x60, 0xf4, 0xb8, 0x2d, 0xd4, 0xcb, 0x72, 0xb6, 0x9b, 0xf1, 253 | 0x7f, 0x22, 0x01, 0xba, 0x58, 0x97, 0x25, 0x47, 0x62, 0x0c, 0xb1, 0x4b, 0x6a, 0xbb, 0xe1, 0x58, 254 | 0x8c, 0x80, 0x2d, 0xee, 0xd7, 0xdb, 0x82, 0x13, 0x11, 0x01, 0xbe, 0x99, 0x73, 0x9a, 0x7d, 0x23, 255 | 0x60, 0x5e, 0xe9, 0x4f, 0x9d, 0x09, 0x44, 0xad, 0xed, 0xec, 0x87, 0xf1, 0x42, 0x89, 0x0a, 0xe4, 256 | 0x94, 0xdc, 0xec, 0xab, 0x83, 0xf5, 0x4a, 0x44, 0x0d, 0x2c, 0x8e, 0x01, 0x57, 0x46, 0x52, 0x7f, 257 | 0x0a, 0xae, 0x8c, 0x38, 0x0b, 0x95, 0x58, 0x8a, 0xf2, 0xf1, 0x39, 0x4c, 0x87, 0x07, 0x08, 0xf5, 258 | 0x26, 0x10, 0xb9, 0x59, 0x19, 0x19, 0xa5, 0x24, 0x67, 0x2a, 0x90, 0x38, 0x01, 0xa6, 0xf7, 0xef, 259 | 0xd6, 0xc8, 0xd8, 0x5f, 0xfd, 0x0b, 0xd9, 0x65, 0x28, 0x9c, 0x00, 0x0d, 0x5d, 0x63, 0x20, 0xaa, 260 | 0x78, 0xe0, 0xc8, 0x85, 0xd9, 0xe2, 0x8e, 0x63, 0xf7, 0x6f, 0x73, 0x5b, 0xae, 0x38, 0xf1, 0x69, 261 | 0x5d, 0xae, 0x38, 0x9d, 0xc7, 0xcf, 0xcc, 0x6f, 0xe2, 0x35, 0xf2, 0xab, 0xb8, 0xf8, 0x09, 0x00, 262 | 0x00, 0xff, 0xff, 0xc6, 0x07, 0xe4, 0x02, 0x99, 0x01, 0x00, 0x00, 263 | } 264 | -------------------------------------------------------------------------------- /cmd/pingtunnel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | _ "net/http/pprof" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/cxjava/pingtunnel/common" 13 | "github.com/cxjava/pingtunnel/geoip" 14 | "github.com/cxjava/pingtunnel/loggo" 15 | "github.com/cxjava/pingtunnel/pingtunnel" 16 | ) 17 | 18 | var usage = ` 19 | 通过伪造ping,把tcp/udp/sock5流量通过远程服务器转发到目的服务器上。用于突破某些运营商封锁TCP/UDP流量。 20 | 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. 21 | 22 | Usage: 23 | 24 | // server 25 | pingtunnel -type server 26 | 27 | // client, Forward udp 28 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 29 | 30 | // client, Forward tcp 31 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 -tcp 1 32 | 33 | // client, Forward sock5, implicitly open tcp, so no target server is needed 34 | pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -sock5 1 35 | 36 | -type 服务器或者客户端 37 | client or server 38 | 39 | 服务器参数server param: 40 | 41 | -key 设置的密码,默认0 42 | Set password, default 0 43 | 44 | -nolog 不写日志文件,只打印标准输出,默认0 45 | Do not write log files, only print standard output, default 0 is off 46 | 47 | -noprint 不打印屏幕输出,默认0 48 | Do not print standard output, default 0 is off 49 | 50 | -loglevel 日志文件等级,默认info 51 | log level, default is info 52 | 53 | -maxconn 最大连接数,默认0,不受限制 54 | the max num of connections, default 0 is no limit 55 | 56 | -maxprt server最大处理线程数,默认100 57 | max process thread in server, default 100 58 | 59 | -maxprb server最大处理线程buffer数,默认1000 60 | max process thread's buffer in server, default 1000 61 | 62 | -conntt server发起连接到目标地址的超时时间,默认1000ms 63 | The timeout period for the server to initiate a connection to the destination address. The default is 1000ms. 64 | 65 | 客户端参数client param: 66 | 67 | -l 本地的地址,发到这个端口的流量将转发到服务器 68 | Local address, traffic sent to this port will be forwarded to the server 69 | 70 | -s 服务器的地址,流量将通过隧道转发到这个服务器 71 | The address of the server, the traffic will be forwarded to this server through the tunnel 72 | 73 | -t 远端服务器转发的目的地址,流量将转发到这个地址 74 | Destination address forwarded by the remote server, traffic will be forwarded to this address 75 | 76 | -timeout 本地记录连接超时的时间,单位是秒,默认60s 77 | The time when the local record connection timed out, in seconds, 60 seconds by default 78 | 79 | -key 设置的密码,默认0 80 | Set password, default 0 81 | 82 | -tcp 设置是否转发tcp,默认0 83 | Set the switch to forward tcp, the default is 0 84 | 85 | -tcp_bs tcp的发送接收缓冲区大小,默认1MB 86 | Tcp send and receive buffer size, default 1MB 87 | 88 | -tcp_mw tcp的最大窗口,默认20000 89 | The maximum window of tcp, the default is 20000 90 | 91 | -tcp_rst tcp的超时发送时间,默认400ms 92 | Tcp timeout resend time, default 400ms 93 | 94 | -tcp_gz 当数据包超过这个大小,tcp将压缩数据,0表示不压缩,默认0 95 | Tcp will compress data when the packet exceeds this size, 0 means no compression, default 0 96 | 97 | -tcp_stat 打印tcp的监控,默认0 98 | Print tcp connection statistic, default 0 is off 99 | 100 | -nolog 不写日志文件,只打印标准输出,默认0 101 | Do not write log files, only print standard output, default 0 is off 102 | 103 | -noprint 不打印屏幕输出,默认0 104 | Do not print standard output, default 0 is off 105 | 106 | -loglevel 日志文件等级,默认info 107 | log level, default is info 108 | 109 | -sock5 开启sock5转发,默认0 110 | Turn on sock5 forwarding, default 0 is off 111 | 112 | -profile 在指定端口开启性能检测,默认0不开启 113 | Enable performance detection on the specified port. The default 0 is not enabled. 114 | 115 | -s5filter sock5模式设置转发过滤,默认全转发,设置CN代表CN地区的直连不转发 116 | 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. 117 | 118 | -s5ftfile sock5模式转发过滤的数据文件,默认读取当前目录的GeoLite2-Country.mmdb 119 | The data file in sock5 filter mode, the default reading of the current directory GeoLite2-Country.mmdb 120 | ` 121 | 122 | func main() { 123 | 124 | defer common.CrashLog() 125 | 126 | t := flag.String("type", "", "client or server") 127 | listen := flag.String("l", "", "listen addr") 128 | target := flag.String("t", "", "target addr") 129 | server := flag.String("s", "", "server addr") 130 | timeout := flag.Int("timeout", 60, "conn timeout") 131 | key := flag.Int("key", 0, "key") 132 | tcpmode := flag.Int("tcp", 0, "tcp mode") 133 | tcpmode_buffersize := flag.Int("tcp_bs", 1*1024*1024, "tcp mode buffer size") 134 | tcpmode_maxwin := flag.Int("tcp_mw", 20000, "tcp mode max win") 135 | tcpmode_resend_timems := flag.Int("tcp_rst", 400, "tcp mode resend time ms") 136 | tcpmode_compress := flag.Int("tcp_gz", 0, "tcp data compress") 137 | nolog := flag.Int("nolog", 0, "write log file") 138 | noprint := flag.Int("noprint", 0, "print stdout") 139 | tcpmode_stat := flag.Int("tcp_stat", 0, "print tcp stat") 140 | loglevel := flag.String("loglevel", "info", "log level") 141 | open_sock5 := flag.Int("sock5", 0, "sock5 mode") 142 | maxconn := flag.Int("maxconn", 0, "max num of connections") 143 | max_process_thread := flag.Int("maxprt", 100, "max process thread in server") 144 | max_process_buffer := flag.Int("maxprb", 1000, "max process thread's buffer in server") 145 | profile := flag.Int("profile", 0, "open profile") 146 | conntt := flag.Int("conntt", 1000, "the connect call's timeout") 147 | s5filter := flag.String("s5filter", "", "sock5 filter") 148 | s5ftfile := flag.String("s5ftfile", "GeoLite2-Country.mmdb", "sock5 filter file") 149 | flag.Usage = func() { 150 | fmt.Printf(usage) 151 | } 152 | 153 | flag.Parse() 154 | 155 | if *t != "client" && *t != "server" { 156 | flag.Usage() 157 | return 158 | } 159 | if *t == "client" { 160 | if len(*listen) == 0 || len(*server) == 0 { 161 | flag.Usage() 162 | return 163 | } 164 | if *open_sock5 == 0 && len(*target) == 0 { 165 | flag.Usage() 166 | return 167 | } 168 | if *open_sock5 != 0 { 169 | *tcpmode = 1 170 | } 171 | } 172 | if *tcpmode_maxwin*10 > pingtunnel.FRAME_MAX_ID { 173 | fmt.Println("set tcp win to big, max = " + strconv.Itoa(pingtunnel.FRAME_MAX_ID/10)) 174 | return 175 | } 176 | 177 | level := loggo.LEVEL_INFO 178 | if loggo.NameToLevel(*loglevel) >= 0 { 179 | level = loggo.NameToLevel(*loglevel) 180 | } 181 | loggo.Ini(loggo.Config{ 182 | Level: level, 183 | Prefix: "pingtunnel", 184 | MaxDay: 3, 185 | NoLogFile: *nolog > 0, 186 | NoPrint: *noprint > 0, 187 | }) 188 | loggo.Info("start...") 189 | loggo.Info("key %d", *key) 190 | 191 | if *t == "server" { 192 | s, err := pingtunnel.NewServer(*key, *maxconn, *max_process_thread, *max_process_buffer, *conntt) 193 | if err != nil { 194 | loggo.Error("ERROR: %s", err.Error()) 195 | return 196 | } 197 | loggo.Info("Server start") 198 | err = s.Run() 199 | if err != nil { 200 | loggo.Error("Run ERROR: %s", err.Error()) 201 | return 202 | } 203 | } else if *t == "client" { 204 | 205 | loggo.Info("type %s", *t) 206 | loggo.Info("listen %s", *listen) 207 | loggo.Info("server %s", *server) 208 | loggo.Info("target %s", *target) 209 | 210 | if *tcpmode == 0 { 211 | *tcpmode_buffersize = 0 212 | *tcpmode_maxwin = 0 213 | *tcpmode_resend_timems = 0 214 | *tcpmode_compress = 0 215 | *tcpmode_stat = 0 216 | } 217 | 218 | if len(*s5filter) > 0 { 219 | err := geoip.Load(*s5ftfile) 220 | if err != nil { 221 | loggo.Error("Load Sock5 ip file ERROR: %s", err.Error()) 222 | return 223 | } 224 | } 225 | filter := func(addr string) bool { 226 | if len(*s5filter) <= 0 { 227 | return true 228 | } 229 | 230 | taddr, err := net.ResolveTCPAddr("tcp", addr) 231 | if err != nil { 232 | return false 233 | } 234 | 235 | ret, err := geoip.GetCountryIsoCode(taddr.IP.String()) 236 | if err != nil { 237 | return false 238 | } 239 | if len(ret) <= 0 { 240 | return false 241 | } 242 | return ret != *s5filter 243 | } 244 | 245 | c, err := pingtunnel.NewClient(*listen, *server, *target, *timeout, *key, 246 | *tcpmode, *tcpmode_buffersize, *tcpmode_maxwin, *tcpmode_resend_timems, *tcpmode_compress, 247 | *tcpmode_stat, *open_sock5, *maxconn, &filter) 248 | if err != nil { 249 | loggo.Error("ERROR: %s", err.Error()) 250 | return 251 | } 252 | loggo.Info("Client Listen %s (%s) Server %s (%s) TargetPort %s:", c.Addr(), c.IPAddr(), 253 | c.ServerAddr(), c.ServerIPAddr(), c.TargetAddr()) 254 | err = c.Run() 255 | if err != nil { 256 | loggo.Error("Run ERROR: %s", err.Error()) 257 | return 258 | } 259 | } else { 260 | return 261 | } 262 | 263 | if *profile > 0 { 264 | go http.ListenAndServe("0.0.0.0:"+strconv.Itoa(*profile), nil) 265 | } 266 | 267 | for { 268 | time.Sleep(time.Hour) 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # Code generated by godownloader on 2023-02-10T02:47:51Z. DO NOT EDIT. 4 | # 5 | 6 | usage() { 7 | this=$1 8 | cat </dev/null 146 | } 147 | echoerr() { 148 | echo "$@" 1>&2 149 | } 150 | log_prefix() { 151 | echo "$0" 152 | } 153 | _logp=6 154 | log_set_priority() { 155 | _logp="$1" 156 | } 157 | log_priority() { 158 | if test -z "$1"; then 159 | echo "$_logp" 160 | return 161 | fi 162 | [ "$1" -le "$_logp" ] 163 | } 164 | log_tag() { 165 | case $1 in 166 | 0) echo "emerg" ;; 167 | 1) echo "alert" ;; 168 | 2) echo "crit" ;; 169 | 3) echo "err" ;; 170 | 4) echo "warning" ;; 171 | 5) echo "notice" ;; 172 | 6) echo "info" ;; 173 | 7) echo "debug" ;; 174 | *) echo "$1" ;; 175 | esac 176 | } 177 | log_debug() { 178 | log_priority 7 || return 0 179 | echoerr "$(log_prefix)" "$(log_tag 7)" "$@" 180 | } 181 | log_info() { 182 | log_priority 6 || return 0 183 | echoerr "$(log_prefix)" "$(log_tag 6)" "$@" 184 | } 185 | log_err() { 186 | log_priority 3 || return 0 187 | echoerr "$(log_prefix)" "$(log_tag 3)" "$@" 188 | } 189 | log_crit() { 190 | log_priority 2 || return 0 191 | echoerr "$(log_prefix)" "$(log_tag 2)" "$@" 192 | } 193 | uname_os() { 194 | os=$(uname -s | tr '[:upper:]' '[:lower:]') 195 | case "$os" in 196 | cygwin_nt*) os="windows" ;; 197 | mingw*) os="windows" ;; 198 | msys_nt*) os="windows" ;; 199 | esac 200 | echo "$os" 201 | } 202 | uname_arch() { 203 | arch=$(uname -m) 204 | case $arch in 205 | x86_64) arch="amd64" ;; 206 | x86) arch="386" ;; 207 | i686) arch="386" ;; 208 | i386) arch="386" ;; 209 | aarch64) arch="arm64" ;; 210 | armv5*) arch="armv5" ;; 211 | armv6*) arch="armv6" ;; 212 | armv7*) arch="armv7" ;; 213 | esac 214 | echo ${arch} 215 | } 216 | uname_os_check() { 217 | os=$(uname_os) 218 | case "$os" in 219 | darwin) return 0 ;; 220 | dragonfly) return 0 ;; 221 | freebsd) return 0 ;; 222 | linux) return 0 ;; 223 | android) return 0 ;; 224 | nacl) return 0 ;; 225 | netbsd) return 0 ;; 226 | openbsd) return 0 ;; 227 | plan9) return 0 ;; 228 | solaris) return 0 ;; 229 | windows) return 0 ;; 230 | esac 231 | log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" 232 | return 1 233 | } 234 | uname_arch_check() { 235 | arch=$(uname_arch) 236 | case "$arch" in 237 | 386) return 0 ;; 238 | amd64) return 0 ;; 239 | arm64) return 0 ;; 240 | armv5) return 0 ;; 241 | armv6) return 0 ;; 242 | armv7) return 0 ;; 243 | ppc64) return 0 ;; 244 | ppc64le) return 0 ;; 245 | mips) return 0 ;; 246 | mipsle) return 0 ;; 247 | mips64) return 0 ;; 248 | mips64le) return 0 ;; 249 | s390x) return 0 ;; 250 | amd64p32) return 0 ;; 251 | esac 252 | log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" 253 | return 1 254 | } 255 | untar() { 256 | tarball=$1 257 | case "${tarball}" in 258 | *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;; 259 | *.tar) tar --no-same-owner -xf "${tarball}" ;; 260 | *.zip) unzip "${tarball}" ;; 261 | *) 262 | log_err "untar unknown archive format for ${tarball}" 263 | return 1 264 | ;; 265 | esac 266 | } 267 | http_download_curl() { 268 | local_file=$1 269 | source_url=$2 270 | header=$3 271 | if [ -z "$header" ]; then 272 | code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") 273 | else 274 | code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") 275 | fi 276 | if [ "$code" != "200" ]; then 277 | log_debug "http_download_curl received HTTP status $code" 278 | return 1 279 | fi 280 | return 0 281 | } 282 | http_download_wget() { 283 | local_file=$1 284 | source_url=$2 285 | header=$3 286 | if [ -z "$header" ]; then 287 | wget -q -O "$local_file" "$source_url" 288 | else 289 | wget -q --header "$header" -O "$local_file" "$source_url" 290 | fi 291 | } 292 | http_download() { 293 | log_debug "http_download $2" 294 | if is_command curl; then 295 | http_download_curl "$@" 296 | return 297 | elif is_command wget; then 298 | http_download_wget "$@" 299 | return 300 | fi 301 | log_crit "http_download unable to find wget or curl" 302 | return 1 303 | } 304 | http_copy() { 305 | tmp=$(mktemp) 306 | http_download "${tmp}" "$1" "$2" || return 1 307 | body=$(cat "$tmp") 308 | rm -f "${tmp}" 309 | echo "$body" 310 | } 311 | github_release() { 312 | owner_repo=$1 313 | version=$2 314 | test -z "$version" && version="latest" 315 | giturl="https://github.com/${owner_repo}/releases/${version}" 316 | json=$(http_copy "$giturl" "Accept:application/json") 317 | test -z "$json" && return 1 318 | version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') 319 | test -z "$version" && return 1 320 | echo "$version" 321 | } 322 | hash_sha256() { 323 | TARGET=${1:-/dev/stdin} 324 | if is_command gsha256sum; then 325 | hash=$(gsha256sum "$TARGET") || return 1 326 | echo "$hash" | cut -d ' ' -f 1 327 | elif is_command sha256sum; then 328 | hash=$(sha256sum "$TARGET") || return 1 329 | echo "$hash" | cut -d ' ' -f 1 330 | elif is_command shasum; then 331 | hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 332 | echo "$hash" | cut -d ' ' -f 1 333 | elif is_command openssl; then 334 | hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 335 | echo "$hash" | cut -d ' ' -f a 336 | else 337 | log_crit "hash_sha256 unable to find command to compute sha-256 hash" 338 | return 1 339 | fi 340 | } 341 | hash_sha256_verify() { 342 | TARGET=$1 343 | checksums=$2 344 | if [ -z "$checksums" ]; then 345 | log_err "hash_sha256_verify checksum file not specified in arg2" 346 | return 1 347 | fi 348 | BASENAME=${TARGET##*/} 349 | want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) 350 | if [ -z "$want" ]; then 351 | log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" 352 | return 1 353 | fi 354 | got=$(hash_sha256 "$TARGET") 355 | if [ "$want" != "$got" ]; then 356 | log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" 357 | return 1 358 | fi 359 | } 360 | cat /dev/null < 0 { 26 | s.processtp = threadpool.NewThreadPool(maxprocessthread, maxprocessbuffer, func(v interface{}) { 27 | packet := v.(*Packet) 28 | s.processDataPacket(packet) 29 | }) 30 | } 31 | 32 | return s, nil 33 | } 34 | 35 | type Server struct { 36 | exit bool 37 | key int 38 | workResultLock sync.WaitGroup 39 | maxconn int 40 | maxprocessthread int 41 | maxprocessbuffer int 42 | connecttmeout int 43 | 44 | conn *icmp.PacketConn 45 | 46 | localConnMap sync.Map 47 | connErrorMap sync.Map 48 | 49 | sendPacket uint64 50 | recvPacket uint64 51 | sendPacketSize uint64 52 | recvPacketSize uint64 53 | localConnMapSize int 54 | 55 | processtp *threadpool.ThreadPool 56 | recvcontrol chan int 57 | } 58 | 59 | type ServerConn struct { 60 | exit bool 61 | timeout int 62 | ipaddrTarget *net.UDPAddr 63 | conn *net.UDPConn 64 | tcpaddrTarget *net.TCPAddr 65 | tcpconn *net.TCPConn 66 | id string 67 | activeRecvTime time.Time 68 | activeSendTime time.Time 69 | close bool 70 | rproto int 71 | fm *frame.FrameMgr 72 | tcpmode int 73 | echoId int 74 | echoSeq int 75 | } 76 | 77 | func (p *Server) Run() error { 78 | 79 | conn, err := icmp.ListenPacket("ip4:icmp", "") 80 | if err != nil { 81 | loggo.Error("Error listening for ICMP packets: %s", err.Error()) 82 | return err 83 | } 84 | p.conn = conn 85 | 86 | recv := make(chan *Packet, 10000) 87 | p.recvcontrol = make(chan int, 1) 88 | go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv) 89 | 90 | go func() { 91 | defer common.CrashLog() 92 | 93 | p.workResultLock.Add(1) 94 | defer p.workResultLock.Done() 95 | 96 | for !p.exit { 97 | p.checkTimeoutConn() 98 | p.showNet() 99 | p.updateConnError() 100 | time.Sleep(time.Second) 101 | } 102 | }() 103 | 104 | go func() { 105 | defer common.CrashLog() 106 | 107 | p.workResultLock.Add(1) 108 | defer p.workResultLock.Done() 109 | 110 | for !p.exit { 111 | select { 112 | case <-p.recvcontrol: 113 | return 114 | case r := <-recv: 115 | p.processPacket(r) 116 | } 117 | } 118 | }() 119 | 120 | return nil 121 | } 122 | 123 | func (p *Server) Stop() { 124 | p.exit = true 125 | p.recvcontrol <- 1 126 | p.workResultLock.Wait() 127 | p.processtp.Stop() 128 | p.conn.Close() 129 | } 130 | 131 | func (p *Server) processPacket(packet *Packet) { 132 | 133 | if packet.my.Key != (int32)(p.key) { 134 | return 135 | } 136 | 137 | if packet.my.Type == (int32)(MyMsg_PING) { 138 | t := time.Time{} 139 | t.UnmarshalBinary(packet.my.Data) 140 | loggo.Info("ping from %s %s %d %d %d", packet.src.String(), t.String(), packet.my.Rproto, packet.echoId, packet.echoSeq) 141 | sendICMP(packet.echoId, packet.echoSeq, *p.conn, packet.src, "", "", (uint32)(MyMsg_PING), packet.my.Data, 142 | (int)(packet.my.Rproto), -1, p.key, 143 | 0, 0, 0, 0, 0, 0, 144 | 0) 145 | return 146 | } 147 | 148 | if packet.my.Type == (int32)(MyMsg_KICK) { 149 | localConn := p.getServerConnById(packet.my.Id) 150 | if localConn != nil { 151 | p.close(localConn) 152 | loggo.Info("remote kick local %s", packet.my.Id) 153 | } 154 | return 155 | } 156 | 157 | if p.maxprocessthread > 0 { 158 | p.processtp.AddJob((int)(common.HashString(packet.my.Id)), packet) 159 | } else { 160 | p.processDataPacket(packet) 161 | } 162 | } 163 | 164 | func (p *Server) processDataPacketNewConn(id string, packet *Packet) *ServerConn { 165 | 166 | now := common.GetNowUpdateInSecond() 167 | 168 | loggo.Info("start add new connect %s %s", id, packet.my.Target) 169 | 170 | if p.maxconn > 0 && p.localConnMapSize >= p.maxconn { 171 | loggo.Info("too many connections %d, server connected target fail %s", p.localConnMapSize, packet.my.Target) 172 | p.remoteError(packet.echoId, packet.echoSeq, id, (int)(packet.my.Rproto), packet.src) 173 | return nil 174 | } 175 | 176 | addr := packet.my.Target 177 | if p.isConnError(addr) { 178 | loggo.Info("addr connect Error before: %s %s", id, addr) 179 | p.remoteError(packet.echoId, packet.echoSeq, id, (int)(packet.my.Rproto), packet.src) 180 | return nil 181 | } 182 | 183 | if packet.my.Tcpmode > 0 { 184 | 185 | c, err := net.DialTimeout("tcp", addr, time.Millisecond*time.Duration(p.connecttmeout)) 186 | if err != nil { 187 | loggo.Error("Error listening for tcp packets: %s %s", id, err.Error()) 188 | p.remoteError(packet.echoId, packet.echoSeq, id, (int)(packet.my.Rproto), packet.src) 189 | p.addConnError(addr) 190 | return nil 191 | } 192 | targetConn := c.(*net.TCPConn) 193 | ipaddrTarget := targetConn.RemoteAddr().(*net.TCPAddr) 194 | 195 | 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), 196 | (int)(packet.my.TcpmodeStat)) 197 | 198 | localConn := &ServerConn{exit: false, timeout: (int)(packet.my.Timeout), tcpconn: targetConn, tcpaddrTarget: ipaddrTarget, id: id, activeRecvTime: now, activeSendTime: now, close: false, 199 | rproto: (int)(packet.my.Rproto), fm: fm, tcpmode: (int)(packet.my.Tcpmode)} 200 | 201 | p.addServerConn(id, localConn) 202 | 203 | go p.RecvTCP(localConn, id, packet.src) 204 | return localConn 205 | 206 | } else { 207 | 208 | c, err := net.DialTimeout("udp", addr, time.Millisecond*time.Duration(p.connecttmeout)) 209 | if err != nil { 210 | loggo.Error("Error listening for udp packets: %s %s", id, err.Error()) 211 | p.remoteError(packet.echoId, packet.echoSeq, id, (int)(packet.my.Rproto), packet.src) 212 | p.addConnError(addr) 213 | return nil 214 | } 215 | targetConn := c.(*net.UDPConn) 216 | ipaddrTarget := targetConn.RemoteAddr().(*net.UDPAddr) 217 | 218 | localConn := &ServerConn{exit: false, timeout: (int)(packet.my.Timeout), conn: targetConn, ipaddrTarget: ipaddrTarget, id: id, activeRecvTime: now, activeSendTime: now, close: false, 219 | rproto: (int)(packet.my.Rproto), tcpmode: (int)(packet.my.Tcpmode)} 220 | 221 | p.addServerConn(id, localConn) 222 | 223 | go p.Recv(localConn, id, packet.src) 224 | 225 | return localConn 226 | } 227 | 228 | return nil 229 | } 230 | 231 | func (p *Server) processDataPacket(packet *Packet) { 232 | 233 | loggo.Debug("processPacket %s %s %d", packet.my.Id, packet.src.String(), len(packet.my.Data)) 234 | 235 | now := common.GetNowUpdateInSecond() 236 | 237 | id := packet.my.Id 238 | localConn := p.getServerConnById(id) 239 | if localConn == nil { 240 | localConn = p.processDataPacketNewConn(id, packet) 241 | if localConn == nil { 242 | return 243 | } 244 | } 245 | 246 | localConn.activeRecvTime = now 247 | localConn.echoId = packet.echoId 248 | localConn.echoSeq = packet.echoSeq 249 | 250 | if packet.my.Type == (int32)(MyMsg_DATA) { 251 | 252 | if packet.my.Tcpmode > 0 { 253 | f := &frame.Frame{} 254 | err := proto.Unmarshal(packet.my.Data, f) 255 | if err != nil { 256 | loggo.Error("Unmarshal tcp Error %s", err) 257 | return 258 | } 259 | 260 | localConn.fm.OnRecvFrame(f) 261 | 262 | } else { 263 | if packet.my.Data == nil { 264 | return 265 | } 266 | _, err := localConn.conn.Write(packet.my.Data) 267 | if err != nil { 268 | loggo.Info("WriteToUDP Error %s", err) 269 | localConn.close = true 270 | return 271 | } 272 | } 273 | 274 | p.recvPacket++ 275 | p.recvPacketSize += (uint64)(len(packet.my.Data)) 276 | } 277 | } 278 | 279 | func (p *Server) RecvTCP(conn *ServerConn, id string, src *net.IPAddr) { 280 | 281 | defer common.CrashLog() 282 | 283 | p.workResultLock.Add(1) 284 | defer p.workResultLock.Done() 285 | 286 | loggo.Info("server waiting target response %s -> %s %s", conn.tcpaddrTarget.String(), conn.id, conn.tcpconn.LocalAddr().String()) 287 | 288 | loggo.Info("start wait remote connect tcp %s %s", conn.id, conn.tcpaddrTarget.String()) 289 | startConnectTime := common.GetNowUpdateInSecond() 290 | for !p.exit && !conn.exit { 291 | if conn.fm.IsConnected() { 292 | break 293 | } 294 | conn.fm.Update() 295 | sendlist := conn.fm.GetSendList() 296 | for e := sendlist.Front(); e != nil; e = e.Next() { 297 | f := e.Value.(*frame.Frame) 298 | mb, _ := conn.fm.MarshalFrame(f) 299 | sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb, 300 | conn.rproto, -1, p.key, 301 | 0, 0, 0, 0, 0, 0, 302 | 0) 303 | p.sendPacket++ 304 | p.sendPacketSize += (uint64)(len(mb)) 305 | } 306 | time.Sleep(time.Millisecond * 10) 307 | now := common.GetNowUpdateInSecond() 308 | diffclose := now.Sub(startConnectTime) 309 | if diffclose > time.Second*5 { 310 | loggo.Info("can not connect remote tcp %s %s", conn.id, conn.tcpaddrTarget.String()) 311 | p.close(conn) 312 | p.remoteError(conn.echoId, conn.echoSeq, id, conn.rproto, src) 313 | return 314 | } 315 | } 316 | 317 | if !conn.exit { 318 | loggo.Info("remote connected tcp %s %s", conn.id, conn.tcpaddrTarget.String()) 319 | } 320 | 321 | bytes := make([]byte, 10240) 322 | 323 | tcpActiveRecvTime := common.GetNowUpdateInSecond() 324 | tcpActiveSendTime := common.GetNowUpdateInSecond() 325 | 326 | for !p.exit && !conn.exit { 327 | now := common.GetNowUpdateInSecond() 328 | sleep := true 329 | 330 | left := common.MinOfInt(conn.fm.GetSendBufferLeft(), len(bytes)) 331 | if left > 0 { 332 | conn.tcpconn.SetReadDeadline(time.Now().Add(time.Millisecond * 1)) 333 | n, err := conn.tcpconn.Read(bytes[0:left]) 334 | if err != nil { 335 | nerr, ok := err.(net.Error) 336 | if !ok || !nerr.Timeout() { 337 | loggo.Info("Error read tcp %s %s %s", conn.id, conn.tcpaddrTarget.String(), err) 338 | conn.fm.Close() 339 | break 340 | } 341 | } 342 | if n > 0 { 343 | sleep = false 344 | conn.fm.WriteSendBuffer(bytes[:n]) 345 | tcpActiveRecvTime = now 346 | } 347 | } 348 | 349 | conn.fm.Update() 350 | 351 | sendlist := conn.fm.GetSendList() 352 | if sendlist.Len() > 0 { 353 | sleep = false 354 | conn.activeSendTime = now 355 | for e := sendlist.Front(); e != nil; e = e.Next() { 356 | f := e.Value.(*frame.Frame) 357 | mb, err := conn.fm.MarshalFrame(f) 358 | if err != nil { 359 | loggo.Error("Error tcp Marshal %s %s %s", conn.id, conn.tcpaddrTarget.String(), err) 360 | continue 361 | } 362 | sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb, 363 | conn.rproto, -1, p.key, 364 | 0, 0, 0, 0, 0, 0, 365 | 0) 366 | p.sendPacket++ 367 | p.sendPacketSize += (uint64)(len(mb)) 368 | } 369 | } 370 | 371 | if conn.fm.GetRecvBufferSize() > 0 { 372 | sleep = false 373 | rr := conn.fm.GetRecvReadLineBuffer() 374 | conn.tcpconn.SetWriteDeadline(time.Now().Add(time.Millisecond * 1)) 375 | n, err := conn.tcpconn.Write(rr) 376 | if err != nil { 377 | nerr, ok := err.(net.Error) 378 | if !ok || !nerr.Timeout() { 379 | loggo.Info("Error write tcp %s %s %s", conn.id, conn.tcpaddrTarget.String(), err) 380 | conn.fm.Close() 381 | break 382 | } 383 | } 384 | if n > 0 { 385 | conn.fm.SkipRecvBuffer(n) 386 | tcpActiveSendTime = now 387 | } 388 | } 389 | 390 | if sleep { 391 | time.Sleep(time.Millisecond * 10) 392 | } 393 | 394 | diffrecv := now.Sub(conn.activeRecvTime) 395 | diffsend := now.Sub(conn.activeSendTime) 396 | tcpdiffrecv := now.Sub(tcpActiveRecvTime) 397 | tcpdiffsend := now.Sub(tcpActiveSendTime) 398 | if diffrecv > time.Second*(time.Duration(conn.timeout)) || diffsend > time.Second*(time.Duration(conn.timeout)) || 399 | (tcpdiffrecv > time.Second*(time.Duration(conn.timeout)) && tcpdiffsend > time.Second*(time.Duration(conn.timeout))) { 400 | loggo.Info("close inactive conn %s %s", conn.id, conn.tcpaddrTarget.String()) 401 | conn.fm.Close() 402 | break 403 | } 404 | 405 | if conn.fm.IsRemoteClosed() { 406 | loggo.Info("closed by remote conn %s %s", conn.id, conn.tcpaddrTarget.String()) 407 | conn.fm.Close() 408 | break 409 | } 410 | } 411 | 412 | conn.fm.Close() 413 | 414 | startCloseTime := common.GetNowUpdateInSecond() 415 | for !p.exit && !conn.exit { 416 | now := common.GetNowUpdateInSecond() 417 | 418 | conn.fm.Update() 419 | 420 | sendlist := conn.fm.GetSendList() 421 | for e := sendlist.Front(); e != nil; e = e.Next() { 422 | f := e.Value.(*frame.Frame) 423 | mb, _ := conn.fm.MarshalFrame(f) 424 | sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), mb, 425 | conn.rproto, -1, p.key, 426 | 0, 0, 0, 0, 0, 0, 427 | 0) 428 | p.sendPacket++ 429 | p.sendPacketSize += (uint64)(len(mb)) 430 | } 431 | 432 | nodatarecv := true 433 | if conn.fm.GetRecvBufferSize() > 0 { 434 | rr := conn.fm.GetRecvReadLineBuffer() 435 | conn.tcpconn.SetWriteDeadline(time.Now().Add(time.Millisecond * 100)) 436 | n, _ := conn.tcpconn.Write(rr) 437 | if n > 0 { 438 | conn.fm.SkipRecvBuffer(n) 439 | nodatarecv = false 440 | } 441 | } 442 | 443 | diffclose := now.Sub(startCloseTime) 444 | if diffclose > time.Second*5 { 445 | loggo.Info("close conn had timeout %s %s", conn.id, conn.tcpaddrTarget.String()) 446 | break 447 | } 448 | 449 | remoteclosed := conn.fm.IsRemoteClosed() 450 | if remoteclosed && nodatarecv { 451 | loggo.Info("remote conn had closed %s %s", conn.id, conn.tcpaddrTarget.String()) 452 | break 453 | } 454 | 455 | time.Sleep(time.Millisecond * 100) 456 | } 457 | 458 | time.Sleep(time.Second) 459 | 460 | loggo.Info("close tcp conn %s %s", conn.id, conn.tcpaddrTarget.String()) 461 | p.close(conn) 462 | } 463 | 464 | func (p *Server) Recv(conn *ServerConn, id string, src *net.IPAddr) { 465 | 466 | defer common.CrashLog() 467 | 468 | p.workResultLock.Add(1) 469 | defer p.workResultLock.Done() 470 | 471 | loggo.Info("server waiting target response %s -> %s %s", conn.ipaddrTarget.String(), conn.id, conn.conn.LocalAddr().String()) 472 | 473 | bytes := make([]byte, 2000) 474 | 475 | for !p.exit { 476 | 477 | conn.conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) 478 | n, _, err := conn.conn.ReadFromUDP(bytes) 479 | if err != nil { 480 | nerr, ok := err.(net.Error) 481 | if !ok || !nerr.Timeout() { 482 | loggo.Info("ReadFromUDP Error read udp %s", err) 483 | conn.close = true 484 | return 485 | } 486 | } 487 | 488 | now := common.GetNowUpdateInSecond() 489 | conn.activeSendTime = now 490 | 491 | sendICMP(conn.echoId, conn.echoSeq, *p.conn, src, "", id, (uint32)(MyMsg_DATA), bytes[:n], 492 | conn.rproto, -1, p.key, 493 | 0, 0, 0, 0, 0, 0, 494 | 0) 495 | 496 | p.sendPacket++ 497 | p.sendPacketSize += (uint64)(n) 498 | } 499 | } 500 | 501 | func (p *Server) close(conn *ServerConn) { 502 | if p.getServerConnById(conn.id) != nil { 503 | conn.exit = true 504 | if conn.conn != nil { 505 | conn.conn.Close() 506 | } 507 | if conn.tcpconn != nil { 508 | conn.tcpconn.Close() 509 | } 510 | p.deleteServerConn(conn.id) 511 | } 512 | } 513 | 514 | func (p *Server) checkTimeoutConn() { 515 | 516 | tmp := make(map[string]*ServerConn) 517 | p.localConnMap.Range(func(key, value interface{}) bool { 518 | id := key.(string) 519 | serverConn := value.(*ServerConn) 520 | tmp[id] = serverConn 521 | return true 522 | }) 523 | 524 | now := common.GetNowUpdateInSecond() 525 | for _, conn := range tmp { 526 | if conn.tcpmode > 0 { 527 | continue 528 | } 529 | diffrecv := now.Sub(conn.activeRecvTime) 530 | diffsend := now.Sub(conn.activeSendTime) 531 | if diffrecv > time.Second*(time.Duration(conn.timeout)) || diffsend > time.Second*(time.Duration(conn.timeout)) { 532 | conn.close = true 533 | } 534 | } 535 | 536 | for id, conn := range tmp { 537 | if conn.tcpmode > 0 { 538 | continue 539 | } 540 | if conn.close { 541 | loggo.Info("close inactive conn %s %s", id, conn.ipaddrTarget.String()) 542 | p.close(conn) 543 | } 544 | } 545 | } 546 | 547 | func (p *Server) showNet() { 548 | p.localConnMapSize = 0 549 | p.localConnMap.Range(func(key, value interface{}) bool { 550 | p.localConnMapSize++ 551 | return true 552 | }) 553 | loggo.Info("send %dPacket/s %dKB/s recv %dPacket/s %dKB/s %dConnections", 554 | p.sendPacket, p.sendPacketSize/1024, p.recvPacket, p.recvPacketSize/1024, p.localConnMapSize) 555 | p.sendPacket = 0 556 | p.recvPacket = 0 557 | p.sendPacketSize = 0 558 | p.recvPacketSize = 0 559 | } 560 | 561 | func (p *Server) addServerConn(uuid string, serverConn *ServerConn) { 562 | p.localConnMap.Store(uuid, serverConn) 563 | } 564 | 565 | func (p *Server) getServerConnById(uuid string) *ServerConn { 566 | ret, ok := p.localConnMap.Load(uuid) 567 | if !ok { 568 | return nil 569 | } 570 | return ret.(*ServerConn) 571 | } 572 | 573 | func (p *Server) deleteServerConn(uuid string) { 574 | p.localConnMap.Delete(uuid) 575 | } 576 | 577 | func (p *Server) remoteError(echoId int, echoSeq int, uuid string, rprpto int, src *net.IPAddr) { 578 | sendICMP(echoId, echoSeq, *p.conn, src, "", uuid, (uint32)(MyMsg_KICK), []byte{}, 579 | rprpto, -1, p.key, 580 | 0, 0, 0, 0, 0, 0, 581 | 0) 582 | } 583 | 584 | func (p *Server) addConnError(addr string) { 585 | _, ok := p.connErrorMap.Load(addr) 586 | if !ok { 587 | now := common.GetNowUpdateInSecond() 588 | p.connErrorMap.Store(addr, now) 589 | } 590 | } 591 | 592 | func (p *Server) isConnError(addr string) bool { 593 | _, ok := p.connErrorMap.Load(addr) 594 | return ok 595 | } 596 | 597 | func (p *Server) updateConnError() { 598 | 599 | tmp := make(map[string]time.Time) 600 | p.connErrorMap.Range(func(key, value interface{}) bool { 601 | id := key.(string) 602 | t := value.(time.Time) 603 | tmp[id] = t 604 | return true 605 | }) 606 | 607 | now := common.GetNowUpdateInSecond() 608 | for id, t := range tmp { 609 | diff := now.Sub(t) 610 | if diff > time.Second*5 { 611 | p.connErrorMap.Delete(id) 612 | } 613 | } 614 | } 615 | -------------------------------------------------------------------------------- /frame/framemgr.go: -------------------------------------------------------------------------------- 1 | package frame 2 | 3 | import ( 4 | "container/list" 5 | "github.com/cxjava/pingtunnel/common" 6 | "github.com/cxjava/pingtunnel/loggo" 7 | "github.com/cxjava/pingtunnel/rbuffergo" 8 | "github.com/golang/protobuf/proto" 9 | "strconv" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | type FrameStat struct { 15 | sendDataNum int 16 | recvDataNum int 17 | sendReqNum int 18 | recvReqNum int 19 | sendAckNum int 20 | recvAckNum int 21 | sendDataNumsMap map[int32]int 22 | recvDataNumsMap map[int32]int 23 | sendReqNumsMap map[int32]int 24 | recvReqNumsMap map[int32]int 25 | sendAckNumsMap map[int32]int 26 | recvAckNumsMap map[int32]int 27 | sendping int 28 | sendpong int 29 | recvping int 30 | recvpong int 31 | recvOldNum int 32 | recvOutWinNum int 33 | } 34 | 35 | type FrameMgr struct { 36 | frame_max_size int 37 | frame_max_id int32 38 | sendb *rbuffergo.RBuffergo 39 | recvb *rbuffergo.RBuffergo 40 | 41 | sendblock sync.Locker 42 | recvblock sync.Locker 43 | 44 | recvlock sync.Locker 45 | windowsize int32 46 | resend_timems int 47 | compress int 48 | 49 | sendwin *rbuffergo.ROBuffergo 50 | sendlist *list.List 51 | sendid int32 52 | 53 | recvwin *rbuffergo.ROBuffergo 54 | recvlist *list.List 55 | recvid int32 56 | 57 | close bool 58 | remoteclosed bool 59 | closesend bool 60 | 61 | lastPingTime int64 62 | lastPongTime int64 63 | rttns int64 64 | 65 | lastSendHBTime int64 66 | lastRecvHBTime int64 67 | 68 | reqmap map[int32]int64 69 | 70 | connected bool 71 | 72 | fs *FrameStat 73 | openstat int 74 | lastPrintStat int64 75 | 76 | debugid string 77 | } 78 | 79 | func (fm *FrameMgr) SetDebugid(debugid string) { 80 | fm.debugid = debugid 81 | } 82 | 83 | func NewFrameMgr(frame_max_size int, frame_max_id int, buffersize int, windowsize int, resend_timems int, compress int, openstat int) *FrameMgr { 84 | 85 | sendb := rbuffergo.New(buffersize, false) 86 | recvb := rbuffergo.New(buffersize, false) 87 | 88 | fm := &FrameMgr{frame_max_size: frame_max_size, frame_max_id: int32(frame_max_id), 89 | sendb: sendb, recvb: recvb, 90 | sendblock: &sync.Mutex{}, recvblock: &sync.Mutex{}, 91 | recvlock: &sync.Mutex{}, 92 | windowsize: int32(windowsize), resend_timems: resend_timems, compress: compress, 93 | sendwin: rbuffergo.NewROBuffer(windowsize, 0, frame_max_id), 94 | sendlist: list.New(), sendid: 0, 95 | recvwin: rbuffergo.NewROBuffer(windowsize, 0, frame_max_id), 96 | recvlist: list.New(), recvid: 0, 97 | close: false, remoteclosed: false, closesend: false, 98 | lastPingTime: time.Now().UnixNano(), lastPongTime: time.Now().UnixNano(), 99 | lastSendHBTime: time.Now().UnixNano(), lastRecvHBTime: time.Now().UnixNano(), 100 | rttns: (int64)(resend_timems * 1000), 101 | reqmap: make(map[int32]int64), 102 | connected: false, openstat: openstat, lastPrintStat: time.Now().UnixNano()} 103 | if openstat > 0 { 104 | fm.resetStat() 105 | } 106 | return fm 107 | } 108 | 109 | func (fm *FrameMgr) GetSendBufferLeft() int { 110 | fm.sendblock.Lock() 111 | defer fm.sendblock.Unlock() 112 | left := fm.sendb.Capacity() - fm.sendb.Size() 113 | return left 114 | } 115 | 116 | func (fm *FrameMgr) WriteSendBuffer(data []byte) { 117 | fm.sendblock.Lock() 118 | defer fm.sendblock.Unlock() 119 | fm.sendb.Write(data) 120 | //loggo.Debug("debugid %v WriteSendBuffer %v %v", fm.debugid, fm.sendb.Size(), len(data)) 121 | } 122 | 123 | func (fm *FrameMgr) Update() { 124 | cur := time.Now().UnixNano() 125 | 126 | fm.cutSendBufferToWindow(cur) 127 | 128 | fm.sendlist.Init() 129 | 130 | tmpreq, tmpack, tmpackto := fm.preProcessRecvList() 131 | fm.processRecvList(tmpreq, tmpack, tmpackto) 132 | 133 | fm.combineWindowToRecvBuffer(cur) 134 | 135 | fm.calSendList(cur) 136 | 137 | fm.ping() 138 | fm.hb() 139 | 140 | fm.printStat(cur) 141 | } 142 | 143 | func (fm *FrameMgr) cutSendBufferToWindow(cur int64) { 144 | 145 | fm.sendblock.Lock() 146 | defer fm.sendblock.Unlock() 147 | 148 | sendall := false 149 | 150 | if fm.sendb.Size() < fm.frame_max_size { 151 | sendall = true 152 | } 153 | 154 | for fm.sendb.Size() >= fm.frame_max_size && fm.sendwin.Size() < int(fm.windowsize) { 155 | fd := &FrameData{Type: (int32)(FrameData_USER_DATA), 156 | Data: make([]byte, fm.frame_max_size)} 157 | fm.sendb.Read(fd.Data) 158 | 159 | if fm.compress > 0 && len(fd.Data) > fm.compress { 160 | newb := common.CompressData(fd.Data) 161 | if len(newb) < len(fd.Data) { 162 | fd.Data = newb 163 | fd.Compress = true 164 | } 165 | } 166 | 167 | f := &Frame{Type: (int32)(Frame_DATA), 168 | Id: fm.sendid, 169 | Data: fd} 170 | 171 | fm.sendid++ 172 | if fm.sendid >= fm.frame_max_id { 173 | fm.sendid = 0 174 | } 175 | 176 | err := fm.sendwin.Set(int(f.Id), f) 177 | if err != nil { 178 | loggo.Error("sendwin Set fail %v", err) 179 | } 180 | //loggo.Debug("debugid %v cut frame push to send win %v %v %v", fm.debugid, f.Id, fm.frame_max_size, fm.sendwin.Len()) 181 | } 182 | 183 | if sendall && fm.sendb.Size() > 0 && fm.sendwin.Size() < int(fm.windowsize) { 184 | fd := &FrameData{Type: (int32)(FrameData_USER_DATA), 185 | Data: make([]byte, fm.sendb.Size())} 186 | fm.sendb.Read(fd.Data) 187 | 188 | if fm.compress > 0 && len(fd.Data) > fm.compress { 189 | newb := common.CompressData(fd.Data) 190 | if len(newb) < len(fd.Data) { 191 | fd.Data = newb 192 | fd.Compress = true 193 | } 194 | } 195 | 196 | f := &Frame{Type: (int32)(Frame_DATA), 197 | Id: fm.sendid, 198 | Data: fd} 199 | 200 | fm.sendid++ 201 | if fm.sendid >= fm.frame_max_id { 202 | fm.sendid = 0 203 | } 204 | 205 | err := fm.sendwin.Set(int(f.Id), f) 206 | if err != nil { 207 | loggo.Error("sendwin Set fail %v", err) 208 | } 209 | //loggo.Debug("debugid %v cut small frame push to send win %v %v %v", fm.debugid, f.Id, len(f.Data.Data), fm.sendwin.Len()) 210 | } 211 | 212 | if fm.sendb.Empty() && fm.close && !fm.closesend && fm.sendwin.Size() < int(fm.windowsize) { 213 | fd := &FrameData{Type: (int32)(FrameData_CLOSE)} 214 | 215 | f := &Frame{Type: (int32)(Frame_DATA), 216 | Id: fm.sendid, 217 | Data: fd} 218 | 219 | fm.sendid++ 220 | if fm.sendid >= fm.frame_max_id { 221 | fm.sendid = 0 222 | } 223 | 224 | err := fm.sendwin.Set(int(f.Id), f) 225 | if err != nil { 226 | loggo.Error("sendwin Set fail %v", err) 227 | } 228 | fm.closesend = true 229 | //loggo.Debug("debugid %v close frame push to send win %v %v", fm.debugid, f.Id, fm.sendwin.Len()) 230 | } 231 | } 232 | 233 | func (fm *FrameMgr) calSendList(cur int64) { 234 | 235 | for e := fm.sendwin.FrontInter(); e != nil; e = e.Next() { 236 | f := e.Value.(*Frame) 237 | if !f.Acked && (f.Resend || cur-f.Sendtime > int64(fm.resend_timems*(int)(time.Millisecond))) && 238 | cur-f.Sendtime > fm.rttns { 239 | f.Sendtime = cur 240 | fm.sendlist.PushBack(f) 241 | f.Resend = false 242 | if fm.openstat > 0 { 243 | fm.fs.sendDataNum++ 244 | fm.fs.sendDataNumsMap[f.Id]++ 245 | } 246 | //loggo.Debug("debugid %v push frame to sendlist %v %v", fm.debugid, f.Id, len(f.Data.Data)) 247 | } 248 | } 249 | } 250 | 251 | func (fm *FrameMgr) GetSendList() *list.List { 252 | return fm.sendlist 253 | } 254 | 255 | func (fm *FrameMgr) OnRecvFrame(f *Frame) { 256 | fm.recvlock.Lock() 257 | defer fm.recvlock.Unlock() 258 | fm.recvlist.PushBack(f) 259 | } 260 | 261 | func (fm *FrameMgr) preProcessRecvList() (map[int32]int, map[int32]int, map[int32]*Frame) { 262 | fm.recvlock.Lock() 263 | defer fm.recvlock.Unlock() 264 | 265 | tmpreq := make(map[int32]int) 266 | tmpack := make(map[int32]int) 267 | tmpackto := make(map[int32]*Frame) 268 | for e := fm.recvlist.Front(); e != nil; e = e.Next() { 269 | f := e.Value.(*Frame) 270 | if f.Type == (int32)(Frame_REQ) { 271 | for _, id := range f.Dataid { 272 | tmpreq[id]++ 273 | //loggo.Debug("debugid %v recv req %v %v", fm.debugid, f.Id, common.Int32ArrayToString(f.Dataid, ",")) 274 | } 275 | } else if f.Type == (int32)(Frame_ACK) { 276 | for _, id := range f.Dataid { 277 | tmpack[id]++ 278 | //loggo.Debug("debugid %v recv ack %v %v", fm.debugid, f.Id, common.Int32ArrayToString(f.Dataid, ",")) 279 | } 280 | } else if f.Type == (int32)(Frame_DATA) { 281 | tmpackto[f.Id] = f 282 | if fm.openstat > 0 { 283 | fm.fs.recvDataNum++ 284 | fm.fs.recvDataNumsMap[f.Id]++ 285 | } 286 | //loggo.Debug("debugid %v recv data %v %v", fm.debugid, f.Id, len(f.Data.Data)) 287 | } else if f.Type == (int32)(Frame_PING) { 288 | fm.processPing(f) 289 | } else if f.Type == (int32)(Frame_PONG) { 290 | fm.processPong(f) 291 | } else { 292 | loggo.Error("error frame type %v", f.Type) 293 | } 294 | } 295 | fm.recvlist.Init() 296 | return tmpreq, tmpack, tmpackto 297 | } 298 | 299 | func (fm *FrameMgr) processRecvList(tmpreq map[int32]int, tmpack map[int32]int, tmpackto map[int32]*Frame) { 300 | 301 | for id, num := range tmpreq { 302 | err, value := fm.sendwin.Get(int(id)) 303 | if err != nil { 304 | loggo.Error("sendwin get id fail %v %v", id, err) 305 | continue 306 | } 307 | if value == nil { 308 | continue 309 | } 310 | f := value.(*Frame) 311 | if f.Id == id { 312 | f.Resend = true 313 | //loggo.Debug("debugid %v choose resend win %v %v", fm.debugid, f.Id, len(f.Data.Data)) 314 | } else { 315 | loggo.Error("sendwin get id diff %v %v", id, f.Id) 316 | continue 317 | } 318 | if fm.openstat > 0 { 319 | fm.fs.recvReqNum += num 320 | fm.fs.recvReqNumsMap[id] += num 321 | } 322 | } 323 | 324 | for id, num := range tmpack { 325 | err, value := fm.sendwin.Get(int(id)) 326 | if err != nil { 327 | continue 328 | } 329 | if value == nil { 330 | continue 331 | } 332 | f := value.(*Frame) 333 | if f.Id == id { 334 | f.Acked = true 335 | //loggo.Debug("debugid %v remove send win %v %v", fm.debugid, f.Id, len(f.Data.Data)) 336 | } else { 337 | loggo.Error("sendwin get id diff %v %v", id, f.Id) 338 | continue 339 | } 340 | if fm.openstat > 0 { 341 | fm.fs.recvAckNum += num 342 | fm.fs.recvAckNumsMap[id] += num 343 | } 344 | } 345 | 346 | for !fm.sendwin.Empty() { 347 | err, value := fm.sendwin.Front() 348 | if err != nil { 349 | loggo.Error("sendwin get Front fail %s", err) 350 | break 351 | } 352 | if value == nil { 353 | break 354 | } 355 | f := value.(*Frame) 356 | if f.Acked { 357 | err := fm.sendwin.PopFront() 358 | if err != nil { 359 | loggo.Error("sendwin PopFront fail ") 360 | break 361 | } 362 | } else { 363 | break 364 | } 365 | } 366 | 367 | if len(tmpackto) > 0 { 368 | tmpsize := common.MinOfInt(len(tmpackto), fm.frame_max_size/2/4) 369 | tmp := make([]int32, len(tmpackto)) 370 | index := 0 371 | for id, rf := range tmpackto { 372 | if fm.addToRecvWin(rf) { 373 | tmp[index] = id 374 | index++ 375 | if fm.openstat > 0 { 376 | fm.fs.sendAckNum++ 377 | fm.fs.sendAckNumsMap[id]++ 378 | } 379 | if index >= tmpsize { 380 | f := &Frame{Type: (int32)(Frame_ACK), Resend: false, Sendtime: 0, 381 | Id: 0, 382 | Dataid: tmp[0:index]} 383 | fm.sendlist.PushBack(f) 384 | index = 0 385 | //loggo.Debug("debugid %v send ack %v %v", fm.debugid, f.Id, common.Int32ArrayToString(f.Dataid, ",")) 386 | } 387 | //loggo.Debug("debugid %v add data to win %v %v", fm.debugid, rf.Id, len(rf.Data.Data)) 388 | } 389 | } 390 | if index > 0 { 391 | f := &Frame{Type: (int32)(Frame_ACK), Resend: false, Sendtime: 0, 392 | Id: 0, 393 | Dataid: tmp[0:index]} 394 | fm.sendlist.PushBack(f) 395 | //loggo.Debug("debugid %v send ack %v %v", fm.debugid, f.Id, common.Int32ArrayToString(f.Dataid, ",")) 396 | } 397 | } 398 | } 399 | 400 | func (fm *FrameMgr) addToRecvWin(rf *Frame) bool { 401 | 402 | if !fm.isIdInRange(rf.Id, fm.frame_max_id) { 403 | //loggo.Debug("debugid %v recv frame not in range %v %v", fm.debugid, rf.Id, fm.recvid) 404 | if fm.isIdOld(rf.Id, fm.frame_max_id) { 405 | if fm.openstat > 0 { 406 | fm.fs.recvOldNum++ 407 | } 408 | return true 409 | } 410 | if fm.openstat > 0 { 411 | fm.fs.recvOutWinNum++ 412 | } 413 | return false 414 | } 415 | 416 | err := fm.recvwin.Set(int(rf.Id), rf) 417 | if err != nil { 418 | loggo.Error("recvwin Set fail %v", err) 419 | return false 420 | } 421 | return true 422 | } 423 | 424 | func (fm *FrameMgr) processRecvFrame(f *Frame) bool { 425 | if f.Data.Type == (int32)(FrameData_USER_DATA) { 426 | fm.recvblock.Lock() 427 | defer fm.recvblock.Unlock() 428 | 429 | left := fm.recvb.Capacity() - fm.recvb.Size() 430 | if left >= len(f.Data.Data) { 431 | src := f.Data.Data 432 | if f.Data.Compress { 433 | old, err := common.DeCompressData(src) 434 | if err != nil { 435 | loggo.Error("recv frame deCompressData error %v", f.Id) 436 | return false 437 | } 438 | if left < len(old) { 439 | return false 440 | } 441 | //loggo.Debug("debugid %v deCompressData recv frame %v %v %v", fm.debugid, f.Id, len(src), len(old)) 442 | src = old 443 | } 444 | 445 | fm.recvb.Write(src) 446 | //loggo.Debug("debugid %v combined recv frame to recv buffer %v %v", fm.debugid, f.Id, len(src)) 447 | return true 448 | } 449 | return false 450 | } else if f.Data.Type == (int32)(FrameData_CLOSE) { 451 | fm.remoteclosed = true 452 | //loggo.Debug("debugid %v recv remote close frame %v", fm.debugid, f.Id) 453 | return true 454 | } else if f.Data.Type == (int32)(FrameData_CONN) { 455 | fm.sendConnectRsp() 456 | fm.connected = true 457 | //loggo.Debug("debugid %v recv remote conn frame %v", fm.debugid, f.Id) 458 | return true 459 | } else if f.Data.Type == (int32)(FrameData_CONNRSP) { 460 | fm.connected = true 461 | //loggo.Debug("debugid %v recv remote conn rsp frame %v", fm.debugid, f.Id) 462 | return true 463 | } else if f.Data.Type == (int32)(FrameData_HB) { 464 | fm.lastRecvHBTime = time.Now().UnixNano() 465 | //loggo.Debug("debugid %v recv remote hb frame %v", fm.debugid, f.Id) 466 | return true 467 | } else { 468 | loggo.Error("recv frame type error %v", f.Data.Type) 469 | return false 470 | } 471 | } 472 | 473 | func (fm *FrameMgr) combineWindowToRecvBuffer(cur int64) { 474 | 475 | for { 476 | done := false 477 | err, value := fm.recvwin.Front() 478 | if err == nil { 479 | f := value.(*Frame) 480 | if f.Id == fm.recvid { 481 | delete(fm.reqmap, f.Id) 482 | if fm.processRecvFrame(f) { 483 | err := fm.recvwin.PopFront() 484 | if err != nil { 485 | loggo.Error("recvwin PopFront fail %v ", err) 486 | } 487 | done = true 488 | //loggo.Debug("debugid %v process recv frame ok %v %v", fm.debugid, f.Id, len(f.Data.Data)) 489 | } 490 | } 491 | } 492 | if !done { 493 | break 494 | } else { 495 | fm.recvid++ 496 | if fm.recvid >= fm.frame_max_id { 497 | fm.recvid = 0 498 | } 499 | //loggo.Debug("debugid %v combined ok add recvid %v ", fm.debugid, fm.recvid) 500 | } 501 | } 502 | 503 | reqtmp := make(map[int32]int) 504 | e := fm.recvwin.FrontInter() 505 | id := fm.recvid 506 | for len(reqtmp) < int(fm.windowsize) && len(reqtmp)*4 < fm.frame_max_size/2 && e != nil { 507 | f := e.Value.(*Frame) 508 | //loggo.Debug("debugid %v start add req id %v %v %v", fm.debugid, fm.recvid, f.Id, id) 509 | if f.Id != id { 510 | oldReq := fm.reqmap[f.Id] 511 | if cur-oldReq > fm.rttns { 512 | reqtmp[id]++ 513 | fm.reqmap[f.Id] = cur 514 | //loggo.Debug("debugid %v add req id %v ", fm.debugid, id) 515 | } 516 | } else { 517 | e = e.Next() 518 | } 519 | 520 | id++ 521 | if id >= fm.frame_max_id { 522 | id = 0 523 | } 524 | } 525 | 526 | if len(reqtmp) > 0 { 527 | f := &Frame{Type: (int32)(Frame_REQ), Resend: false, Sendtime: 0, 528 | Id: 0, 529 | Dataid: make([]int32, len(reqtmp))} 530 | index := 0 531 | for id := range reqtmp { 532 | f.Dataid[index] = id 533 | index++ 534 | if fm.openstat > 0 { 535 | fm.fs.sendReqNum++ 536 | fm.fs.sendReqNumsMap[id]++ 537 | } 538 | } 539 | fm.sendlist.PushBack(f) 540 | //loggo.Debug("debugid %v send req %v %v", fm.debugid, f.Id, common.Int32ArrayToString(f.Dataid, ",")) 541 | } 542 | } 543 | 544 | func (fm *FrameMgr) GetRecvBufferSize() int { 545 | fm.recvblock.Lock() 546 | defer fm.recvblock.Unlock() 547 | 548 | return fm.recvb.Size() 549 | } 550 | 551 | func (fm *FrameMgr) GetRecvReadLineBuffer() []byte { 552 | fm.recvblock.Lock() 553 | defer fm.recvblock.Unlock() 554 | 555 | ret := fm.recvb.GetReadLineBuffer() 556 | //loggo.Debug("debugid %v GetRecvReadLineBuffer %v %v", fm.debugid, fm.recvb.Size(), len(ret)) 557 | return ret 558 | } 559 | 560 | func (fm *FrameMgr) SkipRecvBuffer(size int) { 561 | fm.recvblock.Lock() 562 | defer fm.recvblock.Unlock() 563 | 564 | fm.recvb.SkipRead(size) 565 | //loggo.Debug("debugid %v SkipRead %v %v", fm.debugid, fm.recvb.Size(), size) 566 | } 567 | 568 | func (fm *FrameMgr) Close() { 569 | fm.close = true 570 | } 571 | 572 | func (fm *FrameMgr) IsRemoteClosed() bool { 573 | return fm.remoteclosed 574 | } 575 | 576 | func (fm *FrameMgr) ping() { 577 | cur := time.Now().UnixNano() 578 | if cur-fm.lastPingTime > (int64)(time.Second) { 579 | fm.lastPingTime = cur 580 | f := &Frame{Type: (int32)(Frame_PING), Resend: false, Sendtime: cur, 581 | Id: 0} 582 | fm.sendlist.PushBack(f) 583 | //loggo.Debug("debugid %v send ping %v", fm.debugid, cur) 584 | if fm.openstat > 0 { 585 | fm.fs.sendping++ 586 | } 587 | } 588 | } 589 | 590 | func (fm *FrameMgr) hb() { 591 | cur := time.Now().UnixNano() 592 | if cur-fm.lastSendHBTime > (int64)(time.Second) && fm.sendwin.Size() < int(fm.windowsize) { 593 | fm.lastSendHBTime = cur 594 | 595 | fd := &FrameData{Type: (int32)(FrameData_HB)} 596 | 597 | f := &Frame{Type: (int32)(Frame_DATA), 598 | Id: fm.sendid, 599 | Data: fd} 600 | 601 | fm.sendid++ 602 | if fm.sendid >= fm.frame_max_id { 603 | fm.sendid = 0 604 | } 605 | 606 | err := fm.sendwin.Set(int(f.Id), f) 607 | if err != nil { 608 | loggo.Error("sendwin Set fail %v", err) 609 | } 610 | } 611 | } 612 | 613 | func (fm *FrameMgr) processPing(f *Frame) { 614 | rf := &Frame{Type: (int32)(Frame_PONG), Resend: false, Sendtime: f.Sendtime, 615 | Id: 0} 616 | fm.sendlist.PushBack(rf) 617 | if fm.openstat > 0 { 618 | fm.fs.recvping++ 619 | fm.fs.sendpong++ 620 | } 621 | //loggo.Debug("debugid %v recv ping %v", fm.debugid, f.Sendtime) 622 | } 623 | 624 | func (fm *FrameMgr) processPong(f *Frame) { 625 | cur := time.Now().UnixNano() 626 | if cur > f.Sendtime { 627 | rtt := cur - f.Sendtime 628 | fm.rttns = (fm.rttns + rtt) / 2 629 | if fm.openstat > 0 { 630 | fm.fs.recvpong++ 631 | } 632 | fm.lastPongTime = cur 633 | //loggo.Debug("debugid %v recv pong %v %dms", fm.debugid, rtt, fm.rttns/1000/1000) 634 | } 635 | } 636 | 637 | func (fm *FrameMgr) isIdInRange(id int32, maxid int32) bool { 638 | begin := fm.recvid 639 | end := fm.recvid + fm.windowsize 640 | if end >= maxid { 641 | if id >= 0 && id < end-maxid { 642 | return true 643 | } 644 | end = maxid 645 | } 646 | if id >= begin && id < end { 647 | return true 648 | } 649 | return false 650 | } 651 | 652 | func (fm *FrameMgr) isIdOld(id int32, maxid int32) bool { 653 | begin := fm.recvid - fm.windowsize 654 | if begin < 0 { 655 | begin += maxid 656 | } 657 | end := fm.recvid 658 | 659 | if begin < end { 660 | return id >= begin && id < end 661 | } else { 662 | return (id >= begin && id < maxid) || (id >= 0 && id < end) 663 | } 664 | } 665 | 666 | func (fm *FrameMgr) IsConnected() bool { 667 | return fm.connected 668 | } 669 | 670 | func (fm *FrameMgr) Connect() { 671 | if fm.sendwin.Size() < int(fm.windowsize) { 672 | fd := &FrameData{Type: (int32)(FrameData_CONN)} 673 | 674 | f := &Frame{Type: (int32)(Frame_DATA), 675 | Id: fm.sendid, 676 | Data: fd} 677 | 678 | fm.sendid++ 679 | if fm.sendid >= fm.frame_max_id { 680 | fm.sendid = 0 681 | } 682 | 683 | err := fm.sendwin.Set(int(f.Id), f) 684 | if err != nil { 685 | loggo.Error("sendwin Set fail %v", err) 686 | } 687 | //loggo.Debug("debugid %v start connect", fm.debugid) 688 | } else { 689 | loggo.Error("Connect fail ") 690 | } 691 | } 692 | 693 | func (fm *FrameMgr) sendConnectRsp() { 694 | if fm.sendwin.Size() < int(fm.windowsize) { 695 | fd := &FrameData{Type: (int32)(FrameData_CONNRSP)} 696 | 697 | f := &Frame{Type: (int32)(Frame_DATA), 698 | Id: fm.sendid, 699 | Data: fd} 700 | 701 | fm.sendid++ 702 | if fm.sendid >= fm.frame_max_id { 703 | fm.sendid = 0 704 | } 705 | 706 | err := fm.sendwin.Set(int(f.Id), f) 707 | if err != nil { 708 | loggo.Error("sendwin Set fail %v", err) 709 | } 710 | //loggo.Debug("debugid %v send connect rsp", fm.debugid) 711 | } else { 712 | loggo.Error("sendConnectRsp fail ") 713 | } 714 | } 715 | 716 | func (fm *FrameMgr) resetStat() { 717 | fm.fs = &FrameStat{} 718 | fm.fs.sendDataNumsMap = make(map[int32]int) 719 | fm.fs.recvDataNumsMap = make(map[int32]int) 720 | fm.fs.sendReqNumsMap = make(map[int32]int) 721 | fm.fs.recvReqNumsMap = make(map[int32]int) 722 | fm.fs.sendAckNumsMap = make(map[int32]int) 723 | fm.fs.recvAckNumsMap = make(map[int32]int) 724 | } 725 | 726 | func (fm *FrameMgr) printStat(cur int64) { 727 | if fm.openstat > 0 { 728 | if cur-fm.lastPrintStat > (int64)(time.Second) { 729 | fm.lastPrintStat = cur 730 | fs := fm.fs 731 | loggo.Info("\nsendDataNum %v\nrecvDataNum %v\nsendReqNum %v\nrecvReqNum %v\nsendAckNum %v\nrecvAckNum %v\n"+ 732 | "sendDataNumsMap %v\nrecvDataNumsMap %v\nsendReqNumsMap %v\nrecvReqNumsMap %v\nsendAckNumsMap %v\nrecvAckNumsMap %v\n"+ 733 | "sendping %v\nrecvping %v\nsendpong %v\nrecvpong %v\n"+ 734 | "sendwin %v\nrecvwin %v\n"+ 735 | "recvOldNum %v\nrecvOutWinNum %v\n"+ 736 | "rtt %v\n", 737 | fs.sendDataNum, fs.recvDataNum, 738 | fs.sendReqNum, fs.recvReqNum, 739 | fs.sendAckNum, fs.recvAckNum, 740 | fm.printStatMap(&fs.sendDataNumsMap), fm.printStatMap(&fs.recvDataNumsMap), 741 | fm.printStatMap(&fs.sendReqNumsMap), fm.printStatMap(&fs.recvReqNumsMap), 742 | fm.printStatMap(&fs.sendAckNumsMap), fm.printStatMap(&fs.recvAckNumsMap), 743 | fs.sendping, fs.recvping, 744 | fs.sendpong, fs.recvpong, 745 | fm.sendwin.Size(), fm.recvwin.Size(), 746 | fs.recvOldNum, fs.recvOutWinNum, 747 | time.Duration(fm.rttns).String()) 748 | fm.resetStat() 749 | } 750 | } 751 | } 752 | 753 | func (fm *FrameMgr) printStatMap(m *map[int32]int) string { 754 | tmp := make(map[int]int) 755 | for _, v := range *m { 756 | tmp[v]++ 757 | } 758 | max := 0 759 | for k := range tmp { 760 | if k > max { 761 | max = k 762 | } 763 | } 764 | var ret string 765 | for i := 1; i <= max; i++ { 766 | ret += strconv.Itoa(i) + "->" + strconv.Itoa(tmp[i]) + "," 767 | } 768 | if len(ret) <= 0 { 769 | ret = "none" 770 | } 771 | return ret 772 | } 773 | 774 | func (fm *FrameMgr) MarshalFrame(f *Frame) ([]byte, error) { 775 | resend := f.Resend 776 | sendtime := f.Sendtime 777 | mb, err := proto.Marshal(f) 778 | f.Resend = resend 779 | f.Sendtime = sendtime 780 | return mb, err 781 | } 782 | 783 | func (fm *FrameMgr) IsHBTimeout(timeoutms int) bool { 784 | now := time.Now().UnixNano() 785 | if now-fm.lastRecvHBTime > int64(time.Millisecond)*int64(timeoutms) { 786 | return true 787 | } 788 | return false 789 | } 790 | -------------------------------------------------------------------------------- /pingtunnel/client.go: -------------------------------------------------------------------------------- 1 | package pingtunnel 2 | 3 | import ( 4 | "github.com/cxjava/pingtunnel/common" 5 | "github.com/cxjava/pingtunnel/frame" 6 | "github.com/cxjava/pingtunnel/loggo" 7 | "github.com/cxjava/pingtunnel/network" 8 | "github.com/golang/protobuf/proto" 9 | "golang.org/x/net/icmp" 10 | "io" 11 | "math" 12 | "math/rand" 13 | "net" 14 | "sync" 15 | "time" 16 | ) 17 | 18 | const ( 19 | SEND_PROTO int = 8 20 | RECV_PROTO int = 0 21 | ) 22 | 23 | func NewClient(addr string, server string, target string, timeout int, key int, 24 | tcpmode int, tcpmode_buffersize int, tcpmode_maxwin int, tcpmode_resend_timems int, tcpmode_compress int, 25 | tcpmode_stat int, open_sock5 int, maxconn int, sock5_filter *func(addr string) bool) (*Client, error) { 26 | 27 | var ipaddr *net.UDPAddr 28 | var tcpaddr *net.TCPAddr 29 | var err error 30 | 31 | if tcpmode > 0 { 32 | tcpaddr, err = net.ResolveTCPAddr("tcp", addr) 33 | if err != nil { 34 | return nil, err 35 | } 36 | } else { 37 | ipaddr, err = net.ResolveUDPAddr("udp", addr) 38 | if err != nil { 39 | return nil, err 40 | } 41 | } 42 | 43 | ipaddrServer, err := net.ResolveIPAddr("ip", server) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | rand.Seed(time.Now().UnixNano()) 49 | return &Client{ 50 | exit: false, 51 | rtt: 0, 52 | id: rand.Intn(math.MaxInt16), 53 | ipaddr: ipaddr, 54 | tcpaddr: tcpaddr, 55 | addr: addr, 56 | ipaddrServer: ipaddrServer, 57 | addrServer: server, 58 | targetAddr: target, 59 | timeout: timeout, 60 | key: key, 61 | tcpmode: tcpmode, 62 | tcpmode_buffersize: tcpmode_buffersize, 63 | tcpmode_maxwin: tcpmode_maxwin, 64 | tcpmode_resend_timems: tcpmode_resend_timems, 65 | tcpmode_compress: tcpmode_compress, 66 | tcpmode_stat: tcpmode_stat, 67 | open_sock5: open_sock5, 68 | maxconn: maxconn, 69 | pongTime: time.Now(), 70 | sock5_filter: sock5_filter, 71 | }, nil 72 | } 73 | 74 | type Client struct { 75 | exit bool 76 | rtt time.Duration 77 | workResultLock sync.WaitGroup 78 | maxconn int 79 | 80 | id int 81 | sequence int 82 | 83 | timeout int 84 | sproto int 85 | rproto int 86 | key int 87 | tcpmode int 88 | tcpmode_buffersize int 89 | tcpmode_maxwin int 90 | tcpmode_resend_timems int 91 | tcpmode_compress int 92 | tcpmode_stat int 93 | 94 | open_sock5 int 95 | sock5_filter *func(addr string) bool 96 | 97 | ipaddr *net.UDPAddr 98 | tcpaddr *net.TCPAddr 99 | addr string 100 | 101 | ipaddrServer *net.IPAddr 102 | addrServer string 103 | 104 | targetAddr string 105 | 106 | conn *icmp.PacketConn 107 | listenConn *net.UDPConn 108 | tcplistenConn *net.TCPListener 109 | 110 | localAddrToConnMap sync.Map 111 | localIdToConnMap sync.Map 112 | 113 | sendPacket uint64 114 | recvPacket uint64 115 | sendPacketSize uint64 116 | recvPacketSize uint64 117 | localAddrToConnMapSize int 118 | localIdToConnMapSize int 119 | 120 | recvcontrol chan int 121 | 122 | pongTime time.Time 123 | } 124 | 125 | type ClientConn struct { 126 | exit bool 127 | ipaddr *net.UDPAddr 128 | tcpaddr *net.TCPAddr 129 | id string 130 | activeRecvTime time.Time 131 | activeSendTime time.Time 132 | close bool 133 | 134 | fm *frame.FrameMgr 135 | } 136 | 137 | func (p *Client) Addr() string { 138 | return p.addr 139 | } 140 | 141 | func (p *Client) IPAddr() *net.UDPAddr { 142 | return p.ipaddr 143 | } 144 | 145 | func (p *Client) TargetAddr() string { 146 | return p.targetAddr 147 | } 148 | 149 | func (p *Client) ServerIPAddr() *net.IPAddr { 150 | return p.ipaddrServer 151 | } 152 | 153 | func (p *Client) ServerAddr() string { 154 | return p.addrServer 155 | } 156 | 157 | func (p *Client) RTT() time.Duration { 158 | return p.rtt 159 | } 160 | 161 | func (p *Client) RecvPacketSize() uint64 { 162 | return p.recvPacketSize 163 | } 164 | 165 | func (p *Client) SendPacketSize() uint64 { 166 | return p.sendPacketSize 167 | } 168 | 169 | func (p *Client) RecvPacket() uint64 { 170 | return p.recvPacket 171 | } 172 | 173 | func (p *Client) SendPacket() uint64 { 174 | return p.sendPacket 175 | } 176 | 177 | func (p *Client) LocalIdToConnMapSize() int { 178 | return p.localIdToConnMapSize 179 | } 180 | 181 | func (p *Client) LocalAddrToConnMapSize() int { 182 | return p.localAddrToConnMapSize 183 | } 184 | 185 | func (p *Client) Run() error { 186 | 187 | conn, err := icmp.ListenPacket("ip4:icmp", "") 188 | if err != nil { 189 | loggo.Error("Error listening for ICMP packets: %s", err.Error()) 190 | return err 191 | } 192 | p.conn = conn 193 | 194 | if p.tcpmode > 0 { 195 | tcplistenConn, err := net.ListenTCP("tcp", p.tcpaddr) 196 | if err != nil { 197 | loggo.Error("Error listening for tcp packets: %s", err.Error()) 198 | return err 199 | } 200 | p.tcplistenConn = tcplistenConn 201 | } else { 202 | listener, err := net.ListenUDP("udp", p.ipaddr) 203 | if err != nil { 204 | loggo.Error("Error listening for udp packets: %s", err.Error()) 205 | return err 206 | } 207 | p.listenConn = listener 208 | } 209 | 210 | if p.tcpmode > 0 { 211 | go p.AcceptTcp() 212 | } else { 213 | go p.Accept() 214 | } 215 | 216 | recv := make(chan *Packet, 10000) 217 | p.recvcontrol = make(chan int, 1) 218 | go recvICMP(&p.workResultLock, &p.exit, *p.conn, recv) 219 | 220 | go func() { 221 | defer common.CrashLog() 222 | 223 | p.workResultLock.Add(1) 224 | defer p.workResultLock.Done() 225 | 226 | for !p.exit { 227 | p.checkTimeoutConn() 228 | p.ping() 229 | p.showNet() 230 | time.Sleep(time.Second) 231 | } 232 | }() 233 | 234 | go func() { 235 | defer common.CrashLog() 236 | 237 | p.workResultLock.Add(1) 238 | defer p.workResultLock.Done() 239 | 240 | for !p.exit { 241 | p.updateServerAddr() 242 | time.Sleep(time.Second) 243 | } 244 | }() 245 | 246 | go func() { 247 | defer common.CrashLog() 248 | 249 | p.workResultLock.Add(1) 250 | defer p.workResultLock.Done() 251 | 252 | for !p.exit { 253 | select { 254 | case <-p.recvcontrol: 255 | return 256 | case r := <-recv: 257 | p.processPacket(r) 258 | } 259 | } 260 | }() 261 | 262 | return nil 263 | } 264 | 265 | func (p *Client) Stop() { 266 | p.exit = true 267 | p.recvcontrol <- 1 268 | p.workResultLock.Wait() 269 | p.conn.Close() 270 | if p.tcplistenConn != nil { 271 | p.tcplistenConn.Close() 272 | } 273 | if p.listenConn != nil { 274 | p.listenConn.Close() 275 | } 276 | } 277 | 278 | func (p *Client) AcceptTcp() error { 279 | 280 | defer common.CrashLog() 281 | 282 | p.workResultLock.Add(1) 283 | defer p.workResultLock.Done() 284 | 285 | loggo.Info("client waiting local accept tcp") 286 | 287 | for !p.exit { 288 | p.tcplistenConn.SetDeadline(time.Now().Add(time.Millisecond * 1000)) 289 | 290 | conn, err := p.tcplistenConn.AcceptTCP() 291 | if err != nil { 292 | nerr, ok := err.(net.Error) 293 | if !ok || !nerr.Timeout() { 294 | loggo.Info("Error accept tcp %s", err) 295 | continue 296 | } 297 | } 298 | 299 | if conn != nil { 300 | if p.open_sock5 > 0 { 301 | go p.AcceptSock5Conn(conn) 302 | } else { 303 | go p.AcceptTcpConn(conn, p.targetAddr) 304 | } 305 | } 306 | } 307 | return nil 308 | } 309 | 310 | func (p *Client) AcceptTcpConn(conn *net.TCPConn, targetAddr string) { 311 | 312 | defer common.CrashLog() 313 | 314 | p.workResultLock.Add(1) 315 | defer p.workResultLock.Done() 316 | 317 | tcpsrcaddr := conn.RemoteAddr().(*net.TCPAddr) 318 | 319 | if p.maxconn > 0 && p.localIdToConnMapSize >= p.maxconn { 320 | loggo.Info("too many connections %d, client accept new local tcp fail %s", p.localIdToConnMapSize, tcpsrcaddr.String()) 321 | return 322 | } 323 | 324 | uuid := common.UniqueId() 325 | 326 | 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) 327 | 328 | now := time.Now() 329 | clientConn := &ClientConn{exit: false, tcpaddr: tcpsrcaddr, id: uuid, activeRecvTime: now, activeSendTime: now, close: false, 330 | fm: fm} 331 | p.addClientConn(uuid, tcpsrcaddr.String(), clientConn) 332 | loggo.Info("client accept new local tcp %s %s", uuid, tcpsrcaddr.String()) 333 | 334 | loggo.Info("start connect remote tcp %s %s", uuid, tcpsrcaddr.String()) 335 | clientConn.fm.Connect() 336 | startConnectTime := common.GetNowUpdateInSecond() 337 | for !p.exit && !clientConn.exit { 338 | if clientConn.fm.IsConnected() { 339 | break 340 | } 341 | clientConn.fm.Update() 342 | sendlist := clientConn.fm.GetSendList() 343 | for e := sendlist.Front(); e != nil; e = e.Next() { 344 | f := e.Value.(*frame.Frame) 345 | mb, _ := clientConn.fm.MarshalFrame(f) 346 | p.sequence++ 347 | sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, 348 | SEND_PROTO, RECV_PROTO, p.key, 349 | p.tcpmode, p.tcpmode_buffersize, p.tcpmode_maxwin, p.tcpmode_resend_timems, p.tcpmode_compress, p.tcpmode_stat, 350 | p.timeout) 351 | p.sendPacket++ 352 | p.sendPacketSize += (uint64)(len(mb)) 353 | } 354 | time.Sleep(time.Millisecond * 10) 355 | now := common.GetNowUpdateInSecond() 356 | diffclose := now.Sub(startConnectTime) 357 | if diffclose > time.Second*5 { 358 | loggo.Info("can not connect remote tcp %s %s", uuid, tcpsrcaddr.String()) 359 | p.close(clientConn) 360 | return 361 | } 362 | } 363 | 364 | if !clientConn.exit { 365 | loggo.Info("connected remote tcp %s %s", uuid, tcpsrcaddr.String()) 366 | } 367 | 368 | bytes := make([]byte, 10240) 369 | 370 | tcpActiveRecvTime := common.GetNowUpdateInSecond() 371 | tcpActiveSendTime := common.GetNowUpdateInSecond() 372 | 373 | for !p.exit && !clientConn.exit { 374 | now := common.GetNowUpdateInSecond() 375 | sleep := true 376 | 377 | left := common.MinOfInt(clientConn.fm.GetSendBufferLeft(), len(bytes)) 378 | if left > 0 { 379 | conn.SetReadDeadline(time.Now().Add(time.Millisecond * 1)) 380 | n, err := conn.Read(bytes[0:left]) 381 | if err != nil { 382 | nerr, ok := err.(net.Error) 383 | if !ok || !nerr.Timeout() { 384 | loggo.Info("Error read tcp %s %s %s", uuid, tcpsrcaddr.String(), err) 385 | clientConn.fm.Close() 386 | break 387 | } 388 | } 389 | if n > 0 { 390 | sleep = false 391 | clientConn.fm.WriteSendBuffer(bytes[:n]) 392 | tcpActiveRecvTime = now 393 | } 394 | } 395 | 396 | clientConn.fm.Update() 397 | 398 | sendlist := clientConn.fm.GetSendList() 399 | if sendlist.Len() > 0 { 400 | sleep = false 401 | clientConn.activeSendTime = now 402 | for e := sendlist.Front(); e != nil; e = e.Next() { 403 | f := e.Value.(*frame.Frame) 404 | mb, err := clientConn.fm.MarshalFrame(f) 405 | if err != nil { 406 | loggo.Error("Error tcp Marshal %s %s %s", uuid, tcpsrcaddr.String(), err) 407 | continue 408 | } 409 | p.sequence++ 410 | sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, 411 | SEND_PROTO, RECV_PROTO, p.key, 412 | p.tcpmode, 0, 0, 0, 0, 0, 413 | 0) 414 | p.sendPacket++ 415 | p.sendPacketSize += (uint64)(len(mb)) 416 | } 417 | } 418 | 419 | if clientConn.fm.GetRecvBufferSize() > 0 { 420 | sleep = false 421 | rr := clientConn.fm.GetRecvReadLineBuffer() 422 | conn.SetWriteDeadline(time.Now().Add(time.Millisecond * 1)) 423 | n, err := conn.Write(rr) 424 | if err != nil { 425 | nerr, ok := err.(net.Error) 426 | if !ok || !nerr.Timeout() { 427 | loggo.Info("Error write tcp %s %s %s", uuid, tcpsrcaddr.String(), err) 428 | clientConn.fm.Close() 429 | break 430 | } 431 | } 432 | if n > 0 { 433 | clientConn.fm.SkipRecvBuffer(n) 434 | tcpActiveSendTime = now 435 | } 436 | } 437 | 438 | if sleep { 439 | time.Sleep(time.Millisecond * 10) 440 | } 441 | 442 | diffrecv := now.Sub(clientConn.activeRecvTime) 443 | diffsend := now.Sub(clientConn.activeSendTime) 444 | tcpdiffrecv := now.Sub(tcpActiveRecvTime) 445 | tcpdiffsend := now.Sub(tcpActiveSendTime) 446 | if diffrecv > time.Second*(time.Duration(p.timeout)) || diffsend > time.Second*(time.Duration(p.timeout)) || 447 | (tcpdiffrecv > time.Second*(time.Duration(p.timeout)) && tcpdiffsend > time.Second*(time.Duration(p.timeout))) { 448 | loggo.Info("close inactive conn %s %s", clientConn.id, clientConn.tcpaddr.String()) 449 | clientConn.fm.Close() 450 | break 451 | } 452 | 453 | if clientConn.fm.IsRemoteClosed() { 454 | loggo.Info("closed by remote conn %s %s", clientConn.id, clientConn.tcpaddr.String()) 455 | clientConn.fm.Close() 456 | break 457 | } 458 | } 459 | 460 | clientConn.fm.Close() 461 | 462 | startCloseTime := common.GetNowUpdateInSecond() 463 | for !p.exit && !clientConn.exit { 464 | now := common.GetNowUpdateInSecond() 465 | 466 | clientConn.fm.Update() 467 | 468 | sendlist := clientConn.fm.GetSendList() 469 | for e := sendlist.Front(); e != nil; e = e.Next() { 470 | f := e.Value.(*frame.Frame) 471 | mb, _ := clientConn.fm.MarshalFrame(f) 472 | p.sequence++ 473 | sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, 474 | SEND_PROTO, RECV_PROTO, p.key, 475 | p.tcpmode, 0, 0, 0, 0, 0, 476 | 0) 477 | p.sendPacket++ 478 | p.sendPacketSize += (uint64)(len(mb)) 479 | } 480 | 481 | nodatarecv := true 482 | if clientConn.fm.GetRecvBufferSize() > 0 { 483 | rr := clientConn.fm.GetRecvReadLineBuffer() 484 | conn.SetWriteDeadline(time.Now().Add(time.Millisecond * 100)) 485 | n, _ := conn.Write(rr) 486 | if n > 0 { 487 | clientConn.fm.SkipRecvBuffer(n) 488 | nodatarecv = false 489 | } 490 | } 491 | 492 | diffclose := now.Sub(startCloseTime) 493 | if diffclose > time.Second*5 { 494 | loggo.Info("close conn had timeout %s %s", clientConn.id, clientConn.tcpaddr.String()) 495 | break 496 | } 497 | 498 | remoteclosed := clientConn.fm.IsRemoteClosed() 499 | if remoteclosed && nodatarecv { 500 | loggo.Info("remote conn had closed %s %s", clientConn.id, clientConn.tcpaddr.String()) 501 | break 502 | } 503 | 504 | time.Sleep(time.Millisecond * 100) 505 | } 506 | 507 | loggo.Info("close tcp conn %s %s", clientConn.id, clientConn.tcpaddr.String()) 508 | conn.Close() 509 | p.close(clientConn) 510 | } 511 | 512 | func (p *Client) Accept() error { 513 | 514 | defer common.CrashLog() 515 | 516 | p.workResultLock.Add(1) 517 | defer p.workResultLock.Done() 518 | 519 | loggo.Info("client waiting local accept udp") 520 | 521 | bytes := make([]byte, 10240) 522 | 523 | for !p.exit { 524 | p.listenConn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) 525 | n, srcaddr, err := p.listenConn.ReadFromUDP(bytes) 526 | if err != nil { 527 | nerr, ok := err.(net.Error) 528 | if !ok || !nerr.Timeout() { 529 | loggo.Info("Error read udp %s", err) 530 | continue 531 | } 532 | } 533 | if n <= 0 { 534 | continue 535 | } 536 | 537 | now := common.GetNowUpdateInSecond() 538 | clientConn := p.getClientConnByAddr(srcaddr.String()) 539 | if clientConn == nil { 540 | if p.maxconn > 0 && p.localIdToConnMapSize >= p.maxconn { 541 | loggo.Info("too many connections %d, client accept new local udp fail %s", p.localIdToConnMapSize, srcaddr.String()) 542 | continue 543 | } 544 | uuid := common.UniqueId() 545 | clientConn = &ClientConn{exit: false, ipaddr: srcaddr, id: uuid, activeRecvTime: now, activeSendTime: now, close: false} 546 | p.addClientConn(uuid, srcaddr.String(), clientConn) 547 | loggo.Info("client accept new local udp %s %s", uuid, srcaddr.String()) 548 | } 549 | 550 | clientConn.activeSendTime = now 551 | sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, p.targetAddr, clientConn.id, (uint32)(MyMsg_DATA), bytes[:n], 552 | SEND_PROTO, RECV_PROTO, p.key, 553 | p.tcpmode, 0, 0, 0, 0, 0, 554 | p.timeout) 555 | 556 | p.sequence++ 557 | 558 | p.sendPacket++ 559 | p.sendPacketSize += (uint64)(n) 560 | } 561 | return nil 562 | } 563 | 564 | func (p *Client) processPacket(packet *Packet) { 565 | 566 | if packet.my.Rproto >= 0 { 567 | return 568 | } 569 | 570 | if packet.my.Key != (int32)(p.key) { 571 | return 572 | } 573 | 574 | if packet.echoId != p.id { 575 | return 576 | } 577 | 578 | if packet.my.Type == (int32)(MyMsg_PING) { 579 | t := time.Time{} 580 | t.UnmarshalBinary(packet.my.Data) 581 | now := time.Now() 582 | d := now.Sub(t) 583 | loggo.Info("pong from %s %s", packet.src.String(), d.String()) 584 | p.rtt = d 585 | p.pongTime = now 586 | return 587 | } 588 | 589 | if packet.my.Type == (int32)(MyMsg_KICK) { 590 | clientConn := p.getClientConnById(packet.my.Id) 591 | if clientConn != nil { 592 | p.close(clientConn) 593 | loggo.Info("remote kick local %s", packet.my.Id) 594 | } 595 | return 596 | } 597 | 598 | loggo.Debug("processPacket %s %s %d", packet.my.Id, packet.src.String(), len(packet.my.Data)) 599 | 600 | clientConn := p.getClientConnById(packet.my.Id) 601 | if clientConn == nil { 602 | loggo.Debug("processPacket no conn %s ", packet.my.Id) 603 | p.remoteError(packet.my.Id) 604 | return 605 | } 606 | 607 | now := common.GetNowUpdateInSecond() 608 | clientConn.activeRecvTime = now 609 | 610 | if p.tcpmode > 0 { 611 | f := &frame.Frame{} 612 | err := proto.Unmarshal(packet.my.Data, f) 613 | if err != nil { 614 | loggo.Error("Unmarshal tcp Error %s", err) 615 | return 616 | } 617 | 618 | clientConn.fm.OnRecvFrame(f) 619 | } else { 620 | if packet.my.Data == nil { 621 | return 622 | } 623 | addr := clientConn.ipaddr 624 | _, err := p.listenConn.WriteToUDP(packet.my.Data, addr) 625 | if err != nil { 626 | loggo.Info("WriteToUDP Error read udp %s", err) 627 | clientConn.close = true 628 | return 629 | } 630 | } 631 | 632 | p.recvPacket++ 633 | p.recvPacketSize += (uint64)(len(packet.my.Data)) 634 | } 635 | 636 | func (p *Client) close(clientConn *ClientConn) { 637 | clientConn.exit = true 638 | p.deleteClientConn(clientConn.id, clientConn.ipaddr.String()) 639 | p.deleteClientConn(clientConn.id, clientConn.tcpaddr.String()) 640 | } 641 | 642 | func (p *Client) checkTimeoutConn() { 643 | 644 | if p.tcpmode > 0 { 645 | return 646 | } 647 | 648 | tmp := make(map[string]*ClientConn) 649 | p.localIdToConnMap.Range(func(key, value interface{}) bool { 650 | id := key.(string) 651 | clientConn := value.(*ClientConn) 652 | tmp[id] = clientConn 653 | return true 654 | }) 655 | 656 | now := common.GetNowUpdateInSecond() 657 | for _, conn := range tmp { 658 | diffrecv := now.Sub(conn.activeRecvTime) 659 | diffsend := now.Sub(conn.activeSendTime) 660 | if diffrecv > time.Second*(time.Duration(p.timeout)) || diffsend > time.Second*(time.Duration(p.timeout)) { 661 | conn.close = true 662 | } 663 | } 664 | 665 | for id, conn := range tmp { 666 | if conn.close { 667 | loggo.Info("close inactive conn %s %s", id, conn.ipaddr.String()) 668 | p.close(conn) 669 | } 670 | } 671 | } 672 | 673 | func (p *Client) ping() { 674 | now := time.Now() 675 | b, _ := now.MarshalBinary() 676 | sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, "", "", (uint32)(MyMsg_PING), b, 677 | SEND_PROTO, RECV_PROTO, p.key, 678 | 0, 0, 0, 0, 0, 0, 679 | 0) 680 | loggo.Info("ping %s %s %d %d %d %d", p.addrServer, now.String(), p.sproto, p.rproto, p.id, p.sequence) 681 | p.sequence++ 682 | if now.Sub(p.pongTime) > time.Second*3 { 683 | p.rtt = 0 684 | } 685 | } 686 | 687 | func (p *Client) showNet() { 688 | p.localAddrToConnMapSize = 0 689 | p.localIdToConnMap.Range(func(key, value interface{}) bool { 690 | p.localAddrToConnMapSize++ 691 | return true 692 | }) 693 | p.localIdToConnMapSize = 0 694 | p.localIdToConnMap.Range(func(key, value interface{}) bool { 695 | p.localIdToConnMapSize++ 696 | return true 697 | }) 698 | loggo.Info("send %dPacket/s %dKB/s recv %dPacket/s %dKB/s %d/%dConnections", 699 | p.sendPacket, p.sendPacketSize/1024, p.recvPacket, p.recvPacketSize/1024, p.localAddrToConnMapSize, p.localIdToConnMapSize) 700 | p.sendPacket = 0 701 | p.recvPacket = 0 702 | p.sendPacketSize = 0 703 | p.recvPacketSize = 0 704 | } 705 | 706 | func (p *Client) AcceptSock5Conn(conn *net.TCPConn) { 707 | 708 | defer common.CrashLog() 709 | 710 | p.workResultLock.Add(1) 711 | defer p.workResultLock.Done() 712 | 713 | var err error = nil 714 | if err = network.Sock5HandshakeBy(conn); err != nil { 715 | loggo.Error("socks handshake: %s", err) 716 | conn.Close() 717 | return 718 | } 719 | _, addr, err := network.Sock5GetRequest(conn) 720 | if err != nil { 721 | loggo.Error("error getting request: %s", err) 722 | conn.Close() 723 | return 724 | } 725 | // Sending connection established message immediately to client. 726 | // This some round trip time for creating socks connection with the client. 727 | // But if connection failed, the client will get connection reset error. 728 | _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) 729 | if err != nil { 730 | loggo.Error("send connection confirmation: %s", err) 731 | conn.Close() 732 | return 733 | } 734 | 735 | loggo.Info("accept new sock5 conn: %s", addr) 736 | 737 | if p.sock5_filter == nil { 738 | p.AcceptTcpConn(conn, addr) 739 | } else { 740 | if (*p.sock5_filter)(addr) { 741 | p.AcceptTcpConn(conn, addr) 742 | return 743 | } 744 | p.AcceptDirectTcpConn(conn, addr) 745 | } 746 | } 747 | 748 | func (p *Client) addClientConn(uuid string, addr string, clientConn *ClientConn) { 749 | 750 | p.localAddrToConnMap.Store(addr, clientConn) 751 | p.localIdToConnMap.Store(uuid, clientConn) 752 | } 753 | 754 | func (p *Client) getClientConnByAddr(addr string) *ClientConn { 755 | ret, ok := p.localAddrToConnMap.Load(addr) 756 | if !ok { 757 | return nil 758 | } 759 | return ret.(*ClientConn) 760 | } 761 | 762 | func (p *Client) getClientConnById(uuid string) *ClientConn { 763 | ret, ok := p.localIdToConnMap.Load(uuid) 764 | if !ok { 765 | return nil 766 | } 767 | return ret.(*ClientConn) 768 | } 769 | 770 | func (p *Client) deleteClientConn(uuid string, addr string) { 771 | p.localIdToConnMap.Delete(uuid) 772 | p.localAddrToConnMap.Delete(addr) 773 | } 774 | 775 | func (p *Client) remoteError(uuid string) { 776 | sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, "", uuid, (uint32)(MyMsg_KICK), []byte{}, 777 | SEND_PROTO, RECV_PROTO, p.key, 778 | 0, 0, 0, 0, 0, 0, 779 | 0) 780 | } 781 | 782 | func (p *Client) AcceptDirectTcpConn(conn *net.TCPConn, targetAddr string) { 783 | 784 | defer common.CrashLog() 785 | 786 | p.workResultLock.Add(1) 787 | defer p.workResultLock.Done() 788 | 789 | tcpsrcaddr := conn.RemoteAddr().(*net.TCPAddr) 790 | 791 | loggo.Info("client accept new direct local tcp %s %s", tcpsrcaddr.String(), targetAddr) 792 | 793 | tcpaddrTarget, err := net.ResolveTCPAddr("tcp", targetAddr) 794 | if err != nil { 795 | loggo.Info("direct local tcp ResolveTCPAddr fail: %s %s", targetAddr, err.Error()) 796 | return 797 | } 798 | 799 | targetconn, err := net.DialTCP("tcp", nil, tcpaddrTarget) 800 | if err != nil { 801 | loggo.Info("direct local tcp DialTCP fail: %s %s", targetAddr, err.Error()) 802 | return 803 | } 804 | 805 | go p.transfer(conn, targetconn, conn.RemoteAddr().String(), targetconn.RemoteAddr().String()) 806 | go p.transfer(targetconn, conn, targetconn.RemoteAddr().String(), conn.RemoteAddr().String()) 807 | 808 | loggo.Info("client accept new direct local tcp ok %s %s", tcpsrcaddr.String(), targetAddr) 809 | } 810 | 811 | func (p *Client) transfer(destination io.WriteCloser, source io.ReadCloser, dst string, src string) { 812 | 813 | defer common.CrashLog() 814 | 815 | defer destination.Close() 816 | defer source.Close() 817 | loggo.Info("client begin transfer from %s -> %s", src, dst) 818 | io.Copy(destination, source) 819 | loggo.Info("client end transfer from %s -> %s", src, dst) 820 | } 821 | 822 | func (p *Client) updateServerAddr() { 823 | ipaddrServer, err := net.ResolveIPAddr("ip", p.addrServer) 824 | if err != nil { 825 | return 826 | } 827 | if p.ipaddrServer.String() != ipaddrServer.String() { 828 | p.ipaddrServer = ipaddrServer 829 | } 830 | } 831 | --------------------------------------------------------------------------------