/* * mxverify-cgi -- a ZMailer associated utility for doing web-based * analysis of ``is my incoming email working properly ?'' * * By Matti Aarnio 20-Jan-2000, 2001, 2003 * * This program plays fast&loose with HTTP/CGI interface, and presumes * quite exactly the
stuff that is present in the file * mxverify-cgi.html * */ #include "hostenv.h" #include #include #include #include #include #include "zmsignal.h" #include #include /* #include */ /* poorly portable.. */ #ifdef HAVE_STDARG_H # include #else # include #endif #include #include #include #include #include #include "zresolv.h" #include "libc.h" #ifdef _AIX /* Defines NFDBITS, et.al. */ #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #include #ifndef NFDBITS /* * This stuff taken from the 4.3bsd /usr/include/sys/types.h, but on the * assumption we are dealing with pre-4.3bsd select(). */ /* #error "FDSET macro susceptible" */ typedef long fd_mask; #ifndef NBBY #define NBBY 8 #endif /* NBBY */ #define NFDBITS ((sizeof fd_mask) * NBBY) /* SunOS 3.x and 4.x>2 BSD already defines this in /usr/include/sys/types.h */ #ifdef notdef typedef struct fd_set { fd_mask fds_bits[1]; } fd_set; #endif /* notdef */ #ifndef _Z_FD_SET /* #warning "_Z_FD_SET[1]" */ #define _Z_FD_SET(n, p) ((p)->fds_bits[0] |= (1 << (n))) #define _Z_FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1 << (n))) #define _Z_FD_ISSET(n, p) ((p)->fds_bits[0] & (1 << (n))) #define _Z_FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p))) #endif /* !FD_SET */ #endif /* !NFDBITS */ #ifdef FD_SET /* #warning "_Z_FD_SET[2]" */ #define _Z_FD_SET(sock,var) FD_SET(sock,&var) #define _Z_FD_CLR(sock,var) FD_CLR(sock,&var) #define _Z_FD_ZERO(var) FD_ZERO(&var) #define _Z_FD_ISSET(i,var) FD_ISSET(i,&var) #else /* #warning "_Z_FD_SET[3]" */ #define _Z_FD_SET(sock,var) var |= (1 << sock) #define _Z_FD_CLR(sock,var) var &= ~(1 << sock) #define _Z_FD_ZERO(var) var = 0 #define _Z_FD_ISSET(i,var) ((var & (1 << i)) != 0) #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif /* SEEK_SET */ #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_XTND #define SEEK_XTND 2 #endif #ifndef IPPORT_SMTP #define IPPORT_SMTP 25 #endif /* IPPORT_SMTP */ #define PROGNAME "smtpclient" /* for logging */ #define CHANNEL "smtp" /* the default channel name we deliver for */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif /* MAXHOSTNAMELEN */ #define MAXFORWARDERS 128 /* Max number of MX rr's that can be listed */ struct mxdata { const msgdata *host; int pref; int ttl; }; int timeout_conn = 30; /* 30 seconds for connection */ int timeout_tcpw = 20; /* 20 seconds for write */ int timeout_tcpr = 60; /* 60 seconds for responses */ int plaintext = 0; int conn_ok = 0; int use_ipv6 = 1; /* Input by 'GET' method, domain-name at CGI URL */ /* STDARG && STDC */ void htmlprintf(const char *fmt, ...) { va_list ap; int in_tag = 0; va_start(ap, fmt); for ( ; *fmt ; ++fmt ) { if (!in_tag && *fmt == '<') { in_tag = 1; } else if (in_tag && *fmt == '>') { in_tag = 0; } if (in_tag && plaintext) continue; if (*fmt == '%') { int width = 0; ++fmt; while ('0' <= *fmt && *fmt <= '9') { width = width * 10 + (*fmt - '0'); ++fmt; } switch (*fmt) { case 's': { const char *str = va_arg(ap, const char *); if (plaintext) { printf("%s",str); } else for (;str && *str; ++str) { printf("&#%d;", 0xFF & *str); } } break; case 'd': { int d = va_arg(ap, int); printf("%*d", width, d); } break; default: break; } continue; } printf("%c", *fmt); } } extern int mxverifyrun(); int getmxrr(host, mx, maxmx, depth) const char *host; struct mxdata mx[]; int maxmx, depth; { HEADER *hp; msgdata *eom, *cp; querybuf qbuf, answer; struct mxdata mxtemp; msgdata buf[8192], realname[8192]; int qlen, n, i, j, nmx, ancount, qdcount, maxpref; u_short type; int saw_cname = 0; if (depth == 0) h_errno = 0; if (depth > 3) { htmlprintf("

