问题
The getaddrinfo()
function not only allows for client programs to efficiently find the correct data for creating a socket to a given host, it also allows for servers to bind to the correct socket - in theory.
I just learned about that and started to play around with it via Python:
from socket import *
for i in getaddrinfo(None, 22, AF_UNSPEC, SOCK_STREAM, IPPROTO_IP, AI_PASSIVE): i
yields
(2, 1, 6, '', ('0.0.0.0', 22))
(10, 1, 6, '', ('::', 22, 0, 0))
what makes me wonder about if there is something wrong.
What exactly am I supposed to do with these answers? Should I
- make a
listen()
ing socket of all of these answers, or should I - just pick the first one which really works?
The example in the manpage suggests me to only take the first one and be happy with it if it is error-free, but then I only get a connection via IPv4 n my example.
But if I try all of them, I have to worry with 2 server sockets, which is unnecessary due to the fact that IPv6 server sockets also listen to IPv4 if certain conditions are met (OS, socket flags etc.).
Where am I thinking wrong?
EDIT: Obviously, I'm not thinking wrong, but my PC does the wrong thing. I use the default /etc/gai.conf
shipped with OpenSUSE. It would be nice if anyone could point me towards the right direction.
EDIT 2: In the given case, strace
gives the following calls made internally after reading /etc/gai.conf
(now with port 54321, as I thought that using port 22 might have some bad influence, which was not the case):
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET6, sin6_port=htons(54321), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(3, {sa_family=AF_INET6, sin6_port=htons(38289), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
connect(3, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(54321), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
getsockname(3, {sa_family=AF_INET6, sin6_port=htons(60866), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
close(3) = 0
Obviously, the decision is intended to take place according to the results of the getsockname()
calls...
BTW: https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/673708 and the other bug reports mentionned there confirm my observations. Several people there claim that the new behaviour is correct, so I'm obviously stuck to using AF_INET6
... :-(
回答1:
Your getaddrinfo
is returning the wrong result for some reason. It's supposed to return the IPv6 socket first. The only thing I can think of is if your OS detects that your system has a low prio IPv6 (6to4 or Teredo) and avoids them, IMO wrongly so in that case. Edit: Just noticed my own computer does the same thing, I use 6to4.
However, you can either listen to both of them, or use AF_INET6
instead of AF_UNSPEC
. Then you can do setsockopt to disable IPV6_V6ONLY
.
getaddrinfo does the reasonable thing here and returns all applicable results (though in the wrong order, as I mentioned). Both one and two listen sockets are valid approaches, depending on your application.
回答2:
JFTR: It seems now that the program given in the manpage is wrong.
There are two possible approaches for listening to both IP types:
Create only a IPv6 socket and switch off the v6 only flag:
from socket import * s = socket(AF_INET6, SOCK_STREAM) s.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 0) s.bind(...)
resp.
from socket import * ai = getaddrinfo(None, ..., AF_INET6, SOCK_STREAM, 0, AI_PASSIVE)[0] s = socket(ai[0], ai[1], ai[2]) s.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 0) s.bind(ai[4])
Pros:
- simpler to handle
Cons:
- doesn't work under XP (AFAIK) - there are two different protocol stacks
work with two sockets and switch on the v6only flag:
from socket import * aii = getaddrinfo(None, ..., AF_UNSPEC, SOCK_STREAM, 0, AI_PASSIVE) sl = [] for ai in aii: s = socket(ai[0], ai[1], ai[2]) if ai[0] == AF_INET6: s.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 1) s.bind(ai[4]) sl.append(s)
and handle all sockets in
sl
in accepting loop (useselect()
or nonblocking IO to do so)Pros:
- uses a (nearly) protocol independent handling with
getaddrinfo()
- works under XP as well
Cons:
- complicated to handle
- uses a (nearly) protocol independent handling with
来源:https://stackoverflow.com/questions/8113805/usage-of-getaddrinfo-with-ai-passive