1 /* advertising_proxy_services.h 2 * 3 * Copyright (c) 2020-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 implementation of the SRP Advertising Proxy management 18 * API on MacOS, which is private API used to control and manage the advertising 19 * proxy. 20 */ 21 22 23 24 #include <errno.h> 25 #include <unistd.h> 26 #include <fcntl.h> 27 #include <inttypes.h> 28 #include <netinet/in.h> 29 #include <stdlib.h> 30 31 #include "srp.h" 32 #include "dns-msg.h" 33 #include "ioloop.h" 34 #include "cti-proto.h" 35 36 #include "adv-ctl-common.h" 37 #include "advertising_proxy_services.h" 38 39 #ifndef IOLOOP_MACOS 40 int adv_host_created; 41 int adv_host_finalized; 42 int advertising_proxy_conn_ref_created; 43 int advertising_proxy_conn_ref_finalized; 44 os_log_t global_os_log; 45 #endif 46 47 static void 48 adv_host_finalize(advertising_proxy_host_t *host) 49 { 50 int i; 51 52 free(host->hostname); 53 free(host->regname); 54 55 if (host->addresses != NULL) { 56 for (i = 0; i < host->num_addresses; i++) { 57 free(host->addresses[i].rdata); 58 } 59 free(host->addresses); 60 } 61 if (host->instances != NULL) { 62 for (i = 0; i < host->num_instances; i++) { 63 free(host->instances[i].instance_name); 64 free(host->instances[i].service_type); 65 free(host->instances[i].txt_data); 66 } 67 } 68 free(host); 69 } 70 71 #define adv_host_allocate() adv_host_allocate_(__FILE__, __LINE__) 72 static advertising_proxy_host_t * 73 adv_host_allocate_(const char *file, int line) 74 { 75 advertising_proxy_host_t *host = calloc(1, sizeof(*host)); 76 if (host == NULL) { 77 return host; 78 } 79 RETAIN(host, adv_host); 80 return host; 81 } 82 83 84 85 static void 86 adv_fd_finalize(void *context) 87 { 88 advertising_proxy_conn_ref connection = context; 89 connection->io_context = NULL; 90 RELEASE_HERE(connection, advertising_proxy_conn_ref); 91 } 92 93 void 94 advertising_proxy_ref_dealloc(advertising_proxy_conn_ref conn_ref) 95 { 96 if (conn_ref == NULL) { 97 ERROR("advertising_proxy_ref_dealloc called with NULL advertising_proxy_conn_ref"); 98 return; 99 } 100 conn_ref->callback.callback = NULL; 101 cti_connection_close(conn_ref); 102 103 // This is releasing the caller's reference. We may still have an internal reference. 104 RELEASE_HERE(conn_ref, advertising_proxy_conn_ref); 105 ERROR("advertising_proxy_ref_dealloc successfully released conn_ref"); 106 } 107 108 static void 109 adv_message_parse(cti_connection_t connection) 110 { 111 int err = kDNSSDAdvertisingProxyStatus_NoError; 112 int32_t status; 113 114 cti_connection_parse_start(connection); 115 if (!cti_connection_u16_parse(connection, &connection->message_type)) { 116 err = kDNSSDAdvertisingProxyStatus_Disconnected; 117 goto out; 118 } 119 if (connection->message_type != kDNSSDAdvertisingProxyResponse) { 120 syslog(LOG_ERR, "adv_message_parse: unexpected message type %d", connection->message_type); 121 err = kDNSSDAdvertisingProxyStatus_Disconnected; 122 goto out; 123 } 124 if (!cti_connection_u32_parse(connection, (uint32_t *)&status)) { 125 err = kDNSSDAdvertisingProxyStatus_Disconnected; 126 goto out; 127 } 128 if (status != kDNSSDAdvertisingProxyStatus_NoError) { 129 goto out; 130 } 131 if (connection->internal_callback != NULL) { 132 (*connection->internal_callback)(connection, NULL, status); 133 } else { 134 if (!cti_connection_parse_done(connection)) { 135 err = kDNSSDAdvertisingProxyStatus_Disconnected; 136 goto out; 137 } 138 if (connection->callback.reply != NULL) { 139 connection->callback.reply(connection, NULL, status); 140 } 141 } 142 return; 143 out: 144 cti_connection_close(connection); 145 if (connection->callback.reply != NULL) { 146 connection->callback.reply(connection, NULL, err); 147 } 148 return; 149 } 150 151 static void 152 adv_read_callback(io_t *UNUSED io, void *context) 153 { 154 cti_connection_t connection = context; 155 156 cti_read(connection, adv_message_parse); 157 } 158 159 static void 160 adv_service_list_callback(cti_connection_t connection, void *UNUSED object, cti_status_t UNUSED status) 161 { 162 int i; 163 advertising_proxy_host_t *host = NULL; 164 uint32_t num_hosts; 165 166 if (!cti_connection_u32_parse(connection, &num_hosts)) { 167 ERROR("adv_ctl_list_callback: error parsing host count"); 168 goto fail; 169 } 170 for (i = 0; i < num_hosts; i++) { 171 host = adv_host_allocate(); 172 if (host == NULL) { 173 ERROR("adv_ctl_list_callback: no memory for host object"); 174 cti_connection_close(connection); 175 goto fail; 176 } 177 if (!cti_connection_string_parse(connection, &host->hostname) || 178 !cti_connection_string_parse(connection, &host->regname) || 179 !cti_connection_u32_parse(connection, &host->lease_time) || 180 !cti_connection_bool_parse(connection, &host->removed) || 181 !cti_connection_u64_parse(connection, &host->server_id)) 182 { 183 ERROR("adv_ctl_list_callback: unable to parse host info for host %d", i); 184 cti_connection_close(connection); 185 goto fail; 186 } 187 188 if (!cti_connection_u16_parse(connection, &host->num_addresses)) { 189 ERROR("adv_ctl_list_callback: unable to parse host address count for host %s", host->hostname); 190 cti_connection_close(connection); 191 goto fail; 192 } 193 if (host->num_addresses > 0) { 194 host->addresses = calloc(host->num_addresses, sizeof(advertising_proxy_host_address_t)); 195 if (host->addresses == NULL) { 196 ERROR("adv_ctl_list_callback: no memory for addresses for host %s", host->hostname); 197 goto fail; 198 } 199 } 200 for (i = 0; i < host->num_addresses; i++) { 201 if (!cti_connection_u16_parse(connection, &host->addresses[i].rrtype) || 202 !cti_connection_data_parse(connection, (void **)&host->addresses[i].rdata, &host->addresses[i].rdlen)) 203 { 204 ERROR("adv_ctl_list_callback: unable to parse address %d for host %s", i, host->hostname); 205 goto fail; 206 } 207 } 208 209 if (!cti_connection_u64_parse(connection, &host->server_id)) { 210 ERROR("adv_ctl_list_callback: unable to parse stable server ID for host %s", host->hostname); 211 goto fail; 212 } 213 214 if (!cti_connection_u16_parse(connection, &host->num_instances)) { 215 ERROR("adv_ctl_list_callback: unable to parse host address count for host %s", host->hostname); 216 cti_connection_close(connection); 217 goto fail; 218 } 219 if (host->num_instances > 0) { 220 host->instances = calloc(host->num_instances, sizeof(advertising_proxy_instance_t)); 221 if (host->instances == NULL) { 222 ERROR("adv_ctl_list_callback: no memory for instances for host %s", host->hostname); 223 goto fail; 224 } 225 } 226 for (i = 0; i < host->num_instances; i++) { 227 if (!cti_connection_string_parse(connection, &host->instances[i].instance_name) || 228 !cti_connection_string_parse(connection, &host->instances[i].service_type) || 229 !cti_connection_u16_parse(connection, &host->instances[i].port) || 230 !cti_connection_data_parse(connection, (void **)&host->instances[i].txt_data, &host->instances[i].txt_len)) 231 { 232 ERROR("adv_ctl_list_callback: unable to write address %d for host %s", i, host->hostname); 233 goto fail; 234 } 235 } 236 if (connection->callback.reply != NULL) { 237 connection->callback.reply(connection, host, kDNSSDAdvertisingProxyStatus_NoError); 238 } 239 RELEASE_HERE(host, advertising_proxy_conn_ref); 240 host = NULL; 241 } 242 243 if (!cti_connection_parse_done(connection)) { 244 fail: 245 if (connection->callback.reply != NULL) { 246 connection->callback.reply(connection, NULL, kDNSSDAdvertisingProxyStatus_Disconnected); 247 } 248 } else { 249 if (connection->callback.reply != NULL) { 250 connection->callback.reply(connection, NULL, kDNSSDAdvertisingProxyStatus_NoError); 251 } 252 } 253 if (host != NULL) { 254 RELEASE_HERE(host, advertising_proxy_conn_ref); 255 } 256 } 257 258 static void 259 adv_ula_callback(cti_connection_t connection, void *UNUSED object, cti_status_t UNUSED status) 260 { 261 uint64_t ula_prefix; 262 263 if (!cti_connection_u64_parse(connection, &ula_prefix)) { 264 ERROR("error parsing ula prefix"); 265 goto fail; 266 } 267 268 if (!cti_connection_parse_done(connection)) { 269 fail: 270 if (connection->callback.reply != NULL) { 271 connection->callback.reply(connection, NULL, kDNSSDAdvertisingProxyStatus_Disconnected); 272 } 273 } else { 274 if (connection->callback.reply != NULL) { 275 connection->callback.reply(connection, &ula_prefix, kDNSSDAdvertisingProxyStatus_NoError); 276 } 277 } 278 } 279 280 #define adv_send_command_with_data(ref, client_queue, command_name, command, app_callback, internal_callback, allocation, data, len) \ 281 adv_send_command_(ref, client_queue, command_name, command, app_callback, internal_callback, allocation, __FILE__, __LINE__) 282 #define adv_send_command(ref, client_queue, command_name, command, app_callback, internal_callback, allocation) \ 283 adv_send_command_(ref, client_queue, command_name, command, app_callback, internal_callback, allocation, __FILE__, __LINE__) 284 static advertising_proxy_error_type 285 adv_send_command_(advertising_proxy_conn_ref *ref, run_context_t client_queue, const char *command_name, int command, 286 advertising_proxy_reply app_callback, cti_internal_callback_t internal_callback, size_t allocation, 287 const char *file, int line) 288 { 289 int fd; 290 cti_connection_t connection; 291 292 fd = cti_make_unix_socket(ADV_CTL_SERVER_SOCKET_NAME, sizeof(ADV_CTL_SERVER_SOCKET_NAME), false); 293 if (fd < 0) { 294 return kDNSSDAdvertisingProxyStatus_DaemonNotRunning; 295 } 296 connection = cti_connection_allocate(allocation); 297 if (connection == NULL) { 298 close(fd); 299 return kDNSSDAdvertisingProxyStatus_NoMemory; 300 } 301 connection->fd = fd; 302 RETAIN(connection, advertising_proxy_conn_ref); 303 304 connection->io_context = ioloop_file_descriptor_create(connection->fd, connection, adv_fd_finalize); 305 if (connection->io_context == NULL) { 306 ERROR("cti_listen_callback: no memory for io context."); 307 close(fd); 308 RELEASE_HERE(connection, advertising_proxy_conn_ref); 309 return kDNSSDAdvertisingProxyStatus_NoMemory; 310 } 311 ioloop_add_reader(connection->io_context, adv_read_callback); 312 connection->callback.reply = app_callback; 313 connection->internal_callback = internal_callback; 314 cti_connection_message_create(connection, command, 2); 315 cti_connection_message_send(connection); 316 *ref = connection; 317 return kDNSSDAdvertisingProxyStatus_NoError; 318 } 319 320 321 advertising_proxy_error_type 322 advertising_proxy_flush_entries(advertising_proxy_conn_ref *conn_ref, 323 run_context_t client_queue, advertising_proxy_reply callback) 324 { 325 advertising_proxy_error_type errx; 326 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_flush_entries", 327 kDNSSDAdvertisingProxyFlushEntries, callback, NULL, 0); 328 return errx; 329 } 330 331 advertising_proxy_error_type 332 advertising_proxy_get_service_list(advertising_proxy_conn_ref *conn_ref, 333 run_context_t client_queue, advertising_proxy_reply callback) 334 { 335 advertising_proxy_error_type errx; 336 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_get_service_list", 337 kDNSSDAdvertisingProxyListServices, callback, adv_service_list_callback, 4096); 338 return errx; 339 } 340 341 advertising_proxy_error_type 342 advertising_proxy_block_service(advertising_proxy_conn_ref *conn_ref, 343 run_context_t client_queue, advertising_proxy_reply callback) 344 { 345 advertising_proxy_error_type errx; 346 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_block_service", 347 kDNSSDAdvertisingProxyBlockService, callback, NULL, 0); 348 return errx; 349 } 350 351 advertising_proxy_error_type 352 advertising_proxy_unblock_service(advertising_proxy_conn_ref *conn_ref, 353 run_context_t client_queue, advertising_proxy_reply callback) 354 { 355 advertising_proxy_error_type errx; 356 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_unblock_service", 357 kDNSSDAdvertisingProxyUnblockService, callback, NULL, 0); 358 return errx; 359 } 360 361 advertising_proxy_error_type 362 advertising_proxy_regenerate_ula(advertising_proxy_conn_ref *conn_ref, 363 run_context_t client_queue, advertising_proxy_reply callback) 364 { 365 advertising_proxy_error_type errx; 366 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_regenerate_ula", 367 kDNSSDAdvertisingProxyRegenerateULA, callback, NULL, 0); 368 return errx; 369 } 370 371 advertising_proxy_error_type 372 advertising_proxy_advertise_prefix(advertising_proxy_conn_ref *conn_ref, bool high, 373 run_context_t client_queue, advertising_proxy_reply callback) 374 { 375 advertising_proxy_error_type errx; 376 if (high) { 377 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_advertise_prefix", 378 kDNSSDAdvertisingProxyAdvertisePrefixPriorityHigh, callback, NULL, 0); 379 } else { 380 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_advertise_prefix", 381 kDNSSDAdvertisingProxyAdvertisePrefix, callback, NULL, 0); 382 } 383 return errx; 384 } 385 386 advertising_proxy_error_type 387 advertising_proxy_add_prefix(advertising_proxy_conn_ref *conn_ref, run_context_t client_queue, 388 advertising_proxy_reply callback, const uint8_t *prefix_buf, size_t buf_len) 389 { 390 advertising_proxy_error_type errx; 391 errx = adv_send_command_with_data(conn_ref, client_queue, "advertising_proxy_add_prefix", 392 kDNSSDAdvertisingProxyAddPrefix, callback, NULL, 0, 393 prefix_buf, buf_len); 394 return errx; 395 } 396 397 advertising_proxy_error_type 398 advertising_proxy_remove_prefix(advertising_proxy_conn_ref *conn_ref, run_context_t client_queue, 399 advertising_proxy_reply callback, const uint8_t *prefix_buf, size_t buf_len) 400 { 401 advertising_proxy_error_type errx; 402 errx = adv_send_command_with_data(conn_ref, client_queue, "advertising_proxy_remove_prefix", 403 kDNSSDAdvertisingProxyRemovePrefix, callback, NULL, 0, 404 prefix_buf, buf_len); 405 return errx; 406 } 407 408 advertising_proxy_error_type 409 advertising_proxy_add_nat64_prefix(advertising_proxy_conn_ref *conn_ref, run_context_t client_queue, 410 advertising_proxy_reply callback, const uint8_t *prefix_buf, size_t buf_len) 411 { 412 advertising_proxy_error_type errx; 413 errx = adv_send_command_with_data(conn_ref, client_queue, "advertising_proxy_add_nat64_prefix", 414 kDNSSDAdvertisingProxyAddNAT64Prefix, callback, NULL, 0, 415 prefix_buf, buf_len); 416 return errx; 417 } 418 419 advertising_proxy_error_type 420 advertising_proxy_remove_nat64_prefix(advertising_proxy_conn_ref *conn_ref, run_context_t client_queue, 421 advertising_proxy_reply callback, const uint8_t *prefix_buf, size_t buf_len) 422 { 423 advertising_proxy_error_type errx; 424 errx = adv_send_command_with_data(conn_ref, client_queue, "advertising_proxy_remove_nat64_prefix", 425 kDNSSDAdvertisingProxyRemoveNAT64Prefix, callback, NULL, 0, 426 prefix_buf, buf_len); 427 return errx; 428 } 429 430 advertising_proxy_error_type 431 advertising_proxy_stop(advertising_proxy_conn_ref *conn_ref, 432 run_context_t client_queue, advertising_proxy_reply callback) 433 { 434 advertising_proxy_error_type errx; 435 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_stop", 436 kDNSSDAdvertisingProxyStop, callback, NULL, 0); 437 return errx; 438 } 439 440 advertising_proxy_error_type 441 advertising_proxy_get_ula(advertising_proxy_conn_ref *conn_ref, 442 run_context_t client_queue, advertising_proxy_reply callback) 443 { 444 advertising_proxy_error_type errx; 445 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_get_ula", 446 kDNSSDAdvertisingProxyGetULA, callback, adv_ula_callback, 128); 447 return errx; 448 } 449 450 advertising_proxy_error_type 451 advertising_proxy_disable_srp_replication(advertising_proxy_conn_ref *conn_ref, 452 run_context_t client_queue, advertising_proxy_reply callback) 453 { 454 advertising_proxy_error_type errx; 455 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_disable_srp_replication", 456 kDNSSDAdvertisingProxyDisableReplication, callback, NULL, 0); 457 return errx; 458 } 459 460 advertising_proxy_error_type 461 advertising_proxy_drop_srpl_connection(advertising_proxy_conn_ref *conn_ref, 462 run_context_t client_queue, advertising_proxy_reply callback) 463 { 464 advertising_proxy_error_type errx; 465 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_drop_srpl_connection", 466 kDNSSDAdvertisingProxyDropSrplConnection, callback, NULL, 0); 467 return errx; 468 } 469 470 advertising_proxy_error_type 471 advertising_proxy_undrop_srpl_connection(advertising_proxy_conn_ref *conn_ref, 472 run_context_t client_queue, advertising_proxy_reply callback) 473 { 474 advertising_proxy_error_type errx; 475 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_undrop_srpl_connection", 476 kDNSSDAdvertisingProxyUndropSrplConnection, callback, NULL, 0); 477 return errx; 478 } 479 480 advertising_proxy_error_type 481 advertising_proxy_drop_srpl_advertisement(advertising_proxy_conn_ref *conn_ref, 482 run_context_t client_queue, advertising_proxy_reply callback) 483 { 484 advertising_proxy_error_type errx; 485 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_drop_srpl_advertisement", 486 kDNSSDAdvertisingProxyDropSrplAdvertisement, callback, NULL, 0); 487 return errx; 488 } 489 490 advertising_proxy_error_type 491 advertising_proxy_undrop_srpl_advertisement(advertising_proxy_conn_ref *conn_ref, 492 run_context_t client_queue, advertising_proxy_reply callback) 493 { 494 advertising_proxy_error_type errx; 495 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_disable_undrop_srpl_advertisement", 496 kDNSSDAdvertisingProxyUndropSrplAdvertisement, callback, NULL, 0); 497 return errx; 498 } 499 500 advertising_proxy_error_type 501 advertising_proxy_start_dropping_push_connections(advertising_proxy_conn_ref *conn_ref, 502 run_context_t client_queue, advertising_proxy_reply callback) 503 { 504 advertising_proxy_error_type errx; 505 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_disable_start_dropping_push_connections", 506 kDNSSDAdvertisingProxyStartDroppingPushConnections, callback, NULL, 0); 507 return errx; 508 } 509 510 advertising_proxy_error_type 511 advertising_proxy_start_breaking_time_validation(advertising_proxy_conn_ref *conn_ref, 512 run_context_t client_queue, advertising_proxy_reply callback) 513 { 514 advertising_proxy_error_type errx; 515 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_start_breaking_time_validation", 516 kDNSSDAdvertisingProxyStartBreakingTimeValidation, callback, NULL, 0); 517 return errx; 518 } 519 520 advertising_proxy_error_type 521 advertising_proxy_block_anycast_service(advertising_proxy_conn_ref *conn_ref, 522 run_context_t client_queue, advertising_proxy_reply callback) 523 { 524 advertising_proxy_error_type errx; 525 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_block_anycast_service", 526 kDNSSDAdvertisingProxyBlockAnycastService, callback, NULL, 0); 527 return errx; 528 } 529 530 advertising_proxy_error_type 531 advertising_proxy_unblock_anycast_service(advertising_proxy_conn_ref *conn_ref, 532 run_context_t client_queue, advertising_proxy_reply callback) 533 { 534 advertising_proxy_error_type errx; 535 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_disable_unblock_anycast_service", 536 kDNSSDAdvertisingProxyUnblockAnycastService, callback, NULL, 0); 537 return errx; 538 } 539 540 advertising_proxy_error_type 541 advertising_proxy_start_thread_shutdown(advertising_proxy_conn_ref *conn_ref, 542 run_context_t client_queue, advertising_proxy_reply callback) 543 { 544 advertising_proxy_error_type errx; 545 errx = adv_send_command(conn_ref, client_queue, "advertising_proxy_start_thread_shutdown", 546 kDNSSDAdvertisingProxyStartThreadShutdown, callback, NULL, 0); 547 return errx; 548 } 549 550 static void 551 adv_set_variable_callback(advertising_proxy_conn_ref conn_ref, xpc_object_t *response, advertising_proxy_error_type status) 552 { 553 if (conn_ref->app_response_callback != NULL) { 554 conn_ref->app_response_callback(conn_ref, conn_ref->context, response, status); 555 } 556 } 557 558 advertising_proxy_error_type 559 advertising_proxy_set_variable(advertising_proxy_conn_ref *conn_ref, 560 run_context_t client_queue, advertising_proxy_response_reply callback, void *context, 561 const char *name, const char *value) 562 { 563 advertising_proxy_error_type errx; 564 size_t name_len = strlen(name), value_len = strlen(value); 565 size_t total_len = name_len + value_len + 2; 566 uint8_t *buf = malloc(total_len); 567 if (buf == NULL) { 568 return kDNSSDAdvertisingProxyStatus_NoMemory; 569 } 570 memcpy(buf, name, name_len + 1); 571 memcpy(buf + name_len + 1, value, value_len + 1); 572 errx = adv_send_command_with_data(conn_ref, client_queue, "advertising_proxy_get_service_list", 573 kDNSSDAdvertisingProxySetVariable, NULL, adv_set_variable_callback, 574 0, buf, total_len); 575 free(buf); 576 if (errx == kDNSSDAdvertisingProxyStatus_NoError) { 577 (*conn_ref)->context = context; 578 (*conn_ref)->app_response_callback = callback; 579 } 580 return errx; 581 } 582 583 // Local Variables: 584 // mode: C 585 // tab-width: 4 586 // c-file-style: "bsd" 587 // c-basic-offset: 4 588 // fill-column: 120 589 // indent-tabs-mode: nil 590 // End: 591