mirror of
https://github.com/nadoo/glider.git
synced 2025-04-22 04:02:07 +08:00
feat: support gRPC client
This commit is contained in:
parent
d57d35c062
commit
7f6b4ceb12
@ -5,6 +5,7 @@ import (
|
||||
// _ "github.com/nadoo/glider/service/xxx"
|
||||
|
||||
// comment out the protocols you don't need to make the compiled binary smaller.
|
||||
_ "github.com/nadoo/glider/proxy/grpc"
|
||||
_ "github.com/nadoo/glider/proxy/http"
|
||||
_ "github.com/nadoo/glider/proxy/kcp"
|
||||
_ "github.com/nadoo/glider/proxy/mixed"
|
||||
|
9
go.mod
9
go.mod
@ -3,6 +3,7 @@ module github.com/nadoo/glider
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/Qv2ray/gun v0.0.0-20210314140700-95a65981f2f8
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
|
||||
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d
|
||||
github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb
|
||||
@ -12,11 +13,15 @@ require (
|
||||
github.com/nadoo/ipset v0.5.0
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
|
||||
google.golang.org/grpc v1.36.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.16 // indirect
|
||||
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 // indirect
|
||||
@ -26,7 +31,9 @@ require (
|
||||
github.com/templexxx/xorsimd v0.4.1 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
)
|
||||
|
||||
// Replace dependency modules with local developing copy
|
||||
|
25
go.sum
25
go.sum
@ -1,10 +1,13 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Qv2ray/gun v0.0.0-20210314140700-95a65981f2f8 h1:vXZO6gMcQwOcFR8cF/brd/F6vP5pk5fAI7Az6avsbFw=
|
||||
github.com/Qv2ray/gun v0.0.0-20210314140700-95a65981f2f8/go.mod h1:BxLCVtH80Ox0wPYPeX1BjEXQGUI2cTONpACpac6Ri98=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d h1:CPqTNIigGweVPT4CYb+OO2E6XyRKFOmvTHwWRLgCAlE=
|
||||
@ -16,7 +19,9 @@ github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152/go.mod h1:I9fhc/EvS
|
||||
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 h1:fBHFH+Y/GPGFGo7LIrErQc3p2MeAhoIQNgaxPWYsSxk=
|
||||
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:ucvhdsUCE3TH0LoLRb6ShHiJl8e39dGlx6A4g/ujlow=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@ -29,14 +34,20 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo=
|
||||
@ -84,6 +95,7 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||
@ -169,6 +181,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@ -181,22 +195,33 @@ golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
408
proxy/grpc/client.go
Normal file
408
proxy/grpc/client.go
Normal file
@ -0,0 +1,408 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"github.com/Qv2ray/gun/pkg/cert"
|
||||
"github.com/Qv2ray/gun/pkg/proto"
|
||||
"github.com/nadoo/glider/pkg/pool"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/backoff"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/status"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// https://github.com/v2fly/v2ray-core/blob/v5.0.6/transport/internet/grpc/dial.go
|
||||
var (
|
||||
globalCCMap map[string]*grpc.ClientConn
|
||||
globalCCAccess sync.Mutex
|
||||
)
|
||||
|
||||
type ccCanceller func()
|
||||
|
||||
type ClientConn struct {
|
||||
tun proto.GunService_TunClient
|
||||
closer context.CancelFunc
|
||||
muReading sync.Mutex // muReading protects reading
|
||||
muWriting sync.Mutex // muWriting protects writing
|
||||
muRecv sync.Mutex // muReading protects recv
|
||||
muSend sync.Mutex // muWriting protects send
|
||||
buf []byte
|
||||
offset int
|
||||
|
||||
deadlineMu sync.Mutex
|
||||
readDeadline *time.Timer
|
||||
writeDeadline *time.Timer
|
||||
readClosed chan struct{}
|
||||
writeClosed chan struct{}
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
func NewClientConn(tun proto.GunService_TunClient, closer context.CancelFunc) *ClientConn {
|
||||
return &ClientConn{
|
||||
tun: tun,
|
||||
closer: closer,
|
||||
readClosed: make(chan struct{}),
|
||||
writeClosed: make(chan struct{}),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
type RecvResp struct {
|
||||
hunk *proto.Hunk
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *ClientConn) Read(p []byte) (n int, err error) {
|
||||
select {
|
||||
case <-c.readClosed:
|
||||
return 0, os.ErrDeadlineExceeded
|
||||
case <-c.closed:
|
||||
return 0, io.EOF
|
||||
default:
|
||||
}
|
||||
|
||||
c.muReading.Lock()
|
||||
defer c.muReading.Unlock()
|
||||
if c.buf != nil {
|
||||
n = copy(p, c.buf[c.offset:])
|
||||
c.offset += n
|
||||
if c.offset == len(c.buf) {
|
||||
pool.PutBuffer(c.buf)
|
||||
c.buf = nil
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
// set 1 to avoid channel leak
|
||||
readDone := make(chan RecvResp, 1)
|
||||
// pass channel to the function to avoid closure leak
|
||||
go func(readDone chan RecvResp) {
|
||||
// FIXME: not really abort the send so there is some problems when recover
|
||||
c.muRecv.Lock()
|
||||
defer c.muRecv.Unlock()
|
||||
recv, e := c.tun.Recv()
|
||||
readDone <- RecvResp{
|
||||
hunk: recv,
|
||||
err: e,
|
||||
}
|
||||
}(readDone)
|
||||
select {
|
||||
case <-c.readClosed:
|
||||
return 0, os.ErrDeadlineExceeded
|
||||
case <-c.closed:
|
||||
return 0, io.EOF
|
||||
case recvResp := <-readDone:
|
||||
err = recvResp.err
|
||||
if err != nil {
|
||||
if code := status.Code(err); code == codes.Unavailable || status.Code(err) == codes.OutOfRange {
|
||||
err = io.EOF
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
n = copy(p, recvResp.hunk.Data)
|
||||
c.buf = pool.GetBuffer(len(recvResp.hunk.Data) - n)
|
||||
copy(c.buf, recvResp.hunk.Data[n:])
|
||||
c.offset = 0
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientConn) Write(p []byte) (n int, err error) {
|
||||
select {
|
||||
case <-c.writeClosed:
|
||||
return 0, os.ErrDeadlineExceeded
|
||||
case <-c.closed:
|
||||
return 0, io.EOF
|
||||
default:
|
||||
}
|
||||
|
||||
c.muWriting.Lock()
|
||||
defer c.muWriting.Unlock()
|
||||
// set 1 to avoid channel leak
|
||||
sendDone := make(chan error, 1)
|
||||
// pass channel to the function to avoid closure leak
|
||||
go func(sendDone chan error) {
|
||||
// FIXME: not really abort the send so there is some problems when recover
|
||||
c.muSend.Lock()
|
||||
defer c.muSend.Unlock()
|
||||
e := c.tun.Send(&proto.Hunk{Data: p})
|
||||
sendDone <- e
|
||||
}(sendDone)
|
||||
select {
|
||||
case <-c.writeClosed:
|
||||
return 0, os.ErrDeadlineExceeded
|
||||
case <-c.closed:
|
||||
return 0, io.EOF
|
||||
case err = <-sendDone:
|
||||
if code := status.Code(err); code == codes.Unavailable || status.Code(err) == codes.OutOfRange {
|
||||
err = io.EOF
|
||||
}
|
||||
return len(p), err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClientConn) Close() error {
|
||||
select {
|
||||
case <-c.closed:
|
||||
default:
|
||||
close(c.closed)
|
||||
}
|
||||
c.closer()
|
||||
return nil
|
||||
}
|
||||
func (c *ClientConn) CloseWrite() error {
|
||||
return c.tun.CloseSend()
|
||||
}
|
||||
func (c *ClientConn) LocalAddr() net.Addr {
|
||||
// FIXME
|
||||
return nil
|
||||
}
|
||||
func (c *ClientConn) RemoteAddr() net.Addr {
|
||||
p, _ := peer.FromContext(c.tun.Context())
|
||||
return p.Addr
|
||||
}
|
||||
|
||||
func (c *ClientConn) SetDeadline(t time.Time) error {
|
||||
c.deadlineMu.Lock()
|
||||
defer c.deadlineMu.Unlock()
|
||||
if now := time.Now(); t.After(now) {
|
||||
// refresh the deadline if the deadline has been exceeded
|
||||
select {
|
||||
case <-c.readClosed:
|
||||
c.readClosed = make(chan struct{})
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case <-c.writeClosed:
|
||||
c.writeClosed = make(chan struct{})
|
||||
default:
|
||||
}
|
||||
// reset the deadline timer to make the c.readClosed and c.writeClosed with the new pointer (if it is)
|
||||
if c.readDeadline != nil {
|
||||
c.readDeadline.Stop()
|
||||
}
|
||||
c.readDeadline = time.AfterFunc(t.Sub(now), func() {
|
||||
c.deadlineMu.Lock()
|
||||
defer c.deadlineMu.Unlock()
|
||||
select {
|
||||
case <-c.readClosed:
|
||||
default:
|
||||
close(c.readClosed)
|
||||
}
|
||||
})
|
||||
if c.writeDeadline != nil {
|
||||
c.writeDeadline.Stop()
|
||||
}
|
||||
c.writeDeadline = time.AfterFunc(t.Sub(now), func() {
|
||||
c.deadlineMu.Lock()
|
||||
defer c.deadlineMu.Unlock()
|
||||
select {
|
||||
case <-c.writeClosed:
|
||||
default:
|
||||
close(c.writeClosed)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
select {
|
||||
case <-c.readClosed:
|
||||
default:
|
||||
close(c.readClosed)
|
||||
}
|
||||
select {
|
||||
case <-c.writeClosed:
|
||||
default:
|
||||
close(c.writeClosed)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClientConn) SetReadDeadline(t time.Time) error {
|
||||
c.deadlineMu.Lock()
|
||||
defer c.deadlineMu.Unlock()
|
||||
if now := time.Now(); t.After(now) {
|
||||
// refresh the deadline if the deadline has been exceeded
|
||||
select {
|
||||
case <-c.readClosed:
|
||||
c.readClosed = make(chan struct{})
|
||||
default:
|
||||
}
|
||||
// reset the deadline timer to make the c.readClosed and c.writeClosed with the new pointer (if it is)
|
||||
if c.readDeadline != nil {
|
||||
c.readDeadline.Stop()
|
||||
}
|
||||
c.readDeadline = time.AfterFunc(t.Sub(now), func() {
|
||||
c.deadlineMu.Lock()
|
||||
defer c.deadlineMu.Unlock()
|
||||
select {
|
||||
case <-c.readClosed:
|
||||
default:
|
||||
close(c.readClosed)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
select {
|
||||
case <-c.readClosed:
|
||||
default:
|
||||
close(c.readClosed)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClientConn) SetWriteDeadline(t time.Time) error {
|
||||
c.deadlineMu.Lock()
|
||||
defer c.deadlineMu.Unlock()
|
||||
if now := time.Now(); t.After(now) {
|
||||
// refresh the deadline if the deadline has been exceeded
|
||||
select {
|
||||
case <-c.writeClosed:
|
||||
c.writeClosed = make(chan struct{})
|
||||
default:
|
||||
}
|
||||
if c.writeDeadline != nil {
|
||||
c.writeDeadline.Stop()
|
||||
}
|
||||
c.writeDeadline = time.AfterFunc(t.Sub(now), func() {
|
||||
c.deadlineMu.Lock()
|
||||
defer c.deadlineMu.Unlock()
|
||||
select {
|
||||
case <-c.writeClosed:
|
||||
default:
|
||||
close(c.writeClosed)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
select {
|
||||
case <-c.writeClosed:
|
||||
default:
|
||||
close(c.writeClosed)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewGRPCDialer returns a gRPC proxy dialer.
|
||||
func NewGRPCDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
|
||||
w, err := NewGRPC(s, d, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[gRPC] create instance error: %s", err)
|
||||
}
|
||||
|
||||
if w.certFile != "" {
|
||||
certData, err := os.ReadFile(w.certFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[gRPC] read cert file error: %s", err)
|
||||
}
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
if !certPool.AppendCertsFromPEM(certData) {
|
||||
return nil, fmt.Errorf("[gRPC] can not append cert file: %s", w.certFile)
|
||||
}
|
||||
w.certPool = certPool
|
||||
} else {
|
||||
w.certPool, err = cert.GetSystemCertPool()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get system certificate pool")
|
||||
}
|
||||
}
|
||||
|
||||
return w, err
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address.
|
||||
func (g *GRPC) Addr() string {
|
||||
if g.addr == "" {
|
||||
return g.dialer.Addr()
|
||||
}
|
||||
return g.addr
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (g *GRPC) Dial(network string, address string) (net.Conn, error) {
|
||||
cc, cancel, err := getGrpcClientConn(g.dialer, g.addr, g.serverName, g.certPool, g.skipVerify)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
client := proto.NewGunServiceClient(cc)
|
||||
|
||||
clientX := client.(proto.GunServiceClientX)
|
||||
serviceName := g.serviceName
|
||||
if serviceName == "" {
|
||||
serviceName = "GunService"
|
||||
}
|
||||
// ctx is the lifetime of the tun
|
||||
ctxStream, streamCloser := context.WithCancel(context.Background())
|
||||
tun, err := clientX.TunCustomName(ctxStream, serviceName)
|
||||
if err != nil {
|
||||
streamCloser()
|
||||
return nil, err
|
||||
}
|
||||
return NewClientConn(tun, streamCloser), nil
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (g *GRPC) DialUDP(network, addr string) (net.PacketConn, error) {
|
||||
return nil, proxy.ErrNotSupported
|
||||
}
|
||||
|
||||
func getGrpcClientConn(dialer proxy.Dialer, address string, serverName string, certPool *x509.CertPool, skipVerify bool) (*grpc.ClientConn, ccCanceller, error) {
|
||||
globalCCAccess.Lock()
|
||||
if globalCCMap == nil {
|
||||
globalCCMap = make(map[string]*grpc.ClientConn)
|
||||
}
|
||||
globalCCAccess.Unlock()
|
||||
|
||||
canceller := func() {
|
||||
globalCCAccess.Lock()
|
||||
defer globalCCAccess.Unlock()
|
||||
globalCCMap[address].Close()
|
||||
delete(globalCCMap, address)
|
||||
}
|
||||
|
||||
// TODO Should support chain proxy to the same destination
|
||||
globalCCAccess.Lock()
|
||||
if client, found := globalCCMap[address]; found && client.GetState() != connectivity.Shutdown {
|
||||
globalCCAccess.Unlock()
|
||||
return client, canceller, nil
|
||||
}
|
||||
globalCCAccess.Unlock()
|
||||
dialOptions := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(certPool, serverName)),
|
||||
grpc.WithContextDialer(func(ctxGrpc context.Context, s string) (net.Conn, error) {
|
||||
return dialer.Dial("tcp", s)
|
||||
}), grpc.WithConnectParams(grpc.ConnectParams{
|
||||
Backoff: backoff.Config{
|
||||
BaseDelay: 500 * time.Millisecond,
|
||||
Multiplier: 1.5,
|
||||
Jitter: 0.2,
|
||||
MaxDelay: 19 * time.Second,
|
||||
},
|
||||
MinConnectTimeout: 5 * time.Second,
|
||||
}), grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: 30 * time.Second,
|
||||
Timeout: 10 * time.Second,
|
||||
PermitWithoutStream: true,
|
||||
}),
|
||||
}
|
||||
if skipVerify {
|
||||
dialOptions = append(dialOptions, grpc.WithInsecure())
|
||||
}
|
||||
cc, err := grpc.Dial(address, dialOptions...)
|
||||
globalCCAccess.Lock()
|
||||
globalCCMap[address] = cc
|
||||
globalCCAccess.Unlock()
|
||||
return cc, canceller, err
|
||||
}
|
69
proxy/grpc/grpc.go
Normal file
69
proxy/grpc/grpc.go
Normal file
@ -0,0 +1,69 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("grpc", NewGRPCDialer)
|
||||
}
|
||||
|
||||
// GRPC is the base gRPC proxy struct.
|
||||
type GRPC struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
certPool *x509.CertPool
|
||||
serviceName string
|
||||
serverName string
|
||||
skipVerify bool
|
||||
certFile string
|
||||
}
|
||||
|
||||
// NewGRPC returns a websocket proxy.
|
||||
func NewGRPC(s string, d proxy.Dialer, p proxy.Proxy) (*GRPC, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse url err: %s", err)
|
||||
}
|
||||
|
||||
addr := u.Host
|
||||
if addr == "" && d != nil {
|
||||
addr = d.Addr()
|
||||
}
|
||||
|
||||
if _, p, _ := net.SplitHostPort(addr); p == "" {
|
||||
addr = net.JoinHostPort(addr, "443")
|
||||
}
|
||||
|
||||
query := u.Query()
|
||||
g := &GRPC{
|
||||
dialer: d,
|
||||
addr: addr,
|
||||
skipVerify: query.Get("skipVerify") == "true",
|
||||
serviceName: query.Get("serviceName"),
|
||||
serverName: query.Get("serverName"),
|
||||
certFile: query.Get("cert"),
|
||||
}
|
||||
|
||||
if g.serviceName == "" {
|
||||
g.serviceName = "GunService"
|
||||
}
|
||||
|
||||
if g.serverName == "" {
|
||||
g.serverName = g.addr[:strings.LastIndex(g.addr, ":")]
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.AddUsage("grpc", `
|
||||
gRPC client scheme:
|
||||
grpc://host:port[?serviceName=SERVICENAME][&serverName=SERVERNAME][&skipVerify=true][&cert=PATH]
|
||||
`)
|
||||
}
|
Loading…
Reference in New Issue
Block a user