xref: /openbsd-src/lib/libc/asr/asr_utils.c (revision c4cedade719948a572b37f588bd4174b42e050f4)
1 /*	$OpenBSD: asr_utils.c,v 1.4 2013/03/29 23:01:24 eric Exp $	*/
2 /*
3  * Copyright (c) 2009-2012	Eric Faurot	<eric@faurot.net>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <net/if.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <arpa/nameser.h>
24 
25 #include <ctype.h>
26 #include <errno.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "asr.h"
34 #include "asr_private.h"
35 
36 static int dname_check_label(const char*, size_t);
37 static ssize_t dname_expand(const unsigned char*, size_t, size_t, size_t*,
38     char *, size_t);
39 
40 static int unpack_data(struct unpack*, void*, size_t);
41 static int unpack_u16(struct unpack*, uint16_t*);
42 static int unpack_u32(struct unpack*, uint32_t*);
43 static int unpack_inaddr(struct unpack*, struct in_addr*);
44 static int unpack_in6addr(struct unpack*, struct in6_addr*);
45 static int unpack_dname(struct unpack*, char*, size_t);
46 
47 static int pack_data(struct pack*, const void*, size_t);
48 static int pack_u16(struct pack*, uint16_t);
49 static int pack_dname(struct pack*, const char*);
50 
51 static int
52 dname_check_label(const char *s, size_t l)
53 {
54 	if (l == 0 || l > 63)
55 		return (-1);
56 
57 	for (l--; l; l--, s++)
58 		if (!(isalnum(*s) || *s == '_' || *s == '-'))
59 			return (-1);
60 
61 	return (0);
62 }
63 
64 ssize_t
65 dname_from_fqdn(const char *str, char *dst, size_t max)
66 {
67 	ssize_t	 res;
68 	size_t	 l, n;
69 	char	*d;
70 
71 	res = 0;
72 
73 	/* special case: the root domain */
74 	if (str[0] == '.') {
75 		if (str[1] != '\0')
76 			return (-1);
77 		if (dst && max >= 1)
78 			*dst = '\0';
79 		return (1);
80 	}
81 
82 	for (; *str; str = d + 1) {
83 
84 		d = strchr(str, '.');
85 		if (d == NULL || d == str)
86 			return (-1);
87 
88 		l = (d - str);
89 
90 		if (dname_check_label(str, l) == -1)
91 			return (-1);
92 
93 		res += l + 1;
94 
95 		if (dst) {
96 			*dst++ = l;
97 			max -= 1;
98 			n = (l > max) ? max : l;
99 			memmove(dst, str, n);
100 			max -= n;
101 			if (max == 0)
102 				dst = NULL;
103 			else
104 				dst += n;
105 		}
106 	}
107 
108 	if (dst)
109 		*dst++ = '\0';
110 
111 	return (res + 1);
112 }
113 
114 static ssize_t
115 dname_expand(const unsigned char *data, size_t len, size_t offset,
116     size_t *newoffset, char *dst, size_t max)
117 {
118 	size_t		 n, count, end, ptr, start;
119 	ssize_t		 res;
120 
121 	if (offset >= len)
122 		return (-1);
123 
124 	res = 0;
125 	end = start = offset;
126 
127 	for (; (n = data[offset]); ) {
128 		if ((n & 0xc0) == 0xc0) {
129 			if (offset + 2 > len)
130 				return (-1);
131 			ptr = 256 * (n & ~0xc0) + data[offset + 1];
132 			if (ptr >= start)
133 				return (-1);
134 			if (end < offset + 2)
135 				end = offset + 2;
136 			offset = ptr;
137 			continue;
138 		}
139 		if (offset + n + 1 > len)
140 			return (-1);
141 
142 		if (dname_check_label(data + offset + 1, n) == -1)
143 			return (-1);
144 
145 		/* copy n + at offset+1 */
146 		if (dst != NULL && max != 0) {
147 			count = (max < n + 1) ? (max) : (n + 1);
148 			memmove(dst, data + offset, count);
149 			dst += count;
150 			max -= count;
151 		}
152 		res += n + 1;
153 		offset += n + 1;
154 		if (end < offset)
155 			end = offset;
156 	}
157 	if (end < offset + 1)
158 		end = offset + 1;
159 
160 	if (dst != NULL && max != 0)
161 		dst[0] = 0;
162 	if (newoffset)
163 		*newoffset = end;
164 	return (res + 1);
165 }
166 
167 void
168 pack_init(struct pack *pack, char *buf, size_t len)
169 {
170 	pack->buf = buf;
171 	pack->len = len;
172 	pack->offset = 0;
173 	pack->err = NULL;
174 }
175 
176 void
177 unpack_init(struct unpack *unpack, const char *buf, size_t len)
178 {
179 	unpack->buf = buf;
180 	unpack->len = len;
181 	unpack->offset = 0;
182 	unpack->err = NULL;
183 }
184 
185 static int
186 unpack_data(struct unpack *p, void *data, size_t len)
187 {
188 	if (p->err)
189 		return (-1);
190 
191 	if (p->len - p->offset < len) {
192 		p->err = "too short";
193 		return (-1);
194 	}
195 
196 	memmove(data, p->buf + p->offset, len);
197 	p->offset += len;
198 
199 	return (0);
200 }
201 
202 static int
203 unpack_u16(struct unpack *p, uint16_t *u16)
204 {
205 	if (unpack_data(p, u16, 2) == -1)
206 		return (-1);
207 
208 	*u16 = ntohs(*u16);
209 
210 	return (0);
211 }
212 
213 static int
214 unpack_u32(struct unpack *p, uint32_t *u32)
215 {
216 	if (unpack_data(p, u32, 4) == -1)
217 		return (-1);
218 
219 	*u32 = ntohl(*u32);
220 
221 	return (0);
222 }
223 
224 static int
225 unpack_inaddr(struct unpack *p, struct in_addr *a)
226 {
227 	return (unpack_data(p, a, 4));
228 }
229 
230 static int
231 unpack_in6addr(struct unpack *p, struct in6_addr *a6)
232 {
233 	return (unpack_data(p, a6, 16));
234 }
235 
236 static int
237 unpack_dname(struct unpack *p, char *dst, size_t max)
238 {
239 	ssize_t e;
240 
241 	if (p->err)
242 		return (-1);
243 
244 	e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max);
245 	if (e == -1) {
246 		p->err = "bad domain name";
247 		return (-1);
248 	}
249 	if (e < 0 || e > MAXDNAME) {
250 		p->err = "domain name too long";
251 		return (-1);
252 	}
253 
254 	return (0);
255 }
256 
257 int
258 unpack_header(struct unpack *p, struct header *h)
259 {
260 	if (unpack_data(p, h, HFIXEDSZ) == -1)
261 		return (-1);
262 
263 	h->flags = ntohs(h->flags);
264 	h->qdcount = ntohs(h->qdcount);
265 	h->ancount = ntohs(h->ancount);
266 	h->nscount = ntohs(h->nscount);
267 	h->arcount = ntohs(h->arcount);
268 
269 	return (0);
270 }
271 
272 int
273 unpack_query(struct unpack *p, struct query *q)
274 {
275 	unpack_dname(p, q->q_dname, sizeof(q->q_dname));
276 	unpack_u16(p, &q->q_type);
277 	unpack_u16(p, &q->q_class);
278 
279 	return (p->err) ? (-1) : (0);
280 }
281 
282 int
283 unpack_rr(struct unpack *p, struct rr *rr)
284 {
285 	uint16_t	rdlen;
286 	size_t		save_offset;
287 
288 	unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
289 	unpack_u16(p, &rr->rr_type);
290 	unpack_u16(p, &rr->rr_class);
291 	unpack_u32(p, &rr->rr_ttl);
292 	unpack_u16(p, &rdlen);
293 
294 	if (p->err)
295 		return (-1);
296 
297 	if (p->len - p->offset < rdlen) {
298 		p->err = "too short";
299 		return (-1);
300 	}
301 
302 	save_offset = p->offset;
303 
304 	switch (rr->rr_type) {
305 
306 	case T_CNAME:
307 		unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
308 		break;
309 
310 	case T_MX:
311 		unpack_u16(p, &rr->rr.mx.preference);
312 		unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
313 		break;
314 
315 	case T_NS:
316 		unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
317 		break;
318 
319 	case T_PTR:
320 		unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
321 		break;
322 
323 	case T_SOA:
324 		unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
325 		unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
326 		unpack_u32(p, &rr->rr.soa.serial);
327 		unpack_u32(p, &rr->rr.soa.refresh);
328 		unpack_u32(p, &rr->rr.soa.retry);
329 		unpack_u32(p, &rr->rr.soa.expire);
330 		unpack_u32(p, &rr->rr.soa.minimum);
331 		break;
332 
333 	case T_A:
334 		if (rr->rr_class != C_IN)
335 			goto other;
336 		unpack_inaddr(p, &rr->rr.in_a.addr);
337 		break;
338 
339 	case T_AAAA:
340 		if (rr->rr_class != C_IN)
341 			goto other;
342 		unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
343 		break;
344 	default:
345 	other:
346 		rr->rr.other.rdata = p->buf + p->offset;
347 		rr->rr.other.rdlen = rdlen;
348 		p->offset += rdlen;
349 	}
350 
351 	if (p->err)
352 		return (-1);
353 
354 	/* make sure that the advertised rdlen is really ok */
355 	if (p->offset - save_offset != rdlen)
356 		p->err = "bad dlen";
357 
358 	return (p->err) ? (-1) : (0);
359 }
360 
361 static int
362 pack_data(struct pack *p, const void *data, size_t len)
363 {
364 	if (p->err)
365 		return (-1);
366 
367 	if (p->len < p->offset + len) {
368 		p->err = "no space";
369 		return (-1);
370 	}
371 
372 	memmove(p->buf + p->offset, data, len);
373 	p->offset += len;
374 
375 	return (0);
376 }
377 
378 static int
379 pack_u16(struct pack *p, uint16_t v)
380 {
381 	v = htons(v);
382 
383 	return (pack_data(p, &v, 2));
384 }
385 
386 static int
387 pack_dname(struct pack *p, const char *dname)
388 {
389 	/* dname compression would be nice to have here.
390 	 * need additionnal context.
391 	 */
392 	return (pack_data(p, dname, strlen(dname) + 1));
393 }
394 
395 int
396 pack_header(struct pack *p, const struct header *h)
397 {
398 	struct header c;
399 
400 	c.id = h->id;
401 	c.flags = htons(h->flags);
402 	c.qdcount = htons(h->qdcount);
403 	c.ancount = htons(h->ancount);
404 	c.nscount = htons(h->nscount);
405 	c.arcount = htons(h->arcount);
406 
407 	return (pack_data(p, &c, HFIXEDSZ));
408 }
409 
410 int
411 pack_query(struct pack *p, uint16_t type, uint16_t class, const char *dname)
412 {
413 	pack_dname(p, dname);
414 	pack_u16(p, type);
415 	pack_u16(p, class);
416 
417 	return (p->err) ? (-1) : (0);
418 }
419 
420 int
421 sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
422 {
423 	struct in_addr		 ina;
424 	struct in6_addr		 in6a;
425 	struct sockaddr_in	*sin;
426 	struct sockaddr_in6	*sin6;
427 	char 			*cp, *str2;
428 	const char		*errstr;
429 
430 	switch (family) {
431 	case PF_UNSPEC:
432 		if (sockaddr_from_str(sa, PF_INET, str) == 0)
433 			return (0);
434 		return sockaddr_from_str(sa, PF_INET6, str);
435 
436 	case PF_INET:
437 		if (inet_pton(PF_INET, str, &ina) != 1)
438 			return (-1);
439 
440 		sin = (struct sockaddr_in *)sa;
441 		memset(sin, 0, sizeof *sin);
442 		sin->sin_len = sizeof(struct sockaddr_in);
443 		sin->sin_family = PF_INET;
444 		sin->sin_addr.s_addr = ina.s_addr;
445 		return (0);
446 
447 	case PF_INET6:
448 		cp = strchr(str, SCOPE_DELIMITER);
449 		if (cp) {
450 			str2 = strdup(str);
451 			if (str2 == NULL)
452 				return (-1);
453 			str2[cp - str] = '\0';
454 			if (inet_pton(PF_INET6, str2, &in6a) != 1) {
455 				free(str2);
456 				return (-1);
457 			}
458 			cp++;
459 			free(str2);
460 		} else if (inet_pton(PF_INET6, str, &in6a) != 1)
461 			return (-1);
462 
463 		sin6 = (struct sockaddr_in6 *)sa;
464 		memset(sin6, 0, sizeof *sin6);
465 		sin6->sin6_len = sizeof(struct sockaddr_in6);
466 		sin6->sin6_family = PF_INET6;
467 		sin6->sin6_addr = in6a;
468 
469 		if (cp == NULL)
470 			return (0);
471 
472 		if (IN6_IS_ADDR_LINKLOCAL(&in6a) ||
473 		    IN6_IS_ADDR_MC_LINKLOCAL(&in6a) ||
474 		    IN6_IS_ADDR_MC_INTFACELOCAL(&in6a))
475 			if ((sin6->sin6_scope_id = if_nametoindex(cp)))
476 				return (0);
477 
478 		sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr);
479 		if (errstr)
480 			return (-1);
481 		return (0);
482 
483 	default:
484 		break;
485 	}
486 
487 	return (-1);
488 }
489