1 /* srp.h 2 * 3 * Copyright (c) 2018-2024 Apple Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * Service Registration Protocol common definitions 18 */ 19 20 #ifndef __SRP_H 21 #define __SRP_H 22 23 #ifdef __cplusplus 24 extern "C" { 25 #endif 26 27 #ifndef THREAD_DEVKIT_ADK 28 #include <netinet/in.h> 29 #endif 30 #include <stdint.h> 31 #include <stdbool.h> 32 #include <string.h> 33 #ifdef POSIX_BUILD 34 #include <limits.h> 35 #include <sys/param.h> 36 #endif 37 #ifdef MALLOC_DEBUG_LOGGING 38 # define MDNS_NO_STRICT 1 39 #endif 40 41 #include "srp-features.h" // for feature flags 42 43 44 #ifdef __clang__ 45 #define NULLABLE _Nullable 46 #define NONNULL _Nonnull 47 #define UNUSED __unused 48 #else 49 #define NULLABLE 50 #define NONNULL 51 #define UNUSED __attribute__((unused)) 52 #ifdef POSIX_BUILD 53 #else 54 #define SRP_CRYPTO_MBEDTLS 1 55 #endif // POSIX_BUILD 56 #endif 57 58 #define INT64_HEX_STRING_MAX 17 // Maximum size of an int64_t printed as hex, including NUL termination 59 60 #ifdef __clang__ 61 #pragma clang diagnostic push 62 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 63 #endif 64 //====================================================================================================================== 65 66 #ifdef __clang__ 67 #pragma clang diagnostic pop 68 #endif 69 70 #include "srp-log.h" // For log functions 71 72 #define SRP_OBJ_REF_COUNT_LIMIT 10000 73 74 #ifdef __clang__ 75 #define FILE_TRIM(x) (strrchr(x, '/') + 1) 76 #else 77 #define FILE_TRIM(x) (x) 78 #endif 79 80 #ifdef THREAD_DEVKIT_ADK 81 #define FINALIZED(x) 82 #define CREATED(x) 83 #else 84 #define FINALIZED(x) ((x)++) 85 #define CREATED(x) ((x)++) 86 #endif // THREAD_DEVKIT_ADK 87 88 #ifdef DEBUG_VERBOSE 89 #ifdef __clang_analyzer__ 90 #define RELEASE_BASE(x, object_type, file, line) \ 91 object_type ## _finalize(x) 92 #else 93 #define RELEASE_BASE(x, object_type, file, line) do { \ 94 if ((x) != NULL) { \ 95 if ((x)->ref_count == 0) { \ 96 FAULT("ALLOC: release after finalize at %2.2d: %p (%10s): %s:%d", \ 97 (x)->ref_count, (void *)(x), # x, FILE_TRIM(file), line); \ 98 abort(); \ 99 } else if ((x)->ref_count > SRP_OBJ_REF_COUNT_LIMIT) { \ 100 FAULT("ALLOC: release at %2.2d: %p (%10s): %s:%d", \ 101 (x)->ref_count, (void *)(x), # x, FILE_TRIM(file), line); \ 102 abort(); \ 103 } else { \ 104 INFO("ALLOC: release at %2.2d: %p (%10s): %s:%d", \ 105 (x)->ref_count, (void *)(x), # x, FILE_TRIM(file), line); \ 106 --(x)->ref_count; \ 107 if ((x)->ref_count == 0) { \ 108 INFO("ALLOC: finalize: %p (%10s): %s:%d", \ 109 (void *)(x), # x, FILE_TRIM(file), line); \ 110 FINALIZED(object_type##_finalized); \ 111 object_type##_finalize(x); \ 112 } \ 113 } \ 114 } \ 115 } while (0) 116 117 #endif // __clang_analyzer__ 118 #define RETAIN_BASE(x, object_type, file, line) do { \ 119 if ((x) != NULL) { \ 120 INFO("ALLOC: retain at %2.2d: %p (%10s): %s:%d", \ 121 (x)->ref_count, (void *)(x), # x, FILE_TRIM(file), line); \ 122 if ((x)->ref_count == 0) { \ 123 CREATED(object_type##_created); \ 124 } \ 125 ++((x)->ref_count); \ 126 if ((x)->ref_count > SRP_OBJ_REF_COUNT_LIMIT) { \ 127 FAULT("ALLOC: retain at %2.2d: %p (%10s): %s:%d", \ 128 (x)->ref_count, (void *)(x), # x, FILE_TRIM(file), line); \ 129 abort(); \ 130 } \ 131 } \ 132 } while (0) 133 #define RELEASE(x, object_type) RELEASE_BASE(x, object_type, file, line) 134 #define RETAIN(x, object_type) RETAIN_BASE(x, object_type, file, line) 135 #define RELEASE_HERE(x, object_type) RELEASE_BASE(x, object_type, __FILE__, __LINE__) 136 #define RETAIN_HERE(x, object_type) RETAIN_BASE(x, object_type, __FILE__, __LINE__) 137 #else // DEBUG_VERBOSE 138 #ifdef __clang_analyzer__ 139 #define RELEASE(x, object_type) object_type ## _finalize(x) 140 #define RELEASE_HERE(x, object_type) object_type ## _finalize(x) 141 #define RETAIN(x, object_type) 142 #define RETAIN_HERE(x, object_tyoe) 143 #else 144 #define RELEASE(x, object_type) do { \ 145 if ((x)->ref_count == 0) { \ 146 FAULT("ALLOC: release after finalize at %2.2d: %p (%10s): %s:%d", \ 147 (x)->ref_count, (void *)(x), # x, FILE_TRIM(file), line); \ 148 abort(); \ 149 } \ 150 if ((x)->ref_count > SRP_OBJ_REF_COUNT_LIMIT) { \ 151 FAULT("ALLOC: release at %2.2d: %p (%10s): %s:%d", \ 152 (x)->ref_count, (void *)(x), # x, FILE_TRIM(file), line); \ 153 abort(); \ 154 } \ 155 if (--(x)->ref_count == 0) { \ 156 FINALIZED(object_type##_finalized); \ 157 object_type ## _finalize(x); \ 158 (void)file; (void)line; \ 159 } \ 160 } while (0) 161 #define RETAIN(x, object_type) do { \ 162 (x)->ref_count++; \ 163 if (--(x)->ref_count == 0) { \ 164 CREATED(object_type##_created); \ 165 } \ 166 if ((x)->ref_count > SRP_OBJ_REF_COUNT_LIMIT) { \ 167 FAULT("ALLOC: retain at %2.2d: %p (%10s): %s:%d", \ 168 (x)->ref_count, (void *)(x), # x, FILE_TRIM(file), line); \ 169 abort(); \ 170 } \ 171 } while (0) 172 #define RELEASE_HERE(x, object_type) do { \ 173 if ((x)->ref_count == 0) { \ 174 FAULT("ALLOC: release after finalize at %2.2d: %p (%10s)", \ 175 (x)->ref_count, (void *)(x), # x); \ 176 abort(); \ 177 } \ 178 if ((x)->ref_count > SRP_OBJ_REF_COUNT_LIMIT) { \ 179 FAULT("ALLOC: release at %2.2d: %p (%10s)", \ 180 (x)->ref_count, (void *)(x), # x); \ 181 abort(); \ 182 } \ 183 if (--(x)->ref_count == 0) { \ 184 FINALIZED(object_type##_finalized); \ 185 object_type ## _finalize(x); \ 186 } \ 187 } while (0) 188 #define RETAIN_HERE(x, object_type) do { \ 189 (x)->ref_count++; \ 190 if (--(x)->ref_count == 0) { \ 191 CREATED(object_type##_created); \ 192 } \ 193 if ((x)->ref_count > SRP_OBJ_REF_COUNT_LIMIT) { \ 194 FAULT("ALLOC: retain at %2.2d: %p (%10s)", \ 195 (x)->ref_count, (void *)(x), # x); \ 196 abort(); \ 197 } \ 198 } while (0) 199 #endif 200 #endif // DEBUG_VERBOSE 201 202 #define THREAD_ENTERPRISE_NUMBER ((uint64_t)44970) 203 #define THREAD_SRP_SERVER_ANYCAST_OPTION 0x5c 204 #define THREAD_SRP_SERVER_OPTION 0x5d 205 #define THREAD_PREF_ID_OPTION 0x9d 206 207 #define IS_SRP_SERVICE(service) \ 208 ((cti_service)->enterprise_number == THREAD_ENTERPRISE_NUMBER && \ 209 (cti_service)->service_type == THREAD_SRP_SERVER_OPTION && \ 210 (cti_service)->service_version == 1 && \ 211 (cti_service)->server_length == 18) 212 #define IS_SRP_ANYCAST_SERVICE(service) \ 213 ((cti_service)->enterprise_number == THREAD_ENTERPRISE_NUMBER && \ 214 (cti_service)->service_type == THREAD_SRP_SERVER_ANYCAST_OPTION && \ 215 (cti_service)->service_version == 1 && \ 216 (cti_service)->service_length == 2) 217 #define IS_PREF_ID_SERVICE(service) \ 218 ((cti_service)->enterprise_number == THREAD_ENTERPRISE_NUMBER && \ 219 (cti_service)->service_type == THREAD_PREF_ID_OPTION && \ 220 (cti_service)->service_version == 1 && \ 221 (cti_service)->server_length == 9) 222 223 #ifdef MALLOC_DEBUG_LOGGING 224 void *debug_malloc(size_t len, const char *file, int line); 225 void *debug_calloc(size_t count, size_t len, const char *file, int line); 226 char *debug_strdup(const char *s, const char *file, int line); 227 void debug_free(void *p, const char *file, int line); 228 229 #define malloc(x) debug_malloc(x, __FILE__, __LINE__) 230 #define calloc(c, y) debug_calloc(c, y, __FILE__, __LINE__) 231 #define strdup(s) debug_strdup(s, __FILE__, __LINE__) 232 #define free(p) debug_free(p, __FILE__, __LINE__) 233 #endif 234 235 typedef struct srp_key srp_key_t; 236 237 // This function compares two IPv6 prefixes, up to the specified prefix length (in bytes). 238 // return: -1 if prefix_a < prefix_b 239 // 0 if prefix_a == prefix_b 240 // 1 if prefix_a > prefix_b. 241 static inline int 242 in6prefix_compare(const struct in6_addr *prefix_a, const struct in6_addr *prefix_b, size_t len) 243 { 244 return memcmp(prefix_a, prefix_b, len); 245 } 246 247 // This function compares two full IPv6 addresses. 248 // return: -1 if addr_a < addr_b 249 // 0 if addr_a == addr_b 250 // 1 if addr_a > addr_b. 251 static inline int 252 in6addr_compare(const struct in6_addr *addr_a, const struct in6_addr *addr_b) 253 { 254 return in6prefix_compare(addr_a, addr_b, sizeof (*addr_a)); 255 } 256 257 // This function copies the data into a, up to len bytes or sizeof(*a), whichever is less. 258 // if there are uninitialized bytes remaining in a, sets those to zero. 259 static inline void 260 in6prefix_copy_from_data(struct in6_addr *prefix, const uint8_t *data, size_t len) 261 { 262 size_t copy_len = sizeof(*prefix) < len ? sizeof(*prefix): len; 263 if (copy_len > 0) { 264 memcpy(prefix, data, copy_len); 265 } 266 if (copy_len != sizeof(*prefix)) { 267 memset((char *)prefix + copy_len, 0, sizeof(*prefix) - copy_len); 268 } 269 } 270 271 // This function copies prefix src, into prefix dst, up to len bytes. 272 static inline void 273 in6prefix_copy(struct in6_addr *dst, const struct in6_addr *src, size_t len) 274 { 275 in6prefix_copy_from_data(dst, (const uint8_t*)src, len); 276 } 277 278 // This function copies full IPv6 address src into dst. 279 static inline void 280 in6addr_copy(struct in6_addr *dst, const struct in6_addr *src) 281 { 282 memcpy(dst, src, sizeof(*dst)); 283 } 284 285 // This function zeros the full IPv6 address 286 static inline void 287 in6addr_zero(struct in6_addr *addr) 288 { 289 memset(addr, 0, sizeof(*addr)); 290 } 291 292 // Returns true if this is a Thread mesh-local anycast address. 293 extern const uint8_t thread_anycast_preamble[7]; 294 extern const uint8_t thread_rloc_preamble[6]; 295 296 static inline bool 297 is_thread_mesh_anycast_address(const struct in6_addr *addr) 298 { 299 // Thread 1.3.0 section 5.2.2.2 Anycast Locator (ALOC) 300 if (!memcmp(&addr->s6_addr[8], thread_anycast_preamble, sizeof(thread_anycast_preamble))) { 301 return true; 302 } 303 return false; 304 } 305 306 static inline bool 307 is_thread_mesh_rloc_address(const struct in6_addr *addr) 308 { 309 // Thread 1.3.0 section 5.2.2.1 Routing Locator (RLOC) 310 if (!memcmp(&addr->s6_addr[8], thread_rloc_preamble, sizeof(thread_rloc_preamble))) { 311 return true; 312 } 313 return false; 314 } 315 316 static inline bool 317 is_thread_mesh_synthetic_address(const struct in6_addr *addr) 318 { 319 return is_thread_mesh_anycast_address(addr) || is_thread_mesh_rloc_address(addr); 320 } 321 322 static inline bool 323 is_thread_mesh_synthetic_or_link_local(const struct in6_addr *addr) 324 { 325 return (is_thread_mesh_anycast_address(addr) || is_thread_mesh_rloc_address(addr) || 326 (addr->s6_addr[0] == 0xfe && (addr->s6_addr[1] & 0xc0) == 0x80)); 327 } 328 329 #ifndef _SRP_STRICT_DISPOSE_TEMPLATE 330 #define _SRP_STRICT_DISPOSE_TEMPLATE(PTR, FUNCTION) \ 331 do { \ 332 if (*(PTR) != NULL) { \ 333 FUNCTION(*PTR); \ 334 *(PTR) = NULL; \ 335 } \ 336 } while(0) 337 #endif 338 339 #ifndef DNSServiceRefSourceForget 340 #define DNSServiceRefSourceForget(PTR) _SRP_STRICT_DISPOSE_TEMPLATE(PTR, DNSServiceRefDeallocate) 341 #endif 342 343 344 /*! 345 * @brief 346 * Check the required condition, if the required condition is not met go to the label specified. 347 * 348 * @param ASSERTION 349 * The condition that must be met before continue. 350 * 351 * @param EXCEPTION_LABEL 352 * The label to go to when the required condition ASSERTION is not met. 353 * 354 * @param ACTION 355 * The extra action to take before go to the EXCEPTION_LABEL label when ASSERTION is not met. 356 * 357 * @discussion 358 * Example: 359 * require_action_quiet( 360 * foo == NULL, // required to be true 361 * exit, // if not met goto label 362 * ret = -1; ERROR("foo should not be NULL") // before exiting 363 * ) ; 364 */ 365 #ifndef require_action_quiet 366 #define require_action_quiet(ASSERTION, EXCEPTION_LABEL, ACTION) \ 367 do { \ 368 if (__builtin_expect(!(ASSERTION), 0)) \ 369 { \ 370 { \ 371 ACTION; \ 372 } \ 373 goto EXCEPTION_LABEL; \ 374 } \ 375 } while(0) 376 #endif // #ifndef require_action 377 378 #ifndef require_quiet 379 #define require_quiet(ASSERTION, EXCEPTION_LABEL) \ 380 do { \ 381 if (__builtin_expect(!(ASSERTION), 0)) \ 382 { \ 383 goto EXCEPTION_LABEL; \ 384 } \ 385 } while(0) 386 #endif // #ifndef require_action 387 388 /*! 389 * @brief 390 * Check the required condition, if the required condition is not met go to the label specified. 391 * 392 * @param ASSERTION 393 * The condition that must be met before continue. 394 * 395 * @param EXCEPTION_LABEL 396 * The label to go to when the required condition ASSERTION is not met. 397 * 398 * @param ACTION 399 * The extra action to take before go to the EXCEPTION_LABEL label when ASSERTION is not met. 400 * 401 * @discussion 402 * Example: 403 * require_action( 404 * foo == NULL, // required to be true 405 * exit, // if not met goto label 406 * ret = -1; ERROR("foo should not be NULL") // before exiting 407 * ) ; 408 */ 409 #ifndef require_action 410 #define require_action(ASSERTION, EXCEPTION_LABEL, ACTION) \ 411 do { \ 412 if (__builtin_expect(!(ASSERTION), 0)) \ 413 { \ 414 { \ 415 ACTION; \ 416 } \ 417 goto EXCEPTION_LABEL; \ 418 } \ 419 } while(0) 420 #endif // #ifndef require_action 421 422 /*! 423 * @brief 424 * Check the required condition, if the required condition is not met, do the ACTION. It is usually used as DEBUG macro. 425 * 426 * @param ASSERTION 427 * The condition that must be met before continue. 428 * 429 * @param ACTION 430 * The extra action to take when ASSERTION is not met. 431 * 432 * @discussion 433 * Example: 434 * verify_action( 435 * foo == NULL, // required to be true 436 * ERROR("foo should not be NULL") // action to take if required is false 437 * ) ; 438 */ 439 #undef verify_action 440 #define verify_action(ASSERTION, ACTION) \ 441 if (__builtin_expect(!(ASSERTION), 0)) { \ 442 ACTION; \ 443 } \ 444 else do {} while (0) 445 446 // Print true or false based on boolean value: 447 static inline const char *bool_str(bool tf) { 448 if (tf) return "true"; 449 return "false"; 450 } 451 452 453 #ifdef __cplusplus 454 } // extern "C" 455 #endif 456 457 #ifndef THREAD_DEVKIT_ADK 458 // Object type external definitions 459 #define OBJECT_TYPE(x) extern int x##_created, x##_finalized, old_##x##_created, old_##x##_finalized; 460 #include "object-types.h" 461 #endif // !THREAD_DEVKIT_ADK 462 463 #endif // __SRP_H 464 465 // Local Variables: 466 // mode: C 467 // tab-width: 4 468 // c-file-style: "bsd" 469 // c-basic-offset: 4 470 // fill-column: 108 471 // indent-tabs-mode: nil 472 // End: 473