From f53c56d8797134a556609db33c23dce1ab0c59dc Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Tue, 7 Apr 2009 10:09:50 +0200 Subject: renamed tests to be pure unix tests --- tests/network/GNUmakefile | 4 +- tests/network/test1.c | 506 --------------------------------------------- tests/network/test1_unix.c | 506 +++++++++++++++++++++++++++++++++++++++++++++ tests/network/test2.c | 404 ------------------------------------ tests/network/test2_unix.c | 404 ++++++++++++++++++++++++++++++++++++ 5 files changed, 912 insertions(+), 912 deletions(-) delete mode 100644 tests/network/test1.c create mode 100644 tests/network/test1_unix.c delete mode 100644 tests/network/test2.c create mode 100644 tests/network/test2_unix.c (limited to 'tests') diff --git a/tests/network/GNUmakefile b/tests/network/GNUmakefile index 13dceb7..3e1edbe 100644 --- a/tests/network/GNUmakefile +++ b/tests/network/GNUmakefile @@ -7,8 +7,8 @@ INCLUDE_LIBS = \ $(TOPDIR)/src/libwolf.a TEST_BINS = \ - test1$(EXE) \ - test2$(EXE) + test1_unix$(EXE) \ + test2_unix$(EXE) -include $(TOPDIR)/makefiles/gmake/sub.mk diff --git a/tests/network/test1.c b/tests/network/test1.c deleted file mode 100644 index 2b29445..0000000 --- a/tests/network/test1.c +++ /dev/null @@ -1,506 +0,0 @@ -/* Unix traditional and most portable way of a TCP/IP client using select(). - * - * Also tests asynchrononous connect (for proper timeout handling in case of - * no connection). - */ - -#include "port/netdb.h" /* for getaddrinfo, getnameinfo */ -#include "network/network.h" /* for networking stuff */ -#include "port/stdlib.h" /* for EXIT_XXX */ -#include "port/stdio.h" /* for fprintf */ -#include "port/string.h" /* for memset */ -#include "port/stdbool.h" /* for bool, true, false */ -#include "port/unused.h" /* for WOLF_UNUSED */ - -#include /* for close */ -#include /* for errno */ -#include /* for fcntl */ -#include /* for select */ -#include /* for assertions */ -#include /* for signal */ - -#define DEBUG 0 - -static bool wolf_network_sock_nonblocking( int fd ) { - int flags; - - flags = fcntl( fd, F_GETFL, 0 /* ignored */ ); - if( flags < 0 ) return false; - flags |= O_NONBLOCK; - flags = fcntl( fd, F_SETFL, flags ); - if( flags < 0 ) return false; - return true; -} - -#if DEBUG -static char *fd_set_to_string( fd_set *fd, int min_fd, int max_fd, char *buf, size_t buflen ) { - int i; - size_t j; - - assert( min_fd <= max_fd ); - - for( i = min_fd, j = 0; i <= max_fd && j < buflen; i++, j++ ) { - if( FD_ISSET( i, fd ) ) { - buf[j] = '1'; - } else { - buf[j] = '0'; - } - } - - buf[j] = '\0'; - - return buf; -} -#endif - -#define MAX_IDLE_TIMEOUT 10 -#define WRITE_BUFFER_SIZE 4096 -#define READ_BUFFER_SIZE 4096 -#define CONNECT_TIMEOUT 2 - -#define max(a,b) ((a) < (b) ? (b) : (a)) - -static volatile bool terminate = false; - -static void term_handler( int sig ) { - WOLF_UNUSED( sig ); - terminate = true; -} - -int main( int argc, char *argv[] ) { - char *host; - char *service; - struct addrinfo hints; - struct addrinfo *result; - int error; - int fd; - int res; - int idle_secs; - char read_buffer[READ_BUFFER_SIZE]; - char write_buffer[WRITE_BUFFER_SIZE]; - size_t stdout_to_write; - size_t stdout_written; - size_t fd_to_write; - size_t fd_written; - size_t total_stdin_read; - size_t total_fd_read; - size_t total_fd_written; - size_t total_stdout_written; - int count; - bool stdin_eof; - bool fd_write_closed; - bool fd_eof; - int max_fd; - fd_set read_set; - fd_set write_set; - struct timeval timeout; - socklen_t peer_addr_len; - wolf_network_sockaddr_union_t peer_addr; - char peer_hostname[NI_MAXHOST] = ""; - char peer_service[NI_MAXSERV] = ""; - - if( argc != 3 ) { - fprintf( stderr, "usage: test1 \n" ); - goto FAIL; - } - - host = argv[1]; - service = argv[2]; - -#ifdef _WIN32 - WSADATA wsa_data; - WSAStartup( MAKEWORD( 2, 2 ), &wsa_data ); -#endif - - /* tell getaddrinfo what we want */ - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - /* resolve the domain name into a list of addresses */ - error = getaddrinfo( host, service, &hints, &result ); - if( error != 0 ) { - fprintf( stderr, "getaddrinfo failed: %s (%d)\n", - gai_strerror( error ), error ); - goto FAIL; - } - - /* open the client endpoint */ - fd = socket( result->ai_family, result->ai_socktype, result->ai_protocol ); - if( fd < 0 ) { - fprintf( stderr, "socket failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - /* set socket non-blocking for asynchronous connect */ - if( !wolf_network_sock_nonblocking( fd ) ) { - (void)close( fd ); - fprintf( stderr, "set nonblocking failed for socket: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - /* connect asynchronously, from now on no matter what happens (for instance an - * interrupt by a signal) the kernel tries to establish the connection! So we - * don't have to go back into a connect.. - */ - res = connect( fd, result->ai_addr, result->ai_addrlen ); - if( res < 0 ) { - if( errno == EINPROGRESS ) { - /* this is ok, being connected, we loop later in a select */ - fprintf( stderr, "connecting in progress..\n" ); - } else if( errno == EINTR ) { - /* interrupted, no problem, the connection goes on anyway */ - } else { - (void)close( fd ); - fprintf( stderr, "connect failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - } else { - /* a fast connect */ - goto CONNECTED; - } - - /* wait till we are connected */ -CONNECT_SELECT_AGAIN: - FD_ZERO( &read_set ); - FD_ZERO( &write_set ); - FD_SET( fd, &read_set ); - FD_SET( fd, &write_set ); - timeout.tv_sec = CONNECT_TIMEOUT; - timeout.tv_usec = 0; - res = select( fd + 1, &read_set, &write_set, NULL, &timeout ); - if( res < 0 ) { - if( errno == EINTR || errno == EAGAIN ) { - goto CONNECT_SELECT_AGAIN; - } else { - /* fatal errors */ - fprintf( stderr, "select failed: %s (%d)\n", - strerror( errno ), errno ); - (void)close( fd ); - goto FAIL; - } - } else if( res == 0 ) { - /* connect timeout */ - fprintf( stderr, "Connect timeout..terminating\n" ); - goto FAIL; - } else { - if( FD_ISSET( fd, &read_set ) || FD_ISSET( fd, &write_set ) ) { -/* strategy second connect */ -SECOND_CONNECT_AGAIN: - res = connect( fd, result->ai_addr, result->ai_addrlen ); - if( res < 0 ) { - if( errno == EINTR ) { - /* interrupted, no problem, the connection goes on anyway */ - goto SECOND_CONNECT_AGAIN; - } else if( errno == EISCONN ) { - /* already connected, first connect succeeded */ - goto CONNECTED; - } else { - (void)close( fd ); - fprintf( stderr, "second connect failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - } - -/* strategy setsockopt and SO_ERROR: - -Not really realiable! Can result 0 though the server is not there! - socklen_t sock_error_len; - int sock_error; - - sock_error_len = sizeof( int ); - res = getsockopt( fd, SOL_SOCKET, SO_ERROR, &sock_error, &sock_error_len ); - if( res < 0 ) { - fprintf( stderr, "getsockopt for connection check failed: %s (%d)\n", - strerror( errno ), errno ); - (void)close( fd ); - goto FAIL; - } else { - if( error == 0 ) { - goto CONNECTED; - } else { - fprintf( stderr, "SO_ERROR is not ok: %s (%d)\n", - strerror( error ), error ); - (void)close( fd ); - goto FAIL; - } - } -*/ - } else { - fprintf( stderr, "Socket not ready after select\n" ); - (void)close( fd ); - goto FAIL; - } - } - -CONNECTED: - /* determine the address of the other end of the connection */ - peer_addr_len = sizeof( peer_addr ); - res = getsockname( fd, &peer_addr.addr, &peer_addr_len ); - if( res < 0 ) { - (void)close( fd ); - fprintf( stderr, "getsockname failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - res = getnameinfo( &peer_addr.addr, peer_addr_len, - peer_hostname, NI_MAXHOST, - peer_service, NI_MAXSERV, - NI_NUMERICSERV | NI_NUMERICHOST ); - if( res < 0 ) { - (void)close( fd ); - fprintf( stderr, "getnameinfo failed: %s (%d)\n", - gai_strerror( error ), error ); - goto FAIL; - } - - fprintf( stderr, "Connected to %s, port %s (peer: %s, %s)\n", host, service, - peer_hostname, peer_service ); - - if( !wolf_network_sock_nonblocking( STDIN_FILENO ) ) { - (void)close( fd ); - fprintf( stderr, "set nonblocking failed for stdin: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - if( !wolf_network_sock_nonblocking( STDOUT_FILENO ) ) { - (void)close( fd ); - fprintf( stderr, "set nonblocking failed for stdout: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - idle_secs = 0; - count = 0; - stdout_to_write = 0; - stdout_written = 0; - fd_to_write = 0; - fd_written = 0; - total_stdin_read = 0; - total_fd_read = 0; - total_fd_written = 0; - total_stdout_written = 0; - stdin_eof = false; - fd_write_closed = false; - fd_eof = false; - - signal( SIGTERM, term_handler ); - signal( SIGINT, term_handler ); - signal( SIGPIPE, SIG_IGN ); - - do { - ssize_t rres; -#if DEBUG - char buf[10]; - char buf2[10]; -#endif - - FD_ZERO( &read_set ); - FD_ZERO( &write_set ); - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - /* set up select mask and compute highest file descriptor number */ - max_fd = 0; - max_fd = max( max_fd, STDIN_FILENO ); - max_fd = max( max_fd, STDOUT_FILENO ); - max_fd = max( max_fd, fd ); - - if( !stdin_eof && fd_to_write == 0 ) { - FD_SET( STDIN_FILENO, &read_set ); - } - if( stdout_to_write > 0 ) { - FD_SET( STDOUT_FILENO, &write_set ); - } - if( !fd_eof && stdout_to_write == 0 ) { - FD_SET( fd, &read_set ); - } - if( fd_to_write > 0 ) { - FD_SET( fd, &write_set ); - } - - /* wait for events or timeout */ -#if DEBUG -fprintf( stderr, "select call read_fd: %s write_fd: %s wbuf: %d rbuf: %d stdin_eof: %d fdwcl: %d\n", - fd_set_to_string( &read_set, 0, max_fd, buf, 10 ), - fd_set_to_string( &write_set, 0, max_fd, buf2, 10 ), - fd_to_write, stdout_to_write, stdin_eof, fd_write_closed ); -#endif - - res = select( max_fd + 1, &read_set, &write_set, NULL, &timeout ); - -#if DEBUG -fprintf( stderr, "select %04d read_fd: %s write_fd: %s wbuf: %d rbuf: %d stdin_eof: %d fdwcl: %d\n", - res, fd_set_to_string( &read_set, 0, max_fd, buf, 10 ), - fd_set_to_string( &write_set, 0, max_fd, buf2, 10 ), - fd_to_write, stdout_to_write, stdin_eof, fd_write_closed ); -#endif - - if( res < 0 ) { - if( errno == EINTR || errno == EAGAIN ) { - /* skip */ - } else { - /* fatal errors */ - fprintf( stderr, "select failed: %s (%d)\n", - strerror( errno ), errno ); - (void)close( fd ); - goto FAIL; - } - } else if( res == 0 ) { - /* timeout */ - idle_secs++; - if( idle_secs > MAX_IDLE_TIMEOUT ) { - fprintf( stderr, "Idle connection after %d seconds..terminating\n", - idle_secs ); - goto END; - } - } else { - /* something happened */ - idle_secs = 0; - - /* empty the socket read buffer to stdout */ - if( FD_ISSET( STDOUT_FILENO, &write_set ) ) { - assert( stdout_to_write > 0 ); - rres = write( STDOUT_FILENO, read_buffer + stdout_written, stdout_to_write ); - if( rres < 0 ) { - if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { - /* skip */ - } else { - fprintf( stderr, "write to stdout failed: %s (%d)\n", - strerror( errno ), errno ); - (void)close( fd ); - goto FAIL; - } - } else { - assert( rres >= 0 ); - stdout_written += (size_t)rres; - assert( stdout_to_write >= (size_t)rres ); - stdout_to_write -= (size_t)rres; - total_stdout_written += (size_t)rres; - } - } - - /* empty the socket write buffer writing to fd */ - if( FD_ISSET( fd, &write_set ) ) { - assert( fd_to_write > 0 ); - rres = write( fd, write_buffer + fd_written, fd_to_write ); - if( rres < 0 ) { - if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { - /* skip */ - } else { - fprintf( stderr, "write to socket failed: %s (%d)\n", - strerror( errno ), errno ); - (void)close( fd ); - goto FAIL; - } - } else { - assert( rres >= 0 ); - fd_written += (size_t)rres; - assert( fd_to_write >= (size_t)rres ); - fd_to_write -= (size_t)rres; - total_fd_written += (size_t)rres; - } - } - - /* read from socket, fill socket read buffer */ - if( FD_ISSET( fd, &read_set ) ) { - assert( stdout_to_write == 0 ); - rres = read( fd, read_buffer, READ_BUFFER_SIZE ); - if( rres < 0 ) { - if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { - /* skip */ - } else { - fprintf( stderr, "read from socket failed: %s (%d)\n", - strerror( errno ), errno ); - (void)close( fd ); - goto FAIL; - } - } else if( rres == 0 ) { - /* EOF on socket */ - shutdown( fd, SHUT_RD ); - fd_eof = true; -#if DEBUG - fprintf( stderr, "EOF on socket\n" ); -#endif - } else { - assert( rres > 0 ); - stdout_written = 0; - stdout_to_write = (size_t)rres; - total_fd_read += (size_t)rres; - } - } - - /* read from stdin, fill socket write buffer */ - if( FD_ISSET( STDIN_FILENO, &read_set ) ) { - assert( fd_to_write == 0 ); - rres = read( STDIN_FILENO, write_buffer, WRITE_BUFFER_SIZE ); - if( rres < 0 ) { - if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { - /* skip */ - } else { - fprintf( stderr, "read from stdin failed: %s (%d)\n", - strerror( errno ), errno ); - (void)close( fd ); - goto FAIL; - } - } else if( rres == 0 ) { - /* EOF on STDIN */ - stdin_eof = true; - fclose( stdin ); - -#if DEBUG - fprintf( stderr, "EOF on stdin\n" ); -#endif - } else { - assert( rres > 0 ); - fd_written = 0; - fd_to_write = (size_t)rres; - total_stdin_read += (size_t)rres; - } - } - - /* close writing side of the socket if we have emptied - * the socket write buffer and there is nothing more on - * the input (stdin) - */ - if( fd_to_write == 0 && stdin_eof && !fd_write_closed ) { -#if DEBUG - fprintf( stderr, "socket SHUT_WR\n" ); -#endif - shutdown( fd, SHUT_WR ); - fd_write_closed = true; - } - } - - count++; - if( count % 10000 == 0 ) { - fprintf( stderr, "Transfered nof_selects: %d, stdin: %zd, stdout: %zd, fd-in: %zd, fd-out: %zd\n", - count, total_stdin_read, total_stdout_written, total_fd_read, total_fd_written ); - } - } while( !terminate && !( stdin_eof && fd_eof ) ); - -END: - fprintf( stderr, "Terminated stdin: %zd, stdout: %zd, fd-in: %zd, fd-out: %zd\n", - total_stdin_read, total_stdout_written, total_fd_read, total_fd_written ); - - res = close( fd ); - if( res < 0 ) { - fprintf( stderr, "close failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - goto OK; - -FAIL: - -#ifdef _WIN32 - WSACleanup( ); -#endif - - return EXIT_FAILURE; - -OK: - - freeaddrinfo( result ); - - return EXIT_SUCCESS; -} diff --git a/tests/network/test1_unix.c b/tests/network/test1_unix.c new file mode 100644 index 0000000..2b29445 --- /dev/null +++ b/tests/network/test1_unix.c @@ -0,0 +1,506 @@ +/* Unix traditional and most portable way of a TCP/IP client using select(). + * + * Also tests asynchrononous connect (for proper timeout handling in case of + * no connection). + */ + +#include "port/netdb.h" /* for getaddrinfo, getnameinfo */ +#include "network/network.h" /* for networking stuff */ +#include "port/stdlib.h" /* for EXIT_XXX */ +#include "port/stdio.h" /* for fprintf */ +#include "port/string.h" /* for memset */ +#include "port/stdbool.h" /* for bool, true, false */ +#include "port/unused.h" /* for WOLF_UNUSED */ + +#include /* for close */ +#include /* for errno */ +#include /* for fcntl */ +#include /* for select */ +#include /* for assertions */ +#include /* for signal */ + +#define DEBUG 0 + +static bool wolf_network_sock_nonblocking( int fd ) { + int flags; + + flags = fcntl( fd, F_GETFL, 0 /* ignored */ ); + if( flags < 0 ) return false; + flags |= O_NONBLOCK; + flags = fcntl( fd, F_SETFL, flags ); + if( flags < 0 ) return false; + return true; +} + +#if DEBUG +static char *fd_set_to_string( fd_set *fd, int min_fd, int max_fd, char *buf, size_t buflen ) { + int i; + size_t j; + + assert( min_fd <= max_fd ); + + for( i = min_fd, j = 0; i <= max_fd && j < buflen; i++, j++ ) { + if( FD_ISSET( i, fd ) ) { + buf[j] = '1'; + } else { + buf[j] = '0'; + } + } + + buf[j] = '\0'; + + return buf; +} +#endif + +#define MAX_IDLE_TIMEOUT 10 +#define WRITE_BUFFER_SIZE 4096 +#define READ_BUFFER_SIZE 4096 +#define CONNECT_TIMEOUT 2 + +#define max(a,b) ((a) < (b) ? (b) : (a)) + +static volatile bool terminate = false; + +static void term_handler( int sig ) { + WOLF_UNUSED( sig ); + terminate = true; +} + +int main( int argc, char *argv[] ) { + char *host; + char *service; + struct addrinfo hints; + struct addrinfo *result; + int error; + int fd; + int res; + int idle_secs; + char read_buffer[READ_BUFFER_SIZE]; + char write_buffer[WRITE_BUFFER_SIZE]; + size_t stdout_to_write; + size_t stdout_written; + size_t fd_to_write; + size_t fd_written; + size_t total_stdin_read; + size_t total_fd_read; + size_t total_fd_written; + size_t total_stdout_written; + int count; + bool stdin_eof; + bool fd_write_closed; + bool fd_eof; + int max_fd; + fd_set read_set; + fd_set write_set; + struct timeval timeout; + socklen_t peer_addr_len; + wolf_network_sockaddr_union_t peer_addr; + char peer_hostname[NI_MAXHOST] = ""; + char peer_service[NI_MAXSERV] = ""; + + if( argc != 3 ) { + fprintf( stderr, "usage: test1 \n" ); + goto FAIL; + } + + host = argv[1]; + service = argv[2]; + +#ifdef _WIN32 + WSADATA wsa_data; + WSAStartup( MAKEWORD( 2, 2 ), &wsa_data ); +#endif + + /* tell getaddrinfo what we want */ + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + /* resolve the domain name into a list of addresses */ + error = getaddrinfo( host, service, &hints, &result ); + if( error != 0 ) { + fprintf( stderr, "getaddrinfo failed: %s (%d)\n", + gai_strerror( error ), error ); + goto FAIL; + } + + /* open the client endpoint */ + fd = socket( result->ai_family, result->ai_socktype, result->ai_protocol ); + if( fd < 0 ) { + fprintf( stderr, "socket failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + /* set socket non-blocking for asynchronous connect */ + if( !wolf_network_sock_nonblocking( fd ) ) { + (void)close( fd ); + fprintf( stderr, "set nonblocking failed for socket: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + /* connect asynchronously, from now on no matter what happens (for instance an + * interrupt by a signal) the kernel tries to establish the connection! So we + * don't have to go back into a connect.. + */ + res = connect( fd, result->ai_addr, result->ai_addrlen ); + if( res < 0 ) { + if( errno == EINPROGRESS ) { + /* this is ok, being connected, we loop later in a select */ + fprintf( stderr, "connecting in progress..\n" ); + } else if( errno == EINTR ) { + /* interrupted, no problem, the connection goes on anyway */ + } else { + (void)close( fd ); + fprintf( stderr, "connect failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + } else { + /* a fast connect */ + goto CONNECTED; + } + + /* wait till we are connected */ +CONNECT_SELECT_AGAIN: + FD_ZERO( &read_set ); + FD_ZERO( &write_set ); + FD_SET( fd, &read_set ); + FD_SET( fd, &write_set ); + timeout.tv_sec = CONNECT_TIMEOUT; + timeout.tv_usec = 0; + res = select( fd + 1, &read_set, &write_set, NULL, &timeout ); + if( res < 0 ) { + if( errno == EINTR || errno == EAGAIN ) { + goto CONNECT_SELECT_AGAIN; + } else { + /* fatal errors */ + fprintf( stderr, "select failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else if( res == 0 ) { + /* connect timeout */ + fprintf( stderr, "Connect timeout..terminating\n" ); + goto FAIL; + } else { + if( FD_ISSET( fd, &read_set ) || FD_ISSET( fd, &write_set ) ) { +/* strategy second connect */ +SECOND_CONNECT_AGAIN: + res = connect( fd, result->ai_addr, result->ai_addrlen ); + if( res < 0 ) { + if( errno == EINTR ) { + /* interrupted, no problem, the connection goes on anyway */ + goto SECOND_CONNECT_AGAIN; + } else if( errno == EISCONN ) { + /* already connected, first connect succeeded */ + goto CONNECTED; + } else { + (void)close( fd ); + fprintf( stderr, "second connect failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + } + +/* strategy setsockopt and SO_ERROR: + +Not really realiable! Can result 0 though the server is not there! + socklen_t sock_error_len; + int sock_error; + + sock_error_len = sizeof( int ); + res = getsockopt( fd, SOL_SOCKET, SO_ERROR, &sock_error, &sock_error_len ); + if( res < 0 ) { + fprintf( stderr, "getsockopt for connection check failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } else { + if( error == 0 ) { + goto CONNECTED; + } else { + fprintf( stderr, "SO_ERROR is not ok: %s (%d)\n", + strerror( error ), error ); + (void)close( fd ); + goto FAIL; + } + } +*/ + } else { + fprintf( stderr, "Socket not ready after select\n" ); + (void)close( fd ); + goto FAIL; + } + } + +CONNECTED: + /* determine the address of the other end of the connection */ + peer_addr_len = sizeof( peer_addr ); + res = getsockname( fd, &peer_addr.addr, &peer_addr_len ); + if( res < 0 ) { + (void)close( fd ); + fprintf( stderr, "getsockname failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + res = getnameinfo( &peer_addr.addr, peer_addr_len, + peer_hostname, NI_MAXHOST, + peer_service, NI_MAXSERV, + NI_NUMERICSERV | NI_NUMERICHOST ); + if( res < 0 ) { + (void)close( fd ); + fprintf( stderr, "getnameinfo failed: %s (%d)\n", + gai_strerror( error ), error ); + goto FAIL; + } + + fprintf( stderr, "Connected to %s, port %s (peer: %s, %s)\n", host, service, + peer_hostname, peer_service ); + + if( !wolf_network_sock_nonblocking( STDIN_FILENO ) ) { + (void)close( fd ); + fprintf( stderr, "set nonblocking failed for stdin: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + if( !wolf_network_sock_nonblocking( STDOUT_FILENO ) ) { + (void)close( fd ); + fprintf( stderr, "set nonblocking failed for stdout: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + idle_secs = 0; + count = 0; + stdout_to_write = 0; + stdout_written = 0; + fd_to_write = 0; + fd_written = 0; + total_stdin_read = 0; + total_fd_read = 0; + total_fd_written = 0; + total_stdout_written = 0; + stdin_eof = false; + fd_write_closed = false; + fd_eof = false; + + signal( SIGTERM, term_handler ); + signal( SIGINT, term_handler ); + signal( SIGPIPE, SIG_IGN ); + + do { + ssize_t rres; +#if DEBUG + char buf[10]; + char buf2[10]; +#endif + + FD_ZERO( &read_set ); + FD_ZERO( &write_set ); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + /* set up select mask and compute highest file descriptor number */ + max_fd = 0; + max_fd = max( max_fd, STDIN_FILENO ); + max_fd = max( max_fd, STDOUT_FILENO ); + max_fd = max( max_fd, fd ); + + if( !stdin_eof && fd_to_write == 0 ) { + FD_SET( STDIN_FILENO, &read_set ); + } + if( stdout_to_write > 0 ) { + FD_SET( STDOUT_FILENO, &write_set ); + } + if( !fd_eof && stdout_to_write == 0 ) { + FD_SET( fd, &read_set ); + } + if( fd_to_write > 0 ) { + FD_SET( fd, &write_set ); + } + + /* wait for events or timeout */ +#if DEBUG +fprintf( stderr, "select call read_fd: %s write_fd: %s wbuf: %d rbuf: %d stdin_eof: %d fdwcl: %d\n", + fd_set_to_string( &read_set, 0, max_fd, buf, 10 ), + fd_set_to_string( &write_set, 0, max_fd, buf2, 10 ), + fd_to_write, stdout_to_write, stdin_eof, fd_write_closed ); +#endif + + res = select( max_fd + 1, &read_set, &write_set, NULL, &timeout ); + +#if DEBUG +fprintf( stderr, "select %04d read_fd: %s write_fd: %s wbuf: %d rbuf: %d stdin_eof: %d fdwcl: %d\n", + res, fd_set_to_string( &read_set, 0, max_fd, buf, 10 ), + fd_set_to_string( &write_set, 0, max_fd, buf2, 10 ), + fd_to_write, stdout_to_write, stdin_eof, fd_write_closed ); +#endif + + if( res < 0 ) { + if( errno == EINTR || errno == EAGAIN ) { + /* skip */ + } else { + /* fatal errors */ + fprintf( stderr, "select failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else if( res == 0 ) { + /* timeout */ + idle_secs++; + if( idle_secs > MAX_IDLE_TIMEOUT ) { + fprintf( stderr, "Idle connection after %d seconds..terminating\n", + idle_secs ); + goto END; + } + } else { + /* something happened */ + idle_secs = 0; + + /* empty the socket read buffer to stdout */ + if( FD_ISSET( STDOUT_FILENO, &write_set ) ) { + assert( stdout_to_write > 0 ); + rres = write( STDOUT_FILENO, read_buffer + stdout_written, stdout_to_write ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "write to stdout failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else { + assert( rres >= 0 ); + stdout_written += (size_t)rres; + assert( stdout_to_write >= (size_t)rres ); + stdout_to_write -= (size_t)rres; + total_stdout_written += (size_t)rres; + } + } + + /* empty the socket write buffer writing to fd */ + if( FD_ISSET( fd, &write_set ) ) { + assert( fd_to_write > 0 ); + rres = write( fd, write_buffer + fd_written, fd_to_write ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "write to socket failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else { + assert( rres >= 0 ); + fd_written += (size_t)rres; + assert( fd_to_write >= (size_t)rres ); + fd_to_write -= (size_t)rres; + total_fd_written += (size_t)rres; + } + } + + /* read from socket, fill socket read buffer */ + if( FD_ISSET( fd, &read_set ) ) { + assert( stdout_to_write == 0 ); + rres = read( fd, read_buffer, READ_BUFFER_SIZE ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "read from socket failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else if( rres == 0 ) { + /* EOF on socket */ + shutdown( fd, SHUT_RD ); + fd_eof = true; +#if DEBUG + fprintf( stderr, "EOF on socket\n" ); +#endif + } else { + assert( rres > 0 ); + stdout_written = 0; + stdout_to_write = (size_t)rres; + total_fd_read += (size_t)rres; + } + } + + /* read from stdin, fill socket write buffer */ + if( FD_ISSET( STDIN_FILENO, &read_set ) ) { + assert( fd_to_write == 0 ); + rres = read( STDIN_FILENO, write_buffer, WRITE_BUFFER_SIZE ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "read from stdin failed: %s (%d)\n", + strerror( errno ), errno ); + (void)close( fd ); + goto FAIL; + } + } else if( rres == 0 ) { + /* EOF on STDIN */ + stdin_eof = true; + fclose( stdin ); + +#if DEBUG + fprintf( stderr, "EOF on stdin\n" ); +#endif + } else { + assert( rres > 0 ); + fd_written = 0; + fd_to_write = (size_t)rres; + total_stdin_read += (size_t)rres; + } + } + + /* close writing side of the socket if we have emptied + * the socket write buffer and there is nothing more on + * the input (stdin) + */ + if( fd_to_write == 0 && stdin_eof && !fd_write_closed ) { +#if DEBUG + fprintf( stderr, "socket SHUT_WR\n" ); +#endif + shutdown( fd, SHUT_WR ); + fd_write_closed = true; + } + } + + count++; + if( count % 10000 == 0 ) { + fprintf( stderr, "Transfered nof_selects: %d, stdin: %zd, stdout: %zd, fd-in: %zd, fd-out: %zd\n", + count, total_stdin_read, total_stdout_written, total_fd_read, total_fd_written ); + } + } while( !terminate && !( stdin_eof && fd_eof ) ); + +END: + fprintf( stderr, "Terminated stdin: %zd, stdout: %zd, fd-in: %zd, fd-out: %zd\n", + total_stdin_read, total_stdout_written, total_fd_read, total_fd_written ); + + res = close( fd ); + if( res < 0 ) { + fprintf( stderr, "close failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + goto OK; + +FAIL: + +#ifdef _WIN32 + WSACleanup( ); +#endif + + return EXIT_FAILURE; + +OK: + + freeaddrinfo( result ); + + return EXIT_SUCCESS; +} diff --git a/tests/network/test2.c b/tests/network/test2.c deleted file mode 100644 index 3964c93..0000000 --- a/tests/network/test2.c +++ /dev/null @@ -1,404 +0,0 @@ -/* Unix traditional and most portable way of a TCP/IP server using select(). - * - * Also tests asynchrononous accept (to avoid DoSAs on blocking accepts). - */ - -#include "port/netdb.h" /* for getaddrinfo, getnameinfo */ -#include "network/network.h" /* for networking stuff */ -#include "port/stdlib.h" /* for EXIT_XXX */ -#include "port/stdio.h" /* for fprintf */ -#include "port/string.h" /* for memset */ -#include "port/stdbool.h" /* for bool, true, false */ -#include "port/unused.h" /* for WOLF_UNUSED */ - -#include /* for close */ -#include /* for errno */ -#include /* for fcntl */ -#include /* for select */ -#include /* for assertions */ -#include /* for signal */ - -#define DEBUG 0 - -#define MAX_ACCEPT_IDLE_TIMEOUT 4 -#define MAX_IDLE_TIMEOUT 10 - -static bool wolf_network_sock_nonblocking( int fd ) { - int flags; - - flags = fcntl( fd, F_GETFL, 0 /* ignored */ ); - if( flags < 0 ) return false; - flags |= O_NONBLOCK; - flags = fcntl( fd, F_SETFL, flags ); - if( flags < 0 ) return false; - return true; -} - -#if DEBUG -static char *fd_set_to_string( fd_set *fd, int min_fd, int max_fd, char *buf, size_t buflen ) { - int i; - size_t j; - - assert( min_fd <= max_fd ); - - for( i = min_fd, j = 0; i <= max_fd && j < buflen; i++, j++ ) { - if( FD_ISSET( i, fd ) ) { - buf[j] = '1'; - } else { - buf[j] = '0'; - } - } - - buf[j] = '\0'; - - return buf; -} -#endif - -#define BUFFER_SIZE 4096 - -static volatile bool terminate = false; - -static void term_handler( int sig ) { - WOLF_UNUSED( sig ); - terminate = true; -} - -int main( int argc, char* argv[] ) { - int error; - int serv_fd = -1; - int res; - char *host; - char *service; - struct addrinfo hints; - struct addrinfo *result = NULL; - wolf_network_sockaddr_union_t client_addr; - socklen_t client_addr_len; - fd_set read_set; - fd_set write_set; - struct timeval timeout; - char client_hostname[NI_MAXHOST] = ""; - char client_service[NI_MAXSERV] = ""; - int on; - int client_fd = -1; - int idle_secs; - char buffer[BUFFER_SIZE]; - size_t fd_to_write; - size_t fd_written; - size_t total_fd_read; - size_t total_fd_written; - int count; - bool fd_eof; - - if( argc != 3 ) { - fprintf( stderr, "usage: test2 \n" ); - goto FAIL; - } - - host = argv[1]; - service = argv[2]; - -#ifdef _WIN32 - WSADATA wsa_data; - WSAStartup( MAKEWORD( 2, 2 ), &wsa_data ); -#endif - - signal( SIGTERM, term_handler ); - signal( SIGINT, term_handler ); - signal( SIGPIPE, SIG_IGN ); - - /* tell getaddrinfo what we want */ - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - /* resolve the domain name into a list of addresses */ - error = getaddrinfo( host, service, &hints, &result ); - if( error != 0 ) { - fprintf( stderr, "getaddrinfo failed: %s (%d)\n", - gai_strerror( error ), error ); - goto FAIL; - } - - /* open the server endpoint */ - serv_fd = socket( result->ai_family, result->ai_socktype, result->ai_protocol ); - if( serv_fd < 0 ) { - fprintf( stderr, "socket failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - /* setsockopt SO_REUSEADDR, this is handy while testing or deployment, in - * production in can lead to a certain type of rerouting packets to wrong - * servers - */ - on = 1; - res = setsockopt( serv_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof( on ) ); - if( res < 0 ) { - (void)close( serv_fd ); - fprintf( stderr, "setsockopt(SO_REUSEADDR) failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - /* bind server socket */ - res = bind( serv_fd, result->ai_addr, result->ai_addrlen ); - if( res < 0 ) { - fprintf( stderr, "bind failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - /* listen to the socket */ - res = listen( serv_fd, 10 ); - if( res < 0 ) { - fprintf( stderr, "listen failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - /* set socket non-blocking for accepts (Stevens 15.6) */ - if( !wolf_network_sock_nonblocking( serv_fd ) ) { - fprintf( stderr, "set nonblocking failed for server socket: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - - /* accept the connection in the kernel, select ready for ready tells us there - * is a connection ready. Then we have to call accept again and check the result - */ -FIRST_ACCEPT_AGAIN: - client_addr_len = sizeof( client_addr ); - client_fd = accept( serv_fd, &client_addr.addr, &client_addr_len ); - if( client_fd < 0 ) { - if( errno == EINTR ) { - if( terminate ) goto OK; - goto FIRST_ACCEPT_AGAIN; - } else if( errno == EAGAIN || errno == EWOULDBLOCK ) { - /* as expected, skip */ - } else { - fprintf( stderr, "first non-blocking accept failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - } - - /* go into a select and wait till we are ready for read (this indicates a - * possibly new connection) - */ -ACCEPT_SELECT_AGAIN: - FD_ZERO( &read_set ); - FD_SET( serv_fd, &read_set ); - timeout.tv_sec = MAX_ACCEPT_IDLE_TIMEOUT; - timeout.tv_usec = 0; - - res = select( serv_fd + 1, &read_set, NULL, NULL, &timeout ); - if( res < 0 ) { - if( errno == EINTR ) { - if( terminate ) goto OK; - goto ACCEPT_SELECT_AGAIN; - } else if( errno == EAGAIN ) { - goto ACCEPT_SELECT_AGAIN; - } else { - /* fatal errors */ - fprintf( stderr, "accept/select failed: %s (%d)\n", - strerror( errno ), errno ); - goto FAIL; - } - } else if( res == 0 ) { - /* accept timeout, here we could to periodic stuff, also if we - * get blocked somewhere above we could recover.. - */ - fprintf( stderr, "Idle. No new connections..\n" ); - goto ACCEPT_SELECT_AGAIN; - } else { - if( FD_ISSET( serv_fd, &read_set ) ) { - /* this is a new connection */ - } else { - /* can't happen! */ - assert( false ); - } - } - - /* second accept, now we have to check for possible "short-and-run-away" - * connects. - */ -ACCEPT_AGAIN: - client_addr_len = sizeof( client_addr ); - client_fd = accept( serv_fd, &client_addr.addr, &client_addr_len ); - if( client_fd < 0 ) { - if( errno == EINTR ) { - if( terminate ) goto OK; - /* interrupted, again */ - goto ACCEPT_AGAIN; - } else if( errno == ECONNABORTED ) { - /* connection run away */ - goto FIRST_ACCEPT_AGAIN; - } else { - fprintf( stderr, "second non-blocking accept failed: %s (%d)\n", strerror( errno ), errno ); - goto FAIL; - } - } - - /* determine where the request came from */ - res = getnameinfo( &client_addr.addr, client_addr_len, - client_hostname, NI_MAXHOST, - client_service, NI_MAXSERV, - NI_NUMERICSERV | NI_NUMERICHOST ); - if( res < 0 ) { - fprintf( stderr, "getnameinfo failed: %s (%d)\n", - gai_strerror( error ), error ); - goto FAIL; - } - - fprintf( stderr, "New connection from %s, port %s\n", client_hostname, client_service ); - - /* handle connection input/output as echo server */ - - idle_secs = 0; - count = 0; - fd_to_write = 0; - fd_written = 0; - total_fd_read = 0; - total_fd_written = 0; - fd_eof = false; - - do { - ssize_t rres; -#if DEBUG - char buf[10]; - char buf2[10]; -#endif - FD_ZERO( &read_set ); - FD_ZERO( &write_set ); - timeout.tv_sec = 1; - timeout.tv_usec = 0; - if( fd_to_write == 0 ) { - FD_SET( client_fd, &read_set ); - } - if( fd_to_write > 0 ) { - FD_SET( client_fd, &write_set ); - } - - /* wait for events or timeout */ -#if DEBUG -fprintf( stderr, "select call read_fd: %s write_fd: %s buf: %d eof: %d\n", - fd_set_to_string( &read_set, client_fd, client_fd, buf, 10 ), - fd_set_to_string( &write_set, client_fd, client_fd, buf2, 10 ), - fd_to_write, fd_eof ); -#endif - - res = select( client_fd + 1, &read_set, &write_set, NULL, &timeout ); - -#if DEBUG -fprintf( stderr, "select %04d read_fd: %s write_fd: %s buf: %d eof: %d\n", - res, fd_set_to_string( &read_set, client_fd, client_fd, buf, 10 ), - fd_set_to_string( &write_set, client_fd, client_fd, buf2, 10 ), - fd_to_write, fd_eof ); -#endif - - if( res < 0 ) { - if( errno == EINTR || errno == EAGAIN ) { - /* skip */ - } else { - /* fatal errors */ - fprintf( stderr, "select failed: %s (%d)\n", - strerror( errno ), errno ); - goto FAIL; - } - } else if( res == 0 ) { - /* timeout */ - idle_secs++; - if( idle_secs > MAX_IDLE_TIMEOUT ) { - fprintf( stderr, "Idle connection after %d seconds..terminating\n", - idle_secs ); - goto END; - } - } else { - /* something happened */ - idle_secs = 0; - - /* if we have something to write back to the client, do that */ - if( FD_ISSET( client_fd, &write_set ) ) { - assert( fd_to_write > 0 ); - rres = write( client_fd, buffer + fd_written, fd_to_write ); - if( rres < 0 ) { - if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { - /* skip */ - } else { - fprintf( stderr, "write to stdout failed: %s (%d)\n", - strerror( errno ), errno ); - goto FAIL; - } - } else { - assert( rres >= 0 ); - fd_written += (size_t)rres; - assert( fd_to_write >= (size_t)rres ); - fd_to_write -= (size_t)rres; - fd_written += (size_t)rres; - total_fd_written += (size_t)rres; - } - } - - /* if there is space in the read buffer and something to be read, do so */ - if( FD_ISSET( client_fd, &read_set ) ) { - assert( fd_to_write == 0 ); - rres = read( client_fd, buffer, BUFFER_SIZE ); - if( rres < 0 ) { - if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { - /* skip */ - } else { - fprintf( stderr, "read from socket failed: %s (%d)\n", - strerror( errno ), errno ); - goto FAIL; - } - } else if( rres == 0 ) { - /* EOF on socket */ - shutdown( client_fd, SHUT_RD ); - fd_eof = true; -#if DEBUG - fprintf( stderr, "EOF on socket\n" ); -#endif - } else { - assert( rres > 0 ); - fd_written = 0; - fd_to_write = (size_t)rres; - total_fd_read += (size_t)rres; - } - } - } - - count++; - if( count % 10000 == 0 ) { - fprintf( stderr, "Transfered nof_selects: %d, fd-in: %zd, fd-out: %zd\n", - count, total_fd_read, total_fd_written ); - } - } while( !terminate && !( fd_eof && fd_to_write == 0 ) ); - -END: - - fprintf( stderr, "Terminated nof_selects: %d, fd-in: %zd, fd-out: %zd\n", - count, total_fd_read, total_fd_written ); - - (void)close( client_fd ); - (void)close( serv_fd ); - freeaddrinfo( result ); - - goto OK; - -FAIL: - - if( client_fd >= 0 ) (void)close( client_fd ); - if( serv_fd >= 0 ) (void)close( serv_fd ); - if( result != NULL ) freeaddrinfo( result ); - -#ifdef _WIN32 - WSACleanup( ); -#endif - - return EXIT_FAILURE; - -OK: - -#ifdef _WIN32 - WSACleanup( ); -#endif - - return EXIT_SUCCESS; -} diff --git a/tests/network/test2_unix.c b/tests/network/test2_unix.c new file mode 100644 index 0000000..3964c93 --- /dev/null +++ b/tests/network/test2_unix.c @@ -0,0 +1,404 @@ +/* Unix traditional and most portable way of a TCP/IP server using select(). + * + * Also tests asynchrononous accept (to avoid DoSAs on blocking accepts). + */ + +#include "port/netdb.h" /* for getaddrinfo, getnameinfo */ +#include "network/network.h" /* for networking stuff */ +#include "port/stdlib.h" /* for EXIT_XXX */ +#include "port/stdio.h" /* for fprintf */ +#include "port/string.h" /* for memset */ +#include "port/stdbool.h" /* for bool, true, false */ +#include "port/unused.h" /* for WOLF_UNUSED */ + +#include /* for close */ +#include /* for errno */ +#include /* for fcntl */ +#include /* for select */ +#include /* for assertions */ +#include /* for signal */ + +#define DEBUG 0 + +#define MAX_ACCEPT_IDLE_TIMEOUT 4 +#define MAX_IDLE_TIMEOUT 10 + +static bool wolf_network_sock_nonblocking( int fd ) { + int flags; + + flags = fcntl( fd, F_GETFL, 0 /* ignored */ ); + if( flags < 0 ) return false; + flags |= O_NONBLOCK; + flags = fcntl( fd, F_SETFL, flags ); + if( flags < 0 ) return false; + return true; +} + +#if DEBUG +static char *fd_set_to_string( fd_set *fd, int min_fd, int max_fd, char *buf, size_t buflen ) { + int i; + size_t j; + + assert( min_fd <= max_fd ); + + for( i = min_fd, j = 0; i <= max_fd && j < buflen; i++, j++ ) { + if( FD_ISSET( i, fd ) ) { + buf[j] = '1'; + } else { + buf[j] = '0'; + } + } + + buf[j] = '\0'; + + return buf; +} +#endif + +#define BUFFER_SIZE 4096 + +static volatile bool terminate = false; + +static void term_handler( int sig ) { + WOLF_UNUSED( sig ); + terminate = true; +} + +int main( int argc, char* argv[] ) { + int error; + int serv_fd = -1; + int res; + char *host; + char *service; + struct addrinfo hints; + struct addrinfo *result = NULL; + wolf_network_sockaddr_union_t client_addr; + socklen_t client_addr_len; + fd_set read_set; + fd_set write_set; + struct timeval timeout; + char client_hostname[NI_MAXHOST] = ""; + char client_service[NI_MAXSERV] = ""; + int on; + int client_fd = -1; + int idle_secs; + char buffer[BUFFER_SIZE]; + size_t fd_to_write; + size_t fd_written; + size_t total_fd_read; + size_t total_fd_written; + int count; + bool fd_eof; + + if( argc != 3 ) { + fprintf( stderr, "usage: test2 \n" ); + goto FAIL; + } + + host = argv[1]; + service = argv[2]; + +#ifdef _WIN32 + WSADATA wsa_data; + WSAStartup( MAKEWORD( 2, 2 ), &wsa_data ); +#endif + + signal( SIGTERM, term_handler ); + signal( SIGINT, term_handler ); + signal( SIGPIPE, SIG_IGN ); + + /* tell getaddrinfo what we want */ + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + /* resolve the domain name into a list of addresses */ + error = getaddrinfo( host, service, &hints, &result ); + if( error != 0 ) { + fprintf( stderr, "getaddrinfo failed: %s (%d)\n", + gai_strerror( error ), error ); + goto FAIL; + } + + /* open the server endpoint */ + serv_fd = socket( result->ai_family, result->ai_socktype, result->ai_protocol ); + if( serv_fd < 0 ) { + fprintf( stderr, "socket failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + /* setsockopt SO_REUSEADDR, this is handy while testing or deployment, in + * production in can lead to a certain type of rerouting packets to wrong + * servers + */ + on = 1; + res = setsockopt( serv_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof( on ) ); + if( res < 0 ) { + (void)close( serv_fd ); + fprintf( stderr, "setsockopt(SO_REUSEADDR) failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + /* bind server socket */ + res = bind( serv_fd, result->ai_addr, result->ai_addrlen ); + if( res < 0 ) { + fprintf( stderr, "bind failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + /* listen to the socket */ + res = listen( serv_fd, 10 ); + if( res < 0 ) { + fprintf( stderr, "listen failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + /* set socket non-blocking for accepts (Stevens 15.6) */ + if( !wolf_network_sock_nonblocking( serv_fd ) ) { + fprintf( stderr, "set nonblocking failed for server socket: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + + /* accept the connection in the kernel, select ready for ready tells us there + * is a connection ready. Then we have to call accept again and check the result + */ +FIRST_ACCEPT_AGAIN: + client_addr_len = sizeof( client_addr ); + client_fd = accept( serv_fd, &client_addr.addr, &client_addr_len ); + if( client_fd < 0 ) { + if( errno == EINTR ) { + if( terminate ) goto OK; + goto FIRST_ACCEPT_AGAIN; + } else if( errno == EAGAIN || errno == EWOULDBLOCK ) { + /* as expected, skip */ + } else { + fprintf( stderr, "first non-blocking accept failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + } + + /* go into a select and wait till we are ready for read (this indicates a + * possibly new connection) + */ +ACCEPT_SELECT_AGAIN: + FD_ZERO( &read_set ); + FD_SET( serv_fd, &read_set ); + timeout.tv_sec = MAX_ACCEPT_IDLE_TIMEOUT; + timeout.tv_usec = 0; + + res = select( serv_fd + 1, &read_set, NULL, NULL, &timeout ); + if( res < 0 ) { + if( errno == EINTR ) { + if( terminate ) goto OK; + goto ACCEPT_SELECT_AGAIN; + } else if( errno == EAGAIN ) { + goto ACCEPT_SELECT_AGAIN; + } else { + /* fatal errors */ + fprintf( stderr, "accept/select failed: %s (%d)\n", + strerror( errno ), errno ); + goto FAIL; + } + } else if( res == 0 ) { + /* accept timeout, here we could to periodic stuff, also if we + * get blocked somewhere above we could recover.. + */ + fprintf( stderr, "Idle. No new connections..\n" ); + goto ACCEPT_SELECT_AGAIN; + } else { + if( FD_ISSET( serv_fd, &read_set ) ) { + /* this is a new connection */ + } else { + /* can't happen! */ + assert( false ); + } + } + + /* second accept, now we have to check for possible "short-and-run-away" + * connects. + */ +ACCEPT_AGAIN: + client_addr_len = sizeof( client_addr ); + client_fd = accept( serv_fd, &client_addr.addr, &client_addr_len ); + if( client_fd < 0 ) { + if( errno == EINTR ) { + if( terminate ) goto OK; + /* interrupted, again */ + goto ACCEPT_AGAIN; + } else if( errno == ECONNABORTED ) { + /* connection run away */ + goto FIRST_ACCEPT_AGAIN; + } else { + fprintf( stderr, "second non-blocking accept failed: %s (%d)\n", strerror( errno ), errno ); + goto FAIL; + } + } + + /* determine where the request came from */ + res = getnameinfo( &client_addr.addr, client_addr_len, + client_hostname, NI_MAXHOST, + client_service, NI_MAXSERV, + NI_NUMERICSERV | NI_NUMERICHOST ); + if( res < 0 ) { + fprintf( stderr, "getnameinfo failed: %s (%d)\n", + gai_strerror( error ), error ); + goto FAIL; + } + + fprintf( stderr, "New connection from %s, port %s\n", client_hostname, client_service ); + + /* handle connection input/output as echo server */ + + idle_secs = 0; + count = 0; + fd_to_write = 0; + fd_written = 0; + total_fd_read = 0; + total_fd_written = 0; + fd_eof = false; + + do { + ssize_t rres; +#if DEBUG + char buf[10]; + char buf2[10]; +#endif + FD_ZERO( &read_set ); + FD_ZERO( &write_set ); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + if( fd_to_write == 0 ) { + FD_SET( client_fd, &read_set ); + } + if( fd_to_write > 0 ) { + FD_SET( client_fd, &write_set ); + } + + /* wait for events or timeout */ +#if DEBUG +fprintf( stderr, "select call read_fd: %s write_fd: %s buf: %d eof: %d\n", + fd_set_to_string( &read_set, client_fd, client_fd, buf, 10 ), + fd_set_to_string( &write_set, client_fd, client_fd, buf2, 10 ), + fd_to_write, fd_eof ); +#endif + + res = select( client_fd + 1, &read_set, &write_set, NULL, &timeout ); + +#if DEBUG +fprintf( stderr, "select %04d read_fd: %s write_fd: %s buf: %d eof: %d\n", + res, fd_set_to_string( &read_set, client_fd, client_fd, buf, 10 ), + fd_set_to_string( &write_set, client_fd, client_fd, buf2, 10 ), + fd_to_write, fd_eof ); +#endif + + if( res < 0 ) { + if( errno == EINTR || errno == EAGAIN ) { + /* skip */ + } else { + /* fatal errors */ + fprintf( stderr, "select failed: %s (%d)\n", + strerror( errno ), errno ); + goto FAIL; + } + } else if( res == 0 ) { + /* timeout */ + idle_secs++; + if( idle_secs > MAX_IDLE_TIMEOUT ) { + fprintf( stderr, "Idle connection after %d seconds..terminating\n", + idle_secs ); + goto END; + } + } else { + /* something happened */ + idle_secs = 0; + + /* if we have something to write back to the client, do that */ + if( FD_ISSET( client_fd, &write_set ) ) { + assert( fd_to_write > 0 ); + rres = write( client_fd, buffer + fd_written, fd_to_write ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "write to stdout failed: %s (%d)\n", + strerror( errno ), errno ); + goto FAIL; + } + } else { + assert( rres >= 0 ); + fd_written += (size_t)rres; + assert( fd_to_write >= (size_t)rres ); + fd_to_write -= (size_t)rres; + fd_written += (size_t)rres; + total_fd_written += (size_t)rres; + } + } + + /* if there is space in the read buffer and something to be read, do so */ + if( FD_ISSET( client_fd, &read_set ) ) { + assert( fd_to_write == 0 ); + rres = read( client_fd, buffer, BUFFER_SIZE ); + if( rres < 0 ) { + if( errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR ) { + /* skip */ + } else { + fprintf( stderr, "read from socket failed: %s (%d)\n", + strerror( errno ), errno ); + goto FAIL; + } + } else if( rres == 0 ) { + /* EOF on socket */ + shutdown( client_fd, SHUT_RD ); + fd_eof = true; +#if DEBUG + fprintf( stderr, "EOF on socket\n" ); +#endif + } else { + assert( rres > 0 ); + fd_written = 0; + fd_to_write = (size_t)rres; + total_fd_read += (size_t)rres; + } + } + } + + count++; + if( count % 10000 == 0 ) { + fprintf( stderr, "Transfered nof_selects: %d, fd-in: %zd, fd-out: %zd\n", + count, total_fd_read, total_fd_written ); + } + } while( !terminate && !( fd_eof && fd_to_write == 0 ) ); + +END: + + fprintf( stderr, "Terminated nof_selects: %d, fd-in: %zd, fd-out: %zd\n", + count, total_fd_read, total_fd_written ); + + (void)close( client_fd ); + (void)close( serv_fd ); + freeaddrinfo( result ); + + goto OK; + +FAIL: + + if( client_fd >= 0 ) (void)close( client_fd ); + if( serv_fd >= 0 ) (void)close( serv_fd ); + if( result != NULL ) freeaddrinfo( result ); + +#ifdef _WIN32 + WSACleanup( ); +#endif + + return EXIT_FAILURE; + +OK: + +#ifdef _WIN32 + WSACleanup( ); +#endif + + return EXIT_SUCCESS; +} -- cgit v1.2.3-54-g00ecf