El Taller a resolver es éste:
Se pueden notar que nos permiten solo usar el módulo de Sockets, no podemos usar cosas como Requests 🙁
Introducción
Para esto necesitamos usar Python. Hay varias formas de prepararlo:
- Usar Colab, que tiene un intérprete Python online que podremos usar sin instalar nada. (OJO: leer la nota de Colab al final)
- Instalar Python para Windows, y como IDEs podemos usar VS Code, o PyCharm.
- Usar una imagen de Debian en Virtualbox. Una instalación de escritorio normal ya viene con Python preinstalado y listo para usar.
1. El programa debe recibir dominios de Internet y devolver la IP de cada uno.
En otras palabras, debemos resolver los dominios a su IP.
Tomen en cuenta que se pidió como argumento una lista de dominios, que pueden ser uno, dos, muchos o ninguno. Tendremos que iterar.
Cuando ejecutamos un programa, podemos pasarle argumentos (como cat programa.py
). En Python leeremos los argumentos que el usuarios nos dé, con el módulo sys
.
Código:
import socket # Importamos el módulo socket
import sys
# Ejecuta el programa como:
# python3 ej1_resolvedor.py www.google.com www.facebook.com www.youtube.com
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Creamos un objeto socket
for x in sys.argv: # Recorremos tooodos los argumentos pasados al programa
if x != sys.argv[0]: # Ignoramos el primer argumento (que es el nombre del programa en sí)
hostname = x
try:
ip_address = socket.gethostbyname(hostname) # Solicitamos al PC resolver el hostname a su dirección IP
print(f"La IP de {hostname} es {ip_address}")
except socket.gaierror as e:
print(f"Error: {e}") # Si no se puede resolver el hostname, se imprime el error
client_socket.close() # Cerramos el socket
Esto usa una funcionalidad integrada del módulo de sockets, donde la parte de la resolución del dominio se lo delega al sistema.
También se puede armar una versión donde tú te comuniques directamente al DNS, y en «idioma DNS» tú le pidas que te resuelva el dominio. Hacer eso es una locura, pero si alguien lo quiere probar puede verlo.
2. Programa que dé la hora
Esto se puede hacer con Python, aperturando un puerto TCP/9876 y pueden usar código de ejemplos en Internet, de guías o de otros paralelos.
Cada vez que cualquiera se conecte, este debe devolver la hora y luego cerrar la conexión.
Para el programa cliente, se puede usar cualquiera que abra una conexión súper simple. Telnet es un protocolo que abre conexiones simples, y podremos aprovecharlo:
En Linux podemos conectarnos así: telnet localhost 9876
.
En Windows o Linux podemos conectarnos con el programa Putty de esta manera:
3. Cliente HTML que reciba una dirección web y la abra.
Este programa usa un Socket TCP cliente.
El programa, a grandes rasgos, debe:
1) Resolver el dominio, tal como está en 1.
2) Abrir una conexión TCP cliente a la IP, puerto 80.
3) Enviarle una Petición
4) Recibir el contenido y mostrarlo con print().
La Petición la debemos hardcodear y sería algo parecido a esto:
GET / HTTP/1.1
HOST: google.com
Accept: */*
La primera línea pide el archivo
La segunda pide el dominio
La tercera le dice al server que aceptaremos todo lo que nos dé.
4. Servidor web de archivos
Lo que nos piden:
- Que el servidor corra continuamente
- Que se revise si algún archivo pedido exista, si es el caso, abrirlo
import socket # Importamos el módulo socket
# Declaramos la funcion Pedido
def pedido(conn):
# Llega un pedido
request = conn.recv(1024).decode() # Recibimos el pedido y lo ponemos en un string
if 'GET ' in request: # Si el pedido es un GET
file_name = request.split()[1][1:] # Obtenemos el nombre del archivo pedido
print("Archivo pedido: "+file_name)
try:
with open(file_name, 'r') as f: # Abrimos el archivo pedido
response_body = f.read()
response = f"HTTP/1.1 200 OK\r\n\r\n{response_body}" # Respondemos
except FileNotFoundError: # Si el archivo no existe, devolvemos un error
response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found"
else: # Si nos piden algo que no es GET
response = 'HTTP/1.1 405 Method Not Allowed\r\n\r\nMethod Not Allowed'
conn.sendall(response.encode()) # Enviamos
conn.close()
# -------------------------
# Inicio del programa
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Creamos un objeto socket
server_socket.bind(('0.0.0.0', 8888)) # Asociamos el socket a un host y puerto
server_socket.listen(5) # Ponemos el socket en modo escucha
print('Servidor Funcionando.')
while True: # Bucle infinito, termina una peticion y esperamos otra
client_socket, addr = server_socket.accept() # Si hay una conexion de entrada, la aceptamos y la mandamos a la funcion Pedido
pedido(client_socket)
Este código da una idea de la funcionalidad más básica de un web server.
Faltan varias cosas:
- Cambiar número de puerto de escucha
- No implementa Content-Type, lo cual complica el usarlo con navegadores web
- No implementa Content-Length
- No genera un listado de páginas dentro de la carpeta, o sea para probarlo tendremos que escribir todo el URL a mano.
- No se valida lo que pide el cliente. Alguien podria intentar hackear el webserver con algo como
http://localhost:8888/../../../../../etc/passwd
Sobre el uso de Colab
En la evaluación, necesitamos ejecutar el programa Python como un archivo «.py» y pasarle argumentos.
En Colab no se puede pasar argumentos, pero una forma de hacerlo sin tocar el código es:
import socket
import sys
sys.argv[1] = "google.com"
sys.argv[2] = "facebook.com"
(...)
Inicias desde el 1, y en el 2 o 3 puedes ir poniendo los dominios que quieras.
Esto es técnicamente «usar argumentos», pero esto no está probado si será válido en la defensa.