]> git.saurik.com Git - apple/mdnsresponder.git/blob - DSO/dso-transport.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / DSO / dso-transport.c
1 /* dso-transport.c
2 *
3 * Copyright (c) 2018-2019 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 //*************************************************************************************************************
20 // Headers
21
22 #include <stdio.h>
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <stdbool.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include <netdb.h> // For gethostbyname()
31 #include <sys/socket.h> // For AF_INET, AF_INET6, etc.
32 #include <net/if.h> // For IF_NAMESIZE
33 #include <netinet/in.h> // For INADDR_NONE
34 #include <netinet/tcp.h> // For SOL_TCP, TCP_NOTSENT_LOWAT
35 #include <arpa/inet.h> // For inet_addr()
36 #include <unistd.h>
37 #include <errno.h>
38 #include <fcntl.h>
39
40 #include "dns_sd.h"
41 #include "DNSCommon.h"
42 #include "mDNSEmbeddedAPI.h"
43 #include "dso.h"
44 #include "dso-transport.h"
45
46 #ifdef DSO_USES_NETWORK_FRAMEWORK
47 // Network Framework only works on MacOS X at the moment, and we need the locking primitives for
48 // MacOSX.
49 #include "mDNSMacOSX.h"
50 #endif
51
52 extern mDNS mDNSStorage;
53
54 static dso_connect_state_t *dso_connect_states; // DSO connect states that exist.
55 static dso_transport_t *dso_transport_states; // DSO transport states that exist.
56 #ifdef DSO_USES_NETWORK_FRAMEWORK
57 static uint32_t dso_transport_serial; // Serial number of next dso_transport_state_t or dso_connect_state_t.
58 static dispatch_queue_t dso_dispatch_queue;
59 #else
60 static void dso_read_callback(TCPSocket *sock, void *context, mDNSBool connection_established,
61 mStatus err);
62 #endif
63
64 void
65 dso_transport_init(void)
66 {
67 #ifdef DSO_USES_NETWORK_FRAMEWORK
68 // It's conceivable that we might want a separate queue, but we don't know yet, so for
69 // now we just use the main dispatch queue, which should be on the main dispatch thread,
70 // which is _NOT_ the kevent thread. So whenever we are doing anything on the dispatch
71 // queue (any completion functions for NW framework) we need to acquire the lock before
72 // we even look at any variables that could be changed by the other thread.
73 dso_dispatch_queue = dispatch_get_main_queue();
74 #endif
75 }
76
77 #ifdef DSO_USES_NETWORK_FRAMEWORK
78 static dso_connect_state_t *
79 dso_connect_state_find(uint32_t serial)
80 {
81 dso_connect_state_t *csp;
82 for (csp = dso_connect_states; csp; csp = csp->next) {
83 if (csp->serial == serial) {
84 return csp;
85 }
86 }
87 return NULL;
88 }
89 #endif
90
91 static void
92 dso_transport_finalize(dso_transport_t *transport)
93 {
94 dso_transport_t **tp = &dso_transport_states;
95 if (transport->connection != NULL) {
96 #ifdef DSO_USES_NETWORK_FRAMEWORK
97 nw_connection_cancel(transport->connection);
98 nw_release(transport->connection);
99 #else
100 mDNSPlatformTCPCloseConnection(transport->connection);
101 #endif
102 transport->connection = NULL;
103 }
104 while (*tp) {
105 if (*tp == transport) {
106 *tp = transport->next;
107 } else {
108 tp = &transport->next;
109 }
110 }
111 free(transport);
112 }
113
114 // We do all of the finalization for the dso state object and any objects it depends on here in the
115 // dso_idle function because it avoids the possibility that some code on the way out to the event loop
116 // _after_ the DSO connection has been dropped might still write to the DSO structure or one of the
117 // dependent structures and corrupt the heap, or indeed in the unlikely event that this memory was
118 // freed and then reallocated before the exit to the event loop, there could be a bad pointer
119 // dereference.
120 //
121 // If there is a finalize function, that function MUST either free its own state that references the
122 // DSO state, or else must NULL out the pointer to the DSO state.
123 int32_t dso_transport_idle(void *context, int64_t now_in, int64_t next_timer_event)
124 {
125 dso_connect_state_t *cs, *cnext;
126 mDNS *m = context;
127 mDNSs32 now = (mDNSs32)now_in;
128 mDNSs32 next_event = (mDNSs32)next_timer_event;
129
130 // Notice if a DSO connection state is active but hasn't seen activity in a while.
131 for (cs = dso_connect_states; cs != NULL; cs = cnext) {
132 cnext = cs->next;
133 if (!cs->connecting && cs->last_event != 0) {
134 mDNSs32 expiry = cs->last_event + 90 * mDNSPlatformOneSecond;
135 if (now - expiry > 0) {
136 cs->last_event = 0;
137 cs->callback(cs->context, NULL, NULL, kDSOEventType_ConnectFailed);
138 if (cs->lookup != NULL) {
139 DNSServiceRef ref = cs->lookup;
140 cs->lookup = NULL;
141 mDNS_DropLockBeforeCallback();
142 DNSServiceRefDeallocate(ref);
143 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
144 }
145 } else {
146 if (next_timer_event - expiry > 0) {
147 next_timer_event = expiry;
148 }
149 }
150 } else if (!cs->connecting && cs->reconnect_time && now - cs->reconnect_time > 0) {
151 cs->reconnect_time = 0; // Don't try to immediately reconnect if it fails.
152 // If cs->dso->transport is non-null, we're already connected.
153 if (cs->dso && cs->dso->transport == NULL) {
154 cs->callback(cs->context, NULL, NULL, kDSOEventType_ShouldReconnect);
155 }
156 }
157 if (cs->reconnect_time != 0 && next_event - cs->reconnect_time > 0) {
158 next_event = cs->reconnect_time;
159 }
160 }
161
162 return next_event;
163 }
164
165 // Call to schedule a reconnect at a later time.
166 void dso_schedule_reconnect(mDNS *m, dso_connect_state_t *cs, mDNSs32 when)
167 {
168 cs->reconnect_time = when * mDNSPlatformOneSecond + m->timenow;
169 }
170
171 // If a DSO was created by an incoming connection, the creator of the listener can use this function
172 // to supply context and a callback for future events.
173 void dso_set_callback(dso_state_t *dso, void *context, dso_event_callback_t cb)
174 {
175 dso->cb = cb;
176 dso->context = context;
177 }
178
179 // This is called before writing a DSO message to the output buffer. length is the length of the message.
180 // Returns true if we have successfully selected for write (which means that we're under TCP_NOTSENT_LOWAT).
181 // Otherwise returns false. It is valid to write even if it returns false, but there is a risk that
182 // the write will return EWOULDBLOCK, at which point we'd have to blow away the connection. It is also
183 // valid to give up at this point and not write a message; as long as dso_write_finish isn't called, a later
184 // call to dso_write_start will overwrite the length that was stored by the previous invocation.
185 //
186 // The circumstance in which this would occur is that we have filled the kernel's TCP output buffer for this
187 // connection all the way up to TCP_NOTSENT_LOWAT, and then we get a query from the Discovery Proxy to which we
188 // need to respond. Because TCP_NOTSENT_LOWAT is fairly low, there should be a lot of room in the TCP output
189 // buffer for small responses; it would need to be the case that we are getting requests from the proxy at a
190 // high rate for us to fill the output buffer to the point where a write of a 12-byte response returns
191 // EWOULDBLOCK; in that case, things are so dysfunctional that killing the connection isn't any worse than
192 // allowing it to continue.
193
194 // An additional note about the motivation for this code: the idea originally was that we'd do scatter/gather
195 // I/O here: this lets us write everything out in a single sendmsg() call. This isn't used with the mDNSPlatformTCP
196 // code because it doesn't support scatter/gather. Network Framework does, however, and in principle we could
197 // write to the descriptor directly if that were really needed.
198
199 bool dso_write_start(dso_transport_t *transport, size_t length)
200 {
201 // The transport doesn't support messages outside of this range.
202 if (length < 12 || length > 65535) {
203 return false;
204 }
205
206 #ifdef DSO_USES_NETWORK_FRAMEWORK
207 uint8_t lenbuf[2];
208
209 if (transport->to_write != NULL) {
210 nw_release(transport->to_write);
211 transport->to_write = NULL;
212 }
213 lenbuf[0] = length >> 8;
214 lenbuf[1] = length & 255;
215 transport->to_write = dispatch_data_create(lenbuf, 2, dso_dispatch_queue,
216 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
217 if (transport->to_write == NULL) {
218 transport->write_failed = true;
219 return false;
220 }
221 transport->bytes_to_write = length + 2;
222
223 // We don't have access to TCP_NOTSENT_LOWAT, so for now we track how many bytes we've written
224 // versus how many bytes that we've written have completed, and if that creeps above MAX_UNSENT_BYTES,
225 // we return false here to indicate that there is congestion.
226 if (transport->unsent_bytes > MAX_UNSENT_BYTES) {
227 return false;
228 } else {
229 return true;
230 }
231 #else
232 transport->lenbuf[0] = length >> 8;
233 transport->lenbuf[1] = length & 255;
234
235 transport->to_write[0] = transport->lenbuf;
236 transport->write_lengths[0] = 2;
237 transport->num_to_write = 1;
238
239 return mDNSPlatformTCPWritable(transport->connection);
240 #endif // DSO_USES_NETWORK_FRAMEWORK
241 }
242
243 // Called to finish a write (dso_write_start .. dso_write .. [ dso_write ... ] dso_write_finish). The
244 // write must completely finish--if we get a partial write, this means that the connection is stalled, and
245 // so we drop it. Since this can call dso_drop, the caller must not reference the DSO state object
246 // after this call if the return value is false.
247 bool dso_write_finish(dso_transport_t *transport)
248 {
249 #ifdef DSO_USES_NETWORK_FRAMEWORK
250 uint32_t serial = transport->dso->serial;
251 size_t bytes_to_write = transport->bytes_to_write;
252 transport->bytes_to_write = 0;
253 if (transport->write_failed) {
254 dso_drop(transport->dso);
255 return false;
256 }
257 transport->unsent_bytes += bytes_to_write;
258 nw_connection_send(transport->connection, transport->to_write, NW_CONNECTION_DEFAULT_STREAM_CONTEXT, true,
259 ^(nw_error_t _Nullable error) {
260 dso_state_t *dso;
261 KQueueLock();
262 dso = dso_find_by_serial(serial);
263 if (error != NULL) {
264 LogMsg("dso_write_finish: write failed: %s", strerror(nw_error_get_error_code(error)));
265 if (dso != NULL) {
266 dso_drop(dso);
267 }
268 } else {
269 dso->transport->unsent_bytes -= bytes_to_write;
270 LogMsg("dso_write_finish completion routine: %d bytes written, %d bytes outstanding",
271 bytes_to_write, dso->transport->unsent_bytes);
272 }
273 KQueueUnlock("dso_write_finish completion routine");
274 });
275 nw_release(transport->to_write);
276 transport->to_write = NULL;
277 return true;
278 #else
279 ssize_t result, total = 0;
280 int i;
281
282 if (transport->num_to_write > MAX_WRITE_HUNKS) {
283 LogMsg("dso_write_finish: fatal internal programming error: called %d times (more than limit of %d)",
284 transport->num_to_write, MAX_WRITE_HUNKS);
285 dso_drop(transport->dso);
286 return false;
287 }
288
289 // This is our ersatz scatter/gather I/O.
290 for (i = 0; i < transport->num_to_write; i++) {
291 result = mDNSPlatformWriteTCP(transport->connection, (const char *)transport->to_write[i], transport->write_lengths[i]);
292 if (result != transport->write_lengths[i]) {
293 if (result < 0) {
294 LogMsg("dso_write_finish: fatal: mDNSPlatformWrite on %s returned %d", transport->dso->remote_name, errno);
295 } else {
296 LogMsg("dso_write_finish: fatal: mDNSPlatformWrite: short write on %s: %ld < %ld",
297 transport->dso->remote_name, (long)result, (long)total);
298 }
299 dso_drop(transport->dso);
300 return false;
301 }
302 }
303 #endif
304 return true;
305 }
306
307 // This function may only be called after a previous call to dso_write_start; it records the length of and
308 // pointer to the write buffer. These buffers must remain valid until dso_write_finish() is called. The
309 // caller is responsible for managing the memory they contain. The expected control flow for writing is:
310 // dso_write_start(); dso_write(); dso_write(); dso_write(); dso_write_finished(); There should be one or
311 // more calls to dso_write; these will ideally be translated into a single scatter/gather sendmsg call (or
312 // equivalent) to the kernel.
313 void dso_write(dso_transport_t *transport, const uint8_t *buf, size_t length)
314 {
315 if (length == 0) {
316 return;
317 }
318
319 #ifdef DSO_USES_NETWORK_FRAMEWORK
320 if (transport->write_failed) {
321 return;
322 }
323 dispatch_data_t dpd = dispatch_data_create(buf, length, dso_dispatch_queue,
324 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
325 if (dpd == NULL) {
326 transport->write_failed = true;
327 return;
328 }
329 if (transport->to_write != NULL) {
330 dispatch_data_t dpc = dispatch_data_create_concat(transport->to_write, dpd);
331 dispatch_release(dpd);
332 dispatch_release(transport->to_write);
333 if (dpc == NULL) {
334 transport->to_write = NULL;
335 transport->write_failed = true;
336 return;
337 }
338 transport->to_write = dpc;
339 }
340 #else
341 // We'll report this in dso_write_finish();
342 if (transport->num_to_write >= MAX_WRITE_HUNKS) {
343 transport->num_to_write++;
344 return;
345 }
346
347 transport->to_write[transport->num_to_write] = buf;
348 transport->write_lengths[transport->num_to_write] = length;
349 transport->num_to_write++;
350 #endif
351 }
352
353 // Write a DSO message
354 int dso_message_write(dso_state_t *dso, dso_message_t *msg, bool disregard_low_water)
355 {
356 dso_transport_t *transport = dso->transport;
357 if (transport->connection != NULL) {
358 if (dso_write_start(transport, dso_message_length(msg)) || disregard_low_water) {
359 dso_write(transport, msg->buf, msg->no_copy_bytes_offset);
360 dso_write(transport, msg->no_copy_bytes, msg->no_copy_bytes_len);
361 dso_write(transport, &msg->buf[msg->no_copy_bytes_offset], msg->cur - msg->no_copy_bytes_offset);
362 return dso_write_finish(transport);
363 }
364 }
365 return mStatus_NoMemoryErr;
366 }
367
368 // Replies to some message we were sent with a response code and no data.
369 // This is a convenience function for replies that do not require that a new
370 // packet be constructed. It takes advantage of the fact that the message
371 // to which this is a reply is still in the input buffer, and modifies that
372 // message in place to turn it into a response.
373
374 bool dso_send_simple_response(dso_state_t *dso, int rcode, const DNSMessageHeader *header, const char *pres)
375 {
376 dso_transport_t *transport = dso->transport;
377 (void)pres; // might want this later.
378 DNSMessageHeader response = *header;
379
380 // Just return the message, with no questions, answers, etc.
381 response.flags.b[1] = (response.flags.b[1] & ~kDNSFlag1_RC_Mask) | rcode;
382 response.flags.b[0] |= kDNSFlag0_QR_Response;
383 response.numQuestions = 0;
384 response.numAnswers = 0;
385 response.numAuthorities = 0;
386 response.numAdditionals = 0;
387
388 // Buffered write back to discovery proxy
389 (void)dso_write_start(transport, 12);
390 dso_write(transport, (uint8_t *)&response, 12);
391 if (!dso_write_finish(transport)) {
392 return false;
393 }
394 return true;
395 }
396
397 // DSO Message we received has a primary TLV that's not implemented.
398 // XXX is this what we're supposed to do here? check draft.
399 bool dso_send_not_implemented(dso_state_t *dso, const DNSMessageHeader *header)
400 {
401 return dso_send_simple_response(dso, kDNSFlag1_RC_DSOTypeNI, header, "DSOTYPENI");
402 }
403
404 // Non-DSO message we received is refused.
405 bool dso_send_refused(dso_state_t *dso, const DNSMessageHeader *header)
406 {
407 return dso_send_simple_response(dso, kDNSFlag1_RC_Refused, header, "REFUSED");
408 }
409
410 bool dso_send_formerr(dso_state_t *dso, const DNSMessageHeader *header)
411 {
412 return dso_send_simple_response(dso, kDNSFlag1_RC_FormErr, header, "FORMERR");
413 }
414
415 bool dso_send_servfail(dso_state_t *dso, const DNSMessageHeader *header)
416 {
417 return dso_send_simple_response(dso, kDNSFlag1_RC_ServFail, header, "SERVFAIL");
418 }
419
420 bool dso_send_name_error(dso_state_t *dso, const DNSMessageHeader *header)
421 {
422 return dso_send_simple_response(dso, kDNSFlag1_RC_NXDomain, header, "NXDOMAIN");
423 }
424
425 bool dso_send_no_error(dso_state_t *dso, const DNSMessageHeader *header)
426 {
427 return dso_send_simple_response(dso, kDNSFlag1_RC_NoErr, header, "NOERROR");
428 }
429
430 #ifdef DSO_USES_NETWORK_FRAMEWORK
431 static void dso_read_message(dso_transport_t *transport, uint32_t length);
432
433 static void dso_read_message_length(dso_transport_t *transport)
434 {
435 const uint32_t serial = transport->dso->serial;
436 if (transport->connection == NULL) {
437 LogMsg("dso_read_message_length called with null connection.");
438 return;
439 }
440 nw_connection_receive(transport->connection, 2, 2,
441 ^(dispatch_data_t content, nw_content_context_t __unused context,
442 bool __unused is_complete, nw_error_t error) {
443 dso_state_t *dso;
444 // Don't touch anything or look at anything until we have the lock.
445 KQueueLock();
446 dso = dso_find_by_serial(serial);
447 if (error != NULL) {
448 LogMsg("dso_read_message_length: read failed: %s",
449 strerror(nw_error_get_error_code(error)));
450 fail:
451 if (dso != NULL) {
452 mDNS_Lock(&mDNSStorage);
453 dso_drop(dso);
454 mDNS_Unlock(&mDNSStorage);
455 }
456 } else if (content == NULL) {
457 LogMsg("dso_read_message_length: remote end closed connection.");
458 goto fail;
459 } else {
460 uint32_t length;
461 size_t length_length;
462 const uint8_t *lenbuf;
463 dispatch_data_t map = dispatch_data_create_map(content, (const void **)&lenbuf,
464 &length_length);
465 if (map == NULL) {
466 LogMsg("dso_read_message_length: map create failed");
467 goto fail;
468 } else if (length_length != 2) {
469 LogMsg("dso_read_message_length: invalid length = %d", length_length);
470 dispatch_release(map);
471 goto fail;
472 }
473 length = ((unsigned)(lenbuf[0]) << 8) | ((unsigned)lenbuf[1]);
474 dispatch_release(map);
475 dso_read_message(transport, length);
476 }
477 KQueueUnlock("dso_read_message_length completion routine");
478 });
479 }
480
481 void dso_read_message(dso_transport_t *transport, uint32_t length)
482 {
483 const uint32_t serial = transport->dso->serial;
484 if (transport->connection == NULL) {
485 LogMsg("dso_read_message called with null connection.");
486 return;
487 }
488 nw_connection_receive(transport->connection, length, length,
489 ^(dispatch_data_t content, nw_content_context_t __unused context,
490 bool __unused is_complete, nw_error_t error) {
491 dso_state_t *dso;
492 // Don't touch anything or look at anything until we have the lock.
493 KQueueLock();
494 dso = dso_find_by_serial(serial);
495 if (error != NULL) {
496 LogMsg("dso_read_message: read failed: %s", strerror(nw_error_get_error_code(error)));
497 fail:
498 if (dso != NULL) {
499 mDNS_Lock(&mDNSStorage);
500 dso_drop(dso);
501 mDNS_Unlock(&mDNSStorage);
502 }
503 } else if (content == NULL) {
504 LogMsg("dso_read_message: remote end closed connection");
505 goto fail;
506 } else {
507 size_t bytes_read;
508 const uint8_t *message;
509 dispatch_data_t map = dispatch_data_create_map(content, (const void **)&message, &bytes_read);
510 if (map == NULL) {
511 LogMsg("dso_read_message_length: map create failed");
512 goto fail;
513 } else if (bytes_read != length) {
514 LogMsg("dso_read_message_length: only %d of %d bytes read", bytes_read, length);
515 dispatch_release(map);
516 goto fail;
517 }
518 // Process the message.
519 mDNS_Lock(&mDNSStorage);
520 dns_message_received(dso, message, length);
521 mDNS_Unlock(&mDNSStorage);
522
523 // Release the map object now that we no longer need its buffers.
524 dispatch_release(map);
525
526 // Now read the next message length.
527 dso_read_message_length(transport);
528 }
529 KQueueUnlock("dso_read_message completion routine");
530 });
531 }
532 #else
533 // Called whenever there's data available on a DSO connection
534 void dso_read_callback(TCPSocket *sock, void *context, mDNSBool connection_established, int err)
535 {
536 dso_transport_t *transport = context;
537 dso_state_t *dso;
538 mDNSBool closed = mDNSfalse;
539
540 mDNS_Lock(&mDNSStorage);
541 dso = transport->dso;
542
543 // This shouldn't ever happen.
544 if (err) {
545 LogMsg("dso_read_callback: error %d", err);
546 dso_drop(dso);
547 goto out;
548 }
549
550 // Connection is already established by the time we set this up.
551 if (connection_established) {
552 goto out;
553 }
554
555 // This will be true either if we have never read a message or
556 // if the last thing we did was to finish reading a message and
557 // process it.
558 if (transport->message_length == 0) {
559 transport->need_length = true;
560 transport->inbufp = transport->inbuf;
561 transport->bytes_needed = 2;
562 }
563
564 // Read up to bytes_needed bytes.
565 ssize_t count = mDNSPlatformReadTCP(sock, transport->inbufp, transport->bytes_needed, &closed);
566 // LogMsg("read(%d, %p:%p, %d) -> %d", fd, dso->inbuf, dso->inbufp, dso->bytes_needed, count);
567 if (count < 0) {
568 LogMsg("dso_read_callback: read from %s returned %d", dso->remote_name, errno);
569 dso_drop(dso);
570 goto out;
571 }
572
573 // If we get selected for read and there's nothing to read, the remote end has closed the
574 // connection.
575 if (closed) {
576 LogMsg("dso_read_callback: remote %s closed", dso->remote_name);
577 dso_drop(dso);
578 goto out;
579 }
580
581 transport->inbufp += count;
582 transport->bytes_needed -= count;
583
584 // If we read all the bytes we wanted, do what's next.
585 if (transport->bytes_needed == 0) {
586 // We just finished reading the complete length of a DNS-over-TCP message.
587 if (transport->need_length) {
588 // Get the number of bytes in this DNS message
589 transport->bytes_needed = (((int)transport->inbuf[0]) << 8) | transport->inbuf[1];
590
591 // Under no circumstances can length be zero.
592 if (transport->bytes_needed == 0) {
593 LogMsg("dso_read_callback: %s sent zero-length message.", dso->remote_name);
594 dso_drop(dso);
595 goto out;
596 }
597
598 // The input buffer size is AbsoluteMaxDNSMessageData, which is around 9000 bytes on
599 // big platforms and around 1500 bytes on smaller ones. If the remote end has sent
600 // something larger than that, it's an error from which we can't recover.
601 if (transport->bytes_needed > transport->inbuf_size - 2) {
602 LogMsg("dso_read_callback: fatal: Proxy at %s sent a too-long (%ld bytes) message",
603 dso->remote_name, (long)transport->bytes_needed);
604 dso_drop(dso);
605 goto out;
606 }
607
608 transport->message_length = transport->bytes_needed;
609 transport->inbufp = transport->inbuf + 2;
610 transport->need_length = false;
611
612 // We just finished reading a complete DNS-over-TCP message.
613 } else {
614 dns_message_received(dso, &transport->inbuf[2], transport->message_length);
615 transport->message_length = 0;
616 }
617 }
618 out:
619 mDNS_Unlock(&mDNSStorage);
620 }
621 #endif // DSO_USES_NETWORK_FRAMEWORK
622
623 #ifdef DSO_USES_NETWORK_FRAMEWORK
624 static dso_transport_t *dso_transport_create(nw_connection_t connection, bool is_server, void *context,
625 int max_outstanding_queries, size_t outbuf_size_in, const char *remote_name,
626 dso_event_callback_t cb, dso_state_t *dso)
627 {
628 dso_transport_t *transport;
629 uint8_t *transp;
630 const size_t outbuf_size = outbuf_size_in + 256; // Space for additional TLVs
631
632 // We allocate everything in a single hunk so that we can free it together as well.
633 transp = mallocL("dso_transport_create", (sizeof *transport) + outbuf_size);
634 if (transp == NULL) {
635 transport = NULL;
636 goto out;
637 }
638 // Don't clear the buffers.
639 mDNSPlatformMemZero(transp, sizeof (*transport));
640
641 transport = (dso_transport_t *)transp;
642 transp += sizeof *transport;
643
644 transport->outbuf = transp;
645 transport->outbuf_size = outbuf_size;
646
647 if (dso == NULL) {
648 transport->dso = dso_create(is_server, max_outstanding_queries, remote_name, cb, context, transport);
649 if (transport->dso == NULL) {
650 mDNSPlatformMemFree(transport);
651 transport = NULL;
652 goto out;
653 }
654 } else {
655 transport->dso = dso;
656 }
657 transport->connection = connection;
658 nw_retain(transport->connection);
659 transport->serial = dso_transport_serial++;
660
661 transport->dso->transport = transport;
662 transport->dso->transport_finalize = dso_transport_finalize;
663 transport->next = dso_transport_states;
664 dso_transport_states = transport;
665
666 // Start looking for messages...
667 dso_read_message_length(transport);
668 out:
669 return transport;
670 }
671 #else
672 // Create a dso_transport_t structure
673 static dso_transport_t *dso_transport_create(TCPSocket *sock, bool is_server, void *context, int max_outstanding_queries,
674 size_t inbuf_size_in, size_t outbuf_size_in, const char *remote_name,
675 dso_event_callback_t cb, dso_state_t *dso)
676 {
677 dso_transport_t *transport;
678 size_t outbuf_size;
679 size_t inbuf_size;
680 uint8_t *transp;
681 int status;
682
683 // There's no point in a DSO that doesn't have a callback.
684 if (!cb) {
685 return NULL;
686 }
687
688 outbuf_size = outbuf_size_in + 256; // Space for additional TLVs
689 inbuf_size = inbuf_size_in + 2; // Space for length
690
691 // We allocate everything in a single hunk so that we can free it together as well.
692 transp = mallocL("dso_transport_create", (sizeof *transport) + inbuf_size + outbuf_size);
693 if (transp == NULL) {
694 transport = NULL;
695 goto out;
696 }
697 // Don't clear the buffers.
698 mDNSPlatformMemZero(transp, sizeof (*transport));
699
700 transport = (dso_transport_t *)transp;
701 transp += sizeof *transport;
702
703 transport->inbuf = transp;
704 transport->inbuf_size = inbuf_size;
705 transp += inbuf_size;
706
707 transport->outbuf = transp;
708 transport->outbuf_size = outbuf_size;
709
710 if (dso == NULL) {
711 transport->dso = dso_create(is_server, max_outstanding_queries, remote_name, cb, context, transport);
712 if (transport->dso == NULL) {
713 mDNSPlatformMemFree(transport);
714 transport = NULL;
715 goto out;
716 }
717 } else {
718 transport->dso = dso;
719 }
720 transport->connection = sock;
721
722 status = mDNSPlatformTCPSocketSetCallback(sock, dso_read_callback, transport);
723 if (status != mStatus_NoError) {
724 LogMsg("dso_create: unable to set callback: %d", status);
725 dso_drop(transport->dso);
726 goto out;
727 }
728
729 transport->dso->transport = transport;
730 transport->dso->transport_finalize = dso_transport_finalize;
731 transport->next = dso_transport_states;
732 dso_transport_states = transport;
733 out:
734 return transport;
735 }
736 #endif // DSO_USES_NETWORK_FRAMEWORK
737
738 // This should all be replaced with Network Framework connection setup.
739 dso_connect_state_t *dso_connect_state_create(const char *hostname, mDNSAddr *addr, mDNSIPPort port,
740 int max_outstanding_queries, size_t inbuf_size, size_t outbuf_size,
741 dso_event_callback_t callback, dso_state_t *dso, void *context, const char *detail)
742 {
743 size_t detlen = strlen(detail) + 1;
744 size_t hostlen = hostname == NULL ? 0 : strlen(hostname) + 1;
745 size_t len;
746 dso_connect_state_t *cs;
747 char *csp;
748 char nbuf[INET6_ADDRSTRLEN + 1];
749 dso_connect_state_t **states;
750
751 // Enforce Some Minimums (Xxx these are a bit arbitrary, maybe not worth doing?)
752 if (inbuf_size < MaximumRDSize || outbuf_size < 128 || max_outstanding_queries < 1) {
753 return 0;
754 }
755
756 // If we didn't get a hostname, make a presentation form of the IP address to use instead.
757 if (!hostlen) {
758 if (addr != NULL) {
759 if (addr->type == mDNSAddrType_IPv4) {
760 hostname = inet_ntop(AF_INET, &addr->ip.v4, nbuf, sizeof nbuf);
761 } else {
762 hostname = inet_ntop(AF_INET6, &addr->ip.v6, nbuf, sizeof nbuf);
763 }
764 if (hostname != NULL) {
765 hostlen = strlen(nbuf);
766 }
767 }
768 }
769 // If we don't have a printable name, we won't proceed, because this means we don't know
770 // what to connect to.
771 if (!hostlen) {
772 return 0;
773 }
774
775 len = (sizeof *cs) + detlen + hostlen;
776 csp = malloc(len);
777 if (!csp) {
778 return NULL;
779 }
780 cs = (dso_connect_state_t *)csp;
781 memset(cs, 0, sizeof *cs);
782 csp += sizeof *cs;
783
784 cs->detail = csp;
785 memcpy(cs->detail, detail, detlen);
786 csp += detlen;
787 cs->hostname = csp;
788 memcpy(cs->hostname, hostname, hostlen);
789
790 cs->config_port = port;
791 cs->max_outstanding_queries = max_outstanding_queries;
792 cs->outbuf_size = outbuf_size;
793 if (context) {
794 cs->context = context;
795 } // else cs->context = NULL because of memset call above.
796 cs->callback = callback;
797 cs->connect_port.NotAnInteger = 0;
798 cs->dso = dso;
799 #ifdef DSO_USES_NETWORK_FRAMEWORK
800 cs->serial = dso_transport_serial++;
801 #else
802 cs->inbuf_size = inbuf_size;
803 #endif
804
805 if (addr) {
806 cs->num_addrs = 1;
807 cs->addresses[0] = *addr;
808 cs->ports[0] = port;
809 }
810 for (states = &dso_connect_states; *states != NULL; states = &(*states)->next)
811 ;
812 *states = cs;
813 return cs;
814 }
815
816 #ifdef DSO_USES_NETWORK_FRAMEWORK
817 void dso_connect_state_use_tls(dso_connect_state_t *cs)
818 {
819 cs->tls_enabled = true;
820 }
821 #endif
822
823 void dso_connect_state_drop(dso_connect_state_t *cs)
824 {
825 dso_connect_state_t **states;
826
827 for (states = &dso_connect_states; *states != NULL && *states != cs; states = &(*states)->next)
828 ;
829 if (*states) {
830 *states = cs->next;;
831 } else {
832 LogMsg("dso_connect_state_drop: dropping a connect state that isn't recognized.");
833 }
834 #ifdef DSO_USES_NETWORK_FRAMEWORK
835 if (cs->connection != NULL) {
836 nw_connection_cancel(cs->connection);
837 nw_release(cs->connection);
838 cs->connection = NULL;
839 }
840 #endif
841 mDNSPlatformMemFree(cs);
842 }
843
844 #ifdef DSO_USES_NETWORK_FRAMEWORK
845 static void
846 dso_connection_succeeded(dso_connect_state_t *cs)
847 {
848 // We got a connection.
849 dso_transport_t *transport =
850 dso_transport_create(cs->connection, false, cs->context, cs->max_outstanding_queries,
851 cs->outbuf_size, cs->hostname, cs->callback, cs->dso);
852 nw_release(cs->connection);
853 cs->connection = NULL;
854 if (transport == NULL) {
855 // If dso_transport_create fails, there's no point in continuing to try to connect to new
856 // addresses
857 LogMsg("dso_connection_succeeded: dso_create failed");
858 // XXX we didn't retain the connection, so we're done when it goes out of scope, right?
859 } else {
860 // Call the "we're connected" callback, which will start things up.
861 transport->dso->cb(cs->context, NULL, transport->dso, kDSOEventType_Connected);
862 }
863
864 cs->last_event = 0;
865
866 // When the connection has succeeded, stop asking questions.
867 if (cs->lookup != NULL) {
868 mDNS *m = &mDNSStorage;
869 DNSServiceRef ref = cs->lookup;
870 cs->lookup = NULL;
871 mDNS_DropLockBeforeCallback();
872 DNSServiceRefDeallocate(ref);
873 mDNS_ReclaimLockAfterCallback();
874 }
875 return;
876 }
877
878
879 static void dso_connect_internal(dso_connect_state_t *cs)
880 {
881 uint32_t serial = cs->serial;
882
883 cs->last_event = mDNSStorage.timenow;
884
885 if (cs->num_addrs <= cs->cur_addr) {
886 if (cs->lookup == NULL) {
887 LogMsg("dso_connect_internal: %s: no more addresses to try", cs->hostname);
888 cs->last_event = 0;
889 cs->callback(cs->context, NULL, NULL, kDSOEventType_ConnectFailed);
890 }
891 // Otherwise, we will get more callbacks when outstanding queries either fail or succeed.
892 return;
893 }
894
895 char addrbuf[INET6_ADDRSTRLEN + 1];
896 char portbuf[6];
897
898 inet_ntop(cs->addresses[cs->cur_addr].type == mDNSAddrType_IPv4 ? AF_INET : AF_INET6,
899 cs->addresses[cs->cur_addr].type == mDNSAddrType_IPv4
900 ? (void *)cs->addresses[cs->cur_addr].ip.v4.b
901 : (void *)cs->addresses[cs->cur_addr].ip.v6.b, addrbuf, sizeof addrbuf);
902 snprintf(portbuf, sizeof portbuf, "%u", ntohs(cs->ports[cs->cur_addr].NotAnInteger));
903 cs->cur_addr++;
904
905 nw_endpoint_t endpoint = nw_endpoint_create_host(addrbuf, portbuf);
906 if (endpoint == NULL) {
907 nomem:
908 LogMsg("dso_connect_internal: no memory creating connection.");
909 return;
910 }
911 nw_parameters_t parameters = NULL;
912 nw_parameters_configure_protocol_block_t configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL;
913 if (cs->tls_enabled) {
914 // This sets up a block that's called when we get a TLS connection and want to verify
915 // the cert. Right now we only support opportunistic security, which means we have
916 // no way to validate the cert. Future work: add support for validating the cert
917 // using a TLSA record if one is present.
918 configure_tls = ^(nw_protocol_options_t tls_options) {
919 sec_protocol_options_t sec_options = nw_tls_copy_sec_protocol_options(tls_options);
920 sec_protocol_options_set_verify_block(sec_options,
921 ^(sec_protocol_metadata_t __unused metadata,
922 sec_trust_t __unused trust_ref,
923 sec_protocol_verify_complete_t complete) {
924 complete(true);
925 }, dso_dispatch_queue);
926 };
927 }
928 parameters = nw_parameters_create_secure_tcp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION);
929 if (parameters == NULL) {
930 goto nomem;
931 }
932 nw_connection_t connection = nw_connection_create(endpoint, parameters);
933 if (connection == NULL) {
934 goto nomem;
935 }
936 cs->connection = connection;
937
938 LogMsg("dso_connect_internal: Attempting to connect to %s%%%s", addrbuf, portbuf);
939 nw_connection_set_queue(connection, dso_dispatch_queue);
940 nw_connection_set_state_changed_handler(
941 connection, ^(nw_connection_state_t state, nw_error_t error) {
942 dso_connect_state_t *ncs;
943 KQueueLock();
944 ncs = dso_connect_state_find(serial); // Might have been freed.
945 if (ncs == NULL) {
946 LogMsg("forgotten connection is %s.",
947 state == nw_connection_state_cancelled ? "canceled" :
948 state == nw_connection_state_failed ? "failed" :
949 state == nw_connection_state_waiting ? "canceled" :
950 state == nw_connection_state_ready ? "ready" : "unknown");
951 if (state != nw_connection_state_cancelled) {
952 nw_connection_cancel(connection);
953 // Don't need to release it because only NW framework is holding a reference (XXX right?)
954 }
955 } else {
956 if (state == nw_connection_state_waiting) {
957 LogMsg("connection to %#a%%%d is waiting", &ncs->addresses[ncs->cur_addr], ncs->ports[ncs->cur_addr]);
958
959 // XXX the right way to do this is to just let NW Framework wait until we get a connection,
960 // but there are a bunch of problems with that right now. First, will we get "waiting" on
961 // every connection we try? We aren't relying on NW Framework for DNS lookups, so we are
962 // connecting to an IP address, not a host, which means in principle that a later IP address
963 // might be reachable. So we have to stop trying on this one to try that one. Oops.
964 // Once we get NW Framework to use internal calls to resolve names, we can fix this.
965 // Second, maybe we want to switch to polling if this happens. Probably not, but we need
966 // to think this through. So right now we're just using the semantics of regular sockets,
967 // which we /have/ thought through. So in the future we should do this think-through and
968 // try to use NW Framework as it's intended to work rather than as if it were just sockets.
969 ncs->connecting = mDNSfalse;
970 nw_connection_cancel(connection);
971 } else if (state == nw_connection_state_failed) {
972 // We tried to connect, but didn't succeed.
973 LogMsg("dso_connect_internal: failed to connect to %s on %#a%%%d: %s%s",
974 ncs->hostname, &ncs->addresses[ncs->cur_addr], ncs->ports[ncs->cur_addr],
975 strerror(nw_error_get_error_code(error)), ncs->detail);
976 nw_release(ncs->connection);
977 ncs->connection = NULL;
978 ncs->connecting = mDNSfalse;
979 // This will do the work of figuring out if there are more addresses to try.
980 mDNS_Lock(&mDNSStorage);
981 dso_connect_internal(ncs);
982 mDNS_Unlock(&mDNSStorage);
983 } else if (state == nw_connection_state_ready) {
984 ncs->connecting = mDNSfalse;
985 mDNS_Lock(&mDNSStorage);
986 dso_connection_succeeded(ncs);
987 mDNS_Unlock(&mDNSStorage);
988 } else if (state == nw_connection_state_cancelled) {
989 if (ncs->connection) {
990 nw_release(ncs->connection);
991 }
992 ncs->connection = NULL;
993 ncs->connecting = mDNSfalse;
994 // If we get here and cs exists, we are still trying to connect. So do the next step.
995 mDNS_Lock(&mDNSStorage);
996 dso_connect_internal(ncs);
997 mDNS_Unlock(&mDNSStorage);
998 }
999 }
1000 KQueueUnlock("dso_connect_internal state change handler");
1001 });
1002 nw_connection_start(connection);
1003 cs->connecting = mDNStrue;
1004 }
1005
1006 #else
1007 static void dso_connect_callback(TCPSocket *sock, void *context, mDNSBool connected, int err)
1008 {
1009 dso_connect_state_t *cs = context;
1010 char *detail;
1011 int status;
1012 dso_transport_t *transport;
1013 mDNS *m = &mDNSStorage;
1014
1015 (void)connected;
1016 mDNS_Lock(m);
1017 detail = cs->detail;
1018
1019 // If we had a socket open but the connect failed, close it and try the next address, if we have
1020 // a next address.
1021 if (sock != NULL) {
1022 cs->last_event = m->timenow;
1023
1024 cs->connecting = mDNSfalse;
1025 if (err != mStatus_NoError) {
1026 mDNSPlatformTCPCloseConnection(sock);
1027 LogMsg("dso_connect_callback: connect %p failed (%d)", cs, err);
1028 } else {
1029 success:
1030 // We got a connection.
1031 transport = dso_transport_create(sock, false, cs->context, cs->max_outstanding_queries,
1032 cs->inbuf_size, cs->outbuf_size, cs->hostname, cs->callback, cs->dso);
1033 if (transport == NULL) {
1034 // If dso_create fails, there's no point in continuing to try to connect to new
1035 // addresses
1036 fail:
1037 LogMsg("dso_connect_callback: dso_create failed");
1038 mDNSPlatformTCPCloseConnection(sock);
1039 } else {
1040 // Call the "we're connected" callback, which will start things up.
1041 transport->dso->cb(cs->context, NULL, transport->dso, kDSOEventType_Connected);
1042 }
1043
1044 cs->last_event = 0;
1045
1046 // When the connection has succeeded, stop asking questions.
1047 if (cs->lookup != NULL) {
1048 DNSServiceRef ref = cs->lookup;
1049 cs->lookup = NULL;
1050 mDNS_DropLockBeforeCallback();
1051 DNSServiceRefDeallocate(ref);
1052 mDNS_ReclaimLockAfterCallback();
1053 }
1054 mDNS_Unlock(m);
1055 return;
1056 }
1057 }
1058
1059 // If there are no addresses to connect to, and there are no queries running, then we can give
1060 // up. Otherwise, we wait for one of the queries to deliver an answer.
1061 if (cs->num_addrs <= cs->cur_addr) {
1062 if (cs->lookup == NULL) {
1063 LogMsg("dso_connect_callback: %s: no more addresses to try", cs->hostname);
1064 cs->last_event = 0;
1065 cs->callback(cs->context, NULL, NULL, kDSOEventType_ConnectFailed);
1066 }
1067 // Otherwise, we will get more callbacks when outstanding queries either fail or succeed.
1068 mDNS_Unlock(m);
1069 return;
1070 }
1071
1072 sock = mDNSPlatformTCPSocket(kTCPSocketFlags_Zero, cs->addresses[cs->cur_addr].type, NULL, NULL, mDNSfalse);
1073 if (sock == NULL) {
1074 LogMsg("drConnectCallback: couldn't get a socket for %s: %s%s",
1075 cs->hostname, strerror(errno), detail);
1076 goto fail;
1077 }
1078
1079 LogMsg("dso_connect_callback: Attempting to connect to %#a%%%d",
1080 &cs->addresses[cs->cur_addr], ntohs(cs->ports[cs->cur_addr].NotAnInteger));
1081
1082 status = mDNSPlatformTCPConnect(sock, &cs->addresses[cs->cur_addr], cs->ports[cs->cur_addr], NULL,
1083 dso_connect_callback, cs);
1084 cs->cur_addr++;
1085 if (status == mStatus_NoError || status == mStatus_ConnEstablished) {
1086 // This can't happen in practice on MacOS; we don't know about all other operating systems,
1087 // so we handle it just in case.
1088 LogMsg("dso_connect_callback: synchronous connect to %s", cs->hostname);
1089 goto success;
1090 } else if (status == mStatus_ConnPending) {
1091 LogMsg("dso_connect_callback: asynchronous connect to %s", cs->hostname);
1092 cs->connecting = mDNStrue;
1093 // We should get called back when the connection succeeds or fails.
1094 mDNS_Unlock(m);
1095 return;
1096 }
1097 LogMsg("dso_connect_callback: failed to connect to %s on %#a%d: %s%s",
1098 cs->hostname, &cs->addresses[cs->cur_addr],
1099 ntohs(cs->ports[cs->cur_addr].NotAnInteger), strerror(errno), detail);
1100 mDNS_Unlock(m);
1101 }
1102
1103 static void dso_connect_internal(dso_connect_state_t *cs)
1104 {
1105 dso_connect_callback(NULL, cs, false, mStatus_NoError);
1106 }
1107 #endif // DSO_USES_NETWORK_FRAMEWORK
1108
1109 static void dso_inaddr_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
1110 DNSServiceErrorType errorCode, const char *fullname, const struct sockaddr *sa,
1111 uint32_t ttl, void *context)
1112 {
1113 dso_connect_state_t *cs = context;
1114 char addrbuf[INET6_ADDRSTRLEN + 1];
1115 mDNS *m = &mDNSStorage;
1116 (void)sdRef;
1117
1118 cs->last_event = m->timenow;
1119 inet_ntop(sa->sa_family, (sa->sa_family == AF_INET
1120 ? (void *)&((struct sockaddr_in *)sa)->sin_addr
1121 : (void *)&((struct sockaddr_in6 *)sa)->sin6_addr), addrbuf, sizeof addrbuf);
1122 LogMsg("dso_inaddr_callback: %s: flags %x index %d error %d fullname %s addr %s ttl %lu",
1123 cs->hostname, flags, interfaceIndex, errorCode, fullname, addrbuf, (unsigned long)ttl);
1124
1125 if (errorCode != mStatus_NoError) {
1126 return;
1127 }
1128
1129 if (cs->num_addrs == MAX_DSO_CONNECT_ADDRS) {
1130 if (cs->cur_addr > 1) {
1131 memmove(&cs->addresses, &cs->addresses[cs->cur_addr],
1132 (MAX_DSO_CONNECT_ADDRS - cs->cur_addr) * sizeof cs->addresses[0]);
1133 cs->num_addrs -= cs->cur_addr;
1134 cs->cur_addr = 0;
1135 } else {
1136 LogMsg("dso_inaddr_callback: ran out of room for addresses.");
1137 return;
1138 }
1139 }
1140
1141 if (sa->sa_family == AF_INET) {
1142 cs->addresses[cs->num_addrs].type = mDNSAddrType_IPv4;
1143 mDNSPlatformMemCopy(&cs->addresses[cs->num_addrs].ip.v4,
1144 &((struct sockaddr_in *)sa)->sin_addr, sizeof cs->addresses[cs->num_addrs].ip.v4);
1145 } else {
1146 cs->addresses[cs->num_addrs].type = mDNSAddrType_IPv6;
1147 mDNSPlatformMemCopy(&cs->addresses[cs->num_addrs].ip.v6,
1148 &((struct sockaddr_in *)sa)->sin_addr, sizeof cs->addresses[cs->num_addrs].ip.v6);
1149 }
1150
1151 cs->ports[cs->num_addrs] = cs->config_port;
1152 cs->num_addrs++;
1153 if (!cs->connecting) {
1154 LogMsg("dso_inaddr_callback: starting a new connection.");
1155 dso_connect_internal(cs);
1156 } else {
1157 LogMsg("dso_inaddr_callback: connection in progress, deferring new connect until it fails.");
1158 }
1159 }
1160
1161 bool dso_connect(dso_connect_state_t *cs)
1162 {
1163 struct in_addr in;
1164 struct in6_addr in6;
1165
1166 // If the connection state was created with an address, use that rather than hostname.
1167 if (cs->num_addrs > 0) {
1168 dso_connect_internal(cs);
1169 }
1170 // Else allow an IPv4 address literal string
1171 else if (inet_pton(AF_INET, cs->hostname, &in)) {
1172 cs->num_addrs = 1;
1173 cs->addresses[0].type = mDNSAddrType_IPv4;
1174 cs->addresses[0].ip.v4.NotAnInteger = in.s_addr;
1175 cs->ports[0] = cs->config_port;
1176 dso_connect_internal(cs);
1177 }
1178 // ...or an IPv6 address literal string
1179 else if (inet_pton(AF_INET6, cs->hostname, &in6)) {
1180 cs->num_addrs = 1;
1181 cs->addresses[0].type = mDNSAddrType_IPv6;
1182 memcpy(&cs->addresses[0].ip.v6, &in6, sizeof in6);
1183 cs->ports[0] = cs->config_port;
1184 dso_connect_internal(cs);
1185 }
1186 // ...or else look it up.
1187 else {
1188 mDNS *m = &mDNSStorage;
1189 int err;
1190 mDNS_DropLockBeforeCallback();
1191 err = DNSServiceGetAddrInfo(&cs->lookup, kDNSServiceFlagsReturnIntermediates,
1192 kDNSServiceInterfaceIndexAny, 0, cs->hostname, dso_inaddr_callback, cs);
1193
1194 mDNS_ReclaimLockAfterCallback();
1195 if (err != mStatus_NoError) {
1196 LogMsg("dso_connect: inaddr lookup query allocate failed for '%s': %d", cs->hostname, err);
1197 return false;
1198 }
1199 }
1200 return true;
1201 }
1202
1203 #ifdef DSO_USES_NETWORK_FRAMEWORK
1204 // We don't need this for DNS Push, so it is being left as future work.
1205 int dso_listen(dso_connect_state_t * __unused listen_context)
1206 {
1207 return mStatus_UnsupportedErr;
1208 }
1209
1210 #else
1211
1212 // Called whenever we get a connection on the DNS TCP socket
1213 static void dso_listen_callback(TCPSocket *sock, mDNSAddr *addr, mDNSIPPort *port,
1214 const char *remote_name, void *context)
1215 {
1216 dso_connect_state_t *lc = context;
1217 dso_transport_t *transport;
1218
1219 mDNS_Lock(&mDNSStorage);
1220 transport = dso_transport_create(sock, mDNStrue, lc->context, lc->max_outstanding_queries,
1221 lc->inbuf_size, lc->outbuf_size, remote_name, lc->callback, NULL);
1222 if (transport == NULL) {
1223 mDNSPlatformTCPCloseConnection(sock);
1224 LogMsg("No memory for new DSO connection from %s", remote_name);
1225 goto out;
1226 }
1227
1228 transport->remote_addr = *addr;
1229 transport->remote_port = ntohs(port->NotAnInteger);
1230 if (transport->dso->cb) {
1231 transport->dso->cb(lc->context, 0, transport->dso, kDSOEventType_Connected);
1232 }
1233 LogMsg("DSO connection from %s", remote_name);
1234 out:
1235 mDNS_Unlock(&mDNSStorage);
1236 }
1237
1238 // Listen for connections; each time we get a connection, make a new dso_state_t object with the specified
1239 // parameters and call the callback. Port can be zero to leave it unspecified.
1240
1241 int dso_listen(dso_connect_state_t *listen_context)
1242 {
1243 char addrbuf[INET6_ADDRSTRLEN + 1];
1244 mDNSIPPort port;
1245 mDNSBool reuseAddr = mDNSfalse;
1246
1247 if (listen_context->config_port.NotAnInteger) {
1248 port = listen_context->config_port;
1249 reuseAddr = mDNStrue;
1250 }
1251 listen_context->listener = mDNSPlatformTCPListen(mDNSAddrType_None, &port, NULL, kTCPSocketFlags_Zero,
1252 reuseAddr, 5, dso_listen_callback, listen_context);
1253 if (!listen_context->listener) {
1254 return mStatus_UnknownErr;
1255 }
1256 listen_context->connect_port = port;
1257 if (listen_context->addresses[0].type == mDNSAddrType_IPv4) {
1258 inet_ntop(AF_INET, &listen_context->addresses[0].ip.v4, addrbuf, sizeof addrbuf);
1259 } else {
1260 inet_ntop(AF_INET6, &listen_context->addresses[0].ip.v6, addrbuf, sizeof addrbuf);
1261 }
1262
1263 LogMsg("DSOListen: Listening on %s%%%d", addrbuf, ntohs(listen_context->connect_port.NotAnInteger));
1264 return mStatus_NoError;
1265 }
1266 #endif // DSO_USES_NETWORK_FRAMEWORK
1267
1268 // Local Variables:
1269 // mode: C
1270 // tab-width: 4
1271 // c-file-style: "bsd"
1272 // c-basic-offset: 4
1273 // fill-column: 108
1274 // indent-tabs-mode: nil
1275 // End: