Skip to content

Instantly share code, notes, and snippets.

@michalc
Last active December 2, 2018 19:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michalc/046dc425f4ec4bb0524d736c412421df to your computer and use it in GitHub Desktop.
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)
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