VECTOR_GENERATE(u16buf,uint16_t)1 VECTOR_GENERATE(u16buf, uint16_t)
2 
3 static size_t
4 rdata_from_str_string(const uint8_t *src, ubuf *u) {
5 	const uint8_t *ptr = src;
6 	size_t u_orig_size = ubuf_size(u);
7 	bool is_quoted;
8 
9 	if (*ptr == '"') {
10 		is_quoted = true;
11 		ptr++;
12 	} else {
13 		is_quoted = false;
14 	}
15 
16 	while (*ptr) {
17 		if (is_quoted && *ptr == '"') {
18 			ptr++;
19 			return ptr-src;
20 		} else if ((!is_quoted) && isspace(*ptr)) {
21 			break;
22 		} else if (*ptr == '\\') {
23 			ptr++;
24 			if (*ptr == 0) {
25 				goto err;
26 			} else if (*ptr == '"' || *ptr == '\\') {
27 				ubuf_append(u, ptr++, 1);
28 			} else if (isdigit(*ptr)) {
29 				char dstr[4] = { 0, 0, 0, 0 };
30 				uint8_t c;
31 				uint16_t c_in;
32 				strncpy(dstr, (const char *)ptr, sizeof(dstr)-1);
33 				if (! (isdigit(dstr[1]) && isdigit(dstr[2]))) {
34 					goto err;
35 				}
36 				if (sscanf(dstr, "%hu", &c_in) == 0) {
37 					goto err;
38 				}
39 				c = (uint8_t) c_in;
40 				if (c != c_in) {
41 					goto err;
42 				}
43 				ubuf_append(u, &c, 1);
44 				ptr += 3;
45 			} else {
46 				goto err;
47 			}
48 		} else if (*ptr >= ' ' && *ptr <= '~') {
49 			ubuf_append(u, ptr++, 1);
50 		} else {
51 			goto err;
52 		}
53 	}
54 
55 	if (!is_quoted) {
56 		return ptr-src;
57 	}
58 
59 err:
60 	ubuf_clip(u, u_orig_size);
61 	return 0;
62 }
63 
64 static int
cmp_u16(const void * a,const void * b)65 cmp_u16(const void *a, const void *b) {
66 	uint16_t u1 = *(uint16_t *)a;
67 	uint16_t u2 = *(uint16_t *)b;
68 	return u1 == u2 ? 0 : u1 > u2 ? 1 : -1;
69 }
70 
71 wdns_res
_wdns_str_to_rdata_ubuf(ubuf * u,const char * str,uint16_t rrtype,uint16_t rrclass)72 _wdns_str_to_rdata_ubuf(ubuf *u, const char *str,
73 			uint16_t rrtype, uint16_t rrclass) {
74 	wdns_res res;
75 	const record_descr *descr = NULL;
76 	size_t u_orig_size = ubuf_size(u);
77 
78 	if (rrtype < record_descr_len)
79 		descr = &record_descr_array[rrtype];
80 
81 	if (rrtype >= record_descr_len ||
82 	    (descr != NULL && descr->types[0] == rdf_unknown))
83 	{
84 		/* generic encoding */
85 
86 		if (strncmp(str, "\\#", 2)) {
87 			res = wdns_res_parse_error;
88 			goto err;
89 		}
90 		str += 2;
91 		if (!isspace(*str)) {
92 			res = wdns_res_parse_error;
93 			goto err;
94 		}
95 		while (*str && isspace(*str)) {
96 			str++;
97 		}
98 
99 		const char * ptr = str;
100 		while (*ptr && !isspace(*ptr)) {
101 			if (!isdigit(*ptr)) {
102 				res = wdns_res_parse_error;
103 				goto err;
104 			}
105 			ptr++;
106 		}
107 
108 		uint16_t rdlen;
109 		if (sscanf(str, "%hu", &rdlen) == 0) {
110 			res = wdns_res_parse_error;
111 			goto err;
112 		}
113 		str = ptr;
114 
115 		size_t len = 0;
116 		while (*str) {
117 			uint8_t c;
118 
119 			if (isspace(*str)) {
120 				str++;
121 				continue;
122 			}
123 
124 			if (*(str+1) == 0) {
125 				res = wdns_res_parse_error;
126 				goto err;
127 			}
128 			if (!sscanf(str, "%02hhx", &c)) {
129 				res = wdns_res_parse_error;
130 				goto err;
131 			}
132 			ubuf_append(u, &c, 1);
133 			len++;
134 			str += 2;
135 		}
136 		if (len != rdlen) {
137 			res = wdns_res_parse_error;
138 			goto err;
139 		}
140 
141 		return (wdns_res_success);
142 	} else if (descr != NULL && !(descr->record_class == class_un ||
143 				      descr->record_class == rrclass))
144 	{
145 		return (wdns_res_success);
146 	}
147 
148 	for (const uint8_t *t = &descr->types[0]; *t != rdf_end; t++) {
149 		if (str == NULL) {
150 			break;
151 		}
152 
153 		while (isspace(*str)) {
154 			str++;
155 		}
156 
157 		if (*str == 0) {
158 			break;
159 		}
160 
161 		switch (*t) {
162 		case rdf_name:
163 		case rdf_uname: {
164 			wdns_name_t *name;
165 			char * s;
166 			const char *end = strpbrk(str, " \t\r\n");
167 
168 			if (end != NULL) {
169 				s = strndup(str, end-str);
170 			} else {
171 				s = strdup(str);
172 			}
173 			name = calloc(1, sizeof(*name));
174 
175 			res = wdns_str_to_name_case(s, name);
176 			if (res != wdns_res_success) {
177 				free(s);
178 				free(name);
179 				goto err;
180 			}
181 
182 			ubuf_append(u, name->data, name->len);
183 			str = end;
184 
185 			free(s);
186 			if(name->data) {
187 				free (name->data);
188 			}
189 			free(name);
190 
191 			break;
192 		}
193 
194 		case rdf_bytes:
195 			while (*str) {
196 				uint8_t c;
197 				if (*(str+1) == 0) {
198 					res = wdns_res_parse_error;
199 					goto err;
200 				}
201 				if (!sscanf(str, "%02hhx", &c)) {
202 					res = wdns_res_parse_error;
203 					goto err;
204 				}
205 				ubuf_append(u, &c, 1);
206 				str += 2;
207 			}
208 			break;
209 
210 		case rdf_bytes_b64: {
211 			base64_decodestate b64;
212 			char *buf;
213 			size_t str_len = strlen(str);
214 			size_t buf_len;
215 
216 			base64_init_decodestate(&b64);
217 			buf = malloc(str_len+1);
218 			buf_len = base64_decode_block((const char *) str, str_len, buf, &b64);
219 			ubuf_append(u, (uint8_t *) buf, buf_len);
220 			free(buf);
221 			str += str_len;
222 			break;
223 		}
224 
225 		case rdf_bytes_str: {
226 			size_t str_len = strlen(str);
227 
228 			if (str_len >= 3 && str[0] == '"' && str[str_len - 1] == '"') {
229 				if (rdata_from_str_string((const uint8_t *)str, u) == 0) {
230 					res = wdns_res_parse_error;
231 					goto err;
232 				}
233 				str += str_len;
234 			} else {
235 				res = wdns_res_parse_error;
236 				goto err;
237 			}
238 			break;
239 		}
240 
241 		case rdf_ipv6prefix: {
242 			uint8_t prefix_len;
243 			const char *end = strpbrk(str, " \t\r\n");
244 			const char *ptr = str;
245 
246 			if (end == NULL) {
247 				end = str + strlen(str);
248 			}
249 
250 			while (ptr < end) {
251 				if (!isdigit(*ptr++)) {
252 					res = wdns_res_parse_error;
253 					goto err;
254 				}
255 			}
256 
257 			if (sscanf(str, "%hhu", &prefix_len) == 0) {
258 				res = wdns_res_parse_error;
259 				goto err;
260 			}
261 
262 			if (prefix_len > 128) {
263 				res = wdns_res_parse_error;
264 				goto err;
265 			}
266 
267 			ubuf_append(u, &prefix_len, sizeof(prefix_len));
268 
269 			str = end;
270 			if (str) {
271 				while (isspace(*str)) {
272 					str++;
273 				}
274 			}
275 
276 			if (prefix_len > 0) {
277 				if (str == NULL || *str == 0) {
278 					res = wdns_res_parse_error;
279 					goto err;
280 				}
281 
282 				end = strpbrk(str, " \t\r\n");
283 
284 				uint8_t oclen = prefix_len / 8;
285 				if (prefix_len % 8 > 0) {
286 					oclen++;
287 				}
288 
289 				uint8_t addr[16];
290 				char * pres;
291 
292 				if (end != NULL) {
293 					pres = strndup(str, end-str);
294 				} else {
295 					pres = strdup(str);
296 				}
297 
298 				int pton_res = inet_pton(AF_INET6, pres, addr);
299 				free(pres);
300 
301 				if (pton_res == 1) {
302 					ubuf_append(u, addr, oclen);
303 				} else {
304 					res = wdns_res_parse_error;
305 					goto err;
306 				}
307 
308 				str = end;
309 			}
310 
311 			break;
312 		}
313 
314 		case rdf_salt: {
315 			const char *end = strpbrk(str, " \t\r\n");
316 			if (end == NULL) {
317 				end = str + strlen(str);
318 			}
319 
320 			if (*str == '-' && (end-str) == 1) {
321 				uint8_t c = 0;
322 				ubuf_append(u, &c, 1);
323 				str++;
324 			} else {
325 				if (end-str > (2*UINT8_MAX) || (end-str) % 2 == 1) {
326 					res = wdns_res_parse_error;
327 					goto err;
328 				}
329 				uint8_t oclen = (uint8_t)(end-str)/2;
330 				ubuf_append(u, &oclen, 1);
331 
332 				while (oclen > 0) {
333 					uint8_t c;
334 					if (!sscanf(str, "%02hhx", &c)) {
335 						res = wdns_res_parse_error;
336 						goto err;
337 					}
338 					ubuf_append(u, &c, 1);
339 					str += 2;
340 					oclen--;
341 				}
342 			}
343 			break;
344 		}
345 
346 		case rdf_hash: {
347 			char *buf;
348 			size_t buf_len;
349 			const char *end = strpbrk(str, " \t\r\n");
350 
351 			if (end == NULL) {
352 				end = str + strlen(str);
353 			}
354 
355 			size_t str_len = end-str;
356 
357 			/*
358 			 * The hashed owner name is presented as one base32 digit.
359 			 * A single byte would be two base32 digits, therefore we
360 			 * we can conclude the original data was zero bytes long.
361 			 */
362 			if (str_len == 1) {
363 				uint8_t c = 0;
364 				ubuf_append(u, &c, 1);
365 				str++;
366 				break;
367 			}
368 
369 			buf = malloc(str_len);
370 			buf_len = base32_decode(buf, str_len, str, str_len);
371 
372 			uint8_t oclen = (uint8_t)buf_len;
373 			if (oclen != buf_len) {
374 				free(buf);
375 				res = wdns_res_parse_error;
376 				goto err;
377 			}
378 			ubuf_append(u, &oclen, 1);
379 			ubuf_append(u, (uint8_t *) buf, oclen);
380 			free(buf);
381 			str = end;
382 			break;
383 		}
384 
385 		case rdf_int8: {
386 			uint64_t s_val;
387 			uint8_t val;
388 			const char *ptr = str;
389 			const char *end = strpbrk(str, " \t\r\n");
390 
391 			if (end == NULL) {
392 				end = str + strlen(str);
393 			}
394 
395 			while (ptr < end) {
396 				if (!isdigit(*ptr++)) {
397 					res = wdns_res_parse_error;
398 					goto err;
399 				}
400 			}
401 
402 			if (sscanf(str, "%" PRIu64, &s_val)) {
403 				val = (uint8_t)s_val;
404 				if (val != s_val) {
405 					res = wdns_res_parse_error;
406 					goto err;
407 				}
408 				ubuf_append(u, &val, sizeof(val));
409 			}
410 			str = end;
411 			break;
412 		}
413 
414 		case rdf_int16: {
415 			uint64_t s_val;
416 			uint16_t val;
417 			const char *ptr = str;
418 			const char *end = strpbrk(str, " \t\r\n");
419 
420 			if (end == NULL) {
421 				end = str + strlen(str);
422 			}
423 
424 			while (ptr < end) {
425 				if (!isdigit(*ptr++)) {
426 					res = wdns_res_parse_error;
427 					goto err;
428 				}
429 			}
430 
431 			if (sscanf(str, "%" PRIu64, &s_val)) {
432 				val = (uint16_t)s_val;
433 				if (val != s_val) {
434 					res = wdns_res_parse_error;
435 					goto err;
436 				}
437 				val = htons(val);
438 				ubuf_append(u, (uint8_t*)&val, sizeof(val));
439 			}
440 			str = end;
441 			break;
442 		}
443 
444 		case rdf_int32: {
445 			uint64_t s_val;
446 			uint32_t val;
447 			const char *ptr = str;
448 			const char *end = strpbrk(str, " \t\r\n");
449 
450 			if (end == NULL) {
451 				end = str + strlen(str);
452 			}
453 
454 			while (ptr < end) {
455 				if (!isdigit(*ptr++)) {
456 					res = wdns_res_parse_error;
457 					goto err;
458 				}
459 			}
460 
461 			if (sscanf(str, "%" PRIu64, &s_val)) {
462 				val = (uint32_t)s_val;
463 				if (val != s_val) {
464 					res = wdns_res_parse_error;
465 					goto err;
466 				}
467 				val = htonl(val);
468 				ubuf_append(u, (uint8_t*)&val, sizeof(val));
469 			}
470 			str = end;
471 			break;
472 		}
473 
474 		case rdf_ipv4: {
475 			uint8_t addr[4];
476 			char * pres;
477 			int pton_res;
478 			const char *end = strpbrk(str, " \t\r\n");
479 
480 			if (end == NULL) {
481 				end = str + strlen(str);
482 			}
483 
484 			pres = strdup(str);
485 			pres[end-str] = 0;
486 			pton_res = inet_pton(AF_INET, pres, addr);
487 			free(pres);
488 
489 			if (pton_res == 1) {
490 				ubuf_append(u, addr, sizeof(addr));
491 			} else {
492 				res = wdns_res_parse_error;
493 				goto err;
494 			}
495 
496 			str = end;
497 			break;
498 		}
499 
500 		case rdf_ipv6: {
501 			uint8_t addr[16];
502 			char * pres;
503 			int pton_res;
504 			const char *end = strpbrk(str, " \t\r\n");
505 
506 			if (end != NULL) {
507 				pres = strndup(str, end-str);
508 			} else {
509 				pres = strdup(str);
510 			}
511 			pton_res = inet_pton(AF_INET6, pres, addr);
512 			free(pres);
513 
514 			if (pton_res == 1) {
515 				ubuf_append(u, addr, sizeof(addr));
516 			} else {
517 				res = wdns_res_parse_error;
518 				goto err;
519 			}
520 
521 			str = end;
522 			break;
523 		}
524 
525 		case rdf_eui48: {
526 			uint8_t a[6] = {0};
527 			int ret;
528 
529 			if (strlen(str) != strlen("01-02-03-04-05-06")) {
530 				res = wdns_res_parse_error;
531 				goto err;
532 			}
533 			for (int i = 0; i < 6; i++) {
534 				if (!isxdigit(str[3*i]) ||
535 				    !isxdigit(str[3*i + 1]) ||
536 				    (i < 5 && str[3*i + 2] != '-'))
537 				{
538 					res = wdns_res_parse_error;
539 					goto err;
540 				}
541 			}
542 			ret = sscanf(str, "%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx",
543 			             &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]);
544 			if (ret != 6) {
545 				res = wdns_res_parse_error;
546 				goto err;
547 			}
548 			ubuf_append(u, a, 6);
549 			str += strlen(str);
550 			break;
551 		}
552 
553 		case rdf_eui64: {
554 			uint8_t a[8] = {0};
555 			int ret;
556 
557 			if (strlen(str) != strlen("01-02-03-04-05-06-07-08")) {
558 				res = wdns_res_parse_error;
559 				goto err;
560 			}
561 			for (int i = 0; i < 8; i++) {
562 				if (!isxdigit(str[3*i]) ||
563 				    !isxdigit(str[3*i + 1]) ||
564 				    (i < 7 && str[3*i + 2] != '-'))
565 					{
566 						res = wdns_res_parse_error;
567 						goto err;
568 					}
569 			}
570 			ret = sscanf(str, "%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx",
571 			             &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7]);
572 			if (ret != 8) {
573 				res = wdns_res_parse_error;
574 				goto err;
575 			}
576 			ubuf_append(u, a, 8);
577 			str += strlen(str);
578 			break;
579 		}
580 
581 		case rdf_string: {
582 			const char * end = str;
583 			size_t u_oclen_offset;
584 			size_t str_len;
585 			uint8_t oclen = 0;
586 
587 			u_oclen_offset = ubuf_size(u);
588 			ubuf_append(u, &oclen, sizeof(oclen));
589 
590 			end += rdata_from_str_string((const uint8_t*)str, u);
591 			if (end == str) {
592 				res = wdns_res_parse_error;
593 				goto err;
594 			}
595 			str_len = ubuf_size(u) - u_oclen_offset - 1;
596 
597 			oclen = (uint8_t)str_len;
598 			if (oclen != str_len) {
599 				res = wdns_res_parse_error;
600 				goto err;
601 			}
602 			ubuf_data(u)[u_oclen_offset] = oclen;
603 
604 			str = end;
605 			break;
606 		}
607 
608 		case rdf_repstring: {
609 			const char * end;
610 			size_t u_oclen_offset;
611 			size_t str_len;
612 			uint8_t oclen = 0;
613 
614 			while (*str) {
615 				if (isspace(*str)) {
616 					str++;
617 					continue;
618 				}
619 
620 				end = str;
621 				oclen = 0;
622 
623 				u_oclen_offset = ubuf_size(u);
624 				ubuf_append(u, &oclen, sizeof(oclen));
625 
626 				end += rdata_from_str_string((const uint8_t*)str, u);
627 				if (end == str) {
628 					res = wdns_res_parse_error;
629 					goto err;
630 				}
631 				str_len = ubuf_size(u) - u_oclen_offset - 1;
632 
633 				oclen = (uint8_t)str_len;
634 				if (oclen != str_len) {
635 					res = wdns_res_parse_error;
636 					goto err;
637 				}
638 				ubuf_data(u)[u_oclen_offset] = oclen;
639 
640 				str = end;
641 			}
642 
643 			break;
644 		}
645 
646 		case rdf_rrtype: {
647 			char * s_rrtype;
648 			uint16_t my_rrtype;
649 			const char *end = strpbrk(str, " \t\r\n");
650 
651 			if (end != NULL) {
652 				s_rrtype = strndup(str, end-str);
653 			} else {
654 				s_rrtype = strdup(str);
655 			}
656 			my_rrtype = htons(wdns_str_to_rrtype(s_rrtype));
657 			free(s_rrtype);
658 
659 			if (my_rrtype > 0) {
660 				ubuf_append(u, (const uint8_t*)&my_rrtype, sizeof(my_rrtype));
661 			} else {
662 				res = wdns_res_parse_error;
663 				goto err;
664 			}
665 
666 			str = end;
667 			break;
668 		}
669 
670 		case rdf_type_bitmap: {
671 			const char *end;
672 			char *s_rrtype;
673 			u16buf *rrtypes;
674 			uint16_t my_rrtype, last_rrtype;
675 			size_t n;
676 			uint8_t window_block, bitmap_len;
677 			uint8_t bitmap[32];
678 
679 			rrtypes = u16buf_init(16);
680 			if (! rrtypes) {
681 				res = wdns_res_malloc;
682 				goto err;
683 			}
684 
685 			while (str != NULL && *str) {
686 				if (isspace(*str)) {
687 					str++;
688 					continue;
689 				}
690 
691 				end = strpbrk(str, " \t\r\n");
692 				if (end != NULL) {
693 					s_rrtype = strndup(str, end-str);
694 				} else {
695 					s_rrtype = strdup(str);
696 				}
697 
698 				my_rrtype = wdns_str_to_rrtype(s_rrtype);
699 				free(s_rrtype);
700 
701 				if (my_rrtype == 0 || (rrtype >= 128 && rrtype < 256) || rrtype == 65535) {
702 					u16buf_destroy(&rrtypes);
703 					res = wdns_res_parse_error;
704 					goto err;
705 				}
706 
707 				u16buf_add(rrtypes, my_rrtype);
708 				str = end;
709 			}
710 			qsort(u16buf_data(rrtypes), u16buf_size(rrtypes), sizeof(uint16_t), cmp_u16);
711 
712 			memset(bitmap, 0, sizeof(bitmap));
713 			window_block = 0;
714 			bitmap_len = 0;
715 			last_rrtype = 0;
716 
717 			for (n = 0; n < u16buf_size(rrtypes); n++) {
718 				my_rrtype = u16buf_value(rrtypes, n);
719 				if (my_rrtype == last_rrtype) {
720 					continue;
721 				}
722 				last_rrtype = my_rrtype;
723 
724 				uint8_t cur_window = my_rrtype / 256;
725 
726 				if (cur_window != window_block) {
727 					ubuf_append(u, (const uint8_t*)&window_block, sizeof(window_block));
728 					ubuf_append(u, (const uint8_t*)&bitmap_len, sizeof(bitmap_len));
729 					ubuf_append(u, (const uint8_t*)bitmap, bitmap_len);
730 					memset(bitmap, 0, sizeof(bitmap));
731 					window_block = cur_window;
732 				}
733 
734 				uint8_t offset = my_rrtype % 256;
735 				uint8_t byte = offset / 8;
736 				uint8_t bit = offset % 8;
737 
738 				bitmap[byte] |= 0x80 >> bit;
739 				bitmap_len = 1 + byte;
740 			}
741 			ubuf_append(u, (const uint8_t*)&window_block, sizeof(window_block));
742 			ubuf_append(u, (const uint8_t*)&bitmap_len, sizeof(bitmap_len));
743 			ubuf_append(u, (const uint8_t*)bitmap, bitmap_len);
744 
745 			u16buf_destroy(&rrtypes);
746 			break;
747 		}
748 		default: {
749 			res = wdns_res_failure;
750 			goto err;
751 		}
752 		} /* switch */
753 	}
754 
755 	return wdns_res_success;
756 
757 err:
758 	ubuf_clip(u, u_orig_size);
759 	return res;
760 }
761