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