github.com/xraypb/xray-core@v1.6.6/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: h.senderSettings.MultiplexSettings.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 } 126 } 127 128 h.proxy = proxyHandler 129 return h, nil 130 } 131 132 // Tag implements outbound.Handler. 133 func (h *Handler) Tag() string { 134 return h.tag 135 } 136 137 // Dispatch implements proxy.Outbound.Dispatch. 138 func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { 139 if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) { 140 if err := h.mux.Dispatch(ctx, link); err != nil { 141 err := newError("failed to process mux outbound traffic").Base(err) 142 session.SubmitOutboundErrorToOriginator(ctx, err) 143 err.WriteToLog(session.ExportIDToError(ctx)) 144 common.Interrupt(link.Writer) 145 } 146 } else { 147 err := h.proxy.Process(ctx, link, h) 148 if err != nil { 149 if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, context.Canceled) { 150 err = nil 151 } 152 } 153 if err != nil { 154 // Ensure outbound ray is properly closed. 155 err := newError("failed to process outbound traffic").Base(err) 156 session.SubmitOutboundErrorToOriginator(ctx, err) 157 err.WriteToLog(session.ExportIDToError(ctx)) 158 common.Interrupt(link.Writer) 159 } else { 160 common.Must(common.Close(link.Writer)) 161 } 162 common.Interrupt(link.Reader) 163 } 164 } 165 166 // Address implements internet.Dialer. 167 func (h *Handler) Address() net.Address { 168 if h.senderSettings == nil || h.senderSettings.Via == nil { 169 return nil 170 } 171 return h.senderSettings.Via.AsAddress() 172 } 173 174 // Dial implements internet.Dialer. 175 func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) { 176 if h.senderSettings != nil { 177 if h.senderSettings.ProxySettings.HasTag() { 178 tag := h.senderSettings.ProxySettings.Tag 179 handler := h.outboundManager.GetHandler(tag) 180 if handler != nil { 181 newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx)) 182 ctx = session.ContextWithOutbound(ctx, &session.Outbound{ 183 Target: dest, 184 }) 185 186 opts := pipe.OptionsFromContext(ctx) 187 uplinkReader, uplinkWriter := pipe.New(opts...) 188 downlinkReader, downlinkWriter := pipe.New(opts...) 189 190 go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}) 191 conn := cnc.NewConnection(cnc.ConnectionInputMulti(uplinkWriter), cnc.ConnectionOutputMulti(downlinkReader)) 192 193 if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil { 194 tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) 195 conn = tls.Client(conn, tlsConfig) 196 } 197 198 return h.getStatCouterConnection(conn), nil 199 } 200 201 newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) 202 } 203 204 if h.senderSettings.Via != nil { 205 outbound := session.OutboundFromContext(ctx) 206 if outbound == nil { 207 outbound = new(session.Outbound) 208 ctx = session.ContextWithOutbound(ctx, outbound) 209 } 210 outbound.Gateway = h.senderSettings.Via.AsAddress() 211 } 212 } 213 214 if conn, err := h.getUoTConnection(ctx, dest); err != os.ErrInvalid { 215 return conn, err 216 } 217 218 conn, err := internet.Dial(ctx, dest, h.streamSettings) 219 return h.getStatCouterConnection(conn), err 220 } 221 222 func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection { 223 if h.uplinkCounter != nil || h.downlinkCounter != nil { 224 return &stat.CounterConnection{ 225 Connection: conn, 226 ReadCounter: h.downlinkCounter, 227 WriteCounter: h.uplinkCounter, 228 } 229 } 230 return conn 231 } 232 233 // GetOutbound implements proxy.GetOutbound. 234 func (h *Handler) GetOutbound() proxy.Outbound { 235 return h.proxy 236 } 237 238 // Start implements common.Runnable. 239 func (h *Handler) Start() error { 240 return nil 241 } 242 243 // Close implements common.Closable. 244 func (h *Handler) Close() error { 245 common.Close(h.mux) 246 return nil 247 }