go-socks5

repository·master·Indexed 20 days ago

https://github.com/things-go/go-socks5

A Go implementation of the SOCKS5 protocol that provides a proxy layer to route traffic between clients and servers. It supports TCP/UDP, IPv4/IPv6, and the CONNECT and ASSOCIATE commands. Key features include customizable authentication via the Authenticator and CredentialStore interfaces, granular command filtering, custom DNS resolution, address rewriting, and a flexible configuration system using functional options for handlers, middleware, and logging.

Tokens
6K
Snippets
29
Records
33
Agent score
70%

What's inside go-socks5

  1. Overview of go-socks5 features

    master

    The go-socks5 package implements the SOCKS5 protocol to route traffic through an intermediate proxy layer. Key features include:

    • Protocol Support: SOCKS5 server support, including TCP/UDP and IPv4/IPv6.
    • Authentication: "No Auth" mode and optional User/Password authentication with user address limits.
    • Commands: Support for CONNECT and ASSOCIATE commands (Note: BIND command support is currently a TODO).
    • Extensibility:
      • Granular command filtering via rules.
      • Custom DNS resolution.
      • Custom goroutine pools.
      • Buffer pool design with optional custom buffer pools.
      • Custom logging.
  2. Configure the SOCKS5 server using functional options

    master

    The go-socks5 server is configured using the Option type, which follows the functional options pattern. You can pass multiple Option functions to the server constructor to customize its behavior, such as authentication, name resolution, logging, and request handling. If an option is not provided, the server uses sensible defaults (e.g., io.Discard for logging, DNSResolver for name resolution, and NewPermitAll for rules).

    // Example of how options are typically applied to a server
    // Note: The exact constructor (e.g., NewServer) is assumed based on the Option pattern
    server := NewServer(socks5.WithLogger(myLogger), socks5.WithAuthMethods(myAuthMethods))
  3. Implement granular command filtering with RuleSet

    master

    The RuleSet interface allows you to provide custom logic to allow or prohibit specific SOCKS5 actions (commands). By implementing the Allow method, you can inspect a *Request and decide whether to permit the connection based on the command type (Connect, Bind, or Associate).

    type RuleSet interface {
    	Allow(ctx context.Context, req *Request) (context.Context, bool)
    }
  4. Configure Server behavior using Server struct fields

    master

    The Server struct contains several configurable fields that can be set via Option functions during initialization. Key configuration areas include:

    • Authentication: authMethods (a slice of Authenticator) and credentials (CredentialStore). If credentials is provided but authMethods is empty, UserPassAuthenticator is automatically added.
    • Resolution: resolver (NameResolver) for custom DNS logic.
    • Access Control: rules (RuleSet) to permit or deny specific commands.
    • Address Manipulation: rewriter (AddressRewriter) to transparently rewrite destination addresses before rules are applied.
    • Logging: logger (Logger) to redirect server logs.
    • Concurrency: gPool (GPool) to provide a custom goroutine pool for handling connections.
    • Dialing: dial and dialWithRequest functions to customize how the server connects to destination addresses.
    • Middleware: userConnectMiddlewares, userBindMiddlewares, and userAssociateMiddlewares for intercepting requests.
  5. Understand the AuthContext structure

    master

    The AuthContext is returned by an Authenticator upon a successful handshake. It encapsulates the state of the authenticated session:

    • Method: The uint8 authentication method code used.
    • Payload: A map[string]string containing authentication data. For UserPassAuthenticator, this map contains the keys "username" and "password".
    type AuthContext struct {
    	Method uint8
    	Payload map[string]string
    }
  6. Create a simple SOCKS5 server

    master

    You can initialize a SOCKS5 server using socks5.NewServer. The server can be configured with options like a custom logger. Use server.ListenAndServe(network, address) to start the proxy. In the example below, the server listens on localhost:8000 using the tcp network.

    package main
    
    import (
    	"log"
    	"os"
    
    	"github.com/things-go/go-socks5"
    )
    
    func main() {
    	// Create a SOCKS5 server
    	server := socks5.NewServer(
    		socks5.WithLogger(socks5.NewLogger(log.New(os.Stdout, "socks5: ", log.LstdFlags))),
    	)
    
    	// Create SOCKS5 proxy on localhost port 8000
    	if err := server.ListenAndServe("tcp", ":8000"); err != nil {
    		panic(err)
    	}
    }
  7. Implement custom request handling and middleware

    master

    The server allows you to intercept and handle SOCKS5 commands (CONNECT, BIND, ASSOCIATE) using handlers and middleware.

    Command Handlers

    Use these options to provide custom logic for specific commands:

    • WithConnectHandle(func(ctx context.Context, writer io.Writer, request *Request) error)
    • WithBindHandle(func(ctx context.Context, writer io.Writer, request *Request) error)
    • WithAssociateHandle(func(ctx context.Context, writer io.Writer, request *Request) error)

    Middleware

    Middleware allows you to intercept requests before they reach the handler. You can register middleware for specific commands:

    • WithConnectMiddleware(Middleware)
    • WithBindMiddleware(Middleware)
    • WithAssociateMiddleware(Middleware)

    A Middleware is defined as: func(ctx context.Context, writer io.Writer, request *Request) error.

    // Example: Adding a middleware that logs the request
    middleware := func(ctx context.Context, w io.Writer, r *socks5.Request) error {
        fmt.Printf("Request: %s\n", r.Addr)
        return nil
    }
    
    server := socks5.NewServer(
        socks5.WithConnectMiddleware(middleware),
    )
  8. Send SOCKS5 replies with SendReply

    master

    The SendReply function is used to send a SOCKS5 response message back to the client. It takes an io.Writer, a reply status code (rep), and an optional net.Addr (used for statute.RepSuccess to provide the bound address, such as in ASSOCIATE commands).

    Reply statuses are defined in the statute package (e.g., statute.RepSuccess, statute.RepHostUnreachable, statute.RepConnectionRefused).

    // Example: Sending a success reply with a specific bound address
    err := socks5.SendReply(writer, statute.RepSuccess, boundAddr)
  9. Implement custom authentication with the Authenticator interface

    master

    To implement custom authentication logic in go-socks5, you must satisfy the Authenticator interface. The interface requires two methods:

    1. GetCode() uint8: Returns the SOCKS5 authentication method code (e.g., statute.MethodNoAuth or statute.MethodUserPassAuth).
    2. Authenticate(reader io.Reader, writer io.Writer, userAddr string) (*AuthContext, error): Handles the handshake logic. It reads the client's credentials from the reader, writes the server's response to the writer, and returns an AuthContext containing the authentication method and a Payload map of credentials if successful.

    If authentication fails, you should write the appropriate failure byte to the writer and return an error.

    type Authenticator interface {
    	Authenticate(reader io.Reader, writer io.Writer, userAddr string) (*AuthContext, error)
    	GetCode() uint8
    }
  10. Start a SOCKS5 server with ListenAndServeTLS

    master

    The ListenAndServeTLS method creates a TLS-encrypted listener and serves connections. This is useful for wrapping the SOCKS5 protocol in a TLS layer.

    Parameters:

    • network: The network type (e.g., "tcp").
    • addr: The address to listen on.
    • c: A *tls.Config object defining the TLS settings.
    // Example with TLS
    tlsConfig := &tls.Config{
        // your TLS configuration
    }
    err := server.ListenAndServeTLS("tcp", ":1080", tlsConfig)
    if err != nil {
        log.Fatal(err)
    }
  11. Use predefined RuleSet helpers

    master

    The package provides several helper functions to quickly create common RuleSet configurations using PermitCommand:

    • NewPermitNone(): Disallows all types of connections.
    • NewPermitAll(): Allows all types of connections.
    • NewPermitConnAndAss(): Allows only Connect and Associate connections.
    // Disallow everything
    rules := socks5.NewPermitNone()
    
    // Allow everything
    rules := socks5.NewPermitAll()
    
    // Allow only Connect and Associate
    rules := socks5.NewPermitConnAndAss()