getaddrinfo()与AI_PASSIVE的结合使用 [英] Usage of getaddrinfo() with AI_PASSIVE

查看:658
本文介绍了getaddrinfo()与AI_PASSIVE的结合使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

getaddrinfo()函数不仅允许客户端程序有效地找到正确的数据以创建到给定主机的套接字,而且还允许服务器绑定到正确的套接字-理论上.

我只是了解了这一点并开始玩通过Python进行处理:

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

收益

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

是什么让我怀疑是否有问题.

这些答案到底应该怎么做?我应该

  • 为所有这些答案做一个listen() ing插座,否则我应该
  • 只选择第一个真正有效的方法?

联机帮助页中的示例建议我只选择第一个并成为如果它没有错误,就会对此感到满意,但是我在我的示例中仅通过IPv4获得了连接.

但是,如果我尝试全部尝试,则必须担心2个服务器套接字,这是不必要的,因为如果满足某些条件(OS,套接字标志等),IPv6服务器套接字也会侦听IPv4./p>

我在哪里想错了?


显然,我没有想错,但是我的PC做错了事.我使用OpenSUSE随附的默认/etc/gai.conf.如果有人能指出我正确的方向,那就太好了.

在给定的情况下,strace在读取/etc/gai.conf之后会在内部进行以下调用(现在使用端口54321,因为我认为使用端口22可能会有一些不良影响,事实并非如此) :

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

很明显,该决定旨在根据getsockname()调用的结果进行...

BTW: https://bugs.launchpad.net/ubuntu/+ source/eglibc/+ bug/673708 和那里提到的其他错误报告也证实了我的观察.那里有几个人声称新行为是正确的,因此我显然只能使用AF_INET6 ...:-(

解决方案

您的getaddrinfo由于某种原因返回了错误的结果.应该首先返回IPv6套接字.我唯一能想到的是,如果您的操作系统检测到您的系统具有较低的prio IPv6(6to4或Teredo)并避免使用它们,那么在这种情况下IMO就是错误的.刚注意到我自己的计算机做同样的事情,我使用6to4.

但是,您既可以聆听它们,也可以使用AF_INET6代替AF_UNSPEC.然后,您可以执行setsockopt来禁用IPV6_V6ONLY.

getaddrinfo在这里做合理的事情,并返回所有适用的结果(尽管顺序错误,正如我所提到的).一个和两个侦听套接字都是有效的方法,具体取决于您的应用程序.

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... :-(

解决方案

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.

这篇关于getaddrinfo()与AI_PASSIVE的结合使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