├── .gitignore ├── Makefile ├── README.MD ├── client ├── client.go ├── dns.go ├── lb.go ├── main.go └── util.go ├── go.mod ├── go.sum ├── lib ├── cert.go ├── encoding.go ├── http_test.go └── leakbuf.go ├── log └── log.go ├── pb ├── msg.pb.go └── msg.proto ├── server ├── main.go └── proxy.go └── test ├── rpcc ├── ca.crt ├── client.crt ├── client.key └── main.go ├── rpcs ├── ca.crt ├── main.go ├── server.crt └── server.key ├── udpc ├── a.js ├── c.js └── main.go └── udps └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | /exec_bin 2 | /vendor -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | version=$(shell git rev-parse --short HEAD) 2 | buildAt=$(shell date "+%Y-%m-%d %H:%M:%S %Z") 3 | 4 | 5 | build: 6 | rm -rf exec_bin 7 | mkdir exec_bin 8 | GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.version=$(version) -X 'main.buildAt=$(buildAt)'" -o ./exec_bin/server-linux ./server/*.go 9 | GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -X main.version=$(version) -X 'main.buildAt=$(buildAt)'" -o ./exec_bin/client-linux ./client/*.go 10 | GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -X main.version=$(version) -X 'main.buildAt=$(buildAt)'" -o ./exec_bin/client-darwin ./client/*.go 11 | GOOS=linux GOARCH=arm go build -ldflags "-s -w -X main.version=$(version) -X 'main.buildAt=$(buildAt)'" -o ./exec_bin/client-arm ./client/*.go 12 | 13 | 14 | idl: 15 | rm -rf pb/*.pb.go 16 | protoc -I=. pb/*.proto --go_out=plugins=grpc:. 17 | 18 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ## Yet another socks proxy using gRPC streaming. 2 | 3 | ### Server 4 | 5 | Using docker(using bin file if docker is not supported) to start a server. 6 | 7 | ```Dockerfile 8 | docker run -itd \ 9 | -p 10465:10465 \ 10 | --name grpc-ss \ 11 | --restart always \ 12 | sdrzlyz/grpc-ss -l :10465 13 | ``` 14 | 15 | ### Client 16 | 17 | ``` 18 | client_bin_file -r xxx.xxx.xxx.xxx:10465 19 | ``` 20 | 21 | #### You can download both server and client bin file from github release. -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "time" 7 | 8 | "google.golang.org/grpc" 9 | 10 | "github.com/elvizlai/grpc-socks/log" 11 | "github.com/elvizlai/grpc-socks/pb" 12 | ) 13 | 14 | var callOptions = make([]grpc.CallOption, 0) 15 | 16 | func DialFunc(ctx context.Context, network, addr string) (net.Conn, error) { 17 | log.Debugf("%q<-%s->%q", ctx.Value(nameCtxKey), network, addr) 18 | 19 | tcpAddr, err := net.ResolveTCPAddr(network, addr) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | if isLocal(tcpAddr) { 25 | return net.DialTCP(network, nil, tcpAddr) 26 | } 27 | 28 | // ctx = metadata.AppendToOutgoingContext(ctx, "url", ctx.Value(nameCtxKey).(string)) 29 | stream, err := proxyClient.Pump(ctx, callOptions...) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | err = stream.Send(&pb.Payload{Data: []byte(tcpAddr.String())}) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return &client{addr: tcpAddr, stream: stream}, nil 40 | } 41 | 42 | var _ net.Conn = &client{} 43 | 44 | type client struct { 45 | addr net.Addr 46 | stream pb.Proxy_PumpClient 47 | } 48 | 49 | func (c *client) Read(b []byte) (n int, err error) { 50 | p, err := c.stream.Recv() 51 | if err != nil { 52 | return 0, err 53 | } 54 | 55 | return copy(b, p.Data), nil 56 | } 57 | 58 | func (c *client) Write(b []byte) (n int, err error) { 59 | p := &pb.Payload{ 60 | Data: b, 61 | } 62 | 63 | return len(b), c.stream.Send(p) 64 | } 65 | 66 | func (c *client) Close() error { 67 | return c.stream.CloseSend() 68 | } 69 | 70 | func (c *client) LocalAddr() net.Addr { 71 | return c.addr 72 | } 73 | 74 | func (c *client) RemoteAddr() net.Addr { 75 | return nil 76 | } 77 | 78 | // TODO impl 79 | func (c *client) SetDeadline(t time.Time) error { 80 | return nil 81 | } 82 | 83 | // TODO impl 84 | func (c *client) SetReadDeadline(t time.Time) error { 85 | return nil 86 | } 87 | 88 | // TODO impl 89 | func (c *client) SetWriteDeadline(t time.Time) error { 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /client/dns.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | "github.com/coocood/freecache" 8 | 9 | "github.com/elvizlai/grpc-socks/pb" 10 | ) 11 | 12 | type DNSResolver struct { 13 | cache *freecache.Cache 14 | } 15 | 16 | var expireSeconds = 7200 17 | 18 | var nameCtxKey = struct{}{} 19 | 20 | // DNSResolver uses the remote DNS to resolve host names 21 | func (d DNSResolver) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) { 22 | ctx = context.WithValue(ctx, nameCtxKey, name) 23 | 24 | if v, err := d.cache.Get([]byte(name)); err == nil { 25 | return ctx, v, nil 26 | } 27 | 28 | ipResp, err := proxyClient.ResolveIP(ctx, &pb.IPAddr{ 29 | Address: name, 30 | }) 31 | if err == nil { 32 | d.cache.Set([]byte(name), ipResp.Data, expireSeconds) 33 | } 34 | 35 | return ctx, ipResp.Data, err 36 | } 37 | -------------------------------------------------------------------------------- /client/lb.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | "golang.org/x/net/context" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/resolver" 10 | 11 | "github.com/elvizlai/grpc-socks/lib" 12 | "github.com/elvizlai/grpc-socks/log" 13 | "github.com/elvizlai/grpc-socks/pb" 14 | ) 15 | 16 | type etcdResolver struct { 17 | rawAddr string 18 | cc resolver.ClientConn 19 | hasInit bool 20 | } 21 | 22 | func (r *etcdResolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { 23 | r.cc = cc 24 | 25 | go r.watch(target.Endpoint) 26 | 27 | return r, nil 28 | } 29 | 30 | func (r etcdResolver) Scheme() string { 31 | return "lb" 32 | } 33 | 34 | func (r etcdResolver) ResolveNow(rn resolver.ResolveNowOption) { 35 | log.Infof("ResolveNow") // TODO check 36 | } 37 | 38 | // Close closes the resolver. 39 | func (r etcdResolver) Close() { 40 | log.Infof("Close") 41 | } 42 | 43 | func (r *etcdResolver) watch(addr string) { 44 | addrList := strings.Split(addr, ",") 45 | 46 | if len(addrList) == 1 { 47 | r.cc.NewAddress([]resolver.Address{{Addr: addrList[0]}}) 48 | return 49 | } 50 | 51 | maxTolerant := time.Duration(time.Millisecond * time.Duration(tolerant)) 52 | if maxTolerant <= 0 { 53 | var list []resolver.Address 54 | for i := range addrList { 55 | list = append(list, resolver.Address{Addr: addrList[i]}) 56 | } 57 | r.cc.NewAddress(list) 58 | return 59 | } 60 | 61 | //delay test 62 | var acm = make(map[string]pb.ProxyClient, 0) 63 | for i := range addrList { 64 | conn, err := grpc.Dial(addrList[i], grpc.WithTransportCredentials(lib.ClientTLS())) 65 | if err != nil { 66 | acm[addrList[i]] = nil 67 | } else { 68 | acm[addrList[i]] = pb.NewProxyClient(conn) 69 | } 70 | } 71 | 72 | pt := time.Minute * time.Duration(period) 73 | timer := time.NewTimer(pt) 74 | 75 | for { 76 | var list, dropList []resolver.Address 77 | 78 | for k, v := range acm { 79 | if v != nil { 80 | if delay, info, err := measure(v); err == nil { 81 | if delay <= maxTolerant { 82 | log.Debugf("service %s, time delay: %s, %q", k, delay, info) 83 | list = append(list, resolver.Address{Addr: k}) 84 | } else { 85 | log.Warnf("service %s, time delay %s too high, drop, %q", k, delay, info) 86 | dropList = append(dropList, resolver.Address{Addr: k}) 87 | } 88 | } else { 89 | log.Errorf("conn to service %s failed, err: %s", k, err) 90 | } 91 | } else { 92 | log.Errorf("conn to service %s failed", k) 93 | } 94 | } 95 | 96 | if len(list) == 0 { 97 | log.Errorf("no available services, try using drop list: %v", dropList) 98 | list = dropList 99 | } 100 | 101 | r.cc.NewAddress(list) 102 | 103 | <-timer.C 104 | 105 | if pt == 0 { 106 | break 107 | } 108 | 109 | timer.Reset(pt) 110 | } 111 | 112 | } 113 | 114 | func measure(c pb.ProxyClient) (dur time.Duration, info string, err error) { 115 | defer func(cur time.Time) { 116 | dur = time.Now().Sub(cur) 117 | }(time.Now()) 118 | 119 | var errChan = make(chan error, 3) 120 | 121 | for i := 0; i < 3; i++ { 122 | go func() { 123 | ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5) 124 | defer cancelFunc() 125 | resp, e := c.Echo(ctx, &pb.Payload{}) 126 | if resp != nil && info == "" { 127 | info = string(resp.Data) 128 | } 129 | errChan <- e 130 | }() 131 | } 132 | 133 | rc := 0 134 | 135 | L: 136 | for { 137 | select { 138 | case err = <-errChan: 139 | if err != nil { 140 | return 141 | } 142 | rc++ 143 | if rc == 3 { 144 | break L 145 | } 146 | } 147 | } 148 | 149 | return 150 | } 151 | -------------------------------------------------------------------------------- /client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | socks5 "github.com/armon/go-socks5" 7 | "github.com/coocood/freecache" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/encoding" 10 | "google.golang.org/grpc/resolver" 11 | 12 | "github.com/elvizlai/grpc-socks/lib" 13 | "github.com/elvizlai/grpc-socks/log" 14 | "github.com/elvizlai/grpc-socks/pb" 15 | ) 16 | 17 | var ( 18 | debug = false 19 | compress = false 20 | localAddr = "127.0.0.1:50050" 21 | remoteAddr = "127.0.0.1:50051" 22 | 23 | proxyClient pb.ProxyClient 24 | tolerant uint 25 | period uint 26 | ) 27 | 28 | func init() { 29 | flag.BoolVar(&debug, "d", debug, "debug mode") 30 | flag.StringVar(&localAddr, "l", localAddr, "local addr") 31 | flag.StringVar(&remoteAddr, "r", remoteAddr, "remote addr") 32 | flag.BoolVar(&compress, "cp", compress, "enable snappy compress") 33 | flag.Parse() 34 | 35 | if debug { 36 | log.SetDebugMode() 37 | } 38 | 39 | if compress { 40 | encoding.RegisterCompressor(lib.Snappy()) 41 | callOptions = append(callOptions, grpc.UseCompressor("snappy")) 42 | } 43 | } 44 | 45 | func main() { 46 | resolver.Register(&etcdResolver{}) 47 | 48 | conn, err := grpc.Dial("lb:///"+remoteAddr, grpc.WithBalancerName("round_robin"), grpc.WithTransportCredentials(lib.ClientTLS())) 49 | if err != nil { 50 | panic(err) 51 | } 52 | proxyClient = pb.NewProxyClient(conn) 53 | 54 | conf := &socks5.Config{ 55 | Resolver: DNSResolver{cache: freecache.NewCache(100 * 1024 * 1024)}, 56 | Dial: DialFunc, 57 | } 58 | 59 | server, err := socks5.New(conf) 60 | if err != nil { 61 | panic(err) 62 | } 63 | 64 | if err := server.ListenAndServe("tcp", localAddr); err != nil { 65 | panic(err) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /client/util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 100.64.0.0/10, 17.0.0.0/8 8 | var localAddrList = []*net.IPNet{ 9 | parseCIDR("192.168.0.0/16"), 10 | parseCIDR("10.0.0.0/8"), 11 | parseCIDR("172.16.0.0/12"), 12 | parseCIDR("100.64.0.0/10"), 13 | parseCIDR("17.0.0.00/8"), 14 | } 15 | 16 | func parseCIDR(s string) *net.IPNet { 17 | _, n, err := net.ParseCIDR(s) 18 | if err != nil { 19 | panic(err) 20 | } 21 | return n 22 | } 23 | 24 | func isLocal(addr *net.TCPAddr) bool { 25 | if addr.IP.String() == "127.0.0.1" { 26 | return true 27 | } 28 | 29 | for i := range localAddrList { 30 | if localAddrList[i].Contains(addr.IP) { 31 | return true 32 | } 33 | } 34 | 35 | return false 36 | } 37 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/elvizlai/grpc-socks 2 | 3 | require ( 4 | github.com/OneOfOne/xxhash v1.2.4 // indirect 5 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 6 | github.com/cespare/xxhash v1.1.0 // indirect 7 | github.com/coocood/freecache v1.1.0 8 | github.com/golang/protobuf v1.3.0 9 | github.com/golang/snappy v0.0.1 10 | github.com/soheilhy/cmux v0.1.4 11 | golang.org/x/net v0.0.0-20190310074541-c10a0554eabf 12 | golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa // indirect 13 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 // indirect 14 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 // indirect 15 | google.golang.org/grpc v1.19.0 16 | ) 17 | -------------------------------------------------------------------------------- /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/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 4 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 5 | github.com/OneOfOne/xxhash v1.2.4 h1:HZ+j9jn/+mcsaDSQRZuK00pXWdE25AQLtgm8kZct1Ew= 6 | github.com/OneOfOne/xxhash v1.2.4/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 7 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 8 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 9 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 10 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 11 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 12 | github.com/coocood/freecache v1.1.0 h1:ENiHOsWdj1BrrlPwblhbn4GdAsMymK3pZORJ+bJGAjA= 13 | github.com/coocood/freecache v1.1.0/go.mod h1:ePwxCDzOYvARfHdr1pByNct1at3CoKnsipOHwKlNbzI= 14 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 15 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 16 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 17 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 18 | github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= 19 | github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= 20 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 21 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 22 | github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= 23 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 24 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= 25 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 26 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 27 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 28 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 29 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 30 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 31 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 32 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 33 | golang.org/x/net v0.0.0-20190310074541-c10a0554eabf h1:J7RqX9u0J9ZB37CGaFc2VC+QZZT6E6jnDbrboEFVo0U= 34 | golang.org/x/net v0.0.0-20190310074541-c10a0554eabf/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 35 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 36 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 37 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 38 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= 39 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 40 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 41 | golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa h1:lqti/xP+yD/6zH5TqEwx2MilNIJY5Vbc6Qr8J3qyPIQ= 42 | golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 43 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 44 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 45 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= 46 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 47 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 48 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 49 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 50 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 51 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 52 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 53 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk= 54 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 55 | google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= 56 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 57 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 58 | -------------------------------------------------------------------------------- /lib/cert.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "log" 7 | 8 | "google.golang.org/grpc/credentials" 9 | ) 10 | 11 | var certPEMBlock = []byte(`-----BEGIN CERTIFICATE----- 12 | MIIEMDCCAxigAwIBAgIJAO8uVrXywwnLMA0GCSqGSIb3DQEBBQUAMG0xCzAJBgNV 13 | BAYTAkNIMQ0wCwYDVQQIEwQxMDI0MQ0wCwYDVQQHEwQxMDI0MQ0wCwYDVQQKEwQx 14 | MDI0MQ0wCwYDVQQLEwQxMDI0MQ0wCwYDVQQDEwQxMDI0MRMwEQYJKoZIhvcNAQkB 15 | FgQxMDI0MB4XDTE2MDMyNjAwMTkwMloXDTE5MDMyNjAwMTkwMlowbTELMAkGA1UE 16 | BhMCQ0gxDTALBgNVBAgTBDEwMjQxDTALBgNVBAcTBDEwMjQxDTALBgNVBAoTBDEw 17 | MjQxDTALBgNVBAsTBDEwMjQxDTALBgNVBAMTBDEwMjQxEzARBgkqhkiG9w0BCQEW 18 | BDEwMjQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsHh0cmk/GGgsA 19 | YzKnexeCMgciVR5kFAYzXGWlirfBvRi1hmVJ14guWslKpoM40kuWx77tKhoilcQA 20 | ACfsrmRrXJYZ6z5Y6oawXxjpMEkDXZdje09VPTiTUQaFTjcb7qq9l0AjdBonpMb3 21 | 4In9DwtyWEeQCJYo0gnxZcYOVwdjO8yskM80dgSjfrBMeIzV4bDDxajGQq+ce/gS 22 | 9t3TfColdQhXGFBY/KbOHPTBzxCAt2KN2VyiTFWdw2jhe1k/NRgKjAoMQWmQR9lq 23 | NeKQ8MGhtGT1drsVHVPueT+CW1lmb4ec3ga3v/wiRxXDJRuimiAs2hFJUgN6fkBk 24 | FGYSIe11AgMBAAGjgdIwgc8wHQYDVR0OBBYEFF9Wev8a9eJzLDMvT1wzzWVKvtlp 25 | MIGfBgNVHSMEgZcwgZSAFF9Wev8a9eJzLDMvT1wzzWVKvtlpoXGkbzBtMQswCQYD 26 | VQQGEwJDSDENMAsGA1UECBMEMTAyNDENMAsGA1UEBxMEMTAyNDENMAsGA1UEChME 27 | MTAyNDENMAsGA1UECxMEMTAyNDENMAsGA1UEAxMEMTAyNDETMBEGCSqGSIb3DQEJ 28 | ARYEMTAyNIIJAO8uVrXywwnLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD 29 | ggEBAEWXMh+5nAIdaSwsDe/EkaP8qNr4oDj/8DRVRq7J/7b5ts1b1RI8VmdE/AMi 30 | x2S7Dsh51JvG3u1Oss+PU44uLgGKP6RtBpqeo8uvlCgpWY2Qve5m2r/AR3M75AjB 31 | Rpvdtj7r4BD7uDTYseDazqZpC4A7tKqF/PomkWseA+QQHUjedkEk30e/7nHdZZoV 32 | s/i2kgkU7Rna1wqesihX8SRfLCDvyckuPFgimz0ry+TIpuFgm1orytjcjYsMv6ax 33 | UG+yA/zpmkIfvSrv6Gc6u+2hfBZREMcbyaqRxFlTOxRvVeKt14+pdJ90tc5Jq0aJ 34 | aiJQH2u5zeca9eFkLZzcTUZmgvw= 35 | -----END CERTIFICATE----- 36 | `) 37 | 38 | var keyPEMBlock = []byte(`-----BEGIN RSA PRIVATE KEY----- 39 | MIIEogIBAAKCAQEArB4dHJpPxhoLAGMyp3sXgjIHIlUeZBQGM1xlpYq3wb0YtYZl 40 | SdeILlrJSqaDONJLlse+7SoaIpXEAAAn7K5ka1yWGes+WOqGsF8Y6TBJA12XY3tP 41 | VT04k1EGhU43G+6qvZdAI3QaJ6TG9+CJ/Q8LclhHkAiWKNIJ8WXGDlcHYzvMrJDP 42 | NHYEo36wTHiM1eGww8WoxkKvnHv4Evbd03wqJXUIVxhQWPymzhz0wc8QgLdijdlc 43 | okxVncNo4XtZPzUYCowKDEFpkEfZajXikPDBobRk9Xa7FR1T7nk/gltZZm+HnN4G 44 | t7/8IkcVwyUbopogLNoRSVIDen5AZBRmEiHtdQIDAQABAoIBAEMSfR+/VqUJUw40 45 | mfHAOxoGatdLOkufrgbn08id9Rvvl6htlh0fe7css9J+bxZ+hOxeLJ35UTP3Duk9 46 | JlHKZ+Gas/env6Ugx5oFhQyiP2GrYCppTDIYaGqoIZVjNICTEkBNp7XpMaQPR2Yj 47 | P0K5USmfE0wivHlt2GgU1AiUi5F0gYSYpGoahbgG030Mv+GmKtX69/KwE3mgrNQ/ 48 | 54Czd6r+nHaCE9g0DfHwkIJrHHaWNa7DMoU6ws0glAFWAz8znYCZxPZfMp2oSEDn 49 | BjG//aGrT/jCvD+jrTcJhc/0sVhrDXPmqz49OXVTLjgRmwrEkjPrWnpRA5VKu4a7 50 | GjBFyWECgYEA2lvtSw+MA4O5ed9WE7LyldQojXrBU/gdBKs/OpyMv1bJTYUMkaPn 51 | BZ2fVTJV327bA8yMNrM/Reiqx373NGcw8h57WFbLjwMV72tvy7sOmg8MYTCiNarU 52 | fsigr3cdAoAwonSkUUPJHsJrlLca5pN0KXajCDg80BnMvsYnf+b2V3kCgYEAycmT 53 | RwLVYIQS7DDO/CUHTErIa6jte+bjTTW7G7nG/LhqPThS/b6ohzez6wztFspIldYs 54 | G0tO25q4NZa9jg1noM2CesvsGwlhxNrYbAK9CtZ6fWXgeDMvF71Hh1F+8LwfD4It 55 | tXGiU4pfUp+5OSKZN/jtqqeyYE/MywwXc4gfOt0CgYBIqFQCKO8u8DLUUbNDpMTB 56 | hDHmOdWAikullRHZ/+N5e3hKOh5fi8lAfh1ZbQFT8oAf+H0jamuAaJYDAcViA4Au 57 | 4GOsllzvflhbLUWq5dhK/Pzijhs7fldsxHdrS1g0z9DfDa7rd4HBoXHIr1DdLm11 58 | qos/He9mU19kj2zvSzvnCQKBgCMVxGDNclJUxIGCvwqCWbF/Mzfc6GXpsE3lcMIS 59 | XDHm0roQSAXMl7rjCYpt9e9HfrVmxsZ8Ipr2XN8cdZr0Y7dG5E/7kvLkf7ZdotGs 60 | 7DetMSEKjKv5ok+LXpt9pQewfeoRZWct+d5yqb5Q/UCc7m0YACLzA4XRejc3xAAX 61 | g+6VAoGAI5n5EAi10YAm1P+E7sWtipdxe+FTXyketmWoty6pVw6wWrAgT8/HpNyG 62 | Z93rnVEtVNJ9kqNPqrn4Mn4320cJxGzVNrOKrS8NAlccbKqkuZ3fMr/oZ6/8o5aU 63 | zXPJfyC/bgm20s7B029Ojmwy3ReoTY2oL2hCeRRiUXp82Az+wCQ= 64 | -----END RSA PRIVATE KEY----- 65 | `) 66 | 67 | // ServerTLS server tls 68 | func ServerTLS() credentials.TransportCredentials { 69 | cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | 74 | return credentials.NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}) 75 | } 76 | 77 | // ClientTLS client tls 78 | func ClientTLS() credentials.TransportCredentials { 79 | cp := x509.NewCertPool() 80 | if !cp.AppendCertsFromPEM(certPEMBlock) { 81 | log.Fatal("Credentials: Failed to append certificates") 82 | } 83 | 84 | return credentials.NewClientTLSFromCert(cp, "1024") 85 | } 86 | -------------------------------------------------------------------------------- /lib/encoding.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/golang/snappy" 7 | "google.golang.org/grpc/encoding" 8 | ) 9 | 10 | const UDPMaxSize = 65507 11 | 12 | type snappyCP struct { 13 | } 14 | 15 | // Snappy compressor 16 | func Snappy() encoding.Compressor { 17 | return &snappyCP{} 18 | } 19 | 20 | func (c *snappyCP) Compress(w io.Writer) (io.WriteCloser, error) { 21 | return snappy.NewBufferedWriter(w), nil 22 | } 23 | 24 | func (c *snappyCP) Decompress(r io.Reader) (io.Reader, error) { 25 | return snappy.NewReader(r), nil 26 | } 27 | 28 | func (c *snappyCP) Name() string { 29 | return "snappy" 30 | } 31 | -------------------------------------------------------------------------------- /lib/http_test.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2015-2017, Wothing Co., Ltd. 3 | * All rights reserved. 4 | * 5 | * Created by elvizlai on 2017/12/1 14:07. 6 | */ 7 | 8 | package lib 9 | 10 | import ( 11 | "context" 12 | "crypto/tls" 13 | "io/ioutil" 14 | "net/http" 15 | "testing" 16 | "time" 17 | 18 | "golang.org/x/net/http2" 19 | ) 20 | 21 | var client = &http.Client{} 22 | 23 | func TestName(t *testing.T) { 24 | ts := &http.Transport{ 25 | TLSClientConfig: &tls.Config{ 26 | InsecureSkipVerify: true, 27 | }, 28 | } 29 | 30 | t.Logf("%#v", ts) 31 | 32 | err := http2.ConfigureTransport(ts) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | t.Logf("%#v", ts) 38 | 39 | client.Transport = ts 40 | 41 | req, err := http.NewRequest("GET", "https://47.74.156.21:10465", nil) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | ctx, cFun := context.WithTimeout(req.Context(), time.Second*2) 47 | defer cFun() 48 | 49 | req = req.WithContext(ctx) 50 | 51 | t.Log(req) 52 | 53 | resp, err := client.Do(req) 54 | if err != nil { 55 | t.Fatal(err) 56 | } 57 | 58 | t.Log(resp) 59 | 60 | defer resp.Body.Close() 61 | data, err := ioutil.ReadAll(resp.Body) 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | 66 | t.Log(string(data)) 67 | 68 | } 69 | -------------------------------------------------------------------------------- /lib/leakbuf.go: -------------------------------------------------------------------------------- 1 | // Package lib leakbuf.go is stolen form ss 2 | package lib 3 | 4 | // LeakyBuf leakybuf 5 | type LeakyBuf struct { 6 | bufSize int // size of each buffer 7 | freeList chan []byte 8 | } 9 | 10 | // NewLeakyBuf creates a leaky buffer which can hold at most n buffer, each 11 | // with bufSize bytes. 12 | func NewLeakyBuf(n, bufSize int) *LeakyBuf { 13 | return &LeakyBuf{ 14 | bufSize: bufSize, 15 | freeList: make(chan []byte, n), 16 | } 17 | } 18 | 19 | // Get returns a buffer from the leaky buffer or create a new buffer. 20 | func (lb *LeakyBuf) Get() (b []byte) { 21 | select { 22 | case b = <-lb.freeList: 23 | default: 24 | b = make([]byte, lb.bufSize) 25 | } 26 | return 27 | } 28 | 29 | // Put add the buffer into the free buffer pool for reuse. Panic if the buffer 30 | // size is not the same with the leaky buffer's. This is intended to expose 31 | // error usage of leaky buffer. 32 | func (lb *LeakyBuf) Put(b []byte) { 33 | if len(b) != lb.bufSize { 34 | panic("invalid buffer size that's put into leaky buffer") 35 | } 36 | select { 37 | case lb.freeList <- b: 38 | default: 39 | } 40 | return 41 | } 42 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "log" 5 | "os" 6 | ) 7 | 8 | var std = log.New(os.Stdout, "", log.LstdFlags) 9 | 10 | var debug = false 11 | 12 | // SetDebugMode set debug mode 13 | func SetDebugMode() { 14 | debug = true 15 | } 16 | 17 | // Debugln Debugln 18 | func Debugln(e ...interface{}) { 19 | if debug { 20 | std.Printf("\033[33m%v\033[0m\n", e) 21 | } 22 | } 23 | 24 | // Debugf with format 25 | func Debugf(format string, para ...interface{}) { 26 | if debug { 27 | std.Printf("\033[33m"+format+"\033[0m", para...) 28 | } 29 | } 30 | 31 | // Infof with format 32 | func Infof(format string, para ...interface{}) { 33 | std.Printf("\033[32m"+format+"\033[0m", para...) 34 | } 35 | 36 | // Warnf with format 37 | func Warnf(format string, para ...interface{}) { 38 | std.Printf("\033[35m"+format+"\033[0m", para...) 39 | } 40 | 41 | // Errorf with format 42 | func Errorf(format string, para ...interface{}) { 43 | std.Printf("\033[31m"+format+"\033[0m", para...) 44 | } 45 | 46 | // Errorln Errorln 47 | func Errorln(e interface{}) { 48 | std.Printf("\033[31m%v\033[0m\n", e) 49 | } 50 | 51 | // Fatalf with format 52 | func Fatalf(format string, para ...interface{}) { 53 | std.Fatalf("\033[31m"+format+"\033[0m", para...) 54 | } 55 | -------------------------------------------------------------------------------- /pb/msg.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: pb/msg.proto 3 | 4 | package pb 5 | 6 | import proto "github.com/golang/protobuf/proto" 7 | import fmt "fmt" 8 | import math "math" 9 | 10 | import ( 11 | context "golang.org/x/net/context" 12 | grpc "google.golang.org/grpc" 13 | ) 14 | 15 | // Reference imports to suppress errors if they are not otherwise used. 16 | var _ = proto.Marshal 17 | var _ = fmt.Errorf 18 | var _ = math.Inf 19 | 20 | // This is a compile-time assertion to ensure that this generated file 21 | // is compatible with the proto package it is being compiled against. 22 | // A compilation error at this line likely means your copy of the 23 | // proto package needs to be updated. 24 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 25 | 26 | type Payload struct { 27 | Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` 28 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 29 | XXX_unrecognized []byte `json:"-"` 30 | XXX_sizecache int32 `json:"-"` 31 | } 32 | 33 | func (m *Payload) Reset() { *m = Payload{} } 34 | func (m *Payload) String() string { return proto.CompactTextString(m) } 35 | func (*Payload) ProtoMessage() {} 36 | func (*Payload) Descriptor() ([]byte, []int) { 37 | return fileDescriptor_msg_1cc3c4c731d1ef0f, []int{0} 38 | } 39 | func (m *Payload) XXX_Unmarshal(b []byte) error { 40 | return xxx_messageInfo_Payload.Unmarshal(m, b) 41 | } 42 | func (m *Payload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 43 | return xxx_messageInfo_Payload.Marshal(b, m, deterministic) 44 | } 45 | func (dst *Payload) XXX_Merge(src proto.Message) { 46 | xxx_messageInfo_Payload.Merge(dst, src) 47 | } 48 | func (m *Payload) XXX_Size() int { 49 | return xxx_messageInfo_Payload.Size(m) 50 | } 51 | func (m *Payload) XXX_DiscardUnknown() { 52 | xxx_messageInfo_Payload.DiscardUnknown(m) 53 | } 54 | 55 | var xxx_messageInfo_Payload proto.InternalMessageInfo 56 | 57 | func (m *Payload) GetData() []byte { 58 | if m != nil { 59 | return m.Data 60 | } 61 | return nil 62 | } 63 | 64 | type IPAddr struct { 65 | // option map_entry = true; 66 | Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` 67 | Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` 68 | Zone string `protobuf:"bytes,3,opt,name=zone,proto3" json:"zone,omitempty"` 69 | CreateAt int64 `protobuf:"varint,4,opt,name=create_at,json=createAt,proto3" json:"create_at,omitempty"` 70 | CreateAt1 int64 `protobuf:"varint,5,opt,name=create_at1,json=createAt1,proto3" json:"create_at1,omitempty"` 71 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 72 | XXX_unrecognized []byte `json:"-"` 73 | XXX_sizecache int32 `json:"-"` 74 | } 75 | 76 | func (m *IPAddr) Reset() { *m = IPAddr{} } 77 | func (m *IPAddr) String() string { return proto.CompactTextString(m) } 78 | func (*IPAddr) ProtoMessage() {} 79 | func (*IPAddr) Descriptor() ([]byte, []int) { 80 | return fileDescriptor_msg_1cc3c4c731d1ef0f, []int{1} 81 | } 82 | func (m *IPAddr) XXX_Unmarshal(b []byte) error { 83 | return xxx_messageInfo_IPAddr.Unmarshal(m, b) 84 | } 85 | func (m *IPAddr) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 86 | return xxx_messageInfo_IPAddr.Marshal(b, m, deterministic) 87 | } 88 | func (dst *IPAddr) XXX_Merge(src proto.Message) { 89 | xxx_messageInfo_IPAddr.Merge(dst, src) 90 | } 91 | func (m *IPAddr) XXX_Size() int { 92 | return xxx_messageInfo_IPAddr.Size(m) 93 | } 94 | func (m *IPAddr) XXX_DiscardUnknown() { 95 | xxx_messageInfo_IPAddr.DiscardUnknown(m) 96 | } 97 | 98 | var xxx_messageInfo_IPAddr proto.InternalMessageInfo 99 | 100 | func (m *IPAddr) GetAddress() string { 101 | if m != nil { 102 | return m.Address 103 | } 104 | return "" 105 | } 106 | 107 | func (m *IPAddr) GetData() []byte { 108 | if m != nil { 109 | return m.Data 110 | } 111 | return nil 112 | } 113 | 114 | func (m *IPAddr) GetZone() string { 115 | if m != nil { 116 | return m.Zone 117 | } 118 | return "" 119 | } 120 | 121 | func (m *IPAddr) GetCreateAt() int64 { 122 | if m != nil { 123 | return m.CreateAt 124 | } 125 | return 0 126 | } 127 | 128 | func (m *IPAddr) GetCreateAt1() int64 { 129 | if m != nil { 130 | return m.CreateAt1 131 | } 132 | return 0 133 | } 134 | 135 | func init() { 136 | proto.RegisterType((*Payload)(nil), "pb.Payload") 137 | proto.RegisterType((*IPAddr)(nil), "pb.IPAddr") 138 | } 139 | 140 | // Reference imports to suppress errors if they are not otherwise used. 141 | var _ context.Context 142 | var _ grpc.ClientConn 143 | 144 | // This is a compile-time assertion to ensure that this generated file 145 | // is compatible with the grpc package it is being compiled against. 146 | const _ = grpc.SupportPackageIsVersion4 147 | 148 | // ProxyClient is the client API for Proxy service. 149 | // 150 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 151 | type ProxyClient interface { 152 | Echo(ctx context.Context, in *Payload, opts ...grpc.CallOption) (*Payload, error) 153 | ResolveIP(ctx context.Context, in *IPAddr, opts ...grpc.CallOption) (*IPAddr, error) 154 | Pump(ctx context.Context, opts ...grpc.CallOption) (Proxy_PumpClient, error) 155 | PipelineUDP(ctx context.Context, opts ...grpc.CallOption) (Proxy_PipelineUDPClient, error) 156 | } 157 | 158 | type proxyClient struct { 159 | cc *grpc.ClientConn 160 | } 161 | 162 | func NewProxyClient(cc *grpc.ClientConn) ProxyClient { 163 | return &proxyClient{cc} 164 | } 165 | 166 | func (c *proxyClient) Echo(ctx context.Context, in *Payload, opts ...grpc.CallOption) (*Payload, error) { 167 | out := new(Payload) 168 | err := c.cc.Invoke(ctx, "/pb.Proxy/Echo", in, out, opts...) 169 | if err != nil { 170 | return nil, err 171 | } 172 | return out, nil 173 | } 174 | 175 | func (c *proxyClient) ResolveIP(ctx context.Context, in *IPAddr, opts ...grpc.CallOption) (*IPAddr, error) { 176 | out := new(IPAddr) 177 | err := c.cc.Invoke(ctx, "/pb.Proxy/ResolveIP", in, out, opts...) 178 | if err != nil { 179 | return nil, err 180 | } 181 | return out, nil 182 | } 183 | 184 | func (c *proxyClient) Pump(ctx context.Context, opts ...grpc.CallOption) (Proxy_PumpClient, error) { 185 | stream, err := c.cc.NewStream(ctx, &_Proxy_serviceDesc.Streams[0], "/pb.Proxy/Pump", opts...) 186 | if err != nil { 187 | return nil, err 188 | } 189 | x := &proxyPumpClient{stream} 190 | return x, nil 191 | } 192 | 193 | type Proxy_PumpClient interface { 194 | Send(*Payload) error 195 | Recv() (*Payload, error) 196 | grpc.ClientStream 197 | } 198 | 199 | type proxyPumpClient struct { 200 | grpc.ClientStream 201 | } 202 | 203 | func (x *proxyPumpClient) Send(m *Payload) error { 204 | return x.ClientStream.SendMsg(m) 205 | } 206 | 207 | func (x *proxyPumpClient) Recv() (*Payload, error) { 208 | m := new(Payload) 209 | if err := x.ClientStream.RecvMsg(m); err != nil { 210 | return nil, err 211 | } 212 | return m, nil 213 | } 214 | 215 | func (c *proxyClient) PipelineUDP(ctx context.Context, opts ...grpc.CallOption) (Proxy_PipelineUDPClient, error) { 216 | stream, err := c.cc.NewStream(ctx, &_Proxy_serviceDesc.Streams[1], "/pb.Proxy/PipelineUDP", opts...) 217 | if err != nil { 218 | return nil, err 219 | } 220 | x := &proxyPipelineUDPClient{stream} 221 | return x, nil 222 | } 223 | 224 | type Proxy_PipelineUDPClient interface { 225 | Send(*Payload) error 226 | Recv() (*Payload, error) 227 | grpc.ClientStream 228 | } 229 | 230 | type proxyPipelineUDPClient struct { 231 | grpc.ClientStream 232 | } 233 | 234 | func (x *proxyPipelineUDPClient) Send(m *Payload) error { 235 | return x.ClientStream.SendMsg(m) 236 | } 237 | 238 | func (x *proxyPipelineUDPClient) Recv() (*Payload, error) { 239 | m := new(Payload) 240 | if err := x.ClientStream.RecvMsg(m); err != nil { 241 | return nil, err 242 | } 243 | return m, nil 244 | } 245 | 246 | // ProxyServer is the server API for Proxy service. 247 | type ProxyServer interface { 248 | Echo(context.Context, *Payload) (*Payload, error) 249 | ResolveIP(context.Context, *IPAddr) (*IPAddr, error) 250 | Pump(Proxy_PumpServer) error 251 | PipelineUDP(Proxy_PipelineUDPServer) error 252 | } 253 | 254 | func RegisterProxyServer(s *grpc.Server, srv ProxyServer) { 255 | s.RegisterService(&_Proxy_serviceDesc, srv) 256 | } 257 | 258 | func _Proxy_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 259 | in := new(Payload) 260 | if err := dec(in); err != nil { 261 | return nil, err 262 | } 263 | if interceptor == nil { 264 | return srv.(ProxyServer).Echo(ctx, in) 265 | } 266 | info := &grpc.UnaryServerInfo{ 267 | Server: srv, 268 | FullMethod: "/pb.Proxy/Echo", 269 | } 270 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 271 | return srv.(ProxyServer).Echo(ctx, req.(*Payload)) 272 | } 273 | return interceptor(ctx, in, info, handler) 274 | } 275 | 276 | func _Proxy_ResolveIP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 277 | in := new(IPAddr) 278 | if err := dec(in); err != nil { 279 | return nil, err 280 | } 281 | if interceptor == nil { 282 | return srv.(ProxyServer).ResolveIP(ctx, in) 283 | } 284 | info := &grpc.UnaryServerInfo{ 285 | Server: srv, 286 | FullMethod: "/pb.Proxy/ResolveIP", 287 | } 288 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 289 | return srv.(ProxyServer).ResolveIP(ctx, req.(*IPAddr)) 290 | } 291 | return interceptor(ctx, in, info, handler) 292 | } 293 | 294 | func _Proxy_Pump_Handler(srv interface{}, stream grpc.ServerStream) error { 295 | return srv.(ProxyServer).Pump(&proxyPumpServer{stream}) 296 | } 297 | 298 | type Proxy_PumpServer interface { 299 | Send(*Payload) error 300 | Recv() (*Payload, error) 301 | grpc.ServerStream 302 | } 303 | 304 | type proxyPumpServer struct { 305 | grpc.ServerStream 306 | } 307 | 308 | func (x *proxyPumpServer) Send(m *Payload) error { 309 | return x.ServerStream.SendMsg(m) 310 | } 311 | 312 | func (x *proxyPumpServer) Recv() (*Payload, error) { 313 | m := new(Payload) 314 | if err := x.ServerStream.RecvMsg(m); err != nil { 315 | return nil, err 316 | } 317 | return m, nil 318 | } 319 | 320 | func _Proxy_PipelineUDP_Handler(srv interface{}, stream grpc.ServerStream) error { 321 | return srv.(ProxyServer).PipelineUDP(&proxyPipelineUDPServer{stream}) 322 | } 323 | 324 | type Proxy_PipelineUDPServer interface { 325 | Send(*Payload) error 326 | Recv() (*Payload, error) 327 | grpc.ServerStream 328 | } 329 | 330 | type proxyPipelineUDPServer struct { 331 | grpc.ServerStream 332 | } 333 | 334 | func (x *proxyPipelineUDPServer) Send(m *Payload) error { 335 | return x.ServerStream.SendMsg(m) 336 | } 337 | 338 | func (x *proxyPipelineUDPServer) Recv() (*Payload, error) { 339 | m := new(Payload) 340 | if err := x.ServerStream.RecvMsg(m); err != nil { 341 | return nil, err 342 | } 343 | return m, nil 344 | } 345 | 346 | var _Proxy_serviceDesc = grpc.ServiceDesc{ 347 | ServiceName: "pb.Proxy", 348 | HandlerType: (*ProxyServer)(nil), 349 | Methods: []grpc.MethodDesc{ 350 | { 351 | MethodName: "Echo", 352 | Handler: _Proxy_Echo_Handler, 353 | }, 354 | { 355 | MethodName: "ResolveIP", 356 | Handler: _Proxy_ResolveIP_Handler, 357 | }, 358 | }, 359 | Streams: []grpc.StreamDesc{ 360 | { 361 | StreamName: "Pump", 362 | Handler: _Proxy_Pump_Handler, 363 | ServerStreams: true, 364 | ClientStreams: true, 365 | }, 366 | { 367 | StreamName: "PipelineUDP", 368 | Handler: _Proxy_PipelineUDP_Handler, 369 | ServerStreams: true, 370 | ClientStreams: true, 371 | }, 372 | }, 373 | Metadata: "pb/msg.proto", 374 | } 375 | 376 | func init() { proto.RegisterFile("pb/msg.proto", fileDescriptor_msg_1cc3c4c731d1ef0f) } 377 | 378 | var fileDescriptor_msg_1cc3c4c731d1ef0f = []byte{ 379 | // 247 bytes of a gzipped FileDescriptorProto 380 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0x4f, 0x4e, 0x02, 0x31, 381 | 0x14, 0xc6, 0xe9, 0x30, 0x80, 0xf3, 0x60, 0xf5, 0x56, 0x0d, 0x09, 0x91, 0x34, 0xd1, 0xcc, 0xc6, 382 | 0x11, 0xf4, 0x04, 0x18, 0x5d, 0xb0, 0x6b, 0x9a, 0xb8, 0x36, 0x1d, 0xfa, 0xa2, 0x24, 0x03, 0x6d, 383 | 0xda, 0x6a, 0xc4, 0x23, 0x78, 0x13, 0x6f, 0x69, 0xa6, 0x08, 0xb2, 0xd2, 0xdd, 0xf7, 0xe7, 0xf7, 384 | 0x35, 0x6d, 0x61, 0xe4, 0xea, 0xeb, 0x4d, 0x78, 0xae, 0x9c, 0xb7, 0xd1, 0x62, 0xe6, 0x6a, 0x31, 385 | 0x81, 0x81, 0xd4, 0xbb, 0xc6, 0x6a, 0x83, 0x08, 0xb9, 0xd1, 0x51, 0x73, 0x36, 0x65, 0xe5, 0x48, 386 | 0x25, 0x2d, 0x3e, 0x19, 0xf4, 0x97, 0x72, 0x61, 0x8c, 0x47, 0x0e, 0x03, 0x6d, 0x8c, 0xa7, 0x10, 387 | 0x12, 0x51, 0xa8, 0x83, 0x3d, 0x0e, 0xb3, 0xdf, 0x61, 0x9b, 0x7d, 0xd8, 0x2d, 0xf1, 0x6e, 0x42, 388 | 0x93, 0xc6, 0x73, 0x28, 0x56, 0x9e, 0x74, 0xa4, 0x27, 0x1d, 0x79, 0x3e, 0x65, 0x65, 0xf7, 0x2e, 389 | 0x9b, 0x65, 0xea, 0x6c, 0x1f, 0x2e, 0x22, 0x4e, 0x00, 0x8e, 0xc0, 0x9c, 0xf7, 0x5a, 0x42, 0x15, 390 | 0x87, 0x76, 0x7e, 0xf3, 0xc5, 0xa0, 0x27, 0xbd, 0x7d, 0xdf, 0xa1, 0x80, 0xfc, 0x61, 0xf5, 0x62, 391 | 0x71, 0x58, 0xb9, 0xba, 0xfa, 0xb9, 0xff, 0xf8, 0xd4, 0x88, 0x0e, 0x5e, 0x40, 0xa1, 0x28, 0xd8, 392 | 0xe6, 0x8d, 0x96, 0x12, 0xa1, 0xed, 0xf6, 0x0f, 0x19, 0x9f, 0x68, 0xd1, 0xc1, 0x4b, 0xc8, 0xe5, 393 | 0xeb, 0xc6, 0xfd, 0x75, 0x54, 0xc9, 0x66, 0x0c, 0xaf, 0x60, 0x28, 0xd7, 0x8e, 0x9a, 0xf5, 0x96, 394 | 0x1e, 0xef, 0xe5, 0x7f, 0x78, 0xdd, 0x4f, 0x5f, 0x7c, 0xfb, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xf2, 395 | 0xb8, 0xd1, 0x42, 0x72, 0x01, 0x00, 0x00, 396 | } 397 | -------------------------------------------------------------------------------- /pb/msg.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package pb; 4 | 5 | message Payload { 6 | bytes data = 1; 7 | } 8 | 9 | message IPAddr { 10 | // option map_entry = true; 11 | string address = 1; 12 | bytes data = 2; 13 | string zone = 3; 14 | int64 create_at = 4 [jstype=JS_NUMBER]; 15 | int64 create_at1 = 5; 16 | } 17 | 18 | service Proxy { 19 | rpc Echo (Payload) returns (Payload) {}; 20 | 21 | rpc ResolveIP(IPAddr) returns (IPAddr) {}; 22 | 23 | rpc Pump (stream Payload) returns (stream Payload) {}; 24 | 25 | rpc PipelineUDP (stream Payload) returns (stream Payload) {}; 26 | } -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "net" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "os" 9 | "runtime" 10 | 11 | "github.com/soheilhy/cmux" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/encoding" 14 | 15 | "github.com/elvizlai/grpc-socks/lib" 16 | "github.com/elvizlai/grpc-socks/log" 17 | "github.com/elvizlai/grpc-socks/pb" 18 | ) 19 | 20 | var ( 21 | addr = ":50051" 22 | debug = false 23 | ) 24 | 25 | var ( 26 | showVersion = false 27 | version = "self-build" 28 | buildAt = "" 29 | ) 30 | 31 | func init() { 32 | flag.StringVar(&addr, "l", addr, "listen addr") 33 | flag.BoolVar(&debug, "d", debug, "debug mode") 34 | flag.BoolVar(&showVersion, "v", false, "show version then exit") 35 | 36 | flag.Parse() 37 | 38 | if showVersion { 39 | log.Infof("version:%s, build at %q using %s", version, buildAt, runtime.Version()) 40 | os.Exit(0) 41 | } 42 | 43 | if debug { 44 | log.SetDebugMode() 45 | } 46 | 47 | encoding.RegisterCompressor(lib.Snappy()) 48 | } 49 | 50 | func main() { 51 | ln, err := net.Listen("tcp", addr) 52 | if err != nil { 53 | log.Fatalf("failed to listen: %s", err) 54 | } 55 | defer ln.Close() 56 | 57 | log.Infof("starting proxy server at %q ...", addr) 58 | 59 | m := cmux.New(ln) 60 | 61 | httpL := m.Match(cmux.HTTP1Fast()) 62 | httpS := &http.Server{ 63 | Handler: nil, 64 | } 65 | go httpS.Serve(httpL) 66 | 67 | grpcL := m.Match(cmux.Any()) 68 | grpcS := grpc.NewServer(grpc.Creds(lib.ServerTLS()), grpc.StreamInterceptor(interceptor)) 69 | defer grpcS.GracefulStop() 70 | pb.RegisterProxyServer(grpcS, &proxy{ 71 | serverToken: append([]byte(version), append([]byte("@"), []byte(buildAt)...)...), 72 | }) 73 | go func() { 74 | err := grpcS.Serve(grpcL) 75 | if err != nil { 76 | log.Fatalf("failed to serve grpc: %s", err.Error()) 77 | } 78 | }() 79 | 80 | if err := m.Serve(); err != nil { 81 | log.Fatalf("failed to serve: %s", err) 82 | } 83 | } 84 | 85 | func interceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 86 | return handler(srv, ss) 87 | } 88 | -------------------------------------------------------------------------------- /server/proxy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "time" 7 | 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc/peer" 10 | 11 | "github.com/elvizlai/grpc-socks/lib" 12 | "github.com/elvizlai/grpc-socks/log" 13 | "github.com/elvizlai/grpc-socks/pb" 14 | ) 15 | 16 | type proxy struct { 17 | serverToken []byte 18 | } 19 | 20 | const leakyBufSize = 4108 // data.len(2) + hmacsha1(10) + data(4096) 21 | 22 | var leakyBuf = lib.NewLeakyBuf(2048, leakyBufSize) 23 | 24 | func (p *proxy) Echo(ctx context.Context, req *pb.Payload) (*pb.Payload, error) { 25 | return &pb.Payload{Data: p.serverToken}, nil 26 | } 27 | 28 | // TODO buff 29 | func (p *proxy) ResolveIP(ctx context.Context, req *pb.IPAddr) (*pb.IPAddr, error) { 30 | ipAddr, err := net.ResolveIPAddr("ip", req.Address) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | req.Data = ipAddr.IP 36 | req.Zone = ipAddr.Zone 37 | 38 | return req, nil 39 | } 40 | 41 | func (p *proxy) Pump(stream pb.Proxy_PumpServer) error { 42 | frame := &pb.Payload{} 43 | 44 | // first frame 45 | err := stream.RecvMsg(frame) 46 | if err != nil { 47 | log.Errorf("tcp first frame err: %s", err) 48 | return err 49 | } 50 | 51 | addr := string(frame.Data) 52 | // addr must be a name? 53 | 54 | conn, err := net.DialTimeout("tcp", addr, time.Second*15) 55 | if err != nil { 56 | log.Errorf("tcp dial %q err: %s", addr, err) 57 | return err 58 | } 59 | defer conn.Close() 60 | 61 | conn.(*net.TCPConn).SetKeepAlive(true) 62 | 63 | ctx := stream.Context() 64 | // get peer info from ctx, maybe it won't be nil is this case 65 | info, ok := peer.FromContext(ctx) 66 | if ok { 67 | defer log.Debugf("tcp close %q<-->%q<-->%q", info.Addr.String(), addr, conn.RemoteAddr()) 68 | log.Debugf("tcp conn %q<-->%q<-->%q", info.Addr.String(), addr, conn.RemoteAddr()) 69 | } else { 70 | defer log.Debugf("tcp close %q<-->%q<-->%q", conn.LocalAddr(), addr, conn.RemoteAddr()) 71 | log.Debugf("tcp conn %q<-->%q<-->%q", conn.LocalAddr(), addr, conn.RemoteAddr()) 72 | } 73 | 74 | go func() { 75 | for { 76 | p, err := stream.Recv() 77 | 78 | if err != nil { 79 | if ctx.Err() != context.Canceled && err != io.EOF { 80 | log.Errorf("stream recv err: %s", err) 81 | } 82 | break 83 | } 84 | 85 | _, err = conn.Write(p.Data) 86 | if err != nil { 87 | log.Errorf("tcp conn write err: %s", err) 88 | break 89 | } 90 | } 91 | conn.Close() // close conn 92 | }() 93 | 94 | buff := leakyBuf.Get() 95 | defer leakyBuf.Put(buff) 96 | 97 | for { 98 | n, err := conn.Read(buff) 99 | if err != nil { 100 | break 101 | } 102 | 103 | if n > 0 { 104 | frame.Data = buff[:n] 105 | err = stream.Send(frame) 106 | if err != nil { 107 | log.Errorf("stream send err: %s", err) 108 | break 109 | } 110 | } 111 | } 112 | 113 | return nil 114 | } 115 | 116 | func (p *proxy) PipelineUDP(stream pb.Proxy_PipelineUDPServer) error { 117 | frame := &pb.Payload{} 118 | 119 | err := stream.RecvMsg(frame) 120 | if err != nil { 121 | log.Errorf("udp first frame err: %s", err) 122 | return err 123 | } 124 | 125 | addr := string(frame.Data) 126 | 127 | log.Debugf("recv udp addr: %s", addr) 128 | 129 | conn, err := net.Dial("udp", addr) 130 | if err != nil { 131 | log.Errorf("udp dial %s err: %s", addr, err) 132 | return err 133 | } 134 | defer conn.Close() 135 | 136 | conn.SetReadDeadline(time.Now().Add(time.Second * 600)) 137 | 138 | ctx := stream.Context() 139 | 140 | go func() { 141 | buff := make([]byte, lib.UDPMaxSize) 142 | 143 | for { 144 | n, err := conn.Read(buff) 145 | if n > 0 { 146 | frame.Data = buff[:n] 147 | err = stream.Send(frame) 148 | if err != nil { 149 | log.Errorf("stream send err: %s", err) 150 | break 151 | } 152 | } 153 | 154 | if err != nil { 155 | break 156 | } 157 | } 158 | }() 159 | 160 | for { 161 | p, err := stream.Recv() 162 | 163 | if err == io.EOF { 164 | return nil 165 | } 166 | 167 | if err != nil { 168 | if ctx.Err() == context.Canceled { 169 | break 170 | } 171 | log.Errorf("stream recv err: %s", err) 172 | return err 173 | } 174 | 175 | _, err = conn.Write(p.Data) 176 | if err != nil { 177 | log.Errorf("udp conn write err: %s", err) 178 | return err 179 | } 180 | } 181 | 182 | return nil 183 | } 184 | -------------------------------------------------------------------------------- /test/rpcc/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDCDCCAfACCQCgyalQ+QS87TANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMCAXDTE3MDkwODA0MzMyMloYDzIxMTcwODE1MDQzMzIyWjBFMQsw 5 | CQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJu 6 | ZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC 7 | AQEA47s0jYDAFK+8IDGwv4CLch+uNZm9pyb/02NpKhhUnFrVDHWSGCUol9JQu/o/ 8 | pAqxfluf/6Zdl4NcU/aLeFpompqU4IqUOvU3ool3nDUOUNPf2Az89ZCzD1P25iPk 9 | MZ3Ih8gq/X8mmjQ1r5giEiUzCMJVdVpN2+4X2q56XIbN0lujFl4PIsT/IwIWJOsq 10 | qG6yLaAUW67oG29Bxb6sualciRcv7CbKVPgU+VOtryo0UgPo8h444Vzq3TD5Q15T 11 | ife60QqTyd4AV15XN9uCkhLEx+BLa+RD6mmOx2i4eqkPoDPLo1jaSpEIbNZZEz2+ 12 | rEMA5p5ZMWx16YmFqc0dp239fwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBl0GYS 13 | BhrLII3al+JWvYdaCBXn13OwpEyp7gM5rNODVOOAoyU7bj10JkcX7szUIPm3lqCI 14 | GFhJf+LgxMfgaDTsudBbBlnVi+WYB+XqufTVTstiTCuN8wIq4VkZrCqCvtjaZx7W 15 | lPeWTtzRiifHwXkJSli8qY52F14mX1xYr+4xXboonWIsO8j4bevINR8pnT2o7Xnv 16 | 8Y/0pGZAEuLcvxnbJV6U+ztjy6cfdHpvM+iiWxAge1CfPGojn0Awb25Uhky/B1XL 17 | zSUtrlmMLF/3sU27RzzUWhVMWfqvTgFST7fOyajVIUiSJmjv4VH3k9mpBFy5B7Mq 18 | Lf1K6Yrc6fW5+guc 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /test/rpcc/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID2jCCAsKgAwIBAgICECUwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUx 3 | EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg 4 | UHR5IEx0ZDAgFw0xNzA5MDgwNjQ4MDlaGA8yMTE3MDgxNTA2NDgwOVowWjELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAxMKMTAyNGNsaWVudDCCASIwDQYJKoZI 7 | hvcNAQEBBQADggEPADCCAQoCggEBALBIfkVSGxrdJw+lKfuEmTxzbRxbAFl+ERux 8 | YXxY5dQnbP43zjC/pGcfFDokgMDN3yfNp2qNC7Z+qV0DO0ZKTqXODtUZ7p9qsHKf 9 | R/WD5/wpsls2/FgSWRWxjNO/KQik+DCV3b4XEAfQyYXzCSp/k0fNStNJwww0RvV0 10 | 65txPBLmoTM2MaZvLUdHFefBm0XAYD/WNWksSus1lQcBvj+NzRT8VW2s5e3A3I/g 11 | gOcL1OjSILpC2M70V6tjwMb90dB8Ft5GgcHFHQVrhM5G2fMBl9scwxbIU+8rOfGU 12 | DWsZ47TqhjXVo3l8nF/6Bg37Y36Uv8K3Q/Gfdf7acZUwC39Mf7sCAwEAAaOBvDCB 13 | uTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBD 14 | ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUPWMKlE2+AF+J+eMkn9BdTUJtel0wXwYDVR0j 15 | BFgwVqFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAf 16 | BgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAKDJqVD5BLztMA0GCSqG 17 | SIb3DQEBCwUAA4IBAQBGMP5CmSFne+UcfFElEnB4kWyOhZNfphLAsugeANArwT05 18 | y7Mzg82s1TfJseINa4MGIpzlUoDfiFbZAUr9JdEC/lyWmNFgtjmlClcwx/nyVrP4 19 | 0kG6uOJdAa18ick0jg8V76UDDM8a+4N0j3WkoMygplCDzUyjXNZ9uHWKWWAk0RY2 20 | D2PnawUn3WREQ213CdKG6RkWe3xEqR8YQ3lD7llhVKMqYGx/cP6rdb7xtR3X/5u6 21 | AQRoOfvAzoWq7oDcOdsopbE0qPTtxL9tlLn4kPX8atVorY663RZUOh+mpoCnt0oD 22 | XJqSTJOYxa3sTCy3tqGox2QShy+PpC1Py9+PqF5p 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /test/rpcc/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAsEh+RVIbGt0nD6Up+4SZPHNtHFsAWX4RG7FhfFjl1Cds/jfO 3 | ML+kZx8UOiSAwM3fJ82nao0Ltn6pXQM7RkpOpc4O1Rnun2qwcp9H9YPn/CmyWzb8 4 | WBJZFbGM078pCKT4MJXdvhcQB9DJhfMJKn+TR81K00nDDDRG9XTrm3E8EuahMzYx 5 | pm8tR0cV58GbRcBgP9Y1aSxK6zWVBwG+P43NFPxVbazl7cDcj+CA5wvU6NIgukLY 6 | zvRXq2PAxv3R0HwW3kaBwcUdBWuEzkbZ8wGX2xzDFshT7ys58ZQNaxnjtOqGNdWj 7 | eXycX/oGDftjfpS/wrdD8Z91/tpxlTALf0x/uwIDAQABAoIBAD94Gl6DDVdCwMmV 8 | QeRY/YMSW/qVmS6fgTFjUCIhL76O42M0XHyYw2yNeA7nDaRdezctiFWIqurt97SR 9 | cUoBfwebhSOczpl/JEBgsNvoviHAMYM97OUY1hJFdtrWPMPsAaAclVXXn+Hwfl5H 10 | /sjaXgDAHWytZhiCyfmeOyYgQuzam75hVwUW+RWhco8s5ERvBDNHf4thP2XQomV4 11 | V5fQSuWhOA9kDcc+QOZr170l30TrV+/iV0ANGnbmj71yjG96kTj8iGZwFrmZoP2S 12 | QY6iPPvoKTviL1ScAkcMx2ry71+s1ytBt3SyUDu3C/CSOVVjenHKuKIKpsMWANxU 13 | M4qR38ECgYEA1PP9lriHG8p9xQwKWWKKqHq6v0BA8HmF9KSI4VWiQbgtE6rlQBHL 14 | Bb1cYir6th6Hr9WmMovwGYbgcBopW6E2VQEclII20qO2uwAMC7r5ggrHBPX0OLQM 15 | 9EVEXgXU5RVvSQPGM27K8d3+QwnwmGqSHJ788ll4KivdG6cywGLCIukCgYEA0+rj 16 | fPV6uipvOKLI4fcjDNjQF+nPrd0X/pO92loEKiHJxT9qVM0IBWFUAzXHTBELEmHf 17 | 98d2ALxOH/u+nMlBq5sjOoGk+YOur0FTxLSKUwYikKXCq1KacY7PuTTQDrHsQgL2 18 | +cIdXiZsVnvLf+AC/HsHwaM6QIWlwr6WktxA/wMCgYBpNSnh3sSRTFcalLpAMtkC 19 | J7A/cIUkyUUO+oLNoLsK8O0EPHgKUUAWU9lifo1ikG7Exi7X5h723ZXgjGxGYr0u 20 | /d+a10IyisNqW7mXygYe4yg0ITdJFFsaC2addRAbJZ3XWs/+9Qu6tsw5Xp3JD02k 21 | aj/Zk640V3tjgBUoyyHSkQKBgF1D9CirtP637Eklw3lHliJyTWso2ODw/D1rclPl 22 | 6A1O34ky9q4pqk0L37il83dzzMajUrmvDC0Yst16oxJHtOI8nc2C/0NdBAVXn/Fj 23 | b7lIau/0M+/Io/Y0XxFov+Snw43t1hpnpxsnl+icBRzbTxUlZG2BVALPjvtde9FY 24 | pDZXAoGAKl35vXLj/hipfXg+XpGTbngjhHmqY44GRxFnJwNO6LR124HRSrb7Vt9z 25 | on8OMgiGLxvDg09w6lL9Mzue2dWeTIgmedv6Tz8HTRPqYkalPKNdmuc3arEG4MjS 26 | XqZX/10ADpgMF7IItFhlr7kE52aD6AWd9crlP1pUrU5Q7XYlZIM= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/rpcc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "io/ioutil" 7 | "log" 8 | 9 | "golang.org/x/net/context" 10 | "google.golang.org/grpc" 11 | "google.golang.org/grpc/credentials" 12 | 13 | "github.com/elvizlai/grpc-socks/pb" 14 | ) 15 | 16 | var ( 17 | crt = "client.crt" 18 | key = "client.key" 19 | ca = "ca.crt" 20 | addr = ":8080" 21 | ) 22 | 23 | func main() { 24 | // Load the client certificates from disk 25 | certificate, err := tls.LoadX509KeyPair(crt, key) 26 | if err != nil { 27 | log.Fatalf("could not load client key pair: %s", err) 28 | } 29 | 30 | // Create a certificate pool from the certificate authority 31 | certPool := x509.NewCertPool() 32 | ca, err := ioutil.ReadFile(ca) 33 | if err != nil { 34 | log.Fatalf("could not read ca certificate: %s", err) 35 | } 36 | 37 | // Append the certificates from the CA 38 | if ok := certPool.AppendCertsFromPEM(ca); !ok { 39 | log.Fatalf("failed to append ca certs") 40 | } 41 | 42 | creds := credentials.NewTLS(&tls.Config{ 43 | ServerName: "1024server", 44 | Certificates: []tls.Certificate{certificate}, 45 | RootCAs: certPool, 46 | //InsecureSkipVerify: true, 47 | }) 48 | 49 | // Create a connection with the TLS credentials 50 | conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds)) 51 | if err != nil { 52 | log.Fatalf("could not dial %s: %s", addr, err) 53 | } 54 | 55 | // Initialize the client and make the request 56 | client := pb.NewProxyServiceClient(conn) 57 | pong, err := client.Echo(context.Background(), &pb.Payload{Data: []byte{1, 0, 2, 4}}) 58 | if err != nil { 59 | log.Fatalf("could not ping %s: %s", addr, err) 60 | } 61 | 62 | // Log the ping 63 | log.Printf("%s\n", pong.String()) 64 | } 65 | -------------------------------------------------------------------------------- /test/rpcs/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDCDCCAfACCQCgyalQ+QS87TANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMCAXDTE3MDkwODA0MzMyMloYDzIxMTcwODE1MDQzMzIyWjBFMQsw 5 | CQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJu 6 | ZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC 7 | AQEA47s0jYDAFK+8IDGwv4CLch+uNZm9pyb/02NpKhhUnFrVDHWSGCUol9JQu/o/ 8 | pAqxfluf/6Zdl4NcU/aLeFpompqU4IqUOvU3ool3nDUOUNPf2Az89ZCzD1P25iPk 9 | MZ3Ih8gq/X8mmjQ1r5giEiUzCMJVdVpN2+4X2q56XIbN0lujFl4PIsT/IwIWJOsq 10 | qG6yLaAUW67oG29Bxb6sualciRcv7CbKVPgU+VOtryo0UgPo8h444Vzq3TD5Q15T 11 | ife60QqTyd4AV15XN9uCkhLEx+BLa+RD6mmOx2i4eqkPoDPLo1jaSpEIbNZZEz2+ 12 | rEMA5p5ZMWx16YmFqc0dp239fwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBl0GYS 13 | BhrLII3al+JWvYdaCBXn13OwpEyp7gM5rNODVOOAoyU7bj10JkcX7szUIPm3lqCI 14 | GFhJf+LgxMfgaDTsudBbBlnVi+WYB+XqufTVTstiTCuN8wIq4VkZrCqCvtjaZx7W 15 | lPeWTtzRiifHwXkJSli8qY52F14mX1xYr+4xXboonWIsO8j4bevINR8pnT2o7Xnv 16 | 8Y/0pGZAEuLcvxnbJV6U+ztjy6cfdHpvM+iiWxAge1CfPGojn0Awb25Uhky/B1XL 17 | zSUtrlmMLF/3sU27RzzUWhVMWfqvTgFST7fOyajVIUiSJmjv4VH3k9mpBFy5B7Mq 18 | Lf1K6Yrc6fW5+guc 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /test/rpcs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "net" 10 | 11 | "golang.org/x/net/context" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/credentials" 14 | "google.golang.org/grpc/peer" 15 | 16 | "github.com/elvizlai/grpc-socks/pb" 17 | ) 18 | 19 | var ( 20 | crt = "server.crt" 21 | key = "server.key" 22 | ca = "ca.crt" 23 | addr = ":8080" 24 | ) 25 | 26 | func main() { 27 | // Load the certificates from disk 28 | certificate, err := tls.LoadX509KeyPair(crt, key) 29 | if err != nil { 30 | log.Fatalf("could not load server key pair: %s", err) 31 | } 32 | 33 | // Create a certificate pool from the certificate authority 34 | certPool := x509.NewCertPool() 35 | ca, err := ioutil.ReadFile(ca) 36 | if err != nil { 37 | log.Fatalf("could not read ca certificate: %s", err) 38 | } 39 | 40 | // Append the client certificates from the CA 41 | if ok := certPool.AppendCertsFromPEM(ca); !ok { 42 | log.Fatalf("failed to append client certs") 43 | } 44 | 45 | // Create the channel to listen on 46 | lis, err := net.Listen("tcp", addr) 47 | if err != nil { 48 | log.Fatalf("could not list on %s: %s", addr, err) 49 | } 50 | 51 | // Create the TLS credentials 52 | creds := credentials.NewTLS(&tls.Config{ 53 | ClientAuth: tls.RequireAndVerifyClientCert, 54 | Certificates: []tls.Certificate{certificate}, 55 | ClientCAs: certPool, 56 | }) 57 | 58 | // Create the gRPC server with the credentials 59 | srv := grpc.NewServer(grpc.Creds(creds)) 60 | 61 | // Register the handler object 62 | pb.RegisterProxyServiceServer(srv, &proxy{}) 63 | 64 | // Serve and Listen 65 | if err := srv.Serve(lis); err != nil { 66 | log.Fatalf("grpc serve error: %s", err) 67 | } 68 | } 69 | 70 | type proxy struct { 71 | } 72 | 73 | func (p *proxy) Echo(ctx context.Context, req *pb.Payload) (*pb.Payload, error) { 74 | pr, ok := peer.FromContext(ctx) 75 | 76 | if ok { 77 | fmt.Printf("%#v\n", pr.AuthInfo) 78 | } 79 | 80 | return req, nil 81 | } 82 | 83 | func (p *proxy) Pipeline(stream pb.ProxyService_PipelineServer) error { 84 | return nil 85 | } 86 | 87 | func (p *proxy) PipelineUDP(stream pb.ProxyService_PipelineUDPServer) error { 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /test/rpcs/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID2jCCAsKgAwIBAgICECQwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUx 3 | EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg 4 | UHR5IEx0ZDAgFw0xNzA5MDgwNjQ3MzZaGA8yMTE3MDgxNTA2NDczNlowWjELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAxMKMTAyNHNlcnZlcjCCASIwDQYJKoZI 7 | hvcNAQEBBQADggEPADCCAQoCggEBALOHMcoZk3EGG68TacHsZrDHSteFvUIQ0zFE 8 | YL9Tqd/W+8UGxy6Q/eLWSfme2ZKeQu0FgLRRLdYc81i9xHLZeWBtI7Jn6b8HI1w3 9 | dlaDhh6bx3hwYXfhtor2qh/uOTDmFSXzZG7idiq8bgCgC1pbloKJwL9DqTjJm2sE 10 | 3059rv5at/C3uNT40AgGFMIlijE04UDOFj6Kjdpknx5yUJeGxNVjL+1rTBB1173R 11 | 2x0qr1v6OycFvXDQGtLxDUamCDIZap1ktScespmuxyQjxosZiPehrgUI0TTvjw8v 12 | 3yDSOEEaZE10zuVv3CF2Qm5HJfs/vUHBJ4khN2c2R0CyibeuSKcCAwEAAaOBvDCB 13 | uTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBD 14 | ZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUDVwFPS+obvEWSa5qEUnoYj4jtaMwXwYDVR0j 15 | BFgwVqFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAf 16 | BgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAKDJqVD5BLztMA0GCSqG 17 | SIb3DQEBCwUAA4IBAQBX/mVJloxW19T1/3SkjIXKMCbJXkouVP56gCWhADHWp6al 18 | pHIJWtoSg9+jwdpgcmoF7nxuytL22aVWjUXxwLub1eBM+8aYDvLvueo9skpNxemv 19 | KMy8zOJntSLaiQNNoa51ygsebIdZuYMxLz2QGZsxmixaaYMVgicMv589yLTIlY+l 20 | VkzyRycU0R+fybiB6NnzXmmJRUzWcY3ynqy5zyHFoc/sT7cqzY3/cAwBDFm6XrBR 21 | O483WZMwLyiN7jhph4lwUqHObAnODHg9xgHUvpZ1kfphm+Vhqyoy4DUfpwG6kxhV 22 | gjmd8Z6xWA1zUHYw6W3mWIJmANcG5rrhxmK82yzX 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /test/rpcs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAs4cxyhmTcQYbrxNpwexmsMdK14W9QhDTMURgv1Op39b7xQbH 3 | LpD94tZJ+Z7Zkp5C7QWAtFEt1hzzWL3Ectl5YG0jsmfpvwcjXDd2VoOGHpvHeHBh 4 | d+G2ivaqH+45MOYVJfNkbuJ2KrxuAKALWluWgonAv0OpOMmbawTfTn2u/lq38Le4 5 | 1PjQCAYUwiWKMTThQM4WPoqN2mSfHnJQl4bE1WMv7WtMEHXXvdHbHSqvW/o7JwW9 6 | cNAa0vENRqYIMhlqnWS1Jx6yma7HJCPGixmI96GuBQjRNO+PDy/fINI4QRpkTXTO 7 | 5W/cIXZCbkcl+z+9QcEniSE3ZzZHQLKJt65IpwIDAQABAoIBAEH/uJkgr+iEI1dI 8 | nr+RBeuFSIlU/E+2Wb2p8ptDyZaltScop78BxKRIvSdMc5YIInh6Zmdj1KL7P6bn 9 | FLpYItwFI3Np6i+1ExXKufFyJrNH95vaTKrCytXpen+El3fXjfzGrqxpCqIJZbNL 10 | IwFT3FAfDkICQMrEqyM7xlBXuiZ9W5YBH7yy935DAX9iXKdKFwnO5tlRmqEkUPD6 11 | 7tlxnq8aeIsKUyBd/oNGGVgv7X0aX9Dg11RTF9atT4zLiOqdUc93+dKt1Yv21kjD 12 | ewFfTfP8cCG7zYcMk7NsnsqUgV2VEYCTCPKfWck8wZ46gkEUdgD1L5EMfg5mOZs1 13 | TAljXrECgYEA3sL0Yjk9mlxTeXPAzG9einyOeYvFK1fLNiib/l4zaFuQxfI1UtyS 14 | VWifBkyZf0j1IFeufLatGDZIewZYRbwpc4sOWItyZfDsLRO5ESNsRCOUQhMOXHBY 15 | nxA6B024T5S19B7UIZhzJViWDrWAw50VbQNqMCi2fbpdySmZORkvRK8CgYEAzlDP 16 | Gn29GVR/5Mxx+u6NU+NfBlLyDg+6RhWTrg5Z5QF51/iRBYKiCT3pXuaMY3V3ck7k 17 | lI97RkGbZ2W/QOnAssth8MCcZAQlXPYOty5WoAjTOX5aOI4JhoRDrCmOxMTY/QQm 18 | 3xL9dBULT6q/ePj+C/hj3C750VQkHTaHthBlqYkCgYEAv7DsNAol5MtGWeEuK746 19 | kNmWkSmhxiaKjFRtyHqvqkIBVxWSpXeBxVV+YyFPhaT2q5pIYLGiSo+L17IiDABb 20 | ez5SXgfBrc+grTmnrAPbitdDIYjcdteGq3p/NQHAdVr3KNfOnApgK+cLWiT+ZVar 21 | tV+W5VJjl9XkFtNhMy1k66kCgYEAonJ6P2+VvyYLdYmMjXnbiZvTrFUPBgdYMeq3 22 | VysxmOEqoOS0u1P6ZbB72h9Jlt5c6II59Mds1wq9ZBJ3HpNvCNifC0N9YDrreGC9 23 | gXpY9rU8K17Thwg/c80cw6gNI92ixMXKHYcETaxsNZRKIuRak+5GIo5SYYIQ1Xek 24 | L7SFd5ECgYBRklUFB+heNBuvvfY6+HDzlQe1X2EOoLrLlh0ip5yHVYSXWCIdcHdy 25 | yaZfINDg9hXnJ07E/XE5VUnV1o2Q6VNBIYud980yWQa9UQqkHEUsHqC3iv3OqJnn 26 | x9+nookY1Y5aHWD+he2qAMsrvmlRJ3z/cPsa0CKH6OkksMegbWyGEw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/udpc/a.js: -------------------------------------------------------------------------------- 1 | var SocksClient = require('socks-client'); 2 | 3 | var options = { 4 | proxy: { 5 | ipaddress: "127.0.0.1", // Random public proxy 6 | port: 50050, 7 | type: 5 // type is REQUIRED. Valid types: [4, 5] (note 4 also works for 4a) 8 | }, 9 | target: { 10 | host: "google.com", // can be an ip address or domain (4a and 5 only) 11 | port: 80 12 | }, 13 | command: 'connect' // This defaults to connect, so it's optional if you're not using BIND or Associate. 14 | }; 15 | 16 | SocksClient.createConnection(options, function(err, socket, info) { 17 | if (err) 18 | console.log(err); 19 | else { 20 | // Connection has been established, we can start sending data now: 21 | socket.write("GET / HTTP/1.1\nHost: google.com\n\n"); 22 | socket.on('data', function(data) { 23 | console.log(data.length); 24 | console.log(data); 25 | }); 26 | 27 | // PLEASE NOTE: sockets need to be resumed before any data will come in or out as they are paused right before this callback is fired. 28 | socket.resume(); 29 | 30 | // 569 31 | // { 33 | console.log("socket已关闭"); 34 | }); 35 | 36 | udp.on("error", err => { 37 | console.log(`server error:\n${err.stack}`); 38 | udp.close(); 39 | }); 40 | 41 | udp.on("message", (msg, rinfo) => { 42 | console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`); 43 | }); 44 | 45 | udp.on("listening", () => { 46 | const address = udp.address(); 47 | console.log(`server listening ${address.address}:${address.port}`); 48 | }); 49 | 50 | // In this example we are going to send "Hello" to 1.2.3.4:2323 through the SOCKS proxy. 51 | 52 | //udp.bind(41234); 53 | 54 | setInterval(() => { 55 | let reqStr = "hello" + Math.random(); 56 | let pack = Socks.createUDPFrame( 57 | { host: "1.2.3.4", port: 2323 }, 58 | new Buffer(reqStr) 59 | ); 60 | udp.send(pack, 0, pack.length, info.port, info.host); 61 | console.log("req", reqStr); 62 | }, 1000); 63 | } 64 | }); 65 | -------------------------------------------------------------------------------- /test/udpc/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "time" 8 | 9 | "github.com/elvizlai/grpc-socks/lib" 10 | ) 11 | 12 | func main() { 13 | conn, err := net.Dial("udp", "127.0.0.1:50051") 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | defer conn.Close() 18 | 19 | go func() { 20 | for i := 0; ; i++ { 21 | reqStr := "ping" + fmt.Sprint(i) 22 | log.Println("req", reqStr) 23 | _, err := conn.Write([]byte(reqStr)) 24 | if err != nil { 25 | log.Fatal(err) 26 | break 27 | } 28 | <-time.After(time.Second) 29 | } 30 | }() 31 | 32 | buff := make([]byte, lib.UDPMaxSize) 33 | for { 34 | n, err := conn.Read(buff) 35 | if err != nil { 36 | continue 37 | } 38 | log.Println("resp", string(buff[:n])) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /test/udps/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/elvizlai/grpc-socks/lib" 10 | ) 11 | 12 | func main() { 13 | udpLn, err := net.ListenPacket("udp", ":50051") 14 | if err != nil { 15 | log.Fatal(err) 16 | return 17 | } 18 | 19 | buff := make([]byte, lib.UDPMaxSize) 20 | 21 | for { 22 | n, addr, err := udpLn.ReadFrom(buff) 23 | if err != nil { 24 | log.Println(err) 25 | continue 26 | } 27 | 28 | log.Println("addr:", addr, "data:", string(buff[:n])) 29 | 30 | go func() { 31 | _, err = udpLn.WriteTo([]byte(time.Now().Format(http.TimeFormat)+" ---> "+string(buff[:n])), addr) 32 | if err != nil { 33 | log.Println(err) 34 | } 35 | }() 36 | } 37 | } 38 | --------------------------------------------------------------------------------