ref: 45f7b30244297a8bf87789793eec875246ed063c
dir: /sys/src/cmd/ndb/dnserver.c/
#include <u.h> #include <libc.h> #include <ip.h> #include "dns.h" static RR* doextquery(DNSmsg*, Request*, int); static void hint(RR**, RR*); /* * answer a dns request */ void dnserver(DNSmsg *reqp, DNSmsg *repp, Request *req, uchar *srcip, int rcode) { int recursionflag; char *cp, *errmsg; char tname[32]; DN *nsdp, *dp; Area *myarea; RR *tp, *neg, *rp; recursionflag = cfg.nonrecursive? 0: Fcanrec; memset(repp, 0, sizeof(*repp)); repp->id = reqp->id; repp->flags = Fresp | recursionflag | Oquery; /* move one question from reqp to repp */ tp = reqp->qd; reqp->qd = tp->next; tp->next = nil; repp->qd = tp; if (rcode) { errmsg = ""; if (rcode >= 0 && rcode < nrname) errmsg = rname[rcode]; dnslog("%d: server: response code 0%o (%s), req from %I", req->id, rcode, errmsg, srcip); /* provide feedback to clients who send us trash */ repp->flags = (rcode&Rmask) | Fresp | Fcanrec | Oquery; return; } if(!rrsupported(repp->qd->type)){ if(debug) dnslog("%d: server: unsupported request %s from %I", req->id, rrname(repp->qd->type, tname, sizeof tname), srcip); repp->flags = Runimplimented | Fresp | Fcanrec | Oquery; return; } if(repp->qd->owner->class != Cin){ if(debug) dnslog("%d: server: unsupported class %d from %I", req->id, repp->qd->owner->class, srcip); repp->flags = Runimplimented | Fresp | Fcanrec | Oquery; return; } myarea = inmyarea(repp->qd->owner->name); if(myarea != nil) { if(repp->qd->type == Tixfr || repp->qd->type == Taxfr){ if(debug) dnslog("%d: server: unsupported xfr request %s for %s from %I", req->id, rrname(repp->qd->type, tname, sizeof tname), repp->qd->owner->name, srcip); repp->flags = Runimplimented | Fresp | recursionflag | Oquery; return; } } if(myarea == nil && cfg.nonrecursive) { /* we don't recurse and we're not authoritative */ repp->flags = Rok | Fresp | Oquery; neg = nil; } else { /* * get the answer if we can, in *repp */ if(reqp->flags & Frecurse) neg = doextquery(repp, req, Recurse); else neg = doextquery(repp, req, Dontrecurse); /* authority is transitive */ if(myarea != nil || (repp->an && repp->an->auth)) repp->flags |= Fauth; /* pass on error codes */ if(repp->an == nil){ dp = dnlookup(repp->qd->owner->name, repp->qd->owner->class, 0); if(dp->rr == nil) if(reqp->flags & Frecurse) repp->flags |= dp->respcode | Fauth; } } if(myarea == nil){ /* * add name server if we know */ for(cp = repp->qd->owner->name; cp; cp = walkup(cp)){ nsdp = dnlookup(cp, repp->qd->owner->class, 0); if(nsdp == nil) continue; repp->ns = rrlookup(nsdp, Tns, OKneg); if(repp->ns){ /* don't pass on anything we know is wrong */ if(repp->ns->negative){ rp = repp->ns; repp->ns = nil; rrfreelist(rp); } break; } if(strncmp(nsdp->name, "local#", 6) == 0) dnslog("%d: returning %s as nameserver", req->id, nsdp->name); repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0); if(repp->ns) break; } } /* * add ip addresses as hints */ if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){ for(tp = repp->ns; tp; tp = tp->next) hint(&repp->ar, tp); for(tp = repp->an; tp; tp = tp->next) hint(&repp->ar, tp); } /* * add an soa to the authority section to help client * with negative caching */ if(repp->an == nil){ if(myarea != nil){ rrcopy(myarea->soarr, &tp); rrcat(&repp->ns, tp); } else if(neg != nil) { if(neg->negsoaowner != nil) { tp = rrlookup(neg->negsoaowner, Tsoa, NOneg); rrcat(&repp->ns, tp); } repp->flags |= neg->negrcode; } } /* * get rid of duplicates */ unique(repp->an); unique(repp->ns); unique(repp->ar); rrfreelist(neg); } /* * satisfy a recursive request. dnlookup will handle cnames. */ static RR* doextquery(DNSmsg *mp, Request *req, int recurse) { ushort type; char *name; RR *rp, *neg; name = mp->qd->owner->name; type = mp->qd->type; rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, nil); /* don't return soa hints as answers, it's wrong */ if(rp && rp->db && !rp->auth && rp->type == Tsoa) { rrfreelist(rp); rp = nil; } /* don't let negative cached entries escape */ neg = rrremneg(&rp); rrcat(&mp->an, rp); return neg; } static void hint(RR **last, RR *rp) { RR *hp; switch(rp->type){ case Tns: case Tmx: case Tmb: case Tmf: case Tmd: hp = rrlookup(rp->host, Ta, NOneg); if(hp == nil) hp = dblookup(rp->host->name, Cin, Ta, 0, 0); if(hp == nil) hp = rrlookup(rp->host, Taaaa, NOneg); if(hp == nil) hp = dblookup(rp->host->name, Cin, Taaaa, 0, 0); if (hp && strncmp(hp->owner->name, "local#", 6) == 0) dnslog("returning %s as hint", hp->owner->name); rrcat(last, hp); break; } }