socks5: fix an issue in udp handling with auth (#219)

This commit is contained in:
nadoo 2021-02-12 22:11:07 +08:00
parent d2268b623f
commit fbf694f5cd
5 changed files with 68 additions and 115 deletions

View File

@ -6,18 +6,14 @@ jobs:
test: test:
name: Test name: Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
go-version: [ '1.16.0-rc1' ]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
stable: '!contains(${{ matrix.go-version }}, "beta") && !contains(${{ matrix.go-version }}, "rc")' stable: false
go-version: ${{ matrix.go-version }} go-version: 1.16.0-rc1
- name: Go Env - name: Go Env
run: go env run: go env
- name: Test - name: Test
@ -27,18 +23,14 @@ jobs:
name: Build name: Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [test] needs: [test]
strategy:
matrix:
go-version: [ '1.16.0-rc1' ]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
stable: '!contains(${{ matrix.go-version }}, "beta") && !contains(${{ matrix.go-version }}, "rc")' stable: false
go-version: ${{ matrix.go-version }} go-version: 1.16.0-rc1
- name: Go Env - name: Go Env
run: go env run: go env
- name: Build - name: Build

View File

@ -16,7 +16,8 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.16.x stable: false
go-version: 1.16.0-rc1
- name: Go Env - name: Go Env
run: go env run: go env
- name: Run GoReleaser - name: Run GoReleaser

View File

@ -27,6 +27,21 @@ func (s *Socks5) Addr() string {
// Dial connects to the address addr on the network net via the SOCKS5 proxy. // Dial connects to the address addr on the network net via the SOCKS5 proxy.
func (s *Socks5) Dial(network, addr string) (net.Conn, error) { func (s *Socks5) Dial(network, addr string) (net.Conn, error) {
c, err := s.dial(network, s.addr)
if err != nil {
log.F("[socks5]: dial to %s error: %s", s.addr, err)
return nil, err
}
if _, err := s.connect(c, addr, socks.CmdConnect); err != nil {
c.Close()
return nil, err
}
return c, nil
}
func (s *Socks5) dial(network, addr string) (net.Conn, error) {
switch network { switch network {
case "tcp", "tcp6", "tcp4": case "tcp", "tcp6", "tcp4":
default: default:
@ -39,53 +54,26 @@ func (s *Socks5) Dial(network, addr string) (net.Conn, error) {
return nil, err return nil, err
} }
if err := s.connect(c, addr); err != nil {
c.Close()
return nil, err
}
return c, nil return c, nil
} }
// DialUDP connects to the given address via the proxy. // DialUDP connects to the given address via the proxy.
func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
c, err := s.dialer.Dial("tcp", s.addr) c, err := s.dial("tcp", s.addr)
if err != nil { if err != nil {
log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err) log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err)
return nil, nil, err return nil, nil, err
} }
// send VER, NMETHODS, METHODS var uAddr socks.Addr
c.Write([]byte{Version, 1, 0}) if uAddr, err = s.connect(c, addr, socks.CmdUDPAssociate); err != nil {
c.Close()
return nil, nil, err
}
buf := pool.GetBuffer(socks.MaxAddrLen) buf := pool.GetBuffer(socks.MaxAddrLen)
defer pool.PutBuffer(buf) defer pool.PutBuffer(buf)
// read VER METHOD
if _, err := io.ReadFull(c, buf[:2]); err != nil {
return nil, nil, err
}
dstAddr := socks.ParseAddr(addr)
// write VER CMD RSV ATYP DST.ADDR DST.PORT
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 {
return nil, nil, err
}
rep := buf[1]
if rep != 0 {
log.F("[socks5] server reply: %d, not succeeded", rep)
return nil, nil, errors.New("server connect failed")
}
uAddr, err := socks.ReadAddrBuf(c, buf)
if err != nil {
return nil, nil, err
}
var uAddress string var uAddress string
h, p, _ := net.SplitHostPort(uAddr.String()) h, p, _ := net.SplitHostPort(uAddr.String())
// if returned bind ip is unspecified // if returned bind ip is unspecified
@ -103,25 +91,25 @@ func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A
return nil, nil, err return nil, nil, err
} }
pkc := NewPktConn(pc, nextHop, dstAddr, true, c) pkc := NewPktConn(pc, nextHop, socks.ParseAddr(addr), true, c)
return pkc, nextHop, err return pkc, nextHop, err
} }
// connect takes an existing connection to a socks5 proxy server, // connect takes an existing connection to a socks5 proxy server,
// and commands the server to extend that connection to target, // and commands the server to extend that connection to target,
// which must be a canonical address with a host and port. // which must be a canonical address with a host and port.
func (s *Socks5) connect(conn net.Conn, target string) error { func (s *Socks5) connect(conn net.Conn, target string, cmd byte) (addr socks.Addr, err error) {
host, portStr, err := net.SplitHostPort(target) host, portStr, err := net.SplitHostPort(target)
if err != nil { if err != nil {
return err return
} }
port, err := strconv.Atoi(portStr) port, err := strconv.Atoi(portStr)
if err != nil { if err != nil {
return errors.New("proxy: failed to parse port number: " + portStr) return addr, errors.New("proxy: failed to parse port number: " + portStr)
} }
if port < 1 || port > 0xffff { if port < 1 || port > 0xffff {
return errors.New("proxy: port number out of range: " + portStr) return addr, errors.New("proxy: port number out of range: " + portStr)
} }
// the size here is just an estimate // the size here is just an estimate
@ -135,17 +123,17 @@ func (s *Socks5) connect(conn net.Conn, target string) error {
} }
if _, err := conn.Write(buf); err != nil { if _, err := conn.Write(buf); err != nil {
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) return addr, errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
} }
if _, err := io.ReadFull(conn, buf[:2]); err != nil { 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()) return addr, errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
} }
if buf[0] != Version { if buf[0] != Version {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) return addr, errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
} }
if buf[1] == 0xff { if buf[1] == 0xff {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") return addr, errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
} }
if buf[1] == socks.AuthPassword { if buf[1] == socks.AuthPassword {
@ -157,20 +145,20 @@ func (s *Socks5) connect(conn net.Conn, target string) error {
buf = append(buf, s.password...) buf = append(buf, s.password...)
if _, err := conn.Write(buf); err != nil { if _, err := conn.Write(buf); err != nil {
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) return addr, errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
} }
if _, err := io.ReadFull(conn, buf[:2]); err != nil { if _, err := io.ReadFull(conn, buf[:2]); err != nil {
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) return addr, errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
} }
if buf[1] != 0 { if buf[1] != 0 {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") return addr, errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
} }
} }
buf = buf[:0] buf = buf[:0]
buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */) buf = append(buf, Version, cmd, 0 /* reserved */)
if ip := net.ParseIP(host); ip != nil { if ip := net.ParseIP(host); ip != nil {
if ip4 := ip.To4(); ip4 != nil { if ip4 := ip.To4(); ip4 != nil {
@ -182,7 +170,7 @@ func (s *Socks5) connect(conn net.Conn, target string) error {
buf = append(buf, ip...) buf = append(buf, ip...)
} else { } else {
if len(host) > 255 { if len(host) > 255 {
return errors.New("proxy: destination hostname too long: " + host) return addr, errors.New("proxy: destination hostname too long: " + host)
} }
buf = append(buf, socks.ATypDomain) buf = append(buf, socks.ATypDomain)
buf = append(buf, byte(len(host))) buf = append(buf, byte(len(host)))
@ -191,11 +179,12 @@ func (s *Socks5) connect(conn net.Conn, target string) error {
buf = append(buf, byte(port>>8), byte(port)) buf = append(buf, byte(port>>8), byte(port))
if _, err := conn.Write(buf); err != nil { if _, err := conn.Write(buf); err != nil {
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) return addr, errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
} }
if _, err := io.ReadFull(conn, buf[:4]); err != nil { // read VER REP RSV
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) if _, err := io.ReadFull(conn, buf[:3]); err != nil {
return addr, errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
} }
failure := "unknown error" failure := "unknown error"
@ -204,38 +193,8 @@ func (s *Socks5) connect(conn net.Conn, target string) error {
} }
if len(failure) > 0 { if len(failure) > 0 {
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) return addr, errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
} }
bytesToDiscard := 0 return socks.ReadAddr(conn)
switch buf[3] {
case socks.ATypIP4:
bytesToDiscard = net.IPv4len
case socks.ATypIP6:
bytesToDiscard = net.IPv6len
case socks.ATypDomain:
_, err := io.ReadFull(conn, buf[:1])
if err != nil {
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
bytesToDiscard = int(buf[0])
default:
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
}
if cap(buf) < bytesToDiscard {
buf = make([]byte, bytesToDiscard)
} else {
buf = buf[:bytesToDiscard]
}
if _, err := io.ReadFull(conn, buf); err != nil {
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
// Also need to discard the port number
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
}
return nil
} }

View File

@ -157,27 +157,29 @@ func (s *Socks5) ListenAndServeUDP() {
} }
// Handshake fast-tracks SOCKS initialization to get target address to connect. // Handshake fast-tracks SOCKS initialization to get target address to connect.
func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) { func (s *Socks5) handshake(c net.Conn) (socks.Addr, error) {
// Read RFC 1928 for request and reply structure and sizes // Read RFC 1928 for request and reply structure and sizes
buf := make([]byte, socks.MaxAddrLen) buf := pool.GetBuffer(socks.MaxAddrLen)
defer pool.PutBuffer(buf)
// read VER, NMETHODS, METHODS // read VER, NMETHODS, METHODS
if _, err := io.ReadFull(rw, buf[:2]); err != nil { if _, err := io.ReadFull(c, buf[:2]); err != nil {
return nil, err return nil, err
} }
nmethods := buf[1] nmethods := buf[1]
if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil { if _, err := io.ReadFull(c, buf[:nmethods]); err != nil {
return nil, err return nil, err
} }
// write VER METHOD // write VER METHOD
if s.user != "" && s.password != "" { if s.user != "" && s.password != "" {
_, err := rw.Write([]byte{Version, socks.AuthPassword}) _, err := c.Write([]byte{Version, socks.AuthPassword})
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, err = io.ReadFull(rw, buf[:2]) _, err = io.ReadFull(c, buf[:2])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -185,28 +187,28 @@ func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) {
// Get username // Get username
userLen := int(buf[1]) userLen := int(buf[1])
if userLen <= 0 { if userLen <= 0 {
rw.Write([]byte{1, 1}) c.Write([]byte{1, 1})
return nil, errors.New("auth failed: wrong username length") return nil, errors.New("auth failed: wrong username length")
} }
if _, err := io.ReadFull(rw, buf[:userLen]); err != nil { if _, err := io.ReadFull(c, buf[:userLen]); err != nil {
return nil, errors.New("auth failed: cannot get username") return nil, errors.New("auth failed: cannot get username")
} }
user := string(buf[:userLen]) user := string(buf[:userLen])
// Get password // Get password
_, err = rw.Read(buf[:1]) _, err = c.Read(buf[:1])
if err != nil { if err != nil {
return nil, errors.New("auth failed: cannot get password len") return nil, errors.New("auth failed: cannot get password len")
} }
passLen := int(buf[0]) passLen := int(buf[0])
if passLen <= 0 { if passLen <= 0 {
rw.Write([]byte{1, 1}) c.Write([]byte{1, 1})
return nil, errors.New("auth failed: wrong password length") return nil, errors.New("auth failed: wrong password length")
} }
_, err = io.ReadFull(rw, buf[:passLen]) _, err = io.ReadFull(c, buf[:passLen])
if err != nil { if err != nil {
return nil, errors.New("auth failed: cannot get password") return nil, errors.New("auth failed: cannot get password")
} }
@ -214,7 +216,7 @@ func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) {
// Verify // Verify
if user != s.user || pass != s.password { if user != s.user || pass != s.password {
_, err = rw.Write([]byte{1, 1}) _, err = c.Write([]byte{1, 1})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -222,30 +224,30 @@ func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) {
} }
// Response auth state // Response auth state
_, err = rw.Write([]byte{1, 0}) _, err = c.Write([]byte{1, 0})
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else if _, err := rw.Write([]byte{Version, socks.AuthNone}); err != nil { } else if _, err := c.Write([]byte{Version, socks.AuthNone}); err != nil {
return nil, err return nil, err
} }
// read VER CMD RSV ATYP DST.ADDR DST.PORT // read VER CMD RSV ATYP DST.ADDR DST.PORT
if _, err := io.ReadFull(rw, buf[:3]); err != nil { if _, err := io.ReadFull(c, buf[:3]); err != nil {
return nil, err return nil, err
} }
cmd := buf[1] cmd := buf[1]
addr, err := socks.ReadAddrBuf(rw, buf) addr, err := socks.ReadAddrBuf(c, buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
switch cmd { switch cmd {
case socks.CmdConnect: case socks.CmdConnect:
_, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded _, err = c.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded
case socks.CmdUDPAssociate: case socks.CmdUDPAssociate:
listenAddr := socks.ParseAddr(rw.(net.Conn).LocalAddr().String()) listenAddr := socks.ParseAddr(c.LocalAddr().String())
_, err = rw.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded _, err = c.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded
if err != nil { if err != nil {
return nil, socks.Errors[7] return nil, socks.Errors[7]
} }

View File

@ -7,7 +7,6 @@ Type=simple
User=nobody User=nobody
Restart=always Restart=always
LimitNOFILE=102400 LimitNOFILE=102400
Environment="GODEBUG=madvdontneed=1"
# NOTE: CHANGE to your glider path # NOTE: CHANGE to your glider path
ExecStart=/usr/bin/glider -config /etc/glider/%i.conf ExecStart=/usr/bin/glider -config /etc/glider/%i.conf