socks5

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2025 License: MIT Imports: 12 Imported by: 1

README

go-socks5

Go Report Card PkgGoDev

A complete implementation of a SOCKS5 server in Go. SOCKS (Secure Sockets) is used to route traffic between a client and server through an intermediate proxy layer, enabling functionality such as firewall traversal and traffic anonymization.

Fork History

This library is based on the following forks:

Features

  • Authentication Methods
    • No authentication required
    • Username/password authentication (RFC 1929)
  • Commands
    • TCP CONNECT command for proxying TCP connections
    • UDP ASSOCIATE command for proxying UDP datagrams
  • Flexible Configuration
    • Custom DNS resolution
    • Rule-based access control
    • Address rewriting capabilities
    • Configurable connection timeouts
    • Custom UDP packet sizes and session timeouts
  • Production Ready
    • Graceful server shutdown
    • Context-based cancellation
    • Comprehensive error handling
    • Extensive test coverage

Installation

go get github.com/lanrat/go-socks5

Usage

Basic Server
package main

import (
    "context"
    "log"
    
    "github.com/lanrat/go-socks5"
)

func main() {
    // Create a SOCKS5 server with default configuration
    conf := &socks5.Config{}
    server, err := socks5.New(conf)
    if err != nil {
        log.Fatal(err)
    }

    // Listen on localhost:1080
    if err := server.ListenAndServe(context.Background(), ":1080"); err != nil {
        log.Fatal(err)
    }
}
Server with Authentication
package main

import (
    "context"
    "log"
    
    "github.com/lanrat/go-socks5"
)

func main() {
    // Create credentials store
    creds := socks5.StaticCredentials{
        "user":  "password",
        "admin": "secret123",
    }

    conf := &socks5.Config{
        Credentials: creds,
    }
    
    server, err := socks5.New(conf)
    if err != nil {
        log.Fatal(err)
    }

    if err := server.ListenAndServe(context.Background(), ":1080"); err != nil {
        log.Fatal(err)
    }
}
Server with UDP Support

UDP support in SOCKS5 works through the ASSOCIATE command, which establishes a UDP relay session. Here's how to configure it:

package main

import (
    "context"
    "log"
    "net"
    "time"
    
    "github.com/lanrat/go-socks5"
)

func main() {
    conf := &socks5.Config{
        // Enable UDP by setting a bind IP and port
        BindIP:   net.IPv4(127, 0, 0, 1),
        BindPort: 8080, // UDP server will listen on this port
        
        // Optional: Configure UDP settings
        UDPPacketSize:     4096,              // Max UDP packet size (default: 2048)
        UDPSessionTimeout: 10 * time.Minute,  // Session idle timeout (default: 5 minutes)
    }
    
    server, err := socks5.New(conf)
    if err != nil {
        log.Fatal(err)
    }

    // TCP server on :1080, UDP relay on :8080
    if err := server.ListenAndServe(context.Background(), ":1080"); err != nil {
        log.Fatal(err)
    }
}

How UDP ASSOCIATE Works:

  1. Client connects to TCP port (1080) and sends ASSOCIATE command
  2. Server responds with UDP relay address (127.0.0.1:8080)
  3. Client sends UDP packets to relay address with SOCKS5 UDP header
  4. Server forwards packets to destination and returns responses
  5. Session remains active until TCP connection closes or timeout expires
Advanced Configuration
package main

import (
    "context"
    "log"
    "net"
    "os"
    "time"
    
    "github.com/lanrat/go-socks5"
)

func main() {
    conf := &socks5.Config{
        // Custom authentication
        AuthMethods: []socks5.Authenticator{
            &socks5.NoAuthAuthenticator{},
            &socks5.UserPassAuthenticator{
                Credentials: socks5.StaticCredentials{
                    "user": "pass",
                },
            },
        },
        
        // Custom resolver
        Resolver: &socks5.DNSResolver{},
        
        // Access control rules
        Rules: socks5.PermitAll(), // or PermitNone(), or custom RuleSet
        
        // Custom logger
        Logger: log.New(os.Stdout, "socks5: ", log.LstdFlags),
        
        // Connection timeout
        ConnTimeout: 30 * time.Second,
        
        // UDP configuration
        BindIP:            net.IPv4(0, 0, 0, 0),
        BindPort:          0, // Set to 0 to disable UDP support
        UDPPacketSize:     2048,
        UDPSessionTimeout: 5 * time.Minute,
    }
    
    server, err := socks5.New(conf)
    if err != nil {
        log.Fatal(err)
    }

    if err := server.ListenAndServe(context.Background(), ":1080"); err != nil {
        log.Fatal(err)
    }
}

