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