Logo Search packages:      
Sourcecode: zmailer version File versions  Download package

mxverify-cgi.c

/*
 *  mxverify-cgi  -- a ZMailer associated utility for doing web-based
 *                   analysis of ``is my incoming email working properly ?''
 *
 *  By Matti Aarnio <mea@nic.funet.fi> 20-Jan-2000, 2001, 2003
 *
 *  This program plays fast&loose with HTTP/CGI interface, and presumes
 *  quite exactly the <FORM ... > stuff that is present in the file
 *  mxverify-cgi.html
 *
 */


#include "hostenv.h"

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include "zmsignal.h"
#include <string.h>
#include <sysexits.h>

/* #include <strings.h> */ /* poorly portable.. */
#ifdef HAVE_STDARG_H
# include <stdarg.h>
#else
# include <varargs.h>
#endif
#include <fcntl.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <setjmp.h>

#include "zresolv.h"
#include "libc.h"

#ifdef _AIX /* Defines NFDBITS, et.al. */
#include <sys/types.h>
#endif

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#include <sys/time.h>

#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("<H1>ERROR:  RECURSIVE CNAME ON DNS LOOKUPS: domain=``%s''</H1>\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("<H1>ERROR:  res_mkquery() failed! domain=``%s''</H1>\n", host);
        return EX_SOFTWARE;
      }

      htmlprintf("<H1>Doing resolver lookup for T=MX domain=``%s''</H1>\n", host);

      n = res_send((void*)&qbuf, qlen, (void*)&answer, sizeof answer);
      if (n < 0) {
        htmlprintf("<H1>ERROR:  No resolver response for domain=``%s''</H1>\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("<H1>ERROR:  NO SUCH DOMAIN: ``%s''</H1>\n", host);
          return EX_TEMPFAIL;
        case SERVFAIL:
          htmlprintf("<H1>ERROR:  DNS Server Failure: domain=``%s''</H1>\n", host);
          return EX_TEMPFAIL;
        case NOERROR:
          htmlprintf("<H1>Questionable:  NO MX DATA: domain=``%s''  We SIMULATE!</H1>\n", host);
          htmlprintf("<H1>Do have at least one MX entry added!</H1>\n");
          mx[0].host = host;
          mx[0].pref = 999999;
          mx[1].host = NULL;
          return 0;
        case FORMERR:
          htmlprintf("<H1>ERROR:  DNS Internal FORMERR error: domain=``%s''</H1>\n", host);
          return EX_NOPERM;
        case NOTIMP:
          htmlprintf("<H1>ERROR:  DNS Internal NOTIMP error: domain=``%s''</H1>\n", host);
          return EX_NOPERM;
        case REFUSED:
          htmlprintf("<H1>ERROR:  DNS Internal REFUSED error: domain=``%s''</H1>\n", host);
          return EX_NOPERM;
        }
        htmlprintf("<H1>ERROR:  DNS Unknown Error! (rcode=%d) domain=``%s''</H1>\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("<P><H1>DNS yields following MX entries\n</H1><PRE>\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("</PRE>\n<P>\n");

      if (nmx == 1) {
        htmlprintf("<H2>Only one MX record...\n<BR>Well, no backups, but as all systems are looking for MX record <I>in every case</I>, not bad..</H2>\n<P>\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("<H2>ERROR: Failed to create %s type socket! err='%s'</H2>\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("<CODE>[ CONNECTED! ]</CODE><BR>\n");

        ++conn_ok;

        return EX_OK;
      }

      close(sk);

      se = strerror(errnosave);
      htmlprintf("<H2>ERROR: Connect failure reason: %s</H2><BR>(Still possibly all OK!)<BR>\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:<postmaster@thatdomain> + close

         --- and perhaps if it turns out to be becessary, also:

         2) HELO + MAIL FROM:<postmaster@thisdomain> +
                   RCPT TO:<postmaster@thatdomain> + 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("<PRE>\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("</PRE><P>\n<H2>Grrr...  Doesn't understand ESMTP EHLO greeting</H2><P>\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("<PRE>\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("</PRE><P>\n");
        htmlprintf("<H3>Excellent! It speaks ESMTP!</H3>\n");
        if (!plaintext) htmlprintf("<P><PRE>\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("</PRE><P>");
      if (rc < 0 || rc > 299) { 
        htmlprintf("<H2>Grr! Rejects NULL return path; see RFC 2821 section 6.1</H2>\n");
        nullreject = 1;
      } else {
        htmlprintf("<H4>Fine, it accepts NULL return-path as is mandated by RFC 2821 section 6.1</H4>\n");
      }
      if (!plaintext) htmlprintf("<P><PRE>");
      
      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:<postmaster@%s>\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:<postmaster@%s>\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</PRE>\n");
        htmlprintf("<H2>Eh ? What ?  No ``postmaster'' supported there ?  That violates RFC 2821 section 4.5.1.</H2>\n");
        if (!plaintext) htmlprintf("<PRE>\n");
      }


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


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

      htmlprintf("\n</PRE>\n");
      /* htmlprintf("RC = %d\n", rc); */
      if (wtout)
        htmlprintf("<H2> WRITE TIMEOUT!</H2>\n");
      else if (rc == 0 && !nullreject)
        htmlprintf("<H2>Apparently OK!</H2>\n");
      else if (rc == 0 && nullreject)
        htmlprintf("<H2>Rejects RFC 2821 section 6.1 defined mandatorily supported source address format, otherwise appears to work!</H2>\n");
      else
        htmlprintf("<H2>Something WRONG!! rc=%d</H2>\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("<H2> --- sorry, address lookup for ``%s'' failed;<BR>\n code = %s</H2>\n", hname, gai_strerror(i));
        return i;
      }
      if (!ai) {
        htmlprintf("Address lookup <B>did not</B> yield any addresses!\n");
        return EX_DATAERR;
      }

      htmlprintf("Address lookup did yield following ones:\n<P>\n");
      htmlprintf("<PRE>\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("</PRE>\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("<P>\n");
        htmlprintf("<H2>Testing server at address: %s</H2>\n", buf);
        htmlprintf("<P>\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("<P>\n");
        if (plaintext)
          fprintf(stdout, "-----------------------------------------------------------------------\n");
        else
          fprintf(stdout, "<HR>\n");
        htmlprintf("<H1>Testing MX server: %s</H1>\n<P>\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("<HTML><HEAD><TITLE>MX-VERIFY-CGI run for ``%s''</TITLE></HEAD>\n", getstr);
  if (!plaintext) {
    fprintf(stdout, "<BODY BGCOLOR=WHITE TEXT=BLACK LINK=#0000EE VLINK=#551A8B ALINK=RED>\n\n");

    htmlprintf("<H1>MX-VERIFY-CGI run for ``%s''</H1>\n", getstr);
    fprintf(stdout, "<P><HR>\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, "<P>\n");
    fprintf(stdout, "Sorry, NO MX-VERIFY-CGI run with this input!<P>\n");
  }
  if (!plaintext) {
    fprintf(stdout, "<P><HR></BODY></HTML>\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;
}

Generated by  Doxygen 1.6.0   Back to index