Using getaddrinfo () with AI_PASSIVE

The getaddrinfo() function not only allows client programs to efficiently find the correct data to create a socket for a given host, but also allows servers to bind to the correct socket - theoretically.

I just found out about it and started playing with it through Python:

 from socket import * for i in getaddrinfo(None, 22, AF_UNSPEC, SOCK_STREAM, IPPROTO_IP, AI_PASSIVE): i 

gives

 (2, 1, 6, '', ('0.0.0.0', 22)) (10, 1, 6, '', ('::', 22, 0, 0)) 

which makes me think that something is wrong.

What exactly should I do with these answers? Should I

  • do listen() ing the socket of all these answers, or should I
  • just pick the first one that really works?

The example in the manpage suggests that I only take the first one and be satisfied if it is error free, but then I only get the connection through IPv4 in my example.

But if I try all of them, I have to worry with 2 server sockets, which is not necessary because IPv6 server sockets also listen on IPv4 if certain conditions are met (OS, socket flags, etc.) .

Where am I thinking wrong?


EDIT: Obviously, I'm not mistaken, but my computer is doing the wrong thing. I use the default /etc/gai.conf that ships with OpenSUSE. It would be nice if someone could point me in the right direction.

EDIT 2: In this case, strace issues internal calls after reading /etc/gai.conf (now with port 54321, since I thought using port 22 might have some bad effect, which was not)

 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 solution should be in accordance with the results of getsockname() ...

BTW: https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/673708 , and the other error messages mentioned there confirm my observations. Some people claim that the new behavior is correct, so I obviously stick to using AF_INET6 ... AF_INET6

+6
sockets serversocket getaddrinfo
source share
2 answers

For some reason, your getaddrinfo returns the wrong result. It is assumed that it will first return the IPv6 socket. The only thing I can think of is that your OS detects that your system has low IPv6 prio (6to4 or Teredo) and avoids them, IMO is wrong, so in this case. Edit: just noticed that my own computer is doing the same thing, I am using 6to4.

However, you can either listen to them or use AF_INET6 instead of AF_UNSPEC . You can then do setsockopt to disable IPV6_V6ONLY .

getaddrinfo does a reasonable thing here and returns all the applicable results (albeit in the wrong order, as I mentioned). Both one and two listening jacks are valid approaches, depending on your application.

+3
source share

JFTR: It seems that the program specified in the manpage is incorrect.

There are two possible approaches for listening to both types of IP:

  • Create only an IPv6 socket and disable the v6 flag only:

     from socket import * s = socket(AF_INET6, SOCK_STREAM) s.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 0) s.bind(...) 

    respectively.

     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:

    • easier to handle

    Minuses:

    • does not work under XP (AFAIK) - there are two different protocol stacks.
  • work with two sockets and enable 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 the accept loop (use select() or non-blocking IO for this)

    Pros:

    • uses (almost) protocol independent processing with getaddrinfo()
    • also works under XP

    Minuses:

    • difficult to handle
0
source share

All Articles