Home | History | Annotate | Line # | Download | only in srputil
srputil.c revision 1.1
      1 /* srputil.c
      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  * SRP Advertising Proxy utility program, allows:
     18  *   start/stop advertising proxy
     19  *   get/track list of service types
     20  *   get/track list of services of a particular type
     21  *   get/track list of hosts
     22  *   get/track information about a particular host
     23  */
     24 
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <stdio.h>
     28 #include <unistd.h>
     29 #include <errno.h>
     30 #include <sys/socket.h>
     31 #include <netinet/in.h>
     32 #include <arpa/inet.h>
     33 #include <fcntl.h>
     34 #include <time.h>
     35 #include <dns_sd.h>
     36 #include <net/if.h>
     37 #include <inttypes.h>
     38 
     39 void *main_queue = NULL;
     40 
     41 #include "srp.h"
     42 #include "dns-msg.h"
     43 #include "ioloop.h"
     44 #include "advertising_proxy_services.h"
     45 #include "route-tracker.h"
     46 #include "state-machine.h"
     47 #include "thread-service.h"
     48 #include "service-tracker.h"
     49 #include "probe-srp.h"
     50 #include "cti-services.h"
     51 #include "adv-ctl-server.h"
     52 
     53 
     54 static void
     55 flushed_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
     56 {
     57     INFO("flushed: cref %p  response %p   err %d.", cref, result, err);
     58     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
     59         exit(1);
     60     }
     61     // We don't need to wait around after flushing.
     62     exit(0);
     63 }
     64 
     65 static void
     66 block_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
     67 {
     68     INFO("blocked: cref %p  response %p   err %d.", cref, result, err);
     69     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
     70         exit(1);
     71     }
     72     // We don't need to wait around after blocking.
     73     exit(0);
     74 }
     75 
     76 static void
     77 unblock_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
     78 {
     79     INFO("unblocked: cref %p  response %p   err %d.", cref, result, err);
     80     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
     81         exit(1);
     82     }
     83     // We don't need to wait around after unblocking.
     84     exit(0);
     85 }
     86 
     87 static void
     88 regenerate_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
     89 {
     90     INFO("regenerated ula: cref %p  response %p   err %d.", cref, result, err);
     91     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
     92         exit(1);
     93     }
     94     // We don't need to wait around after unblocking.
     95     exit(0);
     96 }
     97 
     98 static void
     99 prefix_advertise_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    100 {
    101     INFO("advertise prefix: cref %p  response %p   err %d.", cref, result, err);
    102     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    103         exit(1);
    104     }
    105     // We don't need to wait around after advertising prefix.
    106     exit(0);
    107 }
    108 
    109 static void
    110 add_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    111 {
    112     INFO("add prefix: cref %p  response %p   err %d.", cref, result, err);
    113     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    114         exit(1);
    115     }
    116     // We don't need to wait around after advertising prefix.
    117     exit(0);
    118 }
    119 
    120 static void
    121 remove_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    122 {
    123     INFO("remove prefix: cref %p  response %p   err %d.", cref, result, err);
    124     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    125         exit(1);
    126     }
    127     // We don't need to wait around after advertising prefix.
    128     exit(0);
    129 }
    130 
    131 static void
    132 add_nat64_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    133 {
    134     INFO("add nat64 prefix: cref %p  response %p   err %d.", cref, result, err);
    135     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    136         exit(1);
    137     }
    138     exit(0);
    139 }
    140 
    141 static void
    142 remove_nat64_prefix_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    143 {
    144     INFO("remove nat64 prefix: cref %p  response %p   err %d.", cref, result, err);
    145     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    146         exit(1);
    147     }
    148     exit(0);
    149 }
    150 
    151 static void
    152 stop_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    153 {
    154     INFO("stopped: cref %p  response %p   err %d.", cref, result, err);
    155     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    156         exit(1);
    157     }
    158     // We don't need to wait around after stopping.
    159     exit(0);
    160 }
    161 
    162 static const char *
    163 print_address(advertising_proxy_host_address_t *address, char *addrbuf, size_t addrbuf_size)
    164 {
    165     if (address->rrtype == 0) {
    166         return (char *)address->rdata;
    167     } else if (address->rrtype == dns_rrtype_a && address->rdlen == 4) {
    168         inet_ntop(AF_INET, address->rdata, addrbuf, (socklen_t)addrbuf_size);
    169         return addrbuf;
    170     } else if (address->rrtype == dns_rrtype_aaaa && address->rdlen == 16) {
    171         inet_ntop(AF_INET6, address->rdata, addrbuf, (socklen_t)addrbuf_size);
    172         return addrbuf;
    173     } else {
    174         sprintf(addrbuf, "Family-%d", address->rrtype);
    175         return addrbuf;
    176     }
    177 }
    178 
    179 static void
    180 services_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    181 {
    182     int i;
    183     int64_t lease, hours, minutes, seconds;
    184     advertising_proxy_host_t *host = result;
    185     const char *address = "<no address>";
    186     char *addrbuf = NULL;
    187     size_t addrbuflen;
    188     uint64_t ula;
    189 
    190     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    191         INFO("services: cref %p  response %p   err %d.", cref, result, err);
    192         exit(1);
    193     }
    194     if (result == NULL) {
    195         INFO("services: cref %p  response %p   err %d.", cref, result, err);
    196         exit(0);
    197     }
    198 
    199     if (host->num_instances == 0) {
    200         i = -1;
    201     } else {
    202         i = 0;
    203     }
    204     for (; i < host->num_instances; i++) {
    205         const char *instance_name, *service_type, *reg_type;
    206         char port[6]; // uint16_t as ascii
    207 
    208         if (i == -1 || host->instances[i].instance_name == NULL) {
    209             instance_name = "<no instances>";
    210             service_type = "";
    211             port[0] = 0;
    212             reg_type = "";
    213         } else {
    214             instance_name = host->instances[i].instance_name;
    215             service_type = host->instances[i].service_type;
    216             snprintf(port, sizeof(port), "%u", host->instances[i].port);
    217             reg_type = host->instances[i].reg_type;
    218         }
    219 
    220         if (host->num_addresses > 0) {
    221             addrbuflen = host->num_addresses * (INET6_ADDRSTRLEN + 1);
    222             addrbuf = malloc(addrbuflen);
    223             if (addrbuf == NULL) {
    224                 address = "<no memory for address buffer>";
    225             } else {
    226                 char *ap = addrbuf;
    227                 for (int j = 0; j < host->num_addresses; j++) {
    228                     *ap++ = ' ';
    229                     address = print_address(&host->addresses[j], ap, addrbuflen - (ap - addrbuf));
    230                     size_t len = strlen(address);
    231                     if (address != ap) {
    232                         if (len + ap + 1 > addrbuf + addrbuflen) {
    233                             len = addrbuflen - (ap - addrbuf) - 1;
    234                         }
    235                         memcpy(ap, address, len + 1); // Includes NUL
    236                     }
    237                     ap += len;
    238                 }
    239                 address = addrbuf;
    240             }
    241         }
    242         lease = host->lease_time;
    243         hours = lease / 3600 / 1000;
    244         lease -= hours * 3600 * 1000;
    245         minutes = lease / 60 / 1000;
    246         lease -= minutes * 60 * 1000;
    247         seconds = lease / 1000;
    248         lease -= seconds * 1000;
    249 
    250         // Our implementation of the stable server ID uses the server ULA, so just copy out those 48 bits,
    251         // which are in network byte order.
    252         ula = 0;
    253         for (int j = 1; j < 6; j++) {
    254             ula = ula << 8 | (((uint8_t *)&host->server_id)[j]);
    255         }
    256         printf("\"%s\" \"%s\" %s %s %s %" PRIu64 ":%" PRIu64 ":%" PRIu64 ".%" PRIu64 " \"%s\" \"%s\" %s %" PRIx64 "\n",
    257                host->regname, instance_name, service_type, port,
    258                address == NULL ? "" : address, hours, minutes, seconds, lease, host->hostname,
    259                reg_type, host->removed ? "invalid" : "valid", ula);
    260         if (addrbuf != NULL) {
    261             free(addrbuf);
    262         }
    263     }
    264 }
    265 
    266 static void
    267 ula_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    268 {
    269     INFO("get_ula: cref %p  response %p   err %d.", cref, result, err);
    270     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    271         fprintf(stderr, "ULA get failed: %d\n", err);
    272         exit(1);
    273     }
    274     uint64_t ula = *((uint64_t *)result);
    275     printf("ULA: %" PRIx64 "\n", ula);
    276     exit(0);
    277 }
    278 
    279 static void
    280 disable_srp_replication_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    281 {
    282     INFO("disable_srp_replication: cref %p  response %p   err %d.", cref, result, err);
    283     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    284         exit(1);
    285     }
    286     // We don't need to wait around after SRP replication disabled.
    287     exit(0);
    288 }
    289 
    290 static void
    291 drop_srpl_connection_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    292 {
    293     INFO("drop_srpl_connection: cref %p  response %p   err %d.", cref, result, err);
    294     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    295         exit(1);
    296     }
    297     exit(0);
    298 }
    299 
    300 static void
    301 undrop_srpl_connection_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    302 {
    303     INFO("undrop_srpl_connection: cref %p  response %p   err %d.", cref, result, err);
    304     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    305         exit(1);
    306     }
    307     exit(0);
    308 }
    309 
    310 static void
    311 drop_srpl_advertisement_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    312 {
    313     INFO("drop_srpl_advertisement: cref %p  response %p   err %d.", cref, result, err);
    314     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    315         exit(1);
    316     }
    317     exit(0);
    318 }
    319 
    320 static void
    321 undrop_srpl_advertisement_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    322 {
    323     INFO("undrop_srpl_advertisement: cref %p  response %p   err %d.", cref, result, err);
    324     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    325         exit(1);
    326     }
    327     exit(0);
    328 }
    329 
    330 static void
    331 start_dropping_push_connections_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    332 {
    333     INFO("start_dropping_push_connections: cref %p  response %p   err %d.", cref, result, err);
    334     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    335         exit(1);
    336     }
    337     exit(0);
    338 }
    339 
    340 static void
    341 start_breaking_time_validation_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    342 {
    343     INFO("start_breaking_time_validation: cref %p  response %p   err %d.", cref, result, err);
    344     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    345         exit(1);
    346     }
    347     exit(0);
    348 }
    349 
    350 static void
    351 block_anycast_service_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    352 {
    353     INFO("block_anycast_service: cref %p  response %p   err %d.", cref, result, err);
    354     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    355         exit(1);
    356     }
    357     exit(0);
    358 }
    359 
    360 static void
    361 unblock_anycast_service_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    362 {
    363     INFO("unblock_anycast_service: cref %p  response %p   err %d.", cref, result, err);
    364     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    365         exit(1);
    366     }
    367     exit(0);
    368 }
    369 
    370 static void
    371 start_thread_shutdown_callback(advertising_proxy_conn_ref cref, void *result, advertising_proxy_error_type err)
    372 {
    373     INFO("cref %p  response %p   err %d.", cref, result, err);
    374     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    375         exit(1);
    376     }
    377     exit(0);
    378 }
    379 
    380 typedef struct variable variable_t;
    381 struct variable {
    382     variable_t *next;
    383     const char *name, *value;
    384 };
    385 
    386 static void
    387 set_variable_callback(advertising_proxy_conn_ref cref, void *context, void *result, advertising_proxy_error_type err)
    388 {
    389     variable_t *variable = context;
    390     INFO("set_variable: cref %p  response %p   err %d, variable name %s, value %s.",
    391          cref, result, err, variable->name, variable->value);
    392     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    393         if (variable->next == NULL) {
    394             exit(1);
    395         }
    396     }
    397     if (variable->next == NULL) {
    398         exit(0);
    399     }
    400 }
    401 
    402 static comm_t *tcp_connection;
    403 bool do_tcp_zero_test = false;
    404 bool do_tcp_fin_length = false;
    405 bool do_tcp_fin_payload = false;
    406 
    407 service_tracker_t *tracker;
    408 
    409 // Dummy functions required to use service tracker here
    410 void
    411 adv_ctl_thread_shutdown_status_check(srp_server_t *UNUSED server_state) {
    412 }
    413 
    414 static void
    415 service_done_callback(void *context, cti_status_t status)
    416 {
    417     const char *action = context;
    418     if (status != kCTIStatus_NoError) {
    419         fprintf(stderr, PUB_S_SRP " failed, status %d", action, status);
    420         exit(1);
    421     } else {
    422         fprintf(stderr, PUB_S_SRP " done", action);
    423         exit(0);
    424     }
    425 }
    426 
    427 static void
    428 service_set_changed(bool unicast)
    429 {
    430     thread_service_t *winner = NULL;
    431     for (thread_service_t *service = service_tracker_services_get(tracker); service != NULL; service = service->next) {
    432         if (service->ignore) {
    433             continue;
    434         }
    435         if (unicast && service->service_type == unicast_service) {
    436             if (winner == NULL || in6addr_compare(&service->u.unicast.address, &winner->u.unicast.address) < 0) {
    437                 winner = service;
    438             }
    439         }
    440     }
    441     if (winner == NULL) {
    442         fprintf(stderr, "no services present!");
    443         exit(1);
    444     }
    445     winner->u.unicast.address.s6_addr[15] = 0;
    446     uint8_t service_data[1] = { THREAD_SRP_SERVER_OPTION };
    447     uint8_t server_data[18];
    448     memcpy(server_data, &winner->u.unicast.address, 15);
    449     server_data[15] = 0;
    450     server_data[16] = winner->u.unicast.port[0];
    451     server_data[17] = winner->u.unicast.port[1];
    452     int ret = cti_add_service(NULL, "add", service_done_callback, NULL,
    453                               THREAD_ENTERPRISE_NUMBER, service_data, 1, server_data, 18);
    454     if (ret != kCTIStatus_NoError) {
    455         fprintf(stderr, "add_service failed: %d", ret);
    456         exit(1);
    457     }
    458 }
    459 
    460 static void
    461 unicast_service_set_changed(void *UNUSED context)
    462 {
    463     service_set_changed(true);
    464 }
    465 
    466 static void
    467 start_advertising_winning_unicast_service(void)
    468 {
    469     tracker = service_tracker_create(NULL);
    470     if (tracker != NULL) {
    471         service_tracker_callback_add(tracker, unicast_service_set_changed, NULL, NULL);
    472         service_tracker_start(tracker);
    473     } else {
    474         fprintf(stderr, "unable to allocate tracker");
    475         exit(1);
    476     }
    477 }
    478 
    479 static void
    480 start_removing_unicast_service(void)
    481 {
    482     uint8_t service_data[1] = { THREAD_SRP_SERVER_OPTION };
    483     int ret = cti_remove_service(NULL, "remove", service_done_callback, NULL, THREAD_ENTERPRISE_NUMBER, service_data, 1);
    484     if (ret != kCTIStatus_NoError) {
    485         fprintf(stderr, "remove_service failed: %d", ret);
    486         exit(1);
    487     }
    488 }
    489 
    490 static void
    491 tcp_datagram_callback(comm_t *NONNULL comm, message_t *NONNULL message, void *NULLABLE context)
    492 {
    493     (void)comm;
    494     (void)context;
    495     fprintf(stderr, "tcp datagram received, length %d", message->length);
    496 }
    497 
    498 static void
    499 tcp_connect_callback(comm_t *NONNULL connection, void *NULLABLE context)
    500 {
    501     fprintf(stderr, "tcp connection succeeded...\n");
    502     uint8_t length[2];
    503     struct iovec iov[2];
    504     char databuf[128];
    505     memset(databuf, 0, sizeof(databuf));
    506     memset(iov, 0, sizeof(iov));
    507 
    508     (void)context;
    509 
    510     if (do_tcp_zero_test) {
    511         memset(length, 0, sizeof(length));
    512         iov[0].iov_len = 2;
    513         iov[0].iov_base = length;
    514         ioloop_send_data(connection, NULL, iov, 1);
    515     } else if (do_tcp_fin_length) {
    516         memset(length, 0, sizeof(length));
    517         iov[0].iov_len = 1;
    518         iov[0].iov_base = &length;
    519         ioloop_send_final_data(connection, NULL, iov, 1);
    520     } else if (do_tcp_fin_payload) {
    521         length[0] = 0;
    522         length[1] = 255;
    523         iov[0].iov_len = 2;
    524         iov[0].iov_base = length;
    525         iov[1].iov_len = 128;
    526         iov[1].iov_base = databuf;
    527         ioloop_send_final_data(connection, NULL, iov, 2);
    528     }
    529 }
    530 
    531 static void
    532 tcp_disconnect_callback(comm_t *NONNULL comm, void *NULLABLE context, int error)
    533 {
    534     (void)comm;
    535     (void)context;
    536     (void)error;
    537     fprintf(stderr, "tcp remote close.\n");
    538     exit(0);
    539 }
    540 
    541 static int
    542 start_tcp_test(void)
    543 {
    544     addr_t address;
    545     memset(&address, 0, sizeof(address));
    546     address.sa.sa_family = AF_INET;
    547     address.sin.sin_addr.s_addr = htonl(0x7f000001);
    548     address.sin.sin_port = htons(53);
    549 #ifndef NOT_HAVE_SA_LEN
    550     address.sin.sin_len = sizeof(address.sin);
    551 #endif
    552     tcp_connection = ioloop_connection_create(&address, false, true, false, false, tcp_datagram_callback,
    553                                               tcp_connect_callback, tcp_disconnect_callback, NULL, NULL);
    554     if (tcp_connection == NULL) {
    555         return kDNSSDAdvertisingProxyStatus_NoMemory;
    556     }
    557     return kDNSSDAdvertisingProxyStatus_NoError;
    558 }
    559 
    560 const char *need_name, *need_service;
    561 bool needed_flag;
    562 
    563 advertising_proxy_conn_ref service_sub;
    564 
    565 static void
    566 service_callback(advertising_proxy_conn_ref UNUSED sub, void *UNUSED context, advertising_proxy_error_type error)
    567 {
    568     fprintf(stderr, "service callback: %d\n", error);
    569     exit(0);
    570 }
    571 
    572 static void
    573 start_needing_service(void)
    574 {
    575     advertising_proxy_error_type ret = advertising_proxy_set_service_needed(&service_sub, dispatch_get_main_queue(),
    576                                                                             service_callback, NULL, NULL, need_service,
    577                                                                             needed_flag);
    578     if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
    579         fprintf(stderr, "advertising_proxy_service_create failed: %d\n", ret);
    580         exit(1);
    581     }
    582 }
    583 
    584 advertising_proxy_conn_ref instance_sub;
    585 
    586 static void
    587 instance_callback(advertising_proxy_conn_ref UNUSED sub, void *UNUSED context, advertising_proxy_error_type error)
    588 {
    589     fprintf(stderr, "instance callback: %d\n", error);
    590     exit(0);
    591 }
    592 
    593 static void
    594 start_needing_instance(void)
    595 {
    596     advertising_proxy_error_type ret = advertising_proxy_set_service_needed(&instance_sub, dispatch_get_main_queue(),
    597                                                                             instance_callback, NULL, need_name,
    598                                                                             need_service, needed_flag);
    599     if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
    600         fprintf(stderr, "advertising_proxy_set_service_needed failed: %d\n", ret);
    601         exit(1);
    602     }
    603 }
    604 
    605 const char *browse_service, *resolve_name, *resolve_service;
    606 
    607 advertising_proxy_subscription_t *browse_sub;
    608 
    609 static void
    610 browse_callback(advertising_proxy_subscription_t *UNUSED sub, advertising_proxy_error_type error, uint32_t interface_index,
    611                 bool add, const char *instance_name, const char *service_type, void *UNUSED context)
    612 {
    613     if (error != kDNSSDAdvertisingProxyStatus_NoError) {
    614         fprintf(stderr, "browse_callback: %d\n", error);
    615         exit(1);
    616     }
    617 
    618     fprintf(stderr, "browse: %d %s %s %s\n", interface_index, add ? "add" : "rmv", instance_name, service_type);
    619 }
    620 
    621 static void
    622 start_browsing_service(void)
    623 {
    624     advertising_proxy_error_type ret = advertising_proxy_browse_create(&browse_sub, dispatch_get_main_queue(),
    625                                                                         browse_service, browse_callback, NULL);
    626     if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
    627         fprintf(stderr, "advertising_proxy_browse_create failed: %d\n", ret);
    628         exit(1);
    629     }
    630 }
    631 
    632 advertising_proxy_subscription_t *resolve_sub;
    633 
    634 static void
    635 resolve_callback(advertising_proxy_subscription_t *UNUSED sub, advertising_proxy_error_type error,
    636                  uint32_t interface_index, bool add, const char *fullname, const char *hostname, uint16_t port,
    637                  uint16_t txt_length, const uint8_t *UNUSED txt_record, void *UNUSED context)
    638 {
    639     if (error != kDNSSDAdvertisingProxyStatus_NoError) {
    640         fprintf(stderr, "resolve_create callback: %d\n", error);
    641         exit(1);
    642     }
    643 
    644     fprintf(stderr, "resolved: %d %s %s %s %d %d\n", interface_index, add ? "add" : "rmv",
    645             fullname, hostname, port, txt_length);
    646 }
    647 
    648 static void
    649 start_resolving_service(void)
    650 {
    651     advertising_proxy_error_type ret = advertising_proxy_resolve_create(&resolve_sub, dispatch_get_main_queue(),
    652                                                                         resolve_name, resolve_service, NULL,
    653                                                                         resolve_callback, NULL);
    654     if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
    655         fprintf(stderr, "advertising_proxy_resolve_create failed: %d\n", ret);
    656         exit(1);
    657     }
    658 }
    659 
    660 advertising_proxy_subscription_t *registrar_sub;
    661 
    662 static void
    663 registrar_callback(advertising_proxy_subscription_t *UNUSED sub,
    664                    advertising_proxy_error_type error, void *UNUSED context)
    665 {
    666     if (error != kDNSSDAdvertisingProxyStatus_NoError) {
    667         fprintf(stderr, "registrar_callback: %d\n", error);
    668         exit(1);
    669     }
    670 
    671     INFO("SRP registrar is enabled.");
    672 }
    673 
    674 static void
    675 start_registrar(void)
    676 {
    677     advertising_proxy_error_type ret = advertising_proxy_registrar_create(&registrar_sub, dispatch_get_main_queue(),
    678                                                                           registrar_callback, NULL);
    679     if (ret != kDNSSDAdvertisingProxyStatus_NoError) {
    680         fprintf(stderr, "advertising_proxy_registrar_create failed: %d", ret);
    681         exit(1);
    682     }
    683 }
    684 
    685 
    686 static void
    687 usage(void)
    688 {
    689     fprintf(stderr, "srputil start [if1 .. ifN -]            -- start the SRP MDNS Proxy through launchd\n");
    690     fprintf(stderr, "  tcp-zero                              -- connect to port 53, send a DNS message that's got a zero-length payload\n");
    691     fprintf(stderr, "  tcp-fin-length                        -- connect to port 53, send a DNS message that ends before length is complete\n");
    692     fprintf(stderr, "  tcp-fin-payload                       -- connect to port 53, send a DNS message that ends before payload is complete\n");
    693     fprintf(stderr, "  services                              -- get the list of services currently being advertised\n");
    694     fprintf(stderr, "  block                                 -- block the SRP listener\n");
    695     fprintf(stderr, "  unblock                               -- unblock the SRP listener\n");
    696     fprintf(stderr, "  regenerate-ula                        -- generate a new ULA and restart the network\n");
    697     fprintf(stderr, "  adv-prefix-high                       -- advertise high-priority prefix to thread network\n");
    698     fprintf(stderr, "  adv-prefix                            -- advertise prefix to thread network\n");
    699     fprintf(stderr, "  stop                                  -- stop advertising as SRP server\n");
    700     fprintf(stderr, "  get-ula                               -- fetch the current ULA prefix configured on the SRP server\n");
    701     fprintf(stderr, "  disable-srpl                          -- disable SRP replication\n");
    702     fprintf(stderr, "  add-prefix <ipv6 prefix>              -- add an OMR prefix\n");
    703     fprintf(stderr, "  remove-prefix <ipv6 prefix            -- remove an OMR prefix\n");
    704     fprintf(stderr, "  add-nat64-prefix <nat64 prefix>       -- add an nat64 prefix\n");
    705     fprintf(stderr, "  remove-nat64-prefix <nat64 prefix>    -- remove an nat64 prefix\n");
    706     fprintf(stderr, "  drop-srpl-connection                  -- drop existing srp replication connections\n");
    707     fprintf(stderr, "  undrop-srpl-connection                -- restart srp replication connections that were dropped \n");
    708     fprintf(stderr, "  drop-srpl-advertisement               -- stop advertising srpl service (but keep it around)\n");
    709     fprintf(stderr, "  undrop-srpl-advertisement             -- resume advertising srpl service\n");
    710     fprintf(stderr, "  start-dropping-push                   -- start repeatedly dropping any active push connections after 90 seconds\n");
    711     fprintf(stderr, "  start-breaking-time                   -- start breaking time validation on replicated SRP registrations\n");
    712     fprintf(stderr, "  set [variable] [value]                -- set the value of variable to value (e.g. set min-lease-time 100)\n");
    713     fprintf(stderr, "  block-anycast-service                 -- block advertising anycast service\n");
    714     fprintf(stderr, "  unblock-anycast-service               -- unblock advertising anycast service\n");
    715     fprintf(stderr, "  start-thread-shutdown                 -- start thread network shutdown\n");
    716     fprintf(stderr, "  advertise-winning-unicast-service     -- advertise a unicast service that wins over the current service\n");
    717     fprintf(stderr, "  browse <service>                      -- start an advertising_proxy_browse on the specified service\n");
    718     fprintf(stderr, "  resolve <name> <service>              -- start an advertising_proxy_resolve on the specified service instance\n");
    719     fprintf(stderr, "  need-service <service> <flag>         -- signal to srp-mdns-proxy that we need to discover a service\n");
    720     fprintf(stderr, "  need-instance <name> <service> <flag> -- signal to srp-mdns-proxy that we need to discover a service\n");
    721     fprintf(stderr, "  start-srp                             -- on thread device, enable srp registration\n");
    722 #ifdef NOTYET
    723     fprintf(stderr, "  flush                                 -- flush all entries from the SRP proxy (for testing only)\n");
    724 #endif
    725 }
    726 
    727 bool start_proxy = false;
    728 bool flush_entries = false;
    729 bool list_services = false;
    730 bool block = false;
    731 bool unblock = false;
    732 bool regenerate_ula = false;
    733 bool adv_prefix = false;
    734 bool adv_prefix_high = false;
    735 bool stop_proxy = false;
    736 bool dump_stdin = false;
    737 bool get_ula = false;
    738 bool disable_srp_replication = false;
    739 bool dso_test = false;
    740 bool drop_srpl_connection;
    741 bool undrop_srpl_connection;
    742 bool drop_srpl_advertisement;
    743 bool undrop_srpl_advertisement;
    744 bool start_dropping_push_connections;
    745 bool add_thread_prefix = false;
    746 bool remove_thread_prefix = false;
    747 bool add_nat64_prefix = false;
    748 bool remove_nat64_prefix = false;
    749 bool start_breaking_time_validation = false;
    750 bool test_route_tracker = false;
    751 bool block_anycast_service = false;
    752 bool unblock_anycast_service = false;
    753 bool start_thread_shutdown = false;
    754 bool advertise_winning_unicast_service = false;
    755 bool remove_unicast_service = false;
    756 bool start_srp = false;
    757 uint8_t prefix_buf[16];
    758 #ifdef NOTYET
    759 bool watch = false;
    760 bool get = false;
    761 #endif
    762 variable_t *variables;
    763 int num_permitted_interfaces;
    764 char **permitted_interfaces;
    765 
    766 static void
    767 start_activities(void *context)
    768 {
    769     advertising_proxy_error_type err = kDNSSDAdvertisingProxyStatus_NoError;;
    770     advertising_proxy_conn_ref cref = NULL;
    771     (void)context;
    772 
    773     if (err == kDNSSDAdvertisingProxyStatus_NoError && flush_entries) {
    774         err = advertising_proxy_flush_entries(&cref, main_queue, flushed_callback);
    775     }
    776     if (err == kDNSSDAdvertisingProxyStatus_NoError && (do_tcp_zero_test ||
    777                                                         do_tcp_fin_length || do_tcp_fin_payload)) {
    778         err = start_tcp_test();
    779     }
    780     if (err == kDNSSDAdvertisingProxyStatus_NoError && list_services) {
    781         err = advertising_proxy_get_service_list(&cref, main_queue, services_callback);
    782     }
    783     if (err == kDNSSDAdvertisingProxyStatus_NoError && block) {
    784         err = advertising_proxy_block_service(&cref, main_queue, block_callback);
    785     }
    786     if (err == kDNSSDAdvertisingProxyStatus_NoError && unblock) {
    787         err = advertising_proxy_unblock_service(&cref, main_queue, unblock_callback);
    788     }
    789     if (err == kDNSSDAdvertisingProxyStatus_NoError && regenerate_ula) {
    790         err = advertising_proxy_regenerate_ula(&cref, main_queue, regenerate_callback);
    791     }
    792     if (err == kDNSSDAdvertisingProxyStatus_NoError && adv_prefix) {
    793         err = advertising_proxy_advertise_prefix(&cref, adv_prefix_high, main_queue, prefix_advertise_callback);
    794     }
    795     if (err == kDNSSDAdvertisingProxyStatus_NoError && stop_proxy) {
    796         err = advertising_proxy_stop(&cref, main_queue, stop_callback);
    797     }
    798     if (err == kDNSSDAdvertisingProxyStatus_NoError && get_ula) {
    799         err = advertising_proxy_get_ula(&cref, main_queue, ula_callback);
    800     }
    801     if (err == kDNSSDAdvertisingProxyStatus_NoError && disable_srp_replication) {
    802         err = advertising_proxy_disable_srp_replication(&cref, main_queue, disable_srp_replication_callback);
    803     }
    804     if (err == kDNSSDAdvertisingProxyStatus_NoError && add_thread_prefix) {
    805         err = advertising_proxy_add_prefix(&cref, main_queue, add_prefix_callback, prefix_buf, sizeof(prefix_buf));
    806     }
    807     if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_thread_prefix) {
    808         err = advertising_proxy_remove_prefix(&cref, main_queue, remove_prefix_callback, prefix_buf, sizeof(prefix_buf));
    809     }
    810     if (err == kDNSSDAdvertisingProxyStatus_NoError && add_nat64_prefix) {
    811         err = advertising_proxy_add_nat64_prefix(&cref, main_queue, add_nat64_prefix_callback, prefix_buf, sizeof(prefix_buf));
    812     }
    813     if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_nat64_prefix) {
    814         err = advertising_proxy_remove_nat64_prefix(&cref, main_queue, remove_nat64_prefix_callback, prefix_buf, sizeof(prefix_buf));
    815     }
    816     if (err == kDNSSDAdvertisingProxyStatus_NoError && drop_srpl_connection) {
    817         err = advertising_proxy_drop_srpl_connection(&cref, main_queue, drop_srpl_connection_callback);
    818     }
    819     if (err == kDNSSDAdvertisingProxyStatus_NoError && undrop_srpl_connection) {
    820         err = advertising_proxy_undrop_srpl_connection(&cref, main_queue, undrop_srpl_connection_callback);
    821     }
    822     if (err == kDNSSDAdvertisingProxyStatus_NoError && drop_srpl_advertisement) {
    823         err = advertising_proxy_drop_srpl_advertisement(&cref, main_queue, drop_srpl_advertisement_callback);
    824     }
    825     if (err == kDNSSDAdvertisingProxyStatus_NoError && undrop_srpl_advertisement) {
    826         err = advertising_proxy_undrop_srpl_advertisement(&cref, main_queue, undrop_srpl_advertisement_callback);
    827     }
    828     if (err == kDNSSDAdvertisingProxyStatus_NoError && start_dropping_push_connections) {
    829         err = advertising_proxy_start_dropping_push_connections(&cref, main_queue, start_dropping_push_connections_callback);
    830     }
    831     if (err == kDNSSDAdvertisingProxyStatus_NoError && start_breaking_time_validation) {
    832         err = advertising_proxy_start_breaking_time_validation(&cref, main_queue, start_breaking_time_validation_callback);
    833     }
    834     if (err == kDNSSDAdvertisingProxyStatus_NoError && block_anycast_service) {
    835         err = advertising_proxy_block_anycast_service(&cref, main_queue, block_anycast_service_callback);
    836     }
    837     if (err == kDNSSDAdvertisingProxyStatus_NoError && unblock_anycast_service) {
    838         err = advertising_proxy_unblock_anycast_service(&cref, main_queue, unblock_anycast_service_callback);
    839     }
    840     if (err == kDNSSDAdvertisingProxyStatus_NoError && start_thread_shutdown) {
    841         err = advertising_proxy_start_thread_shutdown(&cref, main_queue, start_thread_shutdown_callback);
    842     }
    843     if (err == kDNSSDAdvertisingProxyStatus_NoError && test_route_tracker) {
    844         route_tracker_test_start(1000);
    845     }
    846     if (err == kDNSSDAdvertisingProxyStatus_NoError && advertise_winning_unicast_service) {
    847         start_advertising_winning_unicast_service();
    848     }
    849     if (err == kDNSSDAdvertisingProxyStatus_NoError && remove_unicast_service) {
    850         start_removing_unicast_service();
    851     }
    852     if (err == kDNSSDAdvertisingProxyStatus_NoError && advertise_winning_unicast_service) {
    853         start_advertising_winning_unicast_service();
    854     }
    855     if (err == kDNSSDAdvertisingProxyStatus_NoError && browse_service != NULL) {
    856         start_browsing_service();
    857     }
    858     if (err == kDNSSDAdvertisingProxyStatus_NoError && resolve_service != NULL) {
    859         start_resolving_service();
    860     }
    861     if (err == kDNSSDAdvertisingProxyStatus_NoError && need_service != NULL && need_name == NULL) {
    862         start_needing_service();
    863     }
    864     if (err == kDNSSDAdvertisingProxyStatus_NoError && need_service != NULL && need_name != NULL) {
    865         start_needing_instance();
    866     }
    867     if (err == kDNSSDAdvertisingProxyStatus_NoError && start_srp) {
    868         start_registrar();
    869     }
    870     if (err == kDNSSDAdvertisingProxyStatus_NoError && variables != NULL) {
    871         for (variable_t *variable = variables; variable != NULL; variable = variable->next) {
    872             err = advertising_proxy_set_variable(&cref, main_queue, set_variable_callback, variable, variable->name, variable->value);
    873             if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    874                 break;
    875             }
    876         }
    877     }
    878     if (err != kDNSSDAdvertisingProxyStatus_NoError) {
    879         exit(1);
    880     }
    881 }
    882 
    883 static void
    884 dump_packet(void)
    885 {
    886     ssize_t len;
    887     dns_message_t *message = NULL;
    888     dns_wire_t wire;
    889 
    890     len = read(0, &wire, sizeof(wire));
    891     if (len < 0) {
    892         ERROR("stdin: %s", strerror(errno));
    893         return;
    894     }
    895     if (len < DNS_HEADER_SIZE) {
    896         ERROR("stdin: too short: %zd bytes", len);
    897         return;
    898     }
    899     if (!dns_wire_parse(&message, &wire, (unsigned)len, true)) {
    900         fprintf(stderr, "DNS message parse failed\n");
    901         return;
    902     }
    903 }
    904 
    905 int
    906 main(int argc, char **argv)
    907 {
    908     int i;
    909     bool something = false;
    910     bool log_stderr = false;
    911 
    912     for (i = 1; i < argc; i++) {
    913         if (!strcmp(argv[i], "start")) {
    914 			start_proxy = true;
    915             something = true;
    916             int j;
    917             for (j = i + 1; j < argc; j++) {
    918                 if (!strcmp(argv[j], "-")) {
    919                     break;
    920                 }
    921             }
    922             num_permitted_interfaces = j - i - 1;
    923             permitted_interfaces = argv + i + 1;
    924             i = j;
    925         } else if (!strcmp(argv[i], "tcp-zero")) {
    926             do_tcp_zero_test = true;
    927             something = true;
    928         } else if (!strcmp(argv[i], "tcp-fin-length")) {
    929             do_tcp_fin_length = true;
    930             something = true;
    931         } else if (!strcmp(argv[i], "tcp-fin-payload")) {
    932             do_tcp_fin_payload = true;
    933             something = true;
    934 		} else if (!strcmp(argv[i], "flush")) {
    935             flush_entries = true;
    936             something = true;
    937 		} else if (!strcmp(argv[i], "services")) {
    938             list_services = true;
    939             something = true;
    940 		} else if (!strcmp(argv[i], "block")) {
    941             block = true;
    942             something = true;
    943 		} else if (!strcmp(argv[i], "unblock")) {
    944             unblock = true;
    945             something = true;
    946 		} else if (!strcmp(argv[i], "regenerate-ula")) {
    947             regenerate_ula = true;
    948             something = true;
    949         } else if (!strcmp(argv[i], "adv-prefix")) {
    950             adv_prefix = true;
    951             something = true;
    952         } else if (!strcmp(argv[i], "adv-prefix-high")) {
    953             adv_prefix = true;
    954             adv_prefix_high = true;
    955             something = true;
    956         } else if (!strcmp(argv[i], "stop")) {
    957             stop_proxy = true;
    958             something = true;
    959         } else if (!strcmp(argv[i], "dump")) {
    960             dump_packet();
    961             exit(0);
    962         } else if (!strcmp(argv[i], "get-ula")) {
    963             get_ula = true;
    964             something = true;
    965         } else if (!strcmp(argv[i], "disable-srpl")) {
    966             disable_srp_replication = true;
    967             something = true;
    968         } else if (!strcmp(argv[i], "add-prefix")) {
    969             if (i + 1 >= argc) {
    970                 usage();
    971             }
    972             if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) {
    973                 fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]);
    974                 usage();
    975             } else {
    976                 add_thread_prefix = true;
    977                 something = true;
    978                 i++;
    979             }
    980         } else if (!strcmp(argv[i], "remove-prefix")) {
    981             if (i + 1 >= argc) {
    982                 usage();
    983             }
    984             if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) {
    985                 fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]);
    986                 usage();
    987             } else {
    988                 remove_thread_prefix = true;
    989                 something = true;
    990                 i++;
    991             }
    992         } else if (!strcmp(argv[i], "add-nat64-prefix")) {
    993             if (i + 1 >= argc) {
    994                 usage();
    995             }
    996             if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) {
    997                 fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]);
    998                 usage();
    999             } else {
   1000                 add_nat64_prefix = true;
   1001                 something = true;
   1002                 i++;
   1003             }
   1004         } else if (!strcmp(argv[i], "remove-nat64-prefix")) {
   1005             if (i + 1 >= argc) {
   1006                 usage();
   1007             }
   1008             if (inet_pton(AF_INET6, argv[i + 1], prefix_buf) < 1) {
   1009                 fprintf(stderr, "Invalid ipv6 prefix %s.\n", argv[i + 1]);
   1010                 usage();
   1011             } else {
   1012                 remove_nat64_prefix = true;
   1013                 something = true;
   1014                 i++;
   1015             }
   1016         } else if (!strcmp(argv[i], "browse")) {
   1017             if (i + 1 >= argc) {
   1018                 usage();
   1019             }
   1020             browse_service = argv[i + 1];
   1021             i++;
   1022             something = true;
   1023         } else if (!strcmp(argv[i], "resolve")) {
   1024             if (i + 2 >= argc) {
   1025                 usage();
   1026             }
   1027             resolve_name = argv[i + 1];
   1028             resolve_service = argv[i + 2];
   1029             i += 2;
   1030             something = true;
   1031         } else if (!strcmp(argv[i], "need-service")) {
   1032             if (i + 2 >= argc) {
   1033                 usage();
   1034             }
   1035             need_service = argv[i + 1];
   1036             if (!strcmp(argv[i + 2], "true")) {
   1037                 needed_flag = true;
   1038             } else {
   1039                 needed_flag = false;
   1040             }
   1041             i += 2;
   1042             something = true;
   1043         } else if (!strcmp(argv[i], "need-instance")) {
   1044             if (i + 3 >= argc) {
   1045                 usage();
   1046             }
   1047             need_name = argv[i + 1];
   1048             need_service = argv[i + 2];
   1049             if (!strcmp(argv[i + 3], "true")) {
   1050                 needed_flag = true;
   1051             } else {
   1052                 needed_flag = false;
   1053             }
   1054             i += 3;
   1055             something = true;
   1056         } else if (!strcmp(argv[i], "start-srp")) {
   1057             start_srp = true;
   1058             something = true;
   1059         } else if (!strcmp(argv[i], "drop-srpl-connection")) {
   1060             drop_srpl_connection = true;
   1061             something = true;
   1062         } else if (!strcmp(argv[i], "undrop-srpl-connection")) {
   1063             undrop_srpl_connection = true;
   1064             something = true;
   1065         } else if (!strcmp(argv[i], "drop-srpl-advertisement")) {
   1066             drop_srpl_advertisement = true;
   1067             something = true;
   1068         } else if (!strcmp(argv[i], "undrop-srpl-advertisement")) {
   1069             undrop_srpl_advertisement = true;
   1070             something = true;
   1071         } else if (!strcmp(argv[i], "start-dropping-push")) {
   1072             start_dropping_push_connections = true;
   1073             something = true;
   1074         } else if (!strcmp(argv[i], "start-breaking-time")) {
   1075             start_breaking_time_validation = true;
   1076             something = true;
   1077         } else if (!strcmp(argv[i], "block-anycast-service")) {
   1078             block_anycast_service = true;
   1079             something = true;
   1080         } else if (!strcmp(argv[i], "unblock-anycast-service")) {
   1081             unblock_anycast_service = true;
   1082             something = true;
   1083         } else if (!strcmp(argv[i], "test-route-tracker")) {
   1084             test_route_tracker = true;
   1085             something = true;
   1086         } else if (!strcmp(argv[i], "start-thread-shutdown")) {
   1087             start_thread_shutdown = true;
   1088             something = true;
   1089         } else if (!strcmp(argv[i], "advertise-winning-unicast-service")) {
   1090             advertise_winning_unicast_service = true;
   1091             something = true;
   1092         } else if (!strcmp(argv[i], "remove-unicast-service")) {
   1093             remove_unicast_service = true;
   1094             something = true;
   1095         } else if (!strcmp(argv[i], "set")) {
   1096             if (i + 2 >= argc) {
   1097                 usage();
   1098             }
   1099             variable_t *variable = calloc(1, sizeof(*variable));
   1100             if (variable == NULL) {
   1101                 fprintf(stderr, "no memory for variable %s", argv[i + 1]);
   1102                 exit(1);
   1103             }
   1104             variable->name = argv[i + 1];
   1105             variable->value = argv[i + 2];
   1106             variable->next = variables;
   1107             variables = variable;
   1108             i += 2;
   1109             something = true;
   1110 #ifdef NOTYET
   1111 		} else if (!strcmp(argv[i], "watch")) {
   1112             fprintf(stderr, "Watching not implemented yet.\n");
   1113             exit(1);
   1114 		} else if (!strcmp(argv[i], "get")) {
   1115             fprintf(stderr, "Getting not implemented yet.\n");
   1116             exit(1);
   1117 #endif
   1118         } else if (!strcmp(argv[i], "--debug")) {
   1119             OPENLOG("srputil", true);
   1120             log_stderr = true;
   1121         } else {
   1122             usage();
   1123             exit(1);
   1124         }
   1125     }
   1126 
   1127     if (!something) {
   1128         usage();
   1129         exit(1);
   1130     }
   1131 
   1132     if (log_stderr == false) {
   1133         OPENLOG("srputil", log_stderr);
   1134     }
   1135 
   1136     ioloop_init();
   1137     // Start the queue, //then// do the work
   1138     ioloop_run_async(start_activities, NULL);
   1139     ioloop();
   1140 }
   1141 
   1142 // Local Variables:
   1143 // mode: C
   1144 // tab-width: 4
   1145 // c-file-style: "bsd"
   1146 // c-basic-offset: 4
   1147 // fill-column: 108
   1148 // indent-tabs-mode: nil
   1149 // End:
   1150