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

remote.c

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

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <ws_errmsg.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/param.h>
#endif  /* WIN32 */
#include <ctype.h>
#include <string.h>
#if defined(SOLARIS)
#include <sys/sockio.h>
#endif
#if defined(_AIX)
#include <sys/time.h>                   /* needed for if.h              */
#endif /* AIX */
#if !defined(_WIN32)
#include <net/if.h>                     /* Network interfaces           */
#include <sys/ioctl.h>                  /* ioctl() definitions          */
#endif
#include <Castor_limits.h>
#include <log.h> 
#include <net.h>
#include <serrno.h>
#include <Cnetdb.h>

#if defined(linux) || defined (__APPLE__)
#include <ifaddrs.h>
#endif

#ifndef _WIN32
#if defined(_REENTRANT) || defined(_THREAD_SAFE)
#define strtok(X,Y) strtok_r(X,Y,&last)
#endif /* _REENTRANT || _THREAD_SAFE */
#endif /* _WIN32 */

#ifndef LOCALHOSTSFILE
#if defined(_WIN32)
#define LOCALHOSTSFILE "%SystemRoot%\\system32\\drivers\\etc\\shift.localhosts"
#else
#define LOCALHOSTSFILE "/etc/shift.localhosts"
#endif  /* WIN32 */
#endif
#ifndef RTHOSTSFILE
#if defined(_WIN32)
#define RTHOSTSFILE "%SystemRoot%\\system32\\drivers\\etc\\shift.rthosts"
#else
#define RTHOSTSFILE "/etc/shift.rthosts"
#endif  /* WIN32 */
#endif

#define IN6_FILTER(x) (IN6_IS_ADDR_LOOPBACK(x) || \
                       IN6_IS_ADDR_LINKLOCAL(x) || \
                       IN6_IS_ADDR_SITELOCAL(x) || \
                       IN6_IS_ADDR_MULTICAST(x))

enum scan_sources {
    SOURCE_GETIFADDRS,
    SOURCE_SIOCGLIFCONF,
    SOURCE_SIOCGIFCONF
};

typedef struct if_scan_result_s {
   struct sockaddr *sa;
   int ipv6_prefixlen;
   enum scan_sources scan_source;
   const char *if_name;
} if_scan_result_t;

struct isremote_cb_data {
  int number_of_ipv6_compared;
  int isremote;
  int ipv4_avail;
  struct in_addr from_host;
  struct in6_addr from6_host;
};

struct getifnam_cb_data {
  int is_ipv4;
  struct in_addr from_host;
  struct in6_addr from6_host;
  char *if_name;
  size_t if_namelen;
};

typedef int(*scan_callback_t)(void*, if_scan_result_t*);

extern char *getconfent();
static int DLL_DECL match_ipv6_string _PROTO((const struct in6_addr *, const char *));
static int DLL_DECL match_ipv6 _PROTO((const struct in6_addr *, const struct in6_addr *, int));
static int DLL_DECL netmask_to_prefixlen _PROTO((const struct in6_addr *));
static void *next_xifr _PROTO((void *, const struct sockaddr *, size_t));
static int DLL_DECL scan_interfaces _PROTO((void *, scan_callback_t));
static int DLL_DECL isremote_scan_cb _PROTO((void *, if_scan_result_t *));
static int DLL_DECL getifnam_scan_cb _PROTO((void *, if_scan_result_t *));

