xref: /plan9/sys/src/cmd/ndb/dnudpserver.c (revision 98813beef1db23409911a4b339e6bb9c03d0a5c0)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <ip.h>
47dd7cddfSDavid du Colombier #include "dns.h"
57dd7cddfSDavid du Colombier 
6018fc28bSDavid du Colombier enum {
7018fc28bSDavid du Colombier 	Logqueries = 0,
8018fc28bSDavid du Colombier };
9018fc28bSDavid du Colombier 
107dd7cddfSDavid du Colombier static int	udpannounce(char*);
117dd7cddfSDavid du Colombier static void	reply(int, uchar*, DNSmsg*, Request*);
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier typedef struct Inprogress Inprogress;
147dd7cddfSDavid du Colombier struct Inprogress
157dd7cddfSDavid du Colombier {
167dd7cddfSDavid du Colombier 	int	inuse;
17f27a9a5aSDavid du Colombier 	Udphdr	uh;
187dd7cddfSDavid du Colombier 	DN	*owner;
19adb31a62SDavid du Colombier 	ushort	type;
207dd7cddfSDavid du Colombier 	int	id;
217dd7cddfSDavid du Colombier };
227dd7cddfSDavid du Colombier Inprogress inprog[Maxactive+2];
237dd7cddfSDavid du Colombier 
24410ea80bSDavid du Colombier typedef struct Forwtarg Forwtarg;
25410ea80bSDavid du Colombier struct Forwtarg {
26410ea80bSDavid du Colombier 	char	*host;
27410ea80bSDavid du Colombier 	uchar	addr[IPaddrlen];
28410ea80bSDavid du Colombier 	int	fd;
29410ea80bSDavid du Colombier 	ulong	lastdial;
30410ea80bSDavid du Colombier };
31410ea80bSDavid du Colombier Forwtarg forwtarg[10];
32410ea80bSDavid du Colombier int currtarg;
33410ea80bSDavid du Colombier 
34410ea80bSDavid du Colombier static char *hmsg = "headers";
35410ea80bSDavid du Colombier 
367dd7cddfSDavid du Colombier /*
377dd7cddfSDavid du Colombier  *  record client id and ignore retransmissions.
387dd7cddfSDavid du Colombier  *  we're still single thread at this point.
397dd7cddfSDavid du Colombier  */
407dd7cddfSDavid du Colombier static Inprogress*
clientrxmit(DNSmsg * req,uchar * buf)417dd7cddfSDavid du Colombier clientrxmit(DNSmsg *req, uchar *buf)
427dd7cddfSDavid du Colombier {
437dd7cddfSDavid du Colombier 	Inprogress *p, *empty;
44f27a9a5aSDavid du Colombier 	Udphdr *uh;
457dd7cddfSDavid du Colombier 
46f27a9a5aSDavid du Colombier 	uh = (Udphdr *)buf;
474f8f669cSDavid du Colombier 	empty = nil;
487dd7cddfSDavid du Colombier 	for(p = inprog; p < &inprog[Maxactive]; p++){
497dd7cddfSDavid du Colombier 		if(p->inuse == 0){
504f8f669cSDavid du Colombier 			if(empty == nil)
517dd7cddfSDavid du Colombier 				empty = p;
527dd7cddfSDavid du Colombier 			continue;
537dd7cddfSDavid du Colombier 		}
540bbfaeb2SDavid du Colombier 		if(req->id == p->id && req->qd != nil &&
550bbfaeb2SDavid du Colombier 		    req->qd->owner == p->owner && req->qd->type == p->type &&
560bbfaeb2SDavid du Colombier 		    memcmp(uh, &p->uh, Udphdrsize) == 0)
574f8f669cSDavid du Colombier 			return nil;
587dd7cddfSDavid du Colombier 	}
594f8f669cSDavid du Colombier 	if(empty == nil)
604f8f669cSDavid du Colombier 		return nil; /* shouldn't happen: see slave() & Maxactive def'n */
610bbfaeb2SDavid du Colombier 	if(req->qd == nil) {
620bbfaeb2SDavid du Colombier 		dnslog("clientrxmit: nil req->qd");
630bbfaeb2SDavid du Colombier 		return nil;
640bbfaeb2SDavid du Colombier 	}
657dd7cddfSDavid du Colombier 	empty->id = req->id;
667dd7cddfSDavid du Colombier 	empty->owner = req->qd->owner;
677dd7cddfSDavid du Colombier 	empty->type = req->qd->type;
68adb31a62SDavid du Colombier 	if (empty->type != req->qd->type)
69adb31a62SDavid du Colombier 		dnslog("clientrxmit: bogus req->qd->type %d", req->qd->type);
70f27a9a5aSDavid du Colombier 	memmove(&empty->uh, uh, Udphdrsize);
717dd7cddfSDavid du Colombier 	empty->inuse = 1;
727dd7cddfSDavid du Colombier 	return empty;
737dd7cddfSDavid du Colombier }
747dd7cddfSDavid du Colombier 
75410ea80bSDavid du Colombier int
addforwtarg(char * host)76410ea80bSDavid du Colombier addforwtarg(char *host)
77410ea80bSDavid du Colombier {
78410ea80bSDavid du Colombier 	Forwtarg *tp;
79410ea80bSDavid du Colombier 
80410ea80bSDavid du Colombier 	if (currtarg >= nelem(forwtarg)) {
81410ea80bSDavid du Colombier 		dnslog("too many forwarding targets");
82410ea80bSDavid du Colombier 		return -1;
83410ea80bSDavid du Colombier 	}
84410ea80bSDavid du Colombier 	tp = forwtarg + currtarg;
85410ea80bSDavid du Colombier 	if (parseip(tp->addr, host) < 0) {
86410ea80bSDavid du Colombier 		dnslog("can't parse ip %s", host);
87410ea80bSDavid du Colombier 		return -1;
88410ea80bSDavid du Colombier 	}
89410ea80bSDavid du Colombier 	tp->lastdial = time(nil);
90410ea80bSDavid du Colombier 	tp->fd = udpport(mntpt);
91410ea80bSDavid du Colombier 	if (tp->fd < 0)
92410ea80bSDavid du Colombier 		return -1;
93410ea80bSDavid du Colombier 
94410ea80bSDavid du Colombier 	free(tp->host);
95410ea80bSDavid du Colombier 	tp->host = estrdup(host);
96410ea80bSDavid du Colombier 	currtarg++;
97410ea80bSDavid du Colombier 	return 0;
98410ea80bSDavid du Colombier }
99410ea80bSDavid du Colombier 
100410ea80bSDavid du Colombier /*
101410ea80bSDavid du Colombier  * fast forwarding of incoming queries to other dns servers.
102410ea80bSDavid du Colombier  * intended primarily for debugging.
103410ea80bSDavid du Colombier  */
104410ea80bSDavid du Colombier static void
redistrib(uchar * buf,int len)105410ea80bSDavid du Colombier redistrib(uchar *buf, int len)
106410ea80bSDavid du Colombier {
107410ea80bSDavid du Colombier 	Forwtarg *tp;
108410ea80bSDavid du Colombier 	Udphdr *uh;
109410ea80bSDavid du Colombier 	static uchar outpkt[1500];
110410ea80bSDavid du Colombier 
111410ea80bSDavid du Colombier 	assert(len <= sizeof outpkt);
112410ea80bSDavid du Colombier 	memmove(outpkt, buf, len);
113410ea80bSDavid du Colombier 	uh = (Udphdr *)outpkt;
114410ea80bSDavid du Colombier 	for (tp = forwtarg; tp < forwtarg + currtarg; tp++)
115410ea80bSDavid du Colombier 		if (tp->fd > 0) {
116410ea80bSDavid du Colombier 			memmove(outpkt, tp->addr, sizeof tp->addr);
117*98813beeSDavid du Colombier 			hnputs(uh->rport, Dnsport);
118410ea80bSDavid du Colombier 			if (write(tp->fd, outpkt, len) != len) {
119410ea80bSDavid du Colombier 				close(tp->fd);
120410ea80bSDavid du Colombier 				tp->fd = -1;
121410ea80bSDavid du Colombier 			}
122410ea80bSDavid du Colombier 		} else if (tp->host && time(nil) - tp->lastdial > 60) {
123410ea80bSDavid du Colombier 			tp->lastdial = time(nil);
124410ea80bSDavid du Colombier 			tp->fd = udpport(mntpt);
125410ea80bSDavid du Colombier 		}
126410ea80bSDavid du Colombier }
127410ea80bSDavid du Colombier 
1287dd7cddfSDavid du Colombier /*
1297dd7cddfSDavid du Colombier  *  a process to act as a dns server for outside reqeusts
1307dd7cddfSDavid du Colombier  */
1317dd7cddfSDavid du Colombier void
dnudpserver(char * mntpt)1327dd7cddfSDavid du Colombier dnudpserver(char *mntpt)
1337dd7cddfSDavid du Colombier {
134225077b0SDavid du Colombier 	volatile int fd, len, op, rcode;
135225077b0SDavid du Colombier 	char *volatile err;
136225077b0SDavid du Colombier 	volatile char tname[32];
137*98813beeSDavid du Colombier 	volatile uchar buf[Udphdrsize + Maxpayload];
138225077b0SDavid du Colombier 	volatile DNSmsg reqmsg, repmsg;
139225077b0SDavid du Colombier 	Inprogress *volatile p;
140225077b0SDavid du Colombier 	volatile Request req;
141225077b0SDavid du Colombier 	Udphdr *volatile uh;
1427dd7cddfSDavid du Colombier 
143c73252aeSDavid du Colombier 	/*
144c73252aeSDavid du Colombier 	 * fork sharing text, data, and bss with parent.
145c73252aeSDavid du Colombier 	 * stay in the same note group.
146c73252aeSDavid du Colombier 	 */
147c73252aeSDavid du Colombier 	switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
1487dd7cddfSDavid du Colombier 	case -1:
1497dd7cddfSDavid du Colombier 		break;
1507dd7cddfSDavid du Colombier 	case 0:
1517dd7cddfSDavid du Colombier 		break;
1527dd7cddfSDavid du Colombier 	default:
1537dd7cddfSDavid du Colombier 		return;
1547dd7cddfSDavid du Colombier 	}
1557dd7cddfSDavid du Colombier 
1567dd7cddfSDavid du Colombier 	fd = -1;
1577dd7cddfSDavid du Colombier restart:
1584f8f669cSDavid du Colombier 	procsetname("udp server announcing");
1597dd7cddfSDavid du Colombier 	if(fd >= 0)
1607dd7cddfSDavid du Colombier 		close(fd);
1617dd7cddfSDavid du Colombier 	while((fd = udpannounce(mntpt)) < 0)
1627dd7cddfSDavid du Colombier 		sleep(5000);
1634f8f669cSDavid du Colombier 
1646aaebd7dSDavid du Colombier //	procsetname("udp server");
1654f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
1667dd7cddfSDavid du Colombier 	if(setjmp(req.mret))
167b4b9fc2fSDavid du Colombier 		putactivity(0);
1687dd7cddfSDavid du Colombier 	req.isslave = 0;
1694f8f669cSDavid du Colombier 	req.id = 0;
1704f8f669cSDavid du Colombier 	req.aborttime = 0;
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier 	/* loop on requests */
173b4b9fc2fSDavid du Colombier 	for(;; putactivity(0)){
1746aaebd7dSDavid du Colombier 		procsetname("served %d udp; %d alarms",
1756aaebd7dSDavid du Colombier 			stats.qrecvdudp, stats.alarms);
1764f8f669cSDavid du Colombier 		memset(&repmsg, 0, sizeof repmsg);
1774f8f669cSDavid du Colombier 		memset(&reqmsg, 0, sizeof reqmsg);
17876783259SDavid du Colombier 
1793ff48bf5SDavid du Colombier 		alarm(60*1000);
1804f8f669cSDavid du Colombier 		len = read(fd, buf, sizeof buf);
1817dd7cddfSDavid du Colombier 		alarm(0);
182f27a9a5aSDavid du Colombier 		if(len <= Udphdrsize)
1837dd7cddfSDavid du Colombier 			goto restart;
184410ea80bSDavid du Colombier 
185410ea80bSDavid du Colombier 		redistrib(buf, len);
186410ea80bSDavid du Colombier 
187f27a9a5aSDavid du Colombier 		uh = (Udphdr*)buf;
188f27a9a5aSDavid du Colombier 		len -= Udphdrsize;
18976783259SDavid du Colombier 
1904f8f669cSDavid du Colombier 		// dnslog("read received UDP from %I to %I",
191f27a9a5aSDavid du Colombier 		//	((Udphdr*)buf)->raddr, ((Udphdr*)buf)->laddr);
192b4b9fc2fSDavid du Colombier 		getactivity(&req, 0);
193e02f7f02SDavid du Colombier 		req.aborttime = timems() + Maxreqtm;
194f46c709fSDavid du Colombier //		req.from = smprint("%I", ((Udphdr*)buf)->raddr);
195f46c709fSDavid du Colombier 		req.from = smprint("%I", buf);
1964f8f669cSDavid du Colombier 		rcode = 0;
197a41547ffSDavid du Colombier 		stats.qrecvdudp++;
198186d659cSDavid du Colombier 
199f27a9a5aSDavid du Colombier 		err = convM2DNS(&buf[Udphdrsize], len, &reqmsg, &rcode);
2007dd7cddfSDavid du Colombier 		if(err){
2014f8f669cSDavid du Colombier 			/* first bytes in buf are source IP addr */
2024f8f669cSDavid du Colombier 			dnslog("server: input error: %s from %I", err, buf);
203186d659cSDavid du Colombier 			free(err);
204018fc28bSDavid du Colombier 			goto freereq;
2057dd7cddfSDavid du Colombier 		}
2064f8f669cSDavid du Colombier 		if (rcode == 0)
2077dd7cddfSDavid du Colombier 			if(reqmsg.qdcount < 1){
2084f8f669cSDavid du Colombier 				dnslog("server: no questions from %I", buf);
2097dd7cddfSDavid du Colombier 				goto freereq;
2104f8f669cSDavid du Colombier 			} else if(reqmsg.flags & Fresp){
2114f8f669cSDavid du Colombier 				dnslog("server: reply not request from %I", buf);
2127dd7cddfSDavid du Colombier 				goto freereq;
2137dd7cddfSDavid du Colombier 			}
214dc5a79c1SDavid du Colombier 		op = reqmsg.flags & Omask;
215dc5a79c1SDavid du Colombier 		if(op != Oquery && op != Onotify){
21676783259SDavid du Colombier 			dnslog("server: op %d from %I", reqmsg.flags & Omask,
21776783259SDavid du Colombier 				buf);
2187dd7cddfSDavid du Colombier 			goto freereq;
2197dd7cddfSDavid du Colombier 		}
2207dd7cddfSDavid du Colombier 
2213cbadd90SDavid du Colombier 		if(debug || (trace && subsume(trace, reqmsg.qd->owner->name)))
2224f8f669cSDavid du Colombier 			dnslog("%d: serve (%I/%d) %d %s %s",
2234f8f669cSDavid du Colombier 				req.id, buf, uh->rport[0]<<8 | uh->rport[1],
2243cbadd90SDavid du Colombier 				reqmsg.id, reqmsg.qd->owner->name,
2259a747e4fSDavid du Colombier 				rrname(reqmsg.qd->type, tname, sizeof tname));
2267dd7cddfSDavid du Colombier 
2277dd7cddfSDavid du Colombier 		p = clientrxmit(&reqmsg, buf);
2284f8f669cSDavid du Colombier 		if(p == nil){
2297dd7cddfSDavid du Colombier 			if(debug)
2304f8f669cSDavid du Colombier 				dnslog("%d: duplicate", req.id);
2317dd7cddfSDavid du Colombier 			goto freereq;
2327dd7cddfSDavid du Colombier 		}
2337dd7cddfSDavid du Colombier 
234018fc28bSDavid du Colombier 		if (Logqueries) {
235a41547ffSDavid du Colombier 			RR *rr;
236a41547ffSDavid du Colombier 
237a41547ffSDavid du Colombier 			for (rr = reqmsg.qd; rr; rr = rr->next)
238a41547ffSDavid du Colombier 				syslog(0, "dnsq", "id %d: (%I/%d) %d %s %s",
239a41547ffSDavid du Colombier 					req.id, buf, uh->rport[0]<<8 |
240a41547ffSDavid du Colombier 					uh->rport[1], reqmsg.id,
241a41547ffSDavid du Colombier 					reqmsg.qd->owner->name,
242a41547ffSDavid du Colombier 					rrname(reqmsg.qd->type, tname,
243d9924332SDavid du Colombier 					sizeof tname));
244a41547ffSDavid du Colombier 		}
2457dd7cddfSDavid du Colombier 		/* loop through each question */
2467dd7cddfSDavid du Colombier 		while(reqmsg.qd){
2474f8f669cSDavid du Colombier 			memset(&repmsg, 0, sizeof repmsg);
248dc5a79c1SDavid du Colombier 			switch(op){
249dc5a79c1SDavid du Colombier 			case Oquery:
2504f8f669cSDavid du Colombier 				dnserver(&reqmsg, &repmsg, &req, buf, rcode);
251dc5a79c1SDavid du Colombier 				break;
252dc5a79c1SDavid du Colombier 			case Onotify:
253dc5a79c1SDavid du Colombier 				dnnotify(&reqmsg, &repmsg, &req);
254dc5a79c1SDavid du Colombier 				break;
255dc5a79c1SDavid du Colombier 			}
2563cbadd90SDavid du Colombier 			/* send reply on fd to address in buf's udp hdr */
2577dd7cddfSDavid du Colombier 			reply(fd, buf, &repmsg, &req);
258a41547ffSDavid du Colombier 			freeanswers(&repmsg);
2597dd7cddfSDavid du Colombier 		}
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier 		p->inuse = 0;
2627dd7cddfSDavid du Colombier freereq:
263f46c709fSDavid du Colombier 		free(req.from);
264adb31a62SDavid du Colombier 		req.from = nil;
265a41547ffSDavid du Colombier 		freeanswers(&reqmsg);
2667dd7cddfSDavid du Colombier 		if(req.isslave){
267b4b9fc2fSDavid du Colombier 			putactivity(0);
2687dd7cddfSDavid du Colombier 			_exits(0);
2697dd7cddfSDavid du Colombier 		}
2707dd7cddfSDavid du Colombier 	}
2717dd7cddfSDavid du Colombier }
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier /*
274a41547ffSDavid du Colombier  *  announce on well-known dns udp port and set message style interface
2757dd7cddfSDavid du Colombier  */
2767dd7cddfSDavid du Colombier static int
udpannounce(char * mntpt)2777dd7cddfSDavid du Colombier udpannounce(char *mntpt)
2787dd7cddfSDavid du Colombier {
2796b0d5c8bSDavid du Colombier 	int data, ctl;
2804f8f669cSDavid du Colombier 	char dir[64], datafile[64+6];
2814f8f669cSDavid du Colombier 	static int whined;
2827dd7cddfSDavid du Colombier 
2837dd7cddfSDavid du Colombier 	/* get a udp port */
2847dd7cddfSDavid du Colombier 	sprint(datafile, "%s/udp!*!dns", mntpt);
2857dd7cddfSDavid du Colombier 	ctl = announce(datafile, dir);
2867dd7cddfSDavid du Colombier 	if(ctl < 0){
287b4b9fc2fSDavid du Colombier 		if(!whined++)
28882f6abeeSDavid du Colombier 			warning("can't announce on %s", datafile);
2897dd7cddfSDavid du Colombier 		return -1;
2907dd7cddfSDavid du Colombier 	}
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier 	/* turn on header style interface */
293410ea80bSDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg))
2944f8f669cSDavid du Colombier 		abort();			/* hmsg */
29582f6abeeSDavid du Colombier 
29682f6abeeSDavid du Colombier 	snprint(datafile, sizeof(datafile), "%s/data", dir);
2977dd7cddfSDavid du Colombier 	data = open(datafile, ORDWR);
2987dd7cddfSDavid du Colombier 	if(data < 0){
2997dd7cddfSDavid du Colombier 		close(ctl);
300b4b9fc2fSDavid du Colombier 		if(!whined++)
30182f6abeeSDavid du Colombier 			warning("can't open %s to announce on dns udp port",
30282f6abeeSDavid du Colombier 				datafile);
3037dd7cddfSDavid du Colombier 		return -1;
3047dd7cddfSDavid du Colombier 	}
3057dd7cddfSDavid du Colombier 
3067dd7cddfSDavid du Colombier 	close(ctl);
3077dd7cddfSDavid du Colombier 	return data;
3087dd7cddfSDavid du Colombier }
3097dd7cddfSDavid du Colombier 
3107dd7cddfSDavid du Colombier static void
reply(int fd,uchar * buf,DNSmsg * rep,Request * reqp)3117dd7cddfSDavid du Colombier reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
3127dd7cddfSDavid du Colombier {
3137dd7cddfSDavid du Colombier 	int len;
3147dd7cddfSDavid du Colombier 	char tname[32];
3157dd7cddfSDavid du Colombier 
3167dd7cddfSDavid du Colombier 	if(debug || (trace && subsume(trace, rep->qd->owner->name)))
3174f8f669cSDavid du Colombier 		dnslog("%d: reply (%I/%d) %d %s %s qd %R an %R ns %R ar %R",
3184f8f669cSDavid du Colombier 			reqp->id, buf, buf[4]<<8 | buf[5],
3197dd7cddfSDavid du Colombier 			rep->id, rep->qd->owner->name,
3204f8f669cSDavid du Colombier 			rrname(rep->qd->type, tname, sizeof tname),
3214f8f669cSDavid du Colombier 			rep->qd, rep->an, rep->ns, rep->ar);
3227dd7cddfSDavid du Colombier 
323*98813beeSDavid du Colombier 	len = convDNS2M(rep, &buf[Udphdrsize], Maxdnspayload);
324f27a9a5aSDavid du Colombier 	len += Udphdrsize;
3257dd7cddfSDavid du Colombier 	if(write(fd, buf, len) != len)
3264f8f669cSDavid du Colombier 		dnslog("error sending reply: %r");
3277dd7cddfSDavid du Colombier }
328