Skip to content

net: Resolve{TCP,UDP,IP}Addr returns IPv4 address on host without IPv4 connectivity #28666

Open
@bluecmd

Description

@bluecmd

What version of Go are you using (go version)?

$ go version
go version go1.11rc2 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

Linux. The issue doesn't depend on CPU arch and has been reproduced on
ARMv5 and x86-64.

What did you do?

A minimal repro is this program:

package main

import (
	"log"
	"net"
)

func main() {
	r, _ := net.ResolveUDPAddr("udp", "ping.sunet.se:0") // IPv4 and IPv6 records
	log.Printf("Result of ResolveUDPAddr: %v", r)

	r2, _ := net.ResolveIPAddr("ip", "ping.sunet.se") // IPv4 and IPv6 records
	log.Printf("Result of ResolveIPAddr: %v", r2)

	r3, _ := net.ResolveIPAddr("ip", "ipv6.sunet.se") // Only IPv6 record
	log.Printf("Result of ResolveIPAddr: %v", r3)
}

This shows the behavior of ResolveUDPAddr and ResolveIPAddr with dual-stacked
hosts and a IPv6 only host as the last entry. This also applies to ResolveTCPAddr.

What did you expect to see?

On hosts that have an IPv6 and an IPv4 address, the IPv6 address is preferred
on hosts that has IPv6 only connectivity.

In the program below, the output should be something like this on an IPv6-only system:

[bluecmd]$ ./ipv6_dial
2018/11/08 16:22:56 Result of ResolveUDPAddr: 2001:6b0:7::14
2018/11/08 16:22:56 Result of ResolveIPAddr: 2001:6b0:7::14
2018/11/08 16:22:56 Result of ResolveIPAddr: 2001:6b0:7::14

What did you see instead?

This is the outcome on an IPv6 only system:

[bluecmd]$ sudo ip ro
[bluecmd]$ sudo ip -6 ro
[..]
default via fe80::200:5eff:fe00:265 dev eth0 proto ra metric 100 pref medium

Yet ResolveIPAddr prefers IPv4:

[bluecmd]$ ./ipv6_dial
2018/11/08 16:22:56 Result of ResolveUDPAddr: 192.36.125.18:0
2018/11/08 16:22:56 Result of ResolveIPAddr: 192.36.125.18
2018/11/08 16:22:56 Result of ResolveIPAddr: 2001:6b0:7::14

This is also not dependent on CGO.

For more logs, see here.

Extra information

Problematic code is most likely this part of ipsock.go:

go/src/net/ipsock.go

Lines 73 to 98 in 7da1f7a

func (addrs addrList) forResolve(network, addr string) Addr {
var want6 bool
switch network {
case "ip":
// IPv6 literal (addr does NOT contain a port)
want6 = count(addr, ':') > 0
case "tcp", "udp":
// IPv6 literal. (addr contains a port, so look for '[')
want6 = count(addr, '[') > 0
}
if want6 {
return addrs.first(isNotIPv4)
}
return addrs.first(isIPv4)
}
// first returns the first address which satisfies strategy, or if
// none do, then the first address of any kind.
func (addrs addrList) first(strategy func(Addr) bool) Addr {
for _, addr := range addrs {
if strategy(addr) {
return addr
}
}
return addrs[0]
}

My reading is that the code returns IPv4 unless IPv6 was explicitly asked for.
If the host is IPv6 only, then that host is picked on the fallback return in first().

I would suggest changing the code to not prefer IPv4 addresses if there is no IPv4 connectivity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions