cloudflared-mirror/socks/request_handler.go

118 lines
3.0 KiB
Go

package socks
import (
"fmt"
"io"
"net"
"strings"
"github.com/rs/zerolog"
)
// RequestHandler is the functions needed to handle a SOCKS5 command
type RequestHandler interface {
Handle(*Request, io.ReadWriter) error
}
// StandardRequestHandler implements the base socks5 command processing
type StandardRequestHandler struct {
dialer Dialer
}
// NewRequestHandler creates a standard SOCKS5 request handler
// This handles the SOCKS5 commands and proxies them to their destination
func NewRequestHandler(dialer Dialer) RequestHandler {
return &StandardRequestHandler{
dialer: dialer,
}
}
// Handle processes and responds to socks5 commands
func (h *StandardRequestHandler) Handle(req *Request, conn io.ReadWriter) error {
switch req.Command {
case connectCommand:
return h.handleConnect(conn, req)
case bindCommand:
return h.handleBind(conn, req)
case associateCommand:
return h.handleAssociate(conn, req)
default:
if err := sendReply(conn, commandNotSupported, nil); err != nil {
return fmt.Errorf("Failed to send reply: %v", err)
}
return fmt.Errorf("Unsupported command: %v", req.Command)
}
}
// handleConnect is used to handle a connect command
func (h *StandardRequestHandler) handleConnect(conn io.ReadWriter, req *Request) error {
target, localAddr, err := h.dialer.Dial(req.DestAddr.Address())
if err != nil {
msg := err.Error()
resp := hostUnreachable
if strings.Contains(msg, "refused") {
resp = connectionRefused
} else if strings.Contains(msg, "network is unreachable") {
resp = networkUnreachable
}
if err := sendReply(conn, resp, nil); err != nil {
return fmt.Errorf("Failed to send reply: %v", err)
}
return fmt.Errorf("Connect to %v failed: %v", req.DestAddr, err)
}
defer target.Close()
// Send success
if err := sendReply(conn, successReply, localAddr); err != nil {
return fmt.Errorf("Failed to send reply: %v", err)
}
// Start proxying
proxyDone := make(chan error, 2)
go func() {
_, e := io.Copy(target, req.bufConn)
proxyDone <- e
}()
go func() {
_, e := io.Copy(conn, target)
proxyDone <- e
}()
// Wait for both
for i := 0; i < 2; i++ {
e := <-proxyDone
if e != nil {
return e
}
}
return nil
}
// handleBind is used to handle a bind command
// TODO: Support bind command
func (h *StandardRequestHandler) handleBind(conn io.ReadWriter, req *Request) error {
if err := sendReply(conn, commandNotSupported, nil); err != nil {
return fmt.Errorf("Failed to send reply: %v", err)
}
return nil
}
// handleAssociate is used to handle a connect command
// TODO: Support associate command
func (h *StandardRequestHandler) handleAssociate(conn io.ReadWriter, req *Request) error {
if err := sendReply(conn, commandNotSupported, nil); err != nil {
return fmt.Errorf("Failed to send reply: %v", err)
}
return nil
}
func StreamHandler(tunnelConn io.ReadWriter, originConn net.Conn, log *zerolog.Logger) {
dialer := NewConnDialer(originConn)
requestHandler := NewRequestHandler(dialer)
socksServer := NewConnectionHandler(requestHandler)
socksServer.Serve(tunnelConn)
}