static int scan_interfaces(void *user_cbdata, scan_callback_t callback) {
    if_scan_result_t result;
    int cb_ret;

    /* if getifaddrs is available try scanning with that
     * (usually both AF_INET and AF_INET6)
     */
#if defined(linux) || defined(__APPLE__)
    {
        struct ifaddrs *ifatop, *ifa;
        int plen;

        log(LOG_DEBUG , "scan_interfaces(): Using getifaddrs()\n");
        result.scan_source = SOURCE_GETIFADDRS;

        if (getifaddrs(&ifatop)<0) {
            log(LOG_DEBUG , "scan_interfaces(): getifaddrs() return error %s\n", strerror(errno));
            ifatop = NULL;
        }

        for(ifa = ifatop; ifa != NULL; ifa = ifa->ifa_next) {
            if (ifa->ifa_addr == NULL)
                continue;
            result.sa = (struct sockaddr*)ifa->ifa_addr;
            result.if_name = ifa->ifa_name;
            if (result.sa->sa_family == AF_INET6) {
                plen = 64;
                if (ifa->ifa_netmask != NULL && ((struct sockaddr*)ifa->ifa_netmask)->sa_family == AF_INET6) {
                    struct in6_addr *test6_netmask = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
                    plen = netmask_to_prefixlen(test6_netmask);
                }
                result.ipv6_prefixlen = plen;
            }
            cb_ret = (*callback)(user_cbdata, &result);
            if (cb_ret) {
                freeifaddrs(ifatop);
                return (cb_ret);
            }
        }
        if (ifatop != NULL)
            freeifaddrs(ifatop);
    }
#endif

    /* if available look for AF_INET6 interface details with SIOCGLIFCONF and SIOCGLIFADDR
     */
#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR)
    {
        struct lifconf lifc;
        struct lifreq *lifr, lifr2;
        int plen,s_s;
        char *endp;
        char buf[BUFSIZ];            /* A buffer                     */

        memset(&lifc, '\0', sizeof(lifc));
        lifc.lifc_len = sizeof(buf);
        lifc.lifc_buf = buf;
        lifr = lifc.lifc_req;
        lifc.lifc_family = AF_INET6;

        log(LOG_DEBUG , "scan_interfaces(): Using ioctl SIOCGLIFCONF\n");
        result.scan_source = SOURCE_SIOCGLIFCONF;

        s_s = socket(AF_INET6, SOCK_DGRAM, 0);
        if (s_s<0 || ioctl(s_s, SIOCGLIFCONF, (char *)&lifc)<0) {
            log(LOG_DEBUG , "scan_interfaces(): error creating socket or making SIOCGLIFCONF ioctl call: %s", strerror(errno));
            lifr = NULL;
        }

        endp = (char *) lifr + lifc.lifc_len;
        for( ; lifr && (((char *) &lifr->lifr_addr) < endp) ;
               lifr = (struct lifreq *)next_xifr(lifr, (struct sockaddr *)&lifr->lifr_addr, sizeof(struct lifreq)) ) {
            result.sa = (struct sockaddr *)&lifr->lifr_addr;
            result.if_name = lifr->lifr_name;
            if (result.sa->sa_family == AF_INET6) {
                memset(&lifr2, '\0', sizeof(struct lifreq));
                memcpy(lifr2.lifr_name, lifr->lifr_name, sizeof(lifr2.lifr_name));
                memcpy(&lifr2.lifr_addr, &lifr->lifr_addr, sizeof(lifr2.lifr_addr));
                plen = 64;
                if (ioctl(s_s, SIOCGLIFADDR, &lifr2)>=0) {
                    if (((struct sockaddr *)&lifr2.lifr_addr)->sa_family == AF_INET6 && lifr2.lifr_addrlen>0) {
                        plen = lifr2.lifr_addrlen;
                    }
                }
                result.ipv6_prefixlen = plen;
            }
            cb_ret = (*callback)(user_cbdata, &result);
            if (cb_ret) {
                close(s_s);
                return (cb_ret);
            }
        }
        if (s_s>=0)
            close(s_s);
    }
#endif

    /* scanning with SIO_GET_INTERFACE_LIST (windows) or SIOCGIFCONF (other)
     * (usually only AF_INET)
     */
    {
        int s_s;
        char buf[BUFSIZ];            /* A buffer                     */
#if defined(_WIN32)
        struct  sockaddr_in addr;
        INTERFACE_INFO *iinfo;
        char ibuf[BUFSIZ];
        int buflen;
        int rcode;
#else
        struct  ifconf  ifc;     /* ifconf structure      */
        struct  ifreq   *ifr;    /* Pointer on ifreq structure */
        struct ifreq ifr2;
        char *endp;
#endif

        log(LOG_DEBUG , "scan_interfaces(): Using ioctl SIOCGIFCONF\n");
        result.scan_source = SOURCE_SIOCGIFCONF;

        if( (s_s = socket(AF_INET, SOCK_DGRAM, 0)) != SOCKET_ERROR )  {
#if defined(_WIN32)
            rcode = WSAIoctl(s_s, SIO_GET_INTERFACE_LIST, ibuf, BUFSIZ, buf, BUFSIZ, &buflen, NULL, NULL);
            if( rcode == SOCKET_ERROR )  {
                log(LOG_ERR, "scan_interfaces(): WSAIoctl(SIO_GET_INTERFACE_LIST): %s\n", geterr());
            } else {
                for( iinfo = (INTERFACE_INFO*)buf; iinfo < (INTERFACE_INFO*)(buf + buflen); iinfo++ ) {
                    result.sa = (struct sockaddr *)&iinfo->iiAddress;
                    result.if_name = (char *)NULL;
                    if (result.sa->sa_family == AF_INET6) {
                        struct in6_addr *test6_netmask = &((struct sockaddr_in6 *)&iinfo->iiNetmask)->sin6_addr;
                        result.ipv6_prefixlen = netmask_to_prefixlen(test6_netmask);
                    }
                    cb_ret = (*callback)(user_cbdata, &result);
                    if (cb_ret) {
                        closesock(s_s);
                        return (cb_ret);
                    }
                }
            }
            closesock(s_s);
#else
            ifc.ifc_len = sizeof(buf);
            ifc.ifc_buf = buf;
            ifr = ifc.ifc_req;

            if (ioctl(s_s, SIOCGIFCONF, (char *)&ifc) < 0) {
                log(LOG_ERR, "scan_interfaces(): ioctl(SIOCGIFCONF): %s\n",strerror(errno));
            } else {
                endp = (char *) ifr + ifc.ifc_len;
                while (((char *) &ifr->ifr_addr) < endp) {
                    result.sa = (struct sockaddr *)&ifr->ifr_addr;
                    result.if_name = ifr->ifr_name;
                    if (result.sa->sa_family == AF_INET6) {
                        int plen = 64;
#if defined(SIOCGIFNETMASK)
                        memset(&ifr2, '\0', sizeof(struct ifreq));
                        memcpy(ifr2.ifr_name, ifr->ifr_name, sizeof(ifr2.ifr_name));
                        memcpy(&ifr2.ifr_addr, &ifr->ifr_addr, sizeof(ifr2.ifr_addr));
                        if (ioctl(s_s, SIOCGIFNETMASK, (char *)&ifr2) >= 0) {
                            if (((struct sockaddr *)&ifr2.ifr_addr)->sa_family == AF_INET6) {
                                struct in6_addr *nm = &((struct sockaddr_in6 *)&ifr2.ifr_addr)->sin6_addr;
                                plen = netmask_to_prefixlen(nm);
                            }
                        }
#endif
                        result.ipv6_prefixlen = plen;
                    }

                    cb_ret = (*callback)(user_cbdata, &result);
                    if (cb_ret) {
                        close(s_s);
                        return (cb_ret);
                    }
                    ifr = (struct ifreq *)next_xifr(ifr, (struct sockaddr *)&ifr->ifr_addr, sizeof(struct ifreq));
                }
            }
            close(s_s);
#endif /* _WIN32 */
        }
    }

    return(0);
}

