srp-mdns-proxy.c revision 1.1 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