github.com/moqsien/xraycore@v1.8.5/app/proxyman/outbound/handler.go (about) 1 package outbound 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "os" 8 9 "github.com/moqsien/xraycore/app/proxyman" 10 "github.com/moqsien/xraycore/common" 11 "github.com/moqsien/xraycore/common/buf" 12 "github.com/moqsien/xraycore/common/mux" 13 "github.com/moqsien/xraycore/common/net" 14 "github.com/moqsien/xraycore/common/net/cnc" 15 "github.com/moqsien/xraycore/common/session" 16 "github.com/moqsien/xraycore/core" 17 "github.com/moqsien/xraycore/features/outbound" 18 "github.com/moqsien/xraycore/features/policy" 19 "github.com/moqsien/xraycore/features/stats" 20 "github.com/moqsien/xraycore/proxy" 21 "github.com/moqsien/xraycore/transport" 22 "github.com/moqsien/xraycore/transport/internet" 23 "github.com/moqsien/xraycore/transport/internet/stat" 24 "github.com/moqsien/xraycore/transport/internet/tls" 25 "github.com/moqsien/xraycore/transport/pipe" 26 ) 27 28 func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) { 29 var uplinkCounter stats.Counter 30 var downlinkCounter stats.Counter 31 32 policy := v.GetFeature(policy.ManagerType()).(policy.Manager) 33 if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink { 34 statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) 35 name := "outbound>>>" + tag + ">>>traffic>>>uplink" 36 c, _ := stats.GetOrRegisterCounter(statsManager, name) 37 if c != nil { 38 uplinkCounter = c 39 } 40 } 41 if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink { 42 statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) 43 name := "outbound>>>" + tag + ">>>traffic>>>downlink" 44 c, _ := stats.GetOrRegisterCounter(statsManager, name) 45 if c != nil { 46 downlinkCounter = c 47 } 48 } 49 50 return uplinkCounter, downlinkCounter 51 } 52 53 // Handler is an implements of outbound.Handler. 54 type Handler struct { 55 tag string 56 senderSettings *proxyman.SenderConfig 57 streamSettings *internet.MemoryStreamConfig 58 proxy proxy.Outbound 59 outboundManager outbound.Manager 60 mux *mux.ClientManager 61 xudp *mux.ClientManager 62 udp443 string 63 uplinkCounter stats.Counter 64 downlinkCounter stats.Counter 65 } 66 67 // NewHandler creates a new Handler based on the given configuration. 68 func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbound.Handler, error) { 69 v := core.MustFromContext(ctx) 70 uplinkCounter, downlinkCounter := getStatCounter(v, config.Tag) 71 h := &Handler{ 72 tag: config.Tag, 73 outboundManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager), 74 uplinkCounter: uplinkCounter, 75 downlinkCounter: downlinkCounter, 76 } 77 78 if config.SenderSettings != nil { 79 senderSettings, err := config.SenderSettings.GetInstance() 80 if err != nil { 81 return nil, err 82 } 83 switch s := senderSettings.(type) { 84 case *proxyman.SenderConfig: 85 h.senderSettings = s 86 mss, err := internet.ToMemoryStreamConfig(s.StreamSettings) 87 if err != nil { 88 return nil, newError("failed to parse stream settings").Base(err).AtWarning() 89 } 90 h.streamSettings = mss 91 default: 92 return nil, newError("settings is not SenderConfig") 93 } 94 } 95 96 proxyConfig, err := config.ProxySettings.GetInstance() 97 if err != nil { 98 return nil, err 99 } 100 101 rawProxyHandler, err := common.CreateObject(ctx, proxyConfig) 102 if err != nil { 103 return nil, err 104 } 105 106 proxyHandler, ok := rawProxyHandler.(proxy.Outbound) 107 if !ok { 108 return nil, newError("not an outbound handler") 109 } 110 111 if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil { 112 if config := h.senderSettings.MultiplexSettings; config.Enabled { 113 if config.Concurrency < 0 { 114 h.mux = &mux.ClientManager{Enabled: false} 115 } 116 if config.Concurrency == 0 { 117 config.Concurrency = 8 // same as before 118 } 119 if config.Concurrency > 0 { 120 h.mux = &mux.ClientManager{ 121 Enabled: true, 122 Picker: &mux.IncrementalWorkerPicker{ 123 Factory: &mux.DialingWorkerFactory{ 124 Proxy: proxyHandler, 125 Dialer: h, 126 Strategy: mux.ClientStrategy{ 127 MaxConcurrency: uint32(config.Concurrency), 128 MaxConnection: 128, 129 }, 130 }, 131 }, 132 } 133 } 134 if config.XudpConcurrency < 0 { 135 h.xudp = &mux.ClientManager{Enabled: false} 136 } 137 if config.XudpConcurrency == 0 { 138 h.xudp = nil // same as before 139 } 140 if config.XudpConcurrency > 0 { 141 h.xudp = &mux.ClientManager{ 142 Enabled: true, 143 Picker: &mux.IncrementalWorkerPicker{ 144 Factory: &mux.DialingWorkerFactory{ 145 Proxy: proxyHandler, 146 Dialer: h, 147 Strategy: mux.ClientStrategy{ 148 MaxConcurrency: uint32(config.XudpConcurrency), 149 MaxConnection: 128, 150 }, 151 }, 152 }, 153 } 154 } 155 h.udp443 = config.XudpProxyUDP443 156 } 157 } 158 159 h.proxy = proxyHandler 160 return h, nil 161 } 162 163 // Tag implements outbound.Handler. 164 func (h *Handler) Tag() string { 165 return h.tag 166 } 167 168 // Dispatch implements proxy.Outbound.Dispatch. 169 func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { 170 outbound := session.OutboundFromContext(ctx) 171 if outbound.Target.Network == net.Network_UDP && outbound.OriginalTarget.Address != nil && outbound.OriginalTarget.Address != outbound.Target.Address { 172 link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: outbound.Target.Address, OriginalDest: outbound.OriginalTarget.Address} 173 link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: outbound.Target.Address, OriginalDest: outbound.OriginalTarget.Address} 174 } 175 if h.mux != nil { 176 test := func(err error) { 177 if err != nil { 178 err := newError("failed to process mux outbound traffic").Base(err) 179 session.SubmitOutboundErrorToOriginator(ctx, err) 180 err.WriteToLog(session.ExportIDToError(ctx)) 181 common.Interrupt(link.Writer) 182 } 183 } 184 if outbound.Target.Network == net.Network_UDP && outbound.Target.Port == 443 { 185 switch h.udp443 { 186 case "reject": 187 test(newError("XUDP rejected UDP/443 traffic").AtInfo()) 188 return 189 case "skip": 190 goto out 191 } 192 } 193 if h.xudp != nil && outbound.Target.Network == net.Network_UDP { 194 if !h.xudp.Enabled { 195 goto out 196 } 197 test(h.xudp.Dispatch(ctx, link)) 198 return 199 } 200 if h.mux.Enabled { 201 test(h.mux.Dispatch(ctx, link)) 202 return 203 } 204 } 205 out: 206 err := h.proxy.Process(ctx, link, h) 207 if err != nil { 208 if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, context.Canceled) { 209 err = nil 210 } 211 } 212 if err != nil { 213 // Ensure outbound ray is properly closed. 214 err := newError("failed to process outbound traffic").Base(err) 215 session.SubmitOutboundErrorToOriginator(ctx, err) 216 err.WriteToLog(session.ExportIDToError(ctx)) 217 common.Interrupt(link.Writer) 218 } else { 219 common.Close(link.Writer) 220 } 221 common.Interrupt(link.Reader) 222 } 223 224 // Address implements internet.Dialer. 225 func (h *Handler) Address() net.Address { 226 if h.senderSettings == nil || h.senderSettings.Via == nil { 227 return nil 228 } 229 return h.senderSettings.Via.AsAddress() 230 } 231 232 // Dial implements internet.Dialer. 233 func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) { 234 if h.senderSettings != nil { 235 if h.senderSettings.ProxySettings.HasTag() { 236 tag := h.senderSettings.ProxySettings.Tag 237 handler := h.outboundManager.GetHandler(tag) 238 if handler != nil { 239 newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx)) 240 ctx = session.ContextWithOutbound(ctx, &session.Outbound{ 241 Target: dest, 242 }) 243 244 opts := pipe.OptionsFromContext(ctx) 245 uplinkReader, uplinkWriter := pipe.New(opts...) 246 downlinkReader, downlinkWriter := pipe.New(opts...) 247 248 go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}) 249 conn := cnc.NewConnection(cnc.ConnectionInputMulti(uplinkWriter), cnc.ConnectionOutputMulti(downlinkReader)) 250 251 if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil { 252 tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) 253 conn = tls.Client(conn, tlsConfig) 254 } 255 256 return h.getStatCouterConnection(conn), nil 257 } 258 259 newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) 260 } 261 262 if h.senderSettings.Via != nil { 263 outbound := session.OutboundFromContext(ctx) 264 if outbound == nil { 265 outbound = new(session.Outbound) 266 ctx = session.ContextWithOutbound(ctx, outbound) 267 } 268 outbound.Gateway = h.senderSettings.Via.AsAddress() 269 } 270 } 271 272 if conn, err := h.getUoTConnection(ctx, dest); err != os.ErrInvalid { 273 return conn, err 274 } 275 276 conn, err := internet.Dial(ctx, dest, h.streamSettings) 277 return h.getStatCouterConnection(conn), err 278 } 279 280 func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection { 281 if h.uplinkCounter != nil || h.downlinkCounter != nil { 282 return &stat.CounterConnection{ 283 Connection: conn, 284 ReadCounter: h.downlinkCounter, 285 WriteCounter: h.uplinkCounter, 286 } 287 } 288 return conn 289 } 290 291 // GetOutbound implements proxy.GetOutbound. 292 func (h *Handler) GetOutbound() proxy.Outbound { 293 return h.proxy 294 } 295 296 // Start implements common.Runnable. 297 func (h *Handler) Start() error { 298 return nil 299 } 300 301 // Close implements common.Closable. 302 func (h *Handler) Close() error { 303 common.Close(h.mux) 304 return nil 305 }