/*
* Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
* This will be free software, but only when it is finished.
* Copyright 1991-2003 by Matti Aarnio -- modifications,
* including MIME things.
*/
#include "smtp.h"
int
getmxrr(SS, host, mx, maxmx, depth, realname, realnamesize, realnamettlp)
SmtpState *SS;
const char *host;
struct mxdata mx[];
int maxmx, depth;
char *realname;
const int realnamesize;
time_t *realnamettlp;
{
HEADER *hp;
msgdata *eom, *cp;
struct mxdata mxtemp;
int qlen, n, i, j, nmx, qdcount, ancount, nscount, arcount, maxpref;
int class;
long ttl;
u_short type;
int saw_cname = 0;
int had_eai_again = 0;
querybuf qbuf, answer;
msgdata buf[8192];
char mxtype[MAXFORWARDERS];
h_errno = 0;
#ifndef TEST
if (maxmx > 2) {
notary_setwtt (NULL);
notary_setwttip(NULL);
}
#endif
if (depth == 0) {
SS->mxcount = 0;
sprintf(SS->remotemsg, "-- Lookup of MX/A for '%.200s'", host);
}
if (depth > 3) {
sprintf(SS->remotemsg,"smtp; 500 (DNS: Recursive CNAME on '%.200s')",host);
time(&endtime);
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"5.4.3 (Recursive DNS CNAME)",SS->remotemsg);
#endif
fprintf(stderr, "%s\n", SS->remotemsg);
return EX_NOHOST;
}
qlen = res_mkquery(QUERY, host, C_IN, T_MX, NULL, 0, NULL,
(void*)&qbuf, sizeof qbuf);
if (qlen < 0) {
fprintf(stderr, "res_mkquery failed\n");
sprintf(SS->remotemsg,
"smtp; 466 (Internal: res_mkquery failed on host: %.200s)",host);
if (SS->verboselog)
fprintf(SS->verboselog," %s\n", SS->remotemsg);
time(&endtime);
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"5.4.3 (DNS-failure)",SS->remotemsg);
#endif
return EX_SOFTWARE;
}
n = res_send((void*)&qbuf, qlen, (void*)&answer, sizeof answer);
if (n < 0) {
sprintf(SS->remotemsg,
"smtp; 466 (No DNS response for host: %.200s; h_errno=%d)",
host, h_errno);
if (SS->verboselog)
fprintf(SS->verboselog," %s\n", SS->remotemsg);
time(&endtime);
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"5.4.3 (DNS-failure)",SS->remotemsg);
#endif
return EX_DEFERALL;
}
time(&now);
eom = (msgdata *)&answer + n;
/*
* find first satisfactory answer
*/
hp = (HEADER *) &answer;
qdcount = ntohs(hp->qdcount);
ancount = ntohs(hp->ancount);
nscount = ntohs(hp->nscount);
arcount = ntohs(hp->arcount);
if (SS->verboselog) {
fprintf(SS->verboselog, "DNS lookup reply: len=%d rcode=%d qdcount=%d ancount=%d nscount=%d arcount=%d RD=%d TC=%d AA=%d QR=%d RA=%d",
n, hp->rcode, qdcount, ancount, nscount, arcount,
hp->rd, hp->tc, hp->aa, hp->qr, hp->ra);
#ifdef HAS_HEADER_CD_AD
fprintf(SS->verboselog, " CD=%d AD=%d", hp->cd, hp->ad);
#endif
fprintf(SS->verboselog, "\n");
}
if (maxmx > 2)
rmsgappend(SS, 1,
"\r-> DNSreply: len=%d rcode=%d qd=%d an=%d ns=%d ar=%d",
n, hp->rcode, qdcount, ancount, nscount, arcount);
if (hp->rcode != NOERROR || ancount == 0) {
switch (hp->rcode) {
case NXDOMAIN:
/* Non-authoritative iff response from cache.
* Old BINDs used to return non-auth NXDOMAINs
* due to a bug; if that is the case by you,
* change to return EX_TEMPFAIL iff hp->aa == 0.
*/
sprintf(SS->remotemsg, "smtp; 500 (DNS: no such domain: %.200s)", host);
if (SS->verboselog)
fprintf(SS->verboselog," NXDOMAIN %s\n", SS->remotemsg);
endtime = now;
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"5.4.4 (DNS lookup report)",SS->remotemsg);
#endif
return EX_NOHOST;
case SERVFAIL:
sprintf(SS->remotemsg, "smtp; 500 (DNS: server failure: %.200s)", host);
if (SS->verboselog)
fprintf(SS->verboselog," SERVFAIL %s\n", SS->remotemsg);
endtime = now;
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"5.4.4 (DNS lookup report)",SS->remotemsg);
#endif
return EX_DEFERALL;
case NOERROR:
mx[0].host = NULL;
return EX_OK;
case FORMERR:
case NOTIMP:
case REFUSED:
sprintf(SS->remotemsg, "smtp; 500 (DNS: unsupported query: %.200s)", host);
if (SS->verboselog)
fprintf(SS->verboselog," FORMERR/NOTIMP/REFUSED(%d) %s\n",
hp->rcode, SS->remotemsg);
endtime = now;
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"5.4.4 (DNS lookup report)",SS->remotemsg);
#endif
return EX_NOPERM;
}
sprintf(SS->remotemsg, "smtp; 500 (DNS: unknown error, MX info unavailable: %.200s)", host);
if (SS->verboselog)
fprintf(SS->verboselog," %s\n", SS->remotemsg);
endtime = now;
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"5.4.4 (DNS lookup report)",SS->remotemsg);
#endif
if (had_eai_again)
return EX_DEFERALL;
return EX_UNAVAILABLE;
}
nmx = 0;
cp = (msgdata *)&answer + sizeof(HEADER);
for (; qdcount > 0; --qdcount) {
#if defined(BIND_VER) && (BIND_VER >= 473)
cp += dn_skipname(cp, eom) + QFIXEDSZ;
#else /* !defined(BIND_VER) || (BIND_VER < 473) */
cp += dn_skip(cp) + QFIXEDSZ;
#endif /* defined(BIND_VER) && (BIND_VER >= 473) */
}
realname[0] = '\0';
maxpref = 66000;
while (ancount > 0 && cp < eom && nmx < maxmx-1) {
n = dn_expand((msgdata *)&answer, eom, cp, (void*)buf, sizeof buf);
if (n < 0)
break;
cp += n;
if (cp+10 > eom) { cp = eom; break; }
NS_GET16(type, cp);
NS_GET16(class, cp);
NS_GET32(ttl, cp); /* TTL */
NS_GET16(n, cp); /* dlen */
mx[nmx].expiry = now + ttl;
if (cp + n > eom) { cp = eom; break; }
if (class != C_IN) {
cp += n;
if (cp > eom) break;
--ancount;
continue;
}
if (type == T_CNAME) {
cp += dn_expand((msgdata *)&answer, eom, cp,
(void*)realname, realnamesize);
if (cp > eom) break;
saw_cname = 1;
--ancount;
if (SS->verboselog)
fprintf(SS->verboselog, " -> CNAME: '%s'\n", realname);
if (ttl < *realnamettlp) *realnamettlp = ttl;
continue;
} else if (type != T_MX) {
cp += n;
if (cp > eom) break;
--ancount;
continue;
}
if (cp + n /* dlen */ > eom) { cp = eom; break; }
NS_GET16(mx[nmx].pref, cp); /* MX preference value */
n = dn_expand((msgdata *)&answer, eom, cp, (void*)buf, sizeof buf);
if (n < 0) break;
cp += n;
if (cp > eom) break;
mx[nmx].ai = NULL;
mx[nmx].host = (char *)strdup((void*)buf);
if (mx[nmx].host == NULL) {
fprintf(stderr, "Out of virtual memory!\n");
exit(EX_OSERR);
}
if (SS->verboselog)
fprintf(SS->verboselog, " -> (%lds) MX[%d] pref=%d host=%s\n",
(long)(mx[nmx].expiry - now), nmx, mx[nmx].pref, buf);
if (maxmx > 2)
rmsgappend(SS, 1, "\r-> %lds MX[%d] p=%d '%s'",
mx[nmx].expiry - now, nmx, mx[nmx].pref, mx[nmx].host);
mxtype[nmx] = 0;
++nmx;
--ancount;
} /* Gone thru all answers */
if (nmx >= maxmx-1 && ancount > 0) {
/* If the MAXFORWARDERS count has been exceeded
(quite a feat!) skip over the rest of the
answers, as long as we have them, and the
reply-buffer has not been exhausted...
These are in fact extremely pathological cases of
the DNS datasets, and most MTA systems will simply
barf at this scale of things far before ZMailer... */
if (SS->verboselog)
fprintf(SS->verboselog, " collected MX count matches maximum supported (%d) with still some (%d) answers left to pick, we discard them.\n",
maxmx, ancount);
while (ancount > 0 && cp < eom) {
#if defined(BIND_VER) && (BIND_VER >= 473)
n = dn_skipname(cp, eom);
#else /* !defined(BIND_VER) || (BIND_VER < 473) */
n = dn_skip(cp);
#endif /* defined(BIND_VER) && (BIND_VER >= 473) */
if (n < 0)
break;
cp += n;
if (cp+10 > eom) { cp = eom; break; }
cp += NS_INT16SZ; /* type */
cp += NS_INT16SZ; /* class */
cp += NS_INT32SZ; /* ttl */
NS_GET16(n, cp); /* dlen */
cp += n;
--ancount;
} /* Skipped thru all remaining answers */
}
if (ancount > 0) {
/* URGH!!!! Still answers left over, WHAT ?!?!?! */
for (i = 0; i < nmx; ++i) {
if (mx[i].host) free(mx[i].host);
mx[i].host = NULL;
}
if (hp->tc) {
/* Yes, it is TRUNCATED reply! Must retry with e.g.
by using TCP! */
/* FIXME: FIXME! FIXME! Truncated reply handling! */
if (maxmx > 2)
rmsgappend(SS, 1, "\r TRUNCATED REPLY!");
}
if (SS->verboselog)
fprintf(SS->verboselog," left-over ANCOUNT=%d != 0! TC=%d\n",
ancount, hp->tc);
if (maxmx > 2)
rmsgappend(SS, 1, "\r AnswerCount %d > 0!!", ancount);
return EX_DEFERALL; /* FIXME?? FIXME?? */
}
if (nmx == 0 && realname[0] != 0) {
/* do it recursively for the real name */
n = getmxrr(SS, (char *)realname, mx, maxmx, depth+1,
realname, realnamesize, realnamettlp);
if (had_eai_again)
return EX_DEFERALL;
return n;
} else if (nmx == 0) {
/* "give it the benefit of doubt" */
mx[0].host = NULL;
mx[0].ai = NULL;
SS->mxcount = 0;
if (had_eai_again)
return EX_DEFERALL;
return EX_OK;
}
while (nscount > 0 && cp < eom) {
#if defined(BIND_VER) && (BIND_VER >= 473)
n = dn_skipname(cp, eom);
#else /* !defined(BIND_VER) || (BIND_VER < 473) */
n = dn_skip(cp);
#endif /* defined(BIND_VER) && (BIND_VER >= 473) */
if (n < 0)
break;
cp += n;
if (cp+10 > eom) { cp = eom; break; }
cp += NS_INT16SZ; /* type */
cp += NS_INT16SZ; /* class */
cp += NS_INT32SZ; /* ttl */
NS_GET16(n, cp); /* dlen */
cp += n; /* We simply skip this data.. */
if (cp <= eom)
--nscount;
}
/* =========================================================== */
#if 0 /* Bloody Linux vs. FreeBSD implementation differences... (addrinfo internal handling) */
#include "getmxrr-removed.txt"
#endif /* Linux vs. FreeBSD implementation difference... */
/* =========================================================== */
/* Collect addresses for all those who don't have them from
the ADDITIONAL SECTION data */
for (i = 0; i < nmx; ++i) {
struct addrinfo req, *ai, **aip;
if (SS->verboselog)
fprintf(SS->verboselog, " mx[%d] mxtype=%s%s(%d) host='%s'\n",
i, (mxtype[i]&1)?"4":"-", (mxtype[i]&2)?"6":"-",
mxtype[i], mx[i].host);
#if defined(AF_INET6) && defined(INET6)
/* If not IPv6 speaker, and already have A, skip it. */
if (!use_ipv6 && (mxtype[i] & 1))
continue;
if (mxtype[i] == 3)
continue; /* Have both A and AAAA */
#endif /* INET6 */
memset(&req, 0, sizeof(req));
req.ai_socktype = SOCK_STREAM;
req.ai_protocol = IPPROTO_TCP;
req.ai_flags = AI_CANONNAME;
req.ai_family = PF_INET;
ai = NULL;
n = 0;
if (! (mxtype[i] & 1)) { /* Not have A */
/* This resolves CNAME, it should not happen in case
of MX server, though.. */
#ifdef HAVE_GETADDRINFO
n = getaddrinfo((const char*)mx[i].host, "0", &req, &ai);
#else
n = _getaddrinfo_((const char*)mx[i].host, "0", &req, &ai, SS->verboselog);
#endif /* HAVE_GETADDRINFO */
if (SS->verboselog)
fprintf(SS->verboselog," getaddrinfo('%s','0') (PF_INET) -> r=%d (%s), ai=%p\n",
mx[i].host, n, gai_strerror(n), ai);
if (n != 0)
if (maxmx > 2)
rmsgappend(SS, 1, "\r-> getaddrinfo(INET, '%s','0') -> r=%d (%s); ai=%p",
mx[i].host, n, gai_strerror(n), ai);
#if 0
if (n) {
zsyslog((LOG_INFO,"getmxrr('%s') mx[%d]='%s' getaddrinfo(INET) rc=%d",
host, i, mx[i].host, n));
}
#endif /* .. 0 */
switch (n) {
case 0:
break;
case EAI_AGAIN:
had_eai_again = 1;
break;
case EAI_MEMORY:
exit(EX_OSERR);
break;
case EAI_NONAME:
case EAI_FAIL:
case EAI_SERVICE:
default:
break;
}
}
#if defined(AF_INET6) && defined(INET6)
if (use_ipv6 && !(mxtype[i] & 2) ) {
/* Want, but not have AAAA, ask for it. */
int n2;
struct addrinfo *ai2 = NULL;
memset(&req, 0, sizeof(req));
req.ai_socktype = SOCK_STREAM;
req.ai_protocol = IPPROTO_TCP;
req.ai_flags = AI_CANONNAME;
req.ai_family = PF_INET6;
/* This resolves CNAME, it should not happen in case
of MX server, though.. */
#ifdef HAVE_GETADDRINFO
n2 = getaddrinfo((const char *)mx[i].host, "0", &req, &ai2);
#else
n2 = _getaddrinfo_((const char *)mx[i].host, "0", &req, &ai2,
SS->verboselog);
#endif /* HAVE_GETADDRINFO */
if (SS->verboselog)
fprintf(SS->verboselog," getaddrinfo('%s','0') (PF_INET6) -> r=%d (%s), ai=%p\n",
mx[i].host, n2, gai_strerror(n2), ai2);
if (n2 != 0)
if (maxmx > 2)
rmsgappend(SS, 1, "\r-> getaddrinfo(INET6,'%s') -> r=%d (%s); ai=%p",
mx[i].host, n2, gai_strerror(n2), ai2);
#if 0
if (n) {
zsyslog((LOG_INFO,"getmxrr('%s') mx[%d]='%s' getaddrinfo(INET6) rc=%d",
host, i, mx[i].host, n));
}
#endif /* .. 0 */
switch (n2) {
case 0:
break;
case EAI_AGAIN:
had_eai_again = 1;
break;
case EAI_MEMORY:
exit(EX_OSERR);
break;
case EAI_NONAME:
case EAI_FAIL:
case EAI_NODATA:
case EAI_SERVICE:
default:
break;
}
if (n != 0 && n2 == 0) {
/* IPv6 address, no IPv4 (or error..) */
n = n2;
ai = ai2; ai2 = NULL;
}
if (ai2 && ai) {
/* BOTH ?! Catenate them! */
aip = &ai->ai_next;
while (*aip) aip = &((*aip)->ai_next);
*aip = ai2;
}
}
#endif /* INET6 */
/* Catenate new stuff into the tail of the old ... */
aip = &(mx[i].ai);
while (*aip) aip = &((*aip)->ai_next);
*aip = ai;
if (n != 0) {
if (n == EAI_AGAIN) {
sprintf(SS->remotemsg, "smtp; 500 (DNS: getaddrinfo<%.200s> got EAI_AGAIN)", buf);
endtime = now;
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"4.4.4 (DNS lookup report)",SS->remotemsg);
#endif /* .. TEST */
had_eai_again = 1;
}
}
} /* ... i < nmx ... */
/* Separate all addresses into their own MXes */
for (i = 0; i < nmx && nmx < maxmx-1; ++i) {
struct addrinfo *ai = mx[i].ai;
if (ai) ai = ai->ai_next; /* If more than one.. */
while (ai && nmx < maxmx-1) {
memcpy(&mx[nmx], &mx[i], sizeof(mx[0]));
mx[nmx].ai = ai;
ai = ai->ai_next;
mx[nmx].ai->ai_next = NULL;
mx[nmx].host = (char *)strdup((const char *)mx[i].host);
if (mx[nmx].host == NULL) {
fprintf(stderr, "Out of virtual memory!\n");
exit(EX_OSERR);
}
++nmx;
}
/* If there was something, it has been split out..
Hmm.. except if nmx >= maxmx-1, which is pathological
anyway... 100+ addressed server entities.. */
if (mx[i].ai) mx[i].ai->ai_next = NULL;
}
for (i = 0; i < nmx; ++i) {
if (mx[i].ai == NULL && mx[i].host != NULL) {
if (maxmx > 2)
rmsgappend(SS, 1, "\r-> No addresses for '%s'[%d]", mx[i].host, i);
free(mx[i].host);
mx[i].host = NULL;
continue;
}
if (CISTREQ(mx[i].host, myhostname) ||
(mx[i].ai->ai_canonname &&
CISTREQ(mx[i].ai->ai_canonname, myhostname)) ||
matchmyaddresses(mx[i].ai) == 1) {
if (maxmx > 2)
rmsgappend(SS, 1, "\r-> Selfmatch '%s'(%s)[%d]",
mx[i].host, ((mx[i].ai->ai_canonname) ?
mx[i].ai->ai_canonname : "-"), i);
if (SS->verboselog)
fprintf(SS->verboselog," matchmyaddresses(): matched! canon='%s', myname='%s'\n", mx[i].ai->ai_canonname ? mx[i].ai->ai_canonname : "<NIL>", myhostname);
if (maxpref > (int)mx[i].pref)
maxpref = mx[i].pref;
}
} /* ... i < nmx ... */
SS->mxcount = nmx;
if (SS->verboselog)
fprintf(SS->verboselog," getmxrr('%s') -> nmx=%d, maxpref=%d, realname='%s'\n", host, nmx, maxpref, realname);
if (maxmx > 2)
rmsgappend(SS, 1, "\r-> nmx=%d maxpref=%d realname='%s'",
nmx, maxpref, realname);
/* discard MX RRs with a value >= that of myhost */
for (n = i = 0; n < nmx; ++n) {
if ((int)mx[n].pref >= maxpref && mx[n].host) {
free(mx[n].host);
freeaddrinfo(mx[n].ai);
mx[n].host = NULL;
mx[n].ai = NULL;
++i; /* discard count */
}
}
if (i /* discard count */ == nmx) {
/* All discarded, we are the best MX :-( */
mx[0].host = NULL;
SS->mxcount = 0;
if (had_eai_again)
return EX_DEFERALL;
return EX_OK;
}
#ifndef TEST
#ifdef RFC974
/* discard MX's that do not support SMTP service */
if (checkwks)
for (n = 0; n < nmx; ++n) {
int ttl;
if (mx[n].host == NULL)
continue;
strncpy((char*)buf, (char*)mx[n].host, sizeof(buf));
buf[sizeof(buf)-1] = 0;
/* It is an MX, it CAN'T have CNAME ! */
if (!getrrtype((void*)buf, &ttl, sizeof buf, T_WKS,
0, SS->verboselog)) {
free(mx[n].host);
mx[n].host = NULL;
freeaddrinfo(mx[n].ai);
mx[n].ai = NULL;
}
}
#endif /* RFC974 */
#endif /* TEST */
/* determine how many are left */
for (i = 0, n = 0; i < nmx; ++i) {
if (mx[i].host == NULL)
continue;
if (n < i) {
memcpy(&mx[n], &mx[i], sizeof(mx[0]));
memset(&mx[i], 0, sizeof(mx[0]));
}
++n; /* found one! */
}
nmx = n;
SS->mxcount = nmx;
if (n == 0 && had_eai_again)
return EX_DEFERALL;
if (n == 0) {/* MX's exist, but their WKS's show no TCP smtp service */
if (maxmx > 2) {
rmsgappend(SS, 1, "\r=> NONE of MXes support SMTP!");
time(&endtime);
#ifndef TEST
notary_setxdelay((int)(endtime-starttime));
notaryreport(NULL,FAILED,"5.4.4 (DNS lookup report)",SS->remotemsg);
#endif
}
return EX_UNAVAILABLE;
}
/* sort the records per preferrence value */
for (i = 0; i < nmx; i++) {
for (j = i + 1; j < nmx; j++) {
if (mx[i].pref > mx[j].pref) {
memcpy(&mxtemp, &mx[i], sizeof(mxtemp));
memcpy(&mx[i], &mx[j], sizeof(mxtemp));
memcpy(&mx[j], &mxtemp, sizeof(mxtemp));
}
}
}
/* Randomize the order of those of same preferrence.
This will do some sort of load-balancing on large sites
which have multiple mail-servers at the same priority. */
for (i = 0, maxpref = mx[0].pref; i < nmx; ++i) {
/* They are in numerical order, now we can
detect when a new preferrence group steps in */
j = i;
while (j < nmx && maxpref == mx[j].pref) ++j;
if ((j-i) > 1) {
/* At least two of the same preferrence */
int k, len = j-i;
for (k = 0; k < len; ++k) {
int l = ranny(len-1);
memcpy(&mxtemp, &mx[i+k], sizeof(mxtemp));
memcpy(&mx[i+k], &mx[i+l], sizeof(mxtemp));
memcpy(&mx[i+l], &mxtemp, sizeof(mxtemp));
}
#if defined(AF_INET6) && defined(INET6)
if (prefer_ip6) {
int l; /* Bring IPv6 addresses before IPv4 ones */
for (l = 0, k = 1; k < len; ++k) {
if (mx[l].ai->ai_family == PF_INET &&
mx[k].ai->ai_family == PF_INET6) {
memcpy(&mxtemp, &mx[k], sizeof(mxtemp));
memcpy(&mx[k], &mx[l], sizeof(mxtemp));
memcpy(&mx[l], &mxtemp, sizeof(mxtemp));
++l;
}
}
}
#endif
}
/* Processed that preference, now next */
i = j-1;
if (j < nmx) /* If within the array */
maxpref = mx[j].pref;
}
if (SS->verboselog) {
fprintf(SS->verboselog,"Target has following MXes (cnt=%d):\n",nmx);
for (i=0; i<nmx; ++i) {
struct addrinfo *ai = mx[i].ai;
for (n = 0; ai; ai = ai->ai_next) ++n;
fprintf(SS->verboselog," MX %3d %-30.200s (%d %saddrs)\n",
mx[i].pref, mx[i].host, n,
n == 1 ?
(mx[i].ai->ai_family == PF_INET ? "AF_INET ":"AF_INET6 "):
"");
}
}
mx[nmx].host = NULL;
SS->mxcount = nmx;
/* Even with errors in data retrieval, if we get
ANY MX entries, we are a happy camper! */
if (had_eai_again && nmx == 0)
return EX_DEFERALL;
return EX_OK;
}
/* rmsgappend() is here because of getmxrr() test facility */
#ifdef HAVE_STDARG_H
#ifdef __STDC__
void
rmsgappend(SmtpState *SS, int append, const char *fmt, ...)
#else /* Not ANSI-C */
void
rmsgappend(SS, append, fmt)
SmtpState *SS;
int append;
const char *fmt;
#endif
#else
void
rmsgappend(va_alist)
va_dcl
#endif
{
va_list ap;
char *args;
long argi;
char *cp, *cpend;
char ibuf[15];
int long_flg = 0;
#ifdef HAVE_STDARG_H
va_start(ap,fmt);
#else
const char *fmt;
SmtpState *SS;
int append;
va_start(ap);
SS = va_arg(ap, SmtpState *);
append = va_arg(ap, int);
fmt = va_arg(ap, char *);
#endif
cp = SS->remotemsg + strlen(SS->remotemsg);
cpend = SS->remotemsg + sizeof(SS->remotemsg) -1;
if (SS->prevcmdstate >= SMTPSTATE99) /* magic limit.. */
SS->remotemsgs[(int)SS->cmdstate] = cp = SS->remotemsg;
if (SS->cmdstate > SS->prevcmdstate)
SS->remotemsgs[(int)SS->cmdstate] = cp;
if (!append)
cp = SS->remotemsgs[(int)SS->cmdstate];
SS->prevcmdstate = SS->cmdstate;
if (!fmt) fmt="(NULL)";
for (; *fmt != 0; ++fmt) {
if (*fmt == '%') {
int c = *++fmt;
long_flg = 0;
if (c == 'l') {
long_flg = 1;
c = *++fmt;
}
switch (c) {
case 's':
args = va_arg(ap, char *);
if (!args) args = "(null)";
while (*args && cp < cpend) *cp++ = *args ++;
break;
case 'd':
if (long_flg) {
argi = va_arg(ap, long);
sprintf(ibuf, "%ld", argi);
} else {
argi = va_arg(ap, int);
sprintf(ibuf, "%d", (int)argi);
}
args = ibuf;
while (*args && cp < cpend) *cp++ = *args ++;
break;
default:
if (cp < cpend) *cp++ = '%';
if (cp < cpend) *cp++ = c;
break;
}
} else
if (cp < cpend)
*cp++ = *fmt;
}
*cp = 0;
va_end(ap);
}
#ifdef TEST
time_t endtime, starttime, now;
const char *FAILED = "failed";
int use_ipv6 = 1;
int prefer_ip6 = 1;
int checkwks = 0;
char myhostname[512] = "my.host.name";
const char *progname;
char errormsg[ZBUFSIZ]; /* Global for the use of dnsgetrr.c */
int main(argc, argv)
int argc;
char *argv[];
{
int rc;
SmtpState SS;
char *host, *s;
char realname[1024];
time_t realnamettl;
progname = argv[0];
memset(&SS, 0, sizeof(SS));
SS.verboselog = stdout;
host = argv[1];
if (argc != 2) {
printf("Usage: getmxrr-test domain.name\n");
printf("\n");
printf("Std tests:\n");
printf(" getmxrr-test timeout-mx.zmailer.org\n");
printf(" -> GETMXRR() rc=100 EX_DEFERALL; mxcount=0\n");
printf(" -> NO SUCCESSFULLY COLLECTED MX DATA, LOOKING FOR A/AAAA DATA:\n");
printf(" -> .. getaddrinfo() (INET*) r=-2 (no address)\n");
printf(" -> GOT NO ADDRESSES!\n");
printf("\n");
printf(" getmxrr-test timeout-zone.zmailer.org\n");
printf(" -> GETMXRR() rc=100 EX_DEFERALL; mxcount=0\n");
printf(" -> NO SUCCESSFULLY COLLECTED MX DATA, LOOKING FOR A/AAAA DATA:\n");
printf(" -> .. getaddrinfo() (INET*) r=-3 (temporary failure in name resolution)\n");
printf(" -> GOT NO ADDRESSES!\n");
exit(EX_USAGE);
}
printf("ZMAILER GETMXRR() TEST HARNESS\n");
realname[0] = 0;
realnamettl = 84600;
rc = getmxrr(&SS, host, SS.mxh, MAXFORWARDERS, 0, realname, sizeof(realname), &realnamettl);
switch (rc) {
case EX_OK:
s = "EX_OK";
break;
case EX_USAGE:
s = "EX_USAGE";
break;
case EX_DATAERR:
s = "EX_DATAERR";
break;
case EX_NOINPUT:
s = "EX_NOINPUT";
break;
case EX_NOUSER:
s = "EX_NOUSER";
break;
case EX_NOHOST:
s = "EX_NOHOST";
break;
case EX_UNAVAILABLE:
s = "EX_UNAVAILABLE";
break;
case EX_SOFTWARE:
s = "EX_SOFTWARE";
break;
case EX_OSERR:
s = "EX_OSERR";
break;
case EX_OSFILE:
s = "EX_OSFILE";
break;
case EX_CANTCREAT:
s = "EX_CANTCREAT";
break;
case EX_IOERR:
s = "EX_IOERR";
break;
case EX_TEMPFAIL:
s = "EX_TEMPFAIL";
break;
case EX_PROTOCOL:
s = "EX_PROTOCOL";
break;
case EX_NOPERM:
s = "EX_NOPERM";
break;
case EX_DEFERALL:
s = "EX_DEFERALL";
break;
default:
s = "UNKNOWN!";
}
printf("GETMXRR() rc=%d %s; mxcount=%d\n", rc, s, SS.mxcount);
if (SS.mxcount == 0 || SS.mxh[0].host == NULL) {
struct addrinfo req, *ai;
int r;
printf("NO SUCCESSFULLY COLLECTED MX DATA, LOOKING FOR A/AAAA DATA:\n");
memset(&req, 0, sizeof(req));
req.ai_socktype = SOCK_STREAM;
req.ai_protocol = IPPROTO_TCP;
req.ai_flags = AI_CANONNAME;
req.ai_family = PF_INET;
ai = NULL;
errno = 0;
/* Either forbidden MX usage, or does not have MX entries! */
#ifdef HAVE__GETADDRINFO_
r = _getaddrinfo_(host, "0", &req, &ai, SS.verboselog);
#else
r = getaddrinfo(host, "0", &req, &ai);
#endif
if (SS.verboselog)
fprintf(SS.verboselog,
"getaddrinfo('%s','0' (INET)) -> r=%d (%s), ai=%p\n",
host, r, gai_strerror(r), ai);
#if defined(AF_INET6) && defined(INET6)
if (use_ipv6) {
struct addrinfo *ai2 = NULL, **aip;
int i2;
memset(&req, 0, sizeof(req));
req.ai_socktype = SOCK_STREAM;
req.ai_protocol = IPPROTO_TCP;
req.ai_flags = AI_CANONNAME;
req.ai_family = PF_INET6;
/* This resolves CNAME, it should not happen in case
of MX server, though.. */
#ifdef HAVE__GETADDRINFO_
i2 = _getaddrinfo_(host, "0", &req, &ai2, SS.verboselog);
#else
i2 = getaddrinfo(host, "0", &req, &ai2);
#endif
if (SS.verboselog)
fprintf(SS.verboselog,
"getaddrinfo('%s','0' (INET6)) -> r=%d (%s), ai=%p\n",
host,i2, gai_strerror(i2), ai2);
if (r != 0 && i2 == 0) {
/* IPv6 address, no IPv4 (or error..) */
r = i2;
ai = ai2; ai2 = NULL;
}
if (ai2 && ai) {
/* BOTH ?! Catenate them! */
aip = &(ai->ai_next);
while (*aip) aip = &((*aip)->ai_next);
*aip = ai2;
}
}
#endif
if (ai)
printf(" GOT SOME ADDRESS, SUCCESS!\n");
else
printf(" GOT NO ADDRESSES!\n");
}
return 0;
}
const char * getzenv(name)
const char *name;
{
return NULL;
}
#if 0 /* Deep testing .. */
int _getaddrinfo_ (host, port, req, ai, logfp)
const char *host;
const char *port;
const struct addrinfo *req;
struct addrinfo **ai;
FILE *logfp;
{
return getaddrinfo(host, port, req, ai);
}
#endif
#endif
syntax highlighted by Code2HTML, v. 0.9.1