#From: gdmr@dcs.ed.ac.uk #Date: Mon, 26 Apr 93 11:03:57 BST #Message-Id: <5447.9304261003@bruray.dcs.ed.ac.uk> #To: Paul A Vixie <vixie> #Subject: Re: send me your tools # #In article <9304251928.AA09770@cognition.pa.dec.com>, you write: #> ... if you have something which converts to/from /etc/hosts to/from #> hosts.txt to/from zone files ... # #Here (appended) is my /etc/hosts-to-zonefiles converter. There were a couple #of reasons I wrote it (apart from /etc/hosts format being well understood by #our system support people): # # it only generates stuff for configured-in networks (which doesn't # actually matter to us now, but did at the time I wrote it); and # # it does something reasonable for multi-homed hosts # #There's some documentation at the top of the source file, and an example #configuration file (which happens to be a copy of our working one). #-- #George D M Ross, Department of Computer Science, University of Edinburgh # Kings Buildings, Mayfield Road, Edinburgh, Scotland, EH9 3JZ #Mail: gdmr@dcs.ed.ac.uk Voice: 031-650 5147 Fax: 031-667 7209 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: Configure-example Makefile makeDNS.c makeHS.c updateDNS # Wrapped by bind@bruray on Mon Apr 26 10:58:57 1993 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f Configure-example -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"Configure-example\" else echo shar: Extracting \"Configure-example\" \(1831 characters\) sed "s/^X//" >Configure-example <<'END_OF_Configure-example' X# makeDNS configuration file for cs & cs1 X# X# domains -- format is X#"0.0.0.0" domain zone-file headers-file "other"-zone-file MX-file X0.0.0.0 dcs.ed.ac.uk New/zone.cs Headers/cs New/zone.cs.L X X# wires -- format is X# subnet mask zone-file reverse-headers-file domain X129.215.36.0 255.255.255.0 /dev/null /dev/null dcs.ed.ac.uk X129.215.56.0 255.255.255.0 /dev/null /dev/null dcs.ed.ac.uk X# X129.215.160.0 255.255.255.0 New/zone.160 Headers/reverse dcs.ed.ac.uk X129.215.64.0 255.255.255.0 New/zone.64 Headers/reverse dcs.ed.ac.uk X129.215.32.0 255.255.255.0 New/zone.32 Headers/reverse dcs.ed.ac.uk X129.215.96.0 255.255.255.0 New/zone.96 Headers/reverse dcs.ed.ac.uk X129.215.224.0 255.255.255.0 New/zone.224 Headers/reverse dcs.ed.ac.uk X129.215.216.0 255.255.255.0 New/zone.216 Headers/reverse dcs.ed.ac.uk X129.215.212.0 255.255.255.0 New/zone.212 Headers/reverse dcs.ed.ac.uk X129.215.124.0 255.255.255.0 New/zone.124 Headers/reverse dcs.ed.ac.uk X129.215.252.0 255.255.255.0 New/zone.252 Headers/reverse dcs.ed.ac.uk X129.215.2.0 255.255.255.0 New/zone.2 Headers/reverse dcs.ed.ac.uk X129.215.18.0 255.255.255.0 New/zone.18 Headers/reverse dcs.ed.ac.uk X129.215.74.0 255.255.255.0 New/zone.74 Headers/reverse dcs.ed.ac.uk X129.215.202.0 255.255.255.0 New/zone.202 Headers/reverse dcs.ed.ac.uk X129.215.42.0 255.255.255.0 New/zone.42 Headers/reverse dcs.ed.ac.uk X129.215.90.0 255.255.255.0 New/zone.90 Headers/reverse dcs.ed.ac.uk X129.215.218.0 255.255.255.0 New/zone.218 Headers/reverse dcs.ed.ac.uk X129.215.58.0 255.255.255.0 New/zone.58 Headers/reverse dcs.ed.ac.uk X129.215.186.0 255.255.255.0 New/zone.186 Headers/reverse dcs.ed.ac.uk X# X192.41.110.0 255.255.255.0 New/zone.41.110 Headers/reverse dcs.ed.ac.uk X192.41.131.0 255.255.255.0 New/zone.41.131 Headers/reverse dcs.ed.ac.uk END_OF_Configure-example if test 1831 -ne `wc -c <Configure-example`; then echo shar: \"Configure-example\" unpacked with wrong size! fi # end of overwriting check fi if test -f Makefile -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"Makefile\" else echo shar: Extracting \"Makefile\" \(141 characters\) sed "s/^X//" >Makefile <<'END_OF_Makefile' Xall: makeDNS makeHS X XmakeDNS: makeDNS.c X cc -O -o makeDNS makeDNS.c X strip makeDNS X XmakeHS: makeHS.c X cc -O -o makeHS makeHS.c X strip makeHS END_OF_Makefile if test 141 -ne `wc -c <Makefile`; then echo shar: \"Makefile\" unpacked with wrong size! fi # end of overwriting check fi if test -f makeDNS.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"makeDNS.c\" else echo shar: Extracting \"makeDNS.c\" \(13157 characters\) sed "s/^X//" >makeDNS.c <<'END_OF_makeDNS.c' X/* makeDNS.c -- generate DNS RRs from /etc/hosts X * Copyright (c) 1991, 1992 Department of Computer Science, University of X * Edinburgh. Non-commercial use and redistribution is permitted provided this X * notice remains intact and any modifications are flagged. X * X * This software is offered "as is", with no warranty or support whatsoever. X * Please send comments to gdmr@dcs.ed.ac.uk. X */ X X/* Generate a set of DNS RR files from /etc/hosts and some configuration X * files, under a reasonable set of assumptions. The program expects to X * be running in the target directory. It reads the file ".configure" in X * that directory. The file consists of a sequence of "wire" records, one X * per line, each one listing the network number, netmask, RR file, header X * file and full domain name for all hosts on that wire. Alternatively, X * a "domain" record consists of 0.0.0.0 as network number, domain, RR file X * and header file. X * X * The /etc/hosts file is read in, and hosts are added to a 2-way linked list, X * sorted alphabetically by host name (ordering in the DNS doesn't matter, X * and this sorting allows the program's assumptions to be implemented). X * Primary names are held with their corresponding IP address, while aliases X * have the name of the corresponding primary. X * X * Once the hosts file has been read in, the output files are opened and X * the constant header information is written (with the sequence number X * properly updated). The hosts list is then scanned in ascending order X * and the corresponding entries written according to the following X * heuristics: if a host name has as its leading component one of the X * preceding hosts then it is assumed to be a second interface on a X * multi-homed host, and an additional A record is written; and the domain X * of a host is taken to be that of its first A record, as defined in the X * corresponding "wire" entry. Finally the program exits with a zero status. X * X * Should any errors occur, the program will exit with a non-zero status. X */ X X#define HOSTS "/etc/hosts" X#define CONFIG "Configure" X#define LOG "Log" X#define MAXWIRE 63 X#define MAXDOMAIN 5 X#define LINEBUFFER 120 X X#include <stdio.h> X#include <stdlib.h> X#include <ctype.h> X#include <pwd.h> X#include <sys/time.h> X#include <fcntl.h> X#include <time.h> X Xtypedef struct _domain { X char *domainname; X char *RRfile; X char *headerfile; X char *otherZonefile; X char *MXfile; X FILE *file; X FILE *other; X char *MXdata; X} domain; X Xtypedef struct _wire { X long number; X long netmask; X char *RRfile; X char *headerfile; X char *domainname; X FILE *file; X domain *d; X} wire; X Xtypedef struct _host { X struct _host *forward; X struct _host *backward; X long IP; X char *name; X struct _host *primary; X wire *w; X} host; X Xstatic host *hostlist; Xstatic wire wireTable[MAXWIRE]; Xstatic domain domainTable[MAXDOMAIN]; Xstatic int domains, wires; X Xvoid readconfig(); Xvoid readhosts(); Xvoid printhosts(); Xvoid openfiles(); Xvoid closefiles(); X Xmain() X{ readconfig(); X readhosts(); X openfiles(); X printhosts(); X closefiles(); X return 0; X} X Xstatic void addhost(h) Xhost *h; X{ host *x; X if (hostlist == NULL) { X /* List empty, easy case */ X h->forward = NULL; X h->backward = NULL; X hostlist = h; X return; X } X x = hostlist; X /* Skip to insertion point */ X while (strcmp(x->name, h->name) < 0) { X if (x->forward) { X /* More, skip */ X x = x->forward; X } X else { X /* Add at the tail of the list */ X h->forward = NULL; X h->backward = x; X x->forward = h; X return; X } X } X /* Sanity check: duplicate? */ X if (!strcmp(x->name, h->name)) { X (void) fprintf(stderr, "%s duplicate!\n", h->name); X return; X } X /* Insert before x */ X h->forward = x; X h->backward = x->backward; X if (x->backward) x->backward->forward = h; /* Middle */ X else hostlist = h; /* Head */ X x->backward = h; X} X Xstatic long IPconvert(ch) Xchar **ch; X{ long N1, N2, N3, N4; X N1 = strtol(*ch, ch, 10); (*ch)++; X N2 = strtol(*ch, ch, 10); (*ch)++; X N3 = strtol(*ch, ch, 10); (*ch)++; X N4 = strtol(*ch, ch, 10); (*ch)++; X return (N1 << 24) | (N2 << 16) | (N3 << 8) | N4; X} X Xstatic char *extractname(ch) Xchar **ch; X{ char *it; X char *name; X int n = 0; X while (isspace(**ch)) (*ch)++; X if (**ch == '\0' || **ch == '#') return NULL; X it = *ch; X while (!isspace(**ch)) { X (*ch)++; X n++; X } X **ch = '\0'; (*ch)++; X name = (char *) malloc(n + 1); X strcpy(name, it); X return name; X} X Xstatic wire *findWire(IP) Xlong IP; X{ int n; X wire *w; X if (IP == 0) return NULL; X for (n = 0, w = wireTable; n < wires; n++, w++) { X if ((IP & w->netmask) == w->number) { X return w; X } X } X return NULL; X} X Xstatic domain *findDomain(name) Xchar *name; X{ int n; X domain *d; X for (n = 0, d = domainTable; n < domains; n++, d++) { X if (!strcmp(name, d->domainname)) { X return d; X } X } X return NULL; X} X Xstatic void readhosts() X{ FILE *hosts; X host *h, *ph; X char *ch; X char buffer[LINEBUFFER]; X long IP; X char *primary; X char *alias; X wire *w; X X if ((hosts = fopen(HOSTS, "r")) == NULL) { X (void) fprintf(stderr, "Failed to open %s\n", HOSTS); X exit(1); X } X X for (;;) { X if ((ch = fgets(buffer, LINEBUFFER, hosts)) == NULL) break; X while (isspace(*ch)) ch++; X if (*ch == '\0' || *ch == '#') continue; X IP = IPconvert(&ch); X if ((w = findWire(IP)) == NULL) continue; X primary = extractname(&ch); X ph = (host *) malloc(sizeof(host)); X ph->IP = IP; X ph->w = w; X ph->name = primary; X ph->primary = NULL; X addhost(ph); X while (*ch != '\0') { X alias = extractname(&ch); X if (alias == NULL) break; X h = (host *) malloc(sizeof(host)); X h->IP = 0; X h->w = w; X h->name = alias; X h->primary = ph; X addhost(h); X } X } X (void) fclose(hosts); X} X Xstatic host *findPrincipal(like) Xhost *like; X{ host *h; X int l, n; X X h = like->backward; X l = strlen(like->name); X while (h) { X if (*(h->name) != *(like->name)) return NULL; X n = strlen(h->name); X if ((n < l) && !isalnum(like->name[n]) && X !strncmp(h->name, like->name, n)) { X return h; X } X h = h->backward; X } X return NULL; X} X X#define printIP(file, IP) \ X (void) fprintf(file, "%d.%d.%d.%d", \ X (IP >> 24) & 255, (IP >> 16) & 255, (IP >> 8) & 255, IP & 255) X X/* printhosts has four cases: X * 1 IP address, no principal entry -- write the name as it stands, plus X * the reverse name, again as it stands. X * 2 IP address plus principal entry -- write out the address as for the X * principal entry, plus the current name as another A RR. Write the X * reverse entry as pointing to the principal name. X * 3 alias, no principal entry -- write a CNAME RR pointing to the primary. X * 4 alias plus principal entry -- write an A RR with the primary's address. X * X * For example, suppose the /etc/hosts file has the following: X * 129.215.64.48 baleshare baleshare-b agfahost X * 29.215.160.155 baleshare-gw baleshare-a X * Then baleshare is case 1, baleshare-gw is case 2, baleshare-a and X * baleshare-b are case 4, and agfahost is case 3. Confused? BTW, changing X * baleshare-gw to fred, say, would break the algorithm -- the main name of the X * second interface better be related to the main name of the first! X */ X Xvoid printhosts() X{ host *h, *m, *x; X wire *w; X FILE *f; X int dotted; X X h = hostlist; X while (h) { X m = findPrincipal(h); X if (h->IP) { X if (m) { X /* Case 2 */ X /* A RR for name */ X (void) fprintf(m->w->d->file, X "%-23s IN\tA\t", h->name); X printIP(m->w->d->file, h->IP); X (void) putc('\n', m->w->d->file); X if (m->w->d->MXdata) { X (void) fputs(m->w->d->MXdata, X m->w->d->file); X } X /* A RR for principal's name */ X (void) fprintf(m->w->d->file, X "%-23s IN\tA\t", m->name); X printIP(m->w->d->file, h->IP); X (void) putc('\n', m->w->d->file); X /* back-pointer -> principal */ X (void) fprintf(h->w->file, X "%d\tIN\tPTR\t%s.%s.\n", X h->IP & (~h->w->netmask), X m->name, m->w->d->domainname); X if (m->w->d->other) { X (void) fprintf(m->w->d->other, X "%-23s IN\tCNAME\t%s.%s.\n", X h->name, m->name, X m->w->d->domainname); X } X } X else { X /* Case 1 */ X dotted = (int) index(h->name, '.'); X if (!dotted) { X (void) fprintf(h->w->d->file, X "%-23s IN\tA\t", h->name); X printIP(h->w->d->file, h->IP); X (void) putc('\n', h->w->d->file); X if (h->w->d->MXdata) { X (void) fputs(h->w->d->MXdata, X h->w->d->file); X } X if (h->w->d->other) { X (void) fprintf(h->w->d->other, X "%-23s IN\tCNAME\t%s.%s.\n", X h->name, h->name, X h->w->d->domainname); X } X } X (void) fprintf(h->w->file, X dotted ? "%d\tIN\tPTR\t%s\n" X : "%d\tIN\tPTR\t%s.%s.\n", X h->IP & (~h->w->netmask), X h->name, h->w->d->domainname); X } X } X else { X if (m) { X /* Case 4 */ X (void) fprintf(m->w->d->file, X "%-23s IN\tA\t", h->name); X printIP(m->w->d->file, h->primary->IP); X (void) putc('\n', m->w->d->file); X if (m->w->d->MXdata) { X (void) fputs(m->w->d->MXdata, X m->w->d->file); X } X if (m->w->d->other) { X (void) fprintf(m->w->d->other, X "%-23s IN\tCNAME\t%s.%s.\n", X h->name, m->name, X m->w->d->domainname); X } X } X else { X /* Case 3 */ X x = findPrincipal(h->primary); X if (!x) x = h->primary; X (void) fprintf(x->w->d->file, X "%-23s IN\tCNAME\t%s.%s.\n", X h->name, x->name, X x->w->d->domainname); X if (x->w->d->other) { X (void) fprintf(x->w->d->other, X "%-23s IN\tCNAME\t%s.%s.\n", X h->name, x->name, X x->w->d->domainname); X } X } X } X h = h->forward; X } X} X Xstatic void readconfig() X{ FILE *config; X long IP; X char *ch; X char buffer[LINEBUFFER]; X wire *w; X domain *d; X X if ((config = fopen(CONFIG, "r")) == NULL) { X (void) fprintf(stderr, "Failed to open %s\n", CONFIG); X exit(1); X } X for (;;) { X if ((ch = fgets(buffer, LINEBUFFER, config)) == NULL) break; X while (isspace(*ch)) ch++; X if (*ch == '\0' || *ch == '#') continue; X IP = IPconvert(&ch); X if (IP) { X /* Wire record */ X w = wireTable + wires++; X w->number = IP; X w->netmask = IPconvert(&ch); X w->RRfile = extractname(&ch); X w->headerfile = extractname(&ch); X w->domainname = extractname(&ch); X w->file = stderr; /* meantime */ X w->d = findDomain(w->domainname); X if (!w->d) { X (void) fprintf(stderr, "Undefined domain %s:\n", X w->domainname); X (void) fputs(buffer, stderr); X (void) fputc('\n', stderr); X exit(2); X } X } X else { X /* Domain record */ X d = domainTable + domains++; X d->domainname = extractname(&ch); X d->RRfile = extractname(&ch); X d->headerfile = extractname(&ch); X d->otherZonefile = extractname(&ch); X d->MXfile = extractname(&ch); X d->file = stderr; /* meantime */ X d->other = NULL; X d->MXdata = NULL; X } X } X (void) fclose(config); X} X Xstatic void writeheader(f, h, MXdata) XFILE *f; Xchar *h; Xchar *MXdata; X{ static struct passwd *pwd; X time_t tt; X static struct tm *ttm; X static char *t; X static char stamp[20]; X FILE *hf, *l; X char buffer[LINEBUFFER]; X X if (!pwd) { X pwd = getpwuid(getuid()); X endpwent(); X tt = time(NULL); X ttm = localtime(&tt); X (void) sprintf(stamp, "%d%3.3d%3.3d", X ttm->tm_year, ttm->tm_yday, X ((60 * ttm->tm_hour) + ttm->tm_min) >> 1); X t = asctime(ttm); X if (l = fopen(LOG, "a")) { X fprintf(l, "Generated by %s on %s", pwd->pw_name, t); X fclose(l); X } X } X (void) fprintf(f, ";; Generated by %s on %s\n", pwd->pw_name, t); X if ((hf = fopen(h, "r")) == NULL) { X (void) fprintf(stderr, "Can't open header file %s\n", h); X exit(4); X } X (void) fprintf(f, ";; Including %s\n", h); X for (;;) { X if (fgets(buffer, LINEBUFFER, hf) == NULL) break; X (void) fprintf(f, buffer, stamp); X } X (void) fclose(hf); X (void) fprintf(f, ";; End of %s\n\n", h); X if (MXdata) { X (void) fputc('@', f); X (void) fputs(MXdata, f); X (void) fputc('\n', f); X } X} X Xstatic void openfiles() X{ int i; X int MXfd; X int MXsize; X wire *w; X domain *d; X X for (i = 0, d = domainTable; i < domains; i++, d++) { X if ((d->file = fopen(d->RRfile, "w")) == NULL) { X (void) fprintf(stderr, "Can't open %s\n", d->RRfile); X exit(3); X } X if (d->otherZonefile) { X if ((d->other = fopen(d->otherZonefile, "w")) == NULL) { X (void) fprintf(stderr, "Can't open %s\n", X d->otherZonefile); X exit(3); X } X } X if (d->MXfile) { X if ((MXfd = open(d->MXfile, O_RDONLY, 0)) < 0) { X (void) fprintf(stderr, "Can't open %s\n", X d->MXfile); X exit(3); X } X d->MXdata = (char *) malloc(2048); X if ((MXsize = read(MXfd, d->MXdata, 2048)) < 0) { X (void) fprintf(stderr, X "Couldn't read from %s\n", X d->MXfile); X exit(3); X } X (void) close(MXfd); X *(d->MXdata + MXsize) = '\0'; X } X writeheader(d->file, d->headerfile, d->MXdata); X if (d->otherZonefile) X writeheader(d->other, d->headerfile, d->MXdata); X } X for (i = 0, w = wireTable; i < wires; i++, w++) { X if ((w->file = fopen(w->RRfile, "w")) == NULL) { X exit(3); X } X writeheader(w->file, w->headerfile, (char *) 0); X } X} X Xstatic void closefiles() X{ int i; X domain *d; X wire *w; X X for (i = 0, d = domainTable; i < domains; i++, d++) { X (void) fclose(d->file); X if (d->other) (void) fclose(d->other); X } X for (i = 0, w = wireTable; i < wires; i++, w++) { X (void) fclose(w->file); X } X} END_OF_makeDNS.c if test 13157 -ne `wc -c <makeDNS.c`; then echo shar: \"makeDNS.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f makeHS.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"makeHS.c\" else echo shar: Extracting \"makeHS.c\" \(789 characters\) sed "s/^X//" >makeHS.c <<'END_OF_makeHS.c' X/* Copy stdin to stdout, substituting any %s for the current timestamp X * (hacked out of makeDNS.c). The idea is that the HS-class header should X * just $INCLUDE anything it needs, which will have been generated separately. X */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/time.h> X#include <time.h> X X#define LINEBUFFER 120 X#define LOG "/dev/tty" X Xint main() X{ time_t tt; X struct tm *ttm; X char *t; X char stamp[20]; X char buffer[LINEBUFFER]; X X tt = time(NULL); X ttm = localtime(&tt); X (void) sprintf(stamp, "%d%3.3d%3.3d", X ttm->tm_year, ttm->tm_yday, X ((60 * ttm->tm_hour) + ttm->tm_min) >> 1); X t = asctime(ttm); X X (void) printf(";; Generated on %s\n", t); X X for (;;) { X if (fgets(buffer, LINEBUFFER, stdin) == NULL) break; X (void) printf(buffer, stamp); X } X return 0; X} END_OF_makeHS.c if test 789 -ne `wc -c <makeHS.c`; then echo shar: \"makeHS.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f updateDNS -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"updateDNS\" else echo shar: Extracting \"updateDNS\" \(222 characters\) sed "s/^X//" >updateDNS <<'END_OF_updateDNS' X#! /bin/sh Xcd /var/named.Primary X/usr/local/etc/makeDNS Xecho "Update DNS database" Xif [ $? != 0 ] ; then X exit Xfi X/bin/cp New/zone.* . Xif [ $? != 0 ] ; then X exit Xfi Xecho "Reload DNS server" Xkill -HUP `cat /etc/named.pid` END_OF_updateDNS if test 222 -ne `wc -c <updateDNS`; then echo shar: \"updateDNS\" unpacked with wrong size! fi chmod +x updateDNS # end of overwriting check fi echo shar: End of shell archive. exit 0