""" AUTHOR: Khushal P Soonderji DATE: Friday, 12th Jul., 2024 OBJECTIVE: To provide an easy way to get geolocation information of an IP address. REFERENCES: 1) https://medium.com/@tubelwj/how-to-retrieve-ip-geolocation-information-in-python-929e15041e3e DOWNLOADS: N/A """ # ***************************************************************************************************************** # ***** **** # *** IMPORT *** # ***** **** # ***************************************************************************************************************** # To make sibling directories accessible for imports: import sys sys.path.append(".") sys.path.append("..") # For working with IP Addresses: import ipaddress # ***************************************************************************************************************** # ***** **** # *** MACROS / ONE-TIME INIT *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** VARIABLES *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** FUNCTIONS *** # ***** **** # ***************************************************************************************************************** def ipv4_to_int(ip_string): """ Converts an IP (v4) string to an integer value. :param ip_string: The IP address (v4) that you want to convert to integer format. :return: An integer representation of the IP (v4) address. """ ip_numerical = int(ipaddress.IPv4Address(ip_string)) return ip_numerical # --------------------------------------------------------------------------------------------------------------------- def int_to_ipv4(ip_numerical): """ Interprets the IP (v4) value from the given integer value. :param ip_numerical: The integer value that represents an IP (v4) address. :return: """ ip_string = str(ipaddress.IPv4Address(ip_numerical)) return ip_string # --------------------------------------------------------------------------------------------------------------------- def ipv4_to_bin(ip_string): """ Converts an input IP (v4) address to the binary string that represents the 32 bits. :param ip_string: The IP (v4) string in a format like "192.168.0.1" :return: The binary representation (as a string) of the input IP address. """ ip_binary = bin(int(ipaddress.IPv4Address(ip_string)))[2:].zfill(32) return ip_binary # --------------------------------------------------------------------------------------------------------------------- def bin_to_ipv4(ip_binary): """ Interprets the IP (v4) value from the given binary string. :param ip_binary: The string of 1s and 0s that represents the IP (v4) address. :return: The IP (v4) address as a string. """ ip_string = str(ipaddress.IPv4Address(int(ip_binary, 2))) return ip_string # --------------------------------------------------------------------------------------------------------------------- def get_ipv4_range(ip_string, as_string = True): """ Given a network description in the format "88.95.100.128/25", this function tells you the first and last IP addresses of that network. Useful for determining if an IP address lies in a network. :param ip_string: The input network description in the format "88.95.100.128/25" :param as_string: To select between integer and string formats for the IP range output. :return: The first and last IP addresses of the input network, and the count. """ # Extract the components of the string: ip_components = ip_string.split("/") ip_addr = ipv4_to_int(ip_components[0]) ip_bits = int(ip_components[1]) # Convert the mask number to binary representation: ip_mask = (1 << ip_bits) - 1 ip_mask = ip_mask << (32 - ip_bits) inv_ip_mask = (~ip_mask) & 0xFFFF # Figure out the start and end IP addresses: start_ip = ip_addr & ip_mask end_ip = ip_addr | inv_ip_mask count = end_ip - start_ip + 1 # If the IPs are needed as strings, we perform the conversion: if as_string: start_ip = int_to_ipv4(start_ip) end_ip = int_to_ipv4(end_ip) # Done here: return start_ip, end_ip, count # --------------------------------------------------------------------------------------------------------------------- def to_hyphen_notation(value: str) -> str | None: """ Takes in an IP pool (range) in either CIDR notation or already in hyphen-separated notation and parses it into the hyphen-separated notation. :param value: The IP-range in either of the accepted formats. :return: The IP range in hyphen-separated notation. """ # Let's start by assuming failure: success = False start_ip = None end_ip = None # Ensure that the input is treated as a string: value = str(value) # First we check if the IP has been given in the CIDR notation: if not success: try: # Try to extract the first and last IP addressed from the input: ip_net = ipaddress.IPv4Network(value, strict = False) start_ip = ip_net.network_address end_ip = ip_net.broadcast_address success = True # In case the CIDR interpretation doesn't work: except: success = False # Now we try to parse the input string as a hyphen-separated input: if not success: try: # Split at the hyphen and take the parts: parts = value.split("-") start_ip = parts[0] end_ip = parts[1] success = True # In case the hyphen-separated interpretation doesn't work: except: success = False # Done here: value = f"{start_ip}-{end_ip}" if success else None return value # ***************************************************************************************************************** # ***** **** # *** MAIN PROGRAM *** # ***** **** # ***************************************************************************************************************** if __name__ == "__main__": print(ipv4_to_int("255.255.255.255")) print(ipv4_to_int("x.x.x.x"))