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