/* * convert ndb to zone */ #include #include #include #include #include #include typedef struct Rrtab Rrtab; typedef struct Soatab Soatab; typedef struct Re Re; typedef struct Retab Retab; struct Rrtab { char *s; Ndbtuple *rr; }; struct Re { Reprog *re; char *s; char flag; }; struct Retab { Re *r; int nre; int nalloc; }; struct Soatab { char *s; Re *re; Ndbtuple *soa; uchar ip[IPaddrlen]; uchar m[IPaddrlen]; int bits; Rrtab *rr; int nrr; int arr; }; enum { Fexact = 1<<0, Fneg = 1<<1, }; static int flag[127]; static Biobuf bout; static Soatab soatab[128]; static int soaidx; static ulong soamtime; #define dprint(...) if(flag['d']) fprint(2, __VA_ARGS__); else {} #pragma varargck type "t" char* void usage(void) { fprint(2, "usage: ndb/tozone [-dg] [-f ndbfile] [zone ...]\n"); exits("usage"); } /* there is no way to get " or \n in an ndb rr */ static int fmtzonequote(Fmt *f) { char *s; uchar buf[IPaddrlen]; s = va_arg(f->args, char*); if(strpbrk(s, " \t") == nil){ if(parseip(buf, s) == -1) return fmtprint(f, "%s.", s); else return fmtprint(f, "%s", s); }else return fmtprint(f, "\"%s\"", s); } static int fmttabpad(Fmt *f) { char *s; int l, p; l = strlen(s = va_arg(f->args, char*)); p = f->prec; fmtprint(f, "%s", s); for(; l < p; l += 8) fmtprint(f, "\t"); return 0; } Ndb* ndbopen1(char *s) { Dir *d; Ndb *db; db = ndbopen(s); if(db && (d = dirstat(s))){ if(d->mtime > soamtime) soamtime = d->mtime; free(d); } return db; } Ndbtuple* ndbfcopy0(Ndbtuple *from, char **okrr, int nok) { Ndbtuple *first, *to, *last, *line; int newline, i; newline = 1; last = nil; first = nil; line = nil; for(; from != nil; from = from->entry){ for(i = 0; i < nok; i++) if(strcmp(from->attr, okrr[i]) == 0) break; if(nok > 0 && i == nok){ newline = from->line != from->entry; continue; } to = ndbnew(from->attr, from->val); if(newline) line = to; else last->line = to; if(last != nil) last->entry = to; else { first = to; line = to; } to->entry = nil; to->line = line; last = to; newline = from->line != from->entry; } ndbsetmalloctag(first, getcallerpc(&from)); return first; } Ndbtuple* ndbcopy(Ndbtuple *from) { return ndbfcopy0(from, nil, 0); } static char *glue[] = { "ip", "ns", }; Ndbtuple* ndbfcopy(Ndbtuple *from) { return ndbfcopy0(from, glue, nelem(glue)); } enum { Retry, Expire, Minttl, Refresh, Serial, Mb, }; char *nametab[] = { "retry", "expire", "minttl", "refresh", "serial", "mb", }; char *deftab[] = { "3600", "86400", "3600", "3600", nil, nil, }; char *rrtab[] = { "ns", "cname", "mx", "txtrr", "ip", "aaaa", "a6", "ptr", }; char *rrntab[] = { "ns", "cname", "mx", "txt", "a", "aaaa", "a6", "ptr", }; int rrorigin[] = { 1, 1, 1, 0, 0, 0, 0, 0, }; void origin(char *dom, int domsz, char *zone, char *origin, int inzone) { char *p; int d; p = strstr(zone, origin); if(p != nil && (p[strlen(origin)] != 0 || p > zone && p[-1] != '.')) p = nil; if(p == nil){ if(inzone) sysfatal("%s not subdomain of %s", zone, origin); snprint(dom, domsz, "%q", zone); return; }else d = p - zone - 1; if(d >= domsz || d == 0) sysfatal("bad zone: %s", zone); if(d == -1){ d = 1; zone = "@"; } memcpy(dom, zone, d); dom[d] = 0; } void prsoa(Biobuf *b, Soatab *t) { char *tab[Mb + 1], buf[12], dom[128], nm[128], mx[128], val[128]; int i, j, l, lmax; Ndbtuple *nt, *pt; memcpy(tab, deftab, sizeof deftab); for(nt = t->soa; nt; nt = nt->entry) for(i = 0; i < nelem(nametab); i++) if(strcmp(nt->attr, nametab[i]) == 0) tab[i] = nt->val; if(tab[Serial] == nil){ snprint(buf, sizeof buf, "%lud", soamtime); tab[Serial] = buf; } if(tab[Mb] == nil){ snprint(nm, sizeof nm, "netmaster@%s", t->s); tab[Mb] = nm; } Bprint(b, "$ORIGIN %q\n", t->s); origin(dom, sizeof dom, t->s, t->s, 1); Bprint(b, "%s\t" "soa %s. %s. (\n", dom, t->s, tab[Mb]); Bprint(b, "\t\t\t" "%s\n", tab[Serial]); Bprint(b, "\t\t\t" "%s\n", tab[Refresh]); Bprint(b, "\t\t\t" "%s\n", tab[Retry]); Bprint(b, "\t\t\t" "%s\n", tab[Expire]); Bprint(b, "\t\t\t" "%s\n", tab[Minttl]); Bprint(b, "\t\t" ")\n"); for(i = 0; i < nelem(rrtab); i++) for(nt = t->soa; nt; nt = nt->entry) if(strcmp(nt->attr, rrtab[i]) == 0){ if(strcmp(nt->attr, "mx") == 0){ if(pt = nt->entry) if(strcmp(pt->attr, "pref") == 0){ origin(mx, sizeof mx, nt->val, t->s, 0); Bprint(b, "\t\t" "mx\t" "%s %s\n", pt->val, mx); } }else if(rrorigin[i]){ origin(dom, sizeof dom, nt->val, t->s, 0); Bprint(b, "\t\t" "%s\t" "%s\n", rrntab[i], dom); }else Bprint(b, "\t\t" "%s\t" "%q\n", rrntab[i], nt->val); } lmax = 1; for(j = 0; j < t->nrr; j++){ origin(dom, sizeof dom, t->rr[j].s, t->s, 1); l = strlen(dom); if(l > lmax) lmax = l; } // lmax += 8; lmax -= lmax%8; for(j = 0; j < t->nrr; j++){ origin(dom, sizeof dom, t->rr[j].s, t->s, 1); for(i = 0; i < nelem(rrtab); i++) for(nt = t->rr[j].rr; nt; nt = nt->entry) if(strcmp(nt->attr, rrtab[i]) == 0){ origin(val, sizeof val, nt->val, t->s, 0); Bprint(b, "%.*t" "%s\t" "%s\n", lmax+8, dom, rrntab[i], val); } } } char* lower(char *s) { char *p, c; for(p = s; c = *p; p++) if(c >= 'A' && c <= 'Z') *p = c - ('A' - 'a'); return s; } int iregex(Re *r, char *val, int fmask) { char *s; int m, f; s = lower(strdup(val)); f = r->flag & ~fmask; if(f & Fexact) m = strcmp(r->s, s) == 0; else m = regexec(r->re, s, 0, 0); free(s); if(f & Fneg) m = !m; return m; } Re* iregexes(Retab *r, char *val, int fmask) { int i; for(i = 0; i < r->nre; i++) if(iregex(r->r + i, val, fmask)) return r->r + i; return nil; } void* erealloc(void *a, ulong sz) { if((a = realloc(a, sz)) == nil) sysfatal("realloc: %r"); return a; } void addre(Retab *r, char *s, int flag) { char rebuf[128]; int i; if(r->nre == r->nalloc){ r->r = erealloc(r->r, (r->nalloc += 10)*sizeof r->r[0]); memset(r->r + r->nre, 0, 10*sizeof r->r[0]); } snprint(rebuf, sizeof rebuf, "%s$", s); for(i = r->nre; i > 0 && strlen(s) > strlen(r->r[i - 1].s); i--) r->r[i] = r->r[i - 1]; r->r[i].re = regcomp(rebuf); if(r->r[i].re == nil) sysfatal("regcomp: %r"); r->r[i].s = strdup(s); if(r->r[i].s == nil) sysfatal("strdup: %r"); r->r[i].flag = flag; r->nre++; } void freeretab(Retab *r) { int i; for(i = 0; i < r->nre; i++){ free(r->r[i].re); free(r->r[i].s); } free(r->r); } static char* bitprint(char *p, char *e, char *fmt, uint i, int, int bits) { if(bits > 8) bits = 8; if(bits > 0) p = seprint(p, e, fmt, i & (1<>0, 4, bits); bits -= 4; p = bitprint(p, e, "%x.", a[IPaddrlen - i - 1]>>4, 4, bits); bits -= 4; } }else{ bits = 32 - bits; for(i = 0; i < 4; i++){ p = bitprint(p, e, "%d.", a[IPaddrlen - i - 1]>>0, 4, bits); bits -= 8; } } if(p > buf && p[-1] == '.') p--; p[0] = 0; } int issoa(Ndbtuple *t) { Ndbtuple *nt; for(nt = t; nt; nt = nt->entry) if(strcmp(nt->attr, "soa") == 0) return 1; return 0; } Soatab* addrr(Soatab *s, char *v, Ndbtuple *t, int f) { int j; dprint("%d: rr %s %s\n", f, s->s, v); for(j = 0; j < s->nrr; j++) if(strcmp(s->rr[j].s, v) == 0) goto found; if(j >= s->arr){ s->rr = erealloc(s->rr, (s->arr += 20)*sizeof *s->rr); memset(s->rr + j, 0, sizeof s->rr[j]*20); } s->nrr++; s->rr[j].s = strdup(v); found: if(f && strcmp(v, s->s) != 0) s->rr[j].rr = ndbconcatenate(s->rr[j].rr, ndbfcopy(t)); else s->rr[j].rr = ndbconcatenate(s->rr[j].rr, ndbcopy(t)); return s; } int ipmatch(uchar *a, uchar *b, uchar *mask) { uchar m[2][IPaddrlen]; maskip(a, mask, m[0]); maskip(b, mask, m[1]); return memcmp(m[0], m[1], IPaddrlen) == 0; } void addip(Soatab *s, char *v, char *val, int is6) { char rev[256], r2[256]; int bits; Ndbtuple *rr; bits = s->bits; if(!is6) bits -= 96; revipmask(rev, sizeof rev, val, bits); snprint(r2, sizeof r2, "%s.%s", rev, s->s); rr = ndbnew("ptr", v); addrr(s, r2, rr, 0); ndbfree(rr); } void lookuprev(char *v, Ndbtuple *t) { uchar a[IPaddrlen]; int i, is6; Ndbtuple *nt; Soatab *s; for(nt = t; nt; nt = nt->entry) if(strcmp(nt->attr, "ip") == 0){ is6 = parseip(a, nt->val) == 6; for(i = 0; i < soaidx; i++){ s = soatab + i; if(s->bits >= 0) if(ipmatch(s->ip, a, s->m)) addip(s, v, nt->val, is6); } } } static int domtoip(uchar *a, uchar *m, char *ip) { char *p, *r, *s, buf[8]; int l, i, ip6, rem, bits, bits0, step, base, stop; memset(a, 0, IPaddrlen); memset(m, 0, IPaddrlen); l = strlen(ip); if(l >= 13 && cistrcmp(ip + l - 13, ".in-addr.arpa") == 0){ ip6 = 0; step = 8; base = 0; stop = 96; }else if (l >= 9 && cistrcmp(ip + l - 9, ".ip6.arpa") == 0){ ip6 = 1; step = 4; base = 0xf; stop = 0; }else return -1; bits = rem = 0; s = ip; if((p = strchr(ip, '/')) && p < strchr(ip, '.')){ strtoul(ip, &r, 0); if(r == p){ rem = strtoul(ip, &r, 0); bits = strtoul(p + 1, &s, 0); if(*s) s++; } bits += 96; }else{ i = 0; for(p = ip; p = strchr(p, '.'); p++) i++; if(!ip6 && strchr(ip, '/')) i--; bits = step*(i - 1) + stop; } if(rem){ i = bits%step; if(step == 4 && bits%8 == 0) rem <<= 4; a[i/8] |= rem; } bits0 = bits; bits -= bits%8; for(; bits >= stop && *s; ){ i = strtoul(s, &s, base); if(*s == '/'){ strtoul(s + 1, &s, 0); if(*s) s++; continue; } if(*s) s++; if(step == 4 && bits%8 == 0) i <<= 4; a[bits/8 - 1] |= i; bits -= step; } if(ip6 == 0) a[10] = a[11] = 0xff; if(bits != stop - 8) sysfatal("rev zone %s missing %d bits\n", ip, bits - stop + 8); snprint(buf, sizeof buf, "/%d", bits0); if(parseipmask(m, buf) == bits0) sysfatal("parseipmask: %r"); return bits0; } Soatab* lookup0(Retab *r, char *v, Ndbtuple *t, int f) { int i; Soatab *s; Re *m; if(f == 0) lookuprev(v, t); if((m = iregexes(r, v, Fneg)) == nil) return nil; for(i = 0; i < soaidx; i++) if(cistrcmp(soatab[i].s, v) == 0){ s = soatab + i; dprint("%d: concat %s\n", f, s->s); s->soa = ndbconcatenate(s->soa, ndbcopy(t)); return s; }else if(m == soatab[i].re) return addrr(soatab + i, v, t, f); if(soaidx == nelem(soatab)) sysfatal("too many doms"); s = soatab + soaidx; soaidx++; dprint("%d: new %s\n", f, v); s->soa = ndbconcatenate(s->soa, ndbcopy(t)); s->s = strdup(v); s->re = m; s->bits = domtoip(s->ip, s->m, s->s); return s; } char* match(char *z, char *s, Ndbtuple*) { char *p; if((p = cistrstr(s, z)) > s && p[-1] == '.') return p - 1; return 0; } int isglue(char *z, char *s, Ndbtuple *t) { char *d, *m; m = match(z, s, t); if((d = strchr(s, '.')) && d < m) return 1; if(d <= m && issoa(t)) return 1; return 0; } Soatab* lookup(Retab *r, char *v, Ndbtuple *t, int f) { int i; Soatab *s; s = lookup0(r, v, t, f); if(!flag['g']) for(i = 0; i < soaidx - 1; i++) if(isglue(soatab[i].s, v, t)){ dprint("glue zone=%s %s\n", soatab[i].s, v); addrr(soatab + i, v, t, -1); } return s; } /* equivalent to ndb/requery -f /lib/ndb/external soa 're1|...|ren' dom */ void soas0(char *dbfile, Ndb *db, Retab *r0, Retab *r1, int f) { int m; Ndb *p; Ndbtuple *nt, *t; for(; t = ndbparse(db); ndbfree(t)){ /* why doesn't ndb do this for me? */ if(strcmp(t->attr, "database") == 0){ for(nt = t; nt; nt = nt->entry) if(strcmp(nt->attr, "file") == 0) if(strcmp(nt->val, dbfile) != 0){ p = ndbopen1(nt->val); if(p == nil) sysfatal("bad ndb file: %s\n", nt->val); soas0(nt->val, p, r0, r1, f); ndbclose(p); } } m = 0; if(issoa(t)) m = 1; for(nt = t; nt; nt = nt->entry) if(strcmp(nt->attr, "dom") == 0){ if(f == 2 && m && iregexes(r0, nt->val, 0)) addre(r1, nt->val, 0); else if(f == 2 && m) addre(r1, nt->val, Fneg); else if(f == 1 && m) lookup(r0, nt->val, t, f); else if(f == 0 && !m) lookup(r0, nt->val, t, f); } } } void soas(char *dbfile, Ndb *db, char **re, int nre) { int i; Retab r0, r1; memset(&r0, 0, sizeof r0); memset(&r1, 0, sizeof r1); if(nre == 0) addre(&r0, ".*", 0); else for(i = 0; i < nre; i++) addre(&r0, re[i], Fexact); soas0(dbfile, db, &r0, &r1, 2); //for(int i=0; is); n[i] = gettokens(v[i], f[i], nelem(f[0]), "."); } bias = 0; for(i = 0;; i++){ if(i == n[0] || i == n[1]){ if(n[0] != n[1]) bias = n[1] - n[0]; free(v[0]); free(v[1]); return bias; } bias = cistrcmp(f[0][n[0] - i - 1], f[1][n[1] - i - 1]); } } int ipaddrcmp(void *v1, void *v2) { uchar m[2][IPaddrlen], ip[2][IPaddrlen]; int i, r; Rrtab *a, *b; a = v1; b = v2; domtoip(ip[0], m[0], a->s); domtoip(ip[1], m[1], b->s); for(i = 0; i < IPaddrlen; i++) if(r = ip[0][i] - ip[1][i]) return r; return 0; } void soasort(Soatab *t) { int (*f)(void*, void*); f = alphacmp; if(t->bits > 0) f = ipaddrcmp; qsort(t->rr, t->nrr, sizeof t->rr[0], f); } void main(int argc, char **argv) { char *dbfile; int i; Ndb *db; dbfile = "/lib/ndb/local"; ARGBEGIN{ case 'f': dbfile = EARGF(usage()); break; case 'd': case 'g': flag[ARGC()] = 1; break; default: usage(); }ARGEND; fmtinstall('q', fmtzonequote); fmtinstall('t', fmttabpad); if(Binit(&bout, 1, OWRITE) == -1) sysfatal("Binit: %r"); db = ndbopen1(dbfile); if(db == nil) sysfatal("%s: no db files\n", argv0); soas(dbfile, db, argv, argc); ndbclose(db); for(i = 0; i < soaidx; i++){ if(soatab[i].re->flag & Fneg) continue; Bprint(&bout, "$TTL 3600\n"); soasort(soatab + i); prsoa(&bout, soatab + i); } Bterm(&bout); exits(0); }