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