import socket import struct import ctypes import binascii from contextlib import closing from .platform import WINDOWS from .string import safeencode, safeunicode from .convert import hex2dec, dec2hex # https://github.com/hickeroar/win_inet_pton/blob/master/win_inet_pton.py if WINDOWS: 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 _win_inet_pton(address_family, ip_str): ip_str = safeencode(ip_str) addr = _sockaddr() addr.sa_family = address_family addr_size = ctypes.c_int(ctypes.sizeof(addr)) if WSAStringToAddressA( ip_str, 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 _win_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] socket.inet_pton = _win_inet_pton socket.inet_ntop = _win_inet_ntop # https://github.com/kennethreitz/requests/blob/master/requests/utils.py def dotted_netmask(mask): """ Converts mask from /xx format to xxx.xxx.xxx.xxx Example: if mask is 24 function returns 255.255.255.0 """ mask = int(mask) bits = 0xffffffff ^ (1 << 32 - mask) - 1 return socket.inet_ntoa(struct.pack('>I', bits)) # http://en.wikipedia.org/wiki/Private_network private_ipv4s = [ ('10.0.0.0', 8), # 10.0.0.0 - 10.255.255.255 ('172.16.0.0', 12), # 172.16.0.0 - 172.31.255.255 ('192.168.0.0', 16), # 192.168.0.0 - 192.168.255.255 ] # https://github.com/kennethreitz/requests/blob/master/requests/utils.py def is_ipv4(ip): """ Returns True if the IPv4 address ia valid, otherwise returns False. """ try: socket.inet_aton(ip) except socket.error: return False return True def is_ipv6(ip): """ Returns True if the IPv6 address ia valid, otherwise returns False. """ try: socket.inet_pton(socket.AF_INET6, ip) except socket.error: return False return True def get_free_port(): with closing(socket.socket(socket.AF_INET, type=socket.SOCK_STREAM)) as s: s.bind(('127.0.0.1', 0)) _, port = s.getsockname() return port # https://stackoverflow.com/questions/5619685/conversion-from-ip-string-to-integer-and-backward-in-python # https://stackoverflow.com/questions/11894717/python-convert-ipv6-to-an-integer def ip2int(ip_str): """ Convert ip to integer. Support IPV4 and IPV6. Raise `ValueError` if convert failed. """ try: return struct.unpack("!I", socket.inet_aton(ip_str))[0] except socket.error: pass try: return hex2dec(binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip_str))) except socket.error: pass raise ValueError('{!r} does not appear to be an IPv4 or IPv6 address'.format(ip_str)) # https://stackoverflow.com/questions/5619685/conversion-from-ip-string-to-integer-and-backward-in-python def int2ip(ip_int): """ Convert integer to ip. Support IPV4 and IPV6. Raise `ValueError` if convert failed. """ try: return socket.inet_ntoa(struct.pack("!I", ip_int)) except (socket.error, struct.error): pass try: ip_str = socket.inet_ntop(socket.AF_INET6, binascii.unhexlify(dec2hex(ip_int))) return safeunicode(ip_str, encoding='ascii') except (socket.error, struct.error): pass raise ValueError('{!r} does not appear to be an IPv4 or IPv6 address'.format(ip_int))