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