File:  [Research Unix] / researchv10no / cmd / nupas / ipc / mxconnect.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:21:35 2018 UTC (8 years, 1 month ago) by root
Branches: belllabs, MAIN
CVS tags: researchv10, HEAD
researchv10 Norman

#include <sys/types.h>			/* needed for socket.h */
#include <sys/uio.h>			/* needed for socket.h */
#include <sys/socket.h>
#include <sysexits.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <arpa/nameser.h>
#include <string.h>

/* imports */
extern char *malloc(), *strcpy(), *inet_ntoa();

extern int ipcdebug;
extern char syserrstr[];

/* 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();

mx_connect(dest, param)
char *dest, *param;
{
	int s, lport, mxfatal;
	char **addr;
	char host[100];
	struct hostent *hp;
	struct sockaddr_in sin;
	struct mxitem *mxp;
	extern int tcp_service();
	int port;
	char *bp;
	int errtype = 0;
	char errstr[256];

	strcpy(host, dest);

	if ((port = tcp_service(host)) < 0) {
		ipcsyserr("unknown service");
		return(-1);
	}

	mxfatal = buildmxlist(host);
	if (MXlist[0].host == 0)
		return(tcp_connect(dest, param));
		/*MXlist[0].host = host;*/

	(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++) {
		int i;

		if ((hp = getmxhost(mxp->host)) == 0) {
			if (mxfatal) {
				ipcsyserr("stub - can't find useful host"); /*EX_NOHOST*/
				return -1;
			}
			continue;
		}
		for (i=0; hp->h_addr_list[i]; i++) {
			s = socket(AF_INET, SOCK_STREAM, 0);
			if (s < 0) {
				ipcsyserr("Can't open socket"); /*EX_OSERR*/
				return -1;
			}
			bzero((char *)&sin, sizeof(sin));
			sin.sin_port = port;
			sin.sin_family = hp->h_addrtype;
			bcopy(hp->h_addr_list[i], (char *) &sin.sin_addr,
				hp->h_length);
			if (ipcdebug)
                                fprintf(stderr, "try %s [%s]\n", mxp->host,
				     inet_ntoa(sin.sin_addr));
			if (connect(s, (struct sockaddr *)&sin, sizeof(sin))>=0) {
				if (ipcdebug)
					fprintf(stderr, " connected\n");
				return s;
			}
			ipcgetsyserr();
			fprintf(stderr, "Couldn't connect to %s [%s]: %s\n",
				mxp->host, inet_ntoa(sin.sin_addr), syserrstr);
			if (errtype == 0 || 
			   (errtype != EX_TEMPFAIL && ipcmaperror()==EX_TEMPFAIL)) {
				errtype = ipcmaperror();
				strcpy(errstr, syserrstr);
			}
			close(s);
		}
	}
	ipcseterror(errtype, errstr, "Can't connect to host");
	return -1;
}

/* 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;
	int niter = 0;
	char nhostbuf[BUFSIZ+1];

again:
	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;
	if (ipcdebug)
		fprintf(stderr, "buildmxlist got %d\n", n);
	eom = a + n;
	hp = (HEADER *) a;
	ancount = ntohs(hp->ancount);
	qdcount = ntohs(hp->qdcount);
	if (ipcdebug)
		fprintf(stderr, "buildmx rcode %d ancount %d qdcount %d aa %d\n",
		 hp->rcode, ancount, qdcount, hp->aa);
	if (hp->rcode != NOERROR || ancount == 0) {
		if (hp->aa == 0)
			return 0;	/* non-authoritative in any event */
		return hp->rcode == NOERROR || hp->rcode == NXDOMAIN;
	}
	bp = hostbuf;
	buflen = sizeof(hostbuf);
	cp = a + sizeof(HEADER);
	if (ipcdebug>1) {
		int i;
		fprintf(stderr, "buildmx got:");
		for (i=0; i<n; i++)
			if (a[i]>=' '&& a[i]<0177)
				fprintf(stderr, "%c", a[i]);
			else fprintf(stderr, "\\%.3o", a[i]&0377);
		fprintf(stderr, "\n");
	}
	while (--qdcount >= 0)
		cp += dn_skipname(cp,eom) + QFIXEDSZ;
	/* TODO: if type is CNAME, reissue query */
	while (--ancount >= 0 && cp < eom) {
		int type;
		cp += dn_skipname(cp,eom);	/* name */
		type = _getshort(cp);	
		cp += sizeof(u_short)	/* type */
		    + sizeof(u_short)	/* class */
		    + sizeof(u_long);	/* ttl (see rfc973) */
		reclen = _getshort(cp);
		cp += sizeof(u_short);
		if (type==T_CNAME) {	/* canonical name */
			if (dn_expand(a, eom, cp, nhostbuf, BUFSIZ) < 0)
				if (ipcdebug)
					fprintf(stderr, "CNAME? in mxexpand\n");
			host = nhostbuf;
			if (++niter>10)
				return 0;
			if (ipcdebug)
				fprintf(stderr, "CNAME, try with %s\n", host);
			goto again;
		}
		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 (ipcdebug)
		fprintf(stderr, "MXsave %s\n", host);

	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) {
		ipcsyserr("mx connect malloc");
		exit(1);
	}
	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();
	long inet_addr();
	int err;
	char errbuf[256];
	static struct in_addr ia;
	static char *hlist[2] = {0, 0};
	static struct hostent he =
		{0, 0, AF_INET, sizeof(struct in_addr), hlist};

	if (!host)
		return 0;

	if ((hp = gethostbyname(host)) != 0)
		return hp;
	ipcgetsyserr();

	if ((ia.s_addr = inet_addr(host)) != -1) {
		he.h_addr = (char *)&ia;
		return &he;
	}
	sprintf(errbuf, "Error looking up MX Host '%s'.\n", host);
	ipcsyserr(errbuf);
	return 0;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.