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