├── .github └── workflows │ ├── go.yml │ └── gorelease.yml ├── .goreleaser.yml ├── README.md ├── cmd └── root.go ├── go-socks5 ├── README.md ├── auth.go ├── auth_test.go ├── credentials.go ├── credentials_test.go ├── request.go ├── request_test.go ├── resolver.go ├── resolver_test.go ├── ruleset.go ├── ruleset_test.go ├── socks5.go └── socks5_test.go ├── go.mod ├── go.sum ├── images └── schema.png ├── main.go ├── prompt ├── completer.go ├── help.go └── prompt.go ├── renovate.json ├── router ├── geoip.go └── router.go ├── socks └── socks.go └── utils ├── netstat ├── netstat.go ├── netstat_linux.go └── netstat_windows.go └── utils.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.22 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /.github/workflows/gorelease.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - v*.* 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | goreleaser: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - 16 | name: Checkout 17 | uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | - 21 | name: Set up Go 22 | uses: actions/setup-go@v2 23 | with: 24 | go-version: 1.22 25 | - 26 | name: Run GoReleaser 27 | uses: goreleaser/goreleaser-action@v2 28 | with: 29 | distribution: goreleaser 30 | version: latest 31 | args: release --rm-dist 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - goos: 3 | - linux 4 | - windows 5 | goarch: 6 | - amd64 7 | archives: 8 | - 9 | format_overrides: 10 | - goos: windows 11 | format: zip 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Router socks 2 | ====== 3 | 4 | The next step after compromising a machine is to enumerate the network behind. Many tools exist to expose a socks port on the attacker's machine and send all the traffic through a tunnel to the compromised machine. When several socks ports are available, we have to manage different proxychains configuration to choose the targeted network. 5 | This tool will expose one socks port and route the traffic through the configured path. 6 | 7 | 8 | The idea came after using [chisel](https://github.com/jpillora/chisel). Chisel is really helpful but it can get hard to manage many clients as it is opening a new socks port for each new client with reverse mode. 9 | 10 | ![Schema](./images/schema.png) 11 | 12 | ## Usage 13 | 14 | Start the socks server: 15 | 16 | ``` 17 | Usage: 18 | rsocks [flags] 19 | 20 | Flags: 21 | -h, --help help for rsocks 22 | -i, --ip string IP for socks5 server (default "0.0.0.0") 23 | -p, --port int Socks5 port (default 1080) 24 | ``` 25 | Define the routes: 26 | ``` 27 | RouterSocks> help 28 | route: Manage route to socks servers 29 | chisel: Liste chisel socks server on localhost 30 | help: help command 31 | RouterSocks> route add 192.168.1.0/24 10.0.0.1:1081 32 | [*] Successfull route added 33 | RouterSocks> chisel 34 | [0] 127.0.0.1:1081 35 | RouterSocks> route add 192.168.2.0/24 0 36 | [*] Successfull route added 37 | RouterSocks> route 38 | 192.168.1.0/24 => 10.0.0.1:1081 39 | 192.168.2.0/24 => 127.0.0.1:1081 40 | ``` 41 | 42 | ## Features 43 | - Route network through remote or local socks server 44 | - Use chisel session ID 45 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nodauf/Go-RouterSocks/router" 6 | "log" 7 | "os" 8 | 9 | "github.com/spf13/cobra" 10 | 11 | prompt "github.com/nodauf/Go-RouterSocks/prompt" 12 | socks "github.com/nodauf/Go-RouterSocks/socks" 13 | ) 14 | 15 | var cfgFile string 16 | var port int 17 | var ip string 18 | var geoIPDB string 19 | 20 | var rootCmd = &cobra.Command{ 21 | Use: "rsocks", 22 | Short: "Router socks", 23 | Long: `Run a socks server and redirect the traffic to other socks server 24 | according of the defined routes.`, 25 | PreRunE: func(cmd *cobra.Command, args []string) error { 26 | if geoIPDB != "" { 27 | if _, err := os.Open(geoIPDB); err != nil { 28 | return fmt.Errorf("cannot open the file %s: %s", geoIPDB, err.Error()) 29 | } 30 | } 31 | return nil 32 | }, 33 | Run: func(cmd *cobra.Command, args []string) { 34 | if geoIPDB != "" { 35 | if err := router.LoadGeoIPDatabase(geoIPDB); err != nil { 36 | log.Fatalf("error while loading the GeoIP database: %s", err.Error()) 37 | } else { 38 | log.Printf("[*] GeoIP database %s loaded\n", geoIPDB) 39 | } 40 | } 41 | socks.StartSocks(ip, port) 42 | prompt.Prompt() 43 | }, 44 | } 45 | 46 | func Execute() { 47 | if err := rootCmd.Execute(); err != nil { 48 | fmt.Println(err) 49 | os.Exit(1) 50 | } 51 | } 52 | 53 | func init() { 54 | 55 | rootCmd.Flags().IntVarP( 56 | &port, "port", "p", 1080, 57 | "Socks5 port", 58 | ) 59 | rootCmd.Flags().StringVarP( 60 | &ip, "ip", "i", "0.0.0.0", 61 | "IP for socks5 server", 62 | ) 63 | rootCmd.Flags().StringVarP( 64 | &geoIPDB, "geoip", "g", "", 65 | "Path to the GeoIP database (GeoLite2-Country.mmdb)", 66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /go-socks5/README.md: -------------------------------------------------------------------------------- 1 | go-socks5 [![Build Status](https://travis-ci.org/armon/go-socks5.png)](https://travis-ci.org/armon/go-socks5) 2 | ========= 3 | 4 | Provides the `socks5` package that implements a [SOCKS5 server](http://en.wikipedia.org/wiki/SOCKS). 5 | SOCKS (Secure Sockets) is used to route traffic between a client and server through 6 | an intermediate proxy layer. This can be used to bypass firewalls or NATs. 7 | 8 | Feature 9 | ======= 10 | 11 | The package has the following features: 12 | * "No Auth" mode 13 | * User/Password authentication 14 | * Support for the CONNECT command 15 | * Rules to do granular filtering of commands 16 | * Custom DNS resolution 17 | * Unit tests 18 | 19 | TODO 20 | ==== 21 | 22 | The package still needs the following: 23 | * Support for the BIND command 24 | * Support for the ASSOCIATE command 25 | 26 | 27 | Example 28 | ======= 29 | 30 | Below is a simple example of usage 31 | 32 | ```go 33 | // Create a SOCKS5 server 34 | conf := &socks5.Config{} 35 | server, err := socks5.New(conf) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | // Create SOCKS5 proxy on localhost port 8000 41 | if err := server.ListenAndServe("tcp", "127.0.0.1:8000"); err != nil { 42 | panic(err) 43 | } 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /go-socks5/auth.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | const ( 9 | NoAuth = uint8(0) 10 | noAcceptable = uint8(255) 11 | UserPassAuth = uint8(2) 12 | userAuthVersion = uint8(1) 13 | authSuccess = uint8(0) 14 | authFailure = uint8(1) 15 | ) 16 | 17 | var ( 18 | UserAuthFailed = fmt.Errorf("User authentication failed") 19 | NoSupportedAuth = fmt.Errorf("No supported authentication mechanism") 20 | ) 21 | 22 | // A Request encapsulates authentication state provided 23 | // during negotiation 24 | type AuthContext struct { 25 | // Provided auth method 26 | Method uint8 27 | // Payload provided during negotiation. 28 | // Keys depend on the used auth method. 29 | // For UserPassauth contains Username 30 | Payload map[string]string 31 | } 32 | 33 | type Authenticator interface { 34 | Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) 35 | GetCode() uint8 36 | } 37 | 38 | // NoAuthAuthenticator is used to handle the "No Authentication" mode 39 | type NoAuthAuthenticator struct{} 40 | 41 | func (a NoAuthAuthenticator) GetCode() uint8 { 42 | return NoAuth 43 | } 44 | 45 | func (a NoAuthAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) { 46 | _, err := writer.Write([]byte{socks5Version, NoAuth}) 47 | return &AuthContext{NoAuth, nil}, err 48 | } 49 | 50 | // UserPassAuthenticator is used to handle username/password based 51 | // authentication 52 | type UserPassAuthenticator struct { 53 | Credentials CredentialStore 54 | } 55 | 56 | func (a UserPassAuthenticator) GetCode() uint8 { 57 | return UserPassAuth 58 | } 59 | 60 | func (a UserPassAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error) { 61 | // Tell the client to use user/pass auth 62 | if _, err := writer.Write([]byte{socks5Version, UserPassAuth}); err != nil { 63 | return nil, err 64 | } 65 | 66 | // Get the version and username length 67 | header := []byte{0, 0} 68 | if _, err := io.ReadAtLeast(reader, header, 2); err != nil { 69 | return nil, err 70 | } 71 | 72 | // Ensure we are compatible 73 | if header[0] != userAuthVersion { 74 | return nil, fmt.Errorf("Unsupported auth version: %v", header[0]) 75 | } 76 | 77 | // Get the user name 78 | userLen := int(header[1]) 79 | user := make([]byte, userLen) 80 | if _, err := io.ReadAtLeast(reader, user, userLen); err != nil { 81 | return nil, err 82 | } 83 | 84 | // Get the password length 85 | if _, err := reader.Read(header[:1]); err != nil { 86 | return nil, err 87 | } 88 | 89 | // Get the password 90 | passLen := int(header[0]) 91 | pass := make([]byte, passLen) 92 | if _, err := io.ReadAtLeast(reader, pass, passLen); err != nil { 93 | return nil, err 94 | } 95 | 96 | // Verify the password 97 | if a.Credentials.Valid(string(user), string(pass)) { 98 | if _, err := writer.Write([]byte{userAuthVersion, authSuccess}); err != nil { 99 | return nil, err 100 | } 101 | } else { 102 | if _, err := writer.Write([]byte{userAuthVersion, authFailure}); err != nil { 103 | return nil, err 104 | } 105 | return nil, UserAuthFailed 106 | } 107 | 108 | // Done 109 | return &AuthContext{UserPassAuth, map[string]string{"Username": string(user)}}, nil 110 | } 111 | 112 | // authenticate is used to handle connection authentication 113 | func (s *Server) authenticate(conn io.Writer, bufConn io.Reader) ([]byte, *AuthContext, error) { 114 | // Get the methods 115 | methods, err := readMethods(bufConn) 116 | if err != nil { 117 | return nil, nil, fmt.Errorf("Failed to get auth methods: %v", err) 118 | } 119 | 120 | // Select a usable method 121 | for _, method := range methods { 122 | cator, found := s.authMethods[method] 123 | if found { 124 | authContext, err := cator.Authenticate(bufConn, conn) 125 | return methods, authContext, err 126 | } 127 | } 128 | 129 | // No usable method found 130 | return nil, nil, noAcceptableAuth(conn) 131 | } 132 | 133 | // noAcceptableAuth is used to handle when we have no eligible 134 | // authentication mechanism 135 | func noAcceptableAuth(conn io.Writer) error { 136 | conn.Write([]byte{socks5Version, noAcceptable}) 137 | return NoSupportedAuth 138 | } 139 | 140 | // readMethods is used to read the number of methods 141 | // and proceeding auth methods 142 | func readMethods(r io.Reader) ([]byte, error) { 143 | header := []byte{0} 144 | if _, err := r.Read(header); err != nil { 145 | return nil, err 146 | } 147 | 148 | numMethods := int(header[0]) 149 | methods := make([]byte, numMethods) 150 | _, err := io.ReadAtLeast(r, methods, numMethods) 151 | return methods, err 152 | } 153 | -------------------------------------------------------------------------------- /go-socks5/auth_test.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestNoAuth(t *testing.T) { 9 | req := bytes.NewBuffer(nil) 10 | req.Write([]byte{1, NoAuth}) 11 | var resp bytes.Buffer 12 | 13 | s, _ := New(&Config{}) 14 | _, ctx, err := s.authenticate(&resp, req) 15 | if err != nil { 16 | t.Fatalf("err: %v", err) 17 | } 18 | 19 | if ctx.Method != NoAuth { 20 | t.Fatal("Invalid Context Method") 21 | } 22 | 23 | out := resp.Bytes() 24 | if !bytes.Equal(out, []byte{socks5Version, NoAuth}) { 25 | t.Fatalf("bad: %v", out) 26 | } 27 | } 28 | 29 | func TestPasswordAuth_Valid(t *testing.T) { 30 | req := bytes.NewBuffer(nil) 31 | req.Write([]byte{2, NoAuth, UserPassAuth}) 32 | req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'}) 33 | var resp bytes.Buffer 34 | 35 | cred := StaticCredentials{ 36 | "foo": "bar", 37 | } 38 | 39 | cator := UserPassAuthenticator{Credentials: cred} 40 | 41 | s, _ := New(&Config{AuthMethods: []Authenticator{cator}}) 42 | 43 | _, ctx, err := s.authenticate(&resp, req) 44 | if err != nil { 45 | t.Fatalf("err: %v", err) 46 | } 47 | 48 | if ctx.Method != UserPassAuth { 49 | t.Fatal("Invalid Context Method") 50 | } 51 | 52 | val, ok := ctx.Payload["Username"] 53 | if !ok { 54 | t.Fatal("Missing key Username in auth context's payload") 55 | } 56 | 57 | if val != "foo" { 58 | t.Fatal("Invalid Username in auth context's payload") 59 | } 60 | 61 | out := resp.Bytes() 62 | if !bytes.Equal(out, []byte{socks5Version, UserPassAuth, 1, authSuccess}) { 63 | t.Fatalf("bad: %v", out) 64 | } 65 | } 66 | 67 | func TestPasswordAuth_Invalid(t *testing.T) { 68 | req := bytes.NewBuffer(nil) 69 | req.Write([]byte{2, NoAuth, UserPassAuth}) 70 | req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'z'}) 71 | var resp bytes.Buffer 72 | 73 | cred := StaticCredentials{ 74 | "foo": "bar", 75 | } 76 | cator := UserPassAuthenticator{Credentials: cred} 77 | s, _ := New(&Config{AuthMethods: []Authenticator{cator}}) 78 | 79 | _, ctx, err := s.authenticate(&resp, req) 80 | if err != UserAuthFailed { 81 | t.Fatalf("err: %v", err) 82 | } 83 | 84 | if ctx != nil { 85 | t.Fatal("Invalid Context Method") 86 | } 87 | 88 | out := resp.Bytes() 89 | if !bytes.Equal(out, []byte{socks5Version, UserPassAuth, 1, authFailure}) { 90 | t.Fatalf("bad: %v", out) 91 | } 92 | } 93 | 94 | func TestNoSupportedAuth(t *testing.T) { 95 | req := bytes.NewBuffer(nil) 96 | req.Write([]byte{1, NoAuth}) 97 | var resp bytes.Buffer 98 | 99 | cred := StaticCredentials{ 100 | "foo": "bar", 101 | } 102 | cator := UserPassAuthenticator{Credentials: cred} 103 | 104 | s, _ := New(&Config{AuthMethods: []Authenticator{cator}}) 105 | 106 | _, ctx, err := s.authenticate(&resp, req) 107 | if err != NoSupportedAuth { 108 | t.Fatalf("err: %v", err) 109 | } 110 | 111 | if ctx != nil { 112 | t.Fatal("Invalid Context Method") 113 | } 114 | 115 | out := resp.Bytes() 116 | if !bytes.Equal(out, []byte{socks5Version, noAcceptable}) { 117 | t.Fatalf("bad: %v", out) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /go-socks5/credentials.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | // CredentialStore is used to support user/pass authentication 4 | type CredentialStore interface { 5 | Valid(user, password string) bool 6 | } 7 | 8 | // StaticCredentials enables using a map directly as a credential store 9 | type StaticCredentials map[string]string 10 | 11 | func (s StaticCredentials) Valid(user, password string) bool { 12 | pass, ok := s[user] 13 | if !ok { 14 | return false 15 | } 16 | return password == pass 17 | } 18 | -------------------------------------------------------------------------------- /go-socks5/credentials_test.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestStaticCredentials(t *testing.T) { 8 | creds := StaticCredentials{ 9 | "foo": "bar", 10 | "baz": "", 11 | } 12 | 13 | if !creds.Valid("foo", "bar") { 14 | t.Fatalf("expect valid") 15 | } 16 | 17 | if !creds.Valid("baz", "") { 18 | t.Fatalf("expect valid") 19 | } 20 | 21 | if creds.Valid("foo", "") { 22 | t.Fatalf("expect invalid") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /go-socks5/request.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "strconv" 8 | "strings" 9 | 10 | "golang.org/x/net/context" 11 | ) 12 | 13 | const ( 14 | ConnectCommand = uint8(1) 15 | BindCommand = uint8(2) 16 | AssociateCommand = uint8(3) 17 | ipv4Address = uint8(1) 18 | fqdnAddress = uint8(3) 19 | ipv6Address = uint8(4) 20 | ) 21 | 22 | const ( 23 | successReply uint8 = iota 24 | serverFailure 25 | ruleFailure 26 | networkUnreachable 27 | hostUnreachable 28 | connectionRefused 29 | ttlExpired 30 | commandNotSupported 31 | addrTypeNotSupported 32 | ) 33 | 34 | var ( 35 | unrecognizedAddrType = fmt.Errorf("Unrecognized address type") 36 | ) 37 | 38 | // AddressRewriter is used to rewrite a destination transparently 39 | type AddressRewriter interface { 40 | Rewrite(ctx context.Context, request *Request) (context.Context, *AddrSpec) 41 | } 42 | 43 | // AddrSpec is used to return the target AddrSpec 44 | // which may be specified as IPv4, IPv6, or a FQDN 45 | type AddrSpec struct { 46 | FQDN string 47 | IP net.IP 48 | Port int 49 | } 50 | 51 | func (a *AddrSpec) String() string { 52 | if a.FQDN != "" { 53 | return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port) 54 | } 55 | return fmt.Sprintf("%s:%d", a.IP, a.Port) 56 | } 57 | 58 | // Address returns a string suitable to dial; prefer returning IP-based 59 | // address, fallback to FQDN 60 | func (a AddrSpec) Address() string { 61 | if 0 != len(a.IP) { 62 | return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port)) 63 | } 64 | return net.JoinHostPort(a.FQDN, strconv.Itoa(a.Port)) 65 | } 66 | 67 | // A Request represents request received by a server 68 | type Request struct { 69 | // Protocol version 70 | Version uint8 71 | // Requested command 72 | Command uint8 73 | // AuthContext provided during negotiation 74 | AuthContext *AuthContext 75 | // AddrSpec of the the network that sent the request 76 | RemoteAddr *AddrSpec 77 | // AddrSpec of the desired destination 78 | DestAddr *AddrSpec 79 | // AddrSpec of the actual destination (might be affected by rewrite) 80 | realDestAddr *AddrSpec 81 | bufConn io.Reader 82 | } 83 | 84 | type conn interface { 85 | Write([]byte) (int, error) 86 | RemoteAddr() net.Addr 87 | } 88 | 89 | // NewRequest creates a new Request from the tcp connection 90 | func NewRequest(bufConn io.Reader) ([]byte, *Request, error) { 91 | var receivedByte []byte 92 | // Read the version byte 93 | header := []byte{0, 0, 0} 94 | if _, err := io.ReadAtLeast(bufConn, header, 3); err != nil { 95 | return nil, nil, fmt.Errorf("Failed to get command version: %v", err) 96 | } 97 | receivedByte = append(receivedByte, header...) 98 | 99 | // Ensure we are compatible 100 | if header[0] != socks5Version { 101 | return nil, nil, fmt.Errorf("Unsupported command version: %v", header[0]) 102 | } 103 | 104 | // Read in the destination address 105 | receivedByteAddr, dest, err := readAddrSpec(bufConn) 106 | if err != nil { 107 | return nil, nil, err 108 | } 109 | receivedByte = append(receivedByte, receivedByteAddr...) 110 | 111 | request := &Request{ 112 | Version: socks5Version, 113 | Command: header[1], 114 | DestAddr: dest, 115 | bufConn: bufConn, 116 | } 117 | 118 | return receivedByte, request, nil 119 | } 120 | 121 | // handleRequest is used for request processing after authentication 122 | func (s *Server) handleRequest(req *Request, conn conn) error { 123 | ctx := context.Background() 124 | 125 | // Resolve the address if we have a FQDN 126 | dest := req.DestAddr 127 | if dest.FQDN != "" { 128 | ctx_, addr, err := s.config.Resolver.Resolve(ctx, dest.FQDN) 129 | if err != nil { 130 | if err := sendReply(conn, hostUnreachable, nil); err != nil { 131 | return fmt.Errorf("Failed to send reply: %v", err) 132 | } 133 | return fmt.Errorf("Failed to resolve destination '%v': %v", dest.FQDN, err) 134 | } 135 | ctx = ctx_ 136 | dest.IP = addr 137 | } 138 | // Apply any address rewrites 139 | req.realDestAddr = req.DestAddr 140 | if s.config.Rewriter != nil { 141 | ctx, req.realDestAddr = s.config.Rewriter.Rewrite(ctx, req) 142 | } 143 | 144 | // Switch on the command 145 | switch req.Command { 146 | case ConnectCommand: 147 | return s.handleConnect(ctx, conn, req) 148 | case BindCommand: 149 | return s.handleBind(ctx, conn, req) 150 | case AssociateCommand: 151 | return s.handleAssociate(ctx, conn, req) 152 | default: 153 | if err := sendReply(conn, commandNotSupported, nil); err != nil { 154 | return fmt.Errorf("Failed to send reply: %v", err) 155 | } 156 | return fmt.Errorf("Unsupported command: %v", req.Command) 157 | } 158 | } 159 | 160 | // handleConnect is used to handle a connect command 161 | func (s *Server) handleConnect(ctx context.Context, conn conn, req *Request) error { 162 | // Check if this is allowed 163 | 164 | if ctx_, ok := s.config.Rules.Allow(ctx, req); !ok { 165 | if err := sendReply(conn, ruleFailure, nil); err != nil { 166 | return fmt.Errorf("Failed to send reply: %v", err) 167 | } 168 | return fmt.Errorf("Connect to %v blocked by rules", req.DestAddr) 169 | } else { 170 | ctx = ctx_ 171 | } 172 | 173 | // Attempt to connect 174 | dial := s.config.Dial 175 | if dial == nil { 176 | dial = func(ctx context.Context, net_, addr string) (net.Conn, error) { 177 | return net.Dial(net_, addr) 178 | } 179 | } 180 | target, err := dial(ctx, "tcp", req.realDestAddr.Address()) 181 | if err != nil { 182 | msg := err.Error() 183 | resp := hostUnreachable 184 | if strings.Contains(msg, "refused") { 185 | resp = connectionRefused 186 | } else if strings.Contains(msg, "network is unreachable") { 187 | resp = networkUnreachable 188 | } 189 | if err := sendReply(conn, resp, nil); err != nil { 190 | return fmt.Errorf("Failed to send reply: %v", err) 191 | } 192 | return fmt.Errorf("Connect to %v failed: %v", req.DestAddr, err) 193 | } 194 | defer target.Close() 195 | 196 | // Send success 197 | local := target.LocalAddr().(*net.TCPAddr) 198 | bind := AddrSpec{IP: local.IP, Port: local.Port} 199 | if err := sendReply(conn, successReply, &bind); err != nil { 200 | return fmt.Errorf("Failed to send reply: %v", err) 201 | } 202 | 203 | // Start proxying 204 | errCh := make(chan error, 2) 205 | go proxy(target, req.bufConn, errCh) 206 | go proxy(conn, target, errCh) 207 | 208 | // Wait 209 | for i := 0; i < 2; i++ { 210 | e := <-errCh 211 | if e != nil { 212 | // return from this function closes target (and conn). 213 | return e 214 | } 215 | } 216 | return nil 217 | } 218 | 219 | // handleBind is used to handle a connect command 220 | func (s *Server) handleBind(ctx context.Context, conn conn, req *Request) error { 221 | // Check if this is allowed 222 | if ctx_, ok := s.config.Rules.Allow(ctx, req); !ok { 223 | if err := sendReply(conn, ruleFailure, nil); err != nil { 224 | return fmt.Errorf("Failed to send reply: %v", err) 225 | } 226 | return fmt.Errorf("Bind to %v blocked by rules", req.DestAddr) 227 | } else { 228 | ctx = ctx_ 229 | } 230 | 231 | // TODO: Support bind 232 | if err := sendReply(conn, commandNotSupported, nil); err != nil { 233 | return fmt.Errorf("Failed to send reply: %v", err) 234 | } 235 | return nil 236 | } 237 | 238 | // handleAssociate is used to handle a connect command 239 | func (s *Server) handleAssociate(ctx context.Context, conn conn, req *Request) error { 240 | // Check if this is allowed 241 | if ctx_, ok := s.config.Rules.Allow(ctx, req); !ok { 242 | if err := sendReply(conn, ruleFailure, nil); err != nil { 243 | return fmt.Errorf("Failed to send reply: %v", err) 244 | } 245 | return fmt.Errorf("Associate to %v blocked by rules", req.DestAddr) 246 | } else { 247 | ctx = ctx_ 248 | } 249 | 250 | // TODO: Support associate 251 | if err := sendReply(conn, commandNotSupported, nil); err != nil { 252 | return fmt.Errorf("Failed to send reply: %v", err) 253 | } 254 | return nil 255 | } 256 | 257 | // readAddrSpec is used to read AddrSpec. 258 | // Expects an address type byte, follwed by the address and port 259 | func readAddrSpec(r io.Reader) ([]byte, *AddrSpec, error) { 260 | var receivedByte []byte 261 | d := &AddrSpec{} 262 | 263 | // Get the address type 264 | addrType := []byte{0} 265 | if _, err := r.Read(addrType); err != nil { 266 | return nil, nil, err 267 | } 268 | receivedByte = append(receivedByte, addrType...) 269 | 270 | // Handle on a per type basis 271 | switch addrType[0] { 272 | case ipv4Address: 273 | addr := make([]byte, 4) 274 | if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil { 275 | return nil, nil, err 276 | } 277 | 278 | receivedByte = append(receivedByte, addr...) 279 | d.IP = net.IP(addr) 280 | 281 | case ipv6Address: 282 | addr := make([]byte, 16) 283 | if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil { 284 | return nil, nil, err 285 | } 286 | receivedByte = append(receivedByte, addr...) 287 | d.IP = net.IP(addr) 288 | 289 | case fqdnAddress: 290 | if _, err := r.Read(addrType); err != nil { 291 | return nil, nil, err 292 | } 293 | 294 | receivedByte = append(receivedByte, addrType...) 295 | addrLen := int(addrType[0]) 296 | fqdn := make([]byte, addrLen) 297 | if _, err := io.ReadAtLeast(r, fqdn, addrLen); err != nil { 298 | return nil, nil, err 299 | } 300 | receivedByte = append(receivedByte, fqdn...) 301 | d.FQDN = string(fqdn) 302 | 303 | default: 304 | return nil, nil, unrecognizedAddrType 305 | } 306 | 307 | // Read the port 308 | port := []byte{0, 0} 309 | if _, err := io.ReadAtLeast(r, port, 2); err != nil { 310 | return nil, nil, err 311 | } 312 | receivedByte = append(receivedByte, port...) 313 | d.Port = (int(port[0]) << 8) | int(port[1]) 314 | 315 | return receivedByte, d, nil 316 | } 317 | 318 | // sendReply is used to send a reply message 319 | func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error { 320 | // Format the address 321 | var addrType uint8 322 | var addrBody []byte 323 | var addrPort uint16 324 | switch { 325 | case addr == nil: 326 | addrType = ipv4Address 327 | addrBody = []byte{0, 0, 0, 0} 328 | addrPort = 0 329 | 330 | case addr.FQDN != "": 331 | addrType = fqdnAddress 332 | addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...) 333 | addrPort = uint16(addr.Port) 334 | 335 | case addr.IP.To4() != nil: 336 | addrType = ipv4Address 337 | addrBody = []byte(addr.IP.To4()) 338 | addrPort = uint16(addr.Port) 339 | 340 | case addr.IP.To16() != nil: 341 | addrType = ipv6Address 342 | addrBody = []byte(addr.IP.To16()) 343 | addrPort = uint16(addr.Port) 344 | 345 | default: 346 | return fmt.Errorf("Failed to format address: %v", addr) 347 | } 348 | 349 | // Format the message 350 | msg := make([]byte, 6+len(addrBody)) 351 | msg[0] = socks5Version 352 | msg[1] = resp 353 | msg[2] = 0 // Reserved 354 | msg[3] = addrType 355 | copy(msg[4:], addrBody) 356 | msg[4+len(addrBody)] = byte(addrPort >> 8) 357 | msg[4+len(addrBody)+1] = byte(addrPort & 0xff) 358 | 359 | // Send the message 360 | _, err := w.Write(msg) 361 | return err 362 | } 363 | 364 | type closeWriter interface { 365 | CloseWrite() error 366 | } 367 | 368 | // proxy is used to suffle data from src to destination, and sends errors 369 | // down a dedicated channel 370 | func proxy(dst io.Writer, src io.Reader, errCh chan error) { 371 | _, err := io.Copy(dst, src) 372 | if tcpConn, ok := dst.(closeWriter); ok { 373 | tcpConn.CloseWrite() 374 | } 375 | errCh <- err 376 | } 377 | -------------------------------------------------------------------------------- /go-socks5/request_test.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "log" 8 | "net" 9 | "os" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | type MockConn struct { 15 | buf bytes.Buffer 16 | } 17 | 18 | func (m *MockConn) Write(b []byte) (int, error) { 19 | return m.buf.Write(b) 20 | } 21 | 22 | func (m *MockConn) RemoteAddr() net.Addr { 23 | return &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 65432} 24 | } 25 | 26 | func TestRequest_Connect(t *testing.T) { 27 | // Create a local listener 28 | l, err := net.Listen("tcp", "127.0.0.1:0") 29 | if err != nil { 30 | t.Fatalf("err: %v", err) 31 | } 32 | go func() { 33 | conn, err := l.Accept() 34 | if err != nil { 35 | t.Fatalf("err: %v", err) 36 | } 37 | defer conn.Close() 38 | 39 | buf := make([]byte, 4) 40 | if _, err := io.ReadAtLeast(conn, buf, 4); err != nil { 41 | t.Fatalf("err: %v", err) 42 | } 43 | 44 | if !bytes.Equal(buf, []byte("ping")) { 45 | t.Fatalf("bad: %v", buf) 46 | } 47 | conn.Write([]byte("pong")) 48 | }() 49 | lAddr := l.Addr().(*net.TCPAddr) 50 | 51 | // Make server 52 | s := &Server{config: &Config{ 53 | Rules: PermitAll(), 54 | Resolver: DNSResolver{}, 55 | Logger: log.New(os.Stdout, "", log.LstdFlags), 56 | }} 57 | 58 | // Create the connect request 59 | buf := bytes.NewBuffer(nil) 60 | buf.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1}) 61 | 62 | port := []byte{0, 0} 63 | binary.BigEndian.PutUint16(port, uint16(lAddr.Port)) 64 | buf.Write(port) 65 | 66 | // Send a ping 67 | buf.Write([]byte("ping")) 68 | 69 | // Handle the request 70 | resp := &MockConn{} 71 | _, req, err := NewRequest(buf) 72 | if err != nil { 73 | t.Fatalf("err: %v", err) 74 | } 75 | 76 | if err := s.handleRequest(req, resp); err != nil { 77 | t.Fatalf("err: %v", err) 78 | } 79 | 80 | // Verify response 81 | out := resp.buf.Bytes() 82 | expected := []byte{ 83 | 5, 84 | 0, 85 | 0, 86 | 1, 87 | 127, 0, 0, 1, 88 | 0, 0, 89 | 'p', 'o', 'n', 'g', 90 | } 91 | 92 | // Ignore the port for both 93 | out[8] = 0 94 | out[9] = 0 95 | 96 | if !bytes.Equal(out, expected) { 97 | t.Fatalf("bad: %v %v", out, expected) 98 | } 99 | } 100 | 101 | func TestRequest_Connect_RuleFail(t *testing.T) { 102 | // Create a local listener 103 | l, err := net.Listen("tcp", "127.0.0.1:0") 104 | if err != nil { 105 | t.Fatalf("err: %v", err) 106 | } 107 | go func() { 108 | conn, err := l.Accept() 109 | if err != nil { 110 | t.Fatalf("err: %v", err) 111 | } 112 | defer conn.Close() 113 | 114 | buf := make([]byte, 4) 115 | if _, err := io.ReadAtLeast(conn, buf, 4); err != nil { 116 | t.Fatalf("err: %v", err) 117 | } 118 | 119 | if !bytes.Equal(buf, []byte("ping")) { 120 | t.Fatalf("bad: %v", buf) 121 | } 122 | conn.Write([]byte("pong")) 123 | }() 124 | lAddr := l.Addr().(*net.TCPAddr) 125 | 126 | // Make server 127 | s := &Server{config: &Config{ 128 | Rules: PermitNone(), 129 | Resolver: DNSResolver{}, 130 | Logger: log.New(os.Stdout, "", log.LstdFlags), 131 | }} 132 | 133 | // Create the connect request 134 | buf := bytes.NewBuffer(nil) 135 | buf.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1}) 136 | 137 | port := []byte{0, 0} 138 | binary.BigEndian.PutUint16(port, uint16(lAddr.Port)) 139 | buf.Write(port) 140 | 141 | // Send a ping 142 | buf.Write([]byte("ping")) 143 | 144 | // Handle the request 145 | resp := &MockConn{} 146 | _, req, err := NewRequest(buf) 147 | if err != nil { 148 | t.Fatalf("err: %v", err) 149 | } 150 | 151 | if err := s.handleRequest(req, resp); !strings.Contains(err.Error(), "blocked by rules") { 152 | t.Fatalf("err: %v", err) 153 | } 154 | 155 | // Verify response 156 | out := resp.buf.Bytes() 157 | expected := []byte{ 158 | 5, 159 | 2, 160 | 0, 161 | 1, 162 | 0, 0, 0, 0, 163 | 0, 0, 164 | } 165 | 166 | if !bytes.Equal(out, expected) { 167 | t.Fatalf("bad: %v %v", out, expected) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /go-socks5/resolver.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "net" 5 | 6 | "golang.org/x/net/context" 7 | ) 8 | 9 | // NameResolver is used to implement custom name resolution 10 | type NameResolver interface { 11 | Resolve(ctx context.Context, name string) (context.Context, net.IP, error) 12 | } 13 | 14 | // DNSResolver uses the system DNS to resolve host names 15 | type DNSResolver struct{} 16 | 17 | func (d DNSResolver) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) { 18 | addr, err := net.ResolveIPAddr("ip", name) 19 | if err != nil { 20 | return ctx, nil, err 21 | } 22 | return ctx, addr.IP, err 23 | } 24 | -------------------------------------------------------------------------------- /go-socks5/resolver_test.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "testing" 5 | 6 | "golang.org/x/net/context" 7 | ) 8 | 9 | func TestDNSResolver(t *testing.T) { 10 | d := DNSResolver{} 11 | ctx := context.Background() 12 | 13 | _, addr, err := d.Resolve(ctx, "localhost") 14 | if err != nil { 15 | t.Fatalf("err: %v", err) 16 | } 17 | 18 | if !addr.IsLoopback() { 19 | t.Fatalf("expected loopback") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go-socks5/ruleset.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "golang.org/x/net/context" 5 | ) 6 | 7 | // RuleSet is used to provide custom rules to allow or prohibit actions 8 | type RuleSet interface { 9 | Allow(ctx context.Context, req *Request) (context.Context, bool) 10 | } 11 | 12 | // PermitAll returns a RuleSet which allows all types of connections 13 | func PermitAll() RuleSet { 14 | return &PermitCommand{true, true, true} 15 | } 16 | 17 | // PermitNone returns a RuleSet which disallows all types of connections 18 | func PermitNone() RuleSet { 19 | return &PermitCommand{false, false, false} 20 | } 21 | 22 | // PermitCommand is an implementation of the RuleSet which 23 | // enables filtering supported commands 24 | type PermitCommand struct { 25 | EnableConnect bool 26 | EnableBind bool 27 | EnableAssociate bool 28 | } 29 | 30 | func (p *PermitCommand) Allow(ctx context.Context, req *Request) (context.Context, bool) { 31 | switch req.Command { 32 | case ConnectCommand: 33 | return ctx, p.EnableConnect 34 | case BindCommand: 35 | return ctx, p.EnableBind 36 | case AssociateCommand: 37 | return ctx, p.EnableAssociate 38 | } 39 | 40 | return ctx, false 41 | } 42 | -------------------------------------------------------------------------------- /go-socks5/ruleset_test.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "testing" 5 | 6 | "golang.org/x/net/context" 7 | ) 8 | 9 | func TestPermitCommand(t *testing.T) { 10 | ctx := context.Background() 11 | r := &PermitCommand{true, false, false} 12 | 13 | if _, ok := r.Allow(ctx, &Request{Command: ConnectCommand}); !ok { 14 | t.Fatalf("expect connect") 15 | } 16 | 17 | if _, ok := r.Allow(ctx, &Request{Command: BindCommand}); ok { 18 | t.Fatalf("do not expect bind") 19 | } 20 | 21 | if _, ok := r.Allow(ctx, &Request{Command: AssociateCommand}); ok { 22 | t.Fatalf("do not expect associate") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /go-socks5/socks5.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "net" 8 | "os" 9 | 10 | "golang.org/x/net/context" 11 | ) 12 | 13 | const ( 14 | socks5Version = uint8(5) 15 | ) 16 | 17 | // Config is used to setup and configure a Server 18 | type Config struct { 19 | // AuthMethods can be provided to implement custom authentication 20 | // By default, "auth-less" mode is enabled. 21 | // For password-based auth use UserPassAuthenticator. 22 | AuthMethods []Authenticator 23 | 24 | // If provided, username/password authentication is enabled, 25 | // by appending a UserPassAuthenticator to AuthMethods. If not provided, 26 | // and AUthMethods is nil, then "auth-less" mode is enabled. 27 | Credentials CredentialStore 28 | 29 | // Resolver can be provided to do custom name resolution. 30 | // Defaults to DNSResolver if not provided. 31 | Resolver NameResolver 32 | 33 | // Rules is provided to enable custom logic around permitting 34 | // various commands. If not provided, PermitAll is used. 35 | Rules RuleSet 36 | 37 | // Rewriter can be used to transparently rewrite addresses. 38 | // This is invoked before the RuleSet is invoked. 39 | // Defaults to NoRewrite. 40 | Rewriter AddressRewriter 41 | 42 | // BindIP is used for bind or udp associate 43 | BindIP net.IP 44 | 45 | // Logger can be used to provide a custom log target. 46 | // Defaults to stdout. 47 | Logger *log.Logger 48 | 49 | // Optional function for dialing out 50 | Dial func(ctx context.Context, network, addr string) (net.Conn, error) 51 | } 52 | 53 | // Server is reponsible for accepting connections and handling 54 | // the details of the SOCKS5 protocol 55 | type Server struct { 56 | config *Config 57 | authMethods map[uint8]Authenticator 58 | } 59 | 60 | // New creates a new Server and potentially returns an error 61 | func New(conf *Config) (*Server, error) { 62 | // Ensure we have at least one authentication method enabled 63 | if len(conf.AuthMethods) == 0 { 64 | if conf.Credentials != nil { 65 | conf.AuthMethods = []Authenticator{&UserPassAuthenticator{conf.Credentials}} 66 | } else { 67 | conf.AuthMethods = []Authenticator{&NoAuthAuthenticator{}} 68 | } 69 | } 70 | 71 | // Ensure we have a DNS resolver 72 | if conf.Resolver == nil { 73 | conf.Resolver = DNSResolver{} 74 | } 75 | 76 | // Ensure we have a rule set 77 | if conf.Rules == nil { 78 | conf.Rules = PermitAll() 79 | } 80 | 81 | // Ensure we have a log target 82 | if conf.Logger == nil { 83 | conf.Logger = log.New(os.Stdout, "", log.LstdFlags) 84 | } 85 | 86 | server := &Server{ 87 | config: conf, 88 | } 89 | 90 | server.authMethods = make(map[uint8]Authenticator) 91 | 92 | for _, a := range conf.AuthMethods { 93 | server.authMethods[a.GetCode()] = a 94 | } 95 | 96 | return server, nil 97 | } 98 | 99 | // ListenAndServe is used to create a listener and serve on it 100 | func (s *Server) ListenAndServe(network, addr string) error { 101 | l, err := net.Listen(network, addr) 102 | if err != nil { 103 | return err 104 | } 105 | return s.Serve(l) 106 | } 107 | 108 | // Serve is used to serve connections from a listener 109 | func (s *Server) Serve(l net.Listener) error { 110 | for { 111 | conn, err := l.Accept() 112 | if err != nil { 113 | return err 114 | } 115 | go s.ServeConn(conn) 116 | } 117 | return nil 118 | } 119 | 120 | // ServeConn is used to serve a single connection. 121 | func (s *Server) ServeConn(conn net.Conn) error { 122 | defer conn.Close() 123 | bufConn := bufio.NewReader(conn) 124 | 125 | // Read the version byte 126 | version := []byte{0} 127 | if _, err := bufConn.Read(version); err != nil { 128 | s.config.Logger.Printf("[ERR] socks: Failed to get version byte: %v", err) 129 | return err 130 | } 131 | 132 | // Ensure we are compatible 133 | if version[0] != socks5Version { 134 | err := fmt.Errorf("Unsupported SOCKS version: %v", version) 135 | s.config.Logger.Printf("[ERR] socks: %v", err) 136 | return err 137 | } 138 | 139 | // Authenticate the connection 140 | _, authContext, err := s.authenticate(conn, bufConn) 141 | if err != nil { 142 | err = fmt.Errorf("Failed to authenticate: %v", err) 143 | s.config.Logger.Printf("[ERR] socks: %v", err) 144 | return err 145 | } 146 | 147 | _, request, err := NewRequest(bufConn) 148 | if err != nil { 149 | if err == unrecognizedAddrType { 150 | if err := sendReply(conn, addrTypeNotSupported, nil); err != nil { 151 | return fmt.Errorf("Failed to send reply: %v", err) 152 | } 153 | } 154 | return fmt.Errorf("Failed to read destination address: %v", err) 155 | } 156 | request.AuthContext = authContext 157 | if client, ok := conn.RemoteAddr().(*net.TCPAddr); ok { 158 | request.RemoteAddr = &AddrSpec{IP: client.IP, Port: client.Port} 159 | } 160 | 161 | // Process the client request 162 | if err := s.handleRequest(request, conn); err != nil { 163 | err = fmt.Errorf("Failed to handle request: %v", err) 164 | s.config.Logger.Printf("[ERR] socks: %v", err) 165 | return err 166 | } 167 | 168 | return nil 169 | } 170 | 171 | func (s *Server) GetDest(conn net.Conn) ([]byte, []byte, string, *Request, error) { 172 | var firstBytes []byte 173 | var secondBytes []byte 174 | //defer conn.Close() 175 | bufConn := bufio.NewReader(conn) 176 | 177 | // Read the version byte 178 | version := []byte{0} 179 | if _, err := bufConn.Read(version); err != nil { 180 | s.config.Logger.Printf("[ERR] socks: Failed to get version byte: %v", err) 181 | return nil, nil, "", nil, err 182 | } 183 | firstBytes = append(firstBytes, version...) 184 | 185 | // Ensure we are compatible 186 | if version[0] != socks5Version { 187 | err := fmt.Errorf("Unsupported SOCKS version: %v", version) 188 | s.config.Logger.Printf("[ERR] socks: %v", err) 189 | return nil, nil, "", nil, err 190 | } 191 | 192 | // Authenticate the connection 193 | methodsByte, _, err := s.authenticate(conn, bufConn) 194 | if err != nil { 195 | err = fmt.Errorf("Failed to authenticate: %v", err) 196 | s.config.Logger.Printf("[ERR] socks: %v", err) 197 | return nil, nil, "", nil, err 198 | } 199 | firstBytes = append(firstBytes, byte(len(methodsByte))) 200 | firstBytes = append(firstBytes, methodsByte...) 201 | 202 | receivedByteRequest, request, err := NewRequest(bufConn) 203 | secondBytes = append(secondBytes, receivedByteRequest...) 204 | if err != nil { 205 | if err == unrecognizedAddrType { 206 | if err := sendReply(conn, addrTypeNotSupported, nil); err != nil { 207 | return nil, nil, "", nil, fmt.Errorf("Failed to send reply: %v", err) 208 | } 209 | } 210 | return nil, nil, "", nil, fmt.Errorf("Failed to read destination address: %v", err) 211 | } 212 | 213 | ctx := context.Background() 214 | 215 | // Resolve the address if we have a FQDN 216 | dest := request.DestAddr 217 | if dest.FQDN != "" { 218 | ctx_, addr, err := s.config.Resolver.Resolve(ctx, dest.FQDN) 219 | if err != nil { 220 | if err := sendReply(conn, hostUnreachable, nil); err != nil { 221 | return nil, nil, "", nil, fmt.Errorf("Failed to send reply: %v", err) 222 | } 223 | return nil, nil, "", nil, fmt.Errorf("Failed to resolve destination '%v': %v", dest.FQDN, err) 224 | } 225 | ctx = ctx_ 226 | dest.IP = addr 227 | } 228 | 229 | // Apply any address rewrites 230 | request.realDestAddr = request.DestAddr 231 | 232 | /* request.AuthContext = authContext 233 | if client, ok := conn.RemoteAddr().(*net.TCPAddr); ok { 234 | request.RemoteAddr = &AddrSpec{IP: client.IP, Port: client.Port} 235 | } 236 | 237 | // Process the client request 238 | if err := s.handleRequest(request, conn); err != nil { 239 | err = fmt.Errorf("Failed to handle request: %v", err) 240 | s.config.Logger.Printf("[ERR] socks: %v", err) 241 | return err 242 | } */ 243 | 244 | return firstBytes, secondBytes, request.realDestAddr.IP.String(), request, nil 245 | } 246 | 247 | func (s *Server) Handle(request *Request, conn net.Conn) { 248 | if err := s.handleRequest(request, conn); err != nil { 249 | err = fmt.Errorf("Failed to handle request: %v", err) 250 | s.config.Logger.Printf("[ERR] socks: %v", err) 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /go-socks5/socks5_test.go: -------------------------------------------------------------------------------- 1 | package socks5 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "log" 8 | "net" 9 | "os" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | func TestSOCKS5_Connect(t *testing.T) { 15 | // Create a local listener 16 | l, err := net.Listen("tcp", "127.0.0.1:0") 17 | if err != nil { 18 | t.Fatalf("err: %v", err) 19 | } 20 | go func() { 21 | conn, err := l.Accept() 22 | if err != nil { 23 | t.Fatalf("err: %v", err) 24 | } 25 | defer conn.Close() 26 | 27 | buf := make([]byte, 4) 28 | if _, err := io.ReadAtLeast(conn, buf, 4); err != nil { 29 | t.Fatalf("err: %v", err) 30 | } 31 | 32 | if !bytes.Equal(buf, []byte("ping")) { 33 | t.Fatalf("bad: %v", buf) 34 | } 35 | conn.Write([]byte("pong")) 36 | }() 37 | lAddr := l.Addr().(*net.TCPAddr) 38 | 39 | // Create a socks server 40 | creds := StaticCredentials{ 41 | "foo": "bar", 42 | } 43 | cator := UserPassAuthenticator{Credentials: creds} 44 | conf := &Config{ 45 | AuthMethods: []Authenticator{cator}, 46 | Logger: log.New(os.Stdout, "", log.LstdFlags), 47 | } 48 | serv, err := New(conf) 49 | if err != nil { 50 | t.Fatalf("err: %v", err) 51 | } 52 | 53 | // Start listening 54 | go func() { 55 | if err := serv.ListenAndServe("tcp", "127.0.0.1:12365"); err != nil { 56 | t.Fatalf("err: %v", err) 57 | } 58 | }() 59 | time.Sleep(10 * time.Millisecond) 60 | 61 | // Get a local conn 62 | conn, err := net.Dial("tcp", "127.0.0.1:12365") 63 | if err != nil { 64 | t.Fatalf("err: %v", err) 65 | } 66 | 67 | // Connect, auth and connec to local 68 | req := bytes.NewBuffer(nil) 69 | req.Write([]byte{5}) 70 | req.Write([]byte{2, NoAuth, UserPassAuth}) 71 | req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'}) 72 | req.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1}) 73 | 74 | port := []byte{0, 0} 75 | binary.BigEndian.PutUint16(port, uint16(lAddr.Port)) 76 | req.Write(port) 77 | 78 | // Send a ping 79 | req.Write([]byte("ping")) 80 | 81 | // Send all the bytes 82 | conn.Write(req.Bytes()) 83 | 84 | // Verify response 85 | expected := []byte{ 86 | socks5Version, UserPassAuth, 87 | 1, authSuccess, 88 | 5, 89 | 0, 90 | 0, 91 | 1, 92 | 127, 0, 0, 1, 93 | 0, 0, 94 | 'p', 'o', 'n', 'g', 95 | } 96 | out := make([]byte, len(expected)) 97 | 98 | conn.SetDeadline(time.Now().Add(time.Second)) 99 | if _, err := io.ReadAtLeast(conn, out, len(out)); err != nil { 100 | t.Fatalf("err: %v", err) 101 | } 102 | 103 | // Ignore the port 104 | out[12] = 0 105 | out[13] = 0 106 | 107 | if !bytes.Equal(out, expected) { 108 | t.Fatalf("bad: %v", out) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/nodauf/Go-RouterSocks 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/c-bata/go-prompt v0.2.5 7 | github.com/oschwald/geoip2-golang v1.9.0 8 | github.com/oschwald/maxminddb-golang v1.11.0 9 | github.com/spf13/cobra v1.2.1 10 | golang.org/x/net v0.0.0-20210902165921-8d991716f632 11 | ) 12 | 13 | require ( 14 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 15 | github.com/mattn/go-colorable v0.1.7 // indirect 16 | github.com/mattn/go-isatty v0.0.12 // indirect 17 | github.com/mattn/go-runewidth v0.0.9 // indirect 18 | github.com/mattn/go-tty v0.0.3 // indirect 19 | github.com/pkg/term v1.1.0 // indirect 20 | github.com/spf13/pflag v1.0.5 // indirect 21 | golang.org/x/sys v0.9.0 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 17 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 18 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 19 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 20 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 21 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 22 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 23 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 24 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 25 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 26 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 27 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 28 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 29 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 30 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 31 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 32 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 33 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 34 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 35 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 36 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 37 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 38 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 39 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 40 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 41 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 42 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 43 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 44 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 45 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 46 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 47 | github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 48 | github.com/c-bata/go-prompt v0.2.5 h1:3zg6PecEywxNn0xiqcXHD96fkbxghD+gdB2tbsYfl+Y= 49 | github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= 50 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 51 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 52 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 53 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 54 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 55 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 56 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 57 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 58 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 59 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 60 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 61 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 62 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 63 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 64 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 65 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 66 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 67 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 68 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 69 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 70 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 71 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 72 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 73 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 74 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 75 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 76 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 77 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 78 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 79 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 80 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 81 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 82 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 83 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 84 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 85 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 86 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 87 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 88 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 89 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 90 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 91 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 92 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 93 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 94 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 95 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 96 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 97 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 98 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 99 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 100 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 101 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 102 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 103 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 104 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 105 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 106 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 107 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 108 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 109 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 110 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 111 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 112 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 113 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 114 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 115 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 116 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 117 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 118 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 119 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 120 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 121 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 122 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 123 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 124 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 125 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 126 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 127 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 128 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 129 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 130 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 131 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 132 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 133 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 134 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 135 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 136 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 137 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 138 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 139 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 140 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 141 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 142 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 143 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 144 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 145 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 146 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 147 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 148 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 149 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 150 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 151 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 152 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 153 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 154 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 155 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 156 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 157 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 158 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 159 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 160 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 161 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 162 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 163 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 164 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 165 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 166 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 167 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 168 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 169 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 170 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 171 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 172 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 173 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 174 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 175 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 176 | github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 177 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 178 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 179 | github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= 180 | github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 181 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 182 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 183 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 184 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 185 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 186 | github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 187 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 188 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 189 | github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= 190 | github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= 191 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 192 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 193 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 194 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 195 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 196 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 197 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 198 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 199 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 200 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 201 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 202 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 203 | github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc= 204 | github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y= 205 | github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0= 206 | github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg= 207 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 208 | github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 209 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 210 | github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 211 | github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= 212 | github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= 213 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 214 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 215 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 216 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 217 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 218 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 219 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 220 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 221 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 222 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 223 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 224 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 225 | github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 226 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 227 | github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= 228 | github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 229 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 230 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 231 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 232 | github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 233 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 234 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 235 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 236 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 237 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 238 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 239 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 240 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 241 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 242 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 243 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 244 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 245 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 246 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 247 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 248 | go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 249 | go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 250 | go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= 251 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 252 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 253 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 254 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 255 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 256 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 257 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 258 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 259 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 260 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 261 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 262 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 263 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 264 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 265 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 266 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 267 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 268 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 269 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 270 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 271 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 272 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 273 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 274 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 275 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 276 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 277 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 278 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 279 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 280 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 281 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 282 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 283 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 284 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 285 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 286 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 287 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 288 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 289 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 290 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 291 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 292 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 293 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 294 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 295 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 296 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 297 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 298 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 299 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 300 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 301 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 302 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 303 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 304 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 305 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 306 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 307 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 308 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 309 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 310 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 311 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 312 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 313 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 314 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 315 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 316 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 317 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 318 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 319 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 320 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 321 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 322 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 323 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 324 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 325 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 326 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 327 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 328 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 329 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 330 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 331 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 332 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 333 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 334 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 335 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 336 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 337 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 338 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 339 | golang.org/x/net v0.0.0-20210902165921-8d991716f632 h1:900XJE4Rn/iPU+xD5ZznOe4GKKc4AdFK0IO1P6Z3/lQ= 340 | golang.org/x/net v0.0.0-20210902165921-8d991716f632/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 341 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 342 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 343 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 344 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 345 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 346 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 347 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 348 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 349 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 350 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 351 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 352 | golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 353 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 354 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 355 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 356 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 357 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 358 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 359 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 360 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 361 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 362 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 363 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 364 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 365 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 366 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 367 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 368 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 369 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 382 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 383 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 384 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 385 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 386 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 387 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 388 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 389 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 390 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 391 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 392 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 393 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 394 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 395 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 396 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 397 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 398 | golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 399 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 400 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 401 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 402 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 403 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 404 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 405 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 406 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 407 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 408 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 409 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 410 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 411 | golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= 412 | golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 413 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 414 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 415 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 416 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 417 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 418 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 419 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 420 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 421 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 422 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 423 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 424 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 425 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 426 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 427 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 428 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 429 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 430 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 431 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 432 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 433 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 434 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 435 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 436 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 437 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 438 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 439 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 440 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 441 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 442 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 443 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 444 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 445 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 446 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 447 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 448 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 449 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 450 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 451 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 452 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 453 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 454 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 455 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 456 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 457 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 458 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 459 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 460 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 461 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 462 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 463 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 464 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 465 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 466 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 467 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 468 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 469 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 470 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 471 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 472 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 473 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 474 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 475 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 476 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 477 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 478 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 479 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 480 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 481 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 482 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 483 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 484 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 485 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 486 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 487 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 488 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 489 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 490 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 491 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 492 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 493 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 494 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 495 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 496 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 497 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 498 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 499 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 500 | google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= 501 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 502 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 503 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 504 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 505 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 506 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 507 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 508 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 509 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 510 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 511 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 512 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 513 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 514 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 515 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 516 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 517 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 518 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 519 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 520 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 521 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 522 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 523 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 524 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 525 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 526 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 527 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 528 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 529 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 530 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 531 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 532 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 533 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 534 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 535 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 536 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 537 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 538 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 539 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 540 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 541 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 542 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 543 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 544 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 545 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 546 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 547 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 548 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 549 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 550 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 551 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 552 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 553 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 554 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 555 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 556 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 557 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 558 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 559 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 560 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 561 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 562 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 563 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 564 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 565 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 566 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 567 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 568 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 569 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 570 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 571 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 572 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 573 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 574 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 575 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 576 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 577 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 578 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 579 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 580 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 581 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 582 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 583 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 584 | gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 585 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 586 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 587 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 588 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 589 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 590 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 591 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 592 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 593 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 594 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 595 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 596 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 597 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 598 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 599 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 600 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 601 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 602 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 603 | -------------------------------------------------------------------------------- /images/schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodauf/Go-RouterSocks/c530b09a7b10401effa0150d8eddd5480b1bd52d/images/schema.png -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/nodauf/Go-RouterSocks/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /prompt/completer.go: -------------------------------------------------------------------------------- 1 | package prompt 2 | 3 | import ( 4 | "strings" 5 | 6 | prompt "github.com/c-bata/go-prompt" 7 | ) 8 | 9 | var commands = []prompt.Suggest{ 10 | {Text: "route", Description: "Manage routes. (default print them)"}, 11 | {Text: "geoip", Description: "List the supported GeoIP country"}, 12 | {Text: "chisel", Description: "List chisel port on this machine"}, 13 | {Text: "help", Description: "Help menu"}, 14 | 15 | {Text: "exit", Description: "Exit this program"}, 16 | } 17 | 18 | var actionsRoute = []prompt.Suggest{ 19 | {Text: "add", Description: "Add new route"}, 20 | {Text: "flush", Description: "Remove all the routes"}, 21 | {Text: "print", Description: "Print the routes"}, 22 | {Text: "delete", Description: "Delete one route"}, 23 | {Text: "dump", Description: "Dump the route as a command line to import them later"}, 24 | {Text: "import", Description: "Parse multiple route add command"}, 25 | } 26 | 27 | var geoIPSubCommand = []prompt.Suggest{ 28 | {Text: "load", Description: "Load a GeoIP database"}, 29 | {Text: "print", Description: "Print all ISO codes with the associated country"}, 30 | } 31 | 32 | var helpSubCommand = []prompt.Suggest{ 33 | {Text: "route", Description: "Add new route"}, 34 | {Text: "chisel", Description: "List chisel port on this machine"}, 35 | {Text: "geoip", Description: "List the supported GeoIP country"}, 36 | } 37 | 38 | func complete(d prompt.Document) []prompt.Suggest { 39 | if d.TextBeforeCursor() == "" { 40 | return []prompt.Suggest{} 41 | } 42 | args := strings.Split(d.TextBeforeCursor(), " ") 43 | 44 | if len(args) <= 1 { 45 | return prompt.FilterHasPrefix(commands, args[0], true) 46 | } 47 | first := args[0] 48 | switch first { 49 | case "route": 50 | second := args[1] 51 | if len(args) == 2 { 52 | return prompt.FilterHasPrefix(actionsRoute, second, true) 53 | } 54 | 55 | case "geoip": 56 | second := args[1] 57 | if len(args) == 2 { 58 | return prompt.FilterHasPrefix(geoIPSubCommand, second, true) 59 | } 60 | 61 | case "help": 62 | second := args[1] 63 | if len(args) == 2 { 64 | return prompt.FilterHasPrefix(helpSubCommand, second, true) 65 | } 66 | } 67 | return []prompt.Suggest{} 68 | } 69 | -------------------------------------------------------------------------------- /prompt/help.go: -------------------------------------------------------------------------------- 1 | package prompt 2 | 3 | import "fmt" 4 | 5 | func helpRoute() { 6 | fmt.Println("Route usage: ") 7 | helpRouteAdd() 8 | fmt.Print("\n") 9 | helpRouteDelete() 10 | } 11 | 12 | func helpRouteAdd() { 13 | fmt.Println(`route add cidr/geoIPCountry socksServer:socksPort 14 | route add cidr/geoIPCountry chiselID 15 | route add 192.168.1.0/24 127.0.0.1:1081 16 | route add 192.168.1.0/24 0`) 17 | } 18 | 19 | func helpRouteDelete() { 20 | fmt.Println(`route delete cidr/geoIPCountry 21 | route delete 192.168.1.0/24`) 22 | } 23 | 24 | func helpChisel() { 25 | fmt.Println(`chisel 26 | Output: 27 | [0] 127.0.0.1:1081`) 28 | } 29 | 30 | func helpGeoIP() { 31 | fmt.Println("geoip usage: ") 32 | helpGeoIPLoad() 33 | fmt.Print("\n") 34 | helpGeoPrint() 35 | } 36 | 37 | func helpGeoIPLoad() { 38 | fmt.Println(`geoip load Path_to_the_database 39 | geoip load /tmp/GeoLite2-Country.mmdb`) 40 | } 41 | func helpGeoPrint() { 42 | fmt.Println(`geoip print 43 | Output: 44 | iso code => Country name`) 45 | } 46 | 47 | func help() { 48 | fmt.Println("route: Manage route to socks servers") 49 | fmt.Println("chisel: Liste chisel socks server on localhost") 50 | fmt.Println("geoip: List the supported GeoIP country") 51 | fmt.Println("help: help command") 52 | } 53 | -------------------------------------------------------------------------------- /prompt/prompt.go: -------------------------------------------------------------------------------- 1 | package prompt 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | router "github.com/nodauf/Go-RouterSocks/router" 10 | utils "github.com/nodauf/Go-RouterSocks/utils" 11 | 12 | prompt "github.com/c-bata/go-prompt" 13 | ) 14 | 15 | func executor(in string) { 16 | in = strings.TrimSpace(in) 17 | command := strings.Split(in, " ") 18 | first := command[0] 19 | switch strings.ToLower(first) { 20 | case "help": 21 | if len(command) > 1 { 22 | second := command[1] 23 | switch strings.ToLower(second) { 24 | case "route": 25 | helpRoute() 26 | case "chisel": 27 | helpChisel() 28 | case "geoip": 29 | helpGeoIP() 30 | } 31 | } else { 32 | help() 33 | } 34 | case "route": 35 | if len(command) > 1 { 36 | second := command[1] 37 | switch strings.ToLower(second) { 38 | case "list": 39 | router.PrintRoutes() 40 | case "add": 41 | if len(command) != 4 { 42 | helpRouteAdd() 43 | } else if serverSocks := utils.IsChiselIDValid(command[3]); serverSocks != "" { 44 | remoteNetwork := command[2] 45 | router.AddRoutes(remoteNetwork, serverSocks) 46 | } else if !utils.IsCIDRValid(command[2]) && !router.IsValidIsoCode(command[2]) { 47 | fmt.Println("[-] CIDR or ISO code is not valid") 48 | helpRouteAdd() 49 | } else if !utils.IsRemoteSocksValid(command[3]) { 50 | fmt.Println("[-] Socks server, socks port or chisel ID is not valid") 51 | helpRouteAdd() 52 | } else if !utils.CanResolvedHostname(strings.Split(command[3], ":")[0]) { 53 | fmt.Println("[-] Server socks can be resolved") 54 | helpRouteAdd() 55 | } else if !utils.ServerReachable(command[3]) { 56 | fmt.Println("[-] Server is not reachable") 57 | helpRouteAdd() 58 | } else { 59 | remoteNetwork := command[2] 60 | remoteSocks := command[3] 61 | router.AddRoutes(remoteNetwork, remoteSocks) 62 | } 63 | 64 | case "delete": 65 | if len(command) != 3 { 66 | helpRouteDelete() 67 | } else if !utils.IsCIDRValid(command[2]) { 68 | fmt.Println("[-] CIDR is not valid") 69 | helpRouteDelete() 70 | } else { 71 | router.DeleteRoutes(command[2]) 72 | } 73 | 74 | case "flush": 75 | router.FlushRoutes() 76 | 77 | case "dump": 78 | router.DumpRoutes() 79 | 80 | case "import": 81 | fmt.Println("Paste the output of a route dump and end with an empty line") 82 | var lines string 83 | scanner := bufio.NewScanner(os.Stdin) 84 | for scanner.Scan() { 85 | line := scanner.Text() 86 | lines += line + "\n" 87 | if line == "" { 88 | break 89 | } 90 | } 91 | for _, line := range strings.Split(lines, "\n") { 92 | if line != "" { 93 | executor(line) 94 | } 95 | } 96 | default: 97 | 98 | fmt.Println("Invalid route command") 99 | } 100 | } else { 101 | router.PrintRoutes() 102 | } 103 | case "chisel": 104 | utils.PrintChiselProcess() 105 | case "geoip": 106 | if len(command) > 1 { 107 | second := command[1] 108 | switch strings.ToLower(second) { 109 | case "load": 110 | 111 | if len(command) != 3 { 112 | helpGeoIPLoad() 113 | } else { 114 | err := router.LoadGeoIPDatabase(command[2]) 115 | if err != nil { 116 | fmt.Printf("[-] Fail to load the database %s: %s\n", command[2], err.Error()) 117 | } else { 118 | fmt.Printf("[*] GeoIP database %s loaded\n", command[2]) 119 | } 120 | } 121 | case "print": 122 | router.PrintCountry() 123 | } 124 | } else { 125 | helpGeoIP() 126 | } 127 | case "exit": 128 | os.Exit(0) 129 | case "": 130 | default: 131 | fmt.Println("Invalid command") 132 | } 133 | } 134 | 135 | func Prompt() { 136 | p := prompt.New( 137 | executor, 138 | complete, 139 | prompt.OptionPrefix("RouterSocks> "), 140 | prompt.OptionPrefixTextColor(prompt.Red), 141 | prompt.OptionTitle("Router Socks"), 142 | ) 143 | p.Run() 144 | //fmt.Println("You selected " + t) 145 | } 146 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /router/geoip.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "fmt" 5 | "github.com/oschwald/geoip2-golang" 6 | "github.com/oschwald/maxminddb-golang" 7 | "log" 8 | "slices" 9 | "strings" 10 | ) 11 | 12 | var geoIPDB *geoip2.Reader 13 | var geoIPDB_low *maxminddb.Reader 14 | var geoipEnable bool 15 | var isoCodes []string 16 | var isoWithCountries = make(map[string]string) 17 | 18 | //var isoWithNetworks = make(map[string][]string) 19 | 20 | func LoadGeoIPDatabase(geoipDB string) error { 21 | var err error 22 | geoIPDB, err = geoip2.Open(geoipDB) 23 | if err != nil { 24 | return err 25 | } 26 | geoIPDB_low, err = maxminddb.Open(geoipDB) 27 | geoipEnable = true 28 | // Load the countries and the iso code 29 | var record struct { 30 | Country struct { 31 | ISOCode string `maxminddb:"iso_code"` 32 | CountryNames map[string]string `maxminddb:"names"` 33 | } `maxminddb:"country"` 34 | } // Or any appropriate struct 35 | networks := geoIPDB_low.Networks(maxminddb.SkipAliasedNetworks) 36 | for networks.Next() { 37 | _, err := networks.Network(&record) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | ISOCode := strings.ToLower(record.Country.ISOCode) 42 | if !slices.Contains(isoCodes, ISOCode) { 43 | isoCodes = append(isoCodes, ISOCode) 44 | } 45 | isoWithCountries[ISOCode] = record.Country.CountryNames["en"] 46 | //isoWithNetworks[ISOCode] = append(isoWithNetworks[ISOCode], subnet.String()) 47 | } 48 | if networks.Err() != nil { 49 | log.Panic(networks.Err()) 50 | } 51 | slices.Sort(isoCodes) 52 | return err 53 | } 54 | 55 | func PrintCountry() { 56 | if !geoipEnable { 57 | fmt.Println("GeoIP database was not loaded") 58 | return 59 | } 60 | 61 | for _, isoCode := range isoCodes { 62 | fmt.Printf("%s => %s\n", isoCode, isoWithCountries[isoCode]) 63 | } 64 | } 65 | 66 | func IsValidIsoCode(ISOCode string) bool { 67 | return slices.Contains(isoCodes, strings.ToLower(ISOCode)) 68 | } 69 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "fmt" 5 | utils "github.com/nodauf/Go-RouterSocks/utils" 6 | "net" 7 | "strings" 8 | ) 9 | 10 | var Routes = map[string]string{} 11 | 12 | func AddRoutes(destination string, remoteSocks string) { 13 | destination = strings.ToLower(destination) 14 | if _, ok := Routes[destination]; ok { 15 | fmt.Println("[-] Route already present") 16 | } else { 17 | Routes[destination] = remoteSocks 18 | fmt.Println("[*] Successfull route added for network " + destination) 19 | } 20 | } 21 | 22 | func DeleteRoutes(destination string) { 23 | destination = strings.ToLower(destination) 24 | if _, ok := Routes[destination]; ok { 25 | delete(Routes, destination) 26 | fmt.Println("[*] Successfull route " + destination + " deleted") 27 | } else { 28 | fmt.Println("[-] Route not found") 29 | } 30 | } 31 | 32 | func PrintRoutes() { 33 | for destination, remoteSocks := range Routes { 34 | fmt.Println(destination + " => " + remoteSocks) 35 | } 36 | } 37 | 38 | func FlushRoutes() { 39 | for destination, _ := range Routes { 40 | delete(Routes, destination) 41 | } 42 | 43 | fmt.Println("[*] Successfull route flushed") 44 | } 45 | 46 | func DumpRoutes() { 47 | for destination, remoteSocks := range Routes { 48 | fmt.Println("route add " + destination + " " + remoteSocks) 49 | } 50 | 51 | fmt.Println("[*] Successfull route dumped") 52 | } 53 | 54 | func GetRoute(ip string) (string, string) { 55 | if geoipEnable { 56 | ipNet := net.ParseIP(ip) 57 | record, err := geoIPDB.Country(ipNet) 58 | if err != nil { 59 | fmt.Printf("[-] Fail to retrieve the country of %s\n", ip) 60 | } 61 | isoCode := strings.ToLower(record.Country.IsoCode) 62 | if _, exist := Routes[isoCode]; exist { 63 | return isoCode, Routes[isoCode] 64 | } 65 | } 66 | for destination, remoteSocks := range Routes { 67 | // if destination is iso code 68 | if IsValidIsoCode(destination) { 69 | continue 70 | //for _, network := range isoWithNetworks[destination] { 71 | // if utils.CIDRContainsIP(network, ip) { 72 | // return destination, remoteSocks 73 | // } 74 | //} 75 | 76 | } else if utils.CIDRContainsIP(destination, ip) { 77 | return destination, remoteSocks 78 | } 79 | } 80 | return "", "" 81 | } 82 | -------------------------------------------------------------------------------- /socks/socks.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "os" 9 | "strconv" 10 | "strings" 11 | 12 | socks5 "github.com/nodauf/Go-RouterSocks/go-socks5" 13 | router "github.com/nodauf/Go-RouterSocks/router" 14 | ) 15 | 16 | var serverSocks5 *socks5.Server 17 | 18 | func StartSocks(ip string, port int) { 19 | address := ip + ":" + strconv.Itoa(port) 20 | errorMsg := make(chan error) 21 | go listenAndAccept(address, errorMsg) 22 | status := <-errorMsg 23 | if status != nil { 24 | log.Fatalln(status) 25 | } 26 | log.Println("[*] Server socks server on " + address) 27 | 28 | } 29 | 30 | func listenAndAccept(address string, status chan error) { 31 | var err error 32 | serverSocks5, err = socks5.New(&socks5.Config{}) 33 | if err != nil { 34 | status <- err 35 | } 36 | ln, err := net.Listen("tcp", address) 37 | if err != nil { 38 | status <- err 39 | } 40 | status <- nil 41 | for { 42 | conn, err := ln.Accept() 43 | //log.Println("Got a client") 44 | if err != nil { 45 | fmt.Fprintf(os.Stderr, "Errors accepting!") 46 | } 47 | //log.Println("Passing off to socks5") 48 | go func() { 49 | //firstBytes, secondBytes, dest, err := serverSocks5.GetDest(conn) 50 | firstBytes, secondBytes, dest, request, err := serverSocks5.GetDest(conn) 51 | if err != nil { 52 | // The errors are print to stdout by go-socks5 53 | //log.Println(err) 54 | conn.Close() 55 | return 56 | } 57 | _, remoteSocks := router.GetRoute(dest) 58 | if remoteSocks != "" { 59 | err := connectToSocks(firstBytes, secondBytes, conn, remoteSocks) 60 | // If the socks server is no longer available, we have the error conneciton refused 61 | if err != nil && strings.Contains(err.Error(), "connection refused") { 62 | // The route is no longer valid and we delete it 63 | fmt.Println("Remote socks " + remoteSocks + " server no longer available. Got connection refused") 64 | //router.DeleteRoutes(network) 65 | } 66 | } else { 67 | fmt.Println("\n[-] Unkown route for " + dest + " using direct connection without proxy") 68 | serverSocks5.Handle(request, conn) 69 | } 70 | }() 71 | } 72 | } 73 | 74 | func connectToSocks(firstBytes []byte, secondBytes []byte, src net.Conn, remoteSocks string) error { 75 | 76 | var proxy net.Conn 77 | //log.Println("Connecting to remote socks") 78 | proxy, err := net.Dial("tcp", remoteSocks) 79 | if err != nil { 80 | log.Println(err) 81 | return err 82 | } 83 | defer src.Close() 84 | defer proxy.Close() 85 | // Send first request 86 | proxy.Write(firstBytes) 87 | // Empty the buffer 88 | buf := make([]byte, 100) 89 | proxy.Read(buf) 90 | // Send second request 91 | proxy.Write(secondBytes) 92 | 93 | chanToRemote := streamCopy(proxy, src) 94 | chanToStdout := streamCopy(src, proxy) 95 | select { 96 | case <-chanToStdout: 97 | //log.Println("Remote connection is closed") 98 | case <-chanToRemote: 99 | //log.Println("Local program is terminated") 100 | } 101 | return nil 102 | } 103 | 104 | // Performs copy operation between streams: os and tcp streams 105 | func streamCopy(src io.Reader, dst io.Writer) <-chan int { 106 | buf := make([]byte, 1024) 107 | syncChannel := make(chan int) 108 | go func() { 109 | defer func() { 110 | if con, ok := dst.(net.Conn); ok { 111 | con.Close() 112 | //log.Printf("Connection from %v is closed\n", con.RemoteAddr()) 113 | } 114 | syncChannel <- 0 // Notify that processing is finished 115 | }() 116 | for { 117 | 118 | var nBytes int 119 | var err error 120 | nBytes, err = src.Read(buf) 121 | 122 | if err != nil { 123 | if err != io.EOF { 124 | //log.Printf("Read error: %s\n", err) 125 | } 126 | break 127 | } 128 | _, err = dst.Write(buf[0:nBytes]) 129 | if err != nil { 130 | //log.Fatalf("Write error: %s\n", err) 131 | } 132 | } 133 | }() 134 | return syncChannel 135 | } 136 | -------------------------------------------------------------------------------- /utils/netstat/netstat.go: -------------------------------------------------------------------------------- 1 | package netstat 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | // SockAddr represents an ip:port pair 9 | type SockAddr struct { 10 | IP net.IP 11 | Port uint16 12 | } 13 | 14 | func (s *SockAddr) String() string { 15 | return fmt.Sprintf("%v:%d", s.IP, s.Port) 16 | } 17 | 18 | // SockTabEntry type represents each line of the /proc/net/[tcp|udp] 19 | type SockTabEntry struct { 20 | ino string 21 | LocalAddr *SockAddr 22 | RemoteAddr *SockAddr 23 | State SkState 24 | UID uint32 25 | Process *Process 26 | } 27 | 28 | // Process holds the PID and process name to which each socket belongs 29 | type Process struct { 30 | Pid int 31 | Name string 32 | } 33 | 34 | func (p *Process) String() string { 35 | return fmt.Sprintf("%d/%s", p.Pid, p.Name) 36 | } 37 | 38 | // SkState type represents socket connection state 39 | type SkState uint8 40 | 41 | func (s SkState) String() string { 42 | return skStates[s] 43 | } 44 | 45 | // AcceptFn is used to filter socket entries. The value returned indicates 46 | // whether the element is to be appended to the socket list. 47 | type AcceptFn func(*SockTabEntry) bool 48 | 49 | // NoopFilter - a test function returning true for all elements 50 | func NoopFilter(*SockTabEntry) bool { return true } 51 | 52 | // TCPSocks returns a slice of active TCP sockets containing only those 53 | // elements that satisfy the accept function 54 | func TCPSocks(accept AcceptFn) ([]SockTabEntry, error) { 55 | return osTCPSocks(accept) 56 | } 57 | 58 | // TCP6Socks returns a slice of active TCP IPv4 sockets containing only those 59 | // elements that satisfy the accept function 60 | func TCP6Socks(accept AcceptFn) ([]SockTabEntry, error) { 61 | return osTCP6Socks(accept) 62 | } 63 | 64 | // UDPSocks returns a slice of active UDP sockets containing only those 65 | // elements that satisfy the accept function 66 | func UDPSocks(accept AcceptFn) ([]SockTabEntry, error) { 67 | return osUDPSocks(accept) 68 | } 69 | 70 | // UDP6Socks returns a slice of active UDP IPv6 sockets containing only those 71 | // elements that satisfy the accept function 72 | func UDP6Socks(accept AcceptFn) ([]SockTabEntry, error) { 73 | return osUDP6Socks(accept) 74 | } 75 | -------------------------------------------------------------------------------- /utils/netstat/netstat_linux.go: -------------------------------------------------------------------------------- 1 | // Package netstat provides primitives for getting socket information on a 2 | // Linux based operating system. 3 | package netstat 4 | 5 | import ( 6 | "bufio" 7 | "bytes" 8 | "encoding/binary" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "net" 14 | "os" 15 | "path" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | const ( 21 | pathTCPTab = "/proc/net/tcp" 22 | pathTCP6Tab = "/proc/net/tcp6" 23 | pathUDPTab = "/proc/net/udp" 24 | pathUDP6Tab = "/proc/net/udp6" 25 | 26 | ipv4StrLen = 8 27 | ipv6StrLen = 32 28 | ) 29 | 30 | // Socket states 31 | const ( 32 | Established SkState = 0x01 33 | SynSent = 0x02 34 | SynRecv = 0x03 35 | FinWait1 = 0x04 36 | FinWait2 = 0x05 37 | TimeWait = 0x06 38 | Close = 0x07 39 | CloseWait = 0x08 40 | LastAck = 0x09 41 | Listen = 0x0a 42 | Closing = 0x0b 43 | ) 44 | 45 | var skStates = [...]string{ 46 | "UNKNOWN", 47 | "ESTABLISHED", 48 | "SYN_SENT", 49 | "SYN_RECV", 50 | "FIN_WAIT1", 51 | "FIN_WAIT2", 52 | "TIME_WAIT", 53 | "", // CLOSE 54 | "CLOSE_WAIT", 55 | "LAST_ACK", 56 | "LISTEN", 57 | "CLOSING", 58 | } 59 | 60 | // Errors returned by gonetstat 61 | var ( 62 | ErrNotEnoughFields = errors.New("gonetstat: not enough fields in the line") 63 | ) 64 | 65 | func parseIPv4(s string) (net.IP, error) { 66 | v, err := strconv.ParseUint(s, 16, 32) 67 | if err != nil { 68 | return nil, err 69 | } 70 | ip := make(net.IP, net.IPv4len) 71 | binary.LittleEndian.PutUint32(ip, uint32(v)) 72 | return ip, nil 73 | } 74 | 75 | func parseIPv6(s string) (net.IP, error) { 76 | ip := make(net.IP, net.IPv6len) 77 | const grpLen = 4 78 | i, j := 0, 4 79 | for len(s) != 0 { 80 | grp := s[0:8] 81 | u, err := strconv.ParseUint(grp, 16, 32) 82 | binary.LittleEndian.PutUint32(ip[i:j], uint32(u)) 83 | if err != nil { 84 | return nil, err 85 | } 86 | i, j = i+grpLen, j+grpLen 87 | s = s[8:] 88 | } 89 | return ip, nil 90 | } 91 | 92 | func parseAddr(s string) (*SockAddr, error) { 93 | fields := strings.Split(s, ":") 94 | if len(fields) < 2 { 95 | return nil, fmt.Errorf("netstat: not enough fields: %v", s) 96 | } 97 | var ip net.IP 98 | var err error 99 | switch len(fields[0]) { 100 | case ipv4StrLen: 101 | ip, err = parseIPv4(fields[0]) 102 | case ipv6StrLen: 103 | ip, err = parseIPv6(fields[0]) 104 | default: 105 | err = fmt.Errorf("netstat: bad formatted string: %v", fields[0]) 106 | } 107 | if err != nil { 108 | return nil, err 109 | } 110 | v, err := strconv.ParseUint(fields[1], 16, 16) 111 | if err != nil { 112 | return nil, err 113 | } 114 | return &SockAddr{IP: ip, Port: uint16(v)}, nil 115 | } 116 | 117 | func parseSocktab(r io.Reader, accept AcceptFn) ([]SockTabEntry, error) { 118 | br := bufio.NewScanner(r) 119 | tab := make([]SockTabEntry, 0, 4) 120 | 121 | // Discard title 122 | br.Scan() 123 | 124 | for br.Scan() { 125 | var e SockTabEntry 126 | line := br.Text() 127 | // Skip comments 128 | if i := strings.Index(line, "#"); i >= 0 { 129 | line = line[:i] 130 | } 131 | fields := strings.Fields(line) 132 | if len(fields) < 12 { 133 | return nil, fmt.Errorf("netstat: not enough fields: %v, %v", len(fields), fields) 134 | } 135 | addr, err := parseAddr(fields[1]) 136 | if err != nil { 137 | return nil, err 138 | } 139 | e.LocalAddr = addr 140 | addr, err = parseAddr(fields[2]) 141 | if err != nil { 142 | return nil, err 143 | } 144 | e.RemoteAddr = addr 145 | u, err := strconv.ParseUint(fields[3], 16, 8) 146 | if err != nil { 147 | return nil, err 148 | } 149 | e.State = SkState(u) 150 | u, err = strconv.ParseUint(fields[7], 10, 32) 151 | if err != nil { 152 | return nil, err 153 | } 154 | e.UID = uint32(u) 155 | e.ino = fields[9] 156 | extractProcInfo(&e) 157 | if accept(&e) { 158 | tab = append(tab, e) 159 | } 160 | } 161 | return tab, br.Err() 162 | } 163 | 164 | type procFd struct { 165 | base string 166 | pid int 167 | sktab *SockTabEntry 168 | p *Process 169 | } 170 | 171 | const sockPrefix = "socket:[" 172 | 173 | func getProcName(s []byte) string { 174 | i := bytes.Index(s, []byte("(")) 175 | if i < 0 { 176 | return "" 177 | } 178 | j := bytes.LastIndex(s, []byte(")")) 179 | if i < 0 { 180 | return "" 181 | } 182 | if i > j { 183 | return "" 184 | } 185 | return string(s[i+1 : j]) 186 | } 187 | 188 | func (p *procFd) iterFdDir() { 189 | // link name is of the form socket:[5860846] 190 | fddir := path.Join(p.base, "/fd") 191 | fi, err := ioutil.ReadDir(fddir) 192 | if err != nil { 193 | return 194 | } 195 | var buf [128]byte 196 | 197 | for _, file := range fi { 198 | fd := path.Join(fddir, file.Name()) 199 | lname, err := os.Readlink(fd) 200 | if err != nil || !strings.HasPrefix(lname, sockPrefix) { 201 | continue 202 | } 203 | 204 | sk := p.sktab 205 | ss := sockPrefix + sk.ino + "]" 206 | if ss != lname { 207 | continue 208 | } 209 | if p.p == nil { 210 | stat, err := os.Open(path.Join(p.base, "stat")) 211 | if err != nil { 212 | return 213 | } 214 | n, err := stat.Read(buf[:]) 215 | stat.Close() 216 | if err != nil { 217 | return 218 | } 219 | z := bytes.SplitN(buf[:n], []byte(" "), 3) 220 | name := getProcName(z[1]) 221 | p.p = &Process{p.pid, name} 222 | } 223 | sk.Process = p.p 224 | } 225 | } 226 | 227 | func extractProcInfo(sktab *SockTabEntry) { 228 | const basedir = "/proc" 229 | fi, err := ioutil.ReadDir(basedir) 230 | if err != nil { 231 | return 232 | } 233 | 234 | for _, file := range fi { 235 | if !file.IsDir() { 236 | continue 237 | } 238 | pid, err := strconv.Atoi(file.Name()) 239 | if err != nil { 240 | continue 241 | } 242 | base := path.Join(basedir, file.Name()) 243 | proc := procFd{base: base, pid: pid, sktab: sktab} 244 | proc.iterFdDir() 245 | } 246 | } 247 | 248 | // doNetstat - collect information about network port status 249 | func doNetstat(path string, fn AcceptFn) ([]SockTabEntry, error) { 250 | f, err := os.Open(path) 251 | if err != nil { 252 | return nil, err 253 | } 254 | tabs, err := parseSocktab(f, fn) 255 | f.Close() 256 | if err != nil { 257 | return nil, err 258 | } 259 | //extractProcInfo(tabs) 260 | return tabs, nil 261 | } 262 | 263 | // TCPSocks returns a slice of active TCP sockets containing only those 264 | // elements that satisfy the accept function 265 | func osTCPSocks(accept AcceptFn) ([]SockTabEntry, error) { 266 | return doNetstat(pathTCPTab, accept) 267 | } 268 | 269 | // TCP6Socks returns a slice of active TCP IPv4 sockets containing only those 270 | // elements that satisfy the accept function 271 | func osTCP6Socks(accept AcceptFn) ([]SockTabEntry, error) { 272 | return doNetstat(pathTCP6Tab, accept) 273 | } 274 | 275 | // UDPSocks returns a slice of active UDP sockets containing only those 276 | // elements that satisfy the accept function 277 | func osUDPSocks(accept AcceptFn) ([]SockTabEntry, error) { 278 | return doNetstat(pathUDPTab, accept) 279 | } 280 | 281 | // UDP6Socks returns a slice of active UDP IPv6 sockets containing only those 282 | // elements that satisfy the accept function 283 | func osUDP6Socks(accept AcceptFn) ([]SockTabEntry, error) { 284 | return doNetstat(pathUDP6Tab, accept) 285 | } 286 | -------------------------------------------------------------------------------- /utils/netstat/netstat_windows.go: -------------------------------------------------------------------------------- 1 | // +build amd64 2 | 3 | package netstat 4 | 5 | import ( 6 | "bytes" 7 | "encoding/binary" 8 | "errors" 9 | "fmt" 10 | "net" 11 | "reflect" 12 | "syscall" 13 | "unsafe" 14 | ) 15 | 16 | const ( 17 | errInsuffBuff = syscall.Errno(122) 18 | 19 | Th32csSnapProcess = uint32(0x00000002) 20 | InvalidHandleValue = ^uintptr(0) 21 | MaxPath = 260 22 | ) 23 | 24 | var ( 25 | errNoMoreFiles = errors.New("no more files have been found") 26 | 27 | modiphlpapi = syscall.NewLazyDLL("Iphlpapi.dll") 28 | modkernel32 = syscall.NewLazyDLL("Kernel32.dll") 29 | 30 | procGetTCPTable2 = modiphlpapi.NewProc("GetTcpTable2") 31 | procGetTCP6Table2 = modiphlpapi.NewProc("GetTcp6Table2") 32 | procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable") 33 | procCreateSnapshot = modkernel32.NewProc("CreateToolhelp32Snapshot") 34 | procProcess32First = modkernel32.NewProc("Process32First") 35 | procProcess32Next = modkernel32.NewProc("Process32Next") 36 | ) 37 | 38 | // Socket states 39 | const ( 40 | Close SkState = 0x01 41 | Listen = 0x02 42 | SynSent = 0x03 43 | SynRecv = 0x04 44 | Established = 0x05 45 | FinWait1 = 0x06 46 | FinWait2 = 0x07 47 | CloseWait = 0x08 48 | Closing = 0x09 49 | LastAck = 0x0a 50 | TimeWait = 0x0b 51 | DeleteTcb = 0x0c 52 | ) 53 | 54 | var skStates = [...]string{ 55 | "UNKNOWN", 56 | "", // CLOSE 57 | "LISTEN", 58 | "SYN_SENT", 59 | "SYN_RECV", 60 | "ESTABLISHED", 61 | "FIN_WAIT1", 62 | "FIN_WAIT2", 63 | "CLOSE_WAIT", 64 | "CLOSING", 65 | "LAST_ACK", 66 | "TIME_WAIT", 67 | "DELETE_TCB", 68 | } 69 | 70 | func memToIPv4(p unsafe.Pointer) net.IP { 71 | a := (*[net.IPv4len]byte)(p) 72 | ip := make(net.IP, net.IPv4len) 73 | copy(ip, a[:]) 74 | return ip 75 | } 76 | 77 | func memToIPv6(p unsafe.Pointer) net.IP { 78 | a := (*[net.IPv6len]byte)(p) 79 | ip := make(net.IP, net.IPv6len) 80 | copy(ip, a[:]) 81 | return ip 82 | } 83 | 84 | func memtohs(n unsafe.Pointer) uint16 { 85 | return binary.BigEndian.Uint16((*[2]byte)(n)[:]) 86 | } 87 | 88 | type WinSock struct { 89 | Addr uint32 90 | Port uint32 91 | } 92 | 93 | func (w *WinSock) Sock() *SockAddr { 94 | ip := memToIPv4(unsafe.Pointer(&w.Addr)) 95 | port := memtohs(unsafe.Pointer(&w.Port)) 96 | return &SockAddr{IP: ip, Port: port} 97 | } 98 | 99 | type WinSock6 struct { 100 | Addr [net.IPv6len]byte 101 | ScopeID uint32 102 | Port uint32 103 | } 104 | 105 | func (w *WinSock6) Sock() *SockAddr { 106 | ip := memToIPv6(unsafe.Pointer(&w.Addr[0])) 107 | port := memtohs(unsafe.Pointer(&w.Port)) 108 | return &SockAddr{IP: ip, Port: port} 109 | } 110 | 111 | type MibTCPRow2 struct { 112 | State uint32 113 | LocalAddr WinSock 114 | RemoteAddr WinSock 115 | WinPid 116 | OffloadState uint32 117 | } 118 | 119 | type WinPid uint32 120 | 121 | func (pid WinPid) Process(snp ProcessSnapshot) *Process { 122 | if pid < 1 { 123 | return nil 124 | } 125 | return &Process{ 126 | Pid: int(pid), 127 | Name: snp.ProcPIDToName(uint32(pid)), 128 | } 129 | } 130 | 131 | func (m *MibTCPRow2) LocalSock() *SockAddr { return m.LocalAddr.Sock() } 132 | func (m *MibTCPRow2) RemoteSock() *SockAddr { return m.RemoteAddr.Sock() } 133 | func (m *MibTCPRow2) SockState() SkState { return SkState(m.State) } 134 | 135 | type MibTCPTable2 struct { 136 | NumEntries uint32 137 | Table [1]MibTCPRow2 138 | } 139 | 140 | func (t *MibTCPTable2) Rows() []MibTCPRow2 { 141 | var s []MibTCPRow2 142 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 143 | hdr.Data = uintptr(unsafe.Pointer(&t.Table[0])) 144 | hdr.Len = int(t.NumEntries) 145 | hdr.Cap = int(t.NumEntries) 146 | return s 147 | } 148 | 149 | // MibTCP6Row2 structure contains information that describes an IPv6 TCP 150 | // connection. 151 | type MibTCP6Row2 struct { 152 | LocalAddr WinSock6 153 | RemoteAddr WinSock6 154 | State uint32 155 | WinPid 156 | OffloadState uint32 157 | } 158 | 159 | func (m *MibTCP6Row2) LocalSock() *SockAddr { return m.LocalAddr.Sock() } 160 | func (m *MibTCP6Row2) RemoteSock() *SockAddr { return m.RemoteAddr.Sock() } 161 | func (m *MibTCP6Row2) SockState() SkState { return SkState(m.State) } 162 | 163 | // MibTCP6Table2 structure contains a table of IPv6 TCP connections on the 164 | // local computer. 165 | type MibTCP6Table2 struct { 166 | NumEntries uint32 167 | Table [1]MibTCP6Row2 168 | } 169 | 170 | func (t *MibTCP6Table2) Rows() []MibTCP6Row2 { 171 | var s []MibTCP6Row2 172 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 173 | hdr.Data = uintptr(unsafe.Pointer(&t.Table[0])) 174 | hdr.Len = int(t.NumEntries) 175 | hdr.Cap = int(t.NumEntries) 176 | return s 177 | } 178 | 179 | // MibUDPRowOwnerPID structure contains an entry from the User Datagram 180 | // Protocol (UDP) listener table for IPv4 on the local computer. The entry also 181 | // includes the process ID (PID) that issued the call to the bind function for 182 | // the UDP endpoint 183 | type MibUDPRowOwnerPID struct { 184 | WinSock 185 | WinPid 186 | } 187 | 188 | func (m *MibUDPRowOwnerPID) LocalSock() *SockAddr { return m.Sock() } 189 | func (m *MibUDPRowOwnerPID) RemoteSock() *SockAddr { return &SockAddr{net.IPv4zero, 0} } 190 | func (m *MibUDPRowOwnerPID) SockState() SkState { return Close } 191 | 192 | // MibUDPTableOwnerPID structure contains the User Datagram Protocol (UDP) 193 | // listener table for IPv4 on the local computer. The table also includes the 194 | // process ID (PID) that issued the call to the bind function for each UDP 195 | // endpoint. 196 | type MibUDPTableOwnerPID struct { 197 | NumEntries uint32 198 | Table [1]MibUDPRowOwnerPID 199 | } 200 | 201 | func (t *MibUDPTableOwnerPID) Rows() []MibUDPRowOwnerPID { 202 | var s []MibUDPRowOwnerPID 203 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 204 | hdr.Data = uintptr(unsafe.Pointer(&t.Table[0])) 205 | hdr.Len = int(t.NumEntries) 206 | hdr.Cap = int(t.NumEntries) 207 | return s 208 | } 209 | 210 | // MibUDP6RowOwnerPID serves the same purpose as MibUDPRowOwnerPID, except that 211 | // the information in this case is for IPv6. 212 | type MibUDP6RowOwnerPID struct { 213 | WinSock6 214 | WinPid 215 | } 216 | 217 | func (m *MibUDP6RowOwnerPID) LocalSock() *SockAddr { return m.Sock() } 218 | func (m *MibUDP6RowOwnerPID) RemoteSock() *SockAddr { return &SockAddr{net.IPv4zero, 0} } 219 | func (m *MibUDP6RowOwnerPID) SockState() SkState { return Close } 220 | 221 | // MibUDP6TableOwnerPID serves the same purpose as MibUDPTableOwnerPID for IPv6 222 | type MibUDP6TableOwnerPID struct { 223 | NumEntries uint32 224 | Table [1]MibUDP6RowOwnerPID 225 | } 226 | 227 | func (t *MibUDP6TableOwnerPID) Rows() []MibUDP6RowOwnerPID { 228 | var s []MibUDP6RowOwnerPID 229 | hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s)) 230 | hdr.Data = uintptr(unsafe.Pointer(&t.Table[0])) 231 | hdr.Len = int(t.NumEntries) 232 | hdr.Cap = int(t.NumEntries) 233 | return s 234 | } 235 | 236 | // Processentry32 describes an entry from a list of the processes residing in 237 | // the system address space when a snapshot was taken 238 | type Processentry32 struct { 239 | Size uint32 240 | CntUsage uint32 241 | Th32ProcessID uint32 242 | Th32DefaultHeapID uintptr 243 | Th32ModuleID uint32 244 | CntThreads uint32 245 | Th32ParentProcessID uint32 246 | PriClassBase int32 247 | Flags uint32 248 | ExeFile [MaxPath]byte 249 | } 250 | 251 | func rawGetTCPTable2(proc uintptr, tab unsafe.Pointer, size *uint32, order bool) error { 252 | var oint uintptr 253 | if order { 254 | oint = 1 255 | } 256 | r1, _, callErr := syscall.Syscall( 257 | proc, 258 | uintptr(3), 259 | uintptr(tab), 260 | uintptr(unsafe.Pointer(size)), 261 | oint) 262 | if callErr != 0 { 263 | return callErr 264 | } 265 | if r1 != 0 { 266 | return syscall.Errno(r1) 267 | } 268 | return nil 269 | } 270 | 271 | func getTCPTable2(proc uintptr, order bool) ([]byte, error) { 272 | var ( 273 | size uint32 274 | buf []byte 275 | ) 276 | 277 | // determine size 278 | err := rawGetTCPTable2(proc, unsafe.Pointer(nil), &size, false) 279 | if err != nil && err != errInsuffBuff { 280 | return nil, err 281 | } 282 | buf = make([]byte, size) 283 | table := unsafe.Pointer(&buf[0]) 284 | err = rawGetTCPTable2(proc, table, &size, true) 285 | if err != nil { 286 | return nil, err 287 | } 288 | return buf, nil 289 | } 290 | 291 | // GetTCPTable2 function retrieves the IPv4 TCP connection table 292 | func GetTCPTable2(order bool) (*MibTCPTable2, error) { 293 | b, err := getTCPTable2(procGetTCPTable2.Addr(), true) 294 | if err != nil { 295 | return nil, err 296 | } 297 | return (*MibTCPTable2)(unsafe.Pointer(&b[0])), nil 298 | } 299 | 300 | // GetTCP6Table2 function retrieves the IPv6 TCP connection table 301 | func GetTCP6Table2(order bool) (*MibTCP6Table2, error) { 302 | b, err := getTCPTable2(procGetTCP6Table2.Addr(), true) 303 | if err != nil { 304 | return nil, err 305 | } 306 | return (*MibTCP6Table2)(unsafe.Pointer(&b[0])), nil 307 | } 308 | 309 | // The UDPTableClass enumeration defines the set of values used to indicate 310 | // the type of table returned by calls to GetExtendedUDPTable 311 | type UDPTableClass uint 312 | 313 | // Possible table class values 314 | const ( 315 | UDPTableBasic UDPTableClass = iota 316 | UDPTableOwnerPID 317 | UDPTableOwnerModule 318 | ) 319 | 320 | func getExtendedUDPTable(table unsafe.Pointer, size *uint32, order bool, af uint32, cl UDPTableClass) error { 321 | var oint uintptr 322 | if order { 323 | oint = 1 324 | } 325 | r1, _, callErr := syscall.Syscall6( 326 | procGetExtendedUDPTable.Addr(), 327 | uintptr(6), 328 | uintptr(table), 329 | uintptr(unsafe.Pointer(size)), 330 | oint, 331 | uintptr(af), 332 | uintptr(cl), 333 | uintptr(0)) 334 | if callErr != 0 { 335 | return callErr 336 | } 337 | if r1 != 0 { 338 | return syscall.Errno(r1) 339 | } 340 | return nil 341 | } 342 | 343 | // GetExtendedUDPTable function retrieves a table that contains a list of UDP 344 | // endpoints available to the application 345 | func GetExtendedUDPTable(order bool, af uint32, cl UDPTableClass) ([]byte, error) { 346 | var size uint32 347 | err := getExtendedUDPTable(nil, &size, order, af, cl) 348 | if err != nil && err != errInsuffBuff { 349 | return nil, err 350 | } 351 | buf := make([]byte, size) 352 | err = getExtendedUDPTable(unsafe.Pointer(&buf[0]), &size, order, af, cl) 353 | if err != nil { 354 | return nil, err 355 | } 356 | return buf, nil 357 | } 358 | 359 | func GetUDPTableOwnerPID(order bool) (*MibUDPTableOwnerPID, error) { 360 | b, err := GetExtendedUDPTable(true, syscall.AF_INET, UDPTableOwnerPID) 361 | if err != nil { 362 | return nil, err 363 | } 364 | return (*MibUDPTableOwnerPID)(unsafe.Pointer(&b[0])), nil 365 | } 366 | 367 | func GetUDP6TableOwnerPID(order bool) (*MibUDP6TableOwnerPID, error) { 368 | b, err := GetExtendedUDPTable(true, syscall.AF_INET6, UDPTableOwnerPID) 369 | if err != nil { 370 | return nil, err 371 | } 372 | return (*MibUDP6TableOwnerPID)(unsafe.Pointer(&b[0])), nil 373 | } 374 | 375 | // ProcessSnapshot wraps the syscall.Handle, which represents a snapshot of 376 | // the specified processes. 377 | type ProcessSnapshot syscall.Handle 378 | 379 | // CreateToolhelp32Snapshot takes a snapshot of the specified processes, as 380 | // well as the heaps, modules, and threads used by these processes 381 | func CreateToolhelp32Snapshot(flags uint32, pid uint32) (ProcessSnapshot, error) { 382 | r1, _, callErr := syscall.Syscall( 383 | procCreateSnapshot.Addr(), 384 | uintptr(2), 385 | uintptr(flags), 386 | uintptr(pid), 0) 387 | ret := ProcessSnapshot(r1) 388 | if callErr != 0 { 389 | return ret, callErr 390 | } 391 | if r1 == InvalidHandleValue { 392 | return ret, fmt.Errorf("invalid handle value: %#v", r1) 393 | } 394 | return ret, nil 395 | } 396 | 397 | // ProcPIDToName translates PID to a name 398 | func (snp ProcessSnapshot) ProcPIDToName(pid uint32) string { 399 | var processEntry Processentry32 400 | processEntry.Size = uint32(unsafe.Sizeof(processEntry)) 401 | handle := syscall.Handle(snp) 402 | err := Process32First(handle, &processEntry) 403 | if err != nil { 404 | return "" 405 | } 406 | for { 407 | if processEntry.Th32ProcessID == pid { 408 | return StringFromNullTerminated(processEntry.ExeFile[:]) 409 | } 410 | err = Process32Next(handle, &processEntry) 411 | if err != nil { 412 | return "" 413 | } 414 | } 415 | } 416 | 417 | // Close releases underlying win32 handle 418 | func (snp ProcessSnapshot) Close() error { 419 | return syscall.CloseHandle(syscall.Handle(snp)) 420 | } 421 | 422 | // Process32First retrieves information about the first process encountered 423 | // in a system snapshot 424 | func Process32First(handle syscall.Handle, pe *Processentry32) error { 425 | pe.Size = uint32(unsafe.Sizeof(*pe)) 426 | r1, _, callErr := syscall.Syscall( 427 | procProcess32First.Addr(), 428 | uintptr(2), 429 | uintptr(handle), 430 | uintptr(unsafe.Pointer(pe)), 0) 431 | if callErr != 0 { 432 | return callErr 433 | } 434 | if r1 == 0 { 435 | return errNoMoreFiles 436 | } 437 | return nil 438 | } 439 | 440 | // Process32Next retrieves information about the next process 441 | // recorded in a system snapshot 442 | func Process32Next(handle syscall.Handle, pe *Processentry32) error { 443 | pe.Size = uint32(unsafe.Sizeof(*pe)) 444 | r1, _, callErr := syscall.Syscall( 445 | procProcess32Next.Addr(), 446 | uintptr(2), 447 | uintptr(handle), 448 | uintptr(unsafe.Pointer(pe)), 0) 449 | if callErr != 0 { 450 | return callErr 451 | } 452 | if r1 == 0 { 453 | return errNoMoreFiles 454 | } 455 | return nil 456 | } 457 | 458 | // StringFromNullTerminated returns a string from a nul-terminated byte slice 459 | func StringFromNullTerminated(b []byte) string { 460 | n := bytes.IndexByte(b, '\x00') 461 | if n < 1 { 462 | return "" 463 | } 464 | return string(b[:n]) 465 | } 466 | 467 | type winSockEnt interface { 468 | LocalSock() *SockAddr 469 | RemoteSock() *SockAddr 470 | SockState() SkState 471 | Process(snp ProcessSnapshot) *Process 472 | } 473 | 474 | func toSockTabEntry(ws winSockEnt, snp ProcessSnapshot) SockTabEntry { 475 | return SockTabEntry{ 476 | LocalAddr: ws.LocalSock(), 477 | RemoteAddr: ws.RemoteSock(), 478 | State: ws.SockState(), 479 | Process: ws.Process(snp), 480 | } 481 | } 482 | 483 | func osTCPSocks(accept AcceptFn) ([]SockTabEntry, error) { 484 | tbl, err := GetTCPTable2(true) 485 | if err != nil { 486 | return nil, err 487 | } 488 | snp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0) 489 | if err != nil { 490 | return nil, err 491 | } 492 | var sktab []SockTabEntry 493 | s := tbl.Rows() 494 | for i := range s { 495 | ent := toSockTabEntry(&s[i], snp) 496 | if accept(&ent) { 497 | sktab = append(sktab, ent) 498 | } 499 | } 500 | snp.Close() 501 | return sktab, nil 502 | } 503 | 504 | func osTCP6Socks(accept AcceptFn) ([]SockTabEntry, error) { 505 | tbl, err := GetTCP6Table2(true) 506 | if err != nil { 507 | return nil, err 508 | } 509 | snp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0) 510 | if err != nil { 511 | return nil, err 512 | } 513 | var sktab []SockTabEntry 514 | s := tbl.Rows() 515 | for i := range s { 516 | ent := toSockTabEntry(&s[i], snp) 517 | if accept(&ent) { 518 | sktab = append(sktab, ent) 519 | } 520 | } 521 | snp.Close() 522 | return sktab, nil 523 | } 524 | 525 | func osUDPSocks(accept AcceptFn) ([]SockTabEntry, error) { 526 | tbl, err := GetUDPTableOwnerPID(true) 527 | if err != nil { 528 | return nil, err 529 | } 530 | snp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0) 531 | if err != nil { 532 | return nil, err 533 | } 534 | var sktab []SockTabEntry 535 | s := tbl.Rows() 536 | for i := range s { 537 | ent := toSockTabEntry(&s[i], snp) 538 | if accept(&ent) { 539 | sktab = append(sktab, ent) 540 | } 541 | } 542 | snp.Close() 543 | return sktab, nil 544 | } 545 | 546 | func osUDP6Socks(accept AcceptFn) ([]SockTabEntry, error) { 547 | tbl, err := GetUDP6TableOwnerPID(true) 548 | if err != nil { 549 | return nil, err 550 | } 551 | snp, err := CreateToolhelp32Snapshot(Th32csSnapProcess, 0) 552 | if err != nil { 553 | return nil, err 554 | } 555 | var sktab []SockTabEntry 556 | s := tbl.Rows() 557 | for i := range s { 558 | ent := toSockTabEntry(&s[i], snp) 559 | if accept(&ent) { 560 | sktab = append(sktab, ent) 561 | } 562 | } 563 | snp.Close() 564 | return sktab, nil 565 | } 566 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "regexp" 7 | "strconv" 8 | "strings" 9 | "time" 10 | 11 | netstat "github.com/nodauf/Go-RouterSocks/utils/netstat" 12 | ) 13 | 14 | var ChiselProcess = map[int]*netstat.SockAddr{} 15 | 16 | func IsCIDRValid(cidr string) bool { 17 | var re = regexp.MustCompile(`^([0-9]{1,3}\.){3}[0-9]{1,3}\/([0-9]|[1-2][0-9]|3[0-2])$`) 18 | return re.Match([]byte(cidr)) 19 | } 20 | 21 | func IsRemoteSocksValid(remoteSocks string) bool { 22 | var re = regexp.MustCompile(`^(\w+|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-4][0-9]|[01]?[0-9][0-9]?)):([1-9]|[1-5]?[0-9]{2,4}|6[1-4][0-9]{3}|65[1-4][0-9]{2}|655[1-2][0-9]|6553[1-5])$`) 23 | return re.Match([]byte(remoteSocks)) 24 | } 25 | 26 | func CanResolvedHostname(server string) bool { 27 | _, err := net.LookupIP(server) 28 | if err != nil { 29 | return false 30 | } 31 | return true 32 | } 33 | 34 | func CIDRContainsIP(cidr string, ip string) bool { 35 | _, network, _ := net.ParseCIDR(cidr) 36 | fmt.Println(cidr) 37 | return network.Contains(net.ParseIP(ip)) 38 | } 39 | 40 | func PrintChiselProcess() { 41 | GetChiselProcess() 42 | for id, server := range ChiselProcess { 43 | fmt.Println("[" + strconv.Itoa(id) + "] " + server.String()) 44 | } 45 | } 46 | 47 | func IsChiselIDValid(idString string) string { 48 | // Init the chisel list in case the user didn't print it 49 | GetChiselProcess() 50 | id, err := strconv.Atoi(idString) 51 | if serverSocks, ok := ChiselProcess[id]; err == nil && ok { 52 | return serverSocks.String() 53 | } 54 | return "" 55 | } 56 | 57 | func GetChiselProcess() { 58 | ChiselProcess = map[int]*netstat.SockAddr{} 59 | tabs, err := netstat.TCPSocks(func(s *netstat.SockTabEntry) bool { 60 | return s.State == netstat.Listen && s.Process != nil && (strings.ToLower(s.Process.Name) == "chisel" || strings.ToLower(s.Process.Name) == "chisel.exe") 61 | }) 62 | if err != nil { 63 | fmt.Println("Can list the listen port: " + err.Error()) 64 | } 65 | if len(tabs) > 0 { 66 | for i, e := range tabs { 67 | ChiselProcess[i] = e.LocalAddr 68 | 69 | } 70 | } 71 | } 72 | 73 | func ServerReachable(server string) bool { 74 | timeout := 1 * time.Second 75 | _, err := net.DialTimeout("tcp", server, timeout) 76 | return err == nil 77 | } 78 | --------------------------------------------------------------------------------