Logo Search packages:      
Sourcecode: lfc-postgres version File versions  Download package

Cnetdb.c

/*
 * Copyright (C) 1999-2008 by CERN/IT/PDP/DM
 * All rights reserved
 */


#ifndef lint
static char sccsid[] = "@(#)$RCSfile: Cnetdb.c,v $ $Revision: 1.10 $ $Date: 2008/10/29 10:08:53 $ CERN IT-PDP/DM Olof Barring";
#endif /* not lint */

/*
 * Cnetdb.c - CASTOR MT-safe wrappers on netdb routines.
 */ 

#include <sys/types.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <serrno.h>
#include <errno.h>
#if defined(_WIN32)
#include <winsock2.h>
#else /* _WIN32 */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#endif /* _WIN32 */

#include <Cmutex.h>
#include <Cglobals.h>
#include <Cnetdb.h>
#ifdef ADNS
#include <Cthread_api.h>
#include <adns.h>
#ifndef CA_MAXIPNAMELEN
#define CA_MAXIPNAMELEN 15
#endif
#endif /* ADNS */

static int is_loopback _PROTO((struct sockaddr *));
EXTERN_C int Cdomainname _PROTO((char *, int));
EXTERN_C int isremote_sa(struct sockaddr *, char *);

#if (defined(IRIX5) || defined(IRIX6) || defined(IRIX64))
EXTERN_C struct hostent *gethostbyname_r(const char *, struct hostent *, char *, int, int *);
EXTERN_C struct hostent *gethostbyaddr_r(const void *, size_t, int, struct hostent *, char *, int, int *);
EXTERN_C struct servent   *getservbyname_r(const char *, const char *,
                                         struct servent *, char *, int);
#endif
#ifdef ADNS
int _Cnetdb_ipaddr2domain _PROTO((char *, char *, char **, char **));
int _Cnetdb_ipaddr2str _PROTO((size_t, char *, unsigned long));
char *_Cgethostbyname_adns _PROTO((char *));
char *_Cgethostbyaddr_adns _PROTO((void *));
static int hostdata_key = -1;
/*
 * adns_if_noenv=        0x0001 :  do not look at environment
 * adns_if_noerrprint=   0x0002  : never print output to stderr (_debug overrides)
 * adns_if_noserverwarn= 0x0004  : do not warn to stderr about duff nameservers etc
 * adns_if_debug=        0x0008  : enable all output to stderr plus debug msgs
 * adns_if_logpid=       0x0080  : include pid in diagnostic output
 * adns_if_noautosys=    0x0010  : do not make syscalls at every opportunity
 * adns_if_eintr=        0x0020  : allow _wait and _synchronous to return EINTR
 * adns_if_nosigpipe=    0x0040  : applic has SIGPIPE set to SIG_IGN, do not protect
 * adns_if_checkc_entex= 0x0100  : do consistency checks on entry/exit to adns funcs
 * adns_if_checkc_freq=  0x0300  : do consistency checks very frequently (slow!)
 */
#define ADNS_INIT_FLAGS ( adns_if_noerrprint | adns_if_noserverwarn | adns_if_eintr )
/*
 * adns_qf_search=          0x00000001   : use the searchlist
 * adns_qf_usevc=           0x00000002   : use a virtual circuit (TCP connection)
 * adns_qf_owner=           0x00000004   : fill in the owner field in the answer
 * adns_qf_quoteok_query=   0x00000010   : allow special chars in query domain
 * adns_qf_quoteok_cname=   0x00000000   : allow ... in CNAME we go via - now default
 * adns_qf_quoteok_anshost= 0x00000040   : allow ... in things supposed to be hostnames
 * adns_qf_quotefail_cname= 0x00000080   : refuse if quote-req chars in CNAME we go via
 * adns_qf_cname_loose=     0x00000100   : allow refs to CNAMEs - without, get _s_cname
 * adns_qf_cname_forbid=    0x00000200   : don't follow CNAMEs, instead give _s_cname
 * adns__qf_internalmask=   0x0ff00000
 */
#define ADNS_QUERY_FLAGS ( adns_qf_search | adns_qf_quoteok_cname | adns_qf_cname_loose )
#endif /* ADNS */

#ifdef ADNS
/*
 * Parse the IP address and convert to a reverse domain name.
 */
