Own implementation of getaddrinfo by calling libc directly from Python + ctypes, supporting IPv4 and IPv6 (experimental)
from ctypes import ( | |
byref, | |
c_char_p, c_uint32, c_uint16, c_ubyte, | |
CDLL, | |
POINTER, | |
pointer, | |
Structure, Union, | |
sizeof, | |
) | |
import platform | |
from socket import ( | |
inet_ntop, | |
AF_INET, | |
AF_INET6, | |
) | |
libc = CDLL(None) | |
class c_sockaddr_in4(Structure): | |
_fields_ = [ | |
('sin_family', c_uint16), | |
('sin_port', c_uint16), | |
('sin_addr', c_ubyte * 4), | |
] | |
class c_sockaddr_in6(Structure): | |
_fields_ = [ | |
('sin_family', c_uint16), | |
('sin_port', c_uint16), | |
('sin_flowinfo', c_uint32), | |
('sin_addr', c_ubyte * 16), | |
('sin_scope_id', c_uint32), | |
] | |
class c_sockaddr_in(Union): | |
_fields_ = [ | |
('ai_addr4', c_sockaddr_in4), | |
('ai_addr6', c_sockaddr_in6), | |
] | |
class c_addrinfo(Structure): | |
pass | |
c_addrinfo._fields_ = [ | |
('ai_flags', c_uint32), | |
('ai_family', c_uint32), | |
('ai_socktype', c_uint32), | |
('ai_protocol', c_uint32), | |
('ai_addrlen', c_uint32), | |
] + ([ | |
('ai_canonname', c_char_p), | |
('ai_addr', POINTER(c_sockaddr_in)), | |
] if platform.system() == 'Darwin' else [ | |
('ai_addr', POINTER(c_sockaddr_in)), | |
('ai_canonname', c_char_p), | |
]) + [ | |
('ai_next', POINTER(c_addrinfo)), | |
] | |
c_addrinfo_p = POINTER(c_addrinfo) | |
address_field_names = { | |
AF_INET: 'ai_addr4', | |
AF_INET6: 'ai_addr6', | |
} | |
def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): | |
result = c_addrinfo_p() | |
hints = c_addrinfo() | |
hints.flags = flags | |
hints.ai_family = family | |
hints.ai_socktype = type | |
hints.proto = proto | |
result = c_addrinfo_p() | |
error = libc.getaddrinfo( | |
c_char_p(host), | |
c_char_p(port) if port is not None else None, | |
byref(hints), | |
byref(result), | |
) | |
if error: | |
raise Exception(error) | |
result_addrinfo = result.contents | |
family = result_addrinfo.ai_family | |
address_field_name = address_field_names[family] | |
address_field = getattr(result_addrinfo.ai_addr.contents, address_field_name) | |
address_raw = address_field.sin_addr | |
address = inet_ntop(family, address_raw) | |
libc.freeaddrinfo(result) | |
return (family, address) | |
print(getaddrinfo(b'www.google.com', None, family=AF_INET6)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment