Home | History | Annotate | Line # | Download | only in DSO
      1 /* dso-transport.c
      2  *
      3  * Copyright (c) 2018-2023 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  */
     18 
     19 //*************************************************************************************************************
     20 // Headers
     21 
     22 #include <stdio.h>
     23 #include <signal.h>
     24 #include <stdlib.h>
     25 #include <stdbool.h>
     26 #include <unistd.h>
     27 #include <string.h>
     28 #include <assert.h>
     29 
     30 #include <netdb.h>              // For gethostbyname().
     31 #include <sys/socket.h>         // For AF_INET, AF_INET6, etc.
     32 #include <net/if.h>             // For IF_NAMESIZE.
     33 #include <netinet/in.h>         // For INADDR_NONE.
     34 #include <arpa/inet.h>          // For inet_addr().
     35 #include <unistd.h>
     36 #include <errno.h>
     37 #include <fcntl.h>
     38 
     39 #include "dns_sd.h"
     40 #include "DNSCommon.h"
     41 #include "mDNSEmbeddedAPI.h"
     42 #include "dso.h"
     43 #include "dso-transport.h"
     44 #include "DebugServices.h"      // For check_compile_time_code().
     45 #include "mDNSDebug.h"
     46 #include "misc_utilities.h"     // For mDNSAddr_from_sockaddr().
     47 
     48 #if MDNSRESPONDER_SUPPORTS(COMMON, LOCAL_DNS_RESOLVER_DISCOVERY)
     49 #include "tls-keychain.h"       // For evaluate_tls_cert().
     50 #endif
     51 
     52 #ifdef DSO_USES_NETWORK_FRAMEWORK
     53 // Network Framework only works on MacOS X at the moment, and we need the locking primitives for
     54 // MacOSX.
     55 #include "mDNSMacOSX.h"
     56 #endif
     57 
     58 #include "mDNSFeatures.h"
     59 #include "mdns_strict.h"
     60 
     61 extern mDNS mDNSStorage;
     62 
     63 static dso_connect_state_t *dso_connect_states; // DSO connect states that exist.
     64 static dso_transport_t *dso_transport_states; // DSO transport states that exist.
     65 #ifdef DSO_USES_NETWORK_FRAMEWORK
     66 static dispatch_queue_t dso_dispatch_queue;
     67 #else
     68 static void dso_read_callback(TCPSocket *sock, void *context, mDNSBool connection_established,
     69                        mStatus err);
     70 #endif
     71 
     72 static void dso_connect_state_process_address_port_change(const mDNSAddr *addr_changed, mDNSIPPort port,
     73                                                           bool add, dso_connect_state_t *const cs);
     74 static void dso_connect_internal(dso_connect_state_t *cs, mDNSBool reconnecting);
     75 
     76 void
     77 dso_transport_init(void)
     78 {
     79 #ifdef DSO_USES_NETWORK_FRAMEWORK
     80     // It's conceivable that we might want a separate queue, but we don't know yet, so for
     81     // now we just use the main dispatch queue, which should be on the main dispatch thread,
     82     // which is _NOT_ the kevent thread.   So whenever we are doing anything on the dispatch
     83     // queue (any completion functions for NW framework) we need to acquire the lock before
     84     // we even look at any variables that could be changed by the other thread.
     85     dso_dispatch_queue = dispatch_get_main_queue();
     86 #endif
     87 }
     88 
     89 #ifdef DSO_USES_NETWORK_FRAMEWORK
     90 static dso_connect_state_t *
     91 dso_connect_state_find(uint32_t serial)
     92 {
     93     dso_connect_state_t *csp;
     94     for (csp = dso_connect_states; csp; csp = csp->next) {
     95         if (csp->serial ==  serial) {
     96             return csp;
     97         }
     98     }
     99     return NULL;
    100 }
    101 #endif
    102 
    103 static void
    104 dso_transport_finalize(dso_transport_t *transport, const char *whence)
    105 {
    106     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSO%u->DSOT%u] dso_transport_t finalizing for " PUB_S " - "
    107               "transport: %p.", transport->dso != NULL ? transport->dso->serial : DSO_STATE_INVALID_SERIAL,
    108               transport->serial, whence, transport);
    109 
    110     dso_transport_t **tp = &dso_transport_states;
    111     for (; *tp != NULL && *tp != transport; tp = &((*tp)->next))
    112         ;
    113     if (*tp != NULL) {
    114         *tp = (*tp)->next;
    115     } else {
    116         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT, "dso_transport_t is not in the dso_transport_states list -"
    117             " transport: %p.", transport);
    118     }
    119 
    120     if (transport->connection != NULL) {
    121 #ifdef DSO_USES_NETWORK_FRAMEWORK
    122         MDNS_DISPOSE_NW(transport->connection);
    123 #else
    124         mDNSPlatformTCPCloseConnection(transport->connection);
    125         transport->connection = NULL;
    126 #endif
    127     } else {
    128         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
    129             "Finalizing a dso_transport_t with no corresponding underlying connection - transport: %p.", transport);
    130     }
    131 
    132     mdns_free(transport);
    133 }
    134 
    135 // dso_connect_state_t objects that have been canceled but aren't yet freed.
    136 static dso_connect_state_t *dso_connect_state_needing_clean_up = NULL;
    137 
    138 // We do all of the finalization for the dso state object and any objects it depends on here in the
    139 // dso_idle function because it avoids the possibility that some code on the way out to the event loop
    140 // _after_ the DSO connection has been dropped might still write to the DSO structure or one of the
    141 // dependent structures and corrupt the heap, or indeed in the unlikely event that this memory was
    142 // freed and then reallocated before the exit to the event loop, there could be a bad pointer
    143 // dereference.
    144 //
    145 // If there is a finalize function, that function MUST either free its own state that references the
    146 // DSO state, or else must NULL out the pointer to the DSO state.
    147 int32_t dso_transport_idle(void *context, int32_t now_in, int32_t next_timer_event)
    148 {
    149     dso_connect_state_t *cs, *cnext;
    150     mDNS *m = context;
    151     mDNSs32 now = now_in;
    152     mDNSs32 next_event = next_timer_event;
    153 
    154     // Clean any remaining dso_connect_state_t objects that have been canceled.
    155     for (cs = dso_connect_state_needing_clean_up; cs != NULL; cs = cnext) {
    156         cnext = cs->next;
    157         if (cs->lookup != NULL) {
    158             DNSServiceRef ref = cs->lookup;
    159             cs->lookup = NULL;
    160             // dso_transport_idle runs under KQueueLoop() which holds a mDNS_Lock already, so directly call
    161             // DNSServiceRefDeallocate() will grab the lock again. Given that:
    162             // 1. dso_transport_idle runs under KQueueLoop() that does not traverse any existing mDNSCore structure.
    163             // 2. The work we do here is cleaning up, not starting a new request.
    164             // It is "relatively" safe and reasonable to temporarily unlock the mDNSCore lock here.
    165             mDNS_DropLockBeforeCallback();
    166             MDNS_DISPOSE_DNS_SERVICE_REF(ref);
    167             mDNS_ReclaimLockAfterCallback();
    168         }
    169 
    170         // Remove any remaining unused addresses.
    171         for (dso_transport_address_t **addrs = &cs->addrs; *addrs != NULL; ) {
    172             dso_transport_address_t *addr = *addrs;
    173             *addrs = addr->next;
    174             mdns_free(addr);
    175         }
    176 
    177         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] dso_connect_state_t finalizing - "
    178             "dso_connect: %p, hostname: " PRI_S ", dso_connect->context: %p.", cs->serial, cs, cs->hostname,
    179             cs->context);
    180         // If this connect state object is released before we get canceled event for the underlying nw_connection_t,
    181         // we need to release the reference it holds. The last reference of this nw_connection_t will be released when
    182         // canceled event is delivered.
    183     #if defined(DSO_USES_NETWORK_FRAMEWORK)
    184         MDNS_DISPOSE_NW(cs->connection);
    185     #endif
    186         if (cs->dso_connect_context_callback != NULL) {
    187             cs->dso_connect_context_callback(dso_connect_life_cycle_free, cs->context, cs);
    188         }
    189         mDNSPlatformMemFree(cs);
    190     }
    191     dso_connect_state_needing_clean_up = NULL;
    192 
    193     // Notice if a DSO connection state is active but hasn't seen activity in a while.
    194     for (cs = dso_connect_states; cs != NULL; cs = cnext) {
    195         cnext = cs->next;
    196         if (!cs->connecting && cs->last_event != 0) {
    197             mDNSs32 expiry = cs->last_event + 90 * mDNSPlatformOneSecond;
    198             if (now - expiry > 0) {
    199                 cs->last_event = 0;
    200                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] dso_transport_idle: expiry has happened "
    201                           ": %p, hostname: " PRI_S ", dso_connect->context: %p; now %d expiry %d last_event %d",
    202                           cs->serial, cs, cs->hostname, cs->context, now, expiry, cs->last_event);
    203                 cs->callback(cs->context, NULL, cs->dso, kDSOEventType_ConnectFailed);
    204                 // Should not touch the current dso_connect_state_t after we deliver kDSOEventType_ConnectFailed event,
    205                 // because it is possible that the current dso_connect_state_t has been canceled in the callback.
    206                 // Any status update for the canceled dso_connect_state_t will not work as expected.
    207                 continue;
    208             } else {
    209                 if (next_timer_event - expiry > 0) {
    210                     next_timer_event = expiry;
    211                 }
    212             }
    213         } else if (!cs->connecting && cs->reconnect_time != 0) {
    214             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] reconnect time %d "
    215                       "hostname: " PRI_S ", dso_connect->context: %p.",
    216                       cs->serial, now - cs->reconnect_time, cs->hostname, cs->context);
    217             if (now - cs->reconnect_time > 0) {
    218                 cs->reconnect_time = 0; // Don't try to immediately reconnect if it fails.
    219                 // If cs->dso->transport is non-null, we're already connected.
    220                 if (cs->dso && cs->dso->transport == NULL) {
    221                     cs->callback(cs->context, NULL, cs->dso, kDSOEventType_ShouldReconnect);
    222                 }
    223                 // Should not touch the current dso_connect_state_t after we deliver kDSOEventType_ShouldReconnect event,
    224                 // because it is possible that the current dso_connect_state_t has been canceled in the callback.
    225                 // Any status update for the canceled dso_connect_state_t will not work as expected.
    226                 continue;
    227             }
    228         }
    229         if (cs->reconnect_time != 0 && next_event - cs->reconnect_time > 0) {
    230             next_event = cs->reconnect_time;
    231         }
    232     }
    233 
    234     return next_event;
    235 }
    236 
    237 void dso_reconnect(dso_connect_state_t *cs, dso_state_t *dso)
    238 {
    239     cs->dso = dso;
    240     dso_connect_internal(cs, mDNStrue);
    241 }
    242 
    243 // Call to schedule a reconnect at a later time.
    244 void dso_schedule_reconnect(mDNS *m, dso_connect_state_t *cs, mDNSs32 when)
    245 {
    246     cs->reconnect_time = when * mDNSPlatformOneSecond + m->timenow;
    247     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] scheduling reconnect in %d (%d %d) seconds, "
    248               "hostname: " PRI_S ", dso_connect->context: %p.", cs->serial, when,
    249               m->timenow, cs->reconnect_time, cs->hostname, cs->context);
    250 }
    251 
    252 // If a DSO was created by an incoming connection, the creator of the listener can use this function
    253 // to supply context and a callback for future events.
    254 void dso_set_callback(dso_state_t *dso, void *context, dso_event_callback_t cb)
    255 {
    256     dso->cb = cb;
    257     dso->context = context;
    258 }
    259 
    260 // This is called before writing a DSO message to the output buffer.  length is the length of the message.
    261 // Returns true if we have successfully selected for write (which means that we're under TCP_NOTSENT_LOWAT).
    262 // Otherwise returns false.   It is valid to write even if it returns false, but there is a risk that
    263 // the write will return EWOULDBLOCK, at which point we'd have to blow away the connection.   It is also
    264 // valid to give up at this point and not write a message; as long as dso_write_finish isn't called, a later
    265 // call to dso_write_start will overwrite the length that was stored by the previous invocation.
    266 //
    267 // The circumstance in which this would occur is that we have filled the kernel's TCP output buffer for this
    268 // connection all the way up to TCP_NOTSENT_LOWAT, and then we get a query from the Discovery Proxy to which we
    269 // need to respond.  Because TCP_NOTSENT_LOWAT is fairly low, there should be a lot of room in the TCP output
    270 // buffer for small responses; it would need to be the case that we are getting requests from the proxy at a
    271 // high rate for us to fill the output buffer to the point where a write of a 12-byte response returns
    272 // EWOULDBLOCK; in that case, things are so dysfunctional that killing the connection isn't any worse than
    273 // allowing it to continue.
    274 
    275 // An additional note about the motivation for this code: the idea originally was that we'd do scatter/gather
    276 // I/O here: this lets us write everything out in a single sendmsg() call.   This isn't used with the mDNSPlatformTCP
    277 // code because it doesn't support scatter/gather.   Network Framework does, however, and in principle we could
    278 // write to the descriptor directly if that were really needed.
    279 
    280 bool dso_write_start(dso_transport_t *transport, size_t in_length)
    281 {
    282     // The transport doesn't support messages outside of this range.
    283     if (in_length < 12 || in_length > 65535) {
    284         return false;
    285     }
    286 
    287     const uint16_t length = (uint16_t)in_length;
    288 
    289 #ifdef DSO_USES_NETWORK_FRAMEWORK
    290     uint8_t lenbuf[2];
    291 
    292     if (transport->to_write != NULL) {
    293         nw_release(transport->to_write);
    294         transport->to_write = NULL;
    295     }
    296     lenbuf[0] = length >> 8;
    297     lenbuf[1] = length & 255;
    298     transport->to_write = dispatch_data_create(lenbuf, 2, dso_dispatch_queue,
    299                                                DISPATCH_DATA_DESTRUCTOR_DEFAULT);
    300     if (transport->to_write == NULL) {
    301         transport->write_failed = true;
    302         return false;
    303     }
    304     transport->bytes_to_write = length + 2;
    305 
    306     // We don't have access to TCP_NOTSENT_LOWAT, so for now we track how many bytes we've written
    307     // versus how many bytes that we've written have completed, and if that creeps above MAX_UNSENT_BYTES,
    308     // we return false here to indicate that there is congestion.
    309     if (transport->unsent_bytes > MAX_UNSENT_BYTES) {
    310         return false;
    311     } else {
    312         return true;
    313     }
    314 #else
    315     transport->lenbuf[0] = length >> 8;
    316     transport->lenbuf[1] = length & 255;
    317 
    318     transport->to_write[0] = transport->lenbuf;
    319     transport->write_lengths[0] = 2;
    320     transport->num_to_write = 1;
    321 
    322     return mDNSPlatformTCPWritable(transport->connection);
    323 #endif // DSO_USES_NETWORK_FRAMEWORK
    324 }
    325 
    326 // Called to finish a write (dso_write_start .. dso_write .. [ dso_write ... ] dso_write_finish).  The
    327 // write must completely finish--if we get a partial write, this means that the connection is stalled, and
    328 // so we cancel it.  Since this can call dso_state_cancel, the caller must not reference the DSO state object
    329 // after this call if the return value is false.
    330 bool dso_write_finish(dso_transport_t *transport)
    331 {
    332 #ifdef DSO_USES_NETWORK_FRAMEWORK
    333     const uint32_t serial = transport->dso->serial;
    334     const size_t bytes_to_write = transport->bytes_to_write;
    335     transport->bytes_to_write = 0;
    336     if (transport->write_failed) {
    337         dso_state_cancel(transport->dso);
    338         return false;
    339     }
    340     transport->unsent_bytes += bytes_to_write;
    341     nw_connection_send(transport->connection, transport->to_write, NW_CONNECTION_DEFAULT_STREAM_CONTEXT, true,
    342         ^(nw_error_t  _Nullable error)
    343     {
    344         KQueueLock();
    345         dso_state_t *const dso = dso_find_by_serial(serial);
    346         if (error != NULL) {
    347             const nw_error_t tmp = error;
    348             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSO%u] dso_write_finish: write failed - "
    349                 "error: " PUB_S ".", serial, strerror(nw_error_get_error_code(tmp)));
    350             if (dso != NULL) {
    351                 dso_state_cancel(dso);
    352             }
    353         } else {
    354             if (dso != NULL && dso->transport != NULL) {
    355                 dso->transport->unsent_bytes -= bytes_to_write;
    356                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSO%u] dso_write_finish: completed - "
    357                     "bytes written: %zu, bytes outstanding: %zu.", serial, bytes_to_write,
    358                     dso->transport->unsent_bytes);
    359             } else {
    360                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING,
    361                     "[DSO%u] dso_write_finish: completed but the corresponding dso_state_t has been canceled - "
    362                     "bytes written: %zu.", serial, bytes_to_write);
    363             }
    364         }
    365         KQueueUnlock("dso_write_finish completion routine");
    366     });
    367     nw_release(transport->to_write);
    368     transport->to_write = NULL;
    369     return true;
    370 #else
    371     ssize_t result, total = 0;
    372     int i;
    373 
    374    if (transport->num_to_write > MAX_WRITE_HUNKS) {
    375         LogMsg("dso_write_finish: fatal internal programming error: called %d times (more than limit of %d)",
    376                transport->num_to_write, MAX_WRITE_HUNKS);
    377         dso_state_cancel(transport->dso);
    378         return false;
    379     }
    380 
    381     // This is our ersatz scatter/gather I/O.
    382     for (i = 0; i < transport->num_to_write; i++) {
    383         result = mDNSPlatformWriteTCP(transport->connection, (const char *)transport->to_write[i], transport->write_lengths[i]);
    384         if (result != transport->write_lengths[i]) {
    385             if (result < 0) {
    386                 LogMsg("dso_write_finish: fatal: mDNSPlatformWrite on %s returned %d", transport->dso->remote_name, errno);
    387             } else {
    388                 LogMsg("dso_write_finish: fatal: mDNSPlatformWrite: short write on %s: %ld < %ld",
    389                        transport->dso->remote_name, (long)result, (long)total);
    390             }
    391             dso_state_cancel(transport->dso);
    392             return false;
    393         }
    394     }
    395 #endif
    396     return true;
    397 }
    398 
    399 // This function may only be called after a previous call to dso_write_start; it records the length of and
    400 // pointer to the write buffer.  These buffers must remain valid until dso_write_finish() is called.  The
    401 // caller is responsible for managing the memory they contain.  The expected control flow for writing is:
    402 // dso_write_start(); dso_write(); dso_write(); dso_write(); dso_write_finished(); There should be one or
    403 // more calls to dso_write; these will ideally be translated into a single scatter/gather sendmsg call (or
    404 // equivalent) to the kernel.
    405 void dso_write(dso_transport_t *transport, const uint8_t *buf, size_t length)
    406 {
    407     if (length == 0) {
    408         return;
    409     }
    410 
    411 #ifdef DSO_USES_NETWORK_FRAMEWORK
    412     if (transport->write_failed) {
    413         return;
    414     }
    415     dispatch_data_t dpd = dispatch_data_create(buf, length, dso_dispatch_queue,
    416                                                DISPATCH_DATA_DESTRUCTOR_DEFAULT);
    417     if (dpd == NULL) {
    418         transport->write_failed = true;
    419         return;
    420     }
    421     if (transport->to_write != NULL) {
    422         dispatch_data_t dpc = dispatch_data_create_concat(transport->to_write, dpd);
    423         MDNS_DISPOSE_DISPATCH(dpd);
    424         MDNS_DISPOSE_DISPATCH(transport->to_write);
    425         if (dpc == NULL) {
    426             transport->to_write = NULL;
    427             transport->write_failed = true;
    428             return;
    429         }
    430         transport->to_write = dpc;
    431     }
    432 #else
    433     // We'll report this in dso_write_finish();
    434     if (transport->num_to_write >= MAX_WRITE_HUNKS) {
    435         transport->num_to_write++;
    436         return;
    437     }
    438 
    439     transport->to_write[transport->num_to_write] = buf;
    440     transport->write_lengths[transport->num_to_write] = length;
    441     transport->num_to_write++;
    442 #endif
    443 }
    444 
    445 // Write a DSO message
    446 int dso_message_write(dso_state_t *dso, dso_message_t *msg, bool disregard_low_water)
    447 {
    448     dso_transport_t *transport = dso->transport;
    449     if (transport == NULL || transport->dso == NULL) {
    450         return mStatus_BadStateErr;
    451     }
    452     if (transport->connection != NULL) {
    453         if (dso_write_start(transport, dso_message_length(msg)) || disregard_low_water) {
    454             dso_write(transport, msg->buf, msg->no_copy_bytes_offset);
    455             dso_write(transport, msg->no_copy_bytes, msg->no_copy_bytes_len);
    456             dso_write(transport, &msg->buf[msg->no_copy_bytes_offset], msg->cur - msg->no_copy_bytes_offset);
    457             return dso_write_finish(transport);
    458         }
    459     }
    460     return mStatus_NoMemoryErr;
    461 }
    462 
    463 // Replies to some message we were sent with a response code and no data.
    464 // This is a convenience function for replies that do not require that a new
    465 // packet be constructed.   It takes advantage of the fact that the message
    466 // to which this is a reply is still in the input buffer, and modifies that
    467 // message in place to turn it into a response.
    468 
    469 bool dso_send_simple_response(dso_state_t *dso, int rcode, const DNSMessageHeader *header, const char *pres)
    470 {
    471     dso_transport_t *transport = dso->transport;
    472     (void)pres; // might want this later.
    473     DNSMessageHeader response = *header;
    474 
    475     // The OPCODE is a 4-bit value in DNS message
    476     if (rcode < 0 || rcode > 15) {
    477         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT, "[DSO%u]: rcode[%d] is out of range", dso->serial, rcode);
    478         return false;
    479     }
    480     // Just return the message, with no questions, answers, etc.
    481     response.flags.b[1] = (response.flags.b[1] & ~kDNSFlag1_RC_Mask) | (uint8_t)rcode;
    482     response.flags.b[0] |= kDNSFlag0_QR_Response;
    483     response.numQuestions = 0;
    484     response.numAnswers = 0;
    485     response.numAuthorities = 0;
    486     response.numAdditionals = 0;
    487 
    488     // Buffered write back to discovery proxy
    489     (void)dso_write_start(transport, 12);
    490     dso_write(transport, (uint8_t *)&response, 12);
    491     if (!dso_write_finish(transport)) {
    492         return false;
    493     }
    494     return true;
    495 }
    496 
    497 // DSO Message we received has a primary TLV that's not implemented.
    498 // XXX is this what we're supposed to do here? check draft.
    499 bool dso_send_not_implemented(dso_state_t *dso, const DNSMessageHeader *header)
    500 {
    501     return dso_send_simple_response(dso, kDNSFlag1_RC_DSOTypeNI, header, "DSOTYPENI");
    502 }
    503 
    504 // Non-DSO message we received is refused.
    505 bool dso_send_refused(dso_state_t *dso, const DNSMessageHeader *header)
    506 {
    507     return dso_send_simple_response(dso, kDNSFlag1_RC_Refused, header, "REFUSED");
    508 }
    509 
    510 bool dso_send_formerr(dso_state_t *dso, const DNSMessageHeader *header)
    511 {
    512     return dso_send_simple_response(dso, kDNSFlag1_RC_FormErr, header, "FORMERR");
    513 }
    514 
    515 bool dso_send_servfail(dso_state_t *dso, const DNSMessageHeader *header)
    516 {
    517     return dso_send_simple_response(dso, kDNSFlag1_RC_ServFail, header, "SERVFAIL");
    518 }
    519 
    520 bool dso_send_name_error(dso_state_t *dso, const DNSMessageHeader *header)
    521 {
    522     return dso_send_simple_response(dso, kDNSFlag1_RC_NXDomain, header, "NXDOMAIN");
    523 }
    524 
    525 bool dso_send_no_error(dso_state_t *dso, const DNSMessageHeader *header)
    526 {
    527     return dso_send_simple_response(dso, kDNSFlag1_RC_NoErr, header, "NOERROR");
    528 }
    529 
    530 #ifdef DSO_USES_NETWORK_FRAMEWORK
    531 static void dso_read_message(dso_transport_t *transport, uint32_t length);
    532 
    533 static void dso_read_message_length(dso_transport_t *transport)
    534 {
    535     if (transport == NULL) {
    536         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT,
    537             "dso_read_message_length: dso_transport_t is NULL while reading message");
    538         return;
    539     }
    540 
    541     if (transport->dso == NULL) {
    542         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT,
    543             "dso_read_message_length: transport->dso is NULL while reading message");
    544         return;
    545     }
    546 
    547     const uint32_t serial = transport->dso->serial;
    548     if (transport->connection == NULL) {
    549         LogMsg("dso_read_message_length called with null connection.");
    550         return;
    551     }
    552     nw_connection_receive(transport->connection, 2, 2,
    553                           ^(dispatch_data_t content, nw_content_context_t __unused context,
    554                             bool __unused is_complete, nw_error_t error) {
    555                               dso_state_t *dso;
    556                               // Don't touch anything or look at anything until we have the lock.
    557                               KQueueLock();
    558                               dso = dso_find_by_serial(serial);
    559                               if (error != NULL) {
    560                                   LogMsg("dso_read_message_length: read failed: %s",
    561                                          strerror(nw_error_get_error_code(error)));
    562                               fail:
    563                                   if (dso != NULL) {
    564                                       mDNS_Lock(&mDNSStorage);
    565                                       dso_state_cancel(dso);
    566                                       mDNS_Unlock(&mDNSStorage);
    567                                   }
    568                               } else if (content == NULL) {
    569                                   dso_disconnect_context_t disconnect_context;
    570                                   if (dso != NULL && transport->dso == dso && transport->dso->cb != NULL) {
    571                                       LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
    572                                                 "dso_read_message_length: remote end closed connection: "
    573                                                 "passing disconnect event to callback.");
    574                                       memset(&disconnect_context, 0, sizeof disconnect_context);
    575                                       disconnect_context.reconnect_delay = 1; // reconnect in 1s
    576                                       mDNS_Lock(&mDNSStorage);
    577                                       dso->transport = NULL;
    578                                       nw_connection_cancel(transport->connection);
    579                                       transport->dso->cb(transport->dso->context, &disconnect_context, transport->dso,
    580                                                          kDSOEventType_Disconnected);
    581                                       mDNS_Unlock(&mDNSStorage);
    582                                   } else if (dso != NULL) {
    583                                       LogMsg("dso_read_message_length: remote end closed connection: "
    584                                              "no callback to notify.");
    585                                       mDNS_Lock(&mDNSStorage);
    586                                       dso_state_cancel(dso);
    587                                       mDNS_Unlock(&mDNSStorage);
    588                                   }
    589                               } else if (dso != NULL) {
    590                                   uint32_t length;
    591                                   size_t length_length;
    592                                   const uint8_t *lenbuf;
    593                                   dispatch_data_t map = dispatch_data_create_map(content, (const void **)&lenbuf,
    594                                                                                  &length_length);
    595                                   if (map == NULL) {
    596                                       LogMsg("dso_read_message_length: map create failed");
    597                                       goto fail;
    598                                   } else if (length_length != 2) {
    599                                       LogMsg("dso_read_message_length: invalid length = %d", length_length);
    600                                       MDNS_DISPOSE_DISPATCH(map);
    601                                       goto fail;
    602                                   }
    603                                   length = ((unsigned)(lenbuf[0]) << 8) | ((unsigned)lenbuf[1]);
    604                                   MDNS_DISPOSE_DISPATCH(map);
    605                                   dso_read_message(transport, length);
    606                               } else {
    607                                   LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING,
    608                                       "[DSO%u] dso_read_message_length: the corresponding dso_state_t has been canceled.",
    609                                       serial);
    610                               }
    611                               KQueueUnlock("dso_read_message_length completion routine");
    612                           });
    613 }
    614 
    615 void dso_read_message(dso_transport_t *transport, uint32_t length)
    616 {
    617     if (transport == NULL) {
    618         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT,
    619             "dso_read_message: dso_transport_t is NULL while reading message");
    620         return;
    621     }
    622     if (transport->dso == NULL) {
    623         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT,
    624             "dso_read_message: transport->dso is NULL while reading message");
    625         return;
    626     }
    627 
    628     const uint32_t serial = transport->dso->serial;
    629     if (transport->connection == NULL) {
    630         LogMsg("dso_read_message called with null connection.");
    631         return;
    632     }
    633     nw_connection_receive(transport->connection, length, length,
    634                           ^(dispatch_data_t content, nw_content_context_t __unused context,
    635                             bool __unused is_complete, nw_error_t error) {
    636                               dso_state_t *dso;
    637                               // Don't touch anything or look at anything until we have the lock.
    638                               KQueueLock();
    639                               dso = dso_find_by_serial(serial);
    640                               if (error != NULL) {
    641                                   LogMsg("dso_read_message: read failed: %s", strerror(nw_error_get_error_code(error)));
    642                               fail:
    643                                   if (dso != NULL) {
    644                                       mDNS_Lock(&mDNSStorage);
    645                                       dso_state_cancel(dso);
    646                                       mDNS_Unlock(&mDNSStorage);
    647                                   }
    648                               } else if (content == NULL) {
    649                                   dso_disconnect_context_t disconnect_context;
    650                                   if (dso != NULL && transport->dso == dso && transport->dso->cb != NULL) {
    651                                       LogMsg("dso_read_message: remote end closed connection: "
    652                                              "passing disconnect event to callback.");
    653                                       memset(&disconnect_context, 0, sizeof disconnect_context);
    654                                       disconnect_context.reconnect_delay = 1; // reconnect in 1s
    655                                       mDNS_Lock(&mDNSStorage);
    656                                       dso->transport = NULL;
    657                                       nw_connection_cancel(transport->connection);
    658                                       transport->dso->cb(transport->dso->context, &disconnect_context, transport->dso,
    659                                                          kDSOEventType_Disconnected);
    660                                       mDNS_Unlock(&mDNSStorage);
    661                                   } else if (dso != NULL) {
    662                                       LogMsg("dso_read_message: remote end closed connection: "
    663                                              "no callback to notify.");
    664                                       mDNS_Lock(&mDNSStorage);
    665                                       dso_state_cancel(dso);
    666                                       mDNS_Unlock(&mDNSStorage);
    667                                   }
    668                               } else if (dso != NULL) {
    669                                   dso_message_payload_t message;
    670                                   dispatch_data_t map = dispatch_data_create_map(content,
    671                                                                                  (const void **)&message.message, &message.length);
    672                                   if (map == NULL) {
    673                                       LogMsg("dso_read_message_length: map create failed");
    674                                       goto fail;
    675                                   } else if (message.length != length) {
    676                                       LogMsg("dso_read_message_length: only %d of %d bytes read", message.length, length);
    677                                       MDNS_DISPOSE_DISPATCH(map);
    678                                       goto fail;
    679                                   }
    680                                   // Process the message.
    681                                   mDNS_Lock(&mDNSStorage);
    682                                   dns_message_received(dso, message.message, message.length, &message);
    683                                   mDNS_Unlock(&mDNSStorage);
    684 
    685                                   // Release the map object now that we no longer need its buffers.
    686                                   MDNS_DISPOSE_DISPATCH(map);
    687 
    688                                   // Now read the next message length.
    689                                   dso_read_message_length(transport);
    690                               } else {
    691                                   LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_WARNING,
    692                                       "[DSO%u] dso_read_message: the corresponding dso_state_t has been canceled.",
    693                                       serial);
    694                               }
    695                               KQueueUnlock("dso_read_message completion routine");
    696                           });
    697 }
    698 #else
    699 // Called whenever there's data available on a DSO connection
    700 void dso_read_callback(TCPSocket *sock, void *context, mDNSBool connection_established, int err)
    701 {
    702     dso_transport_t *transport = context;
    703     dso_state_t *dso;
    704     mDNSBool closed = mDNSfalse;
    705 
    706     mDNS_Lock(&mDNSStorage);
    707     dso = transport->dso;
    708 
    709     // This shouldn't ever happen.
    710     if (err) {
    711         LogMsg("dso_read_callback: error %d", err);
    712         dso_state_cancel(dso);
    713         goto out;
    714     }
    715 
    716     // Connection is already established by the time we set this up.
    717     if (connection_established) {
    718         goto out;
    719     }
    720 
    721     // This will be true either if we have never read a message or
    722     // if the last thing we did was to finish reading a message and
    723     // process it.
    724     if (transport->message_length == 0) {
    725         transport->need_length = true;
    726         transport->inbufp = transport->inbuf;
    727         transport->bytes_needed = 2;
    728     }
    729 
    730     // Read up to bytes_needed bytes.
    731     ssize_t count = mDNSPlatformReadTCP(sock, transport->inbufp, transport->bytes_needed, &closed);
    732     // LogMsg("read(%d, %p:%p, %d) -> %d", fd, dso->inbuf, dso->inbufp, dso->bytes_needed, count);
    733     if (count < 0) {
    734         LogMsg("dso_read_callback: read from %s returned %d", dso->remote_name, errno);
    735         dso_state_cancel(dso);
    736         goto out;
    737     }
    738 
    739     // If we get selected for read and there's nothing to read, the remote end has closed the
    740     // connection.
    741     if (closed) {
    742         LogMsg("dso_read_callback: remote %s closed", dso->remote_name);
    743         dso_state_cancel(dso);
    744         goto out;
    745     }
    746 
    747     transport->inbufp += count;
    748     transport->bytes_needed -= count;
    749 
    750     // If we read all the bytes we wanted, do what's next.
    751     if (transport->bytes_needed == 0) {
    752         // We just finished reading the complete length of a DNS-over-TCP message.
    753         if (transport->need_length) {
    754             // Get the number of bytes in this DNS message
    755             size_t bytes_needed = (((size_t)transport->inbuf[0]) << 8) | transport->inbuf[1];
    756 
    757             // Under no circumstances can length be zero.
    758             if (bytes_needed == 0) {
    759                 LogMsg("dso_read_callback: %s sent zero-length message.", dso->remote_name);
    760                 dso_state_cancel(dso);
    761                 goto out;
    762             }
    763 
    764             // The input buffer size is AbsoluteMaxDNSMessageData, which is around 9000 bytes on
    765             // big platforms and around 1500 bytes on smaller ones.   If the remote end has sent
    766             // something larger than that, it's an error from which we can't recover.
    767             if (bytes_needed > transport->inbuf_size - 2) {
    768                 LogMsg("dso_read_callback: fatal: Proxy at %s sent a too-long (%zd bytes) message",
    769                        dso->remote_name, bytes_needed);
    770                 dso_state_cancel(dso);
    771                 goto out;
    772             }
    773 
    774             transport->message_length = bytes_needed;
    775             transport->bytes_needed = bytes_needed;
    776             transport->inbufp = transport->inbuf + 2;
    777             transport->need_length = false;
    778 
    779         // We just finished reading a complete DNS-over-TCP message.
    780         } else {
    781             dso_message_payload_t message = { &transport->inbuf[2], transport->message_length };
    782             dns_message_received(dso, message.message, message.length, &message);
    783             transport->message_length = 0;
    784         }
    785     }
    786 out:
    787     mDNS_Unlock(&mDNSStorage);
    788 }
    789 #endif // DSO_USES_NETWORK_FRAMEWORK
    790 
    791 #ifdef DSO_USES_NETWORK_FRAMEWORK
    792 static dso_transport_t *dso_transport_create(nw_connection_t connection, bool is_server, void *context,
    793     const dso_life_cycle_context_callback_t context_callback, int max_outstanding_queries, size_t outbuf_size_in,
    794     const char *remote_name, dso_event_callback_t cb, dso_state_t *dso)
    795 {
    796     dso_transport_t *transport;
    797     uint8_t *transp;
    798     const size_t outbuf_size = outbuf_size_in + 256; // Space for additional TLVs
    799 
    800     // We allocate everything in a single hunk so that we can free it together as well.
    801     transp = mallocL("dso_transport_create", (sizeof *transport) + outbuf_size);
    802     if (transp == NULL) {
    803         transport = NULL;
    804         goto out;
    805     }
    806     // Don't clear the buffers.
    807     mDNSPlatformMemZero(transp, sizeof (*transport));
    808 
    809     transport = (dso_transport_t *)transp;
    810     transp += sizeof *transport;
    811 
    812     transport->outbuf = transp;
    813     transport->outbuf_size = outbuf_size;
    814 
    815     if (dso == NULL) {
    816         transport->dso = dso_state_create(is_server, max_outstanding_queries, remote_name, cb, context, context_callback,
    817                                           transport);
    818         if (transport->dso == NULL) {
    819             mDNSPlatformMemFree(transport);
    820             transport = NULL;
    821             goto out;
    822         }
    823     } else {
    824         transport->dso = dso;
    825     }
    826     transport->connection = connection;
    827     nw_retain(transport->connection);
    828 
    829     // Used to uniquely mark dso_transport_t objects, incremented once for each dso_transport_t created.
    830     // DSO_TRANSPORT_INVALID_SERIAL(0) is used to identify the invalid dso_transport_t.
    831     static uint32_t dso_transport_serial = DSO_TRANSPORT_INVALID_SERIAL + 1;
    832     transport->serial = dso_transport_serial++;
    833 
    834     transport->dso->transport = transport;
    835     transport->dso->transport_finalize = dso_transport_finalize;
    836     transport->next = dso_transport_states;
    837     dso_transport_states = transport;
    838 
    839     // Start looking for messages...
    840     dso_read_message_length(transport);
    841     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSO%u->DSOT%u] New dso_transport_t created - "
    842         "transport: %p, remote_name: " PRI_S ".", transport->dso->serial, transport->serial, transport, remote_name);
    843 out:
    844     return transport;
    845 }
    846 #else
    847 // Create a dso_transport_t structure
    848 static dso_transport_t *dso_transport_create(TCPSocket *sock, bool is_server, void *context,
    849     const dso_life_cycle_context_callback_t context_callback, int max_outstanding_queries, size_t inbuf_size_in,
    850     size_t outbuf_size_in, const char *remote_name, dso_event_callback_t cb, dso_state_t *dso)
    851 {
    852     dso_transport_t *transport;
    853     size_t outbuf_size;
    854     size_t inbuf_size;
    855     uint8_t *transp;
    856     int status;
    857 
    858     // There's no point in a DSO that doesn't have a callback.
    859     if (!cb) {
    860         return NULL;
    861     }
    862 
    863     outbuf_size = outbuf_size_in + 256; // Space for additional TLVs
    864     inbuf_size = inbuf_size_in + 2;   // Space for length
    865 
    866     // We allocate everything in a single hunk so that we can free it together as well.
    867     transp = mallocL("dso_transport_create", (sizeof *transport) + inbuf_size + outbuf_size);
    868     if (transp == NULL) {
    869         transport = NULL;
    870         goto out;
    871     }
    872     // Don't clear the buffers.
    873     mDNSPlatformMemZero(transp, sizeof (*transport));
    874 
    875     transport = (dso_transport_t *)transp;
    876     transp += sizeof *transport;
    877 
    878     transport->inbuf = transp;
    879     transport->inbuf_size = inbuf_size;
    880     transp += inbuf_size;
    881 
    882     transport->outbuf = transp;
    883     transport->outbuf_size = outbuf_size;
    884 
    885     if (dso == NULL) {
    886         transport->dso = dso_state_create(is_server, max_outstanding_queries, remote_name, cb, context, context_callback,
    887                                           transport);
    888         if (transport->dso == NULL) {
    889             mDNSPlatformMemFree(transport);
    890             transport = NULL;
    891             goto out;
    892         }
    893     } else {
    894         transport->dso = dso;
    895     }
    896     transport->connection = sock;
    897 
    898     // Used to uniquely mark dso_transport_t objects, incremented once for each dso_transport_t created.
    899     // DSO_TRANSPORT_INVALID_SERIAL(0) is used to identify the invalid dso_transport_t.
    900     static uint32_t dso_transport_serial = DSO_TRANSPORT_INVALID_SERIAL + 1;
    901     transport->serial = dso_transport_serial++;
    902 
    903     status = mDNSPlatformTCPSocketSetCallback(sock, dso_read_callback, transport);
    904     if (status != mStatus_NoError) {
    905         LogMsg("dso_state_create: unable to set callback: %d", status);
    906         dso_state_cancel(transport->dso);
    907         goto out;
    908     }
    909 
    910     transport->dso->transport = transport;
    911     transport->dso->transport_finalize = dso_transport_finalize;
    912     transport->next = dso_transport_states;
    913     dso_transport_states = transport;
    914 out:
    915     return transport;
    916 }
    917 #endif // DSO_USES_NETWORK_FRAMEWORK
    918 
    919 // This should all be replaced with Network Framework connection setup.
    920 dso_connect_state_t *dso_connect_state_create(
    921     const char *hostname, mDNSAddr *addr, mDNSIPPort port,
    922     int max_outstanding_queries, size_t inbuf_size, size_t outbuf_size,
    923     dso_event_callback_t callback,
    924     dso_state_t *dso, void *context,
    925     const dso_life_cycle_context_callback_t dso_context_callback,
    926     const dso_connect_life_cycle_context_callback_t dso_connect_context_callback,
    927     const char *detail)
    928 {
    929     size_t detlen = strlen(detail) + 1;
    930     size_t hostlen = hostname == NULL ? 0 : strlen(hostname) + 1;
    931     size_t len;
    932     dso_connect_state_t *cs = NULL;
    933     dso_connect_state_t *cs_to_return = NULL;
    934     char *csp;
    935     char nbuf[INET6_ADDRSTRLEN + 1];
    936     dso_connect_state_t **states;
    937 
    938     // Enforce Some Minimums (Xxx these are a bit arbitrary, maybe not worth doing?)
    939     if (inbuf_size < MaximumRDSize || outbuf_size < 128 || max_outstanding_queries < 1) {
    940         goto exit;
    941     }
    942 
    943     // If we didn't get a hostname, make a presentation form of the IP address to use instead.
    944     if (!hostlen) {
    945         if (addr != NULL) {
    946             if (addr->type == mDNSAddrType_IPv4) {
    947                 hostname = inet_ntop(AF_INET, &addr->ip.v4, nbuf, sizeof nbuf);
    948             } else {
    949                 hostname = inet_ntop(AF_INET6, &addr->ip.v6, nbuf, sizeof nbuf);
    950             }
    951             if (hostname != NULL) {
    952                 hostlen = strlen(nbuf);
    953             }
    954         }
    955     }
    956     // If we don't have a printable name, we won't proceed, because this means we don't know
    957     // what to connect to.
    958     if (!hostlen) {
    959         goto exit;
    960     }
    961 
    962     len = (sizeof *cs) + detlen + hostlen;
    963     csp = mdns_malloc(len);
    964     if (!csp) {
    965         goto exit;
    966     }
    967     cs = (dso_connect_state_t *)csp;
    968     memset(cs, 0, sizeof *cs);
    969     csp += sizeof *cs;
    970 
    971     cs->detail = csp;
    972     memcpy(cs->detail, detail, detlen);
    973     csp += detlen;
    974     cs->hostname = csp;
    975     memcpy(cs->hostname, hostname, hostlen);
    976 
    977     // Used to uniquely mark dso_connect_state_t objects, incremented once for each dso_connect_state_t created.
    978     // DSO_CONNECT_STATE_INVALID_SERIAL(0) is used to identify the invalid dso_connect_state_t.
    979     static uint32_t dso_connect_state_serial = DSO_CONNECT_STATE_INVALID_SERIAL + 1;
    980     cs->serial = dso_connect_state_serial++;
    981 
    982     cs->config_port = port;
    983     cs->max_outstanding_queries = max_outstanding_queries;
    984     cs->outbuf_size = outbuf_size;
    985     if (context) {
    986         cs->context = context;
    987     } // else cs->context = NULL because of memset call above.
    988     if (dso_context_callback != NULL) {
    989         cs->dso_context_callback = dso_context_callback;
    990     }
    991     if (dso_connect_context_callback != NULL) {
    992         cs->dso_connect_context_callback = dso_connect_context_callback;
    993         dso_connect_context_callback(dso_connect_life_cycle_create, context, cs);
    994     }
    995     cs->callback = callback;
    996     cs->connect_port.NotAnInteger = 0;
    997     cs->dso = dso;
    998 #ifndef DSO_USES_NETWORK_FRAMEWORK
    999     cs->inbuf_size = inbuf_size;
   1000 #endif
   1001 
   1002     if (addr != NULL) {
   1003         dso_connect_state_process_address_port_change(addr, port, mDNStrue, cs);
   1004         if (cs->addrs == NULL) {
   1005             goto exit;
   1006         }
   1007     }
   1008 
   1009     // cs->canceled must be set to false here, because we use it to determine if the current dso_connect_state_t is
   1010     // still valid. We do not need to set it explicitly because the memset(cs, 0, sizeof *cs); above will initialize it
   1011     // to 0(false).
   1012     // cs->canceled = mDNSfalse;
   1013 
   1014     for (states = &dso_connect_states; *states != NULL; states = &(*states)->next)
   1015         ;
   1016     *states = cs;
   1017 
   1018     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSO%u->DSOC%u] New dso_connect_state_t created - "
   1019         "dso_connect: %p, hostname: " PUB_S ", context: %p.", dso->serial, cs->serial, cs, hostname, context);
   1020 
   1021     cs_to_return = cs;
   1022     cs = NULL;
   1023 
   1024 exit:
   1025     if (cs != NULL) {
   1026         mdns_free(cs->addrs);
   1027     }
   1028     mdns_free(cs);
   1029     return cs_to_return;
   1030 }
   1031 
   1032 #ifdef DSO_USES_NETWORK_FRAMEWORK
   1033 void dso_connect_state_use_tls(dso_connect_state_t *cs)
   1034 {
   1035     cs->tls_enabled = true;
   1036 }
   1037 #endif
   1038 
   1039 void
   1040 dso_connect_state_cancel(dso_connect_state_t *const cs)
   1041 {
   1042     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] Canceling dso_connect_state_t.", cs->serial);
   1043 
   1044     // Remove the dso_connect_state_t from the main dso_connect_states list.
   1045     dso_connect_state_t **cs_pp;
   1046     for (cs_pp = &dso_connect_states; *cs_pp != NULL && *cs_pp != cs; cs_pp = &(*cs_pp)->next)
   1047         ;
   1048     if (*cs_pp != NULL) {
   1049         *cs_pp = cs->next;
   1050     } else {
   1051         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
   1052             "[DSOC%u] Canceling a dso_connect_state_t that is not in the dso_connect_states list - host name: " PRI_S
   1053             ", detail: " PUB_S ".", cs->serial, cs->hostname, cs->detail);
   1054     }
   1055 
   1056 #ifdef DSO_USES_NETWORK_FRAMEWORK
   1057     // Cancel the underlying nw_connection_t.
   1058     if (cs->connection != NULL) {
   1059         nw_connection_cancel(cs->connection);
   1060     }
   1061     if (cs->transport != NULL && cs->transport->connection != NULL) {
   1062         nw_connection_cancel(cs->transport->connection);
   1063     }
   1064 #endif
   1065 
   1066     // We cannot call `DNSServiceRefDeallocate(cs->lookup);` directly to cancel the address lookup, because we are
   1067     // holding the mDNS_Lock when calling the function. And it is also possible that we are traversing some mDNSCore
   1068     // data structures while calling it, so use mDNS_DropLockBeforeCallback is not correct either.
   1069 
   1070     if (cs->dso_connect_context_callback != NULL) {
   1071         cs->dso_connect_context_callback(dso_connect_life_cycle_cancel, cs->context, cs);
   1072     }
   1073 
   1074     // Invalidate this dso_connect_state_t object, so that when we get a callback for dso_inaddr_callback(), we can skip
   1075     // the callback for the canceled dso_connect_state_t object.
   1076     cs->canceled = mDNStrue;
   1077 
   1078     // We leave cs and cs->lookup to be freed by dso_transport_idle.
   1079     cs->next = dso_connect_state_needing_clean_up;
   1080     dso_connect_state_needing_clean_up = cs;
   1081 }
   1082 
   1083 #ifdef DSO_USES_NETWORK_FRAMEWORK
   1084 static void
   1085 dso_connection_succeeded(dso_connect_state_t *cs)
   1086 {
   1087     // We got a connection.
   1088     dso_transport_t *transport =
   1089         dso_transport_create(cs->connection, false, cs->context, cs->dso_context_callback,
   1090             cs->max_outstanding_queries, cs->outbuf_size, cs->hostname, cs->callback, cs->dso);
   1091     if (transport == NULL) {
   1092         // If dso_transport_create fails, there's no point in continuing to try to connect to new
   1093         // addresses
   1094         LogMsg("dso_connection_succeeded: dso_state_create failed");
   1095         // XXX we didn't retain the connection, so we're done when it goes out of scope, right?
   1096     } else {
   1097         // Call the "we're connected" callback, which will start things up.
   1098         transport->dso->cb(cs->context, NULL, transport->dso, kDSOEventType_Connected);
   1099     }
   1100 
   1101     cs->last_event = 0;
   1102     // Remember the transport on the connect state so that we can cancel it when needed.
   1103     cs->transport = transport;
   1104     return;
   1105 }
   1106 
   1107 static bool tls_cert_verify(const sec_protocol_metadata_t metadata, const sec_trust_t trust_ref,
   1108     const bool trusts_alternative_trusted_server_certificates, const uint32_t cs_serial)
   1109 {
   1110     bool valid;
   1111 
   1112     // When iCloud keychain is enabled, it is possible that the TLS certificate that DNS push server
   1113     // uses has been synced to the client device, so we do the evaluation here.
   1114     const tls_keychain_context_t context = {metadata, trust_ref, trusts_alternative_trusted_server_certificates};
   1115     valid = tls_cert_evaluate(&context);
   1116     if (valid) {
   1117         // If the evaluation succeeds, it means that the DNS push server that mDNSResponder is
   1118         // talking to, is registered under the same iCloud account. Therefore, it is trustworthy.
   1119         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1120                   "[DSOC%u] TLS certificate evaluation SUCCEEDS, the DNS push server is trustworthy.",
   1121                   cs_serial);
   1122     } else {
   1123         // If the evaluation fails, it means that, the DNS push server that mDNSResponder is
   1124         // talking to, is not registered under the same iCloud account or the user does not enable iCloud Keychain.
   1125         // Case 1: The DNS push server is not owned by the user making the request. For example,
   1126         // a user goes to other's home and trying to discover the services there.
   1127         // Case 2: The DNS push server is owned by the client, but does not enable iCloud Keychain.
   1128         // Case 3: The DNS push server is a malicious server.
   1129         // Case 4: The user does not enable iCloud Keychain, thus the TLS certificate on the client may be out of date,
   1130         // or not available due to no certificate syncing.
   1131         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1132                   "[DSOC%u] TLS certificate evaluation FAILS, the DNS push server is not trustworthy.",
   1133                   cs_serial);
   1134     }
   1135 
   1136     return valid;
   1137 }
   1138 
   1139 static void dso_connect_internal(dso_connect_state_t *cs, mDNSBool reconnecting)
   1140 {
   1141     uint32_t serial = cs->serial;
   1142     nw_endpoint_t endpoint = NULL;
   1143     nw_parameters_t parameters = NULL;
   1144     nw_connection_t connection = NULL;
   1145 
   1146     cs->last_event = mDNSStorage.timenow;
   1147 
   1148     // If we do not have more address to connect.
   1149     if (cs->next_addr == NULL) {
   1150         if (reconnecting) {
   1151             if (cs->addrs != NULL) {
   1152                 dso_disconnect_context_t context;
   1153                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "[DSOC%u] At end of address list, delaying retry - "
   1154                           "server name: " PRI_S ".", cs->serial, cs->hostname);
   1155                 cs->last_event = 0;
   1156                 context.reconnect_delay = 60; // Wait one minute before attempting to reconnect.
   1157                 cs->callback(cs->context, &context, cs->dso, kDSOEventType_Disconnected);
   1158                 cs->next_addr = cs->addrs;
   1159             } else {
   1160                 // Otherwise, we will get more callbacks when outstanding queries either fail or succeed.
   1161                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1162                           "[DSOC%u] Waiting for newly resolved address to connect - server name: " PRI_S ".",
   1163                           cs->serial, cs->hostname);
   1164             }
   1165         } else {
   1166             // The expectation is that if we are connecting to a DSO server, we really should succeed. If we
   1167             // don't succeed in connecting to any of the advertised servers, it's a good assumption that it's
   1168             // not working, so we should give up, rather than continuing forever to try.
   1169             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR, "[DSOC%u] No more addresses to try - "
   1170                       "server name: " PRI_S ".", cs->serial, cs->hostname);
   1171             cs->last_event = 0;
   1172             cs->callback(cs->context, NULL, cs->dso, kDSOEventType_ConnectFailed);
   1173         }
   1174         goto exit;
   1175     }
   1176 
   1177     // Always use the first address in the list to set up the connection.
   1178     const dso_transport_address_t *dest_addr = cs->next_addr;
   1179 
   1180     const mDNSAddr addr = dest_addr->address;
   1181     const mDNSIPPort port = dest_addr->port;
   1182 
   1183     char addrbuf[INET6_ADDRSTRLEN + 1];
   1184     char portbuf[6];
   1185     get_address_string_from_mDNSAddr(&addr, addrbuf);
   1186     snprintf(portbuf, sizeof(portbuf), "%u", mDNSVal16(port));
   1187 
   1188     endpoint = nw_endpoint_create_host(addrbuf, portbuf);
   1189     if (endpoint == NULL) {
   1190         goto exit;
   1191     }
   1192 
   1193     nw_parameters_configure_protocol_block_t configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL;
   1194     if (cs->tls_enabled) {
   1195         const uint32_t cs_serial = cs->serial;
   1196         bool trusts_alternative_server_certificates = false;
   1197     #if MDNSRESPONDER_SUPPORTS(APPLE, TERMINUS_ASSISTED_UNICAST_DISCOVERY)
   1198         trusts_alternative_server_certificates = cs->trusts_alternative_server_certificates;
   1199     #endif
   1200         configure_tls = ^(nw_protocol_options_t tls_options) {
   1201             sec_protocol_options_t sec_options = nw_tls_copy_sec_protocol_options(tls_options);
   1202             sec_protocol_options_set_verify_block(sec_options,
   1203                 ^(sec_protocol_metadata_t metadata, sec_trust_t trust_ref, sec_protocol_verify_complete_t complete)
   1204                 {
   1205                     const bool valid = tls_cert_verify(metadata, trust_ref, trusts_alternative_server_certificates,
   1206                         cs_serial);
   1207                     complete(valid);
   1208                 },
   1209                 dso_dispatch_queue);
   1210             sec_release(sec_options);
   1211         };
   1212     }
   1213     parameters = nw_parameters_create_secure_tcp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION);
   1214     if (parameters == NULL) {
   1215         goto exit;
   1216     }
   1217 
   1218     // connection now holds a reference to the nw_connection.
   1219     // It holds the reference during the entire life time of the nw_connection_t, until it is canceled.
   1220     connection = nw_connection_create(endpoint, parameters);
   1221     if (connection == NULL) {
   1222         goto exit;
   1223     }
   1224 
   1225     const uint64_t nw_connection_id = nw_connection_get_id(connection);
   1226 
   1227     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u->C%" PRIu64 "] Connecting to the server - "
   1228         "server: " PRI_IP_ADDR ":%u.", cs->serial, nw_connection_id, &addr, mDNSVal16(port));
   1229 
   1230     nw_connection_set_queue(connection, dso_dispatch_queue);
   1231     nw_connection_set_state_changed_handler(
   1232         connection, ^(nw_connection_state_t state, nw_error_t error) {
   1233             dso_connect_state_t *ncs;
   1234             KQueueLock();
   1235             ncs = dso_connect_state_find(serial); // Might have been canceled.
   1236             if (ncs == NULL) {
   1237                 // If we cannot find dso_connect_state_t in the system's list, it means that we have already canceled it
   1238                 // in dso_connect_state_cancel() including the corresponding nw_connection_t. Therefore, there is no
   1239                 // need to cancel the nw_connection_t again.
   1240                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u->C%" PRIu64
   1241                           "] No connect state found - nw_connection_state_t: " PUB_S ".",
   1242                           serial, nw_connection_id, nw_connection_state_to_string(state));
   1243             } else {
   1244                 if (state == nw_connection_state_waiting) {
   1245                     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1246                               "[DSOC%u->C%" PRIu64 "] Connection to server: " PRI_IP_ADDR ":%u waiting.",
   1247                               serial, nw_connection_id, &addr, mDNSVal16(port));
   1248 
   1249                     // XXX the right way to do this is to just let NW Framework wait until we get a connection,
   1250                     // but there are a bunch of problems with that right now.   First, will we get "waiting" on
   1251                     // every connection we try?   We aren't relying on NW Framework for DNS lookups, so we are
   1252                     // connecting to an IP address, not a host, which means in principle that a later IP address
   1253                     // might be reachable.   So we have to stop trying on this one to try that one.   Oops.
   1254                     // Once we get NW Framework to use internal calls to resolve names, we can fix this.
   1255                     // Second, maybe we want to switch to polling if this happens.   Probably not, but we need
   1256                     // to think this through.   So right now we're just using the semantics of regular sockets,
   1257                     // which we /have/ thought through.   So in the future we should do this think-through and
   1258                     // try to use NW Framework as it's intended to work rather than as if it were just sockets.
   1259                     nw_connection_cancel(connection);
   1260                 } else if (state == nw_connection_state_failed) {
   1261                     // We tried to connect, but didn't succeed.
   1262                     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1263                               "[DSOC%u->C%" PRIu64 "] Connection to server: " PRI_IP_ADDR ":%u failed, error:"
   1264                               PUB_S ", detail: " PUB_S ".", serial, nw_connection_id,
   1265                               &addr, mDNSVal16(port), strerror(nw_error_get_error_code(error)), ncs->detail);
   1266                     nw_connection_cancel(connection);
   1267                 } else if (state == nw_connection_state_ready) {
   1268                     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1269                         "[DSOC%u->C%" PRIu64 "] Connection to server: " PRI_IP_ADDR ":%u ready.",
   1270                         serial, nw_connection_id, &addr, mDNSVal16(port));
   1271 
   1272                     // Get the interface index from the path.
   1273                     nw_path_t curr_path = nw_connection_copy_current_path(connection);
   1274                     if (curr_path) {
   1275                         ncs->if_idx = nw_path_get_interface_index(curr_path);
   1276                     }
   1277                     MDNS_DISPOSE_NW(curr_path);
   1278 
   1279                     ncs->connecting = mDNSfalse;
   1280                     mDNS_Lock(&mDNSStorage);
   1281                     dso_connection_succeeded(ncs);
   1282                     mDNS_Unlock(&mDNSStorage);
   1283                 } else if (state == nw_connection_state_cancelled) {
   1284                     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1285                         "[DSOC%u->C%" PRIu64 "] Connection to server: " PRI_IP_ADDR ":%u canceled.",
   1286                         serial, nw_connection_id, &addr, mDNSVal16(port));
   1287                     if (ncs->transport) {
   1288                         MDNS_DISPOSE_NW(ncs->transport->connection);
   1289                         // If there is a dso state on the connect state and it is referencing this transport,
   1290                         // remove the reference.
   1291                         if (ncs->dso != NULL && ncs->dso->transport == ncs->transport) {
   1292                             ncs->dso->transport = NULL;
   1293                         }
   1294                         // If the dso_state_t is still referencing this transport, remove the reference.
   1295                         if (ncs->transport->dso != NULL && ncs->transport->dso->transport == ncs->transport) {
   1296                             ncs->transport->dso->transport = NULL;
   1297                         }
   1298                         dso_transport_finalize(ncs->transport, "dso_connect_internal");
   1299                         ncs->transport = NULL;
   1300                     }
   1301                     MDNS_DISPOSE_NW(ncs->connection);
   1302                     if (ncs->connecting) {
   1303                         ncs->connecting = mDNSfalse;
   1304                         // If we get here and cs exists, we are still trying to connect.   So do the next step.
   1305                         mDNS_Lock(&mDNSStorage);
   1306                         dso_connect_internal(ncs, reconnecting);
   1307                         mDNS_Unlock(&mDNSStorage);
   1308                     }
   1309                 }
   1310 
   1311                 // Except for the state nw_connection_state_ready, all the other states mean that the nw_connection is
   1312                 // not ready for use. Therefore, it is no longer correct to say that we have an established session.
   1313                 // In which case, set has_session to false.
   1314                 if (state != nw_connection_state_ready) {
   1315                     if (ncs->dso != NULL) {
   1316                         ncs->dso->has_session = false;
   1317                     }
   1318                 }
   1319             }
   1320 
   1321             // Release the nw_connection_t reference held by `connection`, the nw_release here always releases the last
   1322             // reference we have for the nw_connection_t.
   1323             if ((state == nw_connection_state_cancelled) && connection) {
   1324                 nw_release(connection);
   1325             }
   1326 
   1327             KQueueUnlock("dso_connect_internal state change handler");
   1328         });
   1329     nw_connection_start(connection);
   1330     cs->connecting = mDNStrue;
   1331 
   1332     // Connect state now also holds a reference to the nw_connection.
   1333     cs->connection = connection;
   1334     nw_retain(cs->connection);
   1335 
   1336     // We finished setting up the connection with the first address in the list, so remove it from the list.
   1337     cs->next_addr = dest_addr->next;
   1338     dest_addr = NULL;
   1339 exit:
   1340     MDNS_DISPOSE_NW(endpoint);
   1341     MDNS_DISPOSE_NW(parameters);
   1342 }
   1343 
   1344 #else
   1345 static void dso_connect_callback(TCPSocket *sock, void *context, mDNSBool connected, int err)
   1346 {
   1347     dso_connect_state_t *cs = context;
   1348     char *detail;
   1349     int status;
   1350     dso_transport_t *transport;
   1351     mDNS *m = &mDNSStorage;
   1352 
   1353     (void)connected;
   1354     mDNS_Lock(m);
   1355     detail = cs->detail;
   1356 
   1357     // If we had a socket open but the connect failed, close it and try the next address, if we have
   1358     // a next address.
   1359     if (sock != NULL) {
   1360         cs->last_event = m->timenow;
   1361 
   1362         cs->connecting = mDNSfalse;
   1363         if (err != mStatus_NoError) {
   1364             mDNSPlatformTCPCloseConnection(sock);
   1365             LogMsg("dso_connect_callback: connect %p failed (%d)", cs, err);
   1366         } else {
   1367         success:
   1368             // We got a connection.
   1369             transport = dso_transport_create(sock, false, cs->context, cs->dso_context_callback,
   1370                 cs->max_outstanding_queries, cs->inbuf_size, cs->outbuf_size, cs->hostname, cs->callback, cs->dso);
   1371             if (transport == NULL) {
   1372                 // If dso_state_create fails, there's no point in continuing to try to connect to new
   1373                 // addresses
   1374             fail:
   1375                 LogMsg("dso_connect_callback: dso_state_create failed");
   1376                 mDNSPlatformTCPCloseConnection(sock);
   1377             } else {
   1378                 // Call the "we're connected" callback, which will start things up.
   1379                 transport->dso->cb(cs->context, NULL, transport->dso, kDSOEventType_Connected);
   1380             }
   1381 
   1382             cs->last_event = 0;
   1383 
   1384             // When the connection has succeeded, stop asking questions.
   1385             if (cs->lookup != NULL) {
   1386                 DNSServiceRef ref = cs->lookup;
   1387                 cs->lookup = NULL;
   1388                 mDNS_DropLockBeforeCallback();
   1389                 DNSServiceRefDeallocate(ref);
   1390                 mDNS_ReclaimLockAfterCallback();
   1391             }
   1392             mDNS_Unlock(m);
   1393             return;
   1394         }
   1395     }
   1396 
   1397     // If there are no addresses to connect to, and there are no queries running, then we can give
   1398     // up.  Otherwise, we wait for one of the queries to deliver an answer.
   1399     if (cs->next_addr == NULL) {
   1400         // We may get more callbacks when outstanding queries either fail or succeed, at which point we can try to
   1401         // connect to those addresses, or give up.
   1402         mDNS_Unlock(m);
   1403         return;
   1404     }
   1405 
   1406     const mDNSAddr addr = cs->next_addr->address;
   1407     const mDNSIPPort port = cs->next_addr->port;
   1408 
   1409     sock = mDNSPlatformTCPSocket(kTCPSocketFlags_Zero, addr.type, NULL, NULL, mDNSfalse);
   1410     if (sock == NULL) {
   1411         LogMsg("drConnectCallback: couldn't get a socket for %s: %s%s",
   1412                cs->hostname, strerror(errno), detail);
   1413         goto fail;
   1414     }
   1415 
   1416     LogMsg("dso_connect_callback: Attempting to connect to %#a%%%d", &addr, mDNSVal16(port));
   1417 
   1418     status = mDNSPlatformTCPConnect(sock, &addr, port, NULL, dso_connect_callback, cs);
   1419     // We finished setting up the connection with the first address in the list, so remove it from the list.
   1420     cs->next_addr = cs->next_addr->next;
   1421 
   1422     if (status == mStatus_NoError || status == mStatus_ConnEstablished) {
   1423         // This can't happen in practice on MacOS; we don't know about all other operating systems,
   1424         // so we handle it just in case.
   1425         LogMsg("dso_connect_callback: synchronous connect to %s", cs->hostname);
   1426         goto success;
   1427     } else if (status == mStatus_ConnPending) {
   1428         LogMsg("dso_connect_callback: asynchronous connect to %s", cs->hostname);
   1429         cs->connecting = mDNStrue;
   1430         // We should get called back when the connection succeeds or fails.
   1431         mDNS_Unlock(m);
   1432         return;
   1433     }
   1434     LogMsg("dso_connect_callback: failed to connect to %s on %#a%d: %s%s",
   1435            cs->hostname, &addr, mDNSVal16(port), strerror(errno), detail);
   1436     mDNS_Unlock(m);
   1437 }
   1438 
   1439 static void dso_connect_internal(dso_connect_state_t *cs, mDNSBool reconnecting)
   1440 {
   1441     (void)reconnecting;
   1442     dso_connect_callback(NULL, cs, false, mStatus_NoError);
   1443 }
   1444 #endif // DSO_USES_NETWORK_FRAMEWORK
   1445 
   1446 static void dso_connect_state_process_address_port_change(const mDNSAddr *addr_changed, mDNSIPPort port,
   1447                                                           bool add, dso_connect_state_t *const cs)
   1448 {
   1449     bool succeeded;
   1450     dso_transport_address_t **addrs = &cs->addrs;
   1451 
   1452     if (add) {
   1453         // Always add the new address to the tail, so the order of using the address to connect is the same as the order
   1454         // of the address being added.
   1455         while (*addrs != NULL) {
   1456             addrs = &(*addrs)->next;
   1457         }
   1458         dso_transport_address_t *new_addr = mdns_calloc(1, sizeof(*new_addr));
   1459         if (new_addr == NULL) {
   1460             succeeded = false;
   1461             goto exit;
   1462         }
   1463         memcpy(&new_addr->address, addr_changed, sizeof (*addr_changed));
   1464         new_addr->port = port;
   1465         *addrs = new_addr;
   1466         if (cs->next_addr == NULL) {
   1467             cs->next_addr = new_addr;
   1468         }
   1469     } else {
   1470         // Remove address that has been previously added, so that mDNSResponder will not even try the removed address in
   1471         // the future when reconnecting.
   1472         bool removed = mDNSfalse;
   1473         while (*addrs != NULL) {
   1474             dso_transport_address_t *addr = *addrs;
   1475             if (((addr->address.type == mDNSAddrType_IPv4 && !memcmp(&addr->address.ip.v4, &addr_changed->ip.v4,
   1476                                                                      sizeof(addr->address.ip.v4))) ||
   1477                  (addr->address.type == mDNSAddrType_IPv6 && !memcmp(&addr->address.ip.v6, &addr_changed->ip.v6,
   1478                                                                      sizeof(addr->address.ip.v4)))) &&
   1479                 addr->port.NotAnInteger == port.NotAnInteger)
   1480             {
   1481                 if (cs->next_addr == addr) {
   1482                     cs->next_addr = addr->next;
   1483                 }
   1484                 *addrs = addr->next;
   1485                 mdns_free(addr);
   1486                 removed = mDNStrue;
   1487                 break;
   1488             }
   1489             addrs = &addr->next;
   1490         }
   1491 
   1492         if (!removed) {
   1493             // If the address being removed is not in the list, it indicates the following two scenarios:
   1494             // 1. The address has been traversed when dso_connect_state_t tries to connect to the server address.
   1495             // 2. The address is the server address that dso_transport_t currently connects to, for efficiency, it is
   1496             // not terminated immediately. If the address is removed because of the network changes, dso_transport_t
   1497             // will notice that and terminate the connection by itself.
   1498             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1499                 "[DSOC%u] The address being removed has been tried for the connection or is being used right now - "
   1500                 "address: " PRI_IP_ADDR ":%u.", cs->serial, addr_changed, mDNSVal16(cs->config_port));
   1501         }
   1502     }
   1503 
   1504     if (!cs->connecting && !cs->transport) {
   1505         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] Starting a new connection.", cs->serial);
   1506         dso_connect_internal(cs, mDNSfalse);
   1507     } else {
   1508         if (cs->connecting) {
   1509             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1510                       "[DSOC%u] Connection in progress, deferring new connection until it fails.", cs->serial);
   1511         } else {
   1512             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1513                       "[DSOC%u] Already connected, retained new address for later need.", cs->serial);
   1514         }
   1515     }
   1516 
   1517     succeeded = true;
   1518 exit:
   1519     if (!succeeded) {
   1520         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT,
   1521                   "[DSOC%u] Failed to process address changes for dso_connect_state_t.", cs->serial);
   1522     }
   1523 }
   1524 
   1525 static void dso_connect_state_process_address_change(const mDNSAddr *addr_changed, const bool add,
   1526                                                      dso_connect_state_t *const cs)
   1527 {
   1528     dso_connect_state_process_address_port_change(addr_changed, cs->config_port, add, cs);
   1529 }
   1530 
   1531 static void dso_inaddr_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
   1532                                 DNSServiceErrorType errorCode, const char *fullname, const struct sockaddr *sa,
   1533                                 uint32_t ttl, void *context)
   1534 {
   1535     (void)sdRef;
   1536     dso_connect_state_t *cs = context;
   1537     mDNS *const m = &mDNSStorage;
   1538 
   1539     // Do not proceed if we find that the dso_connect_state_t has been canceled previously.
   1540     if (cs->canceled) {
   1541         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1542             "[DSOC%u] The current dso_connect_state_t has been canceled - hostname: " PRI_S ".", cs->serial,
   1543             cs->hostname);
   1544         goto exit;
   1545     }
   1546 
   1547     cs->last_event = mDNSStorage.timenow;
   1548 
   1549     // It is possible that the network does not support IPv4 or IPv6, in which case we will get the
   1550     // kDNSServiceErr_NoSuchRecord error for the corresponding unsupported address type. This is a valid case.
   1551     if (errorCode == kDNSServiceErr_NoSuchRecord) {
   1552         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] No usable IP address for the DNS push server - "
   1553             "host name: " PRI_S ", address type: " PUB_S ".", cs->serial, fullname,
   1554             sa->sa_family == AF_INET ? "A" : "AAAA");
   1555         goto exit;
   1556     }
   1557 
   1558     // All the other error cases other than kDNSServiceErr_NoSuchRecord and kDNSServiceErr_NoError are invalid. They
   1559     // should be reported as FAULT.
   1560     if (errorCode != kDNSServiceErr_NoError) {
   1561         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT, "[DSOC%u] Unexpected dso_inaddr_callback call - "
   1562             "flags: %x, error: %d.", cs->serial, flags, errorCode);
   1563         goto exit;
   1564     }
   1565 
   1566     const mDNSAddr addr_changed = mDNSAddr_from_sockaddr(sa);
   1567     const mDNSBool addr_add = (flags & kDNSServiceFlagsAdd) != 0;
   1568 
   1569     // mDNSPlatformInterfaceIDfromInterfaceIndex() should be called without holding mDNS lock, because the function itself
   1570     // may need to grab mDNS lock.
   1571     const mDNSInterfaceID if_id = mDNSPlatformInterfaceIDfromInterfaceIndex(m, interfaceIndex);
   1572     mDNS_Lock(m);
   1573     const char *const if_name = InterfaceNameForID(m, if_id);
   1574     const mDNSBool on_the_local_subnet = mDNS_AddressIsLocalSubnet(m, if_id, &addr_changed);
   1575     mDNS_Unlock(m);
   1576 
   1577     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] dso_inaddr_callback - address " PUB_ADD_RMV
   1578         ", resolved name: " PRI_S ", flags: %x, interface name: " PUB_S "(%u), erorr: %d, full name: " PRI_S
   1579         ", addr: " PRI_IP_ADDR ":%u, ttl: %u, on the local subnet: " PUB_BOOL ".", cs->serial, ADD_RMV_PARAM(addr_add),
   1580         fullname, flags, if_name, interfaceIndex, errorCode, fullname, &addr_changed, mDNSVal16(cs->config_port), ttl,
   1581         BOOL_PARAM(on_the_local_subnet));
   1582 
   1583     dso_connect_state_process_address_change(&addr_changed, addr_add, cs);
   1584 
   1585 exit:
   1586     return;
   1587 }
   1588 
   1589 bool dso_connect(dso_connect_state_t *cs)
   1590 {
   1591     struct in_addr in;
   1592     struct in6_addr in6;
   1593 
   1594     if (cs->next_addr != NULL) {
   1595         // If the connection state was created with an address, use that rather than hostname,
   1596         dso_connect_internal(cs, mDNSfalse);
   1597 
   1598     } else if (inet_pton(AF_INET, cs->hostname, &in)) {
   1599         // else allow an IPv4 address literal string,
   1600         const mDNSAddr v4 = mDNSAddr_from_in_addr(&in);
   1601         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1602             "[DSOC%u] Add and connecting to an IPv4 address literal string directly - address: " PRI_IP_ADDR ":%u.",
   1603             cs->serial, &v4, mDNSVal16(cs->config_port));
   1604 
   1605         dso_connect_state_process_address_change(&v4, true, cs);
   1606 
   1607     } else if (inet_pton(AF_INET6, cs->hostname, &in6)) {
   1608         // or an IPv6 address literal string,
   1609         const mDNSAddr v6 = mDNSAddr_from_in6_addr(&in6);
   1610         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
   1611             "[DSOC%u] Add and connecting to an IPv6 address literal string directly - address: " PRI_IP_ADDR ":%u.",
   1612             cs->serial, &v6, mDNSVal16(cs->config_port));
   1613 
   1614         dso_connect_state_process_address_change(&v6, true, cs);
   1615 
   1616     } else {
   1617         // else look it up.
   1618         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[DSOC%u] Resolving server name to start a new connection - "
   1619             "server: " PRI_S ".", cs->serial, cs->hostname);
   1620         mDNS *m = &mDNSStorage;
   1621         mDNS_DropLockBeforeCallback();
   1622         const DNSServiceErrorType err = DNSServiceGetAddrInfo(&cs->lookup, kDNSServiceFlagsReturnIntermediates,
   1623             kDNSServiceInterfaceIndexAny, 0, cs->hostname, dso_inaddr_callback, cs);
   1624 
   1625         mDNS_ReclaimLockAfterCallback();
   1626         if (err != mStatus_NoError) {
   1627             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
   1628                 "[DSOC%u] Name resolving failed for the server to connect - server: " PRI_S ", error: %d.",
   1629                 cs->serial, cs->hostname, err);
   1630             return false;
   1631         }
   1632     }
   1633     return true;
   1634 }
   1635 
   1636 #ifdef DSO_USES_NETWORK_FRAMEWORK
   1637 // We don't need this for DNS Push, so it is being left as future work.
   1638 int dso_listen(dso_connect_state_t * __unused listen_context)
   1639 {
   1640     return mStatus_UnsupportedErr;
   1641 }
   1642 
   1643 #else
   1644 
   1645 // Called whenever we get a connection on the DNS TCP socket
   1646 static void dso_listen_callback(TCPSocket *sock, mDNSAddr *addr, mDNSIPPort *port,
   1647                                 const char *remote_name, void *context)
   1648 {
   1649     dso_connect_state_t *lc = context;
   1650     dso_transport_t *transport;
   1651 
   1652     mDNS_Lock(&mDNSStorage);
   1653     transport = dso_transport_create(sock, mDNStrue, lc->context, lc->dso_context_callback, lc->max_outstanding_queries,
   1654                                      lc->inbuf_size, lc->outbuf_size, remote_name, lc->callback, NULL);
   1655     if (transport == NULL) {
   1656         mDNSPlatformTCPCloseConnection(sock);
   1657         LogMsg("No memory for new DSO connection from %s", remote_name);
   1658         goto out;
   1659     }
   1660 
   1661     transport->remote_addr = *addr;
   1662     transport->remote_port = ntohs(port->NotAnInteger);
   1663     if (transport->dso->cb) {
   1664         transport->dso->cb(lc->context, 0, transport->dso, kDSOEventType_Connected);
   1665     }
   1666     LogMsg("DSO connection from %s", remote_name);
   1667 out:
   1668     mDNS_Unlock(&mDNSStorage);
   1669 }
   1670 
   1671 // Listen for connections; each time we get a connection, make a new dso_state_t object with the specified
   1672 // parameters and call the callback.   Port can be zero to leave it unspecified.
   1673 
   1674 int dso_listen(dso_connect_state_t *listen_context)
   1675 {
   1676     mDNSIPPort port;
   1677     mDNSBool reuseAddr = mDNSfalse;
   1678 
   1679     if (listen_context->config_port.NotAnInteger) {
   1680         port = listen_context->config_port;
   1681         reuseAddr = mDNStrue;
   1682     }
   1683     listen_context->listener = mDNSPlatformTCPListen(mDNSAddrType_None, &port, NULL, kTCPSocketFlags_Zero,
   1684                                                      reuseAddr, 5, dso_listen_callback, listen_context);
   1685     if (!listen_context->listener) {
   1686         return mStatus_UnknownErr;
   1687     }
   1688     listen_context->connect_port = port;
   1689 
   1690     LogMsg("DSOListen: Listening on <any>%%%d", mDNSVal16(port));
   1691     return mStatus_NoError;
   1692 }
   1693 #endif // DSO_USES_NETWORK_FRAMEWORK
   1694 
   1695 // Local Variables:
   1696 // mode: C
   1697 // tab-width: 4
   1698 // c-file-style: "bsd"
   1699 // c-basic-offset: 4
   1700 // fill-column: 108
   1701 // indent-tabs-mode: nil
   1702 // End:
   1703