Home | History | Annotate | Line # | Download | only in test
      1 /* test-dnssd.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  * DNSSD intercept API for testing srp-mdns-proxy
     18  */
     19 
     20 #include <dns_sd.h>
     21 #include "srp.h"
     22 #include "srp-test-runner.h"
     23 #include "srp-api.h"
     24 #include "dns-msg.h"
     25 #include "ioloop.h"
     26 #include "srp-proxy.h"
     27 #include "srp-dnssd.h"
     28 #include "srp-mdns-proxy.h"
     29 #include "test-api.h"
     30 #include "test-dnssd.h"
     31 
     32 static char *
     33 dns_service_rdata_to_text(test_state_t *state, int rrtype, const uint8_t *rdata, uint16_t rdlen, char *outbuf, size_t buflen)
     34 {
     35     dns_rr_t rr;
     36     unsigned offp = 0;
     37 
     38     rr.type = rrtype;
     39     TEST_FAIL_CHECK(state, dns_rdata_parse_data(&rr, rdata, &offp, rdlen, rdlen, 0), "rr parse failed");
     40     dns_rdata_dump_to_buf(&rr, outbuf, buflen);
     41     return outbuf;
     42 }
     43 
     44 static void
     45 dns_service_dump_event(test_state_t *state, dns_service_event_t *event, dns_service_event_t *events)
     46 {
     47     char rrbuf[1024];
     48     char *rr_printed;
     49     int rrtype = 0;
     50     uint16_t rdlen;
     51     uint8_t *rdata;
     52 #define STR_OR_NULL(str) ((str) != NULL ? str : "<null>")
     53     switch(event->event_type) {
     54     case dns_service_event_type_register:
     55         rr_printed = dns_service_rdata_to_text(state, dns_rrtype_txt, event->rdata, event->rdlen, rrbuf, sizeof(rrbuf));
     56         INFO("         register: %s.%s.%s port %d IN %s (%p %p) -> %d", STR_OR_NULL(event->name),
     57              STR_OR_NULL(event->regtype), STR_OR_NULL(event->domain), event->port, STR_OR_NULL(rr_printed),
     58              (char *)event->sdref, (char *)event->parent_sdref, event->status);
     59         break;
     60     case dns_service_event_type_register_record:
     61         rr_printed = dns_service_rdata_to_text(state, event->rrtype, event->rdata, event->rdlen, rrbuf, sizeof(rrbuf));
     62         INFO("  register record: %s IN %s (%p %p) -> %d", STR_OR_NULL(event->name),
     63              STR_OR_NULL(rr_printed), (char *)event->rref, (char *)event->sdref, event->status);
     64         break;
     65     case dns_service_event_type_remove_record:
     66         INFO("    remove record: (%p %p) -> %d", (char *)event->rref, (char *)event->sdref, event->status);
     67         break;
     68     case dns_service_event_type_update_record:
     69         // Get the rrtype from the previous event.
     70         rdata = event->rdata;
     71         rdlen = event->rdlen;
     72         for (dns_service_event_t *ep = events; ep != NULL; ep = ep->next) {
     73             if (ep->event_type == dns_service_event_type_register_record && ep->rref == event->rref) {
     74                 rrtype = ep->rrtype;
     75                 // When updating the TSR record, we send rdlen=0 and no rdata, which means just update the
     76                 // TSR and don't change the RR. But that would cause a parse failure, so we need the data
     77                 // as well as the rrtype from the RegisterRecord event.
     78                 if (rdlen == 0 && ep->rdlen != 0) {
     79                     rdata = ep->rdata;
     80                     rdlen = ep->rdlen;
     81                 }
     82                 break;
     83             }
     84         }
     85         rr_printed = dns_service_rdata_to_text(state, rrtype, rdata, rdlen, rrbuf, sizeof(rrbuf));
     86         INFO("    update record: %s (%p %p) -> %d", STR_OR_NULL(rr_printed), (char *)event->rref, (char *)event->sdref,
     87              event->status);
     88         break;
     89     case dns_service_event_type_ref_deallocate:
     90         INFO("   ref deallocate: (%p %p) -> %d", (char *)event->sdref, (char *)event->parent_sdref, event->status);
     91         break;
     92     case dns_service_event_type_register_callback:
     93         INFO("     reg callback: (%p %p) -> %d", (char *)event->sdref, (char *)event->parent_sdref, event->status);
     94         break;
     95     case dns_service_event_type_register_record_callback:
     96         INFO("  regrec callback: (%p %p) -> %d", (char *)event->rref, (char *)event->sdref, event->status);
     97         break;
     98     }
     99 }
    100 
    101 bool
    102 dns_service_dump_unexpected_events(test_state_t *test_state, srp_server_t *server_state)
    103 {
    104     bool ret = true;
    105     for (dns_service_event_t *event = server_state->dns_service_events; event; event = event->next) {
    106         if (!event->consumed) {
    107             dns_service_dump_event(test_state, event, server_state->dns_service_events);
    108             ret = false;
    109         }
    110     }
    111     return ret;
    112 }
    113 
    114 dns_service_event_t *
    115 dns_service_find_first_register_event_by_name_and_type(srp_server_t *state, const char *name,
    116                                                        const char *regtype)
    117 {
    118     for (dns_service_event_t *event = state->dns_service_events; event; event = event->next) {
    119         if (event->event_type == dns_service_event_type_register &&
    120             event->name != NULL && event->regtype != NULL && !event->consumed)
    121         {
    122             INFO("event->name %s name %s  event->regtype %s regtype %s",
    123                  event->name, name, event->regtype, regtype);
    124             if (!strcmp(event->name, name) && !strcmp(event->regtype, regtype)) {
    125                 return event;
    126             }
    127         }
    128     }
    129     return NULL;
    130 }
    131 
    132 dns_service_event_t *
    133 dns_service_find_first_register_record_event_by_name(srp_server_t *state, const char *name)
    134 {
    135     for (dns_service_event_t *event = state->dns_service_events; event; event = event->next) {
    136         if (event->event_type == dns_service_event_type_register_record &&
    137             event->name != NULL && !event->consumed && !strcmp(name, event->name))
    138         {
    139             return event;
    140         }
    141     }
    142     return NULL;
    143 }
    144 
    145 dns_service_event_t *
    146 dns_service_find_callback_for_registration(srp_server_t *state, dns_service_event_t *register_event)
    147 {
    148     for (dns_service_event_t *event = state->dns_service_events; event; event = event->next) {
    149         if (event->event_type == dns_service_event_type_register_callback &&
    150             register_event->event_type == dns_service_event_type_register &&
    151             register_event->sdref == event->sdref && !event->consumed)
    152         {
    153             return event;
    154         }
    155         if (event->event_type == dns_service_event_type_register_record_callback &&
    156             register_event->event_type == dns_service_event_type_register_record &&
    157             register_event->rref == event->rref && !event->consumed)
    158         {
    159             return event;
    160         }
    161     }
    162     return NULL;
    163 }
    164 
    165 dns_service_event_t *
    166 dns_service_find_ref_deallocate_event(srp_server_t *state)
    167 {
    168     for (dns_service_event_t *event = state->dns_service_events; event; event = event->next) {
    169         if (event->event_type == dns_service_event_type_ref_deallocate) {
    170             return event;
    171         }
    172     }
    173     return NULL;
    174 }
    175 
    176 // Find a DNSServiceUpdateRecord that corresponds to a DNSServiceRegister or DNSServiceRegisterRecord event.
    177 // For a DNSServiceRegister update, event->rref will be NULL.
    178 dns_service_event_t *
    179 dns_service_find_update_for_register_event(srp_server_t *state, dns_service_event_t *register_event,
    180                                            dns_service_event_t *after_event)
    181 {
    182     bool after_event_matched = after_event == NULL ? true : false;
    183     for (dns_service_event_t *event = state->dns_service_events; event != NULL; event = event->next) {
    184         // If we are past any after_event and this is an update_record event, see if it's for the same
    185         // registration.
    186         if (after_event_matched && event->event_type == dns_service_event_type_update_record &&
    187             ((register_event->rref == 0 && event->sdref == register_event->sdref) ||
    188              (register_event->rref != 0 && event->rref == register_event->rref)))
    189         {
    190             return event;
    191         }
    192         // Don't match events that are prior to after_event (so that we can skip an event if it's not the right one
    193         if (event == after_event) {
    194             after_event_matched = true;
    195         }
    196     }
    197     return NULL;
    198 }
    199 
    200 // Find a DNSServiceRemoveRecord
    201 dns_service_event_t *
    202 dns_service_find_remove_for_register_event(srp_server_t *state, dns_service_event_t *register_event,
    203                                            dns_service_event_t *after_event)
    204 {
    205     bool after_event_matched = after_event == NULL ? true : false;
    206     for (dns_service_event_t *event = state->dns_service_events; event != NULL; event = event->next) {
    207         // If we are past any after_event and this is an update_record event, see if it's for the same
    208         // registration.
    209         if (after_event_matched && event->event_type == dns_service_event_type_remove_record &&
    210             ((register_event->rref == 0 && event->sdref == register_event->sdref) ||
    211              (register_event->rref != 0 && event->rref == register_event->rref)))
    212         {
    213             return event;
    214         }
    215         // Don't match events that are prior to after_event (so that we can skip an event if it's not the right one)
    216         if (event == after_event) {
    217             after_event_matched = true;
    218         }
    219     }
    220     return NULL;
    221 }
    222 
    223 static dns_service_ref_t *
    224 dns_service_ref_create(srp_server_t *server_state,
    225                        DNSServiceRef *target, int flags, void *context)
    226 {
    227     dns_service_ref_t *ret = calloc(1, sizeof(*ret));
    228     TEST_FAIL_CHECK(server_state->test_state, ret != NULL, "no memory for dns_service_ref_t");
    229     if (flags & kDNSServiceFlagsShareConnection) {
    230         ret->sdref = *target;
    231     }
    232     ret->server_state = server_state;
    233     ret->context = context;
    234     return ret;
    235 }
    236 
    237 static dns_service_ref_t *
    238 dns_service_ref_create_for_register(srp_server_t *server_state,
    239                                     DNSServiceRef *target, int flags, void *context, DNSServiceRegisterReply callback)
    240 {
    241     dns_service_ref_t *ret = dns_service_ref_create(server_state, target, flags, context);
    242     if (ret != NULL) {
    243         ret->callback.register_reply = callback;
    244     }
    245     return ret;
    246 }
    247 
    248 static dns_service_ref_t *
    249 dns_service_ref_create_for_query_record(srp_server_t *server_state,
    250                                         DNSServiceRef *target, int flags, void *context, DNSServiceQueryRecordReply callback)
    251 {
    252     dns_service_ref_t *ret = dns_service_ref_create(server_state, target, flags, context);
    253     if (ret != NULL) {
    254         ret->callback.query_record_reply = callback;
    255     }
    256     return ret;
    257 }
    258 
    259 static dns_record_ref_t *
    260 dns_record_ref_create(srp_server_t *server_state, void *context, DNSServiceRegisterRecordReply callback)
    261 {
    262     dns_record_ref_t *ret = calloc(1, sizeof(*ret));
    263     TEST_FAIL_CHECK(server_state->test_state, ret != NULL, "no memory for dns_record_ref_t");
    264     ret->server_state = server_state;
    265     ret->context = context;
    266     ret->callback = callback;
    267     return ret;
    268 }
    269 
    270 static char *
    271 dns_service_string_dup(const char *src)
    272 {
    273     if (src == NULL) {
    274         return NULL;
    275     }
    276     return strdup(src);
    277 }
    278 
    279 static dns_service_event_t *
    280 dns_service_event_append(srp_server_t *state, dns_service_event_type_t event_type, DNSServiceRef sdref,
    281                          DNSServiceRef in_sdref, DNSRecordRef rref, DNSServiceFlags flags, uint32_t interfaceIndex,
    282                          const char *name, const char *regtype, const char *domain, const char *host, uint16_t port,
    283                          uint16_t rdlen, const void *rdata, uint16_t rrtype, uint16_t rrclass, uint32_t ttl, void *attr,
    284                          void *callBack, void *context, DNSServiceErrorType status)
    285 {
    286     TEST_FAIL_CHECK(NULL, state != NULL, "invalid server state");
    287     dns_service_event_t **ep = &state->dns_service_events;
    288     while (*ep) {
    289         ep = &(*ep)->next;
    290     }
    291     dns_service_event_t *event = calloc(1, sizeof(*event));
    292     TEST_FAIL_CHECK(state->test_state, event != NULL, "no memory for dns service event");
    293     *ep = event;
    294     event->server_state = state;
    295     event->event_type = event_type;
    296     event->sdref = (intptr_t)sdref;
    297     if (in_sdref != NULL && (flags & kDNSServiceFlagsShareConnection)) {
    298         event->parent_sdref = (intptr_t)in_sdref;
    299     }
    300     event->rref = (intptr_t)rref;
    301     event->flags = flags;
    302     event->interface_index = interfaceIndex;
    303     event->name = dns_service_string_dup(name);
    304     event->regtype = dns_service_string_dup(regtype);
    305     event->domain = dns_service_string_dup(domain);
    306     event->host = dns_service_string_dup(host);
    307     event->port = port;
    308     event->rdlen = rdlen;
    309     if (rdata != NULL) {
    310         event->rdata = malloc(rdlen);
    311         TEST_FAIL_CHECK(state->test_state, event->rdata != NULL, "no memory to save rdata");
    312         memcpy(event->rdata, rdata, rdlen);
    313     }
    314     event->rrclass = rrclass;
    315     event->rrtype = rrtype;
    316     event->ttl = ttl;
    317     event->attr = (intptr_t)attr;
    318     event->callBack = (intptr_t)callBack;
    319     event->context = (intptr_t)context;
    320     event->status = status;
    321     return event;
    322 }
    323 
    324 static void
    325 dns_service_register_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
    326                               const char *name, const char *regtype, const char *domain, void *context)
    327 {
    328     dns_service_ref_t *ref = context;
    329     dns_service_event_append(ref->server_state, dns_service_event_type_register_callback, sdRef, NULL, NULL, flags, 0,
    330                              name, regtype, domain, NULL, 0, 0, NULL, 0, 0, 0, NULL, NULL, context, errorCode);
    331     DNSServiceRegisterReply callback = ref->callback.register_reply;
    332     if (callback != NULL) {
    333         callback(ref, flags, errorCode, name, regtype, domain, ref->context);
    334     }
    335 }
    336 
    337 DNSServiceErrorType
    338 dns_service_register(srp_server_t *state, DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    339                      const char *name, const char *regtype, const char *domain, const char *host, uint16_t port,
    340                      uint16_t txtLen, const void *txtRecord, DNSServiceRegisterReply callBack, void *context)
    341 {
    342     return dns_service_register_wa(state, sdRef, flags, interfaceIndex, name, regtype, domain, host, port, txtLen,
    343                                    txtRecord, NULL, callBack, context);
    344 }
    345 
    346 DNSServiceErrorType
    347 dns_service_register_wa(srp_server_t *state, DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    348                         const char *name, const char *regtype, const char *domain, const char *host, uint16_t port,
    349                         uint16_t txtLen, const void *txtRecord, DNSServiceAttributeRef attr,
    350                         DNSServiceRegisterReply callBack, void *context)
    351 {
    352 #undef DNSServiceRegisterWithAttribute
    353     dns_service_ref_t *ret = dns_service_ref_create_for_register(state, sdRef, flags, context, callBack);
    354     char updated_name[DNS_MAX_NAME_SIZE + 1];
    355     snprintf(updated_name, sizeof(updated_name), "%d-%s", state->server_id, name);
    356     int status = DNSServiceRegisterWithAttribute(&ret->sdref, flags, interfaceIndex, updated_name, regtype, domain, host, port,
    357                                                  txtLen, txtRecord, attr, dns_service_register_callback, ret);
    358     dns_service_event_append(state, dns_service_event_type_register, ret->sdref, *sdRef, NULL, flags,
    359                              interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord,
    360                              0, 0, 0, attr, callBack, context, status);
    361     if (status != kDNSServiceErr_NoError) {
    362         free(ret);
    363     }
    364     *sdRef = ret;
    365     return status;
    366 }
    367 
    368 static void
    369 dns_service_register_record_callback(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
    370                                      DNSServiceErrorType errorCode, void *context)
    371 {
    372     dns_record_ref_t *ref = context;
    373     dns_service_event_append(ref->server_state, dns_service_event_type_register_record_callback, sdRef, NULL,
    374                              RecordRef, flags, 0, NULL, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, NULL, NULL,
    375                              context, errorCode);
    376     if (ref->callback != NULL) {
    377         ref->callback(sdRef, ref, flags, errorCode, ref->context);
    378     }
    379 }
    380 
    381 DNSServiceErrorType
    382 dns_service_register_record(srp_server_t *state, DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
    383                             uint32_t interfaceIndex, const char *fullname, uint16_t rrtype, uint16_t rrclass,
    384                             uint16_t rdlen, const void *rdata, uint32_t ttl, DNSServiceRegisterRecordReply callBack,
    385                             void *context)
    386 {
    387     return dns_service_register_record_wa(state, sdRef, RecordRef, flags, interfaceIndex, fullname, rrtype, rrclass, rdlen,
    388                                           rdata, ttl, NULL, callBack, context);
    389 }
    390 
    391 DNSServiceErrorType
    392 dns_service_register_record_wa(srp_server_t *state, DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
    393                                uint32_t interfaceIndex, const char *fullname, uint16_t rrtype, uint16_t rrclass,
    394                                uint16_t rdlen, const void *rdata, uint32_t ttl, DNSServiceAttributeRef attr,
    395                                DNSServiceRegisterRecordReply callBack,
    396                                void *context)
    397 {
    398 #undef DNSServiceRegisterRecordWithAttribute
    399     dns_record_ref_t *ret = dns_record_ref_create(state, context, callBack);
    400 
    401     // Since in testing multiple srp servers share the same mDNSResponder,
    402     // we intentionally rename the record by prepending the server_id
    403     // to the record name when registering with mDNSResponder, so that
    404     // it will not generate conflict for replicated records.
    405     char updated_name[DNS_MAX_NAME_SIZE + 1];
    406     snprintf(updated_name, sizeof(updated_name), "%d-%s", state->server_id, fullname);
    407     int status = DNSServiceRegisterRecordWithAttribute(sdRef, &ret->rref, flags, interfaceIndex, updated_name, rrtype,
    408                                                    rrclass, rdlen, rdata, ttl, attr,
    409                                                    dns_service_register_record_callback, ret);
    410     dns_service_event_append(state, dns_service_event_type_register_record, sdRef, NULL, ret->rref, flags,
    411                              interfaceIndex, fullname, NULL, NULL, NULL, 0, rdlen, rdata, rrtype, rrclass, ttl, attr,
    412                              callBack, context, status);
    413     if (status != kDNSServiceErr_NoError) {
    414         free(ret);
    415     }
    416     *RecordRef = ret;
    417     return status;
    418 }
    419 
    420 // Note that this will not work with a recordref returned by DNSServiceAddRecord(), which we aren't currently intercepting
    421 // because we don't use it.
    422 DNSServiceErrorType
    423 dns_service_remove_record(srp_server_t *state, DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags)
    424 {
    425 #undef DNSServiceRemoveRecord
    426     int ret = DNSServiceRemoveRecord(sdRef, RecordRef->rref, flags);
    427     dns_service_event_append(state, dns_service_event_type_remove_record, sdRef->sdref, NULL, RecordRef->rref,
    428                              flags, 0, NULL, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, NULL, NULL, NULL, ret);
    429     return ret;
    430 }
    431 
    432 DNSServiceErrorType
    433 dns_service_update_record(srp_server_t *state, DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
    434                           uint16_t rdlen, const void *rdata, uint32_t ttl)
    435 {
    436     return dns_service_update_record_wa(state, sdRef, RecordRef, flags, rdlen, rdata, ttl, NULL);
    437 }
    438 
    439 DNSServiceErrorType
    440 dns_service_update_record_wa(srp_server_t *state, DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
    441                              uint16_t rdlen, const void *rdata, uint32_t ttl, DNSServiceAttributeRef attr)
    442 {
    443 #undef DNSServiceUpdateRecordWithAttribute
    444     int ret;
    445     if (RecordRef != NULL) {
    446         ret = DNSServiceUpdateRecordWithAttribute(sdRef, RecordRef->rref, flags, rdlen, rdata, ttl, attr);
    447         dns_service_event_append(state, dns_service_event_type_update_record, sdRef, NULL, RecordRef->rref, flags, 0,
    448                                  NULL, NULL, NULL, NULL, 0, rdlen, rdata, 0, 0, ttl, NULL, NULL, NULL, ret);
    449     } else {
    450         ret = DNSServiceUpdateRecordWithAttribute(sdRef->sdref, NULL, flags, rdlen, rdata, ttl, attr);
    451         dns_service_event_append(state, dns_service_event_type_update_record, sdRef->sdref, NULL, NULL, flags, 0,
    452                                  NULL, NULL, NULL, NULL, 0, rdlen, rdata, 0, 0, ttl, NULL, NULL, NULL, ret);
    453     }
    454     return ret;
    455 }
    456 
    457 static void
    458 dns_service_query_record_callback(DNSServiceRef UNUSED sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
    459                                   DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype,
    460                                   uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context)
    461 {
    462     dns_service_ref_t *ref = context;
    463     srp_server_t *server_state = ref->server_state;
    464     test_state_t *state = server_state->test_state;
    465     bool call_callback = true;
    466     dns_service_query_record_callback_intercept_t intercept = state->dns_service_query_callback_intercept;
    467     if (intercept != NULL) {
    468         call_callback = intercept(ref, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdlen, rdata, ttl, ref->context);
    469     }
    470     if (call_callback) {
    471         DNSServiceQueryRecordReply callback = ref->callback.query_record_reply;
    472         callback(ref, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdlen, rdata, ttl, ref->context);
    473     }
    474 }
    475 
    476 DNSServiceErrorType
    477 dns_service_query_record(srp_server_t *NULLABLE srp_server, DNSServiceRef NONNULL *NULLABLE sdRef,
    478                          DNSServiceFlags flags, uint32_t interfaceIndex, const char *NONNULL fullname,
    479                          uint16_t rrtype, uint16_t rrclass, DNSServiceQueryRecordReply NONNULL callBack,
    480                          void *NULLABLE context)
    481 {
    482     return dns_service_query_record_wa(srp_server, sdRef, flags, interfaceIndex, fullname, rrtype, rrclass,
    483                                        NULL, callBack, context);
    484 }
    485 
    486 DNSServiceErrorType
    487 dns_service_query_record_wa(srp_server_t *NULLABLE srp_server, DNSServiceRef NONNULL *NULLABLE sdRef,
    488                             DNSServiceFlags flags, uint32_t interfaceIndex, const char *NONNULL fullname,
    489                             uint16_t rrtype, uint16_t rrclass, DNSServiceAttribute const *NULLABLE attr,
    490                             DNSServiceQueryRecordReply NONNULL callBack, void *NULLABLE context)
    491 {
    492     dns_service_ref_t *ret = dns_service_ref_create_for_query_record(srp_server, sdRef, flags, context, callBack);
    493     test_state_t *state = srp_server->test_state;
    494     int status;
    495     if (state->query_record_intercept != NULL) {
    496         status = state->query_record_intercept(state, &ret->sdref, flags, interfaceIndex, fullname, rrtype, rrclass,
    497                                                attr, dns_service_query_record_callback, ret);
    498     } else {
    499         status = DNSServiceQueryRecordWithAttribute(&ret->sdref, flags, interfaceIndex, fullname, rrtype, rrclass,
    500                                                     attr, dns_service_query_record_callback, ret);
    501     }
    502     if (status != kDNSServiceErr_NoError) {
    503         free(ret);
    504     }
    505     *sdRef = ret;
    506     return status;
    507 }
    508 
    509 dnssd_txn_t *NULLABLE
    510 dns_service_ioloop_txn_add(srp_server_t UNUSED *NULLABLE srp_server, DNSServiceRef NONNULL sdref,
    511                            void *NULLABLE context, dnssd_txn_finalize_callback_t NULLABLE finalize_callback,
    512                            dnssd_txn_failure_callback_t NULLABLE failure_callback)
    513 {
    514     return ioloop_dnssd_txn_add(sdref->sdref, context, finalize_callback, failure_callback);
    515 }
    516 
    517 void
    518 dns_service_ref_deallocate(srp_server_t *state, DNSServiceRef sdRef)
    519 {
    520 #undef DNSServiceRefDeallocate
    521     dns_service_event_append(state, dns_service_event_type_ref_deallocate, sdRef, NULL, NULL,
    522                              0, 0, NULL, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, NULL, NULL, NULL, 0);
    523     DNSServiceRefDeallocate(sdRef->sdref);
    524     free(sdRef);
    525 }
    526 
    527 
    528 // Local Variables:
    529 // mode: C
    530 // tab-width: 4
    531 // c-file-style: "bsd"
    532 // c-basic-offset: 4
    533 // fill-column: 108
    534 // indent-tabs-mode: nil
    535 // End:
    536