2007-10-04 21:20:49 +04:00
/*
PostgreSQL Database Management System
( formerly known as Postgres , then as Postgres95 )
Portions Copyright ( c ) 1996 - 2005 , The PostgreSQL Global Development Group
Portions Copyright ( c ) 1994 , The Regents of the University of California
Permission to use , copy , modify , and distribute this software and its
documentation for any purpose , without fee , and without a written agreement
is hereby granted , provided that the above copyright notice and this paragraph
and the following two paragraphs appear in all copies .
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
DIRECT , INDIRECT , SPECIAL , INCIDENTAL , OR CONSEQUENTIAL DAMAGES , INCLUDING
LOST PROFITS , ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION ,
EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE .
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES ,
INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE . THE SOFTWARE PROVIDED HEREUNDER IS
ON AN " AS IS " BASIS , AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS
TO PROVIDE MAINTENANCE , SUPPORT , UPDATES , ENHANCEMENTS , OR MODIFICATIONS .
*/
/*-------------------------------------------------------------------------
*
* getaddrinfo . c
* Support getaddrinfo ( ) on platforms that don ' t have it .
*
* We also supply getnameinfo ( ) here , assuming that the platform will have
* it if and only if it has getaddrinfo ( ) . If this proves false on some
* platform , we ' ll need to split this file and provide a separate configure
* test for getnameinfo ( ) .
*
* Copyright ( c ) 2003 - 2007 , PostgreSQL Global Development Group
*
* Copyright ( C ) 2007 Jeremy Allison .
* Modified to return multiple IPv4 addresses for Samba .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
# include "replace.h"
# include "system/network.h"
# ifndef SMB_MALLOC
# define SMB_MALLOC(s) malloc(s)
# endif
# ifndef SMB_STRDUP
# define SMB_STRDUP(s) strdup(s)
# endif
static int check_hostent_err ( struct hostent * hp )
{
if ( ! hp ) {
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_name | | hp - > h_addrtype ! = AF_INET ) {
return EAI_FAIL ;
}
return 0 ;
}
static char * canon_name_from_hostent ( struct hostent * hp ,
int * perr )
{
char * ret = NULL ;
* perr = check_hostent_err ( hp ) ;
if ( * perr ) {
return NULL ;
}
ret = SMB_STRDUP ( hp - > h_name ) ;
if ( ! ret ) {
* perr = EAI_MEMORY ;
}
return ret ;
}
static char * get_my_canon_name ( int * perr )
{
char name [ HOST_NAME_MAX + 1 ] ;
if ( gethostname ( name , HOST_NAME_MAX ) = = - 1 ) {
* perr = EAI_FAIL ;
return NULL ;
}
/* Ensure null termination. */
name [ HOST_NAME_MAX ] = ' \0 ' ;
return canon_name_from_hostent ( gethostbyname ( name ) , perr ) ;
}
static char * get_canon_name_from_addr ( struct in_addr ip ,
int * perr )
{
return canon_name_from_hostent (
gethostbyaddr ( & ip , sizeof ( ip ) , AF_INET ) ,
perr ) ;
}
static struct addrinfo * alloc_entry ( const struct addrinfo * hints ,
struct in_addr ip ,
unsigned short port )
{
struct sockaddr_in * psin = NULL ;
struct addrinfo * ai = SMB_MALLOC ( sizeof ( * ai ) ) ;
if ( ! ai ) {
return NULL ;
}
memset ( ai , ' \0 ' , sizeof ( * ai ) ) ;
psin = SMB_MALLOC ( sizeof ( * psin ) ) ;
if ( ! psin ) {
free ( ai ) ;
return NULL ;
}
memset ( psin , ' \0 ' , sizeof ( * psin ) ) ;
psin - > sin_family = AF_INET ;
psin - > sin_port = htons ( port ) ;
psin - > sin_addr = ip ;
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 ;
return ai ;
}
/*
* get address info for a single ipv4 address .
*
* Bugs : - servname can only be a number , not text .
*/
static int getaddr_info_single_addr ( const char * service ,
uint32_t addr ,
const struct addrinfo * hints ,
struct addrinfo * * res )
{
struct addrinfo * ai = NULL ;
struct in_addr ip ;
unsigned short port = 0 ;
if ( service ) {
port = ( unsigned short ) atoi ( service ) ;
}
ip . s_addr = htonl ( addr ) ;
ai = alloc_entry ( hints , ip , port ) ;
if ( ! ai ) {
return EAI_MEMORY ;
}
/* If we're asked for the canonical name,
* make sure it returns correctly . */
if ( ! ( hints - > ai_flags & AI_NUMERICSERV ) & &
hints - > ai_flags & AI_CANONNAME ) {
int err ;
if ( addr = = INADDR_LOOPBACK | | addr = = INADDR_ANY ) {
ai - > ai_canonname = get_my_canon_name ( & err ) ;
} else {
ai - > ai_canonname =
get_canon_name_from_addr ( ip , & err ) ;
}
if ( ai - > ai_canonname = = NULL ) {
freeaddrinfo ( ai ) ;
return err ;
}
}
* res = ai ;
return 0 ;
}
/*
* get address info for multiple ipv4 addresses .
*
* Bugs : - servname can only be a number , not text .
*/
static int getaddr_info_name ( const char * node ,
const char * service ,
const struct addrinfo * hints ,
struct addrinfo * * res )
{
struct addrinfo * listp = NULL , * prevp = NULL ;
char * * pptr = NULL ;
int err ;
struct hostent * hp = NULL ;
unsigned short port = 0 ;
if ( service ) {
port = ( unsigned short ) atoi ( service ) ;
}
hp = gethostbyname ( node ) ;
err = check_hostent_err ( hp ) ;
if ( err ) {
return err ;
}
for ( pptr = hp - > h_addr_list ; * pptr ; pptr + + ) {
2007-11-03 00:23:10 +03:00
struct in_addr ip = * ( struct in_addr * ) * pptr ;
2007-10-04 21:20:49 +04:00
struct addrinfo * ai = alloc_entry ( hints , ip , port ) ;
if ( ! ai ) {
freeaddrinfo ( listp ) ;
return EAI_MEMORY ;
}
if ( ! listp ) {
listp = ai ;
prevp = ai ;
ai - > ai_canonname = SMB_STRDUP ( hp - > h_name ) ;
if ( ! ai - > ai_canonname ) {
freeaddrinfo ( listp ) ;
return EAI_MEMORY ;
}
} else {
prevp - > ai_next = ai ;
prevp = ai ;
}
}
* res = listp ;
return 0 ;
}
/*
* get address info for ipv4 sockets .
*
* Bugs : - servname can only be a number , not text .
*/
2007-10-25 10:53:38 +04:00
int rep_getaddrinfo ( const char * node ,
2007-10-04 21:20:49 +04:00
const char * service ,
const struct addrinfo * hintp ,
struct addrinfo * * res )
{
struct addrinfo hints ;
/* Setup the hints struct. */
if ( hintp = = NULL ) {
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = AF_INET ;
hints . ai_socktype = SOCK_STREAM ;
} else {
memcpy ( & hints , hintp , sizeof ( hints ) ) ;
}
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 ( ! node & & ! service ) {
return EAI_NONAME ;
}
if ( node ) {
if ( node [ 0 ] = = ' \0 ' ) {
return getaddr_info_single_addr ( service ,
INADDR_ANY ,
& hints ,
res ) ;
} else if ( hints . ai_flags & AI_NUMERICHOST ) {
struct in_addr ip ;
if ( ! inet_aton ( node , & ip ) ) {
return EAI_FAIL ;
}
return getaddr_info_single_addr ( service ,
ntohl ( ip . s_addr ) ,
& hints ,
res ) ;
} else {
return getaddr_info_name ( node ,
service ,
& hints ,
res ) ;
}
} else if ( hints . ai_flags & AI_PASSIVE ) {
return getaddr_info_single_addr ( service ,
INADDR_ANY ,
& hints ,
res ) ;
}
return getaddr_info_single_addr ( service ,
INADDR_LOOPBACK ,
& hints ,
res ) ;
}
2007-10-25 10:53:38 +04:00
void rep_freeaddrinfo ( struct addrinfo * res )
2007-10-04 21:20:49 +04:00
{
struct addrinfo * next = NULL ;
for ( ; res ; res = next ) {
next = res - > ai_next ;
if ( res - > ai_canonname ) {
free ( res - > ai_canonname ) ;
}
if ( res - > ai_addr ) {
free ( res - > ai_addr ) ;
}
free ( res ) ;
}
}
2007-10-25 10:53:38 +04:00
const char * rep_gai_strerror ( int errcode )
2007-10-04 21:20:49 +04:00
{
# ifdef HAVE_HSTRERROR
int hcode ;
switch ( errcode )
{
case EAI_NONAME :
hcode = HOST_NOT_FOUND ;
break ;
case EAI_AGAIN :
hcode = TRY_AGAIN ;
break ;
case EAI_FAIL :
default :
hcode = NO_RECOVERY ;
break ;
}
return hstrerror ( hcode ) ;
# else /* !HAVE_HSTRERROR */
switch ( errcode )
{
case EAI_NONAME :
return " Unknown host " ;
case EAI_AGAIN :
return " Host name lookup failure " ;
# ifdef EAI_BADFLAGS
case EAI_BADFLAGS :
return " Invalid argument " ;
# endif
# ifdef EAI_FAMILY
case EAI_FAMILY :
return " Address family not supported " ;
# endif
# ifdef EAI_MEMORY
case EAI_MEMORY :
return " Not enough memory " ;
# endif
# ifdef EAI_NODATA
case EAI_NODATA :
return " No host data of that type was found " ;
# endif
# ifdef EAI_SERVICE
case EAI_SERVICE :
return " Class type not found " ;
# endif
# ifdef EAI_SOCKTYPE
case EAI_SOCKTYPE :
return " Socket type not supported " ;
# endif
default :
return " Unknown server error " ;
}
# endif /* HAVE_HSTRERROR */
}
static int gethostnameinfo ( const struct sockaddr * sa ,
char * node ,
size_t nodelen ,
int flags )
{
int ret = - 1 ;
char * p = NULL ;
if ( ! ( flags & NI_NUMERICHOST ) ) {
struct hostent * hp = gethostbyaddr (
& ( ( struct sockaddr_in * ) sa ) - > sin_addr ,
sizeof ( struct in_addr ) ,
sa - > sa_family ) ;
ret = check_hostent_err ( hp ) ;
if ( ret = = 0 ) {
/* Name looked up successfully. */
ret = snprintf ( node , nodelen , " %s " , hp - > h_name ) ;
2007-11-03 00:23:10 +03:00
if ( ret < 0 | | ( size_t ) ret > = nodelen ) {
2007-10-04 21:20:49 +04:00
return EAI_MEMORY ;
}
if ( flags & NI_NOFQDN ) {
p = strchr ( node , ' . ' ) ;
if ( p ) {
* p = ' \0 ' ;
}
}
return 0 ;
}
if ( flags & NI_NAMEREQD ) {
/* If we require a name and didn't get one,
* automatically fail . */
return ret ;
}
/* Otherwise just fall into the numeric host code... */
}
p = inet_ntoa ( ( ( struct sockaddr_in * ) sa ) - > sin_addr ) ;
ret = snprintf ( node , nodelen , " %s " , p ) ;
2007-11-03 00:23:10 +03:00
if ( ret < 0 | | ( size_t ) ret > = nodelen ) {
2007-10-04 21:20:49 +04:00
return EAI_MEMORY ;
}
return 0 ;
}
static int getservicenameinfo ( const struct sockaddr * sa ,
char * service ,
size_t servicelen ,
int flags )
{
int ret = - 1 ;
int port = ntohs ( ( ( struct sockaddr_in * ) sa ) - > sin_port ) ;
if ( ! ( flags & NI_NUMERICSERV ) ) {
struct servent * se = getservbyport (
port ,
( flags & NI_DGRAM ) ? " udp " : " tcp " ) ;
if ( se & & se - > s_name ) {
/* Service name looked up successfully. */
ret = snprintf ( service , servicelen , " %s " , se - > s_name ) ;
2007-11-03 00:23:10 +03:00
if ( ret < 0 | | ( size_t ) ret > = servicelen ) {
2007-10-04 21:20:49 +04:00
return EAI_MEMORY ;
}
return 0 ;
}
/* Otherwise just fall into the numeric service code... */
}
ret = snprintf ( service , servicelen , " %d " , port ) ;
2007-11-03 00:23:10 +03:00
if ( ret < 0 | | ( size_t ) ret > = servicelen ) {
2007-10-04 21:20:49 +04:00
return EAI_MEMORY ;
}
return 0 ;
}
/*
* Convert an ipv4 address to a hostname .
*
* Bugs : - No IPv6 support .
*/
2007-10-25 10:53:38 +04:00
int rep_getnameinfo ( const struct sockaddr * sa , socklen_t salen ,
2007-10-04 21:20:49 +04:00
char * node , size_t nodelen ,
char * service , size_t servicelen , int flags )
{
/* Invalid arguments. */
if ( sa = = NULL | | ( node = = NULL & & service = = NULL ) ) {
return EAI_FAIL ;
}
if ( sa - > sa_family ! = AF_INET ) {
return EAI_FAIL ;
}
if ( salen < sizeof ( struct sockaddr_in ) ) {
return EAI_FAIL ;
}
if ( node ) {
return gethostnameinfo ( sa , node , nodelen , flags ) ;
}
if ( service ) {
return getservicenameinfo ( sa , service , servicelen , flags ) ;
}
return 0 ;
}