/* Copyright (C) 2008 Andreas Baumann This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "port/netdb.h" #if !defined HAVE_GETADDRINFO || defined TEST_GETADDRINFO #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 /* 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, 10 ); } } 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 /* for errno */ #ifdef HAVE_THREADS static bool mutex_initialized = false; static wolf_mutex_t mutex; #endif 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 */ #ifdef HAVE_THREADS if( !mutex_initialized ) { wolf_mutex_init( &mutex ); mutex_initialized = true; } wolf_mutex_lock( &mutex ); #endif 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; #ifdef HAVE_THREADS wolf_mutex_unlock( &mutex ); #endif return -1; } strncpy( buf, msg, buflen-1 ); errno = safe_errno; #ifdef HAVE_THREADS wolf_mutex_unlock( &mutex ); #endif return 0; } #endif /* !defined HAVE_GAI_STRERROR_R || defined TEST_GAI_STRERROR_R */