int transport_setup()

in spamc/libspamc.c [2053:2303]


int transport_setup(struct transport *tp, int flags)
{
#ifdef SPAMC_HAS_ADDRINFO
    struct addrinfo hints, *res, *addrp;
    char port[6];
    int origerr;
#else
    struct hostent *hp;
    char **addrp;
#endif
    char *hostlist, *hostname;
    int errbits;

#ifdef _WIN32
    /* Start Winsock up */
    WSADATA wsaData;
    int nCode;
    if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) {
        printf("WSAStartup() returned error code %d\n", nCode);
        return EX_OSERR;
    }

#endif

    assert(tp != NULL);
    tp->flags = flags;

#ifdef SPAMC_HAS_ADDRINFO
    snprintf(port, 6, "%d", tp->port);

    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = 0;
    hints.ai_socktype = SOCK_STREAM;

    if (       (flags & SPAMC_USE_INET4) && !(flags & SPAMC_USE_INET6)) {
      hints.ai_family = PF_INET;
#ifdef PF_INET6
    } else if ((flags & SPAMC_USE_INET6) && !(flags & SPAMC_USE_INET4)) {
      hints.ai_family = PF_INET6;
#endif
    } else {
      hints.ai_family = PF_UNSPEC;
    }
#endif

    switch (tp->type) {
#ifndef _WIN32
    case TRANSPORT_UNIX:
        assert(tp->socketpath != 0);
        return EX_OK;
#endif
    case TRANSPORT_LOCALHOST:
#ifdef SPAMC_HAS_ADDRINFO
        /* getaddrinfo(NULL) will look up the loopback address.
         * See also bug 5057,  ::1 will be tried before 127.0.0.1
         * unless overridden (through hints) by a command line option -4
         */
        if ((origerr = getaddrinfo(NULL, port, &hints, &res)) != 0) {
            libspamc_log(flags, LOG_ERR, 
                  "getaddrinfo for a loopback address failed: %s",
                  gai_strerror(origerr));
            return EX_OSERR;
        }
        tp->hosts[0] = res;
#else
        tp->hosts[0].s_addr = inet_addr("127.0.0.1");
#endif
        tp->nhosts = 1;
        return EX_OK;

    case TRANSPORT_TCP:
        if ((hostlist = strdup(tp->hostname)) == NULL)
            return EX_OSERR;

        /* We want to return the least permanent error, in this bitmask we
         * record the errors seen with:
         *  0: no error
         *  1: EX_TEMPFAIL
         *  2: EX_NOHOST
         * EX_OSERR will return immediately.
         * Bits aren't reset so a check against nhosts is needed to determine
         * if something went wrong.
         */
        errbits = 0;
        tp->nhosts = 0;
        /* Start with char offset in front of the string because we'll add 
         * one in the loop
         */
        hostname = hostlist - 1;
        do {
            char *hostend;
            
            hostname += 1;
            hostend = strchr(hostname, ',');
            if (hostend != NULL) {
                *hostend = '\0';
            }
#ifdef SPAMC_HAS_ADDRINFO            
            if ((origerr = getaddrinfo(hostname, port, &hints, &res))) {
                libspamc_log(flags, LOG_DEBUG, 
                      "getaddrinfo(%s) failed: %s",
                      hostname, gai_strerror(origerr));
                switch (origerr) { 
                case EAI_AGAIN:
                    errbits |= 1;
                    break;
                case EAI_FAMILY: /*address family not supported*/
                case EAI_SOCKTYPE: /*socket type not supported*/
                case EAI_BADFLAGS: /*ai_flags is invalid*/
                case EAI_NONAME: /*node or service unknown*/
                case EAI_SERVICE: /*service not available*/
/* work around Cygwin IPv6 patch - err codes not defined in Windows aren't in patch */
#ifdef HAVE_EAI_ADDRFAMILY
                case EAI_ADDRFAMILY: /*no addresses in requested family*/
#endif
#ifdef HAVE_EAI_SYSTEM
                case EAI_SYSTEM: /*system error, check errno*/
#endif
#ifdef HAVE_EAI_NODATA
                case EAI_NODATA: /*address exists, but no data*/
#endif
                case EAI_MEMORY: /*out of memory*/
                case EAI_FAIL: /*name server returned permanent error*/
                    errbits |= 2;
                    break;
                default:
                    /* should not happen, all errors are checked above */
                    free(hostlist);
                    return EX_OSERR;
                }
                goto nexthost; /* try next host in list */
            }
#else
            if ((hp = gethostbyname(hostname)) == NULL) {
                int origerr = h_errno; /* take a copy before syslog() */
                libspamc_log(flags, LOG_DEBUG, "gethostbyname(%s) failed: h_errno=%d",
                    hostname, origerr);
                switch (origerr) {
                case TRY_AGAIN:
                    errbits |= 1;
                    break;
                case HOST_NOT_FOUND:
                case NO_ADDRESS:
                case NO_RECOVERY:
                    errbits |= 2;
                    break;
                default:
                    /* should not happen, all errors are checked above */
                    free(hostlist);
                    return EX_OSERR;
                }
                goto nexthost; /* try next host in list */
            }
#endif
            
            /* If we have no hosts at all */
#ifdef SPAMC_HAS_ADDRINFO
            if(res == NULL)
#else
            if (hp->h_addr_list[0] == NULL
             || hp->h_length != sizeof tp->hosts[0]
             || hp->h_addrtype != AF_INET)
                /* no hosts/bad size/wrong family */
#endif
            {
                errbits |= 1;
                goto nexthost; /* try next host in list */
            }

            /* Copy all the IP addresses into our private structure.
             * This gets them out of the resolver's static area and
             * means we won't ever walk all over the list with other
             * calls.
             */
#ifdef SPAMC_HAS_ADDRINFO
            if(tp->nhosts == TRANSPORT_MAX_HOSTS) {
               libspamc_log(flags, LOG_NOTICE, 
                     "hit limit of %d hosts, ignoring remainder",
                     TRANSPORT_MAX_HOSTS);
               break;
            }

            /* treat all A or AAAA records of each host as one entry */
            tp->hosts[tp->nhosts++] = res;

            /* alternatively, treat multiple A or AAAA records
               of one host as individual entries */
/*          for (addrp = res; addrp != NULL; ) {
 *              tp->hosts[tp->nhosts] = addrp;
 *              addrp = addrp->ai_next;     /-* before NULLing ai_next *-/
 *              tp->hosts[tp->nhosts]->ai_next = NULL;
 *              tp->nhosts++;
 *          }
 */

#else
            for (addrp = hp->h_addr_list; *addrp; addrp++) {
                if (tp->nhosts == TRANSPORT_MAX_HOSTS) {
                    libspamc_log(flags, LOG_NOTICE, "hit limit of %d hosts, ignoring remainder",
                        TRANSPORT_MAX_HOSTS);
                    break;
                }
                memcpy(&tp->hosts[tp->nhosts], *addrp, hp->h_length);
                tp->nhosts++;
            }
#endif            
nexthost:
            hostname = hostend;
        } while (hostname != NULL);
        free(hostlist);
        
        if (tp->nhosts == 0) {
            if (errbits & 1) {
                libspamc_log(flags, LOG_ERR, "could not resolve any hosts (%s): a temporary error occurred",
                    tp->hostname); 
                return EX_TEMPFAIL;
            }
            else {
                libspamc_log(flags, LOG_ERR, "could not resolve any hosts (%s): no such host",
                    tp->hostname); 
                return EX_NOHOST;
            }
        }
        
        /* QUASI-LOAD-BALANCING
         *
         * If the user wants to do quasi load balancing, "rotate"
         * the list by a random amount based on the current time.
         * This may later be truncated to a single item. This is
         * meaningful only if we have more than one host.
	 */

        if ((flags & SPAMC_RANDOMIZE_HOSTS) && tp->nhosts > 1) {
            _randomize_hosts(tp);
        }

        /* If the user wants no fallback, simply truncate the host
         * list to just one - this pretends that this is the extent
         * of our connection list - then it's not a special case.
         */
        if (!(flags & SPAMC_SAFE_FALLBACK) && tp->nhosts > 1) {
            /* truncating list */
            tp->nhosts = 1;
        }
        
        return EX_OK;
    }
    
    /* oops, unknown transport type */
    return EX_OSERR;
}