Last active
December 2, 2018 19:51
-
-
Save michalc/046dc425f4ec4bb0524d736c412421df to your computer and use it in GitHub Desktop.
Own implementation of getaddrinfo by calling libc directly from Python + ctypes, supporting IPv4 and IPv6 (experimental)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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