github.com/sagernet/sing-box@v1.9.0-rc.20/outbound/proxy.go (about)

     1  package outbound
     2  
     3  import (
     4  	std_bufio "bufio"
     5  	"context"
     6  	"crypto/rand"
     7  	"encoding/hex"
     8  	"net"
     9  
    10  	"github.com/sagernet/sing-box/adapter"
    11  	"github.com/sagernet/sing-box/log"
    12  	"github.com/sagernet/sing/common"
    13  	"github.com/sagernet/sing/common/auth"
    14  	"github.com/sagernet/sing/common/buf"
    15  	"github.com/sagernet/sing/common/bufio"
    16  	E "github.com/sagernet/sing/common/exceptions"
    17  	M "github.com/sagernet/sing/common/metadata"
    18  	N "github.com/sagernet/sing/common/network"
    19  	"github.com/sagernet/sing/common/rw"
    20  	"github.com/sagernet/sing/protocol/http"
    21  	"github.com/sagernet/sing/protocol/socks"
    22  	"github.com/sagernet/sing/protocol/socks/socks4"
    23  	"github.com/sagernet/sing/protocol/socks/socks5"
    24  )
    25  
    26  type ProxyListener struct {
    27  	ctx           context.Context
    28  	logger        log.ContextLogger
    29  	dialer        N.Dialer
    30  	tcpListener   *net.TCPListener
    31  	username      string
    32  	password      string
    33  	authenticator *auth.Authenticator
    34  }
    35  
    36  func NewProxyListener(ctx context.Context, logger log.ContextLogger, dialer N.Dialer) *ProxyListener {
    37  	var usernameB [64]byte
    38  	var passwordB [64]byte
    39  	rand.Read(usernameB[:])
    40  	rand.Read(passwordB[:])
    41  	username := hex.EncodeToString(usernameB[:])
    42  	password := hex.EncodeToString(passwordB[:])
    43  	return &ProxyListener{
    44  		ctx:           ctx,
    45  		logger:        logger,
    46  		dialer:        dialer,
    47  		authenticator: auth.NewAuthenticator([]auth.User{{Username: username, Password: password}}),
    48  		username:      username,
    49  		password:      password,
    50  	}
    51  }
    52  
    53  func (l *ProxyListener) Start() error {
    54  	tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{
    55  		IP: net.IPv4(127, 0, 0, 1),
    56  	})
    57  	if err != nil {
    58  		return err
    59  	}
    60  	l.tcpListener = tcpListener
    61  	go l.acceptLoop()
    62  	return nil
    63  }
    64  
    65  func (l *ProxyListener) Port() uint16 {
    66  	if l.tcpListener == nil {
    67  		panic("start listener first")
    68  	}
    69  	return M.SocksaddrFromNet(l.tcpListener.Addr()).Port
    70  }
    71  
    72  func (l *ProxyListener) Username() string {
    73  	return l.username
    74  }
    75  
    76  func (l *ProxyListener) Password() string {
    77  	return l.password
    78  }
    79  
    80  func (l *ProxyListener) Close() error {
    81  	return common.Close(l.tcpListener)
    82  }
    83  
    84  func (l *ProxyListener) acceptLoop() {
    85  	for {
    86  		tcpConn, err := l.tcpListener.AcceptTCP()
    87  		if err != nil {
    88  			return
    89  		}
    90  		ctx := log.ContextWithNewID(l.ctx)
    91  		go func() {
    92  			hErr := l.accept(ctx, tcpConn)
    93  			if hErr != nil {
    94  				if E.IsClosedOrCanceled(hErr) {
    95  					l.logger.DebugContext(ctx, E.Cause(hErr, "proxy connection closed"))
    96  					return
    97  				}
    98  				l.logger.ErrorContext(ctx, E.Cause(hErr, "proxy"))
    99  			}
   100  		}()
   101  	}
   102  }
   103  
   104  func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
   105  	headerType, err := rw.ReadByte(conn)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	switch headerType {
   110  	case socks4.Version, socks5.Version:
   111  		return socks.HandleConnection0(ctx, conn, headerType, l.authenticator, l, M.Metadata{})
   112  	}
   113  	reader := std_bufio.NewReader(bufio.NewCachedReader(conn, buf.As([]byte{headerType})))
   114  	return http.HandleConnection(ctx, conn, reader, l.authenticator, l, M.Metadata{})
   115  }
   116  
   117  func (l *ProxyListener) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata M.Metadata) error {
   118  	var metadata adapter.InboundContext
   119  	metadata.Network = N.NetworkTCP
   120  	metadata.Destination = upstreamMetadata.Destination
   121  	l.logger.InfoContext(ctx, "proxy connection to ", metadata.Destination)
   122  	return NewConnection(ctx, l.dialer, conn, metadata)
   123  }
   124  
   125  func (l *ProxyListener) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error {
   126  	var metadata adapter.InboundContext
   127  	metadata.Network = N.NetworkUDP
   128  	metadata.Destination = upstreamMetadata.Destination
   129  	l.logger.InfoContext(ctx, "proxy packet connection to ", metadata.Destination)
   130  	return NewPacketConnection(ctx, l.dialer, conn, metadata)
   131  }