Configuration Options

The Config struct provides extensive customization options:

Field Type Description Default
AuthMethods []Authenticator Authentication methods No-auth
Credentials CredentialStore Username/password store None
Resolver NameResolver DNS resolver System DNS
Rules RuleSet Access control rules Permit all
Rewriter AddressRewriter Address rewriting None
BindIP net.IP UDP bind address 127.0.0.1
BindPort int UDP bind port (0 = disabled) 0
Logger ErrorLogger Error logger Stdout
ConnTimeout time.Duration Connection timeout None
UDPPacketSize int Max UDP packet size 2048
UDPSessionTimeout time.Duration UDP session timeout 5 minutes

Limitations

  • BIND Command: Not yet implemented (returns "command not supported")
  • UDP Security: Current UDP implementation accepts packets from any source (see security note in code)

Testing

# Run all tests
go test ./...

# Run tests with coverage
go test -cover ./...

# Run specific test
go test -run TestSOCKS5_Connect

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

Documentation

Overview

Package socks5 provides a complete implementation of a SOCKS5 server as defined in RFC 1928.

This package includes support for:

  • TCP CONNECT command for proxying TCP connections
  • UDP ASSOCIATE command for proxying UDP datagrams
  • Multiple authentication methods (no auth, username/password)
  • Configurable address resolution and connection handling
  • Rule-based access control
  • Address rewriting capabilities
  • Graceful server shutdown
  • Connection timeouts and context-based cancellation

The server is designed to be flexible and extensible, allowing custom implementations of authentication, name resolution, access rules, and address rewriting.

Basic usage:

config := &socks5.Config{}
server, err := socks5.New(config)
if err != nil {
	log.Fatal(err)
}

if err := server.ListenAndServe(context.Background(), ":1080"); err != nil {
	log.Fatal(err)
}

For more advanced configurations, see the Config struct and its various interface types.

Index

Constants

View Source
const (
	// AuthMethodNoAuth indicates no authentication is required (X'00')
	AuthMethodNoAuth = uint8(0)

	// AuthMethodUserPass indicates username/password authentication (X'02')
	AuthMethodUserPass = uint8(2)

	// AuthMethodNoAcceptable indicates no acceptable authentication methods (X'FF')
	AuthMethodNoAcceptable = uint8(255)
)

Authentication method constants as defined in RFC 1928

View Source
const (
	// AuthUserPassVersion is the version field for username/password sub-negotiation (X'01')
	AuthUserPassVersion = uint8(1)
	// AuthUserPassStatusSuccess indicates successful username/password authentication (X'00')
	AuthUserPassStatusSuccess = uint8(0)
	// AuthUserPassStatusFailure indicates failed username/password authentication (X'01')
	AuthUserPassStatusFailure = uint8(1)
)
View Source
const (
	// CommandConnect requests a TCP connection to the target (X'01')
	CommandConnect = uint8(1)
	// CommandBind requests the server to bind to a port for incoming connections (X'02')
	CommandBind = uint8(2)
	// CommandAssociate requests UDP association for relaying UDP datagrams (X'03')
	CommandAssociate = uint8(3)
)

SOCKS5 command constants as defined in RFC 1928

View Source
const (
	// AddressIPv4 indicates an IPv4 address follows (X'01')
	AddressIPv4 = uint8(1)
	// AddressDomainName indicates a domain name follows (X'03')
	AddressDomainName = uint8(3)
	// AddressIPv6 indicates an IPv6 address follows (X'04')
	AddressIPv6 = uint8(4)
)

Address type constants as defined in RFC 1928

View Source
const (
	// ReplySucceeded indicates the request was successful (X'00')
	ReplySucceeded uint8 = iota
	// ReplyServerFailure indicates a general server failure (X'01')
	ReplyServerFailure
	// ReplyRuleFailure indicates the connection was blocked by rules (X'02')
	ReplyRuleFailure
	// ReplyNetworkUnreachable indicates the network is unreachable (X'03')
	ReplyNetworkUnreachable
	// ReplyHostUnreachable indicates the host is unreachable (X'04')
	ReplyHostUnreachable
	// ReplyConnectionRefused indicates the connection was refused (X'05')
	ReplyConnectionRefused
	// ReplyTTLExpired indicates the TTL expired (X'06')
	ReplyTTLExpired
	// ReplyCommandNotSupported indicates the command is not supported (X'07')
	ReplyCommandNotSupported
	// ReplyAddrTypeNotSupported indicates the address type is not supported (X'08')
	ReplyAddrTypeNotSupported
)

