1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include <bio.h>
5 #include <ndb.h>
6 #include "dns.h"
7 
8 static RR*	doextquery(DNSmsg*, Request*, int);
9 static void	hint(RR**, RR*);
10 
11 extern char *logfile;
12 
13 /*
14  *  answer a dns request
15  */
16 void
dnserver(DNSmsg * reqp,DNSmsg * repp,Request * req)17 dnserver(DNSmsg *reqp, DNSmsg *repp, Request *req)
18 {
19 	RR *tp, *neg;
20 	char *cp;
21 	DN *nsdp, *dp;
22 	Area *myarea;
23 	char tname[32];
24 
25 	dncheck(nil, 1);
26 
27 	memset(repp, 0, sizeof(*repp));
28 	repp->id = reqp->id;
29 	repp->flags = Fresp | Fcanrec | Oquery;
30 
31 	/* move one question from reqp to repp */
32 	tp = reqp->qd;
33 	reqp->qd = tp->next;
34 	tp->next = 0;
35 	repp->qd = tp;
36 
37 	if(!rrsupported(repp->qd->type)){
38 		syslog(0, logfile, "server: request %s", rrname(repp->qd->type, tname, sizeof tname));
39 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
40 		return;
41 	}
42 
43 	if(repp->qd->owner->class != Cin){
44 		syslog(0, logfile, "server: class %d", repp->qd->owner->class);
45 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
46 		return;
47 	}
48 
49 	myarea = inmyarea(repp->qd->owner->name);
50 	if(myarea != nil && (repp->qd->type == Tixfr || repp->qd->type == Taxfr)){
51 		syslog(0, logfile, "server: request %s", rrname(repp->qd->type, tname, sizeof tname));
52 		repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
53 		return;
54 	}
55 
56 	/*
57 	 *  get the answer if we can
58 	 */
59 	if(reqp->flags & Frecurse)
60 		neg = doextquery(repp, req, Recurse);
61 	else
62 		neg = doextquery(repp, req, Dontrecurse);
63 
64 	/* authority is transitive */
65 	if(myarea != nil || (repp->an && repp->an->auth))
66 		repp->flags |= Fauth;
67 
68 	/* pass on error codes */
69 	if(repp->an == 0){
70 		dp = dnlookup(repp->qd->owner->name, repp->qd->owner->class, 0);
71 		if(dp->rr == 0)
72 			if(reqp->flags & Frecurse)
73 				repp->flags |= dp->nonexistent|Fauth;
74 	}
75 
76 	if(myarea == nil){
77 		/*
78 		 *  add name server if we know
79 		 */
80 		for(cp = repp->qd->owner->name; cp; cp = walkup(cp)){
81 			nsdp = dnlookup(cp, repp->qd->owner->class, 0);
82 			if(nsdp == 0)
83 				continue;
84 
85 			repp->ns = rrlookup(nsdp, Tns, OKneg);
86 			if(repp->ns){
87 				/* don't pass on anything we know is wrong */
88 				if(repp->ns->negative){
89 					rrfreelist(repp->ns);
90 					repp->ns = nil;
91 				}
92 				break;
93 			}
94 
95 			repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0);
96 			if(repp->ns)
97 				break;
98 		}
99 	}
100 
101 	/*
102 	 *  add ip addresses as hints
103 	 */
104 	if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){
105 		for(tp = repp->ns; tp; tp = tp->next)
106 			hint(&repp->ar, tp);
107 		for(tp = repp->an; tp; tp = tp->next)
108 			hint(&repp->ar, tp);
109 	}
110 
111 	/*
112 	 *  add an soa to the authority section to help client with negative caching
113 	 */
114 	if(repp->an == nil){
115 		if(myarea != nil){
116 			rrcopy(myarea->soarr, &tp);
117 			rrcat(&repp->ns, tp);
118 		} else if(neg != nil) {
119 			if(neg->negsoaowner != nil)
120 				rrcat(&repp->ns, rrlookup(neg->negsoaowner, Tsoa, NOneg));
121 			repp->flags |= neg->negrcode;
122 		}
123 	}
124 
125 	/*
126 	 *  get rid of duplicates
127 	 */
128 	unique(repp->an);
129 	unique(repp->ns);
130 	unique(repp->ar);
131 
132 	rrfreelist(neg);
133 
134 	dncheck(nil, 1);
135 }
136 
137 /*
138  *  satisfy a recursive request.  dnlookup will handle cnames.
139  */
140 static RR*
doextquery(DNSmsg * mp,Request * req,int recurse)141 doextquery(DNSmsg *mp, Request *req, int recurse)
142 {
143 	int type;
144 	char *name;
145 	RR *rp, *neg;
146 
147 	name = mp->qd->owner->name;
148 	type = mp->qd->type;
149 	rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
150 
151 	/* don't return soa hints as answers, it's wrong */
152 	if(rp && rp->db && !rp->auth && rp->type == Tsoa)
153 		rrfreelist(rp);
154 
155 	/* don't let negative cached entries escape */
156 	neg = rrremneg(&rp);
157 	rrcat(&mp->an, rp);
158 	return neg;
159 }
160 
161 static void
hint(RR ** last,RR * rp)162 hint(RR **last, RR *rp)
163 {
164 	RR *hp;
165 
166 	switch(rp->type){
167 	case Tns:
168 	case Tmx:
169 	case Tmb:
170 	case Tmf:
171 	case Tmd:
172 		hp = rrlookup(rp->host, Ta, NOneg);
173 		if(hp == nil)
174 			hp = dblookup(rp->host->name, Cin, Ta, 0, 0);
175 		rrcat(last, hp);
176 		break;
177 	}
178 }
179