Skip to content

Add AsIs in outbound mode #1419

@HirbodBehnam

Description

@HirbodBehnam

Is your feature request related to a problem? Please describe.
One of the problems with the Happy Eyeballs implementation in Hysteria is that it does send both IPv4 and IPv6 requests at the same time and thus, creates a race between them. This makes the websites see your connections fluctuating between server's IPv6 and IPv4. However, in Golang's Happy Eyeballs implementation (and I'm fairly certain even in curl's implementation), the IPv6 connections will be fired at first and if there is no response for 300ms, it will initiate another connection using IPv4. This timeout is configurable in net.Dialer. Here is Hysteria's implementation which creates a race between IPv4 and IPv6:

func (d *directOutbound) dualStackDialTCP(ipv4, ipv6 net.IP, port uint16) (net.Conn, error) {
ch := make(chan dialResult, 2)
go func() {
conn, err := d.dialTCP(ipv4, port)
ch <- dialResult{Conn: conn, Err: err}
}()
go func() {
conn, err := d.dialTCP(ipv6, port)
ch <- dialResult{Conn: conn, Err: err}
}()

Moreover, this is not a very efficient implementation as it will always create two sockets and close one of them on the server.

Describe the solution you'd like
There are two ways to improve this. The first one is to add a new mode to outbound named AsIs (like in Xray). What it should do, is that it should use the system's resolver (overrides the config's DNS) and Dialer.Dial in order to connect to the destination address. This will allow Golang to use their own Happy Eyeballs implementation which prefers IPv6 over IPv4.
Another way this could be achieved is to introduce a delay when dialing IPv4. For instance, in the IPv4 dialing function, inside dualStackDialTCP, we can just create a timer and wait for it for 300ms. If IPv6 connection succeeds, it will cancel the IPv4 timer and no connection will be made to the server. If IPv6 connection fails, it should immediately fire the IPv4 connection. Or, if the timer expires, a IPv4 connection will be made and will race the IPv6 connection.

Additional context
I would be glad to draft a pull request based if needed. Both of these methods should not be very hard to implement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions