github.com/sagernet/sing-box@v1.2.7/outbound/shadowsocks.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 "github.com/sagernet/sing-box/common/mux" 10 C "github.com/sagernet/sing-box/constant" 11 "github.com/sagernet/sing-box/log" 12 "github.com/sagernet/sing-box/option" 13 "github.com/sagernet/sing-box/transport/sip003" 14 "github.com/sagernet/sing-shadowsocks" 15 "github.com/sagernet/sing-shadowsocks/shadowimpl" 16 "github.com/sagernet/sing/common" 17 "github.com/sagernet/sing/common/bufio" 18 E "github.com/sagernet/sing/common/exceptions" 19 M "github.com/sagernet/sing/common/metadata" 20 N "github.com/sagernet/sing/common/network" 21 "github.com/sagernet/sing/common/uot" 22 ) 23 24 var _ adapter.Outbound = (*Shadowsocks)(nil) 25 26 type Shadowsocks struct { 27 myOutboundAdapter 28 dialer N.Dialer 29 method shadowsocks.Method 30 serverAddr M.Socksaddr 31 plugin sip003.Plugin 32 uotClient *uot.Client 33 multiplexDialer N.Dialer 34 } 35 36 func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) { 37 method, err := shadowimpl.FetchMethod(options.Method, options.Password, router.TimeFunc()) 38 if err != nil { 39 return nil, err 40 } 41 outbound := &Shadowsocks{ 42 myOutboundAdapter: myOutboundAdapter{ 43 protocol: C.TypeShadowsocks, 44 network: options.Network.Build(), 45 router: router, 46 logger: logger, 47 tag: tag, 48 }, 49 dialer: dialer.New(router, options.DialerOptions), 50 method: method, 51 serverAddr: options.ServerOptions.Build(), 52 } 53 if options.Plugin != "" { 54 outbound.plugin, err = sip003.CreatePlugin(options.Plugin, options.PluginOptions, router, outbound.dialer, outbound.serverAddr) 55 if err != nil { 56 return nil, err 57 } 58 } 59 uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions) 60 if !uotOptions.Enabled { 61 outbound.multiplexDialer, err = mux.NewClientWithOptions(ctx, (*shadowsocksDialer)(outbound), common.PtrValueOrDefault(options.MultiplexOptions)) 62 if err != nil { 63 return nil, err 64 } 65 } 66 if uotOptions.Enabled { 67 outbound.uotClient = &uot.Client{ 68 Dialer: (*shadowsocksDialer)(outbound), 69 Version: uotOptions.Version, 70 } 71 } 72 return outbound, nil 73 } 74 75 func (h *Shadowsocks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { 76 ctx, metadata := adapter.AppendContext(ctx) 77 metadata.Outbound = h.tag 78 metadata.Destination = destination 79 if h.multiplexDialer == nil { 80 switch N.NetworkName(network) { 81 case N.NetworkTCP: 82 h.logger.InfoContext(ctx, "outbound connection to ", destination) 83 case N.NetworkUDP: 84 if h.uotClient != nil { 85 h.logger.InfoContext(ctx, "outbound UoT connect packet connection to ", destination) 86 return h.uotClient.DialContext(ctx, network, destination) 87 } else { 88 h.logger.InfoContext(ctx, "outbound packet connection to ", destination) 89 } 90 } 91 return (*shadowsocksDialer)(h).DialContext(ctx, network, destination) 92 } else { 93 switch N.NetworkName(network) { 94 case N.NetworkTCP: 95 h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination) 96 case N.NetworkUDP: 97 h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination) 98 } 99 return h.multiplexDialer.DialContext(ctx, network, destination) 100 } 101 } 102 103 func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { 104 ctx, metadata := adapter.AppendContext(ctx) 105 metadata.Outbound = h.tag 106 metadata.Destination = destination 107 if h.multiplexDialer == nil { 108 if h.uotClient != nil { 109 h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination) 110 return h.uotClient.ListenPacket(ctx, destination) 111 } else { 112 h.logger.InfoContext(ctx, "outbound packet connection to ", destination) 113 } 114 h.logger.InfoContext(ctx, "outbound packet connection to ", destination) 115 return (*shadowsocksDialer)(h).ListenPacket(ctx, destination) 116 } else { 117 h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination) 118 return h.multiplexDialer.ListenPacket(ctx, destination) 119 } 120 } 121 122 func (h *Shadowsocks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { 123 return NewConnection(ctx, h, conn, metadata) 124 } 125 126 func (h *Shadowsocks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { 127 return NewPacketConnection(ctx, h, conn, metadata) 128 } 129 130 func (h *Shadowsocks) Close() error { 131 return common.Close(h.multiplexDialer) 132 } 133 134 var _ N.Dialer = (*shadowsocksDialer)(nil) 135 136 type shadowsocksDialer Shadowsocks 137 138 func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { 139 ctx, metadata := adapter.AppendContext(ctx) 140 metadata.Outbound = h.tag 141 metadata.Destination = destination 142 switch N.NetworkName(network) { 143 case N.NetworkTCP: 144 var outConn net.Conn 145 var err error 146 if h.plugin != nil { 147 outConn, err = h.plugin.DialContext(ctx) 148 } else { 149 outConn, err = h.dialer.DialContext(ctx, N.NetworkTCP, h.serverAddr) 150 } 151 if err != nil { 152 return nil, err 153 } 154 return h.method.DialEarlyConn(outConn, destination), nil 155 case N.NetworkUDP: 156 outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr) 157 if err != nil { 158 return nil, err 159 } 160 return bufio.NewBindPacketConn(h.method.DialPacketConn(outConn), destination), nil 161 default: 162 return nil, E.Extend(N.ErrUnknownNetwork, network) 163 } 164 } 165 166 func (h *shadowsocksDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { 167 ctx, metadata := adapter.AppendContext(ctx) 168 metadata.Outbound = h.tag 169 metadata.Destination = destination 170 outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr) 171 if err != nil { 172 return nil, err 173 } 174 return h.method.DialPacketConn(outConn), nil 175 }