int _Cnetdb_ipaddr2domain(buf, start, addr, rest)
     char *buf;
     char *start;
     char **addr;
     char **rest;
{
  char *ptrs[5];
  int i;
  
  ptrs[0]= start;
retry:
  while (!isdigit(*ptrs[0]))
    if (!*ptrs[0]++) {
      *addr= *rest= NULL;
      serrno = EINVAL;
      return(-1);
    }
  for (i= 1; i < 5; i++) {
    ptrs[i]= ptrs[i-1];
    while (isdigit(*ptrs[i]++));
    if (i == 4 && *(ptrs[i]-1) == '\0')
      break;
    if ((i == 4 && !isspace(ptrs[i][-1])) ||
        (i != 4 && ptrs[i][-1] != '.') ||
        (ptrs[i]-ptrs[i-1] > 4)) {
      ptrs[0]= ptrs[i]-1;
      goto retry;
    }
  }
  sprintf(buf, "%.*s.%.*s.%.*s.%.*s.in-addr.arpa.",
        ptrs[4]-ptrs[3]-1, ptrs[3],
        ptrs[3]-ptrs[2]-1, ptrs[2],
        ptrs[2]-ptrs[1]-1, ptrs[1],
        ptrs[1]-ptrs[0]-1, ptrs[0]);
  *addr= ptrs[0];
  *rest= ptrs[4]-1;
  return(0);
}
/*
 * _Cnetdb_ipaddr2str - Convert an unsigned long number to a string %d.%d.%d.%d
 */
int DLL_DECL _Cnetdb_ipaddr2str(maxsize,output,ipaddr)
     size_t maxsize;
     char *output;
     unsigned long ipaddr;
{
  unsigned char *stringified = (unsigned char *) &ipaddr;
  
  if (maxsize < strlen("255.255.255.255")) {
    serrno = SEINTERNAL;
    return(-1);
  }
  sprintf(output,"%d.%d.%d.%d",
          stringified[0] & 0xFF,
          stringified[1] & 0xFF,
          stringified[2] & 0xFF,
          stringified[3] & 0xFF);
  return (0);
}

char *_Cgethostbyname_adns(name)
     char *name;
{
  adns_state adns;
  adns_query query;
  adns_answer *answer;
  char *buffer = NULL;
  int bufsize = 1024;
  int status;

  /* Makes sure that we will return a thread-safe value */
  Cglobals_get(&hostdata_key,(void **)&buffer,bufsize);
  if (buffer == NULL) {
    h_errno = NO_RECOVERY;
    serrno = SEINTERNAL;
    return(NULL);
  }

  if (adns_init(&adns, ADNS_INIT_FLAGS, NULL) != 0) {
    serrno = SEADNSINIT;
    return(NULL);
  }

  if (adns_submit(adns, (char *) name, adns_r_addr, ADNS_QUERY_FLAGS, NULL, &query) != 0) {
    serrno = SEADNSSUBMIT;
    return(NULL);
  }

  if ((status = adns_check(adns, &query, &answer, NULL)) == EAGAIN) {
    while (1) {
      if ((status = adns_wait(adns, &query, &answer, NULL)) != EAGAIN) {
        break;
      }
      if (status == 0) {
        break;
      }
    }
  }
    
  status = answer->status;

  if (status == adns_s_ok) {
    if (answer->nrrs != 1) {
      status = SEADNSTOOMANY;
    }
  } else {
    status = SEADNS;
  }

  if (status == adns_s_ok) {
    CONST char *typename = NULL;
    char *datastr = NULL;
    
    if ((status = adns_rr_info(answer->type, &typename, NULL, NULL, answer->rrs.untyped, &datastr)) != adns_s_nomemory) {
      char *datastrok;
      /* This routine is creating the string "INET ...", and I want to get rid of "INET" */
      if ((datastrok = strrchr(datastr,' ')) != NULL) {
        strcpy(buffer,++datastrok);
      } else {
        strcpy(buffer,datastr);
      }
      free(datastr);
    }
  } else {
    serrno = status;
  }

  free(answer);
  adns_finish(adns);
  return(status == adns_s_ok ? buffer : NULL);
}