static int isremote_scan_cb(void *vp, if_scan_result_t *result) {
    struct isremote_cb_data *data = vp;
    struct in6_addr *test6_addr;

    switch(result->scan_source) {
        case SOURCE_GETIFADDRS:
        case SOURCE_SIOCGLIFCONF:
            if (data->ipv4_avail || result->sa->sa_family != AF_INET6) return(0);
            test6_addr = &((struct sockaddr_in6 *)result->sa)->sin6_addr;
            if (IN6_FILTER(test6_addr)) return(0);
            data->number_of_ipv6_compared++;
            if (match_ipv6(test6_addr,&data->from6_host,result->ipv6_prefixlen)) {
                log(LOG_DEBUG,"isremote_scan_cb(): IPv6 peer is at our site, according to interface list\n");
                data->isremote = 0;
                return(1);
            }
            return(0);
            break;

        case SOURCE_SIOCGIFCONF:
            if (data->ipv4_avail && result->sa->sa_family == AF_INET) {
                struct sockaddr_in *sin = (struct sockaddr_in *)result->sa;
                log(LOG_DEBUG , "isremote_scan_cb: Comparing %d and %d \n",inet_netof(sin->sin_addr),inet_netof(data->from_host));
                if ( inet_netof(sin->sin_addr) == inet_netof(data->from_host) ) {
                    data->isremote = 0;
                    return(1);
                }
            } else if (result->sa->sa_family == AF_INET6) {
                test6_addr = &((struct sockaddr_in6 *)result->sa)->sin6_addr;
                if (IN6_FILTER(test6_addr)) return(0);
                data->number_of_ipv6_compared++;
                if (match_ipv6(test6_addr,&data->from6_host,result->ipv6_prefixlen)) {
                    log(LOG_DEBUG,"isremote_scan_cb(): IPv6 peer is at our site, according to interface list\n");
                    data->isremote = 0;
                    return(1);
                }
            }
            return(0);
            break;
    }
    return(1);
}