Reply constants for server responses as defined in RFC 1928

View Source
const (
	// ClientAddrKey is the context key for storing the client's remote address
	ClientAddrKey contextKey = "client_addr"
	// ServerAddrKey is the context key for storing the server's local address
	ServerAddrKey contextKey = "server_addr"
	// ConnTimeKey is the context key for storing the connection start time
	ConnTimeKey contextKey = "conn_time"
)

Variables

View Source
var (
	// ErrUserAuthFailed is returned when username/password authentication fails
	ErrUserAuthFailed = fmt.Errorf("user authentication failed")
	// ErrNoSupportedAuth is returned when no mutually supported authentication method exists
	ErrNoSupportedAuth = fmt.Errorf("no supported authentication mechanism")
)
View Source
var ErrUDPFragmentNoSupported = errors.New("UDP fragmentation not supported")

ErrUDPFragmentNoSupported is returned when a UDP packet indicates fragmentation

Functions

This section is empty.

Types

type AddrSpec

type AddrSpec struct {
	// FQDN is the fully qualified domain name (empty if IP is used)
	FQDN string
	// IP is the IP address (nil if FQDN is used)
	IP net.IP
	// Port is the port number
	Port int
}

AddrSpec represents a SOCKS5 address specification. It can contain either an IP address (IPv4/IPv6) or a fully qualified domain name (FQDN).

func (AddrSpec) Address

func (a AddrSpec) Address() string

Address returns a string suitable for dialing, preferring IP over FQDN.

func (*AddrSpec) String

func (a *AddrSpec) String() string

String returns a human-readable representation of the address specification.

type AddressRewriter

type AddressRewriter interface {
	// Rewrite takes a request and returns a potentially modified destination address.
	// The context may be modified to include additional routing information.
	Rewrite(ctx context.Context, request *Request) (context.Context, *AddrSpec)
}

AddressRewriter is used to rewrite a destination address transparently. This can be used for implementing features like traffic routing, load balancing, or address translation. The returned context can contain additional metadata.

type AuthContext

type AuthContext struct {
	// Method is the authentication method code that was used
	Method uint8
	// Payload contains method-specific authentication data.
	// For UserPassAuth, contains "Username" key with the authenticated username.
	Payload map[string]string
}

AuthContext encapsulates authentication state provided during negotiation. It contains the authentication method used and any associated payload data.

type Authenticator

type Authenticator interface {
	// Authenticate performs the authentication handshake with the client
	Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error)
	// GetCode returns the authentication method code for this authenticator
	GetCode() uint8
}

Authenticator defines the interface for SOCKS5 authentication methods. Implementations handle the authentication negotiation for specific methods.

type Config

type Config struct {
	// AuthMethods can be provided to implement custom authentication
	// By default, "auth-less" mode is enabled.
	// For password-based auth use UserPassAuthenticator.
	AuthMethods []Authenticator

	// If provided, username/password authentication is enabled,
	// by appending a UserPassAuthenticator to AuthMethods. If not provided,
	// and AUthMethods is nil, then "auth-less" mode is enabled.
	Credentials CredentialStore

	// Resolver can be provided to do custom name resolution.
	// Defaults to DNSResolver if not provided.
	Resolver NameResolver

	// Rules is provided to enable custom logic around permitting
	// various commands. If not provided, PermitAll is used.
	Rules RuleSet

	// Rewriter can be used to transparently rewrite addresses.
	// This is invoked before the RuleSet is invoked.
	// Defaults to NoRewrite.
	Rewriter AddressRewriter

	// BindIP is used for bind or udp associate
	BindIP net.IP

	// BindPort is the port used for bind or UDP associate operations.
	// If set to 0, UDP support is disabled.
	BindPort int

	// Logger can be used to provide a custom log target.
	// Defaults to stdout.
	Logger ErrorLogger

	// ConnTimeout is the maximum time a connection can be active.
	// If zero (default), connections have no timeout.
	ConnTimeout time.Duration

	// Dial is an optional function for making outbound TCP connections.
	// If nil, net.Dial is used.
	Dial func(ctx context.Context, network, addr string) (net.Conn, error)

	// DialUDP is an optional function for making outbound UDP connections.
	// If nil, net.DialUDP is used with a zero source address.
	DialUDP func(ctx context.Context, network string, udpClientSrcAddr, targetUDPAddr *net.UDPAddr) (net.Conn, error)

	// UDPPacketSize sets the maximum size for UDP packets.
	// If zero, defaults to 2048 bytes.
	UDPPacketSize int

	// UDPSessionTimeout sets how long UDP sessions remain active without traffic.
	// If zero, defaults to 5 minutes.
	UDPSessionTimeout time.Duration
}

