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;
}