¡Esta es una revisión vieja del documento!
Tabla de Contenidos
Introducción a los sockets
Módulo perteneciente al curso Python avanzado para proyectos de seguridad
Los sockets son el componente principal que nos permite aprovechar las capacidades del sistema operativo para interactuar con la red. Puede pensar en los sockets como un canal de comunicación punto a punto entre un cliente y un servidor.
Los sockets de red son una manera fácil de establecer una comunicación entre procesos que están en la misma máquina o en máquinas diferentes. El concepto de un socket es muy similar al de los descriptores de archivos UNIX. Los comandos como read() y write() que nos permiten trabajar con el sistema de archivos, funcionan de manera similar a los sockets. Una dirección de socket de red consta de una dirección IP y un número de puerto. El objetivo de un socket es comunicar procesos a través de la red.
Un puerto es una abstracción empleada por los protocolos de transporte a fin de distinguir entre múltiples conexiones simultáneas en un solo nodo de la red.
Sockets de red en Python
La comunicación entre distintas entidades en una red se basa en el clásico concepto de sockets. Los sockets son un concepto abstracto con el que se designa al punto final de una conexión. Los programas utilizan sockets para comunicarse con otros programas, que pueden estar situados en computadoras distintas.
Un socket queda definido por la dirección IP de la máquina, el puerto en el que escucha y el protocolo que utiliza. Los tipos y funciones necesarios para trabajar con sockets se encuentran en Python en el módulo socket. Los sockets se clasifican en sockets de flujo TCP (socket.SOCK_STREAM) o sockets de datagramas UDP (socket.SOCK_DGRAM) dependiendo de si el servicio utiliza TCP, que es orientado a conexión y fiable, o UDP, respectivamente. En esta unidad didáctica solo cubriremos los sockets orientados a conexión TCP, que cubren un 90 % de las necesidades comunes.
Para crear un socket se utiliza el constructor socket.socket() que puede tomar como parámetros opcionales la familia, el tipo y el protocolo. Por defecto se utiliza la familia AF_INET y el tipo SOCK_STREAM. La sintaxis general del método de socket es la siguiente:
s = socket.socket (socket_family, socket_type, protocol = 0)
Estos argumentos representan las familias de direcciones y el protocolo de la capa de transporte. Dependiendo del tipo de socket, los sockets se clasifican en sockets de flujo (socket.SOCK_STREAM) o sockets de datagramas (socket.SOCK_DGRAM), en función de si el servicio utiliza TCP o UDP. socket.SOCK_DGRAM se usa para comunicaciones UDP, y socket.SOCK_STREAM para conexiones TCP.
Los sockets también se pueden clasificar según la familia. Tenemos sockets UNIX (socket.AF_UNIX) que se crearon antes de la concepción de las redes y se basan en ficheros, sockets socket.AF_INET que son los que nos interesan, sockets socket.AF_INET6 para IPv6, etc.
En la siguiente imagen vemos el constructor de la clase socket:
IMAAAAAAAAAAAAAAAAAAAAGEN
Módulo socket en Python
Los tipos y funciones necesarios para trabajar con sockets se pueden encontrar en Python en el módulo de sockets. El módulo socket expone todas las piezas necesarias para escribir rápidamente clientes y servidores TCP y UDP.
El módulo de socket tiene casi todo lo que necesita para construir un servidor o cliente de socket. En el caso de Python, el socket devuelve un objeto al que se pueden aplicar los métodos de socket. Este módulo viene instalado por defecto cuando instala la distribución Python. Para verificarlo, podemos hacerlo desde el intérprete de Python.
Este módulo viene instalado por defecto cuando instalas la distribución de Python. Para comprobarlo podemos hacerlo desde el intérprete de Python:
>>>import socket >>>dir(socket)
IMAAAAAAAAAAAAAAAAAAAAAAAAAGEN
En esta imagen vemos todas las constantes y métodos que tenemos disponibles en este módulo. Las constantes las vemos en primera instancia dentro de la estructura que ha devuelto el objeto. Entre las constantes más utilizadas podemos destacar:
socket.AF_INETsocket.SOCK_STREAM
Para abrir un socket en una determinada máquina utilizamos el constructor de la clase socket que acepta por parámetros la familia, el tipo de socket y el protocolo. Una llamada típica para construir un socket que funcione a nivel TCP es pasando como parámetros la familia y el tipo de socket:
IMAAAAAAAAAAAAAAAAAAAAAAAGEN
Socket.socket(socket.AF_INET,socket.SOCK_STREAM)
Recopilación de información con sockets
Los métodos útiles para recopilar más información son:
gethostbyaddr(dirección): nos permite obtener un nombre de dominio a partir de la dirección IP.gethostbyname(hostname): nos permite obtener una dirección IP a partir de un nombre de dominio.
Podemos obtener más información sobre estos métodos con el comando de help(socket):
IMAAAAAAAAAAAAAAAAAAAAAAAAGEN
Ahora vamos a detallar algunos métodos relacionados con el host, la dirección IP y la resolución del dominio. Para cada uno, mostraremos un ejemplo simple:
socket.gethostbyname(hostname)
Este método convierte un nombre de host al formato de dirección IPv4. La dirección IPv4 se devuelve en forma de cadena. Este método es equivalente al comando nslookup que podemos encontrar en muchos sistemas operativos:
>>> import socket >>> socket.gethostbyname('packtpub.com') '83.166.169.231' >>> socket.gethostbyname('google.com') '216.58.210.142'
socket.gethostbyname_ex(nombre)
Este método devuelve muchas direcciones IP para un solo nombre de dominio. Significa que un dominio se ejecuta en múltiples IP
>>> socket.gethostbyname_ex('packtpub.com') ('packtpub.com', [], ['83.166.169.231']) >>> socket.gethostbyname_ex('google.com') ('google.com', [], ['216.58.211.46'])
Un dominio es un sistema de denominación servidores en Internet el cual está formado por un conjunto de caracteres que identifica un sitio de la red accesible por un usuario. Cada dominio es administrado por un servidor de dominios (DNS). Los más comunes son .com, .edu, .net, .org y .es
Otro de los métodos que disponemos en la clase sockets es el que permite obtener el nombre cualificado de un dominio:
>>> socket.getfqdn('google.com')
socket.gethostbyaddr(ip_address)
Este método devuelve una tupla (hostname, nombre, ip_address_list) donde hostname es el nombre de host que responde a la dirección IP dada, el nombre es una lista de nombres asociados con la misma dirección ip_address_list es una lista de direcciones IP para la misma interfaz de red en el mismo host.
>>> socket.gethostbyaddr('8.8.8.8') ('google-public-dns-a.google.com', [], ['8.8.8.8'])
socket.getservbyname(servicename [,nombre_protocolo])
Este método le permite obtener el número de puerto del nombre del puerto:
>>> import socket >>> socket.getservbyname('http') 80 >>> socket.getservbyname('smtp','tcp') 25
socket.getservbyport(puerto [, nombre_protocolo])
Este método realiza la operación inversa de la anterior, lo que le permite obtener el nombre del puerto a partir del número de puerto:
>>> socket.getservbyport(80) 'http' >>> socket.getservbyport(23) 'telnet'
Ejemplo para los métodos de socket
El siguiente script es un ejemplo de cómo podemos usar estos métodos para obtener información de los servidores de Google.
Puede encontrar el siguiente código en el archivo sockets_metodos.py:
# -*- encoding: utf-8 -*- import socket import sys try: print("gethostbyname") print(socket.gethostbyname_ex('www.google.es')) print("\ngethostbyaddr") print(socket.gethostbyaddr('216.58.211.228')) print("\ngetfqdn") print(socket.getfqdn('www.google.com')) except socket.error as error: print (str(error)) print ("Error de conexion") sys.exit()
socket.getfqdn obtiene el nombre de dominio que identifica unívocamente a un servidor en Internet.
Búsqueda inversa
Este comando obtiene el nombre de host de la dirección IP. Para esta tarea, podemos usar la función gethostbyaddr().
En este script, obtenemos el nombre de host de la dirección IP de 8.8.8.8.
Puede encontrar el siguiente código en el archivo socket_reverse_lookup.py
#!/usr/bin/env python # --*-- coding: UTF-8 --*-- import sys, socket try : result=socket.gethostbyaddr("8.8.8.8") print("The host name is:") print(" "+result[0]) print("\nAddress:") for item in result[2]: print(" "+item) except socket.herror as e: print("error for resolving ip address:",e)
Actividad práctica
Dado un nombre de dominio introducido por la entrada estándar por parte del usuario, obtener información relación con dicho dominio como dirección IP, host asociado y nombre cualificado del dominio.
import socket dominio = input() try: print("Obtener ip a partir del nombre dominio:") ip = socket.xxx(xxx) print(ip) print("\nObtener host a partir de la direccion ip") print(socket.xxx(str(ip))) print("\nObtener nombre cualificado de un dominio") print(socket.xxx(dominio)) except socket.error as error: print (str(error)) print ("Error de conexion")
Completar el script anterior sustituyendo las xxx por variables y métodos del módulo socket.
Solución:
import socket dominio = input() try: print("Obtener ip a partir del nombre dominio:") ip = socket.gethostbyname(dominio) print(ip) print("\nObtener host a partir de la direccion ip") print(socket.gethostbyaddr(str(ip))) print("\nObtener nombre cualificado de un dominio") print(socket.getfqdn(dominio)) except socket.error as error: print (str(error)) print ("Error de conexion")
