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