diff options
Diffstat (limited to 'src/port/netdb.c')
-rw-r--r-- | src/port/netdb.c | 247 |
1 files changed, 246 insertions, 1 deletions
diff --git a/src/port/netdb.c b/src/port/netdb.c index 916b908..a61acd2 100644 --- a/src/port/netdb.c +++ b/src/port/netdb.c @@ -18,5 +18,250 @@ #include "port/netdb.h" #if !defined HAVE_GETADDRINFO || defined TEST_GETADDRINFO -#include "port/getaddrinfo.c" + +#include "port/string.h" /* for memset, memcpy */ +#include "port/stdlib.h" /* for atoi, itoa, malloc, free, NULL */ +#include "port/stdio.h" /* for snprintf */ +#include "port/unused.h" + +#include <netinet/in.h> /* for struct sockaddr_in */ + +int wolf_port_getaddrinfo( const char *host_name, + const char *service_name, + const struct wolf_port_addrinfo *hintp, + struct wolf_port_addrinfo **result ) { + struct wolf_port_addrinfo *ai; + struct sockaddr_in sin, + *psin; + struct wolf_port_addrinfo hints; + + if( hintp == NULL ) { + memset( &hints, 0, sizeof( struct wolf_port_addrinfo ) ); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + } else { + memcpy( &hints, hintp, sizeof( struct wolf_port_addrinfo ) ); + } + + if( hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC ) { + return EAI_FAMILY; + } + + if( hints.ai_socktype == 0 ) { + hints.ai_socktype = SOCK_STREAM; + } + + if( ( host_name == NULL ) && ( service_name == NULL ) ) { + return EAI_NONAME; + } + + memset( &sin, 0, sizeof( struct sockaddr_in) ); + + sin.sin_family = AF_INET; + + if( host_name != NULL ) { + if( host_name[0] == '\0' ) { + sin.sin_addr.s_addr = htonl( INADDR_ANY ); + } else if( hints.ai_flags & AI_NUMERICHOST ) { + if( !inet_aton( host_name, &sin.sin_addr ) ) { + return EAI_FAIL; + } + } else { + struct hostent *hp; + + hp = gethostbyname( host_name ); + + if( hp == NULL ) { + switch( h_errno ) { + case HOST_NOT_FOUND: + case NO_DATA: + return EAI_NONAME; + + case TRY_AGAIN: + return EAI_AGAIN; + + case NO_RECOVERY: + default: + return EAI_FAIL; + } + } + + if( hp->h_addrtype != AF_INET ) { + return EAI_FAIL; + } + + memcpy( &(sin.sin_addr), hp->h_addr, hp->h_length ); + } + } else { + if( hints.ai_flags & AI_PASSIVE ) { + sin.sin_addr.s_addr = htonl( INADDR_ANY ); + } else { + sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + } + } + + if( service_name != NULL ) { + sin.sin_port = htons( (unsigned short)atoi( service_name ) ); + } + + ai = (struct wolf_port_addrinfo *)malloc( sizeof( struct wolf_port_addrinfo ) ); + if( ai == NULL ) { + return EAI_MEMORY; + } + + psin = (struct sockaddr_in *)malloc( sizeof( struct sockaddr_in ) ); + if (!psin) + { + free(ai); + return EAI_MEMORY; + } + + memcpy( psin, &sin, sizeof( struct sockaddr_in ) ); + + ai->ai_flags = 0; + ai->ai_family = AF_INET; + ai->ai_socktype = hints.ai_socktype; + ai->ai_protocol = hints.ai_protocol; + ai->ai_addrlen = sizeof(*psin); + ai->ai_addr = (struct sockaddr *) psin; + ai->ai_canonname = NULL; + ai->ai_next = NULL; + + *result = ai; + + return 0; +} + +void wolf_port_freeaddrinfo( struct wolf_port_addrinfo *result ) { + /* stub doesn't have more than one element in list, adapt if you change that! */ + if( result != NULL ) { + if( result->ai_addr != NULL ) { + free( result->ai_addr ); + } + free( result ); + } +} + +/* + * Convert an ipv4 address to a hostname. + * + * Bugs: - Only supports NI_NUMERICHOST and NI_NUMERICSERV + * It will never resolv a hostname. + * - No IPv6 support. + */ +extern int getnameinfo( const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, + int flags ) { + WOLF_UNUSED( salen ); + + /* Invalid arguments. */ + if( sa == NULL || ( host == NULL && serv == NULL ) ) { + return EAI_FAIL; + } + + /* We don't support those. */ + if( ( host && !( flags & NI_NUMERICHOST ) ) + || ( serv && !( flags & NI_NUMERICSERV ) ) ) { + return EAI_FAIL; + } + +#ifdef HAVE_IPV6 + /* no IPV6 here */ + if( sa->sa_family == AF_INET6 ) { + return EAI_FAMILY; + } +#endif + + if( host != NULL ) { + if( sa->sa_family == AF_INET ) { + char *p; + + /* maximal size of an IP4 addres 255.255.255.255.255 */ + if( hostlen < 16 ) { + return EAI_MEMORY; + } + + p = inet_ntoa( ( (const struct sockaddr_in * ) sa)->sin_addr ); + + strlcpy( host, p, hostlen ); + } + } + + if( serv != NULL ) { + if (sa->sa_family == AF_INET ) { + /* maximal number of digits in an unsigned short */ + if( servlen < 10 ) { + return EAI_MEMORY; + } + (void)itoa( ntohs( ( (const struct sockaddr_in * )sa)->sin_port ), serv, servlen ); + } + } + + return 0; +} + +const char *wolf_port_gai_strerror( int errcode ) { + static char buf[256]; + + switch( errcode ) { + case EAI_NONAME: + return "host nor service provided, or not known"; + + case EAI_AGAIN: + return "temporary failure in name resolution"; + + case EAI_FAIL: + return "non-recoverable failure in name resultion"; + + case EAI_FAMILY: + return "socket family not supported"; + + case EAI_MEMORY: + return "memory allocation failure"; + + default: + snprintf( buf, 256, "Unknown GAI error %d", errcode ); + return buf; + } +} + #endif /* !defined HAVE_GETADDRINFO || defined TEST_GETADDRINFO */ + +#if !defined HAVE_GAI_STRERROR_R || defined TEST_GAI_STRERROR_R + +#include "threads/mutex.h" /* for mutexes */ +#include "port/stdio.h" /* for snprintf */ +#include "port/stdbool.h" /* for bool */ +#include "port/string.h" /* for strncmp */ +#include <errno.h> /* for errno */ + +static bool mutex_initialized = false; +static wolf_mutex_t mutex; + +int wolf_port_gai_strerror_r( int errcode, char *buf, size_t buflen ) { + int safe_errno = errno; + const char *msg; + + /* critical section as gai_strerror is not thread-safe */ + if( !mutex_initialized ) { + wolf_mutex_init( &mutex ); + mutex_initialized = true; + } + wolf_mutex_lock( &mutex ); + + msg = gai_strerror( errcode ); + if( strncmp( msg, "Unknown GAI errror", strlen( "Unknown GAI error" ) ) == 0 /* our own stub implementation */ ) { + (void)snprintf( buf, buflen, "Unknown GAI error %d", errcode ); + errno = EINVAL; + wolf_mutex_unlock( &mutex ); + return -1; + } + + strncpy( buf, msg, buflen-1 ); + errno = safe_errno; + wolf_mutex_unlock( &mutex ); + return 0; +} + +#endif /* !defined HAVE_GAI_STRERROR_R || defined TEST_GAI_STRERROR_R */ |