1 /* dnssd-proxy.c 2 * 3 * Copyright (c) 2018-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 is a Discovery Proxy module for the SRP gateway. 18 * 19 * The motivation here is that it makes sense to co-locate the SRP relay and the Discovery Proxy because 20 * these functions are likely to co-exist on the same node, listening on the same port. For homenet-style 21 * name resolution, we need a DNS proxy that implements DNSSD Discovery Proxy for local queries, but 22 * forwards other queries to an ISP resolver. The SRP gateway is already expecting to do this. 23 * This module implements the functions required to allow the SRP gateway to also do Discovery Relay. 24 * 25 * The Discovery Proxy relies on Apple's DNS-SD library and the mDNSResponder DNSSD server, which is included 26 * in Apple's open source mDNSResponder package, available here: 27 * 28 * https://opensource.apple.com/tarballs/mDNSResponder/ 29 */ 30 31 #ifndef __APPLE_USE_RFC_3542 32 #define __APPLE_USE_RFC_3542 33 #endif // #ifndef __APPLE_USE_RFC_3542 34 35 #include <stdlib.h> 36 #include <string.h> 37 #include <stdio.h> 38 #include <unistd.h> 39 #include <errno.h> 40 #include <sys/socket.h> 41 #include <netinet/in.h> 42 #include <arpa/inet.h> 43 #include <fcntl.h> 44 #include <sys/time.h> 45 #include <ctype.h> 46 #include <sys/types.h> 47 #include <ifaddrs.h> 48 #include <net/if.h> 49 #include <stdarg.h> 50 #include <notify.h> 51 #ifdef IOLOOP_MACOS 52 #include <AssertMacros.h> 53 #include <SystemConfiguration/SystemConfiguration.h> 54 #endif // #ifdef IOLOOP_MACOS 55 56 #include "dns_sd.h" 57 #include "srp.h" 58 #include "dns-msg.h" 59 #include "srp-crypto.h" 60 #include "ioloop.h" 61 #include "dso-utils.h" 62 #include "dso.h" 63 #include "srp-tls.h" 64 #include "config-parse.h" 65 #include "srp-mdns-proxy.h" 66 #include "dnssd-proxy.h" 67 #include "srp-tls.h" 68 #include "srp-gw.h" 69 #include "srp-proxy.h" 70 #include "cti-services.h" 71 #include "route.h" 72 #include "srp-replication.h" 73 #if THREAD_DEVICE 74 # include "state-machine.h" 75 # include "service-publisher.h" 76 #endif 77 #if SRP_FEATURE_NAT64 78 #include "dns_sd_private.h" 79 #include "nat64-macos.h" 80 #endif 81 #include "advertising_proxy_services.h" 82 #include "srp-dnssd.h" 83 84 #define RESPONSE_WINDOW_MSECS 800 85 #define RESPONSE_WINDOW_USECS (RESPONSE_WINDOW_MSECS * 1000) // 800ms in microseconds. 86 87 extern srp_server_t *srp_servers; 88 #define SERIAL(x) ((x) == NULL ? 0 : (x)->serial) 89 90 // When do we build dnssd-proxy? 91 // 1. When we are integrating dnssd-proxy into srp-mdns-proxy: SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY == 1 92 // 2. When we are building standalone dnssd-proxy: !defined(BUILD_SRP_MDNS_PROXY) || (BUILD_SRP_MDNS_PROXY == 0) 93 // When do we not build dnssd-proxy? 94 // 3. When we are building srp-mdns-proxy without dnssd-proxy: other than the two cases above. 95 #if (SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY) || (!defined(BUILD_SRP_MDNS_PROXY) || (BUILD_SRP_MDNS_PROXY == 0)) 96 97 // Enumerate the list of interfaces, map them to interface indexes, give each one a name 98 // Have a tree of subdomains for matching 99 100 // Structures 101 102 typedef struct interface_addr interface_addr_t; 103 struct interface_addr { 104 interface_addr_t *next; 105 addr_t addr, mask; 106 }; 107 108 typedef struct dp_interface dp_interface_t; 109 struct dp_interface { 110 int ifindex; // The interface index (for use with sendmsg() and recvmsg(). 111 bool no_push; // If true, don't set up DNS Push for this domain 112 char *NONNULL name; // The name of the interface 113 interface_addr_t *NULLABLE addresses; // Addresses on this interface. 114 }; 115 116 typedef struct hardwired hardwired_t; 117 struct hardwired { 118 hardwired_t *NULLABLE next; 119 uint16_t type; 120 char *NONNULL name; 121 char *NONNULL fullname; 122 uint8_t *NULLABLE rdata; 123 uint16_t rdlen; 124 }; 125 126 typedef struct question question_t; 127 typedef struct answer answer_t; 128 typedef struct served_domain served_domain_t; 129 struct served_domain { 130 served_domain_t *NULLABLE next; // Active configurations, used for identifying a domain that matches 131 char *NONNULL domain; // The domain name of the interface, represented as a text string. 132 char *NONNULL domain_ld; // The same name, with a leading dot (if_domain_lp == if_domain + 1) 133 dns_name_t *NONNULL domain_name; // The domain name, parsed into labels. 134 hardwired_t *NULLABLE hardwired_responses; // Hardwired responses for this interface. 135 dp_interface_t *NULLABLE interface; // Interface to which this domain applies (may be NULL). 136 question_t *NULLABLE questions; // Questions that have been asked in the served domain. 137 }; 138 139 // There are two ways that a dnssd_query_t can be created. One is that a DNS datagram comes in that's a DNS 140 // query. In this case, we create the query, ask the question, generate a single DNS response, send it, and 141 // the dnssd query is finished. We could optimize retransmissions, but currently do not. UDP queries can 142 // happen either over a TCP connection or a UDP connection--the behavior is the same in either case. The 143 // other way is that it can be a DNS Push subscribe. A DNS Push subscribe query is finished either when the 144 // connection dies, or when we get a corresponding unsubscribe. 145 146 // For DNS Push queries, there is an "activity" object which tracks a particular subscription. Each activity 147 // can hold a reference to the comm_t. A disconnect from the comm_t should cancel all activities. Otherwise, 148 // the lifecycle of activities should not affect the comm_t. 149 150 // For DNS queries, a disconnect should cancel every query associated with the connection. This is complicated 151 // by the fact that we are not tracking outstanding queries--the query survives because the dnssd_txn_t object 152 // holds a reference to it; when the txn_t goes away, the connection will have no remaining references. 153 154 // In order to make this work, dnssd_query_t objects are tracked per connection. Dnssd_query_t objects do not 155 // hold a reference to their comm_t, but rather to their tracker. 156 157 // When a connection drops, the tracker object gets a disconnect callback, which triggers it to cancel out any 158 // remaining DNS transactions, and to cancel the associated DSO object. Cancelling the DSO object cancels all 159 // of the activities on that object; each activity is a dnssd_query_t object. Consequently, the tracker does 160 // not directly track DSO activities. 161 162 // In order to follow RFC 7766, the tracker keeps an idle timer going. If no DNS messages have been received 163 // on a connection for that amount of time, the tracker closes the connection; when the disconnect event arrives, 164 // the tracker is collected. If there is a DSO object on the tracker, the tracker is not responsible for tracking 165 // idle state. 166 167 typedef enum { 168 dp_tracker_session_none, 169 dp_tracker_session_push, 170 dp_tracker_session_srpl 171 } dp_tracker_session_type_t; 172 173 int cur_tracker_serial; 174 typedef struct dnssd_query dnssd_query_t; 175 typedef struct dp_tracker { 176 int ref_count; 177 int serial; 178 comm_t *connection; 179 dnssd_query_t *dns_queries; 180 dso_state_t *dso; 181 wakeup_t *idle_timeout; 182 dp_tracker_session_type_t session_type; 183 } dp_tracker_t; 184 185 struct answer { 186 answer_t *next; // List of answers to a question. 187 char *fullname; // Name returned in callback. 188 uint8_t *rdata; // Rdata returned in callback (wire format). 189 uint32_t interface_index; // Interface index returned in callback. 190 uint32_t ttl; // Time-to-live returned in callback (probably not useful for LLQ). 191 uint16_t rrtype; // Resource record type returned in callback. 192 uint16_t rrclass; // Resource record class returned in callback. 193 uint16_t rdlen; // Length of resource record data returned in callback. 194 }; 195 196 static int cur_question_serial; 197 struct question { 198 question_t *next; // List of questions that are being asked. 199 served_domain_t *served_domain; 200 dnssd_query_t *queries; // dnssd queries that are asking this question. 201 dnssd_txn_t *txn; // Subordinate DNSServiceRef for this question 202 char *name; // The name we are looking up. 203 answer_t *answers; // Answers this question has received. 204 int64_t start_time; // When this question was started. 205 int serviceFlags; // Service flags to use with this question. 206 int ref_count; // Reference count. 207 int serial; 208 uint32_t interface_index; // Which interface the query should use. 209 uint16_t type; // The type. 210 uint16_t qclass; // The class. 211 bool no_data; // True if "no such record" is received or all data gets removed 212 }; 213 214 static int cur_query_serial; 215 struct dnssd_query { 216 int ref_count; 217 int serial; 218 dp_tracker_t *tracker; // Tracks the connection that delivered this query. 219 dnssd_query_t *next; // For DNS queries, tracks other queries on the same connection, if any. 220 wakeup_t *wakeup; 221 dns_name_pointer_t enclosing_domain_pointer; 222 223 message_t *message; 224 dso_state_t *dso; // If this is a DNS Push query, the DSO state associated with it. 225 dso_activity_t *activity; 226 int num_questions; // In case of a multi-question query, how many questions were asked 227 bool is_edns0; 228 dns_towire_state_t towire; 229 uint8_t *p_dso_length; // Where to store the DSO length just before we write out a push notification. 230 dns_wire_t *response; 231 dns_message_t *response_msg; // In case we need to decompose the message to construct a multi-answer message. 232 size_t data_size; // Size of the data payload of the response. 233 dnssd_query_t *question_next; // Linked list of queries on the question this query is subscribed to. 234 question_t *question; // Question asked by this query pointing to a cache entry. 235 bool satisfied; // If true, this query has gotten an answer. Only relevant for straight DNS. 236 bool canceled; // If true, dnssd_query_cancel has been called on this query, so don't cancel it again. 237 }; 238 239 // Structure that is used to setup the mDNS discovery for dnssd-proxy. 240 struct dnssd_proxy_advertisements { 241 wakeup_t *wakeup_timer; // Used to setup a timer to advertise records repeatedly until it succeeds. 242 dnssd_txn_t *txn; // Contains event loop. 243 DNSServiceRef service_ref; // Shared DNSServiceRef for all registering operation. 244 DNSRecordRef ns_record_ref; // Used to update the advertised NS record. 245 DNSRecordRef ptr_record_ref; // Used to update the advertised PTR record. 246 char *domain_to_advertise; // The domain to be advertised in NS and PTR records. 247 srp_server_t *server_state; 248 SCDynamicStoreContext sc_context; 249 }; 250 251 #if SRP_FEATURE_DISCOVERY_PROXY_SERVER 252 // Structure that is used to advertise the push discovery service in .local domain. 253 struct dnssd_dp_proxy_advertisements { 254 wakeup_t *wakeup_timer; // Used to setup a timer to advertise push service repeatedly until it succeeds. 255 dnssd_txn_t *txn; // The event loop. 256 DNSServiceRef service_ref; // DNSServiceRef to register the service. 257 srp_server_t *server_state; 258 }; 259 #endif 260 261 // Configuration file settings 262 263 uint16_t dnssd_proxy_udp_port; 264 uint16_t dnssd_proxy_tcp_port; 265 uint16_t dnssd_proxy_tls_port; 266 const char *my_name = "discoveryproxy.home.arpa."; 267 char *listen_addrs[MAX_ADDRS]; 268 int num_listen_addrs; 269 char *publish_addrs[MAX_ADDRS]; 270 int num_publish_addrs; 271 char *tls_cacert_filename; 272 char *tls_cert_filename = "/etc/dnssd-proxy/server.crt"; 273 char *tls_key_filename = "/etc/dnssd-proxy/server.key"; 274 275 comm_t *dnssd_proxy_listeners[4 + MAX_ADDRS]; 276 int dnssd_proxy_num_listeners; 277 question_t *questions_without_domain; // Questions that aren't in a served domain 278 served_domain_t *served_domains; 279 int num_push_sessions; // Number of connections from DNS Push clients 280 int dp_num_outstanding_queries; 281 int num_push_sessions_dropped_for_load; 282 int num_queries_dropped_for_load; 283 284 #if SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 285 dnssd_txn_t *shared_discovery_txn; 286 #endif // SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 287 wakeup_t *discovery_restart_wakeup; 288 289 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 290 static char uuid_name[DNS_MAX_NAME_SIZE + 1]; 291 static char my_name_buf[DNS_MAX_NAME_SIZE + 1]; 292 static CFStringRef sc_dynamic_store_key_host_name; 293 static char local_host_name[DNS_MAX_NAME_SIZE + 1]; 294 static char local_host_name_dot_local[DNS_MAX_NAME_SIZE + 1]; 295 #endif // #if (SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY) 296 297 #if THREAD_BORDER_ROUTER && SRP_FEATURE_SRP_COMBINED_DNSSD_PROXY 298 extern char *thread_interface_name; 299 #endif // THREAD_BORDER_ROUTER && SRP_FEATURE_COMBINED_DNSSD_PROXY 300 301 // Globals 302 303 const char push_subscription_activity_type[] = "push subscription"; 304 305 static const char local_suffix[] = ".local."; 306 307 bool tls_fail = false; // Command line argument, for testing. 308 309 // Macros 310 311 #define THREAD_DOMAIN "thread.home.arpa." 312 // "openthread." will change in the future once we have a way to get thread network ID 313 #define THREAD_DOMAIN_WITH_ID "openthread." THREAD_DOMAIN 314 #define DEFAULT_SERVICE_ARPA_DOMAIN "default.service.arpa." 315 #define HOME_NET_DOMAIN "home.arpa." 316 #define DOT_HOME_NET_DOMAIN ".home.arpa." 317 #define DOT_LOCAL_DOMAIN "local." 318 #define LOCAL "local." 319 #define DOT_LOCAL ".local." 320 #define IPV4_REVERSE_LOOKUP_DOMAIN "in-addr.arpa." 321 #define IPV6_REVERSE_LOOKUP_DOMAIN "ip6.arpa." 322 #define SRV_TYPE_FOR_AUTOMATIC_BROWSING_DOMAIN "lb._dns-sd._udp" 323 #define TOWIRE_CHECK(note, towire, func) { func; if ((towire)->error != 0 && failnote == NULL) failnote = (note); } 324 #define BUSY_RETRY_DELAY_MS (5 * 60 * 1000) // Five minutes. 325 #define MAX_DSO_CONNECTIONS 15 // Should be enough for a typical home network, assuming more hosts -> more BRs 326 327 // RFC8766 says for us to clamp the TTL on proxied mDNS records to 10s. In practice this appears to be much 328 // too short, because if the DNSSD server (e.g. mDNSResponder) is doing an LLQ, this results in a refresh 329 // interval of <10s, which is kind of painful. 330 #define RFC8766_TTL_CLAMP 300 331 332 #define VALIDATE_TRACKER_CONNECTION_NON_NULL() \ 333 do { \ 334 if (query->tracker == NULL) { \ 335 ERROR("[Q%d] query->tracker NULL for query!", SERIAL(query)); \ 336 return; \ 337 } \ 338 if (query->tracker->connection == NULL) { \ 339 ERROR("[Q%d][TRK%d] query->tracker->connection NULL", SERIAL(query), SERIAL(query->tracker)); \ 340 return; \ 341 } \ 342 } while (false) 343 344 #ifdef SRP_TEST_SERVER 345 extern void (*srp_test_dso_message_finished)(void *context, message_t *message, dso_state_t *dso); 346 extern ready_callback_t srp_test_dnssd_tls_listener_ready; 347 extern void *srp_test_tls_listener_context; 348 #endif 349 350 // Forward references 351 352 static void dns_push_start(dnssd_query_t *query); 353 354 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 355 static void served_domain_free(served_domain_t *const served_domain); 356 #endif 357 358 static served_domain_t *NULLABLE 359 new_served_domain(dp_interface_t *const NULLABLE interface, const char * NONNULL domain); 360 361 #if STUB_ROUTER 362 static served_domain_t *NULLABLE 363 find_served_domain(const char *const NONNULL domain); 364 365 static bool 366 string_ends_with(const char *const NONNULL str, const char *const NONNULL suffix); 367 #endif // STUB_ROUTER 368 369 static void dp_query_towire_reset(dnssd_query_t *query); 370 371 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 372 static served_domain_t *NONNULL 373 add_new_served_domain_with_interface(const char *const NONNULL name, 374 const addr_t *const NULLABLE address, const addr_t *const NULLABLE mask);; 375 376 #if STUB_ROUTER 377 static bool 378 dnssd_hardwired_add_or_remove_address_in_domain(const char *const NONNULL name, 379 const char *const NONNULL domain_to_change, const addr_t *const NONNULL address, const bool add); 380 #endif // STUB_ROUTER 381 382 static bool 383 dnssd_hardwired_setup_dns_push_for_domain(served_domain_t *const NONNULL served_domain); 384 #endif // SRP_FEATURE_DYNAMIC_CONFIGURATION 385 386 #if STUB_ROUTER 387 static bool 388 start_timer_to_advertise(dnssd_proxy_advertisements_t *NONNULL context, 389 const char *const NULLABLE domain_to_advertise, const uint32_t interval); 390 #endif 391 392 static bool 393 interface_process_addr_change(dp_interface_t *const NONNULL interface, const addr_t *const NONNULL address, 394 const addr_t *const NONNULL mask, const enum interface_address_change event_type); 395 396 static void dns_question_callback(DNSServiceRef UNUSED sdRef, DNSServiceFlags flags, uint32_t UNUSED interfaceIndex, 397 DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, 398 uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context); 399 static void dns_push_callback(void *context, void *event_context, dso_state_t *dso, dso_event_type_t eventType); 400 #if SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 401 static void dp_setup_shared_discovery_txn(void); 402 #endif 403 static void dp_query_reply_from_cache(question_t *question, dnssd_query_t *query, bool remove); 404 static void dp_handle_server_disconnect(void *UNUSED context, int status); 405 static void dp_question_answers_free(question_t *question); 406 static void question_finalize(question_t *question); 407 408 // For debugging 409 static wakeup_t *connection_dropper; 410 extern dso_state_t *dso_connections; 411 static void dp_drop_connections(void *UNUSED context); 412 #ifdef SRP_TEST_SERVER 413 served_domain_t *NULLABLE last_freed_domain; 414 #endif 415 416 static void 417 dp_question_context_release(void *context) 418 { 419 question_t *question = context; 420 RELEASE_HERE(question, question); 421 } 422 423 static DNSServiceErrorType 424 dp_start_question(question_t *question, bool dns64) 425 { 426 DNSServiceErrorType err; 427 DNSServiceRef sdref; 428 size_t len; 429 char name[DNS_MAX_NAME_SIZE + 1]; 430 char *np; 431 432 // If a query has a served domain, query->question->name is the subdomain of the served domain that is 433 // being queried; otherwise query->question->name is the whole name. 434 if (question->served_domain != NULL) { 435 len = strlen(question->name); 436 if (question->served_domain->interface != NULL) { 437 if (len + sizeof local_suffix > sizeof name) { 438 ERROR("[QU%d] question name %s is too long for .local.", SERIAL(question), name); 439 return kDNSServiceErr_BadParam; 440 } 441 memcpy(name, question->name, len); 442 memcpy(&name[len], local_suffix, sizeof local_suffix); 443 } else { 444 size_t dlen = strlen(question->served_domain->domain_ld) + 1; 445 if (len + dlen > sizeof name) { 446 ERROR("[QU%d] question name %s is too long for %s.", SERIAL(question), name, question->served_domain->domain); 447 return kDNSServiceErr_BadParam; 448 } 449 memcpy(name, question->name, len); 450 memcpy(&name[len], question->served_domain->domain_ld, dlen); 451 } 452 np = name; 453 } else { 454 np = question->name; 455 } 456 457 int shared_connection_flag = 0; 458 #if SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 459 dp_setup_shared_discovery_txn(); 460 if (shared_discovery_txn != NULL) { 461 sdref = shared_discovery_txn->sdref; 462 shared_connection_flag = kDNSServiceFlagsShareConnection; 463 } 464 #endif // SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 465 466 uint32_t question_interface = question->interface_index; 467 #if SRP_FEATURE_LOCAL_DISCOVERY 468 if (question_interface == kDNSServiceInterfaceIndexInfra) { 469 int ret = -1; 470 #if STUB_ROUTER 471 ret = route_get_current_infra_interface_index(); 472 #else 473 static unsigned en0_ifindex = 0; 474 if (en0_ifindex == 0) { 475 en0_ifindex = if_nametoindex("en0"); 476 if (en0_ifindex == 0) { 477 ERROR("getting en0 ifindex failed!"); 478 } 479 } 480 if (en0_ifindex != 0) { 481 ret = en0_ifindex; 482 } 483 #endif // STUB_ROUTER 484 // If we don't have an infrastructure interface, refuse the query. 485 if (ret < 0) { 486 return kDNSServiceErr_Refused; 487 } 488 question_interface = ret; 489 } 490 #endif // SRP_FEATURE_LOCAL_DISCOVERY 491 492 #if SRP_FEATURE_NAT64 493 const DNSServiceAttribute *attr = NULL; 494 if (dns64 && (question->type == dns_rrtype_aaaa) && (question->qclass == dns_qclass_in)) { 495 attr = &kDNSServiceAttributeAAAAFallback; 496 } 497 err = dns_service_query_record_wa(srp_servers, &sdref, question->serviceFlags | shared_connection_flag, 498 question_interface, np, question->type, 499 question->qclass, attr, dns_question_callback, question); 500 #else 501 (void)dns64; 502 err = dns_service_query_record(srp_servers, &sdref, question->serviceFlags | shared_connection_flag, question_interface, np, 503 question->type, question->qclass, dns_question_callback, question); 504 #endif 505 if (err != kDNSServiceErr_NoError) { 506 ERROR("[QU%d] DNSServiceQueryRecord failed for '%s': %d", SERIAL(question), np, err); 507 } else { 508 INFO("[QU%d] txn %p new sdref %p", SERIAL(question), question->txn, sdref); 509 #if SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTION 510 question->txn = ioloop_dnssd_txn_add_subordinate(sdref, dp_question_context_release, NULL); 511 #else 512 question->txn = dns_service_ioloop_txn_add(srp_servers, sdref, question, dp_question_context_release, dp_handle_server_disconnect); 513 #endif // SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 514 RETAIN_HERE(question, question); // For the callback 515 #if SRP_FEATURE_NAT64 516 INFO("[QU%d] DNSServiceQueryRecordWithAttribute started for '" PRI_S_SRP "': %d", SERIAL(question), np, err); 517 #else 518 INFO("[QU%d] DNSServiceQueryRecord started for '" PRI_S_SRP "': %d", SERIAL(question), np, err); 519 #endif // SRP_FEATURE_NAT64 520 } 521 return err; 522 } 523 524 static bool 525 dp_iterate_questions_on_list(question_t *list, bool (*callback)(question_t *question, void *context), void *context) 526 { 527 for (question_t *question = list; question; question = question->next) { 528 if (callback(question, context)) { 529 return true; 530 } 531 } 532 return false; 533 } 534 535 static bool 536 dp_iterate_questions(bool (*callback)(question_t *question, void *context), void *context) 537 { 538 if (dp_iterate_questions_on_list(questions_without_domain, callback, context)) { 539 return true; 540 } 541 542 for (served_domain_t *domain = served_domains; domain != NULL; domain = domain->next) { 543 if (dp_iterate_questions_on_list(domain->questions, callback, context)) { 544 return true; 545 } 546 } 547 return false; 548 } 549 550 static bool 551 dp_restart_question(question_t *question, void *context) 552 { 553 bool dns64 = *((bool *)context); 554 if (question->txn == NULL) { 555 dp_start_question(question, dns64); 556 } 557 return false; 558 } 559 560 static void 561 dp_restart_all_questions(void *UNUSED context) 562 { 563 bool dns64 = false; 564 #if SRP_FEATURE_NAT64 565 if (srp_servers->srp_nat64_enabled) { 566 dns64 = nat64_is_active(); 567 } 568 #endif 569 dp_iterate_questions(dp_restart_question, &dns64); 570 } 571 572 static bool 573 dp_void_question(question_t *question, void *UNUSED context) 574 { 575 if (question->txn != NULL) { 576 INFO("[QU%d] question->txn = %p", SERIAL(question), question->txn); 577 #if SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 578 question->txn->sdref = NULL; 579 #else 580 ioloop_dnssd_txn_cancel(question->txn); 581 #endif 582 ioloop_dnssd_txn_release(question->txn); 583 question->txn = NULL; 584 } 585 if (question->answers != NULL) { 586 dnssd_query_t *next, *query = question->queries; 587 while(query != NULL) { 588 next = query->question_next; 589 if (query->dso != NULL) { 590 dp_query_reply_from_cache(question, query, true); 591 } 592 query = next; 593 } 594 dp_question_answers_free(question); 595 } 596 597 return false; 598 } 599 600 // NULLs out all outstanding questions (after an mDNSResponder crash). These pointers are rendered invalid when the 601 // parent transaction is deallocated, so this should not result in any leaks. 602 static void 603 dp_void_all_questions(void) 604 { 605 dp_iterate_questions(dp_void_question, NULL); 606 } 607 608 static void 609 dp_handle_server_disconnect(void *UNUSED context, int status) 610 { 611 INFO("status %d", status); 612 dp_void_all_questions(); 613 if (discovery_restart_wakeup == NULL) { 614 discovery_restart_wakeup = ioloop_wakeup_create(); 615 } 616 if (discovery_restart_wakeup != NULL) { 617 // Try to reconnect to mDNSResponder after a second. 618 ioloop_add_wake_event(discovery_restart_wakeup, NULL, dp_restart_all_questions, NULL, 1000); 619 } 620 } 621 622 #if SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 623 static void 624 dp_setup_shared_discovery_txn(void) 625 { 626 if (shared_discovery_txn == NULL) { 627 DNSServiceRef sdref; 628 int err = DNSServiceCreateConnection(&sdref); 629 if (err != kDNSServiceErr_NoError) { 630 return false; 631 } 632 shared_discovery_txn = ioloop_dnssd_txn_add(sdref, NULL, NULL, dp_handle_server_disconnect); 633 if (shared_discovery_txn == NULL) { 634 ERROR("unable to create shared connection for registration."); 635 DNSServiceRefDeallocate(sdref); 636 return false; 637 } 638 INFO("shared_discovery_txn = %p sdref = %p", shared_discovery_txn, sdref); 639 } 640 } 641 #endif // SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 642 643 void 644 dp_start_dropping(void) 645 { 646 if (connection_dropper == NULL) { 647 connection_dropper = ioloop_wakeup_create(); 648 if (connection_dropper == NULL) { 649 ERROR("can't create connection dropper."); 650 return; 651 } 652 } 653 ioloop_add_wake_event(connection_dropper, NULL, dp_drop_connections, NULL, 90 * 1000); 654 } 655 656 static void 657 dp_drop_connections(void *UNUSED context) 658 { 659 for (dso_state_t *dso = dso_connections; dso != NULL; dso = dso->next) { 660 if (dso->cb == dns_push_callback) { 661 dp_tracker_t *tracker = dso->context; 662 INFO("dropping connection for " PRI_S_SRP ".", dso->remote_name); 663 if (tracker->connection != NULL) { 664 ioloop_comm_cancel(tracker->connection); 665 } 666 } 667 } 668 dp_start_dropping(); 669 } 670 671 static void 672 dp_tracker_finalize(dp_tracker_t *tracker) 673 { 674 // At this point tracker should have nothing attached to it that we need to get rid of, except maybe the 675 // wakeup timer. 676 if (tracker->idle_timeout != NULL) { 677 ioloop_wakeup_release(tracker->idle_timeout); 678 } 679 // The only case where tracker->connection should still exist at this point is when the connection turned 680 // out to be an srp replication connection. 681 if (tracker->connection) { 682 ioloop_comm_release(tracker->connection); 683 } 684 free(tracker); 685 } 686 687 static void 688 dp_answer_free(answer_t *answer) 689 { 690 if (answer != NULL) { 691 free(answer->fullname); 692 free(answer); 693 } 694 } 695 696 static void 697 dp_question_answers_free(question_t *question) 698 { 699 // De-allocate answers 700 answer_t *answer = question->answers; 701 answer_t *next; 702 while (answer != NULL) { 703 next = answer->next; 704 dp_answer_free(answer); 705 answer = next; 706 } 707 question->answers = NULL; 708 } 709 710 // The finalize function will deallocate answers associated with the question, 711 // remove question from the question list and deallocate the question. 712 static void 713 question_finalize(question_t *question) 714 { 715 INFO("[QU%d] type %d class %d " PRI_S_SRP, SERIAL(question), question->type, question->qclass, question->name); 716 dp_question_answers_free(question); 717 free(question->name); 718 free(question); 719 } 720 721 static void 722 dp_question_cancel(question_t *question) 723 { 724 if (question->txn != NULL) { 725 INFO("[QU%d] question->txn = %p sdref=%p", SERIAL(question), question->txn, question->txn->sdref); 726 ioloop_dnssd_txn_cancel(question->txn); 727 ioloop_dnssd_txn_release(question->txn); 728 question->txn = NULL; 729 } 730 731 // Remove the question from its list. 732 question_t **questions, *q_cur; 733 if (question->served_domain != NULL) { 734 questions = &question->served_domain->questions; 735 } else { 736 questions = &questions_without_domain; 737 } 738 while (*questions != NULL ) { 739 q_cur = *questions; 740 if (q_cur == question) { 741 *questions = q_cur->next; 742 break; 743 } else { 744 questions = &q_cur->next; 745 } 746 } 747 // If this was the last question, see if the served domain is still on the served domain list; if not, 748 // this is the last reference, so free it. 749 if (question->served_domain != NULL && question->served_domain->questions == NULL) { 750 served_domain_t *served_domain; 751 for (served_domain = served_domains; served_domain; served_domain = served_domain->next) { 752 if (served_domain == question->served_domain) { 753 break; 754 } 755 } 756 if (served_domain == NULL) { 757 served_domain_free(question->served_domain); 758 } 759 } 760 RELEASE_HERE(question, question); // Release from the list. 761 } 762 763 // Called when the last reference on the query has been released. 764 static void 765 dnssd_query_finalize(void *context) 766 { 767 dnssd_query_t *query = context; 768 if (query->tracker != NULL) { 769 RELEASE_HERE(query->tracker, dp_tracker); 770 query->tracker = NULL; 771 } 772 if (query->message != NULL) { 773 ioloop_message_release(query->message); 774 query->message = NULL; 775 } 776 if (query->wakeup != NULL) { 777 ioloop_wakeup_release(query->wakeup); 778 query->wakeup = NULL; 779 } 780 if (query->response != NULL) { 781 free(query->response); 782 query->response = NULL; 783 } 784 if (query->response_msg != NULL) { 785 dns_message_free(query->response_msg); 786 query->response_msg = NULL; 787 } 788 if (query->question != NULL) { 789 RELEASE_HERE(query->question, question); 790 query->question = NULL; 791 } 792 free(query); 793 dp_num_outstanding_queries--; 794 } 795 796 // Remove any finished queries from the question cache query list. 797 static void 798 dp_question_cache_remove_queries(question_t *question) 799 { 800 // Convenience 801 if (question == NULL) { 802 return; 803 } 804 805 dnssd_query_t **pptr = &(question->queries); 806 RETAIN_HERE(question, question); 807 if (question->queries != NULL) { 808 while (*pptr != NULL) { 809 dnssd_query_t *cquery = *pptr; 810 if (cquery->satisfied) { 811 *pptr = cquery->question_next; 812 RELEASE_HERE(cquery, dnssd_query); 813 } else { 814 pptr = &cquery->question_next; 815 } 816 } 817 if (question->queries == NULL) { 818 dp_question_cancel(question); 819 } 820 } 821 RELEASE_HERE(question, question); 822 } 823 824 static void 825 dp_tracker_context_release(void *context) 826 { 827 dp_tracker_t *tracker = context; 828 RELEASE_HERE(tracker, dp_tracker); 829 } 830 831 static void 832 dp_tracker_went_away(dp_tracker_t *tracker) 833 { 834 // Reduce the number of outstanding connections (should never go below zero). 835 if (tracker->session_type == dp_tracker_session_push) { 836 if (--num_push_sessions < 0) { 837 FAULT("DNS Push connection count went negative"); 838 num_push_sessions = 0; 839 } else { 840 INFO("[TRK%d][DSO%d][C%d] dso connection count dropped: %d", SERIAL(tracker), SERIAL(tracker->dso), SERIAL(tracker->connection), num_push_sessions); 841 } 842 } 843 } 844 845 static void 846 dp_tracker_idle(void *context) 847 { 848 dp_tracker_t *tracker = context; 849 // Shouldn't be NULL. 850 if (tracker->connection != NULL) { 851 comm_t *connection = tracker->connection; 852 INFO("[TRK%d][DSO%d][C%d] tracker for connection " PRI_S_SRP " has gone idle.", 853 SERIAL(tracker), SERIAL(tracker->dso), SERIAL(connection), connection->name); 854 855 // If the connection is already disconnected, it's already released its reference to the tracker. If not, 856 // the release below will release tracker as a side effect. So in case tracker survives, clear the 857 // connection pointer. 858 tracker->connection = NULL; 859 // The POSIX ioloop just hands us the "listener", which we do not want to cancel. 860 if (!connection->is_listener) { 861 ioloop_comm_cancel(connection); 862 } 863 ioloop_comm_release(connection); 864 } 865 } 866 867 static void 868 dp_tracker_idle_after(dp_tracker_t *tracker, int seconds, dnssd_query_t *query) 869 { 870 if (tracker->connection != NULL && !tracker->connection->is_listener && 871 tracker->dso == NULL && (query == NULL || 872 (tracker->dns_queries == NULL || 873 (tracker->dns_queries == query && query->next == NULL)))) 874 { 875 if (tracker->idle_timeout == NULL) { 876 tracker->idle_timeout = ioloop_wakeup_create(); 877 } 878 if (tracker->idle_timeout == NULL) { 879 ERROR("[TRK%d] no memory for idle timeout", SERIAL(tracker)); 880 } else { 881 ioloop_add_wake_event(tracker->idle_timeout, tracker, dp_tracker_idle, dp_tracker_context_release, seconds * MSEC_PER_SEC); 882 RETAIN_HERE(tracker, dp_tracker); 883 } 884 } 885 } 886 887 static bool 888 dp_same_message(message_t *a, message_t *b) 889 { 890 // Code commented out below catches retransmissions, but right now this won't work and we'll leak queries, 891 // so saving it for rdar://111808637 (dnssd-proxy is way too complicated) 892 if (a == b /* || (a != NULL && b != NULL && a->wire.id == b->wire.id) */ ) { 893 return true; 894 } 895 return false; 896 } 897 898 // Called at any time (prior to release!) to cancel a query. 899 static void 900 dnssd_query_cancel(dnssd_query_t *query) 901 { 902 INFO("[Q%d][QU%d] " PRI_S_SRP PUB_S_SRP PUB_S_SRP, SERIAL(query), SERIAL(query->question), 903 query->question == NULL ? "<null>" : query->question->name, 904 query->question == NULL ? "" : ((query->question->served_domain 905 ? (query->question->served_domain->interface 906 ? DOT_LOCAL 907 : query->question->served_domain->domain_ld) 908 : "")), 909 query->canceled ? " canceled" : ""); 910 // Avoid double-cancellation 911 if (query->canceled) { 912 return; 913 } 914 // Retain the query for the duration of dnssd_query_cancel so that it doesn't get finalized while we are working on it. 915 RETAIN_HERE(query, dnssd_query); 916 query->canceled = true; // prevent double-cancellation 917 if (query->tracker != NULL) { 918 dp_tracker_t *tracker = query->tracker; 919 920 // Retain the tracker so it doesn't get released while we are working on it. 921 RETAIN_HERE(tracker, dp_tracker); 922 923 if (query->dso == NULL) { 924 bool unsatisfied = false; 925 for (dnssd_query_t *list_query = tracker->dns_queries; list_query != NULL; list_query = list_query->next) { 926 if (dp_same_message(query->message, list_query->message)) { 927 if (!query->satisfied) { 928 unsatisfied = true; 929 } 930 } 931 } 932 933 if (!unsatisfied) { 934 // Scan the list freeing all queries relating to the message attached to the query that's been canceled. 935 // A UDP message will never have any other queries, but TCP connections can have multiple messages. 936 for (dnssd_query_t **qp = &tracker->dns_queries; *qp != NULL; ) { 937 dnssd_query_t *list_query = *qp; 938 939 // Release the current query either if it's the query that's being canceled, or this is a UDP message. 940 if (dp_same_message(query->message, list_query->message)) { 941 *qp = list_query->next; 942 943 // This might release the query, but we know that the tracker holds a reference to it, so 944 // we don't need another reference to it. 945 if (list_query->wakeup != NULL) { 946 ioloop_wakeup_release(list_query->wakeup); 947 list_query->wakeup = NULL; 948 } 949 950 // Release this query's reference to the tracker 951 RELEASE_HERE(tracker, dp_tracker); 952 list_query->tracker = NULL; 953 954 // The tracker was holding a reference to the query. 955 RELEASE_HERE(list_query, dnssd_query); 956 } else { 957 qp = &list_query->next; 958 } 959 } 960 } 961 } else { 962 // For DNS Push queries, drop the activity, which will release the query. 963 if (query->activity != NULL && query->dso != NULL) { 964 dso_activity_t *activity = query->activity; 965 dso_state_t *dso = query->dso; 966 dso_drop_activity(dso, activity); 967 query->activity = NULL; 968 } 969 // Now release the reference the query had on the tracker. 970 query->tracker = NULL; 971 RELEASE_HERE(tracker, dp_tracker); 972 } 973 974 // For TCP connections, wait for it to become idle before closing. 975 if (tracker->connection != NULL && tracker->dns_queries == NULL) { 976 if (tracker->connection->tcp_stream) { 977 dp_tracker_idle_after(tracker, 15, query); 978 } else { 979 #if UDP_LISTENER_USES_CONNECTION_GROUPS 980 ioloop_comm_cancel(tracker->connection); 981 #else 982 ioloop_comm_release(tracker->connection); 983 tracker->connection = NULL; 984 #endif 985 } 986 } 987 988 // Release the reference we retained on entry. 989 RELEASE_HERE(tracker, dp_tracker); 990 } else { 991 if (query->wakeup != NULL) { 992 ioloop_wakeup_release(query->wakeup); 993 query->wakeup = NULL; 994 } 995 } 996 query->satisfied = true; 997 RELEASE_HERE(query, dnssd_query); 998 } 999 1000 static void 1001 dp_query_track(dp_tracker_t *tracker, dnssd_query_t *query) 1002 { 1003 dnssd_query_t **qp = &tracker->dns_queries; 1004 1005 while (*qp != NULL) { 1006 if (*qp == query) { 1007 ERROR("[Q%d][TRK%d] query is already being tracked.", SERIAL(query), SERIAL(query->tracker)); 1008 return; 1009 } 1010 qp = &(*qp)->next; 1011 } 1012 *qp = query; 1013 RETAIN_HERE(query, dnssd_query); 1014 } 1015 1016 static void 1017 dp_tracker_disconnected(comm_t *UNUSED connection, void *context, int UNUSED error) 1018 { 1019 dp_tracker_t *tracker = context; 1020 dnssd_query_t *dns_queries = tracker->dns_queries, **qp, *query; 1021 comm_t *tracker_connection = tracker->connection; 1022 tracker->connection = NULL; 1023 tracker->dns_queries = NULL; 1024 1025 INFO("[TRK%d][DSO%d][C%d] queries %p", SERIAL(tracker), SERIAL(tracker->dso), SERIAL(tracker_connection), dns_queries); 1026 1027 // If there is a DSO state outstanding on the tracker, cancel any activities connected to it. 1028 if (tracker->dso != NULL) { 1029 dso_activity_t *activity = tracker->dso->activities; 1030 while (activity != NULL) { 1031 dso_drop_activity(tracker->dso, tracker->dso->activities); 1032 // Failsafe in case dso_drop_activity for some reason doesn't drop the activity. 1033 if (tracker->dso->activities == activity) { 1034 break; 1035 } 1036 activity = tracker->dso->activities; 1037 } 1038 dso_state_cancel(tracker->dso); 1039 dp_tracker_went_away(tracker); 1040 tracker->session_type = dp_tracker_session_none; 1041 tracker->dso = NULL; 1042 } 1043 1044 // We probably still have the connection object at this point, so we should release it, which could 1045 // in turn finalize the tracker. 1046 if (tracker_connection != NULL) { 1047 ioloop_comm_release(tracker_connection); 1048 tracker->connection = NULL; 1049 } 1050 1051 // If dns_queries is non-null, tracker still exists, but it might go away when we cancel the last 1052 // query. 1053 qp = &dns_queries; 1054 while (*qp != NULL) { 1055 query = *qp; 1056 *qp = query->next; 1057 RELEASE_HERE(query, dnssd_query); 1058 } 1059 } 1060 1061 static void 1062 dns_push_cancel(dso_activity_t *activity) 1063 { 1064 dnssd_query_t *query = (dnssd_query_t *)activity->context; 1065 INFO("[Q%d][QU%d] " PUB_S_SRP, SERIAL(query), SERIAL(query->question), activity->name); 1066 // We can either get here because the dso object is being finalized, or because the activity is being dropped. 1067 // In the former case, we need to cancel the query. In the latter case, we've been called as a result of 1068 // dnssd_query_cancel calling dso_drop_activity. dnssd_query_cancel sets query->activity to NULL before dropping 1069 // it, so we mustn't call back in to dnssd_query_cancel. 1070 if (query->activity != NULL) { 1071 query->activity = NULL; 1072 query->satisfied = true; 1073 dp_question_cache_remove_queries(query->question); 1074 dnssd_query_cancel(query); 1075 } 1076 // The activity held a reference to the query. 1077 RELEASE_HERE(query, dnssd_query); 1078 } 1079 1080 static void 1081 dp_tracker_not_idle(dp_tracker_t *tracker) 1082 { 1083 if (tracker->idle_timeout) { 1084 ioloop_cancel_wake_event(tracker->idle_timeout); 1085 } 1086 } 1087 1088 static served_domain_t * 1089 dp_served(dns_name_t *name, char *buf, size_t bufsize) 1090 { 1091 served_domain_t *sdt; 1092 dns_label_t *lim; 1093 1094 for (sdt = served_domains; sdt; sdt = sdt->next) { 1095 if ((lim = dns_name_subdomain_of(name, sdt->domain_name))) { 1096 dns_name_print_to_limit(name, lim, buf, bufsize); 1097 return sdt; 1098 } 1099 } 1100 return NULL; 1101 } 1102 1103 static bool 1104 is_in_local_domain(const dns_name_t *const NONNULL name) 1105 { 1106 const dns_label_t *prev_root_label; 1107 const dns_label_t *root_label; 1108 1109 for (prev_root_label = NULL, root_label = name; 1110 root_label->next != NULL; 1111 prev_root_label = root_label, root_label = root_label->next) 1112 ; 1113 1114 if (prev_root_label == NULL) { 1115 return false; 1116 } 1117 1118 #define LOCAL_DOMAIN_LABEL "local" 1119 if (prev_root_label->len != strlen(LOCAL_DOMAIN_LABEL)) { 1120 return false; 1121 } 1122 1123 if (!dns_labels_equal(prev_root_label->data, LOCAL_DOMAIN_LABEL, strlen(LOCAL_DOMAIN_LABEL))) { 1124 return false; 1125 } 1126 #undef LOCAL_DOMAIN_LABEL 1127 1128 return true; 1129 } 1130 1131 // Utility function to find "local" on the end of a string of labels. 1132 static bool 1133 truncate_local(dns_name_t *name) 1134 { 1135 dns_label_t *lp, *prev, *prevprev; 1136 1137 prevprev = prev = NULL; 1138 // Find the root label. 1139 for (lp = name; lp && lp->len; lp = lp->next) { 1140 prevprev = prev; 1141 prev = lp; 1142 } 1143 if (lp && prev && prevprev) { 1144 if (prev->len == 5 && dns_labels_equal(prev->data, "local", 5)) { 1145 dns_name_free(prev); 1146 prevprev->next = NULL; 1147 return true; 1148 } 1149 } 1150 return false; 1151 } 1152 1153 static bool 1154 dp_query_add_data_to_response(dnssd_query_t *query, const char *fullname, uint16_t rrtype, uint16_t rrclass, 1155 uint16_t rdlen, const void *rdata, int32_t ttl, const bool hardwired_response, 1156 bool dont_elide, uint16_t *counter) 1157 { 1158 bool record_added; 1159 dns_towire_state_t *towire = &query->towire; 1160 const char *failnote = NULL; 1161 const uint8_t *rd = rdata; 1162 char pbuf[DNS_MAX_NAME_SIZE + 1]; 1163 char rbuf[DNS_MAX_NAME_SIZE + 1]; 1164 uint8_t *revert = query->towire.p; // Remember where we were in case there's no room. 1165 question_t *question = query->question; 1166 1167 // Only do the translation if: 1168 // 1. We serve the domain. 1169 // 2. The response we will add does not come from our hardwired response set. 1170 const bool translate = (question->served_domain != NULL) && (!hardwired_response); 1171 1172 if (rdlen == 0) { 1173 INFO("[Q%d][QU%d] eliding zero-length response for " PRI_S_SRP " " PUB_S_SRP " %d", 1174 SERIAL(query), SERIAL(question), fullname, dns_rrtype_to_string(rrtype), rrclass); 1175 record_added = false; 1176 goto exit; 1177 } 1178 // Don't send A records for 127.* nor AAAA records for ::1 1179 if (dont_elide) { 1180 } else if (rrtype == dns_rrtype_a && rdlen == 4) { 1181 // Should use IN_LINKLOCAL and IN_LOOPBACK macros here, but for some reason they are not present on 1182 // OpenWRT. 1183 if (rd[0] == 127) { 1184 IPv4_ADDR_GEN_SRP(rd, rd_buf); 1185 INFO("[Q%d][QU%d] eliding localhost response for " PRI_S_SRP ": " PRI_IPv4_ADDR_SRP, 1186 SERIAL(query), SERIAL(question), fullname, IPv4_ADDR_PARAM_SRP(rd, rd_buf)); 1187 record_added = false; 1188 goto exit; 1189 } 1190 if (rd[0] == 169 && rd[1] == 254) { 1191 IPv4_ADDR_GEN_SRP(rd, rd_buf); 1192 INFO("[Q%d][QU%d] eliding link-local response for " PRI_S_SRP ": " PRI_IPv4_ADDR_SRP, 1193 SERIAL(query), SERIAL(question), fullname, IPv4_ADDR_PARAM_SRP(rd, rd_buf)); 1194 record_added = false; 1195 goto exit; 1196 } 1197 } else if (rrtype == dns_rrtype_aaaa && rdlen == 16) { 1198 struct in6_addr addr = *(struct in6_addr *)rdata; 1199 if (IN6_IS_ADDR_LOOPBACK(&addr)) { 1200 SEGMENTED_IPv6_ADDR_GEN_SRP(rdata, rdata_buf); 1201 INFO("[Q%d][QU%d] eliding localhost response for " PRI_S_SRP ": " PRI_SEGMENTED_IPv6_ADDR_SRP, 1202 SERIAL(query), SERIAL(question), fullname, SEGMENTED_IPv6_ADDR_PARAM_SRP(rdata, rdata_buf)); 1203 record_added = false; 1204 goto exit; 1205 } 1206 if (IN6_IS_ADDR_LINKLOCAL(&addr)) { 1207 SEGMENTED_IPv6_ADDR_GEN_SRP(rdata, rdata_buf); 1208 INFO("[Q%d][QU%d] eliding link-local response for " PRI_S_SRP ": " PRI_SEGMENTED_IPv6_ADDR_SRP, 1209 SERIAL(query), SERIAL(question), fullname, SEGMENTED_IPv6_ADDR_PARAM_SRP(rdata, rdata_buf)); 1210 record_added = false; 1211 goto exit; 1212 } 1213 } 1214 INFO("survived for rrtype " PUB_S_SRP " rdlen %d", dns_rrtype_to_string(rrtype), rdlen); 1215 1216 if (query->dso != NULL) { 1217 dns_push_start(query); 1218 } 1219 // Rewrite the domain if it's .local. 1220 if (question->served_domain != NULL) { 1221 TOWIRE_CHECK("concatenate_name_to_wire", towire, 1222 dns_concatenate_name_to_wire(towire, NULL, question->name, question->served_domain->domain)); 1223 INFO("[Q%d][QU%d] " PUB_S_SRP " answer: type " PUB_S_SRP " class %02d " PRI_S_SRP "." PRI_S_SRP, SERIAL(query), SERIAL(question), 1224 query->dso != NULL ? "PUSH" : "DNS ", dns_rrtype_to_string(rrtype), rrclass, question->name, question->served_domain->domain); 1225 } else { 1226 TOWIRE_CHECK("compress_name_to_wire", towire, dns_concatenate_name_to_wire(towire, NULL, NULL, question->name)); 1227 INFO("[Q%d][QU%d] " PUB_S_SRP " answer: type " PUB_S_SRP " class %02d " PRI_S_SRP " (p)", 1228 SERIAL(query), SERIAL(question), query->dso != NULL ? "push" : " dns", dns_rrtype_to_string(rrtype), rrclass, question->name); 1229 } 1230 TOWIRE_CHECK("rrtype", towire, dns_u16_to_wire(towire, rrtype)); 1231 TOWIRE_CHECK("rrclass", towire, dns_u16_to_wire(towire, rrclass)); 1232 TOWIRE_CHECK("ttl", towire, dns_ttl_to_wire(towire, ttl)); 1233 1234 // If necessary, correct domain names inside of rrdata. 1235 dns_rr_t answer; 1236 dns_name_t *name; 1237 unsigned offp = 0; 1238 1239 answer.type = rrtype; 1240 answer.qclass = rrclass; 1241 if (dns_rdata_parse_data(&answer, rdata, &offp, rdlen, rdlen, 0)) { 1242 switch(rrtype) { 1243 case dns_rrtype_cname: 1244 case dns_rrtype_ptr: 1245 case dns_rrtype_ns: 1246 case dns_rrtype_md: 1247 case dns_rrtype_mf: 1248 case dns_rrtype_mb: 1249 case dns_rrtype_mg: 1250 case dns_rrtype_mr: 1251 case dns_rrtype_nsap_ptr: 1252 case dns_rrtype_dname: 1253 name = answer.data.ptr.name; 1254 TOWIRE_CHECK("rdlength begin", towire, dns_rdlength_begin(towire)); 1255 break; 1256 case dns_rrtype_srv: 1257 name = answer.data.srv.name; 1258 TOWIRE_CHECK("rdlength begin", towire, dns_rdlength_begin(towire)); 1259 TOWIRE_CHECK("answer.data.srv.priority", towire, dns_u16_to_wire(towire, answer.data.srv.priority)); 1260 TOWIRE_CHECK("answer.data.srv.weight", towire, dns_u16_to_wire(towire, answer.data.srv.weight)); 1261 TOWIRE_CHECK("answer.data.srv.port", towire, dns_u16_to_wire(towire, answer.data.srv.port)); 1262 break; 1263 default: 1264 INFO("[Q%d][QU%d] record type " PUB_S_SRP " not translated", SERIAL(query), SERIAL(question), dns_rrtype_to_string(rrtype)); 1265 dns_rrdata_free(&answer); 1266 goto raw; 1267 } 1268 1269 dns_name_print(name, rbuf, sizeof rbuf); 1270 1271 if (translate && is_in_local_domain(name)) { 1272 // If the response requires the translation from <served domain> to ".local." and the response ends in 1273 // ".local.", truncate it. 1274 truncate_local(name); 1275 dns_name_print(name, pbuf, sizeof pbuf); 1276 TOWIRE_CHECK("concatenate_name_to_wire 2", towire, 1277 dns_concatenate_name_to_wire(towire, name, NULL, question->served_domain->domain)); 1278 INFO("[Q%d][QU%d] translating " PRI_S_SRP " to " PRI_S_SRP " . " PRI_S_SRP, 1279 SERIAL(query), SERIAL(question), rbuf, pbuf, question->served_domain->domain); 1280 } else { 1281 TOWIRE_CHECK("concatenate_name_to_wire 2", towire, 1282 dns_concatenate_name_to_wire(towire, name, NULL, NULL)); 1283 INFO("[Q%d][QU%d] compressing " PRI_S_SRP, SERIAL(query), SERIAL(question), rbuf); 1284 } 1285 1286 dns_name_free(name); 1287 dns_rdlength_end(towire); 1288 } else { 1289 ERROR("[Q%d][QU%d] rdata from mDNSResponder didn't parse!!", SERIAL(query), SERIAL(question)); 1290 raw: 1291 TOWIRE_CHECK("rdlen", towire, dns_u16_to_wire(towire, rdlen)); 1292 TOWIRE_CHECK("rdata", towire, dns_rdata_raw_data_to_wire(towire, rdata, rdlen)); 1293 } 1294 1295 if (towire->truncated || failnote) { 1296 ERROR("[Q%d][QU%d] RR ADD FAIL: " PUB_S_SRP, SERIAL(query), SERIAL(question), failnote); 1297 query->towire.p = revert; 1298 record_added = false; 1299 goto exit; 1300 } 1301 1302 record_added = true; 1303 if (counter != NULL && query->dso == NULL) { 1304 *counter = htons(ntohs(*counter) + 1); 1305 } 1306 exit: 1307 return record_added; 1308 } 1309 1310 static void 1311 dnssd_hardwired_add(served_domain_t *sdt, 1312 const char *name, const char *domain, size_t rdlen, const uint8_t *rdata, uint16_t type) 1313 { 1314 hardwired_t *hp, **hrp; 1315 size_t namelen = strlen(name); 1316 size_t domainlen = strlen(domain); 1317 size_t total = sizeof *hp; 1318 uint8_t *trailer; 1319 total += rdlen; // Space for RDATA 1320 total += namelen; // Space for name 1321 total += 1; // NUL 1322 total += namelen;// space for FQDN 1323 total += domainlen; 1324 total += 1; // NUL 1325 1326 hp = calloc(1, total + 4); 1327 if (hp == NULL) { 1328 ERROR("no memory for %s %s", name, domain); 1329 return; 1330 } 1331 trailer = ((uint8_t *)hp) + total; 1332 memcpy(trailer, "abcd", 4); 1333 hp->rdata = (uint8_t *)(hp + 1); 1334 hp->rdlen = rdlen; 1335 memcpy(hp->rdata, rdata, rdlen); 1336 hp->name = (char *)hp->rdata + rdlen; 1337 memcpy(hp->name, name, namelen); 1338 hp->name[namelen] = '\0'; 1339 hp->fullname = hp->name + namelen + 1; 1340 if (namelen != 0) { 1341 snprintf(hp->fullname, namelen + domainlen + 1, "%s%s", name, domain); 1342 } else { 1343 memcpy(hp->fullname, domain, domainlen); 1344 hp->fullname[domainlen] = '\0'; 1345 } 1346 if (hp->fullname + strlen(hp->fullname) + 1 != (char *)hp + total) { 1347 ERROR("%p != %p", hp->fullname + strlen(hp->fullname) + 1, ((char *)hp) + total); 1348 return; 1349 } 1350 if (memcmp(trailer, "abcd", 4)) { 1351 ERROR("ran off the end."); 1352 return; 1353 } 1354 hp->type = type; 1355 hp->next = NULL; 1356 1357 // Store this new hardwired_t at the end of the list unless a hardwired_t with the same name 1358 // is already on the list. If it is, splice it in. 1359 for (hrp = &sdt->hardwired_responses; *hrp != NULL; hrp = &(*hrp)->next) { 1360 hardwired_t *old = *hrp; 1361 if (old->type != hp->type) { 1362 continue; 1363 } 1364 if (strcasecmp(old->fullname, hp->fullname) != 0) { 1365 continue; 1366 } 1367 // The same name and type 1368 bool superseded; 1369 switch (type) { 1370 case dns_rrtype_a: 1371 case dns_rrtype_aaaa: 1372 case dns_rrtype_ns: 1373 case dns_rrtype_ptr: 1374 superseded = false; 1375 break; 1376 default: 1377 // dns_rrtype_soa 1378 // dns_rrtype_srv 1379 superseded = true; 1380 break; 1381 } 1382 1383 if (superseded) { 1384 INFO("superseding " PRI_S_SRP " name " PRI_S_SRP " type %d rdlen %d", old->fullname, 1385 old->name, old->type, old->rdlen); 1386 hp->next = old->next; 1387 free(old); 1388 } else { 1389 INFO("inserting before " PRI_S_SRP " name " PRI_S_SRP " type %d rdlen %d", old->fullname, 1390 old->name, old->type, old->rdlen); 1391 hp->next = old; 1392 } 1393 break; 1394 } 1395 *hrp = hp; 1396 1397 INFO("fullname " PRI_S_SRP " name " PRI_S_SRP " type %d rdlen %d", 1398 hp->fullname, hp->name, hp->type, hp->rdlen); 1399 } 1400 1401 #if STUB_ROUTER 1402 static bool 1403 dnssd_hardwired_remove_record(served_domain_t *const NONNULL sdt, const char *const NONNULL name, const char *const NONNULL domain, size_t rdlen, 1404 const void *const NULLABLE rdata, uint16_t type) 1405 { 1406 bool removed; 1407 hardwired_t *prev = NULL; 1408 hardwired_t *current; 1409 char full_name[DNS_MAX_NAME_SIZE + 1]; 1410 1411 int bytes_written = snprintf(full_name, sizeof(full_name), "%s%s", name, domain); 1412 require_action_quiet(bytes_written > 0 && (size_t)bytes_written < sizeof(full_name), exit, removed = false; 1413 ERROR("snprintf truncates the string - name length: %zu, domain length: %zu, buffer length: %zu", 1414 strlen(name), strlen(domain), sizeof(full_name)) 1415 ); 1416 1417 for (current = sdt->hardwired_responses; current != NULL; prev = current, current = current->next) { 1418 if (current->type != type) { 1419 continue; 1420 } 1421 if (rdata != NULL && current->rdlen != rdlen) { 1422 continue; 1423 } 1424 if (strcasecmp(current->fullname, full_name) != 0) { 1425 continue; 1426 } 1427 if (rdata != NULL && memcmp(current->rdata, rdata, rdlen) != 0) { 1428 continue; 1429 } 1430 // record found 1431 break; 1432 } 1433 require_action_quiet(current != NULL, exit, removed = false; 1434 ERROR("no matching hardwired_t found - record name: " PUB_S_SRP ", record type: %d", full_name, type)); 1435 1436 if (prev != NULL) { 1437 prev->next = current->next; 1438 } else { 1439 sdt->hardwired_responses = current->next; 1440 } 1441 free(current); 1442 1443 removed = true; 1444 exit: 1445 return removed; 1446 } 1447 1448 static bool 1449 dnssd_hardwired_add_or_remove_addr_record(served_domain_t *const NONNULL sdt, const addr_t *const NONNULL addr, 1450 const char *const NONNULL name, bool add) 1451 { 1452 dns_wire_t wire; 1453 dns_towire_state_t towire; 1454 bool succeeded; 1455 1456 memset(&towire, 0, sizeof towire); 1457 towire.message = &wire; 1458 towire.p = wire.data; 1459 towire.lim = towire.p + sizeof wire.data; 1460 1461 const void *rdata_ptr; 1462 size_t addr_len; 1463 uint16_t addr_type; 1464 if (addr->sa.sa_family == AF_INET) { 1465 rdata_ptr = &addr->sin.sin_addr; 1466 addr_len = sizeof(addr->sin.sin_addr); 1467 addr_type = dns_rrtype_a; 1468 } else { // addr.sa.sa_family == AF_INET6 1469 rdata_ptr = &addr->sin6.sin6_addr; 1470 addr_len = sizeof(addr->sin6.sin6_addr); 1471 addr_type = dns_rrtype_aaaa; 1472 } 1473 dns_rdata_raw_data_to_wire(&towire, rdata_ptr, addr_len); 1474 1475 if (add) { 1476 dnssd_hardwired_add(sdt, name, name[0] == '\0' ? sdt->domain : sdt->domain_ld, towire.p - wire.data, wire.data, 1477 addr_type); 1478 succeeded = true; 1479 } else { 1480 succeeded = dnssd_hardwired_remove_record(sdt, name, name[0] == '\0' ? sdt->domain : sdt->domain_ld, 1481 towire.p - wire.data, wire.data, addr_type); 1482 } 1483 1484 return succeeded; 1485 } 1486 1487 static bool 1488 dnssd_hardwired_add_or_remove_address_in_domain(const char *const NONNULL name, 1489 const char *const NONNULL domain_to_change, const addr_t *const NONNULL address, const bool add) 1490 { 1491 bool succeeded; 1492 1493 served_domain_t *served_domain = find_served_domain(domain_to_change); 1494 require_action_quiet(served_domain != NULL, exit, succeeded = false; 1495 ERROR("could not find served domain with the specified domain name - domain name: " PRI_S_SRP, domain_to_change) 1496 ); 1497 1498 succeeded = dnssd_hardwired_add_or_remove_addr_record(served_domain, address, name, add); 1499 require_action_quiet(succeeded, exit, succeeded = false; 1500 ERROR("failed to " PUB_S_SRP " address record - domain name: " PRI_S_SRP, 1501 domain_to_change, add ? "add" : "remove") 1502 ); 1503 1504 exit: 1505 return succeeded; 1506 } 1507 1508 static bool 1509 dnssd_hardwired_generate_ptr_name(const addr_t *const NONNULL addr, const addr_t *const NONNULL mask, 1510 char *name_buf, size_t buf_size) 1511 { 1512 char *name_ptr = name_buf; 1513 const char *const name_limit = name_ptr + buf_size; 1514 int bytes_written; 1515 bool succeeded; 1516 1517 #define RESET \ 1518 memset(&towire, 0, sizeof towire); \ 1519 towire.message = &wire; \ 1520 towire.p = wire.data; \ 1521 towire.lim = towire.p + sizeof wire.data 1522 1523 bytes_written = snprintf(name_ptr, name_limit - name_ptr, SRV_TYPE_FOR_AUTOMATIC_BROWSING_DOMAIN); 1524 require_action_quiet(bytes_written > 0 && bytes_written < name_limit - name_ptr, exit, succeeded = false; 1525 ERROR("snprintf truncates the string - bytes_written: %d, limit: %zd", bytes_written, name_limit - name_ptr)); 1526 name_ptr += bytes_written; 1527 1528 1529 if (addr->sa.sa_family == AF_INET) { 1530 const uint32_t subnet = (ntohl(addr->sin.sin_addr.s_addr) & ntohl(mask->sin.sin_addr.s_addr)); 1531 bytes_written = snprintf(name_ptr, name_limit - name_ptr, ".%u.%u.%u.%u", 1532 subnet & 0xFFU, (subnet >> 8) & 0xFFU, (subnet >> 16) & 0xFFU, (subnet >> 24) & 0xFFU); 1533 require_action(bytes_written > 0 && bytes_written < name_limit - name_ptr, exit, succeeded = false); 1534 // Remember to increase the name_ptr by bytes_written bytes if name_ptr is used later. 1535 1536 } else if (addr->sa.sa_family == AF_INET6) { 1537 const uint8_t *const addr_bytes = addr->sin6.sin6_addr.s6_addr; 1538 const uint8_t *const mask_bytes = mask->sin6.sin6_addr.s6_addr; 1539 for (int i = 15; i >= 0; i--) { 1540 for (int shift = 0; shift < 8; shift += 4) { 1541 bytes_written = snprintf(name_ptr, name_limit - name_ptr, ".%x", 1542 (addr_bytes[i] >> shift) & (mask_bytes[i] >> shift) & 15); 1543 require_action_quiet(bytes_written > 0 && bytes_written < name_limit - name_ptr, exit, succeeded = false; 1544 ERROR("snprintf truncates the string - bytes_written: %d, limit: %zd", 1545 bytes_written, name_limit - name_ptr) 1546 ); 1547 name_ptr += bytes_written; 1548 } 1549 } 1550 1551 } else { 1552 FAULT("skipping address type other than IPv4/IPv6 - type: %u", addr->sa.sa_family); 1553 succeeded = false; 1554 goto exit; 1555 } 1556 1557 succeeded = true; 1558 exit: 1559 return succeeded; 1560 } 1561 1562 static bool 1563 dnssd_hardwired_add_or_remove_ptr_record(served_domain_t *const NONNULL sdt, const addr_t *const NONNULL addr, 1564 const addr_t *const NONNULL mask, bool add) 1565 { 1566 char name[DNS_MAX_NAME_SIZE + 1]; 1567 dns_wire_t wire; 1568 dns_towire_state_t towire; 1569 bool succeeded; 1570 1571 #define RESET \ 1572 memset(&towire, 0, sizeof towire); \ 1573 towire.message = &wire; \ 1574 towire.p = wire.data; \ 1575 towire.lim = towire.p + sizeof wire.data 1576 1577 succeeded = dnssd_hardwired_generate_ptr_name(addr, mask, name, sizeof(name)); 1578 if (!succeeded) { 1579 INFO("address is not eligible to construct PTR record"); 1580 goto exit; 1581 } 1582 1583 for (served_domain_t *if_domain = served_domains; if_domain != NULL; if_domain = if_domain->next) { 1584 if (if_domain->interface == NULL || if_domain->interface->ifindex == 0) { 1585 continue; 1586 } 1587 RESET; 1588 INFO(PUB_S_SRP " PTR from " PRI_S_SRP " to " PRI_S_SRP, add ? "Adding" : "Removing", name, if_domain->domain); 1589 dns_full_name_to_wire(NULL, &towire, if_domain->domain); 1590 1591 if (add) { 1592 dnssd_hardwired_add(sdt, name, sdt->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_ptr); 1593 succeeded = true; 1594 } else { 1595 succeeded = dnssd_hardwired_remove_record(sdt, name, sdt->domain_ld, towire.p - wire.data, wire.data, 1596 dns_rrtype_ptr); 1597 } 1598 } 1599 1600 exit: 1601 return succeeded; 1602 } 1603 1604 static bool 1605 dnssd_hardwired_add_or_remove_ptr_in_domain(const char *const NONNULL domain_to_change, 1606 const addr_t *const NONNULL address, const addr_t *const NONNULL mask, const bool add) 1607 { 1608 bool succeeded; 1609 1610 served_domain_t *served_domain = find_served_domain(domain_to_change); 1611 require_action_quiet(served_domain != NULL, exit, succeeded = false; 1612 ERROR("could not find served domain with the specified domain name - domain name: " PRI_S_SRP, domain_to_change) 1613 ); 1614 1615 succeeded = dnssd_hardwired_add_or_remove_ptr_record(served_domain, address, mask, add); 1616 require_action_quiet(succeeded, exit, succeeded = false; 1617 ERROR("failed to " PUB_S_SRP " address record - domain name: " PRI_S_SRP, 1618 add ? "adding" : "removing", domain_to_change) 1619 ); 1620 1621 exit: 1622 return succeeded; 1623 } 1624 1625 static bool 1626 is_valid_address_to_publish(const addr_t *const NONNULL address) 1627 { 1628 bool is_valid = true; 1629 1630 if (address->sa.sa_family == AF_INET) { 1631 const struct in_addr *const ipv4_address = &(address->sin.sin_addr); 1632 const bool is_linklocal = is_in_addr_link_local(ipv4_address); 1633 const bool is_loopback = is_in_addr_loopback(ipv4_address); 1634 1635 if (is_linklocal || is_loopback) { 1636 IPv4_ADDR_GEN_SRP(&ipv4_address, ipv4_address_buf); 1637 INFO("ignoring the address for interface - address: " PRI_IPv4_ADDR_SRP ", address type: " PUB_S_SRP ".", 1638 IPv4_ADDR_PARAM_SRP(&ipv4_address, ipv4_address_buf), is_linklocal ? "link local" : "loopback"); 1639 is_valid = false; 1640 } 1641 1642 } else if (address->sa.sa_family == AF_INET6) { 1643 const struct in6_addr *const ipv6_address = &(address->sin6.sin6_addr); 1644 const bool is_linklocal = IN6_IS_ADDR_LINKLOCAL(ipv6_address); 1645 const bool is_loopback = IN6_IS_ADDR_LOOPBACK(ipv6_address); 1646 1647 if (is_linklocal || is_loopback) { 1648 IPv6_ADDR_GEN_SRP(ipv6_address->s6_addr, ipv6_address_buf); 1649 INFO("ignoring the address for interface - address: " PRI_IPv6_ADDR_SRP ", address type: " PUB_S_SRP ".", 1650 IPv6_ADDR_PARAM_SRP(ipv6_address->s6_addr, ipv6_address_buf), is_linklocal ? "link local" : "loopback"); 1651 is_valid = false; 1652 } 1653 1654 } else { 1655 // It is possible that MAC address is added for the interface, so ignore it. 1656 INFO("Non IPv4/IPv6 address added for the interface - sa_family: %u", address->sa.sa_family); 1657 is_valid = false; 1658 } 1659 1660 return is_valid; 1661 } 1662 1663 static bool 1664 dnssd_hardwired_process_addr_change(const addr_t *const NONNULL addr, const addr_t *const NONNULL mask, const bool add) 1665 { 1666 bool succeeded; 1667 1668 if (!is_valid_address_to_publish(addr)) { 1669 succeeded = true; 1670 goto exit; 1671 } 1672 1673 // Update the <local host name>.home.arpa. address mapping. 1674 succeeded = dnssd_hardwired_add_or_remove_address_in_domain("", my_name, addr, add); 1675 if (!succeeded) { 1676 ERROR("failed to update address record for domain - domain: " PRI_S_SRP, my_name); 1677 goto exit; 1678 } 1679 1680 // Update the <local host name>.<Thread ID>.thread.home.arpa. address mapping. 1681 succeeded = dnssd_hardwired_add_or_remove_address_in_domain(local_host_name, THREAD_DOMAIN_WITH_ID, addr, add); 1682 if (!succeeded) { 1683 ERROR("failed to update address record for domain - domain: " PUB_S_SRP, THREAD_DOMAIN_WITH_ID); 1684 goto exit; 1685 } 1686 1687 // Update the default.service.arpa. address mapping. 1688 succeeded = dnssd_hardwired_add_or_remove_address_in_domain(local_host_name, DEFAULT_SERVICE_ARPA_DOMAIN, addr, add); 1689 if (!succeeded) { 1690 ERROR("failed to update address record for domain - domain: " PUB_S_SRP, DEFAULT_SERVICE_ARPA_DOMAIN); 1691 goto exit; 1692 } 1693 1694 #if SRP_FEATURE_LOCAL_DISCOVERY 1695 // Update the default.service.arpa. address mapping. 1696 succeeded = dnssd_hardwired_add_or_remove_address_in_domain(local_host_name, DOT_LOCAL_DOMAIN, addr, add); 1697 if (!succeeded) { 1698 ERROR("failed to update address record for domain - domain: " PUB_S_SRP, LOCAL); 1699 goto exit; 1700 } 1701 #endif 1702 1703 // Setup the "_lb.dns-sd" 1704 // Update the "reverse mapping from address to browsing domain" for each eligible served domain under IPv6 or IPv4 1705 // reverse lookup domain. 1706 if (addr->sa.sa_family == AF_INET6) { 1707 succeeded = dnssd_hardwired_add_or_remove_ptr_in_domain(IPV6_REVERSE_LOOKUP_DOMAIN, addr, mask, add); 1708 } else if (addr->sa.sa_family == AF_INET) { 1709 succeeded = dnssd_hardwired_add_or_remove_ptr_in_domain(IPV4_REVERSE_LOOKUP_DOMAIN, addr, mask, add); 1710 } else { 1711 char buf[INET6_ADDRSTRLEN]; 1712 IOLOOP_NTOP(addr, buf); 1713 INFO("Skipping non IPv6/IPv4 address - addr:" PRI_S_SRP, buf); 1714 succeeded = true; 1715 } 1716 1717 exit: 1718 return succeeded; 1719 } 1720 1721 static void 1722 dnssd_hardwired_lbdomains_setup(void) 1723 { 1724 served_domain_t *ipv6, *ipv4; 1725 #if (SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY) 1726 // When dnssd-proxy is combined with srp-mdns-proxy, IPv4 and IPv6 reverse look up domain is set from the begining. 1727 ipv4 = find_served_domain(IPV4_REVERSE_LOOKUP_DOMAIN); 1728 ipv6 = find_served_domain(IPV6_REVERSE_LOOKUP_DOMAIN); 1729 #else // #if (SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY) 1730 ipv4 = new_served_domain(NULL, IPV4_REVERSE_LOOKUP_DOMAIN); 1731 ipv6 = new_served_domain(NULL, IPV6_REVERSE_LOOKUP_DOMAIN); 1732 #endif // #if (SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY) 1733 require_action_quiet(ipv4 != NULL && ipv6 != NULL, exit, ERROR("cannot find/create new served domain")); 1734 1735 for (served_domain_t *addr_domain = served_domains; addr_domain; addr_domain = addr_domain->next) { 1736 dp_interface_t *interface = addr_domain->interface; 1737 interface_addr_t *ifaddr; 1738 if (interface == NULL) { 1739 INFO("Domain " PRI_S_SRP " has no interface", addr_domain->domain); 1740 continue; 1741 } 1742 INFO("Interface " PUB_S_SRP, interface->name); 1743 // Add lb domain support for link domain 1744 for (ifaddr = interface->addresses; ifaddr != NULL; ifaddr = ifaddr->next) { 1745 // Do not publish link-local or loopback address 1746 if (!is_valid_address_to_publish(&ifaddr->addr)) { 1747 continue; 1748 } 1749 1750 if (ifaddr->addr.sa.sa_family == AF_INET) { 1751 dnssd_hardwired_add_or_remove_ptr_record(ipv4, &ifaddr->addr, &ifaddr->mask, true); 1752 } else if (ifaddr->addr.sa.sa_family == AF_INET6) { 1753 dnssd_hardwired_add_or_remove_ptr_record(ipv6, &ifaddr->addr, &ifaddr->mask, true); 1754 } else { 1755 char buf[INET6_ADDRSTRLEN]; 1756 IOLOOP_NTOP(&ifaddr->addr, buf); 1757 INFO("Skipping " PRI_S_SRP, buf); 1758 } 1759 } 1760 } 1761 exit: 1762 return; 1763 } 1764 #endif 1765 1766 static void 1767 dnssd_hardwired_setup(void) 1768 { 1769 dns_wire_t wire; 1770 dns_towire_state_t towire; 1771 served_domain_t *sdt; 1772 #if STUB_ROUTER 1773 dns_name_t *my_name_parsed = my_name == NULL ? NULL : dns_pres_name_parse(my_name); 1774 #endif 1775 1776 #define RESET \ 1777 memset(&towire, 0, sizeof towire); \ 1778 towire.message = &wire; \ 1779 towire.p = wire.data; \ 1780 towire.lim = towire.p + sizeof wire.data 1781 1782 // For each interface, set up the hardwired names. 1783 for (sdt = served_domains; sdt; sdt = sdt->next) { 1784 if (sdt->interface == NULL) { 1785 continue; 1786 } 1787 1788 // SRV 1789 // _dns-llq._udp 1790 // _dns-llq-tls._tcp 1791 // _dns-update._udp 1792 // _dns-update-tls._udp 1793 // We deny the presence of support for LLQ, because we only support DNS Push 1794 RESET; 1795 dnssd_hardwired_add(sdt, "_dns-llq._udp", sdt->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 1796 dnssd_hardwired_add(sdt, "_dns-llq-tls._tcp", sdt->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 1797 1798 // We deny the presence of support for DNS Update, because a Discovery Proxy zone is stateless. 1799 dnssd_hardwired_add(sdt, "_dns-update._udp", sdt->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 1800 dnssd_hardwired_add(sdt, "_dns-update-tls._tcp", sdt->domain_ld, towire.p - wire.data, wire.data, 1801 dns_rrtype_srv); 1802 1803 // Until we set up the DNS Push listener, we deny its existence. If TLS is ready to go, this will be 1804 // overwritten immediately; otherwise it will be overwritten when the TLS key has been generated and signed. 1805 dnssd_hardwired_add(sdt, "_dns-push-tls._tcp", sdt->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 1806 1807 #if STUB_ROUTER 1808 char namebuf[DNS_MAX_NAME_SIZE + 1]; 1809 const char *local_name; 1810 addr_t addr; 1811 1812 // If my_name wasn't set, or if my_name is in this interface's domain, we need to answer 1813 // for it when queried. 1814 if (my_name == NULL || my_name_parsed != NULL) { 1815 const char *local_domain = NULL; 1816 if (my_name == NULL) { 1817 local_name = "ns"; 1818 local_domain = sdt->domain_ld; 1819 } else { 1820 dns_name_t *lim; 1821 local_name = NULL; 1822 1823 // See if my_name is a subdomain of this interface's domain 1824 if ((lim = dns_name_subdomain_of(my_name_parsed, sdt->domain_name)) != NULL) { 1825 dns_name_print_to_limit(my_name_parsed, lim, namebuf, sizeof namebuf); 1826 local_name = namebuf; 1827 dns_name_free(my_name_parsed); 1828 my_name_parsed = NULL; 1829 if (local_name[0] == '\0') { 1830 local_domain = sdt->domain; 1831 } else { 1832 local_domain = sdt->domain_ld; 1833 } 1834 } 1835 } 1836 if (local_name != NULL) { 1837 for (int i = 0; i < num_publish_addrs; i++) { 1838 RESET; 1839 memset(&addr, 0, sizeof addr); 1840 getipaddr(&addr, publish_addrs[i]); 1841 if (addr.sa.sa_family == AF_INET) { 1842 // A 1843 // ns 1844 dns_rdata_raw_data_to_wire(&towire, &addr.sin.sin_addr, sizeof addr.sin.sin_addr); 1845 dnssd_hardwired_add(sdt, local_name, local_domain, towire.p - wire.data, wire.data, 1846 dns_rrtype_a); 1847 } else { 1848 // AAAA 1849 RESET; 1850 dns_rdata_raw_data_to_wire(&towire, &addr.sin6.sin6_addr, sizeof addr.sin6.sin6_addr); 1851 dnssd_hardwired_add(sdt, local_name, local_domain, towire.p - wire.data, wire.data, 1852 dns_rrtype_aaaa); 1853 } 1854 } 1855 } 1856 } 1857 #endif // STUB_ROUTER 1858 1859 // NS 1860 RESET; 1861 #if STUB_ROUTER 1862 if (string_ends_with(sdt->domain, THREAD_DOMAIN)) { 1863 // For served domain in the THREAD_DOMAIN, set the NS record to the local host name: 1864 // For example, openthread.thread.home.arpa. NS Office.local. 1865 // XXX is this right? 1866 require_quiet(local_host_name_dot_local[0] != 0, exit); 1867 dns_full_name_to_wire(NULL, &towire, local_host_name_dot_local); 1868 } else 1869 #endif 1870 if (uuid_name[0] != 0) { 1871 dns_name_to_wire(NULL, &towire, uuid_name); 1872 dns_full_name_to_wire(NULL, &towire, sdt->domain); 1873 } else { 1874 dns_name_to_wire(NULL, &towire, "ns"); 1875 dns_full_name_to_wire(NULL, &towire, sdt->domain); 1876 } 1877 dnssd_hardwired_add(sdt, "", sdt->domain, towire.p - wire.data, wire.data, dns_rrtype_ns); 1878 1879 // SOA (piggybacking on what we already did for NS, which starts the same. 1880 dns_name_to_wire(NULL, &towire, "postmaster"); 1881 dns_full_name_to_wire(NULL, &towire, sdt->domain); 1882 dns_u32_to_wire(&towire, 0); // serial 1883 dns_ttl_to_wire(&towire, 7200); // refresh 1884 dns_ttl_to_wire(&towire, 3600); // retry 1885 dns_ttl_to_wire(&towire, 86400); // expire 1886 dns_ttl_to_wire(&towire, 120); // minimum 1887 dnssd_hardwired_add(sdt, "", sdt->domain, towire.p - wire.data, wire.data, dns_rrtype_soa); 1888 } 1889 1890 // Setup hardwired response A/AAAA record for <local host name>.home.arpa. 1891 #if SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY 1892 #if STUB_ROUTER 1893 // When dnssd-proxy is combined with srp-mdns-proxy, we get the address from the interface address list not from the 1894 // config file, so we search through the served domains for all available address. 1895 if (my_name_parsed != NULL) { 1896 dns_name_free(my_name_parsed); 1897 my_name_parsed = NULL; 1898 } 1899 1900 require_action_quiet(my_name != NULL, exit, ERROR("Failed to get my_name and unable to set hardwired response")); 1901 served_domain_t *const my_name_served_domain = find_served_domain(my_name); 1902 require_action_quiet(my_name_served_domain != NULL, exit, 1903 ERROR("Failed to find my_name domain - my_name: " PRI_S_SRP, my_name)); 1904 1905 served_domain_t *const thread_served_domain = find_served_domain(THREAD_DOMAIN_WITH_ID); 1906 require_action(thread_served_domain != NULL, exit, 1907 ERROR("Failed to find thread domain - domain: " PUB_S_SRP, THREAD_DOMAIN_WITH_ID)); 1908 1909 served_domain_t *const default_service_arpa_domain = find_served_domain(DEFAULT_SERVICE_ARPA_DOMAIN); 1910 require_action(default_service_arpa_domain != NULL, exit, 1911 ERROR("Failed to find thread domain - domain: " PUB_S_SRP, DEFAULT_SERVICE_ARPA_DOMAIN)); 1912 1913 #if SRP_FEATURE_LOCAL_DISCOVERY 1914 served_domain_t *const dot_local_domain = find_served_domain(DOT_LOCAL_DOMAIN); 1915 require_action(dot_local_domain != NULL, exit, 1916 ERROR("Failed to find thread domain - domain: " PUB_S_SRP, DOT_LOCAL_DOMAIN)); 1917 #endif 1918 for (const served_domain_t *domain = served_domains; domain != NULL; domain = domain->next) { 1919 if (domain->interface == NULL) { 1920 continue; 1921 } 1922 for (const interface_addr_t *if_addrs = domain->interface->addresses; if_addrs != NULL; 1923 if_addrs = if_addrs->next) { 1924 const addr_t *const if_addr = &if_addrs->addr; 1925 // Only publish routable IP address. 1926 if (!is_valid_address_to_publish(if_addr)) { 1927 continue; 1928 } 1929 1930 RESET; 1931 uint16_t rr_type; 1932 if (if_addr->sa.sa_family == AF_INET) { 1933 dns_rdata_raw_data_to_wire(&towire, &if_addr->sin.sin_addr, sizeof(if_addr->sin.sin_addr)); 1934 rr_type = dns_rrtype_a; 1935 } else if (if_addr->sa.sa_family == AF_INET6) { 1936 dns_rdata_raw_data_to_wire(&towire, &if_addr->sin6.sin6_addr, sizeof(if_addr->sin6.sin6_addr)); 1937 rr_type = dns_rrtype_aaaa; 1938 } else { 1939 ERROR("Non IPv4/IPv6 address in interface addresses - sa_family: %u", if_addr->sa.sa_family); 1940 continue; 1941 } 1942 1943 // <local host name>.home.arpa. A/AAAA <IP address> 1944 dnssd_hardwired_add(my_name_served_domain, "", my_name_served_domain->domain, towire.p - wire.data, 1945 wire.data, rr_type); 1946 1947 // <local host name>.openthread.thread.home.arpa. A/AAAA <IP address> 1948 dnssd_hardwired_add(thread_served_domain, local_host_name, thread_served_domain->domain_ld, 1949 towire.p - wire.data, wire.data, rr_type); 1950 1951 // <local host name>.default.service.arpa. A/AAAA <IP address> 1952 dnssd_hardwired_add(default_service_arpa_domain, local_host_name, default_service_arpa_domain->domain_ld, 1953 towire.p - wire.data, wire.data, rr_type); 1954 1955 #if SRP_FEATURE_LOCAL_DISCOVERY 1956 // <local host name>.local. A/AAAA <IP address> 1957 dnssd_hardwired_add(dot_local_domain, local_host_name, dot_local_domain->domain_ld, 1958 towire.p - wire.data, wire.data, rr_type); 1959 #endif 1960 } 1961 } 1962 #endif 1963 #else // SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY 1964 if (my_name_parsed != NULL) { 1965 dns_name_free(my_name_parsed); 1966 my_name_parsed = NULL; 1967 1968 sdt = new_served_domain(NULL, my_name); 1969 if (sdt == NULL) { 1970 ERROR("Unable to allocate domain for %s", my_name); 1971 } else { 1972 for (int i = 0; i < num_publish_addrs; i++) { 1973 // AAAA 1974 // A 1975 RESET; 1976 memset(&addr, 0, sizeof addr); 1977 getipaddr(&addr, publish_addrs[i]); 1978 if (addr.sa.sa_family == AF_INET) { 1979 dns_rdata_raw_data_to_wire(&towire, &addr.sin.sin_addr, sizeof addr.sin.sin_addr); 1980 dnssd_hardwired_add(sdt, "", sdt->domain, towire.p - wire.data, wire.data, dns_rrtype_a); 1981 } else { 1982 dns_rdata_raw_data_to_wire(&towire, &addr.sin6.sin6_addr, sizeof addr.sin6.sin6_addr); 1983 dnssd_hardwired_add(sdt, "", sdt->domain, towire.p - wire.data, wire.data, dns_rrtype_aaaa); 1984 } 1985 } 1986 } 1987 } 1988 #endif // SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY 1989 1990 #if STUB_ROUTER 1991 // Setup _lb._udp.<reversed IP address> PTR record for the domain we are advertising, for example: 1992 // _lb._udp.0.0.168.192.in-addr.arpa. PTR my-discovery-proxy-en0.home.arpa. 1993 dnssd_hardwired_lbdomains_setup(); 1994 1995 exit: 1996 #endif 1997 return; 1998 } 1999 2000 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 2001 static void 2002 dnssd_hardwired_clear(void) 2003 { 2004 INFO("Clearing all hardwired response"); 2005 for (served_domain_t *domain = served_domains; domain != NULL; domain = domain->next) { 2006 hardwired_t *hardwired_responses = domain->hardwired_responses; 2007 if (hardwired_responses == NULL) { 2008 continue; 2009 } 2010 2011 domain->hardwired_responses = NULL; 2012 hardwired_t *next_response; 2013 for (hardwired_t *response = hardwired_responses; response != NULL; response = next_response) { 2014 next_response = response->next; 2015 free(response); 2016 } 2017 } 2018 } 2019 2020 static void 2021 dnssd_hardwired_push_setup(void) 2022 { 2023 // For each interface, set up the hardwired names. 2024 for (served_domain_t *sdt = served_domains; sdt; sdt = sdt->next) { 2025 if (sdt->interface == NULL) { 2026 continue; 2027 } 2028 2029 if (!sdt->interface->no_push) { 2030 // SRV 2031 // _dns-push-tls._tcp 2032 // _dns-query-tls._udp 2033 dnssd_hardwired_setup_dns_push_for_domain(sdt); 2034 } 2035 } 2036 } 2037 2038 static void 2039 dnssd_hardwired_deny_service_existence_for_served_domain(served_domain_t *const NONNULL served_domain) 2040 { 2041 dns_wire_t wire; 2042 dns_towire_state_t towire; 2043 2044 #define RESET \ 2045 memset(&towire, 0, sizeof towire); \ 2046 towire.message = &wire; \ 2047 towire.p = wire.data; \ 2048 towire.lim = towire.p + sizeof wire.data 2049 2050 RESET; 2051 // We deny the presence of support for LLQ, because we only support DNS Push. 2052 dnssd_hardwired_add(served_domain, "_dns-llq._udp", served_domain->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 2053 dnssd_hardwired_add(served_domain, "_dns-llq-tls._tcp", served_domain->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 2054 2055 // We deny the presence of support for DNS Update, because a Discovery Proxy zone is stateless. 2056 dnssd_hardwired_add(served_domain, "_dns-update._udp", served_domain->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 2057 dnssd_hardwired_add(served_domain, "_dns-update-tls._tcp", served_domain->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 2058 2059 // We deny the presence of support for UDP with TLS, because we have not implemented DTLS (datagram TLS). 2060 dnssd_hardwired_add(served_domain, "_dns-query-tls._udp", served_domain->domain_ld, towire.p - wire.data, wire.data, dns_rrtype_srv); 2061 2062 // We deny the presence of "lb._dns-sd._udp" for the served domain, to avoid the response like: 2063 // lb._dns-sd._udp.openthread.thread.home.arpa. PTR openthread.thread.home.arpa. 2064 dnssd_hardwired_add(served_domain, SRV_TYPE_FOR_AUTOMATIC_BROWSING_DOMAIN, served_domain->domain_ld, 2065 towire.p - wire.data, wire.data, dns_rrtype_ptr); 2066 dnssd_hardwired_add(served_domain, "b._dns-sd._udp", served_domain->domain_ld, 2067 towire.p - wire.data, wire.data, dns_rrtype_ptr); 2068 dnssd_hardwired_add(served_domain, "db._dns-sd._udp", served_domain->domain_ld, 2069 towire.p - wire.data, wire.data, dns_rrtype_ptr); 2070 } 2071 2072 static bool 2073 dnssd_hardwired_setup_for_served_domain(served_domain_t *const NONNULL served_domain) 2074 { 2075 bool succeeded = false; 2076 dns_wire_t wire; 2077 dns_towire_state_t towire; 2078 2079 #define RESET \ 2080 memset(&towire, 0, sizeof towire); \ 2081 towire.message = &wire; \ 2082 towire.p = wire.data; \ 2083 towire.lim = towire.p + sizeof wire.data 2084 2085 require_action_quiet(served_domain->interface != NULL, exit, succeeded = false; 2086 ERROR("only domain with usable interface can setup hardwired response - domain name: " PRI_S_SRP, 2087 served_domain->domain) 2088 ); 2089 2090 // deny the existence of the following services: 2091 // _dns-llq._udp 2092 // _dns-llq-tls._tcp 2093 // _dns-update._udp 2094 dnssd_hardwired_deny_service_existence_for_served_domain(served_domain); 2095 2096 // Setup NS record for this served domain. 2097 RESET; 2098 #if STUB_ROUTER 2099 if (string_ends_with(served_domain->domain, THREAD_DOMAIN)) { 2100 // If the response requires the translation from <served domain> to ".local." and the response ends in 2101 // ".local.", truncate it. 2102 require_action_quiet(local_host_name_dot_local[0] != 0, exit, succeeded = false); 2103 dns_full_name_to_wire(NULL, &towire, local_host_name_dot_local); 2104 } else 2105 #endif 2106 if (uuid_name[0] != 0) { 2107 dns_name_to_wire(NULL, &towire, uuid_name); 2108 dns_full_name_to_wire(NULL, &towire, served_domain->domain); 2109 } else { 2110 dns_name_to_wire(NULL, &towire, "ns"); 2111 dns_full_name_to_wire(NULL, &towire, served_domain->domain); 2112 } 2113 dnssd_hardwired_add(served_domain, "", served_domain->domain, towire.p - wire.data, wire.data, dns_rrtype_ns); 2114 2115 // Setup SOA record for this served domain. (piggybacking on what we already did for NS, which starts the same.) 2116 dns_name_to_wire(NULL, &towire, "postmaster"); 2117 dns_full_name_to_wire(NULL, &towire, served_domain->domain); 2118 dns_u32_to_wire(&towire, 0); // serial 2119 dns_ttl_to_wire(&towire, 7200); // refresh 2120 dns_ttl_to_wire(&towire, 3600); // retry 2121 dns_ttl_to_wire(&towire, 86400); // expire 2122 dns_ttl_to_wire(&towire, 120); // minimum 2123 dnssd_hardwired_add(served_domain, "", served_domain->domain, towire.p - wire.data, wire.data, dns_rrtype_soa); 2124 2125 // Setup DNS push 2126 if (served_domain->interface == NULL || !served_domain->interface->no_push) { 2127 succeeded = dnssd_hardwired_setup_dns_push_for_domain(served_domain); 2128 if (!succeeded) { 2129 ERROR("failed to setup DNS push service for hardwired response - domain: " PRI_S_SRP, 2130 served_domain->domain); 2131 goto exit; 2132 } 2133 } 2134 succeeded = true; 2135 2136 exit: 2137 return succeeded; 2138 } 2139 2140 static bool 2141 dnssd_hardwired_setup_dns_push_for_domain(served_domain_t *const NONNULL served_domain) 2142 { 2143 bool succeeded; 2144 2145 require_action_quiet(served_domain->interface != NULL && !served_domain->interface->no_push, exit, succeeded = false; 2146 ERROR("the associated interface does not enable DNS push - domain: " PRI_S_SRP, served_domain->domain)); 2147 2148 require_action_quiet(my_name != NULL, exit, succeeded = false; ERROR("my_name is not set")); 2149 2150 dns_wire_t wire; 2151 dns_towire_state_t towire; 2152 #define RESET \ 2153 memset(&towire, 0, sizeof towire); \ 2154 towire.message = &wire; \ 2155 towire.p = wire.data; \ 2156 towire.lim = towire.p + sizeof wire.data 2157 2158 RESET; 2159 dns_u16_to_wire(&towire, 0); // priority 2160 dns_u16_to_wire(&towire, 0); // weight 2161 dns_u16_to_wire(&towire, 853); // port 2162 2163 #if STUB_ROUTER 2164 if (string_ends_with(served_domain->domain, THREAD_DOMAIN)) { 2165 // If the served domain is subdomain of "thread.home.arpa.", use name <local host name>.local for the DNS push 2166 // service. Currently we only support DNS push in "thread.home.arpa." domain in local subnet, so DNS push 2167 // service for "thread.home.arpa." will be registered with a name in ".local.". 2168 require_action_quiet(local_host_name_dot_local[0] != 0, exit, succeeded = false); 2169 dns_full_name_to_wire(NULL, &towire, local_host_name_dot_local); 2170 } else 2171 #endif 2172 if (uuid_name[0] != 0) { 2173 // Use <local host name>.<domain> 2174 dns_name_to_wire(NULL, &towire, uuid_name); 2175 dns_full_name_to_wire(NULL, &towire, served_domain->domain); 2176 } else { 2177 // Use name ns.<served domain>. 2178 dns_name_to_wire(NULL, &towire, "ns"); 2179 dns_full_name_to_wire(NULL, &towire, served_domain->domain); 2180 } 2181 2182 dnssd_hardwired_add(served_domain, "_dns-push-tls._tcp", served_domain->domain_ld, towire.p - wire.data, wire.data, 2183 dns_rrtype_srv); 2184 2185 succeeded = true; 2186 exit: 2187 return succeeded; 2188 } 2189 #endif // SRP_FEATURE_DYNAMIC_CONFIGURATION 2190 2191 static bool 2192 embiggen(dnssd_query_t *query) 2193 { 2194 dns_wire_t *nr = malloc(query->data_size + DNS_DATA_SIZE + DNS_HEADER_SIZE); // increments wire size by DNS_DATA_SIZE 2195 if (nr == NULL) { 2196 return false; 2197 } 2198 memcpy(nr, query->response, query->data_size + DNS_HEADER_SIZE); 2199 query->data_size += DNS_DATA_SIZE; 2200 size_t len = query->towire.p - &query->response->data[0]; 2201 query->towire.p = &nr->data[0] + len; 2202 query->towire.lim = &nr->data[0] + query->data_size; 2203 query->towire.p_rdlength = NULL; 2204 query->towire.p_opt = NULL; 2205 query->towire.message = nr; 2206 free(query->response); 2207 query->response = nr; 2208 return true; 2209 } 2210 2211 static void 2212 dp_move_rrs(dns_rr_t *first_section, unsigned *p_first_count, dns_rr_t *source_section, unsigned source_count, unsigned count, bool rdata_present) 2213 { 2214 unsigned first_count = *p_first_count; 2215 2216 // Copy the rrs into the combined section. 2217 for (unsigned i = 0; i < source_count; i++) { 2218 // Skip this RR if there's already another one just like it in the section (most likely to happen 2219 // with authority records. 2220 bool duplicate = false; 2221 for (unsigned j = 0; j < first_count; j++) { 2222 if (dns_rrs_equal(&first_section[j], &source_section[i], rdata_present)) { 2223 duplicate = true; 2224 break; 2225 } 2226 } 2227 if (duplicate) { 2228 continue; 2229 } 2230 2231 // Only if there is space... 2232 if (first_count < count) { 2233 first_section[first_count] = source_section[i]; 2234 first_count++; 2235 source_section[i].type = dns_invalid_rr; 2236 } else { 2237 ERROR("first_count %d unexpectedly equal to count %d with i = %d", first_count, count, i); 2238 } 2239 } 2240 *p_first_count = first_count; 2241 } 2242 2243 static dnssd_query_t * 2244 dp_dns_queries_finished(dnssd_query_t *answered_query) 2245 { 2246 dns_message_t *first_message = NULL; 2247 unsigned qdcount = 0, ancount = 0, nscount = 0, arcount = 0; 2248 unsigned first_qdcount, first_ancount, first_nscount, first_arcount; 2249 dnssd_query_t *first_query = NULL; 2250 dp_tracker_t *tracker = answered_query->tracker; 2251 2252 // response_query will be set to NULL if we don't want anything sent yet, to query if query is the 2253 // only query or if there's an error in this function, or to the query that holds the aggregate response 2254 // otherwise. 2255 dnssd_query_t *response_query = answered_query; 2256 const char *name = answered_query->question != NULL ? answered_query->question->name : "(null)"; 2257 2258 require_action_quiet(tracker != NULL, exit, 2259 dns_rcode_set(answered_query->response, dns_rcode_servfail); 2260 ERROR("NULL tracker on " PRI_S_SRP, name)); 2261 require_action_quiet(tracker->dns_queries != NULL, exit, 2262 dns_rcode_set(answered_query->response, dns_rcode_servfail); 2263 ERROR("NULL tracker->dns_queries on " PRI_S_SRP, name)); 2264 2265 // The usual case, there's only one question in the DNS message, so we can just 2266 // return the answer now. 2267 if (answered_query->num_questions == 1) { 2268 goto exit; 2269 } 2270 2271 // Otherwise, we have more than one query, so see if any remain unsatisfied. 2272 int satisfied = 0; 2273 for (dnssd_query_t *match = tracker->dns_queries; match != NULL; match = match->next) { 2274 // It's possible we could creep in here without actually generating one of the responses, 2275 // in which case we should definitely fail at this point. 2276 require_action_quiet(match->response != NULL, 2277 exit, 2278 ERROR("null response on match query"); 2279 dns_rcode_set(response_query->response, dns_rcode_servfail)); 2280 if (match->message == answered_query->message) { 2281 if (!match->satisfied) { 2282 response_query = NULL; // More answers coming. 2283 goto exit; 2284 } 2285 satisfied++; 2286 } 2287 } 2288 if (satisfied != answered_query->num_questions) { 2289 response_query = NULL; 2290 goto exit; 2291 } 2292 // All queries have been satisfied. 2293 2294 // Parse all of the messages (this is gross--later on we should just never convert to wire format until 2295 // we get here. 2296 for (dnssd_query_t *source = tracker->dns_queries; source != NULL; source = source->next) { 2297 if (source->message == answered_query->message) { 2298 // This should never fail, but... 2299 require_action_quiet(dns_wire_parse(&source->response_msg, source->response, 2300 (unsigned)(source->towire.p - source->response->data) + DNS_HEADER_SIZE, false), 2301 exit, 2302 dns_rcode_set(response_query->response, dns_rcode_servfail)); 2303 2304 if (first_query == NULL) { 2305 first_query = source; 2306 first_message = first_query->response_msg; 2307 first_qdcount = first_message->qdcount; 2308 first_ancount = first_message->ancount; 2309 first_nscount = first_message->nscount; 2310 first_arcount = first_message->arcount; 2311 } 2312 qdcount += source->response_msg->qdcount; 2313 ancount += source->response_msg->ancount; 2314 nscount += source->response_msg->nscount; 2315 arcount += source->response_msg->arcount; 2316 } 2317 } 2318 2319 // Copy records from the response. 2320 for (int i = 0; i < 4; i++) { 2321 dns_rr_t *section, **first_section = NULL, **source_section = NULL; 2322 unsigned section_count = 0, source_count = 0, *first_count = NULL; 2323 2324 // Start with the second message, since the first is already populated. 2325 for (dnssd_query_t *source = tracker->dns_queries; 2326 source != NULL && answered_query->message == source->message ; source = source->next) 2327 { 2328 #define SECTION_CASE(index, counter_name, section_name) \ 2329 case index: \ 2330 first_section = &first_message->section_name; \ 2331 source_section = &source->response_msg->section_name; \ 2332 section_count = counter_name; \ 2333 source_count = source->response_msg->counter_name; \ 2334 first_count = &first_message->counter_name; \ 2335 break 2336 2337 switch (i) { 2338 SECTION_CASE(0, qdcount, questions); 2339 SECTION_CASE(1, ancount, answers); 2340 SECTION_CASE(2, nscount, authority); 2341 SECTION_CASE(3, arcount, additional); 2342 } 2343 2344 // If this is the first matching query, expand the current section to be able to fit all of the data we're 2345 // copying in, and then copy the data from the first section. 2346 if (first_section == source_section) { 2347 section = calloc(section_count, sizeof(*section)); 2348 require_action_quiet(section != NULL, exit, 2349 dns_rcode_set(answered_query->response, dns_rcode_servfail); 2350 ERROR("Unable to allocate memory for query response section on " PRI_S_SRP, name)); 2351 memcpy(section, *first_section, source_count * sizeof(*section)); 2352 memset(*first_section, 0, source_count * sizeof(*section)); // NULL out any pointers 2353 free(*first_section); 2354 *first_section = section; 2355 } else { 2356 dp_move_rrs(*first_section, first_count, *source_section, source_count, section_count, i != 0); 2357 } 2358 } 2359 } 2360 2361 // Use the response in the first query to turn the answer to wire format. 2362 redo_message: 2363 dp_query_towire_reset(first_query); 2364 dns_message_rrs_to_wire(&first_query->towire, first_query->response_msg); 2365 if (first_query->towire.truncated) { 2366 if (first_query->tracker->connection->tcp_stream) { 2367 if (embiggen(first_query)) { 2368 first_query->towire.error = false; 2369 first_query->towire.truncated = false; 2370 goto redo_message; 2371 } 2372 } 2373 } 2374 first_query->response->qdcount = htons(first_message->qdcount); 2375 first_query->response->ancount = htons(first_message->ancount); 2376 first_query->response->nscount = htons(first_message->nscount); 2377 first_query->response->arcount = htons(first_message->arcount); 2378 response_query = first_query; 2379 2380 exit: 2381 return response_query; 2382 } 2383 2384 static void 2385 dp_query_send_dns_response(dnssd_query_t *query, const char *context_description) 2386 { 2387 struct iovec iov; 2388 dns_towire_state_t *towire = &query->towire; 2389 const char *failnote = NULL; 2390 uint8_t *revert = towire->p; 2391 uint16_t tc = towire->truncated ? dns_flags_tc : 0; 2392 uint16_t bitfield = ntohs(query->response->bitfield); 2393 uint16_t mask = 0; 2394 int rcode = dns_rcode_get(query->response); 2395 question_t *question = query->question; 2396 2397 // Mark this query as complete. 2398 query->satisfied = true; 2399 2400 VALIDATE_TRACKER_CONNECTION_NON_NULL(); 2401 2402 // Send an SOA record if it's a .local query. 2403 if (question->served_domain != NULL && question->served_domain->interface != NULL && 2404 !towire->truncated && (question->type != dns_rrtype_soa || question->name[0] != '\0')) 2405 { 2406 redo: 2407 // DNSSD Hybrid, Section 6.1. 2408 TOWIRE_CHECK("&query->enclosing_domain_pointer 1", towire, 2409 dns_pointer_to_wire(NULL, towire, &query->enclosing_domain_pointer)); 2410 TOWIRE_CHECK("dns_rrtype_soa", towire, 2411 dns_u16_to_wire(towire, dns_rrtype_soa)); 2412 TOWIRE_CHECK("dns_qclass_in", towire, 2413 dns_u16_to_wire(towire, dns_qclass_in)); 2414 TOWIRE_CHECK("ttl", towire, dns_ttl_to_wire(towire, 3600)); 2415 TOWIRE_CHECK("rdlength_begin ", towire, dns_rdlength_begin(towire)); 2416 if (0) { 2417 #if STUB_ROUTER 2418 } else if (srp_servers->stub_router_enabled && my_name != NULL) { 2419 TOWIRE_CHECK(my_name, towire, dns_full_name_to_wire(NULL, towire, my_name)); 2420 #endif 2421 } else if (uuid_name[0] != 0) { 2422 TOWIRE_CHECK("uuid_name", towire, dns_name_to_wire(NULL, towire, uuid_name)); 2423 TOWIRE_CHECK("&query->enclosing_domain_pointer 2", towire, 2424 dns_pointer_to_wire(NULL, towire, &query->enclosing_domain_pointer)); 2425 } else { 2426 TOWIRE_CHECK("\"ns\"", towire, dns_name_to_wire(NULL, towire, "ns")); 2427 TOWIRE_CHECK("&query->enclosing_domain_pointer 2", towire, 2428 dns_pointer_to_wire(NULL, towire, &query->enclosing_domain_pointer)); 2429 } 2430 TOWIRE_CHECK("\"postmaster\"", towire, 2431 dns_name_to_wire(NULL, towire, "postmaster")); 2432 TOWIRE_CHECK("&query->enclosing_domain_pointer 3", towire, 2433 dns_pointer_to_wire(NULL, towire, &query->enclosing_domain_pointer)); 2434 TOWIRE_CHECK("serial", towire,dns_u32_to_wire(towire, 0)); // serial 2435 TOWIRE_CHECK("refresh", towire, dns_ttl_to_wire(towire, 7200)); // refresh 2436 TOWIRE_CHECK("retry", towire, dns_ttl_to_wire(towire, 3600)); // retry 2437 TOWIRE_CHECK("expire", towire, dns_ttl_to_wire(towire, 86400)); // expire 2438 TOWIRE_CHECK("minimum", towire, dns_ttl_to_wire(towire, 120)); // minimum 2439 dns_rdlength_end(towire); 2440 if (towire->truncated) { 2441 query->towire.p = revert; 2442 if (query->tracker->connection->tcp_stream) { 2443 if (embiggen(query)) { 2444 query->towire.error = 0; 2445 towire->truncated = false; 2446 goto redo; 2447 } 2448 } else { 2449 tc = dns_flags_tc; 2450 } 2451 } else { 2452 query->response->nscount = htons(1); 2453 } 2454 2455 // Response is authoritative and not recursive. 2456 authoritative: 2457 mask = ~dns_flags_ra; 2458 bitfield = bitfield | dns_flags_aa | tc; 2459 bitfield = bitfield & mask; 2460 } else { 2461 // Response is recursive and not authoritative. 2462 if (question->type != dns_rrtype_soa) { 2463 mask = ~dns_flags_aa; 2464 bitfield = bitfield | dns_flags_ra | tc; 2465 bitfield = bitfield & mask; 2466 } else { 2467 goto authoritative; 2468 } 2469 } 2470 2471 INFO("[Q%d][QU%d][QID %x] ->p %p ->lim %p len %zd rcode %d " PUB_S_SRP, SERIAL(query), SERIAL(question), 2472 ntohs(query->message->wire.id), query->towire.p, &query->towire.message->data[0], 2473 query->towire.p - &query->towire.message->data[0], dns_rcode_get(query->response), context_description); 2474 2475 // In the case that we get an error looking something up, we return that error immediately on the query that failed, 2476 // rather than trying to assemble a complete answer. In returning the error, we cancel any outstanding queries. 2477 dnssd_query_t *send_query; 2478 2479 if (!towire->error && rcode == dns_rcode_noerror) { 2480 // It's possible that we got a query with qdcount > 1. In this case, we are going to marshal all of the 2481 // answers from the responses we've constructed into a new response and send it after all of the queries 2482 // have responses. So at this point, if we don't have all the responses yet, there's no point in adding 2483 // the edns0 option. If we do, dp_dns_update_queries_finished will marshal all the answers into one 2484 // message and after that we can add the edns0 option. If there's only one query, this is a no-op. 2485 send_query = dp_dns_queries_finished(query); 2486 if (send_query == NULL) { 2487 #ifdef DNSSD_PROXY_DUMP_TRACKER_QUERIES 2488 if (query->tracker == NULL) { 2489 ERROR("[Q%d] query->tracker is NULL", SERIAL(query)); 2490 } else { 2491 char logbuf[200]; 2492 char *lbp = logbuf; 2493 char *lbend = logbuf + sizeof(logbuf); 2494 char *lbrestart; 2495 bool print_last = true; 2496 if (query->tracker->connection != NULL && query->tracker->connection->tcp_stream) { 2497 int len = snprintf(logbuf, sizeof(logbuf), "[Q%d] TCP %d: ", SERIAL(query), query->num_questions); 2498 lbrestart = logbuf + len; 2499 } else { 2500 int len = snprintf(logbuf, sizeof(logbuf), "[Q%d] UDP %p %d: ", SERIAL(query), query->num_questions); 2501 lbrestart = logbuf + len; 2502 } 2503 lbp = logbuf + strlen(logbuf); 2504 for (dnssd_query_t *list_query = query->tracker->dns_queries; 2505 list_query != NULL; list_query = list_query->next) 2506 { 2507 if (list_query->message != query->message) { 2508 continue; 2509 } 2510 int len = snprintf(lbp, lbend - lbp, "%p%s ", list_query, list_query->satisfied ? "+" : "="); 2511 if (lbp + len < lbend) { 2512 lbp += len; 2513 print_last = true; 2514 } else { 2515 *lbp = 0; 2516 INFO("[Q%d] " PUB_S_SRP, SERIAL(query), logbuf); 2517 lbp = lbrestart; 2518 *lbp = 0; 2519 print_last = false; 2520 } 2521 } 2522 if (print_last) { 2523 INFO("[Q%d] " PUB_S_SRP, SERIAL(query), logbuf); 2524 } 2525 } 2526 #endif // DNSSD_PROXY_DUMP_TRACKER_QUERIES 2527 return; 2528 } 2529 if (dns_rcode_get(send_query->response) != dns_rcode_noerror) { 2530 rcode = dns_rcode_get(send_query->response); 2531 } 2532 2533 towire = &send_query->towire; 2534 revert = towire->p; 2535 } else { 2536 send_query = query; 2537 } 2538 2539 // Not authentic, checking not disabled. 2540 mask = ~(dns_flags_rd | dns_flags_ad | dns_flags_cd); 2541 bitfield = bitfield & mask; 2542 send_query->response->bitfield = htons(bitfield); 2543 2544 // This is a response 2545 dns_qr_set(send_query->response, dns_qr_response); 2546 2547 // If we got a failure from dp_dns_queries_finished(), skip adding the opt RR and checking for a towire error. 2548 if (rcode == dns_rcode_noerror) { 2549 // Send an OPT RR if we got one 2550 // XXX reserve space so we can always send an OPT RR? 2551 if (send_query->is_edns0) { 2552 redo_edns0: 2553 TOWIRE_CHECK("Root label", towire, dns_u8_to_wire(towire, 0)); // Root label 2554 TOWIRE_CHECK("dns_rrtype_opt", towire, dns_u16_to_wire(towire, dns_rrtype_opt)); 2555 TOWIRE_CHECK("UDP Payload size", towire, dns_u16_to_wire(towire, 4096)); // UDP Payload size 2556 TOWIRE_CHECK("extended-rcode", towire, dns_u8_to_wire(towire, 0)); // extended-rcode 2557 TOWIRE_CHECK("EDNS version 0", towire, dns_u8_to_wire(towire, 0)); // EDNS version 0 2558 TOWIRE_CHECK("No extended flags", towire, dns_u16_to_wire(towire, 0)); // No extended flags 2559 TOWIRE_CHECK("No payload", towire, dns_u16_to_wire(towire, 0)); // No payload 2560 if (towire->truncated) { 2561 send_query->towire.p = revert; 2562 if (send_query->tracker->connection->tcp_stream) { 2563 if (embiggen(send_query)) { 2564 send_query->towire.error = false; 2565 send_query->towire.truncated = false; 2566 goto redo_edns0; 2567 } 2568 } 2569 } else { 2570 #if SRP_FEATURE_NAT64 2571 send_query->response->arcount = htons(ntohs(send_query->response->arcount) + 1); 2572 #else 2573 send_query->response->arcount = htons(1); 2574 #endif 2575 } 2576 } 2577 2578 if (towire->error) { 2579 ERROR("[Q%d][QU%d][QID%x] failed on %s", SERIAL(query), SERIAL(question), ntohs(send_query->message->wire.id), failnote); 2580 if (tc == dns_flags_tc) { 2581 dns_rcode_set(send_query->response, dns_rcode_noerror); 2582 } else { 2583 dns_rcode_set(send_query->response, dns_rcode_servfail); 2584 } 2585 } else { 2586 // No error. 2587 dns_rcode_set(send_query->response, dns_rcode_noerror); 2588 } 2589 } else { 2590 dns_rcode_set(send_query->response, rcode); 2591 } 2592 2593 iov.iov_len = (send_query->towire.p - (uint8_t *)send_query->response); 2594 iov.iov_base = send_query->response; 2595 INFO("[Q%d][QU%d] (len %zd)", SERIAL(query), SERIAL(question), iov.iov_len); 2596 2597 ioloop_send_message(send_query->tracker->connection, send_query->message, &iov, 1); 2598 2599 // Cancel the send_query. 2600 dnssd_query_cancel(send_query); 2601 } 2602 2603 static void 2604 dp_query_towire_reset(dnssd_query_t *query) 2605 { 2606 query->towire.p = &query->response->data[0]; // We start storing RR data here. 2607 query->towire.lim = &query->response->data[0] + query->data_size; // This is the limit to how much we can store. 2608 query->towire.message = query->response; 2609 query->towire.p_rdlength = NULL; 2610 query->towire.p_opt = NULL; 2611 query->p_dso_length = NULL; 2612 } 2613 2614 static void 2615 dns_push_start(dnssd_query_t *query) 2616 { 2617 const char *failnote = NULL; 2618 2619 // If we don't have a dso header yet, start one. 2620 if (query->p_dso_length == NULL) { 2621 memset(query->response, 0, (sizeof *query->response) - DNS_DATA_SIZE); 2622 dns_opcode_set(query->response, dns_opcode_dso); 2623 // This is a unidirectional DSO message, which is marked as a query 2624 dns_qr_set(query->response, dns_qr_query); 2625 // No error cuz not a response. 2626 dns_rcode_set(query->response, dns_rcode_noerror); 2627 2628 TOWIRE_CHECK("kDSOType_DNSPushUpdate", &query->towire, 2629 dns_u16_to_wire(&query->towire, kDSOType_DNSPushUpdate)); 2630 if (query->towire.p + 2 > query->towire.lim) { 2631 ERROR("[Q%d] No room for dso length in DNS Push notification message.", SERIAL(query)); 2632 dp_query_towire_reset(query); 2633 return; 2634 } 2635 query->p_dso_length = query->towire.p; 2636 query->towire.p += 2; 2637 } 2638 if (failnote != NULL) { 2639 ERROR("[Q%d] couldn't start update: %s", SERIAL(query), failnote); 2640 } 2641 } 2642 2643 static void 2644 dp_push_response(dnssd_query_t *query, dns_rr_t *original_question) 2645 { 2646 struct iovec iov; 2647 question_t *question = query->question; 2648 char nbuf[DNS_MAX_NAME_SIZE_ESCAPED + 1]; 2649 char *name = "<null question name>"; 2650 if (question != NULL) { 2651 name = question->name; 2652 } else if (original_question != NULL) { 2653 dns_name_print(original_question->name, nbuf, sizeof(nbuf)); 2654 name = nbuf; 2655 } 2656 2657 VALIDATE_TRACKER_CONNECTION_NON_NULL(); 2658 2659 if (query->p_dso_length != NULL) { 2660 int16_t dso_length = query->towire.p - query->p_dso_length - 2; 2661 iov.iov_len = (query->towire.p - (uint8_t *)query->response); 2662 iov.iov_base = query->response; 2663 INFO("[Q%d][QU%d] " PRI_S_SRP " (len %zd)", SERIAL(query), SERIAL(question), name, iov.iov_len); 2664 2665 query->towire.p = query->p_dso_length; 2666 dns_u16_to_wire(&query->towire, dso_length); 2667 ioloop_send_message(query->tracker->connection, query->message, &iov, 1); 2668 dp_query_towire_reset(query); 2669 } 2670 } 2671 2672 static bool 2673 dnssd_hardwired_response(dnssd_query_t *query, DNSServiceQueryRecordReply UNUSED callback) 2674 { 2675 hardwired_t *hp; 2676 question_t *question = query->question; 2677 const char *response_type = NULL; 2678 uint8_t v4mapped[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; 2679 2680 // If the question is for our uuid name in a domain we're authoritative for, respond with the IP address that 2681 // the question was received on. 2682 if (!strcasecmp(question->name, uuid_name) && (question->type == dns_rrtype_a || question->type == dns_rrtype_aaaa)) { 2683 addr_t *local = NULL; 2684 if (query->message != NULL) { 2685 local = &query->message->local; 2686 } else { 2687 local = &query->dso->transport->local; 2688 } 2689 2690 // If it's an IPv4 address we can respond with an A record. 2691 if (question->type == dns_rrtype_a && local->sa.sa_family == AF_INET) { 2692 dp_query_add_data_to_response(query, question->name, question->type, dns_qclass_in, 4, 2693 &local->sin.sin_addr, 300, true, true, &query->response->ancount); 2694 response_type = "local host IPv4 address"; 2695 } 2696 // If it's an IPv4-mapped IPv6 address, we can respond with an A record 2697 else if (local->sa.sa_family == AF_INET6 && question->type == dns_rrtype_a && 2698 !memcmp(&local->sin6.sin6_addr, v4mapped, sizeof(v4mapped))) 2699 { 2700 dp_query_add_data_to_response(query, question->name, question->type, dns_qclass_in, 4, 2701 ((uint8_t *)&local->sin6.sin6_addr) + 12, 3600, true, true, 2702 &query->response->ancount); 2703 response_type = "local host v4-mapped address"; 2704 } 2705 // If it's an IPv6 address and NOT a v4-mapped address, we can respond with an AAAA record. 2706 else if (local->sa.sa_family == AF_INET6 && question->type == dns_rrtype_aaaa && 2707 memcmp(&local->sin6.sin6_addr, v4mapped, sizeof(v4mapped))) 2708 { 2709 struct in6_addr response = local->sin6.sin6_addr; 2710 bool address_is_usable = false; 2711 // If it's not a synthesized anycast or rloc address, we can just use it. 2712 if (!is_thread_mesh_synthetic_address(&response)) { 2713 address_is_usable = true; 2714 response_type = "local host IPv6 address"; 2715 } else { 2716 // Otherwise, we need to find the mesh-local address and respond with that. 2717 srp_server_t *server_state = srp_servers; 2718 #if SRP_TEST_SERVER 2719 for (; !address_is_usable && server_state != NULL; server_state = server_state->next) 2720 #endif 2721 { 2722 if (0) { 2723 #if STUB_ROUTER 2724 } else if (server_state->stub_router_enabled) { 2725 route_state_t *route_state = server_state->route_state; 2726 if (route_state->thread_interface_name != NULL) { 2727 for (interface_address_state_t *address = route_state->interface_addresses; 2728 !address_is_usable && address != NULL; address = address->next) 2729 { 2730 // Wrong interface or wrong type of address 2731 if (strcmp(address->name, route_state->thread_interface_name) || 2732 address->addr.sa.sa_family != AF_INET6) 2733 { 2734 continue; 2735 } 2736 if (!is_thread_mesh_synthetic_or_link_local(&address->addr.sin6.sin6_addr)) 2737 { 2738 memcpy(&response, &address->addr.sin6.sin6_addr, sizeof(response)); 2739 response_type = "thread interface address"; 2740 address_is_usable = true; 2741 } 2742 } 2743 if (address_is_usable == false) { 2744 response_type = "no usable address on thread interface"; 2745 } 2746 } else { 2747 address_is_usable = false; 2748 response_type = "thread interface name unknown"; 2749 } 2750 #endif 2751 } else { 2752 // For thread device, the only thing that can work is the ML-EID. 2753 if (service_publisher_get_ml_eid(server_state->service_publisher, &response)) { 2754 address_is_usable = true; 2755 response_type = "thread device ML-EID"; 2756 } else { 2757 response_type = "thread ML-EID not known"; 2758 } 2759 } 2760 } 2761 } 2762 if (address_is_usable) { 2763 SEGMENTED_IPv6_ADDR_GEN_SRP(&response, response_buf); 2764 INFO(PUB_S_SRP " IN AAAA " PRI_SEGMENTED_IPv6_ADDR_SRP " " PUB_S_SRP, question->name, 2765 SEGMENTED_IPv6_ADDR_PARAM_SRP(&response, response_buf), response_type); 2766 dp_query_add_data_to_response(query, question->name, question->type, dns_qclass_in, 16, 2767 &response, 300, true, true, &query->response->ancount); 2768 } 2769 } 2770 } else { 2771 for (hp = query->question->served_domain->hardwired_responses; hp; hp = hp->next) { 2772 if ((query->question->type == hp->type || query->question->type == dns_rrtype_any) && 2773 query->question->qclass == dns_qclass_in && !strcasecmp(hp->name, query->question->name)) 2774 { 2775 if (query->dso != NULL) { 2776 // Since hardwired response is set by the dnssd-proxy itself, do not do ".local" translation. 2777 dp_query_add_data_to_response(query, hp->fullname, hp->type, dns_qclass_in, hp->rdlen, hp->rdata, 2778 3600, true, false, NULL); 2779 } else { 2780 // Store the response 2781 if (!query->towire.truncated) { 2782 // Since hardwired response is set by the dnssd-proxy itself, do not do ".local" translation. 2783 dp_query_add_data_to_response(query, hp->fullname, hp->type, dns_qclass_in, 2784 hp->rdlen, hp->rdata, 3600, true, false, 2785 &query->response->ancount); 2786 } 2787 } 2788 response_type = "hardwired"; 2789 } 2790 } 2791 } 2792 if (response_type != NULL) { 2793 return true; 2794 } 2795 return false; 2796 } 2797 2798 #if SRP_FEATURE_NAT64 2799 static void 2800 dp_query_append_nat64_prefix_records(dnssd_query_t *query) 2801 { 2802 // 192.0.0.170 and 192.0.0.171 are reserved IPv4 addresses for ipv4only.arpa. 2803 // See <https://tools.ietf.org/html/rfc7050#section-8.2>. 2804 const uint8_t ipv4_addrs[2][4] = { 2805 {192, 0, 0, 170}, 2806 {192, 0, 0, 171} 2807 }; 2808 uint8_t rdata[16] = {0}; 2809 2810 VALIDATE_TRACKER_CONNECTION_NON_NULL(); 2811 2812 const struct in6_addr *prefix = nat64_get_ipv6_prefix(); 2813 memcpy(rdata, prefix->s6_addr, sizeof(rdata)); 2814 for (size_t i = 0; i < countof(ipv4_addrs);) { 2815 memcpy(&rdata[12], ipv4_addrs[i], 4); 2816 uint8_t *revert = query->towire.p; 2817 dp_query_add_data_to_response(query, "ipv4only.arpa.", dns_rrtype_aaaa, query->question->qclass, 2818 (uint16_t)sizeof(rdata), rdata, RFC8766_TTL_CLAMP, true, false, 2819 &query->response->arcount); 2820 if (query->towire.truncated) { 2821 query->towire.p = revert; 2822 if (query->tracker->connection->tcp_stream) { 2823 if (embiggen(query)) { 2824 query->towire.truncated = false; 2825 query->towire.error = false; 2826 continue; 2827 } else { 2828 dns_rcode_set(query->response, dns_rcode_servfail); 2829 } 2830 } 2831 return; 2832 } 2833 i++; 2834 } 2835 } 2836 #endif // SRP_FEATURE_NAT64 2837 2838 static void 2839 dns_query_answer_process(DNSServiceFlags flags, DNSServiceErrorType errorCode, 2840 const char *fullname, uint16_t rrtype, uint16_t rrclass, 2841 uint16_t rdlen, const void *rdata, uint32_t ttl, dnssd_query_t *query, bool send) 2842 { 2843 question_t *question = query->question; 2844 2845 INFO("[Q%d][QU%d] " PRI_S_SRP PUB_S_SRP PUB_S_SRP " %d %x %d %p", SERIAL(query), SERIAL(question), 2846 fullname, (flags & kDNSServiceFlagsMoreComing) ? " m " : " ", dns_rrtype_to_string(rrtype), rrclass, rdlen, 2847 errorCode, query); 2848 2849 VALIDATE_TRACKER_CONNECTION_NON_NULL(); 2850 2851 if (errorCode == kDNSServiceErr_NoError) { 2852 #if SRP_FEATURE_NAT64 2853 const bool aaaa_query_got_a_record = (question->type == dns_rrtype_aaaa) && (rrtype == dns_rrtype_a); 2854 if (srp_servers->srp_nat64_enabled && (ntohs(query->response->arcount) != 0) && !aaaa_query_got_a_record) { 2855 return; 2856 } 2857 #endif 2858 re_add: 2859 if (send) { 2860 uint16_t *counter = &query->response->ancount; 2861 #if SRP_FEATURE_NAT64 2862 if (srp_servers->srp_nat64_enabled && aaaa_query_got_a_record) { 2863 counter = &query->response->arcount; 2864 } 2865 #endif 2866 uint8_t *revert = query->towire.p; 2867 dp_query_add_data_to_response(query, fullname, rrtype, rrclass, rdlen, rdata, 2868 ttl > RFC8766_TTL_CLAMP ? RFC8766_TTL_CLAMP : ttl, false, false, counter); 2869 if (query->towire.truncated) { 2870 query->towire.p = revert; 2871 if (query->tracker->connection->tcp_stream) { 2872 if (embiggen(query)) { 2873 query->towire.truncated = false; 2874 query->towire.error = false; 2875 goto re_add; 2876 } else { 2877 dns_rcode_set(query->response, dns_rcode_servfail); 2878 dp_query_send_dns_response(query, "failed embiggen"); 2879 return; 2880 } 2881 } 2882 } 2883 } 2884 // If there isn't more coming, send the response now 2885 if (!(flags & kDNSServiceFlagsMoreComing) || query->towire.truncated) { 2886 // When we get a CNAME response, we may not get the record it points to with the MoreComing 2887 // flag set, so don't respond yet. 2888 if (question->type != dns_rrtype_cname && rrtype == dns_rrtype_cname) { 2889 INFO("[Q%d][QU%d] not responding yet because CNAME.", SERIAL(query), SERIAL(question)); 2890 } else { 2891 #if SRP_FEATURE_NAT64 2892 if (srp_servers->srp_nat64_enabled && (ntohs(query->response->arcount) != 0)) { 2893 dp_query_append_nat64_prefix_records(query); 2894 } 2895 #endif 2896 dp_query_send_dns_response(query, "normal success"); 2897 } 2898 } 2899 } else if (errorCode == kDNSServiceErr_NoSuchRecord) { 2900 // If we get "no such record," we can't really do much except return the answer. 2901 dp_query_send_dns_response(query, "no such record"); 2902 } else { 2903 dns_rcode_set(query->response, dns_rcode_servfail); 2904 dp_query_send_dns_response(query, "unhandled error"); 2905 } 2906 } 2907 2908 // answer_match is to decide if an answer matches the one requested to be removed. 2909 // Based on rfc8765 2910 // Remove all RRsets from a name in all classes: 2911 // TTL = 0xFFFFFFFE, RDLEN = 0, CLASS = 255 (ANY). 2912 // Remove all RRsets from a name in given class: 2913 // TTL = 0xFFFFFFFE, RDLEN = 0, CLASS gives class, TYPE = 255 (ANY). 2914 // Remove specified RRset from a name in given class: 2915 // TTL = 0xFFFFFFFE, RDLEN = 0, 2916 // CLASS and TYPE specify the RRset being removed. 2917 // Remove an individual RR from a name: 2918 // TTL = 0xFFFFFFFF, 2919 // CLASS, TYPE, RDLEN, and RDATA specify the RR being removed. 2920 static bool 2921 answer_match(const answer_t *answer, uint32_t rdlen, const char *fullname, uint16_t rrtype, uint16_t rrclass, const void *rdata) 2922 { 2923 return (((rrclass == dns_qclass_any) || (rrclass == answer->rrclass)) && 2924 ((rrtype == dns_rrtype_any) || (rrtype == answer->rrtype)) && 2925 ((rdlen == 0) || ((rdlen == answer->rdlen) && (memcmp(answer->rdata, rdata, rdlen) == 0))) && 2926 (!strcmp(answer->fullname, fullname)) 2927 ); 2928 } 2929 2930 static void 2931 dns_push_query_answer_process(DNSServiceFlags flags, DNSServiceErrorType errorCode, 2932 const char *fullname, uint16_t rrtype, uint16_t rrclass, 2933 uint16_t rdlen, const void *rdata, uint32_t ttl, dnssd_query_t *query, bool send); 2934 2935 // This is the callback for both dns query and dns push query results. 2936 static void 2937 dns_question_callback(DNSServiceRef UNUSED sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, 2938 DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, 2939 uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) 2940 { 2941 question_t *question = context; 2942 dnssd_query_t *query, *next; 2943 bool send = true; 2944 2945 // For dns push query, insert or remove answer from the question cache depending on the flags 2946 // For dns query (dso==NULL), add answer when receiving callback to the question 2947 if (errorCode == kDNSServiceErr_NoError) { 2948 if (flags & kDNSServiceFlagsAdd) { 2949 // Eliminate duplicates (appears on more than one interface) 2950 for (answer_t *answer = question->answers; answer != NULL; answer = answer->next) { 2951 if (answer_match(answer, rdlen, fullname, rrtype, rrclass, rdata)) { 2952 INFO("[QU%d] duplicate answer in cache - name: " PRI_S_SRP ", rrtype: " PUB_S_SRP 2953 ", rrclass: " PUB_S_SRP ", rdlen: %u." PUB_S_SRP, SERIAL(question), 2954 fullname, dns_rrtype_to_string(rrtype), dns_qclass_to_string(rrclass), rdlen, 2955 (flags & kDNSServiceFlagsMoreComing) ? " more coming" : " done"); 2956 send = false; 2957 break; 2958 } 2959 } 2960 if (send) { 2961 // Add the extra space rdlen stores rdata at the end 2962 answer_t *answer = calloc(1, sizeof(*answer) + rdlen); 2963 if (answer == NULL) { 2964 ERROR("[QU%d] unable to allocate memory for answer - name: " PRI_S_SRP ", rrtype: " PUB_S_SRP 2965 ", rrclass: " PUB_S_SRP ", rdlen: %u.", SERIAL(question), 2966 fullname, dns_rrtype_to_string(rrtype), dns_qclass_to_string(rrclass), rdlen); 2967 return; 2968 } 2969 answer->fullname = strdup(fullname); 2970 if (answer->fullname == NULL) { 2971 ERROR("[QU%d] strdup failed to copy the answer name: " PRI_S_SRP, SERIAL(question), fullname); 2972 free(answer); 2973 return; 2974 } 2975 answer->interface_index = interfaceIndex; 2976 answer->ttl = ttl; 2977 answer->rrtype = rrtype; 2978 answer->rrclass = rrclass; 2979 answer->rdlen = rdlen; 2980 answer->rdata = (uint8_t *)(answer + 1); 2981 memcpy(answer->rdata, rdata, rdlen); 2982 answer->next = NULL; 2983 // Insert answer at the tail 2984 answer_t **tail = &(question->answers); 2985 while (*tail != NULL) { 2986 tail = &((*tail)->next); 2987 } 2988 *tail = answer; 2989 // Received data; reset no_data flag. 2990 question->no_data = false; 2991 INFO("[QU%d] add answer to cache - name: " PRI_S_SRP ", rrtype: " PUB_S_SRP 2992 ", rrclass: " PUB_S_SRP ", rdlen: %u." PUB_S_SRP, SERIAL(question), 2993 fullname, dns_rrtype_to_string(rrtype), dns_qclass_to_string(rrclass), rdlen, 2994 (flags & kDNSServiceFlagsMoreComing) ? " more coming" : " done"); 2995 } 2996 } else { 2997 // Remove 2998 answer_t **answer = &(question->answers); 2999 answer_t *cur = NULL; 3000 bool matched = false; 3001 while (*answer != NULL) { 3002 cur = *answer; 3003 if (answer_match(cur, rdlen, fullname, rrtype, rrclass, rdata)) { 3004 INFO("[QU%d] remove answer from cache - " 3005 "name: " PRI_S_SRP ", rrtype: " PUB_S_SRP ", rrclass: " PUB_S_SRP ", rdlen: %u." PUB_S_SRP, 3006 SERIAL(question), fullname, dns_rrtype_to_string(rrtype), dns_qclass_to_string(rrclass), rdlen, 3007 (flags & kDNSServiceFlagsMoreComing) ? " more coming" : " done"); 3008 *answer = cur->next; 3009 dp_answer_free(cur); 3010 matched = true; 3011 // If individual RR to be removed, get out of the loop once the RR has been removed 3012 if (rdlen != 0) { 3013 break; 3014 } 3015 } else { 3016 answer = &cur->next; 3017 } 3018 } 3019 if (!matched) { 3020 INFO("[Q%d] remove not found in cache - name: " PRI_S_SRP 3021 ", rrtype: " PUB_S_SRP ", rrclass: " PUB_S_SRP ", rdlen: %u." PUB_S_SRP, SERIAL(question), 3022 fullname, dns_rrtype_to_string(rrtype), dns_qclass_to_string(rrclass), rdlen, 3023 (flags & kDNSServiceFlagsMoreComing) ? " more coming" : " done"); 3024 } 3025 if (*answer == NULL) { 3026 // All the answers get removed; set no_data flag. 3027 question->no_data = true; 3028 } 3029 } 3030 } else if (errorCode == kDNSServiceErr_NoSuchRecord) { 3031 INFO("[QU%d] no data - name: " PRI_S_SRP ", rrtype: " PUB_S_SRP 3032 ", rrclass: " PUB_S_SRP ", rdlen: %u." PUB_S_SRP, SERIAL(question), fullname, dns_rrtype_to_string(rrtype), 3033 dns_qclass_to_string(rrclass), rdlen, (flags & kDNSServiceFlagsMoreComing) ? " more coming" : " done"); 3034 question->no_data = true; 3035 } else if (errorCode == kDNSServiceErr_ServiceNotRunning || errorCode == kDNSServiceErr_DefunctConnection) { 3036 #if SRP_FEATURE_DNSSD_PROXY_SHARED_CONNECTIONS 3037 if (shared_discovery_txn != NULL) { 3038 ioloop_dnssd_txn_cancel(shared_discovery_txn); 3039 ioloop_dnssd_txn_release(shared_discovery_txn); 3040 shared_discovery_txn = NULL; 3041 dp_handle_server_disconnect(NULL, errorCode); 3042 } 3043 #else 3044 ioloop_dnssd_txn_cancel(question->txn); 3045 ioloop_dnssd_txn_release(question->txn); 3046 question->txn = NULL; 3047 dp_handle_server_disconnect(NULL, errorCode); 3048 #endif 3049 return; // This doesn't count as a result. 3050 } 3051 query = question->queries; 3052 while(query != NULL) { 3053 next = query->question_next; 3054 if (query->dso != NULL) { 3055 dns_push_query_answer_process(flags, errorCode, fullname, rrtype, rrclass, 3056 rdlen, rdata, ttl, query, send); 3057 } else { 3058 dns_query_answer_process(flags, errorCode, fullname, rrtype, rrclass, 3059 rdlen, rdata, ttl, query, send); 3060 } 3061 query = next; 3062 } 3063 dp_question_cache_remove_queries(question); 3064 } 3065 3066 static void 3067 dp_query_wakeup(void *context) 3068 { 3069 dnssd_query_t *query = context; 3070 char name[DNS_MAX_NAME_SIZE + 1]; 3071 size_t namelen = strlen(query->question->name); 3072 question_t *question = query->question; 3073 3074 if (question->answers != NULL) { 3075 FAULT("[Q%d][QU%d] answers present, but dp_query_wakeup reached for name " PRI_S_SRP, 3076 SERIAL(query), SERIAL(question), question->name); 3077 } else { 3078 question->no_data = true; 3079 } 3080 3081 // Should never happen. 3082 if (namelen + (question->served_domain 3083 ? (question->served_domain->interface != NULL 3084 ? sizeof local_suffix 3085 // XXX why are we checking this but not copying in the served domain name below? 3086 : strlen(question->served_domain->domain_ld) + 1) 3087 : 0) > sizeof name) { 3088 ERROR("[Q%d][QU%d] no space to construct name.", SERIAL(query), SERIAL(question)); 3089 dnssd_query_cancel(query); 3090 return; 3091 } 3092 3093 memcpy(name, question->name, namelen + 1); 3094 if (question->served_domain != NULL) { 3095 memcpy(name + namelen, local_suffix, sizeof(local_suffix)); 3096 } 3097 RETAIN_HERE(query, dnssd_query); 3098 dp_query_send_dns_response(query, "query wakeup"); 3099 dp_question_cache_remove_queries(question); 3100 RELEASE_HERE(query, dnssd_query); 3101 } 3102 3103 // Search asked question in the cache; if not existing, create one. 3104 static question_t * 3105 dp_query_question_cache_copy(dns_rr_t *search_term, bool *new) 3106 { 3107 char name[DNS_MAX_NAME_SIZE + 1]; 3108 served_domain_t *sdt = dp_served(search_term->name, name, sizeof(name)); 3109 3110 if (sdt == NULL) { 3111 dns_name_print(search_term->name, name, sizeof name); 3112 } 3113 question_t **questions, *ret = NULL; 3114 question_t *new_question = NULL; 3115 // if the query is in served domain, lookup in served_domain->questions 3116 // otherwise lookup in the out-of-domain question cache 3117 if (sdt != NULL) { 3118 questions = &sdt->questions; 3119 } else { 3120 questions = &questions_without_domain; 3121 } 3122 *new = false; 3123 while (*questions != NULL) { 3124 question_t *question = *questions; 3125 if (search_term->type == question->type && 3126 search_term->qclass == question->qclass && 3127 !strcmp(name, question->name)) 3128 { 3129 ret = question; 3130 break; 3131 } 3132 questions = &question->next; 3133 } 3134 3135 // If no cache entry was found, create one 3136 if (*questions == NULL) { 3137 new_question = calloc(1, sizeof(*new_question)); 3138 require_action_quiet(new_question != NULL, exit, 3139 ERROR("Unable to allocate memory for question entry on " PRI_S_SRP, name)); 3140 new_question->name = strdup(name); 3141 require_action_quiet(new_question->name != NULL, exit, 3142 ERROR("unable to allocate memory for question name on " PRI_S_SRP, name)); 3143 new_question->type = search_term->type; 3144 new_question->qclass = search_term->qclass; 3145 new_question->start_time = srp_utime(); 3146 new_question->answers = NULL; 3147 new_question->served_domain = sdt; 3148 new_question->queries = NULL; 3149 new_question->no_data = false; 3150 new_question->serial = ++cur_question_serial; 3151 3152 if (sdt != NULL && sdt->interface != NULL) { 3153 new_question->interface_index = sdt->interface->ifindex; 3154 new_question->serviceFlags = kDNSServiceFlagsForceMulticast; 3155 } else { 3156 new_question->interface_index = kDNSServiceInterfaceIndexAny; 3157 new_question->serviceFlags = kDNSServiceFlagsReturnIntermediates; 3158 } 3159 3160 // Link the new_question to the question list. 3161 new_question->next = *questions; 3162 *questions = new_question; 3163 RETAIN_HERE(*questions, question); // retain 3164 3165 // Successfully created a new question, which will be the returned question. 3166 ret = new_question; 3167 new_question = NULL; 3168 *new = true; 3169 } 3170 exit: 3171 if (new_question != NULL) { 3172 RELEASE_HERE(new_question, question); 3173 } 3174 if (ret != NULL) { 3175 RETAIN_HERE(ret, question); 3176 } 3177 return ret; 3178 } 3179 3180 // Look for answers in the cache for the current query. Remove flag is used when mDNSResponder connection is broken, to signal to 3181 // DNS Push clients only that the records previously sent should be discarded. 3182 static void 3183 dp_query_reply_from_cache(question_t *question, dnssd_query_t *query, bool remove) 3184 { 3185 // For dns query, if no_data is flagged or it's been six seconds since the question 3186 // was started and there is still no answer yet, we should also respond immediately. 3187 // [DNS Discovery Proxy RFC, RFC 8766, Section 5.6] 3188 // Note that six seconds as stated in RFC8766 is probably too long, currently we're using 800ms. 3189 if (query->dso == NULL && 3190 (question->no_data == true || 3191 (question->answers == NULL && 3192 srp_utime() - question->start_time > RESPONSE_WINDOW_USECS))) 3193 { 3194 INFO("[Q%d][QU%d] no data for question - type %d class %d " PRI_S_SRP, 3195 SERIAL(query), SERIAL(question), question->type, question->qclass, question->name); 3196 dns_query_answer_process(0, kDNSServiceErr_NoSuchRecord, question->name, 3197 question->type, question->qclass, 0, 3198 NULL, 0, query, true); 3199 dp_question_cache_remove_queries(question); 3200 return; 3201 } 3202 // answers are available for the question being asked 3203 if (question->answers != NULL) { 3204 INFO("[Q%d][QU%d] reply from cache for question - type %d class %d " PRI_S_SRP, 3205 SERIAL(query), SERIAL(question), question->type, question->qclass, question->name); 3206 DNSServiceFlags flags; 3207 answer_t *answer = question->answers; 3208 while (answer != NULL) { 3209 if (remove) { 3210 flags = 0; 3211 } else { 3212 flags = kDNSServiceFlagsAdd; 3213 } 3214 3215 answer_t *next = answer->next; 3216 if (next != NULL) { 3217 flags |= kDNSServiceFlagsMoreComing; 3218 } 3219 if (query->dso == NULL) { 3220 if (!remove) { 3221 dns_query_answer_process(flags, kDNSServiceErr_NoError, answer->fullname, 3222 answer->rrtype, answer->rrclass, answer->rdlen, 3223 answer->rdata, answer->ttl, query, true); 3224 } 3225 } else { 3226 dns_push_query_answer_process(flags, kDNSServiceErr_NoError, answer->fullname, 3227 answer->rrtype, answer->rrclass, answer->rdlen, 3228 answer->rdata, answer->ttl, query, true); 3229 } 3230 answer = next; 3231 } 3232 dp_question_cache_remove_queries(question); 3233 } 3234 } 3235 3236 static void 3237 dp_query_context_release(void *context) 3238 { 3239 dnssd_query_t *query = context; 3240 RELEASE_HERE(query, dnssd_query); 3241 } 3242 3243 static bool 3244 dp_query_start(dnssd_query_t *query, int *rcode, bool *hardwired, bool dns64) 3245 { 3246 bool local = false; 3247 question_t *question = query->question; 3248 3249 if (question->served_domain != NULL) { 3250 if (dnssd_hardwired_response(query, dns_question_callback)) { 3251 *rcode = dns_rcode_noerror; // indicate that we already sent the response 3252 *hardwired = true; 3253 INFO("[Q%d] hardwired response", SERIAL(query)); 3254 return true; 3255 } 3256 local = true; 3257 } 3258 3259 // If we get an SOA query for record that's under a zone cut we're authoritative for, which 3260 // is the case of query->served_domain->interface != NULL, then answer with a negative response that includes 3261 // our authority records, rather than waiting for the query to time out. 3262 if (question->served_domain != NULL && question->served_domain->interface != NULL && !question->name[0]) { 3263 // If this isn't a DNS Push query, we can signal that there is no data. Otherwise we just never send an answer since 3264 // we will never have one. 3265 if (query->dso == NULL) { 3266 *hardwired = true; 3267 } 3268 return true; 3269 } 3270 3271 // Check if DNSServiceQueryRecord call needs to be made 3272 if (question->txn == NULL) { 3273 int ret = dp_start_question(question, dns64); 3274 if (ret == kDNSServiceErr_Refused) { 3275 *rcode = dns_rcode_servfail; 3276 INFO("question was refused"); 3277 } else if (ret != kDNSServiceErr_NoError) { 3278 *rcode = dns_rcode_servfail; 3279 INFO("[Q%d] couldn't start question", SERIAL(query)); 3280 return false; 3281 } 3282 } else { 3283 if (question->answers != NULL || question->no_data) { 3284 INFO("[Q%d] answering immediately from cache", SERIAL(query)); 3285 *rcode = dns_rcode_noerror; 3286 return true; 3287 } 3288 } 3289 3290 // If this isn't a DNS Push subscription, we need to respond quickly with as much data as we have. It 3291 // turns out that dig gives us a second, but also that responses seem to come back in on the order of a 3292 // millisecond, so we'll wait 100ms. 3293 if (query->dso == NULL && local) { 3294 // [DNS Discovery Proxy RFC, RFC 8766, Section 5.6, Answer Aggregation] 3295 3296 // RFC8766 asks us to wait six seconds, but this is probably too long. Most likely we will have all 3297 // our answers much sooner than that, and waiting this long means that we have to keep state for 3298 // this long; when there are a lot of queries coming in, that can amount to too much state, causing 3299 // us to drop requests we could easily have answered. 3300 if (query->wakeup == NULL) { 3301 query->wakeup = ioloop_wakeup_create(); 3302 if (query->wakeup == NULL) { 3303 *rcode = dns_rcode_servfail; 3304 return false; 3305 } 3306 } 3307 ioloop_add_wake_event(query->wakeup, query, dp_query_wakeup, dp_query_context_release, RESPONSE_WINDOW_MSECS /* ms */); 3308 RETAIN_HERE(query, dnssd_query); 3309 } 3310 3311 INFO("[Q%d] waiting for wakeup or response", SERIAL(query)); 3312 return true; 3313 } 3314 3315 static dnssd_query_t * 3316 dp_query_create(dp_tracker_t *tracker, dns_rr_t *question, message_t *message, dso_state_t *dso, int *rcode) 3317 { 3318 char name[DNS_MAX_NAME_SIZE + 1]; 3319 served_domain_t *sdt = dp_served(question->name, name, sizeof name); 3320 int xid = message == NULL ? 0 : ntohs(message->wire.id); 3321 3322 dnssd_query_t *query = calloc(1,sizeof *query); 3323 require_action_quiet(query != NULL, exit, *rcode = dns_rcode_servfail; 3324 ERROR("Unable to allocate memory for query on " PRI_S_SRP, name)); 3325 RETAIN_HERE(query, dnssd_query); // for the caller 3326 query->serial = ++cur_query_serial; 3327 3328 // If it's a query for a name served by the local discovery proxy, do an mDNS lookup. 3329 if (sdt != NULL) { 3330 INFO("[Q%d][QID%x] msg %p " PUB_S_SRP " question: type %d class %d " PRI_S_SRP "." PRI_S_SRP " -> " 3331 PRI_S_SRP DOT_LOCAL, SERIAL(query), xid, message, dso != NULL ? "push" : " dns", 3332 question->type, question->qclass, name, sdt->domain, name); 3333 } else { 3334 dns_name_print(question->name, name, sizeof name); 3335 INFO("[Q%d][QID%x] msg %p " PUB_S_SRP " question: type %d class %d " PRI_S_SRP, SERIAL(query), xid, 3336 message, dso != NULL ? "push" : " dns", question->type, question->qclass, name); 3337 } 3338 3339 query->response = malloc(sizeof *query->response); 3340 require_action_quiet(query->response != NULL, exit, *rcode = dns_rcode_servfail; 3341 ERROR("[Q%d] Unable to allocate memory for query response on " PRI_S_SRP, 3342 SERIAL(query), name)); 3343 3344 query->data_size = DNS_DATA_SIZE; 3345 3346 // Zero out the DNS header, but not the data. 3347 memset(query->response, 0, DNS_HEADER_SIZE); 3348 3349 // Name now contains the name we want mDNSResponder to look up. 3350 3351 // The only thing holding a reference to query is its tracker. 3352 query->tracker = tracker; 3353 RETAIN_HERE(query->tracker, dp_tracker); 3354 3355 // Remember whether this is a long-lived query. 3356 query->dso = dso; 3357 3358 // Retain the question, as we will need it to send a response. 3359 if (message != NULL) { 3360 query->message = message; 3361 ioloop_message_retain(query->message); 3362 } 3363 3364 // Start writing the response 3365 dp_query_towire_reset(query); 3366 3367 bool new_entry; 3368 query->question = dp_query_question_cache_copy(question, &new_entry); 3369 require_action_quiet(query->question != NULL, exit, *rcode = dns_rcode_servfail); 3370 3371 // add the query to the list of queries that are asking this question. 3372 dnssd_query_t **qr = &(query->question->queries); 3373 while (*qr != NULL) { 3374 qr = &(*qr)->question_next; 3375 } 3376 *qr = query; 3377 // Question query list holds a reference to the query. 3378 RETAIN_HERE(*qr, dnssd_query); 3379 INFO("[Q%d][QID%x] msg %p " PUB_S_SRP " cache entry for question: type %d class %d " PRI_S_SRP, SERIAL(query), 3380 xid, query->message, new_entry ? "new" : " existing", question->type, question->qclass, name); 3381 *rcode = dns_rcode_noerror; 3382 dp_num_outstanding_queries++; 3383 3384 exit: 3385 if (*rcode != dns_rcode_noerror && query != NULL) { 3386 RELEASE_HERE(query, dnssd_query); 3387 query = NULL; 3388 } 3389 return query; 3390 } 3391 3392 3393 static void 3394 dns_push_query_answer_process(DNSServiceFlags flags, DNSServiceErrorType errorCode, 3395 const char *fullname, uint16_t rrtype, uint16_t rrclass, 3396 uint16_t rdlen, const void *rdata, uint32_t ttl, dnssd_query_t *query, bool send) 3397 { 3398 uint8_t *revert = query->towire.p; 3399 3400 VALIDATE_TRACKER_CONNECTION_NON_NULL(); 3401 3402 // From DNSSD-Hybrid, for mDNS queries: 3403 // If we have cached answers, respond immediately, because we probably have all the answers. 3404 // If we don't have cached answers, respond as soon as we get an answer (presumably more-coming will be false). 3405 3406 // The spec says to not query if we have cached answers. We trust the DNSServiceQueryRecord call to handle this. 3407 3408 // If we switch to using a single connection to mDNSResponder, we could have !more-coming trigger a flush of 3409 // all outstanding queries that aren't waiting on a time trigger. This is because more-coming isn't 3410 // query-specific 3411 3412 INFO("[Q%d] PUSH " PRI_S_SRP " %d %d %x %d %p", SERIAL(query), fullname, rrtype, rrclass, rdlen, errorCode, query); 3413 3414 // query_state_waiting means that we're answering a regular DNS question 3415 if (errorCode == kDNSServiceErr_NoError) { 3416 if (send) { 3417 const void *rdata_to_send; 3418 uint32_t ttl_to_send; 3419 // If kDNSServiceFlagsAdd is set, it's an add, otherwise a delete. 3420 re_add: 3421 if (flags & kDNSServiceFlagsAdd) { 3422 rdata_to_send = rdata; 3423 ttl_to_send = ttl; 3424 INFO("[Q%d] DNS Push adding record - " 3425 "name: " PRI_S_SRP ", rrtype: " PUB_S_SRP ", rrclass: " PUB_S_SRP ", rdlen: %u, ttl: %u.", 3426 SERIAL(query), fullname, dns_rrtype_to_string(rrtype), dns_qclass_to_string(rrclass), rdlen, ttl_to_send); 3427 } else { 3428 // See <https://tools.ietf.org/html/rfc8765#section-6.3.1>. 3429 #define TTL_TO_REMOVE_INDIVIDUAL_RECORDS 0xFFFFFFFF 3430 #define TTL_TO_REMOVE_MULTIPLE_RECORDS 0xFFFFFFFE 3431 if (rdlen == 0) { 3432 // Remove specified RRset from a name in given class: 3433 // TTL = 0xFFFFFFFE, RDLEN = 0, 3434 // CLASS and TYPE specify the RRset being removed. 3435 rdata_to_send = NULL; 3436 ttl_to_send = TTL_TO_REMOVE_MULTIPLE_RECORDS; 3437 } else { 3438 // Remove an individual RR from a name: 3439 // TTL = 0xFFFFFFFF, 3440 // CLASS, TYPE, RDLEN, and RDATA specify the RR being removed. 3441 rdata_to_send = rdata; 3442 ttl_to_send = TTL_TO_REMOVE_INDIVIDUAL_RECORDS; 3443 } 3444 INFO("[Q%d] DNS Push removing record - " 3445 "name: " PRI_S_SRP ", rrtype: " PUB_S_SRP ", rrclass: " PUB_S_SRP ", rdlen: %u, ttl: 0x%X.", 3446 SERIAL(query), fullname, dns_rrtype_to_string(rrtype), dns_qclass_to_string(rrclass), rdlen, ttl_to_send); 3447 } 3448 3449 // Do the update. 3450 dp_query_add_data_to_response(query, fullname, rrtype, rrclass, rdlen, rdata_to_send, 3451 ttl_to_send, false, false, NULL); 3452 3453 if (query->towire.truncated) { 3454 query->towire.truncated = false; 3455 query->towire.p = revert; 3456 query->towire.error = 0; 3457 dp_push_response(query, NULL); 3458 dns_push_start(query); 3459 goto re_add; 3460 } 3461 } 3462 // If there isn't more coming, send a DNS Push notification now. 3463 // XXX If enough comes to fill the response, send the message. 3464 if (!(flags & kDNSServiceFlagsMoreComing)) { 3465 dp_push_response(query, NULL); 3466 } 3467 } else if (errorCode != kDNSServiceErr_NoSuchRecord) { // Do nothing if kDNSServiceErr_NoSuchRecord is received. 3468 ERROR("[Q%d] unexpected error code %d", SERIAL(query), errorCode); 3469 dnssd_query_cancel(query); 3470 } 3471 } 3472 3473 static void 3474 dns_push_subscribe(dp_tracker_t *tracker, const dns_wire_t *header, dso_state_t *dso, dns_rr_t *question, 3475 const char *activity_name, const char * UNUSED opcode_name) 3476 { 3477 int rcode; 3478 dnssd_query_t *query = dp_query_create(tracker, question, NULL, dso, &rcode); 3479 3480 if (!query) { 3481 dso_simple_response(tracker->connection, NULL, header, rcode); 3482 return; 3483 } 3484 3485 dso_activity_t *activity = dso_add_activity(dso, activity_name, push_subscription_activity_type, query, 3486 dns_push_cancel); 3487 RETAIN_HERE(query, dnssd_query); // The activity holds a reference to the query. 3488 query->activity = activity; 3489 bool dns64 = false; 3490 #if SRP_FEATURE_NAT64 3491 if (srp_servers->srp_nat64_enabled) { 3492 dns64 = nat64_is_active(); 3493 } 3494 #endif 3495 bool hardwired = false; 3496 if (!dp_query_start(query, &rcode, &hardwired, dns64)) { 3497 dso_simple_response(tracker->connection, NULL, header, rcode); 3498 dp_question_cache_remove_queries(query->question); 3499 dnssd_query_cancel(query); 3500 } else { 3501 char nbuf[DNS_MAX_NAME_SIZE + 1]; 3502 dns_name_print(question->name, nbuf, sizeof(nbuf)); 3503 // The push subscribe can be considered a success at this point. 3504 dso_simple_response(tracker->connection, NULL, header, dns_rcode_noerror); 3505 if (hardwired) { 3506 INFO("[DSO%d][Q%d] hardwired response for " PRI_S_SRP " %d %d", 3507 SERIAL(dso), SERIAL(query), nbuf, question->type, question->qclass); 3508 dp_push_response(query, question); 3509 } else if (query->question != NULL) { 3510 INFO("[DSO%d][Q%d][QU%d] replying from cache for " PRI_S_SRP " %d %d", 3511 SERIAL(dso), SERIAL(query), SERIAL(query->question), nbuf, question->type, question->qclass); 3512 dp_query_reply_from_cache(query->question, query, false); 3513 } 3514 } 3515 3516 // dp_query_create() returned the query retained; when we added the query to the activity, we retained it again; 3517 // if something went wrong, the second retain was released, but whether or not something went wrong, we can now 3518 // safely release the initial retain. 3519 RELEASE_HERE(query, dnssd_query); 3520 } 3521 3522 static void 3523 dns_push_reconfirm(comm_t *comm, const dns_wire_t *header, dso_state_t *dso) 3524 { 3525 dns_rr_t question; 3526 char name[DNS_MAX_NAME_SIZE + 1]; 3527 uint16_t rdlen; 3528 memset(&question, 0, sizeof(question)); 3529 3530 // The TLV offset should always be pointing into the message. 3531 unsigned offp = (unsigned)(dso->primary.payload - &header->data[0]); 3532 unsigned len = offp + dso->primary.length; 3533 dp_tracker_t *tracker = comm->context; 3534 3535 // Parse the name, rrtype and class. We say there's no rdata even though there is 3536 // because there's no ttl and also we want the raw rdata, not parsed rdata. 3537 if (!dns_rr_parse(&question, header->data, len, &offp, false, false) || 3538 !dns_u16_parse(header->data, len, &offp, &rdlen)) 3539 { 3540 dso_simple_response(comm, NULL, header, dns_rcode_formerr); 3541 ERROR("[DSO%d][C%d][TRK%d] RR parse from %s failed", 3542 SERIAL(dso), SERIAL(comm), SERIAL(tracker), dso->remote_name); 3543 goto out; 3544 } 3545 if (rdlen + offp != len) { 3546 dso_simple_response(comm, NULL, header, dns_rcode_formerr); 3547 ERROR("[DSO%d][C%d][TRK%d] RRdata parse from %s failed: length mismatch (%d != %d)", 3548 SERIAL(dso), SERIAL(comm), SERIAL(tracker), dso->remote_name, rdlen + offp, len); 3549 goto out; 3550 } 3551 3552 if ((dp_served(question.name, name, sizeof name))) { 3553 size_t name_len = strlen(name); 3554 if (name_len + sizeof local_suffix > sizeof name) { 3555 dso_simple_response(comm, NULL, header, dns_rcode_formerr); 3556 ERROR("[DSO%d][C%d][TRK%d] name is too long for .local suffix: %s", SERIAL(dso), SERIAL(comm), SERIAL(tracker), name); 3557 goto out; 3558 } 3559 memcpy(&name[name_len], local_suffix, sizeof local_suffix); 3560 } else { 3561 dns_name_print(question.name, &name[8], sizeof name - 8); 3562 } 3563 // transmogrify name. 3564 DNSServiceReconfirmRecord(0, kDNSServiceInterfaceIndexAny, name, 3565 question.type, question.qclass, rdlen, &header->data[offp]); 3566 dso_simple_response(comm, NULL, header, dns_rcode_noerror); 3567 out: 3568 dns_rrdata_free(&question); 3569 dns_name_free(question.name); 3570 } 3571 3572 static void 3573 dns_push_unsubscribe(dso_activity_t *activity) 3574 { 3575 dnssd_query_t *query = activity->context; 3576 dnssd_query_cancel(query); 3577 // No response, unsubscribe is unidirectional. 3578 } 3579 3580 static void 3581 dns_push_subscription_change(const char *opcode_name, dp_tracker_t *tracker, const dns_wire_t *header, dso_state_t *dso) 3582 { 3583 // type-in-hex/class-in-hex/name-to-subscribe 3584 char activity_name[5]; 3585 dso_activity_t *activity; 3586 3587 // The TLV offset should always be pointing into the message. 3588 unsigned offp = (unsigned)(dso->primary.payload - &header->data[0]); 3589 unsigned len = offp + dso->primary.length; 3590 // Get the question 3591 dns_rr_t question; 3592 uint16_t subscribe_xid = ntohs(header->id); 3593 char nbuf[DNS_MAX_NAME_SIZE + 1]; 3594 3595 memset(&question, 0, sizeof(question)); 3596 if (dso->primary.opcode == kDSOType_DNSPushSubscribe) { 3597 if (!dns_rr_parse(&question, header->data, offp + dso->primary.length, &offp, false, false)) { 3598 dso_simple_response(tracker->connection, NULL, header, dns_rcode_formerr); 3599 ERROR("[DSO%d][TRK%d] RR parse for %s from %s failed", SERIAL(dso), SERIAL(tracker), dso->remote_name, opcode_name); 3600 goto out; 3601 } 3602 dns_name_print(question.name, nbuf, sizeof(nbuf)); 3603 } else { 3604 // Unsubscribes are unidirectional, so no response can be sent 3605 if (!dns_u16_parse(header->data, offp + dso->primary.length, &offp, &subscribe_xid)) { 3606 ERROR("unable to get subscribe xid from primary"); 3607 goto out; 3608 } 3609 const char none[] = "none"; 3610 memcpy(nbuf, none, sizeof(none)); 3611 } 3612 if (offp != len) { 3613 if (dso->primary.opcode == kDSOType_DNSPushSubscribe) { 3614 dso_simple_response(tracker->connection, NULL, header, dns_rcode_formerr); 3615 } 3616 ERROR("DNS push " PUB_S_SRP " parse from %s failed: length mismatch (%d != %d)", 3617 dso->primary.opcode == kDSOType_DNSPushSubscribe ? "subscribe" : "unsubscribe", 3618 dso->remote_name, offp, len); 3619 goto out; 3620 } 3621 3622 // Concoct an activity name. The subscribe transaction ID is required to be unique and is used by the 3623 // protocol to identify the subscription, so we can just use that. 3624 snprintf(activity_name, sizeof(activity_name), "%04x", subscribe_xid); 3625 3626 activity = dso_find_activity(dso, activity_name, push_subscription_activity_type, NULL); 3627 if (activity == NULL) { 3628 // Unsubscribe with no activity means no work to do; just return noerror. 3629 if (dso->primary.opcode != kDSOType_DNSPushSubscribe) { 3630 ERROR("[DSO%d][TRK%d] " PUB_S_SRP " for " PRI_S_SRP " (" PUB_S_SRP ") when no subscription exists.", 3631 SERIAL(dso), SERIAL(tracker), opcode_name, nbuf, activity_name); 3632 } else { 3633 INFO("[DSO%d][TRK%d] " PUB_S_SRP " for " PRI_S_SRP " (" PUB_S_SRP ") type %d.", 3634 SERIAL(dso), SERIAL(tracker), opcode_name, nbuf, activity_name, question.type); 3635 // In this case we have a push subscribe for which no subscription exists, which means we can do it. 3636 dns_push_subscribe(tracker, header, dso, &question, activity_name, opcode_name); 3637 } 3638 } else { 3639 // We should never get two subscribes with the same transaction id. 3640 if (dso->primary.opcode == kDSOType_DNSPushSubscribe) { 3641 ERROR("[DSO%d][TRK%d] " PUB_S_SRP " for " PRI_S_SRP " (" PUB_S_SRP ") " 3642 "xid %d when subscription already exists.", SERIAL(dso), SERIAL(tracker), 3643 opcode_name, nbuf, activity_name, subscribe_xid); 3644 dso_simple_response(tracker->connection, NULL, header, dns_rcode_refused); 3645 } 3646 // Otherwise cancel the subscription. 3647 else { 3648 dnssd_query_t *query = activity->context; 3649 char *question_name = query->question != NULL ? query->question->name : nbuf; 3650 INFO("[DSO%d][TRK%d] " PUB_S_SRP " for " PRI_S_SRP " (" PUB_S_SRP ") type %d.", 3651 SERIAL(dso), SERIAL(tracker), opcode_name, question_name, activity_name, question.type); 3652 dns_push_unsubscribe(activity); 3653 } 3654 } 3655 out: 3656 dns_rrdata_free(&question); 3657 dns_name_free(question.name); 3658 } 3659 3660 static bool 3661 dso_limit(dp_tracker_t *tracker, message_t *message, dp_tracker_session_type_t session_type) 3662 { 3663 if (num_push_sessions == MAX_DSO_CONNECTIONS) { 3664 // We are too busy. Return a retry-delay response. 3665 INFO("[TRK%d] no more DNS Push connections allowed--sending retry-delay: %d", SERIAL(tracker), num_push_sessions); 3666 dso_retry_delay_response(tracker->connection, message, &message->wire, dns_rcode_servfail, BUSY_RETRY_DELAY_MS); 3667 3668 num_push_sessions_dropped_for_load++; 3669 // Cancel the connection after five seconds 3670 dp_tracker_idle_after(tracker, 5, NULL); 3671 return true; 3672 } 3673 3674 // Count this as a DSO connection. 3675 (num_push_sessions)++; 3676 INFO("[TRK%d] new DNS Push connection, count is now %d", SERIAL(tracker), num_push_sessions); 3677 3678 tracker->session_type = session_type; 3679 return false; 3680 } 3681 3682 static void dso_message(dp_tracker_t *tracker, message_t *message, dso_state_t *dso) 3683 { 3684 // For the first DSO message we get on a connection, see if we already have too many connections of 3685 // the same type. We track SRP replication and DNS Push separately, because we don't want a surfeit of 3686 // DNS Push messages to prevent replication from working. A surfeit of SRP Replication connections is 3687 // less likely, and less problematic. 3688 if (tracker->session_type == dp_tracker_session_none) { 3689 if (dso->primary.opcode != kDSOType_SRPLSession) { 3690 if (dso_limit(tracker, message, dp_tracker_session_push)) { 3691 return; 3692 } 3693 } 3694 } 3695 3696 switch(dso->primary.opcode) { 3697 case kDSOType_DNSPushSubscribe: 3698 dns_push_subscription_change("DNS Push Subscribe", tracker, &message->wire, dso); 3699 break; 3700 case kDSOType_DNSPushUnsubscribe: 3701 dns_push_subscription_change("DNS Push Unsubscribe", tracker, &message->wire, dso); 3702 break; 3703 3704 case kDSOType_DNSPushReconfirm: 3705 dns_push_reconfirm(tracker->connection, &message->wire, dso); 3706 break; 3707 3708 case kDSOType_DNSPushUpdate: 3709 INFO("[DSO%d][TRK%d] bogus push update message %d", SERIAL(dso), SERIAL(tracker), dso->primary.opcode); 3710 dso_state_cancel(dso); 3711 break; 3712 3713 #if SRP_FEATURE_REPLICATION 3714 case kDSOType_SRPLSession: 3715 if (dso->activities != NULL) { 3716 dso_state_cancel(dso); 3717 ERROR("[DSO%d][TRK%d][C%d] " PRI_S_SRP ": SRP Replication session start received on a connection that is already doing DNS Push.", 3718 SERIAL(dso), SERIAL(tracker), SERIAL(tracker->connection), tracker->connection->name); 3719 return; 3720 } 3721 #ifdef SRP_TEST_SERVER 3722 srpl_dso_server_message(tracker->connection, message, dso, (srp_server_t*)tracker->connection->srp_server); 3723 #else 3724 srpl_dso_server_message(tracker->connection, message, dso, srp_servers); 3725 #endif 3726 break; 3727 #endif 3728 3729 default: 3730 INFO("[DSO%d][TRK%d] unexpected primary TLV %d", SERIAL(dso), SERIAL(tracker), dso->primary.opcode); 3731 dso_simple_response(tracker->connection, NULL, &message->wire, dns_rcode_dsotypeni); 3732 break; 3733 } 3734 // XXX free the message if we didn't consume it. 3735 #ifdef SRP_TEST_SERVER 3736 if (srp_test_dso_message_finished != NULL) { 3737 srp_test_dso_message_finished(srp_test_tls_listener_context, message, dso); 3738 } 3739 #endif 3740 } 3741 3742 static void 3743 dp_keepalive_response_send(dso_keepalive_context_t *keepalive_event, dso_state_t *dso) 3744 { 3745 uint8_t dsobuf[SRPL_KEEPALIVE_MESSAGE_LENGTH]; 3746 dns_towire_state_t towire; 3747 struct iovec iov; 3748 uint16_t *p_dso_length; 3749 dso_message_t state; 3750 3751 if (dso->transport == NULL) { 3752 ERROR("dso state " PRI_S_SRP " has no transport", dso->remote_name); 3753 return; 3754 } 3755 3756 memset(&towire, 0, sizeof(towire)); 3757 towire.p = &dsobuf[DNS_HEADER_SIZE]; 3758 towire.lim = towire.p + (sizeof(dsobuf) - DNS_HEADER_SIZE); 3759 towire.message = (dns_wire_t *)dsobuf; 3760 towire.p_rdlength = NULL; 3761 towire.p_opt = NULL; 3762 p_dso_length = NULL; 3763 3764 dso_make_message(&state, dsobuf, sizeof(dsobuf), dso, false /* unidirectional */, true /* response */, 3765 keepalive_event->xid, dns_rcode_noerror, dso->transport); 3766 dns_u16_to_wire(&towire, kDSOType_Keepalive); 3767 dns_rdlength_begin(&towire); 3768 dns_u32_to_wire(&towire, keepalive_event->inactivity_timeout); // Idle timeout (we are never idle) 3769 dns_u32_to_wire(&towire, keepalive_event->keepalive_interval); // Keepalive timeout 3770 dns_rdlength_end(&towire); 3771 if (towire.error) { 3772 ERROR("ran out of message space at " PUB_S_SRP ", :%d", __FILE__, towire.line); 3773 return; 3774 } 3775 memset(&iov, 0, sizeof(iov)); 3776 iov.iov_len = towire.p - dsobuf; 3777 iov.iov_base = dsobuf; 3778 if (!ioloop_send_message(dso->transport, NULL, &iov, 1)) { 3779 INFO("send failed"); 3780 return; 3781 } 3782 3783 INFO("sent %zd byte response Keepalive, xid %02x%02x (was %04x), to " PRI_S_SRP, 3784 iov.iov_len, dsobuf[0], dsobuf[1], keepalive_event->xid, dso->transport->name); 3785 } 3786 3787 static void 3788 dns_push_callback(void *context, void *event_context, dso_state_t *dso, dso_event_type_t eventType) 3789 { 3790 dso_keepalive_context_t *keepalive_context; 3791 message_t *message; 3792 switch(eventType) 3793 { 3794 case kDSOEventType_DNSMessage: 3795 // We shouldn't get here because we already handled any DNS messages 3796 message = event_context; 3797 INFO("[DSO%d] DNS Message (opcode=%d) received from " PRI_S_SRP, SERIAL(dso), dns_opcode_get(&message->wire), 3798 dso->remote_name); 3799 break; 3800 case kDSOEventType_DNSResponse: 3801 // We shouldn't get here because we already handled any DNS messages 3802 message = event_context; 3803 INFO("[DSO%d] DNS Response (opcode=%d) received from " PRI_S_SRP, SERIAL(dso), dns_opcode_get(&message->wire), 3804 dso->remote_name); 3805 break; 3806 case kDSOEventType_DSOMessage: 3807 INFO("[DSO%d] DSO Message (Primary TLV=%d) received from " PRI_S_SRP, 3808 SERIAL(dso), dso->primary.opcode, dso->remote_name); 3809 message = event_context; 3810 dso_message((dp_tracker_t *)context, message, dso); 3811 break; 3812 case kDSOEventType_DSOResponse: 3813 INFO("[DSO%d] DSO Response (Primary TLV=%d) received from " PRI_S_SRP, 3814 SERIAL(dso), dso->primary.opcode, dso->remote_name); 3815 break; 3816 3817 case kDSOEventType_Finalize: 3818 INFO("[DSO%d] Finalize", SERIAL(dso)); 3819 break; 3820 3821 case kDSOEventType_Connected: 3822 INFO("[DSO%d] Connected to " PRI_S_SRP, SERIAL(dso), dso->remote_name); 3823 break; 3824 3825 case kDSOEventType_ConnectFailed: 3826 INFO("[DSO%d] Connection to " PRI_S_SRP " failed", SERIAL(dso), dso->remote_name); 3827 break; 3828 3829 case kDSOEventType_Disconnected: 3830 INFO("[DSO%d] Connection to " PRI_S_SRP " disconnected", SERIAL(dso), dso->remote_name); 3831 break; 3832 case kDSOEventType_ShouldReconnect: 3833 INFO("[DSO%d] Connection to " PRI_S_SRP " should reconnect (not for a server)", SERIAL(dso), dso->remote_name); 3834 break; 3835 case kDSOEventType_Inactive: 3836 INFO("[DSO%d] Inactivity timer went off, closing connection.", SERIAL(dso)); 3837 break; 3838 case kDSOEventType_Keepalive: 3839 INFO("[DSO%d] should send a keepalive now.", SERIAL(dso)); 3840 break; 3841 case kDSOEventType_KeepaliveRcvd: 3842 keepalive_context = event_context; 3843 keepalive_context->send_response = false; 3844 INFO("[DSO%d] " PRI_S_SRP ": keepalive received, xid %04x.", SERIAL(dso), dso->transport->name, keepalive_context->xid); 3845 3846 // If we are the server, we have to send a response to the keepalive. 3847 if (dso->is_server) { 3848 dp_keepalive_response_send(keepalive_context, dso); 3849 } 3850 break; 3851 case kDSOEventType_RetryDelay: 3852 INFO("[DSO%d] keepalive received.", SERIAL(dso)); 3853 break; 3854 } 3855 } 3856 3857 static bool 3858 dp_dns_query(dp_tracker_t *tracker, message_t *message, dns_rr_t *question, int num_questions) 3859 { 3860 int rcode; 3861 3862 // Limit outstanding queries if we don't have shared connection support 3863 if (dp_num_outstanding_queries >= 256) { 3864 num_queries_dropped_for_load++; 3865 dso_simple_response(tracker->connection, message, &message->wire, dns_rcode_servfail); 3866 ERROR("[TRK%d][QID %x] dropping query because there are too many", SERIAL(tracker), ntohs(message->wire.id)); 3867 return false; 3868 } 3869 3870 3871 dnssd_query_t *query = dp_query_create(tracker, question, message, NULL, &rcode); 3872 const char *failnote = NULL; 3873 if (!query) { 3874 ERROR("[TRK%d][QID %x] query create failed", SERIAL(tracker), ntohs(message->wire.id)); 3875 dso_simple_response(tracker->connection, message, &message->wire, rcode); 3876 return false; 3877 } 3878 query->num_questions = num_questions; 3879 3880 dns_rcode_set(query->response, dns_rcode_noerror); 3881 3882 // For DNS queries, we need to return the question. 3883 query->response->qdcount = htons(1); 3884 if (query->question->served_domain != NULL) { 3885 TOWIRE_CHECK("name", &query->towire, dns_name_to_wire(NULL, &query->towire, query->question->name)); 3886 TOWIRE_CHECK("enclosing_domain", &query->towire, 3887 dns_full_name_to_wire(&query->enclosing_domain_pointer, 3888 &query->towire, query->question->served_domain->domain)); 3889 } else { 3890 TOWIRE_CHECK("full name", &query->towire, dns_full_name_to_wire(NULL, &query->towire, query->question->name)); 3891 } 3892 TOWIRE_CHECK("TYPE", &query->towire, dns_u16_to_wire(&query->towire, question->type)); // TYPE 3893 TOWIRE_CHECK("CLASS", &query->towire, dns_u16_to_wire(&query->towire, question->qclass)); // CLASS 3894 if (failnote != NULL) { 3895 ERROR("[TRK%d][QID %x] failure encoding question: " PUB_S_SRP, SERIAL(tracker), ntohs(message->wire.id), failnote); 3896 goto fail; 3897 } 3898 3899 // Set message ID. 3900 query->towire.message->id = message->wire.id; 3901 3902 // We should check for OPT RR, but for now assume it's there. 3903 query->is_edns0 = true; 3904 3905 bool dns64 = false; 3906 #if SRP_FEATURE_NAT64 3907 if (srp_servers->srp_nat64_enabled) { 3908 dns64 = nat64_is_active(); 3909 } 3910 #endif 3911 dp_query_track(tracker, query); 3912 bool hardwired = false; 3913 if (dp_query_start(query, &rcode, &hardwired, dns64)) { 3914 // If query->question isn't NULL, we need to reply from cache 3915 if (hardwired) { 3916 INFO("[Q%d][TRK%d] hardwired reply", SERIAL(query), SERIAL(tracker)); 3917 dp_query_send_dns_response(query, "hardwired"); 3918 dp_question_cache_remove_queries(query->question); 3919 RELEASE_HERE(query->question, question); 3920 query->question = NULL; 3921 } else if (query->question != NULL) { 3922 INFO("[Q%d][TRK%d] replying from cache", SERIAL(query), SERIAL(tracker)); 3923 dp_query_reply_from_cache(query->question, query, false); 3924 dp_question_cache_remove_queries(query->question); 3925 } else { 3926 INFO("[Q%d][TRK%d] not replying from cache", SERIAL(query), SERIAL(tracker)); 3927 } 3928 } else { 3929 INFO("[Q%d][TRK%d][QID %x] query start failed", SERIAL(query), SERIAL(tracker), ntohs(message->wire.id)); 3930 fail: 3931 dso_simple_response(tracker->connection, message, &message->wire, rcode); 3932 query->satisfied = true; 3933 dp_question_cache_remove_queries(query->question); 3934 dnssd_query_cancel(query); 3935 RELEASE_HERE(query, dnssd_query); 3936 return false; 3937 } 3938 // Query is returned retained, and dp_query_track retains it, so we always need to release the reference here. 3939 RELEASE_HERE(query, dnssd_query); 3940 return true; 3941 } 3942 3943 static void 3944 dp_tracker_dso_cleanup(void *UNUSED context) 3945 { 3946 dso_cleanup(false); 3947 } 3948 3949 static bool 3950 dp_tracker_dso_state_change(const dso_life_cycle_t cycle, void *const context, dso_state_t *const dso) 3951 { 3952 if (cycle == dso_life_cycle_cancel) { 3953 dp_tracker_t *tracker = context; 3954 if (tracker->dso != NULL) { 3955 tracker->dso = NULL; 3956 if (tracker->connection != NULL) { 3957 tracker->connection->dso = NULL; 3958 ioloop_comm_cancel(tracker->connection); 3959 } 3960 for (dnssd_query_t *query = tracker->dns_queries; query != NULL; query = query->next) { 3961 if (query->dso == dso) { 3962 query->dso = NULL; 3963 } 3964 if (query->activity != NULL) { 3965 query->activity = NULL; 3966 // Release the activity's reference to the query. 3967 RELEASE_HERE(query, dnssd_query); 3968 } 3969 } 3970 } 3971 ioloop_run_async(dp_tracker_dso_cleanup, NULL); 3972 return true; 3973 } 3974 return false; 3975 } 3976 3977 static void 3978 dnssd_proxy_dns_evaluate(comm_t *comm, message_t *message, dp_tracker_t *tracker) 3979 { 3980 dns_rr_t question; 3981 unsigned offset = 0; 3982 3983 if (tracker == NULL) { 3984 tracker = calloc(1, sizeof(*tracker)); 3985 if (tracker == NULL) { 3986 ERROR("[C%d] " PRI_S_SRP ": no memory for a connection tracker object!", SERIAL(comm), comm->name); 3987 goto fail; 3988 } 3989 tracker->connection = comm; 3990 tracker->serial = ++cur_tracker_serial; 3991 ioloop_comm_retain(tracker->connection); 3992 if (comm->tcp_stream) { 3993 ioloop_comm_context_set(comm, tracker, dp_tracker_context_release); 3994 RETAIN_HERE(tracker, dp_tracker); // connection has a reference. 3995 } 3996 if (!comm->is_listener) { 3997 ioloop_comm_disconnect_callback_set(comm, dp_tracker_disconnected); 3998 } 3999 } 4000 RETAIN_HERE(tracker, dp_tracker); // For the function. 4001 4002 // Drop incoming responses--we're a server, so we only accept queries. 4003 if (dns_qr_get(&message->wire) == dns_qr_response) { 4004 INFO("[TRK%d][C%d] " PRI_S_SRP ": dropping unexpected response", SERIAL(tracker), SERIAL(comm), comm->name); 4005 goto fail; 4006 } 4007 4008 // If this is a DSO message, see if we have a session yet. 4009 switch(dns_opcode_get(&message->wire)) { 4010 case dns_opcode_dso: 4011 if (!comm->tcp_stream) { 4012 ERROR("[TRK%d][C%d] " PRI_S_SRP ": DSO message received on non-tcp socket.", SERIAL(tracker), SERIAL(comm), comm->name); 4013 dso_simple_response(comm, message, &message->wire, dns_rcode_notimp); 4014 goto fail; 4015 } 4016 4017 if (!tracker->dso) { 4018 tracker->dso = dso_state_create(true, 2, comm->name, dns_push_callback, tracker, 4019 dp_tracker_dso_state_change, comm); 4020 if (!tracker->dso) { 4021 ERROR("[TRK%d][C%d] " PRI_S_SRP ": Unable to create a dso context.", SERIAL(tracker), SERIAL(comm), comm->name); 4022 dso_simple_response(comm, message, &message->wire, dns_rcode_servfail); 4023 goto fail; 4024 } 4025 comm->dso = tracker->dso; 4026 } 4027 dp_tracker_not_idle(tracker); 4028 dso_message_received(comm->dso, (uint8_t *)&message->wire, message->length, message); 4029 break; 4030 4031 case dns_opcode_query: { 4032 int num_questions = ntohs(message->wire.qdcount); 4033 4034 // Some Matter accessories will send queries with more than one question, and if we don't answer these 4035 // queries, automations fail. So even though this is a bit weird, we need to answer the queries. 4036 for (int i = 0; i < num_questions; i++) { 4037 memset(&question, 0, sizeof(question)); 4038 if (!dns_rr_parse(&question, message->wire.data, message->length - DNS_HEADER_SIZE, &offset, false, false)) { 4039 ERROR("[TRK%d][C%d] " PRI_S_SRP ": rr parse failed.", SERIAL(tracker), SERIAL(comm), comm->name); 4040 dso_simple_response(comm, message, &message->wire, dns_rcode_formerr); 4041 goto fail; 4042 } 4043 bool success = dp_dns_query(tracker, message, &question, num_questions); 4044 dns_rrdata_free(&question); 4045 dns_name_free(question.name); 4046 if (!success) { 4047 dnssd_query_t *next = NULL, *match = NULL; 4048 for (dnssd_query_t *query = tracker->dns_queries; query != NULL; query = next) { 4049 next = query->next; 4050 if (dp_same_message(query->message, message)) { 4051 query->satisfied = true; 4052 dp_question_cache_remove_queries(query->question); 4053 if (match == NULL) { 4054 match = query; 4055 RETAIN_HERE(match, dnssd_query); 4056 } 4057 } 4058 } 4059 if (match != NULL) { 4060 dnssd_query_cancel(match); 4061 RELEASE_HERE(match, dnssd_query); 4062 } 4063 goto out; 4064 } 4065 } 4066 dp_tracker_not_idle(tracker); 4067 break; 4068 } 4069 // No support for other opcodes yet. 4070 default: 4071 dso_simple_response(comm, message, &message->wire, dns_rcode_notimp); 4072 break; 4073 } 4074 goto out; 4075 fail: 4076 // For connected connections, if we exit unexpectedly, we need to cancel the connection. 4077 if (comm->tcp_stream) { 4078 ioloop_comm_cancel(tracker->connection); 4079 } 4080 out: 4081 if (tracker != NULL) { 4082 RELEASE_HERE(tracker, dp_tracker); // For the function. 4083 } 4084 } 4085 4086 void 4087 dns_proxy_input_for_server(comm_t *comm, srp_server_t *server_state, message_t *message, void *context) 4088 { 4089 char buf[INET6_ADDRSTRLEN]; 4090 const char *remote_name = buf; 4091 if (comm->tcp_stream) { 4092 remote_name = comm->name; 4093 } else { 4094 IOLOOP_NTOP(&message->src, buf); 4095 } 4096 dp_tracker_t *tracker = comm->context; 4097 INFO("[C%d][TRK%d][QID %x] Received a new DNS message - src: " PRI_S_SRP ", message length: %u bytes.", 4098 SERIAL(comm), SERIAL(tracker), ntohs(message->wire.id), remote_name, message->length); 4099 4100 4101 dnssd_proxy_dns_evaluate(comm, message, context); 4102 } 4103 4104 static void 4105 dns_proxy_input(comm_t *comm, message_t *message, void *context) 4106 { 4107 dns_proxy_input_for_server(comm, srp_servers, message, context); 4108 } 4109 4110 // usage is only called when we are building standalone dnssd-proxy, not the combined one. 4111 #if (!SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY) 4112 static int 4113 usage(const char *progname) 4114 { 4115 ERROR("usage: %s", progname); 4116 ERROR("ex: dnssd-proxy"); 4117 return 1; 4118 } 4119 #endif // #if (!SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY) 4120 4121 // Called whenever we get a connection. 4122 static void UNUSED 4123 connected(comm_t *comm) 4124 { 4125 INFO("[C%d] connection from " PRI_S_SRP, SERIAL(comm), comm->name); 4126 return; 4127 } 4128 4129 static served_domain_t *NULLABLE 4130 new_served_domain(dp_interface_t *const NULLABLE interface, const char *const NONNULL domain) 4131 { 4132 served_domain_t *sdt = calloc(1, sizeof *sdt); 4133 if (sdt == NULL) { 4134 ERROR("Unable to allocate served domain %s", domain); 4135 return NULL; 4136 } 4137 size_t domain_len = strlen(domain); 4138 sdt->domain_ld = malloc(domain_len + 2); 4139 if (sdt->domain_ld == NULL) { 4140 ERROR("Unable to allocate served domain name %s", domain); 4141 free(sdt); 4142 return NULL; 4143 } 4144 sdt->domain_ld[0] = '.'; 4145 sdt->domain = sdt->domain_ld + 1; 4146 memcpy(sdt->domain, domain, domain_len + 1); 4147 sdt->domain_name = dns_pres_name_parse(sdt->domain); 4148 sdt->interface = interface; 4149 if (sdt->domain_name == NULL) { 4150 if (interface != NULL) { 4151 ERROR("invalid domain name for interface %s: %s", interface->name, sdt->domain); 4152 } else { 4153 ERROR("invalid domain name: %s", sdt->domain); 4154 } 4155 free(sdt); 4156 return NULL; 4157 } 4158 sdt->next = served_domains; 4159 served_domains = sdt; 4160 4161 INFO("new served domain created - domain name: " PRI_S_SRP, sdt->domain); 4162 return sdt; 4163 } 4164 4165 #if STUB_ROUTER 4166 static served_domain_t *NULLABLE 4167 find_served_domain(const char *const NONNULL domain) 4168 { 4169 served_domain_t *current; 4170 for (current = served_domains; current != NULL; current = current->next) { 4171 if (strcasecmp(domain, current->domain) == 0) { 4172 break; 4173 } 4174 } 4175 4176 return current; 4177 } 4178 #endif 4179 4180 // served domain can only go away when combined with srp-mdns-proxy and interface going up and down. 4181 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 4182 static void 4183 served_domain_free(served_domain_t *const served_domain) 4184 { 4185 INFO("served domain removed - domain name: " PRI_S_SRP, served_domain->domain); 4186 4187 // free struct interface *NULLABLE interface 4188 if (served_domain->interface != NULL) { 4189 interface_addr_t *current = served_domain->interface->addresses; 4190 interface_addr_t *next; 4191 for (;current != NULL; current = next) { 4192 next = current->next; 4193 free(current); 4194 } 4195 if (served_domain->interface->name != NULL) { 4196 free(served_domain->interface->name); 4197 } 4198 free(served_domain->interface); 4199 } 4200 4201 // free hardwired_t *NULLABLE hardwired_responses 4202 if (served_domain->hardwired_responses != NULL) { 4203 hardwired_t *current = served_domain->hardwired_responses; 4204 hardwired_t *next; 4205 for (; current != NULL; current = next) { 4206 next = current->next; 4207 free(current); 4208 } 4209 } 4210 4211 // free dns_name_t *NONNULL domain_name; 4212 if (served_domain->domain_name != NULL) { 4213 dns_name_free(served_domain->domain_name); 4214 } 4215 4216 // free char *NONNULL domain_ld; 4217 free(served_domain->domain_ld); 4218 4219 #ifdef SRP_TEST_SERVER 4220 last_freed_domain = served_domain; 4221 #endif 4222 4223 // free served_domain_t * 4224 free(served_domain); 4225 } 4226 4227 static void 4228 delete_served_domain(served_domain_t *const served_domain) 4229 { 4230 if (served_domain->questions == NULL) { 4231 served_domain_free(served_domain); 4232 } 4233 } 4234 4235 #if STUB_ROUTER 4236 served_domain_t * 4237 delete_served_domain_by_interface_name(const char *const NONNULL interface_name) 4238 { 4239 served_domain_t *current; 4240 served_domain_t *prev = NULL; 4241 for(current= served_domains; current != NULL; prev = current, current = current->next) { 4242 if (current->interface == NULL) { 4243 continue; 4244 } 4245 if (strcmp(interface_name, current->interface->name) != 0) { 4246 continue; 4247 } 4248 4249 INFO("served domain deleted with interface - " 4250 "domain: " PRI_S_SRP ", interface name: " PUB_S_SRP, current->domain, interface_name); 4251 4252 // Since we are removing the entire served domain and the interface, the addresses that are associated with 4253 // this interface will also be removed. Therefore, any hardwired response that contains these addresses should 4254 // also be removed. 4255 for (interface_addr_t *address = current->interface->addresses; address != NULL; address = address->next) { 4256 dnssd_hardwired_process_addr_change(&address->addr, &address->mask, false); 4257 } 4258 4259 if (prev == NULL) { 4260 served_domains = current->next; 4261 } else { 4262 prev->next = current->next; 4263 } 4264 4265 delete_served_domain(current); 4266 break; 4267 } 4268 4269 return current; 4270 } 4271 #endif // STUB_ROUTER 4272 #endif // SRP_FEATURE_DYNAMIC_CONFIGURATION 4273 4274 // Dynamic interface detection... 4275 // This is called whenever a new interface address is encountered. 4276 4277 void 4278 dnssd_proxy_ifaddr_callback(srp_server_t *UNUSED server_state, void *UNUSED context, const char *name, 4279 const addr_t *address, const addr_t *mask, uint32_t UNUSED flags, 4280 enum interface_address_change event_type) 4281 { 4282 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 4283 bool is_new_interface = true; 4284 #endif 4285 bool succeeded; 4286 const char *const action = (event_type == interface_address_added ? "Adding" : "Removing"); 4287 4288 if (event_type == interface_address_unchanged) { 4289 goto exit; 4290 } 4291 4292 int interface_index = if_nametoindex(name); 4293 if (address->sa.sa_family == AF_INET) { 4294 IPv4_ADDR_GEN_SRP((const uint8_t *)&address->sin.sin_addr, addr_buf); 4295 IPv4_ADDR_GEN_SRP((const uint8_t *)&mask->sin.sin_addr, mask_buf); 4296 INFO("Interface " PUB_S_SRP " address " PRI_IPv4_ADDR_SRP " mask " PRI_IPv4_ADDR_SRP " index %d " PUB_S_SRP, 4297 name, IPv4_ADDR_PARAM_SRP((const uint8_t *)&address->sin.sin_addr, addr_buf), 4298 IPv4_ADDR_PARAM_SRP((const uint8_t *)&mask->sin.sin_addr, mask_buf), interface_index, 4299 event_type == interface_address_added ? "added" : "removed"); 4300 } else if (address->sa.sa_family == AF_INET6) { 4301 IPv6_ADDR_GEN_SRP((const uint8_t *)&address->sin6.sin6_addr, addr_buf); 4302 IPv6_ADDR_GEN_SRP((const uint8_t *)&mask->sin6.sin6_addr, mask_buf); 4303 INFO("Interface " PUB_S_SRP " address " PRI_IPv6_ADDR_SRP " mask " PRI_IPv6_ADDR_SRP " index %d " PUB_S_SRP, 4304 name, IPv6_ADDR_PARAM_SRP((const uint8_t *)&address->sin6.sin6_addr, addr_buf), 4305 IPv6_ADDR_PARAM_SRP((const uint8_t *)&mask->sin6.sin6_addr, mask_buf), interface_index, 4306 event_type == interface_address_added ? "added" : "removed"); 4307 } else { 4308 INFO("Interface " PUB_S_SRP " address type %d index %d " PUB_S_SRP, name, address->sa.sa_family, interface_index, 4309 event_type == interface_address_added ? "added" : "removed"); 4310 INFO("ignoring non IP address"); 4311 goto exit; 4312 } 4313 4314 #if THREAD_BORDER_ROUTER && SRP_FEATURE_COMBINED_DNSSD_PROXY 4315 // Ignore Thread interface 4316 bool is_valid_address = thread_interface_name == NULL || strcmp(thread_interface_name, name) != 0; 4317 if (!is_valid_address) { 4318 INFO("skipping thread interface address"); 4319 goto exit; 4320 } 4321 #endif 4322 4323 // Add/remove the address from the corresponding served domain. 4324 served_domain_t **sp = &served_domains; 4325 while (*sp != NULL) { 4326 served_domain_t *current = *sp; 4327 // Only change the served domain that owns the current interface and address. 4328 if (current->interface == NULL || current->interface->ifindex == 0 || 4329 strcmp(current->interface->name, name) != 0) { 4330 goto again; 4331 } 4332 4333 INFO(PUB_S_SRP " address from the served domain - domain: " PRI_S_SRP, action, current->domain); 4334 succeeded = interface_process_addr_change(current->interface, address, mask, event_type); 4335 require_action_quiet(succeeded, exit, ERROR("failed to " PUB_S_SRP " new interface address", action)); 4336 4337 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 4338 is_new_interface = false; 4339 4340 // if interface loses all usable IP addresses, the interface has gone, remove this interface and the 4341 // corresponding served domain. 4342 if (event_type == interface_address_deleted) { 4343 if (current->interface->addresses == NULL) { 4344 INFO("Removing served domain with 0 address - domain: " PRI_S_SRP ", interface name: " PUB_S_SRP, 4345 current->domain, current->interface->name); 4346 *sp = current->next; 4347 delete_served_domain(current); 4348 continue; 4349 } 4350 } 4351 #else // SRP_FEATURE_DYNAMIC_CONFIGURATION 4352 if (current->interface->addresses == NULL) { 4353 current->interface->ifindex = 0; 4354 } 4355 #endif // SRP_FEATURE_DYNAMIC_CONFIGURATION 4356 again: 4357 sp = &(*sp)->next; 4358 } 4359 4360 // We will only create new served domain from dnssd_proxy_ifaddr_callback if the callback gets called from 4361 // srp-mdns-proxy. 4362 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 4363 if (event_type == interface_address_added && is_new_interface) { 4364 served_domain_t *const new_served_domain = add_new_served_domain_with_interface(name, address, mask); 4365 require_action_quiet(new_served_domain != NULL, exit, 4366 ERROR("failed to add new served domain ""- interface name: " PUB_S_SRP, name)); 4367 4368 bool hardwired_set = dnssd_hardwired_setup_for_served_domain(new_served_domain); 4369 if (!hardwired_set) { 4370 ERROR("failed to setup hardwired response for newly created served domain - domain: " PRI_S_SRP, name); 4371 delete_served_domain(new_served_domain); 4372 } 4373 INFO("New served domain created and hardwired response created - domain: " PRI_S_SRP, 4374 new_served_domain->domain); 4375 } 4376 #endif // SRP_FEATURE_DYNAMIC_CONFIGURATION 4377 4378 #if STUB_ROUTER 4379 // Added or removed address will possibly need hardwired response to be updated. 4380 dnssd_hardwired_process_addr_change(address, mask, event_type == interface_address_added); 4381 #endif 4382 4383 exit: 4384 return; 4385 } 4386 4387 #if !SRP_FEATURE_DYNAMIC_CONFIGURATION 4388 // Config file parsing... 4389 static bool 4390 interface_handler(void * UNUSED context, const char * UNUSED filename, char **hunks, int UNUSED num_hunks, 4391 int UNUSED lineno) 4392 { 4393 dp_interface_t *interface = calloc(1, sizeof *interface); 4394 if (interface == NULL) { 4395 ERROR("Unable to allocate interface %s", hunks[1]); 4396 return false; 4397 } 4398 4399 interface->name = strdup(hunks[1]); 4400 if (interface->name == NULL) { 4401 ERROR("Unable to allocate interface name %s", hunks[1]); 4402 free(interface); 4403 return false; 4404 } 4405 4406 if (!strcmp(hunks[0], "nopush")) { 4407 interface->no_push = true; 4408 } 4409 4410 if (new_served_domain(interface, hunks[2]) == NULL) { 4411 free(interface->name); 4412 free(interface); 4413 return false; 4414 } 4415 return true; 4416 } 4417 4418 static bool 4419 port_handler(void * UNUSED context, const char * UNUSED filename, char **hunks, int UNUSED num_hunks, int UNUSED lineno) 4420 { 4421 char *ep = NULL; 4422 long port = strtol(hunks[1], &ep, 10); 4423 if (port < 0 || port > 65535 || *ep != 0) { 4424 ERROR("Invalid port number: %s", hunks[1]); 4425 return false; 4426 } 4427 if (!strcmp(hunks[0], "udp-port")) { 4428 dnssd_proxy_udp_port = port; 4429 } else if (!strcmp(hunks[0], "tcp-port")) { 4430 dnssd_proxy_tcp_port = port; 4431 } else if (!strcmp(hunks[0], "tls-port")) { 4432 dnssd_proxy_tls_port = port; 4433 } 4434 return true; 4435 } 4436 4437 static bool 4438 config_string_handler(char **ret, const char * UNUSED filename, const char *string, int UNUSED lineno, bool tdot, 4439 bool ldot) 4440 { 4441 char *s; 4442 int add_trailing_dot = 0; 4443 int add_leading_dot = ldot ? 1 : 0; 4444 size_t len = strlen(string); 4445 4446 // Space for NUL and leading dot. 4447 if (tdot && len > 0 && string[len - 1] != '.') { 4448 add_trailing_dot = 1; 4449 } 4450 s = malloc(strlen(string) + add_leading_dot + add_trailing_dot + 1); 4451 if (s == NULL) { 4452 ERROR("Unable to allocate domain name %s", string); 4453 return false; 4454 } 4455 *ret = s; 4456 if (ldot) { 4457 *s++ = '.'; 4458 } 4459 memcpy(s, string, len + add_leading_dot + add_trailing_dot + 1); 4460 if (add_trailing_dot) { 4461 s[len] = '.'; 4462 s[len + 1] = 0; 4463 } 4464 return true; 4465 } 4466 4467 static bool 4468 my_name_handler(void * UNUSED context, const char *filename, char **hunks, int UNUSED num_hunks, int lineno) 4469 { 4470 static char *new_name = NULL; 4471 if (new_name != NULL) { 4472 free(new_name); 4473 my_name = NULL; 4474 new_name = NULL; 4475 } 4476 if (!config_string_handler(&new_name, filename, hunks[1], lineno, false, false)) { 4477 return false; 4478 } 4479 my_name = new_name; 4480 size_t len = strlen(my_name); 4481 size_t bigger = sizeof(DOT_HOME_NET_DOMAIN) > sizeof(DOT_LOCAL) ? sizeof(DOT_HOME_NET_DOMAIN) : sizeof(DOT_LOCAL); 4482 if (len >= sizeof(local_host_name) - bigger) { 4483 ERROR("truncating local hostname %s", my_name); 4484 return false; 4485 } 4486 4487 // Set up existing local host name with .local. suffix 4488 snprintf(local_host_name_dot_local, sizeof(local_host_name_dot_local), "%s" DOT_LOCAL, my_name); 4489 4490 // Set up existing local host name with .home.net. suffix 4491 snprintf(local_host_name, sizeof(local_host_name), "%s" DOT_HOME_NET_DOMAIN, my_name); 4492 return true; 4493 } 4494 4495 static bool 4496 listen_addr_handler(void * UNUSED context, const char *filename, char **hunks, int UNUSED num_hunks, int lineno) 4497 { 4498 if (num_listen_addrs == MAX_ADDRS) { 4499 ERROR("Only %d IPv4 listen addresses can be configured.", MAX_ADDRS); 4500 return false; 4501 } 4502 return config_string_handler(&listen_addrs[num_listen_addrs++], filename, hunks[1], lineno, false, false); 4503 } 4504 4505 static bool 4506 publish_addr_handler(void * UNUSED context, const char *filename, char **hunks, int UNUSED num_hunks, int lineno) 4507 { 4508 if (num_publish_addrs == MAX_ADDRS) { 4509 ERROR("Only %d addresses can be published.", MAX_ADDRS); 4510 return false; 4511 } 4512 return config_string_handler(&publish_addrs[num_publish_addrs++], filename, hunks[1], lineno, false, false); 4513 } 4514 4515 static bool 4516 tls_key_handler(void * UNUSED context, const char *filename, char **hunks, int UNUSED num_hunks, int lineno) 4517 { 4518 return config_string_handler(&tls_key_filename, filename, hunks[1], lineno, false, false); 4519 } 4520 4521 static bool 4522 tls_cert_handler(void * UNUSED context, const char *filename, char **hunks, int UNUSED num_hunks, int lineno) 4523 { 4524 return config_string_handler(&tls_cert_filename, filename, hunks[1], lineno, false, false); 4525 } 4526 4527 static bool 4528 tls_cacert_handler(void * UNUSED context, const char *filename, char **hunks, int UNUSED num_hunks, int lineno) 4529 { 4530 return config_string_handler(&tls_cacert_filename, filename, hunks[1], lineno, false, false); 4531 } 4532 4533 config_file_verb_t dp_verbs[] = { 4534 { "interface", 3, 3, interface_handler }, // interface <name> <domain> 4535 { "nopush", 3, 3, interface_handler }, // nopush <name> <domain> 4536 { "udp-port", 2, 2, port_handler }, // udp-port <number> 4537 { "tcp-port", 2, 2, port_handler }, // tcp-port <number> 4538 { "tls-port", 2, 2, port_handler }, // tls-port <number> 4539 { "my-name", 2, 2, my_name_handler }, // my-name <domain name> 4540 { "tls-key", 2, 2, tls_key_handler }, // tls-key <filename> 4541 { "tls-cert", 2, 2, tls_cert_handler }, // tls-cert <filename> 4542 { "tls-cacert", 2, 2, tls_cacert_handler }, // tls-cacert <filename> 4543 { "listen-addr", 2, 2, listen_addr_handler }, // listen-addr <IP address> 4544 { "publish-addr", 2, 2, publish_addr_handler } // publish-addr <IP address> 4545 }; 4546 #define NUMCFVERBS ((sizeof dp_verbs) / sizeof (config_file_verb_t)) 4547 #endif // !SRP_FEATURE_DYNAMIC_CONFIGURATION 4548 4549 static wakeup_t *tls_listener_wakeup; 4550 static int tls_listener_index; 4551 static void dnssd_tls_listener_restart(comm_t *NONNULL listener, void *NULLABLE context); 4552 4553 static void dnssd_tls_key_change_notification_send(void) 4554 { 4555 static int dnssd_tls_change_notification_token = NOTIFY_TOKEN_INVALID; 4556 4557 if (dnssd_tls_change_notification_token == NOTIFY_TOKEN_INVALID) { 4558 uint32_t notifyStatus = notify_register_check(kDNSSDAdvertisingProxyTLSKeyUpdateNotification, 4559 &dnssd_tls_change_notification_token); 4560 if (notifyStatus != NOTIFY_STATUS_OK) { 4561 dnssd_tls_change_notification_token = NOTIFY_TOKEN_INVALID; 4562 ERROR("notify_register_check(%s) failed with %u", kDNSSDAdvertisingProxyTLSKeyUpdateNotification, notifyStatus); 4563 return; 4564 } 4565 } 4566 4567 if (dnssd_tls_change_notification_token != NOTIFY_TOKEN_INVALID) { 4568 uint32_t notifyStatus = notify_post(kDNSSDAdvertisingProxyTLSKeyUpdateNotification); 4569 if (notifyStatus != NOTIFY_STATUS_OK) { 4570 ERROR("notify_post(%s) %u", kDNSSDAdvertisingProxyTLSKeyUpdateNotification, notifyStatus); 4571 notify_cancel(dnssd_tls_change_notification_token); 4572 dnssd_tls_change_notification_token = NOTIFY_TOKEN_INVALID; 4573 } 4574 } 4575 } 4576 4577 static void 4578 dnssd_tls_listener_ready(void *UNUSED context, uint16_t port) 4579 { 4580 #ifdef SRP_TEST_SERVER 4581 if (srp_test_dnssd_tls_listener_ready != NULL) { 4582 srp_test_dnssd_tls_listener_ready(srp_test_tls_listener_context, port); 4583 } 4584 #else 4585 (void)context; 4586 (void)port; 4587 #endif 4588 } 4589 4590 static void dnssd_tls_listener_listen(void *context, bool init_daemon) 4591 { 4592 addr_t addr; 4593 INFO("starting DoT listener"); 4594 memset(&addr, 0, sizeof(addr)); 4595 addr.sa.sa_family = AF_UNSPEC; 4596 #ifndef NOT_HAVE_SA_LEN 4597 addr.sa.sa_len = sizeof(addr.sin6); 4598 #endif 4599 addr.sin6.sin6_port = htons(dnssd_proxy_tls_port); 4600 #ifndef EXCLUDE_TLS 4601 dnssd_proxy_listeners[tls_listener_index] = 4602 ioloop_listener_create(true, true, init_daemon, NULL, 0, &addr, NULL, "DNS over TLS", 4603 dns_proxy_input, NULL, dnssd_tls_listener_restart, dnssd_tls_listener_ready, 4604 NULL, srp_tls_configure, 0, context); 4605 #else 4606 dnssd_proxy_listeners[tls_listener_index] = 4607 ioloop_listener_create(true, true, init_daemon, NULL, 0, &addr, NULL, "DNS over TLS", 4608 dns_proxy_input, NULL, dnssd_tls_listener_restart, dnssd_tls_listener_ready, 4609 NULL, NULL, 0, context); 4610 #endif 4611 if (dnssd_proxy_listeners[tls_listener_index] == NULL) { 4612 ERROR("DNS Push listener: fail."); 4613 goto exit; 4614 } 4615 4616 // Notify about intial key update 4617 dnssd_tls_key_change_notification_send(); 4618 4619 // Schedule a wake up timer to rotate the expired TLS certificate. 4620 schedule_tls_certificate_rotation(&tls_listener_wakeup, dnssd_proxy_listeners[tls_listener_index]); 4621 exit: 4622 return; 4623 } 4624 4625 static void dnssd_tls_listener_relisten(void *context) 4626 { 4627 dnssd_tls_listener_listen(context, false); 4628 } 4629 4630 static void 4631 dnssd_tls_listener_restart(comm_t *UNUSED in_listener, void *context) 4632 { 4633 const bool doing_rotation = dnssd_proxy_listeners[tls_listener_index]->tls_rotation_ready; 4634 ioloop_listener_release(dnssd_proxy_listeners[tls_listener_index]); 4635 dnssd_proxy_listeners[tls_listener_index] = NULL; 4636 4637 if (doing_rotation) { 4638 const bool succeeded = srp_tls_init(); 4639 if (!succeeded) { 4640 FAULT("srp_tls_init failed."); 4641 return; 4642 } 4643 4644 // Send TLS key update notification 4645 dnssd_tls_key_change_notification_send(); 4646 dnssd_tls_listener_listen(NULL, false); 4647 } else { 4648 INFO("Creation of TLS listener failed; reattempting in 10s."); 4649 4650 if (tls_listener_wakeup == NULL) { 4651 tls_listener_wakeup = ioloop_wakeup_create(); 4652 if (tls_listener_wakeup == NULL) { 4653 ERROR("Unable to allocate wakeup in order to re-attempt TLS listener creation."); 4654 return; 4655 } 4656 } 4657 ioloop_add_wake_event(tls_listener_wakeup, context, dnssd_tls_listener_relisten, NULL, 10 * MSEC_PER_SEC); 4658 } 4659 } 4660 4661 static void 4662 dnssd_push_setup(void) 4663 { 4664 tls_listener_index = dnssd_proxy_num_listeners++; 4665 dnssd_tls_listener_listen(NULL, true); 4666 4667 // Only set hardwired response when dynamic configuration is enabled. Dynamic configuration 4668 // sets up hardwired response when new address of the interface is added. 4669 #if SRP_FEATURE_DYNAMIC_CONFIGURATION // not set hardwired response for now 4670 dnssd_hardwired_push_setup(); 4671 #endif // !SRP_FEATURE_DYNAMIC_CONFIGURATION 4672 } 4673 4674 #if (!SRP_FEATURE_CAN_GENERATE_TLS_CERT) 4675 4676 // Start a key generation or cert signing program. Arguments are key=value pairs. 4677 // Arguments that can be constant should be <"key=value", NULL>. Arguments that 4678 // have a variable component should be <"key", value">. References to arguments 4679 // will be held, except that if the rhs of the pair is variable, memory is allocated 4680 // to store the key=value pair, so the neither the key nor the value is retained. 4681 // The callback is called when the program exits. 4682 4683 static void 4684 keyprogram_start(const char *program, subproc_callback_t callback, ...) 4685 { 4686 #define MAX_SUBPROC_VARS 3 4687 size_t lens[MAX_SUBPROC_VARS]; 4688 char *vars[MAX_SUBPROC_VARS]; 4689 int num_vars = 0; 4690 char *argv[MAX_SUBPROC_ARGS + 1]; 4691 int argc = 0; 4692 va_list vl; 4693 int i; 4694 subproc_t *subproc = NULL; 4695 4696 va_start(vl, callback); 4697 while (true) { 4698 char *vname, *value; 4699 char *arg; 4700 4701 vname = va_arg(vl, char *); 4702 if (vname == NULL) { 4703 break; 4704 } 4705 value = va_arg(vl, char *); 4706 4707 if (argc >= MAX_SUBPROC_ARGS) { 4708 ERROR("keyprogram_start: too many arguments."); 4709 } 4710 4711 if (value == NULL) { 4712 arg = vname; 4713 } else { 4714 if (num_vars >= MAX_SUBPROC_VARS) { 4715 ERROR("Too many variable args: %s %s", vname, value); 4716 goto out; 4717 } 4718 lens[num_vars] = strlen(vname) + strlen(value) + 2; 4719 vars[num_vars] = malloc(lens[num_vars]); 4720 if (vars[num_vars] == NULL) { 4721 ERROR("No memory for variable key=value %s %s", vname, value); 4722 goto out; 4723 } 4724 snprintf(vars[num_vars], lens[num_vars], "%s=%s", vname, value); 4725 arg = vars[num_vars]; 4726 num_vars++; 4727 } 4728 argv[argc++] = arg; 4729 } 4730 argv[argc] = NULL; 4731 subproc = ioloop_subproc(program, argv, argc, callback, NULL, NULL); 4732 if (subproc != NULL) { 4733 ioloop_subproc_run_sync(subproc); 4734 ioloop_subproc_release(subproc); 4735 } 4736 out: 4737 for (i = 0; i < num_vars; i++) { 4738 free(vars[i]); 4739 } 4740 } 4741 4742 static bool 4743 finished_okay(const char *context, int status, const char *error) 4744 { 4745 // If we get an error, something failed before the program had been successfully started. 4746 if (error != NULL) { 4747 ERROR("%s failed on startup: %s", context, error); 4748 } 4749 4750 // The key file generation process completed 4751 else if (WIFEXITED(status)) { 4752 if (WEXITSTATUS(status) != 0) { 4753 ERROR("%s program exited with status %d", context, status); 4754 // And that means we don't have DNS Push--sorry! 4755 } else { 4756 return true; 4757 } 4758 } else if (WIFSIGNALED(status)) { 4759 ERROR("%s program exited on signal %d", context, WTERMSIG(status)); 4760 // And that means we don't have DNS Push--sorry! 4761 } else if (WIFSTOPPED(status)) { 4762 ERROR("%s program stopped on signal %d", context, WSTOPSIG(status)); 4763 // And that means we don't have DNS Push--sorry! 4764 } else { 4765 ERROR("%s program exit status unknown: %d", context, status); 4766 // And that means we don't have DNS Push--sorry! 4767 } 4768 return false; 4769 } 4770 4771 // Called after the cert has been generated. 4772 static void 4773 certfile_finished_callback(void *NULLABLE context, int status, const char *error) 4774 { 4775 (void)context; 4776 4777 // If we were able to generate a cert, we can start DNS Push service and start advertising it. 4778 if (finished_okay("Certificate signing", status, error)) { 4779 int i = dnssd_proxy_num_listeners; 4780 4781 dnssd_push_setup(); 4782 4783 for (; i < dnssd_proxy_num_listeners; i++) { 4784 INFO("Started " PUB_S_SRP, dnssd_proxy_listeners[i]->name); 4785 } 4786 } 4787 } 4788 4789 // Called after the key has been generated. 4790 static void 4791 keyfile_finished_callback(void *context, int status, const char *error) 4792 { 4793 (void)context; 4794 if (finished_okay("Keyfile generation", status, error)) { 4795 INFO("Keyfile generation completed."); 4796 4797 // XXX dates need to not be constant!!! 4798 keyprogram_start(CERTWRITE_PROGRAM, certfile_finished_callback, 4799 "selfsign=1", NULL, "issuer_key", tls_key_filename, "issuer_name=CN", my_name, 4800 "not_before=20210825000000", NULL, "not_after=20230824235959", NULL, "is_ca=1", NULL, 4801 "max_pathlen=0", NULL, "output_file", tls_cert_filename, NULL); 4802 } 4803 4804 } 4805 #endif // #if (SRP_FEATURE_CAN_GENERATE_TLS_CERT) 4806 4807 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 4808 static served_domain_t *NONNULL 4809 add_new_served_domain_with_interface(const char *const NONNULL name, 4810 const addr_t *const NULLABLE address, const addr_t *const NULLABLE mask) 4811 { 4812 dp_interface_t *new_interface = NULL; 4813 served_domain_t *served_domain = NULL; 4814 bool local_only_interface = !strcmp(LOCAL_ONLY_PSEUDO_INTERFACE, name); 4815 bool locally_served_interface = !local_only_interface && !strcmp(ALL_LOCALS_PSEUDO_INTERFACE, name); 4816 #if SRP_FEATURE_LOCAL_DISCOVERY 4817 bool infrastructure_interface = !locally_served_interface && !strcmp(INFRASTRUCTURE_PSEUDO_INTERFACE, name); 4818 #endif 4819 bool succeeded; 4820 4821 new_interface = calloc(1, sizeof(*new_interface)); 4822 require_action_quiet(new_interface != NULL, exit, succeeded = false; 4823 ERROR("calloc failed - name: " PRI_S_SRP ", allocate size: %lu", name, sizeof(*new_interface))); 4824 4825 new_interface->name = strdup(name); 4826 require_action_quiet(new_interface->name != NULL, exit, succeeded = false; 4827 ERROR("strdup failed to copy interface name - interface name: " PRI_S_SRP, name)); 4828 4829 if (local_only_interface) { 4830 new_interface->ifindex = kDNSServiceInterfaceIndexLocalOnly; 4831 } else if (locally_served_interface) { 4832 new_interface->ifindex = kDNSServiceInterfaceIndexAny; 4833 #if SRP_FEATURE_LOCAL_DISCOVERY 4834 } else if (infrastructure_interface) { 4835 new_interface->ifindex = kDNSServiceInterfaceIndexInfra; 4836 #endif 4837 } else { 4838 new_interface->ifindex = if_nametoindex(name); 4839 } 4840 4841 // Enable DNS push by default. 4842 new_interface->no_push = false; 4843 4844 if (address != NULL) { 4845 require_action_quiet(mask != NULL, exit, succeeded = false); 4846 4847 new_interface->addresses = calloc(1, sizeof(*new_interface->addresses)); 4848 require_action_quiet(new_interface->addresses != NULL, exit, succeeded = false; 4849 ERROR("calloc failed - allocated size: %lu", sizeof(*new_interface->addresses))); 4850 new_interface->addresses->addr = *address; 4851 new_interface->addresses->mask = *mask; 4852 } 4853 4854 char *per_interface_served_domain; 4855 #if STUB_ROUTER 4856 char served_domain_buffer[DNS_MAX_NAME_SIZE]; 4857 #endif 4858 if (local_only_interface) { 4859 // All queries sent to <Thread ID>.thread.home.arpa. will only be proxied to local only interface. 4860 per_interface_served_domain = THREAD_DOMAIN_WITH_ID; 4861 } else if (locally_served_interface) { 4862 per_interface_served_domain = DEFAULT_SERVICE_ARPA_DOMAIN; 4863 #if SRP_FEATURE_LOCAL_DISCOVERY 4864 } else if (infrastructure_interface) { 4865 per_interface_served_domain = DOT_LOCAL_DOMAIN; 4866 #endif 4867 } else { 4868 #if STUB_ROUTER 4869 int bytes_written = snprintf(served_domain_buffer, sizeof(served_domain_buffer), 4870 "%s-%s." HOME_NET_DOMAIN, local_host_name, name); 4871 require_action_quiet(bytes_written > 0 && (size_t)bytes_written < sizeof(served_domain_buffer), exit, 4872 succeeded = false; 4873 ERROR("snprintf failed - local host name: " PRI_S_SRP ", interface name: " PUB_S_SRP 4874 ", name buffer size: %lu", my_name, name, sizeof(served_domain_buffer)) 4875 ); 4876 per_interface_served_domain = served_domain_buffer; 4877 #else 4878 ERROR("unexpected served domain " PRI_S_SRP, name); 4879 succeeded = false; 4880 goto exit; 4881 #endif 4882 } 4883 4884 served_domain = new_served_domain(new_interface, per_interface_served_domain); 4885 require_action_quiet(served_domain != NULL, exit, succeeded = false; 4886 ERROR("new_served_domain failed - interface name: " PUB_S_SRP ", served domain: " PRI_S_SRP, 4887 name, per_interface_served_domain) 4888 ); 4889 4890 succeeded = true; 4891 INFO("new served domain added with interface - served domain: " PUB_S_SRP ", interface name: " PUB_S_SRP, 4892 per_interface_served_domain, name); 4893 exit: 4894 if (!succeeded) { 4895 if (new_interface != NULL) { 4896 if (new_interface->addresses != NULL) { 4897 verify_action(new_interface->addresses->next == NULL, 4898 ERROR("multiple addresses added for this new interface")); 4899 free(new_interface->addresses); 4900 } 4901 if (new_interface->name != NULL) { 4902 free(new_interface->name); 4903 } 4904 free(new_interface); 4905 } 4906 } 4907 4908 return served_domain; 4909 } 4910 #endif // !SRP_FEATURE_DYNAMIC_CONFIGURATION 4911 4912 static bool 4913 interface_addr_t_equal(const interface_addr_t *const NONNULL a, const interface_addr_t *const NONNULL b) 4914 { 4915 bool equal; 4916 const addr_t *const a_addr = &a->addr; 4917 const addr_t *const a_mask = &a->mask; 4918 const addr_t *const b_addr = &b->addr; 4919 const addr_t *const b_mask = &b->mask; 4920 4921 if (a_addr->sa.sa_family != b_addr->sa.sa_family) { 4922 equal = false; 4923 goto exit; 4924 } 4925 4926 require_action_quiet(a_addr->sa.sa_family == a_mask->sa.sa_family, exit, equal = false; 4927 ERROR("A address and mask are not in the same sa_family - address family: %d, mask family: %d", 4928 a_addr->sa.sa_family, a_mask->sa.sa_family) 4929 ); 4930 4931 require_action_quiet(b_addr->sa.sa_family == b_mask->sa.sa_family, exit, equal = false; 4932 ERROR("B address and mask are no in the same sa_family - address family: %d, mask family: %d", 4933 b_addr->sa.sa_family, b_mask->sa.sa_family) 4934 ); 4935 4936 if (a_addr->sa.sa_family == AF_INET) { 4937 if (a_addr->sin.sin_addr.s_addr != b_addr->sin.sin_addr.s_addr) { 4938 equal = false; 4939 goto exit; 4940 } 4941 4942 if (a_mask->sin.sin_addr.s_addr != b_mask->sin.sin_addr.s_addr) { 4943 equal = false; 4944 goto exit; 4945 } 4946 } else { // a_addr->sa.sa_family == AF_INET6 4947 if (memcmp(&a_addr->sin6.sin6_addr, &b_addr->sin6.sin6_addr, sizeof(a_addr->sin6.sin6_addr)) != 0) { 4948 equal = false; 4949 goto exit; 4950 } 4951 4952 if (memcmp(&a_mask->sin6.sin6_addr, &b_mask->sin6.sin6_addr, sizeof(a_mask->sin6.sin6_addr)) != 0) { 4953 equal = false; 4954 goto exit; 4955 } 4956 } 4957 4958 equal = true; 4959 exit: 4960 return equal; 4961 } 4962 4963 static bool 4964 interface_add_new_address(dp_interface_t *const NONNULL interface, const addr_t *const NONNULL address, 4965 const addr_t *const NONNULL mask) 4966 { 4967 bool succeeded; 4968 4969 interface_addr_t *new_if_addr = calloc(1, sizeof(*new_if_addr)); 4970 require_action_quiet(new_if_addr != NULL, exit, succeeded = false; 4971 ERROR("calloc failed - allocated size: %zu", sizeof(*new_if_addr))); 4972 new_if_addr->addr = *address; 4973 new_if_addr->mask = *mask; 4974 new_if_addr->next = NULL; 4975 4976 interface_addr_t **ap; 4977 4978 for (ap = &interface->addresses; *ap != NULL; ap = &(*ap)->next) 4979 ; 4980 4981 *ap = new_if_addr; 4982 succeeded = true;; 4983 exit: 4984 return succeeded; 4985 } 4986 4987 static bool 4988 interface_remove_old_address(dp_interface_t *const NONNULL interface, const addr_t *const NONNULL address, 4989 const addr_t *const NONNULL mask) 4990 { 4991 bool succeeded; 4992 interface_addr_t addr_to_remove = {NULL, *address, *mask}; 4993 interface_addr_t **ap; 4994 interface_addr_t *current; 4995 4996 for (ap = &interface->addresses; *ap != NULL; ap = &(*ap)->next) { 4997 if (interface_addr_t_equal(*ap, &addr_to_remove)) { 4998 break; 4999 } 5000 } 5001 if (*ap == NULL) { 5002 INFO("address not found in the interface address list - interface name: " PUB_S_SRP, interface->name); 5003 succeeded = false; 5004 goto exit; 5005 } 5006 current = *ap; 5007 *ap = current->next; 5008 free(current); 5009 5010 succeeded = true; 5011 exit: 5012 return succeeded; 5013 } 5014 5015 static bool 5016 interface_process_addr_change(dp_interface_t *const NONNULL interface, const addr_t *const NONNULL address, 5017 const addr_t *const NONNULL mask, const enum interface_address_change event_type) 5018 { 5019 bool succeeded; 5020 5021 require_action_quiet(event_type != interface_address_unchanged, exit, succeeded = false; 5022 INFO("no address change event happens")); 5023 5024 if (event_type == interface_address_added) { 5025 succeeded = interface_add_new_address(interface, address, mask); 5026 } else { // event_type == interface_address_removed 5027 succeeded = interface_remove_old_address(interface, address, mask); 5028 } 5029 5030 INFO("address added/removed successfully - event: " PUB_S_SRP, 5031 event_type == interface_address_added ? "added" : "removed"); 5032 5033 exit: 5034 return succeeded; 5035 } 5036 5037 static void 5038 towire_init(dns_wire_t * const NONNULL wire_ptr, dns_towire_state_t * const NONNULL towire_ptr) 5039 { 5040 memset(wire_ptr, 0, sizeof(*wire_ptr)); 5041 memset(towire_ptr, 0, sizeof(*towire_ptr)); 5042 towire_ptr->message = wire_ptr; 5043 towire_ptr->lim = &wire_ptr->data[DNS_DATA_SIZE]; 5044 towire_ptr->p = wire_ptr->data; 5045 } 5046 5047 #if STUB_ROUTER 5048 static bool 5049 string_ends_with(const char *const NONNULL str, const char *const NONNULL suffix) 5050 { 5051 size_t str_len = strlen(str); 5052 size_t suffix_len = strlen(suffix); 5053 bool ret; 5054 5055 if (str_len < suffix_len) { 5056 ret = false; 5057 goto exit; 5058 } 5059 5060 if (strcmp(str + (str_len-suffix_len), suffix) != 0) { 5061 ret = false; 5062 goto exit; 5063 } 5064 5065 ret = true; 5066 exit: 5067 return ret; 5068 } 5069 #endif 5070 5071 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 5072 #if STUB_ROUTER 5073 static bool 5074 served_domain_change_domain_name(void) 5075 { 5076 bool succeeded = true; 5077 5078 served_domain_t *next; 5079 for (served_domain_t *current = served_domains; current != NULL; current = next) { 5080 next = current->next; 5081 if (!string_ends_with(current->domain, HOME_NET_DOMAIN)) { 5082 continue; 5083 } 5084 // Skip local only interface because only served domain <Thread ID>.thread.home.arpa. does not contain domain 5085 // string. 5086 if (current->interface != NULL && strcmp(LOCAL_ONLY_PSEUDO_INTERFACE, current->interface->name) == 0) { 5087 continue; 5088 } 5089 5090 // Constructs new served domain name. 5091 char *new_served_domain_name; 5092 char new_served_domain_buff[DNS_MAX_NAME_SIZE]; 5093 5094 if (0) { 5095 } else if (current->interface != NULL) { // <local host name>-<interface name>.home.arpa. 5096 const dp_interface_t *const interface = current->interface; 5097 int bytes_written = snprintf(new_served_domain_buff, sizeof(new_served_domain_buff), 5098 "%s-%s." HOME_NET_DOMAIN, local_host_name, interface->name); 5099 require_action_quiet(bytes_written > 0 && (size_t)bytes_written < sizeof(new_served_domain_buff), exit, 5100 succeeded = false; ERROR("snprintf failed")); 5101 new_served_domain_name = new_served_domain_buff; 5102 } else { // <local host name>.home.arpa. 5103 int bytes_written = snprintf(new_served_domain_buff, sizeof(new_served_domain_buff), 5104 "%s." HOME_NET_DOMAIN, local_host_name); 5105 require_action_quiet(bytes_written > 0 && (size_t)bytes_written < sizeof(new_served_domain_buff), exit, 5106 succeeded = false; ERROR("snprintf failed")); 5107 new_served_domain_name = new_served_domain_buff; 5108 } 5109 5110 INFO("Updating served domain from " PRI_S_SRP " to " PRI_S_SRP, current->domain, new_served_domain_name); 5111 5112 // Free the old served domain name. 5113 free(current->domain_ld); 5114 dns_name_free(current->domain_name); 5115 5116 // Set the new served domain name. 5117 size_t domain_len = strlen(new_served_domain_name); 5118 current->domain_ld = malloc(domain_len + 2); 5119 require_action_quiet(current->domain_ld != NULL, for_loop_exit, succeeded = false; 5120 ERROR("malloc failed - allocated length: %zu", domain_len + 2)); 5121 current->domain_ld[0] = '.'; 5122 current->domain = current->domain_ld + 1; 5123 memcpy(current->domain, new_served_domain_name, domain_len); 5124 current->domain[domain_len] = '\0'; 5125 5126 current->domain_name = dns_pres_name_parse(current->domain); 5127 require_action_quiet(current->domain_name != NULL, for_loop_exit, succeeded = false; 5128 ERROR("failed to create parsed DNS name - domain name to be parsed: " PRI_S_SRP, current->domain) 5129 ); 5130 5131 for_loop_exit: 5132 if (!succeeded) { 5133 delete_served_domain(current); 5134 } 5135 } 5136 5137 exit: 5138 return succeeded; 5139 } 5140 #endif // STUB_ROUTER 5141 5142 static bool 5143 served_domain_process_name_change(void) 5144 { 5145 bool succeeded; 5146 5147 // Deletes all hardwired response set in the served domain. 5148 dnssd_hardwired_clear(); 5149 5150 #if STUB_ROUTER 5151 // Since local host name changes, we need to reflect the change in the served domain name. 5152 succeeded = served_domain_change_domain_name(); 5153 require_action_quiet(succeeded, exit, ERROR("served_domain_change_domain_name failed")); 5154 #endif 5155 5156 // Re-set the hardwired response 5157 dnssd_hardwired_setup(); 5158 5159 // Re-set the DNS push hardwired response 5160 dnssd_hardwired_push_setup(); 5161 5162 succeeded = true; 5163 #if STUB_ROUTER 5164 exit: 5165 #endif 5166 return succeeded; 5167 } 5168 5169 static bool 5170 initialize_uuid_name(srp_server_t *UNUSED server_state) 5171 { 5172 char *s; 5173 uint64_t uuid = srp_random64(); 5174 static const char letters[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 5175 static int letlen = sizeof(letters) - 1; 5176 s = uuid_name; 5177 *s++ = 'u'; // So that it always starts with a letter. 5178 while (s < uuid_name + sizeof(uuid_name) - 1 && uuid != 0) { 5179 *s++ = letters[uuid % letlen]; 5180 uuid /= letlen; 5181 } 5182 *s++ = 0; 5183 return true; 5184 } 5185 5186 static bool 5187 update_my_name(CFStringRef local_host_name_cfstr) 5188 { 5189 bool succeeded; 5190 size_t name_length; 5191 5192 if (local_host_name_cfstr == NULL) { 5193 // If we are a thread device and not a stub router, make up a hostname for the remote server in case we need it. 5194 char localhost[] = "localhost."; 5195 name_length = sizeof(localhost); 5196 memcpy(local_host_name, localhost, name_length); 5197 memcpy(local_host_name_dot_local, localhost, name_length); 5198 memcpy(my_name_buf, localhost, name_length); 5199 } else { 5200 // local host name to c string. 5201 succeeded = CFStringGetCString(local_host_name_cfstr, local_host_name, sizeof(local_host_name), 5202 kCFStringEncodingUTF8); 5203 require_action_quiet(succeeded, exit, succeeded = false; 5204 ERROR("CFStringGetCString failed - local host name: " PRI_S_SRP, 5205 CFStringGetCStringPtr(local_host_name_cfstr, kCFStringEncodingUTF8)) 5206 ); 5207 name_length = strlen(local_host_name); 5208 5209 // Validate the local host name. 5210 for (size_t i = 0; i < name_length; i++) { 5211 char ch = local_host_name[i]; 5212 bool is_valid_char = isalnum(ch) || (ch == '-'); 5213 require_action_quiet(is_valid_char, exit, succeeded = false; 5214 ERROR("invalid DNS name - name: " PUB_S_SRP, local_host_name)); 5215 } 5216 5217 require_action_quiet(name_length + sizeof(DOT_HOME_NET_DOMAIN) <= sizeof(my_name_buf), 5218 exit, 5219 succeeded = false; 5220 ERROR("generated name too long: " PUB_S_SRP DOT_HOME_NET_DOMAIN, local_host_name)); 5221 5222 // Update existing local host name in my_name. 5223 memcpy(my_name_buf, local_host_name, name_length); 5224 memcpy(my_name_buf + name_length, DOT_HOME_NET_DOMAIN, sizeof(DOT_HOME_NET_DOMAIN)); 5225 5226 // Update existing local host name with .local suffix. 5227 int bytes_written = snprintf(local_host_name_dot_local, sizeof(local_host_name_dot_local), "%s" DOT_LOCAL, local_host_name); 5228 if (bytes_written < 0 || (size_t) bytes_written > sizeof(local_host_name_dot_local)) { 5229 ERROR("snprintf failed - name length: %lu, max: %lu", strlen(local_host_name) + sizeof(DOT_LOCAL), 5230 sizeof(local_host_name_dot_local)); 5231 succeeded = false; 5232 goto exit; 5233 } 5234 } 5235 my_name = my_name_buf; 5236 5237 succeeded = true; 5238 INFO(PUB_S_SRP " my_name: " PRI_S_SRP ", local host name: " PRI_S_SRP, my_name == NULL ? "initialized" : "updated", 5239 my_name, local_host_name_dot_local); 5240 5241 exit: 5242 return succeeded; 5243 } 5244 5245 // Gets called when name change event happens 5246 static void 5247 monitor_name_changes_callback(SCDynamicStoreRef store, CFArrayRef changed_keys, void *context) 5248 { 5249 bool succeeded; 5250 CFStringRef local_host_name_cfstring = NULL; 5251 dnssd_proxy_advertisements_t *advertisements = context; 5252 5253 // Check if name changes. 5254 CFRange range = {0, CFArrayGetCount(changed_keys)}; 5255 const bool host_name_changed = CFArrayContainsValue(changed_keys, range, sc_dynamic_store_key_host_name); 5256 if (!host_name_changed) { 5257 goto exit; 5258 } 5259 5260 // Get the new local host name. 5261 local_host_name_cfstring = SCDynamicStoreCopyLocalHostName(store); 5262 require_action_quiet(local_host_name_cfstring != NULL, exit, ERROR("failed to get updated local host name")); 5263 5264 // Update the old my_name 5265 succeeded = update_my_name(local_host_name_cfstring); 5266 require_action_quiet(succeeded, exit, ERROR("failed to update my name")); 5267 5268 // With the new local host name, update the served domains and hardwired response. 5269 succeeded = served_domain_process_name_change(); 5270 require_action_quiet(succeeded, exit, ERROR("failed to process name change for served domains")); 5271 5272 if (advertisements->txn != NULL) { 5273 dns_wire_t wire; 5274 dns_towire_state_t towire; 5275 towire_init(&wire, &towire); 5276 dns_full_name_to_wire(NULL, &towire, local_host_name_dot_local); 5277 5278 DNSServiceErrorType err = DNSServiceUpdateRecord(advertisements->service_ref, advertisements->ns_record_ref, 0, 5279 towire.p - wire.data, wire.data, 0); 5280 if (err != kDNSServiceErr_NoError) { 5281 ERROR("DNSServiceUpdateRecord failed to update NS record to new name - name: " PRI_S_SRP, 5282 local_host_name_dot_local); 5283 } 5284 5285 INFO("Updating record - new NS record rdata: " PRI_S_SRP, local_host_name_dot_local); 5286 } 5287 5288 exit: 5289 if (local_host_name_cfstring != NULL) { 5290 CFRelease(local_host_name_cfstring); 5291 } 5292 return; 5293 } 5294 5295 static bool 5296 monitor_name_changes(dnssd_proxy_advertisements_t *advertisements) 5297 { 5298 bool succeeded; 5299 SCDynamicStoreRef store = NULL; 5300 const void *monitored_keys[1]; 5301 CFArrayRef monitored_keys_array = NULL; 5302 5303 // Set the callback function for name change event. 5304 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("dnssd-proxy:watch for name change events"), 5305 monitor_name_changes_callback, &advertisements->sc_context); 5306 require_action_quiet(store != NULL, exit, succeeded = false; ERROR("failed to create SCDynamicStoreRef")); 5307 5308 // Set the key to be monitored, which is host name 5309 sc_dynamic_store_key_host_name = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault); 5310 require_action_quiet(sc_dynamic_store_key_host_name != NULL, exit, succeeded = false; 5311 ERROR("failed to create SCDynamicStoreKey for host name")); 5312 5313 monitored_keys[0] = sc_dynamic_store_key_host_name; 5314 monitored_keys_array = CFArrayCreate(kCFAllocatorDefault, monitored_keys, countof(monitored_keys), 5315 &kCFTypeArrayCallBacks); 5316 require_action_quiet(monitored_keys_array != NULL, exit, succeeded = false; 5317 ERROR("failed to create CFArrayRef for monitored keys")); 5318 5319 succeeded = SCDynamicStoreSetNotificationKeys(store, monitored_keys_array, NULL); 5320 require_action_quiet(succeeded, exit, ERROR("SCDynamicStoreSetNotificationKeys failed")); 5321 5322 succeeded = SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue()); 5323 require_action_quiet(succeeded, exit, ERROR("SCDynamicStoreSetDispatchQueue failed")); 5324 5325 succeeded = true; 5326 INFO("Start to monitor local host name changes"); 5327 exit: 5328 if (!succeeded) { 5329 if (store != NULL) { 5330 CFRelease(store); 5331 } 5332 } 5333 if (monitored_keys_array != NULL) { 5334 CFRelease(monitored_keys_array); 5335 } 5336 return succeeded; 5337 } 5338 5339 static bool 5340 initialize_my_name_and_monitoring(srp_server_t *server_state) 5341 { 5342 bool succeeded; 5343 CFStringRef local_host_name_cfstring = NULL; 5344 5345 // Set notification from configd. 5346 succeeded = monitor_name_changes(server_state->dnssd_proxy_advertisements); 5347 require_action_quiet(succeeded, exit, ERROR("failed to monitor name changes")); 5348 5349 // Get the initial local host name 5350 local_host_name_cfstring = SCDynamicStoreCopyLocalHostName(NULL); 5351 require_action_quiet(local_host_name != NULL, exit, succeeded = false; ERROR("failed to get local host name")); 5352 5353 succeeded = update_my_name(local_host_name_cfstring); 5354 require_action_quiet(succeeded, exit, ERROR("failed to update myname")); 5355 5356 exit: 5357 if (local_host_name_cfstring != NULL) { 5358 CFRelease(local_host_name_cfstring); 5359 } 5360 return succeeded; 5361 } 5362 5363 #ifndef SRP_TEST_SERVER 5364 static bool 5365 configure_dnssd_proxy(void) 5366 { 5367 dnssd_proxy_udp_port= 53; 5368 dnssd_proxy_tcp_port = 53; 5369 dnssd_proxy_tls_port = 853; 5370 return true; 5371 } 5372 #endif // SRP_TEST_SERVER 5373 #endif // SRP_FEATURE_DYNAMIC_CONFIGURATION 5374 5375 static bool 5376 start_dnssd_proxy_listener(void) 5377 { 5378 bool succeeded; 5379 5380 #if STUB_ROUTER 5381 #ifndef NOT_HAVE_SA_LEN 5382 # define SA_LEN_INIT addr.sa.sa_len = sizeof(addr.sin6) 5383 #else 5384 # define SA_LEN_INIT 5385 #endif // NOT_HAVE_SA_LEN 5386 #define INIT_ADDR_T(PORT) \ 5387 do { \ 5388 memset(&addr, 0, sizeof(addr)); \ 5389 addr.sa.sa_family = AF_UNSPEC; \ 5390 addr.sin6.sin6_port = htons(PORT); \ 5391 SA_LEN_INIT; \ 5392 } while (false) 5393 5394 addr_t addr; 5395 5396 INIT_ADDR_T(dnssd_proxy_udp_port); 5397 dnssd_proxy_listeners[dnssd_proxy_num_listeners] = 5398 ioloop_listener_create(false, false, true, NULL, 0, &addr, NULL, "DNS over UDP", dns_proxy_input, 5399 NULL, NULL, NULL, NULL, NULL, 0, NULL); 5400 require_action_quiet(dnssd_proxy_listeners[dnssd_proxy_num_listeners] != NULL, exit, succeeded = false; 5401 ERROR("failed to start UDP listener - listener index: %d", dnssd_proxy_num_listeners)); 5402 dnssd_proxy_num_listeners++; 5403 5404 INIT_ADDR_T(dnssd_proxy_tcp_port); 5405 dnssd_proxy_listeners[dnssd_proxy_num_listeners] = 5406 ioloop_listener_create(true, false, true, NULL, 0, &addr, NULL, "DNS over TCP", dns_proxy_input, 5407 NULL, NULL, NULL, NULL, NULL, 0, NULL); 5408 require_action_quiet(dnssd_proxy_listeners[dnssd_proxy_num_listeners] != NULL, exit, succeeded = false; 5409 ERROR("failed to start TCP listener - listener index: %d", dnssd_proxy_num_listeners)); 5410 dnssd_proxy_num_listeners++; 5411 #endif // STUB_ROUTER 5412 5413 dnssd_push_setup(); 5414 5415 for (int i = 0; i < dnssd_proxy_num_listeners; i++) { 5416 INFO("listener started - name: " PUB_S_SRP, dnssd_proxy_listeners[i]->name); 5417 } 5418 5419 succeeded = true; 5420 goto exit; 5421 5422 exit: 5423 return succeeded; 5424 } 5425 5426 #define ADVERTISEMENT_RETRY_TIMER 10 * MSEC_PER_SEC 5427 5428 #if STUB_ROUTER 5429 static void 5430 advertisements_finalize(void *context) 5431 { 5432 dnssd_proxy_advertisements_t *advertisements_context = context; 5433 advertisements_context->txn = NULL; 5434 } 5435 5436 static void 5437 advertisements_failed(void *UNUSED context, int status) 5438 { 5439 ERROR("%d", status); 5440 } 5441 5442 static void 5443 advertisements_callback(DNSServiceRef sd_ref, DNSRecordRef record_ref, DNSServiceFlags UNUSED flags, 5444 DNSServiceErrorType error, void *context) 5445 { 5446 dnssd_proxy_advertisements_t *advertisements_context = context; 5447 5448 if (error == kDNSServiceErr_NoError) { 5449 const char * const description = record_ref == advertisements_context->ns_record_ref ? "NS" : "PTR"; 5450 INFO("record registered successfully - registered: " PUB_S_SRP, description); 5451 } else if (error == kDNSServiceErr_ServiceNotRunning) { 5452 // The record is not being advertised because mDNSResponder stopped running for some reason (like crashes), 5453 // in which case, we will stop the previous DNSService operation and start a new one 10s later. 5454 5455 // Release the previous DNSServiceRef. 5456 if (advertisements_context->service_ref != sd_ref) { 5457 ERROR("Invalid DNSServiceRef - context->service_ref: %p, sd_ref: %p", advertisements_context->service_ref, 5458 sd_ref); 5459 } 5460 if (advertisements_context->txn != NULL) { 5461 ioloop_dnssd_txn_cancel(advertisements_context->txn); 5462 ioloop_dnssd_txn_release(advertisements_context->txn); 5463 advertisements_context->txn = NULL; 5464 } 5465 advertisements_context->service_ref = NULL; 5466 5467 // Restart the advertisement. 5468 bool succeeded = start_timer_to_advertise(advertisements_context, NULL, ADVERTISEMENT_RETRY_TIMER); 5469 if (!succeeded) { 5470 ERROR("start_timer_to_advertise failed"); 5471 } else { 5472 INFO("mDNSResponder stopped running, preparing to re-advertise the PTR and NS records"); 5473 } 5474 } else { 5475 ERROR("record not registered - error: %d", error); 5476 } 5477 } 5478 5479 static void 5480 advertise_dnssd_proxy_callback(void *NONNULL context) 5481 { 5482 DNSServiceErrorType err; 5483 bool succeeded; 5484 bool dns_service_initialized = false; 5485 dns_wire_t wire; 5486 dns_towire_state_t towire; 5487 dnssd_proxy_advertisements_t *advertisement_context = context; 5488 srp_server_t *server_state = advertisement_context->server_state; 5489 const char *const domain_to_advertise = advertisement_context->domain_to_advertise; 5490 5491 INFO("Start advertising lb._dns-sd._udp.local. PTR and openthread.thread.home.arpa.local NS records"); 5492 5493 // Create DNSServiceRef 5494 err = DNSServiceCreateConnection(&advertisement_context->service_ref); 5495 if (err != kDNSServiceErr_NoError) { 5496 ERROR("DNSServiceCreateConnection failed"); 5497 succeeded = false; 5498 goto exit; 5499 } 5500 dns_service_initialized = true; 5501 5502 // Setup lb._dns-sd._udp.local. PTR openthread.thread.home.arpa. 5503 towire_init(&wire, &towire); 5504 dns_full_name_to_wire(NULL, &towire, domain_to_advertise); 5505 5506 err = DNSServiceRegisterRecord(advertisement_context->service_ref, &advertisement_context->ptr_record_ref, 5507 kDNSServiceFlagsShared, server_state->advertise_interface, AUTOMATIC_BROWSING_DOMAIN, 5508 kDNSServiceType_PTR, kDNSServiceClass_IN, towire.p - wire.data, wire.data, 0, 5509 advertisements_callback, advertisement_context); 5510 if (err != kDNSServiceErr_NoError) { 5511 ERROR("DNSServiceRegisterRecord failed - record: " PUB_S_SRP " PTR " PRI_S_SRP, AUTOMATIC_BROWSING_DOMAIN, 5512 domain_to_advertise); 5513 succeeded = false; 5514 goto exit; 5515 } 5516 5517 // Setup openthread.thread.home.arpa. NS <local host name>.local. 5518 towire_init(&wire, &towire); 5519 dns_full_name_to_wire(NULL, &towire, local_host_name_dot_local); 5520 5521 err = DNSServiceRegisterRecord(advertisement_context->service_ref, &advertisement_context->ns_record_ref, 5522 kDNSServiceFlagsShared | kDNSServiceFlagsForceMulticast, 5523 server_state->advertise_interface, domain_to_advertise, kDNSServiceType_NS, 5524 kDNSServiceClass_IN, towire.p - wire.data, wire.data, 0, 5525 advertisements_callback, advertisement_context); 5526 if (err != kDNSServiceErr_NoError) { 5527 ERROR("DNSServiceRegisterRecord failed - record: " PUB_S_SRP " NS " PRI_S_SRP, domain_to_advertise, 5528 local_host_name_dot_local); 5529 succeeded = false; 5530 goto exit; 5531 } 5532 5533 // Start the running loop 5534 advertisement_context->txn = ioloop_dnssd_txn_add(advertisement_context->service_ref, advertisement_context, 5535 advertisements_finalize, advertisements_failed); 5536 if (advertisement_context->txn == NULL) { 5537 ERROR("ioloop_dnssd_txn_add failed"); 5538 succeeded = false; 5539 goto exit; 5540 } 5541 5542 INFO("Advertising records - " PUB_S_SRP " PTR " PRI_S_SRP ", " PRI_S_SRP " NS " PRI_S_SRP, 5543 AUTOMATIC_BROWSING_DOMAIN, domain_to_advertise, domain_to_advertise, local_host_name_dot_local); 5544 succeeded = true; 5545 exit: 5546 if (!succeeded) { 5547 if (dns_service_initialized) { 5548 DNSServiceRefDeallocate(advertisement_context->service_ref); 5549 advertisement_context->service_ref = NULL; 5550 } 5551 if (err == kDNSServiceErr_ServiceNotRunning) { 5552 ERROR("mDNSResponder is not running yet when trying to advertise PTR and NS records, try again 10s later"); 5553 // advertise_dnssd_proxy_callback will be called again 10s later, since we did not cancel the timer. 5554 } else { 5555 // Other kDNSServiceErr, should be impossible. If it happens, give up advertising the records. 5556 ioloop_cancel_wake_event(advertisement_context->wakeup_timer); 5557 } 5558 } else { 5559 // Since we registered successfully, there is no need to trigger another timer to set the records. 5560 // Stop the timer. 5561 ioloop_cancel_wake_event(advertisement_context->wakeup_timer); 5562 } 5563 } 5564 5565 static bool 5566 start_timer_to_advertise(dnssd_proxy_advertisements_t *NONNULL context, 5567 const char *const NULLABLE domain_to_advertise, const uint32_t interval) 5568 { 5569 bool succeeded; 5570 5571 // Only create timer once. 5572 if (context->wakeup_timer == NULL) { 5573 context->wakeup_timer = ioloop_wakeup_create(); 5574 if (context->wakeup_timer == NULL) { 5575 succeeded = false; 5576 goto exit; 5577 } 5578 } 5579 5580 // Only copy advertised domain once. 5581 if (context->domain_to_advertise == NULL) { 5582 if (domain_to_advertise == NULL) { 5583 succeeded = false; 5584 goto exit; 5585 } 5586 5587 context->domain_to_advertise = strdup(domain_to_advertise); 5588 if (context->domain_to_advertise == NULL) { 5589 succeeded = false; 5590 goto exit; 5591 } 5592 } 5593 5594 // Start the timer, finalize callback is not necessary here because the context should always be available. 5595 succeeded = ioloop_add_wake_event(context->wakeup_timer, context, advertise_dnssd_proxy_callback, NULL, interval); 5596 if (!succeeded) { 5597 goto exit; 5598 } 5599 5600 succeeded = true; 5601 exit: 5602 if (!succeeded) { 5603 if (context->domain_to_advertise != NULL) { 5604 free(context->domain_to_advertise); 5605 context->domain_to_advertise = NULL; 5606 } 5607 if (context->wakeup_timer != NULL) { 5608 ioloop_wakeup_release(context->wakeup_timer); 5609 context->wakeup_timer = NULL; 5610 } 5611 } 5612 return succeeded; 5613 } 5614 5615 #if SRP_FEATURE_DISCOVERY_PROXY_SERVER 5616 5617 static bool 5618 start_timer_to_advertise_dnssd_dp_proxy(dnssd_dp_proxy_advertisements_t *context, uint32_t interval); 5619 5620 static void 5621 dp_advertisements_finalize(void *const context) 5622 { 5623 dnssd_dp_proxy_advertisements_t *advertisements_context = context; 5624 advertisements_context->txn = NULL; 5625 } 5626 5627 static void 5628 dp_advertisements_failed(void *const UNUSED context, const int status) 5629 { 5630 ERROR("push service advertisement failed -- error: %d", status); 5631 } 5632 5633 static void 5634 dp_advertisements_callback(const DNSServiceRef UNUSED sd_ref, const DNSServiceFlags UNUSED flags, 5635 const DNSServiceErrorType error, const char *const name, const char *const reg_type, 5636 const char *const domain, void *const context) 5637 { 5638 dnssd_dp_proxy_advertisements_t *const ads_ctx = context; 5639 if (error == kDNSServiceErr_NoError) { 5640 INFO("Push service registered successfully -- %s.%s%s", name, reg_type, domain); 5641 } else if (error == kDNSServiceErr_ServiceNotRunning) { 5642 if (ads_ctx->txn != NULL) { 5643 ioloop_dnssd_txn_cancel(ads_ctx->txn); 5644 ioloop_dnssd_txn_forget(&ads_ctx->txn); 5645 } 5646 ads_ctx->service_ref = NULL; 5647 5648 const bool succeeded = start_timer_to_advertise_dnssd_dp_proxy(ads_ctx, ADVERTISEMENT_RETRY_TIMER); 5649 if (!succeeded) { 5650 ERROR("start_timer_to_advertise_dnssd_dp_proxy failed"); 5651 } else { 5652 INFO("mDNSResponder stopped running, preparing to re-advertise DNS push service"); 5653 } 5654 } else { 5655 ERROR("Push service not registered -- error: %d", error); 5656 } 5657 } 5658 5659 static void 5660 advertise_dnssd_dp_proxy_callback(void *const context) 5661 { 5662 DNSServiceRef service_ref = NULL; 5663 dnssd_txn_t *txn = NULL; 5664 dnssd_dp_proxy_advertisements_t *const advertisement_context = context; 5665 srp_server_t *const server_state = advertisement_context->server_state; 5666 int bytes_written = 0; 5667 5668 // Construct ,_local,_openthread#thread#home#arpa 5669 const char *domains_support_push[] = { 5670 DOT_LOCAL_DOMAIN, 5671 THREAD_BROWSING_DOMAIN, 5672 }; 5673 char subtype_domains[256]; 5674 size_t current_len = 0; 5675 for (size_t i = 0; i < countof(domains_support_push); i++) { 5676 const char *const domain_supports_push = domains_support_push[i]; 5677 const size_t len = strlen(domain_supports_push); 5678 char subtype_domain[128]; 5679 // Convert domain name like `_openthread.thread.home.arpa` to `_openthread#thread#home#arpa`. 5680 require_quiet(len < sizeof(subtype_domain), exit); 5681 for (size_t j = 0; j < len + 1; j++) { 5682 if (domain_supports_push[j] == '.') { 5683 subtype_domain[j] = '#'; 5684 } else { 5685 subtype_domain[j] = domain_supports_push[j]; 5686 } 5687 } 5688 // Remove the trailing '#'. 5689 if (subtype_domain[len - 1] == '#') { 5690 subtype_domain[len - 1] = '\0'; 5691 } 5692 5693 bytes_written = snprintf(subtype_domains + current_len, sizeof(subtype_domains) - current_len, ",_%s", 5694 subtype_domain); 5695 require_quiet((bytes_written > 0) && ((size_t)bytes_written < (sizeof(subtype_domains) - current_len)), exit); 5696 current_len += bytes_written; 5697 } 5698 5699 // Construct _dnssd-dp._tcp,_local,_openthread#thread#home#arpa with subtype. 5700 char reg_type[128]; 5701 bytes_written = snprintf(reg_type, sizeof(reg_type), "_dnssd-dp._tcp%s", subtype_domains); 5702 require_quiet((bytes_written > 0) && ((size_t)bytes_written < sizeof(reg_type)), exit); 5703 5704 // Construct service instance name like: p128-undulw2d1vktd1 5705 const uint8_t priority = 128; 5706 const char * const random_identifier = uuid_name; 5707 char name[128]; 5708 bytes_written = snprintf(name, sizeof(name), "p%03d-%s", priority, random_identifier); 5709 require_quiet((bytes_written > 0) && ((size_t)bytes_written < sizeof(name)), exit); 5710 5711 const uint16_t dp_port = dnssd_proxy_tls_port; 5712 5713 // The registered PTRs will be like: 5714 // _openthread#thread#home#arpa._sub._dnssd-dp._tcp.local PTR p128-undulw2d1vktd1._dnssd-dp._tcp.local 5715 // _local._sub._dnssd-dp._tcp.local PTR p128-undulw2d1vktd1._dnssd-dp._tcp.local 5716 const DNSServiceErrorType dnsssd_err = DNSServiceRegister(&service_ref, 0, server_state->advertise_interface, name, 5717 reg_type, DOT_LOCAL_DOMAIN, NULL, htons(dp_port), 0, NULL, dp_advertisements_callback, context); 5718 require_action_quiet(dnsssd_err == kDNSServiceErr_NoError, exit, 5719 ERROR("DNSServiceRegisterRecord failed -- error: %d", dnsssd_err)); 5720 5721 txn = ioloop_dnssd_txn_add(service_ref, advertisement_context, dp_advertisements_finalize, 5722 dp_advertisements_failed); 5723 require_quiet(txn != NULL, exit); 5724 5725 advertisement_context->service_ref = service_ref; 5726 service_ref = NULL; 5727 advertisement_context->txn = txn; 5728 txn = NULL; 5729 INFO("Advertising push discovery service -- reg_type: %s.%s, service instance name: %s._dnssd-dp._tcp.%s", 5730 reg_type, DOT_LOCAL_DOMAIN, name, DOT_LOCAL_DOMAIN); 5731 5732 exit: 5733 DNSServiceRefSourceForget(&service_ref); 5734 if (txn != NULL) { 5735 ioloop_dnssd_txn_cancel(txn); 5736 ioloop_dnssd_txn_forget(&txn); 5737 } 5738 } 5739 5740 static bool 5741 start_timer_to_advertise_dnssd_dp_proxy(dnssd_dp_proxy_advertisements_t *const context, const uint32_t interval) 5742 { 5743 bool succeeded; 5744 wakeup_t *wakeup_timer = context->wakeup_timer; 5745 5746 if (wakeup_timer == NULL) { 5747 wakeup_timer = ioloop_wakeup_create(); 5748 } else { 5749 ioloop_wakeup_retain(wakeup_timer); 5750 } 5751 require_action_quiet(wakeup_timer != NULL, exit, succeeded = false); 5752 5753 succeeded = ioloop_add_wake_event(wakeup_timer, context, advertise_dnssd_dp_proxy_callback, NULL, interval); 5754 require_quiet(succeeded, exit); 5755 5756 if (context->wakeup_timer == NULL) { 5757 context->wakeup_timer = wakeup_timer; 5758 wakeup_timer = NULL; 5759 } 5760 5761 exit: 5762 ioloop_wakeup_forget(&wakeup_timer); 5763 return succeeded; 5764 } 5765 5766 static bool 5767 advertise_dnssd_dp_proxy(srp_server_t *const server_state) 5768 { 5769 return start_timer_to_advertise_dnssd_dp_proxy(server_state->dnssd_dp_proxy_advertisements, 5770 ADVERTISEMENT_RETRY_TIMER); 5771 } 5772 5773 static bool 5774 is_eligible_to_provide_push_discovery_service(void) 5775 { 5776 return true; 5777 } 5778 5779 #endif // SRP_FEATURE_DISCOVERY_PROXY_SERVER 5780 5781 #endif // STUB_ROUTER 5782 5783 #if SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY 5784 # if SRP_FEATURE_DYNAMIC_CONFIGURATION 5785 static bool 5786 served_domain_init(srp_server_t *server_state) 5787 { 5788 bool succeeded; 5789 served_domain_t *my_name_served_domain = NULL; 5790 served_domain_t *ipv6 = NULL; 5791 served_domain_t *ipv4 = NULL; 5792 served_domain_t *thread_served_domain = NULL; 5793 served_domain_t *default_service_arpa_domain = NULL; 5794 #if SRP_FEATURE_LOCAL_DISCOVERY 5795 served_domain_t *dot_local_domain = NULL; 5796 #endif 5797 5798 // <local host name>.home.arpa. 5799 my_name_served_domain = new_served_domain(NULL, my_name); 5800 require_action_quiet(my_name_served_domain != NULL, exit, succeeded = false; 5801 ERROR("failed to create new served domain - domain name: " PUB_S_SRP, my_name)); 5802 5803 #if STUB_ROUTER 5804 if (server_state->stub_router_enabled) { 5805 // ip6.arpa. 5806 // in-addr.arpa. 5807 ipv6 = new_served_domain(NULL, IPV6_REVERSE_LOOKUP_DOMAIN); 5808 ipv4 = new_served_domain(NULL, IPV4_REVERSE_LOOKUP_DOMAIN); 5809 require_action_quiet(ipv6 != NULL && ipv4 != NULL, exit, succeeded = false; 5810 ERROR("failed to create new served domain for reverse look up - domain name: " PUB_S_SRP ", " PUB_S_SRP, 5811 IPV6_REVERSE_LOOKUP_DOMAIN, IPV4_REVERSE_LOOKUP_DOMAIN) 5812 ); 5813 } 5814 #else 5815 (void)server_state; 5816 #endif 5817 5818 // THREAD_BROWSING_DOMAIN 5819 // It will be served by kDNSServiceInterfaceIndexLocalOnly, which is a pseudo interface. 5820 thread_served_domain = add_new_served_domain_with_interface(LOCAL_ONLY_PSEUDO_INTERFACE, NULL, NULL); 5821 require_action_quiet(thread_served_domain != NULL, exit, succeeded = false); 5822 bool hardwired_set = dnssd_hardwired_setup_for_served_domain(thread_served_domain); 5823 require_action_quiet(hardwired_set, exit, succeeded = false); 5824 5825 // default.service.arpa 5826 // For Thread 1.3.0, default.service.arpa has to return all locally-discoverable services 5827 default_service_arpa_domain = add_new_served_domain_with_interface(ALL_LOCALS_PSEUDO_INTERFACE, NULL, NULL); 5828 require_action_quiet(default_service_arpa_domain != NULL, exit, succeeded = false); 5829 hardwired_set = dnssd_hardwired_setup_for_served_domain(default_service_arpa_domain); 5830 require_action_quiet(hardwired_set, exit, succeeded = false); 5831 5832 #if SRP_FEATURE_LOCAL_DISCOVERY 5833 // local 5834 // discovery proxy for the infrastructure interface 5835 dot_local_domain = add_new_served_domain_with_interface(INFRASTRUCTURE_PSEUDO_INTERFACE, NULL, NULL); 5836 require_action_quiet(dot_local_domain != NULL, exit, succeeded = false); 5837 hardwired_set = dnssd_hardwired_setup_for_served_domain(dot_local_domain); 5838 require_action_quiet(hardwired_set, exit, succeeded = false); 5839 #endif 5840 5841 succeeded = true; 5842 exit: 5843 if (!succeeded) { 5844 if (thread_served_domain != NULL) { 5845 delete_served_domain(thread_served_domain); 5846 } 5847 if (default_service_arpa_domain != NULL) { 5848 delete_served_domain(default_service_arpa_domain); 5849 } 5850 #if SRP_FEATURE_LOCAL_DISCOVERY 5851 if (dot_local_domain != NULL) { 5852 delete_served_domain(dot_local_domain); 5853 } 5854 #endif 5855 if (ipv4 != NULL) { 5856 delete_served_domain(ipv4); 5857 } 5858 if (ipv6 != NULL) { 5859 delete_served_domain(ipv6); 5860 } 5861 if (my_name_served_domain != NULL) { 5862 delete_served_domain(my_name_served_domain); 5863 } 5864 } 5865 return succeeded; 5866 } 5867 # endif // SRP_FEATURE_DYNAMIC_CONFIGURATION 5868 #endif // SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY 5869 5870 bool 5871 init_dnssd_proxy(srp_server_t *server_state) 5872 { 5873 bool succeeded; 5874 dnssd_proxy_advertisements_t *advertisements = server_state->dnssd_proxy_advertisements; 5875 if (advertisements == NULL) { 5876 advertisements = calloc(1, sizeof(*advertisements)); 5877 require_action_quiet(advertisements != NULL, exit, 5878 succeeded = false; 5879 ERROR("no memory for advertisements")); 5880 server_state->dnssd_proxy_advertisements = advertisements; 5881 advertisements->server_state = server_state; 5882 advertisements->sc_context.info = advertisements; 5883 } 5884 5885 #if STUB_ROUTER 5886 #if SRP_FEATURE_DISCOVERY_PROXY_SERVER 5887 if (is_eligible_to_provide_push_discovery_service()) { 5888 dnssd_dp_proxy_advertisements_t *dp_ads = server_state->dnssd_dp_proxy_advertisements; 5889 if (dp_ads == NULL) { 5890 dp_ads = calloc(1, sizeof(*dp_ads)); 5891 require_action_quiet(dp_ads != NULL, exit, 5892 succeeded = false; ERROR("no memory for push discovery service advertisements")); 5893 server_state->dnssd_dp_proxy_advertisements = dp_ads; 5894 dp_ads->server_state = server_state; 5895 } 5896 } 5897 #endif // SRP_FEATURE_DISCOVERY_PROXY_SERVER 5898 #endif // STUB_ROUTER 5899 5900 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 5901 succeeded = configure_dnssd_proxy(); 5902 require_action_quiet(succeeded, exit, ERROR("configure_dnssd_proxy failed")); 5903 5904 5905 succeeded = initialize_my_name_and_monitoring(server_state); 5906 5907 require_action_quiet(succeeded, exit, ERROR("initialize_my_name_and_monitoring failed")); 5908 succeeded = initialize_uuid_name(server_state); 5909 require_action_quiet(succeeded, exit, ERROR("initialize_uuid_name failed")); 5910 #if STUB_ROUTER 5911 if (!server_state->stub_router_enabled) { 5912 served_domain_process_name_change(); 5913 } 5914 #endif 5915 5916 #else // SRP_FEATURE_DYNAMIC_CONFIGURATION 5917 // Read the config file 5918 succeeded = config_parse(NULL, "/etc/dnssd-proxy.cf", dp_verbs, NUMCFVERBS); 5919 require_action_quiet(succeeded, 5920 exit,); 5921 5922 // Insist that we have at least one address we're listening on. 5923 succeeded = !(num_listen_addrs == 0 && num_publish_addrs == 0); 5924 require_action_quiet(succeeded, 5925 exit, 5926 ERROR("Please configure at least one my-ipv4-addr and/or one my-ipv6-addr.")); 5927 5928 ioloop_map_interface_addresses(server_state, NULL, &served_domains, dnssd_proxy_ifaddr_callback); 5929 5930 // Set up hardwired answers 5931 dnssd_hardwired_setup(); 5932 #endif // SRP_FEATURE_DYNAMIC_CONFIGURATION 5933 5934 succeeded = srp_tls_init(); 5935 require_action_quiet(succeeded, exit, ERROR("srp_tls_init failed.")); 5936 5937 #if !SRP_FEATURE_CAN_GENERATE_TLS_CERT 5938 // The tls_fail flag allows us to run the proxy in such a way that TLS connections will fail. 5939 // This is never what you want in production, but is useful for testing. 5940 if (!tls_fail) { 5941 if (access(tls_key_filename, R_OK) < 0) { 5942 keyprogram_start(GENKEY_PROGRAM, keyfile_finished_callback, 5943 "type=rsa", NULL, "rsa_keysize=4096", NULL, "filename", tls_key_filename, NULL); 5944 } else if (access(tls_cert_filename, R_OK) < 0) { 5945 keyfile_finished_callback(NULL, 0, NULL); 5946 } 5947 require_action_quiet(access(tls_key_filename, R_OK) >= 0, exit, ERROR("failed to create tls listener key.")); 5948 require_action_quiet(access(tls_cert_filename, R_OK) >= 0, exit, ERROR("failed to create tls listener cert.")); 5949 5950 require_action_quiet(srp_tls_server_init(NULL, tls_cert_filename, tls_key_filename), 5951 exit, ERROR("srp_tls_server_init failed.")); 5952 require_action_quiet(srp_tls_client_init(), exit, ERROR("srp_tls_client_init failed.")); 5953 } 5954 #endif 5955 5956 succeeded = start_dnssd_proxy_listener(); 5957 require_action_quiet(succeeded, exit, ERROR("start_dnssd_proxy_listener failed")); 5958 5959 #if STUB_ROUTER 5960 if (server_state->stub_router_enabled) { 5961 #if SRP_FEATURE_DISCOVERY_PROXY_SERVER 5962 if (server_state->dnssd_dp_proxy_advertisements != NULL) { 5963 succeeded = advertise_dnssd_dp_proxy(server_state); 5964 require_action_quiet(succeeded, exit, ERROR("advertise_dnssd_dp_proxy failed")); 5965 } 5966 #endif 5967 } 5968 #endif 5969 5970 #if SRP_FEATURE_DYNAMIC_CONFIGURATION 5971 succeeded = served_domain_init(server_state); 5972 #endif 5973 5974 exit: 5975 return succeeded; 5976 } 5977 5978 #if !SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY 5979 int 5980 main(int argc, char **argv) 5981 { 5982 int i; 5983 bool log_stderr = false; 5984 5985 dnssd_proxy_udp_port = dnssd_proxy_tcp_port = 53; 5986 dnssd_proxy_tls_port = 853; 5987 5988 // Parse command line arguments 5989 for (i = 1; i < argc; i++) { 5990 if (!strcmp(argv[i], "--tls-fail")) { 5991 tls_fail = true; 5992 } else if (!strcmp(argv[i], "--log-stderr")) { 5993 log_stderr = true; 5994 } else { 5995 return usage(argv[0]); 5996 } 5997 } 5998 5999 OPENLOG("dnssd-proxy", log_stderr); 6000 6001 if (!ioloop_init()) { 6002 return 1; 6003 } 6004 6005 init_dnssd_proxy(); 6006 6007 ioloop(); 6008 } 6009 #endif // #if !SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY 6010 6011 #endif // (SRP_FEATURE_COMBINED_SRP_DNSSD_PROXY) || (!defined(BUILD_SRP_MDNS_PROXY) || (BUILD_SRP_MDNS_PROXY == 0)) 6012 6013 // Local Variables: 6014 // mode: C 6015 // tab-width: 4 6016 // c-file-style: "bsd" 6017 // c-basic-offset: 4 6018 // fill-column: 108 6019 // indent-tabs-mode: nil 6020 // End: 6021