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