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