Вызов getaddrinfo напрямую из Python: ai_addr - нулевой указатель - PullRequest
0 голосов
/ 02 декабря 2018

Я пытаюсь вызвать getaddrinfo из Python, через ctypes / libc, в Mac OS, чтобы найти IP-адрес домена.

Вызов появляется чтобы добиться успеха: код ошибки не возвращается, а ai_addrlen установлен на 28, что, как я понимаю, является подходящей длиной для адреса IPv6.Тем не менее, ai_addr выглядит как нулевой указатель, и я не уверен, как начать его отладку.

Как я могу найти IP-адрес домена, используя libc.getaddrinfo?

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)

1 Ответ

0 голосов
/ 02 декабря 2018

Согласно справочной странице linux для getaddrinfo структура addrinfo, в которой хранятся результаты getaddrinfo, определяется как

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 для getaddrinfo (или одна из страниц руководства Apple для 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 (или один из проектов Apple с открытым исходным кодом , который похож), мы видим немного другое определение:

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 для Mac (/ аналогичное) должно быть

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 больше не равен нулю.Вы также можете увидеть раннюю / экспериментальную версию, которая анализирует сами адреса, которая работает как в Mac, так и в Linux .

Редактировать: похоже, о проблеме документации уже сообщалосьFreeBSD

...