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 typedef struct Forwtarg Forwtarg;
25 struct Forwtarg {
26 char *host;
27 uchar addr[IPaddrlen];
28 int fd;
29 ulong lastdial;
30 };
31 Forwtarg forwtarg[10];
32 int currtarg;
33
34 static char *hmsg = "headers";
35
36 /*
37 * record client id and ignore retransmissions.
38 * we're still single thread at this point.
39 */
40 static Inprogress*
clientrxmit(DNSmsg * req,uchar * buf)41 clientrxmit(DNSmsg *req, uchar *buf)
42 {
43 Inprogress *p, *empty;
44 Udphdr *uh;
45
46 uh = (Udphdr *)buf;
47 empty = nil;
48 for(p = inprog; p < &inprog[Maxactive]; p++){
49 if(p->inuse == 0){
50 if(empty == nil)
51 empty = p;
52 continue;
53 }
54 if(req->id == p->id && req->qd != nil &&
55 req->qd->owner == p->owner && req->qd->type == p->type &&
56 memcmp(uh, &p->uh, Udphdrsize) == 0)
57 return nil;
58 }
59 if(empty == nil)
60 return nil; /* shouldn't happen: see slave() & Maxactive def'n */
61 if(req->qd == nil) {
62 dnslog("clientrxmit: nil req->qd");
63 return nil;
64 }
65 empty->id = req->id;
66 empty->owner = req->qd->owner;
67 empty->type = req->qd->type;
68 if (empty->type != req->qd->type)
69 dnslog("clientrxmit: bogus req->qd->type %d", req->qd->type);
70 memmove(&empty->uh, uh, Udphdrsize);
71 empty->inuse = 1;
72 return empty;
73 }
74
75 int
addforwtarg(char * host)76 addforwtarg(char *host)
77 {
78 Forwtarg *tp;
79
80 if (currtarg >= nelem(forwtarg)) {
81 dnslog("too many forwarding targets");
82 return -1;
83 }
84 tp = forwtarg + currtarg;
85 if (parseip(tp->addr, host) < 0) {
86 dnslog("can't parse ip %s", host);
87 return -1;
88 }
89 tp->lastdial = time(nil);
90 tp->fd = udpport(mntpt);
91 if (tp->fd < 0)
92 return -1;
93
94 free(tp->host);
95 tp->host = estrdup(host);
96 currtarg++;
97 return 0;
98 }
99
100 /*
101 * fast forwarding of incoming queries to other dns servers.
102 * intended primarily for debugging.
103 */
104 static void
redistrib(uchar * buf,int len)105 redistrib(uchar *buf, int len)
106 {
107 Forwtarg *tp;
108 Udphdr *uh;
109 static uchar outpkt[1500];
110
111 assert(len <= sizeof outpkt);
112 memmove(outpkt, buf, len);
113 uh = (Udphdr *)outpkt;
114 for (tp = forwtarg; tp < forwtarg + currtarg; tp++)
115 if (tp->fd > 0) {
116 memmove(outpkt, tp->addr, sizeof tp->addr);
117 hnputs(uh->rport, Dnsport);
118 if (write(tp->fd, outpkt, len) != len) {
119 close(tp->fd);
120 tp->fd = -1;
121 }
122 } else if (tp->host && time(nil) - tp->lastdial > 60) {
123 tp->lastdial = time(nil);
124 tp->fd = udpport(mntpt);
125 }
126 }
127
128 /*
129 * a process to act as a dns server for outside reqeusts
130 */
131 void
dnudpserver(char * mntpt)132 dnudpserver(char *mntpt)
133 {
134 volatile int fd, len, op, rcode;
135 char *volatile err;
136 volatile char tname[32];
137 volatile uchar buf[Udphdrsize + Maxpayload];
138 volatile DNSmsg reqmsg, repmsg;
139 Inprogress *volatile p;
140 volatile Request req;
141 Udphdr *volatile uh;
142
143 /*
144 * fork sharing text, data, and bss with parent.
145 * stay in the same note group.
146 */
147 switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
148 case -1:
149 break;
150 case 0:
151 break;
152 default:
153 return;
154 }
155
156 fd = -1;
157 restart:
158 procsetname("udp server announcing");
159 if(fd >= 0)
160 close(fd);
161 while((fd = udpannounce(mntpt)) < 0)
162 sleep(5000);
163
164 // procsetname("udp server");
165 memset(&req, 0, sizeof req);
166 if(setjmp(req.mret))
167 putactivity(0);
168 req.isslave = 0;
169 req.id = 0;
170 req.aborttime = 0;
171
172 /* loop on requests */
173 for(;; putactivity(0)){
174 procsetname("served %d udp; %d alarms",
175 stats.qrecvdudp, stats.alarms);
176 memset(&repmsg, 0, sizeof repmsg);
177 memset(&reqmsg, 0, sizeof reqmsg);
178
179 alarm(60*1000);
180 len = read(fd, buf, sizeof buf);
181 alarm(0);
182 if(len <= Udphdrsize)
183 goto restart;
184
185 redistrib(buf, len);
186
187 uh = (Udphdr*)buf;
188 len -= Udphdrsize;
189
190 // dnslog("read received UDP from %I to %I",
191 // ((Udphdr*)buf)->raddr, ((Udphdr*)buf)->laddr);
192 getactivity(&req, 0);
193 req.aborttime = timems() + Maxreqtm;
194 // req.from = smprint("%I", ((Udphdr*)buf)->raddr);
195 req.from = smprint("%I", buf);
196 rcode = 0;
197 stats.qrecvdudp++;
198
199 err = convM2DNS(&buf[Udphdrsize], len, &reqmsg, &rcode);
200 if(err){
201 /* first bytes in buf are source IP addr */
202 dnslog("server: input error: %s from %I", err, buf);
203 free(err);
204 goto freereq;
205 }
206 if (rcode == 0)
207 if(reqmsg.qdcount < 1){
208 dnslog("server: no questions from %I", buf);
209 goto freereq;
210 } else if(reqmsg.flags & Fresp){
211 dnslog("server: reply not request from %I", buf);
212 goto freereq;
213 }
214 op = reqmsg.flags & Omask;
215 if(op != Oquery && op != Onotify){
216 dnslog("server: op %d from %I", reqmsg.flags & Omask,
217 buf);
218 goto freereq;
219 }
220
221 if(debug || (trace && subsume(trace, reqmsg.qd->owner->name)))
222 dnslog("%d: serve (%I/%d) %d %s %s",
223 req.id, buf, uh->rport[0]<<8 | uh->rport[1],
224 reqmsg.id, reqmsg.qd->owner->name,
225 rrname(reqmsg.qd->type, tname, sizeof tname));
226
227 p = clientrxmit(&reqmsg, buf);
228 if(p == nil){
229 if(debug)
230 dnslog("%d: duplicate", req.id);
231 goto freereq;
232 }
233
234 if (Logqueries) {
235 RR *rr;
236
237 for (rr = reqmsg.qd; rr; rr = rr->next)
238 syslog(0, "dnsq", "id %d: (%I/%d) %d %s %s",
239 req.id, buf, uh->rport[0]<<8 |
240 uh->rport[1], reqmsg.id,
241 reqmsg.qd->owner->name,
242 rrname(reqmsg.qd->type, tname,
243 sizeof tname));
244 }
245 /* loop through each question */
246 while(reqmsg.qd){
247 memset(&repmsg, 0, sizeof repmsg);
248 switch(op){
249 case Oquery:
250 dnserver(&reqmsg, &repmsg, &req, buf, rcode);
251 break;
252 case Onotify:
253 dnnotify(&reqmsg, &repmsg, &req);
254 break;
255 }
256 /* send reply on fd to address in buf's udp hdr */
257 reply(fd, buf, &repmsg, &req);
258 freeanswers(&repmsg);
259 }
260
261 p->inuse = 0;
262 freereq:
263 free(req.from);
264 req.from = nil;
265 freeanswers(&reqmsg);
266 if(req.isslave){
267 putactivity(0);
268 _exits(0);
269 }
270 }
271 }
272
273 /*
274 * announce on well-known dns udp port and set message style interface
275 */
276 static int
udpannounce(char * mntpt)277 udpannounce(char *mntpt)
278 {
279 int data, ctl;
280 char dir[64], datafile[64+6];
281 static int whined;
282
283 /* get a udp port */
284 sprint(datafile, "%s/udp!*!dns", mntpt);
285 ctl = announce(datafile, dir);
286 if(ctl < 0){
287 if(!whined++)
288 warning("can't announce on %s", datafile);
289 return -1;
290 }
291
292 /* turn on header style interface */
293 if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg))
294 abort(); /* hmsg */
295
296 snprint(datafile, sizeof(datafile), "%s/data", dir);
297 data = open(datafile, ORDWR);
298 if(data < 0){
299 close(ctl);
300 if(!whined++)
301 warning("can't open %s to announce on dns udp port",
302 datafile);
303 return -1;
304 }
305
306 close(ctl);
307 return data;
308 }
309
310 static void
reply(int fd,uchar * buf,DNSmsg * rep,Request * reqp)311 reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
312 {
313 int len;
314 char tname[32];
315
316 if(debug || (trace && subsume(trace, rep->qd->owner->name)))
317 dnslog("%d: reply (%I/%d) %d %s %s qd %R an %R ns %R ar %R",
318 reqp->id, buf, buf[4]<<8 | buf[5],
319 rep->id, rep->qd->owner->name,
320 rrname(rep->qd->type, tname, sizeof tname),
321 rep->qd, rep->an, rep->ns, rep->ar);
322
323 len = convDNS2M(rep, &buf[Udphdrsize], Maxdnspayload);
324 len += Udphdrsize;
325 if(write(fd, buf, len) != len)
326 dnslog("error sending reply: %r");
327 }
328