Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* adv-resolve.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 implementation of advertising proxy resolve
     18  * API, which allows applications that use DNSServiceResolve to signal to
     19  * the advertising proxy (as opposed to mDNSResponder) which services the
     20  * application is trying to resolve.
     21  */
     22 
     23 #include <netdb.h>
     24 #include <dns_sd.h>
     25 #include <os/log.h>
     26 
     27 #include "srp.h"
     28 #include "advertising_proxy_services.h"
     29 
     30 #undef ERROR
     31 #define ERROR(fmt, ...) os_log_error(adv_log, "%{public}s: " # fmt, __FUNCTION__, ##__VA_ARGS__)
     32 #undef INFO
     33 #define INFO(fmt, ...) os_log_info(adv_log, "%{public}s: " # fmt, __FUNCTION__, ##__VA_ARGS__)
     34 #undef FAULT
     35 #define FAULT(fmt, ...) os_log_info(adv_log, "%{public}s: " # fmt, __FUNCTION__, ##__VA_ARGS__)
     36 
     37 #define DECLARE_OBJECT_TYPE(x) int x##_created, x##_finalized, old_##x##_created, old_##x##_finalized
     38 DECLARE_OBJECT_TYPE(adv_instance_state);
     39 DECLARE_OBJECT_TYPE(adv_service_state);
     40 DECLARE_OBJECT_TYPE(adv_resolver_state);
     41 DECLARE_OBJECT_TYPE(advertising_proxy_subscription);
     42 
     43 // Discovery domains seen
     44 typedef struct discovery_domain discovery_domain_t;
     45 typedef struct adv_instance_state adv_instance_state_t;
     46 typedef struct adv_service_state adv_service_state_t;
     47 typedef struct adv_resolver_state adv_resolver_state_t;
     48 
     49 os_log_t advertising_proxy_global_os_log;
     50 #define adv_log advertising_proxy_global_os_log
     51 
     52 struct advertising_proxy_subscription {
     53     int ref_count;
     54     advertising_proxy_resolve_reply instance_callback;
     55     advertising_proxy_browse_reply service_callback;
     56     adv_instance_state_t *local_instance, *push_instance;
     57     adv_service_state_t *service;
     58     void *context;
     59 };
     60 
     61 static void
     62 advertising_proxy_subscription_finalize(advertising_proxy_subscription_t *subscription)
     63 {
     64     if (subscription->local_instance != NULL) {
     65         FAULT("subscription->local_instance is not NULL (%p)", subscription->local_instance);
     66     }
     67     if (subscription->push_instance != NULL) {
     68         FAULT("subscription->push_instance is not NULL (%p)", subscription->push_instance);
     69     }
     70     if (subscription->service != NULL) {
     71         FAULT("subscription->service is not NULL (%p)", subscription->service);
     72     }
     73 }
     74 
     75 // Retained state relating to queries for services of a particular type.
     76 struct adv_service_state {
     77     int ref_count;
     78     adv_service_state_t *next;
     79     char *service_type;
     80     DNSServiceRef null_browse_ref; // default domains
     81     DNSServiceRef push_browse_ref;  // default.service.arpa
     82     adv_instance_state_t *instances;
     83     size_t max_subscribers;
     84     advertising_proxy_subscription_t **subscribers;
     85 };
     86 
     87 static void
     88 adv_service_state_finalize(adv_service_state_t *state)
     89 {
     90     free(state->service_type);
     91     if (state->null_browse_ref != NULL) {
     92         FAULT("state->null_browse_ref is non-null: %p", state->null_browse_ref);
     93         DNSServiceRefDeallocate(state->null_browse_ref);
     94     }
     95     if (state->push_browse_ref != NULL) {
     96         FAULT("state->null_browse_ref is non-null: %p", state->push_browse_ref);
     97         DNSServiceRefDeallocate(state->push_browse_ref);
     98     }
     99     if (state->instances != NULL) {
    100         FAULT("state->instances not NULL (%p)", state->instances);
    101     }
    102     if (state->next != NULL) {
    103         FAULT("state->next not NULL (%p)", state->next);
    104     }
    105     for (size_t i = 0; i < state->max_subscribers; i++) {
    106         if (state->subscribers[i] != NULL) {
    107             RELEASE_HERE(state->subscribers[i], advertising_proxy_subscription);
    108         }
    109     }
    110     free(state->subscribers);
    111 }
    112 
    113 static void
    114 adv_service_state_cancel(adv_service_state_t *state)
    115 {
    116     if (state->push_browse_ref != NULL) {
    117         DNSServiceRefDeallocate(state->push_browse_ref);
    118         state->push_browse_ref = NULL;
    119         RELEASE_HERE(state, adv_service_state); // No more callbacks possible.
    120     }
    121     if (state->null_browse_ref != NULL) {
    122         DNSServiceRefDeallocate(state->null_browse_ref);
    123         state->null_browse_ref = NULL;
    124         RELEASE_HERE(state, adv_service_state); // No more callbacks possible
    125     }
    126 }
    127 
    128 // State relating to an instance of a particular service.
    129 struct adv_instance_state {
    130     int ref_count;
    131     adv_instance_state_t *next, *nsn; // nsn == next same name
    132     char *name;
    133     char *service_type;
    134     char *domain;
    135     DNSServiceRef resolve_ref;
    136 #define INITIAL_MAX_SUBSCRIBERS 10 // Should usually be all we need
    137     size_t max_subscribers;
    138     advertising_proxy_subscription_t **subscribers;
    139 };
    140 
    141 static void
    142 adv_instance_state_finalize(adv_instance_state_t *state)
    143 {
    144     free(state->name);
    145     free(state->service_type);
    146     free(state->domain);
    147     if (state->resolve_ref != NULL) {
    148         FAULT("state->resolve_ref is non-null: %p", state->resolve_ref);
    149         DNSServiceRefDeallocate(state->resolve_ref); // shouldn't be possible
    150     }
    151     for (size_t i = 0; i < state->max_subscribers; i++) {
    152         if (state->subscribers[i] != NULL) {
    153             RELEASE_HERE(state->subscribers[i], advertising_proxy_subscription);
    154         }
    155     }
    156     free(state->subscribers);
    157     if (state->nsn != NULL) {
    158         FAULT("state->nsn is not NULL (%p)", state->nsn);
    159     }
    160     if (state->next != NULL) {
    161         FAULT("state->next is not NULL (%p)", state->next);
    162     }
    163 }
    164 
    165 static void
    166 adv_instance_state_cancel(adv_instance_state_t *state)
    167 {
    168     if (state->resolve_ref != NULL) {
    169         DNSServiceRefDeallocate(state->resolve_ref);
    170         state->resolve_ref = NULL;
    171         RELEASE_HERE(state, adv_instance_state); // No longer possible to get a callback
    172     }
    173 }
    174 
    175 // Call on startup to initialize the advertising proxy resolver function
    176 advertising_proxy_error_type
    177 advertising_proxy_resolver_init(os_log_t log_thingy)
    178 {
    179     if (log_thingy != NULL) {
    180         adv_log = log_thingy;
    181     } else {
    182         adv_log = OS_LOG_DEFAULT;
    183     }
    184 
    185     // Any additional per-run setup...
    186     return kDNSSDAdvertisingProxyStatus_NoError;
    187 }
    188 
    189 static adv_service_state_t *
    190 adv_service_state_create(const char *regtype)
    191 {
    192     adv_service_state_t *state = calloc(1, sizeof(*state));
    193     if (state == NULL) {
    194         ERROR("no memory for service state %{public}s", regtype);
    195     }
    196     RETAIN_HERE(state, adv_service_state);
    197     state->service_type = strdup(regtype);
    198     if (state->service_type == NULL) {
    199         RELEASE_HERE(state, adv_service_state);
    200         state = NULL;
    201         ERROR("no memory for service_type %{public}s", regtype);
    202     }
    203     return state;
    204 }
    205 
    206 static adv_instance_state_t *
    207 adv_instance_state_create(const char *name, const char *regtype, const char *domain)
    208 {
    209     adv_instance_state_t *ret = NULL, *state = calloc(1, sizeof(*state));
    210     if (state == NULL) {
    211         ERROR("no memory for state %{private}s . %{public}s", name, regtype);
    212     } else {
    213         RETAIN_HERE(state, adv_instance_state);
    214         state->name = strdup(name);
    215         if (state->name == NULL) {
    216             ERROR("no memory for name %{private}s . %{public}s", name, regtype);
    217             goto out;
    218         }
    219         state->service_type = strdup(regtype);
    220         if (state->service_type == NULL) {
    221             ERROR("no memory for service type %{private}s . %{public}s", name, regtype);
    222             goto out;
    223         }
    224         if (domain != NULL) {
    225             state->domain = strdup(domain);
    226             if (state->domain == NULL) {
    227                 ERROR("no memory for domain %{private}s . %{public}s", name, domain);
    228                 goto out;
    229             }
    230         }
    231         ret = state;
    232         state = NULL;
    233     }
    234 out:
    235     if (state != NULL) {
    236         RELEASE_HERE(state, adv_instance_state);
    237     }
    238     return ret;
    239 }
    240 
    241 static advertising_proxy_subscription_t *
    242 advertising_proxy_subscription_create(void)
    243 {
    244     advertising_proxy_subscription_t *subscription = calloc(1, sizeof(*subscription));
    245     if (subscription == NULL) {
    246         goto out;
    247     }
    248     RETAIN_HERE(subscription, advertising_proxy_subscription);
    249 out:
    250     return subscription;
    251 }
    252 
    253 
    254 static void
    255 adv_instance_unsubscribe(adv_instance_state_t *instance, advertising_proxy_subscription_t *subscription)
    256 {
    257     if (instance->resolve_ref != NULL) {
    258         DNSServiceRefDeallocate(instance->resolve_ref);
    259         instance->resolve_ref = NULL;
    260     }
    261     for (size_t i = 0; i < instance->max_subscribers; i++) {
    262         if (instance->subscribers[i] == subscription) {
    263             RELEASE_HERE(instance->subscribers[i], advertising_proxy_subscription);
    264             instance->subscribers[i] = NULL;
    265             break;
    266         }
    267     }
    268 }
    269 
    270 static void
    271 adv_service_unsubscribe(adv_service_state_t *service, advertising_proxy_subscription_t *subscription)
    272 {
    273     if (service->null_browse_ref != NULL) {
    274         DNSServiceRefDeallocate(service->null_browse_ref);
    275         service->null_browse_ref = NULL;
    276     }
    277     if (service->push_browse_ref != NULL) {
    278         DNSServiceRefDeallocate(service->push_browse_ref);
    279         service->push_browse_ref = NULL;
    280     }
    281     for (size_t i = 0; i < service->max_subscribers; i++) {
    282         if (service->subscribers[i] == subscription) {
    283             RELEASE_HERE(service->subscribers[i], advertising_proxy_subscription);
    284             service->subscribers[i] = NULL;
    285             break;
    286         }
    287     }
    288 }
    289 
    290 advertising_proxy_error_type
    291 advertising_proxy_subscription_retain_(advertising_proxy_subscription_t *subscription, const char *file, int line)
    292 {
    293     RETAIN(subscription, advertising_proxy_subscription);
    294     return kDNSSDAdvertisingProxyStatus_NoError;
    295 }
    296 
    297 advertising_proxy_error_type
    298 advertising_proxy_subscription_release_(advertising_proxy_subscription_t *subscription, const char *file, int line)
    299 {
    300     RELEASE(subscription, advertising_proxy_subscription);
    301     return kDNSSDAdvertisingProxyStatus_NoError;
    302 }
    303 
    304 advertising_proxy_error_type
    305 advertising_proxy_subscription_cancel(advertising_proxy_subscription_t *subscription)
    306 {
    307     if (subscription->local_instance) {
    308         adv_instance_unsubscribe(subscription->local_instance, subscription);
    309         RELEASE_HERE(subscription->local_instance, adv_instance_state);
    310         subscription->local_instance = NULL;
    311     }
    312     if (subscription->push_instance) {
    313         adv_instance_unsubscribe(subscription->push_instance, subscription);
    314         RELEASE_HERE(subscription->push_instance, adv_instance_state);
    315         subscription->push_instance = NULL;
    316     }
    317     if (subscription->service) {
    318         adv_service_unsubscribe(subscription->service, subscription);
    319         RELEASE_HERE(subscription->service, adv_service_state);
    320         subscription->service = NULL;
    321     }
    322     return kDNSSDAdvertisingProxyStatus_NoError;
    323 }
    324 
    325 advertising_proxy_error_type
    326 advertising_proxy_registrar_create(advertising_proxy_subscription_t **subscription_ret, run_context_t clientq,
    327                                    advertising_proxy_registrar_reply callback, void *context)
    328 {
    329     run_context_t queue = clientq;
    330     advertising_proxy_error_type result = kDNSSDAdvertisingProxyStatus_NoError;
    331     advertising_proxy_subscription_t *subscription = NULL;
    332     // Sanity check arguments.
    333     if (subscription_ret == NULL || callback == NULL) {
    334         result = kDNSSDAdvertisingProxyStatus_Invalid;
    335         goto out;
    336     }
    337     if (queue == NULL) {
    338         queue = dispatch_get_main_queue();
    339     }
    340 
    341     subscription = advertising_proxy_subscription_create();
    342 
    343     if (subscription == NULL) {
    344         result = kDNSSDAdvertisingProxyStatus_NoMemory;
    345         goto out;
    346     }
    347     advertising_proxy_subscription_t *tmp = subscription;
    348     RETAIN_HERE(subscription, advertising_proxy_subscription); // For callback
    349     dispatch_async(clientq, ^{
    350         callback(tmp, kDNSSDAdvertisingProxyStatus_NoError, context);
    351         RELEASE_HERE(subscription, advertising_proxy_subscription); // For callback
    352     });
    353     *subscription_ret = subscription;
    354     subscription = NULL;
    355 
    356 out:
    357     if (subscription != NULL) {
    358         RELEASE_HERE(subscription, advertising_proxy_subscription);
    359     }
    360     return result;
    361 }
    362 
    363 static void
    364 adv_subscriber_add(size_t *num_subscribers, advertising_proxy_subscription_t ***p_subscribers,
    365                    advertising_proxy_subscription_t *subscriber)
    366 {
    367     size_t max_subscribers = *num_subscribers;
    368     advertising_proxy_subscription_t **subscribers = *p_subscribers;
    369 
    370     for (size_t i = 0; i < max_subscribers; i++) {
    371         if (subscribers[i] == NULL) {
    372             subscribers[i] = subscriber;
    373             RETAIN_HERE(subscribers[i], advertising_proxy_subscription);
    374             return;
    375         }
    376     }
    377     size_t new_max = subscribers == NULL ? INITIAL_MAX_SUBSCRIBERS : max_subscribers * 2;
    378     advertising_proxy_subscription_t **new_subscribers = calloc(new_max, sizeof(*subscribers));
    379     if (new_subscribers == NULL) {
    380         ERROR("no memory for %zu subscribers", new_max);
    381         return;
    382     }
    383     if (*p_subscribers != NULL) {
    384         memcpy(new_subscribers, subscribers, max_subscribers * sizeof(*subscribers));
    385         free(*p_subscribers);
    386     }
    387     *p_subscribers = new_subscribers;
    388     new_subscribers[max_subscribers] = subscriber;
    389     RETAIN_HERE(new_subscribers[max_subscribers], advertising_proxy_subscription);
    390     *num_subscribers = new_max;
    391 }
    392 
    393 static void
    394 adv_service_state_subscriber_add(adv_service_state_t *service, advertising_proxy_subscription_t *subscriber)
    395 {
    396     adv_subscriber_add(&service->max_subscribers, &service->subscribers, subscriber);
    397 }
    398 
    399 static void
    400 advertising_proxy_browse_callback(DNSServiceRef UNUSED sdRef, DNSServiceFlags flags, uint32_t interface_index,
    401                                   DNSServiceErrorType error_code,
    402                                   const char *instance_name, const char *service_type, const char *UNUSED domain, void *context)
    403 {
    404     adv_service_state_t *service = context;
    405     advertising_proxy_error_type error = kDNSSDAdvertisingProxyStatus_NoError;
    406 
    407     RETAIN_HERE(service, adv_service_state);
    408     if (error_code != kDNSServiceErr_NoError) {
    409         error = kDNSSDAdvertisingProxyStatus_UnknownErr;
    410     }
    411     for (size_t i = 0; i < service->max_subscribers; i++) {
    412         advertising_proxy_subscription_t *subscription = service->subscribers[i];
    413         if (subscription != NULL) {
    414             subscription->service_callback(subscription, error, interface_index,
    415                                            (flags & kDNSServiceFlagsAdd) ? true : false,
    416                                            instance_name, service_type, context);
    417         }
    418     }
    419     if (error != kDNSSDAdvertisingProxyStatus_NoError) {
    420         adv_service_state_cancel(service);
    421     }
    422     RELEASE_HERE(service, adv_service_state);
    423 }
    424 
    425 advertising_proxy_error_type
    426 advertising_proxy_browse_create(advertising_proxy_subscription_t **subscription_ret, run_context_t clientq,
    427                                 const char *regtype, advertising_proxy_browse_reply callback, void *context)
    428 {
    429     advertising_proxy_error_type status = kDNSSDAdvertisingProxyStatus_NoError;
    430     advertising_proxy_subscription_t *subscription = NULL;
    431     adv_service_state_t *service = NULL;
    432 
    433     // Sanity check arguments.
    434     if (subscription_ret == NULL || regtype == NULL || callback == NULL) {
    435         status = kDNSSDAdvertisingProxyStatus_Invalid;
    436         goto out;
    437     }
    438 
    439     run_context_t queue = clientq;
    440     if (queue == NULL) {
    441         queue = dispatch_get_main_queue();
    442     }
    443 
    444     subscription = advertising_proxy_subscription_create();
    445     if (subscription == NULL) {
    446         status = kDNSSDAdvertisingProxyStatus_NoMemory;
    447         goto out;
    448     }
    449     service = adv_service_state_create(regtype);
    450     if (service == NULL) {
    451         status = kDNSSDAdvertisingProxyStatus_NoMemory;
    452         goto out;
    453     }
    454     adv_service_state_subscriber_add(service, subscription);
    455     subscription->service = service;
    456     RETAIN_HERE(subscription->service, adv_service_state);
    457 
    458     subscription->service_callback = callback;
    459     subscription->context = context;
    460 
    461     DNSServiceErrorType error = DNSServiceBrowse(&service->null_browse_ref, 0, kDNSServiceInterfaceIndexAny,
    462                                                  regtype, NULL, advertising_proxy_browse_callback, service);
    463     if (error == kDNSServiceErr_NoError) {
    464         error = DNSServiceSetDispatchQueue(service->null_browse_ref, queue);
    465     }
    466     if (error != kDNSServiceErr_NoError) {
    467         ERROR("browse for service %{public}s in the default domains failed with %d", regtype, error);
    468         status = kDNSSDAdvertisingProxyStatus_UnknownErr;
    469         goto out;
    470     }
    471     RETAIN_HERE(service, adv_service_state); // For null_browse callback
    472 
    473     error = DNSServiceBrowse(&service->push_browse_ref, 0, kDNSServiceInterfaceIndexAny,
    474                              regtype, "default.service.arpa", advertising_proxy_browse_callback, service);
    475     if (error == kDNSServiceErr_NoError) {
    476         error = DNSServiceSetDispatchQueue(service->push_browse_ref, queue);
    477     }
    478     if (error != kDNSServiceErr_NoError) {
    479         ERROR("browse on service %{public}s in the push domain failed with %d", regtype, error);
    480         status = kDNSSDAdvertisingProxyStatus_UnknownErr;
    481         goto out;
    482     }
    483     RETAIN_HERE(service, adv_service_state); // For push_browse callback
    484     *subscription_ret = subscription;
    485     subscription = NULL;
    486     service = NULL;
    487 
    488 out:
    489     if (subscription != NULL) {
    490         advertising_proxy_subscription_cancel(subscription);
    491         RELEASE_HERE(subscription, advertising_proxy_subscription);
    492     }
    493     if (service != NULL) {
    494         adv_service_state_cancel(service);
    495         RELEASE_HERE(service, adv_service_state);
    496     }
    497     return status;
    498 }
    499 
    500 static void
    501 adv_instance_state_subscriber_add(adv_instance_state_t *instance, advertising_proxy_subscription_t *subscriber)
    502 {
    503     adv_subscriber_add(&instance->max_subscribers, &instance->subscribers, subscriber);
    504 }
    505 
    506 static void
    507 advertising_proxy_resolve_callback(DNSServiceRef UNUSED sdRef, DNSServiceFlags flags, uint32_t interface_index,
    508                                    DNSServiceErrorType error_code, const char *fullname, const char *hosttarget,
    509                                    uint16_t port, uint16_t txt_length, const unsigned char *txt_record, void *context)
    510 {
    511     adv_instance_state_t *instance = context;
    512     advertising_proxy_error_type error = kDNSSDAdvertisingProxyStatus_NoError;
    513 
    514     RELEASE_HERE(instance, adv_instance_state);
    515     if (error_code != kDNSServiceErr_NoError) {
    516         error = kDNSSDAdvertisingProxyStatus_UnknownErr;
    517     }
    518     for (size_t i = 0; i < instance->max_subscribers; i++) {
    519         advertising_proxy_subscription_t *subscription = instance->subscribers[i];
    520         if (subscription != NULL) {
    521             subscription->instance_callback(subscription, error, interface_index,
    522                                             (flags & kDNSServiceFlagsAdd) ? true : true, fullname, hosttarget, port,
    523                                             txt_length, txt_record, context);
    524         }
    525     }
    526     if (error != kDNSSDAdvertisingProxyStatus_NoError) {
    527         adv_instance_state_cancel(instance);
    528     }
    529     RELEASE_HERE(instance, adv_instance_state);
    530 }
    531 
    532 advertising_proxy_error_type
    533 advertising_proxy_resolve_create(advertising_proxy_subscription_t **subscription_ret, run_context_t clientq,
    534                                  const char *name, const char *regtype, const char *domain,
    535                                  advertising_proxy_resolve_reply callback, void *context)
    536 {
    537     advertising_proxy_error_type status = kDNSSDAdvertisingProxyStatus_NoError;
    538     advertising_proxy_subscription_t *subscription = NULL;
    539     adv_instance_state_t *local_instance = NULL;
    540     adv_instance_state_t *push_instance = NULL;
    541 
    542     // Sanity check arguments.
    543     if (subscription_ret == NULL || name == NULL || regtype == NULL || callback == NULL) {
    544         status = kDNSSDAdvertisingProxyStatus_Invalid;
    545         goto out;
    546     }
    547     run_context_t queue = clientq;
    548     if (queue == NULL) {
    549         queue = dispatch_get_main_queue();
    550     }
    551     subscription = advertising_proxy_subscription_create();
    552     if (subscription == NULL) {
    553         status = kDNSSDAdvertisingProxyStatus_NoMemory;
    554         goto out;
    555     }
    556     local_instance = adv_instance_state_create(name, regtype, "local");
    557     if (local_instance == NULL) {
    558         status = kDNSSDAdvertisingProxyStatus_NoMemory;
    559         goto out;
    560     }
    561     adv_instance_state_subscriber_add(local_instance, subscription);
    562     subscription->local_instance = local_instance;
    563     RETAIN_HERE(subscription->local_instance, adv_instance_state);
    564 
    565     subscription->instance_callback = callback;
    566     subscription->context = context;
    567 
    568     DNSServiceErrorType error = DNSServiceResolve(&local_instance->resolve_ref, 0, kDNSServiceInterfaceIndexAny,
    569                                                   name, regtype, domain == NULL ? "local" : domain,
    570                                                   advertising_proxy_resolve_callback, local_instance);
    571     if (error == kDNSServiceErr_NoError) {
    572         error = DNSServiceSetDispatchQueue(local_instance->resolve_ref, queue);
    573         if (error != kDNSServiceErr_NoError) {
    574             status = kDNSSDAdvertisingProxyStatus_UnknownErr;
    575             goto out;
    576         }
    577     }
    578     if (error != kDNSServiceErr_NoError) {
    579         ERROR("resolve for %{private}s on service %{public}s in the default domains failed with %d",
    580               name, regtype, error);
    581         status = kDNSSDAdvertisingProxyStatus_UnknownErr;
    582         goto out;
    583     }
    584     RETAIN_HERE(local_instance, adv_instance_state); // for callback
    585 
    586     if (domain == NULL) {
    587         push_instance = adv_instance_state_create(name, regtype, "default.service.arpa");
    588         if (push_instance == NULL) {
    589             status = kDNSSDAdvertisingProxyStatus_NoMemory;
    590             goto out;
    591         }
    592         adv_instance_state_subscriber_add(push_instance, subscription);
    593         subscription->push_instance = push_instance;
    594         RETAIN_HERE(subscription->push_instance, adv_instance_state);
    595 
    596         error = DNSServiceResolve(&push_instance->resolve_ref, 0, kDNSServiceInterfaceIndexAny,
    597                                   name, regtype, "default.service.arpa",
    598                                   advertising_proxy_resolve_callback, push_instance);
    599         if (error == kDNSServiceErr_NoError) {
    600             error = DNSServiceSetDispatchQueue(push_instance->resolve_ref, queue);
    601             if (error != kDNSServiceErr_NoError) {
    602                 status = kDNSSDAdvertisingProxyStatus_UnknownErr;
    603                 goto out;
    604             }
    605             RETAIN_HERE(push_instance, adv_instance_state); // for callback
    606         }
    607     }
    608 
    609     if (error != kDNSServiceErr_NoError) {
    610         ERROR("resolve for %{private}s on service %{public}s in the push domain failed with %d", name, regtype, error);
    611         status = kDNSSDAdvertisingProxyStatus_UnknownErr;
    612         goto out;
    613     }
    614     *subscription_ret = subscription;
    615     subscription = NULL;
    616     push_instance = NULL;
    617     local_instance = NULL;
    618 
    619 out:
    620     if (push_instance != NULL) {
    621         adv_instance_state_cancel(push_instance);
    622         RELEASE_HERE(push_instance, adv_instance_state);
    623     }
    624     if (local_instance != NULL) {
    625         adv_instance_state_cancel(local_instance);
    626         RELEASE_HERE(local_instance, adv_instance_state);
    627     }
    628     if (subscription != NULL) {
    629         advertising_proxy_subscription_cancel(subscription);
    630         RELEASE_HERE(subscription, advertising_proxy_subscription);
    631     }
    632     return status;
    633 }
    634 
    635 advertising_proxy_error_type
    636 advertising_proxy_get_addresses(advertising_proxy_subscription_t **subscription_ret, run_context_t clientq,
    637                                 const char *name, advertising_proxy_address_reply callback, void *context)
    638 {
    639     (void)subscription_ret;
    640     (void)clientq;
    641     (void)name;
    642     (void)callback;
    643     (void)context;
    644     return kDNSSDAdvertisingProxyStatus_NoError;
    645 }
    646 
    647 // Local Variables:
    648 // mode: C
    649 // tab-width: 4
    650 // c-file-style: "bsd"
    651 // c-basic-offset: 4
    652 // fill-column: 108
    653 // indent-tabs-mode: nil
    654 // End:
    655