xref: /openbsd-src/usr.bin/dig/lib/dns/rdata/in_1/svcb_64.c (revision 16df6568a5dba1fc2b84166fb4d4ef90926cbed4)
1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2022 Florian Obser <florian@openbsd.org>
4  *
5  * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /* $Id: svcb_64.c,v 1.2 2024/10/31 07:40:34 otto Exp $ */
19 
20 /* draft-ietf-dnsop-svcb-https-10, based on srv_33.c */
21 
22 #ifndef RDATA_IN_1_SVCB_64_C
23 #define RDATA_IN_1_SVCB_64_C
24 
25 #define SVC_PARAM_MANDATORY	0
26 #define SVC_PARAM_ALPN		1
27 #define SVC_PARAM_NO_DEF_ALPN	2
28 #define SVC_PARAM_PORT		3
29 #define SVC_PARAM_IPV4HINT	4
30 #define SVC_PARAM_ECH		5
31 #define SVC_PARAM_IPV6HINT	6
32 #define SVC_PARAM_DOHPATH	7
33 
34 static inline const char*
35 svc_param_key_to_text(uint16_t key)
36 {
37 	static char buf[sizeof "key65535"];
38 
39 	switch (key) {
40 	case SVC_PARAM_MANDATORY:
41 		return ("mandatory");
42 	case SVC_PARAM_ALPN:
43 		return ("alpn");
44 	case SVC_PARAM_NO_DEF_ALPN:
45 		return ("no-default-alpn");
46 	case SVC_PARAM_PORT:
47 		return ("port");
48 	case SVC_PARAM_IPV4HINT:
49 		return ("ipv4hint");
50 	case SVC_PARAM_ECH:
51 		return ("ech");
52 	case SVC_PARAM_IPV6HINT:
53 		return ("ipv6hint");
54 	case SVC_PARAM_DOHPATH:
55 		return ("dohpath");
56 	default:
57 		snprintf(buf, sizeof buf, "key%u", key);
58 		return (buf);
59 	}
60 }
61 
62 static inline isc_result_t
63 totext_in_svcb_https(ARGS_TOTEXT) {
64 	isc_region_t region;
65 	dns_name_t name;
66 	dns_name_t prefix;
67 	int sub;
68 	char buf[sizeof "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255"];
69 	unsigned short num;
70 
71 	dns_name_init(&name, NULL);
72 	dns_name_init(&prefix, NULL);
73 
74 	/*
75 	 * Priority.
76 	 */
77 	dns_rdata_toregion(rdata, &region);
78 	num = uint16_fromregion(&region);
79 	isc_region_consume(&region, 2);
80 	snprintf(buf, sizeof buf, "%u", num);
81 	RETERR(isc_str_tobuffer(buf, target));
82 	RETERR(isc_str_tobuffer(" ", target));
83 
84 	/*
85 	 * Target.
86 	 */
87 	dns_name_fromregion(&name, &region);
88 	isc_region_consume(&region, name_length(&name));
89 	sub = name_prefix(&name, tctx->origin, &prefix);
90 	RETERR(dns_name_totext(&prefix, sub, target));
91 
92 	while (region.length > 0) {
93 		isc_region_t val_region;
94 		uint16_t svc_param_key, svc_param_value_len, man_key, port;
95 
96 		RETERR(isc_str_tobuffer(" ", target));
97 
98 		svc_param_key = uint16_fromregion(&region);
99 		isc_region_consume(&region, 2);
100 
101 		svc_param_value_len = uint16_fromregion(&region);
102 		isc_region_consume(&region, 2);
103 
104 		RETERR(isc_str_tobuffer(svc_param_key_to_text(svc_param_key),
105 		    target));
106 
107 		val_region = region;
108 		val_region.length = svc_param_value_len;
109 
110 		isc_region_consume(&region, svc_param_value_len);
111 
112 		switch (svc_param_key) {
113 		case SVC_PARAM_MANDATORY:
114 			INSIST(val_region.length % 2 == 0);
115 			RETERR(isc_str_tobuffer("=", target));
116 			while (val_region.length > 0) {
117 				man_key = uint16_fromregion(&val_region);
118 				isc_region_consume(&val_region, 2);
119 				RETERR(isc_str_tobuffer(svc_param_key_to_text(
120 				    man_key), target));
121 				if (val_region.length != 0)
122 					RETERR(isc_str_tobuffer(",", target));
123 			}
124 			break;
125 		case SVC_PARAM_ALPN:
126 			RETERR(isc_str_tobuffer("=\"", target));
127 			while (val_region.length > 0) {
128 				txt_totext(&val_region, 0, target);
129 				if (val_region.length != 0)
130 					RETERR(isc_str_tobuffer(",", target));
131 			}
132 			RETERR(isc_str_tobuffer("\"", target));
133 			break;
134 		case SVC_PARAM_NO_DEF_ALPN:
135 			INSIST(val_region.length == 0);
136 			break;
137 		case SVC_PARAM_PORT:
138 			INSIST(val_region.length == 2);
139 			RETERR(isc_str_tobuffer("=", target));
140 			port = uint16_fromregion(&val_region);
141 			isc_region_consume(&val_region, 2);
142 			snprintf(buf, sizeof buf, "%u", port);
143 			RETERR(isc_str_tobuffer(buf, target));
144 			break;
145 		case SVC_PARAM_IPV4HINT:
146 			INSIST(val_region.length % 4 == 0);
147 			RETERR(isc_str_tobuffer("=", target));
148 			while (val_region.length > 0) {
149 				inet_ntop(AF_INET, val_region.base, buf,
150 				    sizeof buf);
151 				RETERR(isc_str_tobuffer(buf, target));
152 				isc_region_consume(&val_region, 4);
153 				if (val_region.length != 0)
154 					RETERR(isc_str_tobuffer(",", target));
155 			}
156 			break;
157 		case SVC_PARAM_ECH:
158 			RETERR(isc_str_tobuffer("=", target));
159 			RETERR(isc_base64_totext(&val_region, 0, "", target));
160 			break;
161 		case SVC_PARAM_IPV6HINT:
162 			INSIST(val_region.length % 16 == 0);
163 			RETERR(isc_str_tobuffer("=", target));
164 			while (val_region.length > 0) {
165 				inet_ntop(AF_INET6, val_region.base, buf,
166 				    sizeof buf);
167 				RETERR(isc_str_tobuffer(buf, target));
168 				isc_region_consume(&val_region, 16);
169 				if (val_region.length != 0)
170 					RETERR(isc_str_tobuffer(",", target));
171 			}
172 			break;
173 		case SVC_PARAM_DOHPATH:
174 			RETERR(isc_str_tobuffer("=", target));
175 			RETERR(multitxt_totext(&val_region, target));
176 			break;
177 		default:
178 			RETERR(isc_str_tobuffer("=", target));
179 			RETERR(multitxt_totext(&val_region, target));
180 			break;
181 		}
182 	}
183 	return (ISC_R_SUCCESS);
184 }
185 
186 static inline isc_result_t
187 totext_in_svcb(ARGS_TOTEXT) {
188 	REQUIRE(rdata->type == dns_rdatatype_svcb);
189 	REQUIRE(rdata->rdclass == dns_rdataclass_in);
190 	REQUIRE(rdata->length != 0);
191 
192 	return (totext_in_svcb_https(rdata, tctx, target));
193 }
194 
195 static inline isc_result_t
196 fromwire_in_svcb_https(ARGS_FROMWIRE) {
197 	dns_name_t name;
198 	isc_region_t sr;
199 	unsigned int svc_param_value_len;
200 
201 	UNUSED(type);
202 	UNUSED(rdclass);
203 
204 	dns_decompress_setmethods(dctx, DNS_COMPRESS_NONE);
205 
206 	dns_name_init(&name, NULL);
207 
208 	/*
209 	 * SvcPriority.
210 	 */
211 	isc_buffer_activeregion(source, &sr);
212 	if (sr.length < 2)
213 		return (ISC_R_UNEXPECTEDEND);
214 	RETERR(isc_mem_tobuffer(target, sr.base, 2));
215 	isc_buffer_forward(source, 2);
216 
217 	/*
218 	 * TargetName.
219 	 */
220 	RETERR(dns_name_fromwire(&name, source, dctx, options, target));
221 
222 	isc_buffer_activeregion(source, &sr);
223 	while (sr.length > 0) {
224 		/*
225 		 * SvcParamKey.
226 		 */
227 		if (sr.length < 2)
228 			return (ISC_R_UNEXPECTEDEND);
229 
230 		RETERR(isc_mem_tobuffer(target, sr.base, 2));
231 		isc_region_consume(&sr, 2);
232 		isc_buffer_forward(source, 2);
233 
234 		/*
235 		 * SvcParamValue length.
236 		 */
237 		if (sr.length < 2)
238 			return (ISC_R_UNEXPECTEDEND);
239 
240 		RETERR(isc_mem_tobuffer(target, sr.base, 2));
241 		svc_param_value_len = uint16_fromregion(&sr);
242 		isc_region_consume(&sr, 2);
243 		isc_buffer_forward(source, 2);
244 
245 		if (sr.length < svc_param_value_len)
246 			return (ISC_R_UNEXPECTEDEND);
247 
248 		RETERR(isc_mem_tobuffer(target, sr.base, svc_param_value_len));
249 		isc_region_consume(&sr, svc_param_value_len);
250 		isc_buffer_forward(source, svc_param_value_len);
251 	}
252 
253 	return (ISC_R_SUCCESS);
254 }
255 
256 static inline isc_result_t
257 fromwire_in_svcb(ARGS_FROMWIRE) {
258 	REQUIRE(type == dns_rdatatype_svcb);
259 	REQUIRE(rdclass == dns_rdataclass_in);
260 	return (fromwire_in_svcb_https(rdclass, type, source, dctx, options,
261 	    target));
262 }
263 
264 static inline isc_result_t
265 towire_in_svcb_https(ARGS_TOWIRE) {
266 	dns_name_t name;
267 	dns_offsets_t offsets;
268 	isc_region_t sr;
269 
270 	dns_compress_setmethods(cctx, DNS_COMPRESS_NONE);
271 
272 	/*
273 	 * SvcPriority.
274 	 */
275 	dns_rdata_toregion(rdata, &sr);
276 	RETERR(isc_mem_tobuffer(target, sr.base, 2));
277 	isc_region_consume(&sr, 2);
278 
279 	/*
280 	 * TargetName.
281 	 */
282 	dns_name_init(&name, offsets);
283 	dns_name_fromregion(&name, &sr);
284 	RETERR(dns_name_towire(&name, cctx, target));
285 	isc_region_consume(&sr, name_length(&name));
286 
287 	/*
288 	 * SvcParams.
289 	 */
290 	return (isc_mem_tobuffer(target, sr.base, sr.length));
291 }
292 
293 static inline isc_result_t
294 towire_in_svcb(ARGS_TOWIRE) {
295 	REQUIRE(rdata->type == dns_rdatatype_svcb);
296 	REQUIRE(rdata->length != 0);
297 
298 	return (towire_in_svcb_https(rdata, cctx, target));
299 }
300 #endif	/* RDATA_IN_1_SVCB_64_C */
301