@ -11,25 +11,30 @@
# include <string.h>
# include <string.h>
# include <sys/ioctl.h>
# include <sys/select.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h>
# include <sys/wait.h>
# include <unistd.h>
# 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 ) ;
}
}
}
}