diff --git a/README.md b/README.md index 0df2fae..cd6ee3d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ gost - GO Simple Tunnel ### GO语言实现的安全隧道 #### 特性 -1. 支持设置上层代理(客户端,服务器端均可)。 +1. 支持设置上层代理(客户端,服务器端均可),支持上层代理认证。 2. 客户端可用作http(s), socks5代理。 3. 服务器端兼容标准的socks5协议, 可直接用作socks5代理, 并额外增加协商加密功能。 4. Tunnel UDP over TCP, UDP数据包使用TCP通道传输,以解决防火墙的限制。 @@ -16,15 +16,19 @@ gost - GO Simple Tunnel Google讨论组: https://groups.google.com/d/forum/go-gost #### 版本更新 +##### v1.7 +* 支持认证功能,当作为http(s)代理时使用Basic Auth认证方式,当作为标准socks5代理时使用Username/Password认证方式 +###### Bug fix: +* 修正当作为http代理时,POST请求出错问题 + ##### v1.6 * 增加tls-auth加密方式,此方式必须设置认证密码(-p参数),原tls加密方式与v1.3版以前兼容 - ###### Bug fix: * 修正当不设置上层代理时,连接出错问题 ##### v1.5 * 支持设置上层socks5代理(注: http tunnel不支持) -* 支持上层代理用户名密码验证 +* 支持上层代理认证 ##### V1.4 * 支持http tunnel(-http参数),使用http协议来传输数据(注: 效率低,非特殊情况下,不推荐使用)。 @@ -73,6 +77,13 @@ Google讨论组: https://groups.google.com/d/forum/go-gost * 客户端: `gost -L=:8899 -S=server_ip:8080` * 服务器: `gost -L=:8080` +##### 设置认证信息 +* 客户端: `gost -L=admin:123456@:8899 -S=server_ip:8080` +* 服务器: `gost -L=admin:123456@:8080` + +注:当服务器端设置了认证,默认的无加密模式(-m为空)不可用, +即客户端或者使用认证方式(标准socks5模式),或者设置加密方式(gost兼容模式)。 + ##### 设置加密 * 客户端: `gost -L=:8899 -S=server_ip:8080 -m=rc4-md5 -p=123456` * 服务器: `gost -L=:8080 -m=rc4-md5 -p=123456` diff --git a/client.go b/client.go index 176fdf2..37c6cc8 100644 --- a/client.go +++ b/client.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "crypto/tls" + "encoding/base64" "encoding/binary" "errors" "fmt" @@ -162,21 +163,8 @@ func cliHandle(conn net.Conn) { handleSocks5(conn, methods) return } - log.Println(string(b[:n])) - for { - if bytes.HasSuffix(b[:n], []byte("\r\n\r\n")) { - break - } - nn, err := conn.Read(b[n:]) - if err != nil { - log.Println(err) - return - } - n += nn - } - - req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(b[:n]))) + req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn))) if err != nil { log.Println(err) return @@ -202,7 +190,7 @@ func selectMethod(conn net.Conn, methods ...uint8) error { return err } - log.Println(m) + //log.Println(m) switch m { case gosocks5.MethodUserPass: @@ -333,20 +321,42 @@ func cliTunnelUDP(uconn *net.UDPConn, sconn net.Conn) { } func clientHttpAuth(req *http.Request, conn net.Conn, username, password string) error { - u, p, ok := req.BasicAuth() + u, p, ok := proxyBasicAuth(req.Header.Get("Proxy-Authorization")) + req.Header.Del("Proxy-Authorization") if !ok || (len(username) > 0 && u != username) || (len(password) > 0 && p != password) { - conn.Write([]byte("HTTP/1.1 401 Not Authorized\r\n" + - "WWW-Authenticate: Basic realm=\"Authorization Required\"\r\n" + + conn.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\n" + + "Proxy-Authenticate: Basic realm=\"gost\"\r\n" + "Proxy-Agent: gost/" + Version + "\r\n\r\n")) - return errors.New("Not Authorized") + return errors.New("Proxy Authentication Required") } return nil } +func proxyBasicAuth(auth string) (username, password string, ok bool) { + if auth == "" { + return + } + + if !strings.HasPrefix(auth, "Basic ") { + return + } + c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic ")) + if err != nil { + return + } + cs := string(c) + s := strings.IndexByte(cs, ':') + if s < 0 { + return + } + + return cs[:s], cs[s+1:], true +} + func handleHttp(req *http.Request, conn net.Conn) { var host string var port uint16 @@ -523,3 +533,25 @@ func getShadowRequest(conn net.Conn) (addr *gosocks5.Addr, extra []byte, err err return } + +type reqReader struct { + b []byte + r io.Reader +} + +func newReqReader(b []byte, r io.Reader) *reqReader { + return &reqReader{ + b: b, + r: r, + } +} + +func (r *reqReader) Read(p []byte) (n int, err error) { + if len(r.b) == 0 { + return r.r.Read(p) + } + n = copy(p, r.b) + r.b = r.b[n:] + + return +} diff --git a/socks5.go b/socks5.go index 95e0429..8d3beee 100644 --- a/socks5.go +++ b/socks5.go @@ -94,7 +94,7 @@ func (s *Socks5Server) ListenAndServe() error { } func serverSelectMethod(methods ...uint8) uint8 { - log.Println(methods) + //log.Println(methods) m := gosocks5.MethodNoAuth for _, method := range methods { @@ -116,7 +116,7 @@ func serverSelectMethod(methods ...uint8) uint8 { } func serverMethodSelected(method uint8, conn net.Conn) (net.Conn, error) { - log.Println(method) + //log.Println(method) switch method { case gosocks5.MethodUserPass: var username, password string