package internet import ( "context" core "github.com/v2fly/v2ray-core/v4" "github.com/v2fly/v2ray-core/v4/features/routing" "github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/session" ) // Dialer is the interface for dialing outbound connections. type Dialer interface { // Dial dials a system connection to the given destination. Dial(ctx context.Context, destination net.Destination) (Connection, error) // Address returns the address used by this Dialer. Maybe nil if not known. Address() net.Address } // dialFunc is an interface to dial network connection to a specific destination. type dialFunc func(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (Connection, error) var ( transportDialerCache = make(map[string]dialFunc) ) // RegisterTransportDialer registers a Dialer with given name. func RegisterTransportDialer(protocol string, dialer dialFunc) error { if _, found := transportDialerCache[protocol]; found { return newError(protocol, " dialer already registered").AtError() } transportDialerCache[protocol] = dialer return nil } // Dial dials a internet connection towards the given destination. func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (Connection, error) { if dest.Network == net.Network_TCP { if streamSettings == nil { s, err := ToMemoryStreamConfig(nil) if err != nil { return nil, newError("failed to create default stream settings").Base(err) } streamSettings = s } protocol := streamSettings.ProtocolName dialer := transportDialerCache[protocol] if dialer == nil { return nil, newError(protocol, " dialer not registered").AtError() } return dialer(ctx, dest, streamSettings) } if dest.Network == net.Network_UDP { udpDialer := transportDialerCache["udp"] if udpDialer == nil { return nil, newError("UDP dialer not registered").AtError() } return udpDialer(ctx, dest, streamSettings) } return nil, newError("unknown network ", dest.Network) } // DialSystem calls system dialer to create a network connection. func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { var src net.Address if outbound := session.OutboundFromContext(ctx); outbound != nil { src = outbound.Gateway } if transportLayerOutgoingTag := session.GetTransportLayerProxyTagFromContext(ctx); transportLayerOutgoingTag != "" { return DialTaggedOutbound(ctx, dest, transportLayerOutgoingTag) } return effectiveSystemDialer.Dial(ctx, src, dest, sockopt) } func DialTaggedOutbound(ctx context.Context, dest net.Destination, tag string) (net.Conn, error) { var dispatcher routing.Dispatcher if err := core.RequireFeatures(ctx, func(dispatcherInstance routing.Dispatcher) { dispatcher = dispatcherInstance }); err != nil { return nil, newError("Required Feature dispatcher not resolved").Base(err) } content := new(session.Content) content.SkipDNSResolve = true session.SetForcedOutboundTagToContext(ctx, tag) ctx = session.ContextWithContent(ctx, content) r, err := dispatcher.Dispatch(ctx, dest) if err != nil { return nil, err } var readerOpt net.ConnectionOption if dest.Network == net.Network_TCP { readerOpt = net.ConnectionOutputMulti(r.Reader) } else { readerOpt = net.ConnectionOutputMultiUDP(r.Reader) } return net.NewConnection(net.ConnectionInputMulti(r.Writer), readerOpt), nil }