Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* srp-ioloop.c
      2  *
      3  * Copyright (c) 2019-2024 Apple Inc. All rights reserved.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     https://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  * srp host API implementation for Posix using ioloop primitives.
     18  */
     19 
     20 #include <stdio.h>
     21 #include <arpa/inet.h>
     22 #include <string.h>
     23 #include <stdlib.h>
     24 #include <unistd.h>
     25 #include <dns_sd.h>
     26 #include <errno.h>
     27 #include <fcntl.h>
     28 
     29 #include "srp.h"
     30 #include "srp-api.h"
     31 #include "dns-msg.h"
     32 #include "srp-crypto.h"
     33 #include "ioloop.h"
     34 #include "dso-utils.h"
     35 #include "dso.h"
     36 
     37 #include "cti-services.h"
     38 
     39 static int lease_time = 0;
     40 static bool random_leases = false;
     41 static bool delete_registrations = false;
     42 static bool use_thread_services = false;
     43 static bool change_txt_record = false;
     44 static bool new_ip_dup = false;
     45 static bool dup_instance_name = false;
     46 static bool random_txt_record = false;
     47 static bool remove_added_service = false;
     48 static bool let_added_service_expire = false;
     49 static bool expecting_second_add = true;
     50 static int num_clients = 1;
     51 static int bogusify_signatures = false;
     52 static int bogus_remove = false;
     53 static int push_hardwired = false;
     54 static int push_query = false;
     55 static int push_unsubscribe = false;
     56 static bool push_subscribe_sent = false;
     57 static uint16_t dns_push_subscribe_xid;
     58 static int push_send_bogus_keepalive = false;
     59 static int push_exhaust = false;
     60 static bool test_subtypes = false;
     61 static bool test_renew_subtypes = false;
     62 static bool test_diff_subtypes = false;
     63 static uint8_t first_bogus_address[] = { 198, 51, 100, 1 }; // RFC 5737 documentation prefix TEST-NET-2
     64 static uint8_t second_bogus_address[] = { 203, 0, 113, 1 }; // RFC 5737 documentation prefix TEST-NET-3
     65 static bool host_only = false;
     66 extern bool zero_addresses;
     67 static bool expire_instance = false;
     68 static bool test_bad_sig_time = false;
     69 static bool do_srp = true;
     70 
     71 const uint64_t thread_enterprise_number = 52627;
     72 
     73 cti_connection_t thread_service_context;
     74 
     75 static const char *interface_name = NULL;
     76 static wakeup_t *wait_for_remote_disconnect = NULL;
     77 static dso_state_t *disconnect_expected = NULL;
     78 os_log_t global_os_log;
     79 
     80 #define SRP_IO_CONTEXT_MAGIC 0xFEEDFACEFADEBEEFULL  // BEES!   Everybody gets BEES!
     81 typedef struct io_context {
     82     uint64_t magic_cookie1;
     83     wakeup_t *wakeup;
     84     void *NONNULL srp_context;
     85     comm_t *NULLABLE connection;
     86     srp_wakeup_callback_t wakeup_callback;
     87     srp_datagram_callback_t datagram_callback;
     88     bool deactivated, closed;
     89     uint64_t magic_cookie2;
     90 } io_context_t;
     91 wakeup_t *remove_wakeup;
     92 
     93 typedef struct srp_client {
     94     DNSServiceRef sdref;
     95     int index;
     96     wakeup_t *wakeup;
     97     char *name;
     98     bool updated_txt_record;
     99 } srp_client_t;
    100 
    101 static void start_push_query(void);
    102 static void send_push_unsubscribe(void);
    103 static void send_push_subscribe(void);
    104 
    105 static int
    106 validate_io_context(io_context_t **dest, void *src)
    107 {
    108     io_context_t *context = src;
    109     if (context->magic_cookie1 == SRP_IO_CONTEXT_MAGIC &&
    110         context->magic_cookie2 == SRP_IO_CONTEXT_MAGIC)
    111    {
    112         *dest = context;
    113         return kDNSServiceErr_NoError;
    114     }
    115     return kDNSServiceErr_BadState;
    116 }
    117 
    118 static void
    119 datagram_callback(comm_t *connection, message_t *message, void *context)
    120 {
    121     (void)connection;
    122     io_context_t *io_context = context;
    123     if (!io_context->deactivated) {
    124         io_context->datagram_callback(io_context->srp_context,
    125                                       &message->wire, message->length);
    126     }
    127 }
    128 
    129 static void
    130 wakeup_callback(void *context)
    131 {
    132     io_context_t *io_context;
    133     if (validate_io_context(&io_context, context) == kDNSServiceErr_NoError) {
    134         INFO("wakeup on context %p srp_context %p", io_context, io_context->srp_context);
    135         if (!io_context->deactivated) {
    136             io_context->wakeup_callback(io_context->srp_context);
    137         }
    138     } else {
    139         INFO("wakeup with invalid context: %p", context);
    140     }
    141 }
    142 
    143 int
    144 srp_deactivate_udp_context(void *host_context, void *in_context)
    145 {
    146     io_context_t *io_context;
    147     int err;
    148     (void)host_context;
    149 
    150     err = validate_io_context(&io_context, in_context);
    151     if (err == kDNSServiceErr_NoError) {
    152         if (io_context->wakeup != NULL) {
    153             ioloop_cancel_wake_event(io_context->wakeup);
    154             ioloop_wakeup_release(io_context->wakeup);
    155         }
    156         // Deactivate can be called with a connection still active; in this case, we need to wait for the
    157         // cancel event before freeing the structure. Otherwise, we can free it immediately.
    158         if (io_context->connection != NULL) {
    159             ioloop_comm_cancel(io_context->connection);
    160             io_context->deactivated = true;
    161             io_context->closed = true;
    162         } else {
    163             free(io_context);
    164         }
    165     }
    166     return err;
    167 }
    168 
    169 int
    170 srp_disconnect_udp(void *context)
    171 {
    172     io_context_t *io_context;
    173     int err;
    174 
    175     err = validate_io_context(&io_context, context);
    176     if (err == kDNSServiceErr_NoError) {
    177         if (io_context->connection) {
    178             ioloop_comm_cancel(io_context->connection);
    179         }
    180         io_context->closed = true;
    181     }
    182     return err;
    183 }
    184 
    185 static void
    186 srp_udp_context_canceled(comm_t *UNUSED NONNULL comm, void *NULLABLE context, int UNUSED error)
    187 {
    188     io_context_t *io_context = context;
    189 
    190     if (io_context->connection) {
    191         ioloop_comm_release(io_context->connection);
    192         io_context->connection = NULL;
    193     }
    194     if (io_context->deactivated) {
    195         free(io_context);
    196     }
    197 }
    198 
    199 int
    200 srp_connect_udp(void *context, const uint8_t *port, uint16_t address_type, const uint8_t *address, uint16_t addrlen)
    201 {
    202     io_context_t *io_context;
    203     addr_t remote;
    204     int err;
    205 
    206     err = validate_io_context(&io_context, context);
    207     if (err == kDNSServiceErr_NoError) {
    208         if (io_context->connection) {
    209             ERROR("srp_connect_udp called with non-null I/O context.");
    210             return kDNSServiceErr_Invalid;
    211         }
    212 
    213         if (address_type == dns_rrtype_a) {
    214             if (addrlen != 4) {
    215                 return kDNSServiceErr_Invalid;
    216             }
    217             remote.sa.sa_family = AF_INET;
    218             memcpy(&remote.sin.sin_addr, address, addrlen);
    219 #ifndef NOT_HAVE_SA_LEN
    220             remote.sa.sa_len = sizeof remote.sin;
    221 #endif
    222             memcpy(&remote.sin.sin_port, port, 2);
    223         } else {
    224             if (addrlen != 16) {
    225                 return kDNSServiceErr_Invalid;
    226             }
    227             remote.sa.sa_family = AF_INET6;
    228             memcpy(&remote.sin6.sin6_addr, address, addrlen);
    229 #ifndef NOT_HAVE_SA_LEN
    230             remote.sa.sa_len = sizeof remote.sin;
    231 #endif
    232             memcpy(&remote.sin6.sin6_port, port, 2);
    233         }
    234 
    235         io_context->connection = ioloop_connection_create(&remote, false, false, false, true, datagram_callback,
    236                                                           NULL, srp_udp_context_canceled, NULL, io_context);
    237         if (io_context->connection == NULL) {
    238             return kDNSServiceErr_NoMemory;
    239         }
    240     }
    241     return err;
    242 }
    243 
    244 int
    245 srp_make_udp_context(void *host_context, void **p_context, srp_datagram_callback_t callback, void *context)
    246 {
    247     (void)host_context;
    248 
    249     io_context_t *io_context = calloc(1, sizeof *io_context);
    250     if (io_context == NULL) {
    251         return kDNSServiceErr_NoMemory;
    252     }
    253     io_context->magic_cookie1 = io_context->magic_cookie2 = SRP_IO_CONTEXT_MAGIC;
    254     io_context->datagram_callback = callback;
    255     io_context->srp_context = context;
    256 
    257     io_context->wakeup = ioloop_wakeup_create();
    258     if (io_context->wakeup == NULL) {
    259         free(io_context);
    260         return kDNSServiceErr_NoMemory;
    261     }
    262 
    263     *p_context = io_context;
    264     return kDNSServiceErr_NoError;
    265 }
    266 
    267 int
    268 srp_set_wakeup(void *host_context, void *context, int milliseconds, srp_wakeup_callback_t callback)
    269 {
    270     int err;
    271     io_context_t *io_context;
    272     (void)host_context;
    273 
    274     err = validate_io_context(&io_context, context);
    275     if (err == kDNSServiceErr_NoError) {
    276         io_context->wakeup_callback = callback;
    277         INFO("srp_set_wakeup on context %p, srp_context %p", io_context, io_context->srp_context);
    278         ioloop_add_wake_event(io_context->wakeup, io_context, wakeup_callback, NULL, milliseconds);
    279     }
    280     return err;
    281 }
    282 
    283 int
    284 srp_cancel_wakeup(void *host_context, void *context)
    285 {
    286     int err;
    287     io_context_t *io_context;
    288     (void)host_context;
    289 
    290     err = validate_io_context(&io_context, context);
    291     if (err == kDNSServiceErr_NoError) {
    292         ioloop_cancel_wake_event(io_context->wakeup);
    293     }
    294     return err;
    295 }
    296 
    297 int
    298 srp_send_datagram(void *host_context, void *context, void *message, size_t message_length)
    299 {
    300     int err;
    301     struct iovec iov;
    302     io_context_t *io_context;
    303     (void)host_context;
    304 
    305     memset(&iov, 0, sizeof iov);
    306     iov.iov_base = message;
    307     iov.iov_len = message_length;
    308 
    309     if (bogusify_signatures) {
    310         ((uint8_t *)message)[message_length - 10] = ~(((uint8_t *)message)[message_length - 10]);
    311     }
    312 
    313     err = validate_io_context(&io_context, context);
    314     if (err == kDNSServiceErr_NoError) {
    315         if (io_context->connection == NULL) {
    316             return kDNSServiceErr_DefunctConnection;
    317         }
    318         if (!ioloop_send_message(io_context->connection, message, &iov, 1)) {
    319             return kDNSServiceErr_Unknown;
    320         }
    321     }
    322     return err;
    323 }
    324 
    325 uint32_t
    326 srp_timenow(void)
    327 {
    328     time_t now = time(NULL);
    329     if (test_bad_sig_time) {
    330         return (uint32_t)(now - 10000);
    331     }
    332     return (uint32_t)now;
    333 }
    334 
    335 static void
    336 interface_callback(srp_server_t *UNUSED NULLABLE server_state, void *context, const char *NONNULL name,
    337                    const addr_t *NONNULL address, const addr_t *NONNULL netmask,
    338                    uint32_t flags, enum interface_address_change event_type)
    339 {
    340     bool drop = false;
    341     uint8_t *rdata;
    342     uint16_t rdlen;
    343     uint16_t rrtype;
    344     cti_service_vec_t *cti_services = context;
    345 
    346     (void)netmask;
    347     (void)index;
    348     (void)event_type;
    349 
    350     if (address->sa.sa_family == AF_INET) {
    351         rrtype = dns_rrtype_a;
    352         rdata = (uint8_t *)&address->sin.sin_addr;
    353         rdlen = 4;
    354 
    355         // Should use IN_LINKLOCAL and IN_LOOPBACK macros here, but for some reason they are not present on
    356         // OpenWRT.
    357         if (rdata[0] == 127) {
    358             drop = true;
    359         } else if (rdata[0] == 169 && rdata[1] == 254) {
    360             drop = true;
    361         }
    362     } else if (address->sa.sa_family == AF_INET6) {
    363         rrtype = dns_rrtype_aaaa;
    364         rdata = (uint8_t *)&address->sin6.sin6_addr;
    365         rdlen = 16;
    366         if (IN6_IS_ADDR_LOOPBACK(&address->sin6.sin6_addr)) {
    367             drop = true;
    368         } else if (IN6_IS_ADDR_LINKLOCAL(&address->sin6.sin6_addr)) {
    369             drop = true;
    370         }
    371     } else {
    372         return;
    373     }
    374     if (drop) {
    375         if (address->sa.sa_family == AF_INET) {
    376             IPv4_ADDR_GEN_SRP(rdata, ipv4_rdata_buf);
    377             DEBUG("interface_callback: ignoring " PUB_S_SRP " " PRI_IPv4_ADDR_SRP, name,
    378                   IPv4_ADDR_PARAM_SRP(rdata, ipv4_rdata_buf));
    379         } else if (address->sa.sa_family == AF_INET6) {
    380             SEGMENTED_IPv6_ADDR_GEN_SRP(rdata, ipv6_rdata_buf);
    381             DEBUG("interface_callback: ignoring " PUB_S_SRP " " PRI_SEGMENTED_IPv6_ADDR_SRP, name,
    382                   SEGMENTED_IPv6_ADDR_PARAM_SRP(rdata, ipv6_rdata_buf));
    383         } else {
    384             INFO("ignoring with non-v4/v6 address" PUB_S_SRP, name);
    385         }
    386         return;
    387     }
    388 
    389     if (address->sa.sa_family == AF_INET) {
    390         IPv4_ADDR_GEN_SRP(rdata, ipv4_rdata_buf);
    391         DEBUG("interface_callback: " PUB_S_SRP " " PRI_IPv4_ADDR_SRP " %x", name,
    392               IPv4_ADDR_PARAM_SRP(rdata, ipv4_rdata_buf), flags);
    393     } else if (address->sa.sa_family == AF_INET6) {
    394         SEGMENTED_IPv6_ADDR_GEN_SRP(rdata, ipv6_rdata_buf);
    395         DEBUG("interface_callback: " PUB_S_SRP " " PRI_SEGMENTED_IPv6_ADDR_SRP " %x", name,
    396               SEGMENTED_IPv6_ADDR_PARAM_SRP(rdata, ipv6_rdata_buf), flags);
    397     } else {
    398         DEBUG("interface_callback: " PUB_S_SRP "<none IPv4/IPv6 address> %x", name, flags);
    399     }
    400 
    401     // This is a workaround for a bug in the utun0 code, where packets sent to the IP address of the local
    402     // thread interface are dropped and do not reach the SRP server. To address this, if we find a service
    403     // that is on a local IPv6 address, we replace the address with ::1.
    404     if (cti_services != NULL && rrtype == dns_rrtype_aaaa) {
    405         size_t i;
    406         for (i = 0; i < cti_services->num; i++) {
    407             cti_service_t *cti_service = cti_services->services[i];
    408             // Look for SRP service advertisements only.
    409             if (IS_SRP_SERVICE(cti_service)) {
    410                 // Local IP address?
    411                 if (!memcmp(cti_service->server, rdata, 16)) {
    412                     // ::
    413                     memset(cti_service->server, 0, 15);
    414                     // 1
    415                     cti_service->server[15] = 1;
    416                 }
    417             }
    418         }
    419     }
    420 
    421     srp_add_interface_address(rrtype, rdata, rdlen);
    422 }
    423 
    424 static void
    425 remove_callback(void *context)
    426 {
    427     srp_client_t *client = context;
    428     if (bogus_remove) {
    429         srp_set_hostname("bogus-api-test", NULL);
    430     }
    431     srp_deregister(client);
    432 }
    433 
    434 static void
    435 second_register_callback(DNSServiceRef sdref, DNSServiceFlags flags, DNSServiceErrorType errorCode,
    436                          const char *name, const char *regtype, const char *domain, void *context)
    437 {
    438     srp_client_t *client = context;
    439 
    440     (void)regtype;
    441     (void)flags;
    442     (void)name;
    443     (void)regtype;
    444     (void)domain;
    445     INFO("Second Register Reply for %s: %d", client->name, errorCode);
    446 
    447     if (errorCode == kDNSServiceErr_NoError) {
    448         if (expecting_second_add) {
    449             expecting_second_add = false;
    450             if (remove_added_service) {
    451                 srp_deregister_instance(sdref);
    452                 srp_network_state_stable(NULL);
    453             } else if (let_added_service_expire) {
    454                 DNSServiceRefDeallocate(sdref);
    455             }
    456         } else {
    457             // Test succeeded
    458             exit(0);
    459         }
    460     } else {
    461         // Test failed
    462         exit(1);
    463     }
    464 }
    465 
    466 
    467 static void
    468 register_callback(DNSServiceRef sdref, DNSServiceFlags flags, DNSServiceErrorType errorCode,
    469                   const char *name, const char *regtype, const char *domain, void *context)
    470 {
    471     srp_client_t *client = context;
    472 
    473     (void)regtype;
    474     (void)flags;
    475     (void)name;
    476     (void)regtype;
    477     (void)domain;
    478     INFO("Register Reply for %s: %d", client->name, errorCode);
    479 
    480     if (errorCode == kDNSServiceErr_NoError) {
    481         if ((change_txt_record  && !client->updated_txt_record) || expire_instance) {
    482             TXTRecordRef txt;
    483             const void *txt_data = NULL;
    484             uint16_t txt_len = 0;
    485             char txt_buf[128];
    486 
    487             TXTRecordCreate(&txt, sizeof(txt_buf), txt_buf);
    488             TXTRecordSetValue(&txt, "foo", 1, "1");
    489             TXTRecordSetValue(&txt, "bar", 3, "1.1");
    490             txt_data = TXTRecordGetBytesPtr(&txt);
    491             txt_len = TXTRecordGetLength(&txt);
    492 
    493             if (expire_instance) {
    494                 char hnbuf[128];
    495                 ioloop_strcpy(hnbuf, name, sizeof(hnbuf));
    496 
    497                 // silently let the first expire. This is just going to leak the data, but since this is a one-shot test
    498                 // that's not an actual problem.
    499                 DNSServiceRefDeallocate(sdref);
    500 
    501                 DNSServiceRef nsdref;
    502                 expecting_second_add = true;
    503                 // register a second instance
    504                 int err = DNSServiceRegister(&nsdref, 0, 0, hnbuf, "_second._tcp,foo", 0, 0, htons(1234),
    505                                              txt_len, txt_data, second_register_callback, client);
    506                 if (err != kDNSServiceErr_NoError) {
    507                     ERROR("second DNSServiceRegister failed: %d", err);
    508                     exit(1);
    509                 }
    510             } else {
    511                 (void)DNSServiceUpdateRecord(sdref, NULL, 0, txt_len, txt_data, 0);
    512                 client->updated_txt_record = true;
    513             }
    514             srp_network_state_stable(NULL);
    515             return;
    516         }
    517 
    518         if (delete_registrations) {
    519             client->wakeup = ioloop_wakeup_create();
    520             if (client->wakeup == NULL) {
    521                 ERROR("Unable to allocate a wakeup for %s.", client->name);
    522                 exit(1);
    523             }
    524 
    525             // Do a remove in five seconds.
    526             ioloop_add_wake_event(client->wakeup, client, remove_callback, NULL, 5000);
    527             return;
    528         }
    529 
    530         if (test_renew_subtypes) {
    531             int err = srp_update_service_type(sdref, "_ipps._tcp,othersub", second_register_callback, client);
    532             if (err != kDNSServiceErr_NoError) {
    533                 ERROR("srp_update_service_type failed: %d", err);
    534                 exit(1);
    535             }
    536             srp_network_state_stable(NULL);
    537             return;
    538         }
    539     }
    540 
    541     // We get this with the duplicate instance name. In this case, we change the host IP address. This validates
    542     // the bit of code in srp-mdns-proxy that removes the added address when the update fails--when we look up the
    543     // registered address, we should see only the second bogus address, not the first.
    544     if (errorCode == kDNSServiceErr_NameConflict) {
    545         char nnbuf[128];
    546         char rtbuf[128];
    547         srp_delete_interface_address(dns_rrtype_a, first_bogus_address, sizeof(first_bogus_address));
    548         srp_add_interface_address(dns_rrtype_a, second_bogus_address, sizeof(second_bogus_address));
    549         ioloop_strcpy(nnbuf, name, sizeof(nnbuf));
    550         ioloop_strcpy(rtbuf, regtype, sizeof(rtbuf));
    551         nnbuf[0] = 'a';
    552         INFO("changing service instance name from " PRI_S_SRP " to " PRI_S_SRP " type " PRI_S_SRP, name, nnbuf, rtbuf);
    553         DNSServiceRefDeallocate(sdref);
    554         int err = DNSServiceRegister(&sdref, kDNSServiceFlagsNoAutoRename, 0, nnbuf, rtbuf, 0, 0, htons(1234),
    555                                      0, NULL, register_callback, client);
    556         if (err != kDNSServiceErr_NoError) {
    557             ERROR("DNSServiceRegister rename failed: %d", err);
    558             exit(1);
    559         }
    560         srp_network_state_stable(NULL);
    561     }
    562 }
    563 
    564 comm_t *dso_connection;
    565 typedef struct connection_list connection_list_t;
    566 struct connection_list {
    567     connection_list_t *next;
    568     comm_t *connection;
    569 };
    570 connection_list_t *dso_connection_list;
    571 uint16_t subscribe_xid;
    572 uint16_t keepalive_xid;
    573 
    574 static void
    575 send_push_unsubscribe(void)
    576 {
    577     struct iovec iov;
    578     INFO("unsubscribe");
    579     dns_wire_t dns_message;
    580     uint8_t *buffer = (uint8_t *)&dns_message;
    581     dns_towire_state_t towire;
    582     dso_message_t message;
    583     if (!push_send_bogus_keepalive) {
    584         INFO("unsubscribe");
    585         dso_make_message(&message, buffer, sizeof(dns_message), dso_connection->dso, true, false, 0, 0, NULL);
    586         memset(&towire, 0, sizeof(towire));
    587         towire.p = &buffer[DNS_HEADER_SIZE];
    588         towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
    589         towire.message = &dns_message;
    590         dns_u16_to_wire(&towire, kDSOType_DNSPushUnsubscribe);
    591         dns_rdlength_begin(&towire);
    592         dns_u16_to_wire(&towire, dns_push_subscribe_xid);
    593         dns_rdlength_end(&towire);
    594 
    595         memset(&iov, 0, sizeof(iov));
    596         iov.iov_len = towire.p - buffer;
    597         iov.iov_base = buffer;
    598         ioloop_send_message(dso_connection, NULL, &iov, 1);
    599         subscribe_xid = dns_message.id; // We need this to identify the response.
    600     }
    601 
    602     // Send a keepalive message so that we can get the response, since the unsubscribe is not a response-requiring request.
    603     dso_make_message(&message, buffer, sizeof(dns_message), dso_connection->dso, false, false, 0, 0, NULL);
    604     memset(&towire, 0, sizeof(towire));
    605     towire.p = &buffer[DNS_HEADER_SIZE];
    606     towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
    607     towire.message = &dns_message;
    608     dns_u16_to_wire(&towire, kDSOType_Keepalive);
    609     dns_rdlength_begin(&towire);
    610     dns_u32_to_wire(&towire, 600);
    611     dns_u32_to_wire(&towire, 600);
    612     dns_rdlength_end(&towire);
    613     if (push_send_bogus_keepalive) {
    614         INFO("sending bogus keepalive");
    615         // Send a badly formatted message.
    616         dns_u32_to_wire(&towire, 0x12345678);
    617     }
    618     keepalive_xid = dns_message.id;
    619     memset(&iov, 0, sizeof(iov));
    620     iov.iov_len = towire.p - buffer;
    621     iov.iov_base = buffer;
    622     ioloop_send_message(dso_connection, NULL, &iov, 1);
    623 }
    624 
    625 static void
    626 dso_remote_disconnect_didnt_happen(void *UNUSED context)
    627 {
    628     INFO("remote disconnect didn't happen");
    629     exit(1);
    630 }
    631 
    632 static void
    633 handle_retry_delay(dso_state_t *dso, uint32_t delay)
    634 {
    635     INFO("Got our retry delay, %ums...", delay);
    636     wait_for_remote_disconnect = ioloop_wakeup_create();
    637     if (!wait_for_remote_disconnect) {
    638         INFO("can't wait for remote disconnect.");
    639         exit(1);
    640     }
    641     // Wait six seconds for remote disconnect, which should happen in five.
    642     ioloop_add_wake_event(wait_for_remote_disconnect, NULL, dso_remote_disconnect_didnt_happen, NULL, 6 * 1000);
    643     disconnect_expected = dso;
    644 }
    645 
    646 static void
    647 dso_message(message_t *message, dso_state_t *dso, bool response)
    648 {
    649 #if PRINT_TO_STDERR
    650     char name[DNS_MAX_NAME_SIZE_ESCAPED + 1];
    651     char ptrname[DNS_MAX_NAME_SIZE_ESCAPED + 1];
    652 #endif
    653     unsigned offset, max;
    654     dns_rr_t rr;
    655     uint8_t *message_bytes;
    656 
    657     switch(dso->primary.opcode) {
    658     case kDSOType_RetryDelay:
    659         if (response) {
    660             INFO("server sent a retry delay TLV as a response.");
    661             exit(1);
    662         }
    663         dso_retry_delay(dso, &message->wire);
    664         break;
    665 
    666     case kDSOType_Keepalive:
    667         if (response) {
    668             INFO("Keepalive response from server, rcode = %d", dns_rcode_get(&message->wire));
    669             exit(0);
    670         } else {
    671             INFO("Keepalive from server");
    672         }
    673         if (!push_subscribe_sent) {
    674             send_push_subscribe();
    675             push_subscribe_sent = true;
    676         }
    677         break;
    678 
    679     case kDSOType_DNSPushSubscribe:
    680         if (response) {
    681             // This is a protocol error--the response isn't supposed to contain a primary TLV.
    682             INFO("DNS Push response from server, rcode = %d", dns_rcode_get(&message->wire));
    683             exit(1);
    684         } else {
    685             INFO("Unexpected DNS Push request from server, rcode = %d", dns_rcode_get(&message->wire));
    686         }
    687         break;
    688 
    689     case kDSOType_DNSPushUpdate:
    690         // DNS Push Updates are never responses.
    691         // DNS Push updates are compressed, so we can't just parse data out of the primary--we need to align
    692         // our parse with the start of the message data.
    693         message_bytes = (uint8_t *)message->wire.data;
    694         offset = (unsigned)(dso->primary.payload - message_bytes); // difference can never be greater than sizeof(message->wire).
    695         max = offset + dso->primary.length;
    696         while (offset < max) {
    697             if (!dns_rr_parse(&rr, message_bytes, max, &offset, true, true)) {
    698                 // Should have emitted an error earlier
    699                 break;
    700             }
    701 #if PRINT_TO_STDERR
    702             dns_name_print(rr.name, name, sizeof(name));
    703             if (rr.type != dns_rrtype_ptr) {
    704                 fprintf(stderr, "%s: type %u class %u ttl %" PRIu32 "\n", name, rr.type, rr.qclass, rr.ttl);
    705             } else {
    706                 dns_name_print(rr.data.ptr.name, ptrname, sizeof(ptrname));
    707                 fprintf(stderr, "%s IN PTR %s\n", name, ptrname);
    708             }
    709 #endif
    710         }
    711         if (push_unsubscribe) {
    712             send_push_unsubscribe();
    713         } else if (!push_exhaust) {
    714             exit(0);
    715         }
    716         break;
    717 
    718     case kDSOType_NoPrimaryTLV: // No Primary TLV
    719         if (response) {
    720             if (message->wire.id == subscribe_xid) {
    721                 int rcode = dns_rcode_get(&message->wire);
    722                 INFO("DNS Push Subscribe response from server, rcode = %d", rcode);
    723                 if (rcode != dns_rcode_noerror) {
    724                     exit(0);
    725                 }
    726                 if (push_exhaust) {
    727                     start_push_query();
    728                 }
    729             } else if (message->wire.id == keepalive_xid) {
    730                 int rcode = dns_rcode_get(&message->wire);
    731                 INFO("DNS Keepalive response from server, rcode = %d", rcode);
    732                 exit(0);
    733             } else {
    734                 int rcode = dns_rcode_get(&message->wire);
    735                 INFO("Unexpected DSO response from server, rcode = %d", rcode);
    736             }
    737         } else {
    738             INFO("DSO request with no primary TLV.");
    739             exit(1);
    740         }
    741         break;
    742 
    743     default:
    744         INFO("dso_message: unexpected primary TLV %d", dso->primary.opcode);
    745         dso_simple_response(dso_connection, NULL, &message->wire, dns_rcode_dsotypeni);
    746         break;
    747     }
    748 }
    749 
    750 static void
    751 dso_event_callback(void *UNUSED context, void *event_context, dso_state_t *dso, dso_event_type_t eventType)
    752 {
    753     message_t *message;
    754     dso_query_receive_context_t *response_context;
    755     dso_disconnect_context_t *disconnect_context;
    756 
    757     switch(eventType)
    758     {
    759     case kDSOEventType_DNSMessage:
    760         // We shouldn't get here because we already handled any DNS messages
    761         message = event_context;
    762         INFO("DNS Message (opcode=%d) received from " PRI_S_SRP, dns_opcode_get(&message->wire),
    763              dso->remote_name);
    764         break;
    765     case kDSOEventType_DNSResponse:
    766         // We shouldn't get here because we already handled any DNS messages
    767         message = event_context;
    768         INFO("DNS Response (opcode=%d) received from " PRI_S_SRP, dns_opcode_get(&message->wire),
    769              dso->remote_name);
    770         break;
    771     case kDSOEventType_DSOMessage:
    772         INFO("DSO Message (Primary TLV=%d) received from " PRI_S_SRP,
    773                dso->primary.opcode, dso->remote_name);
    774         message = event_context;
    775         dso_message(message, dso, false);
    776         break;
    777     case kDSOEventType_DSOResponse:
    778         INFO("DSO Response (Primary TLV=%d) received from " PRI_S_SRP,
    779                dso->primary.opcode, dso->remote_name);
    780         response_context = event_context;
    781         message = response_context->message_context;
    782         dso_message(message, dso, true);
    783         break;
    784 
    785     case kDSOEventType_Finalize:
    786         INFO("Finalize");
    787         break;
    788 
    789     case kDSOEventType_Connected:
    790         INFO("Connected to " PRI_S_SRP, dso->remote_name);
    791         break;
    792 
    793     case kDSOEventType_ConnectFailed:
    794         INFO("Connection to " PRI_S_SRP " failed", dso->remote_name);
    795         break;
    796 
    797     case kDSOEventType_Disconnected:
    798         INFO("Connection to " PRI_S_SRP " disconnected", dso->remote_name);
    799         if (dso == disconnect_expected) {
    800             INFO("remote end disconnected as expected.");
    801             exit(0);
    802         }
    803         break;
    804     case kDSOEventType_ShouldReconnect:
    805         INFO("Connection to " PRI_S_SRP " should reconnect (not for a server)", dso->remote_name);
    806         break;
    807     case kDSOEventType_Inactive:
    808         INFO("Inactivity timer went off, closing connection.");
    809         break;
    810     case kDSOEventType_Keepalive:
    811         INFO("should send a keepalive now.");
    812         break;
    813     case kDSOEventType_KeepaliveRcvd:
    814         if (!push_subscribe_sent) {
    815             send_push_subscribe();
    816             push_subscribe_sent = true;
    817         }
    818         INFO("keepalive received.");
    819         break;
    820     case kDSOEventType_RetryDelay:
    821         disconnect_context = event_context;
    822         INFO("retry delay received, %d seconds", disconnect_context->reconnect_delay);
    823         handle_retry_delay(dso, disconnect_context->reconnect_delay);
    824         break;
    825     }
    826 }
    827 
    828 static void
    829 send_push_subscribe(void)
    830 {
    831     struct iovec iov;
    832     INFO("have session");
    833     dns_wire_t dns_message;
    834     uint8_t *buffer = (uint8_t *)&dns_message;
    835     dns_towire_state_t towire;
    836     dso_message_t message;
    837     dso_make_message(&message, buffer, sizeof(dns_message), dso_connection->dso, false, false, 0, 0, NULL);
    838     dns_push_subscribe_xid = ntohs(dns_message.id);
    839     memset(&towire, 0, sizeof(towire));
    840     towire.p = &buffer[DNS_HEADER_SIZE];
    841     towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
    842     towire.message = &dns_message;
    843     dns_u16_to_wire(&towire, kDSOType_DNSPushSubscribe);
    844     dns_rdlength_begin(&towire);
    845     if (push_hardwired) {
    846         dns_full_name_to_wire(NULL, &towire, "default.service.arpa");
    847         dns_u16_to_wire(&towire, dns_rrtype_soa);
    848     } else {
    849         dns_full_name_to_wire(NULL, &towire, "_airplay._tcp.default.service.arpa");
    850         dns_u16_to_wire(&towire, dns_rrtype_ptr);
    851     }
    852     dns_u16_to_wire(&towire, dns_qclass_in);
    853     dns_rdlength_end(&towire);
    854 
    855     memset(&iov, 0, sizeof(iov));
    856     iov.iov_len = towire.p - buffer;
    857     iov.iov_base = buffer;
    858     ioloop_send_message(dso_connection, NULL, &iov, 1);
    859     subscribe_xid = dns_message.id; // We need this to identify the response.
    860 }
    861 
    862 static void
    863 dso_connected(comm_t *connection, void *UNUSED context)
    864 {
    865     struct iovec iov;
    866     INFO("connected");
    867     connection->dso = dso_state_create(false, 1, connection->name, dso_event_callback,
    868                                        dso_connection, NULL, dso_connection);
    869     if (connection->dso == NULL) {
    870         ERROR("can't create dso state object.");
    871         exit(1);
    872     }
    873     dns_wire_t dns_message;
    874     uint8_t *buffer = (uint8_t *)&dns_message;
    875     dns_towire_state_t towire;
    876     dso_message_t message;
    877     dso_make_message(&message, buffer, sizeof(dns_message), connection->dso, false, false, 0, 0, NULL);
    878     memset(&towire, 0, sizeof(towire));
    879     towire.p = &buffer[DNS_HEADER_SIZE];
    880     towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
    881     towire.message = &dns_message;
    882     dns_u16_to_wire(&towire, kDSOType_Keepalive);
    883     dns_rdlength_begin(&towire);
    884     dns_u32_to_wire(&towire, 100); // Inactivity timeout
    885     dns_u32_to_wire(&towire, 100); // Keepalive interval
    886     dns_rdlength_end(&towire);
    887 
    888     memset(&iov, 0, sizeof(iov));
    889     iov.iov_len = towire.p - buffer;
    890     iov.iov_base = buffer;
    891     ioloop_send_message(dso_connection, NULL, &iov, 1);
    892 }
    893 
    894 static void
    895 dso_disconnected(comm_t *UNUSED connection, void *UNUSED context, int UNUSED error)
    896 {
    897     fprintf(stderr, "disconnected.");
    898     exit(0);
    899 }
    900 
    901 static void
    902 dso_datagram_callback(comm_t *connection, message_t *message, void *UNUSED context)
    903 {
    904     // If this is a DSO message, see if we have a session yet.
    905     switch(dns_opcode_get(&message->wire)) {
    906     case dns_opcode_dso:
    907         if (connection->dso == NULL) {
    908             INFO("dso message received with no DSO object on connection " PRI_S_SRP, connection->name);
    909             exit(1);
    910         }
    911         dso_message_received(connection->dso, (uint8_t *)&message->wire, message->length, message);
    912         return;
    913         break;
    914     }
    915     INFO("datagram on connection " PRI_S_SRP " not handled, type = %d.",
    916          connection->name, dns_opcode_get(&message->wire));
    917 }
    918 
    919 static void
    920 start_push_query(void)
    921 {
    922     // If we can (should always be able to) remember the list of connections we've created.
    923     if (dso_connection != NULL) {
    924         connection_list_t *connection = calloc(1, sizeof (*connection));
    925         if (connection != NULL) {
    926             connection->connection = dso_connection;
    927             connection->next = dso_connection_list;
    928             dso_connection_list = connection;
    929         }
    930     }
    931 
    932     addr_t address;
    933     memset(&address, 0, sizeof(address));
    934     address.sa.sa_family = AF_INET;
    935     address.sin.sin_port = htons(853);
    936     address.sin.sin_addr.s_addr = htonl(0x7f000001);  // localhost.
    937                                                       // tls, stream, stable, opportunistic
    938     dso_connection = ioloop_connection_create(&address, true,   true,   true, true,
    939                                               dso_datagram_callback, dso_connected, dso_disconnected, NULL, NULL);
    940     if (dso_connection == NULL) {
    941         ERROR("Unable to create dso connection.");
    942         exit(1);
    943     }
    944 }
    945 
    946 static void
    947 usage(void)
    948 {
    949     fprintf(stderr,
    950             "srp-client [--lease-time <seconds>] [--client-count <client count>] [--server <address>%%<port>]\n"
    951             "           [--push-query] [--push-unsubscribe]\n"
    952             "           [--bogus-server-test] [--change-txt-record] [--service-type] [--test-renew-subtypes]\n"
    953             "           [--random-leases] [--delete-registrations] [--use-thread-services] [--log-stderr]\n"
    954             "           [--interface <interface name>] [--bogusify-signatures] [--remove-added-service]\n"
    955             "           [--dup-instance-name] [--service-port <port number>] [--expire-added-service]\n"
    956             "           [--random-txt-record] [--bogus-remove] [--test-subtypes] [--test-diff-subtypes]\n"
    957             "           [--new-ip-dup] [--push-exhaust] [--test-bad-sig-time] [--zero-addresses]\n"
    958             "           [--host-only] [--expire-instance]");
    959     exit(1);
    960 }
    961 
    962 
    963 static void
    964 cti_service_list_callback(void *UNUSED context, cti_service_vec_t *services, cti_status_t status)
    965 {
    966     size_t i;
    967 
    968     if (status == kCTIStatus_Disconnected || status == kCTIStatus_DaemonNotRunning) {
    969         INFO("disconnected");
    970         exit(1);
    971     }
    972 
    973     if (!new_ip_dup && !zero_addresses) {
    974         srp_start_address_refresh();
    975         ioloop_map_interface_addresses(NULL, interface_name, services, interface_callback);
    976     }
    977     for (i = 0; i < services->num; i++) {
    978         cti_service_t *cti_service = services->services[i];
    979         // Look for SRP service advertisements only.
    980         if (IS_SRP_SERVICE(cti_service)) {
    981             srp_add_server_address(&cti_service->server[16], dns_rrtype_aaaa, cti_service->server, 16);
    982         }
    983     }
    984     if (!new_ip_dup && !zero_addresses) {
    985         srp_finish_address_refresh(NULL);
    986     }
    987     srp_network_state_stable(NULL);
    988 }
    989 
    990 int
    991 main(int argc, char **argv)
    992 {
    993 
    994     uint8_t server_address[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1 };
    995     uint8_t bogus_address[16] = { 0xfc,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1 };
    996         // { 0x26, 0x20, 0x01, 0x49, 0x00, 0x0f, 0x1a, 0x4d, 0x04, 0xff, 0x61, 0x5a, 0xa2, 0x2a, 0xab, 0xe8 };
    997     int err;
    998     DNSServiceRef sdref;
    999     int *nump;
   1000     char *end;
   1001     (void)argc;
   1002     (void)argv;
   1003     int i;
   1004     bool have_server_address = false;
   1005     bool log_stderr = false;
   1006     char instance_name[128];
   1007     const char *service_type = "_ipps._tcp";
   1008     uint16_t service_port = 0;
   1009     bool bogus_server = false;
   1010 
   1011     ioloop_init();
   1012 
   1013     for (i = 1; i < argc; i++) {
   1014         if (!strcmp(argv[i], "--lease-time")) {
   1015             nump = &lease_time;
   1016         number:
   1017             if (i + 1 == argc) {
   1018                 usage();
   1019             }
   1020             *nump = (uint32_t)strtoul(argv[i + 1], &end, 10);
   1021             if (end == argv[i + 1] || end[0] != 0) {
   1022                 usage();
   1023             }
   1024             i++;
   1025         } else if (!strcmp(argv[i], "--client-count")) {
   1026             nump = &num_clients;
   1027             goto number;
   1028         } else if (!strcmp(argv[i], "--interface")) {
   1029             if (i + 1 == argc) {
   1030                 usage();
   1031             }
   1032             interface_name = argv[i + 1];
   1033             i++;
   1034         } else if (!strcmp(argv[i], "--server")) {
   1035             char *percent;
   1036             uint8_t addrbuf[16];
   1037             uint16_t addrtype = dns_rrtype_aaaa;
   1038             int addrlen = 16;
   1039 
   1040             if (i + 1 == argc) {
   1041                 usage();
   1042             }
   1043             percent = strchr(argv[i + 1], '%');
   1044             if (percent == NULL || percent[1] == 0) {
   1045                 usage();
   1046             }
   1047             *percent = 0;
   1048             percent++;
   1049 
   1050             const unsigned long in_server_port = strtoul(percent, &end, 10);
   1051             if (in_server_port > UINT16_MAX || end == percent || end[0] != 0) {
   1052                 usage();
   1053             }
   1054             uint8_t server_port[2];
   1055             server_port[0] = ((uint16_t)in_server_port) >> 8;
   1056             server_port[1] = ((uint16_t)in_server_port) & 255;
   1057 
   1058             if (inet_pton(AF_INET6, argv[i + 1], addrbuf) < 1) {
   1059                 if (inet_pton(AF_INET, argv[i + 1], addrbuf) < 1) {
   1060                     usage();
   1061                 } else {
   1062                     addrtype = dns_rrtype_a;
   1063                     addrlen = 4;
   1064                 }
   1065             }
   1066             srp_add_server_address(server_port, addrtype, addrbuf, addrlen);
   1067             have_server_address = true;
   1068             i++;
   1069         } else if (!strcmp(argv[i], "--random-leases")) {
   1070             random_leases = true;
   1071         } else if (!strcmp(argv[i], "--delete-registrations")) {
   1072             delete_registrations = true;
   1073         } else if (!strcmp(argv[i], "--use-thread-services")) {
   1074             use_thread_services = true;
   1075         } else if (!strcmp(argv[i], "--dup-instance-name")) {
   1076             dup_instance_name = true;
   1077         } else if (!strcmp(argv[i], "--new-ip-dup")) {
   1078             new_ip_dup = true;
   1079         } else if (!strcmp(argv[i], "--log-stderr")) {
   1080             log_stderr = true;
   1081             OPENLOG("srp-client", true);
   1082         } else if (!strcmp(argv[i], "--change-txt-record")) {
   1083             change_txt_record = true;
   1084         } else if (!strcmp(argv[i], "--random-txt-record")) {
   1085             random_txt_record = true;
   1086         } else if (!strcmp(argv[i], "--remove-added-service")) {
   1087             remove_added_service = true;
   1088         } else if (!strcmp(argv[i], "--expire-added-service")) {
   1089             let_added_service_expire = true;
   1090         } else if (!strcmp(argv[1], "--bogus-server-test")) {
   1091             bogus_server = true;
   1092         } else if (!strcmp(argv[i], "--bogusify-signatures")) {
   1093             bogusify_signatures = true;
   1094         } else if (!strcmp(argv[i], "--bogus-remove")) {
   1095             bogus_remove = true;
   1096         } else if (!strcmp(argv[i], "--push-query")) {
   1097             push_query = true;
   1098             do_srp = false;
   1099         } else if (!strcmp(argv[i], "--push-hardwired")) {
   1100             push_query = true;
   1101             push_hardwired = true;
   1102             push_unsubscribe = true;
   1103             do_srp = false;
   1104         } else if (!strcmp(argv[i], "--push-unsubscribe")) {
   1105             push_query = true;
   1106             push_unsubscribe = true;
   1107             do_srp = false;
   1108         } else if (!strcmp(argv[i], "--push-send-bogus-keepalive")) {
   1109             push_query = true;
   1110             push_unsubscribe = true;
   1111             push_send_bogus_keepalive = true;
   1112             do_srp = false;
   1113         } else if (!strcmp(argv[i], "--push-exhaust")) {
   1114             push_exhaust = true;
   1115             do_srp = false;
   1116         } else if (!strcmp(argv[i], "--test-subtypes")) {
   1117             test_subtypes = true;
   1118         } else if (!strcmp(argv[i], "--test-diff-subtypes")) {
   1119             test_diff_subtypes = true;
   1120         } else if (!strcmp(argv[i], "--test-renew-subtypes")) {
   1121             test_renew_subtypes = true;
   1122         } else if (!strcmp(argv[i], "--host-only")) {
   1123             host_only = true;
   1124         } else if (!strcmp(argv[i], "--zero-addresses")) {
   1125             zero_addresses = true;
   1126         } else if (!strcmp(argv[i], "--expire-instance")) {
   1127             expire_instance = true;
   1128         } else if (!strcmp(argv[i], "--test-bad-sig-time")) {
   1129             test_bad_sig_time = true;
   1130         } else if (!strcmp(argv[i], "--service-type")) {
   1131             if (i + 1 == argc) {
   1132                 usage();
   1133             }
   1134             service_type = argv[i + 1];
   1135             i++;
   1136         } else if (!strcmp(argv[i], "--service-port")) {
   1137             if (i + 1 == argc) {
   1138                 usage();
   1139             }
   1140 
   1141             const int in_service_port = atoi(argv[i + 1]);
   1142             if (in_service_port == 0 || in_service_port > UINT16_MAX) {
   1143                 fprintf(stderr, "Service port number %d is out of range or invalid, should be in (0, 65535].\n",
   1144                         in_service_port);
   1145                 usage();
   1146             }
   1147 
   1148             service_port = (uint16_t)in_service_port;
   1149             i++;
   1150         } else {
   1151             usage();
   1152         }
   1153     }
   1154 
   1155     if (!log_stderr) {
   1156         OPENLOG("srp-client", false);
   1157     }
   1158 
   1159     // If we're asked to do a push query, we're not actually going to act as an SRP client, just do the push query.
   1160     if (push_query || push_exhaust) {
   1161         start_push_query();
   1162         ioloop();
   1163         exit(1);
   1164     }
   1165 
   1166     if (!use_thread_services && !new_ip_dup && !zero_addresses) {
   1167         ioloop_map_interface_addresses(NULL, interface_name, NULL, interface_callback);
   1168     }
   1169 
   1170     if (!have_server_address && !use_thread_services) {
   1171         uint8_t port[] = { 0, 53 };
   1172         if (bogus_server) {
   1173             srp_add_server_address(port, dns_rrtype_aaaa, bogus_address, 16);
   1174         }
   1175         srp_add_server_address(port, dns_rrtype_aaaa, server_address, 16);
   1176     }
   1177 
   1178     if (dup_instance_name) {
   1179         num_clients = 2;
   1180         ioloop_strcpy(instance_name, "dup-name-test", sizeof(instance_name));
   1181     }
   1182     if (new_ip_dup || zero_addresses) {
   1183         // Set up the test to validate the "failed update removes address" code in srp-mdns-proxy.
   1184         srp_add_interface_address(dns_rrtype_a, first_bogus_address, sizeof(first_bogus_address));
   1185         if (zero_addresses) {
   1186             srp_delete_interface_address(dns_rrtype_a, first_bogus_address, sizeof(first_bogus_address));
   1187         }
   1188     }
   1189 
   1190     for (i = 0; i < num_clients; i++) {
   1191         srp_client_t *client;
   1192         char hnbuf[128];
   1193         TXTRecordRef txt;
   1194         const void *txt_data = NULL;
   1195         uint16_t txt_len = 0;
   1196         char txt_buf[128];
   1197 
   1198         client = calloc(1, sizeof(*client));
   1199         if (client == NULL) {
   1200             ERROR("no memory for client %d", i);
   1201             exit(1);
   1202         }
   1203 
   1204         if (num_clients == 1) {
   1205             strcpy(hnbuf, "srp-api-test");
   1206         } else {
   1207             snprintf(hnbuf, sizeof(hnbuf), "srp-api-test-%d", i);
   1208         }
   1209         client->name = strdup(hnbuf);
   1210         if (client->name == NULL) {
   1211             ERROR("No memory for client name %s", hnbuf);
   1212             exit(1);
   1213         }
   1214         client->index = i;
   1215 
   1216         srp_host_init(client);
   1217         srp_set_hostname(hnbuf, NULL);
   1218 
   1219         if (random_leases) {
   1220             int random_lease_time = 30 + srp_random16() % 1800; // random
   1221             INFO("Client %d, lease time = %d", i, random_lease_time);
   1222             srp_set_lease_times(random_lease_time, 7 * 24 * 3600); // random host lease, 7 day key lease
   1223         } else if (lease_time > 0) {
   1224             srp_set_lease_times(lease_time, 7 * 24 * 3600); // specified host lease, 7 day key lease
   1225         } else if (let_added_service_expire) {
   1226             srp_set_lease_times(30, 30); // Use short lease times so the lease expires quickly.
   1227         }
   1228 
   1229         if (change_txt_record) {
   1230             TXTRecordCreate(&txt, sizeof(txt_buf), txt_buf);
   1231             TXTRecordSetValue(&txt, "foo", 1, "0");
   1232             TXTRecordSetValue(&txt, "bar", 3, "1.1");
   1233             txt_data = TXTRecordGetBytesPtr(&txt);
   1234             txt_len = TXTRecordGetLength(&txt);
   1235         }
   1236         if (random_txt_record) {
   1237             char rbuf[6];
   1238             snprintf(rbuf, sizeof(rbuf), "%u", srp_random16());
   1239             TXTRecordCreate(&txt, sizeof(txt_buf), txt_buf);
   1240             TXTRecordSetValue(&txt, "foo", strlen(rbuf), rbuf);
   1241             INFO("TXTRecordSetValue(..., \"foo\", %zd, %s)", strlen(rbuf), rbuf);
   1242             txt_data = TXTRecordGetBytesPtr(&txt);
   1243             txt_len = TXTRecordGetLength(&txt);
   1244         }
   1245 
   1246         if (service_port == 0) {
   1247             // If no service port is specified (0 indicates that port is unspecified), the index i will be used to
   1248             // generate the port number.
   1249             service_port = (i % UINT16_MAX) == 0 ? 1 : (i % UINT16_MAX);
   1250         }
   1251 
   1252         if (!test_subtypes && !test_diff_subtypes && !test_renew_subtypes && !host_only) {
   1253             err = DNSServiceRegister(&sdref, new_ip_dup ? kDNSServiceFlagsNoAutoRename : 0, 0,
   1254                                      dup_instance_name ? instance_name : hnbuf, service_type, 0, 0, htons(service_port),
   1255                                      txt_len, txt_data, register_callback, client);
   1256             if (err != kDNSServiceErr_NoError) {
   1257                 ERROR("DNSServiceRegister failed: %d", err);
   1258                 exit(1);
   1259             }
   1260         }
   1261         if (remove_added_service || let_added_service_expire) {
   1262             expecting_second_add = true;
   1263             err = DNSServiceRegister(&sdref, 0, 0, hnbuf, "_second._tcp,foo", 0, 0, htons(service_port),
   1264                                      txt_len, txt_data, second_register_callback, client);
   1265             if (err != kDNSServiceErr_NoError) {
   1266                 ERROR("second DNSServiceRegister failed: %d", err);
   1267                 exit(1);
   1268             }
   1269         }
   1270         // Here we register two services with subtypes. The idea is to see that the srp parsing code does not
   1271         // associate the second subtype with the first service instance and report an error. In order to
   1272         // attempt to trigger the error, we need the service instance name of the second service instance
   1273         // to be different.
   1274         if (test_subtypes || test_diff_subtypes || test_renew_subtypes) {
   1275             expecting_second_add = true;
   1276             err = DNSServiceRegister(&sdref, 0, 0, hnbuf, "_ipps._tcp,subtype",
   1277                                      0, 0, htons(service_port), txt_len, txt_data, register_callback, client);
   1278             if (err != kDNSServiceErr_NoError) {
   1279                 ERROR("DNSServiceRegister failed: %d", err);
   1280                 exit(1);
   1281             }
   1282             if (test_diff_subtypes) {
   1283                 err = DNSServiceRegister(&sdref, 0, 0, hnbuf, "_second._tcp,othersub",
   1284                                          0, 0, htons(service_port), txt_len, txt_data, second_register_callback, client);
   1285                 if (err != kDNSServiceErr_NoError) {
   1286                     ERROR("DNSServiceRegister failed: %d", err);
   1287                     exit(1);
   1288                 }
   1289             } else if (!test_renew_subtypes) {
   1290                 char shnbuf[132];
   1291                 snprintf(shnbuf, sizeof(shnbuf), "foo-%s", hnbuf);
   1292                 err = DNSServiceRegister(&sdref, 0, 0, shnbuf, "_ipps._tcp,othersub",
   1293                                          0, 0, htons(service_port), txt_len, txt_data, second_register_callback, client);
   1294                 if (err != kDNSServiceErr_NoError) {
   1295                     ERROR("DNSServiceRegister failed: %d", err);
   1296                     exit(1);
   1297                 }
   1298             }
   1299         }
   1300     }
   1301 
   1302     if (do_srp) {
   1303         if (use_thread_services) {
   1304             cti_get_service_list(NULL, &thread_service_context, NULL, cti_service_list_callback, NULL);
   1305         } else {
   1306             srp_network_state_stable(NULL);
   1307         }
   1308     }
   1309     ioloop();
   1310 }
   1311 
   1312 // Local Variables:
   1313 // mode: C
   1314 // tab-width: 4
   1315 // c-file-style: "bsd"
   1316 // c-basic-offset: 4
   1317 // fill-column: 108
   1318 // indent-tabs-mode: nil
   1319 // End:
   1320