xref: /plan9-contrib/sys/src/cmd/ndb/dnudpserver.c (revision fececb924262ae5acb31c5c448a4a6a523887b15)
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 static void
14 ding(void *x, char *msg)
15 {
16 	USED(x);
17 	if(strcmp(msg, "alarm") == 0)
18 		noted(NCONT);
19 	else
20 		noted(NDFLT);
21 }
22 
23 typedef struct Inprogress Inprogress;
24 struct Inprogress
25 {
26 	int	inuse;
27 	Udphdr	uh;
28 	DN	*owner;
29 	int	type;
30 	int	id;
31 };
32 Inprogress inprog[Maxactive+2];
33 
34 /*
35  *  record client id and ignore retransmissions.
36  *  we're still single thread at this point.
37  */
38 static Inprogress*
39 clientrxmit(DNSmsg *req, uchar *buf)
40 {
41 	Inprogress *p, *empty;
42 	Udphdr *uh;
43 
44 	uh = (Udphdr *)buf;
45 	empty = nil;
46 	for(p = inprog; p < &inprog[Maxactive]; p++){
47 		if(p->inuse == 0){
48 			if(empty == nil)
49 				empty = p;
50 			continue;
51 		}
52 		if(req->id == p->id)
53 		if(req->qd->owner == p->owner)
54 		if(req->qd->type == p->type)
55 		if(memcmp(uh, &p->uh, Udphdrsize) == 0)
56 			return nil;
57 	}
58 	if(empty == nil)
59 		return nil; /* shouldn't happen: see slave() & Maxactive def'n */
60 
61 	empty->id = req->id;
62 	empty->owner = req->qd->owner;
63 	empty->type = req->qd->type;
64 	memmove(&empty->uh, uh, Udphdrsize);
65 	empty->inuse = 1;
66 	return empty;
67 }
68 
69 /*
70  *  a process to act as a dns server for outside reqeusts
71  */
72 void
73 dnudpserver(char *mntpt)
74 {
75 	int fd, len, op, rcode;
76 	uchar buf[Udphdrsize + Maxudp + 1024];
77 	char *err;
78 	char tname[32];
79 	Request req;
80 	DNSmsg reqmsg, repmsg;
81 	Inprogress *p;
82 	Udphdr *uh;
83 
84 	/* fork sharing text, data, and bss with parent */
85 	switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
86 	case -1:
87 		break;
88 	case 0:
89 		break;
90 	default:
91 		return;
92 	}
93 
94 	fd = -1;
95 	notify(ding);
96 restart:
97 	procsetname("udp server announcing");
98 	if(fd >= 0)
99 		close(fd);
100 	while((fd = udpannounce(mntpt)) < 0)
101 		sleep(5000);
102 
103 	procsetname("udp server loop");
104 	memset(&req, 0, sizeof req);
105 	if(setjmp(req.mret))
106 		putactivity(0);
107 	req.isslave = 0;
108 	req.id = 0;
109 	req.aborttime = 0;
110 
111 	/* loop on requests */
112 	for(;; putactivity(0)){
113 		memset(&repmsg, 0, sizeof repmsg);
114 		memset(&reqmsg, 0, sizeof reqmsg);
115 
116 		alarm(60*1000);
117 		len = read(fd, buf, sizeof buf);
118 		alarm(0);
119 		if(len <= Udphdrsize)
120 			goto restart;
121 		uh = (Udphdr*)buf;
122 		len -= Udphdrsize;
123 
124 		// dnslog("read received UDP from %I to %I",
125 		//	((Udphdr*)buf)->raddr, ((Udphdr*)buf)->laddr);
126 		getactivity(&req, 0);
127 		req.aborttime = now + Maxreqtm;
128 //		req.from = smprint("%I", ((Udphdr*)buf)->raddr);
129 		req.from = smprint("%I", buf);
130 		rcode = 0;
131 		stats.qrecvdudp++;
132 
133 		err = convM2DNS(&buf[Udphdrsize], len, &reqmsg, &rcode);
134 		if(err){
135 			/* first bytes in buf are source IP addr */
136 			dnslog("server: input error: %s from %I", err, buf);
137 			free(err);
138 			goto freereq;
139 		}
140 		if (rcode == 0)
141 			if(reqmsg.qdcount < 1){
142 				dnslog("server: no questions from %I", buf);
143 				goto freereq;
144 			} else if(reqmsg.flags & Fresp){
145 				dnslog("server: reply not request from %I", buf);
146 				goto freereq;
147 			}
148 		op = reqmsg.flags & Omask;
149 		if(op != Oquery && op != Onotify){
150 			dnslog("server: op %d from %I", reqmsg.flags & Omask,
151 				buf);
152 			goto freereq;
153 		}
154 
155 		if(debug || (trace && subsume(trace, reqmsg.qd->owner->name)))
156 			dnslog("%d: serve (%I/%d) %d %s %s",
157 				req.id, buf, uh->rport[0]<<8 | uh->rport[1],
158 				reqmsg.id, reqmsg.qd->owner->name,
159 				rrname(reqmsg.qd->type, tname, sizeof tname));
160 
161 		p = clientrxmit(&reqmsg, buf);
162 		if(p == nil){
163 			if(debug)
164 				dnslog("%d: duplicate", req.id);
165 			goto freereq;
166 		}
167 
168 		if (Logqueries) {
169 			RR *rr;
170 
171 			for (rr = reqmsg.qd; rr; rr = rr->next)
172 				syslog(0, "dnsq", "id %d: (%I/%d) %d %s %s",
173 					req.id, buf, uh->rport[0]<<8 |
174 					uh->rport[1], reqmsg.id,
175 					reqmsg.qd->owner->name,
176 					rrname(reqmsg.qd->type, tname,
177 					sizeof tname));	// DEBUG
178 		}
179 		/* loop through each question */
180 		while(reqmsg.qd){
181 			memset(&repmsg, 0, sizeof repmsg);
182 			switch(op){
183 			case Oquery:
184 				dnserver(&reqmsg, &repmsg, &req, buf, rcode);
185 				break;
186 			case Onotify:
187 				dnnotify(&reqmsg, &repmsg, &req);
188 				break;
189 			}
190 			/* send reply on fd to address in buf's udp hdr */
191 			reply(fd, buf, &repmsg, &req);
192 			freeanswers(&repmsg);
193 		}
194 
195 		p->inuse = 0;
196 freereq:
197 		free(req.from);
198 		freeanswers(&reqmsg);
199 		if(req.isslave){
200 			putactivity(0);
201 			_exits(0);
202 		}
203 	}
204 }
205 
206 /*
207  *  announce on well-known dns udp port and set message style interface
208  */
209 static char *hmsg = "headers";
210 
211 static int
212 udpannounce(char *mntpt)
213 {
214 	int data, ctl;
215 	char dir[64], datafile[64+6];
216 	static int whined;
217 
218 	/* get a udp port */
219 	sprint(datafile, "%s/udp!*!dns", mntpt);
220 	ctl = announce(datafile, dir);
221 	if(ctl < 0){
222 		if(!whined++)
223 			warning("can't announce on dns udp port");
224 		return -1;
225 	}
226 	snprint(datafile, sizeof(datafile), "%s/data", dir);
227 
228 	/* turn on header style interface */
229 	if(write(ctl, hmsg, strlen(hmsg)) , 0)
230 		abort();			/* hmsg */
231 	data = open(datafile, ORDWR);
232 	if(data < 0){
233 		close(ctl);
234 		if(!whined++)
235 			warning("can't announce on dns udp port");
236 		return -1;
237 	}
238 
239 	close(ctl);
240 	return data;
241 }
242 
243 static void
244 reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
245 {
246 	int len;
247 	char tname[32];
248 
249 	if(debug || (trace && subsume(trace, rep->qd->owner->name)))
250 		dnslog("%d: reply (%I/%d) %d %s %s qd %R an %R ns %R ar %R",
251 			reqp->id, buf, buf[4]<<8 | buf[5],
252 			rep->id, rep->qd->owner->name,
253 			rrname(rep->qd->type, tname, sizeof tname),
254 			rep->qd, rep->an, rep->ns, rep->ar);
255 
256 	len = convDNS2M(rep, &buf[Udphdrsize], Maxudp);
257 	len += Udphdrsize;
258 	if(write(fd, buf, len) != len)
259 		dnslog("error sending reply: %r");
260 }
261