feat(server): add username/password auth for outbound SOCKS5 proxy (RFC 1929)

This commit is contained in:
spkprsnts
2026-05-23 05:14:25 +05:00
parent 24ca5b354c
commit d5973cf2c7
4 changed files with 63 additions and 3 deletions

View File

@@ -189,6 +189,8 @@ type Config struct {
DNSServer string
SOCKSProxyAddr string
SOCKSProxyPort int
SOCKSProxyUser string
SOCKSProxyPass string
Video VideoConfig
VP8 VP8Config
SEI SEIConfig
@@ -652,6 +654,8 @@ func runOnce(
DNSServer: cfg.DNSServer,
SOCKSProxyAddr: cfg.SOCKSProxyAddr,
SOCKSProxyPort: cfg.SOCKSProxyPort,
SOCKSProxyUser: cfg.SOCKSProxyUser,
SOCKSProxyPass: cfg.SOCKSProxyPass,
TransportOptions: opts,
Engine: cfg.Engine,
URL: cfg.URL,

View File

@@ -108,6 +108,8 @@ type SOCKS struct {
Pass string `yaml:"pass"`
ProxyAddr string `yaml:"proxy_addr"`
ProxyPort int `yaml:"proxy_port"`
ProxyUser string `yaml:"proxy_user"`
ProxyPass string `yaml:"proxy_pass"`
}
// Engine selects a direct SFU connection when Auth.Provider is "none".
@@ -262,6 +264,8 @@ func Apply(dst session.Config, f File) session.Config {
dst.DNSServer = pickString(dst.DNSServer, f.Net.DNS)
dst.SOCKSProxyAddr = pickString(dst.SOCKSProxyAddr, f.SOCKS.ProxyAddr)
dst.SOCKSProxyPort = pickInt(dst.SOCKSProxyPort, f.SOCKS.ProxyPort)
dst.SOCKSProxyUser = pickString(dst.SOCKSProxyUser, f.SOCKS.ProxyUser)
dst.SOCKSProxyPass = pickString(dst.SOCKSProxyPass, f.SOCKS.ProxyPass)
dst.Video.Width = pickInt(dst.Video.Width, f.Video.Width)
dst.Video.Height = pickInt(dst.Video.Height, f.Video.Height)
dst.Video.FPS = pickInt(dst.Video.FPS, f.Video.FPS)
@@ -307,6 +311,8 @@ func ApplyProfile(base session.Config, p Profile) session.Config {
dst.DNSServer = overlayString(dst.DNSServer, p.Net.DNS)
dst.SOCKSProxyAddr = overlayString(dst.SOCKSProxyAddr, p.SOCKS.ProxyAddr)
dst.SOCKSProxyPort = overlayInt(dst.SOCKSProxyPort, p.SOCKS.ProxyPort)
dst.SOCKSProxyUser = overlayString(dst.SOCKSProxyUser, p.SOCKS.ProxyUser)
dst.SOCKSProxyPass = overlayString(dst.SOCKSProxyPass, p.SOCKS.ProxyPass)
dst.Video.Width = overlayInt(dst.Video.Width, p.Video.Width)
dst.Video.Height = overlayInt(dst.Video.Height, p.Video.Height)
dst.Video.FPS = overlayInt(dst.Video.FPS, p.Video.FPS)

View File

@@ -77,6 +77,8 @@ type Server struct {
resolver *net.Resolver
socksProxyAddr string
socksProxyPort int
socksProxyUser string
socksProxyPass string
liveness control.Config
health *runtime.HealthTracker
done chan struct{}
@@ -110,6 +112,8 @@ type Config struct {
DNSServer string
SOCKSProxyAddr string
SOCKSProxyPort int
SOCKSProxyUser string
SOCKSProxyPass string
TransportOptions transport.Options
Engine string
URL string
@@ -166,6 +170,8 @@ func Run(ctx context.Context, cfg Config) error {
dnsServer: cfg.DNSServer,
socksProxyAddr: cfg.SOCKSProxyAddr,
socksProxyPort: cfg.SOCKSProxyPort,
socksProxyUser: cfg.SOCKSProxyUser,
socksProxyPass: cfg.SOCKSProxyPass,
liveness: cfg.Liveness,
health: runtime.NewHealthTracker(cfg.OnHealth),
peerSessions: make(map[string]*peerSession),
@@ -914,15 +920,55 @@ func (s *Server) dial(req ConnectRequest) (net.Conn, error) {
}
func (s *Server) socks5Connect(conn net.Conn, targetAddr string, targetPort int) error {
if _, err := conn.Write([]byte{5, 1, 0}); err != nil {
return fmt.Errorf("failed to write socks5 auth: %w", err)
if s.socksProxyUser != "" {
// Offer username/password auth (RFC 1929) only.
if _, err := conn.Write([]byte{5, 1, 2}); err != nil {
return fmt.Errorf("failed to write socks5 auth: %w", err)
}
} else {
// No authentication.
if _, err := conn.Write([]byte{5, 1, 0}); err != nil {
return fmt.Errorf("failed to write socks5 auth: %w", err)
}
}
resp := make([]byte, 2)
if _, err := io.ReadFull(conn, resp); err != nil {
return fmt.Errorf("failed to read socks5 auth resp: %w", err)
}
if resp[0] != 5 || resp[1] != 0 {
if resp[0] != 5 {
return ErrSocks5AuthFailed
}
switch resp[1] {
case 0: // no auth accepted
if s.socksProxyUser != "" {
return ErrSocks5AuthFailed
}
case 2: // username/password
user := s.socksProxyUser
pass := s.socksProxyPass
if len(user) > 255 {
user = user[:255]
}
if len(pass) > 255 {
pass = pass[:255]
}
authMsg := make([]byte, 0, 3+len(user)+len(pass))
authMsg = append(authMsg, 1, byte(len(user)))
authMsg = append(authMsg, []byte(user)...)
authMsg = append(authMsg, byte(len(pass)))
authMsg = append(authMsg, []byte(pass)...)
if _, err := conn.Write(authMsg); err != nil {
return fmt.Errorf("failed to write socks5 credentials: %w", err)
}
authResp := make([]byte, 2)
if _, err := io.ReadFull(conn, authResp); err != nil {
return fmt.Errorf("failed to read socks5 credentials resp: %w", err)
}
if authResp[1] != 0 {
return ErrSocks5AuthFailed
}
default:
return ErrSocks5AuthFailed
}