Resolving Multiple Hostnames and IP Addresses with Python Socket Module

If you've been searching for a way to easily resolve hostnames to IP addresses and vice versa, you've come to the right place. In this post, we'll explore the Python Socket Module and its capabilities to resolve hostnames to IP addresses and vice versa, let's get started!

Intro

The Python socket module provides functions for working with network sockets and can be used to resolve hostnames to IP addresses and vice versa. You can use the gethostbyname() function to resolve a hostname to an IP address and gethostbyaddr() function to convert an IP address to a hostname.

Example 1 - Resolving a Single Host

Here's a simple example demonstrating how to resolve a single hostname to an IP address and an IP address to a hostname using the Python socket module.

import socket

# Resolving a single hostname to an IP address
hostname = 'example.com'
ip_address = socket.gethostbyname(hostname)
print(f'{hostname} - {ip_address}')


# Resolving a single IP address to a hostname
ip_address = '8.8.8.8'
host_info = socket.gethostbyaddr(ip_address)
hostname = host_info[0]  # Extracting the primary hostname using indexing
print(f'{ip_address} - {hostname}')
#output

example.com - 93.184.216.34
8.8.8.8 - dns.google

One thing to note when we resolve an IP address to a hostname is that, we store the tuple returned by socket.gethostbyaddr() in the host_info variable. Then, we extract the primary hostname by indexing the tuple with host_info[0]. If you were to just print host_info without indexing, it would look like the following.

print(host_info)

#output
('dns.google', ['8.8.8.8.in-addr.arpa'], ['8.8.8.8'])

The socket.gethostbyaddr() function returns a tuple with three elements:

  1. The primary  hostname is associated with the IP address (The one we are interested in) (string)
  2. An alias list containing alternative hostnames (list of strings)
  3. A list of IP addresses associated with the host (list of strings)

Alternatively, you can also use the following method to unpack the tuple. The end result is exactly the same.

ip_address = '8.8.8.8'
hostname, _, _ = socket.gethostbyaddr(ip_address)
print(f'{ip_address} - {hostname}')

In the above example, hostname, _, _ is a way to unpack the tuple returned by the socket.gethostbyaddr() function while ignoring the other two elements of the tuple.

When using hostname, _, _, the primary hostname is assigned to the hostname variable, while the alias list and the list of IP addresses are assigned to the dummy variables _. The underscore _ is a common convention in Python to indicate that a variable is a placeholder and its value will not be used later in the code. This is a way to make the code more readable and clear about its intentions.

Example 2 - Resolving Multiple Hosts with Error Handling

Here's an example of how to resolve a list of hostnames to IP addresses and convert a list of IP addresses to hostnames.

Exception handling is particularly useful for hostname and IP address resolution tasks, as it ensures your program remains functional when encountering incorrect hostnames or IP addresses. By handling exceptions, you can catch these errors and provide appropriate feedback, allowing your program to continue running without interruption.

import socket

# List of hostnames
hostnames = ['example.com', 'google.com', 'apple.com', 'dummp_host25.com']

# Resolving hostnames to IP addresses
for hostname in hostnames:
    try:
        ip_address = socket.gethostbyname(hostname)
        print(f'{hostname} - {ip_address}')
    except socket.gaierror as e:
        print(f'Error resolving {hostname}: {e}')

print() #Print an empty line

# List of IP addresses
ip_addresses_to_resolve = ['8.8.8.8', '8.8.4.4', '208.67.222.222', '192.168.0.100']

# Converting IP addresses to hostnames
for ip_address in ip_addresses_to_resolve:
    try:
        host, _, _ = socket.gethostbyaddr(ip_address)
        print(f'{ip_address} - {host}')
    except socket.herror as e:
        print(f'Error resolving {ip_address}: {e}')
#output

example.com - 93.184.216.34
google.com - 216.58.213.14
apple.com - 17.253.144.10
Error resolving dummp_host25.com: [Errno 8] nodename nor servname provided, or not known

8.8.8.8 - dns.google
8.8.4.4 - dns.google
208.67.222.222 - dns.umbrella.com
Error resolving 192.168.0.100: [Errno 1] Unknown host

Let's breakdown the code line by line:

  1. import socket: This line imports the socket module, which provides functions for working with network sockets and resolving hostnames and IP addresses.
  2. hostnames = ['example.com', 'google.com', 'apple.com', 'dummp_host25.com']: This line creates a list of hostnames that you want to resolve to IP addresses. I intentionally included a random FQDN that will not resolve.
  3. for hostname in hostnames:: This line starts a for loop, iterating through each hostname in the hostnames list.
  4. try:: This line starts a try block, used to catch exceptions that might occur during the hostname resolution process.
  5. ip_address = socket.gethostbyname(hostname): This line calls the socket.gethostbyname() function to resolve the current hostname to an IP address and stores it in the ip_address variable.
  6. print(f'{hostname} - {ip_address}'): This line prints the hostname and its corresponding IP address.
  7. except socket.gaierror as e:: This line starts an except block that catches the socket.gaierror exception, which occurs when the hostname cannot be resolved.
  8. print(f'Error resolving {hostname}: {e}'): This line prints an error message if the hostname resolution fails.
  9. print(): This line prints an empty line to separate the outputs of the two loops.
  10. ip_addresses_to_resolve = ['8.8.8.8', '8.8.4.4', '208.67.222.222', '192.168.0.100']: This line creates a list of IP addresses that you want to resolve to hostnames. I intentionally included a random IP address that will not resolve.
  11. for ip_address in ip_addresses_to_resolve:: This line starts a for loop, iterating through each IP address in the ip_addresses_to_resolve list.
  12. try:: This line starts a try block, used to catch exceptions that might occur during the IP address resolution process.
  13. host, _, _ = socket.gethostbyaddr(ip_address): This line calls the socket.gethostbyaddr() function to resolve the current ip_address to a hostname and stores the primary hostname in the host variable. The _ characters are placeholders for the other two elements in the tuple returned by the function, indicating that they are not used.
  14. print(f'{ip_address} - {host}'): This line prints the IP address and its corresponding hostname.
  15. except socket.herror as e:: This line starts an except block that catches the socket.herror exception, which occurs when the IP address cannot be resolved to a hostname.
  16. print(f'Error resolving {ip_address}: {e}'): This line prints an error message if the IP address resolution fails.

Conclusion

In conclusion, the Python Socket Module provides a powerful and efficient way to resolve hostnames to IP addresses and vice versa. By incorporating exception handling, you can create reliable and robust programs that gracefully handle incorrect inputs and network issues. With these techniques in your toolbox, you'll be well-equipped to tackle your Network Automation tasks.