Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* dnssd-client.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  * This file contains code to queue and send updates for Thread services.
     18  */
     19 
     20 #ifndef LINUX
     21 #include <netinet/in.h>
     22 #include <net/if.h>
     23 #include <netinet6/in6_var.h>
     24 #include <netinet6/nd6.h>
     25 #include <net/if_media.h>
     26 #include <sys/stat.h>
     27 #else
     28 #define _GNU_SOURCE
     29 #include <netinet/in.h>
     30 #include <fcntl.h>
     31 #include <bsd/stdlib.h>
     32 #include <net/if.h>
     33 #endif
     34 #include <sys/socket.h>
     35 #include <sys/ioctl.h>
     36 #include <net/route.h>
     37 #include <netinet/icmp6.h>
     38 #include <stdio.h>
     39 #include <unistd.h>
     40 #include <errno.h>
     41 #include <stdlib.h>
     42 #include <string.h>
     43 #include <ctype.h>
     44 #include <arpa/inet.h>
     45 #include <stdlib.h>
     46 #include <stddef.h>
     47 #include <dns_sd.h>
     48 #include <inttypes.h>
     49 #include <signal.h>
     50 
     51 #ifdef IOLOOP_MACOS
     52 #include <xpc/xpc.h>
     53 
     54 #include <TargetConditionals.h>
     55 #include <SystemConfiguration/SystemConfiguration.h>
     56 #include <SystemConfiguration/SCPrivate.h>
     57 #include <SystemConfiguration/SCNetworkConfigurationPrivate.h>
     58 #include <SystemConfiguration/SCNetworkSignature.h>
     59 #include <network_information.h>
     60 
     61 #include <CoreUtils/CoreUtils.h>
     62 #include <mrc/private.h>
     63 #endif // IOLOOP_MACOS
     64 
     65 #include "srp.h"
     66 #include "dns-msg.h"
     67 #include "ioloop.h"
     68 #include "srp-crypto.h"
     69 
     70 #include "cti-services.h"
     71 #include "srp-gw.h"
     72 #include "srp-proxy.h"
     73 #include "srp-mdns-proxy.h"
     74 #include "adv-ctl-server.h"
     75 #include "dnssd-proxy.h"
     76 #include "srp-proxy.h"
     77 #include "route.h"
     78 
     79 #define STATE_MACHINE_IMPLEMENTATION 1
     80 typedef enum {
     81     dnssd_client_state_invalid,
     82     dnssd_client_state_startup,
     83     dnssd_client_state_not_client,
     84     dnssd_client_state_probing,
     85     dnssd_client_state_client,
     86 } state_machine_state_t;
     87 #define state_machine_state_invalid dnssd_client_state_invalid
     88 
     89 #include "state-machine.h"
     90 #include "thread-service.h"
     91 #include "service-tracker.h"
     92 #include "service-publisher.h"
     93 
     94 #include "dnssd-client.h"
     95 #include "thread-tracker.h"
     96 #include "probe-srp.h"
     97 
     98 typedef enum {
     99     dnssd_client_dns_service_type_invalid   = 0,
    100     dnssd_client_dns_service_type_do53      = 1,
    101     dnssd_client_dns_service_type_push      = 2,
    102 } dnssd_client_dns_service_type_t;
    103 
    104 struct dnssd_client {
    105     int ref_count;
    106     state_machine_header_t state_header;
    107     char *id;
    108     srp_server_t *server_state;
    109     wakeup_t *wakeup_timer;
    110     cti_connection_t active_data_set_connection;
    111     struct in6_addr mesh_local_prefix;
    112     bool have_mesh_local_prefix;
    113     bool first_time;
    114     bool canceled;
    115     dnssd_txn_t *shared_txn;
    116     thread_service_t *published_service;
    117     DNSServiceRef shared_connection;
    118     mrc_dns_service_registration_t dns_service_registration;        // DNS service registration handler.
    119     dnssd_client_dns_service_type_t dns_service_registration_type;  // The type of the registered DNS service.
    120     int interface_index;
    121     uint16_t dns_service_registration_port;                         // The port of the registered DNS service used.
    122 };
    123 
    124 static uint64_t dnssd_client_serial_number;
    125 
    126 static void
    127 dnssd_client_finalize(dnssd_client_t *client)
    128 {
    129     thread_service_release(client->published_service);
    130     ioloop_wakeup_release(client->wakeup_timer);
    131     free(client->state_header.name);
    132     free(client->id);
    133     free(client);
    134 }
    135 
    136 RELEASE_RETAIN_FUNCS(dnssd_client);
    137 
    138 static void
    139 dnssd_client_context_release(void *context)
    140 {
    141     dnssd_client_t *client = context;
    142     RELEASE_HERE(client, dnssd_client);
    143 }
    144 
    145 static void
    146 dnssd_client_wait_expired(void *context)
    147 {
    148     dnssd_client_t *client = context;
    149     state_machine_event_t *event = state_machine_event_create(state_machine_event_type_timeout, NULL);
    150     if (event == NULL) {
    151         ERROR("unable to allocate event to deliver");
    152         return;
    153     }
    154     state_machine_event_deliver(&client->state_header, event);
    155     RELEASE_HERE(event, state_machine_event);
    156 }
    157 
    158 static void
    159 dnssd_client_service_tracker_callback(void *context)
    160 {
    161     dnssd_client_t *client = context;
    162 
    163     state_machine_event_t *event = state_machine_event_create(state_machine_event_type_service_list_changed, NULL);
    164     if (event == NULL) {
    165         ERROR("unable to allocate event to deliver");
    166         return;
    167     }
    168     state_machine_event_deliver(&client->state_header, event);
    169     RELEASE_HERE(event, state_machine_event);
    170 }
    171 
    172 static void
    173 dnssd_client_probe_callback(thread_service_t *UNUSED service, void *context, bool UNUSED succeeded)
    174 {
    175     dnssd_client_t *client = context;
    176     state_machine_event_t *event = state_machine_event_create(state_machine_event_type_probe_completed, NULL);
    177     if (event == NULL) {
    178         ERROR("unable to allocate event to deliver");
    179         return;
    180     }
    181     state_machine_event_deliver(&client->state_header, event);
    182     RELEASE_HERE(event, state_machine_event);
    183 }
    184 
    185 static void
    186 dnssd_client_get_mesh_local_prefix_callback(void *context, const char *prefix_string, int status)
    187 {
    188     dnssd_client_t *client = context;
    189     INFO(PUB_S_SRP " %d", prefix_string != NULL ? prefix_string : "<null>", status);
    190     if (status != kCTIStatus_NoError || prefix_string == NULL) {
    191         goto fail;
    192     }
    193 
    194     char prefix_buf[INET6_ADDRSTRLEN];
    195 
    196     const char *prefix_addr_string;
    197     char *slash = strchr(prefix_string, '/');
    198     if (slash != NULL) {
    199         size_t len = slash - prefix_string;
    200         if (len == 0) {
    201             ERROR("bogus prefix: " PRI_S_SRP, prefix_string);
    202             goto fail;
    203         }
    204         if (len - 1 > sizeof(prefix_buf)) {
    205             ERROR("prefix too long: " PRI_S_SRP, prefix_string);
    206             goto fail;
    207         }
    208         memcpy(prefix_buf, prefix_string, len);
    209         prefix_buf[len] = 0;
    210         prefix_addr_string = prefix_buf;
    211     } else {
    212         prefix_addr_string = prefix_string;
    213     }
    214     if (!inet_pton(AF_INET6, prefix_addr_string, &client->mesh_local_prefix)) {
    215         ERROR("prefix syntax incorrect: " PRI_S_SRP, prefix_addr_string);
    216         goto fail;
    217     }
    218     client->have_mesh_local_prefix = true;
    219     state_machine_event_t *event = state_machine_event_create(state_machine_event_type_got_mesh_local_prefix, NULL);
    220     if (event == NULL) {
    221         ERROR("unable to allocate event to deliver");
    222         return;
    223     }
    224     state_machine_event_deliver(&client->state_header, event);
    225     RELEASE_HERE(event, state_machine_event);
    226 fail:
    227     RELEASE_HERE(client, dnssd_client); // was retained for callback, can only get one callback
    228     return;
    229 }
    230 
    231 static state_machine_state_t
    232 dnssd_client_remove_published_service(dnssd_client_t *client)
    233 {
    234     if (client->dns_service_registration != NULL) {
    235         mrc_dns_service_registration_forget(&client->dns_service_registration);
    236     }
    237     return dnssd_client_state_not_client;
    238 }
    239 
    240 static state_machine_state_t
    241 dnssd_client_service_unpublish(dnssd_client_t *client)
    242 {
    243     if (client->dns_service_registration != NULL) {
    244         thread_service_note(client->id, client->published_service, "unpublishing service");
    245     }
    246     dnssd_client_remove_published_service(client);
    247 
    248     return dnssd_client_state_not_client;
    249 }
    250 
    251 static void
    252 dnssd_client_dns_service_event_handler(const mrc_dns_service_registration_event_t mrc_event, const OSStatus event_err,
    253                                        dnssd_client_t *client)
    254 {
    255     state_machine_event_t *event = NULL;
    256     bool want_event = false;
    257     switch(mrc_event)
    258     {
    259     case mrc_dns_service_registration_event_started:
    260         INFO("DNS service registration started");
    261         break;
    262     case mrc_dns_service_registration_event_interruption:
    263         INFO("DNS service registration interrupted" );
    264         break;
    265 
    266     case mrc_dns_service_registration_event_invalidation:
    267         want_event = true;
    268         if (event_err) {
    269             ERROR("DNS service registration invalidated with error: %d", (int)event_err);
    270         } else {
    271             INFO("DNS service registration gracefully invalidated");
    272         }
    273         event = state_machine_event_create(state_machine_event_type_dns_registration_invalidated, NULL);
    274         break;
    275     case mrc_dns_service_registration_event_connection_error:
    276         want_event = true;
    277         ERROR("Registered DNS Push connection failed on server with error: %d", (int)event_err);
    278         event = state_machine_event_create(state_machine_event_type_dns_registration_bad_service, NULL);
    279         break;
    280     }
    281 
    282     if (want_event) {
    283         if (event == NULL) {
    284             ERROR("unable to allocate event to deliver");
    285         } else {
    286             state_machine_event_deliver(&client->state_header, event);
    287             RELEASE_HERE(event, state_machine_event);
    288         }
    289         // Either event should be the last event we get.
    290         RELEASE_HERE(client, dnssd_client);
    291     }
    292 }
    293 
    294 
    295 static state_machine_state_t
    296 dnssd_client_service_publish(dnssd_client_t *client)
    297 {
    298     dnssd_client_service_unpublish(client);
    299     state_machine_state_t ret = dnssd_client_state_not_client;
    300 
    301     OSStatus err;
    302     mdns_dns_service_definition_t do53_definition = NULL;
    303     mdns_dns_push_service_definition_t push_definition = NULL;
    304     mdns_domain_name_t domain_name = NULL;
    305     mdns_address_t server_address = NULL;
    306 
    307     domain_name = mdns_domain_name_create("default.service.arpa.", mdns_domain_name_create_opts_none, &err);
    308     require_noerr_action(err, out, ERROR("failed to create default.service.arpa.: %{darwin.errno}d", (int)err));
    309 
    310     const uint8_t *aaaa_data = NULL;
    311     uint16_t port = 0;
    312     // @TODO: Adds SRV service probing to determine the DoT port instead the default dns_service_registration_port.
    313     if (client->published_service->service_type == unicast_service) {
    314         aaaa_data = &client->published_service->u.unicast.address.s6_addr[0];
    315     } else if (client->published_service->service_type == anycast_service) {
    316         aaaa_data = &client->published_service->u.anycast.address.s6_addr[0];
    317     }
    318     port = client->dns_service_registration_port;
    319     require_action(aaaa_data != NULL, out, ERROR("failed to get service address"));
    320 
    321     SEGMENTED_IPv6_ADDR_GEN_SRP(aaaa_data, ipv6_addr_buf);
    322     server_address = mdns_address_create_ipv6(aaaa_data, port, 0);
    323     if (server_address == NULL) {
    324         ERROR("failed to create address object -- address: " PRI_SEGMENTED_IPv6_ADDR_SRP,
    325               SEGMENTED_IPv6_ADDR_PARAM_SRP(aaaa_data, ipv6_addr_buf));
    326         goto out;
    327     }
    328     if (client->dns_service_registration_type == dnssd_client_dns_service_type_push) {
    329         push_definition = mdns_dns_push_service_definition_create();
    330         require_action(push_definition, out, ERROR("unable to allocate mdns_dns_push_service_definition object"));
    331 
    332         err = mdns_dns_push_service_definition_add_domain(push_definition, domain_name);
    333         require_noerr(err, out);
    334 
    335         mdns_dns_push_service_definition_append_server_address(push_definition, server_address);
    336 
    337         client->dns_service_registration = mrc_dns_service_registration_create_push(push_definition);
    338         if (client->dns_service_registration) {
    339             mrc_dns_service_registration_set_reports_connection_errors(client->dns_service_registration,
    340                 true);
    341         }
    342     } else if (client->dns_service_registration_type == dnssd_client_dns_service_type_do53) {
    343         do53_definition = mdns_dns_service_definition_create();
    344         require_action(do53_definition, out, ERROR("unable to allocate mdns_dns_service_definition object"));
    345 
    346         err = mdns_dns_service_definition_add_domain(do53_definition, domain_name);
    347         require_noerr(err, out);
    348 
    349         err = mdns_dns_service_definition_append_server_address(do53_definition, server_address);
    350         require_noerr(err, out);
    351 
    352         client->dns_service_registration = mrc_dns_service_registration_create(do53_definition);
    353     } else {
    354         ERROR("Unknown DNS service registration type: %d", client->dns_service_registration_type);
    355         goto out;
    356     }
    357     require_action(client->dns_service_registration != NULL, out, ERROR("failed to create DNS service registration"));
    358     mrc_dns_service_registration_set_queue(client->dns_service_registration, dispatch_get_main_queue());
    359 
    360     RETAIN_HERE(client, dnssd_client); // For the handler
    361     mrc_dns_service_registration_set_event_handler(client->dns_service_registration,
    362     ^(const mrc_dns_service_registration_event_t event, const OSStatus event_err)
    363     {
    364         dnssd_client_dns_service_event_handler(event, event_err, client);
    365     });
    366     INFO("Publishing dnssd client service -- domain: " PRI_DNS_NAME_SRP ", address: " PRI_SEGMENTED_IPv6_ADDR_SRP,
    367         mdns_domain_name_get_presentation(domain_name), SEGMENTED_IPv6_ADDR_PARAM_SRP(aaaa_data, ipv6_addr_buf));
    368     mrc_dns_service_registration_activate(client->dns_service_registration);
    369 
    370     ret = dnssd_client_state_invalid;
    371 out:
    372     mdns_forget(&do53_definition);
    373     mdns_forget(&push_definition);
    374     mdns_forget(&domain_name);
    375     mdns_forget(&server_address);
    376     return ret;
    377 }
    378 
    379 static state_machine_state_t dnssd_client_action_startup(state_machine_header_t *state_header,
    380                                                          state_machine_event_t *event);
    381 static state_machine_state_t dnssd_client_action_not_client(state_machine_header_t *state_header,
    382                                                             state_machine_event_t *event);
    383 static state_machine_state_t dnssd_client_action_probing(state_machine_header_t *state_header,
    384                                                          state_machine_event_t *event);
    385 static state_machine_state_t dnssd_client_action_client(state_machine_header_t *state_header,
    386                                                         state_machine_event_t *event);
    387 
    388 // States:
    389 // -- not_client: Not a client because server or because no service (in which case hopefully we are becoming server)
    390 // --     client: Found a working service, configured as client
    391 
    392 #define SERVICE_PUB_NAME_DECL(name) dnssd_client_state_##name, #name
    393 static state_machine_decl_t dnssd_client_states[] = {
    394     { SERVICE_PUB_NAME_DECL(invalid),                            NULL },
    395     { SERVICE_PUB_NAME_DECL(startup),                            dnssd_client_action_startup },
    396     { SERVICE_PUB_NAME_DECL(not_client),                         dnssd_client_action_not_client },
    397     { SERVICE_PUB_NAME_DECL(probing),                            dnssd_client_action_probing },
    398     { SERVICE_PUB_NAME_DECL(client),                             dnssd_client_action_client },
    399 };
    400 #define DNSSD_CLIENT_NUM_STATES ((sizeof(dnssd_client_states)) / (sizeof(state_machine_decl_t)))
    401 
    402 #define STATE_MACHINE_HEADER_TO_CLIENT(state_header)                                       \
    403     if (state_header->state_machine_type != state_machine_type_dnssd_client) {             \
    404         ERROR("state header type isn't omr_client: %d", state_header->state_machine_type); \
    405         return dnssd_client_state_invalid;                                                 \
    406     }                                                                                      \
    407     dnssd_client_t *client = state_header->state_object
    408 
    409 static bool
    410 dnssd_client_should_be_client(dnssd_client_t *client)
    411 {
    412     bool might_publish = false;
    413     bool associated = true;
    414     bool should_be_client = false;
    415     srp_server_t *server_state = client->server_state;
    416 
    417     if (!service_publisher_could_publish(server_state->service_publisher)) {
    418         should_be_client = true;
    419         might_publish = true;
    420     }
    421 
    422     if (!thread_tracker_associated_get(server_state->thread_tracker, false)) {
    423         associated = false;
    424         should_be_client = false;
    425     }
    426 
    427     INFO(PUB_S_SRP PUB_S_SRP PUB_S_SRP,
    428          should_be_client ?                "could be client" : "can't be client",
    429          might_publish ?                    " might publish" : " won't publish",
    430          associated ?                                     "" : " not associated ");
    431     return should_be_client;
    432 }
    433 
    434 // We start in this state and remain here until we get a mesh-local prefix.
    435 static state_machine_state_t
    436 dnssd_client_action_startup(state_machine_header_t *state_header, state_machine_event_t *event)
    437 {
    438     STATE_MACHINE_HEADER_TO_CLIENT(state_header);
    439     BR_STATE_ANNOUNCE(client, event);
    440 
    441     if (client->have_mesh_local_prefix) {
    442         return dnssd_client_state_not_client;
    443     }
    444     return dnssd_client_state_invalid;
    445 }
    446 
    447 // We get into this state when there is no SRP service published on the Thread network other than our own.
    448 // If we stop publishing (because a BR-based service showed up), then we go to the probe state.
    449 static state_machine_state_t
    450 dnssd_client_action_not_client(state_machine_header_t *state_header, state_machine_event_t *event)
    451 {
    452     STATE_MACHINE_HEADER_TO_CLIENT(state_header);
    453     BR_STATE_ANNOUNCE(client, event);
    454 
    455     if (event == NULL) {
    456         if (client->published_service != NULL) {
    457             dnssd_client_service_unpublish(client);
    458         }
    459         return dnssd_client_state_invalid;
    460     }
    461 
    462     // We do the same thing here for any event we get, because we're just waiting for conditions to be right.
    463     if (dnssd_client_should_be_client(client)) {
    464         return dnssd_client_state_probing;
    465     }
    466     return dnssd_client_state_invalid;
    467 }
    468 
    469 // We get into this state when we've determined we should be a client
    470 static state_machine_state_t
    471 dnssd_client_action_probing(state_machine_header_t *state_header, state_machine_event_t *event)
    472 {
    473     STATE_MACHINE_HEADER_TO_CLIENT(state_header);
    474     BR_STATE_ANNOUNCE(client, event);
    475 
    476     thread_service_t *service;
    477     srp_server_t *server_state = client->server_state;
    478 
    479     // On arrival in this state, we start a timer. If we get to the publishing state, we cancel the timer. If the
    480     // timer goes off when we're still probing, we tell the service publisher to publish any relevant cached
    481     // services.  The point of this is that it's fairly common at least in testing that we want to be able to
    482     // control an accessory after joining the Thread network /because/ the BR went down, and in this case the BR's
    483     // published service may take 120 seconds to time out from the thread network data. Publishing the cached
    484     // services can potentially help with this.
    485     if (event == NULL) {
    486         ioloop_add_wake_event(client->wakeup_timer, client, dnssd_client_wait_expired,
    487                               dnssd_client_context_release, 5 * MSEC_PER_SEC); // Wait five seconds for probe to succeed.
    488         RETAIN_HERE(client, dnssd_client); // for the wake event
    489     }
    490 
    491     // If we have been waiting for a second since we started probing and haven't succeeded, tell the service publisher
    492     // to publish services.
    493     else if (event->type == state_machine_event_type_timeout) {
    494         if (server_state->service_publisher != NULL) {
    495             INFO("server probe startup timeout expired--publishing cached data.");
    496             service_publisher_re_advertise_matching(server_state->service_publisher);
    497         }
    498         return dnssd_client_state_invalid;
    499     }
    500 
    501     // If we no longer need to be a client, stop trying.
    502     else {
    503         if (!dnssd_client_should_be_client(client)) {
    504             ioloop_cancel_wake_event(client->wakeup_timer);
    505             return dnssd_client_state_not_client;
    506         }
    507     }
    508 
    509     // See if we now have a service that's been successfully probed; if so, publish it.
    510     service = service_tracker_verified_service_get(client->server_state->service_tracker);
    511     if (service != NULL) {
    512         if (client->published_service != NULL) {
    513             thread_service_release(client->published_service);
    514         }
    515         client->published_service = service;
    516         thread_service_retain(client->published_service);
    517 
    518         // Tell the service publisher that we successfully probed a service.
    519         INFO("server probe succeeded--unpublishing cached data.");
    520         service_publisher_unadvertise_all(server_state->service_publisher);
    521 
    522         // We no longer want a wakeup.  Note that this is the only way out of the probing state, so it's not
    523         // possible to exit the state without canceling the timer here.
    524         ioloop_cancel_wake_event(client->wakeup_timer);
    525 
    526         // Publish DNS Push service pointing to probed service.
    527         return dnssd_client_state_client;
    528     }
    529 
    530     // Check to see if we can start a new probe
    531     service = service_tracker_unverified_service_get(client->server_state->service_tracker, unicast_service);
    532     if (service != NULL) {
    533         if (service->checking) {
    534             service_tracker_thread_service_note(client->server_state->service_tracker, service,
    535                                                 " is still being probed");
    536             return dnssd_client_state_invalid;
    537         }
    538         if (service->service_type == anycast_service) {
    539             memcpy(&service->u.anycast.address, &client->mesh_local_prefix, 8);
    540             memcpy(&service->u.anycast.address.s6_addr[8], thread_rloc_preamble, 6);
    541             service->u.anycast.address.s6_addr[14] = service->rloc16 >> 8;
    542             service->u.anycast.address.s6_addr[15] = service->rloc16 & 255;
    543         }
    544         RETAIN_HERE(client, dnssd_client); // For the probe
    545         probe_srp_service(service, client, dnssd_client_probe_callback, dnssd_client_context_release);
    546         return dnssd_client_state_invalid;
    547     }
    548 
    549     // This should only ever happen if we get the service list update before the service publisher gets it.
    550     INFO("no service to publish");
    551     return dnssd_client_state_invalid;
    552 }
    553 
    554 
    555 // We get into this state when we've published a service.
    556 static state_machine_state_t
    557 dnssd_client_action_client(state_machine_header_t *state_header, state_machine_event_t *event)
    558 {
    559     STATE_MACHINE_HEADER_TO_CLIENT(state_header);
    560     BR_STATE_ANNOUNCE(client, event);
    561 
    562     if (event == NULL) {
    563         // Publish the service.
    564         dnssd_client_service_publish(client);
    565         return dnssd_client_state_invalid;
    566     }
    567 
    568     if (event->type == state_machine_event_type_dns_registration_bad_service) {
    569         if (client->published_service == NULL) {
    570             INFO("bad service event received with no published service.");
    571         } else {
    572             client->published_service->ignore = true;  // Don't consider this service when deciding what to advertise
    573             client->published_service->responding = false;
    574             thread_service_release(client->published_service);
    575             client->published_service = NULL;
    576             return dnssd_client_state_probing; // Go back to probing.
    577         }
    578     }
    579 
    580     // This can happen if the daemon disconnects.
    581     if (client->published_service == NULL) {
    582         return dnssd_client_state_not_client;
    583     }
    584 
    585     // If we should no longer be client, unpublish the service and wait
    586     if (!dnssd_client_should_be_client(client)) {
    587         return dnssd_client_state_not_client;
    588     }
    589 
    590     if (service_tracker_verified_service_still_exists(client->server_state->service_tracker,
    591                                                       client->published_service)) {
    592         return dnssd_client_state_invalid;
    593     }
    594 
    595     // If we get here, the service we have been publishing is no longer present.
    596     return dnssd_client_state_not_client;
    597 }
    598 
    599 void
    600 dnssd_client_cancel(dnssd_client_t *client)
    601 {
    602     if (client->server_state->service_tracker != NULL) {
    603         service_tracker_cancel_probes(client->server_state->service_tracker);
    604         service_tracker_callback_cancel(client->server_state->service_tracker, client);
    605     }
    606     if (client->server_state->thread_tracker != NULL) {
    607         thread_tracker_callback_cancel(client->server_state->thread_tracker, client);
    608     }
    609     ioloop_cancel_wake_event(client->wakeup_timer);
    610     if (client->active_data_set_connection != NULL) {
    611         cti_events_discontinue(client->active_data_set_connection);
    612         RELEASE_HERE(client, dnssd_client); // callback held reference
    613         client->active_data_set_connection = NULL;
    614     }
    615     dnssd_client_remove_published_service(client);
    616     state_machine_cancel(&client->state_header);
    617 }
    618 
    619 dnssd_client_t *
    620 dnssd_client_create(srp_server_t *server_state)
    621 {
    622     dnssd_client_t *ret = NULL, *client = calloc(1, sizeof(*client));
    623     if (client == NULL) {
    624         return client;
    625     }
    626     RETAIN_HERE(client, dnssd_client);
    627     client->wakeup_timer = ioloop_wakeup_create();
    628     if (client->wakeup_timer == NULL) {
    629         ERROR("wakeup timer alloc failed");
    630         goto out;
    631     }
    632 
    633     char client_id_buf[100];
    634     snprintf(client_id_buf, sizeof(client_id_buf), "[DC%lld]", ++dnssd_client_serial_number);
    635     client->id = strdup(client_id_buf);
    636     if (client->id == NULL) {
    637         ERROR("no memory for client ID");
    638         goto out;
    639     }
    640     client->interface_index = -1;
    641     client->dns_service_registration_type = dnssd_client_dns_service_type_push;
    642     client->dns_service_registration_port = DNS_OVER_TLS_DEFAULT_PORT;
    643 
    644     if (!state_machine_header_setup(&client->state_header,
    645                                     client, client->id,
    646                                     state_machine_type_dnssd_client,
    647                                     dnssd_client_states,
    648                                     DNSSD_CLIENT_NUM_STATES)) {
    649         ERROR("header setup failed");
    650         goto out;
    651     }
    652 
    653     client->server_state = server_state;
    654     if (!service_tracker_callback_add(server_state->service_tracker, dnssd_client_service_tracker_callback,
    655                                       dnssd_client_context_release, client))
    656     {
    657         goto out;
    658     }
    659     RETAIN_HERE(client, dnssd_client); // for service tracker
    660 
    661     ret = client;
    662     client = NULL;
    663 out:
    664     if (client != NULL) {
    665         RELEASE_HERE(client, dnssd_client);
    666     }
    667     return ret;
    668 }
    669 
    670 static void
    671 dnssd_client_get_tunnel_name_callback(void *context, const char *name, cti_status_t status)
    672 {
    673     dnssd_client_t *client = context;
    674     if (status != kCTIStatus_NoError) {
    675         INFO("didn't get tunnel name, error code %d", status);
    676         goto fail;
    677     }
    678     client->interface_index = if_nametoindex(name);
    679 fail:
    680     RELEASE_HERE(client, dnssd_client); // was retained for callback, can only get one callback
    681 }
    682 
    683 static void
    684 dnssd_client_active_data_set_changed_callback(void *context, cti_status_t status)
    685 {
    686     dnssd_client_t *client = context;
    687 
    688     if (status != kCTIStatus_NoError) {
    689         ERROR("error %d", status);
    690         RELEASE_HERE(client, dnssd_client); // no more callbacks
    691         cti_events_discontinue(client->active_data_set_connection);
    692         client->active_data_set_connection = NULL;
    693         return;
    694     }
    695 
    696     status = cti_get_mesh_local_prefix(client->server_state, client, dnssd_client_get_mesh_local_prefix_callback, NULL);
    697     if (status != kCTIStatus_NoError) {
    698         ERROR("cti_get_mesh_local_prefix failed with status %d", status);
    699     } else {
    700         RETAIN_HERE(client, dnssd_client); // for mesh-local callback
    701     }
    702     status = cti_get_tunnel_name(client->server_state, client, dnssd_client_get_tunnel_name_callback, NULL);
    703     if (status != kCTIStatus_NoError) {
    704         ERROR("cti_get_tunnel_name failed with status %d", status);
    705     } else {
    706         RETAIN_HERE(client, dnssd_client); // for tunnel name callback
    707     }
    708 }
    709 
    710 void
    711 dnssd_client_start(dnssd_client_t *client)
    712 {
    713     cti_track_active_data_set(client->server_state, &client->active_data_set_connection,
    714                               client, dnssd_client_active_data_set_changed_callback, NULL);
    715     RETAIN_HERE(client, dnssd_client); // for callback
    716     dnssd_client_active_data_set_changed_callback(client, kCTIStatus_NoError); // Get the initial state.
    717     state_machine_next_state(&client->state_header, dnssd_client_state_startup);
    718 }
    719 
    720 // Local Variables:
    721 // mode: C
    722 // tab-width: 4
    723 // c-file-style: "bsd"
    724 // c-basic-offset: 4
    725 // fill-column: 120
    726 // indent-tabs-mode: nil
    727 // End:
    728