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