xref: /plan9/sys/src/cmd/ndb/dnudpserver.c (revision 98813beef1db23409911a4b339e6bb9c03d0a5c0)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dns.h"
5 
6 enum {
7 	Logqueries = 0,
8 };
9 
10 static int	udpannounce(char*);
11 static void	reply(int, uchar*, DNSmsg*, Request*);
12 
13 typedef struct Inprogress Inprogress;
14 struct Inprogress
15 {
16 	int	inuse;
17 	Udphdr	uh;
18 	DN	*owner;
19 	ushort	type;
20 	int	id;
21 };
22 Inprogress inprog[Maxactive+2];
23 
24 typedef struct Forwtarg Forwtarg;
25 struct Forwtarg {
26 	char	*host;
27 	uchar	addr[IPaddrlen];
28 	int	fd;
29 	ulong	lastdial;
30 };
31 Forwtarg forwtarg[10];
32 int currtarg;
33 
34 static char *hmsg = "headers";
35 
36 /*
37  *  record client id and ignore retransmissions.
38  *  we're still single thread at this point.
39  */
40 static Inprogress*
clientrxmit(DNSmsg * req,uchar * buf)41 clientrxmit(DNSmsg *req, uchar *buf)
42 {
43 	Inprogress *p, *empty;
44 	Udphdr *uh;
45 
46 	uh = (Udphdr *)buf;
47 	empty = nil;
48 	for(p = inprog; p < &inprog[Maxactive]; p++){
49 		if(p->inuse == 0){
50 			if(empty == nil)
51 				empty = p;
52 			continue;
53 		}
54 		if(req->id == p->id && req->qd != nil &&
55 		    req->qd->owner == p->owner && req->qd->type == p->type &&
56 		    memcmp(uh, &p->uh, Udphdrsize) == 0)
57 			return nil;
58 	}
59 	if(empty == nil)
60 		return nil; /* shouldn't happen: see slave() & Maxactive def'n */
61 	if(req->qd == nil) {
62 		dnslog("clientrxmit: nil req->qd");
63 		return nil;
64 	}
65 	empty->id = req->id;
66 	empty->owner = req->qd->owner;
67 	empty->type = req->qd->type;
68 	if (empty->type != req->qd->type)
69 		dnslog("clientrxmit: bogus req->qd->type %d", req->qd->type);
70 	memmove(&empty->uh, uh, Udphdrsize);
71 	empty->inuse = 1;
72 	return empty;
73 }
74 
75 int
addforwtarg(char * host)76 addforwtarg(char *host)
77 {
78 	Forwtarg *tp;
79 
80 	if (currtarg >= nelem(forwtarg)) {
81 		dnslog("too many forwarding targets");
82 		return -1;
83 	}
84 	tp = forwtarg + currtarg;
85 	if (parseip(tp->addr, host) < 0) {
86 		dnslog("can't parse ip %s", host);
87 		return -1;
88 	}
89 	tp->lastdial = time(nil);
90 	tp->fd = udpport(mntpt);
91 	if (tp->fd < 0)
92 		return -1;
93 
94 	free(tp->host);
95 	tp->host = estrdup(host);
96 	currtarg++;
97 	return 0;
98 }
99 
100 /*
101  * fast forwarding of incoming queries to other dns servers.
102  * intended primarily for debugging.
103  */
104 static void
redistrib(uchar * buf,int len)105 redistrib(uchar *buf, int len)
106 {
107 	Forwtarg *tp;
108 	Udphdr *uh;
109 	static uchar outpkt[1500];
110 
111 	assert(len <= sizeof outpkt);
112 	memmove(outpkt, buf, len);
113 	uh = (Udphdr *)outpkt;
114 	for (tp = forwtarg; tp < forwtarg + currtarg; tp++)
115 		if (tp->fd > 0) {
116 			memmove(outpkt, tp->addr, sizeof tp->addr);
117 			hnputs(uh->rport, Dnsport);
118 			if (write(tp->fd, outpkt, len) != len) {
119 				close(tp->fd);
120 				tp->fd = -1;
121 			}
122 		} else if (tp->host && time(nil) - tp->lastdial > 60) {
123 			tp->lastdial = time(nil);
124 			tp->fd = udpport(mntpt);
125 		}
126 }
127 
128 /*
129  *  a process to act as a dns server for outside reqeusts
130  */
131 void
dnudpserver(char * mntpt)132 dnudpserver(char *mntpt)
133 {
134 	volatile int fd, len, op, rcode;
135 	char *volatile err;
136 	volatile char tname[32];
137 	volatile uchar buf[Udphdrsize + Maxpayload];
138 	volatile DNSmsg reqmsg, repmsg;
139 	Inprogress *volatile p;
140 	volatile Request req;
141 	Udphdr *volatile uh;
142 
143 	/*
144 	 * fork sharing text, data, and bss with parent.
145 	 * stay in the same note group.
146 	 */
147 	switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
148 	case -1:
149 		break;
150 	case 0:
151 		break;
152 	default:
153 		return;
154 	}
155 
156 	fd = -1;
157 restart:
158 	procsetname("udp server announcing");
159 	if(fd >= 0)
160 		close(fd);
161 	while((fd = udpannounce(mntpt)) < 0)
162 		sleep(5000);
163 
164 //	procsetname("udp server");
165 	memset(&req, 0, sizeof req);
166 	if(setjmp(req.mret))
167 		putactivity(0);
168 	req.isslave = 0;
169 	req.id = 0;
170 	req.aborttime = 0;
171 
172 	/* loop on requests */
173 	for(;; putactivity(0)){
174 		procsetname("served %d udp; %d alarms",
175 			stats.qrecvdudp, stats.alarms);
176 		memset(&repmsg, 0, sizeof repmsg);
177 		memset(&reqmsg, 0, sizeof reqmsg);
178 
179 		alarm(60*1000);
180 		len = read(fd, buf, sizeof buf);
181 		alarm(0);
182 		if(len <= Udphdrsize)
183 			goto restart;
184 
185 		redistrib(buf, len);
186 
187 		uh = (Udphdr*)buf;
188 		len -= Udphdrsize;
189 
190 		// dnslog("read received UDP from %I to %I",
191 		//	((Udphdr*)buf)->raddr, ((Udphdr*)buf)->laddr);
192 		getactivity(&req, 0);
193 		req.aborttime = timems() + Maxreqtm;
194 //		req.from = smprint("%I", ((Udphdr*)buf)->raddr);
195 		req.from = smprint("%I", buf);
196 		rcode = 0;
197 		stats.qrecvdudp++;
198 
199 		err = convM2DNS(&buf[Udphdrsize], len, &reqmsg, &rcode);
200 		if(err){
201 			/* first bytes in buf are source IP addr */
202 			dnslog("server: input error: %s from %I", err, buf);
203 			free(err);
204 			goto freereq;
205 		}
206 		if (rcode == 0)
207 			if(reqmsg.qdcount < 1){
208 				dnslog("server: no questions from %I", buf);
209 				goto freereq;
210 			} else if(reqmsg.flags & Fresp){
211 				dnslog("server: reply not request from %I", buf);
212 				goto freereq;
213 			}
214 		op = reqmsg.flags & Omask;
215 		if(op != Oquery && op != Onotify){
216 			dnslog("server: op %d from %I", reqmsg.flags & Omask,
217 				buf);
218 			goto freereq;
219 		}
220 
221 		if(debug || (trace && subsume(trace, reqmsg.qd->owner->name)))
222 			dnslog("%d: serve (%I/%d) %d %s %s",
223 				req.id, buf, uh->rport[0]<<8 | uh->rport[1],
224 				reqmsg.id, reqmsg.qd->owner->name,
225 				rrname(reqmsg.qd->type, tname, sizeof tname));
226 
227 		p = clientrxmit(&reqmsg, buf);
228 		if(p == nil){
229 			if(debug)
230 				dnslog("%d: duplicate", req.id);
231 			goto freereq;
232 		}
233 
234 		if (Logqueries) {
235 			RR *rr;
236 
237 			for (rr = reqmsg.qd; rr; rr = rr->next)
238 				syslog(0, "dnsq", "id %d: (%I/%d) %d %s %s",
239 					req.id, buf, uh->rport[0]<<8 |
240 					uh->rport[1], reqmsg.id,
241 					reqmsg.qd->owner->name,
242 					rrname(reqmsg.qd->type, tname,
243 					sizeof tname));
244 		}
245 		/* loop through each question */
246 		while(reqmsg.qd){
247 			memset(&repmsg, 0, sizeof repmsg);
248 			switch(op){
249 			case Oquery:
250 				dnserver(&reqmsg, &repmsg, &req, buf, rcode);
251 				break;
252 			case Onotify:
253 				dnnotify(&reqmsg, &repmsg, &req);
254 				break;
255 			}
256 			/* send reply on fd to address in buf's udp hdr */
257 			reply(fd, buf, &repmsg, &req);
258 			freeanswers(&repmsg);
259 		}
260 
261 		p->inuse = 0;
262 freereq:
263 		free(req.from);
264 		req.from = nil;
265 		freeanswers(&reqmsg);
266 		if(req.isslave){
267 			putactivity(0);
268 			_exits(0);
269 		}
270 	}
271 }
272 
273 /*
274  *  announce on well-known dns udp port and set message style interface
275  */
276 static int
udpannounce(char * mntpt)277 udpannounce(char *mntpt)
278 {
279 	int data, ctl;
280 	char dir[64], datafile[64+6];
281 	static int whined;
282 
283 	/* get a udp port */
284 	sprint(datafile, "%s/udp!*!dns", mntpt);
285 	ctl = announce(datafile, dir);
286 	if(ctl < 0){
287 		if(!whined++)
288 			warning("can't announce on %s", datafile);
289 		return -1;
290 	}
291 
292 	/* turn on header style interface */
293 	if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg))
294 		abort();			/* hmsg */
295 
296 	snprint(datafile, sizeof(datafile), "%s/data", dir);
297 	data = open(datafile, ORDWR);
298 	if(data < 0){
299 		close(ctl);
300 		if(!whined++)
301 			warning("can't open %s to announce on dns udp port",
302 				datafile);
303 		return -1;
304 	}
305 
306 	close(ctl);
307 	return data;
308 }
309 
310 static void
reply(int fd,uchar * buf,DNSmsg * rep,Request * reqp)311 reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
312 {
313 	int len;
314 	char tname[32];
315 
316 	if(debug || (trace && subsume(trace, rep->qd->owner->name)))
317 		dnslog("%d: reply (%I/%d) %d %s %s qd %R an %R ns %R ar %R",
318 			reqp->id, buf, buf[4]<<8 | buf[5],
319 			rep->id, rep->qd->owner->name,
320 			rrname(rep->qd->type, tname, sizeof tname),
321 			rep->qd, rep->an, rep->ns, rep->ar);
322 
323 	len = convDNS2M(rep, &buf[Udphdrsize], Maxdnspayload);
324 	len += Udphdrsize;
325 	if(write(fd, buf, len) != len)
326 		dnslog("error sending reply: %r");
327 }
328