fix(socks5): select only offered authentication methods

This commit is contained in:
fhgffy 2026-10-14 00:29:10 +08:00
parent 487da737b9
commit 4c3166bf9f
2 changed files with 69 additions and 1 deletions

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"io" "io"
"net" "net"
"slices"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -208,8 +209,19 @@ func (s *Socks5) handshake(c net.Conn) (socks.Addr, error) {
return nil, err return nil, err
} }
// write VER METHOD method := byte(socks.AuthNone)
if s.user != "" && s.password != "" { if s.user != "" && s.password != "" {
method = socks.AuthPassword
}
if !slices.Contains(buf[:nmethods], method) {
if _, err := c.Write([]byte{Version, 0xff}); err != nil {
return nil, err
}
return nil, errors.New("no acceptable authentication method")
}
// write VER METHOD
if method == socks.AuthPassword {
_, err := c.Write([]byte{Version, socks.AuthPassword}) _, err := c.Write([]byte{Version, socks.AuthPassword})
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -0,0 +1,56 @@
package socks5
import (
"io"
"net"
"testing"
"time"
"github.com/nadoo/glider/pkg/socks"
)
func TestHandshakeSelectsOfferedMethod(t *testing.T) {
for _, tt := range []struct {
name string
user string
password string
methods []byte
want byte
}{
{name: "no authentication", methods: []byte{socks.AuthNone}, want: socks.AuthNone},
{name: "password authentication", user: "user", password: "pass", methods: []byte{socks.AuthPassword}, want: socks.AuthPassword},
{name: "password offered second", user: "user", password: "pass", methods: []byte{socks.AuthNone, socks.AuthPassword}, want: socks.AuthPassword},
{name: "no supported method", methods: []byte{0x80}, want: 0xff},
{name: "no methods", want: 0xff},
{name: "password not offered", user: "user", password: "pass", methods: []byte{socks.AuthNone}, want: 0xff},
{name: "no-auth not offered", methods: []byte{socks.AuthPassword}, want: 0xff},
} {
t.Run(tt.name, func(t *testing.T) {
client, server := net.Pipe()
t.Cleanup(func() { client.Close(); server.Close() })
client.SetDeadline(time.Now().Add(3 * time.Second))
done := make(chan error, 1)
go func() {
s := &Socks5{user: tt.user, password: tt.password}
_, err := s.handshake(server)
server.Close()
done <- err
}()
greeting := append([]byte{Version, byte(len(tt.methods))}, tt.methods...)
if _, err := client.Write(greeting); err != nil {
t.Fatal(err)
}
var reply [2]byte
if _, err := io.ReadFull(client, reply[:]); err != nil {
t.Fatal(err)
}
if reply != [2]byte{Version, tt.want} {
t.Errorf("method reply = %v, want [%d %d]", reply, Version, tt.want)
}
client.Close()
if err := <-done; err == nil {
t.Error("incomplete handshake unexpectedly succeeded")
}
})
}
}