Home | History | Annotate | Line # | Download | only in tests
      1 /* dangling-query.c
      2  *
      3  * Copyright (c) 2024 Apple Inc. All rights reserved.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     https://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  * This file contains the SRP server test runner.
     18  */
     19 
     20 #include "srp.h"
     21 #include <dns_sd.h>
     22 #include "srp-test-runner.h"
     23 #include "srp-api.h"
     24 #include "dns-msg.h"
     25 #include "ioloop.h"
     26 #include "srp-mdns-proxy.h"
     27 #include "test-api.h"
     28 #include "srp-proxy.h"
     29 #include "srp-mdns-proxy.h"
     30 #include "test-dnssd.h"
     31 #include "test.h"
     32 #include "dnssd-proxy.h"
     33 
     34 static bool
     35 test_dns_dangling_query_response_intercept(comm_t *UNUSED connection, message_t *UNUSED responding_to,
     36                                            struct iovec *UNUSED iov, int UNUSED iov_len, bool UNUSED final,
     37                                            bool UNUSED send_length)
     38 {
     39     test_state_t *state = connection->test_context;
     40     if (state->counter == 0) {
     41         state->counter = 1;
     42         TEST_FAIL_CHECK(state, state->context != NULL && state->context != last_freed_domain,
     43                         "domain was freed after the first query finished.");
     44         return true;
     45     }
     46     dispatch_async(dispatch_get_main_queue(), ^{
     47             TEST_FAIL_CHECK(state, state->context != NULL && state->context == last_freed_domain,
     48                             "domain wasn't freed last.");
     49             TEST_PASSED(state);
     50         });
     51     return true;
     52 }
     53 
     54 static void
     55 test_dns_dangling_query_ready(void *context, uint16_t UNUSED port)
     56 {
     57     test_state_t *state = context;
     58 
     59     // Hook our callback in to the srp input connection.
     60     state->srp_listener->test_send_intercept = test_dns_dangling_query_response_intercept;
     61     state->srp_listener->test_context = state;
     62 
     63     // Generate a DNS query that's in the infrastructure pseudo interface domain.
     64     message_t *message = ioloop_message_create(DNS_HEADER_SIZE + 256); // should be plenty of room
     65     TEST_FAIL_CHECK(state, message != NULL, "no memory for DNS query");
     66     dns_wire_t *wire = &message->wire;
     67     dns_towire_state_t towire;
     68     memset(&towire, 0, sizeof(towire));
     69 
     70     towire.p = &wire->data[0];               // We start storing RR data here.
     71     towire.lim = &wire->data[DNS_DATA_SIZE]; // This is the limit to how much we can store.
     72     towire.message = wire;
     73 
     74     wire->id = srp_random16();
     75     wire->bitfield = 0;
     76     dns_qr_set(wire, dns_qr_query);
     77     dns_opcode_set(wire, dns_opcode_query);
     78     wire->qdcount = htons(1);
     79 
     80     // A browse query that probably will get at least one answer
     81     dns_full_name_to_wire(NULL, &towire, "_airplay._tcp.local.");
     82     dns_u16_to_wire(&towire, dns_rrtype_ptr);
     83     dns_u16_to_wire(&towire, dns_qclass_in);
     84     // No ttl or length or rdata because question
     85 
     86     dns_proxy_input_for_server(state->srp_listener, state->primary, message, NULL);
     87 
     88     // second query
     89     towire.p = &wire->data[0];               // We start storing RR data here.
     90     towire.lim = &wire->data[DNS_DATA_SIZE]; // This is the limit to how much we can store.
     91     towire.message = wire;
     92 
     93     wire->id = srp_random16();
     94     wire->bitfield = 0;
     95     // A browse query that probably will get at least one answer
     96     dns_full_name_to_wire(NULL, &towire, "_companion-link._tcp.local.");
     97     dns_u16_to_wire(&towire, dns_rrtype_ptr);
     98     dns_u16_to_wire(&towire, dns_qclass_in);
     99     // No ttl or length or rdata because question
    100 
    101     dns_proxy_input_for_server(state->srp_listener, state->primary, message, NULL);
    102 
    103     state->context = delete_served_domain_by_interface_name(INFRASTRUCTURE_PSEUDO_INTERFACE);
    104     TEST_FAIL_CHECK(state, state->context != NULL && state->context != last_freed_domain,
    105                     "domain was freed immediately (way too soon).");
    106 }
    107 
    108 static bool
    109 test_listen_dangling_dnssd_proxy_configure(void)
    110 {
    111     dnssd_proxy_udp_port= 5300;
    112     dnssd_proxy_tcp_port = 5300;
    113     dnssd_proxy_tls_port = 8530;
    114     return true;
    115 }
    116 
    117 void
    118 test_dns_dangling_query(test_state_t *next_test)
    119 {
    120     extern srp_server_t *srp_servers;
    121     const char *description =
    122         "  The goal of this test is to create an interface-specific served domain, start two queries in that\n"
    123 		"  domain, then delete the domain, then wait for the query to finish. This should not (obvsly)\n"
    124         "  crash. In addition, when the queries finish, the test domain should be freed.";
    125     test_state_t *state = test_state_create(srp_servers, "Dangling Query test", NULL, description, NULL);
    126     state->next = next_test;
    127 
    128     srp_proxy_init("local");
    129     srp_test_enable_stub_router(state, srp_servers);
    130     state->dnssd_proxy_configurer = test_listen_dangling_dnssd_proxy_configure;
    131     TEST_FAIL_CHECK(state, init_dnssd_proxy(srp_servers), "failed to setup dnssd-proxy");
    132 
    133     // Start the srp listener.
    134     state->srp_listener = srp_proxy_listen(NULL, 0, NULL, test_dns_dangling_query_ready, NULL, NULL, NULL, state);
    135 
    136     // Test should not take longer than ten seconds.
    137     srp_test_state_add_timeout(state, 10);
    138 }
    139 
    140 // Local Variables:
    141 // mode: C
    142 // tab-width: 4
    143 // c-file-style: "bsd"
    144 // c-basic-offset: 4
    145 // fill-column: 108
    146 // indent-tabs-mode: nil
    147 // End:
    148