diff options
Diffstat (limited to 'src/cmd/ndb/dn.c')
-rwxr-xr-x | src/cmd/ndb/dn.c | 1563 |
1 files changed, 1563 insertions, 0 deletions
diff --git a/src/cmd/ndb/dn.c b/src/cmd/ndb/dn.c new file mode 100755 index 00000000..a38d4305 --- /dev/null +++ b/src/cmd/ndb/dn.c @@ -0,0 +1,1563 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <ctype.h> +#include <bio.h> +#include <ndb.h> +#include "dns.h" + +/* + * Hash table for domain names. The hash is based only on the + * first element of the domain name. + */ +DN *ht[HTLEN]; + + +static struct +{ + Lock lk; + ulong names; /* names allocated */ + ulong oldest; /* longest we'll leave a name around */ + int active; + int mutex; + int id; +} dnvars; + +/* names of RR types */ +char *rrtname[] = +{ +[Ta] "ip", +[Tns] "ns", +[Tmd] "md", +[Tmf] "mf", +[Tcname] "cname", +[Tsoa] "soa", +[Tmb] "mb", +[Tmg] "mg", +[Tmr] "mr", +[Tnull] "null", +[Twks] "wks", +[Tptr] "ptr", +[Thinfo] "hinfo", +[Tminfo] "minfo", +[Tmx] "mx", +[Ttxt] "txt", +[Trp] "rp", +[Tkey] "key", +[Tcert] "cert", +[Tsig] "sig", +[Taaaa] "ipv6", +[Tixfr] "ixfr", +[Taxfr] "axfr", +[Tall] "all", + 0, +}; + +/* names of response codes */ +char *rname[Rmask+1] = +{ +[Rok] "ok", +[Rformat] "format error", +[Rserver] "server failure", +[Rname] "bad name", +[Runimplimented] "unimplemented", +[Rrefused] "we don't like you", +}; + +/* names of op codes */ +char *opname[] = +{ +[Oquery] "query", +[Oinverse] "inverse", +[Ostatus] "status", +}; + +Lock dnlock; + +static int sencodefmt(Fmt*); + +/* + * set up a pipe to use as a lock + */ +void +dninit(void) +{ + fmtinstall('E', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('R', rrfmt); + fmtinstall('Q', rravfmt); + fmtinstall('H', sencodefmt); + + dnvars.oldest = maxage; + dnvars.names = 0; +} + +/* + * hash for a domain name + */ +static ulong +dnhash(char *name) +{ + ulong hash; + uchar *val = (uchar*)name; + + for(hash = 0; *val; val++) + hash = (hash*13) + tolower(*val)-'a'; + return hash % HTLEN; +} + +/* + * lookup a symbol. if enter is not zero and the name is + * not found, create it. + */ +DN* +dnlookup(char *name, int class, int enter) +{ + DN **l; + DN *dp; + + l = &ht[dnhash(name)]; + lock(&dnlock); + for(dp = *l; dp; dp = dp->next) { + assert(dp->magic == DNmagic); + if(dp->class == class && cistrcmp(dp->name, name) == 0){ + dp->referenced = now; + unlock(&dnlock); + return dp; + } + l = &dp->next; + } + if(enter == 0){ + unlock(&dnlock); + return 0; + } + dnvars.names++; + dp = emalloc(sizeof(*dp)); + dp->magic = DNmagic; + dp->name = estrdup(name); + assert(dp->name != 0); + dp->class = class; + dp->rr = 0; + dp->next = 0; + dp->referenced = now; + *l = dp; + unlock(&dnlock); + + return dp; +} + +/* + * dump the cache + */ +void +dndump(char *file) +{ + DN *dp; + int i, fd; + RR *rp; + + fd = open(file, OWRITE|OTRUNC); + if(fd < 0) + return; + lock(&dnlock); + for(i = 0; i < HTLEN; i++){ + for(dp = ht[i]; dp; dp = dp->next){ + fprint(fd, "%s\n", dp->name); + for(rp = dp->rr; rp; rp = rp->next) + fprint(fd, " %R %c%c %lud/%lud\n", rp, rp->auth?'A':'U', + rp->db?'D':'N', rp->expire, rp->ttl); + } + } + unlock(&dnlock); + close(fd); +} + +/* + * purge all records + */ +void +dnpurge(void) +{ + DN *dp; + RR *rp, *srp; + int i; + + lock(&dnlock); + + for(i = 0; i < HTLEN; i++) + for(dp = ht[i]; dp; dp = dp->next){ + srp = rp = dp->rr; + dp->rr = nil; + for(; rp != nil; rp = rp->next) + rp->cached = 0; + rrfreelist(srp); + } + + unlock(&dnlock); +} + +/* + * check the age of resource records, free any that have timed out + */ +void +dnage(DN *dp) +{ + RR **l; + RR *rp, *next; + ulong diff; + + diff = now - dp->referenced; + if(diff < Reserved) + return; + + l = &dp->rr; + for(rp = dp->rr; rp; rp = next){ + assert(rp->magic == RRmagic && rp->cached); + next = rp->next; + if(!rp->db) + if(rp->expire < now || diff > dnvars.oldest){ + *l = next; + rp->cached = 0; + rrfree(rp); + continue; + } + l = &rp->next; + } +} + +#define REF(x) if(x) x->refs++ + +/* + * our target is 4000 names cached, this should be larger on large servers + */ +#define TARGET 4000 + +/* + * periodicly sweep for old records and remove unreferenced domain names + * + * only called when all other threads are locked out + */ +void +dnageall(int doit) +{ + DN *dp, **l; + int i; + RR *rp; + static ulong nextage; + + if(dnvars.names < TARGET && now < nextage && !doit){ + dnvars.oldest = maxage; + return; + } + + if(dnvars.names > TARGET) + dnvars.oldest /= 2; + nextage = now + maxage; + + lock(&dnlock); + + /* time out all old entries (and set refs to 0) */ + for(i = 0; i < HTLEN; i++) + for(dp = ht[i]; dp; dp = dp->next){ + dp->refs = 0; + dnage(dp); + } + + /* mark all referenced domain names */ + for(i = 0; i < HTLEN; i++) + for(dp = ht[i]; dp; dp = dp->next) + for(rp = dp->rr; rp; rp = rp->next){ + REF(rp->owner); + if(rp->negative){ + REF(rp->negsoaowner); + continue; + } + switch(rp->type){ + case Thinfo: + REF(rp->cpu); + REF(rp->os); + break; + case Ttxt: + break; + case Tcname: + case Tmb: + case Tmd: + case Tmf: + case Tns: + REF(rp->host); + break; + case Tmg: + case Tmr: + REF(rp->mb); + break; + case Tminfo: + REF(rp->rmb); + REF(rp->mb); + break; + case Trp: + REF(rp->rmb); + REF(rp->rp); + break; + case Tmx: + REF(rp->host); + break; + case Ta: + case Taaaa: + REF(rp->ip); + break; + case Tptr: + REF(rp->ptr); + break; + case Tsoa: + REF(rp->host); + REF(rp->rmb); + break; + } + } + + /* sweep and remove unreferenced domain names */ + for(i = 0; i < HTLEN; i++){ + l = &ht[i]; + for(dp = *l; dp; dp = *l){ + if(dp->rr == 0 && dp->refs == 0){ + assert(dp->magic == DNmagic); + *l = dp->next; + if(dp->name) + free(dp->name); + dp->magic = ~dp->magic; + dnvars.names--; + free(dp); + continue; + } + l = &dp->next; + } + } + + unlock(&dnlock); +} + +/* + * timeout all database records (used when rereading db) + */ +void +dnagedb(void) +{ + DN *dp; + int i; + RR *rp; + static ulong nextage; + + lock(&dnlock); + + /* time out all database entries */ + for(i = 0; i < HTLEN; i++) + for(dp = ht[i]; dp; dp = dp->next) + for(rp = dp->rr; rp; rp = rp->next) + if(rp->db) + rp->expire = 0; + + unlock(&dnlock); +} + +/* + * mark all local db records about my area as authoritative, time out any others + */ +void +dnauthdb(void) +{ + DN *dp; + int i; + Area *area; + RR *rp; + static ulong nextage; + + lock(&dnlock); + + /* time out all database entries */ + for(i = 0; i < HTLEN; i++) + for(dp = ht[i]; dp; dp = dp->next){ + area = inmyarea(dp->name); + for(rp = dp->rr; rp; rp = rp->next) + if(rp->db){ + if(area){ + if(rp->ttl < area->soarr->soa->minttl) + rp->ttl = area->soarr->soa->minttl; + rp->auth = 1; + } + if(rp->expire == 0){ + rp->db = 0; + dp->referenced = now - Reserved - 1; + } + } + } + + unlock(&dnlock); +} + +/* + * keep track of other processes to know if we can + * garbage collect. block while garbage collecting. + */ +int +getactivity(Request *req) +{ + int rv; + + if(traceactivity) syslog(0, "dns", "get %d by %d", dnvars.active, getpid()); + lock(&dnvars.lk); + while(dnvars.mutex){ + unlock(&dnvars.lk); + sleep(200); + lock(&dnvars.lk); + } + rv = ++dnvars.active; + now = time(0); + req->id = ++dnvars.id; + unlock(&dnvars.lk); + + return rv; +} +void +putactivity(void) +{ + static ulong lastclean; + + if(traceactivity) syslog(0, "dns", "put %d by %d", dnvars.active, getpid()); + lock(&dnvars.lk); + dnvars.active--; + assert(dnvars.active >= 0); /* "dnvars.active %d", dnvars.active */; + + /* + * clean out old entries and check for new db periodicly + */ + if(dnvars.mutex || (needrefresh == 0 && dnvars.active > 0)){ + unlock(&dnvars.lk); + return; + } + + /* wait till we're alone */ + dnvars.mutex = 1; + while(dnvars.active > 0){ + unlock(&dnvars.lk); + sleep(100); + lock(&dnvars.lk); + } + unlock(&dnvars.lk); + + db2cache(needrefresh); + dnageall(0); + + /* let others back in */ + lastclean = now; + needrefresh = 0; + dnvars.mutex = 0; +} + +/* + * Attach a single resource record to a domain name. + * - Avoid duplicates with already present RR's + * - Chain all RR's of the same type adjacent to one another + * - chain authoritative RR's ahead of non-authoritative ones + */ +static void +rrattach1(RR *new, int auth) +{ + RR **l; + RR *rp; + DN *dp; + + assert(new->magic == RRmagic && !new->cached); + + if(!new->db) + new->expire = new->ttl; + else + new->expire = now + Year; + dp = new->owner; + assert(dp->magic == DNmagic); + new->auth |= auth; + new->next = 0; + + /* + * find first rr of the right type + */ + l = &dp->rr; + for(rp = *l; rp; rp = *l){ + assert(rp->magic == RRmagic && rp->cached); + if(rp->type == new->type) + break; + l = &rp->next; + } + + /* + * negative entries replace positive entries + * positive entries replace negative entries + * newer entries replace older entries with the same fields + */ + for(rp = *l; rp; rp = *l){ + assert(rp->magic == RRmagic && rp->cached); + if(rp->type != new->type) + break; + + if(rp->db == new->db && rp->auth == new->auth){ + /* negative drives out positive and vice versa */ + if(rp->negative != new->negative){ + *l = rp->next; + rp->cached = 0; + rrfree(rp); + continue; + } + + /* all things equal, pick the newer one */ + if(rp->arg0 == new->arg0 && rp->arg1 == new->arg1){ + /* new drives out old */ + if(new->ttl > rp->ttl || new->expire > rp->expire){ + *l = rp->next; + rp->cached = 0; + rrfree(rp); + continue; + } else { + rrfree(new); + return; + } + } + + /* Hack for pointer records. This makes sure + * the ordering in the list reflects the ordering + * received or read from the database + */ + if(rp->type == Tptr){ + if(!rp->negative && !new->negative + && rp->ptr->ordinal > new->ptr->ordinal) + break; + } + } + l = &rp->next; + } + + /* + * add to chain + */ + new->cached = 1; + new->next = *l; + *l = new; +} + +/* + * Attach a list of resource records to a domain name. + * - Avoid duplicates with already present RR's + * - Chain all RR's of the same type adjacent to one another + * - chain authoritative RR's ahead of non-authoritative ones + * - remove any expired RR's + */ +void +rrattach(RR *rp, int auth) +{ + RR *next; + + lock(&dnlock); + for(; rp; rp = next){ + next = rp->next; + rp->next = 0; + + /* avoid any outside spoofing */ + if(cachedb && !rp->db && inmyarea(rp->owner->name)) + rrfree(rp); + else + rrattach1(rp, auth); + } + unlock(&dnlock); +} + +/* + * allocate a resource record of a given type + */ +RR* +rralloc(int type) +{ + RR *rp; + + rp = emalloc(sizeof(*rp)); + rp->magic = RRmagic; + rp->pc = getcallerpc(&type); + rp->type = type; + switch(type){ + case Tsoa: + rp->soa = emalloc(sizeof(*rp->soa)); + rp->soa->slaves = nil; + break; + case Tkey: + rp->key = emalloc(sizeof(*rp->key)); + break; + case Tcert: + rp->cert = emalloc(sizeof(*rp->cert)); + break; + case Tsig: + rp->sig = emalloc(sizeof(*rp->sig)); + break; + case Tnull: + rp->null = emalloc(sizeof(*rp->null)); + break; + } + rp->ttl = 0; + rp->expire = 0; + rp->next = 0; + return rp; +} + +/* + * free a resource record and any related structs + */ +void +rrfree(RR *rp) +{ + DN *dp; + RR *nrp; + Txt *t; + + assert(rp->magic = RRmagic); + assert(!rp->cached); + + dp = rp->owner; + if(dp){ + assert(dp->magic == DNmagic); + for(nrp = dp->rr; nrp; nrp = nrp->next) + assert(nrp != rp); /* "rrfree of live rr" */; + } + + switch(rp->type){ + case Tsoa: + freeserverlist(rp->soa->slaves); + free(rp->soa); + break; + case Tkey: + free(rp->key->data); + free(rp->key); + break; + case Tcert: + free(rp->cert->data); + free(rp->cert); + break; + case Tsig: + free(rp->sig->data); + free(rp->sig); + break; + case Tnull: + free(rp->null->data); + free(rp->null); + break; + case Ttxt: + while(rp->txt != nil){ + t = rp->txt; + rp->txt = t->next; + free(t->p); + free(t); + } + break; + } + + rp->magic = ~rp->magic; + free(rp); +} + +/* + * free a list of resource records and any related structs + */ +void +rrfreelist(RR *rp) +{ + RR *next; + + for(; rp; rp = next){ + next = rp->next; + rrfree(rp); + } +} + +extern RR** +rrcopy(RR *rp, RR **last) +{ + RR *nrp; + SOA *soa; + Key *key; + Cert *cert; + Sig *sig; + Null *null; + Txt *t, *nt, **l; + + nrp = rralloc(rp->type); + switch(rp->type){ + case Ttxt: + *nrp = *rp; + l = &nrp->txt; + *l = nil; + for(t = rp->txt; t != nil; t = t->next){ + nt = emalloc(sizeof(*nt)); + nt->p = estrdup(t->p); + nt->next = nil; + *l = nt; + l = &nt->next; + } + break; + case Tsoa: + soa = nrp->soa; + *nrp = *rp; + nrp->soa = soa; + *nrp->soa = *rp->soa; + nrp->soa->slaves = copyserverlist(rp->soa->slaves); + break; + case Tkey: + key = nrp->key; + *nrp = *rp; + nrp->key = key; + *key = *rp->key; + key->data = emalloc(key->dlen); + memmove(key->data, rp->key->data, rp->key->dlen); + break; + case Tsig: + sig = nrp->sig; + *nrp = *rp; + nrp->sig = sig; + *sig = *rp->sig; + sig->data = emalloc(sig->dlen); + memmove(sig->data, rp->sig->data, rp->sig->dlen); + break; + case Tcert: + cert = nrp->cert; + *nrp = *rp; + nrp->cert = cert; + *cert = *rp->cert; + cert->data = emalloc(cert->dlen); + memmove(cert->data, rp->cert->data, rp->cert->dlen); + break; + case Tnull: + null = nrp->null; + *nrp = *rp; + nrp->null = null; + *null = *rp->null; + null->data = emalloc(null->dlen); + memmove(null->data, rp->null->data, rp->null->dlen); + break; + default: + *nrp = *rp; + break; + } + nrp->cached = 0; + nrp->next = 0; + *last = nrp; + return &nrp->next; +} + +/* + * lookup a resource record of a particular type and + * class attached to a domain name. Return copies. + * + * Priority ordering is: + * db authoritative + * not timed out network authoritative + * not timed out network unauthoritative + * unauthoritative db + * + * if flag NOneg is set, don't return negative cached entries. + * return nothing instead. + */ +RR* +rrlookup(DN *dp, int type, int flag) +{ + RR *rp, *first, **last; + + assert(dp->magic == DNmagic); + + first = 0; + last = &first; + lock(&dnlock); + + /* try for an authoritative db entry */ + for(rp = dp->rr; rp; rp = rp->next){ + assert(rp->magic == RRmagic && rp->cached); + if(rp->db) + if(rp->auth) + if(tsame(type, rp->type)) + last = rrcopy(rp, last); + } + if(first) + goto out; + + /* try for an living authoritative network entry */ + for(rp = dp->rr; rp; rp = rp->next){ + if(!rp->db) + if(rp->auth) + if(rp->ttl + 60 > now) + if(tsame(type, rp->type)){ + if(flag == NOneg && rp->negative) + goto out; + last = rrcopy(rp, last); + } + } + if(first) + goto out; + + /* try for an living unauthoritative network entry */ + for(rp = dp->rr; rp; rp = rp->next){ + if(!rp->db) + if(rp->ttl + 60 > now) + if(tsame(type, rp->type)){ + if(flag == NOneg && rp->negative) + goto out; + last = rrcopy(rp, last); + } + } + if(first) + goto out; + + /* try for an unauthoritative db entry */ + for(rp = dp->rr; rp; rp = rp->next){ + if(rp->db) + if(tsame(type, rp->type)) + last = rrcopy(rp, last); + } + if(first) + goto out; + + /* otherwise, settle for anything we got (except for negative caches) */ + for(rp = dp->rr; rp; rp = rp->next){ + if(tsame(type, rp->type)){ + if(rp->negative) + goto out; + last = rrcopy(rp, last); + } + } + +out: + unlock(&dnlock); + unique(first); + return first; +} + +/* + * convert an ascii RR type name to its integer representation + */ +int +rrtype(char *atype) +{ + int i; + + for(i = 0; i <= Tall; i++) + if(rrtname[i] && strcmp(rrtname[i], atype) == 0) + return i; + + // make any a synonym for all + if(strcmp(atype, "any") == 0) + return Tall; + return atoi(atype); +} + +/* + * convert an integer RR type to it's ascii name + */ +char* +rrname(int type, char *buf, int len) +{ + char *t; + + t = 0; + if(type <= Tall) + t = rrtname[type]; + if(t==0){ + snprint(buf, len, "%d", type); + t = buf; + } + return t; +} + +/* + * return 0 if not a supported rr type + */ +int +rrsupported(int type) +{ + if(type < 0 || type >Tall) + return 0; + return rrtname[type] != 0; +} + +/* + * compare 2 types + */ +int +tsame(int t1, int t2) +{ + return t1 == t2 || t1 == Tall; +} + +/* + * Add resource records to a list, duplicate them if they are cached + * RR's since these are shared. + */ +RR* +rrcat(RR **start, RR *rp) +{ + RR **last; + + last = start; + while(*last != 0) + last = &(*last)->next; + + *last = rp; + return *start; +} + +/* + * remove negative cache rr's from an rr list + */ +RR* +rrremneg(RR **l) +{ + RR **nl, *rp; + RR *first; + + first = nil; + nl = &first; + while(*l != nil){ + rp = *l; + if(rp->negative){ + *l = rp->next; + *nl = rp; + nl = &rp->next; + *nl = nil; + } else + l = &rp->next; + } + + return first; +} + +/* + * remove rr's of a particular type from an rr list + */ +RR* +rrremtype(RR **l, int type) +{ + RR **nl, *rp; + RR *first; + + first = nil; + nl = &first; + while(*l != nil){ + rp = *l; + if(rp->type == type){ + *l = rp->next; + *nl = rp; + nl = &rp->next; + *nl = nil; + } else + l = &(*l)->next; + } + + return first; +} + +/* + * print conversion for rr records + */ +int +rrfmt(Fmt *f) +{ + RR *rp; + char *strp; + Fmt fstr; + int rv; + char buf[Domlen]; + Server *s; + Txt *t; + + fmtstrinit(&fstr); + + rp = va_arg(f->args, RR*); + if(rp == 0){ + fmtprint(&fstr, "<null>"); + goto out; + } + + fmtprint(&fstr, "%s %s", rp->owner->name, + rrname(rp->type, buf, sizeof buf)); + + if(rp->negative){ + fmtprint(&fstr, "\tnegative - rcode %d", rp->negrcode); + goto out; + } + + switch(rp->type){ + case Thinfo: + fmtprint(&fstr, "\t%s %s", rp->cpu->name, rp->os->name); + break; + case Tcname: + case Tmb: + case Tmd: + case Tmf: + case Tns: + fmtprint(&fstr, "\t%s", rp->host->name); + break; + case Tmg: + case Tmr: + fmtprint(&fstr, "\t%s", rp->mb->name); + break; + case Tminfo: + fmtprint(&fstr, "\t%s %s", rp->mb->name, rp->rmb->name); + break; + case Tmx: + fmtprint(&fstr, "\t%lud %s", rp->pref, rp->host->name); + break; + case Ta: + case Taaaa: + fmtprint(&fstr, "\t%s", rp->ip->name); + break; + case Tptr: +// fmtprint(&fstr, "\t%s(%lud)", rp->ptr->name, rp->ptr->ordinal); + fmtprint(&fstr, "\t%s", rp->ptr->name); + break; + case Tsoa: + fmtprint(&fstr, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name, + rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry, + rp->soa->expire, rp->soa->minttl); + for(s = rp->soa->slaves; s != nil; s = s->next) + fmtprint(&fstr, " %s", s->name); + break; + case Tnull: + fmtprint(&fstr, "\t%.*H", rp->null->dlen, rp->null->data); + break; + case Ttxt: + fmtprint(&fstr, "\t"); + for(t = rp->txt; t != nil; t = t->next) + fmtprint(&fstr, "%s", t->p); + break; + case Trp: + fmtprint(&fstr, "\t%s %s", rp->rmb->name, rp->rp->name); + break; + case Tkey: + fmtprint(&fstr, "\t%d %d %d", rp->key->flags, rp->key->proto, + rp->key->alg); + break; + case Tsig: + fmtprint(&fstr, "\t%d %d %d %lud %lud %lud %d %s", + rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl, + rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name); + break; + case Tcert: + fmtprint(&fstr, "\t%d %d %d", + rp->sig->type, rp->sig->tag, rp->sig->alg); + break; + default: + break; + } +out: + strp = fmtstrflush(&fstr); + rv = fmtstrcpy(f, strp); + free(strp); + return rv; +} + +/* + * print conversion for rr records in attribute value form + */ +int +rravfmt(Fmt *f) +{ + RR *rp; + char *strp; + Fmt fstr; + int rv; + Server *s; + Txt *t; + int quote; + + fmtstrinit(&fstr); + + rp = va_arg(f->args, RR*); + if(rp == 0){ + fmtprint(&fstr, "<null>"); + goto out; + } + + if(rp->type == Tptr) + fmtprint(&fstr, "ptr=%s", rp->owner->name); + else + fmtprint(&fstr, "dom=%s", rp->owner->name); + + switch(rp->type){ + case Thinfo: + fmtprint(&fstr, " cpu=%s os=%s", rp->cpu->name, rp->os->name); + break; + case Tcname: + fmtprint(&fstr, " cname=%s", rp->host->name); + break; + case Tmb: + case Tmd: + case Tmf: + fmtprint(&fstr, " mbox=%s", rp->host->name); + break; + case Tns: + fmtprint(&fstr, " ns=%s", rp->host->name); + break; + case Tmg: + case Tmr: + fmtprint(&fstr, " mbox=%s", rp->mb->name); + break; + case Tminfo: + fmtprint(&fstr, " mbox=%s mbox=%s", rp->mb->name, rp->rmb->name); + break; + case Tmx: + fmtprint(&fstr, " pref=%lud mx=%s", rp->pref, rp->host->name); + break; + case Ta: + case Taaaa: + fmtprint(&fstr, " ip=%s", rp->ip->name); + break; + case Tptr: + fmtprint(&fstr, " dom=%s", rp->ptr->name); + break; + case Tsoa: + fmtprint(&fstr, " ns=%s mbox=%s serial=%lud refresh=%lud retry=%lud expire=%lud ttl=%lud", + rp->host->name, rp->rmb->name, rp->soa->serial, + rp->soa->refresh, rp->soa->retry, + rp->soa->expire, rp->soa->minttl); + for(s = rp->soa->slaves; s != nil; s = s->next) + fmtprint(&fstr, " dnsslave=%s", s->name); + break; + case Tnull: + fmtprint(&fstr, " null=%.*H", rp->null->dlen, rp->null->data); + break; + case Ttxt: + fmtprint(&fstr, " txt="); + quote = 0; + for(t = rp->txt; t != nil; t = t->next) + if(strchr(t->p, ' ')) + quote = 1; + if(quote) + fmtprint(&fstr, "\""); + for(t = rp->txt; t != nil; t = t->next) + fmtprint(&fstr, "%s", t->p); + if(quote) + fmtprint(&fstr, "\""); + break; + case Trp: + fmtprint(&fstr, " rp=%s txt=%s", rp->rmb->name, rp->rp->name); + break; + case Tkey: + fmtprint(&fstr, " flags=%d proto=%d alg=%d", + rp->key->flags, rp->key->proto, rp->key->alg); + break; + case Tsig: + fmtprint(&fstr, " type=%d alg=%d labels=%d ttl=%lud exp=%lud incep=%lud tag=%d signer=%s", + rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl, + rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name); + break; + case Tcert: + fmtprint(&fstr, " type=%d tag=%d alg=%d", + rp->sig->type, rp->sig->tag, rp->sig->alg); + break; + default: + break; + } +out: + strp = fmtstrflush(&fstr); + rv = fmtstrcpy(f, strp); + free(strp); + return rv; +} + +void +warning(char *fmt, ...) +{ + char dnserr[128]; + va_list arg; + + va_start(arg, fmt); + vseprint(dnserr, dnserr+sizeof(dnserr), fmt, arg); + va_end(arg); + syslog(1, "dns", dnserr); +} + +/* + * create a slave process to handle a request to avoid one request blocking + * another + */ +void +slave(Request *req) +{ + static int slaveid; + + if(req->isslave) + return; /* we're already a slave process */ + + /* limit parallelism */ + if(getactivity(req) > Maxactive){ + putactivity(); + return; + } + + switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ + case -1: + putactivity(); + break; + case 0: + req->isslave = 1; + break; + default: + longjmp(req->mret, 1); + } +} + +/* + * chasing down double free's + */ +void +dncheck(void *p, int dolock) +{ + int i; + DN *dp; + RR *rp; + + if(p != nil){ + dp = p; + assert(dp->magic == DNmagic); + } + + if(!testing) + return; + + if(dolock) + lock(&dnlock); + for(i = 0; i < HTLEN; i++) + for(dp = ht[i]; dp; dp = dp->next){ + assert(dp != p); + assert(dp->magic == DNmagic); + for(rp = dp->rr; rp; rp = rp->next){ + assert(rp->magic == RRmagic); + assert(rp->cached); + assert(rp->owner == dp); + } + } + if(dolock) + unlock(&dnlock); +} + +static int +rrequiv(RR *r1, RR *r2) +{ + return r1->owner == r2->owner + && r1->type == r2->type + && r1->arg0 == r2->arg0 + && r1->arg1 == r2->arg1; +} + +void +unique(RR *rp) +{ + RR **l, *nrp; + + for(; rp; rp = rp->next){ + l = &rp->next; + for(nrp = *l; nrp; nrp = *l){ + if(rrequiv(rp, nrp)){ + *l = nrp->next; + rrfree(nrp); + } else + l = &nrp->next; + } + } +} + +/* + * true if second domain is subsumed by the first + */ +int +subsume(char *higher, char *lower) +{ + int hn, ln; + + ln = strlen(lower); + hn = strlen(higher); + if(ln < hn) + return 0; + + if(cistrcmp(lower + ln - hn, higher) != 0) + return 0; + + if(ln > hn && hn != 0 && lower[ln - hn - 1] != '.') + return 0; + + return 1; +} + +/* + * randomize the order we return items to provide some + * load balancing for servers. + * + * only randomize the first class of entries + */ +RR* +randomize(RR *rp) +{ + RR *first, *last, *x, *base; + ulong n; + + if(rp == nil || rp->next == nil) + return rp; + + /* just randomize addresses and mx's */ + for(x = rp; x; x = x->next) + if(x->type != Ta && x->type != Tmx && x->type != Tns) + return rp; + + base = rp; + + n = rand(); + last = first = nil; + while(rp != nil){ + /* stop randomizing if we've moved past our class */ + if(base->auth != rp->auth || base->db != rp->db){ + last->next = rp; + break; + } + + /* unchain */ + x = rp; + rp = x->next; + x->next = nil; + + if(n&1){ + /* add to tail */ + if(last == nil) + first = x; + else + last->next = x; + last = x; + } else { + /* add to head */ + if(last == nil) + last = x; + x->next = first; + first = x; + } + + /* reroll the dice */ + n >>= 1; + } + return first; +} + +static int +sencodefmt(Fmt *f) +{ + char *out; + char *buf; + int i, len; + int ilen; + int rv; + uchar *b; + char obuf[64]; // rsc optimization + + if(!(f->flags&FmtPrec) || f->prec < 1) + goto error; + + b = va_arg(f->args, uchar*); + if(b == nil) + goto error; + + /* if it's a printable, go for it */ + len = f->prec; + for(i = 0; i < len; i++) + if(!isprint(b[i])) + break; + if(i == len){ + if(len >= sizeof obuf) + len = sizeof(obuf)-1; + memmove(obuf, b, len); + obuf[len] = 0; + fmtstrcpy(f, obuf); + return 0; + } + + ilen = f->prec; + f->prec = 0; + f->flags &= ~FmtPrec; + switch(f->r){ + case '<': + len = (8*ilen+4)/5 + 3; + break; + case '[': + len = (8*ilen+5)/6 + 4; + break; + case 'H': + len = 2*ilen + 1; + break; + default: + goto error; + } + + if(len > sizeof(obuf)){ + buf = malloc(len); + if(buf == nil) + goto error; + } else + buf = obuf; + + // convert + out = buf; + switch(f->r){ + case '<': + rv = enc32(out, len, b, ilen); + break; + case '[': + rv = enc64(out, len, b, ilen); + break; + case 'H': + rv = enc16(out, len, b, ilen); + break; + default: + rv = -1; + break; + } + if(rv < 0) + goto error; + + fmtstrcpy(f, buf); + if(buf != obuf) + free(buf); + return 0; + +error: + return fmtstrcpy(f, "<encodefmt>"); + +} + +void* +emalloc(int size) +{ + char *x; + + x = mallocz(size, 1); + if(x == nil) + abort(); + setmalloctag(x, getcallerpc(&size)); + return x; +} + +char* +estrdup(char *s) +{ + int size; + char *p; + + size = strlen(s)+1; + p = mallocz(size, 0); + if(p == nil) + abort(); + memmove(p, s, size); + setmalloctag(p, getcallerpc(&s)); + return p; +} + +/* + * create a pointer record + */ +static RR* +mkptr(DN *dp, char *ptr, ulong ttl) +{ + DN *ipdp; + RR *rp; + + ipdp = dnlookup(ptr, Cin, 1); + + rp = rralloc(Tptr); + rp->ptr = dp; + rp->owner = ipdp; + rp->db = 1; + if(ttl) + rp->ttl = ttl; + return rp; +} + +/* + * look for all ip addresses in this network and make + * pointer records for them. + */ +void +dnptr(uchar *net, uchar *mask, char *dom, int bytes, int ttl) +{ + int i, j; + DN *dp; + RR *rp, *nrp, *first, **l; + uchar ip[IPaddrlen]; + uchar nnet[IPaddrlen]; + char ptr[Domlen]; + char *p, *e; + + l = &first; + first = nil; + for(i = 0; i < HTLEN; i++){ + for(dp = ht[i]; dp; dp = dp->next){ + for(rp = dp->rr; rp; rp = rp->next){ + if(rp->type != Ta || rp->negative) + continue; + parseip(ip, rp->ip->name); + maskip(ip, mask, nnet); + if(ipcmp(net, nnet) != 0) + continue; + p = ptr; + e = ptr+sizeof(ptr); + for(j = IPaddrlen-1; j >= IPaddrlen-bytes; j--) + p = seprint(p, e, "%d.", ip[j]); + seprint(p, e, "%s", dom); + nrp = mkptr(dp, ptr, ttl); + *l = nrp; + l = &nrp->next; + } + } + } + + for(rp = first; rp != nil; rp = nrp){ + nrp = rp->next; + rp->next = nil; + rrattach(rp, 1); + } +} + +void +freeserverlist(Server *s) +{ + Server *next; + + for(; s != nil; s = next){ + next = s->next; + free(s); + } +} + +void +addserver(Server **l, char *name) +{ + Server *s; + + while(*l) + l = &(*l)->next; + s = malloc(sizeof(Server)+strlen(name)+1); + if(s == nil) + return; + s->name = (char*)(s+1); + strcpy(s->name, name); + s->next = nil; + *l = s; +} + +Server* +copyserverlist(Server *s) +{ + Server *ns; + + + for(ns = nil; s != nil; s = s->next) + addserver(&ns, s->name); + return ns; +} |