Home | History | Annotate | Line # | Download | only in ServiceRegistration
      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