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