├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── auth.go ├── auth_test.go ├── credentials.go ├── credentials_test.go ├── goproxy.ini ├── main.go ├── request.go ├── request_test.go ├── resolver.go ├── resolver_test.go ├── ruleset.go ├── ruleset_test.go ├── screenshoot └── psb.png ├── socks5.go └── socks5_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.1 4 | - tip 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Armon Dadgar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-multi-IP-acceleration 2 | ======================== 3 | #####目前我正在寻找暑假实习,欢迎访问 [r.hi-hi.cn](http://r.hi-hi.cn) 查看我的简历 4 | ####go-multi-IP-acceleration是用golang实现的一个socks5代理,它可以把流量随机从多个本地IP发出,在某些网络环境下可以达到加速网络的效果 5 | ###原理简介: 6 | 先看下宽带用户中流行的[双拨/多拨](http://member.mcplive.cn/space.php?uid=1799&do=thread&id=30167) 7 | > 什么是双拨/多拨呢?就是两台甚至多台设备同时用同一个账号,在同一条线路上拨号,如果能够同时拨通,就称为双拨/多拨。 8 | 为什么要双拨/多拨?因为有些地区的ISP运营商,可能没有完全在线路端口上面限速,限速的只是账号,那么双拨/多拨以后,你每台设备都能达到账号限速的带宽,如果能够叠加起来,你就等于免费拥有了2倍甚至N倍的带宽,想想看,2倍甚至N倍的网速,是不是很吸引人? 9 | 10 | 在我们实验室的校园网中(类似与很多公司的内网),主干路由器限速是根据IP来的,我们同样可以使用openwrt路由器的nwan功能虚拟多张网卡实现带宽叠加。 11 | 12 | 但是实际上,因为免去了拨号过程,我们完全没有必要使用多张虚拟网卡,我们只需要多个IP就足够了。而同一个网卡实际上是可以绑定多个IP的。 13 | 这样实现技术就很简单的,我们手动分配多个IP,然后把流量随机从不同的IP发出去即可。 14 | 15 | 要实现这样的流量分配,我们可以使用iptables。但是这样的话流量分配策略受iptables功能限制,很不灵活,而且不只能自己本机使用。 16 | 于是参考大多数翻墙工具的做法,我们实现一个HTTP代理/Socks5代理,然后在代理端建立的时候指定出口IP即可。 17 | 18 | 这里的实现是一个Socks5代理 19 | 20 | 21 | 效果如下,使用之前下载速度100-280KB/s,使用之后可达2-3MB/s: 22 | 23 | ![效果如下](screenshoot/psb.png) 24 | 25 | ###使用说明: 26 | - 下载golang开发环境,然后编译对应平台的二进文件,也可以直接从release中下载。 27 | - 用nmap等工具扫描出一个空余IP列表 28 | - 然后在本地机器加上多个IP,(windows在设置IP的对话框选择高级,然后加上多个IP),linux自己搜索相关资料。 29 | - 然后把goproxy.ini放到和二进制文件一个目录,并修改配置文件 30 | 配置文件: 31 | - port 为http代理功能保留,暂空 32 | - port_socks5 代表监听端口 33 | - local ip 本机IP地址列表 34 | - allowed ip 允许使用代理的IP列表,暂未实现 35 | 36 | 然后运行, 打开迅雷,进入设置->高级设置->代理设置,然后添加代理,服务器 127.0.0.1, 端口和port_socks5一致,类型socks5,点测试,如果成功的话就ok了。建议把任务默认属性->原始地线程数调到10 37 | 38 | 39 | TODO: 40 | - 身份验证 41 | - http代理 42 | - 自动翻墙 43 | - 自动检测IP冲突 44 | 45 | 特别感谢:https://github.com/armon/go-socks5 46 | -------------------------------------------------------------------------------- /auth.go: -------------------------------------------------------------------------------- 1 | package main 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 | type Authenticator interface { 23 | Authenticate(reader io.Reader, writer io.Writer) error 24 | GetCode() uint8 25 | } 26 | 27 | // NoAuthAuthenticator is used to handle the "No Authentication" mode 28 | type NoAuthAuthenticator struct{} 29 | 30 | func (a NoAuthAuthenticator) GetCode() uint8 { 31 | return noAuth 32 | } 33 | 34 | func (a NoAuthAuthenticator) Authenticate(reader io.Reader, writer io.Writer) error { 35 | _, err := writer.Write([]byte{socks5Version, noAuth}) 36 | return err 37 | } 38 | 39 | // UserPassAuthenticator is used to handle username/password based 40 | // authentication 41 | type UserPassAuthenticator struct { 42 | Credentials CredentialStore 43 | } 44 | 45 | func (a UserPassAuthenticator) GetCode() uint8 { 46 | return userPassAuth 47 | } 48 | 49 | func (a UserPassAuthenticator) Authenticate(reader io.Reader, writer io.Writer) error { 50 | // Tell the client to use user/pass auth 51 | if _, err := writer.Write([]byte{socks5Version, userPassAuth}); err != nil { 52 | return err 53 | } 54 | 55 | // Get the version and username length 56 | header := []byte{0, 0} 57 | if _, err := io.ReadAtLeast(reader, header, 2); err != nil { 58 | return err 59 | } 60 | 61 | // Ensure we are compatible 62 | if header[0] != userAuthVersion { 63 | return fmt.Errorf("Unsupported auth version: %v", header[0]) 64 | } 65 | 66 | // Get the user name 67 | userLen := int(header[1]) 68 | user := make([]byte, userLen) 69 | if _, err := io.ReadAtLeast(reader, user, userLen); err != nil { 70 | return err 71 | } 72 | 73 | // Get the password length 74 | if _, err := reader.Read(header[:1]); err != nil { 75 | return err 76 | } 77 | 78 | // Get the password 79 | passLen := int(header[0]) 80 | pass := make([]byte, passLen) 81 | if _, err := io.ReadAtLeast(reader, pass, passLen); err != nil { 82 | return err 83 | } 84 | 85 | // Verify the password 86 | if a.Credentials.Valid(string(user), string(pass)) { 87 | if _, err := writer.Write([]byte{userAuthVersion, authSuccess}); err != nil { 88 | return err 89 | } 90 | } else { 91 | if _, err := writer.Write([]byte{userAuthVersion, authFailure}); err != nil { 92 | return err 93 | } 94 | return UserAuthFailed 95 | } 96 | 97 | // Done 98 | return nil 99 | 100 | } 101 | 102 | // authenticate is used to handle connection authentication 103 | func (s *Server) authenticate(conn io.Writer, bufConn io.Reader) error { 104 | // Get the methods 105 | methods, err := readMethods(bufConn) 106 | if err != nil { 107 | return fmt.Errorf("Failed to get auth methods: %v", err) 108 | } 109 | 110 | // Select a usable method 111 | for _, method := range methods { 112 | cator, found := s.authMethods[method] 113 | if found { 114 | return cator.Authenticate(bufConn, conn) 115 | } 116 | } 117 | 118 | // No usable method found 119 | return noAcceptableAuth(conn) 120 | } 121 | 122 | // noAcceptableAuth is used to handle when we have no eligible 123 | // authentication mechanism 124 | func noAcceptableAuth(conn io.Writer) error { 125 | conn.Write([]byte{socks5Version, noAcceptable}) 126 | return NoSupportedAuth 127 | } 128 | 129 | // readMethods is used to read the number of methods 130 | // and proceeding auth methods 131 | func readMethods(r io.Reader) ([]byte, error) { 132 | header := []byte{0} 133 | if _, err := r.Read(header); err != nil { 134 | return nil, err 135 | } 136 | 137 | numMethods := int(header[0]) 138 | methods := make([]byte, numMethods) 139 | _, err := io.ReadAtLeast(r, methods, numMethods) 140 | return methods, err 141 | } 142 | -------------------------------------------------------------------------------- /auth_test.go: -------------------------------------------------------------------------------- 1 | package main 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 | if err := s.authenticate(&resp, req); err != nil { 15 | t.Fatalf("err: %v", err) 16 | } 17 | 18 | out := resp.Bytes() 19 | if !bytes.Equal(out, []byte{socks5Version, noAuth}) { 20 | t.Fatalf("bad: %v", out) 21 | } 22 | } 23 | 24 | func TestPasswordAuth_Valid(t *testing.T) { 25 | req := bytes.NewBuffer(nil) 26 | req.Write([]byte{2, noAuth, userPassAuth}) 27 | req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'}) 28 | var resp bytes.Buffer 29 | 30 | cred := StaticCredentials{ 31 | "foo": "bar", 32 | } 33 | 34 | cator := UserPassAuthenticator{Credentials: cred} 35 | 36 | s, _ := New(&Config{AuthMethods: []Authenticator{cator}}) 37 | 38 | if err := s.authenticate(&resp, req); err != nil { 39 | t.Fatalf("err: %v", err) 40 | } 41 | 42 | out := resp.Bytes() 43 | if !bytes.Equal(out, []byte{socks5Version, userPassAuth, 1, authSuccess}) { 44 | t.Fatalf("bad: %v", out) 45 | } 46 | } 47 | 48 | func TestPasswordAuth_Invalid(t *testing.T) { 49 | req := bytes.NewBuffer(nil) 50 | req.Write([]byte{2, noAuth, userPassAuth}) 51 | req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'z'}) 52 | var resp bytes.Buffer 53 | 54 | cred := StaticCredentials{ 55 | "foo": "bar", 56 | } 57 | cator := UserPassAuthenticator{Credentials: cred} 58 | s, _ := New(&Config{AuthMethods: []Authenticator{cator}}) 59 | if err := s.authenticate(&resp, req); err != UserAuthFailed { 60 | t.Fatalf("err: %v", err) 61 | } 62 | 63 | out := resp.Bytes() 64 | if !bytes.Equal(out, []byte{socks5Version, userPassAuth, 1, authFailure}) { 65 | t.Fatalf("bad: %v", out) 66 | } 67 | } 68 | 69 | func TestNoSupportedAuth(t *testing.T) { 70 | req := bytes.NewBuffer(nil) 71 | req.Write([]byte{1, noAuth}) 72 | var resp bytes.Buffer 73 | 74 | cred := StaticCredentials{ 75 | "foo": "bar", 76 | } 77 | cator := UserPassAuthenticator{Credentials: cred} 78 | 79 | s, _ := New(&Config{AuthMethods: []Authenticator{cator}}) 80 | if err := s.authenticate(&resp, req); err != NoSupportedAuth { 81 | t.Fatalf("err: %v", err) 82 | } 83 | 84 | out := resp.Bytes() 85 | if !bytes.Equal(out, []byte{socks5Version, noAcceptable}) { 86 | t.Fatalf("bad: %v", out) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /credentials.go: -------------------------------------------------------------------------------- 1 | package main 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 | -------------------------------------------------------------------------------- /credentials_test.go: -------------------------------------------------------------------------------- 1 | package main 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 | -------------------------------------------------------------------------------- /goproxy.ini: -------------------------------------------------------------------------------- 1 | [port] 2 | port=9000 3 | port_socks5=9005 4 | 5 | [local ip] 6 | -=125.221.233.6 7 | ; -=125.221.233.240 8 | ; -=125.221.233.241 9 | ; -=125.221.233.242 10 | ; -=125.221.233.243 11 | ; -=125.221.233.244 12 | ; -=125.221.233.245 13 | ; -=125.221.233.246 14 | 15 | [allowed ip] 16 | -=127.0.0.1 17 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Unknwon/goconfig" 6 | "log" 7 | "math/rand" 8 | "net" 9 | "sort" 10 | ) 11 | 12 | type configType struct { 13 | port string 14 | localIp []string 15 | allowedIp map[string]bool 16 | } 17 | 18 | var config configType 19 | 20 | var p = fmt.Println 21 | 22 | func Fatal(err error) { 23 | if err != nil { 24 | panic(err) 25 | } 26 | } 27 | 28 | func init() { 29 | ini, err := goconfig.LoadConfigFile("goproxy.ini") 30 | Fatal(err) 31 | config.port, _ = ini.GetValue("port", "port_socks5") 32 | localIp, _ := ini.GetSection("local ip") 33 | allowedIp, _ := ini.GetSection("allowed ip") 34 | 35 | for _, value := range localIp { 36 | config.localIp = append(config.localIp, value) 37 | } 38 | config.allowedIp = make(map[string]bool) 39 | for _, value := range allowedIp { 40 | config.allowedIp[value] = true 41 | } 42 | sort.Strings(config.localIp) 43 | p("Port :", config.port) 44 | p("Local IP :") 45 | for _, ip := range config.localIp { 46 | p("\t" + ip) 47 | } 48 | p("Allowed IP :") 49 | for ip, _ := range config.allowedIp { 50 | p("\t" + ip) 51 | } 52 | if len(config.localIp) == 0 { 53 | log.Fatalln("The number of localIP must be greater than 0") 54 | } 55 | } 56 | 57 | func randLocalAddr() (r *net.TCPAddr) { 58 | ip := config.localIp[rand.Int()%len(config.localIp)] 59 | r, err := net.ResolveTCPAddr("tcp4", ip+":0") 60 | Fatal(err) 61 | p("********local ip", r) 62 | return 63 | } 64 | 65 | func main() { 66 | conf := &Config{} 67 | server, err := New(conf) 68 | Fatal(err) 69 | p("proxy start") 70 | err = server.ListenAndServe("tcp", "0.0.0.0:"+config.port) 71 | Fatal(err) 72 | } 73 | -------------------------------------------------------------------------------- /request.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | const ( 13 | connectCommand = uint8(1) 14 | bindCommand = uint8(2) 15 | associateCommand = uint8(3) 16 | ipv4Address = uint8(1) 17 | fqdnAddress = uint8(3) 18 | ipv6Address = uint8(4) 19 | ) 20 | 21 | const ( 22 | successReply uint8 = iota 23 | serverFailure 24 | ruleFailure 25 | networkUnreachable 26 | hostUnreachable 27 | connectionRefused 28 | ttlExpired 29 | commandNotSupported 30 | addrTypeNotSupported 31 | ) 32 | 33 | var ( 34 | unrecognizedAddrType = fmt.Errorf("Unrecognized address type") 35 | ) 36 | 37 | // AddressRewriter is used to rewrite a destination transparently 38 | type AddressRewriter interface { 39 | Rewrite(addr *AddrSpec) *AddrSpec 40 | } 41 | 42 | // AddrSpec is used to return the target AddrSpec 43 | // which may be specified as IPv4, IPv6, or a FQDN 44 | type AddrSpec struct { 45 | FQDN string 46 | IP net.IP 47 | Port int 48 | } 49 | 50 | type conn interface { 51 | Write([]byte) (int, error) 52 | RemoteAddr() net.Addr 53 | } 54 | 55 | func (a *AddrSpec) String() string { 56 | if a.FQDN != "" { 57 | return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port) 58 | } 59 | return fmt.Sprintf("%s:%d", a.IP, a.Port) 60 | } 61 | 62 | // handleRequest is used for request processing after authentication 63 | func (s *Server) handleRequest(conn conn, bufConn io.Reader) error { 64 | // Read the version byte 65 | header := []byte{0, 0, 0} 66 | if _, err := io.ReadAtLeast(bufConn, header, 3); err != nil { 67 | return fmt.Errorf("Failed to get command version: %v", err) 68 | } 69 | 70 | // Ensure we are compatible 71 | if header[0] != socks5Version { 72 | return fmt.Errorf("Unsupported command version: %v", header[0]) 73 | } 74 | 75 | // Read in the destination address 76 | dest, err := readAddrSpec(bufConn) 77 | if err != nil { 78 | if err == unrecognizedAddrType { 79 | if err := sendReply(conn, addrTypeNotSupported, nil); err != nil { 80 | return fmt.Errorf("Failed to send reply: %v", err) 81 | } 82 | } 83 | return fmt.Errorf("Failed to read destination address: %v", err) 84 | } 85 | 86 | // Resolve the address if we have a FQDN 87 | if dest.FQDN != "" { 88 | addr, err := s.config.Resolver.Resolve(dest.FQDN) 89 | if err != nil { 90 | if err := sendReply(conn, hostUnreachable, nil); err != nil { 91 | return fmt.Errorf("Failed to send reply: %v", err) 92 | } 93 | return fmt.Errorf("Failed to resolve destination '%v': %v", dest.FQDN, err) 94 | } 95 | dest.IP = addr 96 | } 97 | 98 | // Apply any address rewrites 99 | realDest := dest 100 | if s.config.Rewriter != nil { 101 | realDest = s.config.Rewriter.Rewrite(dest) 102 | } 103 | 104 | // Switch on the command 105 | switch header[1] { 106 | case connectCommand: 107 | return s.handleConnect(conn, bufConn, dest, realDest) 108 | case bindCommand: 109 | return s.handleBind(conn, bufConn, dest, realDest) 110 | case associateCommand: 111 | return s.handleAssociate(conn, bufConn, dest, realDest) 112 | default: 113 | if err := sendReply(conn, commandNotSupported, nil); err != nil { 114 | return fmt.Errorf("Failed to send reply: %v", err) 115 | } 116 | return fmt.Errorf("Unsupported command: %v", header[1]) 117 | } 118 | } 119 | 120 | // handleConnect is used to handle a connect command 121 | func (s *Server) handleConnect(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error { 122 | // Check if this is allowed 123 | client := conn.RemoteAddr().(*net.TCPAddr) 124 | if !s.config.Rules.AllowConnect(realDest.IP, realDest.Port, client.IP, client.Port) { 125 | if err := sendReply(conn, ruleFailure, nil); err != nil { 126 | return fmt.Errorf("Failed to send reply: %v", err) 127 | } 128 | return fmt.Errorf("Connect to %v blocked by rules", dest) 129 | } 130 | 131 | // Attempt to connect 132 | addr := net.TCPAddr{IP: realDest.IP, Port: realDest.Port} 133 | target, err := net.DialTCP("tcp", randLocalAddr(), &addr) 134 | if err != nil { 135 | msg := err.Error() 136 | resp := hostUnreachable 137 | if strings.Contains(msg, "refused") { 138 | resp = connectionRefused 139 | } else if strings.Contains(msg, "network is unreachable") { 140 | resp = networkUnreachable 141 | } 142 | if err := sendReply(conn, resp, nil); err != nil { 143 | return fmt.Errorf("Failed to send reply: %v", err) 144 | } 145 | return fmt.Errorf("Connect to %v failed: %v", dest, err) 146 | } 147 | defer target.Close() 148 | 149 | // Send success 150 | local := target.LocalAddr().(*net.TCPAddr) 151 | bind := AddrSpec{IP: local.IP, Port: local.Port} 152 | if err := sendReply(conn, successReply, &bind); err != nil { 153 | return fmt.Errorf("Failed to send reply: %v", err) 154 | } 155 | 156 | // Start proxying 157 | errCh := make(chan error, 2) 158 | go proxy("target", target, bufConn, errCh) 159 | go proxy("client", conn, target, errCh) 160 | 161 | // Wait 162 | select { 163 | case e := <-errCh: 164 | return e 165 | } 166 | } 167 | 168 | // handleBind is used to handle a connect command 169 | func (s *Server) handleBind(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error { 170 | // Check if this is allowed 171 | client := conn.RemoteAddr().(*net.TCPAddr) 172 | if !s.config.Rules.AllowBind(realDest.IP, realDest.Port, client.IP, client.Port) { 173 | if err := sendReply(conn, ruleFailure, nil); err != nil { 174 | return fmt.Errorf("Failed to send reply: %v", err) 175 | } 176 | return fmt.Errorf("Bind to %v blocked by rules", dest) 177 | } 178 | 179 | // TODO: Support bind 180 | if err := sendReply(conn, commandNotSupported, nil); err != nil { 181 | return fmt.Errorf("Failed to send reply: %v", err) 182 | } 183 | return nil 184 | } 185 | 186 | // handleAssociate is used to handle a connect command 187 | func (s *Server) handleAssociate(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error { 188 | // Check if this is allowed 189 | client := conn.RemoteAddr().(*net.TCPAddr) 190 | if !s.config.Rules.AllowAssociate(realDest.IP, realDest.Port, client.IP, client.Port) { 191 | if err := sendReply(conn, ruleFailure, nil); err != nil { 192 | return fmt.Errorf("Failed to send reply: %v", err) 193 | } 194 | return fmt.Errorf("Associate to %v blocked by rules", dest) 195 | } 196 | 197 | // TODO: Support associate 198 | if err := sendReply(conn, commandNotSupported, nil); err != nil { 199 | return fmt.Errorf("Failed to send reply: %v", err) 200 | } 201 | return nil 202 | } 203 | 204 | // readAddrSpec is used to read AddrSpec. 205 | // Expects an address type byte, follwed by the address and port 206 | func readAddrSpec(r io.Reader) (*AddrSpec, error) { 207 | d := &AddrSpec{} 208 | 209 | // Get the address type 210 | addrType := []byte{0} 211 | if _, err := r.Read(addrType); err != nil { 212 | return nil, err 213 | } 214 | 215 | // Handle on a per type basis 216 | switch addrType[0] { 217 | case ipv4Address: 218 | addr := make([]byte, 4) 219 | if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil { 220 | return nil, err 221 | } 222 | d.IP = net.IP(addr) 223 | 224 | case ipv6Address: 225 | addr := make([]byte, 16) 226 | if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil { 227 | return nil, err 228 | } 229 | d.IP = net.IP(addr) 230 | 231 | case fqdnAddress: 232 | if _, err := r.Read(addrType); err != nil { 233 | return nil, err 234 | } 235 | addrLen := int(addrType[0]) 236 | fqdn := make([]byte, addrLen) 237 | if _, err := io.ReadAtLeast(r, fqdn, addrLen); err != nil { 238 | return nil, err 239 | } 240 | d.FQDN = string(fqdn) 241 | 242 | default: 243 | return nil, unrecognizedAddrType 244 | } 245 | 246 | // Read the port 247 | port := []byte{0, 0} 248 | if _, err := io.ReadAtLeast(r, port, 2); err != nil { 249 | return nil, err 250 | } 251 | d.Port = (int(port[0]) << 8) | int(port[1]) 252 | 253 | return d, nil 254 | } 255 | 256 | // sendReply is used to send a reply message 257 | func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error { 258 | // Format the address 259 | var addrType uint8 260 | var addrBody []byte 261 | var addrPort uint16 262 | switch { 263 | case addr == nil: 264 | addrType = ipv4Address 265 | addrBody = []byte{0, 0, 0, 0} 266 | addrPort = 0 267 | 268 | case addr.FQDN != "": 269 | addrType = fqdnAddress 270 | addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...) 271 | addrPort = uint16(addr.Port) 272 | 273 | case addr.IP.To4() != nil: 274 | addrType = ipv4Address 275 | addrBody = []byte(addr.IP.To4()) 276 | addrPort = uint16(addr.Port) 277 | 278 | case addr.IP.To16() != nil: 279 | addrType = ipv6Address 280 | addrBody = []byte(addr.IP.To16()) 281 | addrPort = uint16(addr.Port) 282 | 283 | default: 284 | return fmt.Errorf("Failed to format address: %v", addr) 285 | } 286 | 287 | // Format the message 288 | msg := make([]byte, 6+len(addrBody)) 289 | msg[0] = socks5Version 290 | msg[1] = resp 291 | msg[2] = 0 // Reserved 292 | msg[3] = addrType 293 | copy(msg[4:], addrBody) 294 | msg[4+len(addrBody)] = byte(addrPort >> 8) 295 | msg[4+len(addrBody)+1] = byte(addrPort & 0xff) 296 | 297 | // Send the message 298 | _, err := w.Write(msg) 299 | return err 300 | } 301 | 302 | // proxy is used to suffle data from src to destination, and sends errors 303 | // down a dedicated channel 304 | func proxy(name string, dst io.Writer, src io.Reader, errCh chan error) { 305 | // Copy 306 | n, err := io.Copy(dst, src) 307 | 308 | // Log, and sleep. This is jank but allows the otherside 309 | // to finish a pending copy 310 | log.Printf("[DEBUG] socks: Copied %d bytes to %s", n, name) 311 | time.Sleep(10 * time.Millisecond) 312 | 313 | // Send any errors 314 | errCh <- err 315 | } 316 | -------------------------------------------------------------------------------- /request_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "net" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | type MockConn struct { 13 | buf bytes.Buffer 14 | } 15 | 16 | func (m *MockConn) Write(b []byte) (int, error) { 17 | return m.buf.Write(b) 18 | } 19 | 20 | func (m *MockConn) RemoteAddr() net.Addr { 21 | return &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 65432} 22 | } 23 | 24 | func TestRequest_Connect(t *testing.T) { 25 | // Create a local listener 26 | l, err := net.Listen("tcp", "127.0.0.1:0") 27 | if err != nil { 28 | t.Fatalf("err: %v", err) 29 | } 30 | go func() { 31 | conn, err := l.Accept() 32 | if err != nil { 33 | t.Fatalf("err: %v", err) 34 | } 35 | defer conn.Close() 36 | 37 | buf := make([]byte, 4) 38 | if _, err := io.ReadAtLeast(conn, buf, 4); err != nil { 39 | t.Fatalf("err: %v", err) 40 | } 41 | 42 | if !bytes.Equal(buf, []byte("ping")) { 43 | t.Fatalf("bad: %v", buf) 44 | } 45 | conn.Write([]byte("pong")) 46 | }() 47 | lAddr := l.Addr().(*net.TCPAddr) 48 | 49 | // Make server 50 | s := &Server{config: &Config{ 51 | Rules: PermitAll(), 52 | Resolver: DNSResolver{}, 53 | }} 54 | 55 | // Create the connect request 56 | req := bytes.NewBuffer(nil) 57 | req.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1}) 58 | 59 | port := []byte{0, 0} 60 | binary.BigEndian.PutUint16(port, uint16(lAddr.Port)) 61 | req.Write(port) 62 | 63 | // Send a ping 64 | req.Write([]byte("ping")) 65 | 66 | // Handle the request 67 | resp := &MockConn{} 68 | if err := s.handleRequest(resp, req); err != nil { 69 | t.Fatalf("err: %v", err) 70 | } 71 | 72 | // Verify response 73 | out := resp.buf.Bytes() 74 | expected := []byte{ 75 | 5, 76 | 0, 77 | 0, 78 | 1, 79 | 127, 0, 0, 1, 80 | 0, 0, 81 | 'p', 'o', 'n', 'g', 82 | } 83 | 84 | // Ignore the port for both 85 | out[8] = 0 86 | out[9] = 0 87 | 88 | if !bytes.Equal(out, expected) { 89 | t.Fatalf("bad: %v %v", out, expected) 90 | } 91 | } 92 | 93 | func TestRequest_Connect_RuleFail(t *testing.T) { 94 | // Create a local listener 95 | l, err := net.Listen("tcp", "127.0.0.1:0") 96 | if err != nil { 97 | t.Fatalf("err: %v", err) 98 | } 99 | go func() { 100 | conn, err := l.Accept() 101 | if err != nil { 102 | t.Fatalf("err: %v", err) 103 | } 104 | defer conn.Close() 105 | 106 | buf := make([]byte, 4) 107 | if _, err := io.ReadAtLeast(conn, buf, 4); err != nil { 108 | t.Fatalf("err: %v", err) 109 | } 110 | 111 | if !bytes.Equal(buf, []byte("ping")) { 112 | t.Fatalf("bad: %v", buf) 113 | } 114 | conn.Write([]byte("pong")) 115 | }() 116 | lAddr := l.Addr().(*net.TCPAddr) 117 | 118 | // Make server 119 | s := &Server{config: &Config{ 120 | Rules: PermitNone(), 121 | Resolver: DNSResolver{}, 122 | }} 123 | 124 | // Create the connect request 125 | req := bytes.NewBuffer(nil) 126 | req.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1}) 127 | 128 | port := []byte{0, 0} 129 | binary.BigEndian.PutUint16(port, uint16(lAddr.Port)) 130 | req.Write(port) 131 | 132 | // Send a ping 133 | req.Write([]byte("ping")) 134 | 135 | // Handle the request 136 | resp := &MockConn{} 137 | if err := s.handleRequest(resp, req); !strings.Contains(err.Error(), "blocked by rules") { 138 | t.Fatalf("err: %v", err) 139 | } 140 | 141 | // Verify response 142 | out := resp.buf.Bytes() 143 | expected := []byte{ 144 | 5, 145 | 2, 146 | 0, 147 | 1, 148 | 0, 0, 0, 0, 149 | 0, 0, 150 | } 151 | 152 | if !bytes.Equal(out, expected) { 153 | t.Fatalf("bad: %v %v", out, expected) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /resolver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // NameResolver is used to implement custom name resolution 8 | type NameResolver interface { 9 | Resolve(name string) (net.IP, error) 10 | } 11 | 12 | // DNSResolver uses the system DNS to resolve host names 13 | type DNSResolver struct{} 14 | 15 | func (d DNSResolver) Resolve(name string) (net.IP, error) { 16 | addr, err := net.ResolveIPAddr("ip", name) 17 | if err != nil { 18 | return nil, err 19 | } 20 | return addr.IP, err 21 | } 22 | -------------------------------------------------------------------------------- /resolver_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDNSResolver(t *testing.T) { 8 | d := DNSResolver{} 9 | 10 | addr, err := d.Resolve("localhost") 11 | if err != nil { 12 | t.Fatalf("err: %v", err) 13 | } 14 | 15 | if !addr.IsLoopback() { 16 | t.Fatalf("expected loopback") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ruleset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // RuleSet is used to provide custom rules to allow or prohibit actions 8 | type RuleSet interface { 9 | // AllowConnect is used to filter connect requests 10 | AllowConnect(dstIP net.IP, dstPort int, srcIP net.IP, srcPort int) bool 11 | 12 | // AllowBind is used to filter bind requests 13 | AllowBind(dstIP net.IP, dstPort int, srcIP net.IP, srcPort int) bool 14 | 15 | // AllowAssociate is used to filter associate requests 16 | AllowAssociate(dstIP net.IP, dstPort int, srcIP net.IP, srcPort int) bool 17 | } 18 | 19 | // PermitAll returns a RuleSet which allows all types of connections 20 | func PermitAll() RuleSet { 21 | return &PermitCommand{true, true, true} 22 | } 23 | 24 | // PermitNone returns a RuleSet which disallows all types of connections 25 | func PermitNone() RuleSet { 26 | return &PermitCommand{false, false, false} 27 | } 28 | 29 | // PermitCommand is an implementation of the RuleSet which 30 | // enables filtering supported commands 31 | type PermitCommand struct { 32 | EnableConnect bool 33 | EnableBind bool 34 | EnableAssociate bool 35 | } 36 | 37 | func (p *PermitCommand) AllowConnect(net.IP, int, net.IP, int) bool { 38 | return p.EnableConnect 39 | } 40 | 41 | func (p *PermitCommand) AllowBind(net.IP, int, net.IP, int) bool { 42 | return p.EnableBind 43 | } 44 | 45 | func (p *PermitCommand) AllowAssociate(net.IP, int, net.IP, int) bool { 46 | return p.EnableAssociate 47 | } 48 | -------------------------------------------------------------------------------- /ruleset_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPermitCommand(t *testing.T) { 8 | r := &PermitCommand{true, false, false} 9 | 10 | if !r.AllowConnect(nil, 500, nil, 1000) { 11 | t.Fatalf("expect connect") 12 | } 13 | 14 | if r.AllowBind(nil, 500, nil, 1000) { 15 | t.Fatalf("do not expect bind") 16 | } 17 | 18 | if r.AllowAssociate(nil, 500, nil, 1000) { 19 | t.Fatalf("do not expect associate") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /screenshoot/psb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstat/go-multi-IP-acceleration/7e037ad980173171b4563f6dff0e031390d9257f/screenshoot/psb.png -------------------------------------------------------------------------------- /socks5.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "net" 8 | ) 9 | 10 | const ( 11 | socks5Version = uint8(5) 12 | ) 13 | 14 | // Config is used to setup and configure a Server 15 | type Config struct { 16 | // AuthMethods can be provided to implement custom authentication 17 | // By default, "auth-less" mode is enabled. 18 | // For password-based auth use UserPassAuthenticator. 19 | AuthMethods []Authenticator 20 | 21 | // If provided, username/password authentication is enabled, 22 | // by appending a UserPassAuthenticator to AuthMethods. If not provided, 23 | // and AUthMethods is nil, then "auth-less" mode is enabled. 24 | Credentials CredentialStore 25 | 26 | // Resolver can be provided to do custom name resolution. 27 | // Defaults to DNSResolver if not provided. 28 | Resolver NameResolver 29 | 30 | // Rules is provided to enable custom logic around permitting 31 | // various commands. If not provided, PermitAll is used. 32 | Rules RuleSet 33 | 34 | // Rewriter can be used to transparently rewrite addresses. 35 | // This is invoked before the RuleSet is invoked. 36 | // Defaults to NoRewrite. 37 | Rewriter AddressRewriter 38 | 39 | // BindIP is used for bind or udp associate 40 | BindIP net.IP 41 | } 42 | 43 | // Server is reponsible for accepting connections and handling 44 | // the details of the SOCKS5 protocol 45 | type Server struct { 46 | config *Config 47 | authMethods map[uint8]Authenticator 48 | } 49 | 50 | // New creates a new Server and potentially returns an error 51 | func New(conf *Config) (*Server, error) { 52 | // Ensure we have at least one authentication method enabled 53 | if len(conf.AuthMethods) == 0 { 54 | if conf.Credentials != nil { 55 | conf.AuthMethods = []Authenticator{&UserPassAuthenticator{conf.Credentials}} 56 | } else { 57 | conf.AuthMethods = []Authenticator{&NoAuthAuthenticator{}} 58 | } 59 | } 60 | 61 | // Ensure we have a DNS resolver 62 | if conf.Resolver == nil { 63 | conf.Resolver = DNSResolver{} 64 | } 65 | 66 | // Ensure we have a rule set 67 | if conf.Rules == nil { 68 | conf.Rules = PermitAll() 69 | } 70 | 71 | server := &Server{ 72 | config: conf, 73 | } 74 | 75 | server.authMethods = make(map[uint8]Authenticator) 76 | 77 | for _, a := range conf.AuthMethods { 78 | server.authMethods[a.GetCode()] = a 79 | } 80 | 81 | return server, nil 82 | } 83 | 84 | // ListenAndServe is used to create a listener and serve on it 85 | func (s *Server) ListenAndServe(network, addr string) error { 86 | l, err := net.Listen(network, addr) 87 | if err != nil { 88 | return err 89 | } 90 | return s.Serve(l) 91 | } 92 | 93 | // Serve is used to serve connections from a listener 94 | func (s *Server) Serve(l net.Listener) error { 95 | for { 96 | conn, err := l.Accept() 97 | if err != nil { 98 | return err 99 | } 100 | go s.ServeConn(conn) 101 | } 102 | return nil 103 | } 104 | 105 | // ServeConn is used to serve a single connection. 106 | func (s *Server) ServeConn(conn net.Conn) error { 107 | defer conn.Close() 108 | bufConn := bufio.NewReader(conn) 109 | 110 | // Read the version byte 111 | version := []byte{0} 112 | if _, err := bufConn.Read(version); err != nil { 113 | log.Printf("[ERR] socks: Failed to get version byte: %v", err) 114 | return err 115 | } 116 | 117 | // Ensure we are compatible 118 | if version[0] != socks5Version { 119 | err := fmt.Errorf("Unsupported SOCKS version: %v", version) 120 | log.Printf("[ERR] socks: %v", err) 121 | return err 122 | } 123 | 124 | // Authenticate the connection 125 | if err := s.authenticate(conn, bufConn); err != nil { 126 | err = fmt.Errorf("Failed to authenticate: %v", err) 127 | log.Printf("[ERR] socks: %v", err) 128 | return err 129 | } 130 | 131 | // Process the client request 132 | if err := s.handleRequest(conn, bufConn); err != nil { 133 | err = fmt.Errorf("Failed to handle request: %v", err) 134 | log.Printf("[ERR] socks: %v", err) 135 | return err 136 | } 137 | 138 | return nil 139 | } 140 | -------------------------------------------------------------------------------- /socks5_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "io" 7 | "net" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestSOCKS5_Connect(t *testing.T) { 13 | // Create a local listener 14 | l, err := net.Listen("tcp", "127.0.0.1:0") 15 | if err != nil { 16 | t.Fatalf("err: %v", err) 17 | } 18 | go func() { 19 | conn, err := l.Accept() 20 | if err != nil { 21 | t.Fatalf("err: %v", err) 22 | } 23 | defer conn.Close() 24 | 25 | buf := make([]byte, 4) 26 | if _, err := io.ReadAtLeast(conn, buf, 4); err != nil { 27 | t.Fatalf("err: %v", err) 28 | } 29 | 30 | if !bytes.Equal(buf, []byte("ping")) { 31 | t.Fatalf("bad: %v", buf) 32 | } 33 | conn.Write([]byte("pong")) 34 | }() 35 | lAddr := l.Addr().(*net.TCPAddr) 36 | 37 | // Create a socks server 38 | creds := StaticCredentials{ 39 | "foo": "bar", 40 | } 41 | cator := UserPassAuthenticator{Credentials: creds} 42 | conf := &Config{ 43 | AuthMethods: []Authenticator{cator}, 44 | } 45 | serv, err := New(conf) 46 | if err != nil { 47 | t.Fatalf("err: %v", err) 48 | } 49 | 50 | // Start listening 51 | go func() { 52 | if err := serv.ListenAndServe("tcp", "127.0.0.1:12365"); err != nil { 53 | t.Fatalf("err: %v", err) 54 | } 55 | }() 56 | time.Sleep(10 * time.Millisecond) 57 | 58 | // Get a local conn 59 | conn, err := net.Dial("tcp", "127.0.0.1:12365") 60 | if err != nil { 61 | t.Fatalf("err: %v", err) 62 | } 63 | 64 | // Connect, auth and connec to local 65 | req := bytes.NewBuffer(nil) 66 | req.Write([]byte{5}) 67 | req.Write([]byte{2, noAuth, userPassAuth}) 68 | req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'}) 69 | req.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1}) 70 | 71 | port := []byte{0, 0} 72 | binary.BigEndian.PutUint16(port, uint16(lAddr.Port)) 73 | req.Write(port) 74 | 75 | // Send a ping 76 | req.Write([]byte("ping")) 77 | 78 | // Send all the bytes 79 | conn.Write(req.Bytes()) 80 | 81 | // Verify response 82 | expected := []byte{ 83 | socks5Version, userPassAuth, 84 | 1, authSuccess, 85 | 5, 86 | 0, 87 | 0, 88 | 1, 89 | 127, 0, 0, 1, 90 | 0, 0, 91 | 'p', 'o', 'n', 'g', 92 | } 93 | out := make([]byte, len(expected)) 94 | 95 | conn.SetDeadline(time.Now().Add(time.Second)) 96 | if _, err := io.ReadAtLeast(conn, out, len(out)); err != nil { 97 | t.Fatalf("err: %v", err) 98 | } 99 | 100 | // Ignore the port 101 | out[12] = 0 102 | out[13] = 0 103 | 104 | if !bytes.Equal(out, expected) { 105 | t.Fatalf("bad: %v", out) 106 | } 107 | } 108 | --------------------------------------------------------------------------------