mirror of
				https://github.com/nadoo/glider.git
				synced 2025-11-04 07:42:38 +08:00 
			
		
		
		
	socks5: support auth in server mode. #84
This commit is contained in:
		
							parent
							
								
									822693b05b
								
							
						
					
					
						commit
						2560f6727f
					
				@ -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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								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=
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								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
 | 
			
		||||
 | 
			
		||||
@ -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")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user