char *_Cgethostbyaddr_adns(addr)
     void *addr;
{
  char *line_addr, *line_rest;
  char str[1024];
  adns_state adns;
  adns_query query;
  adns_answer *answer;
  char *buffer = NULL;
  int bufsize = 1024;
  int status;

  /* Makes sure that we will return a thread-safe value */
  Cglobals_get(&hostdata_key,(void **)&buffer,bufsize);
  if (buffer == NULL) {
    h_errno = NO_RECOVERY;
    serrno = SEINTERNAL;
    return(NULL);
  }

  if (adns_init(&adns, ADNS_INIT_FLAGS, NULL) != 0) {
    serrno = SEADNSINIT;
    return(NULL);
  }

  if (_Cnetdb_ipaddr2domain(str, addr, &line_addr, &line_rest) != 0) {
    return(NULL);
  }

  if (adns_submit(adns, str, adns_r_ptr, ADNS_QUERY_FLAGS, NULL, &query) != 0) {
    serrno = SEADNSSUBMIT;
    return(NULL);
  }

  if ((status = adns_check(adns, &query, &answer, NULL)) == EAGAIN) {
    while (1) {
      if ((status = adns_wait(adns, &query, &answer, NULL)) != EAGAIN) {
        break;
      }
      if (status == 0) {
        break;
      }
    }
  }
    
  status = answer->status;

  if (status == adns_s_ok) {
    strcpy(buffer,*answer->rrs.str);
  } else {
    serrno = status;
  }

  free(answer);
  adns_finish(adns);
  return(status == adns_s_ok ? buffer : NULL);
}

#endif /* ADNS */

struct hostent DLL_DECL *Cgethostbyname(name)
CONST char *name;
{
#ifdef ADNS
  /*
   * We will always use a thread or process specific
   * hostent structure
   */
    static int hostent_key = -1;
    static int inaddr_key = -1;
    static int haddrlist_key = -1;
    struct hostent *result = (struct hostent *)NULL;
    struct in_addr *inaddr = (struct in_addr *)NULL;
    char **haddrlist = (char **)NULL;

    Cglobals_get(&hostent_key,(void **)&result,sizeof(struct hostent));
    Cglobals_get(&inaddr_key,(void **)&inaddr,sizeof(struct in_addr));
    Cglobals_get(&haddrlist_key,(void **)&haddrlist,sizeof(char **));
    if ( result == (struct hostent *)NULL || inaddr == (struct in_addr *)NULL || haddrlist == (char **)NULL ) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    if ((result->h_name = _Cgethostbyname_adns((char *) name)) == NULL) {
        /*
         * One retry on EINTR
         */
        if ( errno == EINTR ) {
            if ((result->h_name = _Cgethostbyname_adns((char *) name)) == NULL) 
                return(NULL);
        } else return(NULL);
    }

    /* We convert the address into in_addr structure. This will fail if addr */
    /* is in a wrong format. */
    if (inet_aton((char *) result->h_name, inaddr) == 0) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    /* Resolve succeeded. We store backward the inaddr result */
    haddrlist[0] = (char *) inaddr;
    result->h_addr_list = haddrlist;

    return(result);
#else /* ADNS */
#if (!defined(_REENTRANT) && !defined(_THREAD_SAFE)) || defined(__osf__) && defined(__alpha) || defined(_WIN32) || defined(HPUX11) || defined(__APPLE__)
    /*
     * If single-thread compilation we don't do anything.
     * Also: Windows and Digital UNIX v4 and higher already
     *       provide thread safe version.
     */
    return(gethostbyname(name));
#elif defined(SOLARIS) || defined(IRIX6)
    static int hostent_key = -1;
    static int hostdata_key = -1;
    struct hostent *hp;
    struct hostent *result = (struct hostent *)NULL;
    char *buffer = (char *)NULL;
    int bufsize = 1024;
    int h_errnoop = 0;
    
    Cglobals_get(&hostent_key,(void **)&result,sizeof(struct hostent));
    Cglobals_get(&hostdata_key,(void **)&buffer,bufsize);

    if ( result == (struct hostent *)NULL || buffer == (char *)NULL ) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    hp = gethostbyname_r(name,result,buffer,bufsize,&h_errnoop);
    h_errno = h_errnoop;
    return(hp);
#elif defined(AIX42) || defined(hpux) || defined(HPUX10)
    static int hostent_key = -1;
    static int hostdata_key = -1;
    int rc;
    struct hostent *result = (struct hostent *)NULL;
    struct hostent_data *ht_data = (struct hostent_data *)NULL;

    Cglobals_get(&hostent_key,(void **)&result,sizeof(struct hostent));
    Cglobals_get(&hostdata_key,(void **)&ht_data,sizeof(struct hostent_data));

    if ( result == (struct hostent *)NULL ||
        ht_data == (struct hostent_data *)NULL ) {
         h_errno = NO_RECOVERY;
         return(NULL);
    }

    rc = gethostbyname_r(name,result,ht_data);
    if (rc == -1) return(NULL);
    else return(result);
#elif defined(linux)
    static int hostent_key = -1;
    static int hostdata_key = -1;
    int rc;
    struct hostent *hp;
    struct hostent *result = (struct hostent *)NULL;
    char *buffer = (char *)NULL;
    int bufsize = 1024;
    int h_errnoop = 0;

    Cglobals_get(&hostent_key,(void **)&result,sizeof(struct hostent));
    Cglobals_get(&hostdata_key,(void **)&buffer,bufsize);

    if ( result == (struct hostent *)NULL || buffer == (char *)NULL ) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    rc = gethostbyname_r(name,result,buffer,bufsize,&hp,&h_errnoop);
    h_errno = h_errnoop;
    if ( rc == -1 ) hp = NULL;
    return(hp);
#else
    /*
     * Not supported
     */
    return(NULL);
#endif
#endif /* ADNS */
}

