Home | History | Annotate | Line # | Download | only in test
      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