Imported Upstream version 1096.40.7
[platform/upstream/mdnsresponder.git] / mDNSResponder-1096.40.7 / 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 int64_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     int 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, size_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                                   size_t length;
461                                   const uint8_t *lenbuf;
462                                   dispatch_data_t map = dispatch_data_create_map(content, (const void **)&lenbuf, &length);
463                                   if (map == NULL) {
464                                       LogMsg("dso_read_message_length: map create failed");
465                                       goto fail;
466                                   } else if (length != 2) {
467                                       LogMsg("dso_read_message_length: invalid length = %d", length);
468                                       goto fail;
469                                   }
470                                   length = ((unsigned)(lenbuf[0]) << 8) | ((unsigned)lenbuf[1]);
471                                   dso_read_message(transport, length);
472                               }
473                               KQueueUnlock("dso_read_message_length completion routine");
474                           });
475 }
476
477 void dso_read_message(dso_transport_t *transport, size_t length)
478 {
479     const uint32_t serial = transport->dso->serial;
480     if (transport->connection == NULL) {
481         LogMsg("dso_read_message called with null connection.");
482         return;
483     }
484     nw_connection_receive(transport->connection, length, length,
485                           ^(dispatch_data_t content, nw_content_context_t __unused context,
486                             bool __unused is_complete, nw_error_t error) {
487                               dso_state_t *dso;
488                               // Don't touch anything or look at anything until we have the lock.
489                               KQueueLock();
490                               dso = dso_find_by_serial(serial);
491                               if (error != NULL) {
492                                   LogMsg("dso_read_message: read failed: %s", strerror(nw_error_get_error_code(error)));
493                               fail:
494                                   if (dso != NULL) {
495                                       mDNS_Lock(&mDNSStorage);
496                                       dso_drop(dso);
497                                       mDNS_Unlock(&mDNSStorage);
498                                   }
499                               } else if (content == NULL) {
500                                   LogMsg("dso_read_message: remote end closed connection");
501                                   goto fail;
502                               } else {
503                                   size_t bytes_read;
504                                   const uint8_t *message;
505                                   dispatch_data_t map = dispatch_data_create_map(content, (const void **)&message, &bytes_read);
506                                   if (map == NULL) {
507                                       LogMsg("dso_read_message_length: map create failed");
508                                       goto fail;
509                                   } else if (bytes_read != length) {
510                                       LogMsg("dso_read_message_length: only %d of %d bytes read", bytes_read, length);
511                                       goto fail;
512                                   }
513                                   // Process the message.
514                                   mDNS_Lock(&mDNSStorage);
515                                   dns_message_received(dso, message, length);
516                                   mDNS_Unlock(&mDNSStorage);
517
518                                   // Now read the next message length.
519                                   dso_read_message_length(transport);
520                               }
521                               KQueueUnlock("dso_read_message completion routine");
522                           });
523 }
524 #else
525 // Called whenever there's data available on a DSO connection
526 void dso_read_callback(TCPSocket *sock, void *context, mDNSBool connection_established, int err)
527 {
528     dso_transport_t *transport = context;
529     dso_state_t *dso;
530     mDNSBool closed = mDNSfalse;
531
532     mDNS_Lock(&mDNSStorage);
533     dso = transport->dso;
534
535     // This shouldn't ever happen.
536     if (err) {
537         LogMsg("dso_read_callback: error %d", err);
538         dso_drop(dso);
539         goto out;
540     }
541
542     // Connection is already established by the time we set this up.
543     if (connection_established) {
544         goto out;
545     }
546     
547     // This will be true either if we have never read a message or
548     // if the last thing we did was to finish reading a message and
549     // process it.
550     if (transport->message_length == 0) {
551         transport->need_length = true;
552         transport->inbufp = transport->inbuf;
553         transport->bytes_needed = 2;
554     }
555     
556     // Read up to bytes_needed bytes.
557     ssize_t count = mDNSPlatformReadTCP(sock, transport->inbufp, transport->bytes_needed, &closed);
558     // LogMsg("read(%d, %p:%p, %d) -> %d", fd, dso->inbuf, dso->inbufp, dso->bytes_needed, count);
559     if (count < 0) {
560         LogMsg("dso_read_callback: read from %s returned %d", dso->remote_name, errno);
561         dso_drop(dso);
562         goto out;
563     }
564
565     // If we get selected for read and there's nothing to read, the remote end has closed the
566     // connection.
567     if (closed) {
568         LogMsg("dso_read_callback: remote %s closed", dso->remote_name);
569         dso_drop(dso);
570         goto out;
571     }
572     
573     transport->inbufp += count;
574     transport->bytes_needed -= count;
575
576     // If we read all the bytes we wanted, do what's next.
577     if (transport->bytes_needed == 0) {
578         // We just finished reading the complete length of a DNS-over-TCP message.
579         if (transport->need_length) {
580             // Get the number of bytes in this DNS message
581             transport->bytes_needed = (((int)transport->inbuf[0]) << 8) | transport->inbuf[1];
582
583             // Under no circumstances can length be zero.
584             if (transport->bytes_needed == 0) {
585                 LogMsg("dso_read_callback: %s sent zero-length message.", dso->remote_name);
586                 dso_drop(dso);
587                 goto out;
588             }
589
590             // The input buffer size is AbsoluteMaxDNSMessageData, which is around 9000 bytes on
591             // big platforms and around 1500 bytes on smaller ones.   If the remote end has sent
592             // something larger than that, it's an error from which we can't recover.
593             if (transport->bytes_needed > transport->inbuf_size - 2) {
594                 LogMsg("dso_read_callback: fatal: Proxy at %s sent a too-long (%ld bytes) message",
595                        dso->remote_name, (long)transport->bytes_needed);
596                 dso_drop(dso);
597                 goto out;
598             }
599
600             transport->message_length = transport->bytes_needed;
601             transport->inbufp = transport->inbuf + 2;
602             transport->need_length = false;
603
604         // We just finished reading a complete DNS-over-TCP message.
605         } else {
606             dns_message_received(dso, &transport->inbuf[2], transport->message_length);
607             transport->message_length = 0;
608         }
609     }
610 out:
611     mDNS_Unlock(&mDNSStorage);
612 }
613 #endif // DSO_USES_NETWORK_FRAMEWORK
614
615 #ifdef DSO_USES_NETWORK_FRAMEWORK
616 static dso_transport_t *dso_transport_create(nw_connection_t connection, bool is_server, void *context,
617                                              int max_outstanding_queries, size_t outbuf_size_in, const char *remote_name,
618                                              dso_event_callback_t cb, dso_state_t *dso)
619 {
620     dso_transport_t *transport;
621     uint8_t *transp;
622     const size_t outbuf_size = outbuf_size_in + 256; // Space for additional TLVs
623
624     // We allocate everything in a single hunk so that we can free it together as well.
625     transp = mallocL("dso_transport_create", (sizeof *transport) + outbuf_size);
626     if (transp == NULL) {
627         transport = NULL;
628         goto out;
629     }
630     // Don't clear the buffers.
631     mDNSPlatformMemZero(transp, sizeof (*transport));
632
633     transport = (dso_transport_t *)transp;
634     transp += sizeof *transport;
635
636     transport->outbuf = transp;
637     transport->outbuf_size = outbuf_size;
638
639     if (dso == NULL) {
640         transport->dso = dso_create(is_server, max_outstanding_queries, remote_name, cb, context, transport);
641         if (transport->dso == NULL) {
642             mDNSPlatformMemFree(transport);
643             transport = NULL;
644             goto out;
645         }
646     } else {
647         transport->dso = dso;
648     }
649     transport->connection = connection;
650     nw_retain(transport->connection);
651     transport->serial = dso_transport_serial++;
652
653     transport->dso->transport = transport;
654     transport->dso->transport_finalize = dso_transport_finalize;
655     transport->next = dso_transport_states;
656     dso_transport_states = transport;
657
658     // Start looking for messages...
659     dso_read_message_length(transport);
660 out:
661     return transport;
662 }
663 #else
664 // Create a dso_transport_t structure
665 static dso_transport_t *dso_transport_create(TCPSocket *sock, bool is_server, void *context, int max_outstanding_queries,
666                                              size_t inbuf_size_in, size_t outbuf_size_in, const char *remote_name,
667                                              dso_event_callback_t cb, dso_state_t *dso)
668 {
669     dso_transport_t *transport;
670     size_t outbuf_size;
671     size_t inbuf_size;
672     uint8_t *transp;
673     int status;
674
675     // There's no point in a DSO that doesn't have a callback.
676     if (!cb) {
677         return NULL;
678     }
679
680     outbuf_size = outbuf_size_in + 256; // Space for additional TLVs
681     inbuf_size = inbuf_size_in + 2;   // Space for length
682
683     // We allocate everything in a single hunk so that we can free it together as well.
684     transp = mallocL("dso_transport_create", (sizeof *transport) + inbuf_size + outbuf_size);
685     if (transp == NULL) {
686         transport = NULL;
687         goto out;
688     }
689     // Don't clear the buffers.
690     mDNSPlatformMemZero(transp, sizeof (*transport));
691
692     transport = (dso_transport_t *)transp;
693     transp += sizeof *transport;
694
695     transport->inbuf = transp;
696     transport->inbuf_size = inbuf_size;
697     transp += inbuf_size;
698
699     transport->outbuf = transp;
700     transport->outbuf_size = outbuf_size;
701
702     if (dso == NULL) {
703         transport->dso = dso_create(is_server, max_outstanding_queries, remote_name, cb, context, transport);
704         if (transport->dso == NULL) {
705             mDNSPlatformMemFree(transport);
706             transport = NULL;
707             goto out;
708         }
709     } else {
710         transport->dso = dso;
711     }
712     transport->connection = sock;
713
714     status = mDNSPlatformTCPSocketSetCallback(sock, dso_read_callback, transport);
715     if (status != mStatus_NoError) {
716         LogMsg("dso_create: unable to set callback: %d", status);
717         dso_drop(transport->dso);
718         goto out;
719     }
720
721     transport->dso->transport = transport;
722     transport->dso->transport_finalize = dso_transport_finalize;
723     transport->next = dso_transport_states;
724     dso_transport_states = transport;
725 out:
726     return transport;
727 }
728 #endif // DSO_USES_NETWORK_FRAMEWORK
729
730 // This should all be replaced with Network Framework connection setup.
731 dso_connect_state_t *dso_connect_state_create(const char *hostname, mDNSAddr *addr, mDNSIPPort port,
732                                               int max_outstanding_queries, size_t inbuf_size, size_t outbuf_size,
733                                               dso_event_callback_t callback, dso_state_t *dso, void *context, const char *detail)
734 {
735     int detlen = strlen(detail) + 1;
736     int hostlen = hostname == NULL ? 0 : strlen(hostname) + 1;
737     int len;
738     dso_connect_state_t *cs;
739     char *csp;
740     char nbuf[INET6_ADDRSTRLEN + 1];
741     dso_connect_state_t **states;
742
743     // Enforce Some Minimums (Xxx these are a bit arbitrary, maybe not worth doing?)
744     if (inbuf_size < MaximumRDSize || outbuf_size < 128 || max_outstanding_queries < 1) {
745         return 0;
746     }
747
748     // If we didn't get a hostname, make a presentation form of the IP address to use instead.
749     if (!hostlen) {
750         if (addr != NULL) {
751             if (addr->type == mDNSAddrType_IPv4) {
752                 hostname = inet_ntop(AF_INET, &addr->ip.v4, nbuf, sizeof nbuf);
753             } else {
754                 hostname = inet_ntop(AF_INET6, &addr->ip.v6, nbuf, sizeof nbuf);
755             }
756             if (hostname != NULL) {
757                 hostlen = strlen(nbuf);
758             }
759         }
760     }
761     // If we don't have a printable name, we won't proceed, because this means we don't know
762     // what to connect to.
763     if (!hostlen) {
764         return 0;
765     }
766
767     len = (sizeof *cs) + detlen + hostlen;
768     csp = malloc(len);
769     if (!csp) {
770         return NULL;
771     }
772     cs = (dso_connect_state_t *)csp;
773     memset(cs, 0, sizeof *cs);
774     csp += sizeof *cs;
775
776     cs->detail = csp;
777     memcpy(cs->detail, detail, detlen);
778     csp += detlen;
779     cs->hostname = csp;
780     memcpy(cs->hostname, hostname, hostlen);
781
782     cs->config_port = port;
783     cs->max_outstanding_queries = max_outstanding_queries;
784     cs->outbuf_size = outbuf_size;
785     if (context) {
786         cs->context = context;
787     } // else cs->context = NULL because of memset call above.
788     cs->callback = callback;
789     cs->connect_port.NotAnInteger = 0;
790     cs->dso = dso;
791 #ifdef DSO_USES_NETWORK_FRAMEWORK
792     cs->serial = dso_transport_serial++;
793 #else
794     cs->inbuf_size = inbuf_size;
795 #endif
796
797     if (addr) {
798         cs->num_addrs = 1;
799         cs->addresses[0] = *addr;
800         cs->ports[0] = port;
801     }
802     for (states = &dso_connect_states; *states != NULL; states = &(*states)->next)
803         ;
804     *states = cs;
805     return cs;
806 }
807
808 #ifdef DSO_USES_NETWORK_FRAMEWORK
809 void dso_connect_state_use_tls(dso_connect_state_t *cs)
810 {
811     cs->tls_enabled = true;
812 }
813 #endif
814
815 void dso_connect_state_drop(dso_connect_state_t *cs)
816 {
817     dso_connect_state_t **states;
818
819     for (states = &dso_connect_states; *states != NULL && *states != cs; states = &(*states)->next)
820         ;
821     if (*states) {
822         *states = cs->next;;
823     } else {
824         LogMsg("dso_connect_state_drop: dropping a connect state that isn't recognized.");
825     }
826 #ifdef DSO_USES_NETWORK_FRAMEWORK
827     if (cs->connection != NULL) {
828         nw_connection_cancel(cs->connection);
829         nw_release(cs->connection);
830         cs->connection = NULL;
831     }
832 #endif
833     mDNSPlatformMemFree(cs);
834 }
835
836 #ifdef DSO_USES_NETWORK_FRAMEWORK
837 static void
838 dso_connection_succeeded(dso_connect_state_t *cs)
839 {
840     // We got a connection.
841     dso_transport_t *transport =
842         dso_transport_create(cs->connection, false, cs->context, cs->max_outstanding_queries,
843                              cs->outbuf_size, cs->hostname, cs->callback, cs->dso);
844     nw_release(cs->connection);
845     cs->connection = NULL;
846     if (transport == NULL) {
847         // If dso_transport_create fails, there's no point in continuing to try to connect to new
848         // addresses
849         LogMsg("dso_connection_succeeded: dso_create failed");
850         // XXX we didn't retain the connection, so we're done when it goes out of scope, right?
851     } else {
852         // Call the "we're connected" callback, which will start things up.
853         transport->dso->cb(cs->context, NULL, transport->dso, kDSOEventType_Connected);
854     }
855     
856     cs->last_event = 0;
857
858     // When the connection has succeeded, stop asking questions.
859     if (cs->lookup != NULL) {
860         mDNS *m = &mDNSStorage;
861         DNSServiceRef ref = cs->lookup;
862         cs->lookup = NULL;
863         mDNS_DropLockBeforeCallback();
864         DNSServiceRefDeallocate(ref);
865         mDNS_ReclaimLockAfterCallback();
866     }
867     return;
868 }
869
870
871 static void dso_connect_internal(dso_connect_state_t *cs)
872 {
873     uint32_t serial = cs->serial;
874
875     cs->last_event = mDNSStorage.timenow;
876
877     if (cs->num_addrs <= cs->cur_addr) {
878         if (cs->lookup == NULL) {
879             LogMsg("dso_connect_internal: %s: no more addresses to try", cs->hostname);
880             cs->last_event = 0;
881             cs->callback(cs->context, NULL, NULL, kDSOEventType_ConnectFailed);
882         }
883         // Otherwise, we will get more callbacks when outstanding queries either fail or succeed.
884         return;
885     }            
886         
887     char addrbuf[INET6_ADDRSTRLEN + 1];
888     char portbuf[6];
889
890     inet_ntop(cs->addresses[cs->cur_addr].type == mDNSAddrType_IPv4 ? AF_INET : AF_INET6,
891               cs->addresses[cs->cur_addr].type == mDNSAddrType_IPv4
892               ? (void *)cs->addresses[cs->cur_addr].ip.v4.b
893               : (void *)cs->addresses[cs->cur_addr].ip.v6.b, addrbuf, sizeof addrbuf);
894     snprintf(portbuf, sizeof portbuf, "%u", ntohs(cs->ports[cs->cur_addr].NotAnInteger));
895     cs->cur_addr++;
896
897     nw_endpoint_t endpoint = nw_endpoint_create_host(addrbuf, portbuf);
898     if (endpoint == NULL) {
899     nomem:
900         LogMsg("dso_connect_internal: no memory creating connection.");
901         return;
902     }
903     nw_parameters_t parameters = NULL;
904     nw_parameters_configure_protocol_block_t configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL;
905     if (cs->tls_enabled) {
906         // This sets up a block that's called when we get a TLS connection and want to verify
907         // the cert.   Right now we only support opportunistic security, which means we have
908         // no way to validate the cert.   Future work: add support for validating the cert
909         // using a TLSA record if one is present.
910         configure_tls = ^(nw_protocol_options_t tls_options) {
911             sec_protocol_options_t sec_options = nw_tls_copy_sec_protocol_options(tls_options);
912             sec_protocol_options_set_verify_block(sec_options, 
913                                                   ^(sec_protocol_metadata_t __unused metadata,
914                                                     sec_trust_t __unused trust_ref,
915                                                     sec_protocol_verify_complete_t complete) {
916                                                       complete(true);
917                                                   }, dso_dispatch_queue);
918         };
919     }
920     parameters = nw_parameters_create_secure_tcp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION);
921     if (parameters == NULL) {
922         goto nomem;
923     }
924     nw_connection_t connection = nw_connection_create(endpoint, parameters);
925     if (connection == NULL) {
926         goto nomem;
927     }
928     cs->connection = connection;
929
930     LogMsg("dso_connect_internal: Attempting to connect to %s%%%s", addrbuf, portbuf);
931     nw_connection_set_queue(connection, dso_dispatch_queue);
932     nw_connection_set_state_changed_handler(
933         connection, ^(nw_connection_state_t state, nw_error_t error) {
934             dso_connect_state_t *ncs;
935             KQueueLock();
936             ncs = dso_connect_state_find(serial); // Might have been freed.
937             if (ncs == NULL) {
938                 LogMsg("forgotten connection is %s.",
939                        state == nw_connection_state_cancelled ? "canceled" :
940                        state == nw_connection_state_failed ? "failed" :
941                        state == nw_connection_state_waiting ? "canceled" :
942                        state == nw_connection_state_ready ? "ready" : "unknown");
943                 if (state != nw_connection_state_cancelled) {
944                     nw_connection_cancel(connection);
945                     // Don't need to release it because only NW framework is holding a reference (XXX right?)
946                 }
947             } else {
948                 if (state == nw_connection_state_waiting) {
949                     LogMsg("connection to %#a%%%d is waiting", &ncs->addresses[ncs->cur_addr], ncs->ports[ncs->cur_addr]);
950
951                     // XXX the right way to do this is to just let NW Framework wait until we get a connection,
952                     // but there are a bunch of problems with that right now.   First, will we get "waiting" on
953                     // every connection we try?   We aren't relying on NW Framework for DNS lookups, so we are
954                     // connecting to an IP address, not a host, which means in principle that a later IP address
955                     // might be reachable.   So we have to stop trying on this one to try that one.   Oops.
956                     // Once we get NW Framework to use internal calls to resolve names, we can fix this.
957                     // Second, maybe we want to switch to polling if this happens.   Probably not, but we need
958                     // to think this through.   So right now we're just using the semantics of regular sockets,
959                     // which we /have/ thought through.   So in the future we should do this think-through and
960                     // try to use NW Framework as it's intended to work rather than as if it were just sockets.
961                     ncs->connecting = mDNSfalse;
962                     nw_connection_cancel(connection);
963                 } else if (state == nw_connection_state_failed) {
964                     // We tried to connect, but didn't succeed.
965                     LogMsg("dso_connect_internal: failed to connect to %s on %#a%%%d: %s%s",
966                            ncs->hostname, &ncs->addresses[ncs->cur_addr], ncs->ports[ncs->cur_addr],
967                            strerror(nw_error_get_error_code(error)), ncs->detail);
968                     nw_release(ncs->connection);
969                     ncs->connection = NULL;
970                     ncs->connecting = mDNSfalse;
971                     // This will do the work of figuring out if there are more addresses to try.
972                     mDNS_Lock(&mDNSStorage);
973                     dso_connect_internal(ncs);
974                     mDNS_Unlock(&mDNSStorage);
975                 } else if (state == nw_connection_state_ready) {
976                     ncs->connecting = mDNSfalse;
977                     mDNS_Lock(&mDNSStorage);
978                     dso_connection_succeeded(ncs);
979                     mDNS_Unlock(&mDNSStorage);
980                 } else if (state == nw_connection_state_cancelled) {
981                     if (ncs->connection) {
982                         nw_release(ncs->connection);
983                     }
984                     ncs->connection = NULL;
985                     ncs->connecting = mDNSfalse;
986                     // If we get here and cs exists, we are still trying to connect.   So do the next step.
987                     mDNS_Lock(&mDNSStorage);
988                     dso_connect_internal(ncs);
989                     mDNS_Unlock(&mDNSStorage);
990                 }
991             }                
992             KQueueUnlock("dso_connect_internal state change handler");
993         });
994     nw_connection_start(connection);
995     cs->connecting = mDNStrue;
996 }
997
998 #else
999 static void dso_connect_callback(TCPSocket *sock, void *context, mDNSBool connected, int err)
1000 {
1001     dso_connect_state_t *cs = context;
1002     char *detail;
1003     int status;
1004     dso_transport_t *transport;
1005     mDNS *m = &mDNSStorage;
1006
1007     (void)connected;
1008     mDNS_Lock(m);
1009     detail = cs->detail;
1010     
1011     // If we had a socket open but the connect failed, close it and try the next address, if we have
1012     // a next address.
1013     if (sock != NULL) {
1014         cs->last_event = m->timenow;
1015
1016         cs->connecting = mDNSfalse;
1017         if (err != mStatus_NoError) {
1018             mDNSPlatformTCPCloseConnection(sock);
1019             LogMsg("dso_connect_callback: connect %p failed (%d)", cs, err);
1020         } else {
1021         success:
1022             // We got a connection.
1023             transport = dso_transport_create(sock, false, cs->context, cs->max_outstanding_queries,
1024                                              cs->inbuf_size, cs->outbuf_size, cs->hostname, cs->callback, cs->dso);
1025             if (transport == NULL) {
1026                 // If dso_create fails, there's no point in continuing to try to connect to new
1027                 // addresses
1028             fail:
1029                 LogMsg("dso_connect_callback: dso_create failed");
1030                 mDNSPlatformTCPCloseConnection(sock);
1031             } else {
1032                 // Call the "we're connected" callback, which will start things up.
1033                 transport->dso->cb(cs->context, NULL, transport->dso, kDSOEventType_Connected);
1034             }
1035
1036             cs->last_event = 0;
1037
1038             // When the connection has succeeded, stop asking questions.
1039             if (cs->lookup != NULL) {
1040                 DNSServiceRef ref = cs->lookup;
1041                 cs->lookup = NULL;
1042                 mDNS_DropLockBeforeCallback();
1043                 DNSServiceRefDeallocate(ref);
1044                 mDNS_ReclaimLockAfterCallback();
1045             }
1046             mDNS_Unlock(m);
1047             return;
1048         }
1049     }
1050
1051     // If there are no addresses to connect to, and there are no queries running, then we can give
1052     // up.  Otherwise, we wait for one of the queries to deliver an answer.
1053     if (cs->num_addrs <= cs->cur_addr) {
1054         if (cs->lookup == NULL) {
1055             LogMsg("dso_connect_callback: %s: no more addresses to try", cs->hostname);
1056             cs->last_event = 0;
1057             cs->callback(cs->context, NULL, NULL, kDSOEventType_ConnectFailed);
1058         }
1059         // Otherwise, we will get more callbacks when outstanding queries either fail or succeed.
1060         mDNS_Unlock(m);
1061         return;
1062     }            
1063         
1064     sock = mDNSPlatformTCPSocket(kTCPSocketFlags_Zero, cs->addresses[cs->cur_addr].type, NULL, NULL, mDNSfalse);
1065     if (sock == NULL) {
1066         LogMsg("drConnectCallback: couldn't get a socket for %s: %s%s",
1067                cs->hostname, strerror(errno), detail);
1068         goto fail;
1069     }
1070
1071     LogMsg("dso_connect_callback: Attempting to connect to %#a%%%d",
1072            &cs->addresses[cs->cur_addr], ntohs(cs->ports[cs->cur_addr].NotAnInteger));
1073
1074     status = mDNSPlatformTCPConnect(sock, &cs->addresses[cs->cur_addr], cs->ports[cs->cur_addr], NULL,
1075                                     dso_connect_callback, cs);
1076     cs->cur_addr++;
1077     if (status == mStatus_NoError || status == mStatus_ConnEstablished) {
1078         // This can't happen in practice on MacOS; we don't know about all other operating systems,
1079         // so we handle it just in case.
1080         LogMsg("dso_connect_callback: synchronous connect to %s", cs->hostname);
1081         goto success;
1082     } else if (status == mStatus_ConnPending) {
1083         LogMsg("dso_connect_callback: asynchronous connect to %s", cs->hostname);
1084         cs->connecting = mDNStrue;
1085         // We should get called back when the connection succeeds or fails.
1086         mDNS_Unlock(m);
1087         return;
1088     }
1089     LogMsg("dso_connect_callback: failed to connect to %s on %#a%d: %s%s",
1090            cs->hostname, &cs->addresses[cs->cur_addr],
1091            ntohs(cs->ports[cs->cur_addr].NotAnInteger), strerror(errno), detail);
1092     mDNS_Unlock(m);
1093 }
1094
1095 static void dso_connect_internal(dso_connect_state_t *cs)
1096 {
1097     dso_connect_callback(NULL, cs, false, mStatus_NoError);
1098 }
1099 #endif // DSO_USES_NETWORK_FRAMEWORK
1100
1101 static void dso_inaddr_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
1102                                 DNSServiceErrorType errorCode, const char *fullname, const struct sockaddr *sa,
1103                                 uint32_t ttl, void *context)
1104 {
1105     dso_connect_state_t *cs = context;
1106     char addrbuf[INET6_ADDRSTRLEN + 1];
1107     mDNS *m = &mDNSStorage;
1108     (void)sdRef;
1109
1110     cs->last_event = m->timenow;
1111     inet_ntop(sa->sa_family, (sa->sa_family == AF_INET
1112                               ? (void *)&((struct sockaddr_in *)sa)->sin_addr
1113                               : (void *)&((struct sockaddr_in6 *)sa)->sin6_addr), addrbuf, sizeof addrbuf);
1114     LogMsg("dso_inaddr_callback: %s: flags %x index %d error %d fullname %s addr %s ttl %lu",
1115            cs->hostname, flags, interfaceIndex, errorCode, fullname, addrbuf, (unsigned long)ttl);
1116     
1117     if (errorCode != mStatus_NoError) {
1118         return;
1119     }
1120
1121     if (cs->num_addrs == MAX_DSO_CONNECT_ADDRS) {
1122         if (cs->cur_addr > 1) {
1123             memmove(&cs->addresses, &cs->addresses[cs->cur_addr],
1124                     (MAX_DSO_CONNECT_ADDRS - cs->cur_addr) * sizeof cs->addresses[0]);
1125             cs->num_addrs -= cs->cur_addr;
1126             cs->cur_addr = 0;
1127         } else {
1128             LogMsg("dso_inaddr_callback: ran out of room for addresses.");
1129             return;
1130         }
1131     }
1132
1133     if (sa->sa_family == AF_INET) {
1134         cs->addresses[cs->num_addrs].type = mDNSAddrType_IPv4;
1135         mDNSPlatformMemCopy(&cs->addresses[cs->num_addrs].ip.v4,
1136                             &((struct sockaddr_in *)sa)->sin_addr, sizeof cs->addresses[cs->num_addrs].ip.v4);
1137     } else {
1138         cs->addresses[cs->num_addrs].type = mDNSAddrType_IPv6;
1139         mDNSPlatformMemCopy(&cs->addresses[cs->num_addrs].ip.v6,
1140                             &((struct sockaddr_in *)sa)->sin_addr, sizeof cs->addresses[cs->num_addrs].ip.v6);
1141     }
1142
1143     cs->ports[cs->num_addrs] = cs->config_port;
1144     cs->num_addrs++;
1145     if (!cs->connecting) {
1146         LogMsg("dso_inaddr_callback: starting a new connection.");
1147         dso_connect_internal(cs);
1148     } else {
1149         LogMsg("dso_inaddr_callback: connection in progress, deferring new connect until it fails.");
1150     }
1151 }
1152
1153 bool dso_connect(dso_connect_state_t *cs)
1154 {
1155     struct in_addr in;
1156     struct in6_addr in6;
1157
1158     // If the connection state was created with an address, use that rather than hostname.
1159     if (cs->num_addrs > 0) {
1160         dso_connect_internal(cs);
1161     }
1162     // Else allow an IPv4 address literal string
1163     else if (inet_pton(AF_INET, cs->hostname, &in)) {
1164         cs->num_addrs = 1;
1165         cs->addresses[0].type = mDNSAddrType_IPv4;
1166         cs->addresses[0].ip.v4.NotAnInteger = in.s_addr;
1167         cs->ports[0] = cs->config_port;
1168         dso_connect_internal(cs);
1169     }
1170     // ...or an IPv6 address literal string
1171     else if (inet_pton(AF_INET6, cs->hostname, &in6)) {
1172         cs->num_addrs = 1;
1173         cs->addresses[0].type = mDNSAddrType_IPv6;
1174         memcpy(&cs->addresses[0].ip.v6, &in6, sizeof in6);
1175         cs->ports[0] = cs->config_port;
1176         dso_connect_internal(cs);
1177     }
1178     // ...or else look it up.
1179     else {
1180         mDNS *m = &mDNSStorage;
1181         int err;
1182         mDNS_DropLockBeforeCallback();
1183         err = DNSServiceGetAddrInfo(&cs->lookup, kDNSServiceFlagsReturnIntermediates,
1184                                     kDNSServiceInterfaceIndexAny, 0, cs->hostname, dso_inaddr_callback, cs);
1185
1186         mDNS_ReclaimLockAfterCallback();
1187         if (err != mStatus_NoError) {
1188             LogMsg("dso_connect: inaddr lookup query allocate failed for '%s': %d", cs->hostname, err);
1189             return false;
1190         }
1191     }
1192     return true;
1193 }
1194
1195 #ifdef DSO_USES_NETWORK_FRAMEWORK
1196 // We don't need this for DNS Push, so it is being left as future work.
1197 int dso_listen(dso_connect_state_t * __unused listen_context)
1198 {
1199     return mStatus_UnsupportedErr;
1200 }
1201
1202 #else
1203
1204 // Called whenever we get a connection on the DNS TCP socket
1205 static void dso_listen_callback(TCPSocket *sock, mDNSAddr *addr, mDNSIPPort *port,
1206                                 const char *remote_name, void *context)
1207 {
1208     dso_connect_state_t *lc = context;
1209     dso_transport_t *transport;
1210
1211     mDNS_Lock(&mDNSStorage);
1212     transport = dso_transport_create(sock, mDNStrue, lc->context, lc->max_outstanding_queries,
1213                                      lc->inbuf_size, lc->outbuf_size, remote_name, lc->callback, NULL);
1214     if (transport == NULL) {
1215         mDNSPlatformTCPCloseConnection(sock);
1216         LogMsg("No memory for new DSO connection from %s", remote_name);
1217         goto out;
1218     }
1219
1220     transport->remote_addr = *addr;
1221     transport->remote_port = ntohs(port->NotAnInteger);
1222     if (transport->dso->cb) {
1223         transport->dso->cb(lc->context, 0, transport->dso, kDSOEventType_Connected);
1224     }
1225     LogMsg("DSO connection from %s", remote_name);
1226 out:
1227     mDNS_Unlock(&mDNSStorage);
1228 }
1229
1230 // Listen for connections; each time we get a connection, make a new dso_state_t object with the specified
1231 // parameters and call the callback.   Port can be zero to leave it unspecified.
1232
1233 int dso_listen(dso_connect_state_t *listen_context)
1234 {
1235     char addrbuf[INET6_ADDRSTRLEN + 1];
1236     mDNSIPPort port;
1237     mDNSBool reuseAddr = mDNSfalse;
1238     
1239     if (listen_context->config_port.NotAnInteger) {
1240         port = listen_context->config_port;
1241         reuseAddr = mDNStrue;
1242     }
1243     listen_context->listener = mDNSPlatformTCPListen(mDNSAddrType_None, &port, NULL, kTCPSocketFlags_Zero,
1244                                                      reuseAddr, 5, dso_listen_callback, listen_context);
1245     if (!listen_context->listener) {
1246         return mStatus_UnknownErr;
1247     }
1248     listen_context->connect_port = port;
1249     if (listen_context->addresses[0].type == mDNSAddrType_IPv4) {
1250         inet_ntop(AF_INET, &listen_context->addresses[0].ip.v4, addrbuf, sizeof addrbuf);
1251     } else {
1252         inet_ntop(AF_INET6, &listen_context->addresses[0].ip.v6, addrbuf, sizeof addrbuf);
1253     }
1254     
1255     LogMsg("DSOListen: Listening on %s%%%d", addrbuf, ntohs(listen_context->connect_port.NotAnInteger));
1256     return mStatus_NoError;
1257 }
1258 #endif // DSO_USES_NETWORK_FRAMEWORK
1259
1260 // Local Variables:
1261 // mode: C
1262 // tab-width: 4
1263 // c-file-style: "bsd"
1264 // c-basic-offset: 4
1265 // fill-column: 108
1266 // indent-tabs-mode: nil
1267 // End: