How to perform DNS lookups in Python, including /etc/hosts?

How To Perform DNS Lookups

In this article, we’ll perform DNS Lookups in Python using the ‘socket’ module and ‘socket.getaddrinfo()’ function for generating the domain information and converting it into an IP address while using the /etc/hosts files for storing the generated IPv4 and IPv6 addresses of the domain.

Prerequisites

  • Python Programming: Have a basic understanding of Python language concepts like functions, arrays, loops etc.
  • Command-Line Interface (CLI): Must know the basics of the CLI interface like navigating directories, and executing Python scripts form the terminal.
  • Networking Concepts: Networking concepts like domain addresses, domain names, IP addresses, and protocols must be known.
  • Text Editor: Should have access to any text editor for writing and executing the Python code.

What is DNS lookup?

Before learning what is DNS Lookup, first let us understand what is a DNS

Domain Name System (DNS)

DNS stands for Domain Name System; It is the most essential part of the internet. It is like a distributed database that stores the records of websites and their IP addresses. It converts the human-readable domain names into numeric IP addresses. For example:
“www.example.com” is converted to “192.0.2.1”, which is later used by network devices and computers to communicate with each other with the help of DNS lookup!

DNS Lookup

DNS lookup converts domain names into IP addresses (like 192.0.2.1) which can be understood by computers; which allows network devices to find and connect to websites and services on the internet. DNS lookup queries are processed in the background by DNS servers. Remember, the next time you click any website link, there’s a secret DNS lookup happening in the background! The image below shows how DNS Lookup works.

DNS Lookup
DNS Lookup

What is /etc/hosts?

These are configuration files that store the IP address of the domains searched. These files are only found in operating systems like Linux and macOS. In Windows, they are stored as \etc\hosts. The domain’s converted addresses are stored in the form of the IP address followed by the hostname or domain name. Let’s see an example.

127.0.0.1      localhost

Where,
“localhost” – Domain name
“127.0.0.1” – IP address

Purpose of /etc/hosts file

/etc/hosts file is mainly used to allow local resolution of domain names to a specific IP address without the back help of DNS. Whenever a computer wants to communicate, it simply checks the /etc/hosts file and assigns the IP address.

DNS Lookup in Python

Let’s take look a look at how to perform DNS Lookup through Python with the help of the ‘socket’ module and ‘getaddrinfo()’ function for finding virtual addresses (IPv4, IPv6) of the domain names. Let’s see how it works:

The socket module

The socket module in Python is used for network communications by acting as an endpoint. The module communicates with the network to perform network operations like DNS lookup, IP lookup, etc., by maintaining a server and client connection. Wherein, the server works by listening to the requests from the client and providing what the client side has requested such as a message, file, webpage, or any document.

socket.getaddrinfo()

socket.getaddrinfo() is a function from the ‘socket’ module that is used to perform DNS lookups on the domain and simultaneously gather data about the network address. socket.getaddrinfo() provides complete information consisting of the socket family, socket type about the IP address.

Performing basic DNS Lookups in Python

Let’s see some examples of how can we perform DNS Lookups in Python using the socket module and socket.getaddrinfo() function.

Using the socket module

Let’s see a simple program that performs a client-server interaction using the socket module. In this, the client will send a message to the server side as a connection is successful, and vice-versa the server will reply as received message back to the client.

CLIENT SIDE:

import socket

def start_server():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(("127.0.0.1", 12345))
    server_socket.listen(1)

    print("Server listening on port 12345...")

    while True:
        client_socket, client_address = server_socket.accept()
        print(f"Connection established with {client_address}")

        data = client_socket.recv(1024)
        if not data:
            break

        message = data.decode()
        print(f"Received from client: {message}")

        client_socket.sendall(data)
        client_socket.close()

    server_socket.close()

SERVER SIDE

import socket

def start_client():
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect(("127.0.0.1", 12345))

    data = client_socket.recv(1024)
    print(f"Received from server: {data.decode()}")

    client_socket.close()

After successful connection the server will send the message to the client side and the message will be displayed.

(Terminal 1) SERVER SIDE:

Server listening on port 12345...
Connection established with ('127.0.0.1', 60328)

(Terminal 2) CLIENT SIDE:

Received from server: Hello from the server!

Learn how to extract Domain name from URL in Python

socket.getaddrinfo() to Resolve IP Addresses

Let’s see a program in which, we will use the socket.getaddrinfo() function in Python to perform the DNS lookup on the selected website. In return the website’s IPv4 and IPv6 addresses will be generated which can be used to track the domain locations.

import sys
import socket

def get_dns_info(hostname):
    try:
        results = socket.getaddrinfo(hostname, None, socket.AF_UNSPEC)
        ipv4_addresses = []
        ipv6_addresses = []

        for result in results:
            family, _, _, _, sockaddr = result
            address = sockaddr[0]

            if family == socket.AF_INET:
                ipv4_addresses.append(address)
            elif family == socket.AF_INET6:
                ipv6_addresses.append(address)

        print(f"DNS information for '{hostname}':")
        print("IPv4 addresses:")
        for ipv4_address in ipv4_addresses:
            print(ipv4_address)

        print("\nIPv6 addresses:")
        for ipv6_address in ipv6_addresses:
            print(ipv6_address)
    except socket.gaierror as e:
        print(f"Error: Unable to resolve '{hostname}': {e}")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python dns_lookup.py <hostname>")
        sys.exit(1)

    website = sys.argv[1]
    get_dns_info(website)

Save the code file with name of your choice with .py extension. To run the program, enter the website name in cmd.

python dns_l.py instagram.com

In the output, the IPv4 and IPv6 address is listed out.

DNS information for 'instagram.com':
IPv4 addresses:
93.184.216.34

IPv6 addresses:
2606:2800:220:1:248:1893:25c8:1946
Dns Lookup output
Dns Lookup Output

Learn more about IP Address in Python

Exception Handling

Exception handling is the process of managing errors and conditions that occur during the execution of the program. In most cases, DNS lookups can fail during execution for various reasons like server issues, network problems, etc. In DNS lookup exceptions can be handled using the ‘try-except’ block. The ‘socket.gaierror’ exception is raised whenever a DNS lookup is failed.

Best Practices and Considerations

  • Timeouts: These are used to avoid long wait loops in case of unresponsive DNS servers. The ‘socket.setdefaulttimeout()’ function is set.
  • Rate Limiting: For frequent DNS lookups, rate limiting avoids overwhelming DNS servers.
  • Input Validation: Validating user inputs in DNS lookups can prevent potential injection attacks.

Security range of /etc/hosts

  • No Caching: The /etc/hosts files do not provide the cache of DNS response as compared to other DNS resolving techniques. As the cache increases, storage space also increases which can slow down the system.
  • No TTL: The /etc/hosts files have no time-based expiration Time to Live (TTL). There is no waiting time as the changes take effect immediately.
  • IP Mapping: IP mapping is the process in which the Ip addresses of a system are identified according to their geographical area location. Mapping is always static and it also does not support DNS features like dynamic updates.

Conclusion

And that’s a wrap! We learned how to perform DNS lookup in the Python language for client-server interaction using the socket module and the socket.getaddrinfo() function. Follow the procedures and you will be able to perform the domain search successfully.

Reference

DNS Lookup with /etc/hosts

Reverse DNS Lookup in Python