xref: /illumos-gate/usr/src/contrib/mDNSResponder/mDNSShared/dnssd_clientstub.c (revision ed093b41a93e8563e6e1e5dae0768dda2a7bcc27)
1 /* -*- Mode: C; tab-width: 4 -*-
2  *
3  * Copyright (c) 2003-2020 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
14  *     contributors may be used to endorse or promote products derived from this
15  *     software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 
33 #include "dnssd_ipc.h"
34 
35 #if APPLE_OSX_mDNSResponder
36 #include <mach-o/dyld.h>
37 #include <uuid/uuid.h>
38 #include <TargetConditionals.h>
39 #include "dns_sd_private.h"
40 #include "dnssd_clientstub_apple.h"
41 #include <CoreUtils/CommonServices.h>
42 #if !defined(__i386__)
43 #define CHECK_BUNDLE_VERSION  1
44 #else
45 #define CHECK_BUNDLE_VERSION  0
46 #endif
47 #endif
48 
49 #if defined(_WIN32)
50 
51     #define _SSIZE_T
52     #include <CommonServices.h>
53     #include <DebugServices.h>
54     #include <winsock2.h>
55     #include <ws2tcpip.h>
56     #include <windows.h>
57     #include <stdarg.h>
58     #include <stdio.h>
59 
60     #define sockaddr_mdns sockaddr_in
61     #define AF_MDNS AF_INET
62 
63 // Disable warning: "'type cast' : from data pointer 'void *' to function pointer"
64     #pragma warning(disable:4055)
65 
66 // Disable warning: "nonstandard extension, function/data pointer conversion in expression"
67     #pragma warning(disable:4152)
68 
69 extern BOOL IsSystemServiceDisabled();
70 
71     #define sleep(X) Sleep((X) * 1000)
72 
73 static int g_initWinsock = 0;
74     #define LOG_WARNING kDebugLevelWarning
75     #define LOG_INFO kDebugLevelInfo
76 static void syslog( int priority, const char * message, ...)
77 {
78     va_list args;
79     int len;
80     char * buffer;
81     DWORD err = WSAGetLastError();
82     (void) priority;
83     va_start( args, message );
84     len = _vscprintf( message, args ) + 1;
85     buffer = malloc( len * sizeof(char) );
86     if ( buffer ) { vsnprintf( buffer, len, message, args ); OutputDebugString( buffer ); free( buffer ); }
87     WSASetLastError( err );
88 }
89 #else
90 
91     #include <fcntl.h>      // For O_RDWR etc.
92     #include <sys/time.h>
93     #include <sys/socket.h>
94     #include <syslog.h>
95 
96     #define sockaddr_mdns sockaddr_un
97     #define AF_MDNS AF_LOCAL
98 
99 #endif
100 
101 #if CHECK_BUNDLE_VERSION
102 #include "bundle_utilities.h"
103 #include <os/feature_private.h>
104 #endif
105 
106 #if defined(_WIN32)
107 // <rdar://problem/4096913> Specifies how many times we'll try and connect to the server.
108 
109 #define DNSSD_CLIENT_MAXTRIES 4
110 #endif // _WIN32
111 
112 // Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp)
113 //#define USE_NAMED_ERROR_RETURN_SOCKET 1
114 
115 // If the UDS client has not received a response from the daemon in 60 secs, it is unlikely to get one
116 // Note: Timeout of 3 secs should be sufficient in normal scenarios, but 60 secs is chosen as a safeguard since
117 // some clients may come up before mDNSResponder itself after a BOOT and on rare ocassions IOPM/Keychain/D2D calls
118 // in mDNSResponder's INIT may take a much longer time to return
119 #define DNSSD_CLIENT_TIMEOUT 60
120 
121 #ifndef CTL_PATH_PREFIX
122 #define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
123 #endif
124 
125 typedef struct
126 {
127     ipc_msg_hdr ipc_hdr;
128     DNSServiceFlags cb_flags;
129     uint32_t cb_interface;
130     DNSServiceErrorType cb_err;
131 } CallbackHeader;
132 
133 typedef struct _DNSServiceRef_t DNSServiceOp;
134 typedef struct _DNSRecordRef_t DNSRecord;
135 
136 #if !defined(_WIN32)
137 typedef struct
138 {
139     void             *AppCallback;      // Client callback function and context
140     void             *AppContext;
141 } SleepKAContext;
142 #endif
143 
144 // client stub callback to process message from server and deliver results to client application
145 typedef void (*ProcessReplyFn)(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *msg, const char *const end);
146 
147 #define ValidatorBits 0x12345678
148 #define DNSServiceRefValid(X) (dnssd_SocketValid((X)->sockfd) && (((X)->sockfd ^ (X)->validator) == ValidatorBits))
149 
150 // When using kDNSServiceFlagsShareConnection, there is one primary _DNSServiceOp_t, and zero or more subordinates
151 // For the primary, the 'next' field points to the first subordinate, and its 'next' field points to the next, and so on.
152 // For the primary, the 'primary' field is NULL; for subordinates the 'primary' field points back to the associated primary
153 //
154 // _DNS_SD_LIBDISPATCH is defined where libdispatch/GCD is available. This does not mean that the application will use the
155 // DNSServiceSetDispatchQueue API. Hence any new code guarded with _DNS_SD_LIBDISPATCH should still be backwards compatible.
156 struct _DNSServiceRef_t
157 {
158     DNSServiceOp     *next;             // For shared connection
159     DNSServiceOp     *primary;          // For shared connection
160     dnssd_sock_t sockfd;                // Connected socket between client and daemon
161     dnssd_sock_t validator;             // Used to detect memory corruption, double disposals, etc.
162     client_context_t uid;               // For shared connection requests, each subordinate DNSServiceRef has its own ID,
163                                         // unique within the scope of the same shared parent DNSServiceRef
164     uint32_t op;                        // request_op_t or reply_op_t
165     uint32_t max_index;                 // Largest assigned record index - 0 if no additional records registered
166     uint32_t logcounter;                // Counter used to control number of syslog messages we write
167     int              *moreptr;          // Set while DNSServiceProcessResult working on this particular DNSServiceRef
168     ProcessReplyFn ProcessReply;        // Function pointer to the code to handle received messages
169     void             *AppCallback;      // Client callback function and context
170     void             *AppContext;
171     DNSRecord        *rec;
172 #if _DNS_SD_LIBDISPATCH
173     dispatch_source_t disp_source;
174     dispatch_queue_t disp_queue;
175 #endif
176     void             *kacontext;
177 };
178 
179 struct _DNSRecordRef_t
180 {
181     DNSRecord       *recnext;
182     void *AppContext;
183     DNSServiceRegisterRecordReply AppCallback;
184     DNSRecordRef recref;
185     uint32_t record_index;  // index is unique to the ServiceDiscoveryRef
186     client_context_t uid;  // For demultiplexing multiple DNSServiceRegisterRecord calls
187     DNSServiceOp *sdr;
188 };
189 
190 #if CHECK_BUNDLE_VERSION
191 static bool _should_return_noauth_error(void)
192 {
193     static dispatch_once_t  s_once      = 0;
194     static bool        s_should    = false;
195     dispatch_once(&s_once,
196     ^{
197         s_should = bundle_sdk_is_ios14_or_later();
198     });
199     return s_should;
200 }
201 #endif
202 
203 #if !defined(USE_TCP_LOOPBACK)
204 static void SetUDSPath(struct sockaddr_un *saddr, const char *path)
205 {
206     size_t pathLen;
207 
208     pathLen = strlen(path);
209     if (pathLen < sizeof(saddr->sun_path))
210         memcpy(saddr->sun_path, path, pathLen + 1);
211     else
212         saddr->sun_path[0] = '\0';
213 }
214 #endif
215 
216 enum { write_all_success = 0, write_all_fail = -1, write_all_defunct = -2 };
217 
218 // Write len bytes. Return 0 on success, -1 on error
219 static int write_all(dnssd_sock_t sd, char *buf, size_t len)
220 {
221     // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
222     //if (send(sd, buf, len, MSG_WAITALL) != len) return write_all_fail;
223     while (len)
224     {
225         ssize_t num_written = send(sd, buf, (long)len, 0);
226         if (num_written < 0 || (size_t)num_written > len)
227         {
228             // Check whether socket has gone defunct,
229             // otherwise, an error here indicates some OS bug
230             // or that the mDNSResponder daemon crashed (which should never happen).
231 #if !defined(__ppc__) && defined(SO_ISDEFUNCT)
232             int defunct = 0;
233             socklen_t dlen = sizeof (defunct);
234             if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0)
235                 syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
236             if (!defunct)
237                 syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd,
238                        (long)num_written, (long)len,
239                        (num_written < 0) ? dnssd_errno                 : 0,
240                        (num_written < 0) ? dnssd_strerror(dnssd_errno) : "");
241             else
242                 syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd);
243             return defunct ? write_all_defunct : write_all_fail;
244 #else
245             syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd,
246                    (long)num_written, (long)len,
247                    (num_written < 0) ? dnssd_errno                 : 0,
248                    (num_written < 0) ? dnssd_strerror(dnssd_errno) : "");
249             return write_all_fail;
250 #endif
251         }
252         buf += num_written;
253         len -= num_written;
254     }
255     return write_all_success;
256 }
257 
258 enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2, read_all_defunct = -3 };
259 
260 // Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for
261 static int read_all(dnssd_sock_t sd, char *buf, int len)
262 {
263     // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
264     //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1;
265 
266     while (len)
267     {
268         ssize_t num_read = recv(sd, buf, len, 0);
269         // It is valid to get an interrupted system call error e.g., somebody attaching
270         // in a debugger, retry without failing
271         if ((num_read < 0) && (errno == EINTR))
272         {
273             syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue");
274             continue;
275         }
276         if ((num_read == 0) || (num_read < 0) || (num_read > len))
277         {
278             int printWarn = 0;
279             int defunct = 0;
280 
281             // Check whether socket has gone defunct,
282             // otherwise, an error here indicates some OS bug
283             // or that the mDNSResponder daemon crashed (which should never happen).
284 #if defined(WIN32)
285             // <rdar://problem/7481776> Suppress logs for "A non-blocking socket operation
286             //                          could not be completed immediately"
287             if (WSAGetLastError() != WSAEWOULDBLOCK)
288                 printWarn = 1;
289 #endif
290 #if !defined(__ppc__) && defined(SO_ISDEFUNCT)
291             {
292                 socklen_t dlen = sizeof (defunct);
293                 if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0)
294                     syslog(LOG_WARNING, "dnssd_clientstub read_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
295             }
296             if (!defunct)
297                 printWarn = 1;
298 #endif
299             if (printWarn)
300                 syslog(LOG_WARNING, "dnssd_clientstub read_all(%d) failed %ld/%ld %d %s", sd,
301                        (long)num_read, (long)len,
302                        (num_read < 0) ? dnssd_errno                 : 0,
303                        (num_read < 0) ? dnssd_strerror(dnssd_errno) : "");
304             else if (defunct)
305                 syslog(LOG_INFO, "dnssd_clientstub read_all(%d) DEFUNCT", sd);
306             return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : (defunct ? read_all_defunct : read_all_fail);
307         }
308         buf += num_read;
309         len -= num_read;
310     }
311     return read_all_success;
312 }
313 
314 // Returns 1 if more bytes remain to be read on socket descriptor sd, 0 otherwise
315 static int more_bytes(dnssd_sock_t sd)
316 {
317     struct timeval tv = { 0, 0 };
318     fd_set readfds;
319     fd_set *fs;
320     int ret;
321 
322 #if defined(_WIN32)
323     fs = &readfds;
324     FD_ZERO(fs);
325     FD_SET(sd, fs);
326     ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv);
327 #else
328     // This whole thing would probably be better done using kevent() instead of select()
329     if (sd < FD_SETSIZE)
330     {
331         fs = &readfds;
332         FD_ZERO(fs);
333     }
334     else
335     {
336         // Compute the number of integers needed for storing "sd". Internally fd_set is stored
337         // as an array of ints with one bit for each fd and hence we need to compute
338         // the number of ints needed rather than the number of bytes. If "sd" is 32, we need
339         // two ints and not just one.
340         int nfdbits = sizeof (int) * 8;
341         int nints = (sd/nfdbits) + 1;
342         fs = (fd_set *)calloc(nints, (size_t)sizeof(int));
343         if (fs == NULL)
344         {
345             syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed");
346             return 0;
347         }
348     }
349     FD_SET(sd, fs);
350     ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv);
351     if (fs != &readfds)
352         free(fs);
353 #endif
354     return (ret > 0);
355 }
356 
357 // set_waitlimit() implements a timeout using select. It is called from deliver_request() before recv() OR accept()
358 // to ensure the UDS clients are not blocked in these system calls indefinitely.
359 // Note: Ideally one should never be blocked here, because it indicates either mDNSResponder daemon is not yet up/hung/
360 // superbusy/crashed or some other OS bug. For eg: On Windows which suffers from 3rd party software
361 // (primarily 3rd party firewall software) interfering with proper functioning of the TCP protocol stack it is possible
362 // the next operation on this socket(recv/accept) is blocked since we depend on TCP to communicate with the system service.
363 static int set_waitlimit(dnssd_sock_t sock, int timeout)
364 {
365     int gDaemonErr = kDNSServiceErr_NoError;
366 
367     // The comment below is wrong. The select() routine does not cause stack corruption.
368     // The use of FD_SET out of range for the bitmap is what causes stack corruption.
369     // For how to do this correctly, see the example using calloc() in more_bytes() above.
370     // Even better, both should be changed to use kevent() instead of select().
371     // To prevent stack corruption since select does not work with timeout if fds > FD_SETSIZE(1024)
372     if (!gDaemonErr && sock < FD_SETSIZE)
373     {
374         struct timeval tv;
375         fd_set set;
376 
377         FD_ZERO(&set);
378         FD_SET(sock, &set);
379         tv.tv_sec = timeout;
380         tv.tv_usec = 0;
381         if (!select((int)(sock + 1), &set, NULL, NULL, &tv))
382         {
383             // Ideally one should never hit this case: See comments before set_waitlimit()
384             syslog(LOG_WARNING, "dnssd_clientstub set_waitlimit:_daemon timed out (%d secs) without any response: Socket %d", timeout, sock);
385             gDaemonErr = kDNSServiceErr_Timeout;
386         }
387     }
388     return gDaemonErr;
389 }
390 
391 /* create_hdr
392  *
393  * allocate and initialize an ipc message header. Value of len should initially be the
394  * length of the data, and is set to the value of the data plus the header. data_start
395  * is set to point to the beginning of the data section. SeparateReturnSocket should be
396  * non-zero for calls that can't receive an immediate error return value on their primary
397  * socket, and therefore require a separate return path for the error code result.
398  * if zero, the path to a control socket is appended at the beginning of the message buffer.
399  * data_start is set past this string.
400  */
401 static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int SeparateReturnSocket, DNSServiceOp *ref)
402 {
403     char *msg = NULL;
404     ipc_msg_hdr *hdr;
405     int datalen;
406 #if !defined(USE_TCP_LOOPBACK)
407     char ctrl_path[64] = "";    // "/var/tmp/dnssd_result_socket.xxxxxxxxxx-xxx-xxxxxx"
408 #endif
409 
410     if (SeparateReturnSocket)
411     {
412 #if defined(USE_TCP_LOOPBACK)
413         *len += 2;  // Allocate space for two-byte port number
414 #elif defined(USE_NAMED_ERROR_RETURN_SOCKET)
415         struct timeval tv;
416         if (gettimeofday(&tv, NULL) < 0)
417         { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: gettimeofday failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); return NULL; }
418         snprintf(ctrl_path, sizeof(ctrl_path), "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(),
419                 (unsigned long)(tv.tv_sec & 0xFFF), (unsigned long)(tv.tv_usec));
420         *len += strlen(ctrl_path) + 1;
421 #else
422         *len += 1;      // Allocate space for single zero byte (empty C string)
423 #endif
424     }
425 
426     datalen = (int) *len;
427     *len += sizeof(ipc_msg_hdr);
428 
429     // Write message to buffer
430     msg = malloc(*len);
431     if (!msg) { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: malloc failed"); return NULL; }
432 
433     memset(msg, 0, *len);
434     hdr = (ipc_msg_hdr *)msg;
435     hdr->version                = VERSION;
436     hdr->datalen                = datalen;
437     hdr->ipc_flags              = 0;
438     hdr->op                     = op;
439     hdr->client_context         = ref->uid;
440     hdr->reg_index              = 0;
441     *data_start = msg + sizeof(ipc_msg_hdr);
442 #if defined(USE_TCP_LOOPBACK)
443     // Put dummy data in for the port, since we don't know what it is yet.
444     // The data will get filled in before we send the message. This happens in deliver_request().
445     if (SeparateReturnSocket) put_uint16(0, data_start);
446 #else
447     if (SeparateReturnSocket) put_string(ctrl_path, data_start);
448 #endif
449     return hdr;
450 }
451 
452 static void FreeDNSRecords(DNSServiceOp *sdRef)
453 {
454     DNSRecord *rec = sdRef->rec;
455     while (rec)
456     {
457         DNSRecord *next = rec->recnext;
458         free(rec);
459         rec = next;
460     }
461 }
462 
463 static void FreeDNSServiceOp(DNSServiceOp *x)
464 {
465     // We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed
466     // then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket)
467     if ((x->sockfd ^ x->validator) != ValidatorBits)
468     {
469     }
470     else
471     {
472         x->next         = NULL;
473         x->primary      = NULL;
474         x->sockfd       = dnssd_InvalidSocket;
475         x->validator    = 0xDDDDDDDD;
476         x->op           = request_op_none;
477         x->max_index    = 0;
478         x->logcounter   = 0;
479         x->moreptr      = NULL;
480         x->ProcessReply = NULL;
481         x->AppCallback  = NULL;
482         x->AppContext   = NULL;
483 #if _DNS_SD_LIBDISPATCH
484         if (x->disp_source) dispatch_release(x->disp_source);
485         x->disp_source  = NULL;
486         x->disp_queue   = NULL;
487 #endif
488         // DNSRecords may have been added to subordinate sdRef e.g., DNSServiceRegister/DNSServiceAddRecord
489         // or on the main sdRef e.g., DNSServiceCreateConnection/DNSServiceRegisterRecord.
490         // DNSRecords may have been freed if the application called DNSRemoveRecord.
491         FreeDNSRecords(x);
492         if (x->kacontext)
493         {
494             free(x->kacontext);
495             x->kacontext = NULL;
496         }
497         free(x);
498     }
499 }
500 
501 // Return a connected service ref (deallocate with DNSServiceRefDeallocate)
502 static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext)
503 {
504     #if defined(_WIN32)
505     int NumTries = 0;
506     #endif // _WIN32
507 
508     dnssd_sockaddr_t saddr;
509     DNSServiceOp *sdr;
510 
511     if (!ref)
512     {
513         syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef");
514         return kDNSServiceErr_BadParam;
515     }
516 
517     if (flags & kDNSServiceFlagsShareConnection)
518     {
519         if (!*ref)
520         {
521             syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef");
522             return kDNSServiceErr_BadParam;
523         }
524         if (!DNSServiceRefValid(*ref) || ((*ref)->op != connection_request && (*ref)->op != connection_delegate_request) || (*ref)->primary)
525         {
526             syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X op %d",
527                    (*ref), (*ref)->sockfd, (*ref)->validator, (*ref)->op);
528             *ref = NULL;
529             return kDNSServiceErr_BadReference;
530         }
531     }
532 
533     #if defined(_WIN32)
534     if (!g_initWinsock)
535     {
536         WSADATA wsaData;
537         g_initWinsock = 1;
538         if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; }
539     }
540     // <rdar://problem/4096913> If the system service is disabled, we only want to try to connect once
541     if (IsSystemServiceDisabled())
542         NumTries = DNSSD_CLIENT_MAXTRIES;
543     #endif
544 
545     sdr = malloc(sizeof(DNSServiceOp));
546     if (!sdr)
547     {
548         syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed");
549         *ref = NULL;
550         return kDNSServiceErr_NoMemory;
551     }
552     sdr->next          = NULL;
553     sdr->primary       = NULL;
554     sdr->sockfd        = dnssd_InvalidSocket;
555     sdr->validator     = sdr->sockfd ^ ValidatorBits;
556     sdr->op            = op;
557     sdr->max_index     = 0;
558     sdr->logcounter    = 0;
559     sdr->moreptr       = NULL;
560     sdr->uid.u32[0]    = 0;
561     sdr->uid.u32[1]    = 0;
562     sdr->ProcessReply  = ProcessReply;
563     sdr->AppCallback   = AppCallback;
564     sdr->AppContext    = AppContext;
565     sdr->rec           = NULL;
566 #if _DNS_SD_LIBDISPATCH
567     sdr->disp_source   = NULL;
568     sdr->disp_queue    = NULL;
569 #endif
570     sdr->kacontext     = NULL;
571 
572     if (flags & kDNSServiceFlagsShareConnection)
573     {
574         DNSServiceOp **p = &(*ref)->next;       // Append ourselves to end of primary's list
575         while (*p)
576             p = &(*p)->next;
577         *p = sdr;
578         // Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear
579         if (++(*ref)->uid.u32[0] == 0)
580             ++(*ref)->uid.u32[1];               // In parent DNSServiceOp increment UID counter
581         sdr->primary    = *ref;                 // Set our primary pointer
582         sdr->sockfd     = (*ref)->sockfd;       // Inherit primary's socket
583         sdr->validator  = (*ref)->validator;
584         sdr->uid        = (*ref)->uid;
585         //printf("ConnectToServer sharing socket %d\n", sdr->sockfd);
586     }
587     else
588     {
589         #ifdef SO_NOSIGPIPE
590         const unsigned long optval = 1;
591         #endif
592         #ifndef USE_TCP_LOOPBACK
593         char* uds_serverpath = getenv(MDNS_UDS_SERVERPATH_ENVVAR);
594         if (uds_serverpath == NULL)
595             uds_serverpath = MDNS_UDS_SERVERPATH;
596         else if (strlen(uds_serverpath) >= MAX_CTLPATH)
597         {
598             uds_serverpath = MDNS_UDS_SERVERPATH;
599             syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: using default path since env len is invalid");
600         }
601         #endif
602         *ref = NULL;
603         sdr->sockfd    = socket(AF_DNSSD, SOCK_STREAM, 0);
604         sdr->validator = sdr->sockfd ^ ValidatorBits;
605         if (!dnssd_SocketValid(sdr->sockfd))
606         {
607             syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: socket failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
608             FreeDNSServiceOp(sdr);
609             return kDNSServiceErr_NoMemory;
610         }
611 #if !defined(_WIN32)
612         int fcntl_flags = fcntl(sdr->sockfd, F_GETFD);
613         if (fcntl_flags != -1)
614         {
615             fcntl_flags |= FD_CLOEXEC;
616             int ret = fcntl(sdr->sockfd, F_SETFD, fcntl_flags);
617             if (ret == -1)
618                 syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: Failed to set FD_CLOEXEC on socket %d %s",
619                        dnssd_errno, dnssd_strerror(dnssd_errno));
620         }
621         else
622         {
623             syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: Failed to get the file descriptor flags of socket %d %s",
624                    dnssd_errno, dnssd_strerror(dnssd_errno));
625         }
626 #endif // !defined(_WIN32)
627         #ifdef SO_NOSIGPIPE
628         // Some environments (e.g. OS X) support turning off SIGPIPE for a socket
629         if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
630             syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_NOSIGPIPE failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
631         #endif
632         #if defined(USE_TCP_LOOPBACK)
633         saddr.sin_family      = AF_INET;
634         saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
635         saddr.sin_port        = htons(MDNS_TCP_SERVERPORT);
636         #else
637         saddr.sun_family      = AF_LOCAL;
638         SetUDSPath(&saddr, uds_serverpath);
639         #if !defined(__ppc__) && defined(SO_DEFUNCTOK)
640         {
641             int defunct = 1;
642             if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0)
643                 syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
644         }
645         #endif
646         #endif
647 
648         #if defined(_WIN32)
649         while (1)
650         {
651             int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
652             if (!err)
653                 break; // If we succeeded, return sdr
654 
655             // If we failed, then it may be because the daemon is still launching.
656             // This can happen for processes that launch early in the boot process, while the
657             // daemon is still coming up. Rather than fail here, we wait 1 sec and try again.
658             // If, after DNSSD_CLIENT_MAXTRIES, we still can't connect to the daemon,
659             // then we give up and return a failure code.
660             if (++NumTries < DNSSD_CLIENT_MAXTRIES)
661             {
662                 syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect()-> No of tries: %d", NumTries);
663                 sleep(1); // Sleep a bit, then try again
664             }
665             else
666             {
667                 #if !defined(USE_TCP_LOOPBACK)
668                 syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed path:%s Socket:%d Err:%d Errno:%d %s",
669                        uds_serverpath, sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno));
670                 #endif
671                 dnssd_close(sdr->sockfd);
672                 FreeDNSServiceOp(sdr);
673                 return kDNSServiceErr_ServiceNotRunning;
674             }
675         }
676         #else
677         int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
678         if (err)
679         {
680             #if !defined(USE_TCP_LOOPBACK)
681             syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed path:%s Socket:%d Err:%d Errno:%d %s",
682                    uds_serverpath, sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno));
683             #endif
684             dnssd_close(sdr->sockfd);
685             FreeDNSServiceOp(sdr);
686             return kDNSServiceErr_ServiceNotRunning;
687         }
688         #endif
689     }
690 
691     *ref = sdr;
692     return kDNSServiceErr_NoError;
693 }
694 
695 #define deliver_request_bailout(MSG) \
696     syslog(LOG_WARNING, "dnssd_clientstub deliver_request: %s failed %d (%s)", (MSG), dnssd_errno, dnssd_strerror(dnssd_errno)); goto cleanup
697 
698 static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
699 {
700     uint32_t datalen;
701     dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket;
702     DNSServiceErrorType err = kDNSServiceErr_Unknown;   // Default for the "goto cleanup" cases
703     int MakeSeparateReturnSocket;
704     int ioresult;
705     #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
706     char *data;
707     #endif
708 
709     if (!hdr)
710     {
711         syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr");
712         return kDNSServiceErr_Unknown;
713     }
714 
715     datalen = hdr->datalen;    // We take a copy here because we're going to convert hdr->datalen to network byte order
716     #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
717     data = (char *)hdr + sizeof(ipc_msg_hdr);
718     #endif
719 
720     // Note: need to check hdr->op, not sdr->op.
721     // hdr->op contains the code for the specific operation we're currently doing, whereas sdr->op
722     // contains the original parent DNSServiceOp (e.g. for an add_record_request, hdr->op will be
723     // add_record_request but the parent sdr->op will be connection_request or reg_service_request)
724     MakeSeparateReturnSocket = (sdr->primary ||
725         hdr->op == reg_record_request || hdr->op == add_record_request || hdr->op == update_record_request || hdr->op == remove_record_request);
726 
727     if (!DNSServiceRefValid(sdr))
728     {
729         if (hdr)
730             free(hdr);
731         syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator);
732         return kDNSServiceErr_BadReference;
733     }
734 
735     if (MakeSeparateReturnSocket)
736     {
737         #if defined(USE_TCP_LOOPBACK)
738         {
739             union { uint16_t s; u_char b[2]; } port;
740             dnssd_sockaddr_t caddr;
741             dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr);
742             listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
743             if (!dnssd_SocketValid(listenfd)) {
744 		deliver_request_bailout("TCP socket");
745 	    }
746 
747             caddr.sin_family      = AF_INET;
748             caddr.sin_port        = 0;
749             caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
750             if (bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)) < 0) {
751 		deliver_request_bailout("TCP bind");
752 	    }
753             if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) {
754 		deliver_request_bailout("TCP getsockname");
755 	    }
756             if (listen(listenfd, 1) < 0) {
757 		deliver_request_bailout("TCP listen");
758 	    }
759             port.s = caddr.sin_port;
760             data[0] = port.b[0];  // don't switch the byte order, as the
761             data[1] = port.b[1];  // daemon expects it in network byte order
762         }
763         #elif defined(USE_NAMED_ERROR_RETURN_SOCKET)
764         {
765             mode_t mask;
766             int bindresult;
767             dnssd_sockaddr_t caddr;
768             listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
769             if (!dnssd_SocketValid(listenfd)) {
770 		deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET socket");
771 	    }
772 
773             caddr.sun_family = AF_LOCAL;
774             // According to Stevens (section 3.2), there is no portable way to
775             // determine whether sa_len is defined on a particular platform.
776             #ifndef NOT_HAVE_SA_LEN
777             caddr.sun_len = sizeof(struct sockaddr_un);
778             #endif
779             SetUDSPath(&caddr, data);
780             mask = umask(0);
781             bindresult = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr));
782             umask(mask);
783             if (bindresult < 0) {
784 		deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET bind");
785 	    }
786             if (listen(listenfd, 1) < 0) {
787 		deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET listen");
788 	    }
789         }
790         #else
791         {
792             dnssd_sock_t sp[2];
793             if (socketpair(AF_DNSSD, SOCK_STREAM, 0, sp) < 0) {
794 		deliver_request_bailout("socketpair");
795 	    }
796             else
797             {
798                 errsd    = sp[0];   // We'll read our four-byte error code from sp[0]
799                 listenfd = sp[1];   // We'll send sp[1] to the daemon
800                 #if !defined(__ppc__) && defined(SO_DEFUNCTOK)
801                 {
802                     int defunct = 1;
803                     if (setsockopt(errsd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0)
804                         syslog(LOG_WARNING, "dnssd_clientstub deliver_request: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
805                 }
806                 #endif
807             }
808         }
809         #endif
810     }
811 
812 #if !defined(USE_TCP_LOOPBACK) && !defined(USE_NAMED_ERROR_RETURN_SOCKET)
813     // If we're going to make a separate error return socket, and pass it to the daemon
814     // using sendmsg, then we'll hold back one data byte to go with it.
815     // On some versions of Unix (including Leopard) sending a control message without
816     // any associated data does not work reliably -- e.g. one particular issue we ran
817     // into is that if the receiving program is in a kqueue loop waiting to be notified
818     // of the received message, it doesn't get woken up when the control message arrives.
819     if (MakeSeparateReturnSocket || sdr->op == send_bpf)
820         datalen--;     // Okay to use sdr->op when checking for op == send_bpf
821 #endif
822 
823     // At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to
824     ConvertHeaderBytes(hdr);
825     //syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %lu bytes", (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
826     //if (MakeSeparateReturnSocket) syslog(LOG_WARNING, "dnssd_clientstub deliver_request name is %s", data);
827 #if TEST_SENDING_ONE_BYTE_AT_A_TIME
828     unsigned int i;
829     for (i=0; i<datalen + sizeof(ipc_msg_hdr); i++)
830     {
831         syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %d", i);
832         ioresult = write_all(sdr->sockfd, ((char *)hdr)+i, 1);
833         if (ioresult < write_all_success)
834         {
835             syslog(LOG_WARNING, "dnssd_clientstub deliver_request write_all (byte %u) failed", i);
836             err = (ioresult == write_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning;
837             goto cleanup;
838         }
839         usleep(10000);
840     }
841 #else
842     ioresult = write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr));
843     if (ioresult < write_all_success)
844     {
845         // write_all already prints an error message if there is an error writing to
846         // the socket except for DEFUNCT. Logging here is unnecessary and also wrong
847         // in the case of DEFUNCT sockets
848         syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed",
849                sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
850         err = (ioresult == write_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning;
851         goto cleanup;
852     }
853 #endif
854 
855     if (!MakeSeparateReturnSocket)
856         errsd = sdr->sockfd;
857     if (MakeSeparateReturnSocket || sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
858     {
859 #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
860         // At this point we may wait in accept for a few milliseconds waiting for the daemon to connect back to us,
861         // but that's okay -- the daemon should not take more than a few milliseconds to respond.
862         // set_waitlimit() ensures we do not block indefinitely just in case something is wrong
863         dnssd_sockaddr_t daddr;
864         dnssd_socklen_t len = sizeof(daddr);
865         if ((err = set_waitlimit(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError)
866             goto cleanup;
867         errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
868         if (!dnssd_SocketValid(errsd)) {
869             deliver_request_bailout("accept");
870 	}
871 #else
872 
873         struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS
874         struct msghdr msg;
875         struct cmsghdr *cmsg;
876         char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))];
877 
878         msg.msg_name       = 0;
879         msg.msg_namelen    = 0;
880         msg.msg_iov        = &vec;
881         msg.msg_iovlen     = 1;
882         msg.msg_flags      = 0;
883         if (MakeSeparateReturnSocket || sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
884         {
885             if (sdr->op == send_bpf)
886             {
887                 int i;
888                 char p[12];     // Room for "/dev/bpf999" with terminating null
889                 for (i=0; i<100; i++)
890                 {
891                     snprintf(p, sizeof(p), "/dev/bpf%d", i);
892                     listenfd = open(p, O_RDWR, 0);
893                     //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "dnssd_clientstub deliver_request Sending fd %d for %s", listenfd, p);
894                     if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY)
895                         syslog(LOG_WARNING, "dnssd_clientstub deliver_request Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno));
896                     if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break;
897                 }
898             }
899             msg.msg_control    = cbuf;
900             msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t));
901 
902             cmsg = CMSG_FIRSTHDR(&msg);
903             cmsg->cmsg_len     = CMSG_LEN(sizeof(dnssd_sock_t));
904             cmsg->cmsg_level   = SOL_SOCKET;
905             cmsg->cmsg_type    = SCM_RIGHTS;
906             *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd;
907         }
908 
909 #if TEST_KQUEUE_CONTROL_MESSAGE_BUG
910         sleep(1);
911 #endif
912 
913 #if DEBUG_64BIT_SCM_RIGHTS
914         syslog(LOG_WARNING, "dnssd_clientstub deliver_request sendmsg read sd=%d write sd=%d %ld %ld %ld/%ld/%ld/%ld",
915                errsd, listenfd, sizeof(dnssd_sock_t), sizeof(void*),
916                sizeof(struct cmsghdr) + sizeof(dnssd_sock_t),
917                CMSG_LEN(sizeof(dnssd_sock_t)), (long)CMSG_SPACE(sizeof(dnssd_sock_t)),
918                (long)((char*)CMSG_DATA(cmsg) + 4 - cbuf));
919 #endif // DEBUG_64BIT_SCM_RIGHTS
920 
921         if (sendmsg(sdr->sockfd, &msg, 0) < 0)
922         {
923             syslog(LOG_WARNING, "dnssd_clientstub deliver_request ERROR: sendmsg failed read sd=%d write sd=%d errno %d (%s)",
924                    errsd, listenfd, dnssd_errno, dnssd_strerror(dnssd_errno));
925             err = kDNSServiceErr_Incompatible;
926             goto cleanup;
927         }
928 
929 #if DEBUG_64BIT_SCM_RIGHTS
930         syslog(LOG_WARNING, "dnssd_clientstub deliver_request sendmsg read sd=%d write sd=%d okay", errsd, listenfd);
931 #endif // DEBUG_64BIT_SCM_RIGHTS
932 
933 #endif
934         // Close our end of the socketpair *before* calling read_all() to get the four-byte error code.
935         // Otherwise, if the daemon closes our socket (or crashes), we will have to wait for a timeout
936         // in read_all() because the socket is not closed (we still have an open reference to it)
937         // Note: listenfd is overwritten in the case of send_bpf above and that will be closed here
938         // for send_bpf operation.
939         dnssd_close(listenfd);
940         listenfd = dnssd_InvalidSocket; // Make sure we don't close it a second time in the cleanup handling below
941     }
942 
943     // At this point we may wait in read_all for a few milliseconds waiting for the daemon to send us the error code,
944     // but that's okay -- the daemon should not take more than a few milliseconds to respond.
945     // set_waitlimit() ensures we do not block indefinitely just in case something is wrong
946     if (sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
947         err = kDNSServiceErr_NoError;
948     else if ((err = set_waitlimit(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError)
949     {
950         ioresult = read_all(errsd, (char*)&err, (int)sizeof(err));
951         if (ioresult < read_all_success)
952             err = (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us
953         else
954             err = ntohl(err);
955     }
956     //syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err);
957 
958 cleanup:
959     if (MakeSeparateReturnSocket)
960     {
961         if (dnssd_SocketValid(listenfd)) dnssd_close(listenfd);
962         if (dnssd_SocketValid(errsd)) dnssd_close(errsd);
963 #if defined(USE_NAMED_ERROR_RETURN_SOCKET)
964         // syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removing UDS: %s", data);
965         if (unlink(data) != 0)
966             syslog(LOG_WARNING, "dnssd_clientstub WARNING: unlink(\"%s\") failed errno %d (%s)", data, dnssd_errno, dnssd_strerror(dnssd_errno));
967         // else syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removed UDS: %s", data);
968 #endif
969     }
970 
971     free(hdr);
972     return err;
973 }
974 
975 dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef)
976 {
977     if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with NULL DNSServiceRef"); return dnssd_InvalidSocket; }
978 
979     if (!DNSServiceRefValid(sdRef))
980     {
981         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with invalid DNSServiceRef %p %08X %08X",
982                sdRef, sdRef->sockfd, sdRef->validator);
983         return dnssd_InvalidSocket;
984     }
985 
986     if (sdRef->primary)
987     {
988         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef);
989         return dnssd_InvalidSocket;
990     }
991 
992     return sdRef->sockfd;
993 }
994 
995 #if _DNS_SD_LIBDISPATCH
996 static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error)
997 {
998     DNSServiceOp *sdr = sdRef;
999     DNSServiceOp *sdrNext;
1000     DNSRecord *rec;
1001     DNSRecord *recnext;
1002     int morebytes;
1003 
1004     while (sdr)
1005     {
1006         // We can't touch the sdr after the callback as it can be deallocated in the callback
1007         sdrNext = sdr->next;
1008         morebytes = 1;
1009         sdr->moreptr = &morebytes;
1010         switch (sdr->op)
1011         {
1012         case resolve_request:
1013             if (sdr->AppCallback) ((DNSServiceResolveReply)    sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL,    sdr->AppContext);
1014             break;
1015         case query_request:
1016             if (sdr->AppCallback) ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, 0, sdr->AppContext);
1017             break;
1018         case addrinfo_request:
1019             if (sdr->AppCallback) ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, NULL, 0,          sdr->AppContext);
1020             break;
1021         case browse_request:
1022             if (sdr->AppCallback) ((DNSServiceBrowseReply)     sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, NULL,          sdr->AppContext);
1023             break;
1024         case reg_service_request:
1025             if (sdr->AppCallback) ((DNSServiceRegisterReply)   sdr->AppCallback)(sdr, 0,    error, NULL, 0, NULL,          sdr->AppContext);
1026             break;
1027         case enumeration_request:
1028             if (sdr->AppCallback) ((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL,                   sdr->AppContext);
1029             break;
1030         case connection_request:
1031         case connection_delegate_request:
1032             // This means Register Record, walk the list of DNSRecords to do the callback
1033             rec = sdr->rec;
1034             while (rec)
1035             {
1036                 recnext = rec->recnext;
1037                 if (rec->AppCallback) ((DNSServiceRegisterRecordReply)rec->AppCallback)(sdr, 0, 0, error, rec->AppContext);
1038                 // The Callback can call DNSServiceRefDeallocate which in turn frees sdr and all the records.
1039                 // Detect that and return early
1040                 if (!morebytes) { syslog(LOG_WARNING, "dnssd_clientstub:Record: CallbackwithError morebytes zero"); return; }
1041                 rec = recnext;
1042             }
1043             break;
1044         case port_mapping_request:
1045             if (sdr->AppCallback) ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, 0, 0, error, 0, 0, 0, 0, 0, sdr->AppContext);
1046             break;
1047         default:
1048             syslog(LOG_WARNING, "dnssd_clientstub CallbackWithError called with bad op %d", sdr->op);
1049         }
1050         // If DNSServiceRefDeallocate was called in the callback, morebytes will be zero. As the sdRef
1051         // (and its subordinates) have been freed, we should not proceed further. Note that when we
1052         // call the callback with a subordinate sdRef the application can call DNSServiceRefDeallocate
1053         // on the main sdRef and DNSServiceRefDeallocate handles this case by walking all the sdRefs and
1054         // clears the moreptr so that we can terminate here.
1055         //
1056         // If DNSServiceRefDeallocate was not called in the callback, then set moreptr to NULL so that
1057         // we don't access the stack variable after we return from this function.
1058         if (!morebytes) { syslog(LOG_WARNING, "dnssd_clientstub:sdRef: CallbackwithError morebytes zero sdr %p", sdr); return; }
1059         else {sdr->moreptr = NULL;}
1060         sdr = sdrNext;
1061     }
1062 }
1063 #endif // _DNS_SD_LIBDISPATCH
1064 
1065 // Handle reply from server, calling application client callback. If there is no reply
1066 // from the daemon on the socket contained in sdRef, the call will block.
1067 DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef)
1068 {
1069     int morebytes = 0;
1070     int ioresult;
1071     DNSServiceErrorType error;
1072 
1073     if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
1074 
1075     if (!DNSServiceRefValid(sdRef))
1076     {
1077         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
1078         return kDNSServiceErr_BadReference;
1079     }
1080 
1081     if (sdRef->primary)
1082     {
1083         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef);
1084         return kDNSServiceErr_BadReference;
1085     }
1086 
1087     if (!sdRef->ProcessReply)
1088     {
1089         static int num_logs = 0;
1090         if (num_logs < 10) syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function");
1091         if (num_logs < 1000) num_logs++;else sleep(1);
1092         return kDNSServiceErr_BadReference;
1093     }
1094 
1095     do
1096     {
1097         CallbackHeader cbh;
1098         char *data;
1099 
1100         // return NoError on EWOULDBLOCK. This will handle the case
1101         // where a non-blocking socket is told there is data, but it was a false positive.
1102         // On error, read_all will write a message to syslog for us, so don't need to duplicate that here
1103         // Note: If we want to properly support using non-blocking sockets in the future
1104         ioresult = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr));
1105         if (ioresult == read_all_fail || ioresult == read_all_defunct)
1106         {
1107             error = (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning;
1108 
1109             // Set the ProcessReply to NULL before callback as the sdRef can get deallocated
1110             // in the callback.
1111             sdRef->ProcessReply = NULL;
1112 #if _DNS_SD_LIBDISPATCH
1113             // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult
1114             // is not called by the application and hence need to communicate the error. Cancel the
1115             // source so that we don't get any more events
1116             // Note: read_all fails if we could not read from the daemon which can happen if the
1117             // daemon dies or the file descriptor is disconnected (defunct).
1118             if (sdRef->disp_source)
1119             {
1120                 dispatch_source_cancel(sdRef->disp_source);
1121                 dispatch_release(sdRef->disp_source);
1122                 sdRef->disp_source = NULL;
1123                 CallbackWithError(sdRef, error);
1124             }
1125 #endif
1126             // Don't touch sdRef anymore as it might have been deallocated
1127             return error;
1128         }
1129         else if (ioresult == read_all_wouldblock)
1130         {
1131             if (morebytes && sdRef->logcounter < 100)
1132             {
1133                 sdRef->logcounter++;
1134                 syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult error: select indicated data was waiting but read_all returned EWOULDBLOCK");
1135             }
1136             return kDNSServiceErr_NoError;
1137         }
1138 
1139         ConvertHeaderBytes(&cbh.ipc_hdr);
1140         if (cbh.ipc_hdr.version != VERSION)
1141         {
1142             syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult daemon version %d does not match client version %d", cbh.ipc_hdr.version, VERSION);
1143             sdRef->ProcessReply = NULL;
1144             return kDNSServiceErr_Incompatible;
1145         }
1146 
1147         data = malloc(cbh.ipc_hdr.datalen);
1148         if (!data) return kDNSServiceErr_NoMemory;
1149         ioresult = read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen);
1150         if (ioresult < read_all_success) // On error, read_all will write a message to syslog for us
1151         {
1152             error = (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning;
1153 
1154             // Set the ProcessReply to NULL before callback as the sdRef can get deallocated
1155             // in the callback.
1156             sdRef->ProcessReply = NULL;
1157 #if _DNS_SD_LIBDISPATCH
1158             // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult
1159             // is not called by the application and hence need to communicate the error. Cancel the
1160             // source so that we don't get any more events
1161             if (sdRef->disp_source)
1162             {
1163                 dispatch_source_cancel(sdRef->disp_source);
1164                 dispatch_release(sdRef->disp_source);
1165                 sdRef->disp_source = NULL;
1166                 CallbackWithError(sdRef, error);
1167             }
1168 #endif
1169             // Don't touch sdRef anymore as it might have been deallocated
1170             free(data);
1171             return error;
1172         }
1173         else
1174         {
1175             const char *ptr = data;
1176             cbh.cb_flags     = get_flags     (&ptr, data + cbh.ipc_hdr.datalen);
1177             cbh.cb_interface = get_uint32    (&ptr, data + cbh.ipc_hdr.datalen);
1178             cbh.cb_err       = get_error_code(&ptr, data + cbh.ipc_hdr.datalen);
1179 
1180             // CAUTION: We have to handle the case where the client calls DNSServiceRefDeallocate from within the callback function.
1181             // To do this we set moreptr to point to morebytes. If the client does call DNSServiceRefDeallocate(),
1182             // then that routine will clear morebytes for us, and cause us to exit our loop.
1183             morebytes = more_bytes(sdRef->sockfd);
1184             if (morebytes)
1185             {
1186                 cbh.cb_flags |= kDNSServiceFlagsMoreComing;
1187                 sdRef->moreptr = &morebytes;
1188             }
1189             if (ptr) sdRef->ProcessReply(sdRef, &cbh, ptr, data + cbh.ipc_hdr.datalen);
1190             // Careful code here:
1191             // If morebytes is non-zero, that means we set sdRef->moreptr above, and the operation was not
1192             // cancelled out from under us, so now we need to clear sdRef->moreptr so we don't leave a stray
1193             // dangling pointer pointing to a long-gone stack variable.
1194             // If morebytes is zero, then one of two thing happened:
1195             // (a) morebytes was 0 above, so we didn't set sdRef->moreptr, so we don't need to clear it
1196             // (b) morebytes was 1 above, and we set sdRef->moreptr, but the operation was cancelled (with DNSServiceRefDeallocate()),
1197             //     so we MUST NOT try to dereference our stale sdRef pointer.
1198             if (morebytes) sdRef->moreptr = NULL;
1199         }
1200         free(data);
1201     } while (morebytes);
1202 
1203     return kDNSServiceErr_NoError;
1204 }
1205 
1206 void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef)
1207 {
1208     if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with NULL DNSServiceRef"); return; }
1209 
1210     if (!DNSServiceRefValid(sdRef))     // Also verifies dnssd_SocketValid(sdRef->sockfd) for us too
1211     {
1212         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
1213         return;
1214     }
1215 
1216     // If we're in the middle of a DNSServiceProcessResult() invocation for this DNSServiceRef, clear its morebytes flag to break it out of its while loop
1217     if (sdRef->moreptr) *(sdRef->moreptr) = 0;
1218 
1219     if (sdRef->primary)     // If this is a subordinate DNSServiceOp, just send a 'stop' command
1220     {
1221         DNSServiceOp **p = &sdRef->primary->next;
1222         while (*p && *p != sdRef) p = &(*p)->next;
1223         if (*p)
1224         {
1225             char *ptr;
1226             size_t len = 0;
1227             ipc_msg_hdr *hdr = create_hdr(cancel_request, &len, &ptr, 0, sdRef);
1228             if (hdr)
1229             {
1230                 ConvertHeaderBytes(hdr);
1231                 write_all(sdRef->sockfd, (char *)hdr, len);
1232                 free(hdr);
1233             }
1234             *p = sdRef->next;
1235             FreeDNSServiceOp(sdRef);
1236         }
1237     }
1238     else                    // else, make sure to terminate all subordinates as well
1239     {
1240 #if _DNS_SD_LIBDISPATCH
1241         // The cancel handler will close the fd if a dispatch source has been set
1242         if (sdRef->disp_source)
1243         {
1244             // By setting the ProcessReply to NULL, we make sure that we never call
1245             // the application callbacks ever, after returning from this function. We
1246             // assume that DNSServiceRefDeallocate is called from the serial queue
1247             // that was passed to DNSServiceSetDispatchQueue. Hence, dispatch_source_cancel
1248             // should cancel all the blocks on the queue and hence there should be no more
1249             // callbacks when we return from this function. Setting ProcessReply to NULL
1250             // provides extra protection.
1251             sdRef->ProcessReply = NULL;
1252             shutdown(sdRef->sockfd, SHUT_WR);
1253             dispatch_source_cancel(sdRef->disp_source);
1254             dispatch_release(sdRef->disp_source);
1255             sdRef->disp_source = NULL;
1256         }
1257         // if disp_queue is set, it means it used the DNSServiceSetDispatchQueue API. In that case,
1258         // when the source was cancelled, the fd was closed in the handler. Currently the source
1259         // is cancelled only when the mDNSResponder daemon dies
1260         else if (!sdRef->disp_queue) dnssd_close(sdRef->sockfd);
1261 #else
1262         dnssd_close(sdRef->sockfd);
1263 #endif
1264         // Free DNSRecords added in DNSRegisterRecord if they have not
1265         // been freed in DNSRemoveRecord
1266         while (sdRef)
1267         {
1268             DNSServiceOp *p = sdRef;
1269             sdRef = sdRef->next;
1270             // When there is an error reading from the daemon e.g., bad fd, CallbackWithError
1271             // is called which sets moreptr. It might set the moreptr on a subordinate sdRef
1272             // but the application might call DNSServiceRefDeallocate with the main sdRef from
1273             // the callback. Hence, when we loop through the subordinate sdRefs, we need
1274             // to clear the moreptr so that CallbackWithError can terminate itself instead of
1275             // walking through the freed sdRefs.
1276             if (p->moreptr) *(p->moreptr) = 0;
1277             FreeDNSServiceOp(p);
1278         }
1279     }
1280 }
1281 
1282 DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void *result, uint32_t *size)
1283 {
1284     DNSServiceErrorType err;
1285     char *ptr;
1286     size_t len;
1287     ipc_msg_hdr *hdr;
1288     DNSServiceOp *tmp;
1289     uint32_t actualsize;
1290     int ioresult;
1291 
1292     if (!property || !result || !size)
1293         return kDNSServiceErr_BadParam;
1294 
1295     len = strlen(property) + 1;
1296     err = ConnectToServer(&tmp, 0, getproperty_request, NULL, NULL, NULL);
1297     if (err) return err;
1298 
1299     hdr = create_hdr(getproperty_request, &len, &ptr, 0, tmp);
1300     if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
1301 
1302     put_string(property, &ptr);
1303     err = deliver_request(hdr, tmp);        // Will free hdr for us
1304     if (err) { DNSServiceRefDeallocate(tmp); return err; }
1305 
1306     ioresult = read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize));
1307     if (ioresult < read_all_success)
1308     { DNSServiceRefDeallocate(tmp); return (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; }
1309 
1310     actualsize = ntohl(actualsize);
1311     ioresult = read_all(tmp->sockfd, (char*)result, actualsize < *size ? actualsize : *size);
1312     if (ioresult < read_all_success)
1313     { DNSServiceRefDeallocate(tmp); return (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; }
1314     DNSServiceRefDeallocate(tmp);
1315 
1316     // Swap version result back to local process byte order
1317     if (!strcmp(property, kDNSServiceProperty_DaemonVersion) && *size >= 4)
1318         *(uint32_t*)result = ntohl(*(uint32_t*)result);
1319 
1320     *size = actualsize;
1321     return kDNSServiceErr_NoError;
1322 }
1323 
1324 DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t *pid)
1325 {
1326     char *ptr;
1327     ipc_msg_hdr *hdr;
1328     DNSServiceOp *tmp = NULL;
1329     size_t len = sizeof(int32_t);
1330     int ioresult;
1331 
1332     DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL);
1333     if (err) return err;
1334 
1335     hdr = create_hdr(getpid_request, &len, &ptr, 0, tmp);
1336     if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
1337 
1338     put_uint16(srcport, &ptr);
1339     err = deliver_request(hdr, tmp);        // Will free hdr for us
1340     if (err) { DNSServiceRefDeallocate(tmp); return err; }
1341 
1342     ioresult = read_all(tmp->sockfd, (char*)pid, sizeof(int32_t));
1343     if (ioresult < read_all_success)
1344     { DNSServiceRefDeallocate(tmp); return (ioresult == read_all_defunct) ? kDNSServiceErr_DefunctConnection : kDNSServiceErr_ServiceNotRunning; }
1345 
1346     DNSServiceRefDeallocate(tmp);
1347     return kDNSServiceErr_NoError;
1348 }
1349 
1350 static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end)
1351 {
1352     char fullname[kDNSServiceMaxDomainName];
1353     char target[kDNSServiceMaxDomainName];
1354     uint16_t txtlen;
1355     union { uint16_t s; u_char b[2]; } port;
1356     unsigned char *txtrecord;
1357 
1358     get_string(&data, end, fullname, kDNSServiceMaxDomainName);
1359     get_string(&data, end, target,   kDNSServiceMaxDomainName);
1360     if (!data || data + 2 > end) goto fail;
1361 
1362     port.b[0] = *data++;
1363     port.b[1] = *data++;
1364     txtlen = get_uint16(&data, end);
1365     txtrecord = (unsigned char *)get_rdata(&data, end, txtlen);
1366 
1367     if (!data) goto fail;
1368     ((DNSServiceResolveReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, fullname, target, port.s, txtlen, txtrecord, sdr->AppContext);
1369     return;
1370     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
1371 fail:
1372     syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon");
1373 }
1374 
1375 #if TARGET_OS_IPHONE
1376 
1377 static int32_t libSystemVersion = 0;
1378 
1379 // Return true if the iOS application linked against a version of libsystem where P2P
1380 // interfaces were included by default when using kDNSServiceInterfaceIndexAny.
1381 // Using 160.0.0 == 0xa00000 as the version threshold.
1382 static int includeP2PWithIndexAny()
1383 {
1384     if (libSystemVersion == 0)
1385         libSystemVersion = NSVersionOfLinkTimeLibrary("System");
1386 
1387     if (libSystemVersion < 0xa00000)
1388         return 1;
1389     else
1390         return 0;
1391 }
1392 
1393 #else   // TARGET_OS_IPHONE
1394 
1395 // always return false for non iOS platforms
1396 static int includeP2PWithIndexAny()
1397 {
1398     return 0;
1399 }
1400 
1401 #endif  // TARGET_OS_IPHONE
1402 
1403 DNSServiceErrorType DNSSD_API DNSServiceResolve
1404 (
1405     DNSServiceRef                 *sdRef,
1406     DNSServiceFlags flags,
1407     uint32_t interfaceIndex,
1408     const char                    *name,
1409     const char                    *regtype,
1410     const char                    *domain,
1411     DNSServiceResolveReply callBack,
1412     void                          *context
1413 )
1414 {
1415     char *ptr;
1416     size_t len;
1417     ipc_msg_hdr *hdr;
1418     DNSServiceErrorType err;
1419 
1420     if (!sdRef || !name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam;
1421 
1422     // Need a real InterfaceID for WakeOnResolve
1423     if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 &&
1424         ((interfaceIndex == kDNSServiceInterfaceIndexAny) ||
1425          (interfaceIndex == kDNSServiceInterfaceIndexLocalOnly) ||
1426          (interfaceIndex == kDNSServiceInterfaceIndexUnicast) ||
1427          (interfaceIndex == kDNSServiceInterfaceIndexP2P) ||
1428          (interfaceIndex == kDNSServiceInterfaceIndexBLE)))
1429     {
1430         return kDNSServiceErr_BadParam;
1431     }
1432 
1433     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
1434         flags |= kDNSServiceFlagsIncludeP2P;
1435 
1436     err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, (void *)callBack, context);
1437     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
1438 
1439     // Calculate total message length
1440     len = sizeof(flags);
1441     len += sizeof(interfaceIndex);
1442     len += strlen(name) + 1;
1443     len += strlen(regtype) + 1;
1444     len += strlen(domain) + 1;
1445 
1446     hdr = create_hdr(resolve_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
1447     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
1448 
1449     put_flags(flags, &ptr);
1450     put_uint32(interfaceIndex, &ptr);
1451     put_string(name, &ptr);
1452     put_string(regtype, &ptr);
1453     put_string(domain, &ptr);
1454 
1455     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
1456 #if CHECK_BUNDLE_VERSION
1457     if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error())
1458     {
1459         err = kDNSServiceErr_NoError;
1460     }
1461 #endif
1462     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
1463     return err;
1464 }
1465 
1466 static void handle_query_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
1467 {
1468 #if CHECK_BUNDLE_VERSION
1469     if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error())
1470     {
1471         return;
1472     }
1473 #endif
1474     uint32_t ttl;
1475     char name[kDNSServiceMaxDomainName];
1476     uint16_t rrtype, rrclass, rdlen;
1477     const char *rdata;
1478 
1479     get_string(&data, end, name, kDNSServiceMaxDomainName);
1480     rrtype  = get_uint16(&data, end);
1481     rrclass = get_uint16(&data, end);
1482     rdlen   = get_uint16(&data, end);
1483     rdata   = get_rdata(&data, end, rdlen);
1484     ttl     = get_uint32(&data, end);
1485 
1486     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_query_response: error reading result from daemon");
1487     else ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, name, rrtype, rrclass, rdlen, rdata, ttl, sdr->AppContext);
1488     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
1489 }
1490 
1491 #if APPLE_OSX_mDNSResponder
1492 static size_t get_required_length_for_defaults(const xpc_object_t defaults)
1493 {
1494     size_t required_len = 0;
1495     size_t plist_data_len = 0;
1496     // Add length for IPC_TLV_TYPE_RESOLVER_CONFIG_PLIST_DATA.
1497     if (xpc_dictionary_get_data(defaults, kDNSServiceDefaultsKey_ResolverConfigPListData, &plist_data_len))
1498     {
1499         required_len += get_required_tlv16_length(plist_data_len);
1500     }
1501     // Add length for IPC_TLV_TYPE_REQUIRE_PRIVACY.
1502     required_len += get_required_tlv16_length(sizeof(uint8_t));
1503     return required_len;
1504 }
1505 
1506 static void put_tlvs_for_defaults(const xpc_object_t defaults, ipc_msg_hdr *const hdr, char **ptr)
1507 {
1508     uint8_t require_privacy;
1509     size_t plist_data_len = 0;
1510     const uint8_t *const plist_data_ptr = xpc_dictionary_get_data(defaults,
1511         kDNSServiceDefaultsKey_ResolverConfigPListData, &plist_data_len);
1512     if (plist_data_ptr)
1513     {
1514         put_tlv16(IPC_TLV_TYPE_RESOLVER_CONFIG_PLIST_DATA, (uint16_t)plist_data_len, plist_data_ptr, ptr);
1515     }
1516     require_privacy = xpc_dictionary_get_bool(defaults, kDNSServiceDefaultsKey_RequirePrivacy) ? 1 : 0;
1517     put_tlv16(IPC_TLV_TYPE_REQUIRE_PRIVACY, sizeof(require_privacy), &require_privacy, ptr);
1518     hdr->ipc_flags |= IPC_FLAGS_TRAILING_TLVS;
1519 }
1520 #endif
1521 
1522 DNSServiceErrorType DNSSD_API DNSServiceQueryRecord
1523 (
1524     DNSServiceRef              *sdRef,
1525     DNSServiceFlags flags,
1526     uint32_t interfaceIndex,
1527     const char                 *name,
1528     uint16_t rrtype,
1529     uint16_t rrclass,
1530     DNSServiceQueryRecordReply callBack,
1531     void                       *context
1532 )
1533 {
1534     char *ptr;
1535     size_t len;
1536     ipc_msg_hdr *hdr;
1537     DNSServiceErrorType err;
1538 #if APPLE_OSX_mDNSResponder
1539     xpc_object_t defaults;
1540 #endif
1541     // NULL name handled below.
1542     if (!sdRef || !callBack) return kDNSServiceErr_BadParam;
1543 
1544     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
1545         flags |= kDNSServiceFlagsIncludeP2P;
1546 
1547     err = ConnectToServer(sdRef, flags, query_request, handle_query_response, (void *)callBack, context);
1548     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
1549 
1550     if (!name) name = "\0";
1551 
1552     // Calculate total message length
1553     len = sizeof(flags);
1554     len += sizeof(uint32_t);  // interfaceIndex
1555     len += strlen(name) + 1;
1556     len += 2 * sizeof(uint16_t);  // rrtype, rrclass
1557 #if APPLE_OSX_mDNSResponder
1558     defaults = DNSServiceGetRetainedResolverDefaults();
1559     if (defaults)
1560     {
1561         len += get_required_length_for_defaults(defaults);
1562     }
1563 #endif
1564     hdr = create_hdr(query_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
1565     if (!hdr)
1566     {
1567         DNSServiceRefDeallocate(*sdRef);
1568         *sdRef = NULL;
1569 #if APPLE_OSX_mDNSResponder
1570         xpc_forget(&defaults);
1571 #endif
1572         return kDNSServiceErr_NoMemory;
1573     }
1574     put_flags(flags, &ptr);
1575     put_uint32(interfaceIndex, &ptr);
1576     put_string(name, &ptr);
1577     put_uint16(rrtype, &ptr);
1578     put_uint16(rrclass, &ptr);
1579 #if APPLE_OSX_mDNSResponder
1580     if (defaults)
1581     {
1582         put_tlvs_for_defaults(defaults, hdr, &ptr);
1583         xpc_forget(&defaults);
1584     }
1585 #endif
1586     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
1587 #if CHECK_BUNDLE_VERSION
1588     if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error())
1589     {
1590         err = kDNSServiceErr_NoError;
1591     }
1592 #endif
1593     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
1594     return err;
1595 }
1596 
1597 static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
1598 {
1599 #if CHECK_BUNDLE_VERSION
1600     if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error())
1601     {
1602         return;
1603     }
1604 #endif
1605     char hostname[kDNSServiceMaxDomainName];
1606     uint16_t rrtype, rrclass, rdlen;
1607     const char *rdata;
1608     uint32_t ttl;
1609 
1610     get_string(&data, end, hostname, kDNSServiceMaxDomainName);
1611     rrtype  = get_uint16(&data, end);
1612     rrclass = get_uint16(&data, end);
1613     rdlen   = get_uint16(&data, end);
1614     rdata   = get_rdata (&data, end, rdlen);
1615     ttl     = get_uint32(&data, end);
1616     (void)rrclass; // Unused
1617 
1618     // We only generate client callbacks for A and AAAA results (including NXDOMAIN results for
1619     // those types, if the client has requested those with the kDNSServiceFlagsReturnIntermediates).
1620     // Other result types, specifically CNAME referrals, are not communicated to the client, because
1621     // the DNSServiceGetAddrInfoReply interface doesn't have any meaningful way to communiate CNAME referrals.
1622     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_addrinfo_response: error reading result from daemon");
1623     else if (rrtype == kDNSServiceType_A || rrtype == kDNSServiceType_AAAA)
1624     {
1625         struct sockaddr_in sa4;
1626         struct sockaddr_in6 sa6;
1627         const struct sockaddr *const sa = (rrtype == kDNSServiceType_A) ? (struct sockaddr*)&sa4 : (struct sockaddr*)&sa6;
1628         if (rrtype == kDNSServiceType_A)
1629         {
1630             memset(&sa4, 0, sizeof(sa4));
1631             #ifndef NOT_HAVE_SA_LEN
1632             sa4.sin_len = sizeof(struct sockaddr_in);
1633             #endif
1634             sa4.sin_family = AF_INET;
1635             //  sin_port   = 0;
1636             if (!cbh->cb_err) memcpy(&sa4.sin_addr, rdata, rdlen);
1637         }
1638         else
1639         {
1640             memset(&sa6, 0, sizeof(sa6));
1641             #ifndef NOT_HAVE_SA_LEN
1642             sa6.sin6_len = sizeof(struct sockaddr_in6);
1643             #endif
1644             sa6.sin6_family     = AF_INET6;
1645             //  sin6_port     = 0;
1646             //  sin6_flowinfo = 0;
1647             //  sin6_scope_id = 0;
1648             if (!cbh->cb_err)
1649             {
1650                 memcpy(&sa6.sin6_addr, rdata, rdlen);
1651                 if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface;
1652             }
1653         }
1654 
1655 		((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext);
1656     }
1657     else if (cbh->cb_err == kDNSServiceErr_PolicyDenied)
1658     {
1659         ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, ttl, sdr->AppContext);
1660     }
1661 }
1662 
1663 DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
1664 (
1665     DNSServiceRef                    *sdRef,
1666     DNSServiceFlags flags,
1667     uint32_t interfaceIndex,
1668     uint32_t protocol,
1669     const char                       *hostname,
1670     DNSServiceGetAddrInfoReply callBack,
1671     void                             *context          /* may be NULL */
1672 )
1673 {
1674     char *ptr;
1675     size_t len;
1676     ipc_msg_hdr *hdr;
1677     DNSServiceErrorType err;
1678 #if APPLE_OSX_mDNSResponder
1679     xpc_object_t defaults;
1680 #endif
1681 
1682     if (!sdRef || !hostname || !callBack) return kDNSServiceErr_BadParam;
1683 
1684     err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, (void *)callBack, context);
1685     if (err)
1686     {
1687          return err;    // On error ConnectToServer leaves *sdRef set to NULL
1688     }
1689 
1690     // Calculate total message length
1691     len = sizeof(flags);
1692     len += sizeof(uint32_t);      // interfaceIndex
1693     len += sizeof(uint32_t);      // protocol
1694     len += strlen(hostname) + 1;
1695 #if APPLE_OSX_mDNSResponder
1696     defaults = DNSServiceGetRetainedResolverDefaults();
1697     if (defaults)
1698     {
1699         len += get_required_length_for_defaults(defaults);
1700     }
1701 #endif
1702     hdr = create_hdr(addrinfo_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
1703     if (!hdr)
1704     {
1705         DNSServiceRefDeallocate(*sdRef);
1706         *sdRef = NULL;
1707 #if APPLE_OSX_mDNSResponder
1708         xpc_forget(&defaults);
1709 #endif
1710         return kDNSServiceErr_NoMemory;
1711     }
1712     put_flags(flags, &ptr);
1713     put_uint32(interfaceIndex, &ptr);
1714     put_uint32(protocol, &ptr);
1715     put_string(hostname, &ptr);
1716 #if APPLE_OSX_mDNSResponder
1717     if (defaults)
1718     {
1719         put_tlvs_for_defaults(defaults, hdr, &ptr);
1720         xpc_forget(&defaults);
1721     }
1722 #endif
1723     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
1724 #if CHECK_BUNDLE_VERSION
1725     if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error())
1726     {
1727         err = kDNSServiceErr_NoError;
1728     }
1729 #endif
1730     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
1731     return err;
1732 }
1733 
1734 static void handle_browse_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
1735 {
1736 #if CHECK_BUNDLE_VERSION
1737     if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error())
1738     {
1739         return;
1740     }
1741 #endif
1742     char replyName[256], replyType[kDNSServiceMaxDomainName], replyDomain[kDNSServiceMaxDomainName];
1743     get_string(&data, end, replyName, 256);
1744     get_string(&data, end, replyType, kDNSServiceMaxDomainName);
1745     get_string(&data, end, replyDomain, kDNSServiceMaxDomainName);
1746     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_browse_response: error reading result from daemon");
1747     else ((DNSServiceBrowseReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, replyName, replyType, replyDomain, sdr->AppContext);
1748     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
1749 }
1750 
1751 DNSServiceErrorType DNSSD_API DNSServiceBrowse
1752 (
1753     DNSServiceRef         *sdRef,
1754     DNSServiceFlags flags,
1755     uint32_t interfaceIndex,
1756     const char            *regtype,
1757     const char            *domain,
1758     DNSServiceBrowseReply callBack,
1759     void                  *context
1760 )
1761 {
1762     char *ptr;
1763     size_t len;
1764     ipc_msg_hdr *hdr;
1765     DNSServiceErrorType err;
1766 
1767     // NULL domain handled below
1768     if (!sdRef || !regtype || !callBack) return kDNSServiceErr_BadParam;
1769 
1770     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
1771         flags |= kDNSServiceFlagsIncludeP2P;
1772 
1773     err = ConnectToServer(sdRef, flags, browse_request, handle_browse_response, (void *)callBack, context);
1774     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
1775 
1776     if (!domain) domain = "";
1777     len = sizeof(flags);
1778     len += sizeof(interfaceIndex);
1779     len += strlen(regtype) + 1;
1780     len += strlen(domain) + 1;
1781 
1782     hdr = create_hdr(browse_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
1783     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
1784 
1785     put_flags(flags, &ptr);
1786     put_uint32(interfaceIndex, &ptr);
1787     put_string(regtype, &ptr);
1788     put_string(domain, &ptr);
1789 
1790     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
1791 #if CHECK_BUNDLE_VERSION
1792     if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error())
1793     {
1794         err = kDNSServiceErr_NoError;
1795     }
1796 #endif
1797     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
1798     return err;
1799 }
1800 
1801 DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain)
1802 {
1803     DNSServiceErrorType err;
1804     DNSServiceOp *tmp;
1805     char *ptr;
1806     size_t len;
1807     ipc_msg_hdr *hdr;
1808 
1809     if (!domain) return kDNSServiceErr_BadParam;
1810     len = sizeof(flags) + strlen(domain) + 1;
1811 
1812     err = ConnectToServer(&tmp, 0, setdomain_request, NULL, NULL, NULL);
1813     if (err) return err;
1814 
1815     hdr = create_hdr(setdomain_request, &len, &ptr, 0, tmp);
1816     if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
1817 
1818     put_flags(flags, &ptr);
1819     put_string(domain, &ptr);
1820     err = deliver_request(hdr, tmp);        // Will free hdr for us
1821     DNSServiceRefDeallocate(tmp);
1822     return err;
1823 }
1824 
1825 static void handle_regservice_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
1826 {
1827 #if CHECK_BUNDLE_VERSION
1828     if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error())
1829     {
1830         return;
1831     }
1832 #endif
1833     char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName];
1834     get_string(&data, end, name, 256);
1835     get_string(&data, end, regtype, kDNSServiceMaxDomainName);
1836     get_string(&data, end, domain,  kDNSServiceMaxDomainName);
1837     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_regservice_response: error reading result from daemon");
1838     else ((DNSServiceRegisterReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_err, name, regtype, domain, sdr->AppContext);
1839     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
1840 }
1841 
1842 DNSServiceErrorType DNSSD_API DNSServiceRegister
1843 (
1844     DNSServiceRef                       *sdRef,
1845     DNSServiceFlags flags,
1846     uint32_t interfaceIndex,
1847     const char                          *name,
1848     const char                          *regtype,
1849     const char                          *domain,
1850     const char                          *host,
1851     uint16_t PortInNetworkByteOrder,
1852     uint16_t txtLen,
1853     const void                          *txtRecord,
1854     DNSServiceRegisterReply callBack,
1855     void                                *context
1856 )
1857 {
1858     char *ptr;
1859     size_t len;
1860     ipc_msg_hdr *hdr;
1861     DNSServiceErrorType err;
1862     union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder };
1863 
1864     if (!sdRef || !regtype) return kDNSServiceErr_BadParam;
1865     if (!name) name = "";
1866     if (!domain) domain = "";
1867     if (!host) host = "";
1868     if (!txtRecord) txtRecord = (void*)"";
1869 
1870     // No callback must have auto-rename
1871     if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam;
1872 
1873     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
1874         flags |= kDNSServiceFlagsIncludeP2P;
1875 
1876     err = ConnectToServer(sdRef, flags, reg_service_request, callBack ? handle_regservice_response : NULL, (void *)callBack, context);
1877     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
1878 
1879     len = sizeof(DNSServiceFlags);
1880     len += sizeof(uint32_t);  // interfaceIndex
1881     len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4;
1882     len += 2 * sizeof(uint16_t);  // port, txtLen
1883     len += txtLen;
1884 
1885     hdr = create_hdr(reg_service_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
1886     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
1887     if (!callBack) hdr->ipc_flags |= IPC_FLAGS_NOREPLY;
1888 
1889     put_flags(flags, &ptr);
1890     put_uint32(interfaceIndex, &ptr);
1891     put_string(name, &ptr);
1892     put_string(regtype, &ptr);
1893     put_string(domain, &ptr);
1894     put_string(host, &ptr);
1895     *ptr++ = port.b[0];
1896     *ptr++ = port.b[1];
1897     put_uint16(txtLen, &ptr);
1898     put_rdata(txtLen, txtRecord, &ptr);
1899 
1900     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
1901 #if CHECK_BUNDLE_VERSION
1902     if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error())
1903     {
1904         err = kDNSServiceErr_NoError;
1905     }
1906 #endif
1907     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
1908     return err;
1909 }
1910 
1911 static void handle_enumeration_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
1912 {
1913     char domain[kDNSServiceMaxDomainName];
1914     get_string(&data, end, domain, kDNSServiceMaxDomainName);
1915     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_enumeration_response: error reading result from daemon");
1916     else ((DNSServiceDomainEnumReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, domain, sdr->AppContext);
1917     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
1918 }
1919 
1920 DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains
1921 (
1922     DNSServiceRef             *sdRef,
1923     DNSServiceFlags flags,
1924     uint32_t interfaceIndex,
1925     DNSServiceDomainEnumReply callBack,
1926     void                      *context
1927 )
1928 {
1929     char *ptr;
1930     size_t len;
1931     ipc_msg_hdr *hdr;
1932     DNSServiceErrorType err;
1933     int f1;
1934     int f2;
1935 
1936     if (!sdRef || !callBack) return kDNSServiceErr_BadParam;
1937 
1938     f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0;
1939     f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0;
1940     if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
1941 
1942     err = ConnectToServer(sdRef, flags, enumeration_request, handle_enumeration_response, (void *)callBack, context);
1943     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
1944 
1945     len = sizeof(DNSServiceFlags);
1946     len += sizeof(uint32_t);
1947 
1948     hdr = create_hdr(enumeration_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
1949     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
1950 
1951     put_flags(flags, &ptr);
1952     put_uint32(interfaceIndex, &ptr);
1953 
1954     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
1955     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
1956     return err;
1957 }
1958 
1959 static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *const data, const char *const end)
1960 {
1961     (void)data; // Unused
1962 
1963     //printf("ConnectionResponse got %d\n", cbh->ipc_hdr.op);
1964     if (cbh->ipc_hdr.op != reg_record_reply_op)
1965     {
1966         // When using kDNSServiceFlagsShareConnection, need to search the list of associated DNSServiceOps
1967         // to find the one this response is intended for, and then call through to its ProcessReply handler.
1968         // We start with our first subordinate DNSServiceRef -- don't want to accidentally match the parent DNSServiceRef.
1969         DNSServiceOp *op = sdr->next;
1970         while (op && (op->uid.u32[0] != cbh->ipc_hdr.client_context.u32[0] || op->uid.u32[1] != cbh->ipc_hdr.client_context.u32[1]))
1971             op = op->next;
1972         // Note: We may sometimes not find a matching DNSServiceOp, in the case where the client has
1973         // cancelled the subordinate DNSServiceOp, but there are still messages in the pipeline from the daemon
1974         if (op && op->ProcessReply) op->ProcessReply(op, cbh, data, end);
1975         // WARNING: Don't touch op or sdr after this -- client may have called DNSServiceRefDeallocate
1976         return;
1977     }
1978     else
1979     {
1980 #if CHECK_BUNDLE_VERSION
1981         if (cbh->cb_err == kDNSServiceErr_PolicyDenied && !_should_return_noauth_error())
1982         {
1983             return;
1984         }
1985 #endif
1986         DNSRecordRef rec;
1987         for (rec = sdr->rec; rec; rec = rec->recnext)
1988         {
1989             if (rec->uid.u32[0] == cbh->ipc_hdr.client_context.u32[0] && rec->uid.u32[1] == cbh->ipc_hdr.client_context.u32[1])
1990                 break;
1991         }
1992         // The record might have been freed already and hence not an
1993         // error if the record is not found.
1994         if (!rec)
1995         {
1996             syslog(LOG_INFO, "dnssd_clientstub ConnectionResponse: Record not found");
1997             return;
1998         }
1999         if (rec->sdr != sdr)
2000         {
2001             syslog(LOG_WARNING, "dnssd_clientstub ConnectionResponse: Record sdr mismatch: rec %p sdr %p", rec->sdr, sdr);
2002             return;
2003         }
2004 
2005         if (sdr->op == connection_request || sdr->op == connection_delegate_request)
2006         {
2007             rec->AppCallback(rec->sdr, rec, cbh->cb_flags, cbh->cb_err, rec->AppContext);
2008         }
2009         else
2010         {
2011             syslog(LOG_WARNING, "dnssd_clientstub ConnectionResponse: sdr->op != connection_request");
2012             rec->AppCallback(rec->sdr, rec, 0, kDNSServiceErr_Unknown, rec->AppContext);
2013         }
2014         // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
2015     }
2016 }
2017 
2018 DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef)
2019 {
2020     DNSServiceErrorType err;
2021     char *ptr;
2022     size_t len = 0;
2023     ipc_msg_hdr *hdr;
2024 
2025     if (!sdRef) return kDNSServiceErr_BadParam;
2026     err = ConnectToServer(sdRef, 0, connection_request, ConnectionResponse, NULL, NULL);
2027     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
2028 
2029     hdr = create_hdr(connection_request, &len, &ptr, 0, *sdRef);
2030     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
2031 
2032     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
2033     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
2034     return err;
2035 }
2036 
2037 #if APPLE_OSX_mDNSResponder && !TARGET_OS_SIMULATOR
2038 DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid)
2039 {
2040     char *ptr;
2041     size_t len = 0;
2042     ipc_msg_hdr *hdr;
2043 
2044     if (!sdRef) return kDNSServiceErr_BadParam;
2045     DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_delegate_request, ConnectionResponse, NULL, NULL);
2046     if (err)
2047     {
2048          return err;    // On error ConnectToServer leaves *sdRef set to NULL
2049     }
2050 
2051     // Only one of the two options can be set. If pid is zero, uuid is used.
2052     // If both are specified only pid will be used. We send across the pid
2053     // so that the daemon knows what to read from the socket.
2054 
2055     len += sizeof(int32_t);
2056 
2057     hdr = create_hdr(connection_delegate_request, &len, &ptr, 0, *sdRef);
2058     if (!hdr)
2059     {
2060         DNSServiceRefDeallocate(*sdRef);
2061         *sdRef = NULL;
2062         return kDNSServiceErr_NoMemory;
2063     }
2064 
2065     if (pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED, &pid, sizeof(pid)) == -1)
2066     {
2067         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceCreateDelegateConnection: Could not setsockopt() for PID[%d], no entitlements or process(pid) invalid errno:%d (%s)", pid, errno, strerror(errno));
2068         // Free the hdr in case we return before calling deliver_request()
2069         if (hdr)
2070             free(hdr);
2071         DNSServiceRefDeallocate(*sdRef);
2072         *sdRef = NULL;
2073         return kDNSServiceErr_NoAuth;
2074     }
2075 
2076     if (!pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED_UUID, uuid, sizeof(uuid_t)) == -1)
2077     {
2078         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceCreateDelegateConnection: Could not setsockopt() for UUID, no entitlements or process(uuid) invalid errno:%d (%s) ", errno, strerror(errno));
2079         // Free the hdr in case we return before calling deliver_request()
2080         if (hdr)
2081             free(hdr);
2082         DNSServiceRefDeallocate(*sdRef);
2083         *sdRef = NULL;
2084         return kDNSServiceErr_NoAuth;
2085     }
2086 
2087     put_uint32(pid, &ptr);
2088 
2089     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
2090     if (err)
2091     {
2092         DNSServiceRefDeallocate(*sdRef);
2093         *sdRef = NULL;
2094     }
2095     return err;
2096 }
2097 #elif TARGET_OS_SIMULATOR // This hack is for Simulator platform only
2098 DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid)
2099 {
2100     (void) pid;
2101     (void) uuid;
2102     return DNSServiceCreateConnection(sdRef);
2103 }
2104 #endif
2105 
2106 DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
2107 (
2108     DNSServiceRef sdRef,
2109     DNSRecordRef                  *RecordRef,
2110     DNSServiceFlags flags,
2111     uint32_t interfaceIndex,
2112     const char                    *fullname,
2113     uint16_t rrtype,
2114     uint16_t rrclass,
2115     uint16_t rdlen,
2116     const void                    *rdata,
2117     uint32_t ttl,
2118     DNSServiceRegisterRecordReply callBack,
2119     void                          *context
2120 )
2121 {
2122     DNSServiceErrorType err;
2123     char *ptr;
2124     size_t len;
2125     ipc_msg_hdr *hdr = NULL;
2126     DNSRecordRef rref = NULL;
2127     DNSRecord **p;
2128     // Verify that only one of the following flags is set.
2129     int f1 = (flags & kDNSServiceFlagsShared) != 0;
2130     int f2 = (flags & kDNSServiceFlagsUnique) != 0;
2131     int f3 = (flags & kDNSServiceFlagsKnownUnique) != 0;
2132     if (f1 + f2 + f3 != 1) return kDNSServiceErr_BadParam;
2133 
2134     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
2135         flags |= kDNSServiceFlagsIncludeP2P;
2136 
2137     if (!sdRef || !RecordRef || !fullname || (!rdata && rdlen) || !callBack)
2138     {
2139         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with NULL parameter");
2140         return kDNSServiceErr_BadParam;
2141     }
2142 
2143     if (!DNSServiceRefValid(sdRef))
2144     {
2145         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
2146         return kDNSServiceErr_BadReference;
2147     }
2148 
2149     if (sdRef->op != connection_request && sdRef->op != connection_delegate_request)
2150     {
2151         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op);
2152         return kDNSServiceErr_BadReference;
2153     }
2154 
2155     *RecordRef = NULL;
2156 
2157     len = sizeof(DNSServiceFlags);
2158     len += 2 * sizeof(uint32_t);  // interfaceIndex, ttl
2159     len += 3 * sizeof(uint16_t);  // rrtype, rrclass, rdlen
2160     len += strlen(fullname) + 1;
2161     len += rdlen;
2162 
2163     // Bump up the uid. Normally for shared operations (kDNSServiceFlagsShareConnection), this
2164     // is done in ConnectToServer. For DNSServiceRegisterRecord, ConnectToServer has already
2165     // been called. As multiple DNSServiceRegisterRecords can be multiplexed over a single
2166     // connection, we need a way to demultiplex the response so that the callback corresponding
2167     // to the right DNSServiceRegisterRecord instance can be called. Use the same mechanism that
2168     // is used by kDNSServiceFlagsShareConnection. create_hdr copies the uid value to ipc
2169     // hdr->client_context which will be returned in the ipc response.
2170     if (++sdRef->uid.u32[0] == 0)
2171         ++sdRef->uid.u32[1];
2172     hdr = create_hdr(reg_record_request, &len, &ptr, 1, sdRef);
2173     if (!hdr) return kDNSServiceErr_NoMemory;
2174 
2175     put_flags(flags, &ptr);
2176     put_uint32(interfaceIndex, &ptr);
2177     put_string(fullname, &ptr);
2178     put_uint16(rrtype, &ptr);
2179     put_uint16(rrclass, &ptr);
2180     put_uint16(rdlen, &ptr);
2181     put_rdata(rdlen, rdata, &ptr);
2182     put_uint32(ttl, &ptr);
2183 
2184     rref = malloc(sizeof(DNSRecord));
2185     if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; }
2186     rref->AppContext = context;
2187     rref->AppCallback = callBack;
2188     rref->record_index = sdRef->max_index++;
2189     rref->sdr = sdRef;
2190     rref->recnext = NULL;
2191     *RecordRef = rref;
2192     // Remember the uid that we are sending across so that we can match
2193     // when the response comes back.
2194     rref->uid = sdRef->uid;
2195     hdr->reg_index = rref->record_index;
2196 
2197     p = &(sdRef)->rec;
2198     while (*p) p = &(*p)->recnext;
2199     *p = rref;
2200 
2201     err = deliver_request(hdr, sdRef);     // Will free hdr for us
2202 #if CHECK_BUNDLE_VERSION
2203     if (err == kDNSServiceErr_NoAuth && !_should_return_noauth_error())
2204     {
2205         err = kDNSServiceErr_NoError;
2206     }
2207 #endif
2208     return err;
2209 }
2210 
2211 // sdRef returned by DNSServiceRegister()
2212 DNSServiceErrorType DNSSD_API DNSServiceAddRecord
2213 (
2214     DNSServiceRef sdRef,
2215     DNSRecordRef    *RecordRef,
2216     DNSServiceFlags flags,
2217     uint16_t rrtype,
2218     uint16_t rdlen,
2219     const void      *rdata,
2220     uint32_t ttl
2221 )
2222 {
2223     ipc_msg_hdr *hdr;
2224     size_t len = 0;
2225     char *ptr;
2226     DNSRecordRef rref;
2227     DNSRecord **p;
2228 
2229     if (!sdRef || !RecordRef || (!rdata && rdlen))
2230     {
2231         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL parameter");
2232         return kDNSServiceErr_BadParam;
2233     }
2234     if (sdRef->op != reg_service_request)
2235     {
2236         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with non-DNSServiceRegister DNSServiceRef %p %d", sdRef, sdRef->op);
2237         return kDNSServiceErr_BadReference;
2238     }
2239 
2240     if (!DNSServiceRefValid(sdRef))
2241     {
2242         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
2243         return kDNSServiceErr_BadReference;
2244     }
2245 
2246     *RecordRef = NULL;
2247 
2248     len += 2 * sizeof(uint16_t);  // rrtype, rdlen
2249     len += rdlen;
2250     len += sizeof(uint32_t);
2251     len += sizeof(DNSServiceFlags);
2252 
2253     hdr = create_hdr(add_record_request, &len, &ptr, 1, sdRef);
2254     if (!hdr) return kDNSServiceErr_NoMemory;
2255     put_flags(flags, &ptr);
2256     put_uint16(rrtype, &ptr);
2257     put_uint16(rdlen, &ptr);
2258     put_rdata(rdlen, rdata, &ptr);
2259     put_uint32(ttl, &ptr);
2260 
2261     rref = malloc(sizeof(DNSRecord));
2262     if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; }
2263     rref->AppContext = NULL;
2264     rref->AppCallback = NULL;
2265     rref->record_index = sdRef->max_index++;
2266     rref->sdr = sdRef;
2267     rref->recnext = NULL;
2268     *RecordRef = rref;
2269     hdr->reg_index = rref->record_index;
2270 
2271     p = &(sdRef)->rec;
2272     while (*p) p = &(*p)->recnext;
2273     *p = rref;
2274 
2275     return deliver_request(hdr, sdRef);     // Will free hdr for us
2276 }
2277 
2278 // DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord
2279 DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord
2280 (
2281     DNSServiceRef sdRef,
2282     DNSRecordRef RecordRef,
2283     DNSServiceFlags flags,
2284     uint16_t rdlen,
2285     const void      *rdata,
2286     uint32_t ttl
2287 )
2288 {
2289     ipc_msg_hdr *hdr;
2290     size_t len = 0;
2291     char *ptr;
2292 
2293     if (!sdRef || (!rdata && rdlen))
2294     {
2295         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with NULL parameter");
2296         return kDNSServiceErr_BadParam;
2297     }
2298 
2299     if (!DNSServiceRefValid(sdRef))
2300     {
2301         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
2302         return kDNSServiceErr_BadReference;
2303     }
2304 
2305     // Note: RecordRef is allowed to be NULL
2306 
2307     len += sizeof(uint16_t);
2308     len += rdlen;
2309     len += sizeof(uint32_t);
2310     len += sizeof(DNSServiceFlags);
2311 
2312     hdr = create_hdr(update_record_request, &len, &ptr, 1, sdRef);
2313     if (!hdr) return kDNSServiceErr_NoMemory;
2314     hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX;
2315     put_flags(flags, &ptr);
2316     put_uint16(rdlen, &ptr);
2317     put_rdata(rdlen, rdata, &ptr);
2318     put_uint32(ttl, &ptr);
2319     return deliver_request(hdr, sdRef);     // Will free hdr for us
2320 }
2321 
2322 DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord
2323 (
2324     DNSServiceRef sdRef,
2325     DNSRecordRef RecordRef,
2326     DNSServiceFlags flags
2327 )
2328 {
2329     ipc_msg_hdr *hdr;
2330     size_t len = 0;
2331     char *ptr;
2332     DNSServiceErrorType err;
2333 
2334     if (!sdRef)            { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
2335     if (!RecordRef)        { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSRecordRef");  return kDNSServiceErr_BadParam; }
2336     if (!sdRef->max_index) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with bad DNSServiceRef");  return kDNSServiceErr_BadReference; }
2337 
2338     if (!DNSServiceRefValid(sdRef))
2339     {
2340         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
2341         return kDNSServiceErr_BadReference;
2342     }
2343 
2344     len += sizeof(flags);
2345     hdr = create_hdr(remove_record_request, &len, &ptr, 1, sdRef);
2346     if (!hdr) return kDNSServiceErr_NoMemory;
2347     hdr->reg_index = RecordRef->record_index;
2348     put_flags(flags, &ptr);
2349     err = deliver_request(hdr, sdRef);      // Will free hdr for us
2350     if (!err)
2351     {
2352         // This RecordRef could have been allocated in DNSServiceRegisterRecord or DNSServiceAddRecord.
2353         // If so, delink from the list before freeing
2354         DNSRecord **p = &sdRef->rec;
2355         while (*p && *p != RecordRef) p = &(*p)->recnext;
2356         if (*p) *p = RecordRef->recnext;
2357         free(RecordRef);
2358     }
2359     return err;
2360 }
2361 
2362 DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
2363 (
2364     DNSServiceFlags flags,
2365     uint32_t interfaceIndex,
2366     const char      *fullname,
2367     uint16_t rrtype,
2368     uint16_t rrclass,
2369     uint16_t rdlen,
2370     const void      *rdata
2371 )
2372 {
2373     DNSServiceErrorType err;
2374     char *ptr;
2375     size_t len;
2376     ipc_msg_hdr *hdr;
2377     DNSServiceOp *tmp = NULL;
2378 
2379     if (!fullname || (!rdata && rdlen)) return kDNSServiceErr_BadParam;
2380 
2381     err = ConnectToServer(&tmp, flags, reconfirm_record_request, NULL, NULL, NULL);
2382     if (err) return err;
2383 
2384     len = sizeof(DNSServiceFlags);
2385     len += sizeof(uint32_t);
2386     len += strlen(fullname) + 1;
2387     len += 3 * sizeof(uint16_t);
2388     len += rdlen;
2389     hdr = create_hdr(reconfirm_record_request, &len, &ptr, 0, tmp);
2390     if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
2391 
2392     put_flags(flags, &ptr);
2393     put_uint32(interfaceIndex, &ptr);
2394     put_string(fullname, &ptr);
2395     put_uint16(rrtype, &ptr);
2396     put_uint16(rrclass, &ptr);
2397     put_uint16(rdlen, &ptr);
2398     put_rdata(rdlen, rdata, &ptr);
2399 
2400     err = deliver_request(hdr, tmp);        // Will free hdr for us
2401     DNSServiceRefDeallocate(tmp);
2402     return err;
2403 }
2404 
2405 
2406 static void handle_port_mapping_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
2407 {
2408     union { uint32_t l; u_char b[4]; } addr;
2409     uint8_t protocol;
2410     union { uint16_t s; u_char b[2]; } internalPort;
2411     union { uint16_t s; u_char b[2]; } externalPort;
2412     uint32_t ttl;
2413 
2414     if (!data || data + 13 > end) goto fail;
2415 
2416     addr.b[0] = *data++;
2417     addr.b[1] = *data++;
2418     addr.b[2] = *data++;
2419     addr.b[3] = *data++;
2420     protocol          = *data++;
2421     internalPort.b[0] = *data++;
2422     internalPort.b[1] = *data++;
2423     externalPort.b[0] = *data++;
2424     externalPort.b[1] = *data++;
2425     ttl               = get_uint32(&data, end);
2426     if (!data) goto fail;
2427 
2428     ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, addr.l, protocol, internalPort.s, externalPort.s, ttl, sdr->AppContext);
2429     return;
2430     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
2431 
2432     fail :
2433     syslog(LOG_WARNING, "dnssd_clientstub handle_port_mapping_response: error reading result from daemon");
2434 }
2435 
2436 DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate
2437 (
2438     DNSServiceRef                       *sdRef,
2439     DNSServiceFlags flags,
2440     uint32_t interfaceIndex,
2441     uint32_t protocol,                                /* TCP and/or UDP */
2442     uint16_t internalPortInNetworkByteOrder,
2443     uint16_t externalPortInNetworkByteOrder,
2444     uint32_t ttl,                                     /* time to live in seconds */
2445     DNSServiceNATPortMappingReply callBack,
2446     void                                *context      /* may be NULL */
2447 )
2448 {
2449     char *ptr;
2450     size_t len;
2451     ipc_msg_hdr *hdr;
2452     union { uint16_t s; u_char b[2]; } internalPort = { internalPortInNetworkByteOrder };
2453     union { uint16_t s; u_char b[2]; } externalPort = { externalPortInNetworkByteOrder };
2454 
2455     DNSServiceErrorType err = ConnectToServer(sdRef, flags, port_mapping_request, handle_port_mapping_response, (void *)callBack, context);
2456     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
2457 
2458     len = sizeof(flags);
2459     len += sizeof(interfaceIndex);
2460     len += sizeof(protocol);
2461     len += sizeof(internalPort);
2462     len += sizeof(externalPort);
2463     len += sizeof(ttl);
2464 
2465     hdr = create_hdr(port_mapping_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
2466     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
2467 
2468     put_flags(flags, &ptr);
2469     put_uint32(interfaceIndex, &ptr);
2470     put_uint32(protocol, &ptr);
2471     *ptr++ = internalPort.b[0];
2472     *ptr++ = internalPort.b[1];
2473     *ptr++ = externalPort.b[0];
2474     *ptr++ = externalPort.b[1];
2475     put_uint32(ttl, &ptr);
2476 
2477     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
2478     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
2479     return err;
2480 }
2481 
2482 #if _DNS_SD_LIBDISPATCH
2483 DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue
2484 (
2485     DNSServiceRef service,
2486     dispatch_queue_t queue
2487 )
2488 {
2489     int dnssd_fd  = DNSServiceRefSockFD(service);
2490     if (dnssd_fd == dnssd_InvalidSocket) return kDNSServiceErr_BadParam;
2491     if (!queue)
2492     {
2493         syslog(LOG_WARNING, "dnssd_clientstub: DNSServiceSetDispatchQueue dispatch queue NULL");
2494         return kDNSServiceErr_BadParam;
2495     }
2496     if (service->disp_queue)
2497     {
2498         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch queue set already");
2499         return kDNSServiceErr_BadParam;
2500     }
2501     if (service->disp_source)
2502     {
2503         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch source set already");
2504         return kDNSServiceErr_BadParam;
2505     }
2506     service->disp_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, dnssd_fd, 0, queue);
2507     if (!service->disp_source)
2508     {
2509         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch_source_create failed");
2510         return kDNSServiceErr_NoMemory;
2511     }
2512     service->disp_queue = queue;
2513     dispatch_source_set_event_handler(service->disp_source, ^{DNSServiceProcessResult(service);});
2514     dispatch_source_set_cancel_handler(service->disp_source, ^{dnssd_close(dnssd_fd);});
2515     dispatch_resume(service->disp_source);
2516     return kDNSServiceErr_NoError;
2517 }
2518 #endif // _DNS_SD_LIBDISPATCH
2519 
2520 #if !defined(_WIN32)
2521 
2522 static void DNSSD_API SleepKeepaliveCallback(DNSServiceRef sdRef, DNSRecordRef rec, const DNSServiceFlags flags,
2523                                              DNSServiceErrorType errorCode, void *context)
2524 {
2525     SleepKAContext *ka = (SleepKAContext *)context;
2526     (void)rec;      // Unused
2527     (void)flags;    // Unused
2528 
2529     if (sdRef->kacontext != context)
2530         syslog(LOG_WARNING, "dnssd_clientstub SleepKeepaliveCallback context mismatch");
2531 
2532     if (ka->AppCallback)
2533         ((DNSServiceSleepKeepaliveReply)ka->AppCallback)(sdRef, errorCode, ka->AppContext);
2534 }
2535 
2536 static DNSServiceErrorType _DNSServiceSleepKeepalive_sockaddr
2537 (
2538     DNSServiceRef *                 sdRef,
2539     DNSServiceFlags                 flags,
2540     const struct sockaddr *         localAddr,
2541     const struct sockaddr *         remoteAddr,
2542     unsigned int                    timeout,
2543     DNSServiceSleepKeepaliveReply   callBack,
2544     void *                          context
2545 );
2546 
2547 DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive
2548 (
2549     DNSServiceRef                       *sdRef,
2550     DNSServiceFlags flags,
2551     int fd,
2552     unsigned int timeout,
2553     DNSServiceSleepKeepaliveReply callBack,
2554     void                                *context
2555 )
2556 {
2557     struct sockaddr_storage lss;
2558     struct sockaddr_storage rss;
2559     socklen_t len1, len2;
2560 
2561     len1 = sizeof(lss);
2562     if (getsockname(fd, (struct sockaddr *)&lss, &len1) < 0)
2563     {
2564         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive: getsockname %d\n", errno);
2565         return kDNSServiceErr_BadParam;
2566     }
2567 
2568     len2 = sizeof(rss);
2569     if (getpeername(fd, (struct sockaddr *)&rss, &len2) < 0)
2570     {
2571         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive: getpeername %d\n", errno);
2572         return kDNSServiceErr_BadParam;
2573     }
2574 
2575     if (len1 != len2)
2576     {
2577         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive local/remote info not same");
2578         return kDNSServiceErr_Unknown;
2579     }
2580     return _DNSServiceSleepKeepalive_sockaddr(sdRef, flags, (const struct sockaddr *)&lss, (const struct sockaddr *)&rss,
2581         timeout, callBack, context);
2582 }
2583 
2584 DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive_sockaddr
2585 (
2586     DNSServiceRef *                 sdRef,
2587     DNSServiceFlags                 flags,
2588     const struct sockaddr *         localAddr,
2589     const struct sockaddr *         remoteAddr,
2590     unsigned int                    timeout,
2591     DNSServiceSleepKeepaliveReply   callBack,
2592     void *                          context
2593 )
2594 {
2595     return _DNSServiceSleepKeepalive_sockaddr(sdRef, flags, localAddr, remoteAddr, timeout, callBack, context );
2596 }
2597 
2598 static DNSServiceErrorType _DNSServiceSleepKeepalive_sockaddr
2599 (
2600     DNSServiceRef *                 sdRef,
2601     DNSServiceFlags                 flags,
2602     const struct sockaddr *         localAddr,
2603     const struct sockaddr *         remoteAddr,
2604     unsigned int                    timeout,
2605     DNSServiceSleepKeepaliveReply   callBack,
2606     void *                          context
2607 )
2608 {
2609     char source_str[INET6_ADDRSTRLEN];
2610     char target_str[INET6_ADDRSTRLEN];
2611     unsigned int len, proxyreclen;
2612     char buf[256];
2613     DNSServiceErrorType err;
2614     DNSRecordRef record = NULL;
2615     char name[10];
2616     char recname[128];
2617     SleepKAContext *ka;
2618     unsigned int i, unique;
2619 
2620     (void) flags; //unused
2621     if (!timeout) return kDNSServiceErr_BadParam;
2622 
2623     unique = 0;
2624     if ((localAddr->sa_family == AF_INET) && (remoteAddr->sa_family == AF_INET))
2625     {
2626         const struct sockaddr_in *sl = (const struct sockaddr_in *)localAddr;
2627         const struct sockaddr_in *sr = (const struct sockaddr_in *)remoteAddr;
2628         unsigned char *ptr = (unsigned char *)&sl->sin_addr;
2629 
2630         if (!inet_ntop(AF_INET, (const void *)&sr->sin_addr, target_str, sizeof (target_str)))
2631         {
2632             syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive remote info failed %d", errno);
2633             return kDNSServiceErr_Unknown;
2634         }
2635         if (!inet_ntop(AF_INET, (const void *)&sl->sin_addr, source_str, sizeof (source_str)))
2636         {
2637             syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive local info failed %d", errno);
2638             return kDNSServiceErr_Unknown;
2639         }
2640         // Sum of all bytes in the local address and port should result in a unique
2641         // number in the local network
2642         for (i = 0; i < sizeof(struct in_addr); i++)
2643             unique += ptr[i];
2644         unique += sl->sin_port;
2645         len = snprintf(buf+1, sizeof(buf) - 1, "t=%u h=%s d=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl->sin_port), ntohs(sr->sin_port));
2646     }
2647     else if ((localAddr->sa_family == AF_INET6) && (remoteAddr->sa_family == AF_INET6))
2648     {
2649         const struct sockaddr_in6 *sl6 = (const struct sockaddr_in6 *)localAddr;
2650         const struct sockaddr_in6 *sr6 = (const struct sockaddr_in6 *)remoteAddr;
2651         unsigned char *ptr = (unsigned char *)&sl6->sin6_addr;
2652 
2653         if (!inet_ntop(AF_INET6, (const void *)&sr6->sin6_addr, target_str, sizeof (target_str)))
2654         {
2655             syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive remote6 info failed %d", errno);
2656             return kDNSServiceErr_Unknown;
2657         }
2658         if (!inet_ntop(AF_INET6, (const void *)&sl6->sin6_addr, source_str, sizeof (source_str)))
2659         {
2660             syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive local6 info failed %d", errno);
2661             return kDNSServiceErr_Unknown;
2662         }
2663         for (i = 0; i < sizeof(struct in6_addr); i++)
2664             unique += ptr[i];
2665         unique += sl6->sin6_port;
2666         len = snprintf(buf+1, sizeof(buf) - 1, "t=%u H=%s D=%s l=%u r=%u", timeout, source_str, target_str, ntohs(sl6->sin6_port), ntohs(sr6->sin6_port));
2667     }
2668     else
2669     {
2670         return kDNSServiceErr_BadParam;
2671     }
2672 
2673     if (len >= (sizeof(buf) - 1))
2674     {
2675         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive could not fit local/remote info");
2676         return kDNSServiceErr_Unknown;
2677     }
2678     // Include the NULL byte also in the first byte. The total length of the record includes the
2679     // first byte also.
2680     buf[0] = len + 1;
2681     proxyreclen = len + 2;
2682 
2683     len = snprintf(name, sizeof(name), "%u", unique);
2684     if (len >= sizeof(name))
2685     {
2686         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive could not fit unique");
2687         return kDNSServiceErr_Unknown;
2688     }
2689 
2690     len = snprintf(recname, sizeof(recname), "%s.%s", name, "_keepalive._dns-sd._udp.local");
2691     if (len >= sizeof(recname))
2692     {
2693         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive could not fit name");
2694         return kDNSServiceErr_Unknown;
2695     }
2696 
2697     ka = malloc(sizeof(SleepKAContext));
2698     if (!ka) return kDNSServiceErr_NoMemory;
2699     ka->AppCallback = (void *)callBack;
2700     ka->AppContext = context;
2701 
2702     err = DNSServiceCreateConnection(sdRef);
2703     if (err)
2704     {
2705         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive cannot create connection");
2706         free(ka);
2707         return err;
2708     }
2709 
2710     // we don't care about the "record". When sdRef gets deallocated later, it will be freed too
2711     err = DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, 0, recname,
2712                                    kDNSServiceType_NULL,  kDNSServiceClass_IN, proxyreclen, buf,  kDNSServiceInterfaceIndexAny, SleepKeepaliveCallback, ka);
2713     if (err)
2714     {
2715         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSleepKeepalive cannot create connection");
2716         free(ka);
2717         return err;
2718     }
2719     (*sdRef)->kacontext = ka;
2720     return kDNSServiceErr_NoError;
2721 }
2722 #endif
2723