#include "extremite.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BUFSIZE 1024 static int setup_out(int port) { /* Create the socket. */ int server = socket(AF_INET6, SOCK_STREAM, 0); if (server == -1) { perror("socket"); exit(1); } /* Enable socket re-use, makes debugging easier. */ setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int) {1}, sizeof (int)); /* Bind to any local IPv6 address on port PORT. */ struct sockaddr_in6 server_addr = {0}; server_addr.sin6_family = AF_INET6; server_addr.sin6_port = htons(port); server_addr.sin6_addr = in6addr_any; if (bind(server, (struct sockaddr *) &server_addr, sizeof server_addr) == -1) { perror("bind"); exit(1); } /* Pretty print the matched address. */ char server_addr_pretty[INET6_ADDRSTRLEN] = ""; inet_ntop(AF_INET6, &(server_addr.sin6_addr), server_addr_pretty, sizeof server_addr_pretty); printf("Écoute sur : %s\n", server_addr_pretty); if (listen(server, 1) == -1) { perror("listen"); exit(1); } return server; } static int accept_client(int server) { /* Wait for a connection attempt. */ struct sockaddr_in6 client_addr; socklen_t client_addr_len; puts("Attente d’un client."); int client = accept(server, (struct sockaddr *) &client_addr, &client_addr_len); /* We only serve one peer, no need to keep the server socket * open. */ close(server); /* Pretty print the peer’s address */ char client_addr_pretty[INET6_ADDRSTRLEN] = ""; inet_ntop(AF_INET6, &(client_addr.sin6_addr), client_addr_pretty, sizeof client_addr_pretty); printf("Client connecté : %s\n", client_addr_pretty); return client; } int ext_out(int port) { int server = setup_out(port); return accept_client(server); } static int setup_in() { int s = socket(AF_INET6, SOCK_STREAM, 0); if (s == -1) { perror("socket"); exit(1); } return s; } static int try_connect(int s, const char addr[], int port) { struct sockaddr_in6 sa = {0}; sa.sin6_family = AF_INET6; sa.sin6_port = htons(port); inet_pton(AF_INET6, addr, &sa.sin6_addr); puts("Connexion."); if (connect(s, (struct sockaddr *) &sa, sizeof sa) == -1) { perror("connect"); return 0; } char addr_pretty[INET6_ADDRSTRLEN] = ""; inet_ntop(AF_INET6, &(sa.sin6_addr), addr_pretty, sizeof addr_pretty); printf("Connecté à : %s\n", addr_pretty); return 1; } int ext_in(const char addr[], int port) { int s = setup_in(); /* Attempt a connection every second (waiting for the server to * start). */ while (!try_connect(s, addr, port)) { sleep(1); } return s; } /* On établie ici une connexion bidirectionelle en se connectant à un * serveur distant ET en écoutant nous-même sur un port local en * attente d’un client. Cela nous donne donc deux connexions * matérialisées par deux socket, l’une qui est utilisée en lecture et * l’autre en écriture. * * Il pourrait suffir d’avoir une extremité qui écoute, et l’autre qui * se connecte, établissant ainsi une seule connexion qu’on * utiliserait en lecture/écriture, mais cela pose le problème de * savoir qui écoute et qui se connecte. * * De plus, si les deux tentent en parallèle de se connecter OU * d’accepter une connexion (mais de ne pas attendre que les deux * conditions soient vraies, et d’utiliser une seule socket en * lecture/écriture, la première disponible), se posent tout un tas de * problèmes de synchronisation si les deux extrémités du tunnel * essaient de se connecter en même temps par exemple. */ void ext_bidir(const char addr[], int port, int local_port, int local_r, int local_w) { /* First, let’s establish a full-duplex connection. */ int server = setup_out(local_port); int peer_r; /* Future remote client socket to read from. */ int peer_w = setup_in(addr, port); int client_connected = 0; int server_connected = 0; struct timeval one_sec = {.tv_sec=1, .tv_usec=0}; struct timeval *timeout = &one_sec; fd_set set; while (!(client_connected && server_connected)) { if (!client_connected) { FD_ZERO(&set); FD_SET(server, &set); } select(server + 1, &set, NULL, NULL, timeout); if (FD_ISSET(server, &set)) { /* The server socket is readable, a peer is trying to * connect. */ fputs("acceptation\n", stderr); peer_r = accept_client(server); FD_CLR(server, &set); client_connected = 1; fputs("Client connecté.\n", stderr); } else { /* Attempt to connect every second. */ if (try_connect(peer_w, addr, port)) { timeout = NULL; server_connected = 1; fputs("Connecté au serveur.\n", stderr); } else { timeout->tv_sec = 1; timeout->tv_usec = 0; } } } fputs("Connexion bidirectionelle établie.\n", stderr); fd_set rfd; int nfds = peer_r > local_r ? peer_r : local_r; nfds++; char in_buf[1024]; char out_buf[1024]; while (1) { FD_ZERO(&rfd); FD_SET(peer_r, &rfd); FD_SET(local_r, &rfd); select(nfds, &rfd, NULL, NULL, NULL); if (FD_ISSET(peer_r, &rfd)) { /* Ready to read from client. */ ssize_t n = read(peer_r, in_buf, sizeof in_buf); if (n < 0) { perror("read (from out)"); exit(1); } if (write(local_w, in_buf, n) < 0) { perror("write (to local_out)"); exit(1); } } if (FD_ISSET(local_r, &rfd)) { /* Ready to read from tun dev. */ ssize_t n = read(local_r, out_buf, sizeof out_buf); if (n < 0) { perror("read (from local_in)"); exit(1); } if (write(peer_w, out_buf, n) < 0) { perror("write (to in)"); exit(1); } } } }