从 Python 直接调用 getaddrinfo: ai_addr 是空指针
Calling getaddrinfo directly from Python: ai_addr is null pointer
我正在尝试通过 ctypes / libc 在 Mac OS 上从 Python 调用 getaddrinfo
以查找域的 IP 地址.
调用似乎成功:没有返回错误代码,并且ai_addrlen
设置为28,据我所知这是适合IPv6地址的长度。但是,ai_addr
似乎是一个空指针,我不确定如何开始调试它。
如何使用 libc.getaddrinfo
查找域的 IP 地址?
from ctypes import (
byref,
c_char, c_char_p, c_int, c_size_t, c_void_p,
CDLL,
POINTER,
pointer,
Structure,
)
libc = CDLL(None)
class c_addrinfo(Structure):
pass
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
('ai_addr', c_void_p),
('ai_canonname', c_char_p),
('ai_next', POINTER(c_addrinfo)),
]
c_addrinfo_p = POINTER(c_addrinfo)
result = c_addrinfo_p()
error = libc.getaddrinfo(
c_char_p(b'www.google.com'),
None,
None,
byref(result),
)
print(error) # 0
print(result.contents.ai_canonname) # b'\x1c\x1e
print(result.contents.ai_addrlen) # 28
print(bool(result.contents.ai_addr)) # False === null pointer
libc.freeaddrinfo(result)
根据 linux man page for getaddrinfo,存储 getaddrinfo
结果的 addrinfo
结构定义为
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
并且根据相似的 FreeBSD man page for getaddrinfo (or one of Apple's man pages for getaddrinfo),它的 addrinfo
看起来是一样的,假设所有类型都匹配。
struct addrinfo {
int ai_flags; /* input flags */
int ai_family; /* address family for socket */
int ai_socktype; /* socket type */
int ai_protocol; /* protocol for socket */
socklen_t ai_addrlen; /* length of socket-address */
struct sockaddr *ai_addr; /* socket-address for socket */
char *ai_canonname; /* canonical name for service location */
struct addrinfo *ai_next; /* pointer to next in list */
};
然而 查看相似的 FreeBSD source (or one of the open source Apple projects),我们看到一个微妙的不同定义:
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* AF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
很容易错过,但 ai_canonname
和 ai_addr
与它们的记录方式相反 。这意味着 Python ctypes 定义,对于 Mac(/similar) 应该是
class c_addrinfo(Structure):
pass
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
('ai_canonname', c_char_p),
('ai_addr', c_void_p),
('ai_next', POINTER(c_addrinfo)),
]
或适用于 Mac 和 Linux 的平台(在其他平台上无评论)
import platform
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
] + ([
('ai_canonname', c_char_p),
('ai_addr', c_void_p),
] if platform.system() == 'Darwin' else [
('ai_addr', c_void_p),
('ai_canonname', c_char_p),
]) + [
('ai_next', POINTER(c_addrinfo)),
]
对于这些版本,在 Mac 上,指针 ai_addr
不再为空。您还可以看到 early/experimental version that parses the addresses themselves that works in both Mac and Linux.
编辑:看起来像 documentation issue has already been reported to FreeBSD
我正在尝试通过 ctypes / libc 在 Mac OS 上从 Python 调用 getaddrinfo
以查找域的 IP 地址.
调用似乎成功:没有返回错误代码,并且ai_addrlen
设置为28,据我所知这是适合IPv6地址的长度。但是,ai_addr
似乎是一个空指针,我不确定如何开始调试它。
如何使用 libc.getaddrinfo
查找域的 IP 地址?
from ctypes import (
byref,
c_char, c_char_p, c_int, c_size_t, c_void_p,
CDLL,
POINTER,
pointer,
Structure,
)
libc = CDLL(None)
class c_addrinfo(Structure):
pass
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
('ai_addr', c_void_p),
('ai_canonname', c_char_p),
('ai_next', POINTER(c_addrinfo)),
]
c_addrinfo_p = POINTER(c_addrinfo)
result = c_addrinfo_p()
error = libc.getaddrinfo(
c_char_p(b'www.google.com'),
None,
None,
byref(result),
)
print(error) # 0
print(result.contents.ai_canonname) # b'\x1c\x1e
print(result.contents.ai_addrlen) # 28
print(bool(result.contents.ai_addr)) # False === null pointer
libc.freeaddrinfo(result)
根据 linux man page for getaddrinfo,存储 getaddrinfo
结果的 addrinfo
结构定义为
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
并且根据相似的 FreeBSD man page for getaddrinfo (or one of Apple's man pages for getaddrinfo),它的 addrinfo
看起来是一样的,假设所有类型都匹配。
struct addrinfo {
int ai_flags; /* input flags */
int ai_family; /* address family for socket */
int ai_socktype; /* socket type */
int ai_protocol; /* protocol for socket */
socklen_t ai_addrlen; /* length of socket-address */
struct sockaddr *ai_addr; /* socket-address for socket */
char *ai_canonname; /* canonical name for service location */
struct addrinfo *ai_next; /* pointer to next in list */
};
然而 查看相似的 FreeBSD source (or one of the open source Apple projects),我们看到一个微妙的不同定义:
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* AF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
很容易错过,但 ai_canonname
和 ai_addr
与它们的记录方式相反 。这意味着 Python ctypes 定义,对于 Mac(/similar) 应该是
class c_addrinfo(Structure):
pass
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
('ai_canonname', c_char_p),
('ai_addr', c_void_p),
('ai_next', POINTER(c_addrinfo)),
]
或适用于 Mac 和 Linux 的平台(在其他平台上无评论)
import platform
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
] + ([
('ai_canonname', c_char_p),
('ai_addr', c_void_p),
] if platform.system() == 'Darwin' else [
('ai_addr', c_void_p),
('ai_canonname', c_char_p),
]) + [
('ai_next', POINTER(c_addrinfo)),
]
对于这些版本,在 Mac 上,指针 ai_addr
不再为空。您还可以看到 early/experimental version that parses the addresses themselves that works in both Mac and Linux.
编辑:看起来像 documentation issue has already been reported to FreeBSD