github.com/Uhtred009/v2ray-core-1@v4.31.2+incompatible/app/proxyman/outbound/handler.go (about) 1 package outbound 2 3 import ( 4 "context" 5 6 "v2ray.com/core" 7 "v2ray.com/core/app/proxyman" 8 "v2ray.com/core/common" 9 "v2ray.com/core/common/mux" 10 "v2ray.com/core/common/net" 11 "v2ray.com/core/common/session" 12 "v2ray.com/core/features/outbound" 13 "v2ray.com/core/features/policy" 14 "v2ray.com/core/features/stats" 15 "v2ray.com/core/proxy" 16 "v2ray.com/core/transport" 17 "v2ray.com/core/transport/internet" 18 "v2ray.com/core/transport/internet/tls" 19 "v2ray.com/core/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.DialingWorkerFactory{ 112 Proxy: proxyHandler, 113 Dialer: h, 114 Strategy: mux.ClientStrategy{ 115 MaxConcurrency: config.Concurrency, 116 MaxConnection: 128, 117 }, 118 }, 119 }, 120 } 121 } 122 123 h.proxy = proxyHandler 124 return h, nil 125 } 126 127 // Tag implements outbound.Handler. 128 func (h *Handler) Tag() string { 129 return h.tag 130 } 131 132 // Dispatch implements proxy.Outbound.Dispatch. 133 func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) { 134 if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) { 135 if err := h.mux.Dispatch(ctx, link); err != nil { 136 newError("failed to process mux outbound traffic").Base(err).WriteToLog(session.ExportIDToError(ctx)) 137 common.Interrupt(link.Writer) 138 } 139 } else { 140 if err := h.proxy.Process(ctx, link, h); err != nil { 141 // Ensure outbound ray is properly closed. 142 newError("failed to process outbound traffic").Base(err).WriteToLog(session.ExportIDToError(ctx)) 143 common.Interrupt(link.Writer) 144 } else { 145 common.Must(common.Close(link.Writer)) 146 } 147 common.Interrupt(link.Reader) 148 } 149 } 150 151 // Address implements internet.Dialer. 152 func (h *Handler) Address() net.Address { 153 if h.senderSettings == nil || h.senderSettings.Via == nil { 154 return nil 155 } 156 return h.senderSettings.Via.AsAddress() 157 } 158 159 // Dial implements internet.Dialer. 160 func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { 161 if h.senderSettings != nil { 162 if h.senderSettings.ProxySettings.HasTag() { 163 tag := h.senderSettings.ProxySettings.Tag 164 handler := h.outboundManager.GetHandler(tag) 165 if handler != nil { 166 newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx)) 167 ctx = session.ContextWithOutbound(ctx, &session.Outbound{ 168 Target: dest, 169 }) 170 171 opts := pipe.OptionsFromContext(ctx) 172 uplinkReader, uplinkWriter := pipe.New(opts...) 173 downlinkReader, downlinkWriter := pipe.New(opts...) 174 175 go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}) 176 conn := net.NewConnection(net.ConnectionInputMulti(uplinkWriter), net.ConnectionOutputMulti(downlinkReader)) 177 178 if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil { 179 tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) 180 conn = tls.Client(conn, tlsConfig) 181 } 182 183 return h.getStatCouterConnection(conn), nil 184 } 185 186 newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) 187 } 188 189 if h.senderSettings.Via != nil { 190 outbound := session.OutboundFromContext(ctx) 191 if outbound == nil { 192 outbound = new(session.Outbound) 193 ctx = session.ContextWithOutbound(ctx, outbound) 194 } 195 outbound.Gateway = h.senderSettings.Via.AsAddress() 196 } 197 } 198 199 conn, err := internet.Dial(ctx, dest, h.streamSettings) 200 return h.getStatCouterConnection(conn), err 201 } 202 203 func (h *Handler) getStatCouterConnection(conn internet.Connection) internet.Connection { 204 if h.uplinkCounter != nil || h.downlinkCounter != nil { 205 return &internet.StatCouterConnection{ 206 Connection: conn, 207 ReadCounter: h.downlinkCounter, 208 WriteCounter: h.uplinkCounter, 209 } 210 } 211 return conn 212 } 213 214 // GetOutbound implements proxy.GetOutbound. 215 func (h *Handler) GetOutbound() proxy.Outbound { 216 return h.proxy 217 } 218 219 // Start implements common.Runnable. 220 func (h *Handler) Start() error { 221 return nil 222 } 223 224 // Close implements common.Closable. 225 func (h *Handler) Close() error { 226 common.Close(h.mux) 227 return nil 228 }