📄 dn.c
字号:
#include <u.h>#include <libc.h>#include <ip.h>#include <pool.h>#include <ctype.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; 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",[Tafsdb] "afsdb",[Tx25] "x.25",[Tisdn] "isdn",[Trt] "rt",[Tnsap] "nsap",[Tnsapptr] "nsap-ptr",[Tsig] "sig",[Tkey] "key",[Tpx] "px",[Tgpos] "gpos",[Taaaa] "ipv6",[Tloc] "loc",[Tnxt] "nxt",[Teid] "eid",[Tnimloc] "nimrod",[Tsrv] "srv",[Tatma] "atma",[Tnaptr] "naptr",[Tkx] "kx",[Tcert] "cert",[Ta6] "a6",[Tdname] "dname",[Tsink] "sink",[Topt] "opt",[Tapl] "apl",[Tds] "ds",[Tsshfp] "sshfp",[Tipseckey] "ipseckey",[Trrsig] "rrsig",[Tnsec] "nsec",[Tdnskey] "dnskey",[Tspf] "spf",[Tuinfo] "uinfo",[Tuid] "uid",[Tgid] "gid",[Tunspec] "unspec",[Ttkey] "tkey",[Ttsig] "tsig",[Tixfr] "ixfr",[Taxfr] "axfr",[Tmailb] "mailb",[Tmaila] "maila",[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",[Ryxdomain] "name should not exist",[Ryxrrset] "rr set should not exist",[Rnxrrset] "rr set should exist",[Rnotauth] "not authorative",[Rnotzone] "not in zone",[Rbadvers] "bad opt version",/* [Rbadsig] "bad signature", */[Rbadkey] "bad key",[Rbadtime] "bad signature time",[Rbadmode] "bad mode",[Rbadname] "duplicate key name",[Rbadalg] "bad algorithm",};/* names of op codes */char *opname[] ={[Oquery] "query",[Oinverse] "inverse query (retired)",[Ostatus] "status",[Oupdate] "update",};Lock dnlock;static int sencodefmt(Fmt*);/* * set up a pipe to use as a lock */voiddninit(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 ulongdnhash(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 */voiddndump(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 */voiddnpurge(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 */voiddnage(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 */voiddnageall(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) */voiddnagedb(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 */voiddnauthdb(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. */intgetactivity(Request *req, int recursive){ int rv; if(traceactivity) syslog(0, "dns", "get %d by %d from %p", dnvars.active, getpid(), getcallerpc(&req)); lock(&dnvars); /* * can't block here if we're already holding one * of the dnvars.active (recursive). will deadlock. */ while(!recursive && dnvars.mutex){ unlock(&dnvars); sleep(200); lock(&dnvars); } rv = ++dnvars.active; now = time(0); req->id = ++dnvars.id; unlock(&dnvars); return rv;}voidputactivity(int recursive){ static ulong lastclean; if(traceactivity) syslog(0, "dns", "put %d by %d", dnvars.active, getpid()); lock(&dnvars); dnvars.active--; assert(dnvars.active >= 0); /* "dnvars.active %d", dnvars.active */; /* * clean out old entries and check for new db periodicly * can't block here if being called to let go a "recursive" lock * or we'll deadlock waiting for ourselves to give up the dnvars.active. */ if(recursive || dnvars.mutex || (needrefresh == 0 && dnvars.active > 0)){ unlock(&dnvars); return; } /* wait till we're alone */ dnvars.mutex = 1; while(dnvars.active > 0){ unlock(&dnvars); sleep(100); lock(&dnvars); } unlock(&dnvars); 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 voidrrattach1(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 */voidrrattach(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 */voidrrfree(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 */voidrrfreelist(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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -