Resolución de DNS.
Detrás de toda página web, hay una dirección IP asignada a ese nombre gracias al registro in-addr.arpa (IPv4) o ip6.arpa (IPv6). Dicho registro asigna dicho nombre de dominio a una IP específica, por lo que si el servidor necesita cambiar la IP, siempre se mantendrá el mismo nombre de dominio.
El protocolo DNS es el encargado de cambiar una dirección IP a nombre de dominio, más facilmente recordados por los usuarios que dichos números IPs. Además, se puede asociar una IP a un nombre de dominio y cambiar la IP para dicho nombre específico (como suele ocurrir en casos de servidores dedicados con IP variable).
Existen varios clientes DNS tanto en Windows como en Linux para resolver direcciones DNS. El más famosos en Windows y Linux es NSLOOKUP donde meteremos un nombre de dominio y nos sacará su dirección IP.

Resolución de nombre de dominios.
Para traducir el nombre de dominio a IP y viceversa, DNS utiliza dos procesos distintos:
● Resolución directa: de nombre de dominio a IP.
● Resolución indirecta: De IP a nombre de dominio.
El proceso es el siguiente:
● Tu equipo pregunta al resolver DNS (normalmente el del ISP o 1.1.1.1 / 8.8.8.8).
● El resolver, consulta a un servidor raíz (".").
● El raíz, responde con los TLD (por ejemplo, .com).
● El resolver, pregunta al servidor TLD.
● El TLD responde con los servidores autoritativos del dominio (ej. mirpas.com).
● El autoritativo, devuelve el registro A (IPv4)/AAAA(IPv6) con la IP.
● El resolver, te entrega la IP y la cachea.

Proceso mde resolución DNS.
Socket de Python para dns.
La librería socket de python lleva una serie de métodos que son útiles para recopilar información de servidores sin que tengamos que utilizar NMAP. Es verdad que si añadimos a los escaneos de socket NMAP, el rango de captura de información será mayor.
En la clase anterior ya vimos qué son los sockets de python. En esta práctica vamos a utilizar algunos métodos para capturar información. Esta información es pública, por lo que no corres ningún riesgo de vulnerar las políticas de uso de los servidores, aunque corres el riesgo que te bloqueen la IP temporalmente (no es muy habitual).
Vamos a ir desgranando los métodos más usados para obtener información de los servidores o páginas webs.
Obtener información de un servidor.Ya que hemos visto cómo funciona la resolución DNS, podemos hablar del método gethostbyaddr(dirección), para obtener el nombre de dominio a partir de una IP. O el método inverso, gethostbyname(hostname), que nos permite saber la IP a partir del nombre de dominio:
#Nos devuelve la IP:
print("La dirección IP es: {}".format(socket.gethostbyname('mirpas.com')))
#Ahora nos devuelve el nombre de dominio:
print("El servidor es: {}".format(socket.gethostbyaddr('81.169.145.155')))

