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