static int getifnam_scan_cb(void *vp, if_scan_result_t *result) {
    struct getifnam_cb_data *data = vp;

    if (result->if_name == NULL)
        return(0);

    if (data->is_ipv4 && result->sa->sa_family == AF_INET) {
        if (!memcmp(&data->from_host, &((struct sockaddr_in *)result->sa)->sin_addr, sizeof(struct in_addr))) {
            strncpy(data->if_name, result->if_name, data->if_namelen);
            data->if_name[data->if_namelen-1] = '\0';
            return(1);
        }
    } else if (!data->is_ipv4 && result->sa->sa_family == AF_INET6) {
        if (IN6_ARE_ADDR_EQUAL(&data->from6_host,&((struct sockaddr_in6 *)result->sa)->sin6_addr)) {
            strncpy(data->if_name, result->if_name, data->if_namelen);
            data->if_name[data->if_namelen-1] = '\0';
            return(1);
        }
    }

    return(0);
}

/* currently used in getifnam.c  - not prototyped in any header files */
char *getifnam_sa(const struct sockaddr *sa, char *ifname, size_t ifnamelen) {
    int scan_ret;
    struct getifnam_cb_data data;
    int is_ipv4 = 0;
    struct in_addr from_host;
    struct in6_addr from6_host;

    if (ifname == NULL || ifnamelen<=0)
        return(NULL);

    memset(&data, '\0', sizeof(data));
    memset(&from_host, '\0', sizeof(from_host));
    memset(&from6_host, '\0', sizeof(from6_host));

    if (sa->sa_family == AF_INET) {
        is_ipv4 = 1;
        memcpy(&from_host, &((struct sockaddr_in *)sa)->sin_addr, sizeof(from_host));
    } else if (sa->sa_family == AF_INET6) {
        memcpy(&from6_host, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(from6_host));
        if (IN6_IS_ADDR_V4MAPPED(&from6_host) || IN6_IS_ADDR_V4COMPAT(&from6_host)) {
            memcpy(&from_host,&from6_host.s6_addr[12],sizeof(from_host));
            is_ipv4 = 1;
        }
    } else {
        return(NULL);
    }

    data.is_ipv4 = is_ipv4;
    data.if_name = ifname;
    data.if_namelen = ifnamelen;
    memcpy(&data.from_host, &from_host, sizeof(data.from_host));
    memcpy(&data.from6_host, &from6_host, sizeof(data.from6_host));

    scan_ret = scan_interfaces(&data, &getifnam_scan_cb);

    if (scan_ret) {
      return(ifname);
    }

    return(NULL);
}


/*
 * isremote_sa(): returns 0 if requestor is in site
 *                        1 if requestor is out of site
 *          -1 in case of an error
 */
 /* currently used in various places  - not prototyped in any header files */
