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