ERROR: RECURSIVE CNAME ON DNS LOOKUPS: domain=``%s''

\n", host); return EX_NOHOST; } qlen = res_mkquery(QUERY, host, C_IN, T_MX, NULL, 0, NULL, (void*)&qbuf, sizeof qbuf); if (qlen < 0) { htmlprintf("

ERROR: res_mkquery() failed! domain=``%s''

\n", host); return EX_SOFTWARE; } htmlprintf("

Doing resolver lookup for T=MX domain=``%s''

\n", host); n = res_send((void*)&qbuf, qlen, (void*)&answer, sizeof answer); if (n < 0) { htmlprintf("

ERROR: No resolver response for domain=``%s''

\n", host); return EX_TEMPFAIL; } eom = (msgdata *)&answer + n; /* * find first satisfactory answer */ hp = (HEADER *) &answer; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); 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. */ htmlprintf("

ERROR: NO SUCH DOMAIN: ``%s''

\n", host); return EX_TEMPFAIL; case SERVFAIL: htmlprintf("

ERROR: DNS Server Failure: domain=``%s''

\n", host); return EX_TEMPFAIL; case NOERROR: htmlprintf("

Questionable: NO MX DATA: domain=``%s'' We SIMULATE!

\n", host); htmlprintf("

Do have at least one MX entry added!

\n"); mx[0].host = host; mx[0].pref = 999999; mx[1].host = NULL; return 0; case FORMERR: htmlprintf("

ERROR: DNS Internal FORMERR error: domain=``%s''

\n", host); return EX_NOPERM; case NOTIMP: htmlprintf("

ERROR: DNS Internal NOTIMP error: domain=``%s''

\n", host); return EX_NOPERM; case REFUSED: htmlprintf("

ERROR: DNS Internal REFUSED error: domain=``%s''

\n", host); return EX_NOPERM; } htmlprintf("

ERROR: DNS Unknown Error! (rcode=%d) domain=``%s''

\n", hp->rcode, host); 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 = -1; 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; NS_GET16(type, cp); /* type */ cp += NS_INT16SZ; /* class */ NS_GET32(mx[nmx].ttl, cp); /* ttl */ NS_GET16(n, cp); /* dlen */ if (type == T_CNAME) { cp += dn_expand((msgdata *)&answer, eom, cp, (void*)realname, sizeof realname); saw_cname = 1; continue; } else if (type != T_MX) { cp += n; continue; } 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; mx[nmx].host = (msgdata *)strdup(buf); ++nmx; } if (nmx == 0 && realname[0] != '\0' && strcasecmp(host,(char*)realname) != 0) { /* do it recursively for the real name */ n = getmxrr((char *)realname, mx, maxmx, depth+1); return n; } else if (nmx == 0) { /* "give it the benefit of doubt" */ mx[0].host = NULL; return EX_OK; } /* 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) { mxtemp = mx[i]; mx[i] = mx[j]; mx[j] = mxtemp; } } } htmlprintf("

DNS yields following MX entries\n

