1 /* probe-srp.c 2 * 3 * Copyright (c) 2023 Apple Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * This file contains code to queue and send updates for Thread services. 18 */ 19 20 #ifndef LINUX 21 #include <netinet/in.h> 22 #include <net/if.h> 23 #include <netinet6/in6_var.h> 24 #include <netinet6/nd6.h> 25 #include <net/if_media.h> 26 #include <sys/stat.h> 27 #else 28 #define _GNU_SOURCE 29 #include <netinet/in.h> 30 #include <fcntl.h> 31 #include <bsd/stdlib.h> 32 #include <net/if.h> 33 #endif 34 #include <sys/socket.h> 35 #include <sys/ioctl.h> 36 #include <net/route.h> 37 #include <netinet/icmp6.h> 38 #include <stdio.h> 39 #include <unistd.h> 40 #include <errno.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <ctype.h> 44 #include <arpa/inet.h> 45 #include <stdlib.h> 46 #include <stddef.h> 47 #include <dns_sd.h> 48 #include <inttypes.h> 49 #include <signal.h> 50 51 #include "srp.h" 52 #include "dns-msg.h" 53 #include "ioloop.h" 54 55 #include "srp-mdns-proxy.h" 56 #include "state-machine.h" 57 #include "thread-service.h" 58 #include "service-tracker.h" 59 #include "probe-srp.h" 60 61 struct probe_state { 62 int ref_count; 63 comm_t *connection; 64 wakeup_t *wakeup; 65 thread_service_t *service; 66 void *context; 67 void (*callback)(thread_service_t *service, void *context, bool succeeded); 68 void (*context_release)(void *context); 69 route_state_t *route_state; 70 dns_wire_t question; 71 int num_retransmissions, retransmission_delay; 72 uint16_t question_length; 73 }; 74 75 static void 76 probe_state_finalize(probe_state_t *probe_state) 77 { 78 if (probe_state->wakeup != NULL) { 79 ioloop_wakeup_release(probe_state->wakeup); 80 } 81 if (probe_state->service != NULL) { 82 thread_service_release(probe_state->service); 83 probe_state->service = NULL; 84 } 85 if (probe_state->connection != NULL) { 86 ioloop_comm_release(probe_state->connection); 87 probe_state->connection = NULL; 88 } 89 if (probe_state->context_release) { 90 probe_state->context_release(probe_state->context); 91 } 92 free(probe_state); 93 } 94 95 void 96 probe_srp_service_probe_cancel(thread_service_t *service) 97 { 98 probe_state_t *probe_state = service->probe_state; 99 probe_state->service = NULL; 100 service->probe_state = NULL; 101 102 if (probe_state->context_release != NULL) { 103 probe_state->context_release(probe_state->context); 104 } 105 probe_state->context = NULL; 106 107 thread_service_release(service); // The probe state's reference to the service 108 if (probe_state->wakeup != NULL) { 109 ioloop_cancel_wake_event(probe_state->wakeup); 110 } 111 112 if (probe_state->connection != NULL) { 113 ioloop_comm_cancel(probe_state->connection); // Cancel the connection (should result in the state being released) 114 ioloop_comm_release(probe_state->connection); 115 probe_state->connection = NULL; 116 } 117 RELEASE_HERE(probe_state, probe_state); // The thread_service_t's reference to the probe state 118 } 119 120 static void 121 probe_srp_done(void *context, bool succeeded) 122 { 123 probe_state_t *probe_state = context; 124 struct in6_addr *address; 125 int port; 126 thread_service_t *service = probe_state->service; 127 // Note: we should still have references both to probe_state and to service here because they each held references 128 // to each other. 129 probe_state->service = NULL; 130 service->probe_state = NULL; 131 if (service->service_type == anycast_service) { 132 address = &service->u.anycast.address; 133 port = 53; 134 } else { 135 address = &service->u.unicast.address; 136 // If the anycast service is present, we can use port 53, which we need to prefer because pre-2024 Apple BRs 137 // will not answer DNS queries on the SRP service port. 138 if (service->u.unicast.anycast_also_present) { 139 port = 53; 140 } else { 141 port = (service->u.unicast.port[0] << 8) | service->u.unicast.port[1]; 142 } 143 } 144 SEGMENTED_IPv6_ADDR_GEN_SRP(address->s6_addr, addr_buf); 145 if (!succeeded) { 146 INFO("service " PRI_SEGMENTED_IPv6_ADDR_SRP " not responding on port %d", 147 SEGMENTED_IPv6_ADDR_PARAM_SRP(address->s6_addr, addr_buf), port); 148 service->checking = false; 149 service->ignore = true; // Don't consider this service when deciding what to advertise 150 service->responding = false; 151 } else { 152 INFO("service " PRI_SEGMENTED_IPv6_ADDR_SRP " responded on port %d", 153 SEGMENTED_IPv6_ADDR_PARAM_SRP(address->s6_addr, addr_buf), port); 154 service->checking = false; 155 service->ignore = false; 156 service->checked = true; 157 } 158 service->responding = true; 159 160 if (probe_state->callback != NULL) { 161 probe_state->callback(probe_state->service, probe_state->context, succeeded); 162 probe_state->callback = NULL; 163 } 164 if (probe_state->context_release != NULL) { 165 probe_state->context_release(probe_state->context); 166 } 167 probe_state->context = NULL; 168 169 thread_service_release(service); // The probe state's reference to the service 170 if (probe_state->wakeup != NULL) { 171 ioloop_cancel_wake_event(probe_state->wakeup); 172 } 173 RELEASE_HERE(probe_state, probe_state); // The thread_service_t's reference to the probe state 174 } 175 176 static void 177 probe_srp_datagram(comm_t *connection, message_t *message, void *context) 178 { 179 #ifdef PROBE_SRP_TCP 180 (void)message; 181 // We should never get a datagram 182 ERROR("got a datagram on %p", context); 183 #else 184 int rcode = dns_rcode_get(&message->wire); 185 probe_state_t *probe_state = context; 186 if (connection->connection != NULL) { 187 INFO("datagram from " PRI_S_SRP " on port %d xid %x (question xid %x) rcode %d", connection->name, 188 ntohs(probe_state->connection->address.sin6.sin6_port), message->wire.id, probe_state->question.id, rcode); 189 } else { 190 SEGMENTED_IPv6_ADDR_GEN_SRP(&probe_state->connection->address.sin6.sin6_addr, addr_buf); 191 INFO("datagram from " PRI_SEGMENTED_IPv6_ADDR_SRP " on port %d xid %x (question xid %x) rcode %d", 192 SEGMENTED_IPv6_ADDR_PARAM_SRP(&probe_state->connection->address.sin6.sin6_addr, addr_buf), 193 ntohs(probe_state->connection->address.sin6.sin6_port), message->wire.id, probe_state->question.id, rcode); 194 } 195 if (message->wire.id != probe_state->question.id) { 196 return; // not a response to the question we asked 197 } 198 dns_message_t *dns_message = NULL; 199 if (!dns_wire_parse(&dns_message, &message->wire, message->length, false)) { 200 // Not a valid response, who knows what happened? 201 return; 202 } 203 dns_message_free(dns_message); 204 // If we get a servfail, treat it like a dropped packet, since that might just mean that the remote end is 205 // temporarily busy. 206 if (rcode == dns_rcode_servfail) { 207 return; 208 } 209 probe_srp_done(context, rcode == dns_rcode_noerror); 210 ioloop_comm_cancel(probe_state->connection); // Cancel the connection (should result in the state being released) 211 if (probe_state->wakeup != NULL) { 212 ioloop_cancel_wake_event(probe_state->wakeup); 213 } 214 #endif 215 } 216 217 static void 218 probe_srp_probe_state_context_release(void *context) 219 { 220 probe_state_t *probe_state = context; 221 RELEASE_HERE(probe_state, probe_state); 222 } 223 224 static void 225 probe_srp_disconnected(comm_t *UNUSED connection, void *context, int UNUSED error) 226 { 227 probe_state_t *probe_state = context; 228 229 // We can get here either because the connection object got canceled or because we made a TCP connection that 230 // failed to connect. If we haven't signaled "done" yet, probe_state->service will be non-NULL. 231 if (probe_state->service != NULL) { 232 probe_srp_done(context, false); 233 } 234 // Once we've gotten the connection disconnect event, we should not get any more callbacks from the connection 235 // object. 236 if (probe_state->connection != NULL) { 237 ioloop_comm_release(probe_state->connection); 238 probe_state->connection = NULL; 239 } 240 } 241 242 #ifndef PROBE_SRP_TCP 243 static void probe_srp_schedule_retransmission(probe_state_t *probe_state); 244 245 static void 246 probe_srp_retransmit(void *context) 247 { 248 probe_state_t *probe_state = context; 249 250 // Only retransmit three times. 251 probe_state->num_retransmissions++; 252 INFO("num_retransmissions = %d, time = %lg", probe_state->num_retransmissions, probe_state->retransmission_delay / 1000.0); 253 if (probe_state->num_retransmissions > 3) { 254 probe_srp_done(context, false); // fail 255 ioloop_comm_cancel(probe_state->connection); 256 } else { 257 // Schedule a retransmission with exponential backoff 258 probe_srp_schedule_retransmission(probe_state); 259 260 // Send the question 261 struct iovec iov; 262 iov.iov_len = probe_state->question_length; 263 iov.iov_base = &probe_state->question; 264 ioloop_send_message(probe_state->connection, NULL, &iov, 1); 265 } 266 } 267 268 static void 269 probe_srp_context_release(void *context) 270 { 271 probe_state_t *probe_state = context; 272 RELEASE_HERE(probe_state, probe_state); 273 } 274 275 static void 276 probe_srp_schedule_retransmission(probe_state_t *probe_state) 277 { 278 if (probe_state->wakeup == NULL) { 279 probe_state->wakeup = ioloop_wakeup_create(); 280 if (probe_state->wakeup == NULL) { 281 ERROR("can't allocate probe state wakeup"); 282 probe_srp_done(probe_state, false); 283 ioloop_comm_cancel(probe_state->connection); 284 return; 285 } 286 } 287 int next_time = probe_state->retransmission_delay + srp_random16() % probe_state->retransmission_delay; 288 probe_state->retransmission_delay *= 2; 289 ioloop_add_wake_event(probe_state->wakeup, probe_state, probe_srp_retransmit, probe_srp_context_release, next_time); 290 RETAIN_HERE(probe_state, probe_state); // The wakeup holds a reference to probe_state. 291 } 292 #endif // PROBE_SRP_TCP 293 294 static void 295 probe_srp_connected(comm_t *connection, void *context) 296 { 297 #ifdef PROBE_SRP_TCP 298 probe_srp_done(context, true); 299 #else 300 (void)connection; 301 probe_state_t *probe_state = context; 302 // Initialize a DNS message to send to the target 303 memset(&probe_state->question, 0, DNS_HEADER_SIZE); 304 dns_towire_state_t towire; 305 memset(&towire, 0, sizeof(towire)); 306 towire.p = &probe_state->question.data[0]; 307 towire.lim = &probe_state->question.data[0] + DNS_DATA_SIZE; 308 towire.message = &probe_state->question; 309 310 // Set up the header. 311 probe_state->question.id = srp_random16(); 312 probe_state->question.bitfield = 0; 313 dns_qr_set(&probe_state->question, dns_qr_query); 314 dns_opcode_set(&probe_state->question, dns_opcode_query); 315 probe_state->question.qdcount = htons(1); // Just ask one question 316 317 // Query SOA for default.service.arpa--if this fails, we can't use this server. 318 dns_full_name_to_wire(NULL, &towire, "default.service.arpa"); 319 dns_u16_to_wire(&towire, dns_rrtype_soa); 320 dns_u16_to_wire(&towire, dns_qclass_in); 321 probe_state->question_length = (uint16_t)(towire.p - (uint8_t *)&probe_state->question); 322 323 // We're not in a hurry; the goal is to probe. 324 probe_state->retransmission_delay = 1000; // milliseconds 325 probe_state->num_retransmissions = 0; 326 327 // Schedule the first send 328 probe_srp_schedule_retransmission(probe_state); 329 #endif // PROBE_SRP_TCP 330 } 331 332 static probe_state_t * 333 probe_srp_create(addr_t *address, thread_service_t *service, void *context, 334 void (*callback)(thread_service_t *service, void *context, bool succeeded), 335 void (*context_release)(void *context)) 336 { 337 probe_state_t *ret = NULL, *probe_state = calloc(1, sizeof(*probe_state)); 338 if (probe_state == NULL) { 339 INFO("failed to create probe state"); 340 goto out; 341 } 342 RETAIN_HERE(probe_state, probe_state); // Retain for the caller 343 // tls stream stable opportunistic 344 probe_state->connection = ioloop_connection_create(address, false, false, false, false, 345 probe_srp_datagram, probe_srp_connected, probe_srp_disconnected, 346 probe_srp_probe_state_context_release, probe_state); 347 if (probe_state->connection == NULL) { 348 INFO("failed to create connection"); 349 goto out; 350 } 351 RETAIN_HERE(probe_state, probe_state); // for connection 352 SEGMENTED_IPv6_ADDR_GEN_SRP(&address->sin6.sin6_addr, addr_buf); 353 INFO("probing service " PRI_SEGMENTED_IPv6_ADDR_SRP " on port %d", 354 SEGMENTED_IPv6_ADDR_PARAM_SRP(&address->sin6.sin6_addr, addr_buf), ntohs(address->sin6.sin6_port)); 355 probe_state->context = context; 356 probe_state->callback = callback; 357 service->last_probe_time = srp_time(); 358 359 service->probe_state = probe_state; 360 RETAIN_HERE(service->probe_state, probe_state); 361 362 probe_state->service = service; 363 thread_service_retain(probe_state->service); 364 365 // connection holds the only reference to probe_state. 366 ret = probe_state; 367 probe_state = NULL; 368 out: 369 if (probe_state != NULL) { 370 RELEASE_HERE(probe_state, probe_state); 371 } 372 if (ret == NULL && callback != NULL) { 373 dispatch_async(dispatch_get_main_queue(), ^{ 374 callback(service, context, true); // We claim success here because this should never fail; if it does, it's our problem. 375 if (context_release != NULL) { 376 context_release(context); 377 } 378 }); 379 } 380 return ret; 381 } 382 383 // If we've been asked to probe a service, go through the list. 384 static probe_state_t * 385 probe_srp_anycast_service(thread_service_t *service, void *context, 386 void (*callback)(thread_service_t *service, void *context, bool succeeded), 387 void (*context_release)(void *context)) 388 { 389 addr_t address; 390 memset(&address, 0, sizeof(address)); 391 memcpy(&address.sin6.sin6_addr, &service->u.anycast.address, sizeof(service->u.anycast.address)); 392 address.sin6.sin6_port = htons(53); 393 address.sa.sa_family = AF_INET6; 394 return probe_srp_create(&address, service, context, callback, context_release); 395 } 396 397 static probe_state_t * 398 probe_srp_unicast_service(thread_service_t *service, void *context, 399 void (*callback)(thread_service_t *service, void *context, bool succeeded), 400 void (*context_release)(void *context)){ 401 if (service->checking || service->user) { 402 return NULL; 403 } 404 addr_t address; 405 memset(&address, 0, sizeof(address)); 406 address.sa.sa_family = AF_INET6; 407 memcpy(&address.sin6.sin6_addr, &service->u.unicast.address, sizeof(address.sin6.sin6_addr)); 408 memcpy(&address.sin6.sin6_port, service->u.unicast.port, sizeof(address.sin6.sin6_port)); 409 return probe_srp_create(&address, service, context, callback, context_release); 410 } 411 412 void 413 probe_srp_service(thread_service_t *service, void *context, 414 void (*callback)(thread_service_t *service, void *context, bool succeeded), 415 void (*context_release)(void *context)) 416 { 417 probe_state_t *probe_state; 418 if (service->service_type == unicast_service) { 419 probe_state = probe_srp_unicast_service(service, context, callback, context_release); 420 } else if (service->service_type == anycast_service){ 421 probe_state = probe_srp_anycast_service(service, context, callback, context_release); 422 } else { 423 FAULT("bogus service type in probe_srp_service: %d", service->service_type); 424 if (callback != NULL) { 425 dispatch_async(dispatch_get_main_queue(), ^{ 426 callback(service, context, false); // False because this isn't a valid service 427 if (context_release) { 428 context_release(context); 429 } 430 }); 431 } 432 return; 433 } 434 435 // probe_srp_create returns this retained, but we don't store the pointer. 436 RELEASE_HERE(probe_state, probe_state); 437 } 438 439 // Local Variables: 440 // mode: C 441 // tab-width: 4 442 // c-file-style: "bsd" 443 // c-basic-offset: 4 444 // fill-column: 120 445 // indent-tabs-mode: nil 446 // End: 447