xref: /plan9/sys/src/cmd/ndb/convM2DNS.c (revision 9b943567965ba040fd275927fbe088656eb8ce4f)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dns.h"
5 
6 typedef struct Scan	Scan;
7 struct Scan
8 {
9 	uchar	*base;
10 	uchar	*p;
11 	uchar	*ep;
12 	char	*err;
13 };
14 
15 #define NAME(x)		gname(x, sp)
16 #define SYMBOL(x)	(x = gsym(sp))
17 #define STRING(x)	(x = gstr(sp))
18 #define USHORT(x)	(x = gshort(sp))
19 #define ULONG(x)	(x = glong(sp))
20 #define UCHAR(x)	(x = gchar(sp))
21 #define V4ADDR(x)	(x = gv4addr(sp))
22 #define V6ADDR(x)	(x = gv6addr(sp))
23 #define BYTES(x, y)	(y = gbytes(sp, &x, len - (sp->p - data)))
24 
25 static char *toolong = "too long";
26 
27 /*
28  *  get a ushort/ulong
29  */
30 static ushort
31 gchar(Scan *sp)
32 {
33 	ushort x;
34 
35 	if(sp->err)
36 		return 0;
37 	if(sp->ep - sp->p < 1){
38 		sp->err = toolong;
39 		return 0;
40 	}
41 	x = sp->p[0];
42 	sp->p += 1;
43 	return x;
44 }
45 static ushort
46 gshort(Scan *sp)
47 {
48 	ushort x;
49 
50 	if(sp->err)
51 		return 0;
52 	if(sp->ep - sp->p < 2){
53 		sp->err = toolong;
54 		return 0;
55 	}
56 	x = (sp->p[0]<<8) | sp->p[1];
57 	sp->p += 2;
58 	return x;
59 }
60 static ulong
61 glong(Scan *sp)
62 {
63 	ulong x;
64 
65 	if(sp->err)
66 		return 0;
67 	if(sp->ep - sp->p < 4){
68 		sp->err = toolong;
69 		return 0;
70 	}
71 	x = (sp->p[0]<<24) | (sp->p[1]<<16) | (sp->p[2]<<8) | sp->p[3];
72 	sp->p += 4;
73 	return x;
74 }
75 
76 /*
77  *  get an ip address
78  */
79 static DN*
80 gv4addr(Scan *sp)
81 {
82 	char addr[32];
83 
84 	if(sp->err)
85 		return 0;
86 	if(sp->ep - sp->p < 4){
87 		sp->err = toolong;
88 		return 0;
89 	}
90 	snprint(addr, sizeof(addr), "%V", sp->p);
91 	sp->p += 4;
92 
93 	return dnlookup(addr, Cin, 1);
94 }
95 static DN*
96 gv6addr(Scan *sp)
97 {
98 	char addr[64];
99 
100 	if(sp->err)
101 		return 0;
102 	if(sp->ep - sp->p < IPaddrlen){
103 		sp->err = toolong;
104 		return 0;
105 	}
106 	snprint(addr, sizeof(addr), "%I", sp->p);
107 	sp->p += IPaddrlen;
108 
109 	return dnlookup(addr, Cin, 1);
110 }
111 
112 /*
113  *  get a string.  make it an internal symbol.
114  */
115 static DN*
116 gsym(Scan *sp)
117 {
118 	int n;
119 	char sym[Strlen+1];
120 
121 	if(sp->err)
122 		return 0;
123 	n = *(sp->p++);
124 	if(sp->p+n > sp->ep){
125 		sp->err = toolong;
126 		return 0;
127 	}
128 
129 	if(n > Strlen){
130 		sp->err = "illegal string";
131 		return 0;
132 	}
133 	strncpy(sym, (char*)sp->p, n);
134 	sym[n] = 0;
135 	sp->p += n;
136 
137 	return dnlookup(sym, Csym, 1);
138 }
139 
140 /*
141  *  get a string.  don't make it an internal symbol.
142  */
143 static Txt*
144 gstr(Scan *sp)
145 {
146 	int n;
147 	char sym[Strlen+1];
148 	Txt *t;
149 
150 	if(sp->err)
151 		return 0;
152 	n = *(sp->p++);
153 	if(sp->p+n > sp->ep){
154 		sp->err = toolong;
155 		return 0;
156 	}
157 
158 	if(n > Strlen){
159 		sp->err = "illegal string";
160 		return 0;
161 	}
162 	strncpy(sym, (char*)sp->p, n);
163 	sym[n] = 0;
164 	sp->p += n;
165 
166 	t = emalloc(sizeof(*t));
167 	t->next = nil;
168 	t->p = estrdup(sym);
169 	return t;
170 }
171 
172 /*
173  *  get a sequence of bytes
174  */
175 static int
176 gbytes(Scan *sp, uchar **p, int n)
177 {
178 	if(sp->err)
179 		return 0;
180 	if(sp->p+n > sp->ep || n < 0){
181 		sp->err = toolong;
182 		return 0;
183 	}
184 	*p = emalloc(n);
185 	memmove(*p, sp->p, n);
186 	sp->p += n;
187 
188 	return n;
189 }
190 
191 /*
192  *  get a domain name.  'to' must point to a buffer at least Domlen+1 long.
193  */
194 static char*
195 gname(char *to, Scan *sp)
196 {
197 	int len, off;
198 	int pointer;
199 	int n;
200 	char *tostart;
201 	char *toend;
202 	uchar *p;
203 
204 	tostart = to;
205 	if(sp->err)
206 		goto err;
207 	pointer = 0;
208 	p = sp->p;
209 	toend = to + Domlen;
210 	for(len = 0; *p; len += pointer ? 0 : (n+1)){
211 		if((*p & 0xc0) == 0xc0){
212 			/* pointer to other spot in message */
213 			if(pointer++ > 10){
214 				sp->err = "pointer loop";
215 				goto err;
216 			}
217 			off = ((p[0]<<8) + p[1]) & 0x3ff;
218 			p = sp->base + off;
219 			if(p >= sp->ep){
220 				sp->err = "bad pointer";
221 				goto err;
222 			}
223 			n = 0;
224 			continue;
225 		}
226 		n = *p++;
227 		if(len + n < Domlen - 1){
228 			if(to + n > toend){
229 				sp->err = toolong;
230 				goto err;
231 			}
232 			memmove(to, p, n);
233 			to += n;
234 		}
235 		p += n;
236 		if(*p){
237 			if(to >= toend){
238 				sp->err = toolong;
239 				goto err;
240 			}
241 			*to++ = '.';
242 		}
243 	}
244 	*to = 0;
245 	if(pointer)
246 		sp->p += len + 2;	/* + 2 for pointer */
247 	else
248 		sp->p += len + 1;	/* + 1 for the null domain */
249 	return tostart;
250 err:
251 	*tostart = 0;
252 	return tostart;
253 }
254 
255 /*
256  *  convert the next RR from a message
257  */
258 static RR*
259 convM2RR(Scan *sp)
260 {
261 	RR *rp;
262 	int type;
263 	int class;
264 	uchar *data;
265 	int len;
266 	char dname[Domlen+1];
267 	Txt *t, **l;
268 
269 retry:
270 	NAME(dname);
271 	USHORT(type);
272 	USHORT(class);
273 
274 	rp = rralloc(type);
275 	rp->owner = dnlookup(dname, class, 1);
276 	rp->type = type;
277 
278 	ULONG(rp->ttl);
279 	rp->ttl += now;
280 	USHORT(len);
281 	data = sp->p;
282 
283 	if(sp->p + len > sp->ep)
284 		sp->err = toolong;
285 	if(sp->err){
286 		rrfree(rp);
287 		return 0;
288 	}
289 
290 	switch(type){
291 	default:
292 		/* unknown type, just ignore it */
293 		sp->p = data + len;
294 		rrfree(rp);
295 		goto retry;
296 	case Thinfo:
297 		SYMBOL(rp->cpu);
298 		SYMBOL(rp->os);
299 		break;
300 	case Tcname:
301 	case Tmb:
302 	case Tmd:
303 	case Tmf:
304 	case Tns:
305 		rp->host = dnlookup(NAME(dname), Cin, 1);
306 		break;
307 	case Tmg:
308 	case Tmr:
309 		rp->mb = dnlookup(NAME(dname), Cin, 1);
310 		break;
311 	case Tminfo:
312 		rp->rmb = dnlookup(NAME(dname), Cin, 1);
313 		rp->mb = dnlookup(NAME(dname), Cin, 1);
314 		break;
315 	case Tmx:
316 		USHORT(rp->pref);
317 		rp->host = dnlookup(NAME(dname), Cin, 1);
318 		break;
319 	case Ta:
320 		V4ADDR(rp->ip);
321 		break;
322 	case Taaaa:
323 		V6ADDR(rp->ip);
324 		break;
325 	case Tptr:
326 		rp->ptr = dnlookup(NAME(dname), Cin, 1);
327 		break;
328 	case Tsoa:
329 		rp->host = dnlookup(NAME(dname), Cin, 1);
330 		rp->rmb = dnlookup(NAME(dname), Cin, 1);
331 		ULONG(rp->soa->serial);
332 		ULONG(rp->soa->refresh);
333 		ULONG(rp->soa->retry);
334 		ULONG(rp->soa->expire);
335 		ULONG(rp->soa->minttl);
336 		break;
337 	case Ttxt:
338 		l = &rp->txt;
339 		*l = nil;
340 		while(sp->p-data < len){
341 			STRING(t);
342 			*l = t;
343 			l = &t->next;
344 		}
345 		break;
346 	case Tnull:
347 		BYTES(rp->null->data, rp->null->dlen);
348 		break;
349 	case Trp:
350 		rp->rmb = dnlookup(NAME(dname), Cin, 1);
351 		rp->rp = dnlookup(NAME(dname), Cin, 1);
352 		break;
353 	case Tkey:
354 		USHORT(rp->key->flags);
355 		UCHAR(rp->key->proto);
356 		UCHAR(rp->key->alg);
357 		BYTES(rp->key->data, rp->key->dlen);
358 		break;
359 	case Tsig:
360 		USHORT(rp->sig->type);
361 		UCHAR(rp->sig->alg);
362 		UCHAR(rp->sig->labels);
363 		ULONG(rp->sig->ttl);
364 		ULONG(rp->sig->exp);
365 		ULONG(rp->sig->incep);
366 		USHORT(rp->sig->tag);
367 		rp->sig->signer = dnlookup(NAME(dname), Cin, 1);
368 		BYTES(rp->sig->data, rp->sig->dlen);
369 		break;
370 	case Tcert:
371 		USHORT(rp->cert->type);
372 		USHORT(rp->cert->tag);
373 		UCHAR(rp->cert->alg);
374 		BYTES(rp->cert->data, rp->cert->dlen);
375 		break;
376 	}
377 	if(sp->p - data != len)
378 		sp->err = "bad RR len";
379 	return rp;
380 }
381 
382 /*
383  *  convert the next question from a message
384  */
385 static RR*
386 convM2Q(Scan *sp)
387 {
388 	char dname[Domlen+1];
389 	int type;
390 	int class;
391 	RR *rp;
392 
393 	NAME(dname);
394 	USHORT(type);
395 	USHORT(class);
396 	if(sp->err)
397 		return 0;
398 
399 	rp = rralloc(type);
400 	rp->owner = dnlookup(dname, class, 1);
401 
402 	return rp;
403 }
404 
405 static RR*
406 rrloop(Scan *sp, int count, int quest)
407 {
408 	int i;
409 	static char errbuf[64];
410 	RR *first, *rp, **l;
411 
412 	if(sp->err)
413 		return 0;
414 	l = &first;
415 	first = 0;
416 	for(i = 0; i < count; i++){
417 		rp = quest ? convM2Q(sp) : convM2RR(sp);
418 		if(rp == 0)
419 			break;
420 		if(sp->err){
421 			rrfree(rp);
422 			break;
423 		}
424 		*l = rp;
425 		l = &rp->next;
426 	}
427 	return first;
428 }
429 
430 /*
431  *  convert the next DNS from a message stream
432  */
433 char*
434 convM2DNS(uchar *buf, int len, DNSmsg *m)
435 {
436 	Scan scan;
437 	Scan *sp;
438 	char *err;
439 
440 	scan.base = buf;
441 	scan.p = buf;
442 	scan.ep = buf + len;
443 	scan.err = 0;
444 	sp = &scan;
445 	memset(m, 0, sizeof(DNSmsg));
446 	USHORT(m->id);
447 	USHORT(m->flags);
448 	USHORT(m->qdcount);
449 	USHORT(m->ancount);
450 	USHORT(m->nscount);
451 	USHORT(m->arcount);
452 	m->qd = rrloop(sp, m->qdcount, 1);
453 	m->an = rrloop(sp, m->ancount, 0);
454 	m->ns = rrloop(sp, m->nscount, 0);
455 	err = scan.err;				/* live with bad ar's */
456 	m->ar = rrloop(sp, m->arcount, 0);
457 	return err;
458 }
459