[Python] Underch4t v1.0 (Parte 1)

Iniciado por WhiZ, Diciembre 09, 2015, 01:56:48 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Diciembre 09, 2015, 01:56:48 PM Ultima modificación: Diciembre 19, 2015, 05:22:49 PM por WhiZ


UnderCh4t



Este es un pequeño servidor que acabo de crear en Python en respuesta a No tienes permitido ver los links. Registrarse o Entrar a mi cuenta de @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta.

Voy a ir explicando, paso a paso, cómo armar el servidor y, finalmente, pongo todo el código completo.

Debido a que mi idea es ir explicando todo el proceso sin dejar cabos sueltos, ésta va a ser el primero de una serie de artículos en los que intentaré guiarlos en este maravilloso mundo de los sockets.

Comencemos!





CREANDO EL SERVIDOR



Lo primero que haremos es una clase llamada Servidor, la cual, en primera instancia, podrá realizar las siguientes tareas:
    - Sockets (vengan No tienes permitido ver los links. Registrarse o Entrar a mi cuenta los que no saben de qué estamos hablando ;) ):
        · crear un socket (servidor)
        · establecer interfaz de conexión: esto es, adjudicar ip y puerto local a la que se deberán conectar los clientes
        · poner el servidor en escucha
        · atender conexiones entrantes
        . cerrar socket
    - Informar estado del proceso
    - Manejar errores

Veamos cómo quedaría el código:
Código: python
import socket
import sys

class Servidor(object):
    def __init__(self, ip, puerto, max_con=5):
        self.__ip = ip
        self.__puerto = puerto
        self.__max_con = max_con
        self.__clientes = {} # { socket, [ip, puerto]}

    ##########
    # SOCKET #
    ##########
    def ejecutar(self):
        try:
            self._crear_socket()
            self._bind()
            self._escuchar()
            self._cerrar_socket()
        except KeyboardInterrupt:
            self._cerrar_socket()
        except Exception as e:
            self._mostrar_error(e, critico=True)

    def _crear_socket(self):
        pass

    def _bind(self):
        pass

    def _escuchar(self):
        pass

    def _atender_cliente(self):
        pass

    def _cerrar_socket(self):
        pass

    ###########
    # PROCESO #
    ###########
    def _mostrar_info(self, msj):
        print " [+] {}".format(msj)

    def _mostrar_error(self, error, critico):
        print " [!] Error: {}".format(error)
        if critico:
            self._salir()

    def _salir(self):
        sys.exit(" [+] Finalizando aplicación")


Bien. Hasta aquí no hay mucho que decir. Hasta ahora hemos creado una clase 'Servidor' con dos grupos de métodos: los que se encargan de la conexión y los que se encargan de informar proceso y errores.

Vayamos con los métodos de conexión. El primero que vemos es el método 'ejecutar'. Este método constituye la guía de nuestra aplicación. Él se va a encargar de ir llamando, uno a uno, a cada método necesario para establecer nuestras conexiones.

Lo primero que hace es llamar a '_crear_socket'. Al menos que cambiemos un poco su contenido, no creo que pase mucho jeje. Vamos con eso:
Código: python
def _crear_socket(self):
    self._mostrar_info("Creando socket")
    try:
        self.__servidor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__servidor.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    except Exception as e:
        self._mostrar_error(e, critico=True)


Voy a intentar explicar, de forma sencilla, qué significa todo esto que acabamos de escribir. Más allá del manejo de errores, los logs y el debugging, lo que realmente queremos es que este método nos cree un socket. La pregunta es: ¿cómo hacemos eso? El módulo No tienes permitido ver los links. Registrarse o Entrar a mi cuenta de python es quien nos permite realizar esta tarea. Para crear un socket, debemos escribir lo siguiente:
Código: python
mi_socket = socket.socket()


Todo muy lindo hasta acá pero los que saben y entienden de redes se estarán preguntando: ¿qué características tendrá este socket? ¿sobre qué protocolos se llevará a cabo la comunicación? Bueno, por defecto, el objeto utilizará los protocolos IP y TCP. ¿Y si quisiera utilizar UDP, en vez de TCP? ¿Puedo hacerlo? Absolutamente. Tal como se indica en la No tienes permitido ver los links. Registrarse o Entrar a mi cuenta, podemos indicar la familia, el tipo y el protocolo que querramos utilizar:
Código: python
mi_socket = socket.socket(familia, tipo, protocolo)


Para no complicarnos, sólo nos centraremos en la familia y el tipo. Dentro de las familias de direcciones, encontramos las siguientes:

    - AF_INET: le indica al kernel que vamos a utilizar el protocolo IPv4.
    - AF_INET6: le indica al kernel que vamos a utilizar el protocolo IPv6.
    - AF_UNIX: a diferencia de las familias INET que están ligadas a una tupla ip-puerto, las familias UNIX se vinculan a un archivo específico de tu sistema de archivos. Nos sirve, por ejemplo, para comunicación entre procesos de un mismo host.

Dentro de los tipos, los más utilizados son:
    - SOCK_STREAM: le indica al kernel que para transmitir los datos vamos a utilizar un protocolo confiable (TCP, por ejemplo).
    - SOCK_DGRAM: le indica al kernel que para transmitir los datos vamos a utilizar un protocolo no confiable (como es el caso de UDP)

