1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include <bio.h>
5 #include <ndb.h>
6 #include <thread.h>
7 #include "dns.h"
8 
9 static char adir[40];
10 
11 static int
readmsg(int fd,uchar * buf,int max)12 readmsg(int fd, uchar *buf, int max)
13 {
14 	int n;
15 	uchar x[2];
16 
17 	if(readn(fd, x, 2) != 2)
18 		return -1;
19 	n = (x[0]<<8) | x[1];
20 	if(n > max)
21 		return -1;
22 	if(readn(fd, buf, n) != n)
23 		return -1;
24 	return n;
25 }
26 
27 static int
connreadmsg(int tfd,int * fd,uchar * buf,int max)28 connreadmsg(int tfd, int *fd, uchar *buf, int max)
29 {
30 	int n;
31 	int lfd;
32 	char ldir[40];
33 
34 	lfd = listen(adir, ldir);
35 	if (lfd < 0)
36 		return -1;
37 	*fd = accept(lfd, ldir);
38 	if (*fd >= 0)
39 		n = readmsg(*fd, buf, max);
40 	else
41 		n = -1;
42 	close(lfd);
43 	return n;
44 }
45 
46 static int
reply(int fd,DNSmsg * rep,Request * req,NetConnInfo * caller)47 reply(int fd, DNSmsg *rep, Request *req, NetConnInfo *caller)
48 {
49 	int len;
50 	char tname[32];
51 	uchar buf[4096];
52 	RR *rp;
53 
54 	if(debug){
55 		syslog(0, logfile, "%d: reply (%s) %s %s %ux",
56 			req->id, caller ? caller->raddr : "unk",
57 			rep->qd->owner->name,
58 			rrname(rep->qd->type, tname, sizeof tname),
59 			rep->flags);
60 		for(rp = rep->an; rp; rp = rp->next)
61 			syslog(0, logfile, "an %R", rp);
62 		for(rp = rep->ns; rp; rp = rp->next)
63 			syslog(0, logfile, "ns %R", rp);
64 		for(rp = rep->ar; rp; rp = rp->next)
65 			syslog(0, logfile, "ar %R", rp);
66 	}
67 
68 
69 	len = convDNS2M(rep, buf+2, sizeof(buf) - 2);
70 	if(len <= 0)
71 		abort(); /* "dnserver: converting reply" */;
72 	buf[0] = len>>8;
73 	buf[1] = len;
74 	if(write(fd, buf, len+2) < 0){
75 		syslog(0, logfile, "sending reply: %r");
76 		return -1;
77 	}
78 	return 0;
79 }
80 
81 /*
82  *  Hash table for domain names.  The hash is based only on the
83  *  first element of the domain name.
84  */
85 extern DN	*ht[HTLEN];
86 
87 static int
numelem(char * name)88 numelem(char *name)
89 {
90 	int i;
91 
92 	i = 1;
93 	for(; *name; name++)
94 		if(*name == '.')
95 			i++;
96 	return i;
97 }
98 
99 static int
inzone(DN * dp,char * name,int namelen,int depth)100 inzone(DN *dp, char *name, int namelen, int depth)
101 {
102 	int n;
103 
104 	if(dp->name == 0)
105 		return 0;
106 	if(numelem(dp->name) != depth)
107 		return 0;
108 	n = strlen(dp->name);
109 	if(n < namelen)
110 		return 0;
111 	if(strcmp(name, dp->name + n - namelen) != 0)
112 		return 0;
113 	if(n > namelen && dp->name[n - namelen - 1] != '.')
114 		return 0;
115 	return 1;
116 }
117 
118 static int
dnzone(DNSmsg * reqp,DNSmsg * repp,Request * req,int rfd,NetConnInfo * caller)119 dnzone(DNSmsg *reqp, DNSmsg *repp, Request *req, int rfd, NetConnInfo *caller)
120 {
121 	DN *dp, *ndp;
122 	RR r, *rp;
123 	int h, depth, found, nlen, rv;
124 
125 	rv = 0;
126 	memset(repp, 0, sizeof(*repp));
127 	repp->id = reqp->id;
128 	repp->flags = Fauth | Fresp | Fcanrec | Oquery;
129 	repp->qd = reqp->qd;
130 	reqp->qd = reqp->qd->next;
131 	repp->qd->next = 0;
132 	dp = repp->qd->owner;
133 
134 	/* send the soa */
135 	repp->an = rrlookup(dp, Tsoa, NOneg);
136 	rv = reply(rfd, repp, req, caller);
137 	if(repp->an == 0 || rv < 0)
138 		goto out;
139 	rrfreelist(repp->an);
140 
141 	nlen = strlen(dp->name);
142 
143 	/* construct a breadth first search of the name space (hard with a hash) */
144 	repp->an = &r;
145 	for(depth = numelem(dp->name); ; depth++){
146 		found = 0;
147 		for(h = 0; h < HTLEN; h++)
148 			for(ndp = ht[h]; ndp; ndp = ndp->next)
149 				if(inzone(ndp, dp->name, nlen, depth)){
150 					for(rp = ndp->rr; rp; rp = rp->next){
151 						/* there shouldn't be negatives, but just in case */
152 						if(rp->negative)
153 							continue;
154 
155 						/* don't send an soa's, ns's are enough */
156 						if(rp->type == Tsoa)
157 							continue;
158 
159 						r = *rp;
160 						r.next = 0;
161 						rv = reply(rfd, repp, req, caller);
162 						if(rv < 0)
163 							goto out;
164 					}
165 					found = 1;
166 				}
167 		if(!found)
168 			break;
169 	}
170 
171 	/* resend the soa */
172 	repp->an = rrlookup(dp, Tsoa, NOneg);
173 	rv = reply(rfd, repp, req, caller);
174 out:
175 	if (repp->an)
176 		rrfreelist(repp->an);
177 	rrfree(repp->qd);
178 	return rv;
179 }
180 
181 void
tcpproc(void * v)182 tcpproc(void *v)
183 {
184 	int len, rv;
185 	Request req;
186 	DNSmsg reqmsg, repmsg;
187 	char *err;
188 	uchar buf[512];
189 	char tname[32];
190 	int fd, rfd;
191 	NetConnInfo *caller;
192 
193 	rfd = -1;
194 	fd = (uintptr)v;
195 	caller = 0;
196 	/* loop on requests */
197 	for(;; putactivity()){
198 		if (rfd == 1)
199 			return;
200 		close(rfd);
201 		now = time(0);
202 		memset(&repmsg, 0, sizeof(repmsg));
203 		if (fd == 0) {
204 			len = readmsg(fd, buf, sizeof buf);
205 			rfd = 1;
206 		} else {
207 			len = connreadmsg(fd, &rfd, buf, sizeof buf);
208 		}
209 		if(len <= 0)
210 			continue;
211 		freenetconninfo(caller);
212 		caller = getnetconninfo(0, fd);
213 		getactivity(&req);
214 		req.aborttime = now + 15*Min;
215 		err = convM2DNS(buf, len, &reqmsg);
216 		if(err){
217 			syslog(0, logfile, "server: input error: %s from %I", err, buf);
218 			continue;
219 		}
220 		if(reqmsg.qdcount < 1){
221 			syslog(0, logfile, "server: no questions from %I", buf);
222 			continue;
223 		}
224 		if(reqmsg.flags & Fresp){
225 			syslog(0, logfile, "server: reply not request from %I", buf);
226 			continue;
227 		}
228 		if((reqmsg.flags & Omask) != Oquery){
229 			syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
230 			continue;
231 		}
232 
233 		if(debug)
234 			syslog(0, logfile, "%d: serve (%s) %d %s %s",
235 				req.id, caller ? caller->raddr : 0,
236 				reqmsg.id,
237 				reqmsg.qd->owner->name,
238 				rrname(reqmsg.qd->type, tname, sizeof tname));
239 
240 		/* loop through each question */
241 		while(reqmsg.qd){
242 			if(reqmsg.qd->type == Taxfr){
243 				if(dnzone(&reqmsg, &repmsg, &req, rfd, caller) < 0)
244 					break;
245 			} else {
246 				dnserver(&reqmsg, &repmsg, &req);
247 				rv = reply(rfd, &repmsg, &req, caller);
248 				rrfreelist(repmsg.qd);
249 				rrfreelist(repmsg.an);
250 				rrfreelist(repmsg.ns);
251 				rrfreelist(repmsg.ar);
252 				if(rv < 0)
253 					break;
254 			}
255 		}
256 
257 		rrfreelist(reqmsg.qd);
258 		rrfreelist(reqmsg.an);
259 		rrfreelist(reqmsg.ns);
260 		rrfreelist(reqmsg.ar);
261 	}
262 }
263 
264 enum {
265 	Maxactivetcp = 4
266 };
267 
268 static int
tcpannounce(char * mntpt)269 tcpannounce(char *mntpt)
270 {
271 	int fd;
272 
273 	USED(mntpt);
274 	if((fd=announce(tcpaddr, adir)) < 0)
275 		warning("announce %s: %r", tcpaddr);
276 	return fd;
277 }
278 
279 void
dntcpserver(void * v)280 dntcpserver(void *v)
281 {
282 	int i, fd;
283 
284 	while((fd = tcpannounce(v)) < 0)
285 		sleep(5*1000);
286 
287 	for(i=0; i<Maxactivetcp; i++)
288 		proccreate(tcpproc, (void*)(uintptr)fd, STACK);
289 }
290