2 * By John G. Myers, jgm+@cmu.edu
5 * Process a BITNET "internet.listing" file, producing output
6 * suitable for input to makemap.
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.
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')"
18 * The bitdomain table should be rebuilt monthly.
23 #include <sys/types.h>
24 #include <netinet/in.h>
25 #include <arpa/nameser.h>
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) */
42 extern char *malloc();
54 while ((opt
= getopt(argc
, argv
, "o:")) != -1) {
57 if (!freopen(optarg
, "w", stdout
)) {
64 fprintf(stderr
, "usage: %s [-o outfile] [internet.listing]\n",
71 if (!freopen(argv
[optind
], "r", stdin
)) {
82 * Parse and process an input file
87 int skippingheader
= 1;
88 char buf
[1024], *node
, *hostname
, *p
;
90 while (fgets(buf
, sizeof(buf
), infile
)) {
91 for (p
= buf
; *p
&& isspace(*p
); p
++);
96 if (skippingheader
) continue;
99 for (; *p
&& !isspace(*p
); p
++) {
100 if (isupper(*p
)) *p
= tolower(*p
);
103 fprintf(stderr
, "%-8s: no domain name in input file\n", node
);
108 for (; *p
&& isspace(*p
); p
++) ;
110 fprintf(stderr
, "%-8s no domain name in input file\n", node
);
115 for (; *p
&& !isspace(*p
); p
++) {
116 if (isupper(*p
)) *p
= tolower(*p
);
120 /* Chop off any trailing .bitnet */
121 if (strlen(hostname
) > 7 &&
122 !strcmp(hostname
+strlen(hostname
)-7, ".bitnet")) {
123 hostname
[strlen(hostname
)-7] = '\0';
125 entry(node
, hostname
, sizeof(buf
)-(hostname
- buf
));
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".
136 entry(node
, domain
, domainlen
)
141 char *otherdomain
, *p
, *err
;
143 /* See if we have any remembered information about this node */
144 otherdomain
= lookup(node
);
146 if (otherdomain
&& strchr(otherdomain
, '.')) {
147 /* We already have a domain for this node */
148 if (!strchr(domain
, '.')) {
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.
157 if (!p
|| !strchr(p
, '.')) remember(domain
, otherdomain
);
161 if (!strchr(domain
, '.') || valhost(domain
, domainlen
)) {
162 remember(node
, domain
);
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".
169 p
= lookup(otherdomain
);
170 if (!p
|| !strchr(p
, '.')) remember(otherdomain
, domain
);
176 err
= "not registered in DNS";
180 err
= "temporary DNS lookup failure";
184 err
= "non-recoverable nameserver error";
188 err
= "registered in DNS, but not mailable";
192 err
= "unknown nameserver error";
196 fprintf(stderr
, "%-8s %s %s\n", node
, domain
, err
);
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.
208 valhost(host
, hbsize
)
212 register u_char
*eom
, *ap
;
216 int ancount
, qdcount
;
222 if ((_res
.options
& RES_INIT
) == 0 && res_init() == -1)
225 _res
.options
&= ~(RES_DNSRCH
|RES_DEFNAMES
);
233 ret
= res_querydomain(host
, "", C_IN
, qtype
,
234 &answer
, sizeof(answer
));
237 if (errno
== ECONNREFUSED
|| h_errno
== TRY_AGAIN
)
239 /* the name server seems to be down */
244 if (h_errno
!= HOST_NOT_FOUND
)
246 /* might have another type of interest */
252 else if (qtype
== T_A
)
259 /* otherwise, no record */
264 ** This might be a bogus match. Search for A, MX, or
268 hp
= (HEADER
*) &answer
;
269 ap
= (u_char
*) &answer
+ sizeof(HEADER
);
270 eom
= (u_char
*) &answer
+ ret
;
272 /* skip question part of response -- we know what we asked */
273 for (qdcount
= ntohs(hp
->qdcount
); qdcount
--; ap
+= ret
+ QFIXEDSZ
)
275 if ((ret
= dn_skipname(ap
, eom
)) < 0)
277 return 0; /* ???XXX??? */
281 for (ancount
= ntohs(hp
->ancount
); --ancount
>= 0 && ap
< eom
; ap
+= n
)
283 n
= dn_expand((u_char
*) &answer
, eom
, ap
,
284 (u_char
*) nbuf
, sizeof nbuf
);
289 ap
+= SHORTSIZE
+ LONGSIZE
;
298 /* value points at name */
299 if ((ret
= dn_expand((u_char
*)&answer
,
300 eom
, ap
, (u_char
*)nbuf
, sizeof(nbuf
))) < 0)
302 if (strlen(nbuf
) < hbsize
) {
303 (void)strcpy(host
, nbuf
);
308 /* not a record of interest */
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.
320 else if (qtype
== T_A
)
332 struct entry
*firstentry
;
335 * Find any remembered information about "node"
342 for (p
= firstentry
; p
; p
= p
->next
) {
343 if (!strcmp(node
, p
->node
)) {
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.
355 remember(node
, domain
)
361 if (strchr(domain
, '.')) {
362 fprintf(stdout
, "%-8s %s\n", node
, domain
);
365 for (p
= firstentry
; p
; p
= p
->next
) {
366 if (!strcmp(node
, p
->node
)) {
367 p
->domain
= malloc(strlen(domain
)+1);
371 strcpy(p
->domain
, domain
);
376 p
= (struct entry
*)malloc(sizeof(struct entry
));
377 if (!p
) goto outofmemory
;
379 p
->next
= firstentry
;
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
);
389 fprintf(stderr
, "Out of memory\n");
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.
403 for (p
= firstentry
; p
; p
= p
->next
) {
404 if (!strchr(p
->domain
, '.') && (domain
= lookup(p
->domain
))) {
405 remember(p
->node
, domain
);