struct hostent DLL_DECL *Cgethostbyaddr(addr,len,type)
CONST void *addr;
size_t len;
int type;
{
#ifdef ADNS
  /*
   * We will always use a thread or process specific
   * hostent structure
   */
    static int hostent_key = -1;
    static int inaddr_key = -1;
    static int haddrlist_key = -1;
    struct hostent *result = (struct hostent *)NULL;
    struct in_addr *inaddr = (struct in_addr *)NULL;
    char **haddrlist = (char **)NULL;
    char ipaddr2str[CA_MAXIPNAMELEN+1]; /* IP address as a string */

    if (type != AF_INET) {
      errno = EAFNOSUPPORT;
      h_errno = NO_RECOVERY;
      return(NULL);
    }

    Cglobals_get(&hostent_key,(void **)&result,sizeof(struct hostent));
    Cglobals_get(&inaddr_key,(void **)&inaddr,sizeof(struct in_addr));
    Cglobals_get(&haddrlist_key,(void **)&haddrlist,sizeof(char **));
    if ( result == (struct hostent *)NULL || inaddr == (struct in_addr *)NULL || haddrlist == (char **)NULL ) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    /* We convert the address into in_addr structure. This will fail if addr */
    /* is in a wrong format. */
    if (_Cnetdb_ipaddr2str(CA_MAXIPNAMELEN,ipaddr2str,((struct in_addr *) addr)->s_addr) < 0) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    if ((result->h_name = _Cgethostbyaddr_adns(ipaddr2str)) == NULL) {
        /*
         * One retry on EINTR
         */
        if ( errno == EINTR ) {
            if ((result->h_name = _Cgethostbyaddr_adns(ipaddr2str)) == NULL)
                return(NULL);
        } else return(NULL);
    }
    /* We convert the address into in_addr structure. This will fail if addr */
    /* is in a wrong format. */
    if (inet_aton((char *) ipaddr2str, inaddr) == 0) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    /* Resolve succeeded. We store backward the inaddr result */
    haddrlist[0] = (char *) inaddr;
    result->h_addr_list = haddrlist;

    return(result);
#else /* ADNS */
#if (!defined(_REENTRANT) && !defined(_THREAD_SAFE)) || defined(__osf__) && defined(__alpha) || defined(_WIN32) || defined(HPUX11) || defined(__APPLE__)
    /*
     * If single-thread compilation we don't do anything.
     * Also: Windows and Digital UNIX v4 and higher already
     *       provide thread safe version.
     */
    return(gethostbyaddr(addr,len,type));
#elif defined(SOLARIS) || defined(IRIX6)
    static int hostent_key = -1;
    static int hostdata_key = -1;
    struct hostent *hp;
    struct hostent *result = (struct hostent *)NULL;
    char *buffer = (char *)NULL;
    int bufsize = 1024;
    int h_errnoop = 0;
    
    Cglobals_get(&hostent_key,(void **)&result,sizeof(struct hostent));
    Cglobals_get(&hostdata_key,(void **)&buffer,bufsize);

    if ( result == (struct hostent *)NULL || buffer == (char *)NULL ) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    hp = gethostbyaddr_r(addr,len,type,result,buffer,bufsize,&h_errnoop);
    h_errno = h_errnoop;
    return(hp);
#elif defined(AIX42) || defined(hpux) || defined(HPUX10)
    static int hostent_key = -1;
    static int hostdata_key = -1;
    int rc;
    struct hostent *result = (struct hostent *)NULL;
    struct hostent_data *ht_data = (struct hostent_data *)NULL;

    Cglobals_get(&hostent_key,(void **)&result,sizeof(struct hostent));
    Cglobals_get(&hostdata_key,(void **)&ht_data,sizeof(struct hostent_data));

    if ( result == (struct hostent *)NULL ||
        ht_data == (struct hostent_data *)NULL ) {
         h_errno = NO_RECOVERY;
         return(NULL);
    }

    rc = gethostbyaddr_r(addr,len,type,result,ht_data);
    if (rc == -1) return(NULL);
    else return(result);
#elif defined(linux)
    static int hostent_key = -1;
    static int hostdata_key = -1;
    int rc;
    struct hostent *hp;
    struct hostent *result = (struct hostent *)NULL;
    char *buffer = (char *)NULL;
    int bufsize = 1024;
    int h_errnoop = 0;

    Cglobals_get(&hostent_key,(void **)&result,sizeof(struct hostent));
    Cglobals_get(&hostdata_key,(void **)&buffer,bufsize);

    if ( result == (struct hostent *)NULL || buffer == (char *)NULL ) {
        h_errno = NO_RECOVERY;
        return(NULL);
    }
    rc = gethostbyaddr_r(addr,len,type,result,buffer,bufsize,&hp,&h_errnoop);
    h_errno = h_errnoop;
    if ( rc == -1 ) hp = NULL;
    return(hp);
#else
    /*
     * Not supported
     */
    return(NULL);
#endif
#endif /* ADNS */
}

struct servent DLL_DECL *Cgetservbyname(name,proto)
CONST char *name;
CONST char *proto;
{
#if (!defined(_REENTRANT) && !defined(_THREAD_SAFE)) || defined(__osf__) && defined(__alpha) || defined(_WIN32) || defined(HPUX11)
    /*
     * If single-thread compilation we don't do anything.
     * Also: Windows and Digital UNIX v4 and higher already
     *       provide thread safe version.
     */
    return(getservbyname(name,proto));
#elif defined(SOLARIS) || defined(IRIX6)
    static int servent_key = -1;
    static int servdata_key = -1;
    struct servent *sp;
    struct servent *result = (struct servent *)NULL;
    char *buffer = (char *)NULL;
    int bufsize = 1024;
   
    Cglobals_get(&servent_key,(void **)&result,sizeof(struct servent));
    Cglobals_get(&servdata_key,(void **)&buffer,bufsize);

    if ( result == (struct servent *)NULL || buffer == (char *)NULL ) {
        return(NULL);
    }
    sp = getservbyname_r(name,proto,result,buffer,bufsize);
    return(sp);
#elif defined(AIX42) || defined(hpux) || defined(HPUX10)
    static int servent_key = -1;
    static int servdata_key = -1;
    int rc;
    struct servent *result = (struct servent *)NULL;
    struct servent_data *st_data = (struct servent_data *)NULL;

    Cglobals_get(&servent_key,(void **)&result,sizeof(struct servent));
    Cglobals_get(&servdata_key,(void **)&st_data,sizeof(struct servent_data));

    if ( result == (struct servent *)NULL ||
        st_data == (struct servent_data *)NULL ) {
         return(NULL);
    }

    rc = getservbyname_r(name,proto,result,st_data);
    if (rc == -1) return(NULL);
    else return(result);
#elif defined(linux)
    static int servent_key = -1;
    static int servdata_key = -1;
    int rc;
    struct servent *sp;
    struct servent *result = (struct servent *)NULL;
    char *buffer = (char *)NULL;
    int bufsize = 1024;

    Cglobals_get(&servent_key,(void **)&result,sizeof(struct servent));
    Cglobals_get(&servdata_key,(void **)&buffer,bufsize);

    if ( result == (struct servent *)NULL || buffer == (char *)NULL ) {
        return(NULL);
    }
    rc = getservbyname_r(name,proto,result,buffer,bufsize,&sp);
    if ( rc == -1 ) sp = NULL;
    return(sp);
#elif defined(__APPLE__)
    /* getservbyname() returns thread specfic storage
     */
    return (getservbyname(name,proto));
#else
    /*
     * Not supported
     */
    return(NULL);
#endif
}

