Skip to content

Instantly share code, notes, and snippets.

@nnemkin
Last active January 14, 2024 06:12
Show Gist options
  • Save nnemkin/4966028 to your computer and use it in GitHub Desktop.
Save nnemkin/4966028 to your computer and use it in GitHub Desktop.
Native inet_pton and inet_ntop implementation for Python on Windows (with ctypes).
# This software released into the public domain. Anyone is free to copy,
# modify, publish, use, compile, sell, or distribute this software,
# either in source code form or as a compiled binary, for any purpose,
# commercial or non-commercial, and by any means.
import socket
import ctypes
class sockaddr(ctypes.Structure):
_fields_ = [("sa_family", ctypes.c_short),
("__pad1", ctypes.c_ushort),
("ipv4_addr", ctypes.c_byte * 4),
("ipv6_addr", ctypes.c_byte * 16),
("__pad2", ctypes.c_ulong)]
WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA
WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringA
def inet_pton(address_family, ip_string):
addr = sockaddr()
addr.sa_family = address_family
addr_size = ctypes.c_int(ctypes.sizeof(addr))
if WSAStringToAddressA(ip_string, address_family, None, ctypes.byref(addr), ctypes.byref(addr_size)) != 0:
raise socket.error(ctypes.FormatError())
if address_family == socket.AF_INET:
return ctypes.string_at(addr.ipv4_addr, 4)
if address_family == socket.AF_INET6:
return ctypes.string_at(addr.ipv6_addr, 16)
raise socket.error('unknown address family')
def inet_ntop(address_family, packed_ip):
addr = sockaddr()
addr.sa_family = address_family
addr_size = ctypes.c_int(ctypes.sizeof(addr))
ip_string = ctypes.create_string_buffer(128)
ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string))
if address_family == socket.AF_INET:
if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr):
raise socket.error('packed IP wrong length for inet_ntoa')
ctypes.memmove(addr.ipv4_addr, packed_ip, 4)
elif address_family == socket.AF_INET6:
if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr):
raise socket.error('packed IP wrong length for inet_ntoa')
ctypes.memmove(addr.ipv6_addr, packed_ip, 16)
else:
raise socket.error('unknown address family')
if WSAAddressToStringA(ctypes.byref(addr), addr_size, None, ip_string, ctypes.byref(ip_string_size)) != 0:
raise socket.error(ctypes.FormatError())
return ip_string[:ip_string_size.value - 1]
@EmilStenstrom
Copy link

You need to replace string_at with ctypes.string_at.

@mwander
Copy link

mwander commented Jun 26, 2014

line 39 should read:
ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string))

@mrajcok
Copy link

mrajcok commented Aug 4, 2014

return ip_string[:ip_string_size.value - 1]
Since the last parameter to WSAAddressToString includes a NULL

@hickeroar
Copy link

I've fixed the above issues, done a bit of auto-setup work, and created an installable package for this:
https://github.com/hickeroar/win_inet_pton
https://pypi.python.org/pypi/win_inet_pton

Hopefully this helps some people going forward. I was about to pull my hair out when I found this. Thanks nnemkin!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment