github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/dispatcher/default.go (about) 1 package dispatcher 2 3 //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen 4 5 import ( 6 "context" 7 "strings" 8 "sync" 9 "time" 10 11 core "github.com/v2fly/v2ray-core/v5" 12 "github.com/v2fly/v2ray-core/v5/common" 13 "github.com/v2fly/v2ray-core/v5/common/buf" 14 "github.com/v2fly/v2ray-core/v5/common/log" 15 "github.com/v2fly/v2ray-core/v5/common/net" 16 "github.com/v2fly/v2ray-core/v5/common/protocol" 17 "github.com/v2fly/v2ray-core/v5/common/session" 18 "github.com/v2fly/v2ray-core/v5/common/strmatcher" 19 "github.com/v2fly/v2ray-core/v5/features/outbound" 20 "github.com/v2fly/v2ray-core/v5/features/policy" 21 "github.com/v2fly/v2ray-core/v5/features/routing" 22 routing_session "github.com/v2fly/v2ray-core/v5/features/routing/session" 23 "github.com/v2fly/v2ray-core/v5/features/stats" 24 "github.com/v2fly/v2ray-core/v5/transport" 25 "github.com/v2fly/v2ray-core/v5/transport/pipe" 26 ) 27 28 var errSniffingTimeout = newError("timeout on sniffing") 29 30 type cachedReader struct { 31 sync.Mutex 32 reader *pipe.Reader 33 cache buf.MultiBuffer 34 } 35 36 func (r *cachedReader) Cache(b *buf.Buffer) { 37 mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100) 38 r.Lock() 39 if !mb.IsEmpty() { 40 r.cache, _ = buf.MergeMulti(r.cache, mb) 41 } 42 b.Clear() 43 rawBytes := b.Extend(buf.Size) 44 n := r.cache.Copy(rawBytes) 45 b.Resize(0, int32(n)) 46 r.Unlock() 47 } 48 49 func (r *cachedReader) readInternal() buf.MultiBuffer { 50 r.Lock() 51 defer r.Unlock() 52 53 if r.cache != nil && !r.cache.IsEmpty() { 54 mb := r.cache 55 r.cache = nil 56 return mb 57 } 58 59 return nil 60 } 61 62 func (r *cachedReader) ReadMultiBuffer() (buf.MultiBuffer, error) { 63 mb := r.readInternal() 64 if mb != nil { 65 return mb, nil 66 } 67 68 return r.reader.ReadMultiBuffer() 69 } 70 71 func (r *cachedReader) ReadMultiBufferTimeout(timeout time.Duration) (buf.MultiBuffer, error) { 72 mb := r.readInternal() 73 if mb != nil { 74 return mb, nil 75 } 76 77 return r.reader.ReadMultiBufferTimeout(timeout) 78 } 79 80 func (r *cachedReader) Interrupt() { 81 r.Lock() 82 if r.cache != nil { 83 r.cache = buf.ReleaseMulti(r.cache) 84 } 85 r.Unlock() 86 r.reader.Interrupt() 87 } 88 89 // DefaultDispatcher is a default implementation of Dispatcher. 90 type DefaultDispatcher struct { 91 ohm outbound.Manager 92 router routing.Router 93 policy policy.Manager 94 stats stats.Manager 95 } 96 97 func init() { 98 common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { 99 d := new(DefaultDispatcher) 100 if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error { 101 return d.Init(config.(*Config), om, router, pm, sm) 102 }); err != nil { 103 return nil, err 104 } 105 return d, nil 106 })) 107 } 108 109 // Init initializes DefaultDispatcher. 110 func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error { 111 d.ohm = om 112 d.router = router 113 d.policy = pm 114 d.stats = sm 115 return nil 116 } 117 118 // Type implements common.HasType. 119 func (*DefaultDispatcher) Type() interface{} { 120 return routing.DispatcherType() 121 } 122 123 // Start implements common.Runnable. 124 func (*DefaultDispatcher) Start() error { 125 return nil 126 } 127 128 // Close implements common.Closable. 129 func (*DefaultDispatcher) Close() error { return nil } 130 131 func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *transport.Link) { 132 opt := pipe.OptionsFromContext(ctx) 133 uplinkReader, uplinkWriter := pipe.New(opt...) 134 downlinkReader, downlinkWriter := pipe.New(opt...) 135 136 inboundLink := &transport.Link{ 137 Reader: downlinkReader, 138 Writer: uplinkWriter, 139 } 140 141 outboundLink := &transport.Link{ 142 Reader: uplinkReader, 143 Writer: downlinkWriter, 144 } 145 146 sessionInbound := session.InboundFromContext(ctx) 147 var user *protocol.MemoryUser 148 if sessionInbound != nil { 149 user = sessionInbound.User 150 } 151 152 if user != nil && len(user.Email) > 0 { 153 p := d.policy.ForLevel(user.Level) 154 if p.Stats.UserUplink { 155 name := "user>>>" + user.Email + ">>>traffic>>>uplink" 156 if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil { 157 inboundLink.Writer = &SizeStatWriter{ 158 Counter: c, 159 Writer: inboundLink.Writer, 160 } 161 } 162 } 163 if p.Stats.UserDownlink { 164 name := "user>>>" + user.Email + ">>>traffic>>>downlink" 165 if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil { 166 outboundLink.Writer = &SizeStatWriter{ 167 Counter: c, 168 Writer: outboundLink.Writer, 169 } 170 } 171 } 172 } 173 174 return inboundLink, outboundLink 175 } 176 177 func shouldOverride(result SniffResult, domainOverride []string) bool { 178 if result.Domain() == "" { 179 return false 180 } 181 protocolString := result.Protocol() 182 if resComp, ok := result.(SnifferResultComposite); ok { 183 protocolString = resComp.ProtocolForDomainResult() 184 } 185 for _, p := range domainOverride { 186 if strings.HasPrefix(protocolString, p) || strings.HasSuffix(protocolString, p) { 187 return true 188 } 189 if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok { 190 if resultSubset.IsProtoSubsetOf(p) { 191 return true 192 } 193 } 194 } 195 return false 196 } 197 198 // Dispatch implements routing.Dispatcher. 199 func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (*transport.Link, error) { 200 if !destination.IsValid() { 201 panic("Dispatcher: Invalid destination.") 202 } 203 ob := &session.Outbound{ 204 Target: destination, 205 } 206 ctx = session.ContextWithOutbound(ctx, ob) 207 208 inbound, outbound := d.getLink(ctx) 209 content := session.ContentFromContext(ctx) 210 if content == nil { 211 content = new(session.Content) 212 ctx = session.ContextWithContent(ctx, content) 213 } 214 sniffingRequest := content.SniffingRequest 215 if !sniffingRequest.Enabled { 216 go d.routedDispatch(ctx, outbound, destination) 217 } else { 218 go func() { 219 cReader := &cachedReader{ 220 reader: outbound.Reader.(*pipe.Reader), 221 } 222 outbound.Reader = cReader 223 result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network) 224 if err == nil { 225 content.Protocol = result.Protocol() 226 } 227 if err == nil && shouldOverride(result, sniffingRequest.OverrideDestinationForProtocol) { 228 if domain, err := strmatcher.ToDomain(result.Domain()); err == nil { 229 newError("sniffed domain: ", domain, " for ", destination).WriteToLog(session.ExportIDToError(ctx)) 230 destination.Address = net.ParseAddress(domain) 231 ob.Target = destination 232 } 233 } 234 d.routedDispatch(ctx, outbound, destination) 235 }() 236 } 237 238 return inbound, nil 239 } 240 241 func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) { 242 payload := buf.New() 243 defer payload.Release() 244 245 sniffer := NewSniffer(ctx) 246 247 metaresult, metadataErr := sniffer.SniffMetadata(ctx) 248 249 if metadataOnly { 250 return metaresult, metadataErr 251 } 252 253 contentResult, contentErr := func() (SniffResult, error) { 254 totalAttempt := 0 255 for { 256 select { 257 case <-ctx.Done(): 258 return nil, ctx.Err() 259 default: 260 totalAttempt++ 261 if totalAttempt > 2 { 262 return nil, errSniffingTimeout 263 } 264 265 cReader.Cache(payload) 266 if !payload.IsEmpty() { 267 result, err := sniffer.Sniff(ctx, payload.Bytes(), network) 268 if err != common.ErrNoClue { 269 return result, err 270 } 271 } 272 if payload.IsFull() { 273 return nil, errUnknownContent 274 } 275 } 276 } 277 }() 278 if contentErr != nil && metadataErr == nil { 279 return metaresult, nil 280 } 281 if contentErr == nil && metadataErr == nil { 282 return CompositeResult(metaresult, contentResult), nil 283 } 284 return contentResult, contentErr 285 } 286 287 func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) { 288 var handler outbound.Handler 289 290 if forcedOutboundTag := session.GetForcedOutboundTagFromContext(ctx); forcedOutboundTag != "" { 291 ctx = session.SetForcedOutboundTagToContext(ctx, "") 292 if h := d.ohm.GetHandler(forcedOutboundTag); h != nil { 293 newError("taking platform initialized detour [", forcedOutboundTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) 294 handler = h 295 } else { 296 newError("non existing tag for platform initialized detour: ", forcedOutboundTag).AtError().WriteToLog(session.ExportIDToError(ctx)) 297 common.Close(link.Writer) 298 common.Interrupt(link.Reader) 299 return 300 } 301 } else if d.router != nil { 302 if route, err := d.router.PickRoute(routing_session.AsRoutingContext(ctx)); err == nil { 303 tag := route.GetOutboundTag() 304 if h := d.ohm.GetHandler(tag); h != nil { 305 newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) 306 handler = h 307 } else { 308 newError("non existing tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx)) 309 } 310 } else { 311 newError("default route for ", destination).AtWarning().WriteToLog(session.ExportIDToError(ctx)) 312 } 313 } 314 315 if handler == nil { 316 handler = d.ohm.GetDefaultHandler() 317 } 318 319 if handler == nil { 320 newError("default outbound handler not exist").WriteToLog(session.ExportIDToError(ctx)) 321 common.Close(link.Writer) 322 common.Interrupt(link.Reader) 323 return 324 } 325 326 if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil { 327 if tag := handler.Tag(); tag != "" { 328 accessMessage.Detour = tag 329 if d.policy.ForSystem().OverrideAccessLogDest { 330 accessMessage.To = destination 331 } 332 } 333 log.Record(accessMessage) 334 } 335 336 handler.Dispatch(ctx, link) 337 }