plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

dntcpserver.c (5554B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <ip.h>
      4 #include <bio.h>
      5 #include <ndb.h>
      6 #include <thread.h>
      7 #include "dns.h"
      8 
      9 static char adir[40];
     10 
     11 static int
     12 readmsg(int fd, uchar *buf, int max)
     13 {
     14 	int n;
     15 	uchar x[2];
     16 
     17 	if(readn(fd, x, 2) != 2)
     18 		return -1;
     19 	n = (x[0]<<8) | x[1];
     20 	if(n > max)
     21 		return -1;
     22 	if(readn(fd, buf, n) != n)
     23 		return -1;
     24 	return n;
     25 }
     26 
     27 static int
     28 connreadmsg(int tfd, int *fd, uchar *buf, int max)
     29 {
     30 	int n;
     31 	int lfd;
     32 	char ldir[40];
     33 
     34 	lfd = listen(adir, ldir);
     35 	if (lfd < 0)
     36 		return -1;
     37 	*fd = accept(lfd, ldir);
     38 	if (*fd >= 0)
     39 		n = readmsg(*fd, buf, max);
     40 	else
     41 		n = -1;
     42 	close(lfd);
     43 	return n;
     44 }
     45 
     46 static int
     47 reply(int fd, DNSmsg *rep, Request *req, NetConnInfo *caller)
     48 {
     49 	int len;
     50 	char tname[32];
     51 	uchar buf[4096];
     52 	RR *rp;
     53 
     54 	if(debug){
     55 		syslog(0, logfile, "%d: reply (%s) %s %s %ux",
     56 			req->id, caller ? caller->raddr : "unk",
     57 			rep->qd->owner->name,
     58 			rrname(rep->qd->type, tname, sizeof tname),
     59 			rep->flags);
     60 		for(rp = rep->an; rp; rp = rp->next)
     61 			syslog(0, logfile, "an %R", rp);
     62 		for(rp = rep->ns; rp; rp = rp->next)
     63 			syslog(0, logfile, "ns %R", rp);
     64 		for(rp = rep->ar; rp; rp = rp->next)
     65 			syslog(0, logfile, "ar %R", rp);
     66 	}
     67 
     68 
     69 	len = convDNS2M(rep, buf+2, sizeof(buf) - 2);
     70 	if(len <= 0)
     71 		abort(); /* "dnserver: converting reply" */;
     72 	buf[0] = len>>8;
     73 	buf[1] = len;
     74 	if(write(fd, buf, len+2) < 0){
     75 		syslog(0, logfile, "sending reply: %r");
     76 		return -1;
     77 	}
     78 	return 0;
     79 }
     80 
     81 /*
     82  *  Hash table for domain names.  The hash is based only on the
     83  *  first element of the domain name.
     84  */
     85 extern DN	*ht[HTLEN];
     86 
     87 static int
     88 numelem(char *name)
     89 {
     90 	int i;
     91 
     92 	i = 1;
     93 	for(; *name; name++)
     94 		if(*name == '.')
     95 			i++;
     96 	return i;
     97 }
     98 
     99 static int
    100 inzone(DN *dp, char *name, int namelen, int depth)
    101 {
    102 	int n;
    103 
    104 	if(dp->name == 0)
    105 		return 0;
    106 	if(numelem(dp->name) != depth)
    107 		return 0;
    108 	n = strlen(dp->name);
    109 	if(n < namelen)
    110 		return 0;
    111 	if(strcmp(name, dp->name + n - namelen) != 0)
    112 		return 0;
    113 	if(n > namelen && dp->name[n - namelen - 1] != '.')
    114 		return 0;
    115 	return 1;
    116 }
    117 
    118 static int
    119 dnzone(DNSmsg *reqp, DNSmsg *repp, Request *req, int rfd, NetConnInfo *caller)
    120 {
    121 	DN *dp, *ndp;
    122 	RR r, *rp;
    123 	int h, depth, found, nlen, rv;
    124 
    125 	rv = 0;
    126 	memset(repp, 0, sizeof(*repp));
    127 	repp->id = reqp->id;
    128 	repp->flags = Fauth | Fresp | Fcanrec | Oquery;
    129 	repp->qd = reqp->qd;
    130 	reqp->qd = reqp->qd->next;
    131 	repp->qd->next = 0;
    132 	dp = repp->qd->owner;
    133 
    134 	/* send the soa */
    135 	repp->an = rrlookup(dp, Tsoa, NOneg);
    136 	rv = reply(rfd, repp, req, caller);
    137 	if(repp->an == 0 || rv < 0)
    138 		goto out;
    139 	rrfreelist(repp->an);
    140 
    141 	nlen = strlen(dp->name);
    142 
    143 	/* construct a breadth first search of the name space (hard with a hash) */
    144 	repp->an = &r;
    145 	for(depth = numelem(dp->name); ; depth++){
    146 		found = 0;
    147 		for(h = 0; h < HTLEN; h++)
    148 			for(ndp = ht[h]; ndp; ndp = ndp->next)
    149 				if(inzone(ndp, dp->name, nlen, depth)){
    150 					for(rp = ndp->rr; rp; rp = rp->next){
    151 						/* there shouldn't be negatives, but just in case */
    152 						if(rp->negative)
    153 							continue;
    154 
    155 						/* don't send an soa's, ns's are enough */
    156 						if(rp->type == Tsoa)
    157 							continue;
    158 
    159 						r = *rp;
    160 						r.next = 0;
    161 						rv = reply(rfd, repp, req, caller);
    162 						if(rv < 0)
    163 							goto out;
    164 					}
    165 					found = 1;
    166 				}
    167 		if(!found)
    168 			break;
    169 	}
    170 
    171 	/* resend the soa */
    172 	repp->an = rrlookup(dp, Tsoa, NOneg);
    173 	rv = reply(rfd, repp, req, caller);
    174 out:
    175 	if (repp->an)
    176 		rrfreelist(repp->an);
    177 	rrfree(repp->qd);
    178 	return rv;
    179 }
    180 
    181 void
    182 tcpproc(void *v)
    183 {
    184 	int len, rv;
    185 	Request req;
    186 	DNSmsg reqmsg, repmsg;
    187 	char *err;
    188 	uchar buf[512];
    189 	char tname[32];
    190 	int fd, rfd;
    191 	NetConnInfo *caller;
    192 
    193 	rfd = -1;
    194 	fd = (uintptr)v;
    195 	caller = 0;
    196 	/* loop on requests */
    197 	for(;; putactivity()){
    198 		if (rfd == 1)
    199 			return;
    200 		close(rfd);
    201 		now = time(0);
    202 		memset(&repmsg, 0, sizeof(repmsg));
    203 		if (fd == 0) {
    204 			len = readmsg(fd, buf, sizeof buf);
    205 			rfd = 1;
    206 		} else {
    207 			len = connreadmsg(fd, &rfd, buf, sizeof buf);
    208 		}
    209 		if(len <= 0)
    210 			continue;
    211 		freenetconninfo(caller);
    212 		caller = getnetconninfo(0, fd);
    213 		getactivity(&req);
    214 		req.aborttime = now + 15*Min;
    215 		err = convM2DNS(buf, len, &reqmsg);
    216 		if(err){
    217 			syslog(0, logfile, "server: input error: %s from %I", err, buf);
    218 			continue;
    219 		}
    220 		if(reqmsg.qdcount < 1){
    221 			syslog(0, logfile, "server: no questions from %I", buf);
    222 			continue;
    223 		}
    224 		if(reqmsg.flags & Fresp){
    225 			syslog(0, logfile, "server: reply not request from %I", buf);
    226 			continue;
    227 		}
    228 		if((reqmsg.flags & Omask) != Oquery){
    229 			syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
    230 			continue;
    231 		}
    232 
    233 		if(debug)
    234 			syslog(0, logfile, "%d: serve (%s) %d %s %s",
    235 				req.id, caller ? caller->raddr : 0,
    236 				reqmsg.id,
    237 				reqmsg.qd->owner->name,
    238 				rrname(reqmsg.qd->type, tname, sizeof tname));
    239 
    240 		/* loop through each question */
    241 		while(reqmsg.qd){
    242 			if(reqmsg.qd->type == Taxfr){
    243 				if(dnzone(&reqmsg, &repmsg, &req, rfd, caller) < 0)
    244 					break;
    245 			} else {
    246 				dnserver(&reqmsg, &repmsg, &req);
    247 				rv = reply(rfd, &repmsg, &req, caller);
    248 				rrfreelist(repmsg.qd);
    249 				rrfreelist(repmsg.an);
    250 				rrfreelist(repmsg.ns);
    251 				rrfreelist(repmsg.ar);
    252 				if(rv < 0)
    253 					break;
    254 			}
    255 		}
    256 
    257 		rrfreelist(reqmsg.qd);
    258 		rrfreelist(reqmsg.an);
    259 		rrfreelist(reqmsg.ns);
    260 		rrfreelist(reqmsg.ar);
    261 	}
    262 }
    263 
    264 enum {
    265 	Maxactivetcp = 4
    266 };
    267 
    268 static int
    269 tcpannounce(char *mntpt)
    270 {
    271 	int fd;
    272 
    273 	USED(mntpt);
    274 	if((fd=announce(tcpaddr, adir)) < 0)
    275 		warning("announce %s: %r", tcpaddr);
    276 	return fd;
    277 }
    278 
    279 void
    280 dntcpserver(void *v)
    281 {
    282 	int i, fd;
    283 
    284 	while((fd = tcpannounce(v)) < 0)
    285 		sleep(5*1000);
    286 
    287 	for(i=0; i<Maxactivetcp; i++)
    288 		proccreate(tcpproc, (void*)(uintptr)fd, STACK);
    289 }