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
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 ###
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.
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)
if err != nil {
return c, err