1 /* test-api.c 2 * 3 * Copyright (c) 2023-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 test harness 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 #include "test-api.h" 39 #include "srp-proxy.h" 40 #include "srp-mdns-proxy.h" 41 #include "dnssd-proxy.h" 42 #include "route.h" 43 44 #define SRP_IO_CONTEXT_MAGIC 0xFEEDFACEFADEBEEFULL // BEES! Everybody gets BEES! 45 typedef struct io_context { 46 uint64_t magic_cookie1; 47 wakeup_t *wakeup; 48 void *NONNULL srp_context; 49 void *NONNULL host_context; 50 comm_t *NULLABLE connection; 51 srp_wakeup_callback_t wakeup_callback; 52 srp_datagram_callback_t datagram_callback; 53 bool deactivated, closed; 54 uint64_t magic_cookie2; 55 } io_context_t; 56 57 // For testing signature with a time that's out of range. 58 static bool test_bad_sig_time; 59 // For testing a signature that doesn't validate 60 static bool invalidate_signature; 61 62 bool 63 configure_dnssd_proxy(void) 64 { 65 extern srp_server_t *srp_servers; 66 if (srp_servers->test_state != NULL && srp_servers->test_state->dnssd_proxy_configurer != NULL) { 67 return srp_servers->test_state->dnssd_proxy_configurer(); 68 } else { 69 dnssd_proxy_udp_port= 53; 70 dnssd_proxy_tcp_port = 53; 71 dnssd_proxy_tls_port = 853; 72 return true; 73 } 74 } 75 76 int 77 srp_test_getifaddrs(srp_server_t *server_state, struct ifaddrs **ifaddrs, void *context) 78 { 79 if (server_state->test_state != NULL && server_state->test_state->getifaddrs != NULL) { 80 return server_state->test_state->getifaddrs(server_state, ifaddrs, context); 81 } 82 return getifaddrs(ifaddrs); 83 } 84 85 void 86 srp_test_freeifaddrs(srp_server_t *server_state, struct ifaddrs *ifaddrs, void *context) 87 { 88 if (server_state->test_state != NULL && server_state->test_state->freeifaddrs != NULL) { 89 server_state->test_state->freeifaddrs(server_state, ifaddrs, context); 90 return; 91 } 92 freeifaddrs(ifaddrs); 93 } 94 95 void 96 srp_test_state_add_timeout(test_state_t *state, int timeout) 97 { 98 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * timeout), dispatch_get_main_queue(), ^{ 99 if (!state->test_complete) { 100 TEST_FAIL(state, "test failed: timeout"); 101 exit(1); 102 } 103 }); 104 } 105 106 void 107 srp_test_state_next(test_state_t *state) 108 { 109 if (state->next != NULL) { 110 test_state_t *next_state = state->next; 111 next_state->test_complete = true; 112 next_state->finished_tests = state; 113 if (next_state->continue_testing == NULL) { 114 TEST_FAIL(next_state, "no continue function"); 115 } 116 next_state->continue_testing(next_state); 117 } else { 118 exit(0); 119 } 120 } 121 122 void 123 srp_test_state_explain(test_state_t *state) 124 { 125 if (state != NULL) { 126 if (state->variant_title != NULL) { 127 fprintf(stderr, "\n%s (%s variant)\n", state->title, state->variant_title); 128 } else { 129 fprintf(stderr, "\n%s\n", state->title); 130 } 131 fprintf(stderr, "\n%s\n\n", state->explanation); 132 if (state->variant_info != NULL) { 133 fprintf(stderr, "Variant: %s\n\n", state->variant_info); 134 } 135 } 136 } 137 138 test_state_t * 139 test_state_create(srp_server_t *primary, const char *title, const char *variant_title, 140 const char *explanation, const char *variant_info) 141 { 142 test_state_t *ret = calloc(1, sizeof(*ret)); 143 TEST_FAIL_CHECK(NULL, ret != NULL, "no memory for test state"); 144 ret->primary = primary; 145 primary->test_state = ret; 146 ret->title = title; 147 ret->variant_title = variant_title; 148 ret->explanation = explanation; // Explanation is assumed to be a compile-time constant string. 149 ret->variant_info = variant_info; 150 return ret; 151 } 152 153 void 154 srp_test_set_local_example_address(test_state_t *UNUSED state) 155 { 156 static const uint8_t ifaddr[] = { 157 0x20, 1, 0xd, 0xb8, // 2001:0db8: 158 0, 0, 0, 0, // /64 prefix 159 0, 0, 0, 0, 160 0, 0, 0, 1, // 2001:db8::1 161 }; 162 srp_add_interface_address(dns_rrtype_aaaa, ifaddr, sizeof(ifaddr)); 163 } 164 165 void 166 srp_test_network_localhost_start(test_state_t *UNUSED state) 167 { 168 static const uint8_t localhost[] = { 169 0, 0, 0, 0, 170 0, 0, 0, 0, // /64 prefix 171 0, 0, 0, 0, 172 0, 0, 0, 1, // ::1 173 }; 174 static const uint8_t port[] = { 0, 53 }; 175 176 srp_add_server_address(port, dns_rrtype_aaaa, localhost, sizeof(localhost)); 177 srp_test_set_local_example_address(state); 178 179 srp_network_state_stable(NULL); 180 } 181 182 bool 183 srp_get_last_server(uint16_t *NONNULL UNUSED rrtype, uint8_t *NONNULL UNUSED rdata, uint16_t UNUSED rdlim, 184 uint8_t *NONNULL UNUSED port, void *NULLABLE UNUSED host_context) 185 { 186 return false; 187 } 188 189 bool 190 srp_save_last_server(uint16_t UNUSED rrtype, uint8_t *UNUSED rdata, uint16_t UNUSED rdlength, 191 uint8_t *UNUSED port, void *UNUSED host_context) 192 { 193 return false; 194 } 195 196 static int 197 validate_io_context(io_context_t **dest, void *src) 198 { 199 io_context_t *context = src; 200 if (context->magic_cookie1 == SRP_IO_CONTEXT_MAGIC && 201 context->magic_cookie2 == SRP_IO_CONTEXT_MAGIC) 202 { 203 *dest = context; 204 return kDNSServiceErr_NoError; 205 } 206 return kDNSServiceErr_BadState; 207 } 208 209 int 210 srp_deactivate_udp_context(void *host_context, void *in_context) 211 { 212 io_context_t *io_context; 213 int err; 214 (void)host_context; 215 216 err = validate_io_context(&io_context, in_context); 217 if (err == kDNSServiceErr_NoError) { 218 if (io_context->wakeup != NULL) { 219 ioloop_cancel_wake_event(io_context->wakeup); 220 ioloop_wakeup_release(io_context->wakeup); 221 } 222 // Deactivate can be called with a connection still active; in this case, we need to wait for the 223 // cancel event before freeing the structure. Otherwise, we can free it immediately. 224 if (io_context->connection != NULL) { 225 ioloop_comm_cancel(io_context->connection); 226 io_context->deactivated = true; 227 io_context->closed = true; 228 } else { 229 free(io_context); 230 } 231 } 232 return err; 233 } 234 235 int 236 srp_disconnect_udp(void *context) 237 { 238 io_context_t *io_context; 239 int err; 240 241 err = validate_io_context(&io_context, context); 242 if (err == kDNSServiceErr_NoError) { 243 if (io_context->wakeup != NULL) { 244 ioloop_cancel_wake_event(io_context->wakeup); 245 } 246 if (io_context->connection) { 247 io_context->connection = NULL; 248 } 249 io_context->closed = true; 250 } 251 return err; 252 } 253 254 static bool 255 srp_test_send_intercept(comm_t *connection, message_t *UNUSED responding_to, 256 struct iovec *iov, int iov_len, bool UNUSED final, bool send_length) 257 { 258 bool send_length_real = send_length; 259 srp_server_t *srp_server = connection->test_context; 260 test_state_t *test_state = srp_server->test_state; 261 io_context_t *current_io_context = test_state->current_io_context; 262 TEST_FAIL_CHECK(test_state, test_state != NULL, "no test state"); 263 TEST_FAIL_CHECK(test_state, current_io_context != NULL, "no I/O state"); 264 TEST_FAIL_CHECK(test_state, current_io_context->datagram_callback != NULL, "no datagram callback"); 265 266 // Don't copy if we don't have to. 267 if (!send_length && iov_len == 1) { 268 size_t len = iov[0].iov_len; 269 uint8_t *data = calloc(1, len); 270 memcpy(data, iov[0].iov_base, len); 271 dispatch_async(dispatch_get_main_queue(), ^{ 272 current_io_context->datagram_callback(current_io_context, data, len); 273 free(data); 274 }); 275 return true; 276 } 277 278 // send_length indicates whether we should send a length over TCP, not whether we should send a length. 279 if (!connection->tcp_stream) { 280 send_length_real = false; 281 } 282 283 // We have an actual iov, or need to prepend a length, so we have to allocate and copy. 284 uint8_t *message; 285 size_t length = send_length_real ? 2 : 0; 286 uint8_t *mp; 287 for (int i = 0; i < iov_len; i++) { 288 length += iov[i].iov_len; 289 } 290 291 message = malloc(length); 292 TEST_FAIL_CHECK(test_state, message != NULL, "no memory for message"); 293 mp = message; 294 // Marshal all the data into a single buffer. 295 if (send_length_real) { 296 *mp++ = length >> 8; 297 *mp++ = length & 0xff; 298 } 299 for (int i = 0; i < iov_len; i++) { 300 memcpy(mp, iov[i].iov_base, iov[i].iov_len); 301 mp += iov[i].iov_len; 302 } 303 304 dispatch_async(dispatch_get_main_queue(), ^{ 305 current_io_context->datagram_callback(current_io_context->srp_context, message, length); 306 }); 307 return true; 308 } 309 310 int 311 srp_connect_udp(void *context, const uint8_t *UNUSED port, uint16_t UNUSED address_type, 312 const uint8_t *UNUSED address, uint16_t UNUSED addrlen) 313 { 314 io_context_t *io_context; 315 int err; 316 317 err = validate_io_context(&io_context, context); 318 if (err == kDNSServiceErr_NoError) { 319 if (io_context->connection) { 320 ERROR("srp_connect_udp called with non-null I/O context."); 321 return kDNSServiceErr_Invalid; 322 } 323 324 srp_server_t *server_state = io_context->host_context; 325 test_state_t *test_state = server_state->test_state; 326 if (test_state == NULL || test_state->srp_listener == NULL) { 327 return kDNSServiceErr_NotInitialized; 328 } 329 io_context->connection = test_state->srp_listener; 330 test_state->current_io_context = io_context; 331 io_context->connection->test_send_intercept = srp_test_send_intercept; 332 io_context->connection->test_context = server_state; 333 } 334 return err; 335 } 336 337 int 338 srp_make_udp_context(void *host_context, void **p_context, srp_datagram_callback_t callback, void *context) 339 { 340 io_context_t *io_context = calloc(1, sizeof *io_context); 341 if (io_context == NULL) { 342 return kDNSServiceErr_NoMemory; 343 } 344 io_context->magic_cookie1 = io_context->magic_cookie2 = SRP_IO_CONTEXT_MAGIC; 345 io_context->datagram_callback = callback; 346 io_context->srp_context = context; 347 io_context->host_context = host_context; 348 349 io_context->wakeup = ioloop_wakeup_create(); 350 if (io_context->wakeup == NULL) { 351 free(io_context); 352 return kDNSServiceErr_NoMemory; 353 } 354 355 *p_context = io_context; 356 return kDNSServiceErr_NoError; 357 } 358 359 static void 360 wakeup_callback(void *context) 361 { 362 io_context_t *io_context; 363 if (validate_io_context(&io_context, context) == kDNSServiceErr_NoError) { 364 INFO("wakeup on context %p srp_context %p", io_context, io_context->srp_context); 365 INFO("setting wakeup callback %p wakeup %p", io_context->wakeup_callback, io_context->wakeup); 366 if (!io_context->deactivated) { 367 io_context->wakeup_callback(io_context->srp_context); 368 } 369 } else { 370 INFO("wakeup with invalid context: %p", context); 371 } 372 } 373 374 int 375 srp_set_wakeup(void *host_context, void *context, int milliseconds, srp_wakeup_callback_t callback) 376 { 377 int err; 378 io_context_t *io_context; 379 (void)host_context; 380 381 err = validate_io_context(&io_context, context); 382 if (err == kDNSServiceErr_NoError) { 383 INFO("setting wakeup callback %p wakeup %p", callback, io_context->wakeup); 384 io_context->wakeup_callback = callback; 385 ioloop_add_wake_event(io_context->wakeup, io_context, wakeup_callback, NULL, milliseconds); 386 } 387 return err; 388 } 389 390 int 391 srp_cancel_wakeup(void *host_context, void *context) 392 { 393 int err; 394 io_context_t *io_context; 395 (void)host_context; 396 397 err = validate_io_context(&io_context, context); 398 if (err == kDNSServiceErr_NoError) { 399 ioloop_cancel_wake_event(io_context->wakeup); 400 } 401 return err; 402 } 403 404 int 405 srp_send_datagram(void *host_context, void *context, void *message, size_t message_length) 406 { 407 int err; 408 io_context_t *io_context; 409 srp_server_t *srp_server = host_context; 410 test_state_t *test_state = srp_server->test_state; 411 412 if (invalidate_signature) { 413 ((uint8_t *)message)[message_length - 10] = ~(((uint8_t *)message)[message_length - 10]); 414 } 415 416 err = validate_io_context(&io_context, context); 417 if (err == kDNSServiceErr_NoError) { 418 if (io_context->connection == NULL) { 419 return kDNSServiceErr_DefunctConnection; 420 } 421 TEST_FAIL_CHECK(test_state, io_context->connection->datagram_callback != NULL, "srp listener has no datagram callback"); 422 message_t *actual = ioloop_message_create(message_length); 423 TEST_FAIL_CHECK(test_state, actual != NULL, "no memory for message"); 424 memcpy(&actual->wire, message, message_length); 425 io_context->connection->datagram_callback(io_context->connection, actual, io_context->connection->context); 426 ioloop_message_release(actual); 427 } 428 return err; 429 } 430 431 uint32_t 432 srp_timenow(void) 433 { 434 time_t now = time(NULL); 435 if (test_bad_sig_time) { 436 return (uint32_t)(now - 10000); 437 } 438 return (uint32_t)now; 439 } 440 441 void 442 srp_test_enable_stub_router(test_state_t *state, srp_server_t *NONNULL server_state) 443 { 444 server_state->route_state = route_state_create(server_state, "srp-mdns-proxy"); 445 TEST_FAIL_CHECK(state, server_state->route_state != NULL, "no memory for route state"); 446 server_state->stub_router_enabled = true; 447 } 448 449 // Local Variables: 450 // mode: C 451 // tab-width: 4 452 // c-file-style: "bsd" 453 // c-basic-offset: 4 454 // fill-column: 108 455 // indent-tabs-mode: nil 456 // End: 457