int DLL_DECL isremote_sa(from_saddr, host_name)
struct sockaddr *from_saddr;
char *host_name ;
{
    char *p ;
    char local[CA_MAXHOSTNAMELEN+1];
    char ent[25] ;
#if defined(_REENTRANT) || defined(_THREAD_SAFE)
    char *last = NULL;
#endif /* _REENTRANT || _THREAD_SAFE */

#if defined(_WIN32) || defined(__STDC__)
    char lhfile[CA_MAXPATHLEN+1] =  LOCALHOSTSFILE;
    char rthfile[CA_MAXPATHLEN+1] = RTHOSTSFILE;
#else
    char *lhfile = LOCALHOSTSFILE;
    char *rthfile = RTHOSTSFILE;
#endif
    struct in6_addr from6_host;
    struct in_addr from_host;
    int save_serrno, ipv4_avail;
    struct isremote_cb_data data;

    save_serrno = serrno;
    if ( (p=getconfent("SIMULATION","REMOTE",1))!=NULL &&
        (p=(char *)strtok(p," \t"))!=NULL && !strcmp(p,"YES")) {
        log(LOG_DEBUG,"isremote(): Client simulates remote behaviour\n");
        return 1 ;
    }
    if ( (p=getconfent("ISREMOTE","CALLS",1))!=NULL &&
        (p=(char *)strtok(p," \t") )!=NULL && !strcmp(p,"NO") ) {
        log(LOG_DEBUG,"isremote(): Any connection assumed from local site\n");
        return 0 ;
    }
    serrno = save_serrno;

    ipv4_avail = 0;
    gethostname(local, CA_MAXHOSTNAMELEN+1);
    if (from_saddr->sa_family == AF_INET) {
     memcpy(&from_host,&((struct sockaddr_in *)from_saddr)->sin_addr,sizeof(from_host));
     memset(&from6_host, '\0', sizeof(from6_host));
     memcpy(&from6_host.s6_addr[12], &from_host, 4);
     from6_host.s6_addr[10] = 0xff;
     from6_host.s6_addr[11] = 0xff;
     ipv4_avail = 1;
    } else if (from_saddr->sa_family == AF_INET6) {
        memcpy(&from6_host,&((struct sockaddr_in6 *)from_saddr)->sin6_addr,sizeof(from6_host));

        if (IN6_IS_ADDR_V4MAPPED(&from6_host) || IN6_IS_ADDR_V4COMPAT(&from6_host)) {
            memcpy(&from_host,&from6_host.s6_addr[12],sizeof(from_host));
            ipv4_avail = 1;
        }
    } else {
        log(LOG_ERR, "isremote(): Unhandled address family %d\n", from_saddr->sa_family);
        return -1;
    }

    if ( host_name != NULL ) {
        FILE *fs;
        char *cp ;
        char s[CA_MAXHOSTNAMELEN+1];
#if defined(_WIN32)
        char path[256];
#endif   
        /*
         * Is the hostname declared as a "remote" site host ?
         */
#if defined(_WIN32)
        strcpy(path, rthfile);
        if( (strncmp(path, "%SystemRoot%\\", 13) == 0) && ((p = getenv ("SystemRoot")) != NULL) ) {
            sprintf (rthfile, "%s\\%s", p, strchr (path, '\\'));
        }
#endif
        log(LOG_DEBUG,"isremote(): searching <%s> in %s\n",host_name, rthfile);
        if ( (fs = fopen( rthfile, "r")) != NULL ) {
            while ( fgets(s,CA_MAXHOSTNAMELEN+1,fs) != NULL ) {
                if ( (cp= strtok(s," \n\t"))!=NULL ) {
                    if ( !isdigit(cp[0]) && (cp[0]!='#') &&  !strcmp(cp,host_name) ) {
                        log(LOG_DEBUG,"isremote(): %s is in list of external hosts\n",cp);
                        fclose(fs);
                        return 1;
                    }
                    if ( ipv4_avail && isdigit(cp[0]) && strchr(cp, ':') == NULL) {
                        strcpy(ent,cp) ;
                        if ( strtok(cp,".") ==  NULL ||
                            strtok(NULL,".") == NULL )
                            log(LOG_DEBUG,"%s ignored: IP specification too short\n", ent);
                        else {
                            if ( !strncmp( ent, inet_ntoa( from_host ), strlen(ent))) {
                                log(LOG_DEBUG,"Entry %s matches to %s\n",ent,inet_ntoa(from_host));
                                log(LOG_INFO,"isremote(): %s is classified as remote\n",host_name);
                                fclose(fs) ;
                                return 1 ;
                            }
                        }
                    } else if (match_ipv6_string(&from6_host, cp)) {
                        log(LOG_DEBUG,"Entry %s matches in remote host list\n",cp);
                        log(LOG_INFO,"isremote(): %s is classified as remote\n",host_name);
                        fclose(fs);
                        return 1;
                    }
                }
            }
            fclose(fs);
        }


        /*
         * Is the hostname declared local ?
         */
#if defined(_WIN32)
        strcpy(path, lhfile);
        if( (strncmp(path, "%SystemRoot%\\", 13) == 0) && ((p = getenv ("SystemRoot")) != NULL) ) {
            sprintf (lhfile, "%s\\%s", p, strchr (path, '\\'));
        }
#endif
        log(LOG_DEBUG,"isremote(): searching <%s> in %s\n",host_name, lhfile);
        if ( (fs = fopen( lhfile, "r")) != NULL ) {
            while ( fgets(s,CA_MAXHOSTNAMELEN+1,fs) != NULL ) {
                if ( (cp= strtok(s," \n\t")) != NULL ) {
                    if ( !isdigit(cp[0]) && (cp[0]!='#') &&  !strcmp(cp,host_name) ) {
                        log(LOG_DEBUG,"isremote(): %s is in list of local hosts\n",cp);
                        fclose(fs);
                        return 0;
                    }
                    if ( ipv4_avail && isdigit(cp[0]) && strchr(cp, ':') == NULL) {
                        strcpy(ent,cp) ;
                        if ( strtok(cp,".") ==  NULL || 
                            strtok(NULL,".") == NULL )
                            log(LOG_DEBUG,"%s ignored: IP specification too short \n", ent);
                        else {
                            if ( !strncmp( ent, inet_ntoa( from_host ), strlen(ent) )) {
                                log(LOG_DEBUG,"Entry %s matches to %s\n",ent,inet_ntoa(from_host));
                                log(LOG_DEBUG,"isremote(): %s is classified as local\n",host_name);
                                fclose (fs);
                                return 0 ;
                            }
                        }
                    } else if (match_ipv6_string(&from6_host, cp)) {
                        log(LOG_DEBUG,"Entry %s matches in local host list\n",cp);
                        log(LOG_DEBUG,"isremote(): %s is classified as local\n",host_name);
                        fclose(fs);
                        return 0;
                    }
                }
            }
            fclose(fs);
        }
    } /* if ( host_name != NULL ) */

    if (ipv4_avail) {
        unsigned int netw = inet_netof(from_host);
        log(LOG_DEBUG, "isremote(): Client host being tested is %s\n",inet_ntoa( from_host )) ;
        log(LOG_DEBUG, "inet_netof() returned %u\n", netw);
    } else {
        char ip[INET6_ADDRSTRLEN];
        const char *p;
        p = inet_ntop(AF_INET6, &from6_host, ip, INET6_ADDRSTRLEN);
        if (p != NULL)
            log(LOG_DEBUG, "isremote(): Client host being tested is %s\n",p);
    }

    /* if the test address is IPv6 then immediately match special address types
    */
    if (!ipv4_avail) {
        if (IN6_IS_ADDR_LOOPBACK(&from6_host)) {
            log(LOG_DEBUG,"isremote(): Is the IPv6 loopback address\n");
            return 0;
        }

        if (IN6_IS_ADDR_LINKLOCAL(&from6_host)) {
            log(LOG_DEBUG,"isremote(): Is an IPv6 link local address\n");
            return 0;
        }

        if (IN6_IS_ADDR_SITELOCAL(&from6_host)) {
            log(LOG_DEBUG,"isremote(): Is an IPv6 site local address\n");
            return 0;
        }
    }

    /* now scan all configured interfaces, trying to match the test address with
     * the network portion of the interfaces addresses: if there is a match then
     * report the test address is local
     */

    memset(&data, '\0', sizeof(data));
    data.ipv4_avail = ipv4_avail;
    data.isremote = -1;
    memcpy(&data.from_host,&from_host,sizeof(data.from_host));
    memcpy(&data.from6_host,&from6_host,sizeof(data.from6_host));

    (void) scan_interfaces(&data, &isremote_scan_cb);

    if (data.isremote == 0) {
        log(LOG_DEBUG,"isremote(): Is at our site according to interface scan\n");
        return 0;
    }

    /* if we had a IPv6 address to test and yet did not find any IPv6 addresses
     * via the interface scan, then try to lookup an IPv6 address for the
     * local machine name and compare against that with an assumed prefix length of 64
     */
    if (!ipv4_avail && data.number_of_ipv6_compared == 0) {
        struct addrinfo hints, *aitop, *ai;
        int gaierrno;

        log(LOG_DEBUG,"isremote(): Did not find any IPv6 addresses any other way, looking up local host name %s\n", local);

        memset(&hints, '\0', sizeof(hints));
        hints.ai_family = PF_INET6;
        hints.ai_socktype = SOCK_STREAM;
        gaierrno = Cgetaddrinfo(local, NULL, &hints, &aitop);

#ifdef EAI_NODATA
        if (gaierrno == EAI_NONAME || gaierrno == EAI_NODATA) {
#else
        if (gaierrno == EAI_NONAME) {
#endif
            log(LOG_INFO,"isremote(): IPv6 is remote, no IPv6 address found for local machine\n");
            return 1;
        } else if (gaierrno != 0) {
            log(LOG_ERR,"isremote(): Could not Cgetaddrinfo for local hostname %s\n", local);
            return -1;
        }
        
        for(ai=aitop; ai; ai=ai->ai_next) {
            if (match_ipv6(&from6_host, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, 0x40)) {
                freeaddrinfo(aitop);
                log(LOG_DEBUG,"isremote(): IPv6 peer is at our site, according to local hostname lookup\n");
                return 0;
            }
        }
        freeaddrinfo(aitop);
        log(LOG_INFO ,"isremote(): IPv6 peer is at another site\n");
        return 1;
    }

    log(LOG_INFO ,"isremote(): client is in another site\n");
    return 1;
}

/*
 * isremote(): returns 0 if requestor is in site
 *                     1 if requestor is out of site
 *          -1 in case of an error
 */
 /* currently used in various places  - not prototyped in any header files */
int DLL_DECL isremote(from_host, host_name)
struct in_addr *from_host;
char *host_name ;
{
    struct sockaddr_in sin;

    memset(&sin, '\0', sizeof(sin));
    sin.sin_family = AF_INET;
    memcpy(&sin.sin_addr, from_host, sizeof(struct in_addr));
    return isremote_sa(&sin, host_name);
}


int DLL_DECL CDoubleDnsLookup(SOCKET s, char *host) {
    static int key = -1;
    const char *p;

    p = Cgetnetaddress(s, NULL, 0, &key, NULL, NULL, NI_NAMEREQD, CNA_FWDLOOKUP);
    if (!p) {
        serrno = SENOSHOST;
        return(-1);
    }

    strcpy(host, p);
    return(0);
}

int DLL_DECL isadminhost(SOCKET s, char *peerhost) {
    int i, rc;
#if defined(ADMIN_HOSTS)
    char *defined_admin_hosts = ADMIN_HOSTS;
#endif /* ADMIN_HOSTS */
    char *admin_hosts, *admin_host;

    rc = CDoubleDnsLookup(s,peerhost);
    if ( rc == -1 ) return(rc);

    admin_host = admin_hosts = NULL;
    if ( admin_hosts == NULL ) admin_hosts = getenv("ADMIN_HOSTS");
    if ( admin_hosts == NULL ) admin_hosts = getconfent("ADMIN","HOSTS",1);
#if defined(ADMIN_HOSTS)
    if ( admin_hosts == NULL ) admin_hosts = defined_admin_hosts;
#endif /* ADMIN_HOSTS */

    if ( (admin_hosts != NULL) && 
         ((admin_host = strstr(admin_hosts,peerhost)) != NULL) ) {
        i = strlen(peerhost);
        if ( (admin_host[i] == '\0' ||
              admin_host[i] == ' ' ||
              admin_host[i] == '\t' ||
              admin_host[i] == ',' ) &&
             (admin_host == admin_hosts ||
              admin_host[-1] == ' ' ||
              admin_host[-1] == '\t' ||
              admin_host[-1] == ',') ) return(0);
    }
    serrno = SENOTADMIN;
    return(-1);
}

static int match_ipv6_string(const struct in6_addr *fa, const char *straddr) {
    char *p,*dp,line[64];
    struct in6_addr fb;
    int prefixlen=128;

    strncpy(line, straddr, sizeof(line));
    line[sizeof(line)-1] = '\0';

    if (strchr(line,':') == NULL) {
      return 0;
    }

    if ((p=strchr(line,'/')) != NULL) {
      *p++ = '\0';
      prefixlen = strtol(p, &dp, 10);
      if (*dp != '\0') return 0;
    }
    if (prefixlen<0 || prefixlen>128) prefixlen = 128;

    if (inet_pton(AF_INET6, line, &fb) != 1) return 0;
    return match_ipv6(fa,&fb,prefixlen);
}

static int match_ipv6(const struct in6_addr *fa_in, const struct in6_addr *fb_in, int prefixlen) {
    unsigned char m;
    struct in6_addr a6;
    const struct in6_addr *fa, *fb;
    char ip1[INET6_ADDRSTRLEN],ip2[INET6_ADDRSTRLEN];

    if (inet_ntop(AF_INET6, fa_in, ip1, INET6_ADDRSTRLEN) &&
        inet_ntop(AF_INET6, fb_in, ip2, INET6_ADDRSTRLEN)) {
        log(LOG_DEBUG,"match_ipv6(): Comparing %s and %s\n", ip1, ip2);
    }

    fa = fa_in;
    memcpy(&a6, fb_in, sizeof(a6));
    fb = &a6;

    /* always match v4mapped/v4compat combinations */
    if ((IN6_IS_ADDR_V4MAPPED(fa) || IN6_IS_ADDR_V4COMPAT(fa)) &&
        (IN6_IS_ADDR_V4MAPPED(fb) || IN6_IS_ADDR_V4COMPAT(fb))) {
        a6.s6_addr[10] = fa->s6_addr[10];
        a6.s6_addr[11] = fa->s6_addr[11];
    }

    if (prefixlen>=8 && memcmp(fa->s6_addr, fb->s6_addr, prefixlen/8)) return 0;
    m = prefixlen%8;
    if (m==0) return 1;
    m = 8-m;
    m = (1<<m) - 1;
    m = ~m;
    if ((fa->s6_addr[prefixlen/8]&m) != (fb->s6_addr[prefixlen/8]&m)) return 0;
    return 1;
}

static int netmask_to_prefixlen(const struct in6_addr *a) {
  int prefix = 0;

  while(prefix<128 && a->s6_addr[prefix/8] == 0xff) prefix+=8;
  if (prefix<128) {
    unsigned char c = a->s6_addr[prefix/8];
    while(c&0x80) { c<<=1; prefix++; }
  }

  if (prefix == 0) {
      log(LOG_DEBUG , "netmask_to_prefixlen(): Found zero length prefix, using 64 instead\n");
      prefix = 64;
  }

  return (prefix);
}

static void *next_xifr(void *xifr, const struct sockaddr *sa, size_t slen) {
  size_t len;

  if (xifr == NULL || sa == NULL)
      return (NULL);

#if defined(_AIX) || defined(__APPLE__) || defined(__Lynx__) || defined(SIN6_LEN)
  len = sa->sa_len;
  len += (char *)sa - (char *)xifr;
#else
  len = slen;
#endif

  xifr = (void *)((char *) xifr + len);
  return (xifr);
}

Generated by  Doxygen 1.6.0   Back to index