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