]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dnudpserver.c
ircrc: freenode -> oftc
[plan9front.git] / sys / src / cmd / ndb / dnudpserver.c
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 forwtcount;
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*
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)
55                 if(req->qd->owner == p->owner)
56                 if(req->qd->type == p->type)
57                 if(memcmp(uh, &p->uh, Udphdrsize) == 0)
58                         return nil;
59         }
60         if(empty == nil)
61                 return nil; /* shouldn't happen: see slave() & Maxactive def'n */
62
63         empty->id = req->id;
64         empty->owner = req->qd->owner;
65         empty->type = req->qd->type;
66         if (empty->type != req->qd->type)
67                 dnslog("clientrxmit: bogus req->qd->type %d", req->qd->type);
68         memmove(&empty->uh, uh, Udphdrsize);
69         empty->inuse = 1;
70         return empty;
71 }
72
73 int
74 addforwtarg(char *host)
75 {
76         Forwtarg *tp;
77
78         if (forwtcount >= nelem(forwtarg)) {
79                 dnslog("too many forwarding targets");
80                 return -1;
81         }
82         tp = forwtarg + forwtcount;
83         if(parseip(tp->addr, host) == -1) {
84                 dnslog("can't parse ip %s", host);
85                 return -1;
86         }
87         tp->lastdial = time(nil);
88         tp->fd = udpport(mntpt);
89         if (tp->fd < 0)
90                 return -1;
91
92         free(tp->host);
93         tp->host = estrdup(host);
94         forwtcount++;
95         return 0;
96 }
97
98 /*
99  * fast forwarding of incoming queries to other dns servers.
100  * intended primarily for debugging.
101  */
102 static void
103 redistrib(uchar *buf, int len)
104 {
105         uchar save[Udphdrsize];
106         Forwtarg *tp;
107         Udphdr *uh;
108
109         memmove(save, buf, Udphdrsize);
110
111         uh = (Udphdr *)buf;
112         for (tp = forwtarg; tp < forwtarg + forwtcount; tp++)
113                 if (tp->fd >= 0) {
114                         memmove(uh->raddr, tp->addr, sizeof tp->addr);
115                         hnputs(uh->rport, 53);          /* dns port */
116                         if (write(tp->fd, buf, len) != len) {
117                                 close(tp->fd);
118                                 tp->fd = -1;
119                         }
120                 } else if (tp->host && time(nil) - tp->lastdial > 60) {
121                         tp->lastdial = time(nil);
122                         tp->fd = udpport(mntpt);
123                 }
124
125         memmove(buf, save, Udphdrsize);
126 }
127
128 /*
129  *  a process to act as a dns server for outside reqeusts
130  */
131 void
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 + Maxudp + 1024];
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 %lud udp; %lud 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                 if(forwtcount > 0)
186                         redistrib(buf, len);
187
188                 uh = (Udphdr*)buf;
189                 len -= Udphdrsize;
190
191                 // dnslog("read received UDP from %I to %I",
192                 //      ((Udphdr*)buf)->raddr, ((Udphdr*)buf)->laddr);
193                 getactivity(&req, 0);
194                 req.aborttime = timems() + Maxreqtm;
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(reqmsg.qd == nil){
222                         dnslog("server: no question RR from %I", buf);
223                         goto freereq;
224                 }
225
226                 if(debug || (trace && subsume(trace, reqmsg.qd->owner->name)))
227                         dnslog("%d: serve (%I/%d) %d %s %s",
228                                 req.id, buf, uh->rport[0]<<8 | uh->rport[1],
229                                 reqmsg.id, reqmsg.qd->owner->name,
230                                 rrname(reqmsg.qd->type, tname, sizeof tname));
231
232                 p = clientrxmit(&reqmsg, buf);
233                 if(p == nil){
234                         if(debug)
235                                 dnslog("%d: duplicate", req.id);
236                         goto freereq;
237                 }
238
239                 if (Logqueries) {
240                         RR *rr;
241
242                         for (rr = reqmsg.qd; rr; rr = rr->next)
243                                 syslog(0, "dnsq", "id %d: (%I/%d) %d %s %s",
244                                         req.id, buf, uh->rport[0]<<8 |
245                                         uh->rport[1], reqmsg.id,
246                                         reqmsg.qd->owner->name,
247                                         rrname(reqmsg.qd->type, tname,
248                                         sizeof tname));
249                 }
250                 /* loop through each question */
251                 while(reqmsg.qd){
252                         memset(&repmsg, 0, sizeof repmsg);
253                         switch(op){
254                         case Oquery:
255                                 dnserver(&reqmsg, &repmsg, &req, buf, rcode);
256                                 break;
257                         case Onotify:
258                                 dnnotify(&reqmsg, &repmsg, &req);
259                                 break;
260                         }
261                         /* send reply on fd to address in buf's udp hdr */
262                         reply(fd, buf, &repmsg, &req);
263                         freeanswers(&repmsg);
264                 }
265
266                 p->inuse = 0;
267 freereq:
268                 free(req.from);
269                 req.from = nil;
270                 freeanswers(&reqmsg);
271                 if(req.isslave){
272                         putactivity(0);
273                         _exits(0);
274                 }
275         }
276 }
277
278 /*
279  *  announce on well-known dns udp port and set message style interface
280  */
281 static int
282 udpannounce(char *mntpt)
283 {
284         int data, ctl;
285         char dir[64], datafile[64+6];
286         static int whined;
287
288         /* get a udp port */
289         sprint(datafile, "%s/udp!*!dns", mntpt);
290         ctl = announce(datafile, dir);
291         if(ctl < 0){
292                 if(!whined++)
293                         warning("can't announce on %s", datafile);
294                 return -1;
295         }
296
297         /* turn on header style interface */
298         if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg)){
299                 close(ctl);
300                 if(!whined++)
301                         warning("can't enable headers on %s", datafile);
302                 return -1;
303         }
304
305         snprint(datafile, sizeof(datafile), "%s/data", dir);
306         data = open(datafile, ORDWR);
307         if(data < 0){
308                 close(ctl);
309                 if(!whined++)
310                         warning("can't open %s to announce on dns udp port",
311                                 datafile);
312                 return -1;
313         }
314
315         close(ctl);
316         return data;
317 }
318
319 static void
320 reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
321 {
322         int len;
323         char tname[32];
324
325         if(debug || (trace && subsume(trace, rep->qd->owner->name)))
326                 dnslog("%d: reply (%I/%d) %d %s %s qd %R an %R ns %R ar %R",
327                         reqp->id, buf, buf[4]<<8 | buf[5],
328                         rep->id, rep->qd->owner->name,
329                         rrname(rep->qd->type, tname, sizeof tname),
330                         rep->qd, rep->an, rep->ns, rep->ar);
331
332         len = convDNS2M(rep, &buf[Udphdrsize], Maxudp);
333         len += Udphdrsize;
334         if(write(fd, buf, len) != len)
335                 dnslog("error sending reply: %r");
336 }