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 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; 234f8f669cSDavid du Colombier RR *tp, *neg; 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){ 1127dd7cddfSDavid du Colombier rrfreelist(repp->ns); 1137dd7cddfSDavid du Colombier repp->ns = nil; 1147dd7cddfSDavid du Colombier } 1157dd7cddfSDavid du Colombier break; 1167dd7cddfSDavid du Colombier } 1177dd7cddfSDavid du Colombier 1186dc4800dSDavid du Colombier if (strncmp(nsdp->name, "local#", 6) == 0) 1196dc4800dSDavid du Colombier dnslog("returning %s as nameserver", nsdp->name); 1207dd7cddfSDavid du Colombier repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0); 1217dd7cddfSDavid du Colombier if(repp->ns) 1227dd7cddfSDavid du Colombier break; 1237dd7cddfSDavid du Colombier } 1243e12c5d1SDavid du Colombier 1253e12c5d1SDavid du Colombier /* 1263e12c5d1SDavid du Colombier * add ip addresses as hints 1273e12c5d1SDavid du Colombier */ 1287dd7cddfSDavid du Colombier if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){ 1297dd7cddfSDavid du Colombier for(tp = repp->ns; tp; tp = tp->next) 1307dd7cddfSDavid du Colombier hint(&repp->ar, tp); 1317dd7cddfSDavid du Colombier for(tp = repp->an; tp; tp = tp->next) 1327dd7cddfSDavid du Colombier hint(&repp->ar, tp); 1337dd7cddfSDavid du Colombier } 1343e12c5d1SDavid du Colombier 135*530fef66SDavid du Colombier /* hint calls rrlookup which holds dnlock, so don't lock before this. */ 136*530fef66SDavid du Colombier 1377dd7cddfSDavid du Colombier /* 1384f8f669cSDavid du Colombier * add an soa to the authority section to help client 1394f8f669cSDavid du Colombier * with negative caching 1407dd7cddfSDavid du Colombier */ 1414f8f669cSDavid du Colombier if(repp->an == nil) 1427dd7cddfSDavid du Colombier if(myarea != nil){ 143*530fef66SDavid du Colombier lock(&dnlock); 1447dd7cddfSDavid du Colombier rrcopy(myarea->soarr, &tp); 145b7b24591SDavid du Colombier rrcat(&repp->ns, tp); 146*530fef66SDavid du Colombier unlock(&dnlock); 1477dd7cddfSDavid du Colombier } else if(neg != nil) { 148*530fef66SDavid du Colombier if(neg->negsoaowner != nil) { 149*530fef66SDavid du Colombier tp = rrlookup(neg->negsoaowner, Tsoa, NOneg); 150*530fef66SDavid du Colombier lock(&dnlock); 151*530fef66SDavid du Colombier rrcat(&repp->ns, tp); 152*530fef66SDavid du Colombier unlock(&dnlock); 153*530fef66SDavid du Colombier } 1547dd7cddfSDavid du Colombier repp->flags |= neg->negrcode; 1553e12c5d1SDavid du Colombier } 156bd389b36SDavid du Colombier 1577dd7cddfSDavid du Colombier /* 1587dd7cddfSDavid du Colombier * get rid of duplicates 1597dd7cddfSDavid du Colombier */ 160*530fef66SDavid du Colombier lock(&dnlock); 1617dd7cddfSDavid du Colombier unique(repp->an); 1627dd7cddfSDavid du Colombier unique(repp->ns); 1637dd7cddfSDavid du Colombier unique(repp->ar); 1647dd7cddfSDavid du Colombier 1657dd7cddfSDavid du Colombier rrfreelist(neg); 166*530fef66SDavid du Colombier unlock(&dnlock); 1679a747e4fSDavid du Colombier 1689a747e4fSDavid du Colombier dncheck(nil, 1); 1693e12c5d1SDavid du Colombier } 1703e12c5d1SDavid du Colombier 1713e12c5d1SDavid du Colombier /* 1723e12c5d1SDavid du Colombier * satisfy a recursive request. dnlookup will handle cnames. 1733e12c5d1SDavid du Colombier */ 1747dd7cddfSDavid du Colombier static RR* 1757dd7cddfSDavid du Colombier doextquery(DNSmsg *mp, Request *req, int recurse) 1763e12c5d1SDavid du Colombier { 177adb31a62SDavid du Colombier ushort type; 1783e12c5d1SDavid du Colombier char *name; 1797dd7cddfSDavid du Colombier RR *rp, *neg; 1803e12c5d1SDavid du Colombier 1813e12c5d1SDavid du Colombier name = mp->qd->owner->name; 1823e12c5d1SDavid du Colombier type = mp->qd->type; 1837dd7cddfSDavid du Colombier rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0); 1843e12c5d1SDavid du Colombier 185*530fef66SDavid du Colombier lock(&dnlock); 1867dd7cddfSDavid du Colombier /* don't return soa hints as answers, it's wrong */ 1876dc4800dSDavid du Colombier if(rp && rp->db && !rp->auth && rp->type == Tsoa) { 1887dd7cddfSDavid du Colombier rrfreelist(rp); 1896dc4800dSDavid du Colombier rp = nil; 1906dc4800dSDavid du Colombier } 1913e12c5d1SDavid du Colombier 1927dd7cddfSDavid du Colombier /* don't let negative cached entries escape */ 1937dd7cddfSDavid du Colombier neg = rrremneg(&rp); 1947dd7cddfSDavid du Colombier rrcat(&mp->an, rp); 195*530fef66SDavid du Colombier unlock(&dnlock); 1967dd7cddfSDavid du Colombier return neg; 1973e12c5d1SDavid du Colombier } 1983e12c5d1SDavid du Colombier 1993e12c5d1SDavid du Colombier static void 2003e12c5d1SDavid du Colombier hint(RR **last, RR *rp) 2013e12c5d1SDavid du Colombier { 2027dd7cddfSDavid du Colombier RR *hp; 2037dd7cddfSDavid du Colombier 2043e12c5d1SDavid du Colombier switch(rp->type){ 2053e12c5d1SDavid du Colombier case Tns: 2063e12c5d1SDavid du Colombier case Tmx: 2073e12c5d1SDavid du Colombier case Tmb: 2083e12c5d1SDavid du Colombier case Tmf: 2093e12c5d1SDavid du Colombier case Tmd: 2107dd7cddfSDavid du Colombier hp = rrlookup(rp->host, Ta, NOneg); 2117dd7cddfSDavid du Colombier if(hp == nil) 2127dd7cddfSDavid du Colombier hp = dblookup(rp->host->name, Cin, Ta, 0, 0); 2136dc4800dSDavid du Colombier if (hp && strncmp(hp->owner->name, "local#", 6) == 0) 2146dc4800dSDavid du Colombier dnslog("returning %s as hint", hp->owner->name); 2157dd7cddfSDavid du Colombier rrcat(last, hp); 2163e12c5d1SDavid du Colombier break; 2173e12c5d1SDavid du Colombier } 2183e12c5d1SDavid du Colombier } 219