source: trunk/third/sendmail/contrib/bitdomain.c @ 19204

Revision 19204, 8.7 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * By John G. Myers, jgm+@cmu.edu
3 * Version 1.2
4 *
5 * Process a BITNET "internet.listing" file, producing output
6 * suitable for input to makemap.
7 *
8 * The input file can be obtained via anonymous FTP to bitnic.educom.edu.
9 * Change directory to "netinfo" and get the file internet.listing
10 * The file is updated monthly.
11 *
12 * Feed the output of this program to "makemap hash /etc/mail/bitdomain.db"
13 * to create the table used by the "FEATURE(bitdomain)" config file macro.
14 * If your sendmail does not have the db library compiled in, you can instead
15 * use "makemap dbm /etc/mail/bitdomain" and
16 * "FEATURE(bitdomain,`dbm -o /etc/mail/bitdomain')"
17 *
18 * The bitdomain table should be rebuilt monthly.
19 */
20
21#include <stdio.h>
22#include <errno.h>
23#include <sys/types.h>
24#include <netinet/in.h>
25#include <arpa/nameser.h>
26#include <resolv.h>
27#include <netdb.h>
28#include <ctype.h>
29#include <string.h>
30
31/* don't use sizeof because sizeof(long) is different on 64-bit machines */
32#define SHORTSIZE       2       /* size of a short (really, must be 2) */
33#define LONGSIZE        4       /* size of a long (really, must be 4) */
34
35typedef union
36{
37        HEADER  qb1;
38        char    qb2[PACKETSZ];
39} querybuf;
40
41extern int h_errno;
42extern char *malloc();
43extern char *optarg;
44extern int optind;
45
46char *lookup();
47
48main(argc, argv)
49int argc;
50char **argv;
51{
52    int opt;
53
54    while ((opt = getopt(argc, argv, "o:")) != -1) {
55        switch (opt) {
56        case 'o':
57            if (!freopen(optarg, "w", stdout)) {
58                perror(optarg);
59                exit(1);
60            }
61            break;
62
63        default:
64            fprintf(stderr, "usage: %s [-o outfile] [internet.listing]\n",
65                    argv[0]);
66            exit(1);
67        }
68    }
69
70    if (optind < argc) {
71        if (!freopen(argv[optind], "r", stdin)) {
72            perror(argv[optind]);
73            exit(1);
74        }
75    }
76    readfile(stdin);
77    finish();
78    exit(0);
79}
80
81/*
82 * Parse and process an input file
83 */
84readfile(infile)
85FILE *infile;
86{
87    int skippingheader = 1;
88    char buf[1024], *node, *hostname, *p;
89
90    while (fgets(buf, sizeof(buf), infile)) {
91        for (p = buf; *p && isspace(*p); p++);
92        if (!*p) {
93            skippingheader = 0;
94            continue;
95        }
96        if (skippingheader) continue;
97
98        node = p;
99        for (; *p && !isspace(*p); p++) {
100            if (isupper(*p)) *p = tolower(*p);
101        }
102        if (!*p) {
103            fprintf(stderr, "%-8s: no domain name in input file\n", node);
104            continue;
105        }
106        *p++ = '\0';
107
108        for (; *p && isspace(*p); p++) ;
109        if (!*p) {
110            fprintf(stderr, "%-8s no domain name in input file\n", node);
111            continue;
112        }
113
114        hostname = p;
115        for (; *p && !isspace(*p); p++) {
116            if (isupper(*p)) *p = tolower(*p);
117        }
118        *p = '\0';
119
120        /* Chop off any trailing .bitnet */
121        if (strlen(hostname) > 7 &&
122            !strcmp(hostname+strlen(hostname)-7, ".bitnet")) {
123            hostname[strlen(hostname)-7] = '\0';
124        }
125        entry(node, hostname, sizeof(buf)-(hostname - buf));
126    }
127}
128
129/*
130 * Process a single entry in the input file.
131 * The entry tells us that "node" expands to "domain".
132 * "domain" can either be a domain name or a bitnet node name
133 * The buffer pointed to by "domain" may be overwritten--it
134 * is of size "domainlen".
135 */
136entry(node, domain, domainlen)
137char *node;
138char *domain;
139char *domainlen;
140{
141    char *otherdomain, *p, *err;
142
143    /* See if we have any remembered information about this node */
144    otherdomain = lookup(node);
145
146    if (otherdomain && strchr(otherdomain, '.')) {
147        /* We already have a domain for this node */
148        if (!strchr(domain, '.')) {
149            /*
150             * This entry is an Eric Thomas FOO.BITNET kludge.
151             * He doesn't want LISTSERV to do transitive closures, so we
152             * do them instead.  Give the the domain expansion for "node"
153             * (which is in "otherdomian") to FOO (which is in "domain")
154             * if "domain" doesn't have a domain expansion already.
155             */
156            p = lookup(domain);
157            if (!p || !strchr(p, '.')) remember(domain, otherdomain);
158        }
159    }
160    else {
161        if (!strchr(domain, '.') || valhost(domain, domainlen)) {
162            remember(node, domain);
163            if (otherdomain) {
164                /*
165                 * We previously mapped the node "node" to the node
166                 * "otherdomain".  If "otherdomain" doesn't already
167                 * have a domain expansion, give it the expansion "domain".
168                 */
169                p = lookup(otherdomain);
170                if (!p || !strchr(p, '.')) remember(otherdomain, domain);
171            }
172        }
173        else {
174            switch (h_errno) {
175            case HOST_NOT_FOUND:
176                err = "not registered in DNS";
177                break;
178
179            case TRY_AGAIN:
180                err = "temporary DNS lookup failure";
181                break;
182
183            case NO_RECOVERY:
184                err = "non-recoverable nameserver error";
185                break;
186
187            case NO_DATA:
188                err = "registered in DNS, but not mailable";
189                break;
190
191            default:
192                err = "unknown nameserver error";
193                break;
194            }
195
196            fprintf(stderr, "%-8s %s %s\n", node, domain, err);
197        }
198    }
199}
200
201/*
202 * Validate whether the mail domain "host" is registered in the DNS.
203 * If "host" is a CNAME, it is expanded in-place if the expansion fits
204 * into the buffer of size "hbsize".  Returns nonzero if it is, zero
205 * if it is not.  A BIND error code is left in h_errno.
206 */
207int
208valhost(host, hbsize)
209        char *host;
210        int hbsize;
211{
212        register u_char *eom, *ap;
213        register int n;
214        HEADER *hp;
215        querybuf answer;
216        int ancount, qdcount;
217        int ret;
218        int type;
219        int qtype;
220        char nbuf[1024];
221
222        if ((_res.options & RES_INIT) == 0 && res_init() == -1)
223                return (0);
224
225        _res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
226        _res.retrans = 30;
227        _res.retry = 10;
228
229        qtype = T_ANY;
230
231        for (;;) {
232                h_errno = NO_DATA;
233                ret = res_querydomain(host, "", C_IN, qtype,
234                                      &answer, sizeof(answer));
235                if (ret <= 0)
236                {
237                        if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
238                        {
239                                /* the name server seems to be down */
240                                h_errno = TRY_AGAIN;
241                                return 0;
242                        }
243
244                        if (h_errno != HOST_NOT_FOUND)
245                        {
246                                /* might have another type of interest */
247                                if (qtype == T_ANY)
248                                {
249                                        qtype = T_A;
250                                        continue;
251                                }
252                                else if (qtype == T_A)
253                                {
254                                        qtype = T_MX;
255                                        continue;
256                                }
257                        }
258
259                        /* otherwise, no record */
260                        return 0;
261                }
262
263                /*
264                **  This might be a bogus match.  Search for A, MX, or
265                **  CNAME records.
266                */
267
268                hp = (HEADER *) &answer;
269                ap = (u_char *) &answer + sizeof(HEADER);
270                eom = (u_char *) &answer + ret;
271
272                /* skip question part of response -- we know what we asked */
273                for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
274                {
275                        if ((ret = dn_skipname(ap, eom)) < 0)
276                        {
277                                return 0;               /* ???XXX??? */
278                        }
279                }
280
281                for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
282                {
283                        n = dn_expand((u_char *) &answer, eom, ap,
284                                      (u_char *) nbuf, sizeof nbuf);
285                        if (n < 0)
286                                break;
287                        ap += n;
288                        GETSHORT(type, ap);
289                        ap += SHORTSIZE + LONGSIZE;
290                        GETSHORT(n, ap);
291                        switch (type)
292                        {
293                          case T_MX:
294                          case T_A:
295                                return 1;
296
297                          case T_CNAME:
298                                /* value points at name */
299                                if ((ret = dn_expand((u_char *)&answer,
300                                    eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0)
301                                        break;
302                                if (strlen(nbuf) < hbsize) {
303                                    (void)strcpy(host, nbuf);
304                                }
305                                return 1;
306
307                          default:
308                                /* not a record of interest */
309                                continue;
310                        }
311                }
312
313                /*
314                **  If this was a T_ANY query, we may have the info but
315                **  need an explicit query.  Try T_A, then T_MX.
316                */
317
318                if (qtype == T_ANY)
319                        qtype = T_A;
320                else if (qtype == T_A)
321                        qtype = T_MX;
322                else
323                        return 0;
324        }
325}
326
327struct entry {
328    struct entry *next;
329    char *node;
330    char *domain;
331};
332struct entry *firstentry;
333
334/*
335 * Find any remembered information about "node"
336 */
337char *lookup(node)
338char *node;
339{
340    struct entry *p;
341
342    for (p = firstentry; p; p = p->next) {
343        if (!strcmp(node, p->node)) {
344            return p->domain;
345        }
346    }
347    return 0;
348}
349
350/*
351 * Mark the node "node" as equivalent to "domain".  "domain" can either
352 * be a bitnet node or a domain name--if it is the latter, the mapping
353 * will be written to stdout.
354 */
355remember(node, domain)
356char *node;
357char *domain;
358{
359    struct entry *p;
360
361    if (strchr(domain, '.')) {
362        fprintf(stdout, "%-8s %s\n", node, domain);
363    }
364
365    for (p = firstentry; p; p = p->next) {
366        if (!strcmp(node, p->node)) {
367            p->domain = malloc(strlen(domain)+1);
368            if (!p->domain) {
369                goto outofmemory;
370            }
371            strcpy(p->domain, domain);
372            return;
373        }
374    }
375
376    p = (struct entry *)malloc(sizeof(struct entry));
377    if (!p) goto outofmemory;
378
379    p->next = firstentry;
380    firstentry = p;
381    p->node = malloc(strlen(node)+1);
382    p->domain = malloc(strlen(domain)+1);
383    if (!p->node || !p->domain) goto outofmemory;
384    strcpy(p->node, node);
385    strcpy(p->domain, domain);
386    return;
387
388  outofmemory:
389    fprintf(stderr, "Out of memory\n");
390    exit(1);
391}
392
393/*
394 * Walk through the database, looking for any cases where we know
395 * node FOO is equivalent to node BAR and node BAR has a domain name.
396 * For those cases, give FOO the same domain name as BAR.
397 */
398finish()
399{
400    struct entry *p;
401    char *domain;
402
403    for (p = firstentry; p; p = p->next) {
404        if (!strchr(p->domain, '.') && (domain = lookup(p->domain))) {
405            remember(p->node, domain);
406        }
407    }
408}
409
Note: See TracBrowser for help on using the repository browser.