1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include <bio.h>
5 #include <ndb.h>
6 #include <thread.h>
7 #include "dns.h"
8 
9 static int	udpannounce(char*);
10 static void	reply(int, uchar*, DNSmsg*, Request*);
11 
12 extern char *logfile;
13 
14 typedef struct Inprogress Inprogress;
15 struct Inprogress
16 {
17 	int	inuse;
18 	Udphdr	uh;
19 	DN	*owner;
20 	int	type;
21 	int	id;
22 };
23 Inprogress inprog[Maxactive+2];
24 QLock inproglk;
25 
26 /*
27  *  record client id and ignore retransmissions.
28  */
29 static Inprogress*
clientrxmit(DNSmsg * req,uchar * buf)30 clientrxmit(DNSmsg *req, uchar *buf)
31 {
32 	Inprogress *p, *empty;
33 	Udphdr *uh;
34 
35 	qlock(&inproglk);
36 	uh = (Udphdr *)buf;
37 	empty = 0;
38 	for(p = inprog; p < &inprog[Maxactive]; p++){
39 		if(p->inuse == 0){
40 			if(empty == 0)
41 				empty = p;
42 			continue;
43 		}
44 		if(req->id == p->id)
45 		if(req->qd->owner == p->owner)
46 		if(req->qd->type == p->type)
47 		if(memcmp(uh, &p->uh, Udphdrsize) == 0){
48 			qunlock(&inproglk);
49 			return 0;
50 		}
51 	}
52 	if(empty == 0){
53 		qunlock(&inproglk);
54 		return 0;	/* shouldn't happen - see slave() and definition of Maxactive */
55 	}
56 
57 	empty->id = req->id;
58 	empty->owner = req->qd->owner;
59 	empty->type = req->qd->type;
60 	memmove(&empty->uh, uh, Udphdrsize);
61 	empty->inuse = 1;
62 	qunlock(&inproglk);
63 	return empty;
64 }
65 
66 /*
67  *  a process to act as a dns server for outside reqeusts
68  */
69 static void
udpproc(void * v)70 udpproc(void *v)
71 {
72 	int fd, len, op;
73 	Request req;
74 	DNSmsg reqmsg, repmsg;
75 	uchar buf[Udphdrsize + Maxudp + 1024];
76 	char *err;
77 	Inprogress *p;
78 	char tname[32];
79 	Udphdr *uh;
80 
81 	fd = (uintptr)v;
82 
83 	/* loop on requests */
84 	for(;; putactivity()){
85 		memset(&repmsg, 0, sizeof(repmsg));
86 		memset(&reqmsg, 0, sizeof(reqmsg));
87 		len = udpread(fd, (Udphdr*)buf, buf+Udphdrsize, sizeof(buf)-Udphdrsize);
88 		if(len <= 0)
89 			continue;
90 		uh = (Udphdr*)buf;
91 		getactivity(&req);
92 		req.aborttime = now + 30;	/* don't spend more than 30 seconds */
93 		err = convM2DNS(&buf[Udphdrsize], len, &reqmsg);
94 		if(err){
95 			syslog(0, logfile, "server: input error: %s from %I", err, buf);
96 			continue;
97 		}
98 		if(reqmsg.qdcount < 1){
99 			syslog(0, logfile, "server: no questions from %I", buf);
100 			goto freereq;
101 		}
102 		if(reqmsg.flags & Fresp){
103 			syslog(0, logfile, "server: reply not request from %I", buf);
104 			goto freereq;
105 		}
106 		op = reqmsg.flags & Omask;
107 		if(op != Oquery && op != Onotify){
108 			syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
109 			goto freereq;
110 		}
111 
112 		if(debug || (trace && subsume(trace, reqmsg.qd->owner->name))){
113 			syslog(0, logfile, "%d: serve (%I/%d) %d %s %s",
114 				req.id, buf, ((uh->rport[0])<<8)+uh->rport[1],
115 				reqmsg.id,
116 				reqmsg.qd->owner->name,
117 				rrname(reqmsg.qd->type, tname, sizeof tname));
118 		}
119 
120 		p = clientrxmit(&reqmsg, buf);
121 		if(p == 0){
122 			if(debug)
123 				syslog(0, logfile, "%d: duplicate", req.id);
124 			goto freereq;
125 		}
126 
127 		/* loop through each question */
128 		while(reqmsg.qd){
129 			memset(&repmsg, 0, sizeof(repmsg));
130 			switch(op){
131 			case Oquery:
132 				dnserver(&reqmsg, &repmsg, &req);
133 				break;
134 			case Onotify:
135 				dnnotify(&reqmsg, &repmsg, &req);
136 				break;
137 			}
138 			reply(fd, buf, &repmsg, &req);
139 			rrfreelist(repmsg.qd);
140 			rrfreelist(repmsg.an);
141 			rrfreelist(repmsg.ns);
142 			rrfreelist(repmsg.ar);
143 		}
144 
145 		p->inuse = 0;
146 
147 freereq:
148 		rrfreelist(reqmsg.qd);
149 		rrfreelist(reqmsg.an);
150 		rrfreelist(reqmsg.ns);
151 		rrfreelist(reqmsg.ar);
152 	}
153 }
154 
155 /*
156  *  announce on udp port
157  */
158 static int
udpannounce(char * mntpt)159 udpannounce(char *mntpt)
160 {
161 	int fd;
162 	char buf[40];
163 	USED(mntpt);
164 
165 	if((fd=announce(udpaddr, buf)) < 0)
166 		warning("announce %s: %r", buf);
167 	return fd;
168 }
169 
170 static void
reply(int fd,uchar * buf,DNSmsg * rep,Request * reqp)171 reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
172 {
173 	int len;
174 	char tname[32];
175 	RR *rp;
176 
177 	if(debug || (trace && subsume(trace, rep->qd->owner->name)))
178 		syslog(0, logfile, "%d: reply (%I/%d) %d %s %s an %R ns %R ar %R",
179 			reqp->id, buf, ((buf[4])<<8)+buf[5],
180 			rep->id, rep->qd->owner->name,
181 			rrname(rep->qd->type, tname, sizeof tname), rep->an, rep->ns, rep->ar);
182 
183 	len = convDNS2M(rep, &buf[Udphdrsize], Maxudp);
184 	if(len <= 0){
185 		syslog(0, logfile, "error converting reply: %s %d", rep->qd->owner->name,
186 			rep->qd->type);
187 		for(rp = rep->an; rp; rp = rp->next)
188 			syslog(0, logfile, "an %R", rp);
189 		for(rp = rep->ns; rp; rp = rp->next)
190 			syslog(0, logfile, "ns %R", rp);
191 		for(rp = rep->ar; rp; rp = rp->next)
192 			syslog(0, logfile, "ar %R", rp);
193 		return;
194 	}
195 	if(udpwrite(fd, (Udphdr*)buf, buf+Udphdrsize, len) != len)
196 		syslog(0, logfile, "error sending reply: %r");
197 }
198 
199 void
dnudpserver(void * v)200 dnudpserver(void *v)
201 {
202 	int i, fd;
203 
204 	while((fd = udpannounce(v)) < 0)
205 		sleep(5*1000);
206 	for(i=0; i<Maxactive; i++)
207 		proccreate(udpproc, (void*)(uintptr)fd, STACK);
208 }
209