Last active
January 14, 2024 06:12
-
-
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 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
# 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] |
line 39 should read:
ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string))
return ip_string[:ip_string_size.value - 1]
Since the last parameter to WSAAddressToString includes a NULL
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
You need to replace
string_at
withctypes.string_at
.