138032Speter/*
238032Speter * By John G. Myers, jgm+@cmu.edu
338032Speter * Version 1.2
438032Speter *
538032Speter * Process a BITNET "internet.listing" file, producing output
638032Speter * suitable for input to makemap.
738032Speter *
838032Speter * The input file can be obtained via anonymous FTP to bitnic.educom.edu.
938032Speter * Change directory to "netinfo" and get the file internet.listing
1038032Speter * The file is updated monthly.
1138032Speter *
1264565Sgshapiro * Feed the output of this program to "makemap hash /etc/mail/bitdomain.db"
1338032Speter * to create the table used by the "FEATURE(bitdomain)" config file macro.
1438032Speter * If your sendmail does not have the db library compiled in, you can instead
1564565Sgshapiro * use "makemap dbm /etc/mail/bitdomain" and
1664565Sgshapiro * "FEATURE(bitdomain,`dbm -o /etc/mail/bitdomain')"
1738032Speter *
1838032Speter * The bitdomain table should be rebuilt monthly.
1938032Speter */
2038032Speter
2138032Speter#include <stdio.h>
2238032Speter#include <errno.h>
2338032Speter#include <sys/types.h>
2438032Speter#include <netinet/in.h>
2538032Speter#include <arpa/nameser.h>
2638032Speter#include <resolv.h>
2738032Speter#include <netdb.h>
2838032Speter#include <ctype.h>
2938032Speter#include <string.h>
3038032Speter
3138032Speter/* don't use sizeof because sizeof(long) is different on 64-bit machines */
3238032Speter#define SHORTSIZE	2	/* size of a short (really, must be 2) */
3338032Speter#define LONGSIZE	4	/* size of a long (really, must be 4) */
3438032Speter
3538032Spetertypedef union
3638032Speter{
3738032Speter	HEADER	qb1;
3838032Speter	char	qb2[PACKETSZ];
3938032Speter} querybuf;
4038032Speter
4138032Speterextern int h_errno;
4238032Speterextern char *malloc();
4338032Speterextern char *optarg;
4438032Speterextern int optind;
4538032Speter
4638032Speterchar *lookup();
4738032Speter
4838032Spetermain(argc, argv)
4938032Speterint argc;
5038032Speterchar **argv;
5138032Speter{
5238032Speter    int opt;
5338032Speter
5438076Speter    while ((opt = getopt(argc, argv, "o:")) != -1) {
5538032Speter	switch (opt) {
5638032Speter	case 'o':
5738032Speter	    if (!freopen(optarg, "w", stdout)) {
5838032Speter		perror(optarg);
5938032Speter		exit(1);
6038032Speter	    }
6138032Speter	    break;
6238032Speter
6338032Speter	default:
6438032Speter	    fprintf(stderr, "usage: %s [-o outfile] [internet.listing]\n",
6538032Speter		    argv[0]);
6638032Speter	    exit(1);
6738032Speter	}
6838032Speter    }
6938032Speter
7038032Speter    if (optind < argc) {
7138032Speter	if (!freopen(argv[optind], "r", stdin)) {
7238032Speter	    perror(argv[optind]);
7338032Speter	    exit(1);
7438032Speter	}
7538032Speter    }
7638032Speter    readfile(stdin);
7738032Speter    finish();
7838032Speter    exit(0);
7938032Speter}
8038032Speter
8138032Speter/*
8238032Speter * Parse and process an input file
8338032Speter */
8438032Speterreadfile(infile)
8538032SpeterFILE *infile;
8638032Speter{
8738032Speter    int skippingheader = 1;
8838032Speter    char buf[1024], *node, *hostname, *p;
8938032Speter
9038032Speter    while (fgets(buf, sizeof(buf), infile)) {
9138032Speter	for (p = buf; *p && isspace(*p); p++);
9238032Speter	if (!*p) {
9338032Speter	    skippingheader = 0;
9438032Speter	    continue;
9538032Speter	}
9638032Speter	if (skippingheader) continue;
9738032Speter
9838032Speter	node = p;
9938032Speter	for (; *p && !isspace(*p); p++) {
10038032Speter	    if (isupper(*p)) *p = tolower(*p);
10138032Speter	}
10238032Speter	if (!*p) {
10338032Speter	    fprintf(stderr, "%-8s: no domain name in input file\n", node);
10438032Speter	    continue;
10538032Speter	}
10638032Speter	*p++ = '\0';
10738032Speter
10838032Speter	for (; *p && isspace(*p); p++) ;
10938032Speter	if (!*p) {
11038032Speter	    fprintf(stderr, "%-8s no domain name in input file\n", node);
11138032Speter	    continue;
11238032Speter	}
11338032Speter
11438032Speter	hostname = p;
11538032Speter	for (; *p && !isspace(*p); p++) {
11638032Speter	    if (isupper(*p)) *p = tolower(*p);
11738032Speter	}
11838032Speter	*p = '\0';
11938032Speter
12038032Speter	/* Chop off any trailing .bitnet */
12138032Speter	if (strlen(hostname) > 7 &&
12238032Speter	    !strcmp(hostname+strlen(hostname)-7, ".bitnet")) {
12338032Speter	    hostname[strlen(hostname)-7] = '\0';
12438032Speter	}
12538032Speter	entry(node, hostname, sizeof(buf)-(hostname - buf));
12638032Speter    }
12738032Speter}
12838032Speter
12938032Speter/*
13038032Speter * Process a single entry in the input file.
13138032Speter * The entry tells us that "node" expands to "domain".
13238032Speter * "domain" can either be a domain name or a bitnet node name
13338032Speter * The buffer pointed to by "domain" may be overwritten--it
13438032Speter * is of size "domainlen".
13538032Speter */
13638032Speterentry(node, domain, domainlen)
13738032Speterchar *node;
13838032Speterchar *domain;
13938032Speterchar *domainlen;
14038032Speter{
14138032Speter    char *otherdomain, *p, *err;
14238032Speter
14338032Speter    /* See if we have any remembered information about this node */
14438032Speter    otherdomain = lookup(node);
14538032Speter
14638032Speter    if (otherdomain && strchr(otherdomain, '.')) {
14738032Speter	/* We already have a domain for this node */
14838032Speter	if (!strchr(domain, '.')) {
14938032Speter	    /*
15038032Speter	     * This entry is an Eric Thomas FOO.BITNET kludge.
15138032Speter	     * He doesn't want LISTSERV to do transitive closures, so we
15238032Speter	     * do them instead.  Give the the domain expansion for "node"
15338032Speter	     * (which is in "otherdomian") to FOO (which is in "domain")
15438032Speter	     * if "domain" doesn't have a domain expansion already.
15538032Speter	     */
15638032Speter	    p = lookup(domain);
15738032Speter	    if (!p || !strchr(p, '.')) remember(domain, otherdomain);
15838032Speter	}
15938032Speter    }
16038032Speter    else {
16138032Speter	if (!strchr(domain, '.') || valhost(domain, domainlen)) {
16238032Speter	    remember(node, domain);
16338032Speter	    if (otherdomain) {
16438032Speter		/*
16538032Speter		 * We previously mapped the node "node" to the node
16638032Speter		 * "otherdomain".  If "otherdomain" doesn't already
16738032Speter		 * have a domain expansion, give it the expansion "domain".
16838032Speter		 */
16938032Speter		p = lookup(otherdomain);
17038032Speter		if (!p || !strchr(p, '.')) remember(otherdomain, domain);
17138032Speter	    }
17238032Speter	}
17338032Speter	else {
17438032Speter	    switch (h_errno) {
17538032Speter	    case HOST_NOT_FOUND:
17638032Speter		err = "not registered in DNS";
17738032Speter		break;
17838032Speter
17938032Speter	    case TRY_AGAIN:
18038032Speter		err = "temporary DNS lookup failure";
18138032Speter		break;
18238032Speter
18338032Speter	    case NO_RECOVERY:
18438032Speter		err = "non-recoverable nameserver error";
18538032Speter		break;
18638032Speter
18738032Speter	    case NO_DATA:
18838032Speter		err = "registered in DNS, but not mailable";
18938032Speter		break;
19064565Sgshapiro
19138032Speter	    default:
19238032Speter		err = "unknown nameserver error";
19338032Speter		break;
19438032Speter	    }
19538032Speter
19638032Speter	    fprintf(stderr, "%-8s %s %s\n", node, domain, err);
19738032Speter	}
19838032Speter    }
19938032Speter}
20038032Speter
20138032Speter/*
20238032Speter * Validate whether the mail domain "host" is registered in the DNS.
20338032Speter * If "host" is a CNAME, it is expanded in-place if the expansion fits
20438032Speter * into the buffer of size "hbsize".  Returns nonzero if it is, zero
20538032Speter * if it is not.  A BIND error code is left in h_errno.
20638032Speter */
20738032Speterint
20838032Spetervalhost(host, hbsize)
20938032Speter	char *host;
21038032Speter	int hbsize;
21138032Speter{
21238032Speter	register u_char *eom, *ap;
21364565Sgshapiro	register int n;
21438032Speter	HEADER *hp;
21538032Speter	querybuf answer;
21638032Speter	int ancount, qdcount;
21738032Speter	int ret;
21838032Speter	int type;
21938032Speter	int qtype;
22038032Speter	char nbuf[1024];
22138032Speter
22238032Speter	if ((_res.options & RES_INIT) == 0 && res_init() == -1)
22338032Speter		return (0);
22438032Speter
22538032Speter	_res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
22638032Speter	_res.retrans = 30;
22738032Speter	_res.retry = 10;
22838032Speter
22938032Speter	qtype = T_ANY;
23038032Speter
23138032Speter	for (;;) {
23238032Speter		h_errno = NO_DATA;
23338032Speter		ret = res_querydomain(host, "", C_IN, qtype,
23438032Speter				      &answer, sizeof(answer));
23538032Speter		if (ret <= 0)
23638032Speter		{
23738032Speter			if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
23838032Speter			{
23938032Speter				/* the name server seems to be down */
24038032Speter				h_errno = TRY_AGAIN;
24138032Speter				return 0;
24238032Speter			}
24338032Speter
24438032Speter			if (h_errno != HOST_NOT_FOUND)
24538032Speter			{
24638032Speter				/* might have another type of interest */
24738032Speter				if (qtype == T_ANY)
24838032Speter				{
24938032Speter					qtype = T_A;
25038032Speter					continue;
25138032Speter				}
25238032Speter				else if (qtype == T_A)
25338032Speter				{
25438032Speter					qtype = T_MX;
25538032Speter					continue;
25638032Speter				}
25738032Speter			}
25838032Speter
25938032Speter			/* otherwise, no record */
26038032Speter			return 0;
26138032Speter		}
26238032Speter
26338032Speter		/*
26438032Speter		**  This might be a bogus match.  Search for A, MX, or
26538032Speter		**  CNAME records.
26638032Speter		*/
26738032Speter
26838032Speter		hp = (HEADER *) &answer;
26938032Speter		ap = (u_char *) &answer + sizeof(HEADER);
27038032Speter		eom = (u_char *) &answer + ret;
27138032Speter
27238032Speter		/* skip question part of response -- we know what we asked */
27338032Speter		for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
27438032Speter		{
27538032Speter			if ((ret = dn_skipname(ap, eom)) < 0)
27638032Speter			{
27738032Speter				return 0;		/* ???XXX??? */
27838032Speter			}
27938032Speter		}
28038032Speter
28138032Speter		for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
28238032Speter		{
28338032Speter			n = dn_expand((u_char *) &answer, eom, ap,
28438032Speter				      (u_char *) nbuf, sizeof nbuf);
28538032Speter			if (n < 0)
28638032Speter				break;
28738032Speter			ap += n;
28838032Speter			GETSHORT(type, ap);
28938032Speter			ap += SHORTSIZE + LONGSIZE;
29038032Speter			GETSHORT(n, ap);
29138032Speter			switch (type)
29238032Speter			{
29338032Speter			  case T_MX:
29438032Speter			  case T_A:
29538032Speter				return 1;
29638032Speter
29738032Speter			  case T_CNAME:
29838032Speter				/* value points at name */
29938032Speter				if ((ret = dn_expand((u_char *)&answer,
30038032Speter				    eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0)
30138032Speter					break;
30238032Speter				if (strlen(nbuf) < hbsize) {
30338032Speter				    (void)strcpy(host, nbuf);
30438032Speter				}
30538032Speter				return 1;
30638032Speter
30738032Speter			  default:
30838032Speter				/* not a record of interest */
30938032Speter				continue;
31038032Speter			}
31138032Speter		}
31238032Speter
31338032Speter		/*
31438032Speter		**  If this was a T_ANY query, we may have the info but
31538032Speter		**  need an explicit query.  Try T_A, then T_MX.
31638032Speter		*/
31738032Speter
31838032Speter		if (qtype == T_ANY)
31938032Speter			qtype = T_A;
32038032Speter		else if (qtype == T_A)
32138032Speter			qtype = T_MX;
32238032Speter		else
32338032Speter			return 0;
32438032Speter	}
32538032Speter}
32638032Speter
32738032Speterstruct entry {
32838032Speter    struct entry *next;
32938032Speter    char *node;
33038032Speter    char *domain;
33138032Speter};
33238032Speterstruct entry *firstentry;
33338032Speter
33438032Speter/*
33538032Speter * Find any remembered information about "node"
33638032Speter */
33738032Speterchar *lookup(node)
33838032Speterchar *node;
33938032Speter{
34038032Speter    struct entry *p;
34138032Speter
34238032Speter    for (p = firstentry; p; p = p->next) {
34338032Speter	if (!strcmp(node, p->node)) {
34438032Speter	    return p->domain;
34538032Speter	}
34638032Speter    }
34738032Speter    return 0;
34838032Speter}
34938032Speter
35038032Speter/*
35138032Speter * Mark the node "node" as equivalent to "domain".  "domain" can either
35238032Speter * be a bitnet node or a domain name--if it is the latter, the mapping
35338032Speter * will be written to stdout.
35438032Speter */
35538032Speterremember(node, domain)
35638032Speterchar *node;
35738032Speterchar *domain;
35838032Speter{
35938032Speter    struct entry *p;
36038032Speter
36138032Speter    if (strchr(domain, '.')) {
36238032Speter	fprintf(stdout, "%-8s %s\n", node, domain);
36338032Speter    }
36438032Speter
36538032Speter    for (p = firstentry; p; p = p->next) {
36638032Speter	if (!strcmp(node, p->node)) {
36738032Speter	    p->domain = malloc(strlen(domain)+1);
36838032Speter	    if (!p->domain) {
36938032Speter		goto outofmemory;
37038032Speter	    }
37138032Speter	    strcpy(p->domain, domain);
37238032Speter	    return;
37338032Speter	}
37438032Speter    }
37538032Speter
37638032Speter    p = (struct entry *)malloc(sizeof(struct entry));
37738032Speter    if (!p) goto outofmemory;
37838032Speter
37938032Speter    p->next = firstentry;
38038032Speter    firstentry = p;
38138032Speter    p->node = malloc(strlen(node)+1);
38238032Speter    p->domain = malloc(strlen(domain)+1);
38338032Speter    if (!p->node || !p->domain) goto outofmemory;
38438032Speter    strcpy(p->node, node);
38538032Speter    strcpy(p->domain, domain);
38638032Speter    return;
38738032Speter
38838032Speter  outofmemory:
38938032Speter    fprintf(stderr, "Out of memory\n");
39038032Speter    exit(1);
39138032Speter}
39238032Speter
39338032Speter/*
39438032Speter * Walk through the database, looking for any cases where we know
39538032Speter * node FOO is equivalent to node BAR and node BAR has a domain name.
39638032Speter * For those cases, give FOO the same domain name as BAR.
39738032Speter */
39838032Speterfinish()
39938032Speter{
40038032Speter    struct entry *p;
40138032Speter    char *domain;
40238032Speter
40338032Speter    for (p = firstentry; p; p = p->next) {
40438032Speter	if (!strchr(p->domain, '.') && (domain = lookup(p->domain))) {
40538032Speter	    remember(p->node, domain);
40638032Speter	}
40738032Speter    }
40838032Speter}
40964565Sgshapiro
410