Entonces, si quisiéramos hacer una conexión basada en los protocolos IP y UDP, bastaría con escribir lo siguiente:
Código: python
familia = socket.AF_INET # IPv4
tipo = socket.SOCK_DGRAM # UDP
mi_socket = socket.socket(familia, tipo)


¿Vamos bien hasta ahí? Si es así, entonces continuemos que queda bastante por delante. Tenemos que saber que, una vez creado nuestro socket, podemos acceder a las opciones del mismo para modificarlas. Para hacerlo, el objeto socket nos brinda un método que nos facilita la tarea: setsockopt().

Tal y como nos indica la No tienes permitido ver los links. Registrarse o Entrar a mi cuenta, para modificar una opción debemos indicar el nivel del socket (ya lo veremos unos renglones más abajo),  la opción que queremos modificar y el valor:
Código: python
mi_socket = socket.socket()
mi.socket.setsockopt(nivel, opcion, valor)


Cuando hablamos de nivel, nos referimos, por decirlo de forma sencilla, al protocolo en el que se encuentra la opción que queremos modificar. Cada nivel tiene un valor que lo identifica, por ejemplo:
    - SOL_SOCKET: es el nivel del socket y el valor que lo identifica es 1.
    - SOL_TCP: es el nivel del protocolo TCP, cuyo identificador es el 6.
    - SOL_UDP: nivel del protocolo UDP, identificado por el número 17.

Como a nosotros sólo nos interesa acceder a las opciones del socket, vamos a utilizar SOL_SOCKET para indicar el nivel.

Dentro de las opciones (No tienes permitido ver los links. Registrarse o Entrar a mi cuenta), a mi me interesa una en particular: SO_REUSEADDR. Esta opción nos permite reutilizar un socket local que se encuentre en estado TIME_WAIT, sin tener que esperar su tiempo natural de expiración (para los que no engancharon nada -> No tienes permitido ver los links. Registrarse o Entrar a mi cuenta ;) ). La opción sólo acepta valores booleanos, es decir, reutiliza el socket o no lo utiliza (reutilizar: SI/NO).

Bien, ahora que entendemos un poco mejor cómo es esto de las opciones, vamos a modificar esta opción:
Código: python
mi_socket = socket.socket()
nivel = SOL_SOCKET # Nivel del socket
opcion = SO_REUSADDR # Opción a modificar
valor = True # Sí, queremos reutilizar el socket
mi_socket.setsockopt(nivel, opcion, valor)


Bien. Con esto queda concluido nuestro método para crear sockets. Con todo lo visto, ya son capaces de adaptar el método a sus necesidades. De hecho, podríamos modificar un poco el método para que sea más flexible:

Código: python
def _crear_socket(self, familia, tipo, opciones):
    self._mostrar_info("Creando socket")
    try:
        self.__servidor = socket.socket(familia, tipo)
        for opcion in opciones:
            nivel, opcion, valor = opcion[:]
        self.__servidor.setsockopt(nivel, opcion, valor)
    except Exception as e:
        self._mostrar_error(e, critico=True)





Con esto doy por concluida esta primera parte. En los próximos días iré desarrollando los demás métodos que tenemos pendientes y más ;)

Saludos!
WhiZ

P.D.: muchas gracias @po6xsecpo por la correción.


Diciembre 09, 2015, 02:24:02 PM #1 Ultima modificación: Diciembre 09, 2015, 10:24:13 PM por WhiZ
Gracias por el aporte.
En la línea 7 del primer script tenemos un error, la asignación del parámetro al atributo puerto está como:
Código: python
self.__port = port

Y el parámetro es "puerto". Por ende dará error.


Duda. En la línea 4 del método _crear_socket(self)
Código: python
self.__servidor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Al usar self.__servidor, estás declarando e inicializando el objeto self.__servidor ó este debía existir previamente entre los atributos de la clase Socket(object) ?


Gracias por el aporte. Estaré al tanto de la serie

Diciembre 09, 2015, 04:14:14 PM #2 Ultima modificación: Diciembre 09, 2015, 10:25:13 PM por WhiZ
No tienes permitido ver los links. Registrarse o Entrar a mi cuentaEn la línea 7 del primer script tenemos un error, la asignación del parámetro al atributo puerto está como:
Código: python
self.__port = port

Y el parámetro es "puerto". Por ende dará error.
Sí, es cierto. Pasa que a la aplicación la hice en inglés y la estoy traduciendo para el tuto jeje. Voy a tener más cuidado. Gracias por el aviso ;)

No tienes permitido ver los links. Registrarse o Entrar a mi cuentaEn la línea 4 del método _crear_socket(self)
Código: python
self.__servidor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Al usar self.__servidor, estás declarando e inicializando el objeto self.__servidor ó este debía existir previamente entre los atributos de la clase Socket(object) ?
__servidor es una instancia de socket() que pertenece a la clase Servidor. Es decir, estoy declarando e inicializando el objeto.

No tienes permitido ver los links. Registrarse o Entrar a mi cuentaGracias por el aporte. Estaré al tanto de la serie
No, gracias a vos por tu interés :)

Saludos!
WhiZ