diff --git a/ChangeLog b/ChangeLog index 2ff369404..3eadbccd6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ Features (POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE) * Support for Curve25519 * Support for ECDH-RSA and ECDH-ECDSA key exchanges and ciphersuites + * Support for IPv6 in the NET module Changes * gen_prime() speedup diff --git a/include/polarssl/config.h b/include/polarssl/config.h index 970212ffd..7bbef5522 100644 --- a/include/polarssl/config.h +++ b/include/polarssl/config.h @@ -101,6 +101,16 @@ * Comment if your system does not support time functions */ #define POLARSSL_HAVE_TIME + +/** + * \def POLARSSL_HAVE_IPV6 + * + * System supports the basic socket interface for IPv6 (RFC 3493), + * specifically getaddrinfo(), freeaddrinfo() and struct sockaddr_storage. + * + * Comment if your system does not support the IPv6 socket interface + */ +#define POLARSSL_HAVE_IPV6 /* \} name SECTION: System support */ /** diff --git a/include/polarssl/net.h b/include/polarssl/net.h index 88302ac0a..22698b4ce 100644 --- a/include/polarssl/net.h +++ b/include/polarssl/net.h @@ -82,9 +82,10 @@ int net_bind( int *fd, const char *bind_ip, int port ); * \param bind_fd Relevant socket * \param client_fd Will contain the connected client socket * \param client_ip Will contain the client IP address + * Must be at least 4 bytes, or 16 if IPv6 is supported * * \return 0 if successful, POLARSSL_ERR_NET_ACCEPT_FAILED, or - * POLARSSL_ERR_NET_WOULD_BLOCK is bind_fd was set to + * POLARSSL_ERR_NET_WANT_READ is bind_fd was set to * non-blocking and accept() is blocking. */ int net_accept( int bind_fd, int *client_fd, void *client_ip ); diff --git a/library/net.c b/library/net.c index be2785d98..19f699d50 100644 --- a/library/net.c +++ b/library/net.c @@ -81,6 +81,11 @@ static int wsa_init_done = 0; #include #include +#if defined(_MSC_VER) && !defined snprintf && !defined(EFIX64) && \ + !defined(EFI32) +#define snprintf _snprintf +#endif + #if defined(POLARSSL_HAVE_TIME) #include #endif @@ -115,16 +120,12 @@ unsigned long net_htonl(unsigned long n); #define net_htonl(n) POLARSSL_HTONL(n) /* - * Initiate a TCP connection with host:port + * Prepare for using the sockets interface */ -int net_connect( int *fd, const char *host, int port ) +static void net_prepare( void ) { - struct sockaddr_in server_addr; - struct hostent *server_host; - #if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ !defined(EFI32) - WSADATA wsaData; if( wsa_init_done == 0 ) @@ -139,6 +140,64 @@ int net_connect( int *fd, const char *host, int port ) signal( SIGPIPE, SIG_IGN ); #endif #endif +} + +/* + * Initiate a TCP connection with host:port + */ +int net_connect( int *fd, const char *host, int port ) +{ +#if defined(POLARSSL_HAVE_IPV6) + int ret = POLARSSL_ERR_NET_UNKNOWN_HOST; + struct addrinfo hints, *addr_list, *cur; + char port_str[6]; + + net_prepare(); + + /* getaddrinfo expects port as a string */ + memset( port_str, 0, sizeof( port_str ) ); + snprintf( port_str, sizeof( port_str ), "%d", port ); + + /* Do name resolution with both IPv6 and IPv4, but only TCP */ + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if( getaddrinfo( host, port_str, &hints, &addr_list ) != 0 ) + return( POLARSSL_ERR_NET_UNKNOWN_HOST ); + + /* Try the sockaddrs until a connection succeeds */ + for( cur = addr_list; cur != NULL; cur = cur->ai_next ) + { + *fd = socket( cur->ai_family, cur->ai_socktype, cur->ai_protocol ); + if( *fd < 0 ) + { + ret = POLARSSL_ERR_NET_SOCKET_FAILED; + continue; + } + + if( connect( *fd, cur->ai_addr, cur->ai_addrlen ) == 0 ) + { + ret = 0; + break; + } + + close( *fd ); + ret = POLARSSL_ERR_NET_CONNECT_FAILED; + } + + freeaddrinfo( addr_list ); + + return( ret ); + +#else + /* Legacy IPv4-only version */ + + struct sockaddr_in server_addr; + struct hostent *server_host; + + net_prepare(); if( ( server_host = gethostbyname( host ) ) == NULL ) return( POLARSSL_ERR_NET_UNKNOWN_HOST ); @@ -161,6 +220,7 @@ int net_connect( int *fd, const char *host, int port ) } return( 0 ); +#endif /* POLARSSL_HAVE_IPV6 */ } /* @@ -168,25 +228,72 @@ int net_connect( int *fd, const char *host, int port ) */ int net_bind( int *fd, const char *bind_ip, int port ) { +#if defined(POLARSSL_HAVE_IPV6) + int n, ret = POLARSSL_ERR_NET_UNKNOWN_HOST; + struct addrinfo hints, *addr_list, *cur; + char port_str[6]; + + net_prepare(); + + /* getaddrinfo expects port as a string */ + memset( port_str, 0, sizeof( port_str ) ); + snprintf( port_str, sizeof( port_str ), "%d", port ); + + /* Bind to IPv6 and/or IPv4, but only in TCP */ + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if( bind_ip == NULL ) + hints.ai_flags = AI_PASSIVE; + + if( getaddrinfo( bind_ip, port_str, &hints, &addr_list ) != 0 ) + return( POLARSSL_ERR_NET_UNKNOWN_HOST ); + + /* Try the sockaddrs until a binding succeeds */ + for( cur = addr_list; cur != NULL; cur = cur->ai_next ) + { + *fd = socket( cur->ai_family, cur->ai_socktype, cur->ai_protocol ); + if( *fd < 0 ) + { + ret = POLARSSL_ERR_NET_SOCKET_FAILED; + continue; + } + + n = 1; + setsockopt( *fd, SOL_SOCKET, SO_REUSEADDR, + (const char *) &n, sizeof( n ) ); + + if( bind( *fd, cur->ai_addr, cur->ai_addrlen ) != 0 ) + { + close( *fd ); + ret = POLARSSL_ERR_NET_BIND_FAILED; + continue; + } + + if( listen( *fd, POLARSSL_NET_LISTEN_BACKLOG ) != 0 ) + { + close( *fd ); + ret = POLARSSL_ERR_NET_LISTEN_FAILED; + continue; + } + + /* I we ever get there, it's a success */ + ret = 0; + break; + } + + freeaddrinfo( addr_list ); + + return( ret ); + +#else + /* Legacy IPv4-only version */ + int n, c[4]; struct sockaddr_in server_addr; -#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ - !defined(EFI32) - WSADATA wsaData; - - if( wsa_init_done == 0 ) - { - if( WSAStartup( MAKEWORD(2,0), &wsaData ) == SOCKET_ERROR ) - return( POLARSSL_ERR_NET_SOCKET_FAILED ); - - wsa_init_done = 1; - } -#else -#if !defined(EFIX64) && !defined(EFI32) - signal( SIGPIPE, SIG_IGN ); -#endif -#endif + net_prepare(); if( ( *fd = (int) socket( AF_INET, SOCK_STREAM, IPPROTO_IP ) ) < 0 ) return( POLARSSL_ERR_NET_SOCKET_FAILED ); @@ -230,6 +337,7 @@ int net_bind( int *fd, const char *bind_ip, int port ) } return( 0 ); +#endif /* POLARSSL_HAVE_IPV6 */ } /* @@ -260,7 +368,11 @@ static int net_is_blocking( void ) */ int net_accept( int bind_fd, int *client_fd, void *client_ip ) { +#if defined(POLARSSL_HAVE_IPV6) + struct sockaddr_storage client_addr; +#else struct sockaddr_in client_addr; +#endif #if defined(__socklen_t_defined) || defined(_SOCKLEN_T) || \ defined(_SOCKLEN_T_DECLARED) @@ -281,8 +393,25 @@ int net_accept( int bind_fd, int *client_fd, void *client_ip ) } if( client_ip != NULL ) + { +#if defined(POLARSSL_HAVE_IPV6) + if( client_addr.ss_family == AF_INET ) + { + struct sockaddr_in *addr4 = (struct sockaddr_in *) &client_addr; + memcpy( client_ip, &addr4->sin_addr.s_addr, + sizeof( addr4->sin_addr.s_addr ) ); + } + else + { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &client_addr; + memcpy( client_ip, &addr6->sin6_addr.s6_addr, + sizeof( addr6->sin6_addr.s6_addr ) ); + } +#else memcpy( client_ip, &client_addr.sin_addr.s_addr, sizeof( client_addr.sin_addr.s_addr ) ); +#endif /* POLARSSL_HAVE_IPV6 */ + } return( 0 ); } diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 96cbb4902..7233dff45 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -49,6 +49,7 @@ #include "polarssl/memory.h" #endif +#define DFL_SERVER_ADDR NULL #define DFL_SERVER_PORT 4433 #define DFL_DEBUG_LEVEL 0 #define DFL_CA_FILE "" @@ -91,6 +92,7 @@ */ struct options { + const char *server_addr; /* address on which the ssl service runs */ int server_port; /* port on which the ssl service runs */ int debug_level; /* level of debugging */ const char *ca_file; /* the file with the CA certificate(s) */ @@ -172,6 +174,7 @@ static void my_debug( void *ctx, int level, const char *str ) #define USAGE \ "\n usage: ssl_server2 param=<>...\n" \ "\n acceptable parameters:\n" \ + " server_addr=%%d default: (all interfaces)\n" \ " server_port=%%d default: 4433\n" \ " debug_level=%%d default: 0 (disabled)\n" \ USAGE_IO \ @@ -281,6 +284,7 @@ int main( int argc, char *argv[] ) goto exit; } + opt.server_addr = DFL_SERVER_ADDR; opt.server_port = DFL_SERVER_PORT; opt.debug_level = DFL_DEBUG_LEVEL; opt.ca_file = DFL_CA_FILE; @@ -313,6 +317,8 @@ int main( int argc, char *argv[] ) if( opt.server_port < 1 || opt.server_port > 65535 ) goto usage; } + else if( strcmp( p, "server_addr" ) == 0 ) + opt.server_addr = q; else if( strcmp( p, "debug_level" ) == 0 ) { opt.debug_level = atoi( q ); @@ -678,7 +684,8 @@ int main( int argc, char *argv[] ) printf( " . Bind on tcp://localhost:%-4d/ ...", opt.server_port ); fflush( stdout ); - if( ( ret = net_bind( &listen_fd, NULL, opt.server_port ) ) != 0 ) + if( ( ret = net_bind( &listen_fd, opt.server_addr, + opt.server_port ) ) != 0 ) { printf( " failed\n ! net_bind returned -0x%x\n\n", -ret ); goto exit; diff --git a/tests/compat.sh b/tests/compat.sh index 36f241b0e..ecc2e5804 100755 --- a/tests/compat.sh +++ b/tests/compat.sh @@ -354,8 +354,8 @@ done kill $PROCESS_ID wait $PROCESS_ID 2>/dev/null -log "../programs/ssl/ssl_server2 $P_SERVER_ARGS force_version=$MODE > /dev/null" -../programs/ssl/ssl_server2 $P_SERVER_ARGS force_version=$MODE > /dev/null & +log "../programs/ssl/ssl_server2 server_addr=0.0.0.0 $P_SERVER_ARGS force_version=$MODE > /dev/null" +../programs/ssl/ssl_server2 server_addr=0.0.0.0 $P_SERVER_ARGS force_version=$MODE > /dev/null & PROCESS_ID=$! sleep 1 @@ -390,8 +390,8 @@ done kill $PROCESS_ID wait $PROCESS_ID 2>/dev/null -log "../programs/ssl/ssl_server2 $P_SERVER_ARGS force_version=$MODE" -../programs/ssl/ssl_server2 $P_SERVER_ARGS force_version=$MODE > /dev/null & +log "../programs/ssl/ssl_server2 server_addr=0.0.0.0 $P_SERVER_ARGS force_version=$MODE" +../programs/ssl/ssl_server2 server_addr=0.0.0.0 $P_SERVER_ARGS force_version=$MODE > /dev/null & PROCESS_ID=$! sleep 1