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