Home | History | Annotate | Line # | Download | only in tests
      1  1.1  christos /* dns-push.c
      2  1.1  christos  *
      3  1.1  christos  * Copyright (c) 2024 Apple Inc. All rights reserved.
      4  1.1  christos  *
      5  1.1  christos  * Licensed under the Apache License, Version 2.0 (the "License");
      6  1.1  christos  * you may not use this file except in compliance with the License.
      7  1.1  christos  * You may obtain a copy of the License at
      8  1.1  christos  *
      9  1.1  christos  *     https://www.apache.org/licenses/LICENSE-2.0
     10  1.1  christos  *
     11  1.1  christos  * Unless required by applicable law or agreed to in writing, software
     12  1.1  christos  * distributed under the License is distributed on an "AS IS" BASIS,
     13  1.1  christos  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  1.1  christos  * See the License for the specific language governing permissions and
     15  1.1  christos  * limitations under the License.
     16  1.1  christos  *
     17  1.1  christos  * This file contains the SRP server test runner.
     18  1.1  christos  */
     19  1.1  christos 
     20  1.1  christos #include "srp.h"
     21  1.1  christos #include <dns_sd.h>
     22  1.1  christos #include <arpa/inet.h>
     23  1.1  christos #include "srp-test-runner.h"
     24  1.1  christos #include "srp-api.h"
     25  1.1  christos #include "dns-msg.h"
     26  1.1  christos #include "ioloop.h"
     27  1.1  christos #include "srp-mdns-proxy.h"
     28  1.1  christos #include "test-api.h"
     29  1.1  christos #include "srp-proxy.h"
     30  1.1  christos #include "srp-mdns-proxy.h"
     31  1.1  christos #include "test-dnssd.h"
     32  1.1  christos #include "test.h"
     33  1.1  christos #include "dnssd-proxy.h"
     34  1.1  christos #define DNSMessageHeader dns_wire_t
     35  1.1  christos #include "dso.h"
     36  1.1  christos #include "dso-utils.h"
     37  1.1  christos 
     38  1.1  christos #define SUBSCRIBE_LIMIT 16 // SOA + SRV + A + AAAA || PTR + SRV + A + AAAA
     39  1.1  christos #define TXN_LIMIT SUBSCRIBE_LIMIT * 4
     40  1.1  christos typedef struct push_test_state push_test_state_t;
     41  1.1  christos struct push_test_state {
     42  1.1  christos     test_state_t *test_state;
     43  1.1  christos     comm_t *dso_connection;
     44  1.1  christos     wakeup_t *wait_for_remote_disconnect;
     45  1.1  christos     dso_state_t *disconnect_expected;
     46  1.1  christos     DNSServiceRef register_ref, ptr_sdref;
     47  1.1  christos     DNSServiceRef txns[TXN_LIMIT];
     48  1.1  christos     int num_txns;
     49  1.1  christos     uint16_t subscribe_xids[SUBSCRIBE_LIMIT];
     50  1.1  christos     int num_subscribe_xids, soa_index, ds_index[2];
     51  1.1  christos     char *hostname;
     52  1.1  christos     char *srv_name;
     53  1.1  christos     int num_service_adds_pre, num_service_removes, num_service_adds_post;
     54  1.1  christos     int num_address_adds_pre, num_address_removes, num_address_adds_post;
     55  1.1  christos     uint16_t keepalive_xid;
     56  1.1  christos     int variant, num_a_records, num_aaaa_records;
     57  1.1  christos     int num_txt_records, num_srv_records;
     58  1.1  christos     bool push_send_bogus_keepalive, push_unsubscribe;
     59  1.1  christos     bool push_subscribe_sent, have_address_records;
     60  1.1  christos     bool server_was_crashed, server_is_being_crashed;
     61  1.1  christos     bool test_dns_push, have_keepalive_response, need_service;
     62  1.1  christos };
     63  1.1  christos 
     64  1.1  christos static void test_dns_push_send_push_subscribe(push_test_state_t *push_state, const char *name, int rrtype);
     65  1.1  christos 
     66  1.1  christos static void
     67  1.1  christos test_dns_push_dso_message_finished(void *context, message_t *UNUSED message, dso_state_t *dso)
     68  1.1  christos {
     69  1.1  christos     push_test_state_t *push_state = context;
     70  1.1  christos 
     71  1.1  christos     if (dso->primary.opcode == kDSOType_DNSPushUnsubscribe) {
     72  1.1  christos         if (dso->activities == NULL) {
     73  1.1  christos             dispatch_async(dispatch_get_main_queue(), ^{
     74  1.1  christos                     TEST_PASSED(push_state->test_state);
     75  1.1  christos                 });
     76  1.1  christos         }
     77  1.1  christos     }
     78  1.1  christos }
     79  1.1  christos 
     80  1.1  christos static void
     81  1.1  christos test_dns_push_send_push_unsubscribe(push_test_state_t *push_state, int index)
     82  1.1  christos {
     83  1.1  christos     if (push_state->subscribe_xids[index] != 0) {
     84  1.1  christos         struct iovec iov;
     85  1.1  christos         dns_wire_t dns_message;
     86  1.1  christos         uint8_t *buffer = (uint8_t *)&dns_message;
     87  1.1  christos         dns_towire_state_t towire;
     88  1.1  christos         dso_message_t message;
     89  1.1  christos 
     90  1.1  christos         INFO("unsubscribe %x %d", push_state->subscribe_xids[index], index);
     91  1.1  christos         dso_make_message(&message, buffer, sizeof(dns_message), push_state->dso_connection->dso, true, false, 0, 0, NULL);
     92  1.1  christos         memset(&towire, 0, sizeof(towire));
     93  1.1  christos         towire.p = &buffer[DNS_HEADER_SIZE];
     94  1.1  christos         towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
     95  1.1  christos         towire.message = &dns_message;
     96  1.1  christos         dns_u16_to_wire(&towire, kDSOType_DNSPushUnsubscribe);
     97  1.1  christos         dns_rdlength_begin(&towire);
     98  1.1  christos         dns_u16_to_wire(&towire, push_state->subscribe_xids[index]);
     99  1.1  christos         dns_rdlength_end(&towire);
    100  1.1  christos 
    101  1.1  christos         memset(&iov, 0, sizeof(iov));
    102  1.1  christos         iov.iov_len = towire.p - buffer;
    103  1.1  christos         iov.iov_base = buffer;
    104  1.1  christos         ioloop_send_message(push_state->dso_connection, NULL, &iov, 1);
    105  1.1  christos         push_state->subscribe_xids[index] = 0; // Don't unsubscribe again.
    106  1.1  christos     }
    107  1.1  christos }
    108  1.1  christos 
    109  1.1  christos static void
    110  1.1  christos test_dns_push_unsubscribe_all(push_test_state_t *push_state)
    111  1.1  christos {
    112  1.1  christos     struct iovec iov;
    113  1.1  christos     INFO("unsubscribe");
    114  1.1  christos     dns_wire_t dns_message;
    115  1.1  christos     uint8_t *buffer = (uint8_t *)&dns_message;
    116  1.1  christos     dns_towire_state_t towire;
    117  1.1  christos     dso_message_t message;
    118  1.1  christos     if (!push_state->push_send_bogus_keepalive) {
    119  1.1  christos         for (int i = 0; i < push_state->num_subscribe_xids; i++) {
    120  1.1  christos             test_dns_push_send_push_unsubscribe(push_state, i);
    121  1.1  christos         }
    122  1.1  christos     }
    123  1.1  christos 
    124  1.1  christos     // Send a keepalive message so that we can get the response, since the unsubscribe is not a response-requiring request.
    125  1.1  christos     dso_make_message(&message, buffer, sizeof(dns_message), push_state->dso_connection->dso, false, false, 0, 0, NULL);
    126  1.1  christos     memset(&towire, 0, sizeof(towire));
    127  1.1  christos     towire.p = &buffer[DNS_HEADER_SIZE];
    128  1.1  christos     towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
    129  1.1  christos     towire.message = &dns_message;
    130  1.1  christos     dns_u16_to_wire(&towire, kDSOType_Keepalive);
    131  1.1  christos     dns_rdlength_begin(&towire);
    132  1.1  christos     dns_u32_to_wire(&towire, 600);
    133  1.1  christos     dns_u32_to_wire(&towire, 600);
    134  1.1  christos     dns_rdlength_end(&towire);
    135  1.1  christos     if (push_state->push_send_bogus_keepalive) {
    136  1.1  christos         INFO("sending bogus keepalive");
    137  1.1  christos         // Send a badly formatted message.
    138  1.1  christos         dns_u32_to_wire(&towire, 0x12345678);
    139  1.1  christos     }
    140  1.1  christos     push_state->keepalive_xid = dns_message.id;
    141  1.1  christos     memset(&iov, 0, sizeof(iov));
    142  1.1  christos     iov.iov_len = towire.p - buffer;
    143  1.1  christos     iov.iov_base = buffer;
    144  1.1  christos     ioloop_send_message(push_state->dso_connection, NULL, &iov, 1);
    145  1.1  christos }
    146  1.1  christos 
    147  1.1  christos static void
    148  1.1  christos test_dns_push_remote_disconnect_didnt_happen(void *context)
    149  1.1  christos {
    150  1.1  christos     push_test_state_t *push_state = context;
    151  1.1  christos     TEST_FAIL(push_state->test_state, "remote disconnect didn't happen");
    152  1.1  christos }
    153  1.1  christos 
    154  1.1  christos static void
    155  1.1  christos test_dns_push_handle_retry_delay(push_test_state_t *push_state, dso_state_t *dso, uint32_t delay)
    156  1.1  christos {
    157  1.1  christos     INFO("Got our retry delay, %ums...", delay);
    158  1.1  christos     push_state->wait_for_remote_disconnect = ioloop_wakeup_create();
    159  1.1  christos 
    160  1.1  christos     TEST_FAIL_CHECK(push_state->test_state, push_state->wait_for_remote_disconnect != NULL, "can't wait for remote disconnect.");
    161  1.1  christos 
    162  1.1  christos     // Wait six seconds for remote disconnect, which should happen in five.
    163  1.1  christos     ioloop_add_wake_event(push_state->wait_for_remote_disconnect, push_state, test_dns_push_remote_disconnect_didnt_happen, NULL, 6 * 1000);
    164  1.1  christos     push_state->disconnect_expected = dso;
    165  1.1  christos }
    166  1.1  christos 
    167  1.1  christos static void
    168  1.1  christos test_dns_push_address_update(push_test_state_t *push_state, dns_rr_t *rr, const char *name)
    169  1.1  christos {
    170  1.1  christos     const char *record_name = "AAAA";
    171  1.1  christos     char ntop[INET6_ADDRSTRLEN];
    172  1.1  christos     int num;
    173  1.1  christos 
    174  1.1  christos     if (rr->type == dns_rrtype_a) {
    175  1.1  christos         num = push_state->num_a_records;
    176  1.1  christos         if (rr->ttl != 0xffffffff) {
    177  1.1  christos             ++push_state->num_a_records;
    178  1.1  christos         }
    179  1.1  christos         inet_ntop(AF_INET, &rr->data.a, ntop, sizeof(ntop));
    180  1.1  christos         record_name = "A";
    181  1.1  christos     } else {
    182  1.1  christos         num = push_state->num_aaaa_records;
    183  1.1  christos         if (rr->ttl != 0xffffffff) {
    184  1.1  christos             ++push_state->num_aaaa_records;
    185  1.1  christos         }
    186  1.1  christos         inet_ntop(AF_INET6, &rr->data.aaaa, ntop, sizeof(ntop));
    187  1.1  christos     }
    188  1.1  christos     INFO("%s: %s %s record #%d: %s", name, rr->ttl == 0xffffffff ? "removed" : "added", record_name, num, ntop);
    189  1.1  christos }
    190  1.1  christos 
    191  1.1  christos static void
    192  1.1  christos test_dns_push_update(push_test_state_t *push_state, dns_rr_t *rr)
    193  1.1  christos {
    194  1.1  christos     char name[DNS_MAX_NAME_SIZE_ESCAPED + 1];
    195  1.1  christos     dns_name_print(rr->name, name, sizeof(name));
    196  1.1  christos 
    197  1.1  christos     if (rr->type == dns_rrtype_soa) {
    198  1.1  christos         TEST_FAIL_CHECK_STATUS(push_state->test_state, push_state->variant == PUSH_TEST_VARIANT_HARDWIRED,
    199  1.1  christos                                "SOA received in wrong variant: %s", name);
    200  1.1  christos         char soaname[DNS_MAX_NAME_SIZE_ESCAPED + 1];
    201  1.1  christos         TEST_FAIL_CHECK_STATUS(push_state->test_state, !strcmp(name, "default.service.arpa."), "bad name for SOA: %s", name);
    202  1.1  christos         dns_name_print(rr->data.soa.mname, soaname, sizeof(soaname));
    203  1.1  christos         INFO("%s in SOA %s ...", name, soaname);
    204  1.1  christos         // Look up the SRV record for _dns-push-tls._tcp.default.service.arpa, so that we can get the hostname but also
    205  1.1  christos         // validate that the SRV record is being advertised.
    206  1.1  christos         push_state->srv_name = strdup("_dns-push-tls._tcp.default.service.arpa.");
    207  1.1  christos         test_dns_push_send_push_subscribe(push_state, push_state->srv_name, dns_rrtype_srv);
    208  1.1  christos         test_dns_push_send_push_unsubscribe(push_state, push_state->soa_index);
    209  1.1  christos     } else if (rr->type == dns_rrtype_a || rr->type == dns_rrtype_aaaa) {
    210  1.1  christos         test_dns_push_address_update(push_state, rr, name);
    211  1.1  christos         if (push_state->server_was_crashed) {
    212  1.1  christos             if (rr->ttl == 0xffffffff) {
    213  1.1  christos                 push_state->num_address_removes++;
    214  1.1  christos             } else {
    215  1.1  christos                 push_state->num_address_adds_post++;
    216  1.1  christos             }
    217  1.1  christos         } else {
    218  1.1  christos             push_state->num_address_adds_pre++;
    219  1.1  christos         }
    220  1.1  christos     } else if (rr->type == dns_rrtype_ptr) {
    221  1.1  christos         TEST_FAIL_CHECK_STATUS(push_state->test_state, push_state->variant != PUSH_TEST_VARIANT_HARDWIRED,
    222  1.1  christos                                "PTR received in wrong variant: %s", name);
    223  1.1  christos         TEST_FAIL_CHECK_STATUS(push_state->test_state, !strcmp(name, "_example._tcp.default.service.arpa."),
    224  1.1  christos                                "bad name for PTR: %s", name);
    225  1.1  christos         char ptrname[DNS_MAX_NAME_SIZE_ESCAPED + 1];
    226  1.1  christos         dns_name_print(rr->data.ptr.name, ptrname, sizeof(ptrname));
    227  1.1  christos         INFO("%s %s IN PTR %s", rr->ttl == 0xffffffff ? "removed" : "added", name, ptrname);
    228  1.1  christos         if (push_state->srv_name == NULL) {
    229  1.1  christos             push_state->srv_name = strdup(ptrname);
    230  1.1  christos             test_dns_push_send_push_subscribe(push_state, push_state->srv_name, dns_rrtype_srv);
    231  1.1  christos         }
    232  1.1  christos         if (push_state->server_was_crashed) {
    233  1.1  christos             if (rr->ttl == 0xffffffff) {
    234  1.1  christos                 push_state->num_service_removes++;
    235  1.1  christos             } else {
    236  1.1  christos                 push_state->num_service_adds_post++;
    237  1.1  christos             }
    238  1.1  christos         } else {
    239  1.1  christos             push_state->num_service_adds_pre++;
    240  1.1  christos         }
    241  1.1  christos     } else if (rr->type == dns_rrtype_srv) {
    242  1.1  christos         char hnbuf[DNS_MAX_NAME_SIZE_ESCAPED + 1];
    243  1.1  christos         dns_name_print(rr->data.ptr.name, hnbuf, sizeof(hnbuf));
    244  1.1  christos         INFO("%s IN SRV %s ...", name, hnbuf);
    245  1.1  christos         TEST_FAIL_CHECK_STATUS(push_state->test_state, !strcmp(name, push_state->srv_name), "bad name for SRV: %s", name);
    246  1.1  christos         // Look up address records for SOA name server name.
    247  1.1  christos         if (push_state->hostname == NULL) {
    248  1.1  christos             push_state->hostname = strdup(hnbuf);
    249  1.1  christos             TEST_FAIL_CHECK_STATUS(push_state->test_state, push_state->hostname != NULL, "no memory for %s", hnbuf);
    250  1.1  christos             dispatch_async(dispatch_get_main_queue(), ^{
    251  1.1  christos                 test_dns_push_send_push_subscribe(push_state, push_state->hostname, dns_rrtype_a);
    252  1.1  christos                 });
    253  1.1  christos             dispatch_async(dispatch_get_main_queue(), ^{
    254  1.1  christos                 test_dns_push_send_push_subscribe(push_state, push_state->hostname, dns_rrtype_aaaa);
    255  1.1  christos             });
    256  1.1  christos             // At this point the DS queries should have been started, so we can remove them and make sure that works.
    257  1.1  christos             if (push_state->variant == PUSH_TEST_VARIANT_HARDWIRED) {
    258  1.1  christos                 test_dns_push_send_push_unsubscribe(push_state, push_state->ds_index[0]);
    259  1.1  christos                 test_dns_push_send_push_unsubscribe(push_state, push_state->ds_index[1]);
    260  1.1  christos             }
    261  1.1  christos         }
    262  1.1  christos         push_state->num_srv_records++;
    263  1.1  christos     } else if (rr->type == dns_rrtype_txt) {
    264  1.1  christos         char txt_buf[DNS_DATA_SIZE];
    265  1.1  christos         dns_txt_data_print(txt_buf, DNS_DATA_SIZE, rr->data.txt.len, rr->data.txt.data);
    266  1.1  christos         INFO("%s IN TXT %s ...", name, txt_buf);
    267  1.1  christos         push_state->num_txt_records++;
    268  1.1  christos     } else {
    269  1.1  christos         INFO("unexpected rrtype for %s in push update: %d", name, rr->type);
    270  1.1  christos     }
    271  1.1  christos }
    272  1.1  christos 
    273  1.1  christos static void
    274  1.1  christos test_dns_push_send_appropriate_subscribe(push_test_state_t *push_state)
    275  1.1  christos {
    276  1.1  christos     if (push_state->variant == PUSH_TEST_VARIANT_HARDWIRED) {
    277  1.1  christos         push_state->soa_index = push_state->num_subscribe_xids;
    278  1.1  christos         test_dns_push_send_push_subscribe(push_state, "default.service.arpa", dns_rrtype_soa);
    279  1.1  christos         push_state->ds_index[0] = push_state->num_subscribe_xids;
    280  1.1  christos         test_dns_push_send_push_subscribe(push_state, "default.service.arpa", dns_rrtype_ds);
    281  1.1  christos         push_state->ds_index[1] = push_state->num_subscribe_xids;
    282  1.1  christos         test_dns_push_send_push_subscribe(push_state, "default.service.arpa", dns_rrtype_ds);
    283  1.1  christos     } else {
    284  1.1  christos         test_dns_push_send_push_subscribe(push_state, "_example._tcp.default.service.arpa", dns_rrtype_ptr);
    285  1.1  christos     }
    286  1.1  christos     push_state->push_subscribe_sent = true;
    287  1.1  christos }
    288  1.1  christos 
    289  1.1  christos static void
    290  1.1  christos test_dns_push_satisfied_check(push_test_state_t *push_state)
    291  1.1  christos {
    292  1.1  christos     // Check to see if we have all the address records we wanted.
    293  1.1  christos     if (!push_state->have_address_records) {
    294  1.1  christos         bool satisfied;
    295  1.1  christos         switch(push_state->variant) {
    296  1.1  christos         case PUSH_TEST_VARIANT_HARDWIRED:
    297  1.1  christos             satisfied = push_state->num_a_records != 0 && push_state->num_aaaa_records != 0;
    298  1.1  christos             break;
    299  1.1  christos         case PUSH_TEST_VARIANT_DAEMON_CRASH:
    300  1.1  christos         case PUSH_TEST_VARIANT_MDNS:
    301  1.1  christos             satisfied = push_state->num_a_records != 0 || push_state->num_aaaa_records != 0;
    302  1.1  christos             break;
    303  1.1  christos         case PUSH_TEST_VARIANT_TWO_QUESTIONS:
    304  1.1  christos             satisfied = push_state->num_srv_records > 0 && push_state->num_txt_records > 0;
    305  1.1  christos             break;
    306  1.1  christos         default:
    307  1.1  christos             satisfied = false;
    308  1.1  christos         }
    309  1.1  christos         if (satisfied && push_state->variant == PUSH_TEST_VARIANT_DAEMON_CRASH) {
    310  1.1  christos             if (!push_state->server_was_crashed) {
    311  1.1  christos                 // If the server hasn't been crashed yet
    312  1.1  christos                 // And we haven't already queued up a crash event
    313  1.1  christos                 if (!push_state->server_is_being_crashed) {
    314  1.1  christos                     push_state->server_is_being_crashed = true;
    315  1.1  christos                     // Queue up a crash event
    316  1.1  christos                     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2),
    317  1.1  christos                                    dispatch_get_main_queue(), ^{
    318  1.1  christos                         push_state->server_was_crashed = true;
    319  1.1  christos                         push_state->num_a_records = push_state->num_aaaa_records = 0;
    320  1.1  christos                         dns_service_ref_t *ref = push_state->ptr_sdref;
    321  1.1  christos                         TEST_FAIL_CHECK(push_state->test_state, ref != NULL, "ptr sdref is gone!");
    322  1.1  christos                         for (int i = 0; i < push_state->num_txns; i++) {
    323  1.1  christos                             DNSServiceRefDeallocate(push_state->txns[i]);
    324  1.1  christos                         }
    325  1.1  christos                         DNSServiceQueryRecordReply callback = ref->callback.query_record_reply;
    326  1.1  christos                         callback(ref, 0, 0, kDNSServiceErr_ServiceNotRunning,
    327  1.1  christos                                  NULL, dns_rrtype_ptr, dns_qclass_in, 0, 0, 0, ref->context);
    328  1.1  christos                     });
    329  1.1  christos                 }
    330  1.1  christos                 satisfied = false;
    331  1.1  christos             } else {
    332  1.1  christos                 if (push_state->num_address_removes != push_state->num_address_adds_pre) {
    333  1.1  christos                     satisfied = false;
    334  1.1  christos                 }
    335  1.1  christos                 if (push_state->num_address_adds_pre != push_state->num_address_adds_post) {
    336  1.1  christos                     satisfied = false;
    337  1.1  christos                 }
    338  1.1  christos                 INFO("address removes: %d address adds pre: %d address adds post: %d",
    339  1.1  christos                      push_state->num_address_removes, push_state->num_address_adds_pre,
    340  1.1  christos                      push_state->num_address_adds_post);
    341  1.1  christos                 if (push_state->num_service_removes != push_state->num_service_adds_pre) {
    342  1.1  christos                     satisfied = false;
    343  1.1  christos                 }
    344  1.1  christos                 INFO("service removes: %d service adds pre: %d service adds post: %d",
    345  1.1  christos                      push_state->num_service_removes, push_state->num_service_adds_pre,
    346  1.1  christos                      push_state->num_service_adds_post);
    347  1.1  christos             }
    348  1.1  christos         }
    349  1.1  christos         if (satisfied) {
    350  1.1  christos             push_state->have_address_records = true;
    351  1.1  christos             // If we've been asked to unsubscribe, do that.
    352  1.1  christos             if (push_state->push_unsubscribe) {
    353  1.1  christos                 test_dns_push_unsubscribe_all(push_state);
    354  1.1  christos             } else {
    355  1.1  christos                 // Finish any ongoing activities first...
    356  1.1  christos                 dispatch_async(dispatch_get_main_queue(), ^{
    357  1.1  christos                     TEST_PASSED(push_state->test_state);
    358  1.1  christos                 });
    359  1.1  christos             }
    360  1.1  christos         }
    361  1.1  christos     }
    362  1.1  christos }
    363  1.1  christos 
    364  1.1  christos static void
    365  1.1  christos test_dns_push_dns_response(push_test_state_t *push_state, message_t *message)
    366  1.1  christos {
    367  1.1  christos     unsigned offset, max;
    368  1.1  christos     dns_rr_t rr;
    369  1.1  christos     uint8_t *message_bytes;
    370  1.1  christos     bool question = true;
    371  1.1  christos     int rdata_num = 0;
    372  1.1  christos     int num_answers = ntohs(message->wire.ancount);
    373  1.1  christos 
    374  1.1  christos     message_bytes = (uint8_t *)message->wire.data;
    375  1.1  christos     offset = 0;
    376  1.1  christos     max = message->length - DNS_HEADER_SIZE;
    377  1.1  christos     int rr_index = 0;
    378  1.1  christos     int qdcount = ntohs(message->wire.qdcount);
    379  1.1  christos     while (offset < max) {
    380  1.1  christos         INFO("%d %d", offset, max);
    381  1.1  christos         if (rr_index >= qdcount) {
    382  1.1  christos             question = false;
    383  1.1  christos         }
    384  1.1  christos         if (!dns_rr_parse(&rr, message_bytes, max, &offset, !question, true)) {
    385  1.1  christos             TEST_FAIL_STATUS(push_state->test_state, "dns RR parse failed on rr %d", rr_index);
    386  1.1  christos             break;
    387  1.1  christos         }
    388  1.1  christos         if (!question) {
    389  1.1  christos             if (rdata_num < num_answers) {
    390  1.1  christos                 test_dns_push_update(push_state, &rr);
    391  1.1  christos                 rdata_num++;
    392  1.1  christos             }
    393  1.1  christos         }
    394  1.1  christos         dns_name_free(rr.name);
    395  1.1  christos         rr.name = NULL;
    396  1.1  christos         dns_rrdata_free(&rr);
    397  1.1  christos         rr_index++;
    398  1.1  christos     }
    399  1.1  christos     test_dns_push_satisfied_check(push_state);
    400  1.1  christos }
    401  1.1  christos 
    402  1.1  christos static void
    403  1.1  christos test_dns_push_dso_message(push_test_state_t *push_state, message_t *message, dso_state_t *dso, bool response)
    404  1.1  christos {
    405  1.1  christos     unsigned offset, max;
    406  1.1  christos     dns_rr_t rr;
    407  1.1  christos     uint8_t *message_bytes;
    408  1.1  christos 
    409  1.1  christos     switch(dso->primary.opcode) {
    410  1.1  christos     case kDSOType_RetryDelay:
    411  1.1  christos         if (response) {
    412  1.1  christos             TEST_FAIL(push_state->test_state, "server sent a retry delay TLV as a response.");
    413  1.1  christos         }
    414  1.1  christos         dso_retry_delay(dso, &message->wire);
    415  1.1  christos         break;
    416  1.1  christos 
    417  1.1  christos     case kDSOType_Keepalive:
    418  1.1  christos         if (response) {
    419  1.1  christos             TEST_FAIL_STATUS(push_state->test_state, "keepalive response from server, rcode = %d", dns_rcode_get(&message->wire));
    420  1.1  christos         } else {
    421  1.1  christos             INFO("Keepalive from server");
    422  1.1  christos         }
    423  1.1  christos 
    424  1.1  christos         // We need to wait for the first keepalive response before sending a DNS push subscribe, since until we get
    425  1.1  christos         // it we don't have a session. So this actually kicks off the first (possibly only) DNS Push subscribe in the
    426  1.1  christos         // test.
    427  1.1  christos         if (!push_state->push_subscribe_sent) {
    428  1.1  christos             push_state->have_keepalive_response = true;
    429  1.1  christos             if (!push_state->need_service) {
    430  1.1  christos                 test_dns_push_send_appropriate_subscribe(push_state);
    431  1.1  christos             }
    432  1.1  christos         }
    433  1.1  christos         break;
    434  1.1  christos 
    435  1.1  christos     case kDSOType_DNSPushSubscribe:
    436  1.1  christos         if (response) {
    437  1.1  christos             // This is a protocol error--the response isn't supposed to contain a primary TLV.
    438  1.1  christos             TEST_FAIL_STATUS(push_state->test_state,
    439  1.1  christos                              "DNS Push response from server, rcode = %d", dns_rcode_get(&message->wire));
    440  1.1  christos         } else {
    441  1.1  christos             INFO("Unexpected DNS Push request from server, rcode = %d", dns_rcode_get(&message->wire));
    442  1.1  christos         }
    443  1.1  christos         break;
    444  1.1  christos 
    445  1.1  christos     case kDSOType_DNSPushUpdate:
    446  1.1  christos         // DNS Push Updates are never responses.
    447  1.1  christos         // DNS Push updates are compressed, so we can't just parse data out of the primary--we need to align
    448  1.1  christos         // our parse with the start of the message data.
    449  1.1  christos         message_bytes = (uint8_t *)message->wire.data;
    450  1.1  christos         offset = (unsigned)(dso->primary.payload - message_bytes); // difference can never be greater than sizeof(message->wire).
    451  1.1  christos         max = offset + dso->primary.length;
    452  1.1  christos         while (offset < max) {
    453  1.1  christos             if (!dns_rr_parse(&rr, message_bytes, max, &offset, true, true)) {
    454  1.1  christos                 // Should have emitted an error earlier
    455  1.1  christos                 break;
    456  1.1  christos             }
    457  1.1  christos             test_dns_push_update(push_state, &rr);
    458  1.1  christos             dns_name_free(rr.name);
    459  1.1  christos             rr.name = NULL;
    460  1.1  christos             dns_rrdata_free(&rr);
    461  1.1  christos         }
    462  1.1  christos 
    463  1.1  christos         test_dns_push_satisfied_check(push_state);
    464  1.1  christos         break;
    465  1.1  christos 
    466  1.1  christos     case kDSOType_NoPrimaryTLV: // No Primary TLV
    467  1.1  christos         if (response) {
    468  1.1  christos             bool subscribe_acked = false;
    469  1.1  christos             for (int i = 0; i < push_state->num_subscribe_xids; i++) {
    470  1.1  christos                 if (message->wire.id == htons(push_state->subscribe_xids[i])) {
    471  1.1  christos                     int rcode = dns_rcode_get(&message->wire);
    472  1.1  christos                     INFO("DNS Push Subscribe response from server, rcode = %d", rcode);
    473  1.1  christos                     if (rcode != dns_rcode_noerror) {
    474  1.1  christos                         TEST_FAIL_STATUS(push_state->test_state, "subscribe for %x failed",
    475  1.1  christos                                          push_state->subscribe_xids[i]);
    476  1.1  christos                     }
    477  1.1  christos                     subscribe_acked = true;
    478  1.1  christos                 }
    479  1.1  christos             }
    480  1.1  christos             if (subscribe_acked) {
    481  1.1  christos             } else if (message->wire.id == push_state->keepalive_xid) {
    482  1.1  christos                 int rcode = dns_rcode_get(&message->wire);
    483  1.1  christos                 INFO("DNS Keepalive response from server, rcode = %d", rcode);
    484  1.1  christos                 exit(0);
    485  1.1  christos             } else {
    486  1.1  christos                 int rcode = dns_rcode_get(&message->wire);
    487  1.1  christos                 INFO("Unexpected DSO response from server, rcode = %d", rcode);
    488  1.1  christos             }
    489  1.1  christos         } else {
    490  1.1  christos             INFO("DSO request with no primary TLV.");
    491  1.1  christos             exit(1);
    492  1.1  christos         }
    493  1.1  christos         break;
    494  1.1  christos 
    495  1.1  christos     default:
    496  1.1  christos         INFO("dso_message: unexpected primary TLV %d", dso->primary.opcode);
    497  1.1  christos         dso_simple_response(push_state->dso_connection, NULL, &message->wire, dns_rcode_dsotypeni);
    498  1.1  christos         break;
    499  1.1  christos     }
    500  1.1  christos }
    501  1.1  christos 
    502  1.1  christos static void
    503  1.1  christos test_dns_push_dso_event_callback(void *context, void *event_context, dso_state_t *dso, dso_event_type_t eventType)
    504  1.1  christos {
    505  1.1  christos     push_test_state_t *push_state = context;
    506  1.1  christos 
    507  1.1  christos     message_t *message;
    508  1.1  christos     dso_query_receive_context_t *response_context;
    509  1.1  christos     dso_disconnect_context_t *disconnect_context;
    510  1.1  christos 
    511  1.1  christos     switch(eventType)
    512  1.1  christos     {
    513  1.1  christos     case kDSOEventType_DNSMessage:
    514  1.1  christos         // We shouldn't get here because we already handled any DNS messages
    515  1.1  christos         message = event_context;
    516  1.1  christos         INFO("DNS Message (opcode=%d) received from " PRI_S_SRP, dns_opcode_get(&message->wire),
    517  1.1  christos              dso->remote_name);
    518  1.1  christos         break;
    519  1.1  christos     case kDSOEventType_DNSResponse:
    520  1.1  christos         // We shouldn't get here because we already handled any DNS messages
    521  1.1  christos         message = event_context;
    522  1.1  christos         INFO("DNS Response (opcode=%d) received from " PRI_S_SRP, dns_opcode_get(&message->wire),
    523  1.1  christos              dso->remote_name);
    524  1.1  christos         break;
    525  1.1  christos     case kDSOEventType_DSOMessage:
    526  1.1  christos         INFO("DSO Message (Primary TLV=%d) received from " PRI_S_SRP,
    527  1.1  christos                dso->primary.opcode, dso->remote_name);
    528  1.1  christos         message = event_context;
    529  1.1  christos         test_dns_push_dso_message(push_state, message, dso, false);
    530  1.1  christos         break;
    531  1.1  christos     case kDSOEventType_DSOResponse:
    532  1.1  christos         INFO("DSO Response (Primary TLV=%d) received from " PRI_S_SRP,
    533  1.1  christos                dso->primary.opcode, dso->remote_name);
    534  1.1  christos         response_context = event_context;
    535  1.1  christos         message = response_context->message_context;
    536  1.1  christos         test_dns_push_dso_message(push_state, message, dso, true);
    537  1.1  christos         break;
    538  1.1  christos 
    539  1.1  christos     case kDSOEventType_Finalize:
    540  1.1  christos         INFO("Finalize");
    541  1.1  christos         break;
    542  1.1  christos 
    543  1.1  christos     case kDSOEventType_Connected:
    544  1.1  christos         INFO("Connected to " PRI_S_SRP, dso->remote_name);
    545  1.1  christos         break;
    546  1.1  christos 
    547  1.1  christos     case kDSOEventType_ConnectFailed:
    548  1.1  christos         INFO("Connection to " PRI_S_SRP " failed", dso->remote_name);
    549  1.1  christos         break;
    550  1.1  christos 
    551  1.1  christos     case kDSOEventType_Disconnected:
    552  1.1  christos         INFO("Connection to " PRI_S_SRP " disconnected", dso->remote_name);
    553  1.1  christos         if (dso == push_state->disconnect_expected) {
    554  1.1  christos             INFO("remote end disconnected as expected.");
    555  1.1  christos             exit(0);
    556  1.1  christos         }
    557  1.1  christos         break;
    558  1.1  christos     case kDSOEventType_ShouldReconnect:
    559  1.1  christos         INFO("Connection to " PRI_S_SRP " should reconnect (not for a server)", dso->remote_name);
    560  1.1  christos         break;
    561  1.1  christos     case kDSOEventType_Inactive:
    562  1.1  christos         INFO("Inactivity timer went off, closing connection.");
    563  1.1  christos         break;
    564  1.1  christos     case kDSOEventType_Keepalive:
    565  1.1  christos         INFO("should send a keepalive now.");
    566  1.1  christos         break;
    567  1.1  christos     case kDSOEventType_KeepaliveRcvd:
    568  1.1  christos         if (!push_state->push_subscribe_sent && !push_state->need_service) {
    569  1.1  christos             test_dns_push_send_appropriate_subscribe(push_state);
    570  1.1  christos         } else {
    571  1.1  christos             push_state->have_keepalive_response = true;
    572  1.1  christos         }
    573  1.1  christos         INFO("keepalive received.");
    574  1.1  christos         break;
    575  1.1  christos     case kDSOEventType_RetryDelay:
    576  1.1  christos         disconnect_context = event_context;
    577  1.1  christos         INFO("retry delay received, %d seconds", disconnect_context->reconnect_delay);
    578  1.1  christos         test_dns_push_handle_retry_delay(push_state, dso, disconnect_context->reconnect_delay);
    579  1.1  christos         break;
    580  1.1  christos     }
    581  1.1  christos }
    582  1.1  christos 
    583  1.1  christos static void
    584  1.1  christos test_dns_push_send_push_subscribe(push_test_state_t *push_state, const char *name, int rrtype)
    585  1.1  christos {
    586  1.1  christos     struct iovec iov;
    587  1.1  christos 
    588  1.1  christos     dns_wire_t dns_message;
    589  1.1  christos     uint8_t *buffer = (uint8_t *)&dns_message;
    590  1.1  christos     dns_towire_state_t towire;
    591  1.1  christos     dso_message_t message;
    592  1.1  christos     int i = push_state->num_subscribe_xids;
    593  1.1  christos     if (i >= SUBSCRIBE_LIMIT) {
    594  1.1  christos         TEST_FAIL_STATUS(push_state->test_state, "subscribe xid limit reached: %d", i);
    595  1.1  christos     }
    596  1.1  christos     push_state->num_subscribe_xids++;
    597  1.1  christos 
    598  1.1  christos     if (push_state->test_dns_push) {
    599  1.1  christos         // DNS Push subscription
    600  1.1  christos         dso_make_message(&message, buffer, sizeof(dns_message), push_state->dso_connection->dso, false, false, 0, 0, NULL);
    601  1.1  christos 
    602  1.1  christos         push_state->subscribe_xids[i] = ntohs(dns_message.id);
    603  1.1  christos         INFO("push subscribe for %s, rrtype %d, xid %x, num %d", name, rrtype, push_state->subscribe_xids[i], i);
    604  1.1  christos         memset(&towire, 0, sizeof(towire));
    605  1.1  christos         towire.p = &buffer[DNS_HEADER_SIZE];
    606  1.1  christos         towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
    607  1.1  christos         towire.message = &dns_message;
    608  1.1  christos         dns_u16_to_wire(&towire, kDSOType_DNSPushSubscribe);
    609  1.1  christos         dns_rdlength_begin(&towire);
    610  1.1  christos         dns_full_name_to_wire(NULL, &towire, name);
    611  1.1  christos         dns_u16_to_wire(&towire, rrtype);
    612  1.1  christos         dns_u16_to_wire(&towire, dns_qclass_in);
    613  1.1  christos         dns_rdlength_end(&towire);
    614  1.1  christos     } else {
    615  1.1  christos         // Regular DNS query
    616  1.1  christos         memset(&dns_message, 0, sizeof(dns_message));
    617  1.1  christos         dns_message.id = htons((uint16_t)srp_random16());
    618  1.1  christos         dns_qr_set(&dns_message, 0); // query
    619  1.1  christos         dns_opcode_set(&dns_message, dns_opcode_query);
    620  1.1  christos         int num_questions;
    621  1.1  christos         if (rrtype == dns_rrtype_srv && push_state->variant == PUSH_TEST_VARIANT_TWO_QUESTIONS) {
    622  1.1  christos             num_questions = 2;
    623  1.1  christos         } else {
    624  1.1  christos             num_questions = 1;
    625  1.1  christos         }
    626  1.1  christos         dns_message.qdcount = htons(num_questions);
    627  1.1  christos         memset(&towire, 0, sizeof(towire));
    628  1.1  christos         towire.p = &buffer[DNS_HEADER_SIZE];
    629  1.1  christos         towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
    630  1.1  christos         towire.message = &dns_message;
    631  1.1  christos         dns_name_pointer_t np;
    632  1.1  christos         dns_full_name_to_wire(&np, &towire, name);
    633  1.1  christos         dns_u16_to_wire(&towire, rrtype);
    634  1.1  christos         dns_u16_to_wire(&towire, dns_qclass_in);
    635  1.1  christos         if (num_questions == 2) {
    636  1.1  christos             dns_pointer_to_wire(NULL, &towire, &np);
    637  1.1  christos             dns_u16_to_wire(&towire, dns_rrtype_txt);
    638  1.1  christos             dns_u16_to_wire(&towire, dns_qclass_in);
    639  1.1  christos         }
    640  1.1  christos         push_state->subscribe_xids[i] = ntohs(dns_message.id);
    641  1.1  christos         INFO("DNS query for %s, rrtype %d, xid %x, num %d", name, rrtype, push_state->subscribe_xids[i], i);
    642  1.1  christos     }
    643  1.1  christos 
    644  1.1  christos     memset(&iov, 0, sizeof(iov));
    645  1.1  christos     iov.iov_len = towire.p - buffer;
    646  1.1  christos     iov.iov_base = buffer;
    647  1.1  christos     ioloop_send_message(push_state->dso_connection, NULL, &iov, 1);
    648  1.1  christos }
    649  1.1  christos 
    650  1.1  christos static void
    651  1.1  christos test_dns_push_connected(comm_t *connection, void *context)
    652  1.1  christos {
    653  1.1  christos     push_test_state_t *push_state = context;
    654  1.1  christos     struct iovec iov;
    655  1.1  christos     INFO("connected");
    656  1.1  christos     connection->dso = dso_state_create(false, 3, connection->name, test_dns_push_dso_event_callback,
    657  1.1  christos                                        push_state, NULL, push_state->dso_connection);
    658  1.1  christos     if (connection->dso == NULL) {
    659  1.1  christos         ERROR("can't create dso state object.");
    660  1.1  christos         exit(1);
    661  1.1  christos     }
    662  1.1  christos     dns_wire_t dns_message;
    663  1.1  christos     uint8_t *buffer = (uint8_t *)&dns_message;
    664  1.1  christos     dns_towire_state_t towire;
    665  1.1  christos     dso_message_t message;
    666  1.1  christos     dso_make_message(&message, buffer, sizeof(dns_message), connection->dso, false, false, 0, 0, NULL);
    667  1.1  christos     memset(&towire, 0, sizeof(towire));
    668  1.1  christos     towire.p = &buffer[DNS_HEADER_SIZE];
    669  1.1  christos     towire.lim = towire.p + (sizeof(dns_message) - DNS_HEADER_SIZE);
    670  1.1  christos     towire.message = &dns_message;
    671  1.1  christos     dns_u16_to_wire(&towire, kDSOType_Keepalive);
    672  1.1  christos     dns_rdlength_begin(&towire);
    673  1.1  christos     dns_u32_to_wire(&towire, 100); // Inactivity timeout
    674  1.1  christos     dns_u32_to_wire(&towire, 100); // Keepalive interval
    675  1.1  christos     dns_rdlength_end(&towire);
    676  1.1  christos 
    677  1.1  christos     memset(&iov, 0, sizeof(iov));
    678  1.1  christos     iov.iov_len = towire.p - buffer;
    679  1.1  christos     iov.iov_base = buffer;
    680  1.1  christos     ioloop_send_message(push_state->dso_connection, NULL, &iov, 1);
    681  1.1  christos }
    682  1.1  christos 
    683  1.1  christos static void
    684  1.1  christos test_dns_push_disconnected(comm_t *UNUSED connection, void *context, int UNUSED error)
    685  1.1  christos {
    686  1.1  christos     push_test_state_t *push_state = context;
    687  1.1  christos     TEST_FAIL(push_state->test_state, "push server disconnect.");
    688  1.1  christos }
    689  1.1  christos 
    690  1.1  christos static void
    691  1.1  christos test_dns_push_datagram_callback(comm_t *connection, message_t *message, void *context)
    692  1.1  christos {
    693  1.1  christos     push_test_state_t *push_state = context;
    694  1.1  christos 
    695  1.1  christos     // If this is a DSO message, see if we have a session yet.
    696  1.1  christos     switch(dns_opcode_get(&message->wire)) {
    697  1.1  christos     case dns_opcode_query:
    698  1.1  christos         test_dns_push_dns_response(push_state, message);
    699  1.1  christos         return;
    700  1.1  christos     case dns_opcode_dso:
    701  1.1  christos         if (connection->dso == NULL) {
    702  1.1  christos             INFO("dso message received with no DSO object on connection " PRI_S_SRP, connection->name);
    703  1.1  christos             exit(1);
    704  1.1  christos         }
    705  1.1  christos         dso_message_received(connection->dso, (uint8_t *)&message->wire, message->length, message);
    706  1.1  christos         return;
    707  1.1  christos     }
    708  1.1  christos     INFO("datagram on connection " PRI_S_SRP " not handled, type = %d.",
    709  1.1  christos          connection->name, dns_opcode_get(&message->wire));
    710  1.1  christos }
    711  1.1  christos 
    712  1.1  christos static void
    713  1.1  christos test_dns_push_ready(void *context, uint16_t UNUSED port)
    714  1.1  christos {
    715  1.1  christos     push_test_state_t *push_state = context;
    716  1.1  christos     test_state_t *state = push_state->test_state;
    717  1.1  christos 
    718  1.1  christos     addr_t address;
    719  1.1  christos     memset(&address, 0, sizeof(address));
    720  1.1  christos     address.sa.sa_family = AF_INET;
    721  1.1  christos     address.sin.sin_port = htons(8530);
    722  1.1  christos     address.sin.sin_addr.s_addr = htonl(0x7f000001);  // localhost.
    723  1.1  christos                                                       // tls, stream, stable, opportunistic
    724  1.1  christos     push_state->dso_connection = ioloop_connection_create(&address, true,   true,   true, true,
    725  1.1  christos                                                           test_dns_push_datagram_callback, test_dns_push_connected,
    726  1.1  christos                                                           test_dns_push_disconnected, NULL, push_state);
    727  1.1  christos     TEST_FAIL_CHECK(state, push_state->dso_connection != NULL, "Unable to create dso connection.");
    728  1.1  christos }
    729  1.1  christos 
    730  1.1  christos static bool
    731  1.1  christos test_listen_longevity_dnssd_proxy_configure(void)
    732  1.1  christos {
    733  1.1  christos     dnssd_proxy_udp_port= 53000;
    734  1.1  christos     dnssd_proxy_tcp_port = 53000;
    735  1.1  christos     dnssd_proxy_tls_port = 8530;
    736  1.1  christos     return true;
    737  1.1  christos }
    738  1.1  christos 
    739  1.1  christos static bool
    740  1.1  christos test_dns_push_query_callback_intercept(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    741  1.1  christos                                        DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype,
    742  1.1  christos                                        uint16_t rrclass, uint16_t rdlen, const void *vrdata, uint32_t ttl, void *context)
    743  1.1  christos {
    744  1.1  christos     dns_service_ref_t *ref = sdRef;
    745  1.1  christos     const uint8_t *rdata = vrdata;
    746  1.1  christos     const uint8_t *new_rdata = rdata;
    747  1.1  christos     uint8_t rdbuf[16];
    748  1.1  christos 
    749  1.1  christos     if (rrtype == dns_rrtype_a) {
    750  1.1  christos         if (rdata[0] == 169 && rdata[1] == 254) {
    751  1.1  christos             new_rdata = rdbuf;
    752  1.1  christos             rdbuf[0] = 10;
    753  1.1  christos             rdbuf[1] = 255;
    754  1.1  christos             rdbuf[2] = rdata[2];
    755  1.1  christos             rdbuf[3] = rdata[3];
    756  1.1  christos         }
    757  1.1  christos     } else if (rrtype == dns_rrtype_aaaa) {
    758  1.1  christos         if (rdata[0] == 0xfe && rdata[1] == 0x80) {
    759  1.1  christos             new_rdata = rdbuf;
    760  1.1  christos             rdbuf[0] = 0xfc; // Change to unused 0xFC address space
    761  1.1  christos             memcpy(&rdbuf[1], rdata + 1, 15); // Keep the rest.
    762  1.1  christos         }
    763  1.1  christos     }
    764  1.1  christos     DNSServiceQueryRecordReply callback = ref->callback.query_record_reply;
    765  1.1  christos     callback(ref, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdlen, new_rdata, ttl, context);
    766  1.1  christos     return false;
    767  1.1  christos }
    768  1.1  christos 
    769  1.1  christos static void test_dns_push_register_example_service(push_test_state_t *push_state);
    770  1.1  christos 
    771  1.1  christos static void
    772  1.1  christos test_dns_push_register_callback(const DNSServiceRef UNUSED sd_ref, const DNSServiceFlags UNUSED flags,
    773  1.1  christos                            const DNSServiceErrorType error, const char *const name, const char *const reg_type,
    774  1.1  christos                            const char *const domain, void *const context)
    775  1.1  christos {
    776  1.1  christos     push_test_state_t *push_state = context;
    777  1.1  christos     test_state_t *state = push_state->test_state;
    778  1.1  christos 
    779  1.1  christos     if (error == kDNSServiceErr_ServiceNotRunning) {
    780  1.1  christos         INFO("example service deregistered due to server exit");
    781  1.1  christos         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 10), // 100ms
    782  1.1  christos                        dispatch_get_main_queue(), ^{
    783  1.1  christos             test_dns_push_register_example_service(push_state);
    784  1.1  christos                        });
    785  1.1  christos     } else {
    786  1.1  christos         TEST_FAIL_CHECK_STATUS(state, error == kDNSServiceErr_NoError, "example service registration failed: %d", error);
    787  1.1  christos         INFO("example service registered successfully -- %s.%s%s", name, reg_type, domain);
    788  1.1  christos         if (push_state->need_service) {
    789  1.1  christos             push_state->need_service = false;
    790  1.1  christos             if (push_state->have_keepalive_response) {
    791  1.1  christos                 test_dns_push_send_appropriate_subscribe(push_state);
    792  1.1  christos             }
    793  1.1  christos         }
    794  1.1  christos     }
    795  1.1  christos }
    796  1.1  christos 
    797  1.1  christos static void
    798  1.1  christos test_dns_push_register_example_service(push_test_state_t *push_state)
    799  1.1  christos {
    800  1.1  christos     uint8_t txt_data[] = {
    801  1.1  christos         0x08, 0x53, 0x49, 0x49, 0x3D, 0x35, 0x30, 0x30, 0x30, 0x07,
    802  1.1  christos         0x53, 0x41, 0x49, 0x3D, 0x33, 0x30, 0x30, 0x03, 0x54, 0x3D, 0x30 };
    803  1.1  christos 
    804  1.1  christos     int ret = DNSServiceRegister(&push_state->register_ref, 0, kDNSServiceInterfaceIndexAny, NULL,
    805  1.1  christos                                  "_example._tcp", NULL, NULL, 12345, sizeof(txt_data), txt_data,
    806  1.1  christos                                  test_dns_push_register_callback, push_state);
    807  1.1  christos     TEST_FAIL_CHECK(push_state->test_state, ret == kDNSServiceErr_NoError, "failed to register example service.");
    808  1.1  christos     DNSServiceSetDispatchQueue(push_state->register_ref, dispatch_get_main_queue());
    809  1.1  christos }
    810  1.1  christos 
    811  1.1  christos static DNSServiceErrorType
    812  1.1  christos test_dns_push_crash_intercept(test_state_t *state, DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    813  1.1  christos                          const char *fullname, uint16_t rrtype, uint16_t rrclass, DNSServiceAttribute const *attr,
    814  1.1  christos                          DNSServiceQueryRecordReply callBack, void UNUSED *context)
    815  1.1  christos {
    816  1.1  christos     dns_service_ref_t *ref = context;
    817  1.1  christos     DNSServiceErrorType status = DNSServiceQueryRecordWithAttribute(sdRef, flags, interfaceIndex, fullname,
    818  1.1  christos                                                                     rrtype, rrclass, attr, callBack, ref);
    819  1.1  christos     // We're going to signal the daemon crash on the PTR query.
    820  1.1  christos     push_test_state_t *push_state = state->context;
    821  1.1  christos     if (status == kDNSServiceErr_NoError && rrtype == dns_rrtype_ptr) {
    822  1.1  christos         push_state->ptr_sdref = ref;
    823  1.1  christos     } else {
    824  1.1  christos         if (push_state->num_txns < TXN_LIMIT) {
    825  1.1  christos             push_state->txns[push_state->num_txns++] = ref;
    826  1.1  christos         }
    827  1.1  christos     }
    828  1.1  christos     return status;
    829  1.1  christos }
    830  1.1  christos 
    831  1.1  christos void
    832  1.1  christos test_dns_push(test_state_t *next_test, int variant)
    833  1.1  christos {
    834  1.1  christos     extern srp_server_t *srp_servers;
    835  1.1  christos     test_state_t *state = NULL;
    836  1.1  christos     bool register_example_service = false;
    837  1.1  christos     bool push = true;
    838  1.1  christos     if (variant == PUSH_TEST_VARIANT_HARDWIRED) {
    839  1.1  christos         const char *hardwired =
    840  1.1  christos             "  The goal of this test is to create DNS Push connection to the test server and attempt to\n"
    841  1.1  christos             "  look up a name that goes through the hardwired query path. If we get a response, the test\n"
    842  1.1  christos             "  succeeded.";
    843  1.1  christos         state = test_state_create(srp_servers, "DNS Push Hardwired test", NULL, hardwired, NULL);
    844  1.1  christos     } else if (variant == PUSH_TEST_VARIANT_MDNS) {
    845  1.1  christos         const char *mdns =
    846  1.1  christos             "  The goal of this test is to create DNS Push connection to the test server and attempt to\n"
    847  1.1  christos             "  look up a name that goes through the local (mDNS) query path. If we get a response, the test\n"
    848  1.1  christos             "  succeeded.";
    849  1.1  christos         state = test_state_create(srp_servers, "DNS Push Local test", NULL, mdns, NULL);
    850  1.1  christos         register_example_service = true;
    851  1.1  christos     } else if (variant == PUSH_TEST_VARIANT_DNS_MDNS) {
    852  1.1  christos         const char *mdns =
    853  1.1  christos             "  The goal of this test is to create DSO connection to the test server and send a DNS query to\n"
    854  1.1  christos             "  look up a name that goes through the local (mDNS) query path. If we get a response, the test\n"
    855  1.1  christos             "  succeeded.";
    856  1.1  christos         state = test_state_create(srp_servers, "DNS Local test", NULL, mdns, NULL);
    857  1.1  christos         register_example_service = true;
    858  1.1  christos         push = false;
    859  1.1  christos         variant = PUSH_TEST_VARIANT_MDNS;
    860  1.1  christos     } else if (variant == PUSH_TEST_VARIANT_TWO_QUESTIONS) {
    861  1.1  christos         const char *two =
    862  1.1  christos             "  The goal of this test is to create DSO connection to the test server and send a DNS query with\n"
    863  1.1  christos             "  two questions on the same name, for a TXT and an SRV record. We will register a matter service to\n"
    864  1.1  christos             "  discover.  If we get a well-formed response with answers for both service types, the test\n"
    865  1.1  christos             "  succeeded.";
    866  1.1  christos         state = test_state_create(srp_servers, "DNS Local two-question test", NULL, two, NULL);
    867  1.1  christos         register_example_service = true;
    868  1.1  christos         push = false;
    869  1.1  christos         variant = PUSH_TEST_VARIANT_TWO_QUESTIONS;
    870  1.1  christos     } else if (variant == PUSH_TEST_VARIANT_DNS_HARDWIRED) {
    871  1.1  christos         const char *hardwired =
    872  1.1  christos             "  The goal of this test is to create DSO connection to the test server and send a DNS query that\n"
    873  1.1  christos             "  looks up a name that goes through the hardwired query path. If we get a response, the test\n"
    874  1.1  christos             "  succeeded.";
    875  1.1  christos         state = test_state_create(srp_servers, "DNS query Hardwired test", NULL, hardwired, NULL);
    876  1.1  christos         push = false;
    877  1.1  christos         variant = PUSH_TEST_VARIANT_HARDWIRED;
    878  1.1  christos     } else if (variant == PUSH_TEST_VARIANT_DNS_CRASH) {
    879  1.1  christos         const char *crash =
    880  1.1  christos         "  The goal of this test is to create DSO connection to the test server and attempt to\n"
    881  1.1  christos         "  look up a name that goes through the local (mDNS) query path. Once we have a result, we fake\n"
    882  1.1  christos         "  an mDNSResponder crash and make sure the query is successfully restarted.";
    883  1.1  christos         state = test_state_create(srp_servers, "DNS query daemon crash test", NULL, crash, NULL);
    884  1.1  christos         state->query_record_intercept = test_dns_push_crash_intercept;
    885  1.1  christos         register_example_service = true;
    886  1.1  christos         variant = PUSH_TEST_VARIANT_DAEMON_CRASH;
    887  1.1  christos         push = false;
    888  1.1  christos     } else if (variant == PUSH_TEST_VARIANT_DAEMON_CRASH) {
    889  1.1  christos         const char *crash =
    890  1.1  christos             "  The goal of this test is to create DNS Push connection to the test server and attempt to\n"
    891  1.1  christos             "  look up a name that goes through the local (mDNS) query path. Once we have a result, we fake\n"
    892  1.1  christos             "  an mDNSResponder crash and make sure the query is successfully restarted.";
    893  1.1  christos         state = test_state_create(srp_servers, "DNS Push daemon crash test", NULL, crash, NULL);
    894  1.1  christos         state->query_record_intercept = test_dns_push_crash_intercept;
    895  1.1  christos         register_example_service = true;
    896  1.1  christos     }
    897  1.1  christos     state->next = next_test;
    898  1.1  christos     push_test_state_t *push_state = calloc(1, sizeof (*push_state));
    899  1.1  christos     TEST_FAIL_CHECK(state, push_state != NULL, "no memory for test-specific state.");
    900  1.1  christos     state->context = push_state;
    901  1.1  christos     push_state->test_state = state;
    902  1.1  christos     push_state->variant = variant;
    903  1.1  christos     push_state->test_dns_push = push;
    904  1.1  christos 
    905  1.1  christos     // Might as well always test
    906  1.1  christos     push_state->push_unsubscribe = true;
    907  1.1  christos 
    908  1.1  christos     srp_test_dnssd_tls_listener_ready = test_dns_push_ready;
    909  1.1  christos     srp_test_tls_listener_context = push_state;
    910  1.1  christos     srp_test_dso_message_finished = test_dns_push_dso_message_finished;
    911  1.1  christos 
    912  1.1  christos     srp_proxy_init("local");
    913  1.1  christos     srp_test_enable_stub_router(state, srp_servers);
    914  1.1  christos     state->dns_service_query_callback_intercept = test_dns_push_query_callback_intercept;
    915  1.1  christos     state->dnssd_proxy_configurer = test_listen_longevity_dnssd_proxy_configure;
    916  1.1  christos     TEST_FAIL_CHECK(state, init_dnssd_proxy(srp_servers), "failed to setup dnssd-proxy");
    917  1.1  christos 
    918  1.1  christos     if (register_example_service) {
    919  1.1  christos         push_state->need_service = true;
    920  1.1  christos         test_dns_push_register_example_service(push_state);
    921  1.1  christos     }
    922  1.1  christos 
    923  1.1  christos     // Test should not take longer than ten seconds.
    924  1.1  christos     srp_test_state_add_timeout(state, 20);
    925  1.1  christos }
    926  1.1  christos 
    927  1.1  christos // Local Variables:
    928  1.1  christos // mode: C
    929  1.1  christos // tab-width: 4
    930  1.1  christos // c-file-style: "bsd"
    931  1.1  christos // c-basic-offset: 4
    932  1.1  christos // fill-column: 108
    933  1.1  christos // indent-tabs-mode: nil
    934  1.1  christos // End:
    935