home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <netdb.h>
- #include <sysexits.h>
- #include <sys/errno.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/nameser.h>
- #include "miscerrs.h"
-
- /* imports */
- extern int errno, h_errno;
- extern char *malloc(), *strcpy(), *inet_ntoa();
-
- /* exports */
- int mxconnect();
-
- /* private */
- #define MAXMXLIST 10
- static struct mxitem {
- char *host;
- u_short pref;
- u_char localflag;
- } MXlist[MAXMXLIST + 1];
- static char *strsave();
- static int buildmxlist();
- static void mxsave(), mxinsert(), mxlocal();
- static struct hostent *getmxhost();
-
- #ifdef MXMAIN
-
- #define bomb return
-
- main(argc, argv)
- char **argv;
- { int fd;
- char buf[BUFSIZ], *crlf, *index();
- struct mxitem *mxp;
-
- for (;;) {
- printf("domain: ");
- if (argc > 1)
- strcpy(buf, argv[1]);
- else if (gets(buf) == 0)
- break;
- if ((fd = mxconnect(buf)) >= 0)
- if (read(fd, buf, 512) > 0) {
- if ((crlf = index(buf, '\r')) != 0)
- strcpy(crlf, "\n");
- puts(buf);
- } else
- perror("read");
- close(fd);
- if (argc > 1)
- break;
- for (mxp = MXlist; mxp < MXlist + MAXMXLIST + 1; mxp++)
- mxp->host = 0;
- }
- return 0;
- }
- #endif
-
- mxconnect(host)
- char *host;
- { int s, lport, mxfatal;
- char **addr, errbuf[256];
- struct hostent *hp;
- struct servent *sp;
- struct sockaddr_in sin;
- struct mxitem *mxp;
-
- mxfatal = buildmxlist(host);
- if (MXlist[0].host == 0)
- MXlist[0].host = host;
- if ((sp = getservbyname ("smtp", "tcp")) == NULL) {
- (void)fprintf(stderr,"unknown service TCP/smtp\n");
- bomb(E_OSFILE);
- }
- (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) 0, 0);
-
- /* slop in the loop -- i hate the socket dance */
- for (mxp = MXlist; mxp->host; mxp++) {
- if ((s = rresvport(&lport)) < 0) {
- perror("rresvport");
- bomb(E_CANTOPEN);
- }
- if ((hp = getmxhost(mxp->host)) == 0) {
- (void) close(s);
- if (mxfatal)
- bomb(E_NOHOST);
- continue;
- }
- bzero((char *)&sin, sizeof(sin));
- sin.sin_port = sp->s_port;
- sin.sin_family = hp->h_addrtype;
- for (addr = hp->h_addr_list; *addr; addr++) {
- bcopy(*addr, (char *) &sin.sin_addr, hp->h_length);
- if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- sprintf(errbuf, "%s [%s]", mxp->host, inet_ntoa(sin.sin_addr));
- perror(errbuf);
- continue;
- }
- return s;
- }
- close(s);
- }
-
- bomb(E_TEMPFAIL);
- }
-
- /* return 1 for fatal MX error (authoritative NXDOMAIN), 0 o.w. */
- static int
- buildmxlist(host)
- char *host;
- { register HEADER *hp;
- register char *cp;
- register int n;
- char q[PACKETSZ], a[PACKETSZ]; /* query, answer */
- char *eom, *bp;
- int buflen, ancount, qdcount;
- char hostbuf[BUFSIZ+1];
- u_short preference, reclen;
-
- if ((n = res_mkquery(QUERY, host, C_IN, T_MX, (char *) 0, 0, (struct rrec *) 0, q, sizeof(q))) < 0)
- return 0;
- n = res_send(q, n, a, sizeof(a));
- if (n < 0)
- return 0;
- eom = a + n;
- hp = (HEADER *) a;
- ancount = ntohs(hp->ancount);
- qdcount = ntohs(hp->qdcount);
- if (hp->rcode != NOERROR || ancount == 0)
- return hp->rcode == NXDOMAIN && hp->aa;
- bp = hostbuf;
- buflen = sizeof(hostbuf);
- cp = a + sizeof(HEADER);
- while (--qdcount >= 0)
- cp += dn_skip(cp) + QFIXEDSZ;
- /* TODO: if type is CNAME, reissue query */
- while (--ancount >= 0 && cp < eom) {
- cp += dn_skip(cp) /* name */
- + sizeof(u_short) /* type */
- + sizeof(u_short) /* class */
- + sizeof(u_long); /* ttl (see rfc973) */
- reclen = _getshort(cp);
- cp += sizeof(u_short);
- preference = _getshort(cp);
- if ((n = dn_expand(a, eom, cp + sizeof(u_short), bp, buflen)) < 0)
- break;
- mxsave(bp, preference);
- cp += reclen;
- }
- mxlocal();
- return 0;
- }
-
- /* NOT TODO: issue WKS query. (just try to connect.) */
-
- static void
- mxsave(host, pref)
- char *host;
- u_short pref;
- { struct mxitem *mxp;
- int localflag;
- static char thishost[64];
-
- if (*thishost == 0)
- gethostname(thishost, sizeof(thishost));
-
- if (MXlist[MAXMXLIST].host)
- return; /* full */
-
- localflag = (strcmp(thishost, host) == 0);
-
- /* insertion sort */
- for (mxp = MXlist; mxp < MXlist + MAXMXLIST; mxp++) {
- if (mxp->host == 0) {
- mxinsert(mxp, host, pref, localflag);
- return;
- }
- if (pref < mxp->pref) {
- mxinsert(mxp, host, pref, localflag);
- return;
- }
- if (pref == mxp->pref) {
- if (mxp->localflag)
- return;
- if (localflag) {
- mxp->host = strsave(host);
- mxp->pref = pref;
- mxp->localflag = localflag;
- (++mxp)->host = 0;
- return;
- }
- mxinsert(mxp, host, pref, localflag);
- return;
- }
- }
- }
-
- static void
- mxinsert(mxlistp, host, pref, localflag)
- struct mxitem *mxlistp;
- char *host;
- u_short pref;
- { register struct mxitem *mxp;
-
- for (mxp = MXlist + MAXMXLIST - 1; mxp > mxlistp; --mxp)
- *mxp = mxp[-1];
- mxp->host = strsave(host);
- mxp->pref = pref;
- mxp->localflag = localflag;
- }
-
- static char *
- strsave(str)
- register char *str;
- { register char *rval;
-
- if ((rval = malloc(strlen(str) + 1)) == 0) {
- perror("malloc");
- bomb(-EX_SOFTWARE);
- }
- strcpy(rval, str);
- return rval;
- }
-
- static void
- mxlocal()
- { register struct mxitem *mxp;
-
- if (MXlist[0].host == 0)
- return;
-
- for (mxp = MXlist; mxp->host; mxp++) {
- if (mxp->localflag) {
- mxp->host = 0;
- break;
- }
- }
- }
-
- static struct hostent *
- getmxhost(host)
- char *host;
- { struct hostent *hp, *gethostbyname();
-
- if ((hp = gethostbyname(host)) != 0)
- return hp;
-
- switch(h_errno) {
-
- case HOST_NOT_FOUND:
- (void) fprintf(stderr, "unknown host (%s).\n", host);
- break;
-
- case TRY_AGAIN:
- (void) fprintf(stderr, "name server not responding (%s).\n", host);
- break;
-
- case NO_RECOVERY:
- (void) fprintf(stderr, "name server error (%s).\n", host);
- break;
-
- case NO_ADDRESS:
- (void) fprintf(stderr, "no IP address (%s).\n", host);
- break;
-
- default:
- (void) fprintf(stderr, "unknown resolver error (%s).\n", host);
- break;
- }
- return 0;
- }
-