xref: /plan9/sys/src/cmd/ndb/convDNS2M.c (revision cdf9e71c4df09e92569a0ab1012818c6edd03cfd)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dns.h"
5 
6 /*
7  *  a dictionary of domain names for packing messages
8  */
9 enum
10 {
11 	Ndict=	64,
12 };
13 typedef struct Dict	Dict;
14 struct Dict
15 {
16 	struct {
17 		ushort	offset;		/* pointer to packed name in message */
18 		char	*name;		/* pointer to unpacked name in buf */
19 	} x[Ndict];
20 	int	n;		/* size of dictionary */
21 	uchar	*start;		/* start of packed message */
22 	char	buf[16*1024];	/* buffer for unpacked names (was 4k) */
23 	char	*ep;		/* first free char in buf */
24 };
25 
26 #define NAME(x)		p = pname(p, ep, x, dp)
27 #define SYMBOL(x)	p = psym(p, ep, x)
28 #define STRING(x)	p = pstr(p, ep, x)
29 #define BYTES(x, n)	p = pbytes(p, ep, x, n)
30 #define USHORT(x)	p = pushort(p, ep, x)
31 #define UCHAR(x)	p = puchar(p, ep, x)
32 #define ULONG(x)	p = pulong(p, ep, x)
33 #define V4ADDR(x)	p = pv4addr(p, ep, x)
34 #define V6ADDR(x)	p = pv6addr(p, ep, x)
35 
36 static uchar*
psym(uchar * p,uchar * ep,char * np)37 psym(uchar *p, uchar *ep, char *np)
38 {
39 	int n;
40 
41 	n = strlen(np);
42 	if(n >= Strlen)			/* DNS maximum length string */
43 		n = Strlen - 1;
44 	if(ep - p < n+1)		/* see if it fits in the buffer */
45 		return ep+1;
46 	*p++ = n;
47 	memmove(p, np, n);
48 	return p + n;
49 }
50 
51 static uchar*
pstr(uchar * p,uchar * ep,char * np)52 pstr(uchar *p, uchar *ep, char *np)
53 {
54 	return psym(p, ep, np);
55 }
56 
57 static uchar*
pbytes(uchar * p,uchar * ep,uchar * np,int n)58 pbytes(uchar *p, uchar *ep, uchar *np, int n)
59 {
60 	if(ep - p < n)
61 		return ep+1;
62 	memmove(p, np, n);
63 	return p + n;
64 }
65 
66 static uchar*
puchar(uchar * p,uchar * ep,int val)67 puchar(uchar *p, uchar *ep, int val)
68 {
69 	if(ep - p < 1)
70 		return ep+1;
71 	*p++ = val;
72 	return p;
73 }
74 
75 static uchar*
pushort(uchar * p,uchar * ep,int val)76 pushort(uchar *p, uchar *ep, int val)
77 {
78 	if(ep - p < 2)
79 		return ep+1;
80 	*p++ = val>>8;
81 	*p++ = val;
82 	return p;
83 }
84 
85 static uchar*
pulong(uchar * p,uchar * ep,int val)86 pulong(uchar *p, uchar *ep, int val)
87 {
88 	if(ep - p < 4)
89 		return ep+1;
90 	*p++ = val>>24;
91 	*p++ = val>>16;
92 	*p++ = val>>8;
93 	*p++ = val;
94 	return p;
95 }
96 
97 static uchar*
pv4addr(uchar * p,uchar * ep,char * name)98 pv4addr(uchar *p, uchar *ep, char *name)
99 {
100 	uchar ip[IPaddrlen];
101 
102 	if(ep - p < 4)
103 		return ep+1;
104 	parseip(ip, name);
105 	v6tov4(p, ip);
106 	return p + 4;
107 }
108 
109 static uchar*
pv6addr(uchar * p,uchar * ep,char * name)110 pv6addr(uchar *p, uchar *ep, char *name)
111 {
112 	if(ep - p < IPaddrlen)
113 		return ep+1;
114 	parseip(p, name);
115 	return p + IPaddrlen;
116 }
117 
118 static uchar*
pname(uchar * p,uchar * ep,char * np,Dict * dp)119 pname(uchar *p, uchar *ep, char *np, Dict *dp)
120 {
121 	int i;
122 	char *cp;
123 	char *last;		/* last component packed */
124 
125 	if(strlen(np) >= Domlen) /* make sure we don't exceed DNS limits */
126 		return ep+1;
127 
128 	last = 0;
129 	while(*np){
130 		/* look through every component in the dictionary for a match */
131 		for(i = 0; i < dp->n; i++)
132 			if(strcmp(np, dp->x[i].name) == 0){
133 				if(ep - p < 2)
134 					return ep+1;
135 				if ((dp->x[i].offset>>8) & 0xc0)
136 					dnslog("convDNS2M: offset too big for "
137 						"DNS packet format");
138 				*p++ = dp->x[i].offset>>8 | 0xc0;
139 				*p++ = dp->x[i].offset;
140 				return p;
141 			}
142 
143 		/* if there's room, enter this name in dictionary */
144 		if(dp->n < Ndict)
145 			if(last){
146 				/* the whole name is already in dp->buf */
147 				last = strchr(last, '.') + 1;
148 				dp->x[dp->n].name = last;
149 				dp->x[dp->n].offset = p - dp->start;
150 				dp->n++;
151 			} else {
152 				/* add to dp->buf */
153 				i = strlen(np);
154 				if(dp->ep + i + 1 < &dp->buf[sizeof dp->buf]){
155 					strcpy(dp->ep, np);
156 					dp->x[dp->n].name = dp->ep;
157 					last = dp->ep;
158 					dp->x[dp->n].offset = p - dp->start;
159 					dp->ep += i + 1;
160 					dp->n++;
161 				}
162 			}
163 
164 		/* put next component into message */
165 		cp = strchr(np, '.');
166 		if(cp == nil){
167 			i = strlen(np);
168 			cp = np + i;	/* point to null terminator */
169 		} else {
170 			i = cp - np;
171 			cp++;		/* point past '.' */
172 		}
173 		if(ep-p < i+1)
174 			return ep+1;
175 		if (i > Labellen)
176 			return ep+1;
177 		*p++ = i;		/* count of chars in label */
178 		memmove(p, np, i);
179 		np = cp;
180 		p += i;
181 	}
182 
183 	if(p >= ep)
184 		return ep+1;
185 	*p++ = 0;	/* add top level domain */
186 
187 	return p;
188 }
189 
190 static uchar*
convRR2M(RR * rp,uchar * p,uchar * ep,Dict * dp)191 convRR2M(RR *rp, uchar *p, uchar *ep, Dict *dp)
192 {
193 	uchar *lp, *data;
194 	int len, ttl;
195 	Txt *t;
196 
197 	NAME(rp->owner->name);
198 	USHORT(rp->type);
199 	USHORT(rp->owner->class);
200 
201 	/* egregious overuse of ttl (it's absolute time in the cache) */
202 	if(rp->db)
203 		ttl = rp->ttl;
204 	else
205 		ttl = rp->ttl - now;
206 	if(ttl < 0)
207 		ttl = 0;
208 	ULONG(ttl);
209 
210 	lp = p;			/* leave room for the rdata length */
211 	p += 2;
212 	data = p;
213 
214 	if(data >= ep)
215 		return p+1;
216 
217 	switch(rp->type){
218 	case Thinfo:
219 		SYMBOL(rp->cpu->name);
220 		SYMBOL(rp->os->name);
221 		break;
222 	case Tcname:
223 	case Tmb:
224 	case Tmd:
225 	case Tmf:
226 	case Tns:
227 		NAME(rp->host->name);
228 		break;
229 	case Tmg:
230 	case Tmr:
231 		NAME(rp->mb->name);
232 		break;
233 	case Tminfo:
234 		NAME(rp->rmb->name);
235 		NAME(rp->mb->name);
236 		break;
237 	case Tmx:
238 		USHORT(rp->pref);
239 		NAME(rp->host->name);
240 		break;
241 	case Ta:
242 		V4ADDR(rp->ip->name);
243 		break;
244 	case Taaaa:
245 		V6ADDR(rp->ip->name);
246 		break;
247 	case Tptr:
248 		NAME(rp->ptr->name);
249 		break;
250 	case Tsoa:
251 		NAME(rp->host->name);
252 		NAME(rp->rmb->name);
253 		ULONG(rp->soa->serial);
254 		ULONG(rp->soa->refresh);
255 		ULONG(rp->soa->retry);
256 		ULONG(rp->soa->expire);
257 		ULONG(rp->soa->minttl);
258 		break;
259 	case Tsrv:
260 		USHORT(rp->srv->pri);
261 		USHORT(rp->srv->weight);
262 		USHORT(rp->port);
263 		STRING(rp->host->name);	/* rfc2782 sez no name compression */
264 		break;
265 	case Ttxt:
266 		for(t = rp->txt; t != nil; t = t->next)
267 			STRING(t->p);
268 		break;
269 	case Tnull:
270 		BYTES(rp->null->data, rp->null->dlen);
271 		break;
272 	case Trp:
273 		NAME(rp->rmb->name);
274 		NAME(rp->rp->name);
275 		break;
276 	case Tkey:
277 		USHORT(rp->key->flags);
278 		UCHAR(rp->key->proto);
279 		UCHAR(rp->key->alg);
280 		BYTES(rp->key->data, rp->key->dlen);
281 		break;
282 	case Tsig:
283 		USHORT(rp->sig->type);
284 		UCHAR(rp->sig->alg);
285 		UCHAR(rp->sig->labels);
286 		ULONG(rp->sig->ttl);
287 		ULONG(rp->sig->exp);
288 		ULONG(rp->sig->incep);
289 		USHORT(rp->sig->tag);
290 		NAME(rp->sig->signer->name);
291 		BYTES(rp->sig->data, rp->sig->dlen);
292 		break;
293 	case Tcert:
294 		USHORT(rp->cert->type);
295 		USHORT(rp->cert->tag);
296 		UCHAR(rp->cert->alg);
297 		BYTES(rp->cert->data, rp->cert->dlen);
298 		break;
299 	}
300 
301 	/* stuff in the rdata section length */
302 	len = p - data;
303 	*lp++ = len >> 8;
304 	*lp = len;
305 
306 	return p;
307 }
308 
309 static uchar*
convQ2M(RR * rp,uchar * p,uchar * ep,Dict * dp)310 convQ2M(RR *rp, uchar *p, uchar *ep, Dict *dp)
311 {
312 	NAME(rp->owner->name);
313 	USHORT(rp->type);
314 	USHORT(rp->owner->class);
315 	return p;
316 }
317 
318 static uchar*
rrloop(RR * rp,int * countp,uchar * p,uchar * ep,Dict * dp,int quest)319 rrloop(RR *rp, int *countp, uchar *p, uchar *ep, Dict *dp, int quest)
320 {
321 	uchar *np;
322 
323 	*countp = 0;
324 	for(; rp && p < ep; rp = rp->next){
325 		if(quest)
326 			np = convQ2M(rp, p, ep, dp);
327 		else
328 			np = convRR2M(rp, p, ep, dp);
329 		if(np > ep)
330 			break;
331 		p = np;
332 		(*countp)++;
333 	}
334 	return p;
335 }
336 
337 /*
338  *  convert into a message
339  */
340 int
convDNS2M(DNSmsg * m,uchar * buf,int len)341 convDNS2M(DNSmsg *m, uchar *buf, int len)
342 {
343 	ulong trunc = 0;
344 	uchar *p, *ep, *np;
345 	Dict d;
346 
347 	d.n = 0;
348 	d.start = buf;
349 	d.ep = d.buf;
350 	memset(buf, 0, len);
351 	m->qdcount = m->ancount = m->nscount = m->arcount = 0;
352 
353 	/* first pack in the RR's so we can get real counts */
354 	p = buf + 12;
355 	ep = buf + len;
356 	p = rrloop(m->qd, &m->qdcount, p, ep, &d, 1);
357 	p = rrloop(m->an, &m->ancount, p, ep, &d, 0);
358 	p = rrloop(m->ns, &m->nscount, p, ep, &d, 0);
359 	p = rrloop(m->ar, &m->arcount, p, ep, &d, 0);
360 	if(p > ep) {
361 		trunc = Ftrunc;
362 		dnslog("udp packet full; truncating my reply");
363 		p = ep;
364 	}
365 
366 	/* now pack the rest */
367 	np = p;
368 	p = buf;
369 	ep = buf + len;
370 	USHORT(m->id);
371 	USHORT(m->flags | trunc);
372 	USHORT(m->qdcount);
373 	USHORT(m->ancount);
374 	USHORT(m->nscount);
375 	USHORT(m->arcount);
376 	USED(p);
377 	return np - buf;
378 }
379