int DLL_DECL Cgetnameinfo(savp, salen, host, hostlen, serv, servlen, flags)
CONST void *savp;
size_t salen;
char *host;
size_t hostlen;
char *serv;
size_t servlen;
int flags;
{
    struct addrinfo hints, *res;
    char ho[NI_MAXHOST];
    int gaierrno;

    /* if the user is not interested in the hostname we don't do further checks */
    if (host == NULL)
      return (getnameinfo((struct sockaddr *)savp, salen, host, hostlen, serv, servlen, flags));

    ho[0] = '\0';
    if (!(flags & (NI_NUMERICHOST | NI_NAMEREQD))) {
        gaierrno = getnameinfo((struct sockaddr *)savp, salen, host, hostlen, serv, servlen, flags|NI_NUMERICHOST);
        if (gaierrno) {
            serrno = (gaierrno == EAI_SYSTEM) ? 0 : SEINTERNAL;
            return (gaierrno);
        }

        gaierrno = getnameinfo((struct sockaddr *)savp, salen, ho, sizeof(ho), NULL, 0, flags|NI_NAMEREQD);
        if (gaierrno || strlen(ho)==0 || strlen(ho)>=hostlen)
            return (0);
        if (strchr(ho, ']'))
            return (0);
    } else {
        gaierrno = getnameinfo((struct sockaddr *)savp, salen, host, hostlen, serv, servlen, flags);
    }

    if (gaierrno) {
        serrno = (gaierrno == EAI_SYSTEM) ? 0 : SEINTERNAL;
        return (gaierrno);
    }
    if (strchr(host,']')) {
        serrno = EINVAL;
        return (EAI_FAIL);
    }

    if (strlen(ho)>0 || (flags & NI_NAMEREQD)) {
        memset(&hints, '\0', sizeof(hints));
        hints.ai_flags |= AI_NUMERICHOST;
        if (getaddrinfo(strlen(ho)>0 ? ho : host, NULL, &hints, &res) == 0) {
            freeaddrinfo(res);
            if (strlen(ho)>0)
                return (0);
            serrno = EINVAL;
            return (EAI_FAIL);
        }
        if (strlen(ho)>0)
            strcpy(host, ho);
    }

    return (0);
}

int DLL_DECL Cgetaddrinfo(hostname, servname, hints, res)
CONST char *hostname;
CONST char *servname;
CONST struct addrinfo *hints;
struct addrinfo **res;
{
    struct addrinfo h;
    char ho[NI_MAXHOST];
    int gaierrno;
    size_t namelen = 0;

    if (hints)
      memcpy(&h, hints, sizeof(h));
    else
      memset(&h, '\0', sizeof(h));

    if (hostname != NULL)
      namelen = strlen(hostname);

    if (namelen>2 && !(h.ai_flags & AI_NUMERICHOST) &&
        hostname[0]=='[' && hostname[namelen-1]==']') {
      if (namelen-2 >= NI_MAXHOST) {
        serrno = SENAMETOOLONG;
        return (EAI_NONAME);
      }
      strncpy(ho, &hostname[1], namelen-2);
      ho[namelen-2] = '\0';
      h.ai_flags |= AI_NUMERICHOST;
    } else if (hostname != NULL) {
      if (namelen >= NI_MAXHOST) {
        serrno = SENAMETOOLONG;
        return (EAI_NONAME);
      }
      strcpy(ho, hostname);
    }

    gaierrno = getaddrinfo((hostname!=NULL) ? ho : NULL, servname, &h, res);
#if defined(AI_ADDRCONFIG)
    if (gaierrno == EAI_BADFLAGS && (h.ai_flags & AI_ADDRCONFIG)) {
        h.ai_flags &= ~AI_ADDRCONFIG;
        gaierrno = getaddrinfo((hostname!=NULL) ? ho : NULL, servname, &h, res);
    }
#endif
    switch(gaierrno) {
#ifdef EAI_ADDRFAMILY
        case EAI_ADDRFAMILY:
#endif
      case EAI_FAMILY:
            errno = EAFNOSUPPORT;
            serrno = 0;
            break;
        case EAI_MEMORY:
            errno = ENOMEM;
            serrno = 0;
            break;
        case EAI_NONAME:
            serrno = SENOSHOST;
            break;
        case 0:
            break;
        default:
            serrno = (gaierrno == EAI_SYSTEM) ? 0 : SEINTERNAL;
            break;
    }
    return (gaierrno);
}

