package dialer import ( "context" "net" "net/netip" "time" C "github.com/sagernet/sing-box/constant" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) func DialSerialNetwork(ctx context.Context, dialer N.Dialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) { if len(destinationAddresses) == 0 { if !destination.IsIP() { panic("invalid usage") } destinationAddresses = []netip.Addr{destination.Addr} } if parallelDialer, isParallel := dialer.(ParallelNetworkDialer); isParallel { return parallelDialer.DialParallelNetwork(ctx, network, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) } var errors []error if parallelDialer, isParallel := dialer.(ParallelInterfaceDialer); isParallel { for _, address := range destinationAddresses { conn, err := parallelDialer.DialParallelInterface(ctx, network, M.SocksaddrFrom(address, destination.Port), strategy, interfaceType, fallbackInterfaceType, fallbackDelay) if err == nil { return conn, nil } errors = append(errors, err) } } else { for _, address := range destinationAddresses { conn, err := dialer.DialContext(ctx, network, M.SocksaddrFrom(address, destination.Port)) if err == nil { return conn, nil } errors = append(errors, err) } } return nil, E.Errors(errors...) } func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, preferIPv6 bool, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) { if len(destinationAddresses) == 0 { if !destination.IsIP() { panic("invalid usage") } destinationAddresses = []netip.Addr{destination.Addr} } return N.DialParallel(ctx, &networkStrategyDialer{ ParallelInterfaceDialer: dialer, strategy: strategy, interfaceType: interfaceType, fallbackInterfaceType: fallbackInterfaceType, fallbackDelay: fallbackDelay, }, network, destination, destinationAddresses, preferIPv6, fallbackDelay) } type networkStrategyDialer struct { ParallelInterfaceDialer strategy *C.NetworkStrategy interfaceType []C.InterfaceType fallbackInterfaceType []C.InterfaceType fallbackDelay time.Duration } func (d *networkStrategyDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { return d.DialParallelInterface(ctx, network, destination, d.strategy, d.interfaceType, d.fallbackInterfaceType, d.fallbackDelay) } func ListenSerialNetworkPacket(ctx context.Context, dialer N.Dialer, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) { if len(destinationAddresses) == 0 { if !destination.IsIP() { panic("invalid usage") } destinationAddresses = []netip.Addr{destination.Addr} } if parallelDialer, isParallel := dialer.(ParallelNetworkDialer); isParallel { return parallelDialer.ListenSerialNetworkPacket(ctx, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) } var errors []error if parallelDialer, isParallel := dialer.(ParallelInterfaceDialer); isParallel { for _, address := range destinationAddresses { conn, err := parallelDialer.ListenSerialInterfacePacket(ctx, M.SocksaddrFrom(address, destination.Port), strategy, interfaceType, fallbackInterfaceType, fallbackDelay) if err == nil { return conn, address, nil } errors = append(errors, err) } } else { for _, address := range destinationAddresses { conn, err := dialer.ListenPacket(ctx, M.SocksaddrFrom(address, destination.Port)) if err == nil { return conn, address, nil } errors = append(errors, err) } } return nil, netip.Addr{}, E.Errors(errors...) }