1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /* $Id: naptr_35.c,v 1.14 2022/12/26 19:24:11 jmc Exp $ */
18 
19 /* Reviewed: Thu Mar 16 16:52:50 PST 2000 by bwelling */
20 
21 /* RFC2915 */
22 
23 #ifndef RDATA_GENERIC_NAPTR_35_C
24 #define RDATA_GENERIC_NAPTR_35_C
25 
26 #include <isc/regex.h>
27 
28 /*
29  * Check the wire format of the Regexp field.
30  * Don't allow embedded NUL's.
31  */
32 static inline isc_result_t
txt_valid_regex(const unsigned char * txt)33 txt_valid_regex(const unsigned char *txt) {
34 	unsigned int nsub = 0;
35 	char regex[256];
36 	char *cp;
37 	int flags = 0;
38 	int replace = 0;
39 	unsigned char c;
40 	unsigned char delim;
41 	unsigned int len;
42 	int n;
43 
44 	len = *txt++;
45 	if (len == 0U)
46 		return (ISC_R_SUCCESS);
47 
48 	delim = *txt++;
49 	len--;
50 
51 	/*
52 	 * Digits, backslash and flags can't be delimiters.
53 	 */
54 	switch (delim) {
55 	case '0': case '1': case '2': case '3': case '4':
56 	case '5': case '6': case '7': case '8': case '9':
57 	case '\\': case 'i': case 0:
58 		return (DNS_R_SYNTAX);
59 	}
60 
61 	cp = regex;
62 	while (len-- > 0) {
63 		c = *txt++;
64 		if (c == 0)
65 			return (DNS_R_SYNTAX);
66 		if (c == delim && !replace) {
67 			replace = 1;
68 			continue;
69 		} else if (c == delim && !flags) {
70 			flags = 1;
71 			continue;
72 		} else if (c == delim)
73 			return (DNS_R_SYNTAX);
74 		/*
75 		 * Flags are not escaped.
76 		 */
77 		if (flags) {
78 			switch (c) {
79 			case 'i':
80 				continue;
81 			default:
82 				return (DNS_R_SYNTAX);
83 			}
84 		}
85 		if (!replace)
86 			*cp++ = c;
87 		if (c == '\\') {
88 			if (len == 0)
89 				return (DNS_R_SYNTAX);
90 			c = *txt++;
91 			if (c == 0)
92 				return (DNS_R_SYNTAX);
93 			len--;
94 			if (replace)
95 				switch (c) {
96 				case '0': return (DNS_R_SYNTAX);
97 				case '1': if (nsub < 1) nsub = 1; break;
98 				case '2': if (nsub < 2) nsub = 2; break;
99 				case '3': if (nsub < 3) nsub = 3; break;
100 				case '4': if (nsub < 4) nsub = 4; break;
101 				case '5': if (nsub < 5) nsub = 5; break;
102 				case '6': if (nsub < 6) nsub = 6; break;
103 				case '7': if (nsub < 7) nsub = 7; break;
104 				case '8': if (nsub < 8) nsub = 8; break;
105 				case '9': if (nsub < 9) nsub = 9; break;
106 				}
107 			if (!replace)
108 				*cp++ = c;
109 		}
110 	}
111 	if (!flags)
112 		return (DNS_R_SYNTAX);
113 	*cp = '\0';
114 	n = isc_regex_validate(regex);
115 	if (n < 0 || nsub > (unsigned int)n)
116 		return (DNS_R_SYNTAX);
117 	return (ISC_R_SUCCESS);
118 }
119 
120 static inline isc_result_t
totext_naptr(ARGS_TOTEXT)121 totext_naptr(ARGS_TOTEXT) {
122 	isc_region_t region;
123 	dns_name_t name;
124 	dns_name_t prefix;
125 	int sub;
126 	char buf[sizeof("64000")];
127 	unsigned short num;
128 
129 	REQUIRE(rdata->type == dns_rdatatype_naptr);
130 	REQUIRE(rdata->length != 0);
131 
132 	dns_name_init(&name, NULL);
133 	dns_name_init(&prefix, NULL);
134 
135 	dns_rdata_toregion(rdata, &region);
136 
137 	/*
138 	 * Order.
139 	 */
140 	num = uint16_fromregion(&region);
141 	isc_region_consume(&region, 2);
142 	snprintf(buf, sizeof(buf), "%u", num);
143 	RETERR(isc_str_tobuffer(buf, target));
144 	RETERR(isc_str_tobuffer(" ", target));
145 
146 	/*
147 	 * Preference.
148 	 */
149 	num = uint16_fromregion(&region);
150 	isc_region_consume(&region, 2);
151 	snprintf(buf, sizeof(buf), "%u", num);
152 	RETERR(isc_str_tobuffer(buf, target));
153 	RETERR(isc_str_tobuffer(" ", target));
154 
155 	/*
156 	 * Flags.
157 	 */
158 	RETERR(txt_totext(&region, 1, target));
159 	RETERR(isc_str_tobuffer(" ", target));
160 
161 	/*
162 	 * Service.
163 	 */
164 	RETERR(txt_totext(&region, 1, target));
165 	RETERR(isc_str_tobuffer(" ", target));
166 
167 	/*
168 	 * Regexp.
169 	 */
170 	RETERR(txt_totext(&region, 1, target));
171 	RETERR(isc_str_tobuffer(" ", target));
172 
173 	/*
174 	 * Replacement.
175 	 */
176 	dns_name_fromregion(&name, &region);
177 	sub = name_prefix(&name, tctx->origin, &prefix);
178 	return (dns_name_totext(&prefix, sub, target));
179 }
180 
181 static inline isc_result_t
fromwire_naptr(ARGS_FROMWIRE)182 fromwire_naptr(ARGS_FROMWIRE) {
183 	dns_name_t name;
184 	isc_region_t sr;
185 	unsigned char *regex;
186 
187 	REQUIRE(type == dns_rdatatype_naptr);
188 
189 	UNUSED(type);
190 	UNUSED(rdclass);
191 
192 	dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
193 
194 	dns_name_init(&name, NULL);
195 
196 	/*
197 	 * Order, preference.
198 	 */
199 	isc_buffer_activeregion(source, &sr);
200 	if (sr.length < 4)
201 		return (ISC_R_UNEXPECTEDEND);
202 	RETERR(isc_mem_tobuffer(target, sr.base, 4));
203 	isc_buffer_forward(source, 4);
204 
205 	/*
206 	 * Flags.
207 	 */
208 	RETERR(txt_fromwire(source, target));
209 
210 	/*
211 	 * Service.
212 	 */
213 	RETERR(txt_fromwire(source, target));
214 
215 	/*
216 	 * Regexp.
217 	 */
218 	regex = isc_buffer_used(target);
219 	RETERR(txt_fromwire(source, target));
220 	RETERR(txt_valid_regex(regex));
221 
222 	/*
223 	 * Replacement.
224 	 */
225 	return (dns_name_fromwire(&name, source, dctx, options, target));
226 }
227 
228 static inline isc_result_t
towire_naptr(ARGS_TOWIRE)229 towire_naptr(ARGS_TOWIRE) {
230 	dns_name_t name;
231 	dns_offsets_t offsets;
232 	isc_region_t sr;
233 
234 	REQUIRE(rdata->type == dns_rdatatype_naptr);
235 	REQUIRE(rdata->length != 0);
236 
237 	dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
238 	/*
239 	 * Order, preference.
240 	 */
241 	dns_rdata_toregion(rdata, &sr);
242 	RETERR(isc_mem_tobuffer(target, sr.base, 4));
243 	isc_region_consume(&sr, 4);
244 
245 	/*
246 	 * Flags.
247 	 */
248 	RETERR(isc_mem_tobuffer(target, sr.base, sr.base[0] + 1));
249 	isc_region_consume(&sr, sr.base[0] + 1);
250 
251 	/*
252 	 * Service.
253 	 */
254 	RETERR(isc_mem_tobuffer(target, sr.base, sr.base[0] + 1));
255 	isc_region_consume(&sr, sr.base[0] + 1);
256 
257 	/*
258 	 * Regexp.
259 	 */
260 	RETERR(isc_mem_tobuffer(target, sr.base, sr.base[0] + 1));
261 	isc_region_consume(&sr, sr.base[0] + 1);
262 
263 	/*
264 	 * Replacement.
265 	 */
266 	dns_name_init(&name, offsets);
267 	dns_name_fromregion(&name, &sr);
268 	return (dns_name_towire(&name, cctx, target));
269 }
270 
271 #endif	/* RDATA_GENERIC_NAPTR_35_C */
272