github.com/sagernet/sing-box@v1.2.7/outbound/socks.go (about) 1 package outbound 2 3 import ( 4 "context" 5 "net" 6 7 "github.com/sagernet/sing-box/adapter" 8 "github.com/sagernet/sing-box/common/dialer" 9 C "github.com/sagernet/sing-box/constant" 10 "github.com/sagernet/sing-box/log" 11 "github.com/sagernet/sing-box/option" 12 "github.com/sagernet/sing/common" 13 E "github.com/sagernet/sing/common/exceptions" 14 M "github.com/sagernet/sing/common/metadata" 15 N "github.com/sagernet/sing/common/network" 16 "github.com/sagernet/sing/common/uot" 17 "github.com/sagernet/sing/protocol/socks" 18 ) 19 20 var _ adapter.Outbound = (*Socks)(nil) 21 22 type Socks struct { 23 myOutboundAdapter 24 client *socks.Client 25 resolve bool 26 uotClient *uot.Client 27 } 28 29 func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) { 30 var version socks.Version 31 var err error 32 if options.Version != "" { 33 version, err = socks.ParseVersion(options.Version) 34 } else { 35 version = socks.Version5 36 } 37 if err != nil { 38 return nil, err 39 } 40 outbound := &Socks{ 41 myOutboundAdapter: myOutboundAdapter{ 42 protocol: C.TypeSocks, 43 network: options.Network.Build(), 44 router: router, 45 logger: logger, 46 tag: tag, 47 }, 48 client: socks.NewClient(dialer.New(router, options.DialerOptions), options.ServerOptions.Build(), version, options.Username, options.Password), 49 resolve: version == socks.Version4, 50 } 51 uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions) 52 if uotOptions.Enabled { 53 outbound.uotClient = &uot.Client{ 54 Dialer: outbound.client, 55 Version: uotOptions.Version, 56 } 57 } 58 return outbound, nil 59 } 60 61 func (h *Socks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { 62 ctx, metadata := adapter.AppendContext(ctx) 63 metadata.Outbound = h.tag 64 metadata.Destination = destination 65 switch N.NetworkName(network) { 66 case N.NetworkTCP: 67 h.logger.InfoContext(ctx, "outbound connection to ", destination) 68 case N.NetworkUDP: 69 if h.uotClient != nil { 70 h.logger.InfoContext(ctx, "outbound UoT connect packet connection to ", destination) 71 return h.uotClient.DialContext(ctx, network, destination) 72 } 73 h.logger.InfoContext(ctx, "outbound packet connection to ", destination) 74 default: 75 return nil, E.Extend(N.ErrUnknownNetwork, network) 76 } 77 if h.resolve && destination.IsFqdn() { 78 addrs, err := h.router.LookupDefault(ctx, destination.Fqdn) 79 if err != nil { 80 return nil, err 81 } 82 return N.DialSerial(ctx, h.client, network, destination, addrs) 83 } 84 return h.client.DialContext(ctx, network, destination) 85 } 86 87 func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { 88 ctx, metadata := adapter.AppendContext(ctx) 89 metadata.Outbound = h.tag 90 metadata.Destination = destination 91 if h.uotClient != nil { 92 h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination) 93 return h.uotClient.ListenPacket(ctx, destination) 94 } 95 h.logger.InfoContext(ctx, "outbound packet connection to ", destination) 96 return h.client.ListenPacket(ctx, destination) 97 } 98 99 func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { 100 return NewConnection(ctx, h, conn, metadata) 101 } 102 103 func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { 104 return NewPacketConnection(ctx, h, conn, metadata) 105 }