├── .github └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── branding.png ├── cmd ├── main.go └── sip003 │ ├── README │ ├── env.go │ ├── main.go │ └── option.go ├── go.mod ├── go.sum └── pkg ├── cert ├── cert_pool_others.go └── cert_pool_windows.go ├── docs.go ├── impl ├── client.go └── server.go └── proto ├── customServiceName.go ├── generate.go ├── gun.pb.go └── gun.proto /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ dev, main ] 6 | pull_request: 7 | branches: [ dev, main ] 8 | release: 9 | types: [prereleased] 10 | 11 | jobs: 12 | build: 13 | name: Build 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | GOOS: [linux, windows, darwin, android] 18 | GOARCH: [386, amd64, arm, arm64] 19 | exclude: 20 | - { GOOS: darwin, GOARCH: arm } 21 | - { GOOS: darwin, GOARCH: arm64 } 22 | - { GOOS: darwin, GOARCH: 386 } 23 | - { GOOS: windows, GOARCH: arm64 } 24 | - { GOOS: windows, GOARCH: arm64 } 25 | - { GOOS: android, GOARCH: 386 } 26 | - { GOOS: android, GOARCH: amd64 } 27 | - { GOOS: android, GOARCH: arm } 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Set up Go 1.x 31 | uses: actions/setup-go@v2 32 | with: 33 | go-version: ^1.13 34 | - name: Check out code into the Go module directory 35 | uses: actions/checkout@v2 36 | - name: Get dependencies 37 | run: | 38 | go get -v -t -d ./... 39 | if [ -f Gopkg.toml ]; then 40 | curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 41 | dep ensure 42 | fi 43 | - name: Build dedicated 44 | env: 45 | GOOS: ${{ matrix.GOOS }} 46 | GOARCH: ${{ matrix.GOARCH }} 47 | CGO_ENABLED: 0 48 | run: go build -o gun-dedicated-${{ matrix.GOOS }}-${{ matrix.GOARCH }} github.com/Qv2ray/gun/cmd 49 | - name: Upload dedicated assets 50 | uses: actions/upload-artifact@v2.2.1 51 | with: 52 | name: gun-dedicated-${{ matrix.GOOS }}-${{ matrix.GOARCH }} 53 | path: gun-dedicated-${{ matrix.GOOS }}-${{ matrix.GOARCH }} 54 | - name: Upload dedicated release 55 | uses: svenstaro/upload-release-action@v1-release 56 | if: github.event_name == 'release' 57 | with: 58 | repo_token: ${{ secrets.GITHUB_TOKEN }} 59 | file: gun-dedicated-${{ matrix.GOOS }}-${{ matrix.GOARCH }} 60 | asset_name: gun-dedicated-${{ matrix.GOOS }}-${{ matrix.GOARCH }} 61 | tag: ${{ github.ref }} 62 | overwrite: true 63 | - name: Build SIP003 plugin 64 | env: 65 | GOOS: ${{ matrix.GOOS }} 66 | GOARCH: ${{ matrix.GOARCH }} 67 | CGO_ENABLED: 0 68 | run: go build -o gun-sip003-${{ matrix.GOOS }}-${{ matrix.GOARCH }} github.com/Qv2ray/gun/cmd/sip003 69 | - name: Upload SIP003 plugin assets 70 | uses: actions/upload-artifact@v2.2.1 71 | with: 72 | name: gun-sip003-${{ matrix.GOOS }}-${{ matrix.GOARCH }} 73 | path: gun-sip003-${{ matrix.GOOS }}-${{ matrix.GOARCH }} 74 | - name: Upload SIP003 plugin release 75 | uses: svenstaro/upload-release-action@v1-release 76 | if: github.event_name == 'release' 77 | with: 78 | repo_token: ${{ secrets.GITHUB_TOKEN }} 79 | file: gun-sip003-${{ matrix.GOOS }}-${{ matrix.GOARCH }} 80 | asset_name: gun-sip003-${{ matrix.GOOS }}-${{ matrix.GOARCH }} 81 | tag: ${{ github.ref }} 82 | overwrite: true 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # GoLand 18 | *.iml 19 | /.idea/ 20 | 21 | /.run/ 22 | *.pem 23 | *.key -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Qv2ray Workgroup et al 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gun 2 | 3 | ![branding](branding.png) 4 | 5 | You know what it means. 6 | 7 | ## Guide 8 | 9 | ### Server 10 | 11 | 1. Go to your domain in CloudFlare. In "Network" tab, turn on gRPC. 12 | 13 | 2. In "SSL/TLS" tab, choose "Source Servers" sub tab. Create a new certificate and save as `cert.pem` and `cert.key`. 14 | 15 | 3. In "DNS" Tab, add a record pointing to your own server. Make sure the proxy state is "Proxied". 16 | 17 | 4. Run and persist this on your server. This example will forward the inbound traffic to `127.0.0.1:8899`. 18 | 19 | ```bash 20 | gun -mode server -local :443 -remote 127.0.0.1:8899 -cert cert.pem -key cert.key 21 | ``` 22 | 23 | 5. If you are using a TLS termination proxy, you can set `-cleartext` parameter to use HTTP/2 Cleartext transport 24 | protocol. 25 | 26 | ### Client 27 | 28 | 1. Assume the domain of server is `grpc.example.com`. 29 | 30 | 2. Run locally and persist. This will tunnel connections from `127.0.0.1:8899` to remote. 31 | 32 | ```bash 33 | gun -mode client -local 127.0.0.1:8899 -remote grpc.example.com:443 34 | ``` 35 | 36 | There's also a SIP003 plugin version, see it's [document](cmd/sip003/README) for instruction. 37 | 38 | ## License 39 | 40 | ~~Used to be AGPL3~~ 41 | Relicensed to MIT License. 42 | -------------------------------------------------------------------------------- /branding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qv2ray/gun/9c024a68b8acc94781d77c88de31ea6d6f6a6b36/branding.png -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "strings" 7 | 8 | "github.com/Qv2ray/gun/pkg/impl" 9 | ) 10 | 11 | var ( 12 | RunMode = flag.String("mode", "", "run mode. must be client or server") 13 | LocalAddr = flag.String("local", "", "local address to listen") 14 | RemoteAddr = flag.String("remote", "", "remote address to connect") 15 | ServiceName = flag.String("name", "GunService", "") 16 | CertPath = flag.String("cert", "", "(server) certificate (*.pem) path") 17 | KeyPath = flag.String("key", "", "(server) certificate key (*.key) path") 18 | ServerName = flag.String("sni", "", "(client) optionally override SNI") 19 | Cleartext = flag.Bool("cleartext", false, "use insecure HTTP/2 cleartext mode") 20 | ) 21 | 22 | func init() { 23 | flag.Parse() 24 | } 25 | 26 | func main() { 27 | switch strings.ToLower(*RunMode) { 28 | case "client": 29 | impl.GunServiceClientImpl{ 30 | RemoteAddr: *RemoteAddr, 31 | LocalAddr: *LocalAddr, 32 | ServerName: *ServerName, 33 | Cleartext: *Cleartext, 34 | ServiceName: *ServiceName, 35 | }.Run() 36 | case "server": 37 | impl.GunServiceServerImpl{ 38 | RemoteAddr: *RemoteAddr, 39 | LocalAddr: *LocalAddr, 40 | CertPath: *CertPath, 41 | KeyPath: *KeyPath, 42 | Cleartext: *Cleartext, 43 | ServiceName: *ServiceName, 44 | }.Run() 45 | default: 46 | log.Fatalf("invalid run mode. must be client or server.") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cmd/sip003/README: -------------------------------------------------------------------------------- 1 | _ ___ ___ ____ 2 | (_) / _ \ / _ \___ \ 3 | __ _ _ _ _ __ ______ ___ _ _ __ | | | | | | |__) | 4 | / _` | | | | '_ \______/ __| | '_ \| | | | | | |__ < 5 | | (_| | |_| | | | | \__ \ | |_) | |_| | |_| |__) | 6 | \__, |\__,_|_| |_| |___/_| .__/ \___/ \___/____/ 7 | __/ | | | 8 | |___/ |_| 9 | 10 | === Intro === 11 | This is a specialized version of `gun`, which works as a SIP003 plugin. 12 | For a detailed view of SIP003 specification, please go to https://shadowsocks.org/en/wiki/Plugin.html. 13 | Please visit your shadowsocks implementation's document for configuration instruction. 14 | 15 | === Notes === 16 | Plugin Argument Examples: 17 | * Server: 18 | server:/path/to/cert.pem:/path/to/key.key 19 | * Server, without TLS 20 | server:cleartext 21 | * Client: 22 | client 23 | * Client, without TLS 24 | client:cleartext 25 | * Client, with customized SNI: 26 | client:customized-sni.example.com 27 | 28 | === Credits === 29 | @studentmain 30 | @ducksoft 31 | -------------------------------------------------------------------------------- /cmd/sip003/env.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "os" 7 | ) 8 | 9 | type SIP003Arguments struct { 10 | LocalAddr string 11 | RemoteAddr string 12 | Options string 13 | } 14 | 15 | func GetSIP003Arguments() (arguments *SIP003Arguments, err error) { 16 | localHost, exists := os.LookupEnv("SS_LOCAL_HOST") 17 | if !exists { 18 | return nil, errors.New("no local host") 19 | } 20 | localPort, exists := os.LookupEnv("SS_LOCAL_PORT") 21 | if !exists { 22 | return nil, errors.New("no local port") 23 | } 24 | remoteHost, exists := os.LookupEnv("SS_REMOTE_HOST") 25 | if !exists { 26 | return nil, errors.New("no remote host") 27 | } 28 | remotePort, exists := os.LookupEnv("SS_REMOTE_PORT") 29 | if !exists { 30 | return nil, errors.New("no remote port") 31 | } 32 | options, exists := os.LookupEnv("SS_PLUGIN_OPTIONS") 33 | if !exists { 34 | return nil, errors.New("no plugin options") 35 | } 36 | return &SIP003Arguments{ 37 | LocalAddr: net.JoinHostPort(localHost, localPort), 38 | RemoteAddr: net.JoinHostPort(remoteHost, remotePort), 39 | Options: options, 40 | }, nil 41 | } 42 | -------------------------------------------------------------------------------- /cmd/sip003/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/Qv2ray/gun/pkg/impl" 7 | ) 8 | 9 | func main() { 10 | log.Println("gun is running in SIP003 mode.") 11 | arguments, err := GetSIP003Arguments() 12 | if err != nil { 13 | log.Fatalf("failed to parse sip003 arguments: %v", err) 14 | } 15 | 16 | options, err := ParsePluginOptions(arguments.Options) 17 | if err != nil { 18 | log.Fatalf("failed to parse plugin options: %v", err) 19 | } 20 | 21 | switch options["mode"] { 22 | case "client": 23 | impl.GunServiceClientImpl{ 24 | RemoteAddr: arguments.RemoteAddr, 25 | LocalAddr: arguments.LocalAddr, 26 | ServerName: options["sni"], 27 | Cleartext: options["cleartext"] == "cleartext", 28 | }.Run() 29 | case "server": 30 | impl.GunServiceServerImpl{ 31 | RemoteAddr: arguments.LocalAddr, 32 | LocalAddr: arguments.RemoteAddr, 33 | CertPath: options["cert"], 34 | KeyPath: options["key"], 35 | Cleartext: options["cleartext"] == "cleartext", 36 | }.Run() 37 | default: 38 | log.Fatalf("unknown run mode") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cmd/sip003/option.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | type PluginOptions map[string]string 9 | 10 | // server:/path/to/cert.pem:/path/to/cert.key 11 | // server:cleartext 12 | // client 13 | // client:new-server-name.example.com 14 | // client:cleartext 15 | func ParsePluginOptions(options string) (parsedOptions PluginOptions, err error) { 16 | parts := strings.Split(options, ":") 17 | switch parts[0] { 18 | case "client": 19 | switch len(parts) { 20 | case 1: 21 | return map[string]string{ 22 | "mode": "client", 23 | }, nil 24 | case 2: 25 | if parts[1] == "cleartext" { 26 | return map[string]string{ 27 | "mode": "client", 28 | "cleartext": "cleartext", 29 | }, nil 30 | } 31 | return map[string]string{ 32 | "mode": "client", 33 | "sni": parts[1], 34 | }, nil 35 | default: 36 | return nil, errors.New("client mode expect 0 or 1 extra arguments") 37 | } 38 | case "server": 39 | switch len(parts) { 40 | case 2: 41 | return map[string]string{ 42 | "mode": "server", 43 | "cleartext": "cleartext", 44 | }, nil 45 | case 3: 46 | return map[string]string{ 47 | "mode": "server", 48 | "cert": parts[1], 49 | "key": parts[2], 50 | }, nil 51 | default: 52 | return nil, errors.New("server mode expect 1 or 2 extra arguments") 53 | } 54 | default: 55 | return nil, errors.New("unknown mode") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Qv2ray/gun 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/golang/protobuf v1.4.3 7 | google.golang.org/grpc v1.36.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 5 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 8 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 9 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 10 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 11 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 12 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 13 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 14 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 15 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 16 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 17 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 18 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 19 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 20 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 21 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 22 | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 23 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 24 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 25 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 26 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 27 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 28 | github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= 29 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 30 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 31 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 32 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 33 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 34 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 35 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 36 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 37 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 38 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 39 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 40 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 41 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 42 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 43 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= 44 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 45 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 46 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 47 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 48 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 49 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 50 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 51 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 52 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 53 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 54 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 55 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 56 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 57 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 58 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 59 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 60 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 61 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 62 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 63 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 64 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= 65 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 66 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 67 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 68 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 69 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 70 | google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As= 71 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 72 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 73 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 74 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 75 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 76 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 77 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 78 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 79 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 80 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= 81 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 83 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 84 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 85 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 86 | -------------------------------------------------------------------------------- /pkg/cert/cert_pool_others.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package cert 4 | 5 | import ( 6 | "crypto/x509" 7 | ) 8 | 9 | func GetSystemCertPool() (*x509.CertPool, error) { 10 | return x509.SystemCertPool() 11 | } 12 | -------------------------------------------------------------------------------- /pkg/cert/cert_pool_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package cert 4 | 5 | import ( 6 | "crypto/x509" 7 | "fmt" 8 | "syscall" 9 | "unsafe" 10 | ) 11 | 12 | func GetSystemCertPool() (*x509.CertPool, error) { 13 | rootU16Ptr, err := syscall.UTF16PtrFromString("Root") 14 | if err != nil { 15 | return nil, err 16 | } 17 | storeHandle, err := syscall.CertOpenSystemStore(0, rootU16Ptr) 18 | if err != nil { 19 | fmt.Println(syscall.GetLastError()) 20 | return nil, err 21 | } 22 | 23 | var certs []*x509.Certificate 24 | var cert *syscall.CertContext 25 | for { 26 | cert, err = syscall.CertEnumCertificatesInStore(storeHandle, cert) 27 | if err != nil { 28 | if errno, ok := err.(syscall.Errno); ok { 29 | if errno == 0x80092004 { 30 | break 31 | } 32 | } 33 | fmt.Println(syscall.GetLastError()) 34 | return nil, err 35 | } 36 | if cert == nil { 37 | break 38 | } 39 | // Copy the buf, since ParseCertificate does not create its own copy. 40 | buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] 41 | buf2 := make([]byte, cert.Length) 42 | copy(buf2, buf) 43 | if c, err := x509.ParseCertificate(buf2); err == nil { 44 | certs = append(certs, c) 45 | } 46 | } 47 | pool := x509.NewCertPool() 48 | for _, c := range certs { 49 | pool.AddCert(c) 50 | } 51 | return pool, nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/docs.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | -------------------------------------------------------------------------------- /pkg/impl/client.go: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | "sync" 8 | "time" 9 | 10 | "github.com/Qv2ray/gun/pkg/cert" 11 | "github.com/Qv2ray/gun/pkg/proto" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/backoff" 14 | "google.golang.org/grpc/codes" 15 | "google.golang.org/grpc/credentials" 16 | "google.golang.org/grpc/status" 17 | ) 18 | 19 | type GunServiceClientImpl struct { 20 | RemoteAddr string 21 | LocalAddr string 22 | ServerName string 23 | Cleartext bool 24 | UdpSessions *sync.Map 25 | 26 | ServiceName string 27 | } 28 | 29 | type ClientUdpSession struct { 30 | LastActive time.Time 31 | Tun proto.GunService_TunDatagramClient 32 | } 33 | 34 | func (g GunServiceClientImpl) Run() { 35 | g.UdpSessions = new(sync.Map) 36 | 37 | // start TCP local 38 | local, err := net.Listen("tcp", g.LocalAddr) 39 | if err != nil { 40 | log.Fatalf("failed to listen local: %v", err) 41 | } 42 | log.Printf("client listening tcp at %v", g.LocalAddr) 43 | 44 | // start UDP local 45 | localUdp, err := net.ListenPacket("udp", g.LocalAddr) 46 | if err != nil { 47 | log.Fatalf("failed to listen udp local: %v", err) 48 | } 49 | log.Printf("client listening udp at %v", g.LocalAddr) 50 | 51 | // select h2/h2c 52 | var dialOption grpc.DialOption 53 | if !g.Cleartext { 54 | roots, err := cert.GetSystemCertPool() 55 | if err != nil { 56 | log.Fatalf("failed to get system certificate pool") 57 | } 58 | dialOption = grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(roots, g.ServerName)) 59 | } else { 60 | dialOption = grpc.WithInsecure() 61 | } 62 | 63 | // dial 64 | conn, err := grpc.Dial( 65 | g.RemoteAddr, 66 | dialOption, 67 | grpc.WithConnectParams(grpc.ConnectParams{ 68 | Backoff: backoff.Config{ 69 | BaseDelay: 500 * time.Millisecond, 70 | Multiplier: 1.5, 71 | Jitter: 0.2, 72 | MaxDelay: 19 * time.Second, 73 | }, 74 | MinConnectTimeout: 5 * time.Second, 75 | }), 76 | ) 77 | if err != nil { 78 | log.Fatalf("failed to dial remote: %v", err) 79 | } 80 | 81 | client := proto.NewGunServiceClient(conn) 82 | // work loops 83 | go g.tcpLoop(local, client) 84 | go g.scanInactiveSession(2 * time.Minute) 85 | g.udpLoop(localUdp, client) 86 | } 87 | 88 | func (g GunServiceClientImpl) tcpLoop(local net.Listener, client proto.GunServiceClient) { 89 | clientX := client.(proto.GunServiceClientX) 90 | for { 91 | accept, err := local.Accept() 92 | if err != nil { 93 | continue 94 | } 95 | 96 | log.Printf("accepted: %v <-> %v", accept.LocalAddr(), accept.RemoteAddr()) 97 | go func() { 98 | defer accept.Close() 99 | 100 | // connect rpc 101 | tun, err := clientX.TunCustomName(context.Background(), g.ServiceName) 102 | if err != nil { 103 | log.Printf("failed to create context: %v", err) 104 | return 105 | } 106 | 107 | var wg sync.WaitGroup 108 | wg.Add(2) 109 | 110 | // down link 111 | go func() { 112 | defer wg.Done() 113 | for { 114 | recv, err := tun.Recv() 115 | if err != nil { 116 | if status.Code(err) != codes.Unavailable && status.Code(err) != codes.OutOfRange { 117 | log.Printf("remote read conn closed: %v", err) 118 | } 119 | return 120 | } 121 | _, err = accept.Write(recv.Data) 122 | if err != nil { 123 | log.Printf("local write conn closed: %v", err) 124 | return 125 | } 126 | } 127 | }() 128 | 129 | // up link 130 | go func() { 131 | defer wg.Done() 132 | buf := make([]byte, 32768) 133 | for { 134 | nRecv, err := accept.Read(buf) 135 | if err != nil { 136 | if status.Code(err) != codes.Unavailable && status.Code(err) != codes.OutOfRange { 137 | log.Printf("local read conn closed: %v", err) 138 | } 139 | if err = tun.CloseSend(); err != nil { 140 | log.Printf("remote close uplink conn fail: %v", err) 141 | } 142 | return 143 | } 144 | err = tun.Send(&proto.Hunk{Data: buf[:nRecv]}) 145 | if err != nil { 146 | log.Printf("remote write conn closed: %v", err) 147 | return 148 | } 149 | } 150 | }() 151 | 152 | wg.Wait() 153 | }() 154 | } 155 | } 156 | 157 | func (g GunServiceClientImpl) udpLoop(local net.PacketConn, client proto.GunServiceClient) { 158 | for { 159 | buf := make([]byte, 32768) 160 | l, addr, err := local.ReadFrom(buf) 161 | if err != nil { 162 | log.Printf("failed to read udp packet: %v", err) 163 | } 164 | addrStr := addr.String() 165 | 166 | // associate to exist session 167 | var session ClientUdpSession 168 | s, sessionReused := g.UdpSessions.Load(addrStr) 169 | if !sessionReused { 170 | // not exist, init new session 171 | t, err := client.TunDatagram(context.Background()) 172 | if err != nil { 173 | log.Printf("failed to create context: %v", err) 174 | return 175 | } 176 | 177 | session = ClientUdpSession{ 178 | LastActive: time.Now(), 179 | Tun: t, 180 | } 181 | g.UdpSessions.Store(addrStr, session) 182 | log.Printf("readfrom: %v <-> %v", local.LocalAddr(), addr) 183 | } else { 184 | session = s.(ClientUdpSession) 185 | } 186 | 187 | tun := session.Tun 188 | err = tun.Send(&proto.Hunk{Data: buf[:l]}) 189 | if err != nil { 190 | log.Printf("remote write packet conn closed: %v", err) 191 | continue 192 | } 193 | session.LastActive = time.Now() 194 | 195 | if sessionReused { 196 | // there's already a udp down link goroutine, let it handle down link 197 | continue 198 | } 199 | 200 | // down link 201 | go func() { 202 | for { 203 | recv, err := tun.Recv() 204 | if err != nil { 205 | if status.Code(err) != codes.Unavailable && status.Code(err) != codes.OutOfRange { 206 | log.Printf("remote read packet conn closed: %v", err) 207 | } 208 | // when error, it's obvious 209 | // when eof, it means server timed out, new assoc needed 210 | g.clearUdpSession(addrStr) 211 | return 212 | } 213 | _, err = local.WriteTo(recv.Data, addr) 214 | if err != nil { 215 | log.Printf("local write packet conn closed: %v", err) 216 | return 217 | } 218 | session.LastActive = time.Now() 219 | } 220 | }() 221 | } 222 | } 223 | 224 | func (g GunServiceClientImpl) scanInactiveSession(timeout time.Duration) { 225 | tick := time.NewTicker(timeout) 226 | for { 227 | <-tick.C 228 | needClear := make([]string, 0) 229 | now := time.Now() 230 | g.UdpSessions.Range(func(key, value interface{}) bool { 231 | session := value.(ClientUdpSession) 232 | if session.LastActive.Add(timeout).Before(now) { 233 | needClear = append(needClear, key.(string)) 234 | } 235 | return true 236 | }) 237 | 238 | for _, k := range needClear { 239 | g.clearUdpSession(k) 240 | } 241 | } 242 | } 243 | 244 | func (g GunServiceClientImpl) clearUdpSession(name string) { 245 | s, ok := g.UdpSessions.Load(name) 246 | if !ok { 247 | return 248 | } 249 | log.Printf("clear udp session %v", name) 250 | session := s.(ClientUdpSession) 251 | e := session.Tun.CloseSend() 252 | if e != nil { 253 | log.Printf("error when clear session %v, %v", name, e) 254 | } 255 | g.UdpSessions.Delete(name) 256 | } 257 | -------------------------------------------------------------------------------- /pkg/impl/server.go: -------------------------------------------------------------------------------- 1 | package impl 2 | 3 | import ( 4 | "crypto/tls" 5 | "io/ioutil" 6 | "log" 7 | "net" 8 | "sync" 9 | "time" 10 | 11 | "github.com/Qv2ray/gun/pkg/proto" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/codes" 14 | "google.golang.org/grpc/credentials" 15 | "google.golang.org/grpc/status" 16 | ) 17 | 18 | type GunServiceServerImpl struct { 19 | RemoteAddr string 20 | LocalAddr string 21 | CertPath string 22 | KeyPath string 23 | Cleartext bool 24 | UdpSessions *sync.Map 25 | 26 | ServiceName string 27 | } 28 | 29 | func (g GunServiceServerImpl) Run() { 30 | g.UdpSessions = new(sync.Map) 31 | var s *grpc.Server 32 | if !g.Cleartext { 33 | pub, err := ioutil.ReadFile(g.CertPath) 34 | if err != nil { 35 | log.Fatalf("failed to read certificate: %v", err) 36 | } 37 | key, err := ioutil.ReadFile(g.KeyPath) 38 | if err != nil { 39 | log.Fatalf("failed to read certificate key: %v", err) 40 | } 41 | cert, e := tls.X509KeyPair(pub, key) 42 | if e != nil { 43 | log.Fatalf("failed to build certificate pair: %v", e) 44 | } 45 | log.Println("certificate pair built successfully") 46 | s = grpc.NewServer(grpc.Creds(credentials.NewServerTLSFromCert(&cert))) 47 | } else { 48 | s = grpc.NewServer() 49 | } 50 | 51 | proto.RegisterGunServiceServerX(s, g, g.ServiceName) 52 | 53 | // listen local 54 | listener, e := net.Listen("tcp", g.LocalAddr) 55 | if e != nil { 56 | log.Fatalf("failed to listen: %v", e) 57 | } 58 | 59 | log.Printf("starting listening on: %v", g.LocalAddr) 60 | go g.scanInactiveSession(2 * time.Minute) 61 | e = s.Serve(listener) 62 | log.Fatalf("server abort: %v", e) 63 | } 64 | 65 | func (g GunServiceServerImpl) Tun(server proto.GunService_TunServer) error { 66 | conn, err := net.Dial("tcp", g.RemoteAddr) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | defer conn.Close() 72 | 73 | errChan := make(chan error) 74 | 75 | go func() { 76 | for { 77 | if recv, err := server.Recv(); err != nil { 78 | errChan <- err 79 | return 80 | } else if _, err = conn.Write(recv.Data); err != nil { 81 | errChan <- err 82 | return 83 | } 84 | } 85 | }() 86 | 87 | go func() { 88 | buf := make([]byte, 32768) 89 | for { 90 | if nRecv, err := conn.Read(buf); err != nil { 91 | errChan <- err 92 | return 93 | } else if err = server.Send(&proto.Hunk{Data: buf[:nRecv]}); err != nil { 94 | errChan <- err 95 | return 96 | } 97 | } 98 | }() 99 | 100 | err = <-errChan 101 | return err 102 | } 103 | 104 | type ServerUdpSession struct { 105 | LastActive time.Time 106 | Tun proto.GunService_TunDatagramServer 107 | Socket net.PacketConn 108 | } 109 | 110 | func (g GunServiceServerImpl) TunDatagram(server proto.GunService_TunDatagramServer) error { 111 | raddr, err := net.ResolveUDPAddr("udp", g.RemoteAddr) 112 | if err != nil { 113 | return err 114 | } 115 | conn, err := net.ListenPacket("udp", ":0") 116 | if err != nil { 117 | return err 118 | } 119 | log.Printf("start new udp session %v <-> %v", conn.LocalAddr(), g.RemoteAddr) 120 | sessionName := conn.LocalAddr().String() 121 | session := ServerUdpSession{ 122 | LastActive: time.Now(), 123 | Tun: server, 124 | Socket: conn, 125 | } 126 | g.UdpSessions.Store(sessionName, session) 127 | 128 | defer g.clearUdpSession(sessionName) 129 | 130 | errChan := make(chan error) 131 | 132 | var wg sync.WaitGroup 133 | wg.Add(2) 134 | 135 | // up link 136 | go func() { 137 | defer wg.Done() 138 | for { 139 | if recv, err := server.Recv(); err != nil { 140 | if status.Code(err) != codes.Unavailable && status.Code(err) != codes.OutOfRange { 141 | // report only when not eof, eof is not error 142 | errChan <- err 143 | } else { 144 | errChan <- nil 145 | } 146 | return 147 | } else if _, err = conn.WriteTo(recv.Data, raddr); err != nil { 148 | errChan <- err 149 | return 150 | } 151 | session.LastActive = time.Now() 152 | } 153 | }() 154 | go func() { 155 | defer wg.Done() 156 | buf := make([]byte, 32768) 157 | for { 158 | nRecv, remote, err := conn.ReadFrom(buf) 159 | if err != nil { 160 | errChan <- err 161 | return 162 | } 163 | if remote.String() != raddr.String() { 164 | continue 165 | } 166 | if err = server.Send(&proto.Hunk{Data: buf[:nRecv]}); err != nil { 167 | errChan <- err 168 | return 169 | } 170 | session.LastActive = time.Now() 171 | } 172 | }() 173 | // wait both direction complete 174 | wg.Wait() 175 | err = <-errChan 176 | return err 177 | } 178 | 179 | func (g GunServiceServerImpl) scanInactiveSession(timeout time.Duration) { 180 | tick := time.NewTicker(timeout) 181 | for { 182 | <-tick.C 183 | needClear := make([]string, 0) 184 | now := time.Now() 185 | g.UdpSessions.Range(func(key, value interface{}) bool { 186 | session := value.(ServerUdpSession) 187 | if session.LastActive.Add(timeout).Before(now) { 188 | needClear = append(needClear, key.(string)) 189 | } 190 | return true 191 | }) 192 | 193 | for _, k := range needClear { 194 | g.clearUdpSession(k) 195 | } 196 | } 197 | } 198 | 199 | func (g GunServiceServerImpl) clearUdpSession(name string) { 200 | s, ok := g.UdpSessions.Load(name) 201 | if !ok { 202 | return 203 | } 204 | log.Printf("clear udp session %v", name) 205 | session := s.(ServerUdpSession) 206 | e := session.Socket.Close() 207 | if e != nil { 208 | log.Printf("error when clear session %v, %v", name, e) 209 | } 210 | g.UdpSessions.Delete(name) 211 | } 212 | -------------------------------------------------------------------------------- /pkg/proto/customServiceName.go: -------------------------------------------------------------------------------- 1 | package proto 2 | 3 | import ( 4 | "context" 5 | "google.golang.org/grpc" 6 | ) 7 | 8 | func ServerDesc(name string) grpc.ServiceDesc { 9 | return grpc.ServiceDesc{ 10 | ServiceName: name, 11 | HandlerType: (*GunServiceServer)(nil), 12 | Methods: []grpc.MethodDesc{}, 13 | Streams: []grpc.StreamDesc{ 14 | { 15 | StreamName: "Tun", 16 | Handler: _GunService_Tun_Handler, 17 | ServerStreams: true, 18 | ClientStreams: true, 19 | }, 20 | { 21 | StreamName: "TunDatagram", 22 | Handler: _GunService_TunDatagram_Handler, 23 | ServerStreams: true, 24 | ClientStreams: true, 25 | }, 26 | }, 27 | Metadata: "gun.proto", 28 | } 29 | } 30 | 31 | func (c *gunServiceClient) TunCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GunService_TunClient, error) { 32 | stream, err := c.cc.NewStream(ctx, &ServerDesc(name).Streams[0], "/"+name+"/Tun", opts...) 33 | if err != nil { 34 | return nil, err 35 | } 36 | x := &gunServiceTunClient{stream} 37 | return x, nil 38 | } 39 | 40 | type GunServiceClientX interface { 41 | TunCustomName(ctx context.Context, name string, opts ...grpc.CallOption) (GunService_TunClient, error) 42 | Tun(ctx context.Context, opts ...grpc.CallOption) (GunService_TunClient, error) 43 | TunDatagram(ctx context.Context, opts ...grpc.CallOption) (GunService_TunDatagramClient, error) 44 | } 45 | 46 | func RegisterGunServiceServerX(s *grpc.Server, srv GunServiceServer, name string) { 47 | desc := ServerDesc(name) 48 | s.RegisterService(&desc, srv) 49 | } 50 | -------------------------------------------------------------------------------- /pkg/proto/generate.go: -------------------------------------------------------------------------------- 1 | package proto 2 | 3 | //go:generate protoc gun.proto --go_out=plugins=grpc:. --go_opt=paths=source_relative 4 | -------------------------------------------------------------------------------- /pkg/proto/gun.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: gun.proto 3 | 4 | package proto 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | grpc "google.golang.org/grpc" 11 | codes "google.golang.org/grpc/codes" 12 | status "google.golang.org/grpc/status" 13 | math "math" 14 | ) 15 | 16 | // Reference imports to suppress errors if they are not otherwise used. 17 | var _ = proto.Marshal 18 | var _ = fmt.Errorf 19 | var _ = math.Inf 20 | 21 | // This is a compile-time assertion to ensure that this generated file 22 | // is compatible with the proto package it is being compiled against. 23 | // A compilation error at this line likely means your copy of the 24 | // proto package needs to be updated. 25 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 26 | 27 | type Hunk struct { 28 | Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` 29 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 30 | XXX_unrecognized []byte `json:"-"` 31 | XXX_sizecache int32 `json:"-"` 32 | } 33 | 34 | func (m *Hunk) Reset() { *m = Hunk{} } 35 | func (m *Hunk) String() string { return proto.CompactTextString(m) } 36 | func (*Hunk) ProtoMessage() {} 37 | func (*Hunk) Descriptor() ([]byte, []int) { 38 | return fileDescriptor_5eb68c7936423302, []int{0} 39 | } 40 | 41 | func (m *Hunk) XXX_Unmarshal(b []byte) error { 42 | return xxx_messageInfo_Hunk.Unmarshal(m, b) 43 | } 44 | func (m *Hunk) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 45 | return xxx_messageInfo_Hunk.Marshal(b, m, deterministic) 46 | } 47 | func (m *Hunk) XXX_Merge(src proto.Message) { 48 | xxx_messageInfo_Hunk.Merge(m, src) 49 | } 50 | func (m *Hunk) XXX_Size() int { 51 | return xxx_messageInfo_Hunk.Size(m) 52 | } 53 | func (m *Hunk) XXX_DiscardUnknown() { 54 | xxx_messageInfo_Hunk.DiscardUnknown(m) 55 | } 56 | 57 | var xxx_messageInfo_Hunk proto.InternalMessageInfo 58 | 59 | func (m *Hunk) GetData() []byte { 60 | if m != nil { 61 | return m.Data 62 | } 63 | return nil 64 | } 65 | 66 | func init() { 67 | proto.RegisterType((*Hunk)(nil), "Hunk") 68 | } 69 | 70 | func init() { proto.RegisterFile("gun.proto", fileDescriptor_5eb68c7936423302) } 71 | 72 | var fileDescriptor_5eb68c7936423302 = []byte{ 73 | // 146 bytes of a gzipped FileDescriptorProto 74 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x2f, 0xcd, 0xd3, 75 | 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x92, 0xe2, 0x62, 0xf1, 0x28, 0xcd, 0xcb, 0x16, 0x12, 0xe2, 76 | 0x62, 0x49, 0x49, 0x2c, 0x49, 0x94, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x09, 0x02, 0xb3, 0x8d, 0xdc, 77 | 0xb8, 0xb8, 0xdc, 0x4b, 0xf3, 0x82, 0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, 0x85, 0xc4, 0xb9, 0x98, 78 | 0x43, 0x4a, 0xf3, 0x84, 0x58, 0xf5, 0x40, 0xea, 0xa5, 0x20, 0x94, 0x06, 0xa3, 0x01, 0xa3, 0x90, 79 | 0x3c, 0x17, 0x77, 0x48, 0x69, 0x9e, 0x4b, 0x62, 0x49, 0x62, 0x7a, 0x51, 0x62, 0x2e, 0xa6, 0x02, 80 | 0x27, 0xc5, 0x28, 0xf9, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xc0, 81 | 0x32, 0xa3, 0xa2, 0xc4, 0x4a, 0xfd, 0xf4, 0xd2, 0x3c, 0xfd, 0x82, 0xec, 0x74, 0x7d, 0xb0, 0x33, 82 | 0x92, 0xd8, 0xc0, 0x94, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xae, 0x3c, 0x46, 0x28, 0x9a, 0x00, 83 | 0x00, 0x00, 84 | } 85 | 86 | // Reference imports to suppress errors if they are not otherwise used. 87 | var _ context.Context 88 | var _ grpc.ClientConn 89 | 90 | // This is a compile-time assertion to ensure that this generated file 91 | // is compatible with the grpc package it is being compiled against. 92 | const _ = grpc.SupportPackageIsVersion4 93 | 94 | // GunServiceClient is the client API for GunService service. 95 | // 96 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 97 | type GunServiceClient interface { 98 | Tun(ctx context.Context, opts ...grpc.CallOption) (GunService_TunClient, error) 99 | TunDatagram(ctx context.Context, opts ...grpc.CallOption) (GunService_TunDatagramClient, error) 100 | } 101 | 102 | type gunServiceClient struct { 103 | cc *grpc.ClientConn 104 | } 105 | 106 | func NewGunServiceClient(cc *grpc.ClientConn) GunServiceClient { 107 | return &gunServiceClient{cc} 108 | } 109 | 110 | func (c *gunServiceClient) Tun(ctx context.Context, opts ...grpc.CallOption) (GunService_TunClient, error) { 111 | stream, err := c.cc.NewStream(ctx, &_GunService_serviceDesc.Streams[0], "/GunService/Tun", opts...) 112 | if err != nil { 113 | return nil, err 114 | } 115 | x := &gunServiceTunClient{stream} 116 | return x, nil 117 | } 118 | 119 | type GunService_TunClient interface { 120 | Send(*Hunk) error 121 | Recv() (*Hunk, error) 122 | grpc.ClientStream 123 | } 124 | 125 | type gunServiceTunClient struct { 126 | grpc.ClientStream 127 | } 128 | 129 | func (x *gunServiceTunClient) Send(m *Hunk) error { 130 | return x.ClientStream.SendMsg(m) 131 | } 132 | 133 | func (x *gunServiceTunClient) Recv() (*Hunk, error) { 134 | m := new(Hunk) 135 | if err := x.ClientStream.RecvMsg(m); err != nil { 136 | return nil, err 137 | } 138 | return m, nil 139 | } 140 | 141 | func (c *gunServiceClient) TunDatagram(ctx context.Context, opts ...grpc.CallOption) (GunService_TunDatagramClient, error) { 142 | stream, err := c.cc.NewStream(ctx, &_GunService_serviceDesc.Streams[1], "/GunService/TunDatagram", opts...) 143 | if err != nil { 144 | return nil, err 145 | } 146 | x := &gunServiceTunDatagramClient{stream} 147 | return x, nil 148 | } 149 | 150 | type GunService_TunDatagramClient interface { 151 | Send(*Hunk) error 152 | Recv() (*Hunk, error) 153 | grpc.ClientStream 154 | } 155 | 156 | type gunServiceTunDatagramClient struct { 157 | grpc.ClientStream 158 | } 159 | 160 | func (x *gunServiceTunDatagramClient) Send(m *Hunk) error { 161 | return x.ClientStream.SendMsg(m) 162 | } 163 | 164 | func (x *gunServiceTunDatagramClient) Recv() (*Hunk, error) { 165 | m := new(Hunk) 166 | if err := x.ClientStream.RecvMsg(m); err != nil { 167 | return nil, err 168 | } 169 | return m, nil 170 | } 171 | 172 | // GunServiceServer is the server API for GunService service. 173 | type GunServiceServer interface { 174 | Tun(GunService_TunServer) error 175 | TunDatagram(GunService_TunDatagramServer) error 176 | } 177 | 178 | // UnimplementedGunServiceServer can be embedded to have forward compatible implementations. 179 | type UnimplementedGunServiceServer struct { 180 | } 181 | 182 | func (*UnimplementedGunServiceServer) Tun(srv GunService_TunServer) error { 183 | return status.Errorf(codes.Unimplemented, "method Tun not implemented") 184 | } 185 | func (*UnimplementedGunServiceServer) TunDatagram(srv GunService_TunDatagramServer) error { 186 | return status.Errorf(codes.Unimplemented, "method TunDatagram not implemented") 187 | } 188 | 189 | func RegisterGunServiceServer(s *grpc.Server, srv GunServiceServer) { 190 | s.RegisterService(&_GunService_serviceDesc, srv) 191 | } 192 | 193 | func _GunService_Tun_Handler(srv interface{}, stream grpc.ServerStream) error { 194 | return srv.(GunServiceServer).Tun(&gunServiceTunServer{stream}) 195 | } 196 | 197 | type GunService_TunServer interface { 198 | Send(*Hunk) error 199 | Recv() (*Hunk, error) 200 | grpc.ServerStream 201 | } 202 | 203 | type gunServiceTunServer struct { 204 | grpc.ServerStream 205 | } 206 | 207 | func (x *gunServiceTunServer) Send(m *Hunk) error { 208 | return x.ServerStream.SendMsg(m) 209 | } 210 | 211 | func (x *gunServiceTunServer) Recv() (*Hunk, error) { 212 | m := new(Hunk) 213 | if err := x.ServerStream.RecvMsg(m); err != nil { 214 | return nil, err 215 | } 216 | return m, nil 217 | } 218 | 219 | func _GunService_TunDatagram_Handler(srv interface{}, stream grpc.ServerStream) error { 220 | return srv.(GunServiceServer).TunDatagram(&gunServiceTunDatagramServer{stream}) 221 | } 222 | 223 | type GunService_TunDatagramServer interface { 224 | Send(*Hunk) error 225 | Recv() (*Hunk, error) 226 | grpc.ServerStream 227 | } 228 | 229 | type gunServiceTunDatagramServer struct { 230 | grpc.ServerStream 231 | } 232 | 233 | func (x *gunServiceTunDatagramServer) Send(m *Hunk) error { 234 | return x.ServerStream.SendMsg(m) 235 | } 236 | 237 | func (x *gunServiceTunDatagramServer) Recv() (*Hunk, error) { 238 | m := new(Hunk) 239 | if err := x.ServerStream.RecvMsg(m); err != nil { 240 | return nil, err 241 | } 242 | return m, nil 243 | } 244 | 245 | var _GunService_serviceDesc = grpc.ServiceDesc{ 246 | ServiceName: "GunService", 247 | HandlerType: (*GunServiceServer)(nil), 248 | Methods: []grpc.MethodDesc{}, 249 | Streams: []grpc.StreamDesc{ 250 | { 251 | StreamName: "Tun", 252 | Handler: _GunService_Tun_Handler, 253 | ServerStreams: true, 254 | ClientStreams: true, 255 | }, 256 | { 257 | StreamName: "TunDatagram", 258 | Handler: _GunService_TunDatagram_Handler, 259 | ServerStreams: true, 260 | ClientStreams: true, 261 | }, 262 | }, 263 | Metadata: "gun.proto", 264 | } 265 | -------------------------------------------------------------------------------- /pkg/proto/gun.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/Qv2ray/gun/pkg/proto"; 3 | 4 | message Hunk { 5 | bytes data = 1; 6 | } 7 | 8 | service GunService { 9 | rpc Tun (stream Hunk) returns (stream Hunk); 10 | rpc TunDatagram (stream Hunk) returns (stream Hunk); 11 | } 12 | --------------------------------------------------------------------------------