Tupla de gethostbyaddr.
Fijate que en la anterior imagen, la función gethostbyaddr(), nos ha devuelto una tupla de servidores. Esto indica que un mismo dominio, puede llevar varias direcciones IPs asociadas. Si utilizamos:
print("El servidor es: {}".format(socket.gethostbyname_ex('mirpas.com')))
Nos devuelve el servidor específico y la dirección IP. También en forma de tupla.
El método gethostbyaddr(IP), nos devuelve una tupla con el formato hostname (nombre de host que responde a la IP), name (lista de nombres asociadas a la IP), ip_address_list (lista de direcciones IP para la misma interfaz de red en el host).
El método getfqdn("nombredominio"), nos imprime el nombre completo FQDN (fully qualified domain name), asociado al nombre de dominio. Generalmente devuelve el nombre del servidor raíz. Si te devuelve el mismo nombre, es posible que el servidor no tenga FQDN o PTR asociado.
Otro método de socket nos permitirá comprobar los puertos de un servidor:
print("mirpas.com corre por el puerto {}.".format(getservbyname("http")))
Nos devolverá el puerto 80 si está habilitado. Si interrogamos sobre el FTP, nos devolverá el 21. Solo te devolverá los puertos en uso. Ojo porque esta función nos obligará a importar el módulo getservbyname de socket.
print("mirpas.com corre por el puerto {}.".format(getservbyname("http")))
print("mirpas.com corre por el puerto {}.".format(getservbyname("ftp")))
print("mirpas.com corre por el puerto {}.".format(getservbyname("https")))
Y por supuesto, podemos hacer que introduciendo el número de puerto, nos muestre el protocolo que usa (siempre que exista o esté habilitado dicho puerto). Eso lo hacemos con la función getservbyport(numero), que también tendremos que importar de socket.
print("el puerto 80 es: {}.".format(getservbyport(80)))
Que nos devuelve "http".
Escaner de puertos.Podiamos ejecutar todo lo anterior en un mismo script para que nos devolviese información sobre los servidores de mi hosting w9b.rzone.de. Para ello ejecutamos:
import sys
try:
print("gethostbyname:")
print(socket.gethostbyname_ex("w9b.rzone.de"))
print("\ngethostbyaddr:")
print(socket.gethostbyaddr('81.169.145.155'))
print("\ngetfqdn:")
print(socket.getfqdn('www.google.es')) #Preguntamos sobre servidores de google
except socket.error as error:
print(str(error))
print("Error en la conexión!")
sys.exit()

Datos del servidor mirpas.com y google.es.
Recopilador.
Has aprendido algunos métodos para interrogar servidores con socket. Pero socket va más allá porque tiene un método específico para escanear puertos de una página en cuestión. Ese método es connect_ex(dirección, puerto).
Así podríamos hacer un script que nos indique qué puertos están abiertos.
ip = '81.169.145.155'
puertos = [22, 80, 443]
print("Los puertos abiertos de {}, son:".format(socket.gethostbyaddr(ip)[0]))
for p in puertos:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
resultado = s.connect_ex((ip, p))
if resultado == 0:
print(" - Puerto {} abierto".format(p))
else:
print(" - Puerto {} cerrado".format(p))
s.close()
Si quieres escanear más puertos, simplemente tienes que cambiar la lista de los puertos o añadir más puertos.
Escaner avanzado de puertos.
Vamos a ampliar la información de forma que ahora el programa nos permitirá escanear un nombre de dominio para buscar los puertos abiertos. Insertaremos la dirección de la página y esto nos indicará los puertos abiertos y cerrados que tiene dicha IP. Este es el código:
from socket import *
# Preguntamos por un nombre de dominio.
pagina = input("Introduce página web: ")
port_init = int(input("Introduce el puerto de origen: "))
port_end = int(input("Introduce el puerto final: "))
# Obtenemos la IP del dominio
ip = gethostbyname(pagina)
print("Escaneando IP {}".format(ip))
for port in range(port_init, port_end + 1):
print("Probando puerto {} ...".format(port))
# Creamos el socket
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(1)
# Probamos la conexión
resultado = s.connect_ex((ip, port))
if resultado == 0:
print(" → El puerto {} está ABIERTO".format(port))
s.close()
print("Escaneo finalizado")

El script escanea los puertos de una página.
Lo que hay que mencionar en el script anterior es que gracias la instrucción resultado=s.connect_ex((ip, port), nos intentamos conectar al puerto. Si nos devuelve 0, el puerto está abierto. si nos devuelve otro valor, el puerto está cerrado o filtrado.
Podríamos hacer un escaner de puertos gráfico con Tkinter. Pero eso se lo vas a pedir a la IA a partir del script anterior. ¿Te animas a hacerlo? Muy bien. te paso el prompt:

Script con formato gráfico de escaneo de puertos.
La aplicación la puedes mejorar haciendo un prompt más explícito en cuanto a posición de los controles, colores, funciones que realice el escaneo, etc., etc.
Como siempre los script aquí escritos, te los puedes descargar desde el pie de la página.