Reputation: 2072
I am trying to write a server application that listens to both IPv6 and IPv4 connections. The proper way to accomplish this seems to be listening on IPv6 address, which will also accept IPv4 connections.
The relevant piece of code is:
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, MYPORT, &hints, &res);
(pretty much copypasted from Beej's Guide)
The problem is that at least on my system, getaddrinfo
returns an entry with AF_INET
first and AF_INET6
second -- whereas client's getaddrinfo
returns AF_INET6
first, as per spec. With my naive approach, server picks IPv4, client picks IPv6 and the connection fails.
I tried to fix this by setting hints.ai_family = AF_INET6
, but that fails on systems where IPv6 is not available.
I see two obvious solutions:
a) try asking for IPv6 first and fall back to IPv4 if that fails, or
b) walk through results of getaddrinfo
, look for IPv6, and if is not present, pick the first entry
but i don't like either one too much ;) I feel that there should be a way to convince getaddrinfo
to do the right thing, or perhaps a different way to accomplish my goal.
Upvotes: 3
Views: 2950
Reputation: 43486
Your code should work the way you described. Unfortunately, there's a bug in glibc as described in launchpad bug #673708, which causes it to choose IPv4 first.
There is a work-around which can be done on each Linux computer on which you're running your server program: edit /etc/gai.conf
, enable all the default rules (uncomment them):
label ::1/128 0
label ::/0 1
label 2002::/16 2
label ::/96 3
label ::ffff:0:0/96 4
label fec0::/10 5
label fc00::/7 6
label 2001:0::/32 7
then add:
label ::ffff:7f00:1/128 8
Then your code should open IPv6 if supported, and will also accept IPv4 connections.
If the above is not practical (it's only practical if you're willing to change the configuration on every computer you run on), then modify your code to prefer IPv6. E.g. I've done this:
getaddrinfo()
results.IPV6_V6ONLY
socket option to support both IPv6 and IPv4.Upvotes: 2
Reputation: 239361
The order of the address that getaddrinfo()
returns is unspecified, so you have to be prepared to handle either case. That probably means traversing the list, keeping track of the "best address seen so far".
Alternatively, you could try to bind()
and listen()
on all the addresses returned by getaddrinfo()
. This is probably the best option, since some OSes do not accept IPv4 connections to IPv6 sockets listening on 0::0
.
Upvotes: 2