Config is used to setup and configure a SOCKS5 Server. It provides options for authentication, networking, logging, and connection handling.

type CredentialStore

type CredentialStore interface {
	// Valid checks if the provided username and password are valid
	Valid(user, password string) bool
}

CredentialStore defines the interface for validating user credentials. Implementations should return true if the username/password combination is valid.

type DNSResolver

type DNSResolver struct{}

DNSResolver uses the system's default DNS resolver to resolve hostnames. This is the default resolver used when no custom resolver is provided.

func (DNSResolver) Resolve

func (d DNSResolver) Resolve(ctx context.Context, name string) (context.Context, net.IP, error)

Resolve uses the system DNS to resolve a hostname to an IP address.

type ErrorLogger

type ErrorLogger interface {
	// Printf formats and prints a log message similar to fmt.Printf
	Printf(format string, v ...interface{})
}

ErrorLogger is an error handler interface compatible with the standard library logger. It is used by the SOCKS5 server to log errors and diagnostic information.

type NameResolver

type NameResolver interface {
	// Resolve resolves a hostname to an IP address, returning the updated context and IP
	Resolve(ctx context.Context, name string) (context.Context, net.IP, error)
}

NameResolver defines the interface for resolving hostnames to IP addresses. Custom implementations can provide alternative resolution strategies.

type NoAuthAuthenticator

type NoAuthAuthenticator struct{}

NoAuthAuthenticator is used to handle the "No Authentication" mode

func (NoAuthAuthenticator) Authenticate

func (a NoAuthAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error)

Authenticate performs the no-auth handshake by simply confirming the method.

func (NoAuthAuthenticator) GetCode

func (a NoAuthAuthenticator) GetCode() uint8

GetCode returns the authentication method code for no authentication.

type PermitCommand

type PermitCommand struct {
	// EnableConnect allows or denies CONNECT commands for TCP proxying
	EnableConnect bool
	// EnableBind allows or denies BIND commands for incoming connections
	EnableBind bool
	// EnableAssociate allows or denies ASSOCIATE commands for UDP proxying
	EnableAssociate bool
}

PermitCommand implements RuleSet to allow or deny specific SOCKS5 commands. It provides granular control over which operations are permitted.

func (*PermitCommand) Allow

func (p *PermitCommand) Allow(ctx context.Context, req *Request) (context.Context, bool)

Allow checks if the requested command is enabled in this rule set.

type Request

type Request struct {
	// Version is the SOCKS protocol version (should be 5)
	Version uint8
	// Command is the requested SOCKS command (CONNECT, BIND, ASSOCIATE)
	Command uint8
	// AuthContext contains authentication information from negotiation
	AuthContext *AuthContext
	// RemoteAddr is the address of the client that sent the request
	RemoteAddr *AddrSpec
	// DestAddr is the desired destination address from the client
	DestAddr *AddrSpec
	// contains filtered or unexported fields
}

Request represents a SOCKS5 request received from a client. It contains the parsed command, destination address, and authentication context.

func NewRequest

func NewRequest(bufConn io.Reader) (*Request, error)

NewRequest parses a SOCKS5 request from the given reader. It reads and validates the request header and destination address. Returns an error if the request format is invalid or unsupported.

type RuleSet

type RuleSet interface {
	// Allow determines if a request should be permitted, returning updated context and decision
	Allow(ctx context.Context, req *Request) (context.Context, bool)
}

RuleSet defines the interface for implementing access control rules. Custom implementations can provide fine-grained control over which requests are allowed.

func PermitAll

func PermitAll() RuleSet

PermitAll returns a RuleSet that allows all SOCKS5 commands (CONNECT, BIND, ASSOCIATE).

func PermitNone

func PermitNone() RuleSet

PermitNone returns a RuleSet that disallows all SOCKS5 commands.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is responsible for accepting connections and handling the details of the SOCKS5 protocol. It supports TCP CONNECT, UDP ASSOCIATE commands, and provides graceful shutdown capabilities.

func New

