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