fshttp: fix --bind 0.0.0.0 allowing IPv6 and --bind ::0 allowing IPv4

Due to a bug/misfeature in the go standard library as described here:
https://github.com/golang/go/issues/48723 the go standard library
binds to both IPv4 and IPv6 when passed 0.0.0.0 or ::0.

This patch detects the bind address and forces the correct IP
protocol.

Fixes #6124
Fixes #6244
See: https://forum.rclone.org/t/issues-with-bind-0-0-0-0-and-onedrive-getting-etag-mismatch-when-using-ipv6/41379/
This commit is contained in:
Nick Craig-Wood 2023-08-30 23:41:36 +01:00
parent d12a92eac9
commit cffe85e6c5
2 changed files with 14 additions and 0 deletions

View File

@ -641,6 +641,9 @@ IPv4 address (1.2.3.4), an IPv6 address (1234::789A) or host name. If
the host name doesn't resolve or resolves to more than one IP address the host name doesn't resolve or resolves to more than one IP address
it will give an error. it will give an error.
You can use `--bind 0.0.0.0` to force rclone to use IPv4 addresses and
`--bind ::0` to force rclone to use IPv6 addresses.
### --bwlimit=BANDWIDTH_SPEC ### ### --bwlimit=BANDWIDTH_SPEC ###
This option controls the bandwidth limit. For example This option controls the bandwidth limit. For example

View File

@ -52,6 +52,17 @@ var warnDSCPFail, warnDSCPWindows sync.Once
// DialContext connects to the network address using the provided context. // DialContext connects to the network address using the provided context.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
// If local address is 0.0.0.0 or ::0 force IPv4 or IPv6
// This works around https://github.com/golang/go/issues/48723
// Which means 0.0.0.0 and ::0 both bind to both IPv4 and IPv6
if ip, ok := d.Dialer.LocalAddr.(*net.TCPAddr); ok && ip.IP.IsUnspecified() && (network == "tcp" || network == "udp") {
if ip.IP.To4() != nil {
network += "4" // IPv4 address
} else {
network += "6" // IPv6 address
}
}
c, err := d.Dialer.DialContext(ctx, network, address) c, err := d.Dialer.DialContext(ctx, network, address)
if err != nil { if err != nil {
return c, err return c, err