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