1 1.1 christos /* adv-ctl-proxy.c 2 1.1 christos * 3 1.1 christos * Copyright (c) 2019-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 the SRP Advertising Proxy control interface, which allows clients to control the advertising proxy 18 1.1 christos * and discover information about its internal state. This is largely used for testing. 19 1.1 christos */ 20 1.1 christos 21 1.1 christos #include <stdlib.h> 22 1.1 christos #include <string.h> 23 1.1 christos #include <stdio.h> 24 1.1 christos #include <unistd.h> 25 1.1 christos #include <pwd.h> 26 1.1 christos #include <errno.h> 27 1.1 christos #include <sys/socket.h> 28 1.1 christos #include <netinet/in.h> 29 1.1 christos #include <arpa/inet.h> 30 1.1 christos #include <fcntl.h> 31 1.1 christos #include <time.h> 32 1.1 christos #include <dns_sd.h> 33 1.1 christos #include <net/if.h> 34 1.1 christos #include <inttypes.h> 35 1.1 christos #include <sys/resource.h> 36 1.1 christos 37 1.1 christos #include "srp.h" 38 1.1 christos #include "dns-msg.h" 39 1.1 christos #include "ioloop.h" 40 1.1 christos #include "srp-gw.h" 41 1.1 christos #include "srp-proxy.h" 42 1.1 christos #include "cti-services.h" 43 1.1 christos #include "srp-mdns-proxy.h" 44 1.1 christos #include "route.h" 45 1.1 christos #include "nat64.h" 46 1.1 christos #include "adv-ctl-server.h" 47 1.1 christos #include "srp-replication.h" 48 1.1 christos #include "dnssd-proxy.h" 49 1.1 christos #include "thread-device.h" 50 1.1 christos 51 1.1 christos #include "state-machine.h" 52 1.1 christos #include "thread-service.h" 53 1.1 christos #include "omr-watcher.h" 54 1.1 christos #include "omr-publisher.h" 55 1.1 christos #include "dnssd-client.h" 56 1.1 christos #include "service-publisher.h" 57 1.1 christos #include "thread-tracker.h" 58 1.1 christos #include "service-tracker.h" 59 1.1 christos #include "route-tracker.h" 60 1.1 christos #include "ifpermit.h" 61 1.1 christos 62 1.1 christos #include "cti-proto.h" 63 1.1 christos #include "adv-ctl-common.h" 64 1.1 christos #include "advertising_proxy_services.h" 65 1.1 christos 66 1.1 christos static void srp_xpc_client_finalize(srp_wanted_state_t *wanted); 67 1.1 christos 68 1.1 christos 69 1.1 christos static int 70 1.1 christos adv_ctl_block_service(bool enable, void *context) 71 1.1 christos { 72 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 73 1.1 christos #if THREAD_BORDER_ROUTER 74 1.1 christos srp_server_t *server_state = context; 75 1.1 christos route_state_t *route_state = server_state->route_state; 76 1.1 christos if (enable) { 77 1.1 christos if (route_state->srp_listener != NULL) { 78 1.1 christos ioloop_comm_cancel(route_state->srp_listener); 79 1.1 christos server_state->srp_unicast_service_blocked = true; 80 1.1 christos } else { 81 1.1 christos status = kDNSSDAdvertisingProxyStatus_UnknownErr; 82 1.1 christos } 83 1.1 christos } else { 84 1.1 christos if (route_state->srp_listener == NULL) { 85 1.1 christos server_state->srp_unicast_service_blocked = false; 86 1.1 christos route_refresh_interface_list(route_state); 87 1.1 christos } else { 88 1.1 christos status = kDNSSDAdvertisingProxyStatus_UnknownErr; 89 1.1 christos } 90 1.1 christos } 91 1.1 christos #else 92 1.1 christos (void)enable; 93 1.1 christos (void)context; 94 1.1 christos #endif // THREAD_DEVICE 95 1.1 christos return status; 96 1.1 christos } 97 1.1 christos 98 1.1 christos static bool 99 1.1 christos adv_ctl_regenerate_ula(void *context) 100 1.1 christos { 101 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 102 1.1 christos #if STUB_ROUTER 103 1.1 christos srp_server_t *server_state = context; 104 1.1 christos 105 1.1 christos infrastructure_network_shutdown(server_state->route_state); 106 1.1 christos route_ula_generate(server_state->route_state); 107 1.1 christos infrastructure_network_startup(server_state->route_state); 108 1.1 christos #else 109 1.1 christos (void)context; 110 1.1 christos #endif 111 1.1 christos return status; 112 1.1 christos } 113 1.1 christos 114 1.1 christos static int 115 1.1 christos adv_ctl_advertise_prefix(void *context, omr_prefix_priority_t priority) 116 1.1 christos { 117 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 118 1.1 christos #if STUB_ROUTER 119 1.1 christos srp_server_t *server_state = context; 120 1.1 christos 121 1.1 christos if (server_state->route_state != NULL && server_state->route_state->omr_publisher != NULL) { 122 1.1 christos omr_publisher_force_publication(server_state->route_state->omr_publisher, priority); 123 1.1 christos } 124 1.1 christos #else 125 1.1 christos (void)context; 126 1.1 christos (void)priority; 127 1.1 christos #endif 128 1.1 christos return status; 129 1.1 christos } 130 1.1 christos 131 1.1 christos static int 132 1.1 christos adv_ctl_stop_advertising_service(void *context) 133 1.1 christos { 134 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 135 1.1 christos #if THREAD_DEVICE 136 1.1 christos srp_server_t *server_state = context; 137 1.1 christos 138 1.1 christos #if STUB_ROUTER 139 1.1 christos if (server_state->stub_router_enabled) { 140 1.1 christos partition_discontinue_all_srp_service(server_state->route_state); 141 1.1 christos } else 142 1.1 christos #endif 143 1.1 christos { 144 1.1 christos thread_device_stop(server_state); 145 1.1 christos } 146 1.1 christos #else 147 1.1 christos (void)context; 148 1.1 christos #endif 149 1.1 christos return status; 150 1.1 christos } 151 1.1 christos 152 1.1 christos static int 153 1.1 christos adv_ctl_disable_replication(void *context) 154 1.1 christos { 155 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 156 1.1 christos 157 1.1 christos #if SRP_FEATURE_REPLICATION 158 1.1 christos srp_server_t *server_state = context; 159 1.1 christos srpl_disable(server_state); 160 1.1 christos #else 161 1.1 christos (void)context; 162 1.1 christos #endif 163 1.1 christos return status; 164 1.1 christos } 165 1.1 christos 166 1.1 christos static int 167 1.1 christos adv_ctl_drop_srpl_connection(void *context) 168 1.1 christos { 169 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 170 1.1 christos 171 1.1 christos #if SRP_FEATURE_REPLICATION 172 1.1 christos srp_server_t *server_state = context; 173 1.1 christos srpl_drop_srpl_connection(server_state); 174 1.1 christos #else 175 1.1 christos (void)context; 176 1.1 christos #endif 177 1.1 christos return status; 178 1.1 christos } 179 1.1 christos 180 1.1 christos static int 181 1.1 christos adv_ctl_undrop_srpl_connection(void *context) 182 1.1 christos { 183 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 184 1.1 christos 185 1.1 christos #if SRP_FEATURE_REPLICATION 186 1.1 christos srp_server_t *server_state = context; 187 1.1 christos srpl_undrop_srpl_connection(server_state); 188 1.1 christos #else 189 1.1 christos (void)context; 190 1.1 christos #endif 191 1.1 christos return status; 192 1.1 christos } 193 1.1 christos 194 1.1 christos static int 195 1.1 christos adv_ctl_drop_srpl_advertisement(void *context) 196 1.1 christos { 197 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 198 1.1 christos 199 1.1 christos #if SRP_FEATURE_REPLICATION 200 1.1 christos srp_server_t *server_state = context; 201 1.1 christos srpl_drop_srpl_advertisement(server_state); 202 1.1 christos #else 203 1.1 christos (void)context; 204 1.1 christos #endif 205 1.1 christos return status; 206 1.1 christos } 207 1.1 christos 208 1.1 christos static int 209 1.1 christos adv_ctl_undrop_srpl_advertisement(void *context) 210 1.1 christos { 211 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 212 1.1 christos 213 1.1 christos #if SRP_FEATURE_REPLICATION 214 1.1 christos srp_server_t *server_state = context; 215 1.1 christos srpl_undrop_srpl_advertisement(server_state); 216 1.1 christos #else 217 1.1 christos (void)context; 218 1.1 christos #endif 219 1.1 christos return status; 220 1.1 christos } 221 1.1 christos 222 1.1 christos static void 223 1.1 christos adv_ctl_start_breaking_time(void *context) 224 1.1 christos { 225 1.1 christos srp_server_t *server_state = context; 226 1.1 christos 227 1.1 christos server_state->break_srpl_time = true; 228 1.1 christos } 229 1.1 christos 230 1.1 christos #if STUB_ROUTER || THREAD_DEVICE 231 1.1 christos static void 232 1.1 christos adv_ctl_thread_shutdown_continue(void *context) 233 1.1 christos { 234 1.1 christos srp_server_t *server_state = context; 235 1.1 christos 236 1.1 christos if (server_state->shutdown_timeout != NULL) { 237 1.1 christos ioloop_cancel_wake_event(server_state->shutdown_timeout); 238 1.1 christos ioloop_wakeup_release(server_state->shutdown_timeout); 239 1.1 christos server_state->shutdown_timeout = NULL; 240 1.1 christos } 241 1.1 christos 242 1.1 christos xpc_object_t response; 243 1.1 christos response = xpc_dictionary_create_reply(server_state->shutdown_request); 244 1.1 christos if (response == NULL) { 245 1.1 christos ERROR("adv_xpc_message: Unable to create reply dictionary."); 246 1.1 christos return; 247 1.1 christos } 248 1.1 christos xpc_dictionary_set_uint64(response, kDNSSDAdvertisingProxyResponseStatus, 0); 249 1.1 christos xpc_connection_send_message(server_state->shutdown_connection, response); 250 1.1 christos xpc_release(response); 251 1.1 christos xpc_release(server_state->shutdown_request); 252 1.1 christos server_state->shutdown_request = NULL; 253 1.1 christos xpc_release(server_state->shutdown_connection); 254 1.1 christos server_state->shutdown_connection = NULL; 255 1.1 christos 256 1.1 christos server_state->awaiting_service_removal = false; 257 1.1 christos server_state->awaiting_prefix_removal = false; 258 1.1 christos 259 1.1 christos if (server_state->wanted != NULL) { 260 1.1 christos INFO("clearing server_state->wanted (%p) unconditionally.", server_state->wanted); 261 1.1 christos srp_xpc_client_finalize(server_state->wanted); 262 1.1 christos } 263 1.1 christos } 264 1.1 christos 265 1.1 christos static void 266 1.1 christos adv_ctl_start_thread_shutdown_timer(srp_server_t *server_state) 267 1.1 christos { 268 1.1 christos if (server_state->shutdown_timeout == NULL) { 269 1.1 christos server_state->shutdown_timeout = ioloop_wakeup_create(); 270 1.1 christos } 271 1.1 christos // If we can't allocate the shutdown timer, send the response immediately (which also probably won't 272 1.1 christos // work, oh well). 273 1.1 christos if (server_state->shutdown_timeout == NULL) { 274 1.1 christos adv_ctl_thread_shutdown_continue(server_state); 275 1.1 christos return; 276 1.1 christos } 277 1.1 christos // Wait no longer than two seconds for thread network data update 278 1.1 christos ioloop_add_wake_event(server_state->shutdown_timeout, 279 1.1 christos server_state, adv_ctl_thread_shutdown_continue, NULL, 2 * IOLOOP_SECOND); 280 1.1 christos } 281 1.1 christos 282 1.1 christos static bool 283 1.1 christos adv_ctl_start_thread_shutdown(xpc_object_t request, xpc_connection_t connection, void *context) 284 1.1 christos { 285 1.1 christos srp_server_t *server_state = context; 286 1.1 christos 287 1.1 christos server_state->shutdown_connection = connection; 288 1.1 christos xpc_retain(server_state->shutdown_connection); 289 1.1 christos server_state->shutdown_request = request; 290 1.1 christos xpc_retain(server_state->shutdown_request); 291 1.1 christos if (0) { 292 1.1 christos #if STUB_ROUTER 293 1.1 christos } else if (server_state->stub_router_enabled) { 294 1.1 christos if (server_state->service_tracker != NULL && 295 1.1 christos service_tracker_local_service_seen(server_state->service_tracker)) 296 1.1 christos { 297 1.1 christos service_tracker_cancel_probes(server_state->service_tracker); // This is a no-op for now. 298 1.1 christos server_state->awaiting_service_removal = true; 299 1.1 christos } 300 1.1 christos if (server_state->route_state->omr_publisher != NULL && 301 1.1 christos omr_publisher_publishing_prefix(server_state->route_state->omr_publisher)) 302 1.1 christos { 303 1.1 christos omr_publisher_unpublish_prefix(server_state->route_state->omr_publisher); 304 1.1 christos if (server_state->route_state->omr_watcher != NULL) { 305 1.1 christos server_state->awaiting_prefix_removal = true; 306 1.1 christos } 307 1.1 christos } 308 1.1 christos if (route_tracker_local_routes_seen(server_state->route_state->route_tracker)) { 309 1.1 christos nat64_thread_shutdown(server_state->route_state); 310 1.1 christos route_tracker_shutdown(server_state->route_state); 311 1.1 christos server_state->awaiting_route_removal = true; 312 1.1 christos } 313 1.1 christos srpl_shutdown(server_state); 314 1.1 christos partition_discontinue_all_srp_service(server_state->route_state); 315 1.1 christos adv_ctl_start_thread_shutdown_timer(server_state); 316 1.1 christos adv_ctl_thread_shutdown_status_check(server_state); 317 1.1 christos #endif 318 1.1 christos #if THREAD_DEVICE 319 1.1 christos } else { 320 1.1 christos if (server_state->service_tracker != NULL) { 321 1.1 christos service_tracker_cancel_probes(server_state->service_tracker); 322 1.1 christos } 323 1.1 christos if (server_state->dnssd_client != NULL) { 324 1.1 christos dnssd_client_cancel(server_state->dnssd_client); 325 1.1 christos dnssd_client_release(server_state->dnssd_client); 326 1.1 christos server_state->dnssd_client = NULL; 327 1.1 christos } 328 1.1 christos if (server_state->service_publisher != NULL) { 329 1.1 christos service_publisher_stop_publishing(server_state->service_publisher); 330 1.1 christos service_publisher_cancel(server_state->service_publisher); 331 1.1 christos service_publisher_release(server_state->service_publisher); 332 1.1 christos server_state->service_publisher = NULL; 333 1.1 christos if (server_state->service_tracker != NULL && 334 1.1 christos service_tracker_local_service_seen(server_state->service_tracker)) 335 1.1 christos { 336 1.1 christos server_state->awaiting_service_removal = true; 337 1.1 christos } 338 1.1 christos } 339 1.1 christos adv_ctl_thread_shutdown_status_check(server_state); 340 1.1 christos adv_ctl_start_thread_shutdown_timer(server_state); 341 1.1 christos #endif 342 1.1 christos } 343 1.1 christos return true; 344 1.1 christos } 345 1.1 christos 346 1.1 christos void 347 1.1 christos adv_ctl_thread_shutdown_status_check(srp_server_t *server_state) 348 1.1 christos { 349 1.1 christos if (0) { 350 1.1 christos #if STUB_ROUTER 351 1.1 christos } else if (server_state->stub_router_enabled) { 352 1.1 christos if (!server_state->awaiting_prefix_removal && 353 1.1 christos !server_state->awaiting_service_removal && 354 1.1 christos !server_state->awaiting_route_removal) 355 1.1 christos { 356 1.1 christos adv_ctl_thread_shutdown_continue(server_state); 357 1.1 christos } 358 1.1 christos #endif 359 1.1 christos #if THREAD_DEVICE 360 1.1 christos } else { 361 1.1 christos if (!server_state->awaiting_service_removal) { 362 1.1 christos adv_ctl_thread_shutdown_continue(server_state); 363 1.1 christos } 364 1.1 christos #endif 365 1.1 christos } 366 1.1 christos } 367 1.1 christos #endif // STUB_ROUTER || THREAD_DEVICE 368 1.1 christos 369 1.1 christos static int 370 1.1 christos adv_ctl_block_anycast_service(bool block, void *context) 371 1.1 christos { 372 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 373 1.1 christos 374 1.1 christos #if STUB_ROUTER 375 1.1 christos srp_server_t *server_state = context; 376 1.1 christos partition_block_anycast_service(server_state->route_state, block); 377 1.1 christos #else 378 1.1 christos (void)context; 379 1.1 christos (void)block; 380 1.1 christos #endif 381 1.1 christos return status; 382 1.1 christos } 383 1.1 christos 384 1.1 christos typedef struct variable variable_t; 385 1.1 christos static void adv_ctl_set_int(srp_server_t *server_state, variable_t *which, const char *value); 386 1.1 christos 387 1.1 christos struct variable { 388 1.1 christos const char *name; 389 1.1 christos enum { type_int, type_bool, type_string } format; 390 1.1 christos void (*set_function)(srp_server_t *server_state, variable_t *which, const char *value); 391 1.1 christos size_t offset; 392 1.1 christos } variables[] = { 393 1.1 christos { "min-lease-time", type_int, adv_ctl_set_int, offsetof(srp_server_t, min_lease_time) }, 394 1.1 christos { "max-lease-time", type_int, adv_ctl_set_int, offsetof(srp_server_t, min_lease_time) }, 395 1.1 christos { "key-min-lease-time", type_int, adv_ctl_set_int, offsetof(srp_server_t, key_min_lease_time) }, 396 1.1 christos { "key-min-lease-time", type_int, adv_ctl_set_int, offsetof(srp_server_t, key_min_lease_time) }, 397 1.1 christos { NULL, type_int, NULL, 0 }, 398 1.1 christos }; 399 1.1 christos 400 1.1 christos static void 401 1.1 christos adv_ctl_set_int(srp_server_t *server_state, variable_t *which, const char *value) 402 1.1 christos { 403 1.1 christos int *dest = (int *)((char *)server_state + which->offset); 404 1.1 christos long new_value; 405 1.1 christos char *endptr; 406 1.1 christos 407 1.1 christos // Sanity check 408 1.1 christos if (which->offset > sizeof(*server_state)) { 409 1.1 christos ERROR("which->offset out of range: %zu vs %zu", which->offset, sizeof(*server_state)); 410 1.1 christos return; 411 1.1 christos } 412 1.1 christos if (value[0] == '0' && value[1] == 'x') { 413 1.1 christos new_value = strtol(value + 2, &endptr, 16); 414 1.1 christos } else { 415 1.1 christos new_value = strtol(value, &endptr, 10); 416 1.1 christos } 417 1.1 christos if (endptr == value || (endptr != NULL && *endptr != '\0') || new_value < INT_MIN || new_value > INT_MAX) { 418 1.1 christos ERROR("invalid int for " PUB_S_SRP ": " PUB_S_SRP, which->name, value); 419 1.1 christos return; 420 1.1 christos } 421 1.1 christos 422 1.1 christos INFO("setting " PUB_S_SRP " to '" PUB_S_SRP "' %ld (%lx), originally %d (%x)", 423 1.1 christos which->name, value, new_value, new_value, *dest, *dest); 424 1.1 christos *dest = (int)new_value; 425 1.1 christos } 426 1.1 christos 427 1.1 christos static int 428 1.1 christos adv_ctl_set_variable(void *context, const uint8_t *data, size_t data_len) 429 1.1 christos { 430 1.1 christos srp_server_t *server_state = context; 431 1.1 christos char *name = (char *)data, *value, *value_end; 432 1.1 christos size_t remain; 433 1.1 christos char hexbuf[1000]; 434 1.1 christos char *hp = hexbuf; 435 1.1 christos 436 1.1 christos for (size_t i = 0; i < data_len && hp < hexbuf + sizeof(hexbuf); ) { 437 1.1 christos size_t len = snprintf(hp, hexbuf + sizeof(hexbuf) - hp, "%02x ", data[i++]); 438 1.1 christos hp += len; 439 1.1 christos } 440 1.1 christos INFO("hexbuf: " PUB_S_SRP, hexbuf); 441 1.1 christos 442 1.1 christos // Find the end of the name 443 1.1 christos value = memchr(data, 0, data_len); 444 1.1 christos if (value == NULL) { 445 1.1 christos ERROR("name not NUL-terminated"); 446 1.1 christos return kDNSSDAdvertisingProxyStatus_BadParam; 447 1.1 christos } 448 1.1 christos value++; 449 1.1 christos remain = value - name; 450 1.1 christos if (remain >= data_len) { 451 1.1 christos ERROR("no value"); 452 1.1 christos return kDNSSDAdvertisingProxyStatus_BadParam; 453 1.1 christos } 454 1.1 christos value_end = memchr(value, 0, remain); 455 1.1 christos if (value_end == NULL) { 456 1.1 christos ERROR("value not NUL-terminated"); 457 1.1 christos return kDNSSDAdvertisingProxyStatus_BadParam; 458 1.1 christos } 459 1.1 christos // value_end - name is the length of all the data but the final NUL. 460 1.1 christos if ((size_t)(value_end - name) != data_len - 1) { 461 1.1 christos ERROR("extra bytes at end of name/value buffer: %zd != %zd %p %p %p %zu", 462 1.1 christos value_end - name, data_len, name, value, value_end, remain); 463 1.1 christos return kDNSSDAdvertisingProxyStatus_BadParam; 464 1.1 christos } 465 1.1 christos for (int i = 0; variables[i].name != NULL; i++) { 466 1.1 christos if (!strcmp(name, variables[i].name)) { 467 1.1 christos if (variables[i].set_function != NULL) { 468 1.1 christos variables[i].set_function(server_state, &variables[i], value); 469 1.1 christos } 470 1.1 christos break; 471 1.1 christos } 472 1.1 christos } 473 1.1 christos return kDNSSDAdvertisingProxyStatus_NoError; 474 1.1 christos } 475 1.1 christos 476 1.1 christos 477 1.1 christos static void 478 1.1 christos adv_ctl_fd_finalize(void *context) 479 1.1 christos { 480 1.1 christos advertising_proxy_conn_ref connection = context; 481 1.1 christos connection->io_context = NULL; 482 1.1 christos RELEASE_HERE(connection, advertising_proxy_conn_ref); 483 1.1 christos } 484 1.1 christos 485 1.1 christos static bool 486 1.1 christos adv_ctl_list_services(advertising_proxy_conn_ref connection, void *context) 487 1.1 christos { 488 1.1 christos srp_server_t *server_state = context; 489 1.1 christos adv_host_t *host; 490 1.1 christos int i; 491 1.1 christos int64_t now = ioloop_timenow(); 492 1.1 christos int num_hosts = 0; 493 1.1 christos 494 1.1 christos for (host = server_state->hosts; host != NULL; host = host->next) { 495 1.1 christos num_hosts++; 496 1.1 christos } 497 1.1 christos if (!cti_connection_message_create(connection, kDNSSDAdvertisingProxyResponse, 200) || 498 1.1 christos !cti_connection_u32_put(connection, (uint32_t)kDNSSDAdvertisingProxyStatus_NoError) || 499 1.1 christos !cti_connection_u32_put(connection, num_hosts)) 500 1.1 christos { 501 1.1 christos ERROR("adv_ctl_list_services: error starting response"); 502 1.1 christos cti_connection_close(connection); 503 1.1 christos return false; 504 1.1 christos } 505 1.1 christos for (host = server_state->hosts; host != NULL; host = host->next) { 506 1.1 christos int num_addresses = 0; 507 1.1 christos int num_instances = 0; 508 1.1 christos if (!cti_connection_string_put(connection, host->name) || 509 1.1 christos !cti_connection_string_put(connection, host->registered_name) || 510 1.1 christos !cti_connection_u32_put(connection, host->lease_expiry >= now ? host->lease_expiry - now : 0) || 511 1.1 christos !cti_connection_bool_put(connection, host->removed) || 512 1.1 christos !cti_connection_u64_put(connection, host->update_server_id)) 513 1.1 christos { 514 1.1 christos ERROR("adv_ctl_list_services: unable to write host info for host %s", host->name); 515 1.1 christos cti_connection_close(connection); 516 1.1 christos return false; 517 1.1 christos } 518 1.1 christos 519 1.1 christos cti_connection_u64_put(connection, host->server_stable_id); 520 1.1 christos 521 1.1 christos if (host->addresses != NULL) { 522 1.1 christos for (i = 0; i < host->addresses->num; i++) { 523 1.1 christos if (host->addresses->vec[i] != NULL) { 524 1.1 christos num_addresses++; 525 1.1 christos } 526 1.1 christos } 527 1.1 christos } 528 1.1 christos cti_connection_u16_put(connection, num_addresses); 529 1.1 christos if (host->addresses != NULL) { 530 1.1 christos for (i = 0; i < host->addresses->num; i++) { 531 1.1 christos if (host->addresses->vec[i] != NULL) { 532 1.1 christos if (!cti_connection_u16_put(connection, host->addresses->vec[i]->rrtype) || 533 1.1 christos !cti_connection_data_put(connection, host->addresses->vec[i]->rdata, host->addresses->vec[i]->rdlen)) 534 1.1 christos { 535 1.1 christos ERROR("adv_ctl_list_services: unable to write address %d for host %s", i, host->name); 536 1.1 christos cti_connection_close(connection); 537 1.1 christos return false; 538 1.1 christos } 539 1.1 christos } 540 1.1 christos } 541 1.1 christos } 542 1.1 christos if (host->instances != NULL) { 543 1.1 christos for (i = 0; i < host->instances->num; i++) { 544 1.1 christos if (host->instances->vec[i] != NULL) { 545 1.1 christos num_instances++; 546 1.1 christos } 547 1.1 christos } 548 1.1 christos } 549 1.1 christos cti_connection_u16_put(connection, num_instances); 550 1.1 christos if (host->instances != NULL) { 551 1.1 christos char *regtype; 552 1.1 christos if (memcmp(&host->server_stable_id, &server_state->ula_prefix, sizeof(host->server_stable_id))) { 553 1.1 christos regtype = "replicated"; 554 1.1 christos } else { 555 1.1 christos regtype = instance->anycast ? "anycast" : "unicast"; 556 1.1 christos } 557 1.1 christos for (i = 0; i < host->instances->num; i++) { 558 1.1 christos adv_instance_t *instance = host->instances->vec[i]; 559 1.1 christos if (instance != NULL) { 560 1.1 christos if (!cti_connection_string_put(connection, instance->instance_name) || 561 1.1 christos !cti_connection_string_put(connection, instance->service_type) || 562 1.1 christos !cti_connection_u16_put(connection, instance->port) || 563 1.1 christos !cti_connection_data_put(connection, instance->txt_data, instance->txt_length) || 564 1.1 christos !cti_connection_string_put(connection, regtype)) 565 1.1 christos { 566 1.1 christos ERROR("adv_ctl_list_services: unable to write address %d for host %s", i, host->name); 567 1.1 christos cti_connection_close(connection); 568 1.1 christos return false; 569 1.1 christos } 570 1.1 christos } 571 1.1 christos } 572 1.1 christos } 573 1.1 christos } 574 1.1 christos return cti_connection_message_send(connection); 575 1.1 christos } 576 1.1 christos 577 1.1 christos static bool 578 1.1 christos adv_ctl_get_ula(advertising_proxy_conn_ref connection, void *context) 579 1.1 christos { 580 1.1 christos srp_server_t *server_state = context; 581 1.1 christos 582 1.1 christos if (!cti_connection_message_create(connection, kDNSSDAdvertisingProxyResponse, 200) || 583 1.1 christos !cti_connection_u32_put(connection, (uint32_t)kDNSSDAdvertisingProxyStatus_NoError)) 584 1.1 christos { 585 1.1 christos ERROR("error starting response"); 586 1.1 christos cti_connection_close(connection); 587 1.1 christos return false; 588 1.1 christos } 589 1.1 christos // Copy out just the global ID part of the ULA prefix. 590 1.1 christos uint64_t ula = 0; 591 1.1 christos for (int j = 1; j < 6; j++) { 592 1.1 christos ula = ula << 8 | (((uint8_t *)&server_state->ula_prefix)[j]); 593 1.1 christos } 594 1.1 christos if (!cti_connection_u64_put(connection, ula)) { 595 1.1 christos ERROR("error sending ula"); 596 1.1 christos cti_connection_close(connection); 597 1.1 christos return false; 598 1.1 christos } 599 1.1 christos return cti_connection_message_send(connection); 600 1.1 christos } 601 1.1 christos 602 1.1 christos static void 603 1.1 christos adv_ctl_message_parse(advertising_proxy_conn_ref connection) 604 1.1 christos { 605 1.1 christos int status = kDNSSDAdvertisingProxyStatus_NoError; 606 1.1 christos cti_connection_parse_start(connection); 607 1.1 christos if (!cti_connection_u16_parse(connection, &connection->message_type)) { 608 1.1 christos return; 609 1.1 christos } 610 1.1 christos switch(connection->message_type) { 611 1.1 christos case kDNSSDAdvertisingProxyEnable: 612 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyEnable request.", 613 1.1 christos connection->uid, connection->gid); 614 1.1 christos break; 615 1.1 christos case kDNSSDAdvertisingProxyListServiceTypes: 616 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyListServiceTypes request.", 617 1.1 christos connection->uid, connection->gid); 618 1.1 christos break; 619 1.1 christos case kDNSSDAdvertisingProxyListServices: 620 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyListServices request.", 621 1.1 christos connection->uid, connection->gid); 622 1.1 christos adv_ctl_list_services(connection, connection->context); 623 1.1 christos return; 624 1.1 christos case kDNSSDAdvertisingProxyListHosts: 625 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyListHosts request.", 626 1.1 christos connection->uid, connection->gid); 627 1.1 christos break; 628 1.1 christos case kDNSSDAdvertisingProxyGetHost: 629 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyGetHost request.", 630 1.1 christos connection->uid, connection->gid); 631 1.1 christos break; 632 1.1 christos case kDNSSDAdvertisingProxyFlushEntries: 633 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyFlushEntries request.", 634 1.1 christos connection->uid, connection->gid); 635 1.1 christos srp_mdns_flush(connection->context); 636 1.1 christos break; 637 1.1 christos case kDNSSDAdvertisingProxyBlockService: 638 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyBlockService request.", 639 1.1 christos connection->uid, connection->gid); 640 1.1 christos adv_ctl_block_service(true, connection->context); 641 1.1 christos break; 642 1.1 christos case kDNSSDAdvertisingProxyUnblockService: 643 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyUnblockService request.", 644 1.1 christos connection->uid, connection->gid); 645 1.1 christos adv_ctl_block_service(false, connection->context); 646 1.1 christos break; 647 1.1 christos case kDNSSDAdvertisingProxyRegenerateULA: 648 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyRegenerateULA request.", 649 1.1 christos connection->uid, connection->gid); 650 1.1 christos adv_ctl_regenerate_ula(connection->context); 651 1.1 christos break; 652 1.1 christos case kDNSSDAdvertisingProxyAdvertisePrefix: 653 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyAdvertisePrefix request.", 654 1.1 christos connection->uid, connection->gid); 655 1.1 christos adv_ctl_advertise_prefix(connection->context); 656 1.1 christos break; 657 1.1 christos case kDNSSDAdvertisingProxyAddPrefix: { 658 1.1 christos void *data = NULL; 659 1.1 christos uint16_t data_len; 660 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyAddPrefix request.", 661 1.1 christos connection->uid, connection->gid); 662 1.1 christos if (!cti_connection_data_parse(connection, &data, &data_len)) { 663 1.1 christos ERROR("faile to parse data for kDNSSDAdvertisingProxyAddPrefix request."); 664 1.1 christos status = kDNSSDAdvertisingProxyStatus_BadParam; 665 1.1 christos } else { 666 1.1 christos if (data != NULL && data_len == 16) { 667 1.1 christos SEGMENTED_IPv6_ADDR_GEN_SRP(data, prefix_buf); 668 1.1 christos INFO("got prefix " PRI_SEGMENTED_IPv6_ADDR_SRP, SEGMENTED_IPv6_ADDR_PARAM_SRP(data, prefix_buf)); 669 1.1 christos adv_ctl_add_prefix(connection->context, data); 670 1.1 christos status = kDNSSDAdvertisingProxyStatus_NoError; 671 1.1 christos } else { 672 1.1 christos ERROR("invalid add prefix request, data[%p], data_len[%d]", data, data_len); 673 1.1 christos status = kDNSSDAdvertisingProxyStatus_BadParam; 674 1.1 christos } 675 1.1 christos } 676 1.1 christos break; 677 1.1 christos } 678 1.1 christos case kDNSSDAdvertisingProxyRemovePrefix: { 679 1.1 christos void *data = NULL; 680 1.1 christos uint16_t data_len; 681 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyRemovePrefix request.", 682 1.1 christos connection->uid, connection->gid); 683 1.1 christos if (!cti_connection_data_parse(connection, &data, &data_len)) { 684 1.1 christos ERROR("faile to parse data for kDNSSDAdvertisingProxyRemovePrefix request."); 685 1.1 christos status = kDNSSDAdvertisingProxyStatus_BadParam; 686 1.1 christos } else { 687 1.1 christos if (data != NULL && data_len == 16) { 688 1.1 christos SEGMENTED_IPv6_ADDR_GEN_SRP(data, prefix_buf); 689 1.1 christos INFO("got prefix " PRI_SEGMENTED_IPv6_ADDR_SRP, SEGMENTED_IPv6_ADDR_PARAM_SRP(data, prefix_buf)); 690 1.1 christos adv_ctl_remove_prefix(connection->context, data); 691 1.1 christos status = kDNSSDAdvertisingProxyStatus_NoError; 692 1.1 christos } else { 693 1.1 christos ERROR("invalid add prefix request, data[%p], data_len[%d]", data, data_len); 694 1.1 christos status = kDNSSDAdvertisingProxyStatus_BadParam; 695 1.1 christos } 696 1.1 christos } 697 1.1 christos break; 698 1.1 christos } 699 1.1 christos case kDNSSDAdvertisingProxyStop: 700 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyStop request.", 701 1.1 christos connection->uid, connection->gid); 702 1.1 christos adv_ctl_stop_advertising_service(connection->context); 703 1.1 christos break; 704 1.1 christos case kDNSSDAdvertisingProxyGetULA: 705 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyULA request.", 706 1.1 christos connection->uid, connection->gid); 707 1.1 christos adv_ctl_get_ula(connection, connection->context); 708 1.1 christos break; 709 1.1 christos case kDNSSDAdvertisingProxyDisableReplication: 710 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyDisableReplication request.", 711 1.1 christos connection->uid, connection->gid); 712 1.1 christos adv_ctl_disable_replication(connection->context); 713 1.1 christos break; 714 1.1 christos case kDNSSDAdvertisingProxyDropSrplConnection: 715 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyDropSrplConnection request.", 716 1.1 christos connection->uid, connection->gid); 717 1.1 christos adv_ctl_drop_srpl_connection(connection->context); 718 1.1 christos break; 719 1.1 christos case kDNSSDAdvertisingProxyUndropSrplConnection: 720 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyUndropSrplConnection request.", 721 1.1 christos connection->uid, connection->gid); 722 1.1 christos adv_ctl_undrop_srpl_connection(connection->context); 723 1.1 christos break; 724 1.1 christos case kDNSSDAdvertisingProxyDropSrplAdvertisement: 725 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyDropSrplAdvertisement request.", 726 1.1 christos connection->uid, connection->gid); 727 1.1 christos adv_ctl_drop_srpl_advertisement(connection->context); 728 1.1 christos break; 729 1.1 christos case kDNSSDAdvertisingProxyUndropSrplAdvertisement: 730 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyUndropSrplAdvertisement request.", 731 1.1 christos connection->uid, connection->gid); 732 1.1 christos adv_ctl_undrop_srpl_advertisement(connection->context); 733 1.1 christos break; 734 1.1 christos 735 1.1 christos case kDNSSDAdvertisingProxyStartDroppingPushConnections: 736 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyStartDroppingPushConnections request.", 737 1.1 christos connection->uid, connection->gid); 738 1.1 christos dp_start_dropping(); 739 1.1 christos break; 740 1.1 christos 741 1.1 christos case kDNSSDAdvertisingProxyStartBreakingTimeValidation: 742 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyStartBreakingTimeValidation request.", 743 1.1 christos connection->uid, connection->pid); 744 1.1 christos adv_ctl_start_breaking_time(connection->context); 745 1.1 christos break; 746 1.1 christos 747 1.1 christos case kDNSSDAdvertisingProxyStartThreadShutdown: 748 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyStartThreadShutdown request.", 749 1.1 christos connection->uid, connection->pid); 750 1.1 christos return adv_ctl_start_thread_shutdown(request, connection, context); 751 1.1 christos 752 1.1 christos case kDNSSDAdvertisingProxySetVariable: 753 1.1 christos void *data = NULL; 754 1.1 christos uint16_t data_len; 755 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxySetVariable request.", 756 1.1 christos connection->uid, connection->pid); 757 1.1 christos if (!cti_connection_data_parse(connection, &data, &data_len)) { 758 1.1 christos ERROR("faile to parse data for kDNSSDAdvertisingProxySetVariable request."); 759 1.1 christos status = kDNSSDAdvertisingProxyStatus_BadParam; 760 1.1 christos } else { 761 1.1 christos status = adv_ctl_set_variable(connection->context, data, data_len); 762 1.1 christos } 763 1.1 christos break; 764 1.1 christos 765 1.1 christos case kDNSSDAdvertisingProxyBlockAnycastService: 766 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyBlockAnycastService request.", 767 1.1 christos connection->uid, connection->gid); 768 1.1 christos adv_ctl_block_anycast_service(true, connection->context); 769 1.1 christos break; 770 1.1 christos 771 1.1 christos case kDNSSDAdvertisingProxyUnblockBlockAnycastService: 772 1.1 christos INFO("Client uid %d pid %d sent a kDNSSDAdvertisingProxyUnblockAnycastService request.", 773 1.1 christos connection->uid, connection->gid); 774 1.1 christos adv_ctl_block_anycast_service(false, connection->context); 775 1.1 christos break; 776 1.1 christos 777 1.1 christos default: 778 1.1 christos ERROR("Client uid %d pid %d sent a request with unknown message type %d.", 779 1.1 christos connection->uid, connection->gid, connection->message_type); 780 1.1 christos status = kDNSSDAdvertisingProxyStatus_Invalid; 781 1.1 christos break; 782 1.1 christos } 783 1.1 christos cti_send_response(connection, status); 784 1.1 christos cti_connection_close(connection); 785 1.1 christos } 786 1.1 christos 787 1.1 christos static void 788 1.1 christos adv_ctl_read_callback(io_t *UNUSED io, void *context) 789 1.1 christos { 790 1.1 christos advertising_proxy_conn_ref connection = context; 791 1.1 christos 792 1.1 christos cti_read(connection, adv_ctl_message_parse); 793 1.1 christos } 794 1.1 christos 795 1.1 christos static void 796 1.1 christos adv_ctl_listen_callback(io_t *UNUSED io, void *context) 797 1.1 christos { 798 1.1 christos srp_server_t *server_state = context; 799 1.1 christos uid_t uid; 800 1.1 christos gid_t gid; 801 1.1 christos pid_t pid; 802 1.1 christos 803 1.1 christos int fd = cti_accept(server_state->adv_ctl_listener->fd, &uid, &gid, &pid); 804 1.1 christos if (fd < 0) { 805 1.1 christos return; 806 1.1 christos } 807 1.1 christos 808 1.1 christos advertising_proxy_conn_ref connection = cti_connection_allocate(500); 809 1.1 christos if (connection == NULL) { 810 1.1 christos ERROR("cti_listen_callback: no memory for connection."); 811 1.1 christos close(fd); 812 1.1 christos return; 813 1.1 christos } 814 1.1 christos RETAIN_HERE(connection, advertising_proxy_conn_ref); 815 1.1 christos 816 1.1 christos connection->fd = fd; 817 1.1 christos connection->uid = uid; 818 1.1 christos connection->gid = gid; 819 1.1 christos connection->pid = pid; 820 1.1 christos connection->io_context = ioloop_file_descriptor_create(connection->fd, connection, adv_ctl_fd_finalize); 821 1.1 christos if (connection->io_context == NULL) { 822 1.1 christos ERROR("cti_listen_callback: no memory for io context."); 823 1.1 christos close(fd); 824 1.1 christos RELEASE_HERE(connection, advertising_proxy_conn_ref); 825 1.1 christos return; 826 1.1 christos } 827 1.1 christos ioloop_add_reader(connection->io_context, adv_ctl_read_callback); 828 1.1 christos connection->context = context; 829 1.1 christos connection->callback.reply = NULL; 830 1.1 christos connection->internal_callback = NULL; 831 1.1 christos return; 832 1.1 christos } 833 1.1 christos 834 1.1 christos static int 835 1.1 christos adv_ctl_listen(srp_server_t *server_state) 836 1.1 christos { 837 1.1 christos int fd = cti_make_unix_socket(ADV_CTL_SERVER_SOCKET_NAME, sizeof(ADV_CTL_SERVER_SOCKET_NAME), true); 838 1.1 christos if (fd < 0) { 839 1.1 christos int ret = (errno == ECONNREFUSED 840 1.1 christos ? kDNSSDAdvertisingProxyStatus_DaemonNotRunning 841 1.1 christos : errno == EPERM ? kDNSSDAdvertisingProxyStatus_NotPermitted : kDNSSDAdvertisingProxyStatus_UnknownErr); 842 1.1 christos ERROR("adv_ctl_listener: socket: %s", strerror(errno)); 843 1.1 christos return ret; 844 1.1 christos } 845 1.1 christos 846 1.1 christos server_state->adv_ctl_listener = ioloop_file_descriptor_create(fd, server_state, NULL); 847 1.1 christos if (server_state->adv_ctl_listener == NULL) { 848 1.1 christos ERROR("adv_ctl_listener: no memory for io_t object."); 849 1.1 christos close(fd); 850 1.1 christos return kDNSSDAdvertisingProxyStatus_NoMemory; 851 1.1 christos } 852 1.1 christos RETAIN_HERE(server_state->adv_ctl_listener, ioloop_file_descriptor); 853 1.1 christos 854 1.1 christos ioloop_add_reader(server_state->adv_ctl_listener, adv_ctl_listen_callback); 855 1.1 christos return kDNSSDAdvertisingProxyStatus_NoError; 856 1.1 christos } 857 1.1 christos 858 1.1 christos bool 859 1.1 christos adv_ctl_init(void *context) 860 1.1 christos { 861 1.1 christos srp_server_t *server_state = context; 862 1.1 christos return adv_ctl_listen(server_state); 863 1.1 christos } 864 1.1 christos 865 1.1 christos // Local Variables: 866 1.1 christos // mode: C 867 1.1 christos // tab-width: 4 868 1.1 christos // c-file-style: "bsd" 869 1.1 christos // c-basic-offset: 4 870 1.1 christos // fill-column: 108 871 1.1 christos // indent-tabs-mode: nil 872 1.1 christos // End: 873