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 }