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*
svc_param_key_to_text(uint16_t key)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
totext_in_svcb_https(ARGS_TOTEXT)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, ®ion);
78 num = uint16_fromregion(®ion);
79 isc_region_consume(®ion, 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, ®ion);
88 isc_region_consume(®ion, 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(®ion);
99 isc_region_consume(®ion, 2);
100
101 svc_param_value_len = uint16_fromregion(®ion);
102 isc_region_consume(®ion, 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(®ion, 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
totext_in_svcb(ARGS_TOTEXT)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
fromwire_in_svcb_https(ARGS_FROMWIRE)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
fromwire_in_svcb(ARGS_FROMWIRE)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
towire_in_svcb_https(ARGS_TOWIRE)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
towire_in_svcb(ARGS_TOWIRE)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