def valid_bin(bin_val, width): """ :param bin_val: A network address in Python's binary representation format ('0bxxx'). :param width: Maximum width (in bits) of a network address (excluding delimiters). :return: ``True`` if network address is valid, ``False`` otherwise. """ if not _is_str(bin_val): return False if not bin_val.startswith('0b'): return False bin_val = bin_val.replace('0b', '') if len(bin_val) > width: return False max_int = 2 ** width - 1 try: if 0 <= int(bin_val, 2) <= max_int: return True except ValueError: pass return False
def _set_value(self, value): if self._module is None: # EUI version is implicit, detect it from value. for module in (_eui48, _eui64): try: self._value = module.str_to_int(value) self._module = module break except AddrFormatError: try: if 0 <= int(value) <= module.max_int: self._value = int(value) self._module = module break except ValueError: pass if self._module is None: raise AddrFormatError('failed to detect EUI version: %r' % value) else: # EUI version is explicit. if _is_str(value): try: self._value = self._module.str_to_int(value) except AddrFormatError: raise AddrFormatError('address %r is not an EUIv%d' % (value, self._module.version)) else: if 0 <= int(value) <= self._module.max_int: self._value = int(value) else: raise AddrFormatError('bad address format: %r' % value)
def valid_bits(bits, width, word_sep=''): """ :param bits: A network address in a delimited binary string format. :param width: Maximum width (in bits) of a network address (excluding delimiters). :param word_sep: (optional) character or string used to delimit word groups (default: '', no separator). :return: ``True`` if network address is valid, ``False`` otherwise. """ if not _is_str(bits): return False if word_sep != '': bits = bits.replace(word_sep, '') if len(bits) != width: return False max_int = 2 ** width - 1 try: if 0 <= int(bits, 2) <= max_int: return True except ValueError: pass return False
def int_to_bits(int_val, word_size, num_words, word_sep=''): """ :param int_val: An unsigned integer. :param word_size: Width (in bits) of each unsigned integer word value. :param num_words: Number of unsigned integer words expected. :param word_sep: (optional) character or string used to delimit word groups (default: '', no separator). :return: A network address in a delimited binary string format that is equivalent in value to unsigned integer. """ bit_words = [] for word in int_to_words(int_val, word_size, num_words): bits = [] while word: bits.append(BYTES_TO_BITS[word & 255]) word >>= 8 bits.reverse() bit_str = ''.join(bits) or '0' * word_size bits = ('0' * word_size + bit_str)[-word_size:] bit_words.append(bits) if word_sep is not '': # Check custom separator. if not _is_str(word_sep): raise ValueError('word separator is not a string: %r!' % word_sep) return word_sep.join(bit_words)
def valid_bits(bits, width, word_sep=''): """ :param bits: A network address in a delimited binary string format. :param width: Maximum width (in bits) of a network address (excluding delimiters). :param word_sep: (optional) character or string used to delimit word groups (default: '', no separator). :return: ``True`` if network address is valid, ``False`` otherwise. """ if not _is_str(bits): return False if word_sep != '': bits = bits.replace(word_sep, '') if len(bits) != width: return False max_int = 2**width - 1 try: if 0 <= int(bits, 2) <= max_int: return True except ValueError: pass return False
def test_compat_py2(): assert _is_str(unicode('')) # Python 2.x - 8 bit strings are just regular strings str_8bit = _bytes_join(['a', 'b', 'c']) assert str_8bit == 'abc'.encode() assert "'abc'" == '%r' % str_8bit
def _inet_pton_af_inet(ip_string): """ Convert an IP address in string format (123.45.67.89) to the 32-bit packed binary format used in low-level network functions. Differs from inet_aton by only support decimal octets. Using octal or hexadecimal values will raise a ValueError exception. """ #TODO: optimise this ... use inet_aton with mods if available ... if _is_str(ip_string): invalid_addr = ValueError('illegal IP address string %r' % ip_string) # Support for hexadecimal and octal octets. tokens = ip_string.split('.') # Pack octets. if len(tokens) == 4: words = [] for token in tokens: if token.startswith('0x') or \ (token.startswith('0') and len(token) > 1): raise invalid_addr try: octet = int(token) except ValueError: raise invalid_addr if (octet >> 8) != 0: raise invalid_addr words.append(_pack('B', octet)) return _bytes_join(words) else: raise invalid_addr raise ValueError('argument should be a string, not %s' % type(ip_string))
def inet_ntop(af, packed_ip): """Convert an packed IP address of the given family to string format.""" if af == AF_INET: # IPv4. return inet_ntoa(packed_ip) elif af == AF_INET6: # IPv6. if len(packed_ip) != 16 or not _is_str(packed_ip): raise ValueError('invalid length of packed IP address string') tokens = ['%x' % i for i in _unpack('>8H', packed_ip)] # Convert packed address to an integer value. words = list(_unpack('>8H', packed_ip)) int_val = 0 for i, num in enumerate(reversed(words)): word = num word = word << 16 * i int_val = int_val | word if 0xffff < int_val <= 0xffffffff or int_val >> 32 == 0xffff: # IPv4 compatible / mapped IPv6. packed_ipv4 = _pack('>2H', *[int(i, 16) for i in tokens[-2:]]) ipv4_str = inet_ntoa(packed_ipv4) tokens = tokens[0:-2] + [ipv4_str] return ':'.join(_compact_ipv6_tokens(tokens)) else: raise ValueError('unknown address family %d' % af)
def valid_bin(bin_val, width): """ :param bin_val: A network address in Python's binary representation format ('0bxxx'). :param width: Maximum width (in bits) of a network address (excluding delimiters). :return: ``True`` if network address is valid, ``False`` otherwise. """ if not _is_str(bin_val): return False if not bin_val.startswith('0b'): return False bin_val = bin_val.replace('0b', '') if len(bin_val) > width: return False max_int = 2**width - 1 try: if 0 <= int(bin_val, 2) <= max_int: return True except ValueError: pass return False
def _set_value(self, value): if self._module is None: # EUI version is implicit, detect it from value. for module in (_eui48, _eui64): try: self._value = module.str_to_int(value) self._module = module break except AddrFormatError: try: if 0 <= int(value) <= module.max_int: self._value = int(value) self._module = module break except ValueError: pass if self._module is None: raise AddrFormatError("failed to detect EUI version: %r" % value) else: # EUI version is explicit. if _is_str(value): try: self._value = self._module.str_to_int(value) except AddrFormatError: raise AddrFormatError("address %r is not an EUIv%d" % (value, self._module.version)) else: if 0 <= int(value) <= self._module.max_int: self._value = int(value) else: raise AddrFormatError("bad address format: %r" % value)
def _inet_pton_af_inet(ip_string): """ Convert an IP address in string format (123.45.67.89) to the 32-bit packed binary format used in low-level network functions. Differs from inet_aton by only support decimal octets. Using octal or hexadecimal values will raise a ValueError exception. """ #TODO: optimise this ... use inet_aton with mods if available ... if _is_str(ip_string): invalid_addr = ValueError('illegal IP address string %r' % ip_string) # Support for hexadecimal and octal octets. tokens = ip_string.split('.') # Pack octets. if len(tokens) == 4: words = [] for token in tokens: if token.startswith('0x') or (token.startswith('0') and len(token) > 1): raise invalid_addr try: octet = int(token) except ValueError: raise invalid_addr if (octet >> 8) != 0: raise invalid_addr words.append(_pack('B', octet)) return _bytes_join(words) else: raise invalid_addr raise ValueError('argument should be a string, not %s' % type(ip_string))
def test_compat_py3(): assert _is_str(''.encode()) # byte string join tests. str_8bit = _bytes_join(['a'.encode(), 'b'.encode(), 'c'.encode()]) assert str_8bit == 'abc'.encode() assert "b'abc'" == '%r' % str_8bit
def inet_ntoa(packed_ip): """ Convert an IP address from 32-bit packed binary format to string format. """ if not _is_str(packed_ip): raise TypeError('string type expected, not %s' % str(type(packed_ip))) if len(packed_ip) != 4: raise ValueError('invalid length of packed IP address string') return '%d.%d.%d.%d' % _unpack('4B', packed_ip)
def valid_glob(ipglob): """ :param ipglob: An IP address range in a glob-style format. :return: ``True`` if IP range glob is valid, ``False`` otherwise. """ #TODO: Add support for abbreviated ipglobs. #TODO: e.g. 192.0.*.* == 192.0.* #TODO: *.*.*.* == * #TODO: Add strict flag to enable verbose ipglob checking. if not _is_str(ipglob): return False seen_hyphen = False seen_asterisk = False octets = ipglob.split('.') if len(octets) != 4: return False for octet in octets: if '-' in octet: if seen_hyphen: return False seen_hyphen = True if seen_asterisk: # Asterisks cannot precede hyphenated octets. return False try: (octet1, octet2) = [int(i) for i in octet.split('-')] except ValueError: return False if octet1 >= octet2: return False if not 0 <= octet1 <= 254: return False if not 1 <= octet2 <= 255: return False elif octet == '*': seen_asterisk = True else: if seen_hyphen is True: return False if seen_asterisk is True: return False try: if not 0 <= int(octet) <= 255: return False except ValueError: return False return True
def str_to_int(addr): """ :param addr: An IEEE EUI-48 (MAC) address in string form. :return: An unsigned integer that is equivalent to value represented by EUI-48/MAC string address formatted according to the dialect settings. """ words = [] if _is_str(addr): found_match = False for regexp in RE_MAC_FORMATS: match_result = regexp.findall(addr) if len(match_result) != 0: found_match = True if isinstance(match_result[0], tuple): words = match_result[0] else: words = (match_result[0], ) break if not found_match: raise AddrFormatError('%r is not a supported MAC format!' % (addr, )) else: raise TypeError('%r is not str() or unicode()!' % (addr, )) int_val = None if len(words) == 6: # 2 bytes x 6 (UNIX, Windows, EUI-48) int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16) elif len(words) == 3: # 4 bytes x 3 (Cisco) int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16) elif len(words) == 2: # 6 bytes x 2 (PostgreSQL) int_val = int(''.join(['%.6x' % int(w, 16) for w in words]), 16) elif len(words) == 1: # 12 bytes (bare, no delimiters) int_val = int('%012x' % int(words[0], 16), 16) else: raise AddrFormatError('unexpected word count in MAC address %r!' % (addr, )) return int_val
def inet_aton(ip_string): """ Convert an IP address in string format (123.45.67.89) to the 32-bit packed binary format used in low-level network functions. """ if _is_str(ip_string): invalid_addr = ValueError('illegal IP address string %r' % ip_string) # Support for hexadecimal and octal octets. tokens = [] base = 10 for token in ip_string.split('.'): if token.startswith('0x'): base = 16 elif token.startswith('0') and len(token) > 1: base = 8 elif token == '': continue try: tokens.append(int(token, base)) except ValueError: raise invalid_addr # Zero fill missing octets. num_tokens = len(tokens) if num_tokens < 4: fill_tokens = [0] * (4 - num_tokens) if num_tokens > 1: end_token = tokens.pop() tokens = tokens + fill_tokens + [end_token] else: tokens = tokens + fill_tokens # Pack octets. if len(tokens) == 4: words = [] for token in tokens: if (token >> 8) != 0: raise invalid_addr words.append(_pack('B', token)) return _bytes_join(words) else: raise invalid_addr raise ValueError('argument should be a string, not %s' % type(ip_string))
def _generate_nmap_octet_ranges(nmap_target_spec): # Generate 4 lists containing all octets defined by a given nmap Target # specification. if not _is_str(nmap_target_spec): raise TypeError('string expected, not %s' % type(nmap_target_spec)) if not nmap_target_spec: raise ValueError('nmap target specification cannot be blank!') tokens = nmap_target_spec.split('.') if len(tokens) != 4: raise AddrFormatError('invalid nmap range: %s' % nmap_target_spec) return (_nmap_octet_target_values(tokens[0]), _nmap_octet_target_values(tokens[1]), _nmap_octet_target_values(tokens[2]), _nmap_octet_target_values(tokens[3]))
def str_to_int(addr): """ :param addr: An IEEE EUI-48 (MAC) address in string form. :return: An unsigned integer that is equivalent to value represented by EUI-48/MAC string address formatted according to the dialect settings. """ words = [] if _is_str(addr): found_match = False for regexp in RE_MAC_FORMATS: match_result = regexp.findall(addr) if len(match_result) != 0: found_match = True if isinstance(match_result[0], tuple): words = match_result[0] else: words = (match_result[0],) break if not found_match: raise AddrFormatError('%r is not a supported MAC format!' % addr) else: raise TypeError('%r is not str() or unicode()!' % addr) int_val = None if len(words) == 6: # 2 bytes x 6 (UNIX, Windows, EUI-48) int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16) elif len(words) == 3: # 4 bytes x 3 (Cisco) int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16) elif len(words) == 2: # 6 bytes x 2 (PostgreSQL) int_val = int(''.join(['%.6x' % int(w, 16) for w in words]), 16) elif len(words) == 1: # 12 bytes (bare, no delimiters) int_val = int('%012x' % int(words[0], 16), 16) else: raise AddrFormatError('unexpected word count in MAC address %r!' \ % addr) return int_val
def str_to_int(addr): """ :param addr: An IEEE EUI-64 indentifier in string form. :return: An unsigned integer that is equivalent to value represented by EUI-64 string identifier. """ words = [] try: words = _get_match_result(addr, RE_EUI64_FORMATS) if not words: raise TypeError except TypeError: raise AddrFormatError('invalid IEEE EUI-64 identifier: %r!' % addr) if _is_str(words): return int(words, 16) if len(words) != num_words: raise AddrFormatError('bad word count for EUI-64 identifier: %r!' \ % addr) return int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
def str_to_int(addr): """ :param addr: An IEEE EUI-64 indentifier in string form. :return: An unsigned integer that is equivalent to value represented by EUI-64 string identifier. """ words = [] try: words = _get_match_result(addr, RE_EUI64_FORMATS) if not words: raise TypeError except TypeError: raise AddrFormatError('invalid IEEE EUI-64 identifier: %r!' % addr) if _is_str(words): return int(words, 16) if len(words) != num_words: raise AddrFormatError( 'bad word count for EUI-64 identifier: %r!' % addr) return int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
def test_compat_string_and_int_detection(): assert _is_int(_sys_maxint) assert not _is_str(_sys_maxint) assert _is_str('') assert _is_str(''.encode())
def inet_pton(af, ip_string): """ Convert an IP address from string format to a packed string suitable for use with low-level network functions. """ if af == AF_INET: # IPv4. return _inet_pton_af_inet(ip_string) elif af == AF_INET6: invalid_addr = ValueError('illegal IP address string %r' % ip_string) # IPv6. values = [] if not _is_str(ip_string): raise invalid_addr if 'x' in ip_string: # Don't accept hextets with the 0x prefix. raise invalid_addr if '::' in ip_string: if ip_string == '::': # Unspecified address. return '\x00'.encode() * 16 # IPv6 compact mode. try: prefix, suffix = ip_string.split('::') except ValueError: raise invalid_addr l_prefix = [] l_suffix = [] if prefix != '': l_prefix = prefix.split(':') if suffix != '': l_suffix = suffix.split(':') # IPv6 compact IPv4 compatibility mode. if len(l_suffix) and '.' in l_suffix[-1]: ipv4_str = _inet_pton_af_inet(l_suffix.pop()) l_suffix.append('%x' % _unpack('>H', ipv4_str[0:2])[0]) l_suffix.append('%x' % _unpack('>H', ipv4_str[2:4])[0]) token_count = len(l_prefix) + len(l_suffix) if not 0 <= token_count <= 8 - 1: raise invalid_addr gap_size = 8 - ( len(l_prefix) + len(l_suffix) ) values = [_pack('>H', int(i, 16)) for i in l_prefix] \ + ['\x00\x00'.encode() for i in range(gap_size)] \ + [_pack('>H', int(i, 16)) for i in l_suffix] try: for token in l_prefix + l_suffix: word = int(token, 16) if not 0 <= word <= 0xffff: raise invalid_addr except ValueError: raise invalid_addr else: # IPv6 verbose mode. if ':' in ip_string: tokens = ip_string.split(':') if '.' in ip_string: ipv6_prefix = tokens[:-1] if ipv6_prefix[:-1] != ['0', '0', '0', '0', '0']: raise invalid_addr if ipv6_prefix[-1].lower() not in ('0', 'ffff'): raise invalid_addr # IPv6 verbose IPv4 compatibility mode. if len(tokens) != 7: raise invalid_addr ipv4_str = _inet_pton_af_inet(tokens.pop()) tokens.append('%x' % _unpack('>H', ipv4_str[0:2])[0]) tokens.append('%x' % _unpack('>H', ipv4_str[2:4])[0]) values = [_pack('>H', int(i, 16)) for i in tokens] else: # IPv6 verbose mode. if len(tokens) != 8: raise invalid_addr try: tokens = [int(token, 16) for token in tokens] for token in tokens: if not 0 <= token <= 0xffff: raise invalid_addr except ValueError: raise invalid_addr values = [_pack('>H', i) for i in tokens] else: raise invalid_addr return _bytes_join(values) else: raise ValueError('Unknown address family %d' % af)
def inet_pton(af, ip_string): """ Convert an IP address from string format to a packed string suitable for use with low-level network functions. """ if af == AF_INET: # IPv4. return _inet_pton_af_inet(ip_string) elif af == AF_INET6: invalid_addr = ValueError('illegal IP address string %r' % ip_string) # IPv6. values = [] if not _is_str(ip_string): raise invalid_addr if 'x' in ip_string: # Don't accept hextets with the 0x prefix. raise invalid_addr if '::' in ip_string: if ip_string == '::': # Unspecified address. return '\x00'.encode() * 16 # IPv6 compact mode. try: prefix, suffix = ip_string.split('::') except ValueError: raise invalid_addr l_prefix = [] l_suffix = [] if prefix != '': l_prefix = prefix.split(':') if suffix != '': l_suffix = suffix.split(':') # IPv6 compact IPv4 compatibility mode. if len(l_suffix) and '.' in l_suffix[-1]: ipv4_str = _inet_pton_af_inet(l_suffix.pop()) l_suffix.append('%x' % _unpack('>H', ipv4_str[0:2])[0]) l_suffix.append('%x' % _unpack('>H', ipv4_str[2:4])[0]) token_count = len(l_prefix) + len(l_suffix) if not 0 <= token_count <= 8 - 1: raise invalid_addr gap_size = 8 - (len(l_prefix) + len(l_suffix)) values = ([_pack('>H', int(i, 16)) for i in l_prefix] + ['\x00\x00'.encode() for i in range(gap_size)] + [_pack('>H', int(i, 16)) for i in l_suffix]) try: for token in l_prefix + l_suffix: word = int(token, 16) if not 0 <= word <= 0xffff: raise invalid_addr except ValueError: raise invalid_addr else: # IPv6 verbose mode. if ':' in ip_string: tokens = ip_string.split(':') if '.' in ip_string: ipv6_prefix = tokens[:-1] if ipv6_prefix[:-1] != ['0', '0', '0', '0', '0']: raise invalid_addr if ipv6_prefix[-1].lower() not in ('0', 'ffff'): raise invalid_addr # IPv6 verbose IPv4 compatibility mode. if len(tokens) != 7: raise invalid_addr ipv4_str = _inet_pton_af_inet(tokens.pop()) tokens.append('%x' % _unpack('>H', ipv4_str[0:2])[0]) tokens.append('%x' % _unpack('>H', ipv4_str[2:4])[0]) values = [_pack('>H', int(i, 16)) for i in tokens] else: # IPv6 verbose mode. if len(tokens) != 8: raise invalid_addr try: tokens = [int(token, 16) for token in tokens] for token in tokens: if not 0 <= token <= 0xffff: raise invalid_addr except ValueError: raise invalid_addr values = [_pack('>H', i) for i in tokens] else: raise invalid_addr return _bytes_join(values) else: raise ValueError('Unknown address family %d' % af)