xref: /netbsd-src/external/apache2/mDNSResponder/dist/ServiceRegistration/service-tracker.c (revision 32d1c65c71fbdb65a012e8392a62a757dd6853e9)
1 /* service-tracker.c
2  *
3  * Copyright (c) 2023-2024 Apple Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     https://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * Track services on the Thread mesh.
18  */
19 
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <pwd.h>
25 #include <errno.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <fcntl.h>
30 #include <time.h>
31 #include <dns_sd.h>
32 #include <net/if.h>
33 #include <inttypes.h>
34 #include <sys/resource.h>
35 #include <netinet/icmp6.h>
36 #include "srp.h"
37 #include "dns-msg.h"
38 #include "srp-crypto.h"
39 #include "ioloop.h"
40 #include "srp-gw.h"
41 #include "srp-proxy.h"
42 #include "srp-mdns-proxy.h"
43 #include "dnssd-proxy.h"
44 #include "config-parse.h"
45 #include "cti-services.h"
46 #include "thread-device.h"
47 #include "state-machine.h"
48 #include "thread-service.h"
49 #include "service-tracker.h"
50 #include "probe-srp.h"
51 #include "adv-ctl-server.h"
52 
53 struct service_tracker_callback {
54 	service_tracker_callback_t *next;
55 	void (*context_release)(void *NONNULL context);
56 	void (*callback)(void *context);
57     void *context;
58 };
59 
60 struct service_tracker {
61 	int ref_count;
62     uint64_t id;
63     route_state_t *route_state;
64     srp_server_t *server_state;
65     cti_connection_t NULLABLE thread_service_context;
66 	service_tracker_callback_t *callbacks;
67     thread_service_t *NULLABLE thread_services;
68     uint16_t rloc16;
69     bool user_service_seen;
70 };
71 
72 static uint64_t service_tracker_serial_number = 0;
73 
74 static void
75 service_tracker_finalize(service_tracker_t *tracker)
76 {
77     thread_service_t *next;
78     for (thread_service_t *service = tracker->thread_services; service != NULL; service = next) {
79         next = service->next;
80         thread_service_release(service);
81     }
82     free(tracker);
83 }
84 
85 static void
86 service_tracker_context_release(void *context)
87 {
88     service_tracker_t *tracker = context;
89     if (tracker != NULL) {
90         RELEASE_HERE(tracker, service_tracker);
91     }
92 }
93 
94 RELEASE_RETAIN_FUNCS(service_tracker);
95 
96 void
97 service_tracker_thread_service_note(service_tracker_t *tracker, thread_service_t *tservice, const char *event_description)
98 {
99     char owner_id[20];
100     snprintf(owner_id, sizeof(owner_id), "[ST%lld]", tracker->id);
101     thread_service_note(owner_id, tservice, event_description);
102 }
103 
104 typedef struct state_debug_accumulator {
105     char change[20]; // " +stable +user +ncp"
106     char *p_change;
107     size_t left;
108     bool changed;
109 } accumulator_t;
110 
111 static void
112 service_tracker_flags_accumulator_init(accumulator_t *accumulator)
113 {
114     memset(accumulator, 0, sizeof(*accumulator));
115     accumulator->p_change = accumulator->change;
116     accumulator->left = sizeof(accumulator->change);
117 }
118 
119 static void
120 service_tracker_flags_accumulate(accumulator_t *accumulator, bool previous, bool cur, const char *name)
121 {
122     size_t len;
123     if (previous != cur) {
124         snprintf(accumulator->p_change, accumulator->left, "%s%s%s",
125                  accumulator->p_change == accumulator->change ? "" : " ", cur ? "+" : "-", name);
126         len = strlen(accumulator->p_change);
127         accumulator->p_change += len;
128         accumulator->left -= len;
129         accumulator->changed = true;
130     }
131 }
132 
133 static void
134 service_tracker_callback(void *context, cti_service_vec_t *services, cti_status_t status)
135 {
136     service_tracker_t *tracker = context;
137     size_t i;
138     thread_service_t **pservice = &tracker->thread_services, *service = NULL;
139     tracker->user_service_seen = false;
140 
141     if (status == kCTIStatus_Disconnected || status == kCTIStatus_DaemonNotRunning) {
142         INFO("[ST%lld] disconnected", tracker->id);
143         cti_events_discontinue(tracker->thread_service_context);
144         tracker->thread_service_context = NULL;
145         RELEASE_HERE(tracker, service_tracker); // Not expecting any more callbacks.
146         return;
147     }
148 
149     if (status != kCTIStatus_NoError) {
150         ERROR("[ST%lld] %d", tracker->id, status);
151     } else {
152         // Delete any SRP services that are not in the list provided by Thread.
153         while (*pservice != NULL) {
154             service = *pservice;
155             if (service->service_type == unicast_service) {
156                 struct thread_unicast_service *uservice = &service->u.unicast;
157                 for (i = 0; i < services->num; i++) {
158                     cti_service_t *cti_service = services->services[i];
159                     // Is this a valid SRP service?
160                     if (IS_SRP_SERVICE(cti_service)) {
161                         // Is this service still present?
162                         if (!memcmp(&uservice->address, cti_service->server, 16) &&
163                             !memcmp(&uservice->port, &cti_service->server[16], 2)) {
164                             break;
165                         }
166                     }
167                 }
168             } else if (service->service_type == anycast_service) {
169                 struct thread_anycast_service *aservice = &service->u.anycast;
170                 for (i = 0; i < services->num; i++) {
171                     cti_service_t *cti_service = services->services[i];
172                     // Is this a valid SRP anycast service?
173                     if (IS_SRP_ANYCAST_SERVICE(cti_service)) {
174                         // Is this service still present?
175                         if (service->rloc16 == cti_service->rloc16 &&
176                             aservice->sequence_number == cti_service->service[1]) {
177                             break;
178                         }
179                     }
180                 }
181             } else if (service->service_type == pref_id) {
182                 struct thread_pref_id *pref_id = &service->u.pref_id;
183                 for (i = 0; i < services->num; i++) {
184                     cti_service_t *cti_service = services->services[i];
185                     // Is this an SRP service?
186                     if (IS_PREF_ID_SERVICE(cti_service)) {
187                         // Is this service still present?
188                         if (!memcmp(&pref_id->partition_id, cti_service->server, 4) &&
189                             !memcmp(pref_id->prefix, &cti_service->server[4], 5))
190                         {
191                             break;
192                         }
193                     }
194                 }
195             } else {
196                 i = services->num;
197             }
198 
199             if (i == services->num) {
200                 service_tracker_thread_service_note(tracker, service, "went away");
201                 *pservice = service->next;
202                 thread_service_release(service);
203                 service = NULL;
204             } else {
205                 // We'll re-initialize these flags from the service list when we check for duplicates.
206                 service->previous_user = service->user;
207                 service->user = false;
208                 service->previous_stable = service->stable;
209                 service->stable = false;
210                 service->previous_ncp = service->ncp;
211                 service->ncp = false;
212                 pservice = &service->next;
213                 service->ignore = false;
214             }
215         }
216 
217         // Add any services that are not present.
218         for (i = 0; i < services->num; i++) {
219             cti_service_t *cti_service = services->services[i];
220             for (service = tracker->thread_services; service != NULL; service = service->next) {
221                 if (IS_SRP_SERVICE(cti_service) && service->service_type == unicast_service) {
222                     if (!memcmp(&service->u.unicast.address, cti_service->server, 16) &&
223                         !memcmp(&service->u.unicast.port, &cti_service->server[16], 2)) {
224                         break;
225                     }
226                 } else if (IS_SRP_ANYCAST_SERVICE(cti_service) && service->service_type == anycast_service) {
227                     uint8_t sequence_number = cti_service->service[1];
228                     if (service->rloc16 == cti_service->rloc16 &&
229                         service->u.anycast.sequence_number == sequence_number) {
230                         break;
231                     }
232                 } else if (IS_PREF_ID_SERVICE(cti_service) && service->service_type == pref_id) {
233 
234                     if (!memcmp(&service->u.pref_id.partition_id, cti_service->server, 4) &&
235                         !memcmp(service->u.pref_id.prefix, &cti_service->server[4], 5))
236                     {
237                         break;
238                     }
239                 }
240             }
241             if (service == NULL) {
242                 bool save = false;
243                 if (IS_SRP_SERVICE(cti_service)) {
244                     service = thread_service_unicast_create(cti_service->rloc16, cti_service->server,
245                                                             &cti_service->server[16], cti_service->service_id);
246                     save = true;
247                 } else if (IS_SRP_ANYCAST_SERVICE(cti_service)) {
248                     uint8_t sequence_number = cti_service->service[1];
249                     service = thread_service_anycast_create(cti_service->rloc16, sequence_number,
250                                                             cti_service->service_id);
251                     save = true;
252                 } else if (IS_PREF_ID_SERVICE(cti_service)) {
253                     save = true;
254                     service = thread_service_pref_id_create(cti_service->rloc16, cti_service->server,
255                                                             &cti_service->server[4], cti_service->service_id);
256                 }
257                 if (save) {
258                     if (service == NULL) {
259                         ERROR("[ST%lld] no memory for service.", tracker->id);
260                     } else {
261                         service_tracker_thread_service_note(tracker, service, "showed up");
262                         *pservice = service;
263                         pservice = &service->next;
264                     }
265                 }
266             }
267             // Also, since we're combing the list, update ncp, user and stable flags.   Note that a service can
268             // appear more than once in the thread service list.
269             if (service != NULL) {
270                 if (cti_service->flags & kCTIFlag_NCP) {
271                     service->ncp = true;
272                 } else {
273                     service->user = true;
274                     tracker->user_service_seen = true;
275                 }
276                 if (cti_service->flags & kCTIFlag_Stable) {
277                     service->stable = true;
278                 }
279             }
280         }
281         accumulator_t accumulator;
282         for (service = tracker->thread_services; service != NULL; service = service->next) {
283             // For unicast services, see if there's also an anycast service on the same RLOC16.
284             if (service->service_type == unicast_service) {
285                 service->u.unicast.anycast_also_present = false;
286                 for (thread_service_t *aservice = tracker->thread_services; aservice != NULL; aservice = aservice->next)
287                 {
288                     if (aservice->service_type == anycast_service && aservice->rloc16 == service->rloc16) {
289                         service->u.unicast.anycast_also_present = true;
290                     }
291                 }
292             }
293             service_tracker_flags_accumulator_init(&accumulator);
294             service_tracker_flags_accumulate(&accumulator, service->previous_ncp, service->ncp, "ncp");
295             service_tracker_flags_accumulate(&accumulator, service->previous_stable, service->ncp, "stable");
296             service_tracker_flags_accumulate(&accumulator, service->previous_user, service->user, "user");
297             if (accumulator.changed) {
298                 service_tracker_thread_service_note(tracker, service, accumulator.change);
299             }
300         }
301 
302         // At this point the thread prefix list contains the same information as what we just received.
303         // Call any callbacks to trigger updates based on new information.
304         for (service_tracker_callback_t *callback = tracker->callbacks; callback != NULL; callback = callback->next) {
305             callback->callback(callback->context);
306         }
307         if (!tracker->user_service_seen && tracker->server_state != NULL &&
308             tracker->server_state->awaiting_service_removal)
309         {
310             tracker->server_state->awaiting_service_removal = false;
311             adv_ctl_thread_shutdown_status_check(tracker->server_state);
312         }
313     }
314 }
315 
316 bool
317 service_tracker_local_service_seen(service_tracker_t *tracker)
318 {
319     return tracker->user_service_seen;
320 }
321 
322 service_tracker_t *
323 service_tracker_create(srp_server_t *server_state)
324 {
325 	service_tracker_t *ret = NULL;
326 	service_tracker_t *tracker = calloc(1, sizeof(*ret));
327 	if (tracker == NULL) {
328 		ERROR("[ST%lld] no memory", ++service_tracker_serial_number);
329 		goto exit;
330 	}
331 	RETAIN_HERE(tracker, service_tracker);
332     tracker->id = ++service_tracker_serial_number;
333     tracker->server_state = server_state;
334 
335 	ret = tracker;
336 	tracker = NULL;
337 exit:
338 	if (tracker != NULL) {
339 		RELEASE_HERE(tracker, service_tracker);
340 	}
341 	return ret;
342 }
343 
344 void
345 service_tracker_start(service_tracker_t *tracker)
346 {
347     if (tracker->thread_service_context != NULL) {
348         cti_events_discontinue(tracker->thread_service_context);
349         tracker->thread_service_context = NULL;
350         INFO("[ST%lld] restarting", tracker->id);
351         if (tracker->ref_count != 1) {
352             RELEASE_HERE(tracker, service_tracker); // Release the old retain for the callback.
353         } else {
354             FAULT("service tracker reference count should not be 1 here!");
355         }
356     }
357     int status = cti_get_service_list(tracker->server_state, &tracker->thread_service_context,
358                                       tracker, service_tracker_callback, NULL);
359     if (status != kCTIStatus_NoError) {
360         INFO("[ST%lld] service list get failed: %d", tracker->id, status);
361         return;
362     }
363     INFO("[ST%lld] service list get started", tracker->id);
364     RETAIN_HERE(tracker, service_tracker); // for the callback.
365 }
366 
367 bool
368 service_tracker_callback_add(service_tracker_t *tracker,
369 							 void (*callback)(void *context), void (*context_release)(void *context), void *context)
370 {
371 	bool ret = false;
372     service_tracker_callback_t **tpp;
373 
374 	// It's an error for two callbacks to have the same context
375 	for (tpp = &tracker->callbacks; *tpp != NULL; tpp = &(*tpp)->next) {
376 		if ((*tpp)->context == context) {
377 			FAULT("[ST%lld] duplicate context %p", tracker->id, context);
378 			goto exit;
379 		}
380 	}
381 
382 	service_tracker_callback_t *tracker_callback = calloc(1, sizeof(*tracker_callback));
383 	if (tracker_callback == NULL) {
384 		ERROR("[ST%lld] no memory", tracker->id);
385 		goto exit;
386 	}
387 	tracker_callback->callback = callback;
388 	tracker_callback->context_release = context_release;
389 	tracker_callback->context = context;
390 
391 	// The callback list holds a reference to the tracker
392 	if (tracker->callbacks == NULL) {
393 		RETAIN_HERE(tracker, service_tracker);
394 	}
395 
396 	// Keep the callback on the list.
397 	*tpp = tracker_callback;
398 
399 	ret = true;
400 exit:
401 	return ret;
402 
403 }
404 
405 static void
406 service_tracker_callback_free(service_tracker_callback_t *callback)
407 {
408     if (callback->context_release != NULL) {
409         callback->context_release(callback->context);
410     }
411     free(callback);
412 }
413 
414 void
415 service_tracker_stop(service_tracker_t *tracker)
416 {
417     if (tracker == NULL) {
418         return;
419     }
420 	if (tracker->thread_service_context != NULL) {
421 		cti_events_discontinue(tracker->thread_service_context);
422 		tracker->thread_service_context = NULL;
423 		RELEASE_HERE(tracker, service_tracker);
424 	}
425 }
426 
427 void
428 service_tracker_cancel(service_tracker_t *tracker)
429 {
430     if (tracker == NULL) {
431         return;
432     }
433     service_tracker_stop(tracker);
434 
435 	if (tracker->callbacks != NULL) {
436         service_tracker_callback_t *next;
437 		for (service_tracker_callback_t *callback = tracker->callbacks; callback != NULL; callback = next) {
438 			next = callback->next;
439             service_tracker_callback_free(callback);
440         }
441 		tracker->callbacks = NULL;
442 		// Release the reference held by the callback list.
443 		RELEASE_HERE(tracker, service_tracker);
444 	}
445 }
446 
447 void
448 service_tracker_callback_cancel(service_tracker_t *tracker, void *context)
449 {
450     if (tracker == NULL) {
451         return;
452     }
453 	for (service_tracker_callback_t **tpp = &tracker->callbacks; *tpp != NULL; tpp = &((*tpp)->next)) {
454 		service_tracker_callback_t *callback = *tpp;
455 		if (callback->context == context) {
456             *tpp = callback->next;
457             service_tracker_callback_free(callback);
458             return;
459 		}
460 	}
461 }
462 
463 static int
464 service_tracker_get_winning_anycast_sequence_number(service_tracker_t *NULLABLE tracker)
465 {
466     if (tracker == NULL) {
467         return -1;
468     }
469     int winning_sequence_number = -1;
470     // Find the sequence number that would win.
471     for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next)
472     {
473         if (service->ignore) {
474             continue;
475         }
476         if ((int)service->u.anycast.sequence_number > winning_sequence_number) {
477             winning_sequence_number = service->u.anycast.sequence_number;
478         }
479     }
480     return winning_sequence_number;
481 }
482 
483 thread_service_t *
484 service_tracker_services_get(service_tracker_t *NULLABLE tracker)
485 {
486 	if (tracker != NULL) {
487 		return tracker->thread_services;
488 	}
489     return NULL;
490 }
491 
492 // Check to see if a service exists that matches the service passed in as an argument, and if it is still validated.
493 // Service object might be a different object
494 bool
495 service_tracker_verified_service_still_exists(service_tracker_t *NULLABLE tracker, thread_service_t *old_service)
496 {
497     if (tracker == NULL || old_service == NULL) {
498         return false;
499     }
500     for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) {
501         if (service->ignore) {
502             continue;
503         }
504         if (service->responding && old_service->service_type == service->service_type) {
505             if (service->service_type == unicast_service) {
506                 if (service->u.unicast.port == old_service->u.unicast.port &&
507                     !memcmp(&service->u.unicast.address, &old_service->u.unicast.address,
508                             sizeof(service->u.unicast.address)))
509                 {
510                     return true;
511                 }
512             } else if (service->service_type == anycast_service) {
513                 if (service->u.anycast.sequence_number == old_service->u.anycast.sequence_number) {
514                     return true;
515                 }
516             }
517             FAULT("old_service type is bogus: %d", old_service->service_type);
518             return false;
519         }
520     }
521     return false;
522 }
523 
524 // If true, there is a service on the list that we've verified. Return it (caller must retain if saving pointer).
525 thread_service_t *
526 service_tracker_verified_service_get(service_tracker_t *NULLABLE tracker)
527 {
528     if (tracker == NULL) {
529         return false;
530     }
531     for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) {
532         if (service->ignore) {
533             continue;
534         }
535         if (service->checked && service->responding) {
536             return service;
537         }
538     }
539     return false;
540 }
541 
542 // Check for an unverified service on the list. If we are currently checking a service, return that service.
543 thread_service_t *
544 service_tracker_unverified_service_get(service_tracker_t *NULLABLE tracker, thread_service_type_t service_type)
545 {
546     thread_service_t *ret = NULL;
547     if (tracker != NULL) {
548         for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) {
549             if (service_type != any_service && service_type != service->service_type) {
550                 continue;
551             }
552             if (service->ignore) {
553                 continue;
554             }
555             if (service->checking) {
556                 return service;
557             }
558             if (ret == NULL && !service->checked && !service->probe_state && !service->user) {
559                 ret = service;
560             }
561         }
562     }
563     if (tracker != NULL && ret != NULL) {
564         char buf[128];
565         snprintf(buf, sizeof(buf), "service_tracker_unverified_service_get returning %p", ret);
566         service_tracker_thread_service_note(tracker, ret, buf);
567     }
568     return ret;
569 }
570 
571 static void
572 service_tracker_probe_callback(thread_service_t *UNUSED service, void *context, bool UNUSED succeeded)
573 {
574     service_tracker_t *tracker = context;
575     // Notify consumers of service tracker callbacks that something has changed.
576     for (service_tracker_callback_t *callback = tracker->callbacks; callback != NULL; callback = callback->next) {
577         callback->callback(callback->context);
578     }
579 }
580 
581 // Find a service that is not currently being probed and has not been probed, and start probing it. If we are already probing a service,
582 // or if there are no services remaining to probe, do nothing.
583 void
584 service_tracker_verify_next_service(service_tracker_t *NULLABLE tracker)
585 {
586     thread_service_t *service;
587     if (tracker == NULL) {
588         return;
589     }
590     int winning_sequence_number = service_tracker_get_winning_anycast_sequence_number(tracker);
591 
592     for (service = tracker->thread_services; service != NULL; service = service->next) {
593         if (service->probe_state) {
594             return;
595         }
596         // For anycast services, if not on the winning sequence number, don't check
597         if (service->service_type == anycast_service && service->u.anycast.sequence_number != winning_sequence_number) {
598             continue;
599         }
600         // If this is our service, we don't need to check it.
601         if (service->user) {
602             continue;
603         }
604         // If we've probed it recently, don't probe it again yet.
605         if (srp_time() - service->last_probe_time < 300)
606         {
607             continue;
608         }
609         if (service->checked) {
610             continue;
611         }
612         // If we didn't continue, yet, it's because we found a service we can probe, so probe it.
613         RETAIN_HERE(tracker, service_tracker); // For the srp probe
614         probe_srp_service(service, tracker, service_tracker_probe_callback, service_tracker_context_release);
615         return;
616     }
617 }
618 
619 void
620 service_tracker_cancel_probes(service_tracker_t *NULLABLE tracker)
621 {
622     if (tracker == NULL) {
623         return;
624     }
625     for (thread_service_t *service = tracker->thread_services; service != NULL; service = service->next) {
626         if (service->probe_state) {
627             probe_srp_service_probe_cancel(service);
628         }
629     }
630 }
631 
632 // Local Variables:
633 // mode: C
634 // tab-width: 4
635 // c-file-style: "bsd"
636 // c-basic-offset: 4
637 // fill-column: 120
638 // indent-tabs-mode: nil
639 // End:
640