Home | History | Annotate | Line # | Download | only in ServiceRegistration
      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