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