dso-transport.c revision 1.1 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