Ensure :: in -http argument ends up as IPv6 socket
Do a first pass on the argument's host port alone to derive the address family in this specific case before leaving it up to Golang.
The empty addresses 0.0.0.0 and :: are resolved differently depending
on the OS. At least on OpenBSD, which does not support dual-family sockets
like Linux, net.Dial() turns both into an IPv4 wildcard socket unless the
"tcp6" network (Golang terminology) is requested.
Therefore, such systems are unable to have dms listen on all IPv6 addresses.
Imho, this should be fixed in Golang proper, but that's a whole different story; until then, this is enough to make dms work in my IPv6-only network as tested with an Android-based appliance using VLC:
$ fstat -p $(pgrep dms) | grep internet
_dms dms 91669 3* internet6 stream tcp 0x0 *:1338
_dms dms 91669 7* internet6 dgram udp *:1900
_dms dms 91669 8* internet6 dgram udp *:1900
To illustrate the problem:
$ go version
go version go1.25.1 openbsd/amd64
$ cat listener.go
package main
import (
"fmt"
"net"
)
func main() {
nets := []string{"tcp", "tcp4", "tcp6"}
addrs := []string{"", "0.0.0.0", "127.0.0.1", "[::]", "[::1]"}
for _, n := range nets {
for _, a := range addrs {
fmt.Printf("%4s %7s => ", n, a)
l, err := net.Listen(n, a + ":1338")
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("%14s\n", l.Addr())
l.Close()
}
}
}
}
$ go run listener.go
tcp => 0.0.0.0:1338
tcp 0.0.0.0 => 0.0.0.0:1338
tcp 127.0.0.1 => 127.0.0.1:1338
tcp [::] => 0.0.0.0:1338
tcp [::1] => [::1]:1338
tcp4 => 0.0.0.0:1338
tcp4 0.0.0.0 => 0.0.0.0:1338
tcp4 127.0.0.1 => 127.0.0.1:1338
tcp4 [::] => 0.0.0.0:1338
tcp4 [::1] => listen tcp4: address ::1: no suitable address found
tcp6 => [::]:1338
tcp6 0.0.0.0 => listen tcp6: address 0.0.0.0: no suitable address found
tcp6 127.0.0.1 => listen tcp6: address 127.0.0.1: no suitable address found
tcp6 [::] => [::]:1338
tcp6 [::1] => [::1]:1338
Seems weird, shouldn't the dual stack stuff take care of this? Can't you specify just :1338? I know on some platforms it's not automatic that implies INADDR_ANY, I've seen plenty of standard libraries screw this up like ponylang.
Seems weird, shouldn't the dual stack stuff take care of this?
What "stuff" do you mean?
Can't you specify just
:1338?
No, the test program tried that and Golang prefers IPv4 for "tcp", hence explicit "tcp6" is the only way to get an IPv6 socket:
tcp => 0.0.0.0:1338
...
tcp4 => 0.0.0.0:1338
...
tcp6 => [::]:1338
...
I know on some platforms it's not automatic that implies INADDR_ANY, I've seen plenty of standard libraries screw this up like ponylang.
INADDR_ANY is chosen and that's the problem, because it is IPv4 (only), whereas I need an IPv6 socket aka IN6ADDR_ANY_INIT:
/usr/include/netinet/in.h:#define INADDR_ANY __IPADDR(0x00000000)
--
/usr/include/netinet6/in6.h:#define IN6ADDR_ANY_INIT \
/usr/include/netinet6/in6.h- {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
/usr/include/netinet6/in6.h- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}
@anacrolix Hi, what do you think?
I believe it might be better to use net.ParseIP then check for Is6 or Is4 to select the family.
I believe it might be better to use net.ParseIP then check for Is6 or Is4 to select the family.
Could do, but then you'd have to extract the IP address from -http [address]:port first, which seems like hand-rolling too much already.
Looking for [ is a hack as well, really, but at least it's super simple and specific for literal IPv6 addresses in a way that does not break or even change behaviour for other -http arguments.
There is net.Split or Parse HostPort or something to that effect. I'm on my phone or I'd check but I'm sure it's in there
There is net.Split or Parse HostPort or something to that effect. I'm on my phone or I'd check but I'm sure it's in there
Done, thanks.
Did you mean to have strings.Contains(... "::") or have I missed something?
Did you mean to have strings.Contains(... "::") or have I missed something?
I did not; where exactly?
Looking for :: in the argument would match compressed addresses like 2001:db8::1, so no.
And host is already the full address extracted from the argument, so that should match exactly likewise.
Golang misinterprets only the unspecified address ::, any other literal IPv6 address with at least one bit set results in "tcp6" being used, as expected.
Ah, I mean I might want to listen on a IPv6 address that isn't just ::. That also requires explicit tcp6 network on bsd? If not we're good to go
Ah, I mean I might want to listen on a IPv6 address that isn't just ::. That also requires explicit tcp6 network on bsd? If not we're good to go
No, that works already as Golang picks "tcp4/6" correctly for literal IPv4/6 addresses.
This PR is entirely about the :: edge case, nothing else.