func New(conf *Config) (*Server, error)

New creates a new SOCKS5 server with the given configuration. It validates the configuration and sets up default values for any missing required fields. Returns an error if the configuration is invalid.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(ctx context.Context, addr string) error

ListenAndServe creates a network listener on the given address and serves SOCKS5 connections. It blocks until the context is cancelled or an error occurs. The network parameter should be "tcp", "tcp4", or "tcp6".

func (*Server) Serve

func (s *Server) Serve(ctx context.Context, l net.Listener) error

Serve accepts incoming connections from a listener and handles SOCKS5 protocol negotiations. It starts a UDP server if BindPort is configured, and spawns a goroutine for each TCP connection. This method blocks until the context is cancelled or an error occurs.

func (*Server) ServeConn

func (s *Server) ServeConn(ctx context.Context, conn net.Conn) error

ServeConn handles the SOCKS5 protocol for a single client connection. It performs authentication, parses the client request, and handles the requested command. The connection is automatically closed when this method returns.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the server without interrupting any active connections.

type StaticCredentials

type StaticCredentials map[string]string

StaticCredentials enables using a map directly as a credential store. The map keys are usernames and values are passwords.

func (StaticCredentials) Valid

func (s StaticCredentials) Valid(user, password string) bool

Valid checks if the provided username exists and the password matches.

type UDPSession

type UDPSession struct {
	// Context from the ASSOCIATE command, with any rule modifications applied
	Context context.Context
	// ClientAddr is the client's control connection address for session identification
	ClientAddr string
	// CreatedAt records when this session was established
	CreatedAt time.Time
	// LastActivity tracks the last time UDP traffic was seen for this session
	LastActivity time.Time
	// Request is the original ASSOCIATE request that created this session
	Request *Request
}

UDPSession represents an active UDP association session created by an ASSOCIATE command. It tracks the client's context, timing information, and the original request.

type UDPSessionManager

type UDPSessionManager struct {
	// contains filtered or unexported fields
}

UDPSessionManager manages active UDP sessions for SOCKS5 UDP association. It provides thread-safe access to sessions and automatic cleanup of idle sessions.

func NewUDPSessionManager

func NewUDPSessionManager(sessionTimeout time.Duration) *UDPSessionManager

NewUDPSessionManager creates and initializes a new UDP session manager. It starts a background goroutine for cleaning up expired sessions.

func (*UDPSessionManager) GetSession

func (m *UDPSessionManager) GetSession(clientAddr string) (*UDPSession, bool)

GetSession retrieves a UDP session by exact client address match.

func (*UDPSessionManager) GetSessionByIP added in v0.1.1

func (m *UDPSessionManager) GetSessionByIP(clientIP string) *UDPSession

GetSessionByIP retrieves a UDP session by client IP only, ignoring port. This is useful when client port numbers change but IP remains the same.

func (*UDPSessionManager) RegisterSession

func (m *UDPSessionManager) RegisterSession(clientAddr string, ctx context.Context, req *Request)

RegisterSession creates and registers a new UDP session for the given client. The session will be indexed by both full address and IP-only for flexible lookup.

func (*UDPSessionManager) Stop

func (m *UDPSessionManager) Stop()

Stop gracefully shuts down the session manager and its cleanup goroutine.

func (*UDPSessionManager) UnregisterSession

func (m *UDPSessionManager) UnregisterSession(clientAddr string)

UnregisterSession removes a UDP session from both address indexes.

func (*UDPSessionManager) UpdateActivity added in v0.1.1

func (m *UDPSessionManager) UpdateActivity(clientAddr string)

UpdateActivity updates the last activity timestamp for a session by exact address.

func (*UDPSessionManager) UpdateActivityByIP added in v0.1.1

func (m *UDPSessionManager) UpdateActivityByIP(clientIP string)

UpdateActivityByIP updates the last activity timestamp for a session by IP only.

type UserPassAuthenticator

type UserPassAuthenticator struct {
	Credentials CredentialStore
}

UserPassAuthenticator is used to handle username/password based authentication

func (UserPassAuthenticator) Authenticate

func (a UserPassAuthenticator) Authenticate(reader io.Reader, writer io.Writer) (*AuthContext, error)

Authenticate performs username/password authentication as per RFC 1929. It reads the username and password from the client and validates them using the credential store.

func (UserPassAuthenticator) GetCode

func (a UserPassAuthenticator) GetCode() uint8

GetCode returns the authentication method code for username/password authentication.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL