diff --git a/partage/Makefile b/partage/Makefile index 2acdc80..828c7e5 100644 --- a/partage/Makefile +++ b/partage/Makefile @@ -1,4 +1,4 @@ -CC := gcc -Wall -Wextra -Wpedantic -Wshadow=local -Iinclude -Isrc -g -std=c11 +CC := gcc -Wall -Wextra -Wpedantic -Iinclude -Isrc -g -std=c11 # pkg-config # LIBS := diff --git a/partage/src/extremite.c b/partage/src/extremite.c index b3147ce..84b49ff 100644 --- a/partage/src/extremite.c +++ b/partage/src/extremite.c @@ -11,25 +11,30 @@ #include #include #include +#include #include #include +#include #include -#include #include +#include #define BUFSIZE 1024 -int ext_out(int port) { +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); @@ -38,6 +43,8 @@ int ext_out(int port) { 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); @@ -47,87 +54,184 @@ int ext_out(int port) { 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; - - /* char buf[BUFSIZE]; */ - /* ssize_t n; */ - /* while (1) { */ - /* n = read(client, buf, sizeof buf); */ - /* if (n == -1) { */ - /* perror("read"); */ - /* exit(1); */ - /* } */ - /* n = write(out, buf, n); */ - /* if (n == -1) { */ - /* perror("write"); */ - /* exit(1); */ - /* } */ - /* } */ - - /* close(client); */ } -int ext_in(const char addr[], int port) { +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."); - while (connect(s, (struct sockaddr *) &sa, sizeof sa) == -1) { + if (connect(s, (struct sockaddr *) &sa, sizeof sa) == -1) { perror("connect"); - sleep(1); + 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; - - /* char buf[BUFSIZE]; */ - /* ssize_t n; */ - /* while (1) { */ - /* n = read(in, buf, sizeof buf); */ - /* if (n == -1) { */ - /* perror("read"); */ - /* exit(1); */ - /* } */ - /* n = write(s, buf, n); */ - /* if (n == -1) { */ - /* perror("write"); */ - /* exit(1); */ - /* } */ - /* } */ - - /* close(s); */ } + -void ext_bidir(const char addr[], int port, int in, int out) { - pid_t pid = fork(); - if (pid == 0) { - ext_in(addr, port); +/* 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; + } + } } - else { - ext_out(port); - waitpid(pid, NULL, 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); + } + } } } diff --git a/partage/src/extremite.h b/partage/src/extremite.h index 5753af1..5c3b67e 100644 --- a/partage/src/extremite.h +++ b/partage/src/extremite.h @@ -2,9 +2,18 @@ #define EXTREMITE_H +/* Sets up a tunnel exit on the specified PORT, returns a readable fd + * representing it. */ int ext_out(int port); + +/* Sets up a tunnel entrance connected to the exit at ADDR and PORT, + * returns a writable fd representing it. */ int ext_in(const char addr[], int port); -void ext_bidir(const char addr[], int port, int in, int out); + +/* Establishes a full-duplex tunnel listening on LOCAL_PORT and + * connected to ADDR and PORT. Copies data from local_r to the tunnel, + * and data from the tunnel to local_w. */ +void ext_bidir(const char addr[], int port, int local_port, int local_r, int local_w); #endif diff --git a/partage/src/iftun.c b/partage/src/iftun.c index 1d17640..67e4c70 100644 --- a/partage/src/iftun.c +++ b/partage/src/iftun.c @@ -34,6 +34,7 @@ int tun_alloc(const char *dev) { int err = ioctl(fd, TUNSETIFF, (void *) &ifr); if(err < 0){ + perror("ioctl"); exit(1); } @@ -46,7 +47,15 @@ int copy(int src, int dest) { int n = 0; while (1) { - n = read(src, buf, 1024); - write(dest, buf, n); + n = read(src, buf, sizeof buf); + if (n < 0) { + perror("copy"); + exit(1); + } + n = write(dest, buf, n); + if (n < 0) { + perror("copy"); + exit(1); + } } } diff --git a/partage/src/tunnel64d.c b/partage/src/tunnel64d.c index 558b184..7e3ad16 100644 --- a/partage/src/tunnel64d.c +++ b/partage/src/tunnel64d.c @@ -68,7 +68,7 @@ int main(int argc, char *argv[argc]) { fprintf(stderr, "Erreur lors de l’exécution du script de configuration de l’interface.\n"); return 1; } - ext_bidir(outip, outport, tun, tun); + ext_bidir(outip, outport, inport, tun, tun); return 0; } diff --git a/partage/test/iftun.c b/partage/test/iftun.c index 321c500..b15408b 100644 --- a/partage/test/iftun.c +++ b/partage/test/iftun.c @@ -1,6 +1,7 @@ #include "iftun.h" #include +#include int main(int argc, char *argv[argc]) { @@ -8,6 +9,12 @@ int main(int argc, char *argv[argc]) { fprintf(stderr, "Utilisation : %s interface\n", argv[0]); return 1; } + + if (getuid() != 0) { + fprintf(stderr, "%s doit être lancé en tant que superutilisateur.\n", argv[0]); + return 1; + } + int tun = tun_alloc(argv[1]); fprintf(stderr, "Configurez l’interface puis appuyez sur ... "); fflush(stderr);