1 static bool
is_digit(char c)2 is_digit(char c)
3 {
4 	if (c >= '0' && c <= '9')
5 		return (true);
6 	return (false);
7 }
8 
9 static wdns_res
_wdns_str_to_name(const char * str,wdns_name_t * name,bool downcase)10 _wdns_str_to_name(const char *str, wdns_name_t *name, bool downcase)
11 {
12 	const char *p;
13 	size_t label_len;
14 	ssize_t slen;
15 	uint8_t c, *oclen, *data;
16 	wdns_res res;
17 
18 	res = wdns_res_parse_error;
19 
20 	p = str;
21 	slen = strlen(str);
22 
23 	if (slen == 1 && *p == '.') {
24 		name->len = 1;
25 		name->data = my_malloc(1);
26 		name->data[0] = '\0';
27 		return (wdns_res_success);
28 	}
29 
30 	name->len = 0;
31 	name->data = my_malloc(WDNS_MAXLEN_NAME);
32 
33 	data = name->data;
34 	label_len = 0;
35 	oclen = data++;
36 	name->len++;
37 
38 	for (;;) {
39 		c = *p++;
40 		label_len++;
41 
42 		if (slen == 0) {
43 			/* end of input */
44 			if (name->len == WDNS_MAXLEN_NAME) {
45 				res = wdns_res_name_overflow;
46 				goto out;
47 			}
48 			*oclen = --label_len;
49 			*data++ = '\0';
50 			name->len++;
51 			break;
52 		}
53 
54 		if (name->len >= WDNS_MAXLEN_NAME) {
55 			res = wdns_res_name_overflow;
56 			goto out;
57 		}
58 
59 		if (c >= 'A' && c <= 'Z') {
60 			/* an upper case letter; downcase it */
61 			if (downcase)
62 				c |= 0x20;
63 			*data++ = c;
64 			name->len++;
65 		} else if (c == '\\' && !is_digit(*p)) {
66 			/* an escaped character */
67 			if (slen <= 0)
68 				goto out;
69 			*data++ = *p;
70 			name->len++;
71 			p++;
72 			slen--;
73 		} else if (c == '\\' && slen >= 3) {
74 			/* an escaped octet */
75 			char d[4];
76 			char *endptr = NULL;
77 			long int val;
78 
79 			d[0] = *p++;
80 			d[1] = *p++;
81 			d[2] = *p++;
82 			d[3] = '\0';
83 			slen -= 3;
84 			if (!is_digit(d[0]) || !is_digit(d[1]) || !is_digit(d[2]))
85 				goto out;
86 			val = strtol(d, &endptr, 10);
87 			if (endptr != NULL && *endptr == '\0'
88 			    && val >= 0 && val <= 255)
89 			{
90 				uint8_t uval;
91 
92 				uval = (uint8_t) val;
93 				*data++ = uval;
94 				name->len++;
95 			} else {
96 				goto out;
97 			}
98 		} else if (c == '\\') {
99 			/* should not occur */
100 			goto out;
101 		} else if (c == '.') {
102 			/* end of label */
103 			*oclen = --label_len;
104 			if (label_len == 0)
105 				goto out;
106 			oclen = data++;
107 			if (slen > 1)
108 				name->len++;
109 			label_len = 0;
110 		} else if (c != '\0') {
111 			*data++ = c;
112 			name->len++;
113 		}
114 
115 		slen--;
116 	}
117 
118 	return (wdns_res_success);
119 
120 out:
121 	my_free(name->data);
122 	return (res);
123 }
124 
125 wdns_res
wdns_str_to_name(const char * str,wdns_name_t * name)126 wdns_str_to_name(const char *str, wdns_name_t *name)
127 {
128 	return _wdns_str_to_name(str, name, true);
129 }
130 
131 wdns_res
wdns_str_to_name_case(const char * str,wdns_name_t * name)132 wdns_str_to_name_case(const char *str, wdns_name_t *name)
133 {
134 	return _wdns_str_to_name(str, name, false);
135 }
136