mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 01:15:41 +08:00
socks5: fix an issue in udp handling with auth (#219)
This commit is contained in:
parent
d2268b623f
commit
fbf694f5cd
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@ -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
|
||||
|
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user