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