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, ®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 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