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