| #include <u.h> |
| #include <libc.h> |
| #include <ip.h> |
| #include <ctype.h> |
| #include <bio.h> |
| #include <ndb.h> |
| #include <thread.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[Tall+2] = |
| { |
| nil, |
| "ip", |
| "ns", |
| "md", |
| "mf", |
| "cname", |
| "soa", |
| "mb", |
| "mg", |
| "mr", |
| "null", |
| "wks", |
| "ptr", |
| "hinfo", |
| "minfo", |
| "mx", |
| "txt", |
| "rp", |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| "sig", |
| "key", |
| nil, |
| nil, |
| "aaaa", |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| nil, |
| "cert", |
| nil, |
| nil, |
| |
| /* 40 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 48 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 56 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 64 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 72 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 80 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 88 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 96 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 104 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 112 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 120 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 128 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 136 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 144 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 152 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 160 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 168 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 176 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 184 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 192 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 200 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 208 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 216 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 224 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 232 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 240 */ nil, nil, nil, nil, nil, nil, nil, nil, |
| /* 248 */ nil, nil, nil, |
| |
| "ixfr", |
| "axfr", |
| "mailb", |
| nil, |
| "all", |
| nil |
| }; |
| |
| /* names of response codes */ |
| char *rname[Rmask+1] = |
| { |
| "ok", |
| "format error", |
| "server failure", |
| "bad name", |
| "unimplemented", |
| "we don't like you" |
| }; |
| |
| 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; |
| |
| 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; |
| |
| 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.%d", dnvars.active, getpid(), threadid()); |
| 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) |
| { |
| if(traceactivity) syslog(0, "dns", "put %d by %d.%d", dnvars.active, getpid(), threadid()); |
| 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 */ |
| 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); |
| } |
| |
| /* |
| * chasing down double free's |
| */ |
| void |
| dncheck(void *p, int dolock) |
| { |
| DN *dp; |
| int i; |
| 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; |
| } |