13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
3219b2ee8SDavid du Colombier #include <ip.h>
43e12c5d1SDavid du Colombier #include "dns.h"
53e12c5d1SDavid du Colombier
67dd7cddfSDavid du Colombier static RR* doextquery(DNSmsg*, Request*, int);
73e12c5d1SDavid du Colombier static void hint(RR**, RR*);
83e12c5d1SDavid du Colombier
9b85a8364SDavid du Colombier /* set in dns.c */
10b85a8364SDavid du Colombier int norecursion; /* don't allow recursive requests */
11b85a8364SDavid du Colombier
123e12c5d1SDavid du Colombier /*
137dd7cddfSDavid du Colombier * answer a dns request
143e12c5d1SDavid du Colombier */
153e12c5d1SDavid du Colombier void
dnserver(DNSmsg * reqp,DNSmsg * repp,Request * req,uchar * srcip,int rcode)164f8f669cSDavid du Colombier dnserver(DNSmsg *reqp, DNSmsg *repp, Request *req, uchar *srcip, int rcode)
173e12c5d1SDavid du Colombier {
184f8f669cSDavid du Colombier int recursionflag;
194f8f669cSDavid du Colombier char *cp, *errmsg;
204f8f669cSDavid du Colombier char tname[32];
217dd7cddfSDavid du Colombier DN *nsdp, *dp;
227dd7cddfSDavid du Colombier Area *myarea;
23e6d9d902SDavid du Colombier RR *tp, *neg, *rp;
243e12c5d1SDavid du Colombier
259a747e4fSDavid du Colombier dncheck(nil, 1);
269a747e4fSDavid du Colombier
27b85a8364SDavid du Colombier recursionflag = norecursion? 0: Fcanrec;
287dd7cddfSDavid du Colombier memset(repp, 0, sizeof(*repp));
297dd7cddfSDavid du Colombier repp->id = reqp->id;
30b85a8364SDavid du Colombier repp->flags = Fresp | recursionflag | Oquery;
317dd7cddfSDavid du Colombier
327dd7cddfSDavid du Colombier /* move one question from reqp to repp */
337dd7cddfSDavid du Colombier tp = reqp->qd;
347dd7cddfSDavid du Colombier reqp->qd = tp->next;
354f8f669cSDavid du Colombier tp->next = nil;
367dd7cddfSDavid du Colombier repp->qd = tp;
377dd7cddfSDavid du Colombier
384f8f669cSDavid du Colombier if (rcode) {
394f8f669cSDavid du Colombier errmsg = "";
404f8f669cSDavid du Colombier if (rcode >= 0 && rcode < nrname)
414f8f669cSDavid du Colombier errmsg = rname[rcode];
424f8f669cSDavid du Colombier dnslog("server: response code 0%o (%s), req from %I",
434f8f669cSDavid du Colombier rcode, errmsg, srcip);
444f8f669cSDavid du Colombier /* provide feedback to clients who send us trash */
454f8f669cSDavid du Colombier repp->flags = (rcode&Rmask) | Fresp | Fcanrec | Oquery;
464f8f669cSDavid du Colombier return;
474f8f669cSDavid du Colombier }
485d459b5aSDavid du Colombier if(!rrsupported(repp->qd->type)){
494f8f669cSDavid du Colombier dnslog("server: unsupported request %s from %I",
504f8f669cSDavid du Colombier rrname(repp->qd->type, tname, sizeof tname), srcip);
515d459b5aSDavid du Colombier repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
525d459b5aSDavid du Colombier return;
535d459b5aSDavid du Colombier }
545d459b5aSDavid du Colombier
557dd7cddfSDavid du Colombier if(repp->qd->owner->class != Cin){
564f8f669cSDavid du Colombier dnslog("server: unsupported class %d from %I",
574f8f669cSDavid du Colombier repp->qd->owner->class, srcip);
587dd7cddfSDavid du Colombier repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
593e12c5d1SDavid du Colombier return;
603e12c5d1SDavid du Colombier }
613e12c5d1SDavid du Colombier
627dd7cddfSDavid du Colombier myarea = inmyarea(repp->qd->owner->name);
63b85a8364SDavid du Colombier if(myarea != nil) {
64b85a8364SDavid du Colombier if(repp->qd->type == Tixfr || repp->qd->type == Taxfr){
6576783259SDavid du Colombier dnslog("server: unsupported xfr request %s for %s from %I",
664f8f669cSDavid du Colombier rrname(repp->qd->type, tname, sizeof tname),
674f8f669cSDavid du Colombier repp->qd->owner->name, srcip);
684f8f669cSDavid du Colombier repp->flags = Runimplimented | Fresp | recursionflag |
694f8f669cSDavid du Colombier Oquery;
707dd7cddfSDavid du Colombier return;
713e12c5d1SDavid du Colombier }
724f8f669cSDavid du Colombier } else
73b85a8364SDavid du Colombier if(norecursion) {
74b85a8364SDavid du Colombier /* we don't recurse and we're not authoritative */
75b85a8364SDavid du Colombier repp->flags = Rok | Fresp | Oquery;
76b85a8364SDavid du Colombier return;
77b85a8364SDavid du Colombier }
783e12c5d1SDavid du Colombier
793e12c5d1SDavid du Colombier /*
806d8e4566SDavid du Colombier * get the answer if we can, in *repp
813e12c5d1SDavid du Colombier */
827dd7cddfSDavid du Colombier if(reqp->flags & Frecurse)
837dd7cddfSDavid du Colombier neg = doextquery(repp, req, Recurse);
843e12c5d1SDavid du Colombier else
857dd7cddfSDavid du Colombier neg = doextquery(repp, req, Dontrecurse);
867dd7cddfSDavid du Colombier
877dd7cddfSDavid du Colombier /* authority is transitive */
887dd7cddfSDavid du Colombier if(myarea != nil || (repp->an && repp->an->auth))
897dd7cddfSDavid du Colombier repp->flags |= Fauth;
907dd7cddfSDavid du Colombier
917dd7cddfSDavid du Colombier /* pass on error codes */
924f8f669cSDavid du Colombier if(repp->an == nil){
937dd7cddfSDavid du Colombier dp = dnlookup(repp->qd->owner->name, repp->qd->owner->class, 0);
944f8f669cSDavid du Colombier if(dp->rr == nil)
957dd7cddfSDavid du Colombier if(reqp->flags & Frecurse)
964f8f669cSDavid du Colombier repp->flags |= dp->respcode | Fauth;
977dd7cddfSDavid du Colombier }
987dd7cddfSDavid du Colombier
994f8f669cSDavid du Colombier if(myarea == nil)
1007dd7cddfSDavid du Colombier /*
1017dd7cddfSDavid du Colombier * add name server if we know
1027dd7cddfSDavid du Colombier */
1037dd7cddfSDavid du Colombier for(cp = repp->qd->owner->name; cp; cp = walkup(cp)){
1047dd7cddfSDavid du Colombier nsdp = dnlookup(cp, repp->qd->owner->class, 0);
1054f8f669cSDavid du Colombier if(nsdp == nil)
1067dd7cddfSDavid du Colombier continue;
1077dd7cddfSDavid du Colombier
1087dd7cddfSDavid du Colombier repp->ns = rrlookup(nsdp, Tns, OKneg);
1097dd7cddfSDavid du Colombier if(repp->ns){
1107dd7cddfSDavid du Colombier /* don't pass on anything we know is wrong */
1117dd7cddfSDavid du Colombier if(repp->ns->negative){
112c3617180SDavid du Colombier lock(&dnlock);
113e6d9d902SDavid du Colombier rp = repp->ns;
1147dd7cddfSDavid du Colombier repp->ns = nil;
115e6d9d902SDavid du Colombier rrfreelist(rp);
116c3617180SDavid du Colombier unlock(&dnlock);
1177dd7cddfSDavid du Colombier }
1187dd7cddfSDavid du Colombier break;
1197dd7cddfSDavid du Colombier }
1207dd7cddfSDavid du Colombier
1216dc4800dSDavid du Colombier if (strncmp(nsdp->name, "local#", 6) == 0)
1226dc4800dSDavid du Colombier dnslog("returning %s as nameserver", nsdp->name);
1237dd7cddfSDavid du Colombier repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0);
1247dd7cddfSDavid du Colombier if(repp->ns)
1257dd7cddfSDavid du Colombier break;
1267dd7cddfSDavid du Colombier }
1273e12c5d1SDavid du Colombier
1283e12c5d1SDavid du Colombier /*
1293e12c5d1SDavid du Colombier * add ip addresses as hints
1303e12c5d1SDavid du Colombier */
1317dd7cddfSDavid du Colombier if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){
1327dd7cddfSDavid du Colombier for(tp = repp->ns; tp; tp = tp->next)
1337dd7cddfSDavid du Colombier hint(&repp->ar, tp);
1347dd7cddfSDavid du Colombier for(tp = repp->an; tp; tp = tp->next)
1357dd7cddfSDavid du Colombier hint(&repp->ar, tp);
1367dd7cddfSDavid du Colombier }
1373e12c5d1SDavid du Colombier
138530fef66SDavid du Colombier /* hint calls rrlookup which holds dnlock, so don't lock before this. */
139530fef66SDavid du Colombier
1407dd7cddfSDavid du Colombier /*
1414f8f669cSDavid du Colombier * add an soa to the authority section to help client
1424f8f669cSDavid du Colombier * with negative caching
1437dd7cddfSDavid du Colombier */
1444f8f669cSDavid du Colombier if(repp->an == nil)
1457dd7cddfSDavid du Colombier if(myarea != nil){
146530fef66SDavid du Colombier lock(&dnlock);
1477dd7cddfSDavid du Colombier rrcopy(myarea->soarr, &tp);
148b7b24591SDavid du Colombier rrcat(&repp->ns, tp);
149530fef66SDavid du Colombier unlock(&dnlock);
1507dd7cddfSDavid du Colombier } else if(neg != nil) {
151530fef66SDavid du Colombier if(neg->negsoaowner != nil) {
152530fef66SDavid du Colombier tp = rrlookup(neg->negsoaowner, Tsoa, NOneg);
153530fef66SDavid du Colombier lock(&dnlock);
154530fef66SDavid du Colombier rrcat(&repp->ns, tp);
155530fef66SDavid du Colombier unlock(&dnlock);
156530fef66SDavid du Colombier }
1577dd7cddfSDavid du Colombier repp->flags |= neg->negrcode;
1583e12c5d1SDavid du Colombier }
159bd389b36SDavid du Colombier
1607dd7cddfSDavid du Colombier /*
1617dd7cddfSDavid du Colombier * get rid of duplicates
1627dd7cddfSDavid du Colombier */
163530fef66SDavid du Colombier lock(&dnlock);
1647dd7cddfSDavid du Colombier unique(repp->an);
1657dd7cddfSDavid du Colombier unique(repp->ns);
1667dd7cddfSDavid du Colombier unique(repp->ar);
1677dd7cddfSDavid du Colombier
1687dd7cddfSDavid du Colombier rrfreelist(neg);
169530fef66SDavid du Colombier unlock(&dnlock);
1709a747e4fSDavid du Colombier
1719a747e4fSDavid du Colombier dncheck(nil, 1);
1723e12c5d1SDavid du Colombier }
1733e12c5d1SDavid du Colombier
1743e12c5d1SDavid du Colombier /*
1753e12c5d1SDavid du Colombier * satisfy a recursive request. dnlookup will handle cnames.
1763e12c5d1SDavid du Colombier */
1777dd7cddfSDavid du Colombier static RR*
doextquery(DNSmsg * mp,Request * req,int recurse)1787dd7cddfSDavid du Colombier doextquery(DNSmsg *mp, Request *req, int recurse)
1793e12c5d1SDavid du Colombier {
180adb31a62SDavid du Colombier ushort type;
1813e12c5d1SDavid du Colombier char *name;
1827dd7cddfSDavid du Colombier RR *rp, *neg;
1833e12c5d1SDavid du Colombier
1843e12c5d1SDavid du Colombier name = mp->qd->owner->name;
1853e12c5d1SDavid du Colombier type = mp->qd->type;
1867dd7cddfSDavid du Colombier rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
1873e12c5d1SDavid du Colombier
188530fef66SDavid du Colombier lock(&dnlock);
1897dd7cddfSDavid du Colombier /* don't return soa hints as answers, it's wrong */
1906dc4800dSDavid du Colombier if(rp && rp->db && !rp->auth && rp->type == Tsoa) {
1917dd7cddfSDavid du Colombier rrfreelist(rp);
1926dc4800dSDavid du Colombier rp = nil;
1936dc4800dSDavid du Colombier }
1943e12c5d1SDavid du Colombier
1957dd7cddfSDavid du Colombier /* don't let negative cached entries escape */
1967dd7cddfSDavid du Colombier neg = rrremneg(&rp);
1977dd7cddfSDavid du Colombier rrcat(&mp->an, rp);
198530fef66SDavid du Colombier unlock(&dnlock);
1997dd7cddfSDavid du Colombier return neg;
2003e12c5d1SDavid du Colombier }
2013e12c5d1SDavid du Colombier
2023e12c5d1SDavid du Colombier static void
hint(RR ** last,RR * rp)2033e12c5d1SDavid du Colombier hint(RR **last, RR *rp)
2043e12c5d1SDavid du Colombier {
2057dd7cddfSDavid du Colombier RR *hp;
2067dd7cddfSDavid du Colombier
2073e12c5d1SDavid du Colombier switch(rp->type){
2083e12c5d1SDavid du Colombier case Tns:
2093e12c5d1SDavid du Colombier case Tmx:
2103e12c5d1SDavid du Colombier case Tmb:
2113e12c5d1SDavid du Colombier case Tmf:
2123e12c5d1SDavid du Colombier case Tmd:
213*ada60c6cSDavid du Colombier assert(rp->host != nil);
2147dd7cddfSDavid du Colombier hp = rrlookup(rp->host, Ta, NOneg);
2157dd7cddfSDavid du Colombier if(hp == nil)
2167dd7cddfSDavid du Colombier hp = dblookup(rp->host->name, Cin, Ta, 0, 0);
217d9924332SDavid du Colombier if(hp == nil)
218d9924332SDavid du Colombier hp = rrlookup(rp->host, Taaaa, NOneg);
219d9924332SDavid du Colombier if(hp == nil)
220d9924332SDavid du Colombier hp = dblookup(rp->host->name, Cin, Taaaa, 0, 0);
221*ada60c6cSDavid du Colombier if (hp && hp->owner && hp->owner->name &&
222*ada60c6cSDavid du Colombier strncmp(hp->owner->name, "local#", 6) == 0)
2236dc4800dSDavid du Colombier dnslog("returning %s as hint", hp->owner->name);
2244faf7596SDavid du Colombier lock(&dnlock);
2257dd7cddfSDavid du Colombier rrcat(last, hp);
2264faf7596SDavid du Colombier unlock(&dnlock);
2273e12c5d1SDavid du Colombier break;
2283e12c5d1SDavid du Colombier }
2293e12c5d1SDavid du Colombier }
230