diff --git a/README.md b/README.md index 76739bf..1f780f1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Forward (local proxy client/upstream proxy server): DNS Forwarding Server (udp2tcp): +- DNS Over Proxy - Listen on UDP and forward dns requests to remote dns server in TCP via forwarders - Specify different upstream dns server based on destinations(in rule file) - Tunnel mode: forward to a fixed upstream dns server diff --git a/go.mod b/go.mod index 2feb293..567b0ab 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,8 @@ require ( github.com/xtaci/kcp-go v5.4.11+incompatible github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 - golang.org/x/net v0.0.0-20191011234655-491137f69257 // indirect + golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect + golang.org/x/sys v0.0.0-20191018095205-727590c5006e // indirect ) // Replace dependency modules with local developing copy diff --git a/go.sum b/go.sum index d82ae79..0e4d449 100644 --- a/go.sum +++ b/go.sum @@ -37,11 +37,13 @@ golang.org/x/crypto v0.0.0-20191010185427-af544f31c8ac/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20191011234655-491137f69257 h1:ry8e2D+cwaV6hk7lb3aRTjjZo24shrbK0e11QEOkTIg= -golang.org/x/net v0.0.0-20191011234655-491137f69257/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA= +golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191018095205-727590c5006e h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ= +golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go index 29c808e..4a9e7a4 100644 --- a/main.go +++ b/main.go @@ -29,7 +29,7 @@ import ( _ "github.com/nadoo/glider/proxy/ws" ) -var version = "0.8.3" +var version = "0.9.0" func main() { // read configs diff --git a/proxy/http/http.go b/proxy/http/http.go index 3fa08d0..5211e60 100644 --- a/proxy/http/http.go +++ b/proxy/http/http.go @@ -1,6 +1,7 @@ -// http proxy +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages // NOTE: never keep-alive so the implementation can be much easier. +// Package http implements a http proxy. package http import ( @@ -107,7 +108,7 @@ func (s *HTTP) Serve(c net.Conn) { reqR := bufio.NewReader(c) reqTP := textproto.NewReader(reqR) - method, requestURI, proto, ok := parseFirstLine(reqTP) + method, requestURI, proto, ok := parseStartLine(reqTP) if !ok { return } @@ -139,7 +140,7 @@ func (s *HTTP) Serve(c net.Conn) { return } - var tgt = u.Host + tgt := u.Host if !strings.Contains(u.Host, ":") { tgt += ":80" } @@ -157,12 +158,12 @@ func (s *HTTP) Serve(c net.Conn) { u.Scheme, u.Host = "", "" uri := u.String() - var reqBuf bytes.Buffer - writeFirstLine(&reqBuf, method, uri, proto) - writeHeaders(&reqBuf, reqHeader) + var buf bytes.Buffer + writeStartLine(&buf, method, uri, proto) + writeHeaders(&buf, reqHeader) // send request to remote server - rc.Write(reqBuf.Bytes()) + rc.Write(buf.Bytes()) // copy the left request bytes to remote server. eg. length specificed or chunked body go func() { @@ -175,7 +176,7 @@ func (s *HTTP) Serve(c net.Conn) { respR := bufio.NewReader(rc) respTP := textproto.NewReader(respR) - proto, code, status, ok := parseFirstLine(respTP) + proto, code, status, ok := parseStartLine(respTP) if !ok { return } @@ -189,12 +190,12 @@ func (s *HTTP) Serve(c net.Conn) { respHeader.Set("Proxy-Connection", "close") respHeader.Set("Connection", "close") - var respBuf bytes.Buffer - writeFirstLine(&respBuf, proto, code, status) - writeHeaders(&respBuf, respHeader) + buf.Reset() + writeStartLine(&buf, proto, code, status) + writeHeaders(&buf, respHeader) log.F("[http] %s <-> %s via %s", c.RemoteAddr(), tgt, p) - c.Write(respBuf.Bytes()) + c.Write(buf.Bytes()) io.Copy(c, respR) } @@ -256,7 +257,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) { c := conn.NewConn(rc) tpr := textproto.NewReader(c.Reader()) - _, code, _, ok := parseFirstLine(tpr) + _, code, _, ok := parseStartLine(tpr) if ok && code == "200" { tpr.ReadMIMEHeader() return c, err @@ -276,8 +277,8 @@ func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Add return nil, nil, errors.New("http client does not support udp") } -// parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts. -func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) { +// parseStartLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts. +func parseStartLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) { line, err := tp.ReadLine() if err != nil { return @@ -304,7 +305,7 @@ func cleanHeaders(header textproto.MIMEHeader) { header.Del("Upgrade") } -func writeFirstLine(buf *bytes.Buffer, s1, s2, s3 string) { +func writeStartLine(buf *bytes.Buffer, s1, s2, s3 string) { buf.WriteString(s1 + " " + s2 + " " + s3 + "\r\n") } diff --git a/proxy/socks5/socks5.go b/proxy/socks5/socks5.go index 0de87f9..39d3c02 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -6,9 +6,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// socks5 server: -// https://github.com/shadowsocks/go-shadowsocks2/tree/master/socks - // Package socks5 implements a socks5 proxy. package socks5 @@ -129,7 +126,7 @@ func (s *Socks5) Serve(c net.Conn) { } } - log.F("[socks5] failed to get target address: %v", err) + log.F("[socks5] failed in handshake: %v", err) return } @@ -253,7 +250,7 @@ func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A } // send VER, NMETHODS, METHODS - c.Write([]byte{5, 1, 0}) + c.Write([]byte{Version, 1, 0}) buf := make([]byte, socks.MaxAddrLen) // read VER METHOD @@ -263,7 +260,7 @@ func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A dstAddr := socks.ParseAddr(addr) // write VER CMD RSV ATYP DST.ADDR DST.PORT - c.Write(append([]byte{5, socks.CmdUDPAssociate, 0}, dstAddr...)) + c.Write(append([]byte{Version, socks.CmdUDPAssociate, 0}, dstAddr...)) // read VER REP RSV ATYP BND.ADDR BND.PORT if _, err := io.ReadFull(c, buf[:3]); err != nil { @@ -325,7 +322,7 @@ func (s *Socks5) connect(conn net.Conn, target string) error { if _, err := io.ReadFull(conn, buf[:2]); err != nil { return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } - if buf[0] != 5 { + if buf[0] != Version { return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) } if buf[1] == 0xff { @@ -432,14 +429,73 @@ func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) { if _, err := io.ReadFull(rw, buf[:2]); err != nil { return nil, err } + nmethods := buf[1] if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil { return nil, err } + // write VER METHOD - if _, err := rw.Write([]byte{5, 0}); err != nil { + if s.user != "" && s.password != "" { + _, err := rw.Write([]byte{Version, socks.AuthPassword}) + if err != nil { + return nil, err + } + + _, err = io.ReadFull(rw, buf[:2]) + if err != nil { + return nil, err + } + + // Get username + userLen := int(buf[1]) + if userLen <= 0 { + rw.Write([]byte{1, 1}) + return nil, errors.New("auth failed: wrong username length") + } + + if _, err := io.ReadFull(rw, buf[:userLen]); err != nil { + return nil, errors.New("auth failed: cannot get username") + } + user := string(buf[:userLen]) + + // Get password + _, err = rw.Read(buf[:1]) + if err != nil { + return nil, errors.New("auth failed: cannot get password len") + } + + passLen := int(buf[0]) + if passLen <= 0 { + rw.Write([]byte{1, 1}) + return nil, errors.New("auth failed: wrong password length") + } + + _, err = io.ReadFull(rw, buf[:passLen]) + if err != nil { + return nil, errors.New("auth failed: cannot get password") + } + pass := string(buf[:passLen]) + + // Verify + if user != s.user || pass != s.password { + _, err = rw.Write([]byte{1, 1}) + if err != nil { + return nil, err + } + return nil, errors.New("auth failed") + } + + // Response auth state + _, err = rw.Write([]byte{1, 0}) + if err != nil { + return nil, err + } + + } else if _, err := rw.Write([]byte{Version, socks.AuthNone}); err != nil { return nil, err } + // read VER CMD RSV ATYP DST.ADDR DST.PORT if _, err := io.ReadFull(rw, buf[:3]); err != nil { return nil, err