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