1 /* 2 * Copyright (c) 2022 Apple Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //====================================================================================================================== 18 // MARK: - Headers 19 20 #include "dns_obj_log.h" 21 #include "dns_obj_rr_private.h" 22 #include "domain_name_labels.h" 23 #include "dns_common.h" 24 #include <string.h> // For memcmp(). 25 26 #include "dns_assert_macros.h" 27 #include "mdns_strict.h" 28 29 //====================================================================================================================== 30 // MARK: - DNS Resource Record Kind Definition 31 32 // The definition of the resource record object is in `dns_obj_rr_private.h` 33 DNS_OBJECT_DEFINE_FULL(rr); 34 35 //====================================================================================================================== 36 // MARK: - Local Prototypes 37 38 static uint8_t * 39 dns_obj_rr_create_signed_data(dns_obj_rr_t NONNULL me, size_t * NONNULL signed_data_len); 40 41 //====================================================================================================================== 42 // MARK: - DNSSEC Resource Record Public Methods 43 44 dns_obj_rr_t 45 dns_obj_rr_create(const uint8_t * const name, const uint16_t type, const uint16_t class, 46 const uint8_t * const rdata, const uint16_t rdata_len, const bool allocate_memory, dns_obj_error_t * const out_error) 47 { 48 dns_obj_error_t err; 49 dns_obj_rr_t record = NULL; 50 dns_obj_rr_t obj = NULL; 51 52 obj = _dns_obj_rr_new(); 53 require_action(obj != NULL, exit, err = DNS_OBJ_ERROR_NO_MEMORY); 54 55 dns_obj_rr_init_fields(obj, name, type, class, rdata, rdata_len, allocate_memory, NULL, &err); 56 require_noerr(err, exit); 57 58 record = obj; 59 obj = NULL; 60 err = DNS_OBJ_ERROR_NO_ERROR; 61 62 exit: 63 if (out_error != NULL) { 64 *out_error = err; 65 } 66 MDNS_DISPOSE_DNS_OBJ(obj); 67 return record; 68 } 69 70 //====================================================================================================================== 71 72 dns_obj_domain_name_t 73 dns_obj_rr_get_name(const dns_obj_rr_t me) 74 { 75 return me->name; 76 } 77 78 //====================================================================================================================== 79 80 const uint8_t * 81 dns_obj_rr_get_name_in_labels(const dns_obj_rr_t me) 82 { 83 const dns_obj_domain_name_t name = me->name; 84 return dns_obj_domain_name_get_labels(name); 85 } 86 87 //====================================================================================================================== 88 89 uint16_t 90 dns_obj_rr_get_type(const dns_obj_rr_t me) 91 { 92 return me->type; 93 } 94 95 //====================================================================================================================== 96 97 uint16_t 98 dns_obj_rr_get_class(const dns_obj_rr_t me) 99 { 100 return me->class; 101 } 102 103 //====================================================================================================================== 104 105 uint16_t 106 dns_obj_rr_get_rdata_len(const dns_obj_rr_t me) 107 { 108 return me->rdata_len; 109 } 110 111 //====================================================================================================================== 112 113 const uint8_t * 114 dns_obj_rr_get_rdata(const dns_obj_rr_t me) 115 { 116 if (!me->allocated_memory) { 117 return me->rdata_u.const_rdata; 118 } else { 119 return me->rdata_u.allocated_rdata; 120 } 121 } 122 123 //====================================================================================================================== 124 125 uint32_t 126 dns_obj_rr_get_ttl(const dns_obj_rr_t me) 127 { 128 return me->ttl; 129 } 130 131 //====================================================================================================================== 132 133 const uint8_t * 134 dns_obj_rr_get_signed_data(const dns_obj_rr_t me) 135 { 136 if (me->signed_data == NULL) { 137 size_t signed_data_len; 138 me->signed_data = dns_obj_rr_create_signed_data(me, &signed_data_len); 139 if (me->signed_data != NULL) { 140 me->signed_data_len = signed_data_len; 141 } 142 } 143 144 return me->signed_data; 145 } 146 147 //====================================================================================================================== 148 149 size_t 150 dns_obj_rr_get_signed_data_len(const dns_obj_rr_t me) 151 { 152 // Call dns_obj_rr_get_signed_data() will also set signed_data_len. 153 dns_obj_rr_get_signed_data(me); 154 return me->signed_data_len; 155 } 156 157 //====================================================================================================================== 158 159 char * 160 dns_obj_rr_copy_rdata_rfc_description(const dns_obj_rr_t me, dns_obj_error_t * const out_error) 161 { 162 if (me->copy_rdata_rfc_description_method != NULL) { 163 return me->copy_rdata_rfc_description_method(me, out_error); 164 } 165 166 // "65535 <Hex Dump>" 167 dns_obj_error_t err; 168 const size_t max_len = strlen("65535") + 1 + 2 * dns_obj_rr_get_rdata_len(me) + 1; 169 char *buffer = mdns_calloc(1, max_len); 170 require_action(buffer != NULL, exit, err = DNS_OBJ_ERROR_NO_MEMORY); 171 172 char *ptr = buffer; 173 const char * const limit = buffer + max_len; 174 ptr += snprintf(ptr, (size_t)(limit - ptr), "%u ", dns_obj_rr_get_rdata_len(me)); 175 176 const char * const end = put_hex_from_bytes(dns_obj_rr_get_rdata(me), dns_obj_rr_get_rdata_len(me), ptr, (size_t)(limit - ptr)); 177 require_action(end != ptr, exit, err = DNS_OBJ_ERROR_OVER_RUN); 178 err = DNS_OBJ_ERROR_NO_ERROR; 179 180 exit: 181 if (out_error != NULL) { 182 *out_error = err; 183 } 184 return buffer; 185 } 186 187 //====================================================================================================================== 188 189 void 190 dns_obj_rr_clear_comparison_attributes(const dns_obj_rr_t me) 191 { 192 me->original_ttl = 0; 193 me->rrsig_labels = 0; 194 me->signed_data_len = 0; 195 mdns_free(me->signed_data); 196 } 197 198 //====================================================================================================================== 199 200 void 201 dns_obj_rr_set_comparison_attributes(const dns_obj_rr_t me, const uint32_t original_ttl, const uint8_t rrsig_labels) 202 { 203 me->original_ttl = original_ttl; 204 me->rrsig_labels = rrsig_labels; 205 } 206 207 //====================================================================================================================== 208 209 void 210 dns_obj_rrs_set_comparison_attributes(dns_obj_rr_t * const rrs, const size_t rr_count, const uint32_t original_ttl, 211 const uint8_t rrsig_labels) 212 { 213 for (size_t i = 0; i < rr_count; i++) { 214 dns_obj_rr_set_comparison_attributes(rrs[i], original_ttl, rrsig_labels); 215 } 216 } 217 218 //====================================================================================================================== 219 220 void 221 dns_obj_rrs_clear_comparison_attributes(dns_obj_rr_t * const rrs, const size_t rr_count) 222 { 223 for (size_t i = 0; i < rr_count; i++) { 224 dns_obj_rr_clear_comparison_attributes(rrs[i]); 225 } 226 } 227 228 //====================================================================================================================== 229 230 bool 231 dns_obj_rr_equal_to_raw_data(const dns_obj_rr_t me, const uint8_t * const name, const uint16_t type, 232 const uint16_t class, const uint8_t * const rdata, const uint16_t rdata_len) 233 { 234 if (type != dns_obj_rr_get_type(me)) { 235 return false; 236 } 237 238 if (class != dns_obj_rr_get_class(me)) { 239 return false; 240 } 241 242 if (rdata_len != dns_obj_rr_get_rdata_len(me)) { 243 return false; 244 } 245 246 if (domain_name_labels_canonical_compare(name, dns_obj_rr_get_name_in_labels(me), true) != compare_result_equal) { 247 return false; 248 } 249 250 const bool points_to_same_memory = (rdata == dns_obj_rr_get_rdata(me)); 251 if (!points_to_same_memory && memcmp(rdata, dns_obj_rr_get_rdata(me), rdata_len) != 0) { 252 return false; 253 } 254 255 return true; 256 } 257 258 //====================================================================================================================== 259 260 bool 261 dns_obj_rrs_belong_to_one_rrset(dns_obj_rr_t * const rrs, const size_t count) 262 { 263 require_return_value(count > 0, false); 264 265 const uint16_t class = dns_obj_rr_get_class(rrs[0]); 266 const uint16_t type = dns_obj_rr_get_type(rrs[0]); 267 const dns_obj_domain_name_t name = dns_obj_rr_get_name(rrs[0]); 268 269 for (size_t i = 1; i < count; i++) { 270 const dns_obj_rr_t rr = rrs[i]; 271 if (dns_obj_rr_get_class(rr) != class) { 272 return false; 273 } 274 if (dns_obj_rr_get_type(rr) != type) { 275 return false; 276 } 277 if (!dns_obj_equal(dns_obj_rr_get_name(rr), name)) { 278 return false; 279 } 280 } 281 282 return true; 283 } 284 285 //====================================================================================================================== 286 // MARK: - DNSSEC Resource Record Private Methods 287 288 static compare_result_t 289 _dns_obj_rr_compare(const dns_obj_rr_t me, const dns_obj_rr_t other, const bool check_equality_only) 290 { 291 if (check_equality_only) { 292 if (me->class != other->class) { 293 return compare_result_notequal; 294 } 295 296 if (me->type != other->type) { 297 return compare_result_notequal; 298 } 299 300 if (me->rdata_len != other->rdata_len) { 301 return compare_result_notequal; 302 } 303 304 if (!dns_obj_equal(me->name, other->name)) { 305 return compare_result_notequal; 306 } 307 308 const uint8_t * const my_data = dns_obj_rr_get_rdata(me); 309 const uint8_t * const others_data = dns_obj_rr_get_rdata(other); 310 311 const bool rdata_equal = (memcmp(my_data, others_data, me->rdata_len) == 0); 312 313 return rdata_equal ? compare_result_equal : compare_result_notequal; 314 } else { 315 316 // <https://datatracker.ietf.org/doc/html/rfc4034#section-6.3> Canonical RR Ordering within an RRset: 317 // For the purposes of DNS security, RRs with the same owner name, class, and type are sorted by treating the 318 // RDATA portion of the canonical form of each RR as a left-justified unsigned octet sequence in which the 319 // absence of an octet sorts before a zero octet. 320 321 if (!dns_obj_equal(dns_obj_rr_get_name(me), dns_obj_rr_get_name(other))) { 322 return compare_result_unknown; 323 } 324 325 if (dns_obj_rr_get_class(me) != dns_obj_rr_get_class(other)) { 326 return compare_result_unknown; 327 } 328 329 if (dns_obj_rr_get_type(me) != dns_obj_rr_get_type(other)) { 330 return compare_result_unknown; 331 } 332 333 const uint8_t * const my_rdata = dns_obj_rr_get_rdata(me); 334 const size_t my_rdata_len = dns_obj_rr_get_rdata_len(me); 335 const uint8_t * const others_rdata = dns_obj_rr_get_rdata(other); 336 const size_t others_rdata_len = dns_obj_rr_get_rdata_len(other); 337 const size_t len_to_compare = MIN(my_rdata_len, others_rdata_len); 338 339 const int compare_result = memcmp(my_rdata, others_rdata, len_to_compare); 340 if (compare_result < 0) { 341 return compare_result_less; 342 } else if (compare_result > 0) { 343 return compare_result_greater; 344 } else { 345 // The absence of an octet sorts before a zero octet. 346 if (my_rdata_len < others_rdata_len) { 347 return compare_result_less; 348 } else if (my_rdata_len > others_rdata_len) { 349 return compare_result_greater; 350 } else { 351 return compare_result_equal; 352 } 353 } 354 } 355 } 356 357 //====================================================================================================================== 358 359 static void 360 _dns_obj_rr_finalize(const dns_obj_rr_t me) 361 { 362 MDNS_DISPOSE_DNS_OBJ(me->name); 363 if (me->allocated_memory) { 364 mdns_free(me->rdata_u.allocated_rdata); 365 } 366 mdns_free(me->signed_data); 367 } 368 369 // ===================================================================================================================== 370 // MARK: - Resource Record Project Private Functions 371 372 void 373 dns_obj_rr_init_fields(const dns_obj_rr_t me, const uint8_t * const name, const uint16_t type, 374 const uint16_t class, const uint8_t * const rdata, const uint16_t rdata_len, const bool allocate_memory, 375 const dns_obj_rr_copy_rdata_rfc_description_f copy_rdata_rfc_description_method, dns_obj_error_t * const out_error) 376 { 377 dns_obj_error_t err; 378 uint8_t *allocated_rdata = NULL; 379 dns_obj_domain_name_t name_obj = NULL; 380 381 name_obj = dns_obj_domain_name_create_with_labels(name, allocate_memory, &err); 382 require_noerr(err, exit); 383 384 me->allocated_memory = allocate_memory; 385 if (!me->allocated_memory) { 386 me->rdata_u.const_rdata = rdata; 387 } else { 388 if (rdata_len != 0) { 389 require_action(rdata != NULL, exit, err = DNS_OBJ_ERROR_PARAM_ERR); 390 allocated_rdata = mdns_malloc(rdata_len); 391 require_action(allocated_rdata != NULL, exit, err = DNS_OBJ_ERROR_NO_MEMORY); 392 memcpy(allocated_rdata, rdata, rdata_len); 393 } 394 me->rdata_u.allocated_rdata = allocated_rdata; 395 allocated_rdata = NULL; 396 } 397 398 me->name = name_obj; 399 name_obj = NULL; 400 401 me->type = type; 402 me->class = class; 403 me->rdata_len = rdata_len; 404 me->ttl = MAX_UNICAST_TTL_IN_SECONDS; 405 me->copy_rdata_rfc_description_method = copy_rdata_rfc_description_method; 406 err = DNS_OBJ_ERROR_NO_ERROR; 407 408 exit: 409 if (out_error != NULL) { 410 *out_error = err; 411 } 412 mdns_free(allocated_rdata); 413 MDNS_DISPOSE_DNS_OBJ(name_obj); 414 } 415 416 //====================================================================================================================== 417 // MARK: - Local Functions 418 419 static uint8_t * 420 dns_obj_rr_create_signed_data(const dns_obj_rr_t me, size_t * const out_signed_data_len) 421 { 422 // RR(i) = name | type | class | OrigTTL | RDATA length | RDATA 423 dns_obj_error_t err; 424 dns_obj_domain_name_t name_to_sign = NULL; 425 uint8_t *signed_data = NULL; 426 427 require_return_value(me->original_ttl != 0, NULL); 428 429 const dns_obj_domain_name_t name = dns_obj_rr_get_name(me); 430 const uint16_t type = dns_obj_rr_get_type(me); 431 const uint16_t class = dns_obj_rr_get_class(me); 432 const uint16_t rdata_len = dns_obj_rr_get_rdata_len(me); 433 const uint32_t original_ttl = me->original_ttl; 434 435 // Get the name in canonical form. 436 name_to_sign = dns_obj_domain_name_create_canonical(name, me->rrsig_labels, &err); 437 require_noerr(err, exit); 438 439 // Pre-calculate the length of the signed data. 440 size_t signed_data_len = dns_obj_domain_name_get_length(name_to_sign); 441 signed_data_len += sizeof(type); 442 signed_data_len += sizeof(class); 443 signed_data_len += sizeof(original_ttl); 444 signed_data_len += sizeof(rdata_len); 445 signed_data_len += rdata_len; 446 447 signed_data = mdns_malloc(signed_data_len); 448 require(signed_data != NULL, exit); 449 *out_signed_data_len = signed_data_len; 450 451 uint8_t *ptr = signed_data; 452 453 // RR(i) += name 454 memcpy(ptr, dns_obj_domain_name_get_labels(name_to_sign), dns_obj_domain_name_get_length(name_to_sign)); 455 ptr += dns_obj_domain_name_get_length(name_to_sign); 456 457 // RR(i) += type 458 put_uint16_to_bytes(type, &ptr); 459 460 // RR(i) += class 461 put_uint16_to_bytes(class, &ptr); 462 463 // RR(i) += OrigTTL 464 put_uint32_to_bytes(original_ttl, &ptr); 465 466 // RR(i) += RDATA length 467 put_uint16_to_bytes(rdata_len, &ptr); 468 469 // RR(i) += RDATA 470 memcpy(ptr, dns_obj_rr_get_rdata(me), rdata_len); 471 472 exit: 473 MDNS_DISPOSE_DNS_OBJ(name_to_sign); 474 return signed_data; 475 } 476