char DLL_DECL *Cgetnetaddress(sock, sa, salen, skey, numeric_out, name_out, flags, cflags)
int sock;
CONST void *sa;
size_t salen;
int *skey;
CONST char **numeric_out;
CONST char **name_out;
int flags;
int cflags;
{
    struct sockaddr_storage peeraddr;
#if defined(_WIN32)
    int addrlen;
#else
    socklen_t addrlen;
#endif
    struct addrinfo hints, *ai, *aitop;
    char *ipbuf,*namebuf,*cp;
    char tmpbuf[NI_MAXHOST+1];
    int gaierrno, match, needalloc;

    if (numeric_out)
        *numeric_out = NULL;

    if (name_out)
        *name_out = NULL;

    /* return error if neither socket nor sockaddr passed */
    if (sock<0 && sa == NULL) {
        serrno = EINVAL;
        return (NULL);
    }

    /* return error if both socket and sockaddr passed */
    if (sock>=0 && sa) {
        serrno = EINVAL;
        return (NULL);
    }

    /* check if some inconsistent flags are set */
    if ((flags & NI_NUMERICHOST) && (flags & NI_NAMEREQD)) {
        serrno = EINVAL;
        return (NULL);
    }

    if (skey != NULL) {
        /* get the storage for the hostname/ip representation */
        Cglobals_get(skey,(void **)&cp,2*(NI_MAXHOST));
        if (cp == NULL) {
            serrno = SEINTERNAL;
            return (NULL);
        }
        ipbuf = &cp[0];
        namebuf = &cp[NI_MAXHOST];
        needalloc = 0;
    } else {
        needalloc = 1;
    }

    if (sock>=0) {
        addrlen = sizeof(peeraddr);
        if (getpeername(sock, (struct sockaddr *)&peeraddr, &addrlen)) {
            serrno = 0;
            return (NULL);
        }
        sa = &peeraddr;
        salen = addrlen;
    }

    /* lookup only the IP number first */
    if (needalloc) ipbuf = malloc(NI_MAXHOST);

    gaierrno = Cgetnameinfo((struct sockaddr *)sa, salen, ipbuf, NI_MAXHOST,
                            NULL, 0, (flags|NI_NUMERICHOST)&(~NI_NAMEREQD) );
    if (gaierrno != 0) {
        if (needalloc) free(ipbuf);
        serrno = (gaierrno == EAI_SYSTEM) ? 0: SEINTERNAL;
        return (NULL);
    }

    if (numeric_out)
        *numeric_out = ipbuf;

    if (flags & NI_NUMERICHOST) {
      /* only the IP number was wanted; can just return what we have */
      return (ipbuf);
    }

    /* relating to validation of the name */
    match = 0;

    if (needalloc) namebuf = malloc(NI_MAXHOST);

    /* special case for loopback */
    if (!(cflags & CNA_WANTLOOPBACK) && is_loopback((struct sockaddr *)sa)) {
        char lhost[NI_MAXHOST];
        if (gethostname(lhost, NI_MAXHOST)==0) {
            cp = strchr(lhost, '.');
            if (flags & NI_NOFQDN) {
                if (cp != NULL)
                    *cp = '\0';
                strcpy(namebuf, lhost);
                match = 1;
            } else if (cp != NULL) {
                strcpy(namebuf, lhost);
                match = 1;
            } else {
                char ldomain[NI_MAXHOST];
                if (Cdomainname(ldomain,sizeof(ldomain)) == 0) {
                    if (strlen(lhost)+1+strlen(ldomain) < NI_MAXHOST) {
                        strcat(lhost,".");
                        strcat(lhost,ldomain);
                        strcpy(namebuf, lhost);
                        match = 1;
                    }
                } else {
                    /* no domainname available */
                    strcpy(namebuf, lhost);
                    match = 1;
                }
            }
        }
    }

    /* if it may be needed lookup the full name */
    if (!match) {
       gaierrno = Cgetnameinfo((struct sockaddr *)sa, salen, namebuf, NI_MAXHOST, NULL, 0, flags|NI_NAMEREQD);
    } else {
       gaierrno = 0;
    }
    if (gaierrno != 0) {
        if (!(flags & NI_NAMEREQD)) {
            /* no hostname available, but one was not required so just return the IP */
            if (needalloc) free(namebuf);
            return (ipbuf);
        }
        /* return nothing */
        if (numeric_out) *numeric_out = NULL;
        if (needalloc) { free(ipbuf); free(namebuf); }
        if (gaierrno == EAI_NONAME)
            serrno = SENOSHOST;
        else
            serrno = (gaierrno == EAI_SYSTEM) ? 0 : SEINTERNAL;
        return (NULL);
    }

    if (!(cflags & CNA_FWDLOOKUP)) {
        match = 1;
    }

   /* unless disabled, check that the hostname resolves
    * to at least one address which matches the original
    * address by doing a forward lookup the hostname
    */
    aitop = NULL;
    if (!match) {
        /* if it is a remote address add period suffix to stop local name resolution */
        strcpy(tmpbuf, namebuf);
        if (isremote_sa((struct sockaddr *)sa, tmpbuf)) {
          strcat(tmpbuf, ".");
        }
        memset(&hints, '\0', sizeof(hints));
        hints.ai_family = PF_UNSPEC;
        if (Cgetaddrinfo(tmpbuf, NULL, &hints, &aitop)) {
            aitop = NULL;
        }
    }

    if (aitop) {
        struct in_addr target4;
        struct in6_addr targets6[2];
        int target4_valid = 0;
        int targets6_valid = 0;

        if (((struct sockaddr *)sa)->sa_family == AF_INET) {
            memcpy(&target4, &((struct sockaddr_in *)sa)->sin_addr, sizeof(target4));
            target4_valid++;
            memset(&targets6[0], '\0', sizeof(struct in6_addr));
            memcpy(&targets6[0].s6_addr[12], &((struct sockaddr_in *)sa)->sin_addr, 4);
            memcpy(&targets6[1], &targets6[0], sizeof(struct in6_addr));
            targets6[1].s6_addr[10] = 0xff;
            targets6[1].s6_addr[11] = 0xff;
            targets6_valid = 2;
        } else if (((struct sockaddr *)sa)->sa_family == AF_INET6) {
            if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *) sa)->sin6_addr) ||
                    IN6_IS_ADDR_V4COMPAT(&((struct sockaddr_in6 *) sa)->sin6_addr)) {
                memcpy(&target4, &((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[12], sizeof(target4));
                target4_valid++;
            }
            memcpy(&targets6[0], &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr));
            targets6_valid = 1;
        }
                    
        for(ai=aitop;ai && !match;ai=ai->ai_next) {
            switch(ai->ai_family) {
                case AF_INET: {
                    struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
                    if (target4_valid && sin->sin_addr.s_addr == target4.s_addr) {
                        match = 1;
                    }
                    break; }
                case AF_INET6: {
                    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
                    if (targets6_valid == 2 &&
                          (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,&targets6[0]) || IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,&targets6[1]))) {
                        match = 1;
                    } else if (targets6_valid == 1 && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,&targets6[0])) {
                        match = 1;
                    }
                    break; }
            }
        }
        freeaddrinfo(aitop);
    }

    /* if we have a good name return it */
    if (match) {
        if (name_out)
            *name_out = namebuf;
        return (namebuf);
    }

    /* apparently the hostname doesn't match. */
    if (flags & NI_NAMEREQD) {
        /* return nothing */
        if (numeric_out) *numeric_out = NULL;
        if (needalloc) { free(ipbuf); free(namebuf); }
        serrno = SENOSHOST;
        return (NULL);
    }

    /* return only the IP number representation */
    if (needalloc) free(namebuf);
    return (ipbuf);
}

CONST char DLL_DECL *Cgai_strerror(errcode)
int errcode;
{
    static int key = -1;
    char *namebuf;
    const char *p;

    Cglobals_get(&key,(void **)&namebuf,80);
    if (namebuf == NULL) {
      return ("problem with error string generation");
    }
    Cmutex_lock(&key, -1);
    p = gai_strerror(errcode);
    if (p && strlen(p)<80) {
      strcpy(namebuf, p);
    } else {
      strcpy(namebuf, "unknown error");
    }
    Cmutex_unlock(&key);

    return (p);
}

static int is_loopback(struct sockaddr *sa) {
  int result = 0; 

  switch (sa->sa_family) {
    case AF_INET:  
      if (*(unsigned char *) &((struct sockaddr_in *)
          sa)->sin_addr.s_addr == 127)
      {
          result = 1;
      }
      break;

    case AF_INET6:
      if(IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *) sa)->sin6_addr) ||
          (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *) sa)->sin6_addr) &&
           *(uint8_t *) &((struct sockaddr_in6 *)
               sa)->sin6_addr.s6_addr[12] == 127))
      {
          result = 1;  
      }
      break;
  }

  return result;   
}

Generated by  Doxygen 1.6.0   Back to index