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

View File

@ -16,7 +16,8 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16.x
stable: false
go-version: 1.16.0-rc1
- name: Go Env
run: go env
- 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.
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 {
case "tcp", "tcp6", "tcp4":
default:
@ -39,53 +54,26 @@ func (s *Socks5) Dial(network, addr string) (net.Conn, error) {
return nil, err
}
if err := s.connect(c, addr); err != nil {
c.Close()
return nil, err
}
return c, nil
}
// DialUDP connects to the given address via the proxy.
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 {
log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err)
return nil, nil, err
}
// send VER, NMETHODS, METHODS
c.Write([]byte{Version, 1, 0})
var uAddr socks.Addr
if uAddr, err = s.connect(c, addr, socks.CmdUDPAssociate); err != nil {
c.Close()
return nil, nil, err
}
buf := pool.GetBuffer(socks.MaxAddrLen)
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
h, p, _ := net.SplitHostPort(uAddr.String())
// 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
}
pkc := NewPktConn(pc, nextHop, dstAddr, true, c)
pkc := NewPktConn(pc, nextHop, socks.ParseAddr(addr), true, c)
return pkc, nextHop, err
}
// connect takes an existing connection to a socks5 proxy server,
// and commands the server to extend that connection to target,
// 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)
if err != nil {
return err
return
}
port, err := strconv.Atoi(portStr)
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 {
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
@ -135,17 +123,17 @@ func (s *Socks5) connect(conn net.Conn, target string) error {
}
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 {
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 {
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 {
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 {
@ -157,20 +145,20 @@ func (s *Socks5) connect(conn net.Conn, target string) error {
buf = append(buf, s.password...)
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 {
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 {
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 = append(buf, Version, socks.CmdConnect, 0 /* reserved */)
buf = append(buf, Version, cmd, 0 /* reserved */)
if ip := net.ParseIP(host); ip != 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...)
} else {
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, 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))
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 {
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
// read VER REP RSV
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"
@ -204,38 +193,8 @@ func (s *Socks5) connect(conn net.Conn, target string) error {
}
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
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
return socks.ReadAddr(conn)
}

View File

@ -157,27 +157,29 @@ func (s *Socks5) ListenAndServeUDP() {
}
// 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
buf := make([]byte, socks.MaxAddrLen)
buf := pool.GetBuffer(socks.MaxAddrLen)
defer pool.PutBuffer(buf)
// 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
}
nmethods := buf[1]
if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil {
if _, err := io.ReadFull(c, buf[:nmethods]); err != nil {
return nil, err
}
// write VER METHOD
if s.user != "" && s.password != "" {
_, err := rw.Write([]byte{Version, socks.AuthPassword})
_, err := c.Write([]byte{Version, socks.AuthPassword})
if err != nil {
return nil, err
}
_, err = io.ReadFull(rw, buf[:2])
_, err = io.ReadFull(c, buf[:2])
if err != nil {
return nil, err
}
@ -185,28 +187,28 @@ func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) {
// Get username
userLen := int(buf[1])
if userLen <= 0 {
rw.Write([]byte{1, 1})
c.Write([]byte{1, 1})
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")
}
user := string(buf[:userLen])
// Get password
_, err = rw.Read(buf[:1])
_, err = c.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})
c.Write([]byte{1, 1})
return nil, errors.New("auth failed: wrong password length")
}
_, err = io.ReadFull(rw, buf[:passLen])
_, err = io.ReadFull(c, buf[:passLen])
if err != nil {
return nil, errors.New("auth failed: cannot get password")
}
@ -214,7 +216,7 @@ func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) {
// Verify
if user != s.user || pass != s.password {
_, err = rw.Write([]byte{1, 1})
_, err = c.Write([]byte{1, 1})
if err != nil {
return nil, err
}
@ -222,30 +224,30 @@ func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) {
}
// Response auth state
_, err = rw.Write([]byte{1, 0})
_, err = c.Write([]byte{1, 0})
if err != nil {
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
}
// 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
}
cmd := buf[1]
addr, err := socks.ReadAddrBuf(rw, buf)
addr, err := socks.ReadAddrBuf(c, buf)
if err != nil {
return nil, err
}
switch cmd {
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:
listenAddr := socks.ParseAddr(rw.(net.Conn).LocalAddr().String())
_, err = rw.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded
listenAddr := socks.ParseAddr(c.LocalAddr().String())
_, err = c.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded
if err != nil {
return nil, socks.Errors[7]
}

View File

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