\n");
	for (i = 0; i < nmx; ++i)
	  htmlprintf("  %s  (%ds) IN MX %3d %s\n", host,mx[i].ttl,mx[i].pref,mx[i].host);
	htmlprintf("
\n

\n"); if (nmx == 1) { htmlprintf("

Only one MX record...\n
Well, no backups, but as all systems are looking for MX record in every case, not bad..

\n

\n"); } mx[nmx].host = NULL; return EX_OK; } int vcsetup(sa, fdp, myname, mynamemax) struct sockaddr *sa; int *fdp, mynamemax; char *myname; { int af, gotalarm = 0; volatile int addrsiz; int sk; struct sockaddr_in *sai = (struct sockaddr_in *)sa; struct sockaddr_in sad; #if defined(AF_INET6) && defined(INET6) struct sockaddr_in6 *sai6 = (struct sockaddr_in6 *)sa; struct sockaddr_in6 sad6; #endif struct hostent *hp; union { struct sockaddr_in sai; #if defined(AF_INET6) && defined(INET6) struct sockaddr_in6 sai6; #endif } upeername; int upeernamelen = 0; int errnosave, flg; char *se; af = sa->sa_family; #if defined(AF_INET6) && defined(INET6) if (sa->sa_family == AF_INET6) { addrsiz = sizeof(*sai6); memset(&sad6, 0, sizeof(sad6)); } else #endif { addrsiz = sizeof(*sai); memset(&sad, 0, sizeof(sad)); } sk = socket(af, SOCK_STREAM, 0); if (sk < 0) { se = strerror(errno); htmlprintf("

ERROR: Failed to create %s type socket! err='%s'

\n", af == AF_INET ? "AF_INET" : "AF_INET6", se); return EX_TEMPFAIL; } if (af == AF_INET) sai->sin_port = htons(25); #if defined(AF_INET6) && defined(INET6) if (af == AF_INET6) sai6->sin6_port = htons(25); #endif /* The socket will be non-blocking for its entire lifetime.. */ #ifdef O_NONBLOCK fcntl(sk, F_SETFL, fcntl(sk, F_GETFL, 0) | O_NONBLOCK); #else #ifdef FNONBLOCK fcntl(sk, F_SETFL, fcntl(sk, F_GETFL, 0) | FNONBLOCK); #else fcntl(sk, F_SETFL, fcntl(sk, F_GETFL, 0) | FNDELAY); #endif #endif errnosave = errno = 0; if (sa->sa_family == AF_INET) { struct sockaddr_in *si = (struct sockaddr_in*) sa; unsigned long ia = ntohl(si->sin_addr.s_addr); int anet = ia >> 24; if (anet <= 0 || anet == 127 || anet >= 224) { close(sk); errno = EADDRNOTAVAIL; return EX_UNAVAILABLE; } } if (connect(sk, sa, addrsiz) < 0 && (errno == EWOULDBLOCK || errno == EINPROGRESS)) { /* Wait for the connection -- or timeout.. */ struct timeval tv; fd_set wrset; int rc; /* Select for the establishment, or for the timeout */ tv.tv_sec = timeout_conn; tv.tv_usec = 0; _Z_FD_ZERO(wrset); _Z_FD_SET(sk, wrset); rc = select(sk+1, NULL, &wrset, NULL, &tv); errno = 0; /* All fine ? */ if (rc == 0) { /* Timed out :-( */ gotalarm = 1; /* Well, sort of ... */ errno = ETIMEDOUT; } } if (!errnosave) errnosave = errno; #ifdef SO_ERROR flg = 0; if (errnosave == 0) { int flglen = sizeof(flg); getsockopt(sk, SOL_SOCKET, SO_ERROR, (void*)&flg, &flglen); } if (flg != 0 && errnosave == 0) errnosave = flg; /* "flg" contains socket specific error condition data */ #endif if (errnosave == 0) { /* We have successfull connection, lets record its peering data */ memset(&upeername, 0, sizeof(upeername)); upeernamelen = sizeof(upeername); getsockname(sk, (struct sockaddr*) &upeername, &upeernamelen); if (upeername.sai.sin_family == AF_INET) hp = gethostbyaddr((char*)&upeername.sai.sin_addr, 4, AF_INET); #if defined(AF_INET6) && defined(INET6) else if (upeername.sai6.sin6_family == AF_INET6) hp = gethostbyaddr((char*)&upeername.sai6.sin6_addr, 16, AF_INET6); #endif else hp = NULL; /* Ok, NOW we have a hostent with our IP-address reversed to a name */ if (hp) strncpy(myname, hp->h_name, mynamemax); else getmyhostname(myname, mynamemax); } if (errnosave == 0 && !gotalarm) { *fdp = sk; htmlprintf("[ CONNECTED! ]
\n"); ++conn_ok; return EX_OK; } close(sk); se = strerror(errnosave); htmlprintf("

ERROR: Connect failure reason: %s


(Still possibly all OK!)
\n",se); return 0; } int smtpgetc(sock, tout) int sock, tout; { static unsigned char buf[1024]; static int bufin = 0, bufout = 0; static int eof = 0; if (sock < 0) { bufin = bufout = eof = 0; return 0; } if (eof) return -1; for (;;) { /* Pick from input buffer */ if (bufin > bufout) { return buf[bufout++]; } if (bufin <= bufout) bufin = bufout = 0; if (bufin == 0) { struct timeval tv; fd_set rdset; int rc; rc = read(sock, buf, sizeof(buf)); if (rc > 0) { bufin = rc; continue; } if (rc == 0) { /* EOF! */ eof = 1; return -1; } if (errno == EINTR) continue; if (errno != EWOULDBLOCK && errno != EAGAIN) { eof = 1; return -errno; } _Z_FD_ZERO(rdset); _Z_FD_SET(sock, rdset); tv.tv_sec = tout ? 1 : timeout_tcpr; tv.tv_usec = 0; rc = select(sock+1, &rdset, NULL, NULL, &tv); if (rc > 0) continue; /* THINGS TO READ! */ if (rc == 0) { if (!tout) eof = 1; return -ETIMEDOUT; } /* Errors ?? */ if (errno == EINTR) continue; return -errno; } } } void htmlwrite(str, len) char *str; { int i; if (plaintext) fwrite(str, 1, len, stdout); else for (i = 0; i < len; ++i) { if (str[i] == '\n') fprintf(stdout, "\n"); else fprintf(stdout, "&#%d;", str[i]); } } int readsmtp(sock) int sock; { char linebuf[8192]; int c = 0; int end_seen = 0; int no_more = 0; while ( !no_more && c >= 0 ) { int newline_seen = 0; int linelen = 0; while (!newline_seen) { c = smtpgetc(sock, end_seen); if (c < 0) { if (end_seen && c == -ETIMEDOUT) /* Quick additional read timeout.. */ no_more = 1; else /* ERROR !!?? */ ; if (linelen > 0) { fprintf(stdout, " "); htmlwrite(linebuf, linelen); fprintf(stdout, "\n"); linelen = 0; } break; } if (c == '\r') continue; /* Ignore that */ if (linelen < sizeof(linebuf)) linebuf[linelen ++] = c; if (c == '\n') newline_seen = 1; } /* Got a full line, now what it might be.. */ if (linelen > 3 && ('0' <= linebuf[0] && linebuf[0] <= '9') && ('0' <= linebuf[1] && linebuf[1] <= '9') && ('0' <= linebuf[2] && linebuf[2] <= '9') && (linebuf[3] == '\r' || linebuf[3] == '\n' || linebuf[3] == ' ' || linebuf[3] == '\t')) { end_seen = atoi(linebuf); } if (linelen > 0) { fprintf(stdout, " "); htmlwrite(linebuf, linelen); linelen = 0; } } if (c < 0 && no_more) c = 0; return (c < 0) ? c : end_seen; } int writesmtp(sock, str) int sock; char *str; { int rc, len, e; len = strlen(str); while (len > 0) { SIGNAL_HANDLE(SIGPIPE, SIG_IGN); rc = write(sock, str, len); e = errno; SIGNAL_HANDLE(SIGPIPE, SIG_DFL); errno = e; if (rc >= 0) { len -= rc; str += rc; continue; } /* Right, now error handling.. */ if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) { fd_set wrset; struct timeval tv; _Z_FD_ZERO(wrset); _Z_FD_SET(sock, wrset); tv.tv_sec = timeout_tcpw; tv.tv_usec = 0; rc = select(sock+1, NULL, &wrset, NULL, &tv); if (rc > 0) continue; if (rc == 0) { /* TIMEOUT! */ return ETIMEDOUT; } /* Error processing! */ if (errno == EINTR) continue; } return errno; } return 0; } int smtptest(thatuser, ai) char *thatuser; struct addrinfo *ai; { int sock, rc, wtout = 0; int nullreject = 0; char myhostname[200]; char smtpline[500]; char *thatdomain = strchr(thatuser, '@'); if (!thatdomain) thatdomain = thatuser; else ++thatdomain; /* Try two sessions: 1) HELO + MAIL FROM:<> + RCPT TO: + close --- and perhaps if it turns out to be becessary, also: 2) HELO + MAIL FROM: + RCPT TO: + close */ smtpgetc(-1); sock = -1; rc = vcsetup(ai->ai_addr, &sock, myhostname, sizeof(myhostname)); if (rc != EX_OK || sock < 0) return rc; /* D'uh! */ if (!plaintext) htmlprintf("
\n");

	/* Initial greeting */

	rc = readsmtp(sock); /* Read response.. */
	if (rc < 0 || rc > 299) goto end_test_1;


	sprintf(smtpline, "EHLO %s\r\n", myhostname);
	fprintf(stdout, " EHLO %s\n", myhostname);
	rc = writesmtp(sock, smtpline);

	if (rc == ETIMEDOUT) wtout = 1;
	if (rc != EX_OK) goto end_test_1;
	rc = readsmtp(sock); /* Read response.. */
	if (rc < 0 || rc > 299) {

	  htmlprintf("

\n

Grrr... Doesn't understand ESMTP EHLO greeting

\n"); /* Close, and reconnect... */ close(sock); sock = -1; rc = vcsetup(ai->ai_addr, &sock, myhostname, sizeof(myhostname)); if (rc != EX_OK || sock < 0) return rc; /* D'uh! */ if (!plaintext) htmlprintf("

\n");

	  /* Initial greeting */

	  rc = readsmtp(sock); /* Read response.. */
	  if (rc < 0 || rc > 299) goto end_test_1;

	  sprintf(smtpline, "HELO %s\r\n", myhostname);
	  fprintf(stdout, " HELO %s\n", myhostname);
	  rc = writesmtp(sock, smtpline);

	  if (rc == ETIMEDOUT) wtout = 1;
	  if (rc != EX_OK) goto end_test_1;
	  rc = readsmtp(sock); /* Read response.. */
	  if (rc < 0 || rc > 299) goto end_test_1;

	} else {
	  if (!plaintext) htmlprintf("

\n"); htmlprintf("

Excellent! It speaks ESMTP!

\n"); if (!plaintext) htmlprintf("

\n");
	}
	

	sprintf(smtpline, "MAIL FROM:<>\r\n");
	htmlprintf(" MAIL FROM:%s%s\n","<",">");
	rc = writesmtp(sock, smtpline);
	if (rc == ETIMEDOUT) wtout = 1;
	if (rc != EX_OK) goto end_test_1;
	rc = readsmtp(sock); /* Read response.. */

	if (!plaintext) htmlprintf("

"); if (rc < 0 || rc > 299) { htmlprintf("

Grr! Rejects NULL return path; see RFC 2821 section 6.1

\n"); nullreject = 1; } else { htmlprintf("

Fine, it accepts NULL return-path as is mandated by RFC 2821 section 6.1

\n"); } if (!plaintext) htmlprintf("

");
	
	sprintf(smtpline, "RSET\r\n");
	htmlprintf(" RSET\n");
	rc = writesmtp(sock, smtpline);
	if (rc == ETIMEDOUT) wtout = 1;
	if (rc != EX_OK) goto end_test_1;
	rc = readsmtp(sock); /* Read response.. */
	/* Ignore the result ? */

	sprintf(smtpline, "MAIL FROM:\r\n", myhostname);
	htmlprintf(" MAIL FROM:%spostmaster@%s%s\n","<",myhostname,">");
	rc = writesmtp(sock, smtpline);
	if (rc == ETIMEDOUT) wtout = 1;
	if (rc != EX_OK) goto end_test_1;
	rc = readsmtp(sock); /* Read response.. */
	if (rc < 0 || rc > 299) { 
	  goto end_test_1;
	}

	if (thatdomain != thatuser) {
	  sprintf(smtpline, "RCPT TO:<%s>\r\n", thatuser);
	  htmlprintf(" RCPT TO:%s%s%s\n","<",thatuser,">");
	  rc = writesmtp(sock, smtpline);
	  if (rc == ETIMEDOUT) wtout = 1;
	  if (rc != EX_OK) goto end_test_1;
	  rc = readsmtp(sock); /* Read response.. */
	  if (rc < 0 || rc > 299) goto end_test_1;
	}

	sprintf(smtpline, "RCPT TO:\r\n", thatdomain);
	htmlprintf(" RCPT TO:%spostmaster@%s%s\n","<",thatdomain,">");
	rc = writesmtp(sock, smtpline);
	if (rc == ETIMEDOUT) wtout = 1;
	if (rc != EX_OK) goto end_test_1;
	rc = readsmtp(sock); /* Read response.. */
	if (rc < 0 || rc > 299) {
	  if (!plaintext) htmlprintf("\n
\n"); htmlprintf("

Eh ? What ? No ``postmaster'' supported there ? That violates RFC 2821 section 4.5.1.

\n"); if (!plaintext) htmlprintf("
\n");
	}


	rc = 0; /* All fine, no complaints! */


 end_test_1:
	sprintf(smtpline, "RSET\r\nQUIT\r\n");
	writesmtp(sock, smtpline);
	close(sock);

	htmlprintf("\n
\n"); /* htmlprintf("RC = %d\n", rc); */ if (wtout) htmlprintf("

WRITE TIMEOUT!

\n"); else if (rc == 0 && !nullreject) htmlprintf("

Apparently OK!

\n"); else if (rc == 0 && nullreject) htmlprintf("

Rejects RFC 2821 section 6.1 defined mandatorily supported source address format, otherwise appears to work!

\n"); else htmlprintf("

Something WRONG!! rc=%d

\n", rc); return rc; } int testmxsrv(thatdomain, hname) char *thatdomain; char *hname; { struct addrinfo req, *ai, *ai2, *a; int i, i2, rc = 0, rc2; memset(&req, 0, sizeof(req)); req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_TCP; req.ai_flags = AI_CANONNAME; req.ai_family = AF_INET; ai = ai2 = NULL; /* This resolves CNAME, it should not be done in case of MX server, though.. */ i = getaddrinfo(hname, "0", &req, &ai); #if defined(AF_INET6) && defined(INET6) if (use_ipv6) { memset(&req, 0, sizeof(req)); req.ai_socktype = SOCK_STREAM; req.ai_protocol = IPPROTO_TCP; req.ai_flags = AI_CANONNAME; req.ai_family = AF_INET6; i2 = getaddrinfo(hname, "0", &req, &ai2); if (i2 == 0 && i != 0) { /* IPv6 address, but no IPv4 address ? */ i = i2; ai = ai2; ai2 = NULL; } if (ai2 && ai) { /* BOTH ?! Catenate them! */ a = ai; while (a && a->ai_next) a = a->ai_next; if (a) a->ai_next = ai2; } } #endif if (i) { /* It is fucked up somehow.. */ htmlprintf("

--- sorry, address lookup for ``%s'' failed;
\n code = %s

\n", hname, gai_strerror(i)); return i; } if (!ai) { htmlprintf("Address lookup did not yield any addresses!\n"); return EX_DATAERR; } htmlprintf("Address lookup did yield following ones:\n

\n"); htmlprintf("

\n");

	for (a = ai; a; a = a->ai_next) {
	  char buf[200];
	  struct sockaddr_in *si;
#if defined(AF_INET6) && defined(INET6)
	  struct sockaddr_in6 *si6;
#endif

	  if (a->ai_family == AF_INET) {
	    si = (struct sockaddr_in *)a->ai_addr;
	    strcpy(buf, "IPv4 ");
	    inet_ntop(AF_INET, &si->sin_addr, buf+5, sizeof(buf)-5);
	  } else
#if defined(AF_INET6) && defined(INET6)
	  if (a->ai_family == AF_INET6) {
	    si6 = (struct sockaddr_in6*)a->ai_addr;
	    strcpy(buf, "IPv6 ");
	    inet_ntop(AF_INET6, &si6->sin6_addr, buf+5, sizeof(buf)-5);
	  } else
#endif
	    sprintf(buf,"UNKNOWN-ADDR-FAMILY-%d", a->ai_family);
	  
	  fprintf(stdout,"  %s\n", buf);
	}

	htmlprintf("
\n"); for (a = ai; a; a = a->ai_next) { char buf[200]; struct sockaddr_in *si; #if defined(AF_INET6) && defined(INET6) struct sockaddr_in6 *si6; #endif if (a->ai_family == AF_INET) { si = (struct sockaddr_in *)a->ai_addr; strcpy(buf, "IPv4 "); inet_ntop(AF_INET, &si->sin_addr, buf+5, sizeof(buf)-5); } else #if defined(AF_INET6) && defined(INET6) if (a->ai_family == AF_INET6) { si6 = (struct sockaddr_in6*)a->ai_addr; strcpy(buf, "IPv6 "); inet_ntop(AF_INET6, &si6->sin6_addr, buf+5, sizeof(buf)-5); } else #endif sprintf(buf,"UNKNOWN-ADDR-FAMILY-%d", a->ai_family); htmlprintf("

\n"); htmlprintf("

Testing server at address: %s

\n", buf); htmlprintf("

\n"); rc2 = smtptest(thatdomain, a); if (!rc) rc = rc2; } return rc; } int mxverifyrun(thatuser) char *thatuser; { struct mxdata mx[80+1]; int rc, rc2, i; char *thatdomain = strchr(thatuser,'@'); if (!thatdomain) thatdomain = thatuser; else ++thatdomain; rc = getmxrr(thatdomain, mx, 80, 0); if (rc) return rc; for (i = 0; mx[i].host != NULL; ++i) { htmlprintf("

\n"); if (plaintext) fprintf(stdout, "-----------------------------------------------------------------------\n"); else fprintf(stdout, "


\n"); htmlprintf("

Testing MX server: %s

\n

\n", mx[i].host); rc2 = testmxsrv(thatuser, mx[i].host); if (!rc) rc = rc2; /* Yield 'error' if any errs. */ } if (!rc && !conn_ok) { /* No SUCCESSFULL connections anywhere, either the network is in trouble towards all destination system MX sites, or the site really is in trouble... */ rc = 1; } return rc; } int main(argc, argv) int argc; char *argv[]; { char *getstr = getenv("QUERY_STRING"); /* We PRESUME that in all conditions our input is of something which does not need decoding... */ int err = 0; SIGNAL_HANDLE(SIGPIPE, SIG_DFL); #if defined(AF_INET6) && defined(INET6) { int sk = socket(AF_INET6, SOCK_STREAM, 0); if (sk > 0) close(sk); if (sk < 0) use_ipv6 = 0; /* No go :-( Can't create IPv6 socket */ } #endif res_init(); #ifdef RES_USE_INET6 #if defined(AF_INET6) && defined(INET6) if (!use_ipv6) _res.options &= ~RES_USE_INET6; #else _res.options &= ~RES_USE_INET6; #endif #endif if (!getstr) err = 1; if (!getstr) getstr = "--DESTINATION-DOMAIN-NOT-SUPPLIED--"; if (!err) { char *s = strchr(getstr, '&'); if (s) *s = 0; if (strncasecmp(getstr,"DOMAIN=",7)==0) { getstr += 7; } else err = 1; } if (argc == 3) { if (strcmp(argv[1],"-domain") == 0) { err = 0; getstr = argv[2]; plaintext = 1; } } if (!err) { char *s, *p; /* Turn '+' to space */ while ((s = strchr(getstr,'+')) != NULL) *s = ' '; p = s = getstr; while (*s) { if (*s == '%') { /* '%HH' -> a char */ int c1 = *++s; int c2 = 0; if ('0' <= c1 && c1 <= '9') c1 = c1 - '0'; else if ('A' <= c1 && c1 <= 'F') c1 = c1 - 'A' + 10; else if ('a' <= c1 && c1 <= 'f') c1 = c1 - 'a' + 10; else err = 1; if (*s) c2 = *++s; if ('0' <= c2 && c2 <= '9') c2 = c2 - '0'; else if ('A' <= c2 && c2 <= 'F') c2 = c2 - 'A' + 10; else if ('a' <= c2 && c2 <= 'f') c2 = c2 - 'a' + 10; else err = 1; if (!err) { c1 <<= 4; c1 |= c2; if (c1 < ' ' || c2 >= 127) err = 1; } if (!err) *p++ = c1; if (*s) ++s; continue; } /* Anything else, just copy.. */ *p++ = *s++; } *p = 0; } setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IOLBF, 0); if (!plaintext) { fprintf(stdout, "Content-Type: TEXT/HTML\nPragma: no-cache\n\n"); fprintf(stdout, "\n"); } htmlprintf("MX-VERIFY-CGI run for ``%s''\n", getstr); if (!plaintext) { fprintf(stdout, "\n\n"); htmlprintf("

MX-VERIFY-CGI run for ``%s''

\n", getstr); fprintf(stdout, "


\n"); } if (!err) err = mxverifyrun(getstr); else { if (plaintext) { fprintf(stdout, "\n\nSorry, NO MX-VERIFY-CGI run with this input!\n"); exit(EX_USAGE); } fprintf(stdout, "

\n"); fprintf(stdout, "Sorry, NO MX-VERIFY-CGI run with this input!

\n"); } if (!plaintext) { fprintf(stdout, "


\n"); } if ((err & 127) == 0 && err != 0) err = 1; /* Make sure that after an exit() the caller will see non-zero exit code. */ return err; }