| #include <u.h> | 
 | #include <libc.h> | 
 | #include <ip.h> | 
 | #include <bio.h> | 
 | #include <ndb.h> | 
 | #include "dns.h" | 
 |  | 
 | /* | 
 |  *  a dictionary of domain names for packing messages | 
 |  */ | 
 | enum | 
 | { | 
 | 	Ndict=	64 | 
 | }; | 
 | typedef struct Dict	Dict; | 
 | struct Dict | 
 | { | 
 | 	struct { | 
 | 		ushort	offset;		/* pointer to packed name in message */ | 
 | 		char	*name;		/* pointer to unpacked name in buf */ | 
 | 	} x[Ndict]; | 
 | 	int n;			/* size of dictionary */ | 
 | 	uchar *start;		/* start of packed message */ | 
 | 	char buf[4*1024];	/* buffer for unpacked names */ | 
 | 	char *ep;		/* first free char in buf */ | 
 | }; | 
 |  | 
 | #define NAME(x)		p = pname(p, ep, x, dp) | 
 | #define SYMBOL(x)	p = psym(p, ep, x) | 
 | #define STRING(x)	p = pstr(p, ep, x) | 
 | #define BYTES(x, n)	p = pbytes(p, ep, x, n) | 
 | #define USHORT(x)	p = pushort(p, ep, x) | 
 | #define UCHAR(x)	p = puchar(p, ep, x) | 
 | #define ULONG(x)	p = pulong(p, ep, x) | 
 | #define V4ADDR(x)	p = pv4addr(p, ep, x) | 
 | #define V6ADDR(x)	p = pv6addr(p, ep, x) | 
 |  | 
 | static uchar* | 
 | psym(uchar *p, uchar *ep, char *np) | 
 | { | 
 | 	int n; | 
 |  | 
 | 	n = strlen(np); | 
 | 	if(n >= Strlen)			/* DNS maximum length string */ | 
 | 		n = Strlen - 1; | 
 | 	if(ep - p < n+1)		/* see if it fits in the buffer */ | 
 | 		return ep+1; | 
 | 	*p++ = n; | 
 | 	memcpy(p, np, n); | 
 | 	return p + n; | 
 | } | 
 |  | 
 | static uchar* | 
 | pstr(uchar *p, uchar *ep, char *np) | 
 | { | 
 | 	int n; | 
 |  | 
 | 	n = strlen(np); | 
 | 	if(n >= Strlen)			/* DNS maximum length string */ | 
 | 		n = Strlen - 1; | 
 | 	if(ep - p < n+1)		/* see if it fits in the buffer */ | 
 | 		return ep+1; | 
 | 	*p++ = n; | 
 | 	memcpy(p, np, n); | 
 | 	return p + n; | 
 | } | 
 |  | 
 | static uchar* | 
 | pbytes(uchar *p, uchar *ep, uchar *np, int n) | 
 | { | 
 | 	if(ep - p < n) | 
 | 		return ep+1; | 
 | 	memcpy(p, np, n); | 
 | 	return p + n; | 
 | } | 
 |  | 
 | static uchar* | 
 | puchar(uchar *p, uchar *ep, int val) | 
 | { | 
 | 	if(ep - p < 1) | 
 | 		return ep+1; | 
 | 	*p++ = val; | 
 | 	return p; | 
 | } | 
 |  | 
 | static uchar* | 
 | pushort(uchar *p, uchar *ep, int val) | 
 | { | 
 | 	if(ep - p < 2) | 
 | 		return ep+1; | 
 | 	*p++ = val>>8; | 
 | 	*p++ = val; | 
 | 	return p; | 
 | } | 
 |  | 
 | static uchar* | 
 | pulong(uchar *p, uchar *ep, int val) | 
 | { | 
 | 	if(ep - p < 4) | 
 | 		return ep+1; | 
 | 	*p++ = val>>24; | 
 | 	*p++ = val>>16; | 
 | 	*p++ = val>>8; | 
 | 	*p++ = val; | 
 | 	return p; | 
 | } | 
 |  | 
 | static uchar* | 
 | pv4addr(uchar *p, uchar *ep, char *name) | 
 | { | 
 | 	uchar ip[IPaddrlen]; | 
 |  | 
 | 	if(ep - p < 4) | 
 | 		return ep+1; | 
 | 	parseip(ip, name); | 
 | 	v6tov4(p, ip); | 
 | 	return p + 4; | 
 |  | 
 | } | 
 |  | 
 | static uchar* | 
 | pv6addr(uchar *p, uchar *ep, char *name) | 
 | { | 
 | 	if(ep - p < IPaddrlen) | 
 | 		return ep+1; | 
 | 	parseip(p, name); | 
 | 	return p + IPaddrlen; | 
 |  | 
 | } | 
 |  | 
 | static uchar* | 
 | pname(uchar *p, uchar *ep, char *np, Dict *dp) | 
 | { | 
 | 	char *cp; | 
 | 	int i; | 
 | 	char *last;		/* last component packed */ | 
 |  | 
 | 	if(strlen(np) >= Domlen)	/* make sure we don't exceed DNS limits */ | 
 | 		return ep+1; | 
 |  | 
 | 	last = 0; | 
 | 	while(*np){ | 
 | 		/* look through every component in the dictionary for a match */ | 
 | 		for(i = 0; i < dp->n; i++){ | 
 | 			if(strcmp(np, dp->x[i].name) == 0){ | 
 | 				if(ep - p < 2) | 
 | 					return ep+1; | 
 | 				*p++ = (dp->x[i].offset>>8) | 0xc0; | 
 | 				*p++ = dp->x[i].offset; | 
 | 				return p; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* if there's room, enter this name in dictionary */ | 
 | 		if(dp->n < Ndict){ | 
 | 			if(last){ | 
 | 				/* the whole name is already in dp->buf */ | 
 | 				last = strchr(last, '.') + 1; | 
 | 				dp->x[dp->n].name = last; | 
 | 				dp->x[dp->n].offset = p - dp->start; | 
 | 				dp->n++; | 
 | 			} else { | 
 | 				/* add to dp->buf */ | 
 | 				i = strlen(np); | 
 | 				if(dp->ep + i + 1 < &dp->buf[sizeof(dp->buf)]){ | 
 | 					strcpy(dp->ep, np); | 
 | 					dp->x[dp->n].name = dp->ep; | 
 | 					last = dp->ep; | 
 | 					dp->x[dp->n].offset = p - dp->start; | 
 | 					dp->ep += i + 1; | 
 | 					dp->n++; | 
 | 				} | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* put next component into message */ | 
 | 		cp = strchr(np, '.'); | 
 | 		if(cp == 0){ | 
 | 			i = strlen(np); | 
 | 			cp = np + i;	/* point to null terminator */ | 
 | 		} else { | 
 | 			i = cp - np; | 
 | 			cp++;		/* point past '.' */ | 
 | 		} | 
 | 		if(ep-p < i+1) | 
 | 			return ep+1; | 
 | 		*p++ = i;		/* count of chars in label */ | 
 | 		memcpy(p, np, i); | 
 | 		np = cp; | 
 | 		p += i; | 
 | 	} | 
 |  | 
 | 	if(p >= ep) | 
 | 		return ep+1; | 
 | 	*p++ = 0;	/* add top level domain */ | 
 |  | 
 | 	return p; | 
 | } | 
 |  | 
 | static uchar* | 
 | convRR2M(RR *rp, uchar *p, uchar *ep, Dict *dp) | 
 | { | 
 | 	uchar *lp, *data; | 
 | 	int len, ttl; | 
 | 	Txt *t; | 
 |  | 
 | 	NAME(rp->owner->name); | 
 | 	USHORT(rp->type); | 
 | 	USHORT(rp->owner->class); | 
 |  | 
 | 	/* egregious overuse of ttl (it's absolute time in the cache) */ | 
 | 	if(rp->db) | 
 | 		ttl = rp->ttl; | 
 | 	else | 
 | 		ttl = rp->ttl - now; | 
 | 	if(ttl < 0) | 
 | 		ttl = 0; | 
 | 	ULONG(ttl); | 
 |  | 
 | 	lp = p;			/* leave room for the rdata length */ | 
 | 	p += 2; | 
 | 	data = p; | 
 |  | 
 | 	if(data >= ep) | 
 | 		return p+1; | 
 |  | 
 | 	switch(rp->type){ | 
 | 	case Thinfo: | 
 | 		SYMBOL(rp->cpu->name); | 
 | 		SYMBOL(rp->os->name); | 
 | 		break; | 
 | 	case Tcname: | 
 | 	case Tmb: | 
 | 	case Tmd: | 
 | 	case Tmf: | 
 | 	case Tns: | 
 | 		NAME(rp->host->name); | 
 | 		break; | 
 | 	case Tmg: | 
 | 	case Tmr: | 
 | 		NAME(rp->mb->name); | 
 | 		break; | 
 | 	case Tminfo: | 
 | 		NAME(rp->rmb->name); | 
 | 		NAME(rp->mb->name); | 
 | 		break; | 
 | 	case Tmx: | 
 | 		USHORT(rp->pref); | 
 | 		NAME(rp->host->name); | 
 | 		break; | 
 | 	case Ta: | 
 | 		V4ADDR(rp->ip->name); | 
 | 		break; | 
 | 	case Taaaa: | 
 | 		V6ADDR(rp->ip->name); | 
 | 		break; | 
 | 	case Tptr: | 
 | 		NAME(rp->ptr->name); | 
 | 		break; | 
 | 	case Tsoa: | 
 | 		NAME(rp->host->name); | 
 | 		NAME(rp->rmb->name); | 
 | 		ULONG(rp->soa->serial); | 
 | 		ULONG(rp->soa->refresh); | 
 | 		ULONG(rp->soa->retry); | 
 | 		ULONG(rp->soa->expire); | 
 | 		ULONG(rp->soa->minttl); | 
 | 		break; | 
 | 	case Ttxt: | 
 | 		for(t = rp->txt; t != nil; t = t->next) | 
 | 			STRING(t->p); | 
 | 		break; | 
 | 	case Tnull: | 
 | 		BYTES(rp->null->data, rp->null->dlen); | 
 | 		break; | 
 | 	case Trp: | 
 | 		NAME(rp->rmb->name); | 
 | 		NAME(rp->rp->name); | 
 | 		break; | 
 | 	case Tkey: | 
 | 		USHORT(rp->key->flags); | 
 | 		UCHAR(rp->key->proto); | 
 | 		UCHAR(rp->key->alg); | 
 | 		BYTES(rp->key->data, rp->key->dlen); | 
 | 		break; | 
 | 	case Tsig: | 
 | 		USHORT(rp->sig->type); | 
 | 		UCHAR(rp->sig->alg); | 
 | 		UCHAR(rp->sig->labels); | 
 | 		ULONG(rp->sig->ttl); | 
 | 		ULONG(rp->sig->exp); | 
 | 		ULONG(rp->sig->incep); | 
 | 		USHORT(rp->sig->tag); | 
 | 		NAME(rp->sig->signer->name); | 
 | 		BYTES(rp->sig->data, rp->sig->dlen); | 
 | 		break; | 
 | 	case Tcert: | 
 | 		USHORT(rp->cert->type); | 
 | 		USHORT(rp->cert->tag); | 
 | 		UCHAR(rp->cert->alg); | 
 | 		BYTES(rp->cert->data, rp->cert->dlen); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	/* stuff in the rdata section length */ | 
 | 	len = p - data; | 
 | 	*lp++ = len >> 8; | 
 | 	*lp = len; | 
 |  | 
 | 	return p; | 
 | } | 
 |  | 
 | static uchar* | 
 | convQ2M(RR *rp, uchar *p, uchar *ep, Dict *dp) | 
 | { | 
 | 	NAME(rp->owner->name); | 
 | 	USHORT(rp->type); | 
 | 	USHORT(rp->owner->class); | 
 | 	return p; | 
 | } | 
 |  | 
 | static uchar* | 
 | rrloop(RR *rp, int *countp, uchar *p, uchar *ep, Dict *dp, int quest) | 
 | { | 
 | 	uchar *np; | 
 |  | 
 | 	*countp = 0; | 
 | 	for(; rp && p < ep; rp = rp->next){ | 
 | 		if(quest) | 
 | 			np = convQ2M(rp, p, ep, dp); | 
 | 		else | 
 | 			np = convRR2M(rp, p, ep, dp); | 
 | 		if(np > ep) | 
 | 			break; | 
 | 		p = np; | 
 | 		(*countp)++; | 
 | 	} | 
 | 	return p; | 
 | } | 
 |  | 
 | /* | 
 |  *  convert into a message | 
 |  */ | 
 | int | 
 | convDNS2M(DNSmsg *m, uchar *buf, int len) | 
 | { | 
 | 	uchar *p, *ep, *np; | 
 | 	Dict d; | 
 |  | 
 | 	d.n = 0; | 
 | 	d.start = buf; | 
 | 	d.ep = d.buf; | 
 | 	memset(buf, 0, len); | 
 | 	m->qdcount = m->ancount = m->nscount = m->arcount = 0; | 
 |  | 
 | 	/* first pack in the RR's so we can get real counts */ | 
 | 	p = buf + 12; | 
 | 	ep = buf + len; | 
 | 	p = rrloop(m->qd, &m->qdcount, p, ep, &d, 1); | 
 | 	p = rrloop(m->an, &m->ancount, p, ep, &d, 0); | 
 | 	p = rrloop(m->ns, &m->nscount, p, ep, &d, 0); | 
 | 	p = rrloop(m->ar, &m->arcount, p, ep, &d, 0); | 
 | 	if(p > ep) | 
 | 		return -1; | 
 |  | 
 | 	/* now pack the rest */ | 
 | 	np = p; | 
 | 	p = buf; | 
 | 	ep = buf + len; | 
 | 	USHORT(m->id); | 
 | 	USHORT(m->flags); | 
 | 	USHORT(m->qdcount); | 
 | 	USHORT(m->ancount); | 
 | 	USHORT(m->nscount); | 
 | 	USHORT(m->arcount); | 
 | 	if(p > ep) | 
 | 		return -1; | 
 |  | 
 | 	return np - buf; | 
 | } |