Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1  1.1  christos /* srp-mdns-proxy.c
      2  1.1  christos  *
      3  1.1  christos  * Copyright (c) 2019-2024 Apple Inc. All rights reserved.
      4  1.1  christos  *
      5  1.1  christos  * Licensed under the Apache License, Version 2.0 (the "License");
      6  1.1  christos  * you may not use this file except in compliance with the License.
      7  1.1  christos  * You may obtain a copy of the License at
      8  1.1  christos  *
      9  1.1  christos  *     https://www.apache.org/licenses/LICENSE-2.0
     10  1.1  christos  *
     11  1.1  christos  * Unless required by applicable law or agreed to in writing, software
     12  1.1  christos  * distributed under the License is distributed on an "AS IS" BASIS,
     13  1.1  christos  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  1.1  christos  * See the License for the specific language governing permissions and
     15  1.1  christos  * limitations under the License.
     16  1.1  christos  *
     17  1.1  christos  * This file contains the SRP Advertising Proxy, which is an SRP Server
     18  1.1  christos  * that offers registered addresses using mDNS.
     19  1.1  christos  */
     20  1.1  christos 
     21  1.1  christos #include <stdlib.h>
     22  1.1  christos #include <string.h>
     23  1.1  christos #include <stdio.h>
     24  1.1  christos #include <time.h>
     25  1.1  christos #include <unistd.h>
     26  1.1  christos #include <pwd.h>
     27  1.1  christos #include <errno.h>
     28  1.1  christos #include <sys/socket.h>
     29  1.1  christos #include <netinet/in.h>
     30  1.1  christos #include <arpa/inet.h>
     31  1.1  christos #include <fcntl.h>
     32  1.1  christos #include <time.h>
     33  1.1  christos #include <dns_sd.h>
     34  1.1  christos #include <net/if.h>
     35  1.1  christos #include <inttypes.h>
     36  1.1  christos #include <sys/resource.h>
     37  1.1  christos #include <ctype.h>
     38  1.1  christos #include <mdns/pf.h>
     39  1.1  christos 
     40  1.1  christos #include "srp.h"
     41  1.1  christos #include "dns-msg.h"
     42  1.1  christos #include "srp-crypto.h"
     43  1.1  christos #include "ioloop.h"
     44  1.1  christos #include "srp-gw.h"
     45  1.1  christos #include "srp-proxy.h"
     46  1.1  christos #include "srp-mdns-proxy.h"
     47  1.1  christos #include "dnssd-proxy.h"
     48  1.1  christos #include "config-parse.h"
     49  1.1  christos #include "cti-services.h"
     50  1.1  christos #include "route.h"
     51  1.1  christos #include "adv-ctl-server.h"
     52  1.1  christos #include "srp-replication.h"
     53  1.1  christos #include "ioloop-common.h"
     54  1.1  christos #include "thread-device.h"
     55  1.1  christos #include "nat64-macos.h"
     56  1.1  christos #include "srp-dnssd.h"
     57  1.1  christos #include "ifpermit.h"
     58  1.1  christos #include "state-machine.h"
     59  1.1  christos #include "thread-service.h"
     60  1.1  christos #include "omr-watcher.h"
     61  1.1  christos #include "omr-publisher.h"
     62  1.1  christos #include "service-publisher.h"
     63  1.1  christos 
     64  1.1  christos 
     65  1.1  christos #if SRP_FEATURE_NAT64
     66  1.1  christos #include "nat64.h"
     67  1.1  christos #endif
     68  1.1  christos 
     69  1.1  christos 
     70  1.1  christos #ifdef SRP_TEST_SERVER
     71  1.1  christos #include "srp-test-runner.h"
     72  1.1  christos #endif
     73  1.1  christos 
     74  1.1  christos #define ADDRESS_RECORD_TTL  4500
     75  1.1  christos #define OTHER_RECORD_TTL    4500
     76  1.1  christos 
     77  1.1  christos #define _DNSSD_API_AVAILABLE_FALL_2024 (1)
     78  1.1  christos 
     79  1.1  christos static const char local_suffix_ld[] = ".local";
     80  1.1  christos static const char *local_suffix = &local_suffix_ld[1];
     81  1.1  christos 
     82  1.1  christos os_log_t global_os_log;
     83  1.1  christos void *dns_service_op_not_to_be_freed;
     84  1.1  christos srp_server_t *srp_servers;
     85  1.1  christos const uint8_t thread_anycast_preamble[7] = { 0, 0, 0, 0xff, 0xfe, 0, 0xfc };
     86  1.1  christos const uint8_t thread_rloc_preamble[6] = { 0, 0, 0, 0xff, 0xfe, 0 };
     87  1.1  christos 
     88  1.1  christos extern int num_push_sessions;
     89  1.1  christos extern int dp_num_outstanding_queries;
     90  1.1  christos extern int num_push_sessions_dropped_for_load;
     91  1.1  christos extern int num_queries_dropped_for_load;
     92  1.1  christos 
     93  1.1  christos //======================================================================================================================
     94  1.1  christos // MARK: - Forward references
     95  1.1  christos 
     96  1.1  christos static bool register_host_record(adv_host_t *host, adv_record_t *record, const bool skipping);
     97  1.1  christos static void register_host_record_completion(DNSServiceRef sdref, DNSRecordRef rref,
     98  1.1  christos                                             DNSServiceFlags flags, DNSServiceErrorType error_code, void *context);
     99  1.1  christos static bool register_instance(adv_instance_t *instance);
    100  1.1  christos static void register_instance_completion(DNSServiceRef sdref, DNSServiceFlags flags, DNSServiceErrorType error_code,
    101  1.1  christos                                          const char *name, const char *regtype, const char *domain, void *context);
    102  1.1  christos static void update_from_host(adv_host_t *host);
    103  1.1  christos static void start_host_update(adv_host_t *host);
    104  1.1  christos static void prepare_update(adv_host_t *host, client_update_t *client_update);
    105  1.1  christos static void delete_host(void *context);
    106  1.1  christos static void lease_callback(void *context);
    107  1.1  christos static void adv_host_finalize(adv_host_t *host);
    108  1.1  christos static void adv_record_finalize(adv_record_t *record);
    109  1.1  christos static void adv_update_finalize(adv_update_t *update);
    110  1.1  christos 
    111  1.1  christos //======================================================================================================================
    112  1.1  christos // MARK: - Functions
    113  1.1  christos 
    114  1.1  christos void
    115  1.1  christos srp_mdns_shared_record_remove(srp_server_t *server_state, adv_record_t *record)
    116  1.1  christos {
    117  1.1  christos     RETAIN_HERE(record, adv_record);
    118  1.1  christos     if (record->rref != NULL) {
    119  1.1  christos         if (record->shared_txn != 0 && record->shared_txn == (intptr_t)server_state->shared_registration_txn) {
    120  1.1  christos             INFO("removing rref %p", record->rref);
    121  1.1  christos             int err = dns_service_remove_record(server_state,
    122  1.1  christos                                                 server_state->shared_registration_txn->sdref, record->rref, 0);
    123  1.1  christos             // We can't release the record here if we got an error removing it, because if we get an error removing it,
    124  1.1  christos             // it doesn't get removed from the list. This should never happen, but if it does, the record will leak.
    125  1.1  christos             if (err == kDNSServiceErr_NoError) {
    126  1.1  christos                 RELEASE_HERE(record, adv_record); // Release the DNSService callback's reference
    127  1.1  christos             } else {
    128  1.1  christos                 // At this point we should never see an error calling DNSServiceRemoveRecord, so if we do, call
    129  1.1  christos                 // attention to it.
    130  1.1  christos                 if (!record->update_pending) {
    131  1.1  christos                     FAULT("DNSServiceRemoveRecord(%p, %p, %p, 0) returned %d",
    132  1.1  christos                           server_state->shared_registration_txn->sdref, record, record->rref, err);
    133  1.1  christos                 }
    134  1.1  christos             }
    135  1.1  christos         } else {
    136  1.1  christos             INFO("didn't remove stale rref %p because %lx != %p",
    137  1.1  christos                  record->rref, record->shared_txn, server_state->shared_registration_txn);
    138  1.1  christos         }
    139  1.1  christos         record->rref = NULL;
    140  1.1  christos     }
    141  1.1  christos     record->shared_txn = 0;
    142  1.1  christos     RELEASE_HERE(record, adv_record);
    143  1.1  christos }
    144  1.1  christos 
    145  1.1  christos 
    146  1.1  christos static void
    147  1.1  christos adv_record_finalize(adv_record_t *record)
    148  1.1  christos {
    149  1.1  christos     // We should not be able to get to the finalize function without having removed the rref, because the DNSService
    150  1.1  christos     // callback always holds a reference to the record.
    151  1.1  christos     if (record->update != NULL) {
    152  1.1  christos         RELEASE_HERE(record->update, adv_update);
    153  1.1  christos     }
    154  1.1  christos     if (record->host != NULL) {
    155  1.1  christos         RELEASE_HERE(record->host, adv_host);
    156  1.1  christos     }
    157  1.1  christos 
    158  1.1  christos     free(record->rdata);
    159  1.1  christos     free(record);
    160  1.1  christos }
    161  1.1  christos 
    162  1.1  christos static void
    163  1.1  christos adv_instance_finalize(adv_instance_t *instance)
    164  1.1  christos {
    165  1.1  christos     if (instance->txn != NULL) {
    166  1.1  christos         ioloop_dnssd_txn_cancel_srp(instance->host->server_state, instance->txn);
    167  1.1  christos         ioloop_dnssd_txn_release(instance->txn);
    168  1.1  christos     }
    169  1.1  christos     if (instance->txt_data != NULL) {
    170  1.1  christos         free(instance->txt_data);
    171  1.1  christos     }
    172  1.1  christos     if (instance->instance_name != NULL) {
    173  1.1  christos         free(instance->instance_name);
    174  1.1  christos     }
    175  1.1  christos     if (instance->service_type != NULL) {
    176  1.1  christos         free(instance->service_type);
    177  1.1  christos     }
    178  1.1  christos     if (instance->host != NULL) {
    179  1.1  christos         RELEASE_HERE(instance->host, adv_host);
    180  1.1  christos         instance->host = NULL;
    181  1.1  christos     }
    182  1.1  christos     if (instance->message != NULL) {
    183  1.1  christos         ioloop_message_release(instance->message);
    184  1.1  christos         instance->message = NULL;
    185  1.1  christos     }
    186  1.1  christos     if (instance->update != NULL) {
    187  1.1  christos         RELEASE_HERE(instance->update, adv_update);
    188  1.1  christos         instance->update = NULL;
    189  1.1  christos     }
    190  1.1  christos     if (instance->retry_wakeup != NULL) {
    191  1.1  christos         ioloop_wakeup_release(instance->retry_wakeup);
    192  1.1  christos         instance->retry_wakeup = NULL;
    193  1.1  christos     }
    194  1.1  christos     free(instance);
    195  1.1  christos }
    196  1.1  christos 
    197  1.1  christos void
    198  1.1  christos adv_instance_context_release(void *NONNULL context)
    199  1.1  christos {
    200  1.1  christos     adv_instance_t *instance = context;
    201  1.1  christos     RELEASE_HERE(instance, adv_instance);
    202  1.1  christos }
    203  1.1  christos 
    204  1.1  christos void
    205  1.1  christos adv_instance_retain_(adv_instance_t *instance, const char *file, int line)
    206  1.1  christos {
    207  1.1  christos     RETAIN(instance, adv_instance);
    208  1.1  christos }
    209  1.1  christos 
    210  1.1  christos void
    211  1.1  christos adv_instance_release_(adv_instance_t *instance, const char *file, int line)
    212  1.1  christos {
    213  1.1  christos     RELEASE(instance, adv_instance);
    214  1.1  christos }
    215  1.1  christos 
    216  1.1  christos void
    217  1.1  christos adv_record_retain_(adv_record_t *record, const char *file, int line)
    218  1.1  christos {
    219  1.1  christos     RETAIN(record, adv_record);
    220  1.1  christos }
    221  1.1  christos 
    222  1.1  christos void
    223  1.1  christos adv_record_release_(adv_record_t *record, const char *file, int line)
    224  1.1  christos {
    225  1.1  christos     RELEASE(record, adv_record);
    226  1.1  christos }
    227  1.1  christos 
    228  1.1  christos #define DECLARE_VEC_CREATE(type)                        \
    229  1.1  christos static type ## _vec_t *                                 \
    230  1.1  christos type ## _vec_create(int size)                           \
    231  1.1  christos {                                                       \
    232  1.1  christos     type ## _vec_t *vec;                                \
    233  1.1  christos                                                         \
    234  1.1  christos     vec = calloc(1, sizeof(*vec));                      \
    235  1.1  christos     if (vec != NULL) {                                  \
    236  1.1  christos         if (size == 0) {                                \
    237  1.1  christos             size = 1;                                   \
    238  1.1  christos         }                                               \
    239  1.1  christos         vec->vec = calloc(size, sizeof (*(vec->vec)));  \
    240  1.1  christos         if (vec->vec == NULL) {                         \
    241  1.1  christos             free(vec);                                  \
    242  1.1  christos             vec = NULL;                                 \
    243  1.1  christos         } else {                                        \
    244  1.1  christos             RETAIN_HERE(vec, type##_vec);               \
    245  1.1  christos         }                                               \
    246  1.1  christos     }                                                   \
    247  1.1  christos     return vec;                                         \
    248  1.1  christos }
    249  1.1  christos 
    250  1.1  christos #define DECLARE_VEC_COPY(type)                          \
    251  1.1  christos static type ## _vec_t *                                 \
    252  1.1  christos type ## _vec_copy(type ## _vec_t *vec)                  \
    253  1.1  christos {                                                       \
    254  1.1  christos     type ## _vec_t *new_vec;                            \
    255  1.1  christos     int i;                                              \
    256  1.1  christos                                                         \
    257  1.1  christos     new_vec = type ## _vec_create(vec->num);            \
    258  1.1  christos     if (new_vec != NULL) {                              \
    259  1.1  christos         for (i = 0; i < vec->num; i++) {                \
    260  1.1  christos             if (vec->vec[i] != NULL) {                  \
    261  1.1  christos                 new_vec->vec[i] = vec->vec[i];          \
    262  1.1  christos                 RETAIN_HERE(new_vec->vec[i], type);     \
    263  1.1  christos             }                                           \
    264  1.1  christos         }                                               \
    265  1.1  christos         new_vec->num = vec->num;                        \
    266  1.1  christos     }                                                   \
    267  1.1  christos     return new_vec;                                     \
    268  1.1  christos }
    269  1.1  christos 
    270  1.1  christos #define DECLARE_VEC_FINALIZE(type)                              \
    271  1.1  christos static void                                                     \
    272  1.1  christos type ## _vec_finalize(type ## _vec_t *vec)                      \
    273  1.1  christos {                                                               \
    274  1.1  christos     int i;                                                      \
    275  1.1  christos                                                                 \
    276  1.1  christos     for (i = 0; i < vec->num; i++) {                            \
    277  1.1  christos         if (vec->vec[i] != NULL) {                              \
    278  1.1  christos             RELEASE_HERE(vec->vec[i], type);                    \
    279  1.1  christos             vec->vec[i] = NULL;                                 \
    280  1.1  christos         }                                                       \
    281  1.1  christos     }                                                           \
    282  1.1  christos     free(vec->vec);                                             \
    283  1.1  christos     free(vec);                                                  \
    284  1.1  christos }
    285  1.1  christos 
    286  1.1  christos DECLARE_VEC_CREATE(adv_instance);
    287  1.1  christos DECLARE_VEC_COPY(adv_instance);
    288  1.1  christos DECLARE_VEC_FINALIZE(adv_instance);
    289  1.1  christos 
    290  1.1  christos DECLARE_VEC_CREATE(adv_record);
    291  1.1  christos DECLARE_VEC_COPY(adv_record);
    292  1.1  christos DECLARE_VEC_FINALIZE(adv_record);
    293  1.1  christos 
    294  1.1  christos static void
    295  1.1  christos srp_dump_server_stats(srp_server_t *server_state, bool full, bool periodic)
    296  1.1  christos {
    297  1.1  christos     // For testing, emit a count of how many hosts, services and address records there are
    298  1.1  christos     int host_count = 0;
    299  1.1  christos     int a_record_count = 0;
    300  1.1  christos     int aaaa_record_count = 0;
    301  1.1  christos     int instance_count = 0;
    302  1.1  christos     int matter_host_count = 0;
    303  1.1  christos     int hap_host_count = 0;
    304  1.1  christos     int64_t now = ioloop_timenow();
    305  1.1  christos     static int last_num_push_sessions;
    306  1.1  christos     static int last_dp_num_outstanding_queries;
    307  1.1  christos     static int last_num_push_sessions_dropped_for_load;
    308  1.1  christos     static int last_num_queries_dropped_for_load;
    309  1.1  christos 
    310  1.1  christos     for (adv_host_t *hp = server_state->hosts; hp != NULL; hp = hp->next) {
    311  1.1  christos         if (hp->removed) {
    312  1.1  christos             continue;
    313  1.1  christos         }
    314  1.1  christos         host_count++;
    315  1.1  christos         int expiry;
    316  1.1  christos         if (hp->lease_expiry < now) {
    317  1.1  christos             expiry = -1;
    318  1.1  christos         } else {
    319  1.1  christos             expiry = (int)((hp->lease_expiry - now) / 1000); // This should never be >MAXINT
    320  1.1  christos         }
    321  1.1  christos         if (full) {
    322  1.1  christos             INFO("host " PRI_S_SRP " key_id %xu stable %" PRIx64 " lease %d key_lease %d expiry %d" PUB_S_SRP PUB_S_SRP,
    323  1.1  christos                  hp->name, hp->key_id, hp->server_stable_id, hp->lease_interval, hp->key_lease, expiry,
    324  1.1  christos                  hp->removed ? " removed" : "", hp->update_pending ? " update-pending" : "");
    325  1.1  christos         }
    326  1.1  christos         if (hp->addresses != NULL) {
    327  1.1  christos             for (int i = 0; i < hp->addresses->num; i++) {
    328  1.1  christos                 if (hp->addresses->vec[i] != NULL) {
    329  1.1  christos                     adv_record_t *record = hp->addresses->vec[i];
    330  1.1  christos                     if (record->rrtype == dns_rrtype_a) {
    331  1.1  christos                         if (full) {
    332  1.1  christos                             IPv4_ADDR_GEN_SRP(record->rdata, addr_buf);
    333  1.1  christos                             INFO("  IN    A " PRI_IPv4_ADDR_SRP PRI_S_SRP, IPv4_ADDR_PARAM_SRP(record->rdata, addr_buf),
    334  1.1  christos                                  record->shared_txn == (intptr_t)server_state->shared_registration_txn ? " live" : "");
    335  1.1  christos                         }
    336  1.1  christos                         a_record_count++;
    337  1.1  christos                     } else if (record->rrtype == dns_rrtype_aaaa) {
    338  1.1  christos                         if (full) {
    339  1.1  christos                             IPv6_ADDR_GEN_SRP((const uint8_t *)record->rdata, addr_buf);
    340  1.1  christos                             INFO("  IN AAAA " PRI_IPv6_ADDR_SRP PRI_S_SRP, IPv6_ADDR_PARAM_SRP(record->rdata, addr_buf),
    341  1.1  christos                                  record->shared_txn == (intptr_t)server_state->shared_registration_txn ? " live" : "");
    342  1.1  christos                         }
    343  1.1  christos                         aaaa_record_count++;
    344  1.1  christos                     }
    345  1.1  christos                 }
    346  1.1  christos             }
    347  1.1  christos         }
    348  1.1  christos         bool matter_instance_present = false, hap_instance_present = false;
    349  1.1  christos         if (hp->instances != NULL) {
    350  1.1  christos             for (int i = 0; i < hp->instances->num; i++) {
    351  1.1  christos                 adv_instance_t *instance = hp->instances->vec[i];
    352  1.1  christos                 if (instance != NULL) {
    353  1.1  christos                     if (full) {
    354  1.1  christos                         char txt_buf[DNS_DATA_SIZE];
    355  1.1  christos                         if (instance->txt_data != NULL) {
    356  1.1  christos                             dns_txt_data_print(txt_buf, DNS_DATA_SIZE, instance->txt_length, instance->txt_data);
    357  1.1  christos                         } else {
    358  1.1  christos                             txt_buf[0] = 0;
    359  1.1  christos                         }
    360  1.1  christos                         const char *status = "removed";
    361  1.1  christos                         if (!instance->removed) {
    362  1.1  christos                             if (instance->txn == NULL) {
    363  1.1  christos                                 status = "unregistered";
    364  1.1  christos                             } else if (instance->shared_txn != (intptr_t)server_state->shared_registration_txn) {
    365  1.1  christos                                 status = "stale";
    366  1.1  christos                             } else {
    367  1.1  christos                                 status = "live";
    368  1.1  christos                             }
    369  1.1  christos                         }
    370  1.1  christos                         INFO("  " PUB_S_SRP " instance " PRI_S_SRP " " PRI_S_SRP " %d (" PRI_S_SRP ")",
    371  1.1  christos                              status, instance->instance_name, instance->service_type, instance->port, txt_buf);
    372  1.1  christos                     }
    373  1.1  christos                     if (!instance->removed) {
    374  1.1  christos                         instance_count++;
    375  1.1  christos                         if (instance->service_type != NULL) {
    376  1.1  christos                             const char matter_prefix[] = "_matter";
    377  1.1  christos                             const char hap_prefix[] = "_hap._udp";
    378  1.1  christos                             if (!strncmp(instance->service_type, matter_prefix, sizeof(matter_prefix) - 1)) {
    379  1.1  christos                                 matter_instance_present = true;
    380  1.1  christos                             } else if (!strncmp(instance->service_type, hap_prefix, sizeof(hap_prefix) - 1)) {
    381  1.1  christos                                 hap_instance_present = true;
    382  1.1  christos                             }
    383  1.1  christos                         }
    384  1.1  christos                     }
    385  1.1  christos                 }
    386  1.1  christos             }
    387  1.1  christos         }
    388  1.1  christos         if (matter_instance_present) {
    389  1.1  christos             matter_host_count++;
    390  1.1  christos         } else if (hap_instance_present) { // If both, only count matter.
    391  1.1  christos             hap_host_count++;
    392  1.1  christos         }
    393  1.1  christos     }
    394  1.1  christos     INFO(PUB_S_SRP "%d hosts (%d matter, %d hap), %d instances, %d a records, %d aaaa records at %.6lf",
    395  1.1  christos          periodic ? "" : "after update, ", host_count, matter_host_count, hap_host_count, instance_count, a_record_count,
    396  1.1  christos          aaaa_record_count, srp_fractional_time());
    397  1.1  christos 
    398  1.1  christos #if STUB_ROUTER
    399  1.1  christos     route_state_t *route_state = server_state->route_state;
    400  1.1  christos     if (route_state) {
    401  1.1  christos         SEGMENTED_IPv6_ADDR_GEN_SRP(&route_state->srp_listener_ip_address, addr_buf);
    402  1.1  christos         // do we have an SRP listener?
    403  1.1  christos         if (route_state->srp_listener != NULL) {
    404  1.1  christos             INFO("have SRP listener on " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    405  1.1  christos                  SEGMENTED_IPv6_ADDR_PARAM_SRP(&route_state->srp_listener_ip_address, addr_buf),
    406  1.1  christos                  route_state->srp_service_listen_port);
    407  1.1  christos         } else {
    408  1.1  christos             INFO("no SRP listener");
    409  1.1  christos         }
    410  1.1  christos 
    411  1.1  christos         // are we publishing anycast services?
    412  1.1  christos         INFO(PUB_S_SRP "advertising anycast service", route_state->advertising_srp_anycast_service ? "" : "not ");
    413  1.1  christos 
    414  1.1  christos         // are we publishing unicast service?
    415  1.1  christos         if (route_state->advertising_srp_unicast_service) {
    416  1.1  christos             INFO("advertising unicast service on " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    417  1.1  christos                  SEGMENTED_IPv6_ADDR_PARAM_SRP(&route_state->srp_listener_ip_address, addr_buf),
    418  1.1  christos                  route_state->srp_service_listen_port);
    419  1.1  christos         } else {
    420  1.1  christos             INFO("not advertising unicast service");
    421  1.1  christos         }
    422  1.1  christos 
    423  1.1  christos         // what SRP replication peers do we see? and how many are we actively connected to?
    424  1.1  christos         srpl_dump_connection_states(server_state);
    425  1.1  christos 
    426  1.1  christos         // are we publishing OMR prefix?
    427  1.1  christos         if (route_state->omr_publisher != NULL &&
    428  1.1  christos             omr_publisher_publishing_prefix(route_state->omr_publisher))
    429  1.1  christos         {
    430  1.1  christos             omr_prefix_t *prefix = omr_publisher_published_prefix_get(route_state->omr_publisher);
    431  1.1  christos             SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    432  1.1  christos             INFO("publishing " PUB_S_SRP " OMR prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d",
    433  1.1  christos                  omr_publisher_publishing_dhcp(route_state->omr_publisher) ? "dhcp" : "ula",
    434  1.1  christos                  SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf),
    435  1.1  christos                  prefix->prefix_length);
    436  1.1  christos         } else {
    437  1.1  christos             INFO("not publishing OMR prefix");
    438  1.1  christos         }
    439  1.1  christos 
    440  1.1  christos         // what prefixes do we see on Thread?
    441  1.1  christos         if (route_state->omr_watcher != NULL) {
    442  1.1  christos             omr_prefix_t *thread_prefixes = omr_watcher_prefixes_get(route_state->omr_watcher);
    443  1.1  christos             for (struct omr_prefix *prefix = thread_prefixes; prefix != NULL; prefix = prefix->next) {
    444  1.1  christos                 SEGMENTED_IPv6_ADDR_GEN_SRP(prefix->prefix.s6_addr, prefix_buf);
    445  1.1  christos                 INFO("OMR prefix " PRI_SEGMENTED_IPv6_ADDR_SRP "/%d seen on thread" PUB_S_SRP PUB_S_SRP
    446  1.1  christos                      PUB_S_SRP, SEGMENTED_IPv6_ADDR_PARAM_SRP(prefix->prefix.s6_addr, prefix_buf),
    447  1.1  christos                      prefix->prefix_length, prefix->user ? " (user)" : "", prefix->ncp ? " (ncp)": "",
    448  1.1  christos                      prefix->stable ? " (stable)" : "");
    449  1.1  christos             }
    450  1.1  christos         }
    451  1.1  christos 
    452  1.1  christos         // are we publishing infrastructure prefix?
    453  1.1  christos         interface_t *interface;
    454  1.1  christos         bool is_advertising = false;
    455  1.1  christos         for (interface = route_state->interfaces; interface; interface = interface->next) {
    456  1.1  christos             if (interface->our_prefix_advertised) {
    457  1.1  christos                SEGMENTED_IPv6_ADDR_GEN_SRP(interface->ipv6_prefix.s6_addr, ipv6_prefix_buf);
    458  1.1  christos                INFO("advertising infrastructure prefix " PRI_SEGMENTED_IPv6_ADDR_SRP " on " PUB_S_SRP,
    459  1.1  christos                     SEGMENTED_IPv6_ADDR_PARAM_SRP(interface->ipv6_prefix.s6_addr, ipv6_prefix_buf),
    460  1.1  christos                     interface->name);
    461  1.1  christos                is_advertising = true;
    462  1.1  christos             }
    463  1.1  christos         }
    464  1.1  christos         if (!is_advertising) {
    465  1.1  christos             INFO("not advertising infrastructure prefix");
    466  1.1  christos         }
    467  1.1  christos     }
    468  1.1  christos #endif // STUB_ROUTER
    469  1.1  christos 
    470  1.1  christos     // how many DNS push queries added since last state dump?
    471  1.1  christos     // how many DNS queries seen since last state dump?
    472  1.1  christos     // how many DNS queries dropped for load?
    473  1.1  christos     // how many DNS Push connections dropped for load?
    474  1.1  christos     INFO("%d push sessions and %d queries added, %d push sessions and %d queries dropped for load",
    475  1.1  christos          num_push_sessions - last_num_push_sessions,
    476  1.1  christos          dp_num_outstanding_queries - last_dp_num_outstanding_queries,
    477  1.1  christos          num_push_sessions_dropped_for_load - last_num_push_sessions_dropped_for_load,
    478  1.1  christos          num_queries_dropped_for_load - last_num_queries_dropped_for_load);
    479  1.1  christos     last_num_push_sessions = num_push_sessions;
    480  1.1  christos     last_dp_num_outstanding_queries = dp_num_outstanding_queries;
    481  1.1  christos     last_num_push_sessions_dropped_for_load = num_push_sessions_dropped_for_load;
    482  1.1  christos     last_num_queries_dropped_for_load = num_queries_dropped_for_load;
    483  1.1  christos }
    484  1.1  christos 
    485  1.1  christos // We call advertise_finished when a client request has finished, successfully or otherwise.
    486  1.1  christos #if SRP_FEATURE_REPLICATION
    487  1.1  christos static bool
    488  1.1  christos srp_replication_advertise_finished(adv_host_t *host, char *hostname, srp_server_t *server_state,
    489  1.1  christos                                    srpl_connection_t *srpl_connection, comm_t *connection, int rcode, bool last)
    490  1.1  christos {
    491  1.1  christos 	if (server_state->srp_replication_enabled) {
    492  1.1  christos         INFO("hostname = " PRI_S_SRP "  host = %p  server_state = %p  srpl_connection = %p  connection = %p  rcode = "
    493  1.1  christos              PUB_S_SRP, hostname, host, server_state, srpl_connection, connection, dns_rcode_name(rcode));
    494  1.1  christos         if (connection == NULL) {
    495  1.1  christos             // connection is the SRP client connection on which an update arrived. If it's null,
    496  1.1  christos             // this is an SRP replication update, not an actual client we're communicating with.
    497  1.1  christos             INFO("replication advertise finished: host " PRI_S_SRP ": rcode = " PUB_S_SRP,
    498  1.1  christos                  hostname, dns_rcode_name(rcode));
    499  1.1  christos             if (srpl_connection != NULL) {
    500  1.1  christos                 if (last) {
    501  1.1  christos                     srpl_advertise_finished_event_send(hostname, rcode, server_state);
    502  1.1  christos #ifdef SRP_TEST_SERVER
    503  1.1  christos                     if (srpl_connection->srpl_advertise_finished_callback != NULL) {
    504  1.1  christos                         srpl_connection->srpl_advertise_finished_callback(srpl_connection);
    505  1.1  christos                     }
    506  1.1  christos #endif
    507  1.1  christos                 }
    508  1.1  christos 
    509  1.1  christos                 if (host != NULL && host->srpl_connection != NULL) {
    510  1.1  christos                     if (rcode == dns_rcode_noerror) {
    511  1.1  christos                         host->update_server_id = host->srpl_connection->remote_partner_id;
    512  1.1  christos                         host->server_stable_id = host->srpl_connection->stashed_host.server_stable_id;
    513  1.1  christos                         INFO("replicated host " PRI_S_SRP " server stable ID %" PRIx64, hostname, host->server_stable_id);
    514  1.1  christos                     }
    515  1.1  christos 
    516  1.1  christos                     // This is the safest place to clear this pointer--we do not want the srpl_connection pointer to not
    517  1.1  christos                     // get reset because of some weird sequence of events, leaving this host unable to be further updated
    518  1.1  christos                     // or worse.
    519  1.1  christos                     srpl_connection_release(host->srpl_connection);
    520  1.1  christos                     host->srpl_connection = NULL;
    521  1.1  christos                 } else {
    522  1.1  christos                     if (host != NULL) {
    523  1.1  christos                         INFO("disconnected host " PRI_S_SRP " server stable ID %" PRIx64, hostname, host->server_stable_id);
    524  1.1  christos                     }
    525  1.1  christos                 }
    526  1.1  christos             } else {
    527  1.1  christos                 if (host != NULL) {
    528  1.1  christos                     INFO("context-free host " PRI_S_SRP " server stable ID %" PRIx64, hostname, host->server_stable_id);
    529  1.1  christos                 }
    530  1.1  christos             }
    531  1.1  christos             return true;
    532  1.1  christos         }
    533  1.1  christos 
    534  1.1  christos         if (host != NULL) {
    535  1.1  christos             if (rcode == dns_rcode_noerror) {
    536  1.1  christos                 memcpy(&host->server_stable_id, &host->server_state->ula_prefix, sizeof(host->server_stable_id));
    537  1.1  christos             }
    538  1.1  christos             INFO("local host " PRI_S_SRP " server stable ID %" PRIx64, hostname, host->server_stable_id);
    539  1.1  christos             srpl_srp_client_update_finished_event_send(host, rcode);
    540  1.1  christos             host->update_server_id = 0;
    541  1.1  christos         }
    542  1.1  christos     } else
    543  1.1  christos     {
    544  1.1  christos         if (host != NULL && host->server_state != NULL) {
    545  1.1  christos             memcpy(&host->server_stable_id, &host->server_state->ula_prefix, sizeof(host->server_stable_id));
    546  1.1  christos             host->update_server_id = 0;
    547  1.1  christos         }
    548  1.1  christos     }
    549  1.1  christos     return false;
    550  1.1  christos }
    551  1.1  christos #endif // SRP_FEATURE_REPLICATION
    552  1.1  christos 
    553  1.1  christos static void
    554  1.1  christos srp_ml_eid_mapping_callback(void *context, cti_status_t status)
    555  1.1  christos {
    556  1.1  christos     adv_record_t *arec = context;
    557  1.1  christos     adv_host_t *host = arec->host;
    558  1.1  christos     SEGMENTED_IPv6_ADDR_GEN_SRP(arec->rdata, omr_buf);
    559  1.1  christos     if (status == kCTIStatus_NoError) {
    560  1.1  christos         if (host == NULL) {
    561  1.1  christos             INFO("mapping for address " PRI_SEGMENTED_IPv6_ADDR_SRP " was orphaned.",
    562  1.1  christos                  SEGMENTED_IPv6_ADDR_PARAM_SRP(arec->rdata, omr_buf));
    563  1.1  christos         } else {
    564  1.1  christos             INFO("mapping for address " PRI_SEGMENTED_IPv6_ADDR_SRP " to host " PRI_S_SRP " succeeded",
    565  1.1  christos                  SEGMENTED_IPv6_ADDR_PARAM_SRP(arec->rdata, omr_buf), host->name);
    566  1.1  christos         }
    567  1.1  christos     } else {
    568  1.1  christos         if (host == NULL) {
    569  1.1  christos             INFO("orphaned mapping for address " PRI_SEGMENTED_IPv6_ADDR_SRP " failed: %d",
    570  1.1  christos                  SEGMENTED_IPv6_ADDR_PARAM_SRP(arec->rdata, omr_buf), status);
    571  1.1  christos         } else {
    572  1.1  christos             INFO("mapping for address " PRI_SEGMENTED_IPv6_ADDR_SRP " to host " PRI_S_SRP " failed: %d",
    573  1.1  christos                  SEGMENTED_IPv6_ADDR_PARAM_SRP(arec->rdata, omr_buf), host->name, status);
    574  1.1  christos         }
    575  1.1  christos     }
    576  1.1  christos     RELEASE_HERE(arec, adv_record);
    577  1.1  christos }
    578  1.1  christos 
    579  1.1  christos // We call advertise_finished when a client request has finished, successfully or otherwise.
    580  1.1  christos static void
    581  1.1  christos advertise_finished(adv_host_t *host, char *hostname, srp_server_t *server_state, srpl_connection_t *srpl_connection,
    582  1.1  christos                    comm_t *connection, message_t *message, int rcode, client_update_t *client, bool send_response,
    583  1.1  christos                    bool last)
    584  1.1  christos {
    585  1.1  christos     struct iovec iov;
    586  1.1  christos     dns_wire_t response;
    587  1.1  christos 
    588  1.1  christos #if SRP_FEATURE_REPLICATION
    589  1.1  christos     if (srp_replication_advertise_finished(host, hostname, server_state, srpl_connection, connection, rcode, last)) {
    590  1.1  christos         return;
    591  1.1  christos     }
    592  1.1  christos #else
    593  1.1  christos     (void)host;
    594  1.1  christos     (void)server_state;
    595  1.1  christos     (void)srpl_connection;
    596  1.1  christos     (void)last;
    597  1.1  christos #endif // SRP_FEATURE_REPLICATION
    598  1.1  christos     INFO("host " PRI_S_SRP ": rcode = " PUB_S_SRP ", lease = %d, key_lease = %d  connection = %p", hostname, dns_rcode_name(rcode),
    599  1.1  christos          client ? client->host_lease : 0, client ? client->key_lease : 0, connection);
    600  1.1  christos 
    601  1.1  christos     // This can happen if we turn off replication in the middle of an update of a replicated host.
    602  1.1  christos     if (connection == NULL) {
    603  1.1  christos         return;
    604  1.1  christos     }
    605  1.1  christos     if (!send_response) {
    606  1.1  christos         INFO("not sending response.");
    607  1.1  christos         return;
    608  1.1  christos     }
    609  1.1  christos 
    610  1.1  christos     memset(&response, 0, DNS_HEADER_SIZE);
    611  1.1  christos     response.id = message->wire.id;
    612  1.1  christos     response.bitfield = message->wire.bitfield;
    613  1.1  christos     dns_rcode_set(&response, rcode);
    614  1.1  christos     dns_qr_set(&response, dns_qr_response);
    615  1.1  christos 
    616  1.1  christos     iov.iov_base = &response;
    617  1.1  christos     // If this was a successful update, send back the lease time, which will either
    618  1.1  christos     // be what the client asked for, or a shorter lease, depending on what limit has
    619  1.1  christos     // been set.
    620  1.1  christos     if (client != NULL) {
    621  1.1  christos         dns_towire_state_t towire;
    622  1.1  christos         memset(&towire, 0, sizeof towire);
    623  1.1  christos         towire.p = &response.data[0];               // We start storing RR data here.
    624  1.1  christos         towire.lim = &response.data[DNS_DATA_SIZE]; // This is the limit to how much we can store.
    625  1.1  christos         towire.message = &response;
    626  1.1  christos         response.qdcount = 0;
    627  1.1  christos         response.ancount = 0;
    628  1.1  christos         response.nscount = 0;
    629  1.1  christos         response.arcount = htons(1);
    630  1.1  christos         dns_edns0_header_to_wire(&towire, DNS_MAX_UDP_PAYLOAD, 0, 0, 1);
    631  1.1  christos         dns_rdlength_begin(&towire);
    632  1.1  christos         dns_u16_to_wire(&towire, dns_opt_update_lease);  // OPTION-CODE
    633  1.1  christos         dns_edns0_option_begin(&towire);                 // OPTION-LENGTH
    634  1.1  christos         dns_u32_to_wire(&towire, client->host_lease);    // LEASE (e.g. 1 hour)
    635  1.1  christos         dns_u32_to_wire(&towire, client->key_lease);     // KEY-LEASE (7 days)
    636  1.1  christos         dns_edns0_option_end(&towire);                   // Now we know OPTION-LENGTH
    637  1.1  christos         dns_rdlength_end(&towire);
    638  1.1  christos         // It should not be possible for this to happen; if it does, the client
    639  1.1  christos         // might not renew its lease in a timely manner.
    640  1.1  christos         if (towire.error) {
    641  1.1  christos             ERROR("unexpectedly failed to send EDNS0 lease option.");
    642  1.1  christos             iov.iov_len = DNS_HEADER_SIZE;
    643  1.1  christos         } else {
    644  1.1  christos             iov.iov_len = towire.p - (uint8_t *)&response;
    645  1.1  christos         }
    646  1.1  christos     } else {
    647  1.1  christos         iov.iov_len = DNS_HEADER_SIZE;
    648  1.1  christos     }
    649  1.1  christos     ioloop_send_message(connection, message, &iov, 1);
    650  1.1  christos }
    651  1.1  christos 
    652  1.1  christos static void
    653  1.1  christos retry_callback(void *context)
    654  1.1  christos {
    655  1.1  christos     adv_host_t *host = (adv_host_t *)context;
    656  1.1  christos     if (host->update == NULL) {
    657  1.1  christos         update_from_host(host);
    658  1.1  christos     } else {
    659  1.1  christos         start_host_update(host);
    660  1.1  christos     }
    661  1.1  christos }
    662  1.1  christos 
    663  1.1  christos static void
    664  1.1  christos srp_adv_host_context_release(void *context)
    665  1.1  christos {
    666  1.1  christos     adv_host_t *host = context;
    667  1.1  christos     RELEASE_HERE(host, adv_host);
    668  1.1  christos }
    669  1.1  christos 
    670  1.1  christos static void
    671  1.1  christos wait_retry(adv_host_t *host)
    672  1.1  christos {
    673  1.1  christos     int64_t now = ioloop_timenow();
    674  1.1  christos #define MIN_HOST_RETRY_INTERVAL 15
    675  1.1  christos #define MAX_HOST_RETRY_INTERVAL 120
    676  1.1  christos     // If we've been retrying long enough for the lease to expire, give up.
    677  1.1  christos     if (!host->lease_expiry || host->lease_expiry < now) {
    678  1.1  christos         INFO("host lease has expired, not retrying: lease_expiry = %" PRId64
    679  1.1  christos              " now = %" PRId64 " difference = %" PRId64, host->lease_expiry, now, host->lease_expiry - now);
    680  1.1  christos         delete_host(host);
    681  1.1  christos         return;
    682  1.1  christos     }
    683  1.1  christos     if (host->retry_interval == 0) {
    684  1.1  christos         host->retry_interval = MIN_HOST_RETRY_INTERVAL;
    685  1.1  christos     } else if (host->retry_interval < MAX_HOST_RETRY_INTERVAL) {
    686  1.1  christos         host->retry_interval *= 2;
    687  1.1  christos     }
    688  1.1  christos     INFO("waiting %d seconds...", host->retry_interval);
    689  1.1  christos     ioloop_add_wake_event(host->retry_wakeup, host, retry_callback, srp_adv_host_context_release, host->retry_interval * 1000);
    690  1.1  christos     RETAIN_HERE(host, adv_host);
    691  1.1  christos }
    692  1.1  christos 
    693  1.1  christos static void
    694  1.1  christos shared_registration_fail(void *context, int UNUSED status)
    695  1.1  christos {
    696  1.1  christos     srp_server_t *server_state = context;
    697  1.1  christos     dnssd_txn_t *txn = server_state->shared_registration_txn;
    698  1.1  christos     DNSServiceRef sdref = txn == NULL ? NULL : txn->sdref;
    699  1.1  christos     INFO("shared registration failed: txn %p sdref %p", server_state->shared_registration_txn, sdref);
    700  1.1  christos     if (txn != NULL) {
    701  1.1  christos         ioloop_dnssd_txn_cancel(txn);
    702  1.1  christos         ioloop_dnssd_txn_release(txn);
    703  1.1  christos         server_state->shared_registration_txn = NULL;
    704  1.1  christos     }
    705  1.1  christos }
    706  1.1  christos 
    707  1.1  christos bool
    708  1.1  christos srp_mdns_shared_registration_txn_setup(srp_server_t *server_state)
    709  1.1  christos {
    710  1.1  christos     if (server_state->shared_registration_txn == NULL) {
    711  1.1  christos         DNSServiceRef sdref;
    712  1.1  christos         int err = DNSServiceCreateConnection(&sdref);
    713  1.1  christos         if (err != kDNSServiceErr_NoError) {
    714  1.1  christos             return false;
    715  1.1  christos         }
    716  1.1  christos         server_state->shared_registration_txn = ioloop_dnssd_txn_add(sdref, server_state, NULL, shared_registration_fail);
    717  1.1  christos         if (server_state->shared_registration_txn == NULL) {
    718  1.1  christos             ERROR("unable to create shared connection for registration.");
    719  1.1  christos             dns_service_op_not_to_be_freed = NULL;
    720  1.1  christos             DNSServiceRefDeallocate(sdref);
    721  1.1  christos             return false;
    722  1.1  christos         }
    723  1.1  christos         dns_service_op_not_to_be_freed = server_state->shared_registration_txn->sdref;
    724  1.1  christos         INFO("server_state->shared_registration_txn = %p  sdref = %p", server_state->shared_registration_txn, sdref);
    725  1.1  christos     }
    726  1.1  christos     return true;
    727  1.1  christos }
    728  1.1  christos 
    729  1.1  christos static void
    730  1.1  christos record_txn_forget(adv_record_t *record, intptr_t affected_service_pointer,
    731  1.1  christos                   const char *parent_type, const void *parent_pointer, const char *hostname)
    732  1.1  christos {
    733  1.1  christos     if (record == NULL) {
    734  1.1  christos         return;
    735  1.1  christos     }
    736  1.1  christos     if (record->rref != NULL && record->shared_txn == affected_service_pointer) {
    737  1.1  christos         INFO("forgetting rref %p on " PUB_S_SRP " %p " PRI_S_SRP, record->rref, parent_type, parent_pointer, hostname);
    738  1.1  christos         record->rref = NULL;
    739  1.1  christos     }
    740  1.1  christos }
    741  1.1  christos 
    742  1.1  christos static void
    743  1.1  christos record_vec_txns_forget(adv_record_vec_t *records, intptr_t affected_service_pointer,
    744  1.1  christos                        const char *parent_type, const void *parent_pointer, const char *hostname)
    745  1.1  christos {
    746  1.1  christos     if (records == NULL) {
    747  1.1  christos         return;
    748  1.1  christos     }
    749  1.1  christos     for (int i = 0; i < records->num; i++) {
    750  1.1  christos         record_txn_forget(records->vec[i], affected_service_pointer, parent_type, parent_pointer, hostname);
    751  1.1  christos     }
    752  1.1  christos }
    753  1.1  christos 
    754  1.1  christos static void
    755  1.1  christos instance_vec_txns_forget(adv_instance_vec_t *instances, intptr_t affected_service_pointer,
    756  1.1  christos                          const char *parent_type, const void *parent_pointer, const char *hostname)
    757  1.1  christos {
    758  1.1  christos     if (instances == NULL) {
    759  1.1  christos         return;
    760  1.1  christos     }
    761  1.1  christos     for (int i = 0; i < instances->num; i++) {
    762  1.1  christos         adv_instance_t *instance = instances->vec[i];
    763  1.1  christos         if (instance != NULL && instance->txn != NULL && instance->txn->sdref != NULL &&
    764  1.1  christos             instance->shared_txn == affected_service_pointer)
    765  1.1  christos         {
    766  1.1  christos             INFO("forgetting sdref %p on " PUB_S_SRP " %p " PRI_S_SRP " instance " PRI_S_SRP " . " PRI_S_SRP,
    767  1.1  christos                  instance->txn->sdref,
    768  1.1  christos                  parent_type, parent_pointer, hostname, instance->instance_name, instance->service_type);
    769  1.1  christos             instance->txn->sdref = NULL;
    770  1.1  christos         }
    771  1.1  christos     }
    772  1.1  christos }
    773  1.1  christos 
    774  1.1  christos static void
    775  1.1  christos host_txns_forget(adv_host_t *host, intptr_t affected_service_pointer)
    776  1.1  christos {
    777  1.1  christos     // We call this when the shared transaction fails for some reason. That failure invalidates all the subsidiary
    778  1.1  christos     // RecordRefs and ServiceRefs hanging off of the shared transaction; to avoid holding on to invalid pointers,
    779  1.1  christos     // we traverse the registration database and NULL out all the rrefs and sdrefs that relate to the subsidiary
    780  1.1  christos     // service pointer.
    781  1.1  christos     record_vec_txns_forget(host->addresses, affected_service_pointer, "host", host, host->name);
    782  1.1  christos     instance_vec_txns_forget(host->instances, affected_service_pointer, "host", host, host->name);
    783  1.1  christos     record_txn_forget(host->key_record, affected_service_pointer, "host key", host, host->name);
    784  1.1  christos     if (host->update != NULL) {
    785  1.1  christos         record_vec_txns_forget(host->update->remove_addresses, affected_service_pointer,
    786  1.1  christos                                "host update remove addresses", host->update, host->name);
    787  1.1  christos         record_vec_txns_forget(host->update->add_addresses, affected_service_pointer,
    788  1.1  christos                                "host update add addresses", host->update, host->name);
    789  1.1  christos         record_txn_forget(host->update->key, affected_service_pointer, "host update key", host->update, host->name);
    790  1.1  christos         instance_vec_txns_forget(host->update->update_instances, affected_service_pointer,
    791  1.1  christos                                  "host update update instances", host->update, host->name);
    792  1.1  christos         instance_vec_txns_forget(host->update->remove_instances, affected_service_pointer,
    793  1.1  christos                                  "host update remove instances", host->update, host->name);
    794  1.1  christos         instance_vec_txns_forget(host->update->renew_instances, affected_service_pointer,
    795  1.1  christos                                  "host update renew instances", host->update, host->name);
    796  1.1  christos         instance_vec_txns_forget(host->update->add_instances, affected_service_pointer,
    797  1.1  christos                                  "host update add instances", host->update, host->name);
    798  1.1  christos     }
    799  1.1  christos }
    800  1.1  christos 
    801  1.1  christos static void
    802  1.1  christos service_disconnected(srp_server_t *server_state, intptr_t service_pointer)
    803  1.1  christos {
    804  1.1  christos     if (service_pointer == (intptr_t)server_state->shared_registration_txn &&
    805  1.1  christos         server_state->shared_registration_txn != NULL)
    806  1.1  christos     {
    807  1.1  christos         INFO("server_state->shared_registration_txn = %p  sdref = %p",
    808  1.1  christos              server_state->shared_registration_txn, server_state->shared_registration_txn->sdref);
    809  1.1  christos         // For every host that's active right now that has transactions on this shared transaction, forget all those
    810  1.1  christos         // transactions. The txn_cancel following this will free all of the memory in the client stub.
    811  1.1  christos         for (adv_host_t *host = server_state->hosts; host != NULL; host = host->next) {
    812  1.1  christos             host_txns_forget(host, service_pointer);
    813  1.1  christos         }
    814  1.1  christos         dns_service_op_not_to_be_freed = NULL;
    815  1.1  christos         ioloop_dnssd_txn_cancel(server_state->shared_registration_txn);
    816  1.1  christos         ioloop_dnssd_txn_release(server_state->shared_registration_txn);
    817  1.1  christos         server_state->shared_registration_txn = NULL;
    818  1.1  christos     }
    819  1.1  christos }
    820  1.1  christos 
    821  1.1  christos static void
    822  1.1  christos adv_record_vec_remove_update(adv_record_vec_t *vec, adv_update_t *update)
    823  1.1  christos {
    824  1.1  christos     for (int i = 0; i < vec->num; i++) {
    825  1.1  christos         if (vec->vec[i] != NULL && vec->vec[i]->update != NULL && vec->vec[i]->update == update) {
    826  1.1  christos             RELEASE_HERE(vec->vec[i]->update, adv_update);
    827  1.1  christos             vec->vec[i]->update = NULL;
    828  1.1  christos         }
    829  1.1  christos     }
    830  1.1  christos }
    831  1.1  christos 
    832  1.1  christos static void
    833  1.1  christos adv_instance_vec_remove_update(adv_instance_vec_t *vec, adv_update_t *update)
    834  1.1  christos {
    835  1.1  christos     for (int i = 0; i < vec->num; i++) {
    836  1.1  christos         if (vec->vec[i] != NULL && vec->vec[i]->update != NULL && vec->vec[i]->update == update) {
    837  1.1  christos             RELEASE_HERE(vec->vec[i]->update, adv_update);
    838  1.1  christos             vec->vec[i]->update = NULL;
    839  1.1  christos         }
    840  1.1  christos     }
    841  1.1  christos }
    842  1.1  christos 
    843  1.1  christos static void
    844  1.1  christos adv_instances_cancel(adv_instance_vec_t *instances)
    845  1.1  christos {
    846  1.1  christos     for (int i = 0; i < instances->num; i++) {
    847  1.1  christos         adv_instance_t *instance = instances->vec[i];
    848  1.1  christos         if (instance != NULL) {
    849  1.1  christos             if (instance->txn != NULL) {
    850  1.1  christos                 ioloop_dnssd_txn_cancel_srp(instance->host->server_state, instance->txn);
    851  1.1  christos                 ioloop_dnssd_txn_release(instance->txn);
    852  1.1  christos                 instance->txn = NULL;
    853  1.1  christos             }
    854  1.1  christos             if (instance->retry_wakeup != NULL) {
    855  1.1  christos                 ioloop_cancel_wake_event(instance->retry_wakeup);
    856  1.1  christos                 ioloop_wakeup_release(instance->retry_wakeup);
    857  1.1  christos                 instance->retry_wakeup = NULL;
    858  1.1  christos             }
    859  1.1  christos         }
    860  1.1  christos     }
    861  1.1  christos }
    862  1.1  christos 
    863  1.1  christos static void
    864  1.1  christos adv_update_free_instance_vectors(adv_update_t *NONNULL update)
    865  1.1  christos {
    866  1.1  christos     if (update->update_instances != NULL) {
    867  1.1  christos         adv_instance_vec_remove_update(update->update_instances, update);
    868  1.1  christos         adv_instances_cancel(update->update_instances);
    869  1.1  christos         RELEASE_HERE(update->update_instances, adv_instance_vec);
    870  1.1  christos         update->update_instances = NULL;
    871  1.1  christos     }
    872  1.1  christos     if (update->remove_instances != NULL) {
    873  1.1  christos         adv_instance_vec_remove_update(update->remove_instances, update);
    874  1.1  christos         RELEASE_HERE(update->remove_instances, adv_instance_vec);
    875  1.1  christos         update->remove_instances = NULL;
    876  1.1  christos     }
    877  1.1  christos     if (update->renew_instances != NULL) {
    878  1.1  christos         adv_instance_vec_remove_update(update->renew_instances, update);
    879  1.1  christos         RELEASE_HERE(update->renew_instances, adv_instance_vec);
    880  1.1  christos         update->renew_instances = NULL;
    881  1.1  christos     }
    882  1.1  christos     if (update->add_instances != NULL) {
    883  1.1  christos         adv_instance_vec_remove_update(update->add_instances, update);
    884  1.1  christos         adv_instances_cancel(update->add_instances);
    885  1.1  christos         RELEASE_HERE(update->add_instances, adv_instance_vec);
    886  1.1  christos         update->add_instances = NULL;
    887  1.1  christos     }
    888  1.1  christos }
    889  1.1  christos 
    890  1.1  christos static void
    891  1.1  christos adv_update_finalize(adv_update_t *NONNULL update)
    892  1.1  christos {
    893  1.1  christos     if (update->host != NULL) {
    894  1.1  christos         RELEASE_HERE(update->host, adv_host);
    895  1.1  christos     }
    896  1.1  christos 
    897  1.1  christos     if (update->client != NULL) {
    898  1.1  christos         srp_parse_client_updates_free(update->client);
    899  1.1  christos         update->client = NULL;
    900  1.1  christos     }
    901  1.1  christos 
    902  1.1  christos     if (update->remove_addresses != NULL) {
    903  1.1  christos         adv_record_vec_remove_update(update->remove_addresses, update);
    904  1.1  christos         RELEASE_HERE(update->remove_addresses, adv_record_vec);
    905  1.1  christos     }
    906  1.1  christos 
    907  1.1  christos     if (update->add_addresses != NULL) {
    908  1.1  christos         adv_record_vec_remove_update(update->add_addresses, update);
    909  1.1  christos         RELEASE_HERE(update->add_addresses, adv_record_vec);
    910  1.1  christos     }
    911  1.1  christos 
    912  1.1  christos     adv_update_free_instance_vectors(update);
    913  1.1  christos     if (update->key != NULL) {
    914  1.1  christos         RELEASE_HERE(update->key, adv_record);
    915  1.1  christos     }
    916  1.1  christos     free(update);
    917  1.1  christos }
    918  1.1  christos 
    919  1.1  christos static void
    920  1.1  christos adv_update_cancel(adv_update_t *NONNULL update)
    921  1.1  christos {
    922  1.1  christos     adv_host_t *host = update->host;
    923  1.1  christos     bool faulted = false;
    924  1.1  christos 
    925  1.1  christos     RETAIN_HERE(update, adv_update); // ensure that update remains valid for the duration of this function call.
    926  1.1  christos 
    927  1.1  christos     if (host != NULL) {
    928  1.1  christos         RETAIN_HERE(host, adv_host); // in case the update is holding the last reference to the host
    929  1.1  christos         RELEASE_HERE(update->host, adv_host);
    930  1.1  christos         update->host = NULL;
    931  1.1  christos 
    932  1.1  christos         INFO("cancelling update %p for host " PRI_S_SRP, update, host->registered_name);
    933  1.1  christos 
    934  1.1  christos         if (host->update == update) {
    935  1.1  christos             RELEASE_HERE(host->update, adv_update);
    936  1.1  christos             host->update = NULL;
    937  1.1  christos         }
    938  1.1  christos 
    939  1.1  christos         // In case we needed to re-register some of the host's addresses, remove the update pointer from them.
    940  1.1  christos         if (host->addresses != NULL) {
    941  1.1  christos             for (int i = 0; i < host->addresses->num; i++) {
    942  1.1  christos                 adv_record_t *record = host->addresses->vec[i];
    943  1.1  christos                 if (record->update == update) {
    944  1.1  christos                     RELEASE_HERE(host->addresses->vec[i]->update, adv_update);
    945  1.1  christos                     record->update = NULL;
    946  1.1  christos                 }
    947  1.1  christos             }
    948  1.1  christos         }
    949  1.1  christos     } else {
    950  1.1  christos         INFO("canceling update with no host.");
    951  1.1  christos     }
    952  1.1  christos 
    953  1.1  christos     adv_update_free_instance_vectors(update);
    954  1.1  christos 
    955  1.1  christos     if (update->add_addresses != NULL) {
    956  1.1  christos         // Any record that we attempted to add as part of this update should be removed because the update failed.
    957  1.1  christos         for (int i = 0; i < update->add_addresses->num; i++) {
    958  1.1  christos             adv_record_t *record = update->add_addresses->vec[i];
    959  1.1  christos             if (record != NULL) {
    960  1.1  christos                 if (host == NULL) {
    961  1.1  christos                     if (!faulted) {
    962  1.1  christos                         FAULT("unable to clean up host address registration because host object is gone from update.");
    963  1.1  christos                         faulted = true;
    964  1.1  christos                     }
    965  1.1  christos                 } else {
    966  1.1  christos                     if (record->rref != NULL) {
    967  1.1  christos                         srp_mdns_shared_record_remove(host->server_state, record);
    968  1.1  christos                     }
    969  1.1  christos                 }
    970  1.1  christos             }
    971  1.1  christos         }
    972  1.1  christos         adv_record_vec_remove_update(update->add_addresses, update);
    973  1.1  christos         RELEASE_HERE(update->add_addresses, adv_record_vec);
    974  1.1  christos         update->add_addresses = NULL;
    975  1.1  christos     }
    976  1.1  christos 
    977  1.1  christos     if (update->remove_addresses != NULL) {
    978  1.1  christos         adv_record_vec_remove_update(update->remove_addresses, update);
    979  1.1  christos         RELEASE_HERE(update->remove_addresses, adv_record_vec);
    980  1.1  christos         update->remove_addresses = NULL;
    981  1.1  christos     }
    982  1.1  christos 
    983  1.1  christos     if (update->key != NULL) {
    984  1.1  christos         if (update->key->update != NULL) {
    985  1.1  christos             RELEASE_HERE(update->key->update, adv_update);
    986  1.1  christos             update->key->update = NULL;
    987  1.1  christos         }
    988  1.1  christos         // Any record that we attempted to add as part of this update should be removed because the update failed.
    989  1.1  christos         if (update->key->rref != NULL) {
    990  1.1  christos             if (host == NULL) {
    991  1.1  christos                 if (!faulted) {
    992  1.1  christos                     FAULT("unable to clean up host key registration because host object is gone from update.");
    993  1.1  christos                     faulted = true;
    994  1.1  christos                 }
    995  1.1  christos             } else {
    996  1.1  christos                 srp_mdns_shared_record_remove(host->server_state, update->key);
    997  1.1  christos             }
    998  1.1  christos         }
    999  1.1  christos         RELEASE_HERE(update->key, adv_record);
   1000  1.1  christos         update->key = NULL;
   1001  1.1  christos     }
   1002  1.1  christos     if (host != NULL) {
   1003  1.1  christos         RELEASE_HERE(host, adv_host);
   1004  1.1  christos     }
   1005  1.1  christos     RELEASE_HERE(update, adv_update);
   1006  1.1  christos }
   1007  1.1  christos 
   1008  1.1  christos static void
   1009  1.1  christos update_failed(adv_update_t *update, int rcode, bool expire, bool send_response)
   1010  1.1  christos {
   1011  1.1  christos     // Retain the update for the life of this function call, since we may well release the last other reference to it.
   1012  1.1  christos     RETAIN_HERE(update, adv_update);
   1013  1.1  christos 
   1014  1.1  christos     // If we still have a client waiting for the result of this update, tell it we failed.
   1015  1.1  christos     // Updates that have never worked are abandoned when the client is notified.
   1016  1.1  christos     if (update->client != NULL) {
   1017  1.1  christos         adv_host_t *host = update->host;
   1018  1.1  christos         client_update_t *client = update->client;
   1019  1.1  christos         adv_update_cancel(update);
   1020  1.1  christos         advertise_finished(host, host->name, host->server_state, host->srpl_connection,
   1021  1.1  christos                            client->connection, client->message, rcode, NULL, send_response, true);
   1022  1.1  christos         srp_parse_client_updates_free(client);
   1023  1.1  christos         update->client = NULL;
   1024  1.1  christos         // If we don't have a lease yet, or the old lease has expired, remove the host.
   1025  1.1  christos         // However, if the expire flag is false, it's because we're already finalizing the
   1026  1.1  christos         // host, so doing an expiry here would double free the host. In this case, we leave
   1027  1.1  christos         // it to the caller to do the expiry (really, to finalize the host).
   1028  1.1  christos         if (expire && (host->lease_expiry == 0 || host->lease_expiry <= ioloop_timenow())) {
   1029  1.1  christos             delete_host(host);
   1030  1.1  christos         }
   1031  1.1  christos         RELEASE_HERE(update, adv_update);
   1032  1.1  christos         return;
   1033  1.1  christos     }
   1034  1.1  christos     adv_update_cancel(update);
   1035  1.1  christos     RELEASE_HERE(update, adv_update);
   1036  1.1  christos }
   1037  1.1  christos 
   1038  1.1  christos static void
   1039  1.1  christos host_addr_free(adv_host_t *host)
   1040  1.1  christos {
   1041  1.1  christos     int i;
   1042  1.1  christos 
   1043  1.1  christos     // We can't actually deallocate the address vector until the host object is collected, so deallocate the address
   1044  1.1  christos     // records.
   1045  1.1  christos     if (host->addresses != NULL) {
   1046  1.1  christos         for (i = 0; i < host->addresses->num; i++) {
   1047  1.1  christos             if (host->addresses->vec[i] != NULL) {
   1048  1.1  christos                 INFO("Removing AAAA record for " PRI_S_SRP, host->registered_name);
   1049  1.1  christos                 srp_mdns_shared_record_remove(host->server_state, host->addresses->vec[i]);
   1050  1.1  christos                 RELEASE_HERE(host->addresses->vec[i], adv_record);
   1051  1.1  christos                 host->addresses->vec[i] = NULL;
   1052  1.1  christos             }
   1053  1.1  christos         }
   1054  1.1  christos         host->addresses->num = 0;
   1055  1.1  christos     }
   1056  1.1  christos }
   1057  1.1  christos 
   1058  1.1  christos // Free just those parts that are no longer needed when the host is no longer valid.
   1059  1.1  christos static void
   1060  1.1  christos host_invalidate(adv_host_t *host)
   1061  1.1  christos {
   1062  1.1  christos     // Get rid of the retry wake event.
   1063  1.1  christos     if (host->retry_wakeup != NULL) {
   1064  1.1  christos         ioloop_cancel_wake_event(host->retry_wakeup);
   1065  1.1  christos     }
   1066  1.1  christos     if (host->re_register_wakeup != NULL) {
   1067  1.1  christos         ioloop_cancel_wake_event(host->re_register_wakeup);
   1068  1.1  christos     }
   1069  1.1  christos 
   1070  1.1  christos     // Remove the address records.
   1071  1.1  christos     host_addr_free(host);
   1072  1.1  christos 
   1073  1.1  christos     // Remove the services.
   1074  1.1  christos     if (host->instances != NULL) {
   1075  1.1  christos         adv_instances_cancel(host->instances);
   1076  1.1  christos         RELEASE_HERE(host->instances, adv_instance_vec);
   1077  1.1  christos         host->instances = NULL;
   1078  1.1  christos     }
   1079  1.1  christos 
   1080  1.1  christos     if (host->update != NULL) {
   1081  1.1  christos         RELEASE_HERE(host->update, adv_update);
   1082  1.1  christos         host->update = NULL;
   1083  1.1  christos     }
   1084  1.1  christos     if (host->key_record != NULL) {
   1085  1.1  christos         srp_mdns_shared_record_remove(host->server_state, host->key_record);
   1086  1.1  christos         RELEASE_HERE(host->key_record, adv_record);
   1087  1.1  christos         host->key_record = NULL;
   1088  1.1  christos     }
   1089  1.1  christos     host->update = NULL;
   1090  1.1  christos     host->removed = true;
   1091  1.1  christos }
   1092  1.1  christos 
   1093  1.1  christos // Free everything associated with the host, including the host object.
   1094  1.1  christos static void
   1095  1.1  christos adv_host_finalize(adv_host_t *host)
   1096  1.1  christos {
   1097  1.1  christos     // Just in case this hasn't happened yet, free the non-identifying host data and cancel any outstanding
   1098  1.1  christos     // transactions.
   1099  1.1  christos     host_invalidate(host);
   1100  1.1  christos 
   1101  1.1  christos     if (host->addresses != NULL) {
   1102  1.1  christos         RELEASE_HERE(host->addresses, adv_record_vec);
   1103  1.1  christos         host->addresses = NULL;
   1104  1.1  christos     }
   1105  1.1  christos 
   1106  1.1  christos     if (host->key_rdata != NULL) {
   1107  1.1  christos         free(host->key_rdata);
   1108  1.1  christos         host->key_rdata = NULL;
   1109  1.1  christos     }
   1110  1.1  christos     if (host->key_record != NULL) {
   1111  1.1  christos         RELEASE_HERE(host->key_record, adv_record);
   1112  1.1  christos         host->key_record = NULL;
   1113  1.1  christos     }
   1114  1.1  christos 
   1115  1.1  christos     if (host->message != NULL) {
   1116  1.1  christos         ioloop_message_release(host->message);
   1117  1.1  christos         host->message = NULL;
   1118  1.1  christos     }
   1119  1.1  christos 
   1120  1.1  christos     // We definitely don't want a lease callback at this point.
   1121  1.1  christos     if (host->lease_wakeup != NULL) {
   1122  1.1  christos         ioloop_cancel_wake_event(host->lease_wakeup);
   1123  1.1  christos         ioloop_wakeup_release(host->lease_wakeup);
   1124  1.1  christos         host->lease_wakeup = NULL; // this will make us crash if we use it after free
   1125  1.1  christos     }
   1126  1.1  christos     // Get rid of the retry wake event.
   1127  1.1  christos     if (host->retry_wakeup != NULL) {
   1128  1.1  christos         ioloop_cancel_wake_event(host->retry_wakeup);
   1129  1.1  christos         ioloop_wakeup_release(host->retry_wakeup);
   1130  1.1  christos         host->retry_wakeup = NULL;
   1131  1.1  christos     }
   1132  1.1  christos 
   1133  1.1  christos     if (host->re_register_wakeup != NULL) {
   1134  1.1  christos         ioloop_cancel_wake_event(host->re_register_wakeup);
   1135  1.1  christos         ioloop_wakeup_release(host->re_register_wakeup);
   1136  1.1  christos         host->re_register_wakeup = NULL;
   1137  1.1  christos     }
   1138  1.1  christos     INFO("removed " PRI_S_SRP ", key_id %x", host->name ? host->name : "<null>", host->key_id);
   1139  1.1  christos 
   1140  1.1  christos     // In the default case, host->name and host->registered_name point to the same memory: we don't want a double free.
   1141  1.1  christos     if (host->registered_name == host->name) {
   1142  1.1  christos         host->registered_name = NULL;
   1143  1.1  christos     }
   1144  1.1  christos     if (host->name != NULL) {
   1145  1.1  christos         free(host->name);
   1146  1.1  christos     }
   1147  1.1  christos     if (host->registered_name != NULL) {
   1148  1.1  christos         free(host->registered_name);
   1149  1.1  christos     }
   1150  1.1  christos     free(host);
   1151  1.1  christos }
   1152  1.1  christos 
   1153  1.1  christos void
   1154  1.1  christos srp_adv_host_release_(adv_host_t *host, const char *file, int line)
   1155  1.1  christos {
   1156  1.1  christos     RELEASE(host, adv_host);
   1157  1.1  christos }
   1158  1.1  christos 
   1159  1.1  christos void
   1160  1.1  christos srp_adv_host_retain_(adv_host_t *host, const char *file, int line)
   1161  1.1  christos {
   1162  1.1  christos     RETAIN(host, adv_host);
   1163  1.1  christos }
   1164  1.1  christos 
   1165  1.1  christos bool
   1166  1.1  christos srp_adv_host_valid(adv_host_t *host)
   1167  1.1  christos {
   1168  1.1  christos     // If the host has been removed, it's not valid.
   1169  1.1  christos     if (host->removed) {
   1170  1.1  christos         return false;
   1171  1.1  christos     }
   1172  1.1  christos     // If there is no key data, the host is invalid.
   1173  1.1  christos     if (host->key_rdata == NULL) {
   1174  1.1  christos         return false;
   1175  1.1  christos     }
   1176  1.1  christos     return true;
   1177  1.1  christos }
   1178  1.1  christos 
   1179  1.1  christos int
   1180  1.1  christos srp_current_valid_host_count(srp_server_t *server_state)
   1181  1.1  christos {
   1182  1.1  christos     adv_host_t *host;
   1183  1.1  christos     int count = 0;
   1184  1.1  christos     for (host = server_state->hosts; host; host = host->next) {
   1185  1.1  christos         if (srp_adv_host_valid(host)) {
   1186  1.1  christos             count++;
   1187  1.1  christos         }
   1188  1.1  christos     }
   1189  1.1  christos     return count;
   1190  1.1  christos }
   1191  1.1  christos 
   1192  1.1  christos int
   1193  1.1  christos srp_hosts_to_array(srp_server_t *server_state, adv_host_t **host_array, int max)
   1194  1.1  christos {
   1195  1.1  christos     int count = 0;
   1196  1.1  christos     for (adv_host_t *host = server_state->hosts; count < max && host != NULL; host = host->next) {
   1197  1.1  christos         if (srp_adv_host_valid(host)) {
   1198  1.1  christos             host_array[count] = host;
   1199  1.1  christos             RETAIN_HERE(host_array[count], adv_host);
   1200  1.1  christos             count++;
   1201  1.1  christos         }
   1202  1.1  christos     }
   1203  1.1  christos     return count;
   1204  1.1  christos }
   1205  1.1  christos 
   1206  1.1  christos adv_host_t *
   1207  1.1  christos srp_adv_host_copy_(srp_server_t *server_state, dns_name_t *name, const char *file, int line)
   1208  1.1  christos {
   1209  1.1  christos     for (adv_host_t *host = server_state->hosts; host; host = host->next) {
   1210  1.1  christos         if (srp_adv_host_valid(host) && dns_names_equal_text(name, host->name)) {
   1211  1.1  christos             RETAIN(host, adv_host);
   1212  1.1  christos             return host;
   1213  1.1  christos         }
   1214  1.1  christos     }
   1215  1.1  christos     return NULL;
   1216  1.1  christos }
   1217  1.1  christos 
   1218  1.1  christos static void
   1219  1.1  christos host_remove(adv_host_t *host)
   1220  1.1  christos {
   1221  1.1  christos     // This host is no longer valid. Get rid of the associated transactions and other stuff that's not required to
   1222  1.1  christos     // identify it, and then release the host list reference to it.
   1223  1.1  christos     host_invalidate(host);
   1224  1.1  christos     // Note that while adv_host_finalize calls host_invalidate, adv_host_finalize won't necessarily be called here because there
   1225  1.1  christos     // may be outstanding references on the host. It's okay to call host_invalidate twice--the second time it should be
   1226  1.1  christos     // a no-op.
   1227  1.1  christos     RELEASE_HERE(host, adv_host);
   1228  1.1  christos }
   1229  1.1  christos 
   1230  1.1  christos static adv_host_t **
   1231  1.1  christos host_ready(adv_host_t *host)
   1232  1.1  christos {
   1233  1.1  christos     adv_host_t **p_hosts;
   1234  1.1  christos 
   1235  1.1  christos     // Find the host on the list of hosts.
   1236  1.1  christos     for (p_hosts = &host->server_state->hosts; *p_hosts != NULL; p_hosts = &(*p_hosts)->next) {
   1237  1.1  christos         if (*p_hosts == host) {
   1238  1.1  christos             break;
   1239  1.1  christos         }
   1240  1.1  christos     }
   1241  1.1  christos     if (*p_hosts == NULL) {
   1242  1.1  christos         ERROR("called with nonexistent host.");
   1243  1.1  christos         return NULL;
   1244  1.1  christos     }
   1245  1.1  christos 
   1246  1.1  christos     // It's possible that we got an update to this host, but haven't processed it yet.  In this
   1247  1.1  christos     // case, we don't want to get rid of the host, but we do want to get rid of it later if the
   1248  1.1  christos     // update fails.  So postpone the removal for a bit.
   1249  1.1  christos     if (host->update != NULL) {
   1250  1.1  christos         INFO("reached with pending updates on host " PRI_S_SRP ".", host->registered_name);
   1251  1.1  christos         ioloop_add_wake_event(host->lease_wakeup, host, lease_callback, srp_adv_host_context_release, 10 * 1000);
   1252  1.1  christos         RETAIN_HERE(host, adv_host);
   1253  1.1  christos         host->lease_expiry = ioloop_timenow() + 10 * 1000; // ten seconds
   1254  1.1  christos         return NULL;
   1255  1.1  christos     }
   1256  1.1  christos 
   1257  1.1  christos     return p_hosts;
   1258  1.1  christos }
   1259  1.1  christos 
   1260  1.1  christos static void
   1261  1.1  christos lease_callback(void *context)
   1262  1.1  christos {
   1263  1.1  christos     int64_t now = ioloop_timenow();
   1264  1.1  christos     adv_host_t **p_hosts, *host = context;
   1265  1.1  christos     int i, num_instances = 0;
   1266  1.1  christos 
   1267  1.1  christos     p_hosts = host_ready(host);
   1268  1.1  christos     if (p_hosts == NULL) {
   1269  1.1  christos         INFO("host expired");
   1270  1.1  christos         return;
   1271  1.1  christos     }
   1272  1.1  christos 
   1273  1.1  christos     INFO("host " PRI_S_SRP, host->name);
   1274  1.1  christos 
   1275  1.1  christos     // If the host entry lease has expired, any instance leases have also.
   1276  1.1  christos     if (host->lease_expiry < now) {
   1277  1.1  christos         delete_host(host);
   1278  1.1  christos         return;
   1279  1.1  christos     }
   1280  1.1  christos 
   1281  1.1  christos     INFO("host " PRI_S_SRP " is still alive", host->name);
   1282  1.1  christos 
   1283  1.1  christos     if (host->instances == NULL) {
   1284  1.1  christos         INFO("no instances");
   1285  1.1  christos         return;
   1286  1.1  christos     }
   1287  1.1  christos 
   1288  1.1  christos     // Find instances that have expired and release them.
   1289  1.1  christos     for (i = 0; i < host->instances->num; i++) {
   1290  1.1  christos         adv_instance_t *instance = host->instances->vec[i];
   1291  1.1  christos         if (instance == NULL) {
   1292  1.1  christos             continue;
   1293  1.1  christos         }
   1294  1.1  christos         if (instance->lease_expiry < now) {
   1295  1.1  christos             INFO("host " PRI_S_SRP " instance " PRI_S_SRP "." PRI_S_SRP " has expired",
   1296  1.1  christos                  host->name, instance->instance_name, instance->service_type);
   1297  1.1  christos             // We have to release the transaction so that we can release the reference the transaction has to the instance.
   1298  1.1  christos             if (instance->txn != NULL) {
   1299  1.1  christos                 dnssd_txn_t *txn = instance->txn;
   1300  1.1  christos                 instance->txn = NULL;
   1301  1.1  christos                 ioloop_dnssd_txn_release(txn);
   1302  1.1  christos             }
   1303  1.1  christos             host->instances->vec[i] = NULL;
   1304  1.1  christos             RELEASE_HERE(instance, adv_instance);
   1305  1.1  christos             continue;
   1306  1.1  christos         } else {
   1307  1.1  christos             INFO("host " PRI_S_SRP " instance " PRI_S_SRP "." PRI_S_SRP " has not expired",
   1308  1.1  christos                  host->name, instance->instance_name, instance->service_type);
   1309  1.1  christos         }
   1310  1.1  christos         num_instances++;
   1311  1.1  christos     }
   1312  1.1  christos 
   1313  1.1  christos     int64_t next_lease_expiry = host->lease_expiry;
   1314  1.1  christos 
   1315  1.1  christos     // Get rid of holes in the host instance vector and compute the next lease callback time
   1316  1.1  christos     int j = 0;
   1317  1.1  christos 
   1318  1.1  christos     for (i = 0; i < host->instances->num; i++) {
   1319  1.1  christos         if (host->instances->vec[i] != NULL) {
   1320  1.1  christos             adv_instance_t *instance = host->instances->vec[i];
   1321  1.1  christos             host->instances->vec[j++] = instance;
   1322  1.1  christos             if (next_lease_expiry > instance->lease_expiry) {
   1323  1.1  christos                 next_lease_expiry = instance->lease_expiry;
   1324  1.1  christos             }
   1325  1.1  christos         }
   1326  1.1  christos     }
   1327  1.1  christos     INFO("host " PRI_S_SRP " lost %d instances", host->name, host->instances->num - j);
   1328  1.1  christos     host->instances->num = j;
   1329  1.1  christos 
   1330  1.1  christos     // Now set a timer for the next lease expiry event
   1331  1.1  christos     uint64_t when = next_lease_expiry - now;
   1332  1.1  christos     if (when > INT32_MAX) {
   1333  1.1  christos         when = INT32_MAX;
   1334  1.1  christos     }
   1335  1.1  christos 
   1336  1.1  christos     ioloop_add_wake_event(host->lease_wakeup, host, lease_callback, srp_adv_host_context_release, (uint32_t)when);
   1337  1.1  christos     RETAIN_HERE(host, adv_host);
   1338  1.1  christos }
   1339  1.1  christos 
   1340  1.1  christos // Called when we definitely want to make all the advertisements associated with a host go away.
   1341  1.1  christos static void
   1342  1.1  christos delete_host(void *context)
   1343  1.1  christos {
   1344  1.1  christos     adv_host_t **p_hosts, *host = context;
   1345  1.1  christos 
   1346  1.1  christos 
   1347  1.1  christos     p_hosts = host_ready(host);
   1348  1.1  christos     if (p_hosts == NULL) {
   1349  1.1  christos         return;
   1350  1.1  christos     }
   1351  1.1  christos 
   1352  1.1  christos     INFO("deleting host " PRI_S_SRP, host->name);
   1353  1.1  christos 
   1354  1.1  christos     // De-link the host.
   1355  1.1  christos     *p_hosts = host->next;
   1356  1.1  christos 
   1357  1.1  christos     // Get rid of any transactions attached to the host, any timer events, and any other associated data.
   1358  1.1  christos     host_remove(host);
   1359  1.1  christos }
   1360  1.1  christos 
   1361  1.1  christos // We remember the message that produced this instance so that if we get an update that doesn't update everything,
   1362  1.1  christos // we know which instances /were/ updated by this particular message. instance->recent_message is a copy of the pointer
   1363  1.1  christos // to the message that most recently updated this instance. When we set instance->recent_message, we don't yet know
   1364  1.1  christos // if the update is going to succeed; if it fails, we can't have changed update->message. If it succeeds, then when we
   1365  1.1  christos // get down to srp_mdns_update_finished, we can compare the message that did the update to instance->recent_message; if they
   1366  1.1  christos // are the same, then we set the message on the instance.
   1367  1.1  christos // Note that we only set instance->recent_message during register_instance_completion, so there's no timing race that
   1368  1.1  christos // could happen as a result of receiving a second update to the same instance before the first has been processed.
   1369  1.1  christos static void
   1370  1.1  christos set_instance_message(adv_instance_t *instance, message_t *message)
   1371  1.1  christos {
   1372  1.1  christos     if (message != NULL && (ptrdiff_t)message == instance->recent_message) {
   1373  1.1  christos         if (instance->message != NULL) {
   1374  1.1  christos             ioloop_message_release(instance->message);
   1375  1.1  christos         }
   1376  1.1  christos         instance->message = message;
   1377  1.1  christos         ioloop_message_retain(instance->message);
   1378  1.1  christos         instance->recent_message = 0;
   1379  1.1  christos     }
   1380  1.1  christos }
   1381  1.1  christos 
   1382  1.1  christos void
   1383  1.1  christos srp_mdns_update_finished(adv_update_t *update)
   1384  1.1  christos {
   1385  1.1  christos     adv_host_t *host = update->host;
   1386  1.1  christos     client_update_t *client = update->client;
   1387  1.1  christos     int num_addresses = 0;
   1388  1.1  christos     adv_record_vec_t *addresses = NULL;
   1389  1.1  christos     int num_instances = 0;
   1390  1.1  christos     adv_instance_vec_t *instances = NULL;
   1391  1.1  christos     int i, j;
   1392  1.1  christos     int num_host_addresses = 0;
   1393  1.1  christos     int num_add_addresses = 0;
   1394  1.1  christos     int num_host_instances = 0;
   1395  1.1  christos     int num_add_instances = 0;
   1396  1.1  christos     message_t *message = NULL;
   1397  1.1  christos     client_update_t *remaining_updates = NULL;
   1398  1.1  christos     srp_server_t *server_state = host->server_state;
   1399  1.1  christos 
   1400  1.1  christos     // Get the message that produced the update, if any
   1401  1.1  christos     if (client != NULL) {
   1402  1.1  christos         message = client->message;
   1403  1.1  christos     }
   1404  1.1  christos 
   1405  1.1  christos     // Once an update has finished, we need to apply all of the proposed changes to the host object.
   1406  1.1  christos     if (host->addresses != NULL) {
   1407  1.1  christos         for (i = 0; i < host->addresses->num; i++) {
   1408  1.1  christos             if (host->addresses->vec[i] != NULL &&
   1409  1.1  christos                 (update->remove_addresses == NULL || update->remove_addresses->vec[i] == NULL))
   1410  1.1  christos             {
   1411  1.1  christos                 num_host_addresses++;
   1412  1.1  christos             }
   1413  1.1  christos         }
   1414  1.1  christos     }
   1415  1.1  christos 
   1416  1.1  christos     if (update->add_addresses != NULL) {
   1417  1.1  christos         for (i = 0; i < update->add_addresses->num; i++) {
   1418  1.1  christos             if (update->add_addresses->vec[i] != NULL) {
   1419  1.1  christos                 num_add_addresses++;
   1420  1.1  christos             }
   1421  1.1  christos         }
   1422  1.1  christos     }
   1423  1.1  christos 
   1424  1.1  christos     num_addresses = num_host_addresses + num_add_addresses;
   1425  1.1  christos     if (num_addresses > 0) {
   1426  1.1  christos         addresses = adv_record_vec_create(num_addresses);
   1427  1.1  christos         if (addresses == NULL) {
   1428  1.1  christos             update_failed(update, dns_rcode_servfail, true, true);
   1429  1.1  christos             return;
   1430  1.1  christos         }
   1431  1.1  christos 
   1432  1.1  christos         j = 0;
   1433  1.1  christos 
   1434  1.1  christos         if (host->addresses != NULL) {
   1435  1.1  christos             for (i = 0; i < host->addresses->num; i++) {
   1436  1.1  christos                 adv_record_t *rec = host->addresses->vec[i];
   1437  1.1  christos                 if (rec != NULL && (update->remove_addresses == NULL || update->remove_addresses->vec[i] == NULL))
   1438  1.1  christos                 {
   1439  1.1  christos #ifdef DEBUG_VERBOSE
   1440  1.1  christos                     uint8_t *rdp = rec->rdata;
   1441  1.1  christos                     if (rec->rrtype == dns_rrtype_aaaa) {
   1442  1.1  christos                         SEGMENTED_IPv6_ADDR_GEN_SRP(rdp, rdp_buf);
   1443  1.1  christos                         INFO("retaining " PRI_SEGMENTED_IPv6_ADDR_SRP "on host " PRI_S_SRP,
   1444  1.1  christos                              SEGMENTED_IPv6_ADDR_PARAM_SRP(rdp, rdp_buf), host->registered_name);
   1445  1.1  christos                     } else {
   1446  1.1  christos                         IPv4_ADDR_GEN_SRP(rdp, rdp_buf);
   1447  1.1  christos                         INFO("retaining " PRI_IPv4_ADDR_SRP "on host " PRI_S_SRP,
   1448  1.1  christos                              IPv4_ADDR_PARAM_SRP(rdp, rdp_buf), host->registered_name);
   1449  1.1  christos                     }
   1450  1.1  christos #endif
   1451  1.1  christos                     addresses->vec[j] = rec;
   1452  1.1  christos                     RETAIN_HERE(addresses->vec[j], adv_record);
   1453  1.1  christos                     j++;
   1454  1.1  christos                 }
   1455  1.1  christos             }
   1456  1.1  christos         }
   1457  1.1  christos         if (update->add_addresses != NULL) {
   1458  1.1  christos             for (i = 0; i < update->add_addresses->num; i++) {
   1459  1.1  christos                 adv_record_t *rec = update->add_addresses->vec[i];
   1460  1.1  christos                 if (rec != NULL) {
   1461  1.1  christos #ifdef DEBUG_VERBOSE
   1462  1.1  christos                     uint8_t *rdp = rec->rdata;
   1463  1.1  christos                     if (rec->rrtype == dns_rrtype_aaaa) {
   1464  1.1  christos                         SEGMENTED_IPv6_ADDR_GEN_SRP(rdp, rdp_buf);
   1465  1.1  christos                         INFO("adding " PRI_SEGMENTED_IPv6_ADDR_SRP "to host " PRI_S_SRP,
   1466  1.1  christos                              SEGMENTED_IPv6_ADDR_PARAM_SRP(rdp, rdp_buf), host->registered_name);
   1467  1.1  christos                     } else {
   1468  1.1  christos                         IPv4_ADDR_GEN_SRP(rdp, rdp_buf);
   1469  1.1  christos                         INFO("adding " PRI_IPv4_ADDR_SRP "to host " PRI_S_SRP,
   1470  1.1  christos                              IPv4_ADDR_PARAM_SRP(rdp, rdp_buf), host->registered_name);
   1471  1.1  christos                     }
   1472  1.1  christos #endif
   1473  1.1  christos                     addresses->vec[j] = rec;
   1474  1.1  christos                     RETAIN_HERE(addresses->vec[j], adv_record);
   1475  1.1  christos                     j++;
   1476  1.1  christos                     if (rec->update != NULL) {
   1477  1.1  christos                         RELEASE_HERE(update->add_addresses->vec[i]->update, adv_update);
   1478  1.1  christos                         update->add_addresses->vec[i]->update = NULL;
   1479  1.1  christos                     }
   1480  1.1  christos                     RELEASE_HERE(update->add_addresses->vec[i], adv_record);
   1481  1.1  christos                     update->add_addresses->vec[i] = NULL;
   1482  1.1  christos                 }
   1483  1.1  christos             }
   1484  1.1  christos         }
   1485  1.1  christos         addresses->num = j;
   1486  1.1  christos     }
   1487  1.1  christos 
   1488  1.1  christos     // Do the same for instances.
   1489  1.1  christos     if (host->instances != NULL) {
   1490  1.1  christos         for (i = 0; i < host->instances->num; i++) {
   1491  1.1  christos             // We're counting the number of non-NULL instances in the host instance vector, which is probably always
   1492  1.1  christos             // going to be the same as host->instances->num, but we are not relying on this.
   1493  1.1  christos             if (host->instances->vec[i] != NULL) {
   1494  1.1  christos                 num_host_instances++;
   1495  1.1  christos             }
   1496  1.1  christos         }
   1497  1.1  christos     }
   1498  1.1  christos 
   1499  1.1  christos     if (update->add_instances != NULL) {
   1500  1.1  christos         for (i = 0; i < update->add_instances->num; i++) {
   1501  1.1  christos             if (update->add_instances->vec[i] != NULL) {
   1502  1.1  christos                 num_add_instances++;
   1503  1.1  christos             }
   1504  1.1  christos         }
   1505  1.1  christos     }
   1506  1.1  christos 
   1507  1.1  christos     num_instances = num_add_instances + num_host_instances;
   1508  1.1  christos     instances = adv_instance_vec_create(num_instances);
   1509  1.1  christos     if (instances == NULL) {
   1510  1.1  christos         if (addresses != NULL) {
   1511  1.1  christos             RELEASE_HERE(addresses, adv_record_vec);
   1512  1.1  christos             addresses = NULL;
   1513  1.1  christos         }
   1514  1.1  christos         update_failed(update, dns_rcode_servfail, true, true);
   1515  1.1  christos         return;
   1516  1.1  christos     }
   1517  1.1  christos 
   1518  1.1  christos     j = 0;
   1519  1.1  christos     if (host->instances != NULL) {
   1520  1.1  christos         for (i = 0; i < host->instances->num; i++) {
   1521  1.1  christos             if (j == num_instances) {
   1522  1.1  christos                 FAULT("j (%d) == num_instances (%d)", j, num_instances);
   1523  1.1  christos                 break;
   1524  1.1  christos             }
   1525  1.1  christos             if (update->update_instances != NULL && update->update_instances->vec[i] != NULL) {
   1526  1.1  christos                 adv_instance_t *instance = update->update_instances->vec[i];
   1527  1.1  christos                 if (update->remove_instances != NULL && update->remove_instances->vec[i] != NULL) {
   1528  1.1  christos                     adv_instance_t *removed_instance = update->remove_instances->vec[i];
   1529  1.1  christos                     INFO("removed instance " PRI_S_SRP " " PRI_S_SRP " %d",
   1530  1.1  christos                          removed_instance->instance_name, removed_instance->service_type, removed_instance->port);
   1531  1.1  christos                     INFO("added instance " PRI_S_SRP " " PRI_S_SRP " %d",
   1532  1.1  christos                          instance->instance_name, instance->service_type, instance->port);
   1533  1.1  christos                 } else {
   1534  1.1  christos                     INFO("updated instance " PRI_S_SRP " " PRI_S_SRP " %d",
   1535  1.1  christos                          instance->instance_name, instance->service_type, instance->port);
   1536  1.1  christos                 }
   1537  1.1  christos                 instances->vec[j] = instance;
   1538  1.1  christos                 RETAIN_HERE(instances->vec[j], adv_instance);
   1539  1.1  christos                 j++;
   1540  1.1  christos                 RELEASE_HERE(update->update_instances->vec[i], adv_instance);
   1541  1.1  christos                 update->update_instances->vec[i] = NULL;
   1542  1.1  christos                 if (instance->update != NULL) {
   1543  1.1  christos                     RELEASE_HERE(instance->update, adv_update);
   1544  1.1  christos                     instance->update = NULL;
   1545  1.1  christos                 }
   1546  1.1  christos                 set_instance_message(instance, message);
   1547  1.1  christos             } else {
   1548  1.1  christos                 if (update->remove_instances != NULL && update->remove_instances->vec[i] != NULL) {
   1549  1.1  christos                     adv_instance_t *instance = update->remove_instances->vec[i];
   1550  1.1  christos                     INFO("removed instance " PRI_S_SRP " " PRI_S_SRP " %d",
   1551  1.1  christos                          instance->instance_name, instance->service_type, instance->port);
   1552  1.1  christos                     instances->vec[j] = instance;
   1553  1.1  christos                     RETAIN_HERE(instances->vec[j], adv_instance);
   1554  1.1  christos                     j++;
   1555  1.1  christos                     instance->removed = true;
   1556  1.1  christos                     if (message != NULL) {
   1557  1.1  christos                         if (instance->message != NULL) {
   1558  1.1  christos                             ioloop_message_release(instance->message);
   1559  1.1  christos                         }
   1560  1.1  christos                         instance->message = message;
   1561  1.1  christos                         ioloop_message_retain(instance->message);
   1562  1.1  christos                     }
   1563  1.1  christos                     if (instance->txn == NULL) {
   1564  1.1  christos                         ERROR("instance " PRI_S_SRP "." PRI_S_SRP " for host " PRI_S_SRP " has no connection.",
   1565  1.1  christos                               instance->instance_name, instance->service_type, host->name);
   1566  1.1  christos                     } else {
   1567  1.1  christos                         ioloop_dnssd_txn_cancel_srp(host->server_state, instance->txn);
   1568  1.1  christos                         ioloop_dnssd_txn_release(instance->txn);
   1569  1.1  christos                         instance->txn = NULL;
   1570  1.1  christos                     }
   1571  1.1  christos                 } else {
   1572  1.1  christos                     if (host->instances->vec[i] != NULL) {
   1573  1.1  christos                         adv_instance_t *instance = host->instances->vec[i];
   1574  1.1  christos                         INFO("kept instance " PRI_S_SRP " " PRI_S_SRP " %d, instance->message = %p",
   1575  1.1  christos                              instance->instance_name, instance->service_type, instance->port, instance->message);
   1576  1.1  christos                         instances->vec[j] = instance;
   1577  1.1  christos                         RETAIN_HERE(instances->vec[j], adv_instance);
   1578  1.1  christos                         j++;
   1579  1.1  christos                         set_instance_message(instance, message);
   1580  1.1  christos                     }
   1581  1.1  christos                 }
   1582  1.1  christos             }
   1583  1.1  christos         }
   1584  1.1  christos     }
   1585  1.1  christos 
   1586  1.1  christos     // Set the message on all of the instances that were renewed to the current message.
   1587  1.1  christos     if (update->renew_instances != NULL) {
   1588  1.1  christos         for (i = 0; i < update->renew_instances->num; i++) {
   1589  1.1  christos             adv_instance_t *instance = update->renew_instances->vec[i];
   1590  1.1  christos             if (instance != NULL) {
   1591  1.1  christos                 if (message != NULL) { // Should never not be NULL for a renew, of course.
   1592  1.1  christos                     if (instance->message != NULL) {
   1593  1.1  christos                         ioloop_message_release(instance->message);
   1594  1.1  christos                     }
   1595  1.1  christos                     instance->message = message;
   1596  1.1  christos                     ioloop_message_retain(instance->message);
   1597  1.1  christos                 }
   1598  1.1  christos                 instance->recent_message = 0;
   1599  1.1  christos                 INFO("renewed instance " PRI_S_SRP " " PRI_S_SRP " %d",
   1600  1.1  christos                      instance->instance_name, instance->service_type, instance->port);
   1601  1.1  christos             }
   1602  1.1  christos         }
   1603  1.1  christos     }
   1604  1.1  christos 
   1605  1.1  christos     if (update->add_instances != NULL) {
   1606  1.1  christos         for (i = 0; i < update->add_instances->num; i++) {
   1607  1.1  christos             adv_instance_t *instance = update->add_instances->vec[i];
   1608  1.1  christos             if (instance != NULL) {
   1609  1.1  christos                 INFO("added instance " PRI_S_SRP " " PRI_S_SRP " %d",
   1610  1.1  christos                       instance->instance_name, instance->service_type, instance->port);
   1611  1.1  christos                 instances->vec[j] = instance;
   1612  1.1  christos                 RETAIN_HERE(instances->vec[j], adv_instance);
   1613  1.1  christos                 j++;
   1614  1.1  christos                 RELEASE_HERE(update->add_instances->vec[i], adv_instance);
   1615  1.1  christos                 update->add_instances->vec[i] = NULL;
   1616  1.1  christos                 if (instance->update != NULL) {
   1617  1.1  christos                     RELEASE_HERE(instance->update, adv_update);
   1618  1.1  christos                     instance->update = NULL;
   1619  1.1  christos                 }
   1620  1.1  christos                 set_instance_message(instance, message);
   1621  1.1  christos             }
   1622  1.1  christos         }
   1623  1.1  christos     }
   1624  1.1  christos     instances->num = j;
   1625  1.1  christos     // Clear "skip update" flag on instances.
   1626  1.1  christos     for (i = 0; i < instances->num; i++) {
   1627  1.1  christos         if (instances->vec[i] != NULL) {
   1628  1.1  christos             instances->vec[i]->skip_update = false;
   1629  1.1  christos         }
   1630  1.1  christos     }
   1631  1.1  christos 
   1632  1.1  christos     // At this point we can safely modify the host object because we aren't doing any more
   1633  1.1  christos     // allocations.
   1634  1.1  christos     if (host->addresses != NULL) {
   1635  1.1  christos         RELEASE_HERE(host->addresses, adv_record_vec);
   1636  1.1  christos     }
   1637  1.1  christos     host->addresses = addresses; // Either NULL or else returned retained by adv_record_vec_create().
   1638  1.1  christos 
   1639  1.1  christos     if (host->instances != NULL) {
   1640  1.1  christos         for (i = 0; i < host->instances->num; i++) {
   1641  1.1  christos             adv_instance_t *instance = host->instances->vec[i];
   1642  1.1  christos             if (instance != NULL) {
   1643  1.1  christos                 INFO("old host instance %d " PRI_S_SRP "." PRI_S_SRP " for host " PRI_S_SRP " has ref_count %d",
   1644  1.1  christos                      i, instance->instance_name, instance->service_type, host->name, instance->ref_count);
   1645  1.1  christos             } else {
   1646  1.1  christos                 INFO("old host instance %d is NULL", i);
   1647  1.1  christos             }
   1648  1.1  christos         }
   1649  1.1  christos         RELEASE_HERE(host->instances, adv_instance_vec);
   1650  1.1  christos     }
   1651  1.1  christos     host->instances = instances;
   1652  1.1  christos 
   1653  1.1  christos     if (host->key_record != NULL && update->key != NULL && host->key_record != update->key) {
   1654  1.1  christos         srp_mdns_shared_record_remove(host->server_state, host->key_record);
   1655  1.1  christos         RELEASE_HERE(host->key_record, adv_record);
   1656  1.1  christos         host->key_record = NULL;
   1657  1.1  christos     }
   1658  1.1  christos     if (host->key_record == NULL && update->key != NULL) {
   1659  1.1  christos         host->key_record = update->key;
   1660  1.1  christos         RETAIN_HERE(host->key_record, adv_record);
   1661  1.1  christos         if (update->key->update != NULL) {
   1662  1.1  christos             RELEASE_HERE(update->key->update, adv_update);
   1663  1.1  christos             update->key->update = NULL;
   1664  1.1  christos         }
   1665  1.1  christos     }
   1666  1.1  christos 
   1667  1.1  christos     // Remove any instances that are to be removed
   1668  1.1  christos     if (update->remove_addresses != NULL) {
   1669  1.1  christos         for (i = 0; i < update->remove_addresses->num; i++) {
   1670  1.1  christos             adv_record_t *record = update->remove_addresses->vec[i];
   1671  1.1  christos             if (record != NULL) {
   1672  1.1  christos                 srp_mdns_shared_record_remove(host->server_state, record);
   1673  1.1  christos             }
   1674  1.1  christos         }
   1675  1.1  christos     }
   1676  1.1  christos 
   1677  1.1  christos     time_t lease_offset = 0;
   1678  1.1  christos 
   1679  1.1  christos     if (client) {
   1680  1.1  christos         if (host->message != NULL) {
   1681  1.1  christos             ioloop_message_release(host->message);
   1682  1.1  christos         }
   1683  1.1  christos         host->message = client->message;
   1684  1.1  christos         ioloop_message_retain(host->message);
   1685  1.1  christos         advertise_finished(host, host->name, host->server_state, host->srpl_connection,
   1686  1.1  christos                            client->connection, client->message, dns_rcode_noerror, client, true,
   1687  1.1  christos                            client->next == NULL);
   1688  1.1  christos         remaining_updates = client->next;
   1689  1.1  christos         client->next = NULL;
   1690  1.1  christos         srp_parse_client_updates_free(client);
   1691  1.1  christos         update->client = NULL;
   1692  1.1  christos         if (host->message->received_time != 0) {
   1693  1.1  christos             host->update_time = host->message->received_time;
   1694  1.1  christos             lease_offset = srp_time() - host->update_time;
   1695  1.1  christos             INFO("setting host update time based on message received time: %ld, lease offset = %ld",
   1696  1.1  christos                  host->update_time, lease_offset);
   1697  1.1  christos         } else {
   1698  1.1  christos             INFO("setting host update time based on current time: %ld", host->message->received_time);
   1699  1.1  christos             host->update_time = srp_time();
   1700  1.1  christos         }
   1701  1.1  christos     } else {
   1702  1.1  christos         INFO("lease offset = %ld", lease_offset);
   1703  1.1  christos         lease_offset = srp_time() - host->update_time;
   1704  1.1  christos     }
   1705  1.1  christos     RETAIN_HERE(update, adv_update); // We need to hold a reference to the update since this might be the last.
   1706  1.1  christos 
   1707  1.1  christos     // The update should still be on the host.
   1708  1.1  christos     if (host->update == NULL) {
   1709  1.1  christos         ERROR("p_update is null.");
   1710  1.1  christos     } else {
   1711  1.1  christos         RELEASE_HERE(host->update, adv_update);
   1712  1.1  christos         host->update = NULL;
   1713  1.1  christos     }
   1714  1.1  christos 
   1715  1.1  christos     // Reset the retry interval, since we succeeded in updating.
   1716  1.1  christos     host->retry_interval = 0;
   1717  1.1  christos 
   1718  1.1  christos     // Set the lease time based on this update. Even if we scheduled an update for the next time we
   1719  1.1  christos     // enter the dispatch loop, we still want to schedule a lease expiry here, because it's possible
   1720  1.1  christos     // that in the process of returning to the dispatch loop, the scheduled update will be removed.
   1721  1.1  christos     if (0) {
   1722  1.1  christos #if STUB_ROUTER
   1723  1.1  christos     } else if (server_state->stub_router_enabled) {
   1724  1.1  christos         host->lease_interval = update->host_lease;
   1725  1.1  christos         host->key_lease = update->key_lease;
   1726  1.1  christos #endif
   1727  1.1  christos     } else {
   1728  1.1  christos         // For the Thread in Mobile use case, use the duration of the key lease to determine when to expire host
   1729  1.1  christos         // entries, rather than expiring them when the host lease expires. This is technically out of spec, but
   1730  1.1  christos         // accomplishes part of the stated goal of keeping usable cached data around for use immediately after
   1731  1.1  christos         // connecting to a Thread mesh.
   1732  1.1  christos         host->lease_interval = update->key_lease;
   1733  1.1  christos         host->key_lease = update->key_lease;
   1734  1.1  christos     }
   1735  1.1  christos 
   1736  1.1  christos     // It would probably be harmless to set this for replications, since the value currently wouldn't change,
   1737  1.1  christos     // but to avoid future issues we only set this if it's a direct SRP update and not a replicated update.
   1738  1.1  christos     // We know it's a direct SRP update because host->message->lease is zero. It would not be zero if we
   1739  1.1  christos     // had received this as an SRP update, but is always zero when received directly via UDP.
   1740  1.1  christos     INFO("host->message->lease = %d, host->lease_interval = %d, host->key_lease = %d",
   1741  1.1  christos          host->message->lease, host->lease_interval, host->key_lease);
   1742  1.1  christos     if (host->message->lease == 0) {
   1743  1.1  christos         host->message->lease = host->lease_interval;
   1744  1.1  christos         host->message->key_lease = host->key_lease;
   1745  1.1  christos     }
   1746  1.1  christos 
   1747  1.1  christos     // We want the lease expiry event to fire the next time the lease on any instance expires, or
   1748  1.1  christos     // at the time the lease for the current update would expire, whichever is sooner.
   1749  1.1  christos     int64_t next_lease_expiry = INT64_MAX;
   1750  1.1  christos     int64_t now = ioloop_timenow();
   1751  1.1  christos 
   1752  1.1  christos #define LEASE_EXPIRY_DEBUGGING 1
   1753  1.1  christos     // update->lease_expiry is nonzero if we are re-doing a previous registration.
   1754  1.1  christos     if (update->lease_expiry != 0) {
   1755  1.1  christos         if (update->lease_expiry < now) {
   1756  1.1  christos #ifdef LEASE_EXPIRY_DEBUGGING
   1757  1.1  christos             ERROR("lease expiry for host " PRI_S_SRP " happened %" PRIu64 " milliseconds ago.",
   1758  1.1  christos                   host->registered_name, now - update->lease_expiry);
   1759  1.1  christos #endif
   1760  1.1  christos             // Expire the lease when next we hit the run loop
   1761  1.1  christos             next_lease_expiry = now;
   1762  1.1  christos         } else {
   1763  1.1  christos #ifdef LEASE_EXPIRY_DEBUGGING
   1764  1.1  christos             INFO("lease_expiry (1) for host " PRI_S_SRP " set to %" PRId64, host->name,
   1765  1.1  christos                  (int64_t)(update->lease_expiry - now));
   1766  1.1  christos #endif
   1767  1.1  christos             next_lease_expiry = update->lease_expiry;
   1768  1.1  christos         }
   1769  1.1  christos         host->lease_expiry = update->lease_expiry;
   1770  1.1  christos     }
   1771  1.1  christos     // This is the more usual case.
   1772  1.1  christos     else {
   1773  1.1  christos #ifdef LEASE_EXPIRY_DEBUGGING
   1774  1.1  christos         INFO("lease_expiry (2) for host " PRI_S_SRP " set to %ld", host->name, (host->lease_interval - lease_offset) * 1000);
   1775  1.1  christos #endif
   1776  1.1  christos         next_lease_expiry = now + (host->lease_interval - lease_offset) * 1000;
   1777  1.1  christos         if (next_lease_expiry < now) {
   1778  1.1  christos             next_lease_expiry = now;
   1779  1.1  christos         }
   1780  1.1  christos         host->lease_expiry = next_lease_expiry;
   1781  1.1  christos     }
   1782  1.1  christos 
   1783  1.1  christos     // We're doing two things here: setting the lease expiry on instances that were touched by the current
   1784  1.1  christos     // update, and also finding the soonest update.
   1785  1.1  christos     for (i = 0; i < host->instances->num; i++) {
   1786  1.1  christos         adv_instance_t *instance = host->instances->vec[i];
   1787  1.1  christos 
   1788  1.1  christos         if (instance != NULL) {
   1789  1.1  christos             // This instance was updated by the current update, so set its lease time to
   1790  1.1  christos             // next_lease_expiry.
   1791  1.1  christos             if (instance->message == message) {
   1792  1.1  christos                 if (instance->removed) {
   1793  1.1  christos #ifdef LEASE_EXPIRY_DEBUGGING
   1794  1.1  christos                     INFO("lease_expiry (7) for host " PRI_S_SRP " removed instance " PRI_S_SRP "." PRI_S_SRP
   1795  1.1  christos                          " left at %" PRId64, host->name, instance->instance_name, instance->service_type,
   1796  1.1  christos                          (int64_t)(instance->lease_expiry - now));
   1797  1.1  christos #endif
   1798  1.1  christos                 } else {
   1799  1.1  christos #ifdef LEASE_EXPIRY_DEBUGGING
   1800  1.1  christos                     INFO("lease_expiry (4) for host " PRI_S_SRP " instance " PRI_S_SRP "." PRI_S_SRP " set to %" PRId64,
   1801  1.1  christos                          host->name, instance->instance_name, instance->service_type,
   1802  1.1  christos                          (int64_t)(host->lease_expiry - now));
   1803  1.1  christos #endif
   1804  1.1  christos                     instance->lease_expiry = host->lease_expiry;
   1805  1.1  christos                 }
   1806  1.1  christos             }
   1807  1.1  christos             // Instance was not updated by this update, so see if it expires sooner than this update
   1808  1.1  christos             // (which is likely).
   1809  1.1  christos             else if (instance->lease_expiry > now && instance->lease_expiry < next_lease_expiry) {
   1810  1.1  christos #ifdef LEASE_EXPIRY_DEBUGGING
   1811  1.1  christos                 INFO("lease_expiry (3) for host " PRI_S_SRP " instance " PRI_S_SRP "." PRI_S_SRP " set to %" PRId64,
   1812  1.1  christos                      host->name, instance->instance_name, instance->service_type,
   1813  1.1  christos                      (int64_t)(instance->lease_expiry - now));
   1814  1.1  christos #endif
   1815  1.1  christos                 next_lease_expiry = instance->lease_expiry;
   1816  1.1  christos             } else {
   1817  1.1  christos                 if (instance->lease_expiry <= now) {
   1818  1.1  christos #ifdef LEASE_EXPIRY_DEBUGGING
   1819  1.1  christos                     INFO("lease_expiry (5) for host " PRI_S_SRP " instance " PRI_S_SRP "." PRI_S_SRP
   1820  1.1  christos                          " in the past at %" PRId64,
   1821  1.1  christos                          host->name, instance->instance_name, instance->service_type,
   1822  1.1  christos                          (int64_t)(now - instance->lease_expiry));
   1823  1.1  christos #endif
   1824  1.1  christos                     next_lease_expiry = now;
   1825  1.1  christos #ifdef LEASE_EXPIRY_DEBUGGING
   1826  1.1  christos                 } else {
   1827  1.1  christos                     INFO("lease_expiry (6) for host " PRI_S_SRP " instance " PRI_S_SRP "." PRI_S_SRP
   1828  1.1  christos                          " is later than next_lease_expiry by %" PRId64, host->name, instance->instance_name,
   1829  1.1  christos                          instance->service_type, (int64_t)(next_lease_expiry - instance->lease_expiry));
   1830  1.1  christos 
   1831  1.1  christos #endif
   1832  1.1  christos                 }
   1833  1.1  christos             }
   1834  1.1  christos         }
   1835  1.1  christos     }
   1836  1.1  christos 
   1837  1.1  christos     // Now set a timer for the next lease expiry.
   1838  1.1  christos     uint64_t when = next_lease_expiry - now;
   1839  1.1  christos     if (when > INT32_MAX) {
   1840  1.1  christos         when = INT32_MAX;
   1841  1.1  christos     }
   1842  1.1  christos 
   1843  1.1  christos     if (next_lease_expiry == now) {
   1844  1.1  christos         INFO("scheduling immediate call to lease_callback in the run loop for " PRI_S_SRP, host->name);
   1845  1.1  christos         ioloop_run_async(lease_callback, host);
   1846  1.1  christos     } else {
   1847  1.1  christos         INFO("scheduling wakeup to lease_callback in %" PRIu64 " for host " PRI_S_SRP,
   1848  1.1  christos              when / 1000, host->name);
   1849  1.1  christos         ioloop_add_wake_event(host->lease_wakeup, host, lease_callback, srp_adv_host_context_release, (uint32_t)when);
   1850  1.1  christos         RETAIN_HERE(host, adv_host);
   1851  1.1  christos     }
   1852  1.1  christos 
   1853  1.1  christos     // Instance vectors can hold circular references to the update object, which won't get freed until we call
   1854  1.1  christos     // adv_update_finalize, which we will never do because of the circular reference. So break any remaining
   1855  1.1  christos     // circular references before releasing the update.
   1856  1.1  christos     adv_update_free_instance_vectors(update);
   1857  1.1  christos 
   1858  1.1  christos     // This is letting go of the reference we retained earlier in this function, not some outstanding reference retained elsewhere.
   1859  1.1  christos     RELEASE_HERE(update, adv_update);
   1860  1.1  christos 
   1861  1.1  christos     // If we were processing an SRP update, we may have additional updates to do. Start the next one now if so.
   1862  1.1  christos     if (remaining_updates != NULL) {
   1863  1.1  christos         srp_update_start(remaining_updates);
   1864  1.1  christos     } else {
   1865  1.1  christos         srp_dump_server_stats(server_state, false, false);
   1866  1.1  christos     }
   1867  1.1  christos }
   1868  1.1  christos 
   1869  1.1  christos #ifdef USE_DNSSERVICE_QUEUING
   1870  1.1  christos static void
   1871  1.1  christos process_dnsservice_error(adv_update_t *update, int err)
   1872  1.1  christos {
   1873  1.1  christos     if (err != kDNSServiceErr_NoError) {
   1874  1.1  christos         update_failed(update, dns_rcode_servfail, true, true);
   1875  1.1  christos         if (err == kDNSServiceErr_ServiceNotRunning || err == kDNSServiceErr_DefunctConnection || err == 1) {
   1876  1.1  christos             if (err == 1) {
   1877  1.1  christos                 FAULT("bogus error code 1");
   1878  1.1  christos             }
   1879  1.1  christos             if (update->host != NULL) {
   1880  1.1  christos                 if (update->host->server_state != NULL) {
   1881  1.1  christos                     service_disconnected(update->host->server_state,
   1882  1.1  christos                                          (intptr_t)update->host->server_state->shared_registration_txn);
   1883  1.1  christos                 }
   1884  1.1  christos                 wait_retry(update->host);
   1885  1.1  christos             }
   1886  1.1  christos         }
   1887  1.1  christos     }
   1888  1.1  christos }
   1889  1.1  christos #endif // USE_DNSSERVICE_QUEUING
   1890  1.1  christos 
   1891  1.1  christos #define GENERATE_WAKEUP(ptr)                  \
   1892  1.1  christos     if ((*ptr) == NULL) {                     \
   1893  1.1  christos         (*ptr) = ioloop_wakeup_create();      \
   1894  1.1  christos     }                                         \
   1895  1.1  christos     if ((*ptr) == NULL) {                     \
   1896  1.1  christos         ERROR("unable to make wakeup " #ptr); \
   1897  1.1  christos     } else
   1898  1.1  christos 
   1899  1.1  christos static void
   1900  1.1  christos srp_instance_retry_callback(void *context)
   1901  1.1  christos {
   1902  1.1  christos     adv_instance_t *instance = context;
   1903  1.1  christos     adv_host_t *host = instance->host;
   1904  1.1  christos     if (host == NULL || host->removed) {
   1905  1.1  christos         INFO("no longer updating instance %p because host is no longer valid.", instance);
   1906  1.1  christos         return;
   1907  1.1  christos     }
   1908  1.1  christos     INFO("re-registering updating instance %p.", instance);
   1909  1.1  christos     register_instance(instance);
   1910  1.1  christos }
   1911  1.1  christos 
   1912  1.1  christos static void
   1913  1.1  christos srp_schedule_instance_retry(adv_instance_t *instance)
   1914  1.1  christos {
   1915  1.1  christos     GENERATE_WAKEUP(&instance->retry_wakeup) {
   1916  1.1  christos         if (instance->wakeup_interval == 0) {
   1917  1.1  christos             instance->wakeup_interval = 5 * 1000;
   1918  1.1  christos         } else {
   1919  1.1  christos             instance->wakeup_interval *= 2;
   1920  1.1  christos         }
   1921  1.1  christos         unsigned interval = instance->wakeup_interval * 1.5 - (srp_random32() % instance->wakeup_interval);
   1922  1.1  christos         RETAIN_HERE(instance, adv_instance);
   1923  1.1  christos         ioloop_add_wake_event(instance->retry_wakeup, instance, srp_instance_retry_callback, adv_instance_context_release, interval);
   1924  1.1  christos         INFO("will attempt to reregister instance %p in %.3lf seconds", instance, ((double)interval) / 1000.0);
   1925  1.1  christos     }
   1926  1.1  christos }
   1927  1.1  christos 
   1928  1.1  christos static void
   1929  1.1  christos srp_host_record_retry_callback(void *context)
   1930  1.1  christos {
   1931  1.1  christos     adv_host_t *host = context;
   1932  1.1  christos     if (host != NULL) {
   1933  1.1  christos         host->re_register_pending = false;
   1934  1.1  christos     }
   1935  1.1  christos     if (host == NULL || host->removed) {
   1936  1.1  christos         INFO("no longer updating host %p because host is no longer valid.", host);
   1937  1.1  christos         return;
   1938  1.1  christos     }
   1939  1.1  christos 
   1940  1.1  christos     if (host->addresses != NULL) {
   1941  1.1  christos         for (int i = 0; i < host->addresses->num; i++) {
   1942  1.1  christos             adv_record_t *record = host->addresses->vec[i];
   1943  1.1  christos             if (record != NULL) {
   1944  1.1  christos                 INFO("re-registering host record %p.", record);
   1945  1.1  christos                 register_host_record(host, record, false);
   1946  1.1  christos             }
   1947  1.1  christos         }
   1948  1.1  christos     }
   1949  1.1  christos     if (host->key_record != NULL) {
   1950  1.1  christos         INFO("re-registering host record %p.", host->key_record);
   1951  1.1  christos         register_host_record(host, host->key_record, false);
   1952  1.1  christos     }
   1953  1.1  christos }
   1954  1.1  christos 
   1955  1.1  christos static void
   1956  1.1  christos srp_schedule_host_record_retry(adv_record_t *record)
   1957  1.1  christos {
   1958  1.1  christos     // If the host isn't valid or we're already re-registering, don't schedule a retry.
   1959  1.1  christos     if (record->host == NULL || record->host->removed) {
   1960  1.1  christos         INFO("will not attempt to reregister record %p", record);
   1961  1.1  christos         return;
   1962  1.1  christos     }
   1963  1.1  christos     if (record->host->re_register_pending) {
   1964  1.1  christos         INFO("already scheduled attempt to reregister record %p", record);
   1965  1.1  christos         return;
   1966  1.1  christos     }
   1967  1.1  christos 
   1968  1.1  christos     adv_host_t *host = record->host;
   1969  1.1  christos     GENERATE_WAKEUP(&host->re_register_wakeup) {
   1970  1.1  christos         if (host->wakeup_interval == 0) {
   1971  1.1  christos             host->wakeup_interval = 5 * 1000;
   1972  1.1  christos         } else {
   1973  1.1  christos             host->wakeup_interval *= 2;
   1974  1.1  christos         }
   1975  1.1  christos         unsigned interval = host->wakeup_interval * 1.5 - (srp_random32() % host->wakeup_interval);
   1976  1.1  christos         RETAIN_HERE(host, adv_host);
   1977  1.1  christos         ioloop_add_wake_event(host->re_register_wakeup, host, srp_host_record_retry_callback, srp_adv_host_context_release, interval);
   1978  1.1  christos         INFO("will attempt to reregister record %p in %.3lf seconds", record, ((double)interval) / 1000.0);
   1979  1.1  christos     }
   1980  1.1  christos }
   1981  1.1  christos 
   1982  1.1  christos 
   1983  1.1  christos // When the host registration has completed, we get this callback.   Completion either means that we succeeded in
   1984  1.1  christos // registering the record, or that something went wrong and the registration has failed.
   1985  1.1  christos static void
   1986  1.1  christos register_instance_completion(DNSServiceRef sdref, DNSServiceFlags flags, DNSServiceErrorType error_code,
   1987  1.1  christos                              const char *name, const char *regtype, const char *domain, void *context)
   1988  1.1  christos {
   1989  1.1  christos     (void)flags;
   1990  1.1  christos     (void)sdref;
   1991  1.1  christos     adv_instance_t *instance = context;
   1992  1.1  christos     adv_update_t *update = instance->update;
   1993  1.1  christos     adv_host_t *host = instance->host;
   1994  1.1  christos 
   1995  1.1  christos     // Retain the instance for the life of this function, just in case we release stuff that is holding the last reference to it.
   1996  1.1  christos     RETAIN_HERE(instance, adv_instance);
   1997  1.1  christos 
   1998  1.1  christos     // It's possible that we could restart a host update due to an error while a callback is still pending on a stale
   1999  1.1  christos     // update.  In this case, we just cancel all of the work that's been done on the stale update (it's probably already
   2000  1.1  christos     // moot anyway.
   2001  1.1  christos     if (update != NULL && host->update != update) {
   2002  1.1  christos         INFO("registration for service " PRI_S_SRP "." PRI_S_SRP " completed with invalid state.", name, regtype);
   2003  1.1  christos         RELEASE_HERE(instance->update, adv_update);
   2004  1.1  christos         instance->update = NULL;
   2005  1.1  christos         RELEASE_HERE(instance, adv_instance);
   2006  1.1  christos         return;
   2007  1.1  christos     }
   2008  1.1  christos 
   2009  1.1  christos     // We will generally get a callback on success or failure of the initial registration; this is what causes
   2010  1.1  christos     // the update to complete or fail. We may get subsequent callbacks because of name conflicts. So the first
   2011  1.1  christos     // time we get a callback, instance->update will always be valid; thereafter, it will not, so null it out.
   2012  1.1  christos     if (update != NULL) {
   2013  1.1  christos         RETAIN_HERE(update, adv_update); // We need to hold onto this until we are done with the update.
   2014  1.1  christos         RELEASE_HERE(instance->update, adv_update);
   2015  1.1  christos         instance->update = NULL;
   2016  1.1  christos     }
   2017  1.1  christos 
   2018  1.1  christos     if (error_code == kDNSServiceErr_NoError || error_code == kDNSServiceErr_NameConflict) {
   2019  1.1  christos         INFO("registration for service " PRI_S_SRP "." PRI_S_SRP "." PRI_S_SRP " -> "
   2020  1.1  christos              PRI_S_SRP " has completed" PUB_S_SRP ".", instance->instance_name, instance->service_type, domain,
   2021  1.1  christos              host->registered_name, error_code == kDNSServiceErr_NoError ? ":" : " with a conflict");
   2022  1.1  christos         INFO("registration is under " PRI_S_SRP "." PRI_S_SRP PRI_S_SRP, name, regtype,
   2023  1.1  christos              domain);
   2024  1.1  christos 
   2025  1.1  christos         if (error_code != kDNSServiceErr_NoError) {
   2026  1.1  christos             if (instance->txn == NULL) {
   2027  1.1  christos                 FAULT("instance->txn is NULL for instance %p!", instance);
   2028  1.1  christos             } else {
   2029  1.1  christos                 ioloop_dnssd_txn_cancel_srp(host->server_state, instance->txn);
   2030  1.1  christos                 ioloop_dnssd_txn_release(instance->txn);
   2031  1.1  christos                 instance->txn = NULL;
   2032  1.1  christos             }
   2033  1.1  christos             srp_schedule_instance_retry(instance);
   2034  1.1  christos         }
   2035  1.1  christos 
   2036  1.1  christos         // In principle update->instance should always be non-NULL here because a no-error response should
   2037  1.1  christos         // only happen once or not at all. But just to be safe...
   2038  1.1  christos         if (update != NULL) {
   2039  1.1  christos             if (instance->update_pending) {
   2040  1.1  christos                 if (update->client != NULL) {
   2041  1.1  christos                     instance->recent_message = (ptrdiff_t)update->client->message; // for comparison later in srp_mdns_update_finished
   2042  1.1  christos                 }
   2043  1.1  christos                 update->num_instances_completed++;
   2044  1.1  christos                 if (update->num_records_completed == update->num_records_started &&
   2045  1.1  christos                     update->num_instances_completed == update->num_instances_started)
   2046  1.1  christos                 {
   2047  1.1  christos                     srp_mdns_update_finished(update);
   2048  1.1  christos                 }
   2049  1.1  christos                 RELEASE_HERE(update, adv_update);
   2050  1.1  christos                 instance->update_pending = false;
   2051  1.1  christos                 update = NULL;
   2052  1.1  christos             }
   2053  1.1  christos         } else {
   2054  1.1  christos             INFO("re-update succeeded for instance " PRI_S_SRP " (" PRI_S_SRP
   2055  1.1  christos                   " " PRI_S_SRP " " PRI_S_SRP ")", instance->instance_name, name, regtype, domain);
   2056  1.1  christos         }
   2057  1.1  christos     } else {
   2058  1.1  christos         INFO("registration for service " PRI_S_SRP "." PRI_S_SRP "." PRI_S_SRP " -> "
   2059  1.1  christos              PRI_S_SRP " failed with code %d", instance->instance_name, instance->service_type, domain,
   2060  1.1  christos              host->registered_name, error_code);
   2061  1.1  christos 
   2062  1.1  christos         // If the reason this failed is that we couldn't talk to mDNSResponder, or mDNSResponder disconnected, then we want to retry
   2063  1.1  christos         // later on in the hopes that mDNSResponder will come back.
   2064  1.1  christos         if (error_code == kDNSServiceErr_ServiceNotRunning || error_code == kDNSServiceErr_DefunctConnection) {
   2065  1.1  christos             service_disconnected(host->server_state, instance->shared_txn);
   2066  1.1  christos             instance->shared_txn = 0;
   2067  1.1  christos             wait_retry(host);
   2068  1.1  christos         } else {
   2069  1.1  christos             if (update != NULL) {
   2070  1.1  christos                 update_failed(update, (error_code == kDNSServiceErr_NameConflict
   2071  1.1  christos                                        ? dns_rcode_yxdomain
   2072  1.1  christos                                        : dns_rcode_servfail), true, true);
   2073  1.1  christos                 if (instance->update != NULL) {
   2074  1.1  christos                     RELEASE_HERE(instance->update, adv_update);
   2075  1.1  christos                     instance->update = NULL;
   2076  1.1  christos                 }
   2077  1.1  christos                 RELEASE_HERE(update, adv_update);
   2078  1.1  christos             } else {
   2079  1.1  christos             }
   2080  1.1  christos         }
   2081  1.1  christos 
   2082  1.1  christos         // The transaction still holds a reference to the instance. instance->txn should never be NULL here. When we cancel
   2083  1.1  christos         // the transaction, the reference the transaction held on the instance will be released.
   2084  1.1  christos         if (instance->txn == NULL) {
   2085  1.1  christos             FAULT("instance->txn is NULL for instance %p!", instance);
   2086  1.1  christos         } else {
   2087  1.1  christos             ioloop_dnssd_txn_cancel_srp(host->server_state, instance->txn);
   2088  1.1  christos             ioloop_dnssd_txn_release(instance->txn);
   2089  1.1  christos             instance->txn = NULL;
   2090  1.1  christos         }
   2091  1.1  christos     }
   2092  1.1  christos     RELEASE_HERE(instance, adv_instance);
   2093  1.1  christos }
   2094  1.1  christos 
   2095  1.1  christos static bool
   2096  1.1  christos extract_instance_name(char *instance_name, size_t instance_name_max,
   2097  1.1  christos                       char *service_type, size_t service_type_max, service_instance_t *instance)
   2098  1.1  christos {
   2099  1.1  christos     dns_name_t *end_of_service_type = instance->service->rr->name->next;
   2100  1.1  christos     size_t service_index;
   2101  1.1  christos     service_t *service, *base_type;
   2102  1.1  christos     if (end_of_service_type != NULL) {
   2103  1.1  christos         if (end_of_service_type->next != NULL) {
   2104  1.1  christos             end_of_service_type = end_of_service_type->next;
   2105  1.1  christos         }
   2106  1.1  christos     }
   2107  1.1  christos     dns_name_print_to_limit(instance->service->rr->name, end_of_service_type, service_type, service_type_max);
   2108  1.1  christos 
   2109  1.1  christos     // It's possible that the registration might include subtypes. If so, we need to convert them to the
   2110  1.1  christos     // format that DNSServiceRegister expects: service_type,subtype,subtype...
   2111  1.1  christos     service_index = strlen(service_type);
   2112  1.1  christos     base_type = instance->service->base_type;
   2113  1.1  christos     for (service = instance->service->next; service != NULL && service->base_type == base_type; service = service->next)
   2114  1.1  christos     {
   2115  1.1  christos         if (service_index + service->rr->name->len + 2 > service_type_max) {
   2116  1.1  christos             ERROR("service name: " PRI_S_SRP " is too long for additional subtype " PRI_S_SRP,
   2117  1.1  christos                   service_type, service->rr->name->data);
   2118  1.1  christos             return false;
   2119  1.1  christos         }
   2120  1.1  christos         service_type[service_index++] = ',';
   2121  1.1  christos         memcpy(&service_type[service_index], service->rr->name->data, service->rr->name->len + 1);
   2122  1.1  christos         service_index += service->rr->name->len;
   2123  1.1  christos     }
   2124  1.1  christos 
   2125  1.1  christos     // Make a presentation-format version of the service instance name.
   2126  1.1  christos     dns_name_print_to_limit(instance->name, instance->name != NULL ? instance->name->next : NULL,
   2127  1.1  christos                             instance_name, instance_name_max);
   2128  1.1  christos     return true;
   2129  1.1  christos }
   2130  1.1  christos 
   2131  1.1  christos void
   2132  1.1  christos srp_format_time_offset(char *buf, size_t buf_len, time_t offset)
   2133  1.1  christos {
   2134  1.1  christos     struct tm tm_now;
   2135  1.1  christos     time_t when = time(NULL) - offset;
   2136  1.1  christos     localtime_r(&when, &tm_now);
   2137  1.1  christos     strftime(buf, buf_len, "%F %T", &tm_now);
   2138  1.1  christos }
   2139  1.1  christos 
   2140  1.1  christos DNSServiceAttributeRef
   2141  1.1  christos srp_message_tsr_attribute_generate(message_t *message, uint32_t key_id, char *time_buf, size_t time_buf_size)
   2142  1.1  christos {
   2143  1.1  christos     DNSServiceAttributeRef attribute = DNSServiceAttributeCreate();
   2144  1.1  christos     if (attribute == NULL) {
   2145  1.1  christos         ERROR("Failed to create new DNSServiceAttributeRef");
   2146  1.1  christos     } else {
   2147  1.1  christos         uint32_t offset = 0;
   2148  1.1  christos 
   2149  1.1  christos         if (message != NULL && message->received_time != 0) {
   2150  1.1  christos             offset = (uint32_t)(srp_time() - message->received_time);
   2151  1.1  christos             srp_format_time_offset(time_buf, time_buf_size, offset);
   2152  1.1  christos         } else {
   2153  1.1  christos             static char msg[] = "now";
   2154  1.1  christos             if (time_buf_size < sizeof(msg)) {
   2155  1.1  christos                 FAULT("bogus time buf size %zd", time_buf_size);
   2156  1.1  christos                 time_buf[0] = 0;
   2157  1.1  christos             } else {
   2158  1.1  christos                 memcpy(time_buf, msg, sizeof(msg));
   2159  1.1  christos             }
   2160  1.1  christos         }
   2161  1.1  christos         if (_DNSSD_API_AVAILABLE_FALL_2024) {
   2162  1.1  christos             DNSServiceAttributeSetHostKeyHash(attribute, key_id);
   2163  1.1  christos         }
   2164  1.1  christos         DNSServiceAttributeSetTimestamp(attribute, offset);
   2165  1.1  christos     }
   2166  1.1  christos     return attribute;
   2167  1.1  christos }
   2168  1.1  christos 
   2169  1.1  christos DNSServiceAttributeRef
   2170  1.1  christos srp_adv_instance_tsr_attribute_generate(adv_instance_t *instance, char *time_buf, size_t time_buf_size)
   2171  1.1  christos {
   2172  1.1  christos     message_t *message = NULL;
   2173  1.1  christos     if (instance->update != NULL && instance->update->client != NULL && instance->update->client->message != NULL) {
   2174  1.1  christos         message = instance->update->client->message;
   2175  1.1  christos     } else if (instance->update == NULL && instance->message != NULL) {
   2176  1.1  christos         message = instance->message;
   2177  1.1  christos     }
   2178  1.1  christos     return srp_message_tsr_attribute_generate(message, instance->host->key_id, time_buf, time_buf_size);
   2179  1.1  christos }
   2180  1.1  christos 
   2181  1.1  christos static bool
   2182  1.1  christos register_instance(adv_instance_t *instance)
   2183  1.1  christos {
   2184  1.1  christos     int err = kDNSServiceErr_Unknown;
   2185  1.1  christos     bool exit_status = false;
   2186  1.1  christos     srp_server_t *server_state = instance->host->server_state;
   2187  1.1  christos 
   2188  1.1  christos     // If we don't yet have a shared connection, create one.
   2189  1.1  christos     if (!srp_mdns_shared_registration_txn_setup(server_state)) {
   2190  1.1  christos         goto exit;
   2191  1.1  christos     }
   2192  1.1  christos     DNSServiceRef service_ref = server_state->shared_registration_txn->sdref;
   2193  1.1  christos 
   2194  1.1  christos     INFO(PUB_S_SRP "DNSServiceRegister(%p, " PRI_S_SRP ", " PRI_S_SRP ", " PRI_S_SRP ", %d, %p)",
   2195  1.1  christos          instance->skip_update ? "skipping " : "", service_ref, instance->instance_name, instance->service_type,
   2196  1.1  christos          instance->host->registered_name, instance->port, instance);
   2197  1.1  christos 
   2198  1.1  christos     if (instance->skip_update) {
   2199  1.1  christos         if (instance->update->client != NULL) {
   2200  1.1  christos             instance->recent_message = (ptrdiff_t)instance->update->client->message; // for comparison later in srp_mdns_update_finished
   2201  1.1  christos         }
   2202  1.1  christos         exit_status = true;
   2203  1.1  christos         goto exit;
   2204  1.1  christos     }
   2205  1.1  christos 
   2206  1.1  christos     char time_buf[TSR_TIMESTAMP_STRING_LEN];
   2207  1.1  christos     DNSServiceAttributeRef tsr_attribute =
   2208  1.1  christos         srp_adv_instance_tsr_attribute_generate(instance, time_buf, sizeof(time_buf));
   2209  1.1  christos     if (tsr_attribute == NULL) {
   2210  1.1  christos         err = kDNSServiceErr_NoMemory;
   2211  1.1  christos     } else {
   2212  1.1  christos         uint32_t flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename;
   2213  1.1  christos         if (_DNSSD_API_AVAILABLE_FALL_2024) {
   2214  1.1  christos             flags |= kDNSServiceFlagsKnownUnique;
   2215  1.1  christos         }
   2216  1.1  christos         err = dns_service_register_wa(server_state, &service_ref, flags,
   2217  1.1  christos                                       server_state->advertise_interface,
   2218  1.1  christos                                       instance->instance_name, instance->service_type, local_suffix,
   2219  1.1  christos                                       instance->host->registered_name, htons(instance->port), instance->txt_length,
   2220  1.1  christos                                       instance->txt_data, tsr_attribute, register_instance_completion, instance);
   2221  1.1  christos         DNSServiceAttributeDeallocate(tsr_attribute);
   2222  1.1  christos         if (err == kDNSServiceErr_NoError) {
   2223  1.1  christos             INFO("DNSServiceRegister, TSR for instance " PRI_S_SRP " host " PRI_S_SRP " set to " PUB_S_SRP
   2224  1.1  christos                  "(instance %p sdref %p)", instance->instance_name,
   2225  1.1  christos                  instance->host->name == NULL ? "<null>" : instance->host->name, time_buf, instance, service_ref);
   2226  1.1  christos         }
   2227  1.1  christos     }
   2228  1.1  christos 
   2229  1.1  christos     // This would happen if we pass NULL for regtype, which we don't, or if we run out of memory, or if
   2230  1.1  christos     // the server isn't running; in the second two cases, we can always try again later.
   2231  1.1  christos     if (err != kDNSServiceErr_NoError) {
   2232  1.1  christos         if (err == kDNSServiceErr_ServiceNotRunning || err == kDNSServiceErr_DefunctConnection ||
   2233  1.1  christos             err == kDNSServiceErr_BadParam || err == kDNSServiceErr_BadReference || err == 1)
   2234  1.1  christos         {
   2235  1.1  christos             if (err == 1) {
   2236  1.1  christos                 FAULT("bogus error code 1");
   2237  1.1  christos             }
   2238  1.1  christos             INFO("DNSServiceRegister failed: " PUB_S_SRP " (instance %p)",
   2239  1.1  christos                  err == kDNSServiceErr_ServiceNotRunning ? "not running" : "defunct", instance);
   2240  1.1  christos             service_disconnected(server_state, (intptr_t)server_state->shared_registration_txn);
   2241  1.1  christos         } else {
   2242  1.1  christos             INFO("DNSServiceRegister failed: %d (instance %p)", err, instance);
   2243  1.1  christos         }
   2244  1.1  christos         goto exit;
   2245  1.1  christos     }
   2246  1.1  christos     if (instance->update != NULL) {
   2247  1.1  christos         instance->update->num_instances_started++;
   2248  1.1  christos         instance->update_pending = true;
   2249  1.1  christos     }
   2250  1.1  christos     // After DNSServiceRegister succeeds, it creates a copy of DNSServiceRef that indirectly uses the shared connection,
   2251  1.1  christos     // so we update it here.
   2252  1.1  christos     instance->txn = ioloop_dnssd_txn_add_subordinate(service_ref, instance, adv_instance_context_release, NULL);
   2253  1.1  christos     if (instance->txn == NULL) {
   2254  1.1  christos         ERROR("no memory for instance transaction.");
   2255  1.1  christos         goto exit;
   2256  1.1  christos     }
   2257  1.1  christos     instance->shared_txn = (intptr_t)server_state->shared_registration_txn;
   2258  1.1  christos     RETAIN_HERE(instance, adv_instance); // for the callback
   2259  1.1  christos     exit_status = true;
   2260  1.1  christos 
   2261  1.1  christos exit:
   2262  1.1  christos     return exit_status;
   2263  1.1  christos }
   2264  1.1  christos 
   2265  1.1  christos // When we get a late name conflict on the hostname, we need to update the host registration and all of the
   2266  1.1  christos // service registrations. To do this, we construct an update and then apply it. If there is already an update
   2267  1.1  christos // in progress, we put this update at the end of the list.
   2268  1.1  christos static void
   2269  1.1  christos update_from_host(adv_host_t *host)
   2270  1.1  christos {
   2271  1.1  christos     adv_update_t *update = NULL;
   2272  1.1  christos     int i;
   2273  1.1  christos 
   2274  1.1  christos     if (host->update != NULL) {
   2275  1.1  christos         ERROR("already have an update.");
   2276  1.1  christos     }
   2277  1.1  christos 
   2278  1.1  christos     // Allocate the update structure.
   2279  1.1  christos     update = calloc(1, sizeof *update);
   2280  1.1  christos     if (update == NULL) {
   2281  1.1  christos         ERROR("no memory for update.");
   2282  1.1  christos         goto fail;
   2283  1.1  christos     }
   2284  1.1  christos     RETAIN_HERE(update, adv_update);
   2285  1.1  christos 
   2286  1.1  christos     if (host->addresses != NULL) {
   2287  1.1  christos         update->add_addresses = adv_record_vec_copy(host->addresses);
   2288  1.1  christos         if (update->add_addresses == NULL) {
   2289  1.1  christos             ERROR("no memory for addresses");
   2290  1.1  christos             goto fail;
   2291  1.1  christos         }
   2292  1.1  christos         for (i = 0; i < update->add_addresses->num; i++) {
   2293  1.1  christos             if (update->add_addresses->vec[i] != NULL) {
   2294  1.1  christos                 update->add_addresses->vec[i]->update = update;
   2295  1.1  christos                 RETAIN_HERE(update, adv_update);
   2296  1.1  christos             }
   2297  1.1  christos         }
   2298  1.1  christos     }
   2299  1.1  christos 
   2300  1.1  christos     // We can never update more instances than currently exist for this host.
   2301  1.1  christos     if (host->instances != NULL) {
   2302  1.1  christos         update->update_instances = adv_instance_vec_copy(host->instances);
   2303  1.1  christos         if (update->update_instances == NULL) {
   2304  1.1  christos             ERROR("no memory for update_instances");
   2305  1.1  christos             goto fail;
   2306  1.1  christos         }
   2307  1.1  christos         for (i = 0; i < update->update_instances->num; i++) {
   2308  1.1  christos             if (update->update_instances->vec[i] != NULL) {
   2309  1.1  christos                 update->update_instances->vec[i]->update = update;
   2310  1.1  christos                 RETAIN_HERE(update, adv_update);
   2311  1.1  christos             }
   2312  1.1  christos         }
   2313  1.1  christos 
   2314  1.1  christos         // We aren't actually adding or deleting any instances, but...
   2315  1.1  christos         update->remove_instances = adv_instance_vec_create(host->instances->num);
   2316  1.1  christos         if (update->remove_instances == NULL) {
   2317  1.1  christos             ERROR("no memory for remove_instances");
   2318  1.1  christos             goto fail;
   2319  1.1  christos         }
   2320  1.1  christos         update->remove_instances->num = host->instances->num;
   2321  1.1  christos 
   2322  1.1  christos         update->add_instances = adv_instance_vec_create(host->instances->num);
   2323  1.1  christos         if (update->add_instances == NULL) {
   2324  1.1  christos             ERROR("no memory for add_instances");
   2325  1.1  christos             goto fail;
   2326  1.1  christos         }
   2327  1.1  christos         update->add_instances->num = host->instances->num;
   2328  1.1  christos     }
   2329  1.1  christos 
   2330  1.1  christos 
   2331  1.1  christos     // At this point we have figured out all the work we need to do, so hang it off an update structure.
   2332  1.1  christos     update->host = host;
   2333  1.1  christos     RETAIN_HERE(update->host, adv_host);
   2334  1.1  christos     update->host_lease = host->lease_interval;
   2335  1.1  christos     update->key_lease = host->key_lease;
   2336  1.1  christos     update->lease_expiry = host->lease_expiry;
   2337  1.1  christos 
   2338  1.1  christos     // Stash the update on the host.
   2339  1.1  christos     host->update = update;
   2340  1.1  christos     RETAIN_HERE(host->update, adv_update);  // host gets a reference
   2341  1.1  christos     RELEASE_HERE(update, adv_update);       // we're done with our reference.
   2342  1.1  christos     start_host_update(host);
   2343  1.1  christos     return;
   2344  1.1  christos 
   2345  1.1  christos fail:
   2346  1.1  christos     if (update != NULL) {
   2347  1.1  christos         adv_update_cancel(update);
   2348  1.1  christos         RELEASE_HERE(update, adv_update);
   2349  1.1  christos     }
   2350  1.1  christos     wait_retry(host);
   2351  1.1  christos     return;
   2352  1.1  christos }
   2353  1.1  christos 
   2354  1.1  christos // When the host registration has completed, we get this callback.   Completion either means that we succeeded in
   2355  1.1  christos // registering the record, or that something went wrong and the registration has failed.
   2356  1.1  christos static void
   2357  1.1  christos register_host_record_completion(DNSServiceRef sdref, DNSRecordRef rref,
   2358  1.1  christos                                 DNSServiceFlags flags, DNSServiceErrorType error_code, void *context)
   2359  1.1  christos {
   2360  1.1  christos     adv_record_t *record = context;
   2361  1.1  christos     adv_host_t *host = NULL;
   2362  1.1  christos     adv_update_t *update = NULL;
   2363  1.1  christos     (void)sdref;
   2364  1.1  christos     (void)rref;
   2365  1.1  christos     (void)error_code;
   2366  1.1  christos     (void)flags;
   2367  1.1  christos 
   2368  1.1  christos     // This can happen if for some reason DNSServiceRemoveRecord returns something other than success. In this case, all
   2369  1.1  christos     // the cleanup that can be done has already been done, and all we can do is ignore the problem.
   2370  1.1  christos     if (record->rref == NULL) {
   2371  1.1  christos         ERROR("null rref");
   2372  1.1  christos         return;
   2373  1.1  christos     }
   2374  1.1  christos     // For analyzer, can't actually happen.
   2375  1.1  christos     if (record == NULL) {
   2376  1.1  christos         ERROR("null record");
   2377  1.1  christos         return;
   2378  1.1  christos     }
   2379  1.1  christos     host = record->host;
   2380  1.1  christos     if (host == NULL) {
   2381  1.1  christos         ERROR("no host");
   2382  1.1  christos         return;
   2383  1.1  christos     }
   2384  1.1  christos 
   2385  1.1  christos     // Make sure record remains valid for the duration of this call.
   2386  1.1  christos     RETAIN_HERE(record, adv_record);
   2387  1.1  christos 
   2388  1.1  christos     // It's possible that we could restart a host update due to an error while a callback is still pending on a stale
   2389  1.1  christos     // update.  In this case, we just cancel all of the work that's been done on the stale update (it's probably already
   2390  1.1  christos     // moot anyway.
   2391  1.1  christos     if (record->update != NULL && host->update != record->update) {
   2392  1.1  christos         INFO("registration for host record completed with invalid state.");
   2393  1.1  christos         adv_update_cancel(record->update);
   2394  1.1  christos         RELEASE_HERE(record->update, adv_update);
   2395  1.1  christos         record->update = NULL;
   2396  1.1  christos         srp_mdns_shared_record_remove(host->server_state, record); // This will prevent further callbacks and release the reference held by the transaction.
   2397  1.1  christos         RELEASE_HERE(record, adv_record); // The callback has a reference to the record.
   2398  1.1  christos         RELEASE_HERE(record, adv_record); // Release the reference to the record that we retained at the beginning
   2399  1.1  christos         return;
   2400  1.1  christos 
   2401  1.1  christos     }
   2402  1.1  christos     update = record->update;
   2403  1.1  christos     if (update != NULL) {
   2404  1.1  christos         RETAIN_HERE(update, adv_update);
   2405  1.1  christos     }
   2406  1.1  christos 
   2407  1.1  christos     if (error_code == kDNSServiceErr_NoError || error_code == kDNSServiceErr_NameConflict) {
   2408  1.1  christos         // If the update is pending, it means that we just finished registering this record for the first time,
   2409  1.1  christos         // so we can count it as complete and check to see if there is any work left to do; if not, we call
   2410  1.1  christos         // srp_mdns_update_finished to apply the update to the host object.
   2411  1.1  christos         const char *note = " has completed.";
   2412  1.1  christos         if (record->update_pending) {
   2413  1.1  christos             record->update_pending = false;
   2414  1.1  christos             if (update != NULL) {
   2415  1.1  christos                 update->num_records_completed++;
   2416  1.1  christos                 if (update->num_records_completed == update->num_records_started &&
   2417  1.1  christos                     update->num_instances_completed == update->num_instances_started)
   2418  1.1  christos                 {
   2419  1.1  christos                     srp_mdns_update_finished(update);
   2420  1.1  christos                 }
   2421  1.1  christos             }
   2422  1.1  christos         } else {
   2423  1.1  christos             note = " got spurious success callback after completion.";
   2424  1.1  christos         }
   2425  1.1  christos 
   2426  1.1  christos         if (error_code != kDNSServiceErr_NoError) {
   2427  1.1  christos             // Shared record is no longer good.
   2428  1.1  christos             srp_mdns_shared_record_remove(host->server_state, record);
   2429  1.1  christos             note = " completed with conflict.";
   2430  1.1  christos             srp_schedule_host_record_retry(record);
   2431  1.1  christos         }
   2432  1.1  christos 
   2433  1.1  christos         if (record->rrtype == dns_rrtype_a) {
   2434  1.1  christos             IPv4_ADDR_GEN_SRP(record->rdata, addr_buf);
   2435  1.1  christos             INFO("registration for host " PRI_S_SRP " address " PRI_IPv4_ADDR_SRP PUB_S_SRP,
   2436  1.1  christos                  host->registered_name, IPv4_ADDR_PARAM_SRP(record->rdata, addr_buf), note);
   2437  1.1  christos         } else if (record->rrtype == dns_rrtype_aaaa) {
   2438  1.1  christos             SEGMENTED_IPv6_ADDR_GEN_SRP(record->rdata, addr_buf);
   2439  1.1  christos             INFO("registration for host " PRI_S_SRP " address " PRI_SEGMENTED_IPv6_ADDR_SRP PUB_S_SRP,
   2440  1.1  christos                  host->registered_name, SEGMENTED_IPv6_ADDR_PARAM_SRP(record->rdata, addr_buf), note);
   2441  1.1  christos         } else if (record->rrtype == dns_rrtype_key) {
   2442  1.1  christos             INFO("registration for host " PRI_S_SRP " key" PUB_S_SRP, host->registered_name, note);
   2443  1.1  christos         } else {
   2444  1.1  christos             INFO("registration for host " PRI_S_SRP " unknown record type %d " PUB_S_SRP, host->registered_name, record->rrtype, note);
   2445  1.1  christos         }
   2446  1.1  christos     } else {
   2447  1.1  christos         if (record->rrtype == dns_rrtype_a) {
   2448  1.1  christos             IPv4_ADDR_GEN_SRP(record->rdata, addr_buf);
   2449  1.1  christos             INFO("registration for host " PRI_S_SRP " address " PRI_IPv4_ADDR_SRP " failed, error code = %d.",
   2450  1.1  christos              host->registered_name, IPv4_ADDR_PARAM_SRP(record->rdata, addr_buf), error_code);
   2451  1.1  christos         } else if (record->rrtype == dns_rrtype_aaaa) {
   2452  1.1  christos             SEGMENTED_IPv6_ADDR_GEN_SRP(record->rdata, addr_buf);
   2453  1.1  christos             INFO("registration for host " PRI_S_SRP " address " PRI_SEGMENTED_IPv6_ADDR_SRP " failed, error code = %d.",
   2454  1.1  christos                  host->registered_name, SEGMENTED_IPv6_ADDR_PARAM_SRP(record->rdata, addr_buf), error_code);
   2455  1.1  christos         } else if (record->rrtype == dns_rrtype_key) {
   2456  1.1  christos             INFO("registration for host " PRI_S_SRP " key failed, error code = %d.", host->registered_name, error_code);
   2457  1.1  christos         } else {
   2458  1.1  christos             INFO("registration for host " PRI_S_SRP " unknown record type %d failed, error code = %d.",
   2459  1.1  christos                  host->registered_name, record->rrtype, error_code);
   2460  1.1  christos         }
   2461  1.1  christos 
   2462  1.1  christos         // If the reason this failed is that we couldn't talk to mDNSResponder, or mDNSResponder disconnected, then we want to retry
   2463  1.1  christos         // later on in the hopes that mDNSResponder will come back.
   2464  1.1  christos         if (error_code == kDNSServiceErr_ServiceNotRunning || error_code == kDNSServiceErr_DefunctConnection) {
   2465  1.1  christos             service_disconnected(host->server_state, record->shared_txn);
   2466  1.1  christos             if (update != NULL) {
   2467  1.1  christos                 wait_retry(host);
   2468  1.1  christos             }
   2469  1.1  christos         } else {
   2470  1.1  christos             // The other error we could get is a name conflict. This means that some other advertising proxy or host on
   2471  1.1  christos             // the network is advertising the hostname we chose, and either got there first with no TSR record, or got
   2472  1.1  christos             // its copy of the host information later than ours. So if we get a name conflict, it's up to the client or
   2473  1.1  christos             // the replication peer to make the next move.
   2474  1.1  christos 
   2475  1.1  christos             if (update != NULL) {
   2476  1.1  christos                 update_failed(update, (error_code == kDNSServiceErr_NameConflict
   2477  1.1  christos                                        ? dns_rcode_yxdomain
   2478  1.1  christos                                        : dns_rcode_servfail), true, true);
   2479  1.1  christos             } else {
   2480  1.1  christos             }
   2481  1.1  christos         }
   2482  1.1  christos         // Regardless of what else happens, this transaction is dead, so get rid of our references to it.
   2483  1.1  christos         srp_mdns_shared_record_remove(host->server_state, record);
   2484  1.1  christos     }
   2485  1.1  christos     if (update != NULL) {
   2486  1.1  christos         RELEASE_HERE(update, adv_update);
   2487  1.1  christos     }
   2488  1.1  christos     RELEASE_HERE(record, adv_record); // Release the reference to the record that we retained at the beginning
   2489  1.1  christos }
   2490  1.1  christos 
   2491  1.1  christos static adv_instance_t *
   2492  1.1  christos adv_instance_create(service_instance_t *raw, adv_host_t *host, adv_update_t *update)
   2493  1.1  christos {
   2494  1.1  christos     char service_type[DNS_MAX_LABEL_SIZE_ESCAPED * 2 + 2]; // sizeof '.' + sizeof '\0'.
   2495  1.1  christos     char instance_name[DNS_MAX_NAME_SIZE_ESCAPED + 1];
   2496  1.1  christos     uint8_t *txt_data;
   2497  1.1  christos 
   2498  1.1  christos     // Allocate the raw registration
   2499  1.1  christos     adv_instance_t *instance = calloc(1, sizeof *instance);
   2500  1.1  christos     if (instance == NULL) {
   2501  1.1  christos         ERROR("adv_instance:create: unable to allocate raw registration struct.");
   2502  1.1  christos         return NULL;
   2503  1.1  christos     }
   2504  1.1  christos     RETAIN_HERE(instance, adv_instance);
   2505  1.1  christos     instance->host = host;
   2506  1.1  christos     RETAIN_HERE(instance->host, adv_host);
   2507  1.1  christos     instance->update = update;
   2508  1.1  christos     RETAIN_HERE(instance->update, adv_update);
   2509  1.1  christos 
   2510  1.1  christos     // SRV records have priority, weight and port, but DNSServiceRegister only uses port.
   2511  1.1  christos     instance->port = (raw->srv == NULL) ? 0 : raw->srv->data.srv.port;
   2512  1.1  christos 
   2513  1.1  christos     // Make a presentation-format version of the service name.
   2514  1.1  christos     if (!extract_instance_name(instance_name, sizeof instance_name, service_type, sizeof service_type, raw)) {
   2515  1.1  christos         RELEASE_HERE(instance, adv_instance);
   2516  1.1  christos         return NULL;
   2517  1.1  christos     }
   2518  1.1  christos 
   2519  1.1  christos     instance->instance_name = strdup(instance_name);
   2520  1.1  christos     if (instance->instance_name == NULL) {
   2521  1.1  christos         ERROR("adv_instance:create: unable to allocate instance name.");
   2522  1.1  christos         RELEASE_HERE(instance, adv_instance);
   2523  1.1  christos         return NULL;
   2524  1.1  christos     }
   2525  1.1  christos     instance->service_type = strdup(service_type);
   2526  1.1  christos     if (instance->service_type == NULL) {
   2527  1.1  christos         ERROR("adv_instance:create: unable to allocate instance type.");
   2528  1.1  christos         RELEASE_HERE(instance, adv_instance);
   2529  1.1  christos         return NULL;
   2530  1.1  christos     }
   2531  1.1  christos 
   2532  1.1  christos     // Allocate the text record buffer
   2533  1.1  christos     if (raw->txt != NULL) {
   2534  1.1  christos         txt_data = malloc(raw->txt->data.txt.len);
   2535  1.1  christos         if (txt_data == NULL) {
   2536  1.1  christos             RELEASE_HERE(instance, adv_instance);
   2537  1.1  christos             ERROR("adv_instance:create: unable to allocate txt_data buffer");
   2538  1.1  christos             return NULL;
   2539  1.1  christos         }
   2540  1.1  christos         // Format the txt buffer as required by DNSServiceRegister().
   2541  1.1  christos         memcpy(txt_data, raw->txt->data.txt.data, raw->txt->data.txt.len);
   2542  1.1  christos         instance->txt_data = txt_data;
   2543  1.1  christos         instance->txt_length = raw->txt->data.txt.len;
   2544  1.1  christos     } else {
   2545  1.1  christos         instance->txt_data = NULL;
   2546  1.1  christos         instance->txt_length = 0;
   2547  1.1  christos     }
   2548  1.1  christos 
   2549  1.1  christos     // If the service_instance_t is marked to skip updating, mark the adv_instance_t as well.
   2550  1.1  christos     instance->skip_update = raw->skip_update;
   2551  1.1  christos 
   2552  1.1  christos     return instance;
   2553  1.1  christos }
   2554  1.1  christos 
   2555  1.1  christos #define adv_record_create(rrtype, rdlen, rdata, host) \
   2556  1.1  christos     adv_record_create_(rrtype, rdlen, rdata, host, __FILE__, __LINE__)
   2557  1.1  christos static adv_record_t *
   2558  1.1  christos adv_record_create_(uint16_t rrtype, uint16_t rdlen, uint8_t *rdata, adv_host_t *host, const char *file, int line)
   2559  1.1  christos {
   2560  1.1  christos 
   2561  1.1  christos     adv_record_t *new_record = calloc(1, sizeof(*new_record) + rdlen - 1);
   2562  1.1  christos     if (new_record == NULL) {
   2563  1.1  christos         ERROR("no memory for new_record");
   2564  1.1  christos         return NULL;
   2565  1.1  christos     }
   2566  1.1  christos     new_record->rdata = malloc(rdlen);
   2567  1.1  christos     if (new_record->rdata == NULL) {
   2568  1.1  christos         ERROR("no memory for new_record->rdata");
   2569  1.1  christos         free(new_record);
   2570  1.1  christos         return NULL;
   2571  1.1  christos     }
   2572  1.1  christos     new_record->host = host;
   2573  1.1  christos     RETAIN(host, adv_host);
   2574  1.1  christos     new_record->rrtype = rrtype;
   2575  1.1  christos     new_record->rdlen = rdlen;
   2576  1.1  christos     memcpy(new_record->rdata, rdata, rdlen);
   2577  1.1  christos     RETAIN(new_record, adv_record);
   2578  1.1  christos     return new_record;
   2579  1.1  christos }
   2580  1.1  christos 
   2581  1.1  christos // Given a pair of service types which may or may not have subtypes, e.g. _foo._tcp, which doesn't have subtypes, or
   2582  1.1  christos // _foo.tcp,bar, which does, return true if type1 matches the type2 for the base type, ignoring subtypes.
   2583  1.1  christos static bool
   2584  1.1  christos service_types_equal(const char *type1, const char *type2)
   2585  1.1  christos {
   2586  1.1  christos     size_t len1;
   2587  1.1  christos     char *comma1 = strchr(type1, ',');
   2588  1.1  christos     if (comma1 == NULL) {
   2589  1.1  christos         len1 = strlen(type1);
   2590  1.1  christos     } else {
   2591  1.1  christos         len1 = comma1 - type1;
   2592  1.1  christos     }
   2593  1.1  christos     char *comma2 = strchr(type2, ',');
   2594  1.1  christos     size_t len2;
   2595  1.1  christos     if (comma2 != NULL) {
   2596  1.1  christos         len2 = comma2 - type2;
   2597  1.1  christos     } else {
   2598  1.1  christos         len2 = strlen(type2);
   2599  1.1  christos     }
   2600  1.1  christos     if (len1 != len2) {
   2601  1.1  christos         return false;
   2602  1.1  christos     }
   2603  1.1  christos     if (memcmp(type2, type1, len1)) {
   2604  1.1  christos         return false;
   2605  1.1  christos     }
   2606  1.1  christos     return true;
   2607  1.1  christos }
   2608  1.1  christos 
   2609  1.1  christos DNSServiceAttributeRef
   2610  1.1  christos srp_adv_host_tsr_attribute_generate(adv_host_t *host, char *time_buf, size_t time_buf_size)
   2611  1.1  christos {
   2612  1.1  christos     message_t *message = NULL;
   2613  1.1  christos     if (host->update != NULL && host->update->client != NULL && host->update->client->message != NULL) {
   2614  1.1  christos         message = host->update->client->message;
   2615  1.1  christos     } else if (host->update == NULL && host->message != NULL) {
   2616  1.1  christos         message = host->message;
   2617  1.1  christos     }
   2618  1.1  christos     return srp_message_tsr_attribute_generate(message, host->key_id, time_buf, time_buf_size);
   2619  1.1  christos }
   2620  1.1  christos 
   2621  1.1  christos static bool
   2622  1.1  christos register_host_record(adv_host_t *host, adv_record_t *record, bool skipping)
   2623  1.1  christos {
   2624  1.1  christos     int err;
   2625  1.1  christos 
   2626  1.1  christos     // If this record is already registered, get rid of the old transaction.
   2627  1.1  christos     if (record->rref != NULL && !skipping) {
   2628  1.1  christos         srp_mdns_shared_record_remove(host->server_state, record);
   2629  1.1  christos     }
   2630  1.1  christos 
   2631  1.1  christos     // If we don't yet have a shared connection, create one.
   2632  1.1  christos     if (!srp_mdns_shared_registration_txn_setup(host->server_state)) {
   2633  1.1  christos         return false;
   2634  1.1  christos     }
   2635  1.1  christos 
   2636  1.1  christos     const DNSServiceRef service_ref = host->server_state->shared_registration_txn->sdref;
   2637  1.1  christos 
   2638  1.1  christos     if (record->rrtype == dns_rrtype_a) {
   2639  1.1  christos         IPv4_ADDR_GEN_SRP(record->rdata, rdata_buf);
   2640  1.1  christos         INFO(PUB_S_SRP "DNSServiceRegisterRecord(%p %p %d %d %s %d %d %d " PRI_IPv4_ADDR_SRP " %d %p %p)",
   2641  1.1  christos              skipping ? "skipping " : "", service_ref, &record->rref, kDNSServiceFlagsShared,
   2642  1.1  christos              host->server_state->advertise_interface, host->registered_name, record->rrtype, dns_qclass_in,
   2643  1.1  christos              record->rdlen, IPv4_ADDR_PARAM_SRP(record->rdata, rdata_buf),
   2644  1.1  christos              ADDRESS_RECORD_TTL, register_host_record_completion, record);
   2645  1.1  christos     } else if (record->rrtype == dns_rrtype_aaaa) {
   2646  1.1  christos         SEGMENTED_IPv6_ADDR_GEN_SRP(record->rdata, rdata_buf);
   2647  1.1  christos         INFO(PUB_S_SRP "DNSServiceRegisterRecord(%p %p %d %d %s %d %d %d " PRI_SEGMENTED_IPv6_ADDR_SRP " %d %p %p)",
   2648  1.1  christos              skipping ? "skipping " : "", service_ref, &record->rref, kDNSServiceFlagsShared,
   2649  1.1  christos              host->server_state->advertise_interface, host->registered_name, record->rrtype, dns_qclass_in,
   2650  1.1  christos              record->rdlen, SEGMENTED_IPv6_ADDR_PARAM_SRP(record->rdata, rdata_buf),
   2651  1.1  christos              ADDRESS_RECORD_TTL, register_host_record_completion, record);
   2652  1.1  christos     } else {
   2653  1.1  christos         INFO(PUB_S_SRP "DNSServiceRegisterRecord(%p %p %d %d %s %d %d %d %p %d %p %p)",
   2654  1.1  christos              skipping ? "skipping " : "", service_ref, &record->rref,
   2655  1.1  christos              kDNSServiceFlagsShared,
   2656  1.1  christos              host->server_state->advertise_interface, host->registered_name,
   2657  1.1  christos              record->rrtype, dns_qclass_in, record->rdlen, record->rdata, ADDRESS_RECORD_TTL,
   2658  1.1  christos              register_host_record_completion, record);
   2659  1.1  christos     }
   2660  1.1  christos     // If we're skipping, we don't actually have to do any work.
   2661  1.1  christos     if (skipping) {
   2662  1.1  christos         return true;
   2663  1.1  christos     }
   2664  1.1  christos 
   2665  1.1  christos     char time_buf[TSR_TIMESTAMP_STRING_LEN];
   2666  1.1  christos     DNSServiceAttributeRef tsr_attribute =
   2667  1.1  christos         srp_adv_host_tsr_attribute_generate(host, time_buf, sizeof(time_buf));
   2668  1.1  christos     if (tsr_attribute == NULL) {
   2669  1.1  christos         ERROR("Failed to create new DNSServiceAttributeRef");
   2670  1.1  christos         return false;
   2671  1.1  christos     } else {
   2672  1.1  christos         err = dns_service_register_record_wa(host->server_state, service_ref, &record->rref,
   2673  1.1  christos                                              kDNSServiceFlagsKnownUnique,
   2674  1.1  christos                                              host->server_state->advertise_interface, host->registered_name,
   2675  1.1  christos                                              record->rrtype, dns_qclass_in, record->rdlen, record->rdata,
   2676  1.1  christos                                              ADDRESS_RECORD_TTL, tsr_attribute, register_host_record_completion,
   2677  1.1  christos                                              record);
   2678  1.1  christos         DNSServiceAttributeDeallocate(tsr_attribute);
   2679  1.1  christos         if (err == kDNSServiceErr_NoError) {
   2680  1.1  christos             INFO("DNSServiceRegisterRecord for " PRI_S_SRP ", TSR set to " PUB_S_SRP " (record %p rref %p)",
   2681  1.1  christos                  host->name, time_buf, record, record->rref);
   2682  1.1  christos         }
   2683  1.1  christos     }
   2684  1.1  christos 
   2685  1.1  christos     if (err != kDNSServiceErr_NoError) {
   2686  1.1  christos         if (err == kDNSServiceErr_ServiceNotRunning || err == kDNSServiceErr_DefunctConnection ||
   2687  1.1  christos             err == kDNSServiceErr_BadParam || err == kDNSServiceErr_BadReference || err == 1)
   2688  1.1  christos         {
   2689  1.1  christos             if (err == 1) { // This is for an old bug that probably doesn't happen anymore.
   2690  1.1  christos                 FAULT("bogus error code 1");
   2691  1.1  christos             }
   2692  1.1  christos             INFO("DNSServiceRegisterRecord failed on host " PUB_S_SRP ": " PUB_S_SRP " (record %p)", host->name,
   2693  1.1  christos                  err == kDNSServiceErr_ServiceNotRunning ? "not running" : "defunct", record);
   2694  1.1  christos             service_disconnected(host->server_state, (intptr_t)host->server_state->shared_registration_txn);
   2695  1.1  christos         } else {
   2696  1.1  christos             INFO("DNSServiceRegisterRecord failed: %d (record %p)", err, record);
   2697  1.1  christos         }
   2698  1.1  christos         return false;
   2699  1.1  christos     }
   2700  1.1  christos     record->shared_txn = (intptr_t)host->server_state->shared_registration_txn;
   2701  1.1  christos     RETAIN_HERE(record, adv_record); // for the callback
   2702  1.1  christos     if (host->update != NULL) {
   2703  1.1  christos         record->update_pending = true;
   2704  1.1  christos     }
   2705  1.1  christos     return true;
   2706  1.1  christos }
   2707  1.1  christos 
   2708  1.1  christos static bool
   2709  1.1  christos update_instance_tsr(adv_instance_t *instance, adv_instance_t *new_instance)
   2710  1.1  christos {
   2711  1.1  christos     int err = kDNSServiceErr_NoError;
   2712  1.1  christos     bool success = false;
   2713  1.1  christos 
   2714  1.1  christos     if (instance->txn == NULL) {
   2715  1.1  christos         ERROR("txn is NULL updating instance TSR.");
   2716  1.1  christos         goto out;
   2717  1.1  christos     }
   2718  1.1  christos     if (instance->txn->sdref == NULL) {
   2719  1.1  christos         ERROR("sdref is NULL when updating instance TSR.");
   2720  1.1  christos         goto out;
   2721  1.1  christos     }
   2722  1.1  christos     // Currently if we want to update the rdata, we need to do that separately from the TSR.
   2723  1.1  christos     if (new_instance != NULL) {
   2724  1.1  christos         if (instance->skip_update) {
   2725  1.1  christos             err = kDNSServiceErr_NoError;
   2726  1.1  christos         } else {
   2727  1.1  christos             err = dns_service_update_record(instance->host->server_state, instance->txn->sdref,
   2728  1.1  christos                                             NULL, 0, new_instance->txt_length, new_instance->txt_data, 0);
   2729  1.1  christos         }
   2730  1.1  christos         if (err != kDNSServiceErr_NoError) {
   2731  1.1  christos             INFO("DNSServiceUpdateRecord for instance " PRI_S_SRP " TXT record failed: %d (instance %p)",
   2732  1.1  christos                  instance->instance_name, err, instance);
   2733  1.1  christos             goto out;
   2734  1.1  christos         } else {
   2735  1.1  christos             INFO("updated TXT record for " PRI_S_SRP " . " PRI_S_SRP " (instance %p sdref %p).",
   2736  1.1  christos                  instance->instance_name, instance->service_type, instance, instance->txn->sdref);
   2737  1.1  christos             success = true;
   2738  1.1  christos         }
   2739  1.1  christos     }
   2740  1.1  christos 
   2741  1.1  christos     DNSServiceAttributeRef attr;
   2742  1.1  christos 
   2743  1.1  christos     if (instance->skip_update) {
   2744  1.1  christos         INFO("skipping DNSServiceUpdateRecord for instance " PRI_S_SRP " TSR (instance %p)",
   2745  1.1  christos              instance->instance_name, instance);
   2746  1.1  christos     } else {
   2747  1.1  christos         success = false;
   2748  1.1  christos         attr = DNSServiceAttributeCreate();
   2749  1.1  christos         if (attr == NULL) {
   2750  1.1  christos             ERROR("failed to create new DNSServiceAttributeRef");
   2751  1.1  christos         } else {
   2752  1.1  christos             uint32_t offset = 0;
   2753  1.1  christos             char time_buf[TSR_TIMESTAMP_STRING_LEN];
   2754  1.1  christos             if (instance->update != NULL && instance->update->client != NULL && instance->update->client->message != NULL &&
   2755  1.1  christos                 instance->update->client->message->received_time != 0)
   2756  1.1  christos             {
   2757  1.1  christos                 offset = (uint32_t)(srp_time() - instance->update->client->message->received_time);
   2758  1.1  christos                 srp_format_time_offset(time_buf, sizeof(time_buf), offset);
   2759  1.1  christos             } else {
   2760  1.1  christos                 static char msg[] = "now";
   2761  1.1  christos                 memcpy(time_buf, msg, sizeof(msg));
   2762  1.1  christos             }
   2763  1.1  christos             if (_DNSSD_API_AVAILABLE_FALL_2024) {
   2764  1.1  christos                 DNSServiceAttributeSetHostKeyHash(attr, instance->host->key_id);
   2765  1.1  christos             }
   2766  1.1  christos             DNSServiceAttributeSetTimestamp(attr, offset);
   2767  1.1  christos             err = dns_service_update_record_wa(instance->host->server_state,
   2768  1.1  christos                                                instance->txn->sdref, NULL, 0, 0, NULL, 0, attr);
   2769  1.1  christos             DNSServiceAttributeDeallocate(attr);
   2770  1.1  christos             if (err == kDNSServiceErr_NoError) {
   2771  1.1  christos                 INFO("DNSServiceUpdateRecord for " PRI_S_SRP ", TSR set to " PUB_S_SRP " (instance %p sdref %p)",
   2772  1.1  christos                      instance->host == NULL ? "<null>" : instance->host->name, time_buf, instance, instance->txn->sdref);
   2773  1.1  christos                 success = true;
   2774  1.1  christos             } else {
   2775  1.1  christos                 INFO("DNSServiceUpdateRecord for instance " PRI_S_SRP ", TSR failed: %d (instance %p sdref %p)",
   2776  1.1  christos                      instance->instance_name, err, instance, instance->txn->sdref);
   2777  1.1  christos             }
   2778  1.1  christos         }
   2779  1.1  christos     }
   2780  1.1  christos 
   2781  1.1  christos out:
   2782  1.1  christos     if (success == false) {
   2783  1.1  christos         if (instance->txn != NULL) {
   2784  1.1  christos             // We should never get a bad reference error.
   2785  1.1  christos             if (err == kDNSServiceErr_BadReference || err == kDNSServiceErr_BadParam) {
   2786  1.1  christos                 FAULT("we got a bad reference error: why?");
   2787  1.1  christos             }
   2788  1.1  christos             // For all errors, we should cancel and release the transaction.
   2789  1.1  christos             ioloop_dnssd_txn_cancel_srp(instance->host->server_state, instance->txn);
   2790  1.1  christos             ioloop_dnssd_txn_release(instance->txn);
   2791  1.1  christos             instance->txn = NULL;
   2792  1.1  christos         }
   2793  1.1  christos     } else if (new_instance != NULL) {
   2794  1.1  christos         // If we have new_instance, the caller is going to get rid of it, so we need to
   2795  1.1  christos         // steal the (possibly changed) data from it and put it on instance.
   2796  1.1  christos         free(instance->txt_data);
   2797  1.1  christos         instance->txt_data = new_instance->txt_data;
   2798  1.1  christos         instance->txt_length = new_instance->txt_length;
   2799  1.1  christos         new_instance->txt_data = NULL;
   2800  1.1  christos         new_instance->txt_length = 0;
   2801  1.1  christos     }
   2802  1.1  christos     return success;
   2803  1.1  christos }
   2804  1.1  christos 
   2805  1.1  christos static void
   2806  1.1  christos update_host_tsr(adv_record_t *record, adv_update_t *update)
   2807  1.1  christos {
   2808  1.1  christos     DNSServiceAttributeRef attr;
   2809  1.1  christos     int err;
   2810  1.1  christos     dnssd_txn_t *shared_txn;
   2811  1.1  christos 
   2812  1.1  christos     if (record->host == NULL || record->rref == NULL) {
   2813  1.1  christos         ERROR("record->host[%p], record->rref[%p] when we update host TSR.", record->host, record->rref);
   2814  1.1  christos         return;
   2815  1.1  christos     }
   2816  1.1  christos 
   2817  1.1  christos     shared_txn = record->host->server_state->shared_registration_txn;
   2818  1.1  christos     if (shared_txn == NULL) {
   2819  1.1  christos         ERROR("shared_txn is NULL when we update host TSR.");
   2820  1.1  christos         return;
   2821  1.1  christos     }
   2822  1.1  christos     if (shared_txn->sdref == NULL) {
   2823  1.1  christos         ERROR("shared_txn->sdref is NULL when we update host TSR.");
   2824  1.1  christos         return;
   2825  1.1  christos     }
   2826  1.1  christos 
   2827  1.1  christos     attr = DNSServiceAttributeCreate();
   2828  1.1  christos     if (attr == NULL) {
   2829  1.1  christos         ERROR("failed to create new DNSServiceAttributeRef");
   2830  1.1  christos     } else {
   2831  1.1  christos         uint32_t offset = 0;
   2832  1.1  christos         char time_buf[TSR_TIMESTAMP_STRING_LEN];
   2833  1.1  christos         if (update->client != NULL && update->client->message != NULL && update->client->message->received_time != 0) {
   2834  1.1  christos             offset = (uint32_t)(srp_time() - update->client->message->received_time);
   2835  1.1  christos             srp_format_time_offset(time_buf, sizeof(time_buf), offset);
   2836  1.1  christos         } else {
   2837  1.1  christos             static char msg[] = "now";
   2838  1.1  christos             memcpy(time_buf, msg, sizeof(msg));
   2839  1.1  christos         }
   2840  1.1  christos         if (_DNSSD_API_AVAILABLE_FALL_2024) {
   2841  1.1  christos             DNSServiceAttributeSetHostKeyHash(attr, record->host->key_id);
   2842  1.1  christos         }
   2843  1.1  christos         DNSServiceAttributeSetTimestamp(attr, offset);
   2844  1.1  christos         err = dns_service_update_record_wa(record->host->server_state,
   2845  1.1  christos                                            shared_txn->sdref, record->rref, 0, 0, NULL, 0, attr);
   2846  1.1  christos         DNSServiceAttributeDeallocate(attr);
   2847  1.1  christos         if (err == kDNSServiceErr_NoError) {
   2848  1.1  christos             INFO("DNSServiceUpdateRecord TSR for " PRI_S_SRP " set to " PUB_S_SRP " (record %p rref %p)",
   2849  1.1  christos                  record->host == NULL ? "<null>" : record->host->name, time_buf, record, record->rref);
   2850  1.1  christos         } else {
   2851  1.1  christos             INFO("DNSServiceUpdateRecordWithAttribute for host tsr failed: %d (record %p rref %p)",
   2852  1.1  christos                  err, record, record->rref);
   2853  1.1  christos         }
   2854  1.1  christos     }
   2855  1.1  christos }
   2856  1.1  christos 
   2857  1.1  christos // When we need to register a host with mDNSResponder, start_host_update is called.   This can be either because
   2858  1.1  christos // we just got a new registration for a host, or if the daemon dies and we need to re-do the host registration.
   2859  1.1  christos // This just registers the host; if that succeeds, then we register the service instances.
   2860  1.1  christos static void
   2861  1.1  christos start_host_update(adv_host_t *host)
   2862  1.1  christos {
   2863  1.1  christos     adv_update_t *update = host->update;
   2864  1.1  christos #ifdef USE_DNSSERVICE_QUEUING
   2865  1.1  christos     int err;
   2866  1.1  christos #endif
   2867  1.1  christos     int i;
   2868  1.1  christos 
   2869  1.1  christos     // No work to do?
   2870  1.1  christos     if (update == NULL) {
   2871  1.1  christos         ERROR("start_host_update: no work to do for host " PRI_S_SRP, host->registered_name);
   2872  1.1  christos         return;
   2873  1.1  christos     }
   2874  1.1  christos 
   2875  1.1  christos     bool skip_host_updates = (update->client != NULL && update->client->skip_host_updates);
   2876  1.1  christos 
   2877  1.1  christos 
   2878  1.1  christos     update->num_records_started = 0;
   2879  1.1  christos 
   2880  1.1  christos     // Add all of the addresses that have been registered.
   2881  1.1  christos     if (update->add_addresses != NULL) {
   2882  1.1  christos         for (i = 0; i < update->add_addresses->num; i++) {
   2883  1.1  christos             if (update->add_addresses->vec[i] != NULL) {
   2884  1.1  christos                 if (!register_host_record(host, update->add_addresses->vec[i], skip_host_updates)) {
   2885  1.1  christos                     update_failed(update, dns_rcode_servfail, true, true);
   2886  1.1  christos                     return;
   2887  1.1  christos                 } else if (!skip_host_updates) {
   2888  1.1  christos                     update->num_records_started++;
   2889  1.1  christos                 }
   2890  1.1  christos             }
   2891  1.1  christos         }
   2892  1.1  christos     }
   2893  1.1  christos 
   2894  1.1  christos     // It's possible that some existing addresses are no longer registered because of a service disconnect. Check all the
   2895  1.1  christos     // existing addresses for this situation: if an existing address has no rref, and does not appear in update->remove_addrs,
   2896  1.1  christos     // then re-register it.
   2897  1.1  christos     if (host->addresses != NULL) {
   2898  1.1  christos         for (i = 0; i < host->addresses->num; i++) {
   2899  1.1  christos             adv_record_t *record = host->addresses->vec[i];
   2900  1.1  christos             adv_record_t *remove_address = NULL;
   2901  1.1  christos             if (update->remove_addresses != NULL) {
   2902  1.1  christos                 remove_address = update->remove_addresses->vec[i];
   2903  1.1  christos             }
   2904  1.1  christos             if (remove_address == NULL && record != NULL && record->rref == NULL) {
   2905  1.1  christos                 host->addresses->vec[i]->update = update;
   2906  1.1  christos                 RETAIN_HERE(host->addresses->vec[i]->update, adv_update);
   2907  1.1  christos                 if (!register_host_record(host, record, skip_host_updates)) {
   2908  1.1  christos                     update_failed(update, dns_rcode_servfail, true, true);
   2909  1.1  christos                     return;
   2910  1.1  christos                 } else if (!skip_host_updates) {
   2911  1.1  christos                     update->num_records_started++;
   2912  1.1  christos                 }
   2913  1.1  christos             }
   2914  1.1  christos         }
   2915  1.1  christos     }
   2916  1.1  christos 
   2917  1.1  christos     if (update->key != NULL) {
   2918  1.1  christos         if (!register_host_record(host, update->key, skip_host_updates)) {
   2919  1.1  christos             update_failed(update, dns_rcode_servfail, true, true);
   2920  1.1  christos             return;
   2921  1.1  christos         } else if (!skip_host_updates) {
   2922  1.1  christos             update->num_records_started++;
   2923  1.1  christos         }
   2924  1.1  christos     }
   2925  1.1  christos 
   2926  1.1  christos     // If the shared transaction has changed since the key record was added, add it again.
   2927  1.1  christos     if (update->key == NULL && host->key_record != NULL &&
   2928  1.1  christos         (host->key_record->shared_txn != (intptr_t)host->server_state->shared_registration_txn ||
   2929  1.1  christos          host->key_record->rref == NULL))
   2930  1.1  christos     {
   2931  1.1  christos         update->key = host->key_record;
   2932  1.1  christos         RETAIN_HERE(update->key, adv_record);
   2933  1.1  christos         RELEASE_HERE(host->key_record, adv_record);
   2934  1.1  christos         host->key_record = NULL;
   2935  1.1  christos         update->key->rref = NULL;
   2936  1.1  christos         update->key->update = update;
   2937  1.1  christos         RETAIN_HERE(update, adv_update);
   2938  1.1  christos         if (!register_host_record(host, update->key, skip_host_updates)) {
   2939  1.1  christos             update_failed(update, dns_rcode_servfail, true, true);
   2940  1.1  christos             return;
   2941  1.1  christos         } else if (!skip_host_updates) {
   2942  1.1  christos             update->num_records_started++;
   2943  1.1  christos         }
   2944  1.1  christos     }
   2945  1.1  christos 
   2946  1.1  christos     if (update->num_records_started == 0) {
   2947  1.1  christos         adv_record_t *record = update->key != NULL ? update->key : (host->key_record != NULL ? host->key_record : NULL);
   2948  1.1  christos         if (record == NULL) {
   2949  1.1  christos         } else {
   2950  1.1  christos             if (record->rref == NULL) {
   2951  1.1  christos                 if (!register_host_record(host, record, skip_host_updates)) {
   2952  1.1  christos                     update_failed(update, dns_rcode_servfail, true, true);
   2953  1.1  christos                     return;
   2954  1.1  christos                 } else if (!skip_host_updates) {
   2955  1.1  christos                     update->num_records_started++;
   2956  1.1  christos                 }
   2957  1.1  christos             } else if (!skip_host_updates) {
   2958  1.1  christos                 update_host_tsr(record, update);
   2959  1.1  christos             }
   2960  1.1  christos         }
   2961  1.1  christos     }
   2962  1.1  christos 
   2963  1.1  christos     if (host->instances != NULL) {
   2964  1.1  christos         // For each service instance that's being added, register it.
   2965  1.1  christos         if (update->add_instances != NULL) {
   2966  1.1  christos             for (i = 0; i < update->add_instances->num; i++) {
   2967  1.1  christos                 if (update->add_instances->vec[i] != NULL) {
   2968  1.1  christos                     if (!register_instance(update->add_instances->vec[i])) {
   2969  1.1  christos                         update_failed(update, dns_rcode_servfail, true, true);
   2970  1.1  christos                         return;
   2971  1.1  christos                     }
   2972  1.1  christos                 }
   2973  1.1  christos             }
   2974  1.1  christos         }
   2975  1.1  christos 
   2976  1.1  christos         // For each service instance that's being renewed, update its TSR if the original registration still exist,
   2977  1.1  christos         // Otherwise re-register the instance.
   2978  1.1  christos         if (update->renew_instances != NULL) {
   2979  1.1  christos             for (i = 0; i < update->renew_instances->num; i++) {
   2980  1.1  christos                 if (update->renew_instances->vec[i] != NULL) {
   2981  1.1  christos                     adv_instance_t *instance = update->renew_instances->vec[i];
   2982  1.1  christos                     bool must_update = true;
   2983  1.1  christos                     bool renew_failed = instance->txn != NULL;
   2984  1.1  christos                     if (instance->txn != NULL) {
   2985  1.1  christos                         bool must_remove = false;
   2986  1.1  christos                         // Make sure the instance is still registered and is registered on the current shared connection.
   2987  1.1  christos                         if (instance->txn->sdref != NULL) {
   2988  1.1  christos                             if (((intptr_t)host->server_state->shared_registration_txn == instance->shared_txn)) {
   2989  1.1  christos                                 if (update_instance_tsr(instance, NULL)) {
   2990  1.1  christos                                     must_remove = false;
   2991  1.1  christos                                     must_update = false;
   2992  1.1  christos                                     instance->recent_message = (ptrdiff_t)update->client->message;
   2993  1.1  christos                                 } else {
   2994  1.1  christos                                     INFO("instance " PRI_S_SRP " (%p) tsr update failed, re-registering",
   2995  1.1  christos                                          instance->instance_name, instance);
   2996  1.1  christos                                     must_remove = true;
   2997  1.1  christos                                 }
   2998  1.1  christos                             } else {
   2999  1.1  christos                                 // If the shared transaction has changed, then the registration no longer exists, and
   3000  1.1  christos                                 // the sdref is no longer valid.
   3001  1.1  christos                                 INFO("instance " PRI_S_SRP " (%p) shared connection (%" PRIxPTR ") is stale, re-registering",
   3002  1.1  christos                                      instance->instance_name, instance, instance->shared_txn);
   3003  1.1  christos                                 instance->txn->sdref = NULL;
   3004  1.1  christos                                 must_remove = true;
   3005  1.1  christos                                 must_update = true;
   3006  1.1  christos                                 renew_failed = false;
   3007  1.1  christos                             }
   3008  1.1  christos                         }
   3009  1.1  christos                         if (must_remove) {
   3010  1.1  christos                             // If not, dispose of the transaction and re-register.
   3011  1.1  christos                             if (instance->txn != NULL) {
   3012  1.1  christos                                 ioloop_dnssd_txn_cancel_srp(host->server_state, instance->txn);
   3013  1.1  christos                                 ioloop_dnssd_txn_release(instance->txn);
   3014  1.1  christos                                 instance->txn = NULL;
   3015  1.1  christos                             }
   3016  1.1  christos                         }
   3017  1.1  christos                     }
   3018  1.1  christos                     if (must_update) {
   3019  1.1  christos                         if (renew_failed) {
   3020  1.1  christos                             INFO(PRI_S_SRP " (%p): failed to update TSR, re-registering", instance->instance_name, instance);
   3021  1.1  christos                         }
   3022  1.1  christos                         if (!register_instance(update->renew_instances->vec[i])) {
   3023  1.1  christos                             update_failed(update, dns_rcode_servfail, true, true);
   3024  1.1  christos                             return;
   3025  1.1  christos                         }
   3026  1.1  christos                     }
   3027  1.1  christos                 }
   3028  1.1  christos             }
   3029  1.1  christos         }
   3030  1.1  christos 
   3031  1.1  christos         // Sanity check that the instance vector sizes match between host and update.
   3032  1.1  christos         if (update->update_instances != NULL && update->update_instances->num != host->instances->num) {
   3033  1.1  christos             FAULT("update instance count %d differs from host instance count %d",
   3034  1.1  christos                   update->update_instances->num, host->instances->num);
   3035  1.1  christos             update_failed(update, dns_rcode_servfail, true, true);
   3036  1.1  christos             return;
   3037  1.1  christos         }
   3038  1.1  christos         if (update->remove_instances != NULL && update->remove_instances->num != host->instances->num) {
   3039  1.1  christos             FAULT("delete instance count %d differs from host instance count %d",
   3040  1.1  christos                   update->remove_instances->num, host->instances->num);
   3041  1.1  christos             update_failed(update, dns_rcode_servfail, true, true);
   3042  1.1  christos             return;
   3043  1.1  christos         }
   3044  1.1  christos         for (i = 0; i < host->instances->num; i++) {
   3045  1.1  christos             adv_instance_t *update_instance = update->update_instances->vec[i];
   3046  1.1  christos             if (update_instance != NULL && !update_instance->removed) {
   3047  1.1  christos                 adv_instance_t *host_instance = host->instances->vec[i];
   3048  1.1  christos                 bool must_register = true;
   3049  1.1  christos                 // Check to see if just the TXT record changes; in this case use DNSServiceUpdateRecord rather than re-registering
   3050  1.1  christos                 // the instance. If we can't update, we have to remove and then add. We could do this as a pair of atomic transactions
   3051  1.1  christos                 // if we used DNSServiceRegisterRecord rather than DNSServiceRegister, but currently we don't do that.
   3052  1.1  christos                 // Of course if the previous registration is no longer valid, re-register.
   3053  1.1  christos                 if (host_instance->txn != NULL && host_instance->txn->sdref != NULL && host->server_state != NULL &&
   3054  1.1  christos                     ((intptr_t)host->server_state->shared_registration_txn == host_instance->shared_txn))
   3055  1.1  christos                 {
   3056  1.1  christos                     if (update_instance->port == host_instance->port &&
   3057  1.1  christos                         update_instance->txt_length != 0 &&
   3058  1.1  christos                         memcmp(update_instance->txt_data, host_instance->txt_data, update_instance->txt_length))
   3059  1.1  christos                     {
   3060  1.1  christos                         // If we are able to update the TXT record using DNSServiceUpdateRecord, we don't actually need
   3061  1.1  christos                         // this update instance.
   3062  1.1  christos                         if (update_instance_tsr(host_instance, update_instance)) {
   3063  1.1  christos                             host_instance->recent_message = (ptrdiff_t)update->client->message;
   3064  1.1  christos                             RELEASE_HERE(update->update_instances->vec[i], adv_instance);
   3065  1.1  christos                             update_instance = NULL;
   3066  1.1  christos                             update->update_instances->vec[i] = NULL;
   3067  1.1  christos                             must_register = false;
   3068  1.1  christos                         }
   3069  1.1  christos                     }
   3070  1.1  christos                 }
   3071  1.1  christos                 if (must_register) {
   3072  1.1  christos                     if (host_instance->txn != NULL) {
   3073  1.1  christos                         ioloop_dnssd_txn_cancel_srp(host->server_state, host->instances->vec[i]->txn);
   3074  1.1  christos                         ioloop_dnssd_txn_release(host->instances->vec[i]->txn);
   3075  1.1  christos                         host->instances->vec[i]->txn = NULL;
   3076  1.1  christos                     }
   3077  1.1  christos 
   3078  1.1  christos                     if (!register_instance(update->update_instances->vec[i])) {
   3079  1.1  christos                         INFO("register instance failed.");
   3080  1.1  christos                         update_failed(update, dns_rcode_servfail, true, true);
   3081  1.1  christos                         return;
   3082  1.1  christos                     }
   3083  1.1  christos                 }
   3084  1.1  christos             }
   3085  1.1  christos         }
   3086  1.1  christos     }
   3087  1.1  christos 
   3088  1.1  christos     if (update->num_instances_started == 0 && update->num_records_started == 0) {
   3089  1.1  christos         INFO("no service or record updates, so we're finished.");
   3090  1.1  christos         srp_mdns_update_finished(update);
   3091  1.1  christos         return;
   3092  1.1  christos     }
   3093  1.1  christos 
   3094  1.1  christos }
   3095  1.1  christos 
   3096  1.1  christos // When a host has no update in progress, and there is a client update ready to process, we need to analyze
   3097  1.1  christos // the client update to see what work needs to be done.  This work is constructed as an translation from the
   3098  1.1  christos // raw update sent by the client (host->clients) into a prepared update that can be used directly to
   3099  1.1  christos // register the information with mDNSResponder.
   3100  1.1  christos //
   3101  1.1  christos // Normally a host will only have one prepared update in progress; however, if we lose our connection to
   3102  1.1  christos // mDNSResponder, then we need to re-create the host advertisement.  If there was an update in progress when
   3103  1.1  christos // this happened, we then need to reapply that as well.  In this case an update is constructed from the host, to
   3104  1.1  christos // get the host into the intended state, and the in-progress update is pushed below that; when the host has
   3105  1.1  christos // been re-created on the daemon, the pending update is popped back off the stack and restarted.
   3106  1.1  christos static void
   3107  1.1  christos prepare_update(adv_host_t *host, client_update_t *client_update)
   3108  1.1  christos {
   3109  1.1  christos     host_addr_t *addr;
   3110  1.1  christos     int i, j;
   3111  1.1  christos     service_instance_t *instance;
   3112  1.1  christos     adv_record_vec_t *remove_addrs = NULL;
   3113  1.1  christos     int num_remove_addrs = 0;
   3114  1.1  christos     adv_record_vec_t *add_addrs = NULL;
   3115  1.1  christos     int num_add_addrs = 0;
   3116  1.1  christos     int num_update_instances = 0;
   3117  1.1  christos     int num_add_instances = 0;
   3118  1.1  christos     int num_remove_instances = 0;
   3119  1.1  christos     int num_renew_instances = 0;
   3120  1.1  christos     adv_instance_vec_t *update_instances = NULL, *add_instances = NULL;
   3121  1.1  christos     adv_instance_vec_t *remove_instances = NULL, *renew_instances = NULL;
   3122  1.1  christos     adv_update_t *update = NULL;
   3123  1.1  christos 
   3124  1.1  christos     // Work to do:
   3125  1.1  christos     // - Figure out what address records to add and what address records to delete.
   3126  1.1  christos     // - Because we can only have one address record at a time currently, figure out which address record we want
   3127  1.1  christos     // - If we already have an address record published, and it's the same, do nothing
   3128  1.1  christos     //   - else if we already have an address record published, and it's changed to a different address, do an update
   3129  1.1  christos     //   - else if we have a new address record, publish it
   3130  1.1  christos     //   - else publish the key to hold the name
   3131  1.1  christos     // - Go through the set of service instances, identifying deletes, changes and adds
   3132  1.1  christos     //   - We don't currently allow deletes, but what that would look like would be an instance with no SRV or TXT
   3133  1.1  christos     //     record.
   3134  1.1  christos     //   - What about a delete that keeps the name but un-advertises the service?   How would we indicate that?
   3135  1.1  christos     //     Maybe if there's no service PTR for the service?
   3136  1.1  christos     //   - Changes means that the contents of the text record changed, or the contents of the SRV record
   3137  1.1  christos     //     changed (but not the hostname) or both.
   3138  1.1  christos     //   - New means that we don't have a service with that service instance name on the host (and we previously
   3139  1.1  christos     //     eliminated the possibility that it exists on some other host).
   3140  1.1  christos 
   3141  1.1  christos     // Allocate the update structure.
   3142  1.1  christos     update = calloc(1, sizeof *update);
   3143  1.1  christos     if (update == NULL) {
   3144  1.1  christos         ERROR("no memory for update.");
   3145  1.1  christos         goto fail;
   3146  1.1  christos     }
   3147  1.1  christos     RETAIN_HERE(update, adv_update); // For the lifetime of this function
   3148  1.1  christos 
   3149  1.1  christos     if (host->re_register_wakeup != NULL) {
   3150  1.1  christos         ioloop_cancel_wake_event(host->re_register_wakeup);
   3151  1.1  christos     }
   3152  1.1  christos     host->re_register_pending = false;
   3153  1.1  christos     update->start_time = srp_time();
   3154  1.1  christos 
   3155  1.1  christos     // The maximum number of addresses we could be deleting is all the ones the host currently has.
   3156  1.1  christos     if (host->addresses == NULL || host->addresses->num == 0) {
   3157  1.1  christos         num_remove_addrs = 0;
   3158  1.1  christos         remove_addrs = NULL;
   3159  1.1  christos     } else {
   3160  1.1  christos         num_remove_addrs = host->addresses->num;
   3161  1.1  christos         if (num_remove_addrs != 0) {
   3162  1.1  christos             remove_addrs = adv_record_vec_create(num_remove_addrs);
   3163  1.1  christos             // If we can't allocate space, just wait a bit.
   3164  1.1  christos             if (remove_addrs == NULL) {
   3165  1.1  christos                 ERROR("no memory for remove_addrs");
   3166  1.1  christos                 goto fail;
   3167  1.1  christos             }
   3168  1.1  christos             remove_addrs->num = num_remove_addrs;
   3169  1.1  christos         }
   3170  1.1  christos     }
   3171  1.1  christos 
   3172  1.1  christos     num_add_addrs = 0;
   3173  1.1  christos     for (addr = client_update->host->addrs; addr != NULL; addr = addr->next) {
   3174  1.1  christos         num_add_addrs++;
   3175  1.1  christos     }
   3176  1.1  christos     add_addrs = adv_record_vec_create(num_add_addrs);
   3177  1.1  christos     if (add_addrs == NULL) {
   3178  1.1  christos         ERROR("no memory for add_addrs");
   3179  1.1  christos         goto fail;
   3180  1.1  christos     }
   3181  1.1  christos 
   3182  1.1  christos     // Copy all of the addresses in the update into add_addresses
   3183  1.1  christos     num_add_addrs = 0;
   3184  1.1  christos     for (addr = client_update->host->addrs; addr; addr = addr->next) {
   3185  1.1  christos         bool add = true;
   3186  1.1  christos         for (i = 0; i < num_add_addrs; i++) {
   3187  1.1  christos             // If the client sends duplicate addresses, only add one of them.
   3188  1.1  christos             if (add_addrs->vec[i] != NULL &&
   3189  1.1  christos                 add_addrs->vec[i]->rrtype == addr->rr.type &&
   3190  1.1  christos                 add_addrs->vec[i]->rdlen == (addr->rr.type == dns_rrtype_a ? 4 : 16) &&
   3191  1.1  christos                 !memcmp(add_addrs->vec[i]->rdata, (uint8_t *)&addr->rr.data, add_addrs->vec[i]->rdlen))
   3192  1.1  christos             {
   3193  1.1  christos                 add = false;
   3194  1.1  christos             }
   3195  1.1  christos         }
   3196  1.1  christos         if (add) {
   3197  1.1  christos             adv_record_t *prepared_address = adv_record_create(addr->rr.type, addr->rr.type == dns_rrtype_a ? 4 : 16,
   3198  1.1  christos                                                                (uint8_t *)&addr->rr.data, host);
   3199  1.1  christos             if (prepared_address == NULL) {
   3200  1.1  christos                 ERROR("No memory for prepared address");
   3201  1.1  christos                 goto fail;
   3202  1.1  christos             }
   3203  1.1  christos             add_addrs->vec[num_add_addrs++] = prepared_address;
   3204  1.1  christos         }
   3205  1.1  christos     }
   3206  1.1  christos     add_addrs->num = num_add_addrs;
   3207  1.1  christos     for (i = 0; i < add_addrs->num; i++) {
   3208  1.1  christos         if (add_addrs->vec[i] != NULL) {
   3209  1.1  christos             add_addrs->vec[i]->update = update;
   3210  1.1  christos             RETAIN_HERE(add_addrs->vec[i]->update, adv_update);
   3211  1.1  christos         }
   3212  1.1  christos     }
   3213  1.1  christos 
   3214  1.1  christos #ifdef DEBUG_HOST_RECORDS_VERBOSE
   3215  1.1  christos     for (i = 0; i < 2; i++) {
   3216  1.1  christos         for (j = 0; j < (i ? num_add_addrs : num_remove_addrs); j++) {
   3217  1.1  christos             adv_record_t *address = i ? add_addrs->vec[j] : (host->addresses != NULL ? host->addresses->vec[j] : NULL);
   3218  1.1  christos             if (address == NULL) {
   3219  1.1  christos                 INFO(PUB_S_SRP " before: %d NULL", i ? "add" : "rmv", j);
   3220  1.1  christos             } else {
   3221  1.1  christos                 char foobuf[385], *foop = foobuf;
   3222  1.1  christos                 for (unsigned long k = 0; k < address->rdlen && k * 3 + 1 < sizeof(foobuf); k++) {
   3223  1.1  christos                     snprintf(foop, 4, k ? ":%02x" : "%02x", address->rdata[k]);
   3224  1.1  christos                     foop += k ? 3 : 2;
   3225  1.1  christos                 }
   3226  1.1  christos                 *foop = 0;
   3227  1.1  christos                 INFO(PUB_S_SRP " before: %d rrtype %d rdlen %d rdata " PRI_S_SRP, i ? "add" : "rmv", j,
   3228  1.1  christos                      address->rrtype, address->rdlen, foobuf);
   3229  1.1  christos             }
   3230  1.1  christos         }
   3231  1.1  christos     }
   3232  1.1  christos #endif // DEBUG_HOST_RECORDS_VERBOSE
   3233  1.1  christos 
   3234  1.1  christos     // For every host address, see if it's in add_addresses.   If it's not, it needs to be removed.
   3235  1.1  christos     // If it is, it doesn't need to be added.
   3236  1.1  christos     if (num_remove_addrs != 0) {
   3237  1.1  christos         for (i = 0; i < num_remove_addrs; i++) {
   3238  1.1  christos             if (host->addresses != NULL && host->addresses->vec[i] != NULL) {
   3239  1.1  christos                 remove_addrs->vec[i] = host->addresses->vec[i];
   3240  1.1  christos                 RETAIN_HERE(remove_addrs->vec[i], adv_record);
   3241  1.1  christos             }
   3242  1.1  christos             for (j = 0; j < num_add_addrs; j++) {
   3243  1.1  christos                 // If the address is present in both places, and has a valid registration, remove it from the list of
   3244  1.1  christos                 // addresses to add, and also remove it from the list of addresses to remove.  When we're done, all that
   3245  1.1  christos                 // will be remaining in the list to remove will be addresses that weren't present in the add list.
   3246  1.1  christos                 if (remove_addrs->vec[i] != NULL && add_addrs->vec[j] != NULL &&
   3247  1.1  christos                     remove_addrs->vec[i]->rref != NULL && host->server_state != NULL &&
   3248  1.1  christos                     (intptr_t)host->server_state->shared_registration_txn == remove_addrs->vec[i]->shared_txn &&
   3249  1.1  christos                     add_addrs->vec[j]->rrtype == remove_addrs->vec[i]->rrtype &&
   3250  1.1  christos                     add_addrs->vec[j]->rdlen == remove_addrs->vec[i]->rdlen &&
   3251  1.1  christos                     !memcmp(add_addrs->vec[j]->rdata, remove_addrs->vec[i]->rdata, remove_addrs->vec[i]->rdlen))
   3252  1.1  christos                 {
   3253  1.1  christos                     RELEASE_HERE(remove_addrs->vec[i], adv_record);
   3254  1.1  christos                     remove_addrs->vec[i] = NULL;
   3255  1.1  christos                     RELEASE_HERE(add_addrs->vec[j], adv_record);
   3256  1.1  christos                     add_addrs->vec[j] = NULL;
   3257  1.1  christos                 }
   3258  1.1  christos             }
   3259  1.1  christos         }
   3260  1.1  christos         remove_addrs->num = num_remove_addrs;
   3261  1.1  christos     }
   3262  1.1  christos 
   3263  1.1  christos #ifdef DEBUG_HOST_RECORDS_VERBOSE
   3264  1.1  christos     for (i = 0; i < 2; i++) {
   3265  1.1  christos         for (j = 0; j < (i ? num_add_addrs : num_remove_addrs); j++) {
   3266  1.1  christos             adv_record_t *address = i ? add_addrs->vec[j] : (remove_addrs != NULL ? remove_addrs->vec[j] : NULL);
   3267  1.1  christos             if (address == NULL) {
   3268  1.1  christos                 INFO(PUB_S_SRP "  after: %d NULL", i ? "add" : "rmv", j);
   3269  1.1  christos             } else {
   3270  1.1  christos                 char foobuf[385], *foop = foobuf;
   3271  1.1  christos                 for (unsigned long k = 0; k < address->rdlen && k * 3 + 1 < sizeof(foobuf); k++) {
   3272  1.1  christos                     snprintf(foop, 4, k ? ":%02x" : "%02x", address->rdata[k]);
   3273  1.1  christos                     foop += k ? 3 : 2;
   3274  1.1  christos                 }
   3275  1.1  christos                 *foop = 0;
   3276  1.1  christos                 INFO(PUB_S_SRP "  after: %d rrtype %d rdlen %d rdata " PRI_S_SRP, i ? "add" : "rmv", j,
   3277  1.1  christos                      address->rrtype, address->rdlen, foobuf);
   3278  1.1  christos             }
   3279  1.1  christos         }
   3280  1.1  christos     }
   3281  1.1  christos #endif // DEBUG_HOST_RECORDS_VERBOSE
   3282  1.1  christos 
   3283  1.1  christos     // Make a key record
   3284  1.1  christos     if (host->key_record == NULL) {
   3285  1.1  christos         update->key = adv_record_create(dns_rrtype_key, host->key_rdlen, host->key_rdata, host);
   3286  1.1  christos         if (update->key == NULL) {
   3287  1.1  christos             ERROR("no memory for key record");
   3288  1.1  christos             goto fail;
   3289  1.1  christos         }
   3290  1.1  christos         update->key->update = update;
   3291  1.1  christos         RETAIN_HERE(update->key->update, adv_update);
   3292  1.1  christos     }
   3293  1.1  christos 
   3294  1.1  christos     // We can never update more instances than currently exist for this host.
   3295  1.1  christos     num_update_instances = host->instances->num;
   3296  1.1  christos     num_remove_instances = host->instances->num;
   3297  1.1  christos     num_renew_instances = host->instances->num;
   3298  1.1  christos 
   3299  1.1  christos     update_instances = adv_instance_vec_create(num_update_instances);
   3300  1.1  christos     if (update_instances == NULL) {
   3301  1.1  christos         ERROR("no memory for update_instances");
   3302  1.1  christos         goto fail;
   3303  1.1  christos     }
   3304  1.1  christos     update_instances->num = num_update_instances;
   3305  1.1  christos 
   3306  1.1  christos     remove_instances = adv_instance_vec_create(num_remove_instances);
   3307  1.1  christos     if (remove_instances == NULL) {
   3308  1.1  christos         ERROR("no memory for remove_instances");
   3309  1.1  christos         goto fail;
   3310  1.1  christos     }
   3311  1.1  christos     remove_instances->num = num_remove_instances;
   3312  1.1  christos 
   3313  1.1  christos     renew_instances = adv_instance_vec_create(num_renew_instances);
   3314  1.1  christos     if (renew_instances == NULL) {
   3315  1.1  christos         ERROR("no memory for renew_instances");
   3316  1.1  christos         goto fail;
   3317  1.1  christos     }
   3318  1.1  christos     renew_instances->num = num_renew_instances;
   3319  1.1  christos 
   3320  1.1  christos     // Handle removes. Service instance removes have to remove the whole service instance, not some subset.
   3321  1.1  christos     for (delete_t *dp = client_update->removes; dp; dp = dp->next) {
   3322  1.1  christos         // Removes can be for services or service instances. Because we're acting as an
   3323  1.1  christos         // Advertising Proxy and not a regular name server, we don't track service instances,
   3324  1.1  christos         // and so we don't need to match them here. This if statement checks to see if the
   3325  1.1  christos         // name could possibly be a service instance name followed by a service type. We
   3326  1.1  christos         // can then extract the putative service instance name and service type and compare;
   3327  1.1  christos         // if they match, they are in fact those things, and if they don't, we don't care.
   3328  1.1  christos         if (dp->name != NULL && dp->name->next != NULL && dp->name->next->next != NULL) {
   3329  1.1  christos             char instance_name[DNS_MAX_LABEL_SIZE_ESCAPED + 1];
   3330  1.1  christos             char service_type[DNS_MAX_LABEL_SIZE_ESCAPED + 2];
   3331  1.1  christos 
   3332  1.1  christos             dns_name_print_to_limit(dp->name, dp->name->next, instance_name, sizeof(instance_name));
   3333  1.1  christos             dns_name_print_to_limit(dp->name->next, dp->name->next->next->next, service_type, sizeof(service_type));
   3334  1.1  christos 
   3335  1.1  christos             for (i = 0; i < host->instances->num; i++) {
   3336  1.1  christos                 adv_instance_t *remove_instance = host->instances->vec[i];
   3337  1.1  christos                 if (remove_instance != NULL) {
   3338  1.1  christos                     if (!strcmp(instance_name, remove_instance->instance_name) &&
   3339  1.1  christos                         service_types_equal(service_type, remove_instance->service_type))
   3340  1.1  christos                     {
   3341  1.1  christos                         remove_instances->vec[i] = remove_instance;
   3342  1.1  christos                         RETAIN_HERE(remove_instances->vec[i], adv_instance);
   3343  1.1  christos                         break;
   3344  1.1  christos                     }
   3345  1.1  christos                 }
   3346  1.1  christos             }
   3347  1.1  christos         }
   3348  1.1  christos     }
   3349  1.1  christos 
   3350  1.1  christos     // The number of instances to add can be as many as there are instances in the update.
   3351  1.1  christos     num_add_instances = 0;
   3352  1.1  christos     for (instance = client_update->instances; instance; instance = instance->next) {
   3353  1.1  christos         num_add_instances++;
   3354  1.1  christos     }
   3355  1.1  christos     add_instances = adv_instance_vec_create(num_add_instances);
   3356  1.1  christos     if (add_instances == NULL) {
   3357  1.1  christos         ERROR("prepare_update: no memory for add_instances");
   3358  1.1  christos         goto fail;
   3359  1.1  christos     }
   3360  1.1  christos 
   3361  1.1  christos     // Convert all of the instances in the client update to adv_instance_t structures for easy comparison.
   3362  1.1  christos     // Any that are unchanged will have to be freed--oh well.
   3363  1.1  christos     i = 0;
   3364  1.1  christos     for (instance = client_update->instances; instance != NULL; instance = instance->next) {
   3365  1.1  christos         adv_instance_t *prepared_instance = adv_instance_create(instance, host, update);
   3366  1.1  christos         if (prepared_instance == NULL) {
   3367  1.1  christos             // prepare_instance logs.
   3368  1.1  christos             goto fail;
   3369  1.1  christos         }
   3370  1.1  christos         if (i >= num_add_instances) {
   3371  1.1  christos             FAULT("while preparing client update instances, i >= num_add_instances");
   3372  1.1  christos             RELEASE_HERE(prepared_instance, adv_instance);
   3373  1.1  christos             prepared_instance = NULL;
   3374  1.1  christos             goto fail;
   3375  1.1  christos         }
   3376  1.1  christos 
   3377  1.1  christos         prepared_instance->anycast = false;
   3378  1.1  christos         if (client_update != NULL && client_update->connection != NULL) {
   3379  1.1  christos             const struct sockaddr *server_addr = connection_get_local_address(client_update->message);
   3380  1.1  christos             if (server_addr && server_addr->sa_family == AF_INET6) {
   3381  1.1  christos                 const struct in6_addr *const ipv6_address = &(((const struct sockaddr_in6 *)server_addr)->sin6_addr);
   3382  1.1  christos                 uint16_t server_port = ntohs(((const struct sockaddr_in6 *)server_addr)->sin6_port);
   3383  1.1  christos                 SEGMENTED_IPv6_ADDR_GEN_SRP(ipv6_address, addr_buf);
   3384  1.1  christos                 INFO("server address " PRI_SEGMENTED_IPv6_ADDR_SRP "; server port %d",
   3385  1.1  christos                       SEGMENTED_IPv6_ADDR_PARAM_SRP(ipv6_address, addr_buf), server_port);
   3386  1.1  christos                 if (is_thread_mesh_anycast_address(ipv6_address) && server_port == 53) {
   3387  1.1  christos                     prepared_instance->anycast = true;
   3388  1.1  christos                 }
   3389  1.1  christos             }
   3390  1.1  christos         }
   3391  1.1  christos         add_instances->vec[i++] = prepared_instance;
   3392  1.1  christos     }
   3393  1.1  christos     add_instances->num = i;
   3394  1.1  christos 
   3395  1.1  christos     // The instances in the update are now in add_instances.  If they are updates, move them to update_instances.  If
   3396  1.1  christos     // they are unchanged, free them and null them out, and remember the current instance in renew_instances.  If they
   3397  1.1  christos     // are adds, leave them.
   3398  1.1  christos     for (i = 0; i < num_add_instances; i++) {
   3399  1.1  christos         adv_instance_t *add_instance = add_instances->vec[i];
   3400  1.1  christos 
   3401  1.1  christos         if (add_instance != NULL) {
   3402  1.1  christos             for (j = 0; j < host->instances->num; j++) {
   3403  1.1  christos                 adv_instance_t *host_instance = host->instances->vec[j];
   3404  1.1  christos 
   3405  1.1  christos                 // See if the instance names match.
   3406  1.1  christos                 if (host_instance != NULL &&
   3407  1.1  christos                     !strcmp(add_instance->instance_name, host_instance->instance_name) &&
   3408  1.1  christos                     service_types_equal(add_instance->service_type, host_instance->service_type))
   3409  1.1  christos                 {
   3410  1.1  christos                     // If the rdata is the same, and the service type is the same (including subtypes), and it's not
   3411  1.1  christos                     // deleted, it's not an add or an update.
   3412  1.1  christos                     if (!host_instance->removed && add_instance->txt_length == host_instance->txt_length &&
   3413  1.1  christos                         add_instance->port == host_instance->port &&
   3414  1.1  christos                         !strcmp(add_instance->service_type, host_instance->service_type) &&
   3415  1.1  christos                         (add_instance->txt_length == 0 ||
   3416  1.1  christos                          !memcmp(add_instance->txt_data, host_instance->txt_data, add_instance->txt_length)))
   3417  1.1  christos                     {
   3418  1.1  christos                         RELEASE_HERE(add_instances->vec[i], adv_instance);
   3419  1.1  christos                         add_instances->vec[i] = NULL;
   3420  1.1  christos                         renew_instances->vec[j] = host_instance;
   3421  1.1  christos                         RETAIN_HERE(host_instance, adv_instance);
   3422  1.1  christos                         renew_instances->vec[j]->update = update;
   3423  1.1  christos                         RETAIN_HERE(renew_instances->vec[j]->update, adv_update);
   3424  1.1  christos                         INFO(PRI_S_SRP "." PRI_S_SRP " renewed for host " PRI_S_SRP,
   3425  1.1  christos                              host_instance->instance_name, host_instance->service_type, host->name);
   3426  1.1  christos                     } else {
   3427  1.1  christos                         update_instances->vec[j] = add_instance;
   3428  1.1  christos                         RETAIN_HERE(update_instances->vec[j], adv_instance);
   3429  1.1  christos                         RELEASE_HERE(add_instances->vec[i], adv_instance);
   3430  1.1  christos                         add_instances->vec[i] = NULL;
   3431  1.1  christos                     }
   3432  1.1  christos                     break;
   3433  1.1  christos                 }
   3434  1.1  christos             }
   3435  1.1  christos         }
   3436  1.1  christos     }
   3437  1.1  christos 
   3438  1.1  christos     // At this point we have figured out all the work we need to do, so hang it off an update structure.
   3439  1.1  christos     update->host = host;
   3440  1.1  christos     RETAIN_HERE(update->host, adv_host);
   3441  1.1  christos     update->client = client_update;
   3442  1.1  christos     update->remove_addresses = remove_addrs;
   3443  1.1  christos     update->add_addresses = add_addrs;
   3444  1.1  christos     update->remove_instances = remove_instances;
   3445  1.1  christos     update->add_instances = add_instances;
   3446  1.1  christos     update->update_instances = update_instances;
   3447  1.1  christos     update->renew_instances = renew_instances;
   3448  1.1  christos     update->host_lease = client_update->host_lease;
   3449  1.1  christos     update->key_lease = client_update->key_lease;
   3450  1.1  christos 
   3451  1.1  christos     // Register any added addresses with threadradiod before we actually advertise them, to avoid a spurious
   3452  1.1  christos     // address query.
   3453  1.1  christos 
   3454  1.1  christos     host->update = update;
   3455  1.1  christos     RETAIN_HERE(host->update, adv_update);
   3456  1.1  christos     RELEASE_HERE(update, adv_update);
   3457  1.1  christos     update = NULL;
   3458  1.1  christos 
   3459  1.1  christos 
   3460  1.1  christos     start_host_update(host);
   3461  1.1  christos     return;
   3462  1.1  christos 
   3463  1.1  christos fail:
   3464  1.1  christos     if (client_update != NULL) {
   3465  1.1  christos         srp_parse_client_updates_free(client_update);
   3466  1.1  christos         client_update = NULL;
   3467  1.1  christos     }
   3468  1.1  christos     if (remove_addrs != NULL) {
   3469  1.1  christos         // Addresses in remove_addrs are owned by the host and don't need to be freed.
   3470  1.1  christos         RELEASE_HERE(remove_addrs, adv_record_vec);
   3471  1.1  christos         remove_addrs = NULL;
   3472  1.1  christos     }
   3473  1.1  christos     if (add_addrs != NULL) {
   3474  1.1  christos         RELEASE_HERE(add_addrs, adv_record_vec);
   3475  1.1  christos         add_addrs = NULL;
   3476  1.1  christos     }
   3477  1.1  christos     if (add_instances != NULL) {
   3478  1.1  christos         RELEASE_HERE(add_instances, adv_instance_vec);
   3479  1.1  christos         add_instances = NULL;
   3480  1.1  christos     }
   3481  1.1  christos     if (remove_instances != NULL) {
   3482  1.1  christos         RELEASE_HERE(remove_instances, adv_instance_vec);
   3483  1.1  christos         remove_instances = NULL;
   3484  1.1  christos     }
   3485  1.1  christos     if (update_instances != NULL) {
   3486  1.1  christos         RELEASE_HERE(update_instances, adv_instance_vec);
   3487  1.1  christos         update_instances = NULL;
   3488  1.1  christos     }
   3489  1.1  christos     if (update) {
   3490  1.1  christos         RELEASE_HERE(update, adv_update);
   3491  1.1  christos     }
   3492  1.1  christos }
   3493  1.1  christos 
   3494  1.1  christos typedef enum { missed, match, conflict } instance_outcome_t;
   3495  1.1  christos static instance_outcome_t
   3496  1.1  christos compare_instance(adv_instance_t *instance,
   3497  1.1  christos                  dns_host_description_t *new_host, adv_host_t *host,
   3498  1.1  christos                  char *instance_name, char *service_type)
   3499  1.1  christos {
   3500  1.1  christos     if (instance == NULL) {
   3501  1.1  christos         return missed;
   3502  1.1  christos     }
   3503  1.1  christos     if (host->removed) {
   3504  1.1  christos         return missed;
   3505  1.1  christos     }
   3506  1.1  christos     if (!strcmp(instance_name, instance->instance_name) && service_types_equal(service_type, instance->service_type)) {
   3507  1.1  christos         if (!dns_names_equal_text(new_host->name, host->name)) {
   3508  1.1  christos             return conflict;
   3509  1.1  christos         }
   3510  1.1  christos         return match;
   3511  1.1  christos     }
   3512  1.1  christos     return missed;
   3513  1.1  christos }
   3514  1.1  christos 
   3515  1.1  christos bool
   3516  1.1  christos srp_update_start(client_update_t *client_update)
   3517  1.1  christos {
   3518  1.1  christos     dns_host_description_t *new_host = client_update->host;
   3519  1.1  christos     char new_host_name[DNS_MAX_NAME_SIZE_ESCAPED + 1];
   3520  1.1  christos     srp_server_t *server_state = client_update->server_state;
   3521  1.1  christos     uint32_t key_id = 0;
   3522  1.1  christos     dns_name_print(new_host->name, new_host_name, sizeof new_host_name);
   3523  1.1  christos     adv_host_t *host = NULL;
   3524  1.1  christos     srpl_connection_t *srpl_connection = client_update->srpl_connection;
   3525  1.1  christos     message_t *raw_message = client_update->message;
   3526  1.1  christos     comm_t *connection = client_update->connection;
   3527  1.1  christos 
   3528  1.1  christos 
   3529  1.1  christos     // Compute a checksum on the key, ignoring up to three bytes at the end.
   3530  1.1  christos     for (client_update_t *update = client_update; update != NULL; update = update->next) {
   3531  1.1  christos         dns_host_description_t *update_host = update->host;
   3532  1.1  christos 
   3533  1.1  christos         uint32_t update_key_id = 0;
   3534  1.1  christos         for (unsigned i = 0; i < update_host->key->data.key.len; i += 4) {
   3535  1.1  christos             update_key_id += ((update_host->key->data.key.key[i] << 24) | (update_host->key->data.key.key[i + 1] << 16) |
   3536  1.1  christos                               (update_host->key->data.key.key[i + 2] << 8) | (update_host->key->data.key.key[i + 3]));
   3537  1.1  christos         }
   3538  1.1  christos         if (update == client_update) {
   3539  1.1  christos             key_id = update_key_id;
   3540  1.1  christos         } else if (key_id != update_key_id) {
   3541  1.1  christos             ERROR("update contains multiple key ids %x and %x", key_id, update_key_id);
   3542  1.1  christos             advertise_finished(NULL, new_host_name, server_state,
   3543  1.1  christos                                srpl_connection, NULL, raw_message, dns_rcode_refused, NULL, false, true);
   3544  1.1  christos             goto cleanup;
   3545  1.1  christos         }
   3546  1.1  christos     }
   3547  1.1  christos 
   3548  1.1  christos     char seenbuf[200];
   3549  1.1  christos     char *already_seen = seenbuf;
   3550  1.1  christos     const char *plural = "";
   3551  1.1  christos 
   3552  1.1  christos     // For replicated updates, check the transaction IDs to make sure we aren't applying an update we've already gotten.
   3553  1.1  christos     if (srpl_connection != NULL) {
   3554  1.1  christos         for (host = server_state->hosts; host != NULL; host = host->next) {
   3555  1.1  christos             if (host->key_id == key_id && !strcmp(host->name, new_host_name)) {
   3556  1.1  christos                 break;
   3557  1.1  christos             }
   3558  1.1  christos         }
   3559  1.1  christos 
   3560  1.1  christos         if (host != NULL) {
   3561  1.1  christos             bool replay = true;
   3562  1.1  christos             while (client_update != NULL && replay) {
   3563  1.1  christos                 replay = false;
   3564  1.1  christos                 if (host->message != NULL && host->message->wire.id == client_update->message->wire.id) {
   3565  1.1  christos                     replay = true;
   3566  1.1  christos                 } else if (host->instances != NULL) {
   3567  1.1  christos                     for (int i = 0; i < host->instances->num; i++) {
   3568  1.1  christos                         adv_instance_t *instance = host->instances->vec[i];
   3569  1.1  christos                         if (instance != NULL) {
   3570  1.1  christos                             if (instance->message != NULL &&
   3571  1.1  christos                                 instance->message->wire.id == client_update->message->wire.id)
   3572  1.1  christos                             {
   3573  1.1  christos                                 replay = true;
   3574  1.1  christos                                 break;
   3575  1.1  christos                             }
   3576  1.1  christos                         }
   3577  1.1  christos                     }
   3578  1.1  christos                 }
   3579  1.1  christos                 if (replay) {
   3580  1.1  christos                     client_update_t *skip_update = client_update;
   3581  1.1  christos                     client_update = client_update->next;
   3582  1.1  christos                     if (already_seen != seenbuf) {
   3583  1.1  christos                         plural = "s";
   3584  1.1  christos                     }
   3585  1.1  christos                     if (already_seen + 6 < &seenbuf[sizeof(seenbuf)]) {
   3586  1.1  christos                         snprintf(already_seen, 6, " %04x", skip_update->message->wire.id);
   3587  1.1  christos                         already_seen += 5;
   3588  1.1  christos                     }
   3589  1.1  christos                     skip_update->next = NULL;
   3590  1.1  christos                     srp_parse_client_updates_free(skip_update);
   3591  1.1  christos                     if (client_update != NULL) {
   3592  1.1  christos                         new_host = client_update->host;
   3593  1.1  christos                     } else {
   3594  1.1  christos                         new_host = NULL;
   3595  1.1  christos                     }
   3596  1.1  christos                 }
   3597  1.1  christos             }
   3598  1.1  christos         }
   3599  1.1  christos     }
   3600  1.1  christos     if (already_seen != seenbuf) {
   3601  1.1  christos         INFO("host update for " PRI_S_SRP ", key id %" PRIx32 " " PUB_S_SRP " (skipped xid" PUB_S_SRP PUB_S_SRP ")",
   3602  1.1  christos              new_host_name, key_id, srpl_connection == NULL ? "" : srpl_connection->name, plural, seenbuf);
   3603  1.1  christos     } else {
   3604  1.1  christos         INFO("host update for " PRI_S_SRP ", key id %" PRIx32 " " PUB_S_SRP,
   3605  1.1  christos              new_host_name, key_id, srpl_connection == NULL ? "" : srpl_connection->name);
   3606  1.1  christos     }
   3607  1.1  christos     if (client_update == NULL) {
   3608  1.1  christos         advertise_finished(host, new_host_name, server_state, srpl_connection,
   3609  1.1  christos                            NULL, raw_message, dns_rcode_noerror, NULL, false, true);
   3610  1.1  christos         return true; // It's safe to just return here because we've freed all the client updates.
   3611  1.1  christos     }
   3612  1.1  christos 
   3613  1.1  christos     service_instance_t *instances = client_update->instances;
   3614  1.1  christos     delete_t *removes = client_update->removes;
   3615  1.1  christos     adv_host_t **p_hosts = NULL;
   3616  1.1  christos     char pres_name[DNS_MAX_NAME_SIZE_ESCAPED + 1];
   3617  1.1  christos     service_instance_t *new_instance;
   3618  1.1  christos     instance_outcome_t outcome = missed;
   3619  1.1  christos     char instance_name[DNS_MAX_LABEL_SIZE_ESCAPED + 1];
   3620  1.1  christos     char service_type[DNS_MAX_LABEL_SIZE_ESCAPED * 2 + 2];
   3621  1.1  christos     host_addr_t *addr;
   3622  1.1  christos     const bool remove = client_update->host_lease == 0;
   3623  1.1  christos     const char *updatestr = client_update->host_lease == 0 ? "remove" : "update";
   3624  1.1  christos     delete_t *dp;
   3625  1.1  christos 
   3626  1.1  christos 
   3627  1.1  christos     for (addr = new_host->addrs; addr != NULL; addr = addr->next) {
   3628  1.1  christos         if (addr->rr.type == dns_rrtype_a) {
   3629  1.1  christos             IPv4_ADDR_GEN_SRP(&addr->rr.data.a.s_addr, addr_buf);
   3630  1.1  christos             INFO("host " PUB_S_SRP " for " PRI_S_SRP ", address " PRI_IPv4_ADDR_SRP " " PUB_S_SRP, updatestr,
   3631  1.1  christos                  new_host_name, IPv4_ADDR_PARAM_SRP(&addr->rr.data.a.s_addr, addr_buf),
   3632  1.1  christos                  srpl_connection == NULL ? "" : srpl_connection->name);
   3633  1.1  christos         } else {
   3634  1.1  christos             SEGMENTED_IPv6_ADDR_GEN_SRP(addr->rr.data.aaaa.s6_addr, addr_buf);
   3635  1.1  christos             INFO("host " PUB_S_SRP " for " PRI_S_SRP ", address " PRI_SEGMENTED_IPv6_ADDR_SRP " " PUB_S_SRP,
   3636  1.1  christos                  updatestr, new_host_name, SEGMENTED_IPv6_ADDR_PARAM_SRP(addr->rr.data.aaaa.s6_addr, addr_buf),
   3637  1.1  christos                  srpl_connection == NULL ? "" : srpl_connection->name);
   3638  1.1  christos         }
   3639  1.1  christos     }
   3640  1.1  christos     for (new_instance = instances; new_instance != NULL; new_instance = new_instance->next) {
   3641  1.1  christos         extract_instance_name(instance_name, sizeof instance_name, service_type, sizeof service_type, new_instance);
   3642  1.1  christos         INFO("host " PUB_S_SRP " for " PRI_S_SRP ", instance name " PRI_S_SRP ", type " PRI_S_SRP
   3643  1.1  christos              ", port %d " PUB_S_SRP, updatestr, new_host_name, instance_name, service_type,
   3644  1.1  christos              new_instance->srv != NULL ? new_instance->srv->data.srv.port : -1,
   3645  1.1  christos              srpl_connection == NULL ? "" : srpl_connection->name);
   3646  1.1  christos         if (new_instance->txt != NULL) {
   3647  1.1  christos             char txt_buf[DNS_DATA_SIZE];
   3648  1.1  christos             dns_txt_data_print(txt_buf, DNS_DATA_SIZE, new_instance->txt->data.txt.len, new_instance->txt->data.txt.data);
   3649  1.1  christos             INFO("text data for instance " PRI_S_SRP ": " PRI_S_SRP, instance_name, txt_buf);
   3650  1.1  christos         }
   3651  1.1  christos     }
   3652  1.1  christos 
   3653  1.1  christos     // Look for matching service instance names.   A service instance name that matches, but has a different
   3654  1.1  christos     // hostname, means that there is a conflict.   We have to look through all the entries; the presence of
   3655  1.1  christos     // a matching hostname doesn't mean we are done UNLESS there's a matching service instance name pointing
   3656  1.1  christos     // to that hostname.
   3657  1.1  christos     for (host = server_state->hosts; host; host = host->next) {
   3658  1.1  christos         // If a host has been removed, it won't have any instances to compare against. Later on, if we find that
   3659  1.1  christos         // there is no matching host for this update, we look through the host list again and remove the
   3660  1.1  christos         // "removed" host if it has the same name, so we don't need to do anything further here.
   3661  1.1  christos         if (host->removed) {
   3662  1.1  christos             continue;
   3663  1.1  christos         }
   3664  1.1  christos         // We need to look for matches both in the registered instances for this registration, and also in
   3665  1.1  christos         // the list of new instances, in case we get a duplicate update while a previous update is in progress.
   3666  1.1  christos         for (new_instance = instances; new_instance; new_instance = new_instance->next) {
   3667  1.1  christos             extract_instance_name(instance_name, sizeof instance_name, service_type, sizeof service_type, new_instance);
   3668  1.1  christos 
   3669  1.1  christos             // First check for a match or conflict in the host itself.
   3670  1.1  christos             for (int i = 0; i < host->instances->num; i++) {
   3671  1.1  christos                 outcome = compare_instance(host->instances->vec[i], new_host, host,
   3672  1.1  christos                                            instance_name, service_type);
   3673  1.1  christos                 if (outcome != missed) {
   3674  1.1  christos                     goto found_something;
   3675  1.1  christos                 }
   3676  1.1  christos             }
   3677  1.1  christos 
   3678  1.1  christos             // Then look for the same thing in any subsequent updates that have been baked.
   3679  1.1  christos             if (host->update != NULL) {
   3680  1.1  christos                 if (host->update->add_instances != NULL) {
   3681  1.1  christos                     for (int i = 0; i < host->update->add_instances->num; i++) {
   3682  1.1  christos                         outcome = compare_instance(host->update->add_instances->vec[i], new_host, host,
   3683  1.1  christos                                                    instance_name, service_type);
   3684  1.1  christos                         if (outcome != missed) {
   3685  1.1  christos                             goto found_something;
   3686  1.1  christos                         }
   3687  1.1  christos                     }
   3688  1.1  christos                 }
   3689  1.1  christos             }
   3690  1.1  christos         }
   3691  1.1  christos     }
   3692  1.1  christos found_something:
   3693  1.1  christos     if (outcome == conflict) {
   3694  1.1  christos         ERROR("service instance name " PRI_S_SRP "/" PRI_S_SRP " already pointing to host "
   3695  1.1  christos               PRI_S_SRP ", not host " PRI_S_SRP, instance_name, service_type, host->name, new_host_name);
   3696  1.1  christos         advertise_finished(NULL, host->name,
   3697  1.1  christos                            server_state, srpl_connection, connection, raw_message, dns_rcode_yxdomain, NULL, true, true);
   3698  1.1  christos         goto cleanup;
   3699  1.1  christos     }
   3700  1.1  christos 
   3701  1.1  christos     // We may have received removes for individual records. In this case, we need to make sure they only remove
   3702  1.1  christos     // records that have been added to the host that matches.
   3703  1.1  christos     for (adv_host_t *rhp = server_state->hosts; rhp != NULL; rhp = rhp->next) {
   3704  1.1  christos         if (rhp->removed) {
   3705  1.1  christos             continue;
   3706  1.1  christos         }
   3707  1.1  christos 
   3708  1.1  christos         // Look for removes that conflict
   3709  1.1  christos         for (dp = removes; dp != NULL; dp = dp->next) {
   3710  1.1  christos             // We only need to do this for service instance names. We don't really know what is and isn't a
   3711  1.1  christos             // service instance name, but if it /could/ be a service instance name, we compare; if it matches,
   3712  1.1  christos             // it is a service instance name, and if not, no problem.
   3713  1.1  christos             if (dp->name != NULL && dp->name->next != NULL && dp->name->next->next != NULL) {
   3714  1.1  christos                 dns_name_print_to_limit(dp->name, dp->name->next, instance_name, sizeof(instance_name));
   3715  1.1  christos                 dns_name_print_to_limit(dp->name->next, dp->name->next->next->next, service_type, sizeof(service_type));
   3716  1.1  christos 
   3717  1.1  christos                 // See if the delete deletes an instance on the host
   3718  1.1  christos                 for (int i = 0; i < rhp->instances->num; i++) {
   3719  1.1  christos                     adv_instance_t *instance = rhp->instances->vec[i];
   3720  1.1  christos                     if (instance != NULL) {
   3721  1.1  christos                         if (!strcmp(instance_name, instance->instance_name) &&
   3722  1.1  christos                             service_types_equal(service_type, instance->service_type))
   3723  1.1  christos                         {
   3724  1.1  christos                             if (!strcmp(new_host_name, rhp->name)) {
   3725  1.1  christos                                 ERROR("remove for " PRI_S_SRP "." PRI_S_SRP " matches instance on host " PRI_S_SRP,
   3726  1.1  christos                                       instance_name, service_type, rhp->name);
   3727  1.1  christos                                 dp->consumed = true;
   3728  1.1  christos                             } else {
   3729  1.1  christos                                 ERROR("remove for " PRI_S_SRP "." PRI_S_SRP " conflicts with instance on host " PRI_S_SRP,
   3730  1.1  christos                                       instance_name, service_type, rhp->name);
   3731  1.1  christos                                 advertise_finished(NULL, rhp->name, server_state, srpl_connection,
   3732  1.1  christos                                                    connection, raw_message, dns_rcode_formerr, NULL, true, true);
   3733  1.1  christos                                 goto cleanup;
   3734  1.1  christos                             }
   3735  1.1  christos                         }
   3736  1.1  christos                     }
   3737  1.1  christos                 }
   3738  1.1  christos 
   3739  1.1  christos                 // See if the remove removes an instance on an update on the host
   3740  1.1  christos                 if (rhp->update) {
   3741  1.1  christos                     if (rhp->update->add_instances != NULL) {
   3742  1.1  christos                         for (int i = 0; i < rhp->update->add_instances->num; i++) {
   3743  1.1  christos                             adv_instance_t *instance = rhp->update->add_instances->vec[i];
   3744  1.1  christos                             if (instance != NULL) {
   3745  1.1  christos                                 if (!strcmp(instance_name, instance->instance_name) &&
   3746  1.1  christos                                     service_types_equal(service_type, instance->service_type))
   3747  1.1  christos                                 {
   3748  1.1  christos                                     if (!strcmp(new_host_name, rhp->name)) {
   3749  1.1  christos                                         dp->consumed = true;
   3750  1.1  christos                                     } else {
   3751  1.1  christos                                         ERROR("remove for " PRI_S_SRP " conflicts with instance on update to host " PRI_S_SRP,
   3752  1.1  christos                                               instance->instance_name, rhp->name);
   3753  1.1  christos                                         advertise_finished(NULL, rhp->name, server_state, srpl_connection,
   3754  1.1  christos                                                            connection, raw_message, dns_rcode_formerr, NULL, true, true);
   3755  1.1  christos                                         goto cleanup;
   3756  1.1  christos                                     }
   3757  1.1  christos                                 }
   3758  1.1  christos                             }
   3759  1.1  christos                         }
   3760  1.1  christos                     }
   3761  1.1  christos                 }
   3762  1.1  christos             }
   3763  1.1  christos         }
   3764  1.1  christos     }
   3765  1.1  christos 
   3766  1.1  christos     // Log any unmatched deletes, but we don't consider these to be errors.
   3767  1.1  christos     for (dp = removes; dp != NULL; dp = dp->next) {
   3768  1.1  christos         if (!dp->consumed) {
   3769  1.1  christos             DNS_NAME_GEN_SRP(dp->name, name_buf);
   3770  1.1  christos             INFO("remove for " PRI_DNS_NAME_SRP " doesn't match any instance on any host.",
   3771  1.1  christos                  DNS_NAME_PARAM_SRP(dp->name, name_buf));
   3772  1.1  christos         }
   3773  1.1  christos     }
   3774  1.1  christos 
   3775  1.1  christos     // If we fall off the end looking for a matching service instance, there isn't a matching
   3776  1.1  christos     // service instance, but there may be a matching host, so look for that.
   3777  1.1  christos     if (outcome == missed) {
   3778  1.1  christos         // Search for the new hostname in the list of hosts, which is sorted.
   3779  1.1  christos         for (p_hosts = &server_state->hosts; *p_hosts; p_hosts = &host->next) {
   3780  1.1  christos             host = *p_hosts;
   3781  1.1  christos             int comparison = strcasecmp(new_host_name, host->name);
   3782  1.1  christos             if (comparison == 0) {
   3783  1.1  christos                 // If we get an update for a host that was removed, and it's not also a remove,
   3784  1.1  christos                 // remove the host entry that's marking the remove. If this is a remove, just flag
   3785  1.1  christos                 // it as a miss.
   3786  1.1  christos                 if (host->removed) {
   3787  1.1  christos                     outcome = missed;
   3788  1.1  christos                     if (remove) {
   3789  1.1  christos                         break;
   3790  1.1  christos                     }
   3791  1.1  christos                     // if remove is more recent than this message (for example, we firt receive remove
   3792  1.1  christos                     // from the actual client and then receive a stale update message from a replication
   3793  1.1  christos                     // peer), we don't apply this message and end processing here.
   3794  1.1  christos                     if (host->remove_received_time > client_update->message->received_time) {
   3795  1.1  christos                         INFO("update for host " PRI_S_SRP " which has been deleted.", host->name);
   3796  1.1  christos                         advertise_finished(NULL, host->name, server_state, srpl_connection,
   3797  1.1  christos                                            connection, raw_message, dns_rcode_servfail, NULL, true, true);
   3798  1.1  christos                         goto cleanup;
   3799  1.1  christos                     }
   3800  1.1  christos                     *p_hosts = host->next;
   3801  1.1  christos                     host_invalidate(host);
   3802  1.1  christos                     RELEASE_HERE(host, adv_host);
   3803  1.1  christos                     host = NULL;
   3804  1.1  christos                     break;
   3805  1.1  christos                 }
   3806  1.1  christos                 if (key_id == host->key_id && dns_keys_rdata_equal(new_host->key, &host->key)) {
   3807  1.1  christos                     outcome = match;
   3808  1.1  christos                     break;
   3809  1.1  christos                 }
   3810  1.1  christos                 ERROR("update for host " PRI_S_SRP " has key id %" PRIx32
   3811  1.1  christos                       " which doesn't match host key id %" PRIx32 ".",
   3812  1.1  christos                       host->name, key_id, host->key_id);
   3813  1.1  christos                 advertise_finished(NULL, host->name, server_state, srpl_connection,
   3814  1.1  christos                                    connection, raw_message, dns_rcode_yxdomain, NULL, true, true);
   3815  1.1  christos                 goto cleanup;
   3816  1.1  christos             } else if (comparison < 0) {
   3817  1.1  christos                 break;
   3818  1.1  christos             }
   3819  1.1  christos         }
   3820  1.1  christos     } else {
   3821  1.1  christos         if (key_id != host->key_id || !dns_keys_rdata_equal(new_host->key, &host->key)) {
   3822  1.1  christos             ERROR("new host with name " PRI_S_SRP " and key id %" PRIx32
   3823  1.1  christos                   " conflicts with existing host " PRI_S_SRP " with key id %" PRIx32,
   3824  1.1  christos                   new_host_name, key_id, host->name, host->key_id);
   3825  1.1  christos             advertise_finished(NULL, host->name, server_state, srpl_connection,
   3826  1.1  christos                                connection, raw_message, dns_rcode_yxdomain, NULL, true, true);
   3827  1.1  christos             goto cleanup;
   3828  1.1  christos         }
   3829  1.1  christos     }
   3830  1.1  christos 
   3831  1.1  christos     // If we didn't find a matching host, we can make a new one.   When we create it, it just has
   3832  1.1  christos     // a name and no records.  The update that we then construct will have the missing records.
   3833  1.1  christos     // We don't want to do this for a remove, obviously.
   3834  1.1  christos     if (outcome == missed) {
   3835  1.1  christos         if (remove) {
   3836  1.1  christos             ERROR("Remove for host " PRI_S_SRP " which doesn't exist.", new_host_name);
   3837  1.1  christos             advertise_finished(NULL, new_host_name, server_state, srpl_connection,
   3838  1.1  christos                                connection, raw_message, dns_rcode_noerror, NULL, true, true);
   3839  1.1  christos             goto cleanup;
   3840  1.1  christos         }
   3841  1.1  christos         host = calloc(1, sizeof *host);
   3842  1.1  christos         if (host == NULL) {
   3843  1.1  christos             ERROR("no memory for host data structure.");
   3844  1.1  christos             advertise_finished(NULL, new_host_name, server_state, srpl_connection,
   3845  1.1  christos                                connection, raw_message, dns_rcode_servfail, NULL, true, true);
   3846  1.1  christos             goto cleanup;
   3847  1.1  christos         }
   3848  1.1  christos         RETAIN_HERE(host, adv_host);
   3849  1.1  christos         host->server_state = server_state;
   3850  1.1  christos         host->instances = adv_instance_vec_create(0);
   3851  1.1  christos         if (host->instances == NULL) {
   3852  1.1  christos             ERROR("no memory for host instance vector.");
   3853  1.1  christos             advertise_finished(NULL, new_host_name, server_state, srpl_connection,
   3854  1.1  christos                                connection, raw_message, dns_rcode_servfail, NULL, true, true);
   3855  1.1  christos             RELEASE_HERE(host, adv_host);
   3856  1.1  christos             host = NULL;
   3857  1.1  christos             goto cleanup;
   3858  1.1  christos         }
   3859  1.1  christos         host->addresses = adv_record_vec_create(0);
   3860  1.1  christos         if (host->addresses == NULL) {
   3861  1.1  christos             ERROR("no memory for host address vector.");
   3862  1.1  christos             advertise_finished(NULL, new_host_name, server_state, srpl_connection,
   3863  1.1  christos                                connection, raw_message, dns_rcode_servfail, NULL, true, true);
   3864  1.1  christos             RELEASE_HERE(host, adv_host);
   3865  1.1  christos             host = NULL;
   3866  1.1  christos             goto cleanup;
   3867  1.1  christos         }
   3868  1.1  christos 
   3869  1.1  christos         host->retry_wakeup = ioloop_wakeup_create();
   3870  1.1  christos         if (host->retry_wakeup != NULL) {
   3871  1.1  christos             host->lease_wakeup = ioloop_wakeup_create();
   3872  1.1  christos         }
   3873  1.1  christos         if (host->lease_wakeup == NULL) {
   3874  1.1  christos             ERROR("no memory for wake event on host");
   3875  1.1  christos             advertise_finished(NULL, new_host_name, server_state, srpl_connection,
   3876  1.1  christos                                connection, raw_message, dns_rcode_servfail, NULL, true, true);
   3877  1.1  christos             RELEASE_HERE(host, adv_host);
   3878  1.1  christos             host = NULL;
   3879  1.1  christos             goto cleanup;
   3880  1.1  christos         }
   3881  1.1  christos         dns_name_print(new_host->name, pres_name, sizeof pres_name);
   3882  1.1  christos         host->name = strdup(pres_name);
   3883  1.1  christos         if (host->name == NULL) {
   3884  1.1  christos             RELEASE_HERE(host, adv_host);
   3885  1.1  christos             host = NULL;
   3886  1.1  christos             ERROR("no memory for hostname.");
   3887  1.1  christos             advertise_finished(NULL, new_host_name, server_state, srpl_connection,
   3888  1.1  christos                                connection, raw_message, dns_rcode_servfail, NULL, true, true);
   3889  1.1  christos             goto cleanup;
   3890  1.1  christos         }
   3891  1.1  christos         host->key = *new_host->key;
   3892  1.1  christos #ifndef __clang_analyzer__
   3893  1.1  christos         // Normally this would be invalid, but we never use the name of the key record.
   3894  1.1  christos         host->key.name = NULL;
   3895  1.1  christos #endif
   3896  1.1  christos         host->key_rdlen = new_host->key->data.key.len + 4;
   3897  1.1  christos         host->key_rdata = malloc(host->key_rdlen);
   3898  1.1  christos         if (host->key_rdata == NULL) {
   3899  1.1  christos             RELEASE_HERE(host, adv_host);
   3900  1.1  christos             host = NULL;
   3901  1.1  christos             ERROR("no memory for host key.");
   3902  1.1  christos             advertise_finished(NULL, new_host_name, server_state, srpl_connection,
   3903  1.1  christos                                connection, raw_message, dns_rcode_servfail, NULL, true, true);
   3904  1.1  christos             goto cleanup;
   3905  1.1  christos         }
   3906  1.1  christos         memcpy(host->key_rdata, &new_host->key->data.key.flags, 2);
   3907  1.1  christos         host->key_rdata[2] = new_host->key->data.key.protocol;
   3908  1.1  christos         host->key_rdata[3] = new_host->key->data.key.algorithm;
   3909  1.1  christos         memcpy(&host->key_rdata[4], new_host->key->data.key.key, new_host->key->data.key.len);
   3910  1.1  christos         host->key.data.key.key = &host->key_rdata[4];
   3911  1.1  christos         host->key_id = key_id;
   3912  1.1  christos 
   3913  1.1  christos         // Insert this in the list where it would have sorted.  The if test is because the optimizer doesn't notice that
   3914  1.1  christos         // p_hosts can never be null here--it will always be pointing to the end of the list of hosts if we get here.
   3915  1.1  christos         if (p_hosts != NULL) {
   3916  1.1  christos             host->next = *p_hosts;
   3917  1.1  christos             *p_hosts = host;
   3918  1.1  christos         }
   3919  1.1  christos         p_hosts = NULL;
   3920  1.1  christos     }
   3921  1.1  christos 
   3922  1.1  christos     // If we are already updating this host, either this is a retransmission, or it's a new transaction. In the case
   3923  1.1  christos     // of a retransmission, we need to keep doing the work we've been asked to do, and hopefully we'll reply before the
   3924  1.1  christos     // client gives up. In the case of a new request, we aren't ready for it yet; the client really shouldn't have sent
   3925  1.1  christos     // it so quickly, but if it's behaving correctly, we should be done with the current update before it retransmits,
   3926  1.1  christos     // so we can safely ignore it. If we're getting a replication update, it can't be newer than the current update.
   3927  1.1  christos     // So we can ignore it--we'll send a replication update when we're done processing the client update.
   3928  1.1  christos     if (host->update != NULL) {
   3929  1.1  christos #ifdef SRP_DETECT_STALLS
   3930  1.1  christos         time_t now = srp_time();
   3931  1.1  christos         // It's possible that we could get an update that stalls due to a problem communicating with mDNSResponder
   3932  1.1  christos         // and that a timing race prevents this from being detected correctly. In this case, cancel the update and
   3933  1.1  christos         // let the retry go through. We don't want to do this unless there's a clear stall, so we're allowing ten
   3934  1.1  christos         // seconds.
   3935  1.1  christos         if (now - host->update->start_time > 10) {
   3936  1.1  christos             INFO("update has stalled, failing it silently.");
   3937  1.1  christos             update_failed(host->update, dns_rcode_servfail, false, false);
   3938  1.1  christos             service_disconnected(server_state, (intptr_t)server_state->shared_registration_txn);
   3939  1.1  christos         } else {
   3940  1.1  christos #endif // SRP_DETECT_STALLS
   3941  1.1  christos             INFO("dropping retransmission of in-progress update for host " PRI_S_SRP, host->name);
   3942  1.1  christos #if SRP_FEATURE_REPLICATION
   3943  1.1  christos             srp_replication_advertise_finished(host, host->name, server_state, srpl_connection,
   3944  1.1  christos                                                connection, dns_rcode_servfail, true);
   3945  1.1  christos #endif
   3946  1.1  christos         cleanup:
   3947  1.1  christos             srp_parse_client_updates_free(client_update);
   3948  1.1  christos             return false;
   3949  1.1  christos #ifdef SRP_DETECT_STALLS
   3950  1.1  christos         }
   3951  1.1  christos #endif
   3952  1.1  christos     }
   3953  1.1  christos 
   3954  1.1  christos     // If this is a remove, remove the host registrations and mark the host removed. We keep it around until the
   3955  1.1  christos     // lease expires to prevent replication accidentally re-adding a removed host as a result of a bad timing
   3956  1.1  christos     // coincidence.
   3957  1.1  christos     if (remove) {
   3958  1.1  christos         host_invalidate(host);
   3959  1.1  christos         // We need to propagate the remove message.
   3960  1.1  christos         if (host->message != NULL) {
   3961  1.1  christos             ioloop_message_release(host->message);
   3962  1.1  christos         }
   3963  1.1  christos         host->message = raw_message;
   3964  1.1  christos         // remember the time when the message that removes the host was received
   3965  1.1  christos         host->remove_received_time = host->message->received_time;
   3966  1.1  christos         ioloop_message_retain(host->message);
   3967  1.1  christos         advertise_finished(host, new_host_name, server_state, srpl_connection,
   3968  1.1  christos                            connection, raw_message, dns_rcode_noerror, NULL, true, true);
   3969  1.1  christos         goto cleanup;
   3970  1.1  christos     }
   3971  1.1  christos 
   3972  1.1  christos     // At this point we have an update and a host to which to apply it.  We may already be doing an earlier
   3973  1.1  christos     // update, or not.  Create a client update structure to hold the communication, so that when we are done,
   3974  1.1  christos     // we can respond.
   3975  1.1  christos     if (outcome == missed) {
   3976  1.1  christos         INFO("New host " PRI_S_SRP ", key id %" PRIx32 , host->name, host->key_id);
   3977  1.1  christos     } else {
   3978  1.1  christos         if (host->registered_name != host->name) {
   3979  1.1  christos             INFO("Renewing host " PRI_S_SRP ", alias " PRI_S_SRP ", key id %" PRIx32,
   3980  1.1  christos                  host->name, host->registered_name, host->key_id);
   3981  1.1  christos         } else {
   3982  1.1  christos             INFO("Renewing host " PRI_S_SRP ", key id %" PRIx32, host->name, host->key_id);
   3983  1.1  christos         }
   3984  1.1  christos     }
   3985  1.1  christos 
   3986  1.1  christos     if (host->registered_name == NULL) {
   3987  1.1  christos         host->registered_name = host->name;
   3988  1.1  christos     }
   3989  1.1  christos 
   3990  1.1  christos     // We have to take the lease from the SRP update--the original registrar negotiated it, and if it's out
   3991  1.1  christos     // of our range, that's too bad (ish).
   3992  1.1  christos     if (raw_message->lease != 0) {
   3993  1.1  christos         INFO("basing lease time on message: raw_message->lease = %d, raw_message->key_lease = %d",
   3994  1.1  christos              raw_message->lease, raw_message->key_lease);
   3995  1.1  christos         client_update->host_lease = raw_message->lease;
   3996  1.1  christos         client_update->key_lease = raw_message->key_lease;
   3997  1.1  christos     } else {
   3998  1.1  christos         if (client_update->host_lease < server_state->max_lease_time) {
   3999  1.1  christos             if (client_update->host_lease < server_state->min_lease_time) {
   4000  1.1  christos                 INFO("basing lease time on server_state->min_lease_time: %d", server_state->min_lease_time);
   4001  1.1  christos                 client_update->host_lease = server_state->min_lease_time;
   4002  1.1  christos             } else {
   4003  1.1  christos                 INFO("basing lease time on client_update->host_lease: %d", client_update->host_lease);
   4004  1.1  christos                 // client_update->host_lease = client_update->host_lease;
   4005  1.1  christos             }
   4006  1.1  christos         } else {
   4007  1.1  christos             client_update->host_lease = server_state->max_lease_time;
   4008  1.1  christos                 INFO("basing lease time on server_state->max_lease_time: %d", server_state->max_lease_time);
   4009  1.1  christos         }
   4010  1.1  christos         if (client_update->key_lease < server_state->key_max_lease_time) {
   4011  1.1  christos             if (client_update->key_lease < server_state->key_min_lease_time) {
   4012  1.1  christos                 client_update->key_lease = server_state->key_min_lease_time;
   4013  1.1  christos             } else {
   4014  1.1  christos                 // client_update->key_lease = client_update->key_lease;
   4015  1.1  christos             }
   4016  1.1  christos         } else {
   4017  1.1  christos             client_update->key_lease = server_state->key_max_lease_time;
   4018  1.1  christos         }
   4019  1.1  christos     }
   4020  1.1  christos 
   4021  1.1  christos #if SRP_FEATURE_REPLICATION
   4022  1.1  christos     if (srpl_connection != NULL) {
   4023  1.1  christos         host->srpl_connection = srpl_connection;
   4024  1.1  christos         srpl_connection_retain(host->srpl_connection);
   4025  1.1  christos     }
   4026  1.1  christos #endif // SRP_FEATURE_REPLICATION
   4027  1.1  christos 
   4028  1.1  christos     // Apply the update.
   4029  1.1  christos     prepare_update(host, client_update);
   4030  1.1  christos     return true;
   4031  1.1  christos }
   4032  1.1  christos 
   4033  1.1  christos void
   4034  1.1  christos srp_mdns_flush(srp_server_t *server_state)
   4035  1.1  christos {
   4036  1.1  christos     adv_host_t *host, *host_next;
   4037  1.1  christos 
   4038  1.1  christos     INFO("flushing all host entries.");
   4039  1.1  christos     for (host = server_state->hosts; host; host = host_next) {
   4040  1.1  christos         INFO("Flushing services and host entry for " PRI_S_SRP " (" PRI_S_SRP ")",
   4041  1.1  christos              host->name, host->registered_name);
   4042  1.1  christos         // Get rid of the updates before calling delete_host, which will fail if update is not NULL.
   4043  1.1  christos         if (host->update != NULL) {
   4044  1.1  christos             update_failed(host->update, dns_rcode_refused, false, true);
   4045  1.1  christos         }
   4046  1.1  christos         host_next = host->next;
   4047  1.1  christos         host_remove(host);
   4048  1.1  christos     }
   4049  1.1  christos     server_state->hosts = NULL;
   4050  1.1  christos }
   4051  1.1  christos 
   4052  1.1  christos static void
   4053  1.1  christos usage(void)
   4054  1.1  christos {
   4055  1.1  christos     ERROR("srp-mdns-proxy [--max-lease-time <seconds>] [--min-lease-time <seconds>] [--log-stderr]");
   4056  1.1  christos     ERROR("               [--enable-replication | --disable-replication]");
   4057  1.1  christos #if SRP_FEATURE_NAT64
   4058  1.1  christos     ERROR("               [--enable-nat64 | --disable-nat64]");
   4059  1.1  christos #endif
   4060  1.1  christos     exit(1);
   4061  1.1  christos }
   4062  1.1  christos 
   4063  1.1  christos srp_server_t *
   4064  1.1  christos server_state_create(const char *name, int max_lease_time, int min_lease_time,
   4065  1.1  christos                     int key_max_lease_time, int key_min_lease_time)
   4066  1.1  christos {
   4067  1.1  christos     srp_server_t *server_state = calloc(1, sizeof(*server_state));
   4068  1.1  christos     if (server_state == NULL || (server_state->name = strdup(name)) == NULL) {
   4069  1.1  christos         ERROR("no memory for server state");
   4070  1.1  christos         free(server_state);
   4071  1.1  christos         return NULL;
   4072  1.1  christos     }
   4073  1.1  christos     server_state->max_lease_time = max_lease_time;
   4074  1.1  christos     server_state->min_lease_time = min_lease_time;
   4075  1.1  christos     server_state->key_max_lease_time = key_max_lease_time;
   4076  1.1  christos     server_state->key_min_lease_time = key_min_lease_time;
   4077  1.1  christos     server_state->priority = PRIORITY_DEFAULT;
   4078  1.1  christos #if TARGET_OS_TV
   4079  1.1  christos #endif
   4080  1.1  christos     INFO("priority set to %d", server_state->priority);
   4081  1.1  christos     return server_state;
   4082  1.1  christos }
   4083  1.1  christos 
   4084  1.1  christos static void
   4085  1.1  christos object_allocation_stats_dump_callback(void *context)
   4086  1.1  christos {
   4087  1.1  christos     srp_server_t *server_state = context;
   4088  1.1  christos 
   4089  1.1  christos     ioloop_dump_object_allocation_stats();
   4090  1.1  christos 
   4091  1.1  christos     if (server_state->full_dump_count == 0) {
   4092  1.1  christos         srp_dump_server_stats(server_state, true, true);
   4093  1.1  christos         server_state->full_dump_count = 12;
   4094  1.1  christos     } else {
   4095  1.1  christos         srp_dump_server_stats(server_state, false, true);
   4096  1.1  christos     }
   4097  1.1  christos     --server_state->full_dump_count;
   4098  1.1  christos 
   4099  1.1  christos     // Do the next object memory allocation statistics dump in five minutes
   4100  1.1  christos     ioloop_add_wake_event(server_state->object_allocation_stats_dump_wakeup, server_state,
   4101  1.1  christos                           object_allocation_stats_dump_callback, NULL, 5 * 60 * 1000);
   4102  1.1  christos }
   4103  1.1  christos 
   4104  1.1  christos int
   4105  1.1  christos main(int argc, char **argv)
   4106  1.1  christos {
   4107  1.1  christos     int i;
   4108  1.1  christos     char *end;
   4109  1.1  christos     int log_stderr = false;
   4110  1.1  christos #ifdef SRP_TEST_SERVER
   4111  1.1  christos     char *test_to_run = NULL;
   4112  1.1  christos     bool normal_srp_startup = false;
   4113  1.1  christos #else
   4114  1.1  christos     bool normal_srp_startup = true;
   4115  1.1  christos #endif
   4116  1.1  christos #if STUB_ROUTER
   4117  1.1  christos     bool stub_router_enabled = false;
   4118  1.1  christos #endif
   4119  1.1  christos     bool thread_device_enabled = false;
   4120  1.1  christos 
   4121  1.1  christos     srp_servers = server_state_create("srp-mdns-proxy",
   4122  1.1  christos                                       3600 * 27,     // max lease time one day plus 20%
   4123  1.1  christos                                       30,            // min lease time 30 seconds
   4124  1.1  christos                                       3600 * 24 * 7, // max key lease 7 days
   4125  1.1  christos                                       30);           // min key lease time 30s
   4126  1.1  christos     if (srp_servers == NULL) {
   4127  1.1  christos         return 1;
   4128  1.1  christos     }
   4129  1.1  christos 
   4130  1.1  christos     if (normal_srp_startup) {
   4131  1.1  christos         srp_servers->srp_replication_enabled = true;
   4132  1.1  christos #  if SRP_FEATURE_NAT64
   4133  1.1  christos         srp_servers->srp_nat64_enabled = true;
   4134  1.1  christos #  endif
   4135  1.1  christos     }
   4136  1.1  christos 
   4137  1.1  christos 
   4138  1.1  christos     // Set the advertise interface
   4139  1.1  christos     if (0) {
   4140  1.1  christos #if STUB_ROUTER
   4141  1.1  christos     } else if (stub_router_enabled) {
   4142  1.1  christos         srp_servers->advertise_interface = kDNSServiceInterfaceIndexAny;
   4143  1.1  christos #endif
   4144  1.1  christos     } else {
   4145  1.1  christos         srp_servers->advertise_interface = if_nametoindex("lo0");
   4146  1.1  christos     }
   4147  1.1  christos     for (i = 1; i < argc; i++) {
   4148  1.1  christos         if (!strcmp(argv[i], "--max-lease-time")) {
   4149  1.1  christos             if (i + 1 == argc) {
   4150  1.1  christos                 usage();
   4151  1.1  christos             }
   4152  1.1  christos             srp_servers->max_lease_time = (uint32_t)strtoul(argv[i + 1], &end, 10);
   4153  1.1  christos             if (end == argv[i + 1] || end[0] != 0) {
   4154  1.1  christos                 usage();
   4155  1.1  christos             }
   4156  1.1  christos             i++;
   4157  1.1  christos         } else if (!strcmp(argv[i], "--min-lease-time")) {
   4158  1.1  christos             if (i + 1 == argc) {
   4159  1.1  christos                 usage();
   4160  1.1  christos             }
   4161  1.1  christos             srp_servers->min_lease_time = (uint32_t)strtoul(argv[i + 1], &end, 10);
   4162  1.1  christos             if (end == argv[i + 1] || end[0] != 0) {
   4163  1.1  christos                 usage();
   4164  1.1  christos             }
   4165  1.1  christos             i++;
   4166  1.1  christos         } else if (!strcmp(argv[i], "--log-stderr")) {
   4167  1.1  christos             log_stderr = true;
   4168  1.1  christos #ifdef LOG_FPRINTF_STDERR
   4169  1.1  christos         } else if (!strcmp(argv[i], "--log-relative-timestamp")) {
   4170  1.1  christos             srp_log_timestamp_relative = true;
   4171  1.1  christos #endif
   4172  1.1  christos         } else if (!strcmp(argv[i], "--enable-replication")) {
   4173  1.1  christos             srp_servers->srp_replication_enabled = true;
   4174  1.1  christos         } else if (!strcmp(argv[i], "--disable-replication")) {
   4175  1.1  christos             srp_servers->srp_replication_enabled = false;
   4176  1.1  christos         } else if (!strcmp(argv[i], "--fake-xpanid")) {
   4177  1.1  christos             if (i + 1 == argc) {
   4178  1.1  christos                 usage();
   4179  1.1  christos             }
   4180  1.1  christos             srp_servers->xpanid = strtoul(argv[i + 1], &end, 16);
   4181  1.1  christos             if (end == argv[i + 1] || end[0] != 0) {
   4182  1.1  christos                 usage();
   4183  1.1  christos             }
   4184  1.1  christos #ifdef SRP_TEST_SERVER
   4185  1.1  christos         } else if (!strcmp(argv[i], "--test")) {
   4186  1.1  christos             if (i + 1 == argc) {
   4187  1.1  christos                 usage();
   4188  1.1  christos             }
   4189  1.1  christos             test_to_run = argv[i + 1];
   4190  1.1  christos             i++;
   4191  1.1  christos #endif
   4192  1.1  christos #if SRP_FEATURE_NAT64
   4193  1.1  christos         } else if (!strcmp(argv[i], "--enable-nat64")) {
   4194  1.1  christos             srp_servers->srp_nat64_enabled = true;
   4195  1.1  christos         } else if (!strcmp(argv[i], "--disable-nat64")) {
   4196  1.1  christos             srp_servers->srp_nat64_enabled = false;
   4197  1.1  christos #endif
   4198  1.1  christos         } else {
   4199  1.1  christos             usage();
   4200  1.1  christos         }
   4201  1.1  christos     }
   4202  1.1  christos 
   4203  1.1  christos     // Setup log category for srp-mdns-prox and dnssd-proxy.
   4204  1.1  christos     OPENLOG("srp-mdns-proxy", log_stderr);
   4205  1.1  christos 
   4206  1.1  christos #ifdef SRP_TEST_SERVER
   4207  1.1  christos     INFO("srp-test-server starting, compiled on " PUB_S_SRP ", " PUB_S_SRP, __DATE__, __TIME__);
   4208  1.1  christos #else
   4209  1.1  christos     INFO("--------------------------------"
   4210  1.1  christos          "srp-mdns-proxy starting, compiled on " PUB_S_SRP ", " PUB_S_SRP
   4211  1.1  christos          "--------------------------------", __DATE__, __TIME__);
   4212  1.1  christos #endif
   4213  1.1  christos 
   4214  1.1  christos     if (!ioloop_init()) {
   4215  1.1  christos         return 1;
   4216  1.1  christos     }
   4217  1.1  christos 
   4218  1.1  christos     if (normal_srp_startup) {
   4219  1.1  christos #if THREAD_DEVICE
   4220  1.1  christos         if (0) {
   4221  1.1  christos #if STUB_ROUTER
   4222  1.1  christos         } else if (stub_router_enabled) {
   4223  1.1  christos             srp_servers->route_state = route_state_create(srp_servers, "srp-mdns-proxy");
   4224  1.1  christos             if (srp_servers->route_state == NULL) {
   4225  1.1  christos                 return 1;
   4226  1.1  christos             }
   4227  1.1  christos #endif // STUB_ROUTER
   4228  1.1  christos         }
   4229  1.1  christos 
   4230  1.1  christos         if (!srp_mdns_shared_registration_txn_setup(srp_servers)) {
   4231  1.1  christos             return 1;
   4232  1.1  christos         }
   4233  1.1  christos         dns_service_op_not_to_be_freed = srp_servers->shared_registration_txn->sdref;
   4234  1.1  christos #endif // THREAD_DEVICE
   4235  1.1  christos 
   4236  1.1  christos #if STUB_ROUTER
   4237  1.1  christos         if (stub_router_enabled) {
   4238  1.1  christos             // Set up the ULA early just in case we get an early registration, nat64 will use the ula
   4239  1.1  christos             route_ula_setup(srp_servers->route_state);
   4240  1.1  christos         }
   4241  1.1  christos #endif
   4242  1.1  christos 
   4243  1.1  christos 
   4244  1.1  christos #if (SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY)
   4245  1.1  christos         if (!init_dnssd_proxy(srp_servers)) {
   4246  1.1  christos             ERROR("failed to setup dnssd-proxy");
   4247  1.1  christos         }
   4248  1.1  christos #endif // #if (SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY)
   4249  1.1  christos 
   4250  1.1  christos #if STUB_ROUTER
   4251  1.1  christos         if (stub_router_enabled) {
   4252  1.1  christos             if (!start_icmp_listener()) {
   4253  1.1  christos                 ERROR("failed to start icmp listener");
   4254  1.1  christos             }
   4255  1.1  christos         }
   4256  1.1  christos #endif
   4257  1.1  christos 
   4258  1.1  christos 
   4259  1.1  christos         infrastructure_network_startup(srp_servers->route_state);
   4260  1.1  christos 
   4261  1.1  christos         if (adv_ctl_init(srp_servers) != kDNSServiceErr_NoError) {
   4262  1.1  christos             ERROR("Can't start advertising proxy control server.");
   4263  1.1  christos         }
   4264  1.1  christos 
   4265  1.1  christos         // We require one open file per service and one per instance.
   4266  1.1  christos         struct rlimit limits;
   4267  1.1  christos         if (getrlimit(RLIMIT_NOFILE, &limits) < 0) {
   4268  1.1  christos             ERROR("getrlimit failed: " PUB_S_SRP, strerror(errno));
   4269  1.1  christos         }
   4270  1.1  christos 
   4271  1.1  christos         if (limits.rlim_cur < 1024) {
   4272  1.1  christos             if (limits.rlim_max < 1024) {
   4273  1.1  christos                 INFO("file descriptor hard limit is %llu", (unsigned long long)limits.rlim_max);
   4274  1.1  christos                 if (limits.rlim_cur != limits.rlim_max) {
   4275  1.1  christos                     limits.rlim_cur = limits.rlim_max;
   4276  1.1  christos                 }
   4277  1.1  christos             } else {
   4278  1.1  christos                 limits.rlim_cur = 1024;
   4279  1.1  christos             }
   4280  1.1  christos             if (setrlimit(RLIMIT_NOFILE, &limits) < 0) {
   4281  1.1  christos                 ERROR("setrlimit failed: " PUB_S_SRP, strerror(errno));
   4282  1.1  christos             }
   4283  1.1  christos         }
   4284  1.1  christos 
   4285  1.1  christos         srp_proxy_init("local");
   4286  1.1  christos #ifdef SRP_TEST_SERVER
   4287  1.1  christos     } else  {
   4288  1.1  christos         ioloop_run_async(srp_test_server_run_test, test_to_run);
   4289  1.1  christos #endif
   4290  1.1  christos     }
   4291  1.1  christos 
   4292  1.1  christos     srp_servers->object_allocation_stats_dump_wakeup = ioloop_wakeup_create();
   4293  1.1  christos     if (srp_servers->object_allocation_stats_dump_wakeup == NULL) {
   4294  1.1  christos         INFO("no memory for srp_servers->object_allocation_stats_dump_wakeup");
   4295  1.1  christos     } else {
   4296  1.1  christos         // Do an object memory allocation statistics dump every five minutes, and a full database dump every half hour
   4297  1.1  christos         // starting after the first five minutes
   4298  1.1  christos         srp_servers->full_dump_count = 1;
   4299  1.1  christos         object_allocation_stats_dump_callback(srp_servers);
   4300  1.1  christos     }
   4301  1.1  christos 
   4302  1.1  christos     ioloop();
   4303  1.1  christos }
   4304  1.1  christos 
   4305  1.1  christos // Local Variables:
   4306  1.1  christos // mode: C
   4307  1.1  christos // tab-width: 4
   4308  1.1  christos // c-file-style: "bsd"
   4309  1.1  christos // c-basic-offset: 4
   4310  1.1  christos // fill-column: 120
   4311  1.1  christos // indent-tabs-mode: nil
   4312  1.1  christos // End:
   4313