14b22b933Srs /* -*- Mode: C; tab-width: 4 -*-
24b22b933Srs  *
3cda73f64SToomas Soome  * Copyright (c) 2003-2015 Apple Inc. All rights reserved.
44b22b933Srs  *
55ffb0c9bSToomas Soome  * Redistribution and use in source and binary forms, with or without
64b22b933Srs  * modification, are permitted provided that the following conditions are met:
74b22b933Srs  *
85ffb0c9bSToomas Soome  * 1.  Redistributions of source code must retain the above copyright notice,
95ffb0c9bSToomas Soome  *     this list of conditions and the following disclaimer.
105ffb0c9bSToomas Soome  * 2.  Redistributions in binary form must reproduce the above copyright notice,
115ffb0c9bSToomas Soome  *     this list of conditions and the following disclaimer in the documentation
125ffb0c9bSToomas Soome  *     and/or other materials provided with the distribution.
13cda73f64SToomas Soome  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
145ffb0c9bSToomas Soome  *     contributors may be used to endorse or promote products derived from this
155ffb0c9bSToomas Soome  *     software without specific prior written permission.
164b22b933Srs  *
175ffb0c9bSToomas Soome  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
185ffb0c9bSToomas Soome  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
195ffb0c9bSToomas Soome  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
205ffb0c9bSToomas Soome  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
215ffb0c9bSToomas Soome  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
225ffb0c9bSToomas Soome  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
235ffb0c9bSToomas Soome  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
245ffb0c9bSToomas Soome  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255ffb0c9bSToomas Soome  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
264b22b933Srs  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
274b22b933Srs  */
284b22b933Srs 
294b22b933Srs #include <errno.h>
304b22b933Srs #include <stdlib.h>
31*3b436d06SToomas Soome #include <fcntl.h>
325ffb0c9bSToomas Soome 
33cda73f64SToomas Soome #include "dnssd_ipc.h"
34cda73f64SToomas Soome 
355ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
365ffb0c9bSToomas Soome #include <mach-o/dyld.h>
375ffb0c9bSToomas Soome #include <uuid/uuid.h>
385ffb0c9bSToomas Soome #include <TargetConditionals.h>
39c65ebfc7SToomas Soome #include "dns_sd_internal.h"
405ffb0c9bSToomas Soome #endif
414b22b933Srs 
424b22b933Srs #if defined(_WIN32)
434b22b933Srs 
445ffb0c9bSToomas Soome     #define _SSIZE_T
455ffb0c9bSToomas Soome     #include <CommonServices.h>
465ffb0c9bSToomas Soome     #include <DebugServices.h>
475ffb0c9bSToomas Soome     #include <winsock2.h>
485ffb0c9bSToomas Soome     #include <ws2tcpip.h>
495ffb0c9bSToomas Soome     #include <windows.h>
505ffb0c9bSToomas Soome     #include <stdarg.h>
515ffb0c9bSToomas Soome     #include <stdio.h>
524b22b933Srs 
535ffb0c9bSToomas Soome     #define sockaddr_mdns sockaddr_in
545ffb0c9bSToomas Soome     #define AF_MDNS AF_INET
554b22b933Srs 
565ffb0c9bSToomas Soome // Disable warning: "'type cast' : from data pointer 'void *' to function pointer"
575ffb0c9bSToomas Soome     #pragma warning(disable:4055)
584b22b933Srs 
595ffb0c9bSToomas Soome // Disable warning: "nonstandard extension, function/data pointer conversion in expression"
605ffb0c9bSToomas Soome     #pragma warning(disable:4152)
614b22b933Srs 
624b22b933Srs extern BOOL IsSystemServiceDisabled();
634b22b933Srs 
645ffb0c9bSToomas Soome     #define sleep(X) Sleep((X) * 1000)
654b22b933Srs 
664b22b933Srs static int g_initWinsock = 0;
675ffb0c9bSToomas Soome     #define LOG_WARNING kDebugLevelWarning
685ffb0c9bSToomas Soome     #define LOG_INFO kDebugLevelInfo
695ffb0c9bSToomas Soome static void syslog( int priority, const char * message, ...)
705ffb0c9bSToomas Soome {
715ffb0c9bSToomas Soome     va_list args;
725ffb0c9bSToomas Soome     int len;
735ffb0c9bSToomas Soome     char * buffer;
745ffb0c9bSToomas Soome     DWORD err = WSAGetLastError();
755ffb0c9bSToomas Soome     (void) priority;
765ffb0c9bSToomas Soome     va_start( args, message );
775ffb0c9bSToomas Soome     len = _vscprintf( message, args ) + 1;
785ffb0c9bSToomas Soome     buffer = malloc( len * sizeof(char) );
79cda73f64SToomas Soome     if ( buffer ) { vsnprintf( buffer, len, message, args ); OutputDebugString( buffer ); free( buffer ); }
805ffb0c9bSToomas Soome     WSASetLastError( err );
815ffb0c9bSToomas Soome }
824b22b933Srs #else
834b22b933Srs 
84*3b436d06SToomas Soome     #include <sys/fcntl.h>      // For O_RDWR etc.
855ffb0c9bSToomas Soome     #include <sys/time.h>
865ffb0c9bSToomas Soome     #include <sys/socket.h>
875ffb0c9bSToomas Soome     #include <syslog.h>
884b22b933Srs 
895ffb0c9bSToomas Soome     #define sockaddr_mdns sockaddr_un
905ffb0c9bSToomas Soome     #define AF_MDNS AF_LOCAL
914b22b933Srs 
924b22b933Srs #endif
934b22b933Srs 
945ffb0c9bSToomas Soome // <rdar://problem/4096913> Specifies how many times we'll try and connect to the server.
954b22b933Srs 
964b22b933Srs #define DNSSD_CLIENT_MAXTRIES 4
974b22b933Srs 
985ffb0c9bSToomas Soome // Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp)
995ffb0c9bSToomas Soome //#define USE_NAMED_ERROR_RETURN_SOCKET 1
1004b22b933Srs 
1015ffb0c9bSToomas Soome // If the UDS client has not received a response from the daemon in 60 secs, it is unlikely to get one
1025ffb0c9bSToomas Soome // Note: Timeout of 3 secs should be sufficient in normal scenarios, but 60 secs is chosen as a safeguard since
1035ffb0c9bSToomas Soome // some clients may come up before mDNSResponder itself after a BOOT and on rare ocassions IOPM/Keychain/D2D calls
1045ffb0c9bSToomas Soome // in mDNSResponder's INIT may take a much longer time to return
1055ffb0c9bSToomas Soome #define DNSSD_CLIENT_TIMEOUT 60
1065ffb0c9bSToomas Soome 
1075ffb0c9bSToomas Soome #ifndef CTL_PATH_PREFIX
1085ffb0c9bSToomas Soome #define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
1095ffb0c9bSToomas Soome #endif
1105ffb0c9bSToomas Soome 
1115ffb0c9bSToomas Soome typedef struct
1125ffb0c9bSToomas Soome {
1135ffb0c9bSToomas Soome     ipc_msg_hdr ipc_hdr;
1145ffb0c9bSToomas Soome     DNSServiceFlags cb_flags;
1155ffb0c9bSToomas Soome     uint32_t cb_interface;
1165ffb0c9bSToomas Soome     DNSServiceErrorType cb_err;
1175ffb0c9bSToomas Soome } CallbackHeader;
1185ffb0c9bSToomas Soome 
1195ffb0c9bSToomas Soome typedef struct _DNSServiceRef_t DNSServiceOp;
1205ffb0c9bSToomas Soome typedef struct _DNSRecordRef_t DNSRecord;
1215ffb0c9bSToomas Soome 
1225ffb0c9bSToomas Soome #if !defined(_WIN32)
1235ffb0c9bSToomas Soome typedef struct
1245ffb0c9bSToomas Soome {
1255ffb0c9bSToomas Soome     void             *AppCallback;      // Client callback function and context
1265ffb0c9bSToomas Soome     void             *AppContext;
1275ffb0c9bSToomas Soome } SleepKAContext;
1285ffb0c9bSToomas Soome #endif
1295ffb0c9bSToomas Soome 
1305ffb0c9bSToomas Soome // client stub callback to process message from server and deliver results to client application
1315ffb0c9bSToomas Soome typedef void (*ProcessReplyFn)(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *msg, const char *const end);
1325ffb0c9bSToomas Soome 
1335ffb0c9bSToomas Soome #define ValidatorBits 0x12345678
1345ffb0c9bSToomas Soome #define DNSServiceRefValid(X) (dnssd_SocketValid((X)->sockfd) && (((X)->sockfd ^ (X)->validator) == ValidatorBits))
1355ffb0c9bSToomas Soome 
1365ffb0c9bSToomas Soome // When using kDNSServiceFlagsShareConnection, there is one primary _DNSServiceOp_t, and zero or more subordinates
1375ffb0c9bSToomas Soome // For the primary, the 'next' field points to the first subordinate, and its 'next' field points to the next, and so on.
1385ffb0c9bSToomas Soome // For the primary, the 'primary' field is NULL; for subordinates the 'primary' field points back to the associated primary
1395ffb0c9bSToomas Soome //
1405ffb0c9bSToomas Soome // _DNS_SD_LIBDISPATCH is defined where libdispatch/GCD is available. This does not mean that the application will use the
1415ffb0c9bSToomas Soome // DNSServiceSetDispatchQueue API. Hence any new code guarded with _DNS_SD_LIBDISPATCH should still be backwards compatible.
1425ffb0c9bSToomas Soome struct _DNSServiceRef_t
1435ffb0c9bSToomas Soome {
1445ffb0c9bSToomas Soome     DNSServiceOp     *next;             // For shared connection
1455ffb0c9bSToomas Soome     DNSServiceOp     *primary;          // For shared connection
1465ffb0c9bSToomas Soome     dnssd_sock_t sockfd;                // Connected socket between client and daemon
1475ffb0c9bSToomas Soome     dnssd_sock_t validator;             // Used to detect memory corruption, double disposals, etc.
1485ffb0c9bSToomas Soome     client_context_t uid;               // For shared connection requests, each subordinate DNSServiceRef has its own ID,
1495ffb0c9bSToomas Soome                                         // unique within the scope of the same shared parent DNSServiceRef
1505ffb0c9bSToomas Soome     uint32_t op;                        // request_op_t or reply_op_t
1515ffb0c9bSToomas Soome     uint32_t max_index;                 // Largest assigned record index - 0 if no additional records registered
1525ffb0c9bSToomas Soome     uint32_t logcounter;                // Counter used to control number of syslog messages we write
1535ffb0c9bSToomas Soome     int              *moreptr;          // Set while DNSServiceProcessResult working on this particular DNSServiceRef
1545ffb0c9bSToomas Soome     ProcessReplyFn ProcessReply;        // Function pointer to the code to handle received messages
1555ffb0c9bSToomas Soome     void             *AppCallback;      // Client callback function and context
1565ffb0c9bSToomas Soome     void             *AppContext;
1575ffb0c9bSToomas Soome     DNSRecord        *rec;
1585ffb0c9bSToomas Soome #if _DNS_SD_LIBDISPATCH
1595ffb0c9bSToomas Soome     dispatch_source_t disp_source;
1605ffb0c9bSToomas Soome     dispatch_queue_t disp_queue;
1615ffb0c9bSToomas Soome #endif
1625ffb0c9bSToomas Soome     void             *kacontext;
1635ffb0c9bSToomas Soome };
1645ffb0c9bSToomas Soome 
1655ffb0c9bSToomas Soome struct _DNSRecordRef_t
1665ffb0c9bSToomas Soome {
1675ffb0c9bSToomas Soome     DNSRecord       *recnext;
1685ffb0c9bSToomas Soome     void *AppContext;
1695ffb0c9bSToomas Soome     DNSServiceRegisterRecordReply AppCallback;
1704b22b933Srs     DNSRecordRef recref;
1714b22b933Srs     uint32_t record_index;  // index is unique to the ServiceDiscoveryRef
1725ffb0c9bSToomas Soome     client_context_t uid;  // For demultiplexing multiple DNSServiceRegisterRecord calls
1735ffb0c9bSToomas Soome     DNSServiceOp *sdr;
1745ffb0c9bSToomas Soome };
1754b22b933Srs 
176c65ebfc7SToomas Soome #if !defined(USE_TCP_LOOPBACK)
177c65ebfc7SToomas Soome static void SetUDSPath(struct sockaddr_un *saddr, const char *path)
178c65ebfc7SToomas Soome {
179c65ebfc7SToomas Soome     size_t pathLen;
180c65ebfc7SToomas Soome 
181c65ebfc7SToomas Soome     pathLen = strlen(path);
182c65ebfc7SToomas Soome     if (pathLen < sizeof(saddr->sun_path))
183c65ebfc7SToomas Soome         memcpy(saddr->sun_path, path, pathLen + 1);
184c65ebfc7SToomas Soome     else
185c65ebfc7SToomas Soome         saddr->sun_path[0] = '\0';
186c65ebfc7SToomas Soome }
187c65ebfc7SToomas Soome #endif
188c65ebfc7SToomas Soome 
1895ffb0c9bSToomas Soome // Write len bytes. Return 0 on success, -1 on error
1905ffb0c9bSToomas Soome static int write_all(dnssd_sock_t sd, char *buf, size_t len)
1915ffb0c9bSToomas Soome {
1924b22b933Srs     // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
1935ffb0c9bSToomas Soome     //if (send(sd, buf, len, MSG_WAITALL) != len) return -1;
1944b22b933Srs     while (len)
1955ffb0c9bSToomas Soome     {
1965ffb0c9bSToomas Soome         ssize_t num_written = send(sd, buf, (long)len, 0);
1975ffb0c9bSToomas Soome         if (num_written < 0 || (size_t)num_written > len)
1985ffb0c9bSToomas Soome         {
199c65ebfc7SToomas Soome             // Check whether socket has gone defunct,
200c65ebfc7SToomas Soome             // otherwise, an error here indicates some OS bug
2015ffb0c9bSToomas Soome             // or that the mDNSResponder daemon crashed (which should never happen).
202c65ebfc7SToomas Soome #if !defined(__ppc__) && defined(SO_ISDEFUNCT)
203c65ebfc7SToomas Soome             int defunct = 0;
2045ffb0c9bSToomas Soome             socklen_t dlen = sizeof (defunct);
2055ffb0c9bSToomas Soome             if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0)
2065ffb0c9bSToomas Soome                 syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
2075ffb0c9bSToomas Soome             if (!defunct)
2085ffb0c9bSToomas Soome                 syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd,
2095ffb0c9bSToomas Soome                        (long)num_written, (long)len,
2105ffb0c9bSToomas Soome                        (num_written < 0) ? dnssd_errno                 : 0,
2115ffb0c9bSToomas Soome                        (num_written < 0) ? dnssd_strerror(dnssd_errno) : "");
2125ffb0c9bSToomas Soome             else
2135ffb0c9bSToomas Soome                 syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd);
214c65ebfc7SToomas Soome #else
2155ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd,
2165ffb0c9bSToomas Soome                    (long)num_written, (long)len,
2175ffb0c9bSToomas Soome                    (num_written < 0) ? dnssd_errno                 : 0,
2185ffb0c9bSToomas Soome                    (num_written < 0) ? dnssd_strerror(dnssd_errno) : "");
219c65ebfc7SToomas Soome #endif
2205ffb0c9bSToomas Soome             return -1;
2215ffb0c9bSToomas Soome         }
2225ffb0c9bSToomas Soome         buf += num_written;
2235ffb0c9bSToomas Soome         len -= num_written;
2244b22b933Srs     }
2255ffb0c9bSToomas Soome     return 0;
2265ffb0c9bSToomas Soome }
2275ffb0c9bSToomas Soome 
2285ffb0c9bSToomas Soome enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2 };
2294b22b933Srs 
2305ffb0c9bSToomas Soome // Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for
2314b22b933Srs static int read_all(dnssd_sock_t sd, char *buf, int len)
2325ffb0c9bSToomas Soome {
2334b22b933Srs     // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
2345ffb0c9bSToomas Soome     //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1;
2355ffb0c9bSToomas Soome 
2364b22b933Srs     while (len)
2375ffb0c9bSToomas Soome     {
2385ffb0c9bSToomas Soome         ssize_t num_read = recv(sd, buf, len, 0);
2395ffb0c9bSToomas Soome         // It is valid to get an interrupted system call error e.g., somebody attaching
2405ffb0c9bSToomas Soome         // in a debugger, retry without failing
241c65ebfc7SToomas Soome         if ((num_read < 0) && (errno == EINTR))
242c65ebfc7SToomas Soome         {
243c65ebfc7SToomas Soome             syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue");
244c65ebfc7SToomas Soome             continue;
2455ffb0c9bSToomas Soome         }
2465ffb0c9bSToomas Soome         if ((num_read == 0) || (num_read < 0) || (num_read > len))
2475ffb0c9bSToomas Soome         {
2485ffb0c9bSToomas Soome             int printWarn = 0;
2495ffb0c9bSToomas Soome             int defunct = 0;
250c65ebfc7SToomas Soome 
251c65ebfc7SToomas Soome             // Check whether socket has gone defunct,
252c65ebfc7SToomas Soome             // otherwise, an error here indicates some OS bug
2535ffb0c9bSToomas Soome             // or that the mDNSResponder daemon crashed (which should never happen).
2545ffb0c9bSToomas Soome #if defined(WIN32)
2555ffb0c9bSToomas Soome             // <rdar://problem/7481776> Suppress logs for "A non-blocking socket operation
2565ffb0c9bSToomas Soome             //                          could not be completed immediately"
2575ffb0c9bSToomas Soome             if (WSAGetLastError() != WSAEWOULDBLOCK)
2585ffb0c9bSToomas Soome                 printWarn = 1;
2595ffb0c9bSToomas Soome #endif
2605ffb0c9bSToomas Soome #if !defined(__ppc__) && defined(SO_ISDEFUNCT)
2615ffb0c9bSToomas Soome             {
2625ffb0c9bSToomas Soome                 socklen_t dlen = sizeof (defunct);
2635ffb0c9bSToomas Soome                 if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0)
2645ffb0c9bSToomas Soome                     syslog(LOG_WARNING, "dnssd_clientstub read_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
2655ffb0c9bSToomas Soome             }
2665ffb0c9bSToomas Soome             if (!defunct)
2675ffb0c9bSToomas Soome                 printWarn = 1;
2685ffb0c9bSToomas Soome #endif
2695ffb0c9bSToomas Soome             if (printWarn)
2705ffb0c9bSToomas Soome                 syslog(LOG_WARNING, "dnssd_clientstub read_all(%d) failed %ld/%ld %d %s", sd,
2715ffb0c9bSToomas Soome                        (long)num_read, (long)len,
2725ffb0c9bSToomas Soome                        (num_read < 0) ? dnssd_errno                 : 0,
2735ffb0c9bSToomas Soome                        (num_read < 0) ? dnssd_strerror(dnssd_errno) : "");
2745ffb0c9bSToomas Soome             else if (defunct)
2755ffb0c9bSToomas Soome                 syslog(LOG_INFO, "dnssd_clientstub read_all(%d) DEFUNCT", sd);
2765ffb0c9bSToomas Soome             return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : read_all_fail;
2775ffb0c9bSToomas Soome         }
2785ffb0c9bSToomas Soome         buf += num_read;
2795ffb0c9bSToomas Soome         len -= num_read;
2804b22b933Srs     }
2815ffb0c9bSToomas Soome     return read_all_success;
2825ffb0c9bSToomas Soome }
2835ffb0c9bSToomas Soome 
2845ffb0c9bSToomas Soome // Returns 1 if more bytes remain to be read on socket descriptor sd, 0 otherwise
2855ffb0c9bSToomas Soome static int more_bytes(dnssd_sock_t sd)
2865ffb0c9bSToomas Soome {
2875ffb0c9bSToomas Soome     struct timeval tv = { 0, 0 };
2885ffb0c9bSToomas Soome     fd_set readfds;
2895ffb0c9bSToomas Soome     fd_set *fs;
2905ffb0c9bSToomas Soome     int ret;
2915ffb0c9bSToomas Soome 
292c65ebfc7SToomas Soome #if defined(_WIN32)
293c65ebfc7SToomas Soome     fs = &readfds;
294c65ebfc7SToomas Soome     FD_ZERO(fs);
295c65ebfc7SToomas Soome     FD_SET(sd, fs);
296c65ebfc7SToomas Soome     ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv);
297c65ebfc7SToomas Soome #else
2985ffb0c9bSToomas Soome     if (sd < FD_SETSIZE)
2995ffb0c9bSToomas Soome     {
3005ffb0c9bSToomas Soome         fs = &readfds;
3015ffb0c9bSToomas Soome         FD_ZERO(fs);
3025ffb0c9bSToomas Soome     }
3035ffb0c9bSToomas Soome     else
3045ffb0c9bSToomas Soome     {
3055ffb0c9bSToomas Soome         // Compute the number of integers needed for storing "sd". Internally fd_set is stored
3065ffb0c9bSToomas Soome         // as an array of ints with one bit for each fd and hence we need to compute
3075ffb0c9bSToomas Soome         // the number of ints needed rather than the number of bytes. If "sd" is 32, we need
3085ffb0c9bSToomas Soome         // two ints and not just one.
3095ffb0c9bSToomas Soome         int nfdbits = sizeof (int) * 8;
3105ffb0c9bSToomas Soome         int nints = (sd/nfdbits) + 1;
311cda73f64SToomas Soome         fs = (fd_set *)calloc(nints, (size_t)sizeof(int));
312c65ebfc7SToomas Soome         if (fs == NULL)
313c65ebfc7SToomas Soome         {
314c65ebfc7SToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed");
315c65ebfc7SToomas Soome             return 0;
3165ffb0c9bSToomas Soome         }
3175ffb0c9bSToomas Soome     }
3185ffb0c9bSToomas Soome     FD_SET(sd, fs);
3195ffb0c9bSToomas Soome     ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv);
320c65ebfc7SToomas Soome     if (fs != &readfds)
3215ffb0c9bSToomas Soome         free(fs);
322c65ebfc7SToomas Soome #endif
3235ffb0c9bSToomas Soome     return (ret > 0);
3245ffb0c9bSToomas Soome }
3255ffb0c9bSToomas Soome 
3265ffb0c9bSToomas Soome // set_waitlimit() implements a timeout using select. It is called from deliver_request() before recv() OR accept()
3275ffb0c9bSToomas Soome // to ensure the UDS clients are not blocked in these system calls indefinitely.
3285ffb0c9bSToomas Soome // Note: Ideally one should never be blocked here, because it indicates either mDNSResponder daemon is not yet up/hung/
329c65ebfc7SToomas Soome // superbusy/crashed or some other OS bug. For eg: On Windows which suffers from 3rd party software
330c65ebfc7SToomas Soome // (primarily 3rd party firewall software) interfering with proper functioning of the TCP protocol stack it is possible
3315ffb0c9bSToomas Soome // the next operation on this socket(recv/accept) is blocked since we depend on TCP to communicate with the system service.
3325ffb0c9bSToomas Soome static int set_waitlimit(dnssd_sock_t sock, int timeout)
3335ffb0c9bSToomas Soome {
3345ffb0c9bSToomas Soome     int gDaemonErr = kDNSServiceErr_NoError;
3355ffb0c9bSToomas Soome 
3365ffb0c9bSToomas Soome     // To prevent stack corruption since select does not work with timeout if fds > FD_SETSIZE(1024)
3375ffb0c9bSToomas Soome     if (!gDaemonErr && sock < FD_SETSIZE)
3385ffb0c9bSToomas Soome     {
3395ffb0c9bSToomas Soome         struct timeval tv;
3405ffb0c9bSToomas Soome         fd_set set;
3415ffb0c9bSToomas Soome 
3425ffb0c9bSToomas Soome         FD_ZERO(&set);
3435ffb0c9bSToomas Soome         FD_SET(sock, &set);
3445ffb0c9bSToomas Soome         tv.tv_sec = timeout;
3455ffb0c9bSToomas Soome         tv.tv_usec = 0;
3465ffb0c9bSToomas Soome         if (!select((int)(sock + 1), &set, NULL, NULL, &tv))
3475ffb0c9bSToomas Soome         {
3485ffb0c9bSToomas Soome             // Ideally one should never hit this case: See comments before set_waitlimit()
3495ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub set_waitlimit:_daemon timed out (%d secs) without any response: Socket %d", timeout, sock);
3505ffb0c9bSToomas Soome             gDaemonErr = kDNSServiceErr_Timeout;
3515ffb0c9bSToomas Soome         }
3525ffb0c9bSToomas Soome     }
3535ffb0c9bSToomas Soome     return gDaemonErr;
3545ffb0c9bSToomas Soome }
3554b22b933Srs 
3564b22b933Srs /* create_hdr
3574b22b933Srs  *
3585ffb0c9bSToomas Soome  * allocate and initialize an ipc message header. Value of len should initially be the
3595ffb0c9bSToomas Soome  * length of the data, and is set to the value of the data plus the header. data_start
3605ffb0c9bSToomas Soome  * is set to point to the beginning of the data section. SeparateReturnSocket should be
3615ffb0c9bSToomas Soome  * non-zero for calls that can't receive an immediate error return value on their primary
3625ffb0c9bSToomas Soome  * socket, and therefore require a separate return path for the error code result.
3634b22b933Srs  * if zero, the path to a control socket is appended at the beginning of the message buffer.
3644b22b933Srs  * data_start is set past this string.
3654b22b933Srs  */
3665ffb0c9bSToomas Soome static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int SeparateReturnSocket, DNSServiceOp *ref)
3675ffb0c9bSToomas Soome {
3684b22b933Srs     char *msg = NULL;
3694b22b933Srs     ipc_msg_hdr *hdr;
3704b22b933Srs     int datalen;
3714b22b933Srs #if !defined(USE_TCP_LOOPBACK)
3725ffb0c9bSToomas Soome     char ctrl_path[64] = "";    // "/var/tmp/dnssd_result_socket.xxxxxxxxxx-xxx-xxxxxx"
3734b22b933Srs #endif
3744b22b933Srs 
3755ffb0c9bSToomas Soome     if (SeparateReturnSocket)
3765ffb0c9bSToomas Soome     {
3774b22b933Srs #if defined(USE_TCP_LOOPBACK)
3785ffb0c9bSToomas Soome         *len += 2;  // Allocate space for two-byte port number
3795ffb0c9bSToomas Soome #elif defined(USE_NAMED_ERROR_RETURN_SOCKET)
3805ffb0c9bSToomas Soome         struct timeval tv;
3815ffb0c9bSToomas Soome         if (gettimeofday(&tv, NULL) < 0)
3825ffb0c9bSToomas Soome         { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: gettimeofday failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); return NULL; }
383cda73f64SToomas Soome         snprintf(ctrl_path, sizeof(ctrl_path), "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(),
3845ffb0c9bSToomas Soome                 (unsigned long)(tv.tv_sec & 0xFFF), (unsigned long)(tv.tv_usec));
3854b22b933Srs         *len += strlen(ctrl_path) + 1;
3865ffb0c9bSToomas Soome #else
3875ffb0c9bSToomas Soome         *len += 1;      // Allocate space for single zero byte (empty C string)
3884b22b933Srs #endif
3895ffb0c9bSToomas Soome     }
3904b22b933Srs 
3914b22b933Srs     datalen = (int) *len;
3924b22b933Srs     *len += sizeof(ipc_msg_hdr);
3934b22b933Srs 
3945ffb0c9bSToomas Soome     // Write message to buffer
3954b22b933Srs     msg = malloc(*len);
3965ffb0c9bSToomas Soome     if (!msg) { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: malloc failed"); return NULL; }
3975ffb0c9bSToomas Soome 
3985ffb0c9bSToomas Soome     memset(msg, 0, *len);
3995ffb0c9bSToomas Soome     hdr = (ipc_msg_hdr *)msg;
4005ffb0c9bSToomas Soome     hdr->version                = VERSION;
4015ffb0c9bSToomas Soome     hdr->datalen                = datalen;
4025ffb0c9bSToomas Soome     hdr->ipc_flags              = 0;
4035ffb0c9bSToomas Soome     hdr->op                     = op;
4045ffb0c9bSToomas Soome     hdr->client_context         = ref->uid;
4055ffb0c9bSToomas Soome     hdr->reg_index              = 0;
4064b22b933Srs     *data_start = msg + sizeof(ipc_msg_hdr);
4074b22b933Srs #if defined(USE_TCP_LOOPBACK)
4085ffb0c9bSToomas Soome     // Put dummy data in for the port, since we don't know what it is yet.
4095ffb0c9bSToomas Soome     // The data will get filled in before we send the message. This happens in deliver_request().
4105ffb0c9bSToomas Soome     if (SeparateReturnSocket) put_uint16(0, data_start);
4114b22b933Srs #else
4125ffb0c9bSToomas Soome     if (SeparateReturnSocket) put_string(ctrl_path, data_start);
4134b22b933Srs #endif
4144b22b933Srs     return hdr;
4155ffb0c9bSToomas Soome }
4164b22b933Srs 
4175ffb0c9bSToomas Soome static void FreeDNSRecords(DNSServiceOp *sdRef)
4185ffb0c9bSToomas Soome {
4195ffb0c9bSToomas Soome     DNSRecord *rec = sdRef->rec;
4205ffb0c9bSToomas Soome     while (rec)
4214b22b933Srs     {
4225ffb0c9bSToomas Soome         DNSRecord *next = rec->recnext;
4235ffb0c9bSToomas Soome         free(rec);
4245ffb0c9bSToomas Soome         rec = next;
4255ffb0c9bSToomas Soome     }
4265ffb0c9bSToomas Soome }
4274b22b933Srs 
4285ffb0c9bSToomas Soome static void FreeDNSServiceOp(DNSServiceOp *x)
4295ffb0c9bSToomas Soome {
4305ffb0c9bSToomas Soome     // We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed
4315ffb0c9bSToomas Soome     // then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket)
4325ffb0c9bSToomas Soome     if ((x->sockfd ^ x->validator) != ValidatorBits)
433cda73f64SToomas Soome     {
434cda73f64SToomas Soome         static DNSServiceOp *op_were_not_going_to_free_but_we_need_to_fool_the_analyzer;
4355ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub attempt to dispose invalid DNSServiceRef %p %08X %08X", x, x->sockfd, x->validator);
436cda73f64SToomas Soome         op_were_not_going_to_free_but_we_need_to_fool_the_analyzer = x;
437cda73f64SToomas Soome     }
4385ffb0c9bSToomas Soome     else
4395ffb0c9bSToomas Soome     {
4405ffb0c9bSToomas Soome         x->next         = NULL;
4415ffb0c9bSToomas Soome         x->primary      = NULL;
4425ffb0c9bSToomas Soome         x->sockfd       = dnssd_InvalidSocket;
4435ffb0c9bSToomas Soome         x->validator    = 0xDDDDDDDD;
4445ffb0c9bSToomas Soome         x->op           = request_op_none;
4455ffb0c9bSToomas Soome         x->max_index    = 0;
4465ffb0c9bSToomas Soome         x->logcounter   = 0;
4475ffb0c9bSToomas Soome         x->moreptr      = NULL;
4485ffb0c9bSToomas Soome         x->ProcessReply = NULL;
4495ffb0c9bSToomas Soome         x->AppCallback  = NULL;
4505ffb0c9bSToomas Soome         x->AppContext   = NULL;
4515ffb0c9bSToomas Soome #if _DNS_SD_LIBDISPATCH
4525ffb0c9bSToomas Soome         if (x->disp_source) dispatch_release(x->disp_source);
4535ffb0c9bSToomas Soome         x->disp_source  = NULL;
4545ffb0c9bSToomas Soome         x->disp_queue   = NULL;
4555ffb0c9bSToomas Soome #endif
4565ffb0c9bSToomas Soome         // DNSRecords may have been added to subordinate sdRef e.g., DNSServiceRegister/DNSServiceAddRecord
457c65ebfc7SToomas Soome         // or on the main sdRef e.g., DNSServiceCreateConnection/DNSServiceRegisterRecord.
458c65ebfc7SToomas Soome         // DNSRecords may have been freed if the application called DNSRemoveRecord.
4595ffb0c9bSToomas Soome         FreeDNSRecords(x);
4605ffb0c9bSToomas Soome         if (x->kacontext)
4615ffb0c9bSToomas Soome         {
4625ffb0c9bSToomas Soome             free(x->kacontext);
4635ffb0c9bSToomas Soome             x->kacontext = NULL;
4645ffb0c9bSToomas Soome         }
4655ffb0c9bSToomas Soome         free(x);
4665ffb0c9bSToomas Soome     }
4675ffb0c9bSToomas Soome }
4684b22b933Srs 
4695ffb0c9bSToomas Soome // Return a connected service ref (deallocate with DNSServiceRefDeallocate)
4705ffb0c9bSToomas Soome static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext)
4715ffb0c9bSToomas Soome {
4725ffb0c9bSToomas Soome     int NumTries = 0;
4734b22b933Srs 
4745ffb0c9bSToomas Soome     dnssd_sockaddr_t saddr;
4755ffb0c9bSToomas Soome     DNSServiceOp *sdr;
4764b22b933Srs 
477c65ebfc7SToomas Soome     if (!ref)
478c65ebfc7SToomas Soome     {
479c65ebfc7SToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef");
480c65ebfc7SToomas Soome         return kDNSServiceErr_BadParam;
4815ffb0c9bSToomas Soome     }
4824b22b933Srs 
4835ffb0c9bSToomas Soome     if (flags & kDNSServiceFlagsShareConnection)
4845ffb0c9bSToomas Soome     {
4855ffb0c9bSToomas Soome         if (!*ref)
4865ffb0c9bSToomas Soome         {
4875ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef");
4885ffb0c9bSToomas Soome             return kDNSServiceErr_BadParam;
4895ffb0c9bSToomas Soome         }
4905ffb0c9bSToomas Soome         if (!DNSServiceRefValid(*ref) || ((*ref)->op != connection_request && (*ref)->op != connection_delegate_request) || (*ref)->primary)
4915ffb0c9bSToomas Soome         {
4925ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X op %d",
4935ffb0c9bSToomas Soome                    (*ref), (*ref)->sockfd, (*ref)->validator, (*ref)->op);
4945ffb0c9bSToomas Soome             *ref = NULL;
4955ffb0c9bSToomas Soome             return kDNSServiceErr_BadReference;
4965ffb0c9bSToomas Soome         }
4975ffb0c9bSToomas Soome     }
4984b22b933Srs 
4995ffb0c9bSToomas Soome     #if defined(_WIN32)
5005ffb0c9bSToomas Soome     if (!g_initWinsock)
5015ffb0c9bSToomas Soome     {
5025ffb0c9bSToomas Soome         WSADATA wsaData;
5035ffb0c9bSToomas Soome         g_initWinsock = 1;
5045ffb0c9bSToomas Soome         if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; }
5055ffb0c9bSToomas Soome     }
5065ffb0c9bSToomas Soome     // <rdar://problem/4096913> If the system service is disabled, we only want to try to connect once
507c65ebfc7SToomas Soome     if (IsSystemServiceDisabled())
5085ffb0c9bSToomas Soome         NumTries = DNSSD_CLIENT_MAXTRIES;
5095ffb0c9bSToomas Soome     #endif
5105ffb0c9bSToomas Soome 
5115ffb0c9bSToomas Soome     sdr = malloc(sizeof(DNSServiceOp));
512c65ebfc7SToomas Soome     if (!sdr)
513c65ebfc7SToomas Soome     {
514c65ebfc7SToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed");
515c65ebfc7SToomas Soome         *ref = NULL;
516c65ebfc7SToomas Soome         return kDNSServiceErr_NoMemory;
5175ffb0c9bSToomas Soome     }
5185ffb0c9bSToomas Soome     sdr->next          = NULL;
5195ffb0c9bSToomas Soome     sdr->primary       = NULL;
5205ffb0c9bSToomas Soome     sdr->sockfd        = dnssd_InvalidSocket;
5215ffb0c9bSToomas Soome     sdr->validator     = sdr->sockfd ^ ValidatorBits;
5225ffb0c9bSToomas Soome     sdr->op            = op;
5235ffb0c9bSToomas Soome     sdr->max_index     = 0;
5245ffb0c9bSToomas Soome     sdr->logcounter    = 0;
5255ffb0c9bSToomas Soome     sdr->moreptr       = NULL;
5265ffb0c9bSToomas Soome     sdr->uid.u32[0]    = 0;
5275ffb0c9bSToomas Soome     sdr->uid.u32[1]    = 0;
5285ffb0c9bSToomas Soome     sdr->ProcessReply  = ProcessReply;
5295ffb0c9bSToomas Soome     sdr->AppCallback   = AppCallback;
5305ffb0c9bSToomas Soome     sdr->AppContext    = AppContext;
5315ffb0c9bSToomas Soome     sdr->rec           = NULL;
5325ffb0c9bSToomas Soome #if _DNS_SD_LIBDISPATCH
5335ffb0c9bSToomas Soome     sdr->disp_source   = NULL;
5345ffb0c9bSToomas Soome     sdr->disp_queue    = NULL;
5354b22b933Srs #endif
5365ffb0c9bSToomas Soome     sdr->kacontext     = NULL;
5374b22b933Srs 
5385ffb0c9bSToomas Soome     if (flags & kDNSServiceFlagsShareConnection)
5394b22b933Srs     {
5405ffb0c9bSToomas Soome         DNSServiceOp **p = &(*ref)->next;       // Append ourselves to end of primary's list
541c65ebfc7SToomas Soome         while (*p)
5425ffb0c9bSToomas Soome             p = &(*p)->next;
5435ffb0c9bSToomas Soome         *p = sdr;
5445ffb0c9bSToomas Soome         // Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear
545c65ebfc7SToomas Soome         if (++(*ref)->uid.u32[0] == 0)
5465ffb0c9bSToomas Soome             ++(*ref)->uid.u32[1];               // In parent DNSServiceOp increment UID counter
5475ffb0c9bSToomas Soome         sdr->primary    = *ref;                 // Set our primary pointer
5485ffb0c9bSToomas Soome         sdr->sockfd     = (*ref)->sockfd;       // Inherit primary's socket
5495ffb0c9bSToomas Soome         sdr->validator  = (*ref)->validator;
5505ffb0c9bSToomas Soome         sdr->uid        = (*ref)->uid;
5515ffb0c9bSToomas Soome         //printf("ConnectToServer sharing socket %d\n", sdr->sockfd);
5525ffb0c9bSToomas Soome     }
5535ffb0c9bSToomas Soome     else
5545ffb0c9bSToomas Soome     {
5555ffb0c9bSToomas Soome         #ifdef SO_NOSIGPIPE
5565ffb0c9bSToomas Soome         const unsigned long optval = 1;
5575ffb0c9bSToomas Soome         #endif
5585ffb0c9bSToomas Soome         #ifndef USE_TCP_LOOPBACK
5595ffb0c9bSToomas Soome         char* uds_serverpath = getenv(MDNS_UDS_SERVERPATH_ENVVAR);
5605ffb0c9bSToomas Soome         if (uds_serverpath == NULL)
5615ffb0c9bSToomas Soome             uds_serverpath = MDNS_UDS_SERVERPATH;
562cda73f64SToomas Soome         else if (strlen(uds_serverpath) >= MAX_CTLPATH)
563cda73f64SToomas Soome         {
564cda73f64SToomas Soome             uds_serverpath = MDNS_UDS_SERVERPATH;
565cda73f64SToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: using default path since env len is invalid");
566cda73f64SToomas Soome         }
5675ffb0c9bSToomas Soome         #endif
5685ffb0c9bSToomas Soome         *ref = NULL;
5695ffb0c9bSToomas Soome         sdr->sockfd    = socket(AF_DNSSD, SOCK_STREAM, 0);
5705ffb0c9bSToomas Soome         sdr->validator = sdr->sockfd ^ ValidatorBits;
5715ffb0c9bSToomas Soome         if (!dnssd_SocketValid(sdr->sockfd))
5725ffb0c9bSToomas Soome         {
5735ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: socket failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
5745ffb0c9bSToomas Soome             FreeDNSServiceOp(sdr);
5755ffb0c9bSToomas Soome             return kDNSServiceErr_NoMemory;
5765ffb0c9bSToomas Soome         }
5775ffb0c9bSToomas Soome         #ifdef SO_NOSIGPIPE
5785ffb0c9bSToomas Soome         // Some environments (e.g. OS X) support turning off SIGPIPE for a socket
5795ffb0c9bSToomas Soome         if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
5805ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_NOSIGPIPE failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
5815ffb0c9bSToomas Soome         #endif
5825ffb0c9bSToomas Soome         #if defined(USE_TCP_LOOPBACK)
5835ffb0c9bSToomas Soome         saddr.sin_family      = AF_INET;
5845ffb0c9bSToomas Soome         saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
5855ffb0c9bSToomas Soome         saddr.sin_port        = htons(MDNS_TCP_SERVERPORT);
5865ffb0c9bSToomas Soome         #else
5875ffb0c9bSToomas Soome         saddr.sun_family      = AF_LOCAL;
588c65ebfc7SToomas Soome         SetUDSPath(&saddr, uds_serverpath);
5895ffb0c9bSToomas Soome         #if !defined(__ppc__) && defined(SO_DEFUNCTOK)
5905ffb0c9bSToomas Soome         {
5915ffb0c9bSToomas Soome             int defunct = 1;
5925ffb0c9bSToomas Soome             if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0)
5935ffb0c9bSToomas Soome                 syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
5945ffb0c9bSToomas Soome         }
5955ffb0c9bSToomas Soome         #endif
5965ffb0c9bSToomas Soome         #endif
597c65ebfc7SToomas Soome 
5985ffb0c9bSToomas Soome         while (1)
5995ffb0c9bSToomas Soome         {
6005ffb0c9bSToomas Soome             int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
6015ffb0c9bSToomas Soome             if (!err)
6025ffb0c9bSToomas Soome                 break; // If we succeeded, return sdr
6035ffb0c9bSToomas Soome             // If we failed, then it may be because the daemon is still launching.
6045ffb0c9bSToomas Soome             // This can happen for processes that launch early in the boot process, while the
6055ffb0c9bSToomas Soome             // daemon is still coming up. Rather than fail here, we wait 1 sec and try again.
6065ffb0c9bSToomas Soome             // If, after DNSSD_CLIENT_MAXTRIES, we still can't connect to the daemon,
6075ffb0c9bSToomas Soome             // then we give up and return a failure code.
6085ffb0c9bSToomas Soome             if (++NumTries < DNSSD_CLIENT_MAXTRIES)
6095ffb0c9bSToomas Soome             {
610c65ebfc7SToomas Soome                 syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect()-> No of tries: %d", NumTries);
6115ffb0c9bSToomas Soome                 sleep(1); // Sleep a bit, then try again
6125ffb0c9bSToomas Soome             }
613c65ebfc7SToomas Soome             else
6145ffb0c9bSToomas Soome             {
615cda73f64SToomas Soome                 #if !defined(USE_TCP_LOOPBACK)
616c65ebfc7SToomas Soome                 syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed path:%s Socket:%d Err:%d Errno:%d %s",
6175ffb0c9bSToomas Soome                        uds_serverpath, sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno));
618cda73f64SToomas Soome                 #endif
619c65ebfc7SToomas Soome                 dnssd_close(sdr->sockfd);
620c65ebfc7SToomas Soome                 FreeDNSServiceOp(sdr);
621c65ebfc7SToomas Soome                 return kDNSServiceErr_ServiceNotRunning;
6225ffb0c9bSToomas Soome             }
6235ffb0c9bSToomas Soome         }
6245ffb0c9bSToomas Soome         //printf("ConnectToServer opened socket %d\n", sdr->sockfd);
6255ffb0c9bSToomas Soome     }
6265ffb0c9bSToomas Soome 
6275ffb0c9bSToomas Soome     *ref = sdr;
6285ffb0c9bSToomas Soome     return kDNSServiceErr_NoError;
6295ffb0c9bSToomas Soome }
6305ffb0c9bSToomas Soome 
6315ffb0c9bSToomas Soome #define deliver_request_bailout(MSG) \
632*3b436d06SToomas Soome     syslog(LOG_WARNING, "dnssd_clientstub deliver_request: %s failed %d (%s)", (MSG), dnssd_errno, dnssd_strerror(dnssd_errno)); goto cleanup
6335ffb0c9bSToomas Soome 
6345ffb0c9bSToomas Soome static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
6355ffb0c9bSToomas Soome {
636c65ebfc7SToomas Soome     uint32_t datalen;
637c65ebfc7SToomas Soome     dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket;
638c65ebfc7SToomas Soome     DNSServiceErrorType err = kDNSServiceErr_Unknown;   // Default for the "goto cleanup" cases
639c65ebfc7SToomas Soome     int MakeSeparateReturnSocket;
640c65ebfc7SToomas Soome     #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
641c65ebfc7SToomas Soome     char *data;
642c65ebfc7SToomas Soome     #endif
643c65ebfc7SToomas Soome 
644cda73f64SToomas Soome     if (!hdr)
645cda73f64SToomas Soome     {
646cda73f64SToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr");
647cda73f64SToomas Soome         return kDNSServiceErr_Unknown;
648cda73f64SToomas Soome     }
649cda73f64SToomas Soome 
650c65ebfc7SToomas Soome     datalen = hdr->datalen;    // We take a copy here because we're going to convert hdr->datalen to network byte order
6515ffb0c9bSToomas Soome     #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
652c65ebfc7SToomas Soome     data = (char *)hdr + sizeof(ipc_msg_hdr);
6535ffb0c9bSToomas Soome     #endif
6545ffb0c9bSToomas Soome 
6555ffb0c9bSToomas Soome     // Note: need to check hdr->op, not sdr->op.
6565ffb0c9bSToomas Soome     // hdr->op contains the code for the specific operation we're currently doing, whereas sdr->op
6575ffb0c9bSToomas Soome     // contains the original parent DNSServiceOp (e.g. for an add_record_request, hdr->op will be
6585ffb0c9bSToomas Soome     // add_record_request but the parent sdr->op will be connection_request or reg_service_request)
659c65ebfc7SToomas Soome     MakeSeparateReturnSocket = (sdr->primary ||
660cda73f64SToomas Soome         hdr->op == reg_record_request || hdr->op == add_record_request || hdr->op == update_record_request || hdr->op == remove_record_request);
6615ffb0c9bSToomas Soome 
6625ffb0c9bSToomas Soome     if (!DNSServiceRefValid(sdr))
6635ffb0c9bSToomas Soome     {
6645ffb0c9bSToomas Soome         if (hdr)
6655ffb0c9bSToomas Soome             free(hdr);
6665ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator);
6675ffb0c9bSToomas Soome         return kDNSServiceErr_BadReference;
6685ffb0c9bSToomas Soome     }
6694b22b933Srs 
6705ffb0c9bSToomas Soome     if (MakeSeparateReturnSocket)
6715ffb0c9bSToomas Soome     {
6725ffb0c9bSToomas Soome         #if defined(USE_TCP_LOOPBACK)
6735ffb0c9bSToomas Soome         {
6745ffb0c9bSToomas Soome             union { uint16_t s; u_char b[2]; } port;
6755ffb0c9bSToomas Soome             dnssd_sockaddr_t caddr;
6765ffb0c9bSToomas Soome             dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr);
6775ffb0c9bSToomas Soome             listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
678*3b436d06SToomas Soome             if (!dnssd_SocketValid(listenfd)) {
679*3b436d06SToomas Soome 		deliver_request_bailout("TCP socket");
680*3b436d06SToomas Soome 	    }
6815ffb0c9bSToomas Soome 
6825ffb0c9bSToomas Soome             caddr.sin_family      = AF_INET;
6835ffb0c9bSToomas Soome             caddr.sin_port        = 0;
6845ffb0c9bSToomas Soome             caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
685*3b436d06SToomas Soome             if (bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)) < 0) {
686*3b436d06SToomas Soome 		deliver_request_bailout("TCP bind");
687*3b436d06SToomas Soome 	    }
688*3b436d06SToomas Soome             if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) {
689*3b436d06SToomas Soome 		deliver_request_bailout("TCP getsockname");
690*3b436d06SToomas Soome 	    }
691*3b436d06SToomas Soome             if (listen(listenfd, 1) < 0) {
692*3b436d06SToomas Soome 		deliver_request_bailout("TCP listen");
693*3b436d06SToomas Soome 	    }
6945ffb0c9bSToomas Soome             port.s = caddr.sin_port;
6955ffb0c9bSToomas Soome             data[0] = port.b[0];  // don't switch the byte order, as the
6965ffb0c9bSToomas Soome             data[1] = port.b[1];  // daemon expects it in network byte order
6975ffb0c9bSToomas Soome         }
6985ffb0c9bSToomas Soome         #elif defined(USE_NAMED_ERROR_RETURN_SOCKET)
6995ffb0c9bSToomas Soome         {
7005ffb0c9bSToomas Soome             mode_t mask;
7015ffb0c9bSToomas Soome             int bindresult;
7025ffb0c9bSToomas Soome             dnssd_sockaddr_t caddr;
7035ffb0c9bSToomas Soome             listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
704*3b436d06SToomas Soome             if (!dnssd_SocketValid(listenfd)) {
705*3b436d06SToomas Soome 		deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET socket");
706*3b436d06SToomas Soome 	    }
7075ffb0c9bSToomas Soome 
7085ffb0c9bSToomas Soome             caddr.sun_family = AF_LOCAL;
7095ffb0c9bSToomas Soome             // According to Stevens (section 3.2), there is no portable way to
7105ffb0c9bSToomas Soome             // determine whether sa_len is defined on a particular platform.
7115ffb0c9bSToomas Soome             #ifndef NOT_HAVE_SA_LEN
7125ffb0c9bSToomas Soome             caddr.sun_len = sizeof(struct sockaddr_un);
7135ffb0c9bSToomas Soome             #endif
714c65ebfc7SToomas Soome             SetUDSPath(&caddr, data);
7155ffb0c9bSToomas Soome             mask = umask(0);
7165ffb0c9bSToomas Soome             bindresult = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr));
7175ffb0c9bSToomas Soome             umask(mask);
718*3b436d06SToomas Soome             if (bindresult < 0) {
719*3b436d06SToomas Soome 		deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET bind");
720*3b436d06SToomas Soome 	    }
721*3b436d06SToomas Soome             if (listen(listenfd, 1) < 0) {
722*3b436d06SToomas Soome 		deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET listen");
723*3b436d06SToomas Soome 	    }
7245ffb0c9bSToomas Soome         }
7255ffb0c9bSToomas Soome         #else
7265ffb0c9bSToomas Soome         {
7275ffb0c9bSToomas Soome             dnssd_sock_t sp[2];
728*3b436d06SToomas Soome             if (socketpair(AF_DNSSD, SOCK_STREAM, 0, sp) < 0) {
729*3b436d06SToomas Soome 		deliver_request_bailout("socketpair");
730*3b436d06SToomas Soome 	    }
7315ffb0c9bSToomas Soome             else
7325ffb0c9bSToomas Soome             {
7335ffb0c9bSToomas Soome                 errsd    = sp[0];   // We'll read our four-byte error code from sp[0]
7345ffb0c9bSToomas Soome                 listenfd = sp[1];   // We'll send sp[1] to the daemon
7355ffb0c9bSToomas Soome                 #if !defined(__ppc__) && defined(SO_DEFUNCTOK)
7365ffb0c9bSToomas Soome                 {
7375ffb0c9bSToomas Soome                     int defunct = 1;
7385ffb0c9bSToomas Soome                     if (setsockopt(errsd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0)
7395ffb0c9bSToomas Soome                         syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
7405ffb0c9bSToomas Soome                 }
7415ffb0c9bSToomas Soome                 #endif
7425ffb0c9bSToomas Soome             }
7435ffb0c9bSToomas Soome         }
7445ffb0c9bSToomas Soome         #endif
7455ffb0c9bSToomas Soome     }
7464b22b933Srs 
7475ffb0c9bSToomas Soome #if !defined(USE_TCP_LOOPBACK) && !defined(USE_NAMED_ERROR_RETURN_SOCKET)
7485ffb0c9bSToomas Soome     // If we're going to make a separate error return socket, and pass it to the daemon
7495ffb0c9bSToomas Soome     // using sendmsg, then we'll hold back one data byte to go with it.
7505ffb0c9bSToomas Soome     // On some versions of Unix (including Leopard) sending a control message without
7515ffb0c9bSToomas Soome     // any associated data does not work reliably -- e.g. one particular issue we ran
7525ffb0c9bSToomas Soome     // into is that if the receiving program is in a kqueue loop waiting to be notified
7535ffb0c9bSToomas Soome     // of the received message, it doesn't get woken up when the control message arrives.
754c65ebfc7SToomas Soome     if (MakeSeparateReturnSocket || sdr->op == send_bpf)
7555ffb0c9bSToomas Soome         datalen--;     // Okay to use sdr->op when checking for op == send_bpf
7565ffb0c9bSToomas Soome #endif
7575ffb0c9bSToomas Soome 
7585ffb0c9bSToomas Soome     // At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to
7595ffb0c9bSToomas Soome     ConvertHeaderBytes(hdr);
7605ffb0c9bSToomas Soome     //syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %lu bytes", (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
7615ffb0c9bSToomas Soome     //if (MakeSeparateReturnSocket) syslog(LOG_WARNING, "dnssd_clientstub deliver_request name is %s", data);
7625ffb0c9bSToomas Soome #if TEST_SENDING_ONE_BYTE_AT_A_TIME
7635ffb0c9bSToomas Soome     unsigned int i;
7645ffb0c9bSToomas Soome     for (i=0; i<datalen + sizeof(ipc_msg_hdr); i++)
7655ffb0c9bSToomas Soome     {
7665ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %d", i);
7675ffb0c9bSToomas Soome         if (write_all(sdr->sockfd, ((char *)hdr)+i, 1) < 0)
7685ffb0c9bSToomas Soome         { syslog(LOG_WARNING, "write_all (byte %u) failed", i); goto cleanup; }
7695ffb0c9bSToomas Soome         usleep(10000);
7705ffb0c9bSToomas Soome     }
7714b22b933Srs #else
7725ffb0c9bSToomas Soome     if (write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)) < 0)
7735ffb0c9bSToomas Soome     {
7745ffb0c9bSToomas Soome         // write_all already prints an error message if there is an error writing to
7755ffb0c9bSToomas Soome         // the socket except for DEFUNCT. Logging here is unnecessary and also wrong
7765ffb0c9bSToomas Soome         // in the case of DEFUNCT sockets
7775ffb0c9bSToomas Soome         syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed",
7785ffb0c9bSToomas Soome                sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
7795ffb0c9bSToomas Soome         goto cleanup;
7805ffb0c9bSToomas Soome     }
7814b22b933Srs #endif
7825ffb0c9bSToomas Soome 
783c65ebfc7SToomas Soome     if (!MakeSeparateReturnSocket)
7845ffb0c9bSToomas Soome         errsd = sdr->sockfd;
7855ffb0c9bSToomas Soome     if (MakeSeparateReturnSocket || sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
7865ffb0c9bSToomas Soome     {
7875ffb0c9bSToomas Soome #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
7885ffb0c9bSToomas Soome         // At this point we may wait in accept for a few milliseconds waiting for the daemon to connect back to us,
7895ffb0c9bSToomas Soome         // but that's okay -- the daemon should not take more than a few milliseconds to respond.
7905ffb0c9bSToomas Soome         // set_waitlimit() ensures we do not block indefinitely just in case something is wrong
7915ffb0c9bSToomas Soome         dnssd_sockaddr_t daddr;
7925ffb0c9bSToomas Soome         dnssd_socklen_t len = sizeof(daddr);
793c65ebfc7SToomas Soome         if ((err = set_waitlimit(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError)
7945ffb0c9bSToomas Soome             goto cleanup;
7955ffb0c9bSToomas Soome         errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
796*3b436d06SToomas Soome         if (!dnssd_SocketValid(errsd)) {
7975ffb0c9bSToomas Soome             deliver_request_bailout("accept");
798*3b436d06SToomas Soome 	}
7995ffb0c9bSToomas Soome #else
8005ffb0c9bSToomas Soome 
8015ffb0c9bSToomas Soome         struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS
8025ffb0c9bSToomas Soome         struct msghdr msg;
8035ffb0c9bSToomas Soome         struct cmsghdr *cmsg;
8045ffb0c9bSToomas Soome         char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))];
8055ffb0c9bSToomas Soome 
8065ffb0c9bSToomas Soome         msg.msg_name       = 0;
8075ffb0c9bSToomas Soome         msg.msg_namelen    = 0;
8085ffb0c9bSToomas Soome         msg.msg_iov        = &vec;
8095ffb0c9bSToomas Soome         msg.msg_iovlen     = 1;
8105ffb0c9bSToomas Soome         msg.msg_flags      = 0;
8115ffb0c9bSToomas Soome         if (MakeSeparateReturnSocket || sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
8125ffb0c9bSToomas Soome         {
8135ffb0c9bSToomas Soome             if (sdr->op == send_bpf)
8145ffb0c9bSToomas Soome             {
8155ffb0c9bSToomas Soome                 int i;
8165ffb0c9bSToomas Soome                 char p[12];     // Room for "/dev/bpf999" with terminating null
8175ffb0c9bSToomas Soome                 for (i=0; i<100; i++)
8185ffb0c9bSToomas Soome                 {
8195ffb0c9bSToomas Soome                     snprintf(p, sizeof(p), "/dev/bpf%d", i);
8205ffb0c9bSToomas Soome                     listenfd = open(p, O_RDWR, 0);
8215ffb0c9bSToomas Soome                     //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p);
8225ffb0c9bSToomas Soome                     if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY)
8235ffb0c9bSToomas Soome                         syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno));
8245ffb0c9bSToomas Soome                     if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break;
8255ffb0c9bSToomas Soome                 }
8265ffb0c9bSToomas Soome             }
8275ffb0c9bSToomas Soome             msg.msg_control    = cbuf;
8285ffb0c9bSToomas Soome             msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t));
8295ffb0c9bSToomas Soome 
8305ffb0c9bSToomas Soome             cmsg = CMSG_FIRSTHDR(&msg);
8315ffb0c9bSToomas Soome             cmsg->cmsg_len     = CMSG_LEN(sizeof(dnssd_sock_t));
8325ffb0c9bSToomas Soome             cmsg->cmsg_level   = SOL_SOCKET;
8335ffb0c9bSToomas Soome             cmsg->cmsg_type    = SCM_RIGHTS;
8345ffb0c9bSToomas Soome             *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd;
8355ffb0c9bSToomas Soome         }
8365ffb0c9bSToomas Soome 
8375ffb0c9bSToomas Soome #if TEST_KQUEUE_CONTROL_MESSAGE_BUG
8385ffb0c9bSToomas Soome         sleep(1);
8394b22b933Srs #endif
8404b22b933Srs 
8415ffb0c9bSToomas Soome #if DEBUG_64BIT_SCM_RIGHTS
8425ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d %ld %ld %ld/%ld/%ld/%ld",
8435ffb0c9bSToomas Soome                errsd, listenfd, sizeof(dnssd_sock_t), sizeof(void*),
8445ffb0c9bSToomas Soome                sizeof(struct cmsghdr) + sizeof(dnssd_sock_t),
8455ffb0c9bSToomas Soome                CMSG_LEN(sizeof(dnssd_sock_t)), (long)CMSG_SPACE(sizeof(dnssd_sock_t)),
8465ffb0c9bSToomas Soome                (long)((char*)CMSG_DATA(cmsg) + 4 - cbuf));
8475ffb0c9bSToomas Soome #endif // DEBUG_64BIT_SCM_RIGHTS
8484b22b933Srs 
8495ffb0c9bSToomas Soome         if (sendmsg(sdr->sockfd, &msg, 0) < 0)
8504b22b933Srs         {
8515ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub deliver_request ERROR: sendmsg failed read sd=%d write sd=%d errno %d (%s)",
8525ffb0c9bSToomas Soome                    errsd, listenfd, dnssd_errno, dnssd_strerror(dnssd_errno));
8535ffb0c9bSToomas Soome             err = kDNSServiceErr_Incompatible;
8545ffb0c9bSToomas Soome             goto cleanup;
8554b22b933Srs         }
8564b22b933Srs 
8575ffb0c9bSToomas Soome #if DEBUG_64BIT_SCM_RIGHTS
8585ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d okay", errsd, listenfd);
8595ffb0c9bSToomas Soome #endif // DEBUG_64BIT_SCM_RIGHTS
8604b22b933Srs 
8615ffb0c9bSToomas Soome #endif
8625ffb0c9bSToomas Soome         // Close our end of the socketpair *before* calling read_all() to get the four-byte error code.
8635ffb0c9bSToomas Soome         // Otherwise, if the daemon closes our socket (or crashes), we will have to wait for a timeout
8645ffb0c9bSToomas Soome         // in read_all() because the socket is not closed (we still have an open reference to it)
8655ffb0c9bSToomas Soome         // Note: listenfd is overwritten in the case of send_bpf above and that will be closed here
8665ffb0c9bSToomas Soome         // for send_bpf operation.
8675ffb0c9bSToomas Soome         dnssd_close(listenfd);
8685ffb0c9bSToomas Soome         listenfd = dnssd_InvalidSocket; // Make sure we don't close it a second time in the cleanup handling below
8695ffb0c9bSToomas Soome     }
8705ffb0c9bSToomas Soome 
8715ffb0c9bSToomas Soome     // At this point we may wait in read_all for a few milliseconds waiting for the daemon to send us the error code,
8725ffb0c9bSToomas Soome     // but that's okay -- the daemon should not take more than a few milliseconds to respond.
8735ffb0c9bSToomas Soome     // set_waitlimit() ensures we do not block indefinitely just in case something is wrong
8745ffb0c9bSToomas Soome     if (sdr->op == send_bpf)    // Okay to use sdr->op when checking for op == send_bpf
8755ffb0c9bSToomas Soome         err = kDNSServiceErr_NoError;
8765ffb0c9bSToomas Soome     else if ((err = set_waitlimit(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError)
8775ffb0c9bSToomas Soome     {
8785ffb0c9bSToomas Soome         if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0)
8795ffb0c9bSToomas Soome             err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us
8805ffb0c9bSToomas Soome         else
8815ffb0c9bSToomas Soome             err = ntohl(err);
8825ffb0c9bSToomas Soome     }
8835ffb0c9bSToomas Soome     //syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err);
8844b22b933Srs 
8854b22b933Srs cleanup:
8865ffb0c9bSToomas Soome     if (MakeSeparateReturnSocket)
8875ffb0c9bSToomas Soome     {
8885ffb0c9bSToomas Soome         if (dnssd_SocketValid(listenfd)) dnssd_close(listenfd);
8895ffb0c9bSToomas Soome         if (dnssd_SocketValid(errsd)) dnssd_close(errsd);
8905ffb0c9bSToomas Soome #if defined(USE_NAMED_ERROR_RETURN_SOCKET)
8915ffb0c9bSToomas Soome         // syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removing UDS: %s", data);
8925ffb0c9bSToomas Soome         if (unlink(data) != 0)
8935ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub WARNING: unlink(\"%s\") failed errno %d (%s)", data, dnssd_errno, dnssd_strerror(dnssd_errno));
8945ffb0c9bSToomas Soome         // else syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removed UDS: %s", data);
8954b22b933Srs #endif
8964b22b933Srs     }
8974b22b933Srs 
8985ffb0c9bSToomas Soome     free(hdr);
8995ffb0c9bSToomas Soome     return err;
9005ffb0c9bSToomas Soome }
9015ffb0c9bSToomas Soome 
902c65ebfc7SToomas Soome dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef)
9035ffb0c9bSToomas Soome {
9045ffb0c9bSToomas Soome     if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with NULL DNSServiceRef"); return dnssd_InvalidSocket; }
9055ffb0c9bSToomas Soome 
9065ffb0c9bSToomas Soome     if (!DNSServiceRefValid(sdRef))
9074b22b933Srs     {
9085ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with invalid DNSServiceRef %p %08X %08X",
9095ffb0c9bSToomas Soome                sdRef, sdRef->sockfd, sdRef->validator);
9105ffb0c9bSToomas Soome         return dnssd_InvalidSocket;
9115ffb0c9bSToomas Soome     }
9125ffb0c9bSToomas Soome 
9135ffb0c9bSToomas Soome     if (sdRef->primary)
9145ffb0c9bSToomas Soome     {
9155ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef);
9165ffb0c9bSToomas Soome         return dnssd_InvalidSocket;
9175ffb0c9bSToomas Soome     }
9185ffb0c9bSToomas Soome 
919c65ebfc7SToomas Soome     return sdRef->sockfd;
9205ffb0c9bSToomas Soome }
9215ffb0c9bSToomas Soome 
9225ffb0c9bSToomas Soome #if _DNS_SD_LIBDISPATCH
9235ffb0c9bSToomas Soome static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error)
9245ffb0c9bSToomas Soome {
9255ffb0c9bSToomas Soome     DNSServiceOp *sdr = sdRef;
9265ffb0c9bSToomas Soome     DNSServiceOp *sdrNext;
9275ffb0c9bSToomas Soome     DNSRecord *rec;
9285ffb0c9bSToomas Soome     DNSRecord *recnext;
9295ffb0c9bSToomas Soome     int morebytes;
9305ffb0c9bSToomas Soome 
9315ffb0c9bSToomas Soome     while (sdr)
9325ffb0c9bSToomas Soome     {
9335ffb0c9bSToomas Soome         // We can't touch the sdr after the callback as it can be deallocated in the callback
9345ffb0c9bSToomas Soome         sdrNext = sdr->next;
9355ffb0c9bSToomas Soome         morebytes = 1;
9365ffb0c9bSToomas Soome         sdr->moreptr = &morebytes;
9375ffb0c9bSToomas Soome         switch (sdr->op)
9385ffb0c9bSToomas Soome         {
9395ffb0c9bSToomas Soome         case resolve_request:
9405ffb0c9bSToomas Soome             if (sdr->AppCallback) ((DNSServiceResolveReply)    sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL,    sdr->AppContext);
9415ffb0c9bSToomas Soome             break;
9425ffb0c9bSToomas Soome         case query_request:
9435ffb0c9bSToomas Soome             if (sdr->AppCallback) ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, 0, sdr->AppContext);
9445ffb0c9bSToomas Soome             break;
9455ffb0c9bSToomas Soome         case addrinfo_request:
9465ffb0c9bSToomas Soome             if (sdr->AppCallback) ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, NULL, 0,          sdr->AppContext);
9475ffb0c9bSToomas Soome             break;
9485ffb0c9bSToomas Soome         case browse_request:
9495ffb0c9bSToomas Soome             if (sdr->AppCallback) ((DNSServiceBrowseReply)     sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, NULL,          sdr->AppContext);
9505ffb0c9bSToomas Soome             break;
9515ffb0c9bSToomas Soome         case reg_service_request:
9525ffb0c9bSToomas Soome             if (sdr->AppCallback) ((DNSServiceRegisterReply)   sdr->AppCallback)(sdr, 0,    error, NULL, 0, NULL,          sdr->AppContext);
9535ffb0c9bSToomas Soome             break;
9545ffb0c9bSToomas Soome         case enumeration_request:
9555ffb0c9bSToomas Soome             if (sdr->AppCallback) ((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL,                   sdr->AppContext);
9565ffb0c9bSToomas Soome             break;
9575ffb0c9bSToomas Soome         case connection_request:
9585ffb0c9bSToomas Soome         case connection_delegate_request:
9595ffb0c9bSToomas Soome             // This means Register Record, walk the list of DNSRecords to do the callback
9605ffb0c9bSToomas Soome             rec = sdr->rec;
9615ffb0c9bSToomas Soome             while (rec)
9625ffb0c9bSToomas Soome             {
9635ffb0c9bSToomas Soome                 recnext = rec->recnext;
9645ffb0c9bSToomas Soome                 if (rec->AppCallback) ((DNSServiceRegisterRecordReply)rec->AppCallback)(sdr, 0, 0, error, rec->AppContext);
9655ffb0c9bSToomas Soome                 // The Callback can call DNSServiceRefDeallocate which in turn frees sdr and all the records.
9665ffb0c9bSToomas Soome                 // Detect that and return early
9675ffb0c9bSToomas Soome                 if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:Record: CallbackwithError morebytes zero"); return;}
9685ffb0c9bSToomas Soome                 rec = recnext;
9695ffb0c9bSToomas Soome             }
9705ffb0c9bSToomas Soome             break;
9715ffb0c9bSToomas Soome         case port_mapping_request:
9725ffb0c9bSToomas Soome             if (sdr->AppCallback) ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, 0, 0, error, 0, 0, 0, 0, 0, sdr->AppContext);
9735ffb0c9bSToomas Soome             break;
9745ffb0c9bSToomas Soome         default:
9755ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub CallbackWithError called with bad op %d", sdr->op);
9765ffb0c9bSToomas Soome         }
9775ffb0c9bSToomas Soome         // If DNSServiceRefDeallocate was called in the callback, morebytes will be zero. As the sdRef
9785ffb0c9bSToomas Soome         // (and its subordinates) have been freed, we should not proceed further. Note that when we
9795ffb0c9bSToomas Soome         // call the callback with a subordinate sdRef the application can call DNSServiceRefDeallocate
9805ffb0c9bSToomas Soome         // on the main sdRef and DNSServiceRefDeallocate handles this case by walking all the sdRefs and
9815ffb0c9bSToomas Soome         // clears the moreptr so that we can terminate here.
9825ffb0c9bSToomas Soome         //
9835ffb0c9bSToomas Soome         // If DNSServiceRefDeallocate was not called in the callback, then set moreptr to NULL so that
9845ffb0c9bSToomas Soome         // we don't access the stack variable after we return from this function.
9855ffb0c9bSToomas Soome         if (!morebytes) {syslog(LOG_WARNING, "dnssdclientstub:sdRef: CallbackwithError morebytes zero sdr %p", sdr); return;}
9865ffb0c9bSToomas Soome         else {sdr->moreptr = NULL;}
9875ffb0c9bSToomas Soome         sdr = sdrNext;
9884b22b933Srs     }
9895ffb0c9bSToomas Soome }
9905ffb0c9bSToomas Soome #endif // _DNS_SD_LIBDISPATCH
9914b22b933Srs 
9925ffb0c9bSToomas Soome // Handle reply from server, calling application client callback. If there is no reply
9934b22b933Srs // from the daemon on the socket contained in sdRef, the call will block.
9944b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef)
9955ffb0c9bSToomas Soome {
9965ffb0c9bSToomas Soome     int morebytes = 0;
9975ffb0c9bSToomas Soome 
9985ffb0c9bSToomas Soome     if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
9995ffb0c9bSToomas Soome 
10005ffb0c9bSToomas Soome     if (!DNSServiceRefValid(sdRef))
10014b22b933Srs     {
10025ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
10035ffb0c9bSToomas Soome         return kDNSServiceErr_BadReference;
10045ffb0c9bSToomas Soome     }
10054b22b933Srs 
10065ffb0c9bSToomas Soome     if (sdRef->primary)
10075ffb0c9bSToomas Soome     {
10085ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef);
10094b22b933Srs         return kDNSServiceErr_BadReference;
10105ffb0c9bSToomas Soome     }
10114b22b933Srs 
10125ffb0c9bSToomas Soome     if (!sdRef->ProcessReply)
10135ffb0c9bSToomas Soome     {
10145ffb0c9bSToomas Soome         static int num_logs = 0;
10155ffb0c9bSToomas Soome         if (num_logs < 10) syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function");
10165ffb0c9bSToomas Soome         if (num_logs < 1000) num_logs++;else sleep(1);
10175ffb0c9bSToomas Soome         return kDNSServiceErr_BadReference;
10184b22b933Srs     }
10194b22b933Srs 
10205ffb0c9bSToomas Soome     do
10215ffb0c9bSToomas Soome     {
10225ffb0c9bSToomas Soome         CallbackHeader cbh;
10235ffb0c9bSToomas Soome         char *data;
10245ffb0c9bSToomas Soome 
10255ffb0c9bSToomas Soome         // return NoError on EWOULDBLOCK. This will handle the case
10265ffb0c9bSToomas Soome         // where a non-blocking socket is told there is data, but it was a false positive.
10275ffb0c9bSToomas Soome         // On error, read_all will write a message to syslog for us, so don't need to duplicate that here
10285ffb0c9bSToomas Soome         // Note: If we want to properly support using non-blocking sockets in the future
10295ffb0c9bSToomas Soome         int result = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr));
10305ffb0c9bSToomas Soome         if (result == read_all_fail)
10315ffb0c9bSToomas Soome         {
10325ffb0c9bSToomas Soome             // Set the ProcessReply to NULL before callback as the sdRef can get deallocated
10335ffb0c9bSToomas Soome             // in the callback.
10345ffb0c9bSToomas Soome             sdRef->ProcessReply = NULL;
10355ffb0c9bSToomas Soome #if _DNS_SD_LIBDISPATCH
10365ffb0c9bSToomas Soome             // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult
10375ffb0c9bSToomas Soome             // is not called by the application and hence need to communicate the error. Cancel the
10385ffb0c9bSToomas Soome             // source so that we don't get any more events
10395ffb0c9bSToomas Soome             // Note: read_all fails if we could not read from the daemon which can happen if the
10405ffb0c9bSToomas Soome             // daemon dies or the file descriptor is disconnected (defunct).
10415ffb0c9bSToomas Soome             if (sdRef->disp_source)
10425ffb0c9bSToomas Soome             {
10435ffb0c9bSToomas Soome                 dispatch_source_cancel(sdRef->disp_source);
10445ffb0c9bSToomas Soome                 dispatch_release(sdRef->disp_source);
10455ffb0c9bSToomas Soome                 sdRef->disp_source = NULL;
10465ffb0c9bSToomas Soome                 CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning);
10475ffb0c9bSToomas Soome             }
10485ffb0c9bSToomas Soome #endif
10495ffb0c9bSToomas Soome             // Don't touch sdRef anymore as it might have been deallocated
10505ffb0c9bSToomas Soome             return kDNSServiceErr_ServiceNotRunning;
10515ffb0c9bSToomas Soome         }
10525ffb0c9bSToomas Soome         else if (result == read_all_wouldblock)
10535ffb0c9bSToomas Soome         {
10545ffb0c9bSToomas Soome             if (morebytes && sdRef->logcounter < 100)
10555ffb0c9bSToomas Soome             {
10565ffb0c9bSToomas Soome                 sdRef->logcounter++;
10575ffb0c9bSToomas Soome                 syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult error: select indicated data was waiting but read_all returned EWOULDBLOCK");
10585ffb0c9bSToomas Soome             }
10595ffb0c9bSToomas Soome             return kDNSServiceErr_NoError;
10605ffb0c9bSToomas Soome         }
10615ffb0c9bSToomas Soome 
10625ffb0c9bSToomas Soome         ConvertHeaderBytes(&cbh.ipc_hdr);
10635ffb0c9bSToomas Soome         if (cbh.ipc_hdr.version != VERSION)
10645ffb0c9bSToomas Soome         {
10655ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult daemon version %d does not match client version %d", cbh.ipc_hdr.version, VERSION);
10665ffb0c9bSToomas Soome             sdRef->ProcessReply = NULL;
10675ffb0c9bSToomas Soome             return kDNSServiceErr_Incompatible;
10685ffb0c9bSToomas Soome         }
10695ffb0c9bSToomas Soome 
10705ffb0c9bSToomas Soome         data = malloc(cbh.ipc_hdr.datalen);
10715ffb0c9bSToomas Soome         if (!data) return kDNSServiceErr_NoMemory;
10725ffb0c9bSToomas Soome         if (read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen) < 0) // On error, read_all will write a message to syslog for us
10735ffb0c9bSToomas Soome         {
10745ffb0c9bSToomas Soome             // Set the ProcessReply to NULL before callback as the sdRef can get deallocated
10755ffb0c9bSToomas Soome             // in the callback.
10765ffb0c9bSToomas Soome             sdRef->ProcessReply = NULL;
10775ffb0c9bSToomas Soome #if _DNS_SD_LIBDISPATCH
10785ffb0c9bSToomas Soome             // Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult
10795ffb0c9bSToomas Soome             // is not called by the application and hence need to communicate the error. Cancel the
10805ffb0c9bSToomas Soome             // source so that we don't get any more events
10815ffb0c9bSToomas Soome             if (sdRef->disp_source)
10825ffb0c9bSToomas Soome             {
10835ffb0c9bSToomas Soome                 dispatch_source_cancel(sdRef->disp_source);
10845ffb0c9bSToomas Soome                 dispatch_release(sdRef->disp_source);
10855ffb0c9bSToomas Soome                 sdRef->disp_source = NULL;
10865ffb0c9bSToomas Soome                 CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning);
10875ffb0c9bSToomas Soome             }
10885ffb0c9bSToomas Soome #endif
10895ffb0c9bSToomas Soome             // Don't touch sdRef anymore as it might have been deallocated
10905ffb0c9bSToomas Soome             free(data);
10915ffb0c9bSToomas Soome             return kDNSServiceErr_ServiceNotRunning;
10925ffb0c9bSToomas Soome         }
10935ffb0c9bSToomas Soome         else
10945ffb0c9bSToomas Soome         {
10955ffb0c9bSToomas Soome             const char *ptr = data;
10965ffb0c9bSToomas Soome             cbh.cb_flags     = get_flags     (&ptr, data + cbh.ipc_hdr.datalen);
10975ffb0c9bSToomas Soome             cbh.cb_interface = get_uint32    (&ptr, data + cbh.ipc_hdr.datalen);
10985ffb0c9bSToomas Soome             cbh.cb_err       = get_error_code(&ptr, data + cbh.ipc_hdr.datalen);
10995ffb0c9bSToomas Soome 
11005ffb0c9bSToomas Soome             // CAUTION: We have to handle the case where the client calls DNSServiceRefDeallocate from within the callback function.
11015ffb0c9bSToomas Soome             // To do this we set moreptr to point to morebytes. If the client does call DNSServiceRefDeallocate(),
11025ffb0c9bSToomas Soome             // then that routine will clear morebytes for us, and cause us to exit our loop.
11035ffb0c9bSToomas Soome             morebytes = more_bytes(sdRef->sockfd);
11045ffb0c9bSToomas Soome             if (morebytes)
11055ffb0c9bSToomas Soome             {
11065ffb0c9bSToomas Soome                 cbh.cb_flags |= kDNSServiceFlagsMoreComing;
11075ffb0c9bSToomas Soome                 sdRef->moreptr = &morebytes;
11085ffb0c9bSToomas Soome             }
11095ffb0c9bSToomas Soome             if (ptr) sdRef->ProcessReply(sdRef, &cbh, ptr, data + cbh.ipc_hdr.datalen);
11105ffb0c9bSToomas Soome             // Careful code here:
11115ffb0c9bSToomas Soome             // If morebytes is non-zero, that means we set sdRef->moreptr above, and the operation was not
11125ffb0c9bSToomas Soome             // cancelled out from under us, so now we need to clear sdRef->moreptr so we don't leave a stray
11135ffb0c9bSToomas Soome             // dangling pointer pointing to a long-gone stack variable.
11145ffb0c9bSToomas Soome             // If morebytes is zero, then one of two thing happened:
11155ffb0c9bSToomas Soome             // (a) morebytes was 0 above, so we didn't set sdRef->moreptr, so we don't need to clear it
11165ffb0c9bSToomas Soome             // (b) morebytes was 1 above, and we set sdRef->moreptr, but the operation was cancelled (with DNSServiceRefDeallocate()),
11175ffb0c9bSToomas Soome             //     so we MUST NOT try to dereference our stale sdRef pointer.
11185ffb0c9bSToomas Soome             if (morebytes) sdRef->moreptr = NULL;
11195ffb0c9bSToomas Soome         }
11205ffb0c9bSToomas Soome         free(data);
11215ffb0c9bSToomas Soome     } while (morebytes);
11225ffb0c9bSToomas Soome 
11235ffb0c9bSToomas Soome     return kDNSServiceErr_NoError;
11245ffb0c9bSToomas Soome }
11255ffb0c9bSToomas Soome 
11264b22b933Srs void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef)
11275ffb0c9bSToomas Soome {
11285ffb0c9bSToomas Soome     if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with NULL DNSServiceRef"); return; }
11295ffb0c9bSToomas Soome 
11305ffb0c9bSToomas Soome     if (!DNSServiceRefValid(sdRef))     // Also verifies dnssd_SocketValid(sdRef->sockfd) for us too
11315ffb0c9bSToomas Soome     {
11325ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
11335ffb0c9bSToomas Soome         return;
11345ffb0c9bSToomas Soome     }
11355ffb0c9bSToomas Soome 
11365ffb0c9bSToomas Soome     // 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
11375ffb0c9bSToomas Soome     if (sdRef->moreptr) *(sdRef->moreptr) = 0;
11385ffb0c9bSToomas Soome 
11395ffb0c9bSToomas Soome     if (sdRef->primary)     // If this is a subordinate DNSServiceOp, just send a 'stop' command
11405ffb0c9bSToomas Soome     {
11415ffb0c9bSToomas Soome         DNSServiceOp **p = &sdRef->primary->next;
11425ffb0c9bSToomas Soome         while (*p && *p != sdRef) p = &(*p)->next;
11435ffb0c9bSToomas Soome         if (*p)
11445ffb0c9bSToomas Soome         {
11455ffb0c9bSToomas Soome             char *ptr;
11465ffb0c9bSToomas Soome             size_t len = 0;
11475ffb0c9bSToomas Soome             ipc_msg_hdr *hdr = create_hdr(cancel_request, &len, &ptr, 0, sdRef);
11485ffb0c9bSToomas Soome             if (hdr)
11495ffb0c9bSToomas Soome             {
11505ffb0c9bSToomas Soome                 ConvertHeaderBytes(hdr);
11515ffb0c9bSToomas Soome                 write_all(sdRef->sockfd, (char *)hdr, len);
11525ffb0c9bSToomas Soome                 free(hdr);
11535ffb0c9bSToomas Soome             }
11545ffb0c9bSToomas Soome             *p = sdRef->next;
11555ffb0c9bSToomas Soome             FreeDNSServiceOp(sdRef);
11565ffb0c9bSToomas Soome         }
11575ffb0c9bSToomas Soome     }
11585ffb0c9bSToomas Soome     else                    // else, make sure to terminate all subordinates as well
11595ffb0c9bSToomas Soome     {
11605ffb0c9bSToomas Soome #if _DNS_SD_LIBDISPATCH
11615ffb0c9bSToomas Soome         // The cancel handler will close the fd if a dispatch source has been set
11625ffb0c9bSToomas Soome         if (sdRef->disp_source)
11635ffb0c9bSToomas Soome         {
11645ffb0c9bSToomas Soome             // By setting the ProcessReply to NULL, we make sure that we never call
11655ffb0c9bSToomas Soome             // the application callbacks ever, after returning from this function. We
11665ffb0c9bSToomas Soome             // assume that DNSServiceRefDeallocate is called from the serial queue
11675ffb0c9bSToomas Soome             // that was passed to DNSServiceSetDispatchQueue. Hence, dispatch_source_cancel
11685ffb0c9bSToomas Soome             // should cancel all the blocks on the queue and hence there should be no more
11695ffb0c9bSToomas Soome             // callbacks when we return from this function. Setting ProcessReply to NULL
11705ffb0c9bSToomas Soome             // provides extra protection.
11715ffb0c9bSToomas Soome             sdRef->ProcessReply = NULL;
11725ffb0c9bSToomas Soome             shutdown(sdRef->sockfd, SHUT_WR);
11735ffb0c9bSToomas Soome             dispatch_source_cancel(sdRef->disp_source);
11745ffb0c9bSToomas Soome             dispatch_release(sdRef->disp_source);
11755ffb0c9bSToomas Soome             sdRef->disp_source = NULL;
11765ffb0c9bSToomas Soome         }
11775ffb0c9bSToomas Soome         // if disp_queue is set, it means it used the DNSServiceSetDispatchQueue API. In that case,
11785ffb0c9bSToomas Soome         // when the source was cancelled, the fd was closed in the handler. Currently the source
11795ffb0c9bSToomas Soome         // is cancelled only when the mDNSResponder daemon dies
11805ffb0c9bSToomas Soome         else if (!sdRef->disp_queue) dnssd_close(sdRef->sockfd);
11815ffb0c9bSToomas Soome #else
11825ffb0c9bSToomas Soome         dnssd_close(sdRef->sockfd);
11835ffb0c9bSToomas Soome #endif
11845ffb0c9bSToomas Soome         // Free DNSRecords added in DNSRegisterRecord if they have not
11855ffb0c9bSToomas Soome         // been freed in DNSRemoveRecord
11865ffb0c9bSToomas Soome         while (sdRef)
11875ffb0c9bSToomas Soome         {
11885ffb0c9bSToomas Soome             DNSServiceOp *p = sdRef;
11895ffb0c9bSToomas Soome             sdRef = sdRef->next;
11905ffb0c9bSToomas Soome             // When there is an error reading from the daemon e.g., bad fd, CallbackWithError
11915ffb0c9bSToomas Soome             // is called which sets moreptr. It might set the moreptr on a subordinate sdRef
11925ffb0c9bSToomas Soome             // but the application might call DNSServiceRefDeallocate with the main sdRef from
11935ffb0c9bSToomas Soome             // the callback. Hence, when we loop through the subordinate sdRefs, we need
11945ffb0c9bSToomas Soome             // to clear the moreptr so that CallbackWithError can terminate itself instead of
11955ffb0c9bSToomas Soome             // walking through the freed sdRefs.
11965ffb0c9bSToomas Soome             if (p->moreptr) *(p->moreptr) = 0;
11975ffb0c9bSToomas Soome             FreeDNSServiceOp(p);
11985ffb0c9bSToomas Soome         }
11995ffb0c9bSToomas Soome     }
12005ffb0c9bSToomas Soome }
12015ffb0c9bSToomas Soome 
12025ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void *result, uint32_t *size)
12035ffb0c9bSToomas Soome {
1204c65ebfc7SToomas Soome     DNSServiceErrorType err;
12055ffb0c9bSToomas Soome     char *ptr;
1206c65ebfc7SToomas Soome     size_t len;
12075ffb0c9bSToomas Soome     ipc_msg_hdr *hdr;
12085ffb0c9bSToomas Soome     DNSServiceOp *tmp;
12095ffb0c9bSToomas Soome     uint32_t actualsize;
12105ffb0c9bSToomas Soome 
1211c65ebfc7SToomas Soome     if (!property || !result || !size)
1212c65ebfc7SToomas Soome         return kDNSServiceErr_BadParam;
1213c65ebfc7SToomas Soome 
1214c65ebfc7SToomas Soome     len = strlen(property) + 1;
1215c65ebfc7SToomas Soome     err = ConnectToServer(&tmp, 0, getproperty_request, NULL, NULL, NULL);
12165ffb0c9bSToomas Soome     if (err) return err;
12175ffb0c9bSToomas Soome 
12185ffb0c9bSToomas Soome     hdr = create_hdr(getproperty_request, &len, &ptr, 0, tmp);
12195ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
12205ffb0c9bSToomas Soome 
12215ffb0c9bSToomas Soome     put_string(property, &ptr);
12225ffb0c9bSToomas Soome     err = deliver_request(hdr, tmp);        // Will free hdr for us
1223cda73f64SToomas Soome     if (err) { DNSServiceRefDeallocate(tmp); return err; }
1224cda73f64SToomas Soome 
12255ffb0c9bSToomas Soome     if (read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize)) < 0)
12265ffb0c9bSToomas Soome     { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; }
12275ffb0c9bSToomas Soome 
12285ffb0c9bSToomas Soome     actualsize = ntohl(actualsize);
12295ffb0c9bSToomas Soome     if (read_all(tmp->sockfd, (char*)result, actualsize < *size ? actualsize : *size) < 0)
12305ffb0c9bSToomas Soome     { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; }
12315ffb0c9bSToomas Soome     DNSServiceRefDeallocate(tmp);
12325ffb0c9bSToomas Soome 
12335ffb0c9bSToomas Soome     // Swap version result back to local process byte order
12345ffb0c9bSToomas Soome     if (!strcmp(property, kDNSServiceProperty_DaemonVersion) && *size >= 4)
12355ffb0c9bSToomas Soome         *(uint32_t*)result = ntohl(*(uint32_t*)result);
12365ffb0c9bSToomas Soome 
12375ffb0c9bSToomas Soome     *size = actualsize;
12385ffb0c9bSToomas Soome     return kDNSServiceErr_NoError;
12395ffb0c9bSToomas Soome }
12405ffb0c9bSToomas Soome 
12415ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t *pid)
12425ffb0c9bSToomas Soome {
12435ffb0c9bSToomas Soome     char *ptr;
12445ffb0c9bSToomas Soome     ipc_msg_hdr *hdr;
1245c65ebfc7SToomas Soome     DNSServiceOp *tmp = NULL;
12465ffb0c9bSToomas Soome     size_t len = sizeof(int32_t);
12475ffb0c9bSToomas Soome 
12485ffb0c9bSToomas Soome     DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL);
1249cda73f64SToomas Soome     if (err) return err;
12505ffb0c9bSToomas Soome 
12515ffb0c9bSToomas Soome     hdr = create_hdr(getpid_request, &len, &ptr, 0, tmp);
1252cda73f64SToomas Soome     if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
12534b22b933Srs 
12545ffb0c9bSToomas Soome     put_uint16(srcport, &ptr);
12555ffb0c9bSToomas Soome     err = deliver_request(hdr, tmp);        // Will free hdr for us
1256cda73f64SToomas Soome     if (err) { DNSServiceRefDeallocate(tmp); return err; }
12575ffb0c9bSToomas Soome 
12585ffb0c9bSToomas Soome     if (read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)) < 0)
1259cda73f64SToomas Soome     { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; }
12605ffb0c9bSToomas Soome 
12615ffb0c9bSToomas Soome     DNSServiceRefDeallocate(tmp);
12625ffb0c9bSToomas Soome     return kDNSServiceErr_NoError;
12635ffb0c9bSToomas Soome }
12645ffb0c9bSToomas Soome 
12655ffb0c9bSToomas Soome static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end)
12665ffb0c9bSToomas Soome {
12674b22b933Srs     char fullname[kDNSServiceMaxDomainName];
12684b22b933Srs     char target[kDNSServiceMaxDomainName];
12694b22b933Srs     uint16_t txtlen;
12704b22b933Srs     union { uint16_t s; u_char b[2]; } port;
12714b22b933Srs     unsigned char *txtrecord;
12725ffb0c9bSToomas Soome 
12735ffb0c9bSToomas Soome     get_string(&data, end, fullname, kDNSServiceMaxDomainName);
12745ffb0c9bSToomas Soome     get_string(&data, end, target,   kDNSServiceMaxDomainName);
12755ffb0c9bSToomas Soome     if (!data || data + 2 > end) goto fail;
12765ffb0c9bSToomas Soome 
12774b22b933Srs     port.b[0] = *data++;
12784b22b933Srs     port.b[1] = *data++;
12795ffb0c9bSToomas Soome     txtlen = get_uint16(&data, end);
12805ffb0c9bSToomas Soome     txtrecord = (unsigned char *)get_rdata(&data, end, txtlen);
12814b22b933Srs 
12825ffb0c9bSToomas Soome     if (!data) goto fail;
12835ffb0c9bSToomas Soome     ((DNSServiceResolveReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, fullname, target, port.s, txtlen, txtrecord, sdr->AppContext);
12845ffb0c9bSToomas Soome     return;
12855ffb0c9bSToomas Soome     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
12865ffb0c9bSToomas Soome fail:
12875ffb0c9bSToomas Soome     syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon");
12885ffb0c9bSToomas Soome }
12895ffb0c9bSToomas Soome 
12905ffb0c9bSToomas Soome #if TARGET_OS_EMBEDDED
12915ffb0c9bSToomas Soome 
12925ffb0c9bSToomas Soome static int32_t libSystemVersion = 0;
12935ffb0c9bSToomas Soome 
12945ffb0c9bSToomas Soome // Return true if the iOS application linked against a version of libsystem where P2P
12955ffb0c9bSToomas Soome // interfaces were included by default when using kDNSServiceInterfaceIndexAny.
12965ffb0c9bSToomas Soome // Using 160.0.0 == 0xa00000 as the version threshold.
12975ffb0c9bSToomas Soome static int includeP2PWithIndexAny()
12985ffb0c9bSToomas Soome {
12995ffb0c9bSToomas Soome     if (libSystemVersion == 0)
13005ffb0c9bSToomas Soome         libSystemVersion = NSVersionOfLinkTimeLibrary("System");
13015ffb0c9bSToomas Soome 
13025ffb0c9bSToomas Soome     if (libSystemVersion < 0xa00000)
13035ffb0c9bSToomas Soome         return 1;
13045ffb0c9bSToomas Soome     else
13055ffb0c9bSToomas Soome         return 0;
13065ffb0c9bSToomas Soome }
13075ffb0c9bSToomas Soome 
13085ffb0c9bSToomas Soome #else   // TARGET_OS_EMBEDDED
13095ffb0c9bSToomas Soome 
13105ffb0c9bSToomas Soome // always return false for non iOS platforms
13115ffb0c9bSToomas Soome static int includeP2PWithIndexAny()
13125ffb0c9bSToomas Soome {
13135ffb0c9bSToomas Soome     return 0;
13145ffb0c9bSToomas Soome }
13155ffb0c9bSToomas Soome 
13165ffb0c9bSToomas Soome #endif  // TARGET_OS_EMBEDDED
13174b22b933Srs 
13184b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceResolve
13195ffb0c9bSToomas Soome (
13205ffb0c9bSToomas Soome     DNSServiceRef                 *sdRef,
13215ffb0c9bSToomas Soome     DNSServiceFlags flags,
13225ffb0c9bSToomas Soome     uint32_t interfaceIndex,
13235ffb0c9bSToomas Soome     const char                    *name,
13245ffb0c9bSToomas Soome     const char                    *regtype,
13255ffb0c9bSToomas Soome     const char                    *domain,
13265ffb0c9bSToomas Soome     DNSServiceResolveReply callBack,
13275ffb0c9bSToomas Soome     void                          *context
13285ffb0c9bSToomas Soome )
13295ffb0c9bSToomas Soome {
13305ffb0c9bSToomas Soome     char *ptr;
13314b22b933Srs     size_t len;
13324b22b933Srs     ipc_msg_hdr *hdr;
13334b22b933Srs     DNSServiceErrorType err;
13344b22b933Srs 
1335c65ebfc7SToomas Soome     if (!sdRef || !name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam;
13365ffb0c9bSToomas Soome 
13375ffb0c9bSToomas Soome     // Need a real InterfaceID for WakeOnResolve
13385ffb0c9bSToomas Soome     if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 &&
13395ffb0c9bSToomas Soome         ((interfaceIndex == kDNSServiceInterfaceIndexAny) ||
13405ffb0c9bSToomas Soome          (interfaceIndex == kDNSServiceInterfaceIndexLocalOnly) ||
13415ffb0c9bSToomas Soome          (interfaceIndex == kDNSServiceInterfaceIndexUnicast) ||
1342c65ebfc7SToomas Soome          (interfaceIndex == kDNSServiceInterfaceIndexP2P) ||
1343c65ebfc7SToomas Soome          (interfaceIndex == kDNSServiceInterfaceIndexBLE)))
13445ffb0c9bSToomas Soome     {
13455ffb0c9bSToomas Soome         return kDNSServiceErr_BadParam;
13465ffb0c9bSToomas Soome     }
13475ffb0c9bSToomas Soome 
13485ffb0c9bSToomas Soome     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
13495ffb0c9bSToomas Soome         flags |= kDNSServiceFlagsIncludeP2P;
13504b22b933Srs 
1351*3b436d06SToomas Soome     err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, (void *)callBack, context);
13525ffb0c9bSToomas Soome     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
13534b22b933Srs 
13545ffb0c9bSToomas Soome     // Calculate total message length
13554b22b933Srs     len = sizeof(flags);
13564b22b933Srs     len += sizeof(interfaceIndex);
13574b22b933Srs     len += strlen(name) + 1;
13584b22b933Srs     len += strlen(regtype) + 1;
13594b22b933Srs     len += strlen(domain) + 1;
13604b22b933Srs 
13615ffb0c9bSToomas Soome     hdr = create_hdr(resolve_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
13625ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
13634b22b933Srs 
13644b22b933Srs     put_flags(flags, &ptr);
13655ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
13664b22b933Srs     put_string(name, &ptr);
13674b22b933Srs     put_string(regtype, &ptr);
13684b22b933Srs     put_string(domain, &ptr);
13694b22b933Srs 
13705ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
13715ffb0c9bSToomas Soome     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
13724b22b933Srs     return err;
13735ffb0c9bSToomas Soome }
13744b22b933Srs 
13755ffb0c9bSToomas Soome static void handle_query_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
13765ffb0c9bSToomas Soome {
13775ffb0c9bSToomas Soome     uint32_t ttl;
13784b22b933Srs     char name[kDNSServiceMaxDomainName];
13794b22b933Srs     uint16_t rrtype, rrclass, rdlen;
13805ffb0c9bSToomas Soome     const char *rdata;
13815ffb0c9bSToomas Soome 
13825ffb0c9bSToomas Soome     get_string(&data, end, name, kDNSServiceMaxDomainName);
13835ffb0c9bSToomas Soome     rrtype  = get_uint16(&data, end);
13845ffb0c9bSToomas Soome     rrclass = get_uint16(&data, end);
13855ffb0c9bSToomas Soome     rdlen   = get_uint16(&data, end);
13865ffb0c9bSToomas Soome     rdata   = get_rdata(&data, end, rdlen);
13875ffb0c9bSToomas Soome     ttl     = get_uint32(&data, end);
13885ffb0c9bSToomas Soome 
13895ffb0c9bSToomas Soome     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_query_response: error reading result from daemon");
13905ffb0c9bSToomas Soome     else ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, name, rrtype, rrclass, rdlen, rdata, ttl, sdr->AppContext);
13915ffb0c9bSToomas Soome     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
13925ffb0c9bSToomas Soome }
13934b22b933Srs 
13944b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceQueryRecord
13955ffb0c9bSToomas Soome (
13964b22b933Srs     DNSServiceRef              *sdRef,
13975ffb0c9bSToomas Soome     DNSServiceFlags flags,
13985ffb0c9bSToomas Soome     uint32_t interfaceIndex,
13994b22b933Srs     const char                 *name,
14005ffb0c9bSToomas Soome     uint16_t rrtype,
14015ffb0c9bSToomas Soome     uint16_t rrclass,
14025ffb0c9bSToomas Soome     DNSServiceQueryRecordReply callBack,
14034b22b933Srs     void                       *context
14045ffb0c9bSToomas Soome )
14055ffb0c9bSToomas Soome {
14065ffb0c9bSToomas Soome     char *ptr;
14074b22b933Srs     size_t len;
14084b22b933Srs     ipc_msg_hdr *hdr;
14094b22b933Srs     DNSServiceErrorType err;
14104b22b933Srs 
1411c65ebfc7SToomas Soome     // NULL name handled below.
1412c65ebfc7SToomas Soome     if (!sdRef || !callBack) return kDNSServiceErr_BadParam;
1413c65ebfc7SToomas Soome 
14145ffb0c9bSToomas Soome     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
14155ffb0c9bSToomas Soome         flags |= kDNSServiceFlagsIncludeP2P;
14165ffb0c9bSToomas Soome 
1417*3b436d06SToomas Soome     err = ConnectToServer(sdRef, flags, query_request, handle_query_response, (void *)callBack, context);
14185ffb0c9bSToomas Soome     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
14194b22b933Srs 
14204b22b933Srs     if (!name) name = "\0";
14214b22b933Srs 
14225ffb0c9bSToomas Soome     // Calculate total message length
14234b22b933Srs     len = sizeof(flags);
14245ffb0c9bSToomas Soome     len += sizeof(uint32_t);  // interfaceIndex
14254b22b933Srs     len += strlen(name) + 1;
14264b22b933Srs     len += 2 * sizeof(uint16_t);  // rrtype, rrclass
14274b22b933Srs 
14285ffb0c9bSToomas Soome     hdr = create_hdr(query_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
14295ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
14304b22b933Srs 
14314b22b933Srs     put_flags(flags, &ptr);
14325ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
14334b22b933Srs     put_string(name, &ptr);
14345ffb0c9bSToomas Soome     put_uint16(rrtype, &ptr);
14355ffb0c9bSToomas Soome     put_uint16(rrclass, &ptr);
14364b22b933Srs 
14375ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
14385ffb0c9bSToomas Soome     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
14395ffb0c9bSToomas Soome     return err;
14405ffb0c9bSToomas Soome }
14415ffb0c9bSToomas Soome 
14425ffb0c9bSToomas Soome static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
14435ffb0c9bSToomas Soome {
14445ffb0c9bSToomas Soome     char hostname[kDNSServiceMaxDomainName];
1445cda73f64SToomas Soome     uint16_t rrtype, rrclass, rdlen;
14465ffb0c9bSToomas Soome     const char *rdata;
14475ffb0c9bSToomas Soome     uint32_t ttl;
14485ffb0c9bSToomas Soome 
14495ffb0c9bSToomas Soome     get_string(&data, end, hostname, kDNSServiceMaxDomainName);
14505ffb0c9bSToomas Soome     rrtype  = get_uint16(&data, end);
1451cda73f64SToomas Soome     rrclass = get_uint16(&data, end);
14525ffb0c9bSToomas Soome     rdlen   = get_uint16(&data, end);
14535ffb0c9bSToomas Soome     rdata   = get_rdata (&data, end, rdlen);
14545ffb0c9bSToomas Soome     ttl     = get_uint32(&data, end);
1455cda73f64SToomas Soome     (void)rrclass; // Unused
14565ffb0c9bSToomas Soome 
14575ffb0c9bSToomas Soome     // We only generate client callbacks for A and AAAA results (including NXDOMAIN results for
14585ffb0c9bSToomas Soome     // those types, if the client has requested those with the kDNSServiceFlagsReturnIntermediates).
14595ffb0c9bSToomas Soome     // Other result types, specifically CNAME referrals, are not communicated to the client, because
14605ffb0c9bSToomas Soome     // the DNSServiceGetAddrInfoReply interface doesn't have any meaningful way to communiate CNAME referrals.
14615ffb0c9bSToomas Soome     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_addrinfo_response: error reading result from daemon");
14625ffb0c9bSToomas Soome     else if (rrtype == kDNSServiceType_A || rrtype == kDNSServiceType_AAAA)
14635ffb0c9bSToomas Soome     {
14645ffb0c9bSToomas Soome         struct sockaddr_in sa4;
14655ffb0c9bSToomas Soome         struct sockaddr_in6 sa6;
14665ffb0c9bSToomas Soome         const struct sockaddr *const sa = (rrtype == kDNSServiceType_A) ? (struct sockaddr*)&sa4 : (struct sockaddr*)&sa6;
14675ffb0c9bSToomas Soome         if (rrtype == kDNSServiceType_A)
14684b22b933Srs         {
14695ffb0c9bSToomas Soome             memset(&sa4, 0, sizeof(sa4));
14705ffb0c9bSToomas Soome             #ifndef NOT_HAVE_SA_LEN
14715ffb0c9bSToomas Soome             sa4.sin_len = sizeof(struct sockaddr_in);
14725ffb0c9bSToomas Soome             #endif
14735ffb0c9bSToomas Soome             sa4.sin_family = AF_INET;
14745ffb0c9bSToomas Soome             //  sin_port   = 0;
14755ffb0c9bSToomas Soome             if (!cbh->cb_err) memcpy(&sa4.sin_addr, rdata, rdlen);
14765ffb0c9bSToomas Soome         }
14775ffb0c9bSToomas Soome         else
14785ffb0c9bSToomas Soome         {
14795ffb0c9bSToomas Soome             memset(&sa6, 0, sizeof(sa6));
14805ffb0c9bSToomas Soome             #ifndef NOT_HAVE_SA_LEN
14815ffb0c9bSToomas Soome             sa6.sin6_len = sizeof(struct sockaddr_in6);
14825ffb0c9bSToomas Soome             #endif
14835ffb0c9bSToomas Soome             sa6.sin6_family     = AF_INET6;
14845ffb0c9bSToomas Soome             //  sin6_port     = 0;
14855ffb0c9bSToomas Soome             //  sin6_flowinfo = 0;
14865ffb0c9bSToomas Soome             //  sin6_scope_id = 0;
14875ffb0c9bSToomas Soome             if (!cbh->cb_err)
14885ffb0c9bSToomas Soome             {
14895ffb0c9bSToomas Soome                 memcpy(&sa6.sin6_addr, rdata, rdlen);
14905ffb0c9bSToomas Soome                 if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface;
14915ffb0c9bSToomas Soome             }
14924b22b933Srs         }
14935ffb0c9bSToomas Soome         // Validation results are always delivered separately from the actual results of the
14945ffb0c9bSToomas Soome         // DNSServiceGetAddrInfo. Set the "addr" to NULL as per the documentation.
14955ffb0c9bSToomas Soome         //
14965ffb0c9bSToomas Soome         // Note: If we deliver validation results along with the "addr" in the future, we need
14975ffb0c9bSToomas Soome         // a way to differentiate the negative response from validation-only response as both
14985ffb0c9bSToomas Soome         // has zero address.
14995ffb0c9bSToomas Soome         if (!(cbh->cb_flags & kDNSServiceFlagsValidate))
15005ffb0c9bSToomas Soome             ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext);
15015ffb0c9bSToomas Soome         else
15025ffb0c9bSToomas Soome             ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, 0, sdr->AppContext);
15035ffb0c9bSToomas Soome         // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
15045ffb0c9bSToomas Soome     }
15055ffb0c9bSToomas Soome }
15064b22b933Srs 
15075ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
15085ffb0c9bSToomas Soome (
15095ffb0c9bSToomas Soome     DNSServiceRef                    *sdRef,
15105ffb0c9bSToomas Soome     DNSServiceFlags flags,
15115ffb0c9bSToomas Soome     uint32_t interfaceIndex,
15125ffb0c9bSToomas Soome     uint32_t protocol,
15135ffb0c9bSToomas Soome     const char                       *hostname,
15145ffb0c9bSToomas Soome     DNSServiceGetAddrInfoReply callBack,
15155ffb0c9bSToomas Soome     void                             *context          /* may be NULL */
15165ffb0c9bSToomas Soome )
15175ffb0c9bSToomas Soome {
15185ffb0c9bSToomas Soome     char *ptr;
15195ffb0c9bSToomas Soome     size_t len;
15205ffb0c9bSToomas Soome     ipc_msg_hdr *hdr;
15215ffb0c9bSToomas Soome     DNSServiceErrorType err;
15224b22b933Srs 
1523c65ebfc7SToomas Soome     if (!sdRef || !hostname || !callBack) return kDNSServiceErr_BadParam;
15244b22b933Srs 
1525*3b436d06SToomas Soome     err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, (void *)callBack, context);
15265ffb0c9bSToomas Soome     if (err)
15274b22b933Srs     {
15285ffb0c9bSToomas Soome          return err;    // On error ConnectToServer leaves *sdRef set to NULL
15294b22b933Srs     }
15304b22b933Srs 
15315ffb0c9bSToomas Soome     // Calculate total message length
15325ffb0c9bSToomas Soome     len = sizeof(flags);
15335ffb0c9bSToomas Soome     len += sizeof(uint32_t);      // interfaceIndex
15345ffb0c9bSToomas Soome     len += sizeof(uint32_t);      // protocol
15355ffb0c9bSToomas Soome     len += strlen(hostname) + 1;
15365ffb0c9bSToomas Soome 
15375ffb0c9bSToomas Soome     hdr = create_hdr(addrinfo_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
15385ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
15395ffb0c9bSToomas Soome 
15405ffb0c9bSToomas Soome     put_flags(flags, &ptr);
15415ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
15425ffb0c9bSToomas Soome     put_uint32(protocol, &ptr);
15435ffb0c9bSToomas Soome     put_string(hostname, &ptr);
15445ffb0c9bSToomas Soome 
15455ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
15465ffb0c9bSToomas Soome     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
15475ffb0c9bSToomas Soome     return err;
15485ffb0c9bSToomas Soome }
15495ffb0c9bSToomas Soome 
15505ffb0c9bSToomas Soome static void handle_browse_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
15515ffb0c9bSToomas Soome {
15525ffb0c9bSToomas Soome     char replyName[256], replyType[kDNSServiceMaxDomainName], replyDomain[kDNSServiceMaxDomainName];
15535ffb0c9bSToomas Soome     get_string(&data, end, replyName, 256);
15545ffb0c9bSToomas Soome     get_string(&data, end, replyType, kDNSServiceMaxDomainName);
15555ffb0c9bSToomas Soome     get_string(&data, end, replyDomain, kDNSServiceMaxDomainName);
15565ffb0c9bSToomas Soome     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_browse_response: error reading result from daemon");
15575ffb0c9bSToomas Soome     else ((DNSServiceBrowseReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, replyName, replyType, replyDomain, sdr->AppContext);
15585ffb0c9bSToomas Soome     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
15595ffb0c9bSToomas Soome }
15605ffb0c9bSToomas Soome 
15614b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceBrowse
15625ffb0c9bSToomas Soome (
15635ffb0c9bSToomas Soome     DNSServiceRef         *sdRef,
15645ffb0c9bSToomas Soome     DNSServiceFlags flags,
15655ffb0c9bSToomas Soome     uint32_t interfaceIndex,
15665ffb0c9bSToomas Soome     const char            *regtype,
15675ffb0c9bSToomas Soome     const char            *domain,
15685ffb0c9bSToomas Soome     DNSServiceBrowseReply callBack,
15695ffb0c9bSToomas Soome     void                  *context
15705ffb0c9bSToomas Soome )
15715ffb0c9bSToomas Soome {
15725ffb0c9bSToomas Soome     char *ptr;
15734b22b933Srs     size_t len;
15744b22b933Srs     ipc_msg_hdr *hdr;
15754b22b933Srs     DNSServiceErrorType err;
15764b22b933Srs 
1577c65ebfc7SToomas Soome     // NULL domain handled below
1578c65ebfc7SToomas Soome     if (!sdRef || !regtype || !callBack) return kDNSServiceErr_BadParam;
1579c65ebfc7SToomas Soome 
15805ffb0c9bSToomas Soome     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
15815ffb0c9bSToomas Soome         flags |= kDNSServiceFlagsIncludeP2P;
15824b22b933Srs 
1583*3b436d06SToomas Soome     err = ConnectToServer(sdRef, flags, browse_request, handle_browse_response, (void *)callBack, context);
15845ffb0c9bSToomas Soome     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
15854b22b933Srs 
15865ffb0c9bSToomas Soome     if (!domain) domain = "";
15874b22b933Srs     len = sizeof(flags);
15884b22b933Srs     len += sizeof(interfaceIndex);
15894b22b933Srs     len += strlen(regtype) + 1;
15904b22b933Srs     len += strlen(domain) + 1;
15914b22b933Srs 
15925ffb0c9bSToomas Soome     hdr = create_hdr(browse_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
15935ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
15945ffb0c9bSToomas Soome 
15954b22b933Srs     put_flags(flags, &ptr);
15965ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
15974b22b933Srs     put_string(regtype, &ptr);
15984b22b933Srs     put_string(domain, &ptr);
15994b22b933Srs 
16005ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
16015ffb0c9bSToomas Soome     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
16024b22b933Srs     return err;
16035ffb0c9bSToomas Soome }
16044b22b933Srs 
16055ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain)
16065ffb0c9bSToomas Soome {
1607c65ebfc7SToomas Soome     DNSServiceErrorType err;
16085ffb0c9bSToomas Soome     DNSServiceOp *tmp;
16095ffb0c9bSToomas Soome     char *ptr;
1610c65ebfc7SToomas Soome     size_t len;
16115ffb0c9bSToomas Soome     ipc_msg_hdr *hdr;
1612c65ebfc7SToomas Soome 
1613c65ebfc7SToomas Soome     if (!domain) return kDNSServiceErr_BadParam;
1614c65ebfc7SToomas Soome     len = sizeof(flags) + strlen(domain) + 1;
1615c65ebfc7SToomas Soome 
1616c65ebfc7SToomas Soome     err = ConnectToServer(&tmp, 0, setdomain_request, NULL, NULL, NULL);
16175ffb0c9bSToomas Soome     if (err) return err;
16185ffb0c9bSToomas Soome 
16195ffb0c9bSToomas Soome     hdr = create_hdr(setdomain_request, &len, &ptr, 0, tmp);
16205ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
16214b22b933Srs 
16224b22b933Srs     put_flags(flags, &ptr);
16234b22b933Srs     put_string(domain, &ptr);
16245ffb0c9bSToomas Soome     err = deliver_request(hdr, tmp);        // Will free hdr for us
16255ffb0c9bSToomas Soome     DNSServiceRefDeallocate(tmp);
16265ffb0c9bSToomas Soome     return err;
16275ffb0c9bSToomas Soome }
16284b22b933Srs 
16295ffb0c9bSToomas Soome static void handle_regservice_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
16305ffb0c9bSToomas Soome {
16314b22b933Srs     char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName];
16325ffb0c9bSToomas Soome     get_string(&data, end, name, 256);
16335ffb0c9bSToomas Soome     get_string(&data, end, regtype, kDNSServiceMaxDomainName);
16345ffb0c9bSToomas Soome     get_string(&data, end, domain,  kDNSServiceMaxDomainName);
16355ffb0c9bSToomas Soome     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_regservice_response: error reading result from daemon");
16365ffb0c9bSToomas Soome     else ((DNSServiceRegisterReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_err, name, regtype, domain, sdr->AppContext);
16375ffb0c9bSToomas Soome     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
16385ffb0c9bSToomas Soome }
16394b22b933Srs 
16404b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceRegister
16415ffb0c9bSToomas Soome (
16424b22b933Srs     DNSServiceRef                       *sdRef,
16435ffb0c9bSToomas Soome     DNSServiceFlags flags,
16445ffb0c9bSToomas Soome     uint32_t interfaceIndex,
16454b22b933Srs     const char                          *name,
16464b22b933Srs     const char                          *regtype,
16474b22b933Srs     const char                          *domain,
16484b22b933Srs     const char                          *host,
16495ffb0c9bSToomas Soome     uint16_t PortInNetworkByteOrder,
16505ffb0c9bSToomas Soome     uint16_t txtLen,
16514b22b933Srs     const void                          *txtRecord,
16525ffb0c9bSToomas Soome     DNSServiceRegisterReply callBack,
16534b22b933Srs     void                                *context
16545ffb0c9bSToomas Soome )
16555ffb0c9bSToomas Soome {
16565ffb0c9bSToomas Soome     char *ptr;
16574b22b933Srs     size_t len;
16584b22b933Srs     ipc_msg_hdr *hdr;
16594b22b933Srs     DNSServiceErrorType err;
16604b22b933Srs     union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder };
16614b22b933Srs 
1662c65ebfc7SToomas Soome     if (!sdRef || !regtype) return kDNSServiceErr_BadParam;
16634b22b933Srs     if (!name) name = "";
16644b22b933Srs     if (!domain) domain = "";
16654b22b933Srs     if (!host) host = "";
16664b22b933Srs     if (!txtRecord) txtRecord = (void*)"";
16674b22b933Srs 
16685ffb0c9bSToomas Soome     // No callback must have auto-rename
16694b22b933Srs     if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam;
16704b22b933Srs 
16715ffb0c9bSToomas Soome     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
16725ffb0c9bSToomas Soome         flags |= kDNSServiceFlagsIncludeP2P;
16735ffb0c9bSToomas Soome 
1674*3b436d06SToomas Soome     err = ConnectToServer(sdRef, flags, reg_service_request, callBack ? handle_regservice_response : NULL, (void *)callBack, context);
16755ffb0c9bSToomas Soome     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
16765ffb0c9bSToomas Soome 
16774b22b933Srs     len = sizeof(DNSServiceFlags);
16784b22b933Srs     len += sizeof(uint32_t);  // interfaceIndex
16794b22b933Srs     len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4;
16804b22b933Srs     len += 2 * sizeof(uint16_t);  // port, txtLen
16814b22b933Srs     len += txtLen;
16824b22b933Srs 
16835ffb0c9bSToomas Soome     hdr = create_hdr(reg_service_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
16845ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
1685cda73f64SToomas Soome     if (!callBack) hdr->ipc_flags |= IPC_FLAGS_NOREPLY;
16865ffb0c9bSToomas Soome 
16874b22b933Srs     put_flags(flags, &ptr);
16885ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
16894b22b933Srs     put_string(name, &ptr);
16904b22b933Srs     put_string(regtype, &ptr);
16914b22b933Srs     put_string(domain, &ptr);
16924b22b933Srs     put_string(host, &ptr);
16934b22b933Srs     *ptr++ = port.b[0];
16944b22b933Srs     *ptr++ = port.b[1];
16955ffb0c9bSToomas Soome     put_uint16(txtLen, &ptr);
16964b22b933Srs     put_rdata(txtLen, txtRecord, &ptr);
16974b22b933Srs 
16985ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
16995ffb0c9bSToomas Soome     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
17004b22b933Srs     return err;
17015ffb0c9bSToomas Soome }
17024b22b933Srs 
17035ffb0c9bSToomas Soome static void handle_enumeration_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
17045ffb0c9bSToomas Soome {
17054b22b933Srs     char domain[kDNSServiceMaxDomainName];
17065ffb0c9bSToomas Soome     get_string(&data, end, domain, kDNSServiceMaxDomainName);
17075ffb0c9bSToomas Soome     if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_enumeration_response: error reading result from daemon");
17085ffb0c9bSToomas Soome     else ((DNSServiceDomainEnumReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, domain, sdr->AppContext);
17095ffb0c9bSToomas Soome     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
17105ffb0c9bSToomas Soome }
17114b22b933Srs 
17124b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains
17135ffb0c9bSToomas Soome (
17145ffb0c9bSToomas Soome     DNSServiceRef             *sdRef,
17155ffb0c9bSToomas Soome     DNSServiceFlags flags,
17165ffb0c9bSToomas Soome     uint32_t interfaceIndex,
17175ffb0c9bSToomas Soome     DNSServiceDomainEnumReply callBack,
17185ffb0c9bSToomas Soome     void                      *context
17195ffb0c9bSToomas Soome )
17205ffb0c9bSToomas Soome {
17215ffb0c9bSToomas Soome     char *ptr;
17224b22b933Srs     size_t len;
17234b22b933Srs     ipc_msg_hdr *hdr;
17244b22b933Srs     DNSServiceErrorType err;
1725c65ebfc7SToomas Soome     int f1;
1726c65ebfc7SToomas Soome     int f2;
17275ffb0c9bSToomas Soome 
1728c65ebfc7SToomas Soome     if (!sdRef || !callBack) return kDNSServiceErr_BadParam;
1729c65ebfc7SToomas Soome 
1730c65ebfc7SToomas Soome     f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0;
1731c65ebfc7SToomas Soome     f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0;
17324b22b933Srs     if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
17334b22b933Srs 
1734*3b436d06SToomas Soome     err = ConnectToServer(sdRef, flags, enumeration_request, handle_enumeration_response, (void *)callBack, context);
17355ffb0c9bSToomas Soome     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
17364b22b933Srs 
17375ffb0c9bSToomas Soome     len = sizeof(DNSServiceFlags);
17384b22b933Srs     len += sizeof(uint32_t);
17394b22b933Srs 
17405ffb0c9bSToomas Soome     hdr = create_hdr(enumeration_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
17415ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
17424b22b933Srs 
17434b22b933Srs     put_flags(flags, &ptr);
17445ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
17454b22b933Srs 
17465ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
17475ffb0c9bSToomas Soome     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
17485ffb0c9bSToomas Soome     return err;
17495ffb0c9bSToomas Soome }
17505ffb0c9bSToomas Soome 
17515ffb0c9bSToomas Soome static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *const data, const char *const end)
17525ffb0c9bSToomas Soome {
17535ffb0c9bSToomas Soome     (void)data; // Unused
17545ffb0c9bSToomas Soome 
17555ffb0c9bSToomas Soome     //printf("ConnectionResponse got %d\n", cbh->ipc_hdr.op);
17565ffb0c9bSToomas Soome     if (cbh->ipc_hdr.op != reg_record_reply_op)
17575ffb0c9bSToomas Soome     {
17585ffb0c9bSToomas Soome         // When using kDNSServiceFlagsShareConnection, need to search the list of associated DNSServiceOps
17595ffb0c9bSToomas Soome         // to find the one this response is intended for, and then call through to its ProcessReply handler.
17605ffb0c9bSToomas Soome         // We start with our first subordinate DNSServiceRef -- don't want to accidentally match the parent DNSServiceRef.
17615ffb0c9bSToomas Soome         DNSServiceOp *op = sdr->next;
17625ffb0c9bSToomas Soome         while (op && (op->uid.u32[0] != cbh->ipc_hdr.client_context.u32[0] || op->uid.u32[1] != cbh->ipc_hdr.client_context.u32[1]))
17635ffb0c9bSToomas Soome             op = op->next;
17645ffb0c9bSToomas Soome         // Note: We may sometimes not find a matching DNSServiceOp, in the case where the client has
17655ffb0c9bSToomas Soome         // cancelled the subordinate DNSServiceOp, but there are still messages in the pipeline from the daemon
17665ffb0c9bSToomas Soome         if (op && op->ProcessReply) op->ProcessReply(op, cbh, data, end);
17675ffb0c9bSToomas Soome         // WARNING: Don't touch op or sdr after this -- client may have called DNSServiceRefDeallocate
17685ffb0c9bSToomas Soome         return;
17695ffb0c9bSToomas Soome     }
17705ffb0c9bSToomas Soome     else
17715ffb0c9bSToomas Soome     {
17725ffb0c9bSToomas Soome         DNSRecordRef rec;
17735ffb0c9bSToomas Soome         for (rec = sdr->rec; rec; rec = rec->recnext)
17744b22b933Srs         {
17755ffb0c9bSToomas Soome             if (rec->uid.u32[0] == cbh->ipc_hdr.client_context.u32[0] && rec->uid.u32[1] == cbh->ipc_hdr.client_context.u32[1])
17765ffb0c9bSToomas Soome                 break;
17775ffb0c9bSToomas Soome         }
17785ffb0c9bSToomas Soome         // The record might have been freed already and hence not an
17795ffb0c9bSToomas Soome         // error if the record is not found.
17805ffb0c9bSToomas Soome         if (!rec)
17815ffb0c9bSToomas Soome         {
17825ffb0c9bSToomas Soome             syslog(LOG_INFO, "ConnectionResponse: Record not found");
17835ffb0c9bSToomas Soome             return;
17845ffb0c9bSToomas Soome         }
17855ffb0c9bSToomas Soome         if (rec->sdr != sdr)
17865ffb0c9bSToomas Soome         {
17875ffb0c9bSToomas Soome             syslog(LOG_WARNING, "ConnectionResponse: Record sdr mismatch: rec %p sdr %p", rec->sdr, sdr);
17885ffb0c9bSToomas Soome             return;
17894b22b933Srs         }
17904b22b933Srs 
17915ffb0c9bSToomas Soome         if (sdr->op == connection_request || sdr->op == connection_delegate_request)
17925ffb0c9bSToomas Soome         {
17935ffb0c9bSToomas Soome             rec->AppCallback(rec->sdr, rec, cbh->cb_flags, cbh->cb_err, rec->AppContext);
17945ffb0c9bSToomas Soome         }
17955ffb0c9bSToomas Soome         else
17965ffb0c9bSToomas Soome         {
17975ffb0c9bSToomas Soome             syslog(LOG_WARNING, "dnssd_clientstub ConnectionResponse: sdr->op != connection_request");
17985ffb0c9bSToomas Soome             rec->AppCallback(rec->sdr, rec, 0, kDNSServiceErr_Unknown, rec->AppContext);
17995ffb0c9bSToomas Soome         }
18005ffb0c9bSToomas Soome         // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
18015ffb0c9bSToomas Soome     }
18025ffb0c9bSToomas Soome }
18035ffb0c9bSToomas Soome 
18045ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef)
18055ffb0c9bSToomas Soome {
1806c65ebfc7SToomas Soome     DNSServiceErrorType err;
18075ffb0c9bSToomas Soome     char *ptr;
18085ffb0c9bSToomas Soome     size_t len = 0;
18095ffb0c9bSToomas Soome     ipc_msg_hdr *hdr;
1810c65ebfc7SToomas Soome 
1811c65ebfc7SToomas Soome     if (!sdRef) return kDNSServiceErr_BadParam;
1812c65ebfc7SToomas Soome     err = ConnectToServer(sdRef, 0, connection_request, ConnectionResponse, NULL, NULL);
18135ffb0c9bSToomas Soome     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
18145ffb0c9bSToomas Soome 
18155ffb0c9bSToomas Soome     hdr = create_hdr(connection_request, &len, &ptr, 0, *sdRef);
18165ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
18175ffb0c9bSToomas Soome 
18185ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
18195ffb0c9bSToomas Soome     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
18204b22b933Srs     return err;
18215ffb0c9bSToomas Soome }
18225ffb0c9bSToomas Soome 
18235ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder && !TARGET_IPHONE_SIMULATOR
18245ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid)
18255ffb0c9bSToomas Soome {
18265ffb0c9bSToomas Soome     char *ptr;
18275ffb0c9bSToomas Soome     size_t len = 0;
18285ffb0c9bSToomas Soome     ipc_msg_hdr *hdr;
18294b22b933Srs 
1830c65ebfc7SToomas Soome     if (!sdRef) return kDNSServiceErr_BadParam;
18315ffb0c9bSToomas Soome     DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_delegate_request, ConnectionResponse, NULL, NULL);
18325ffb0c9bSToomas Soome     if (err)
18335ffb0c9bSToomas Soome     {
18345ffb0c9bSToomas Soome          return err;    // On error ConnectToServer leaves *sdRef set to NULL
18354b22b933Srs     }
18364b22b933Srs 
1837c65ebfc7SToomas Soome     // Only one of the two options can be set. If pid is zero, uuid is used.
18385ffb0c9bSToomas Soome     // If both are specified only pid will be used. We send across the pid
18395ffb0c9bSToomas Soome     // so that the daemon knows what to read from the socket.
18405ffb0c9bSToomas Soome 
18415ffb0c9bSToomas Soome     len += sizeof(int32_t);
18425ffb0c9bSToomas Soome 
18435ffb0c9bSToomas Soome     hdr = create_hdr(connection_delegate_request, &len, &ptr, 0, *sdRef);
18445ffb0c9bSToomas Soome     if (!hdr)
18454b22b933Srs     {
18465ffb0c9bSToomas Soome         DNSServiceRefDeallocate(*sdRef);
18475ffb0c9bSToomas Soome         *sdRef = NULL;
18485ffb0c9bSToomas Soome         return kDNSServiceErr_NoMemory;
18495ffb0c9bSToomas Soome     }
18504b22b933Srs 
18515ffb0c9bSToomas Soome     if (pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED, &pid, sizeof(pid)) == -1)
1852c65ebfc7SToomas Soome     {
1853c65ebfc7SToomas Soome         syslog(LOG_WARNING, "dnssdclientstub: Could not setsockopt() for PID[%d], no entitlements or process(pid) invalid errno:%d (%s)", pid, errno, strerror(errno));
1854c65ebfc7SToomas Soome         // Free the hdr in case we return before calling deliver_request()
18555ffb0c9bSToomas Soome         if (hdr)
18565ffb0c9bSToomas Soome             free(hdr);
18575ffb0c9bSToomas Soome         DNSServiceRefDeallocate(*sdRef);
18585ffb0c9bSToomas Soome         *sdRef = NULL;
18595ffb0c9bSToomas Soome         return kDNSServiceErr_NoAuth;
18605ffb0c9bSToomas Soome     }
18614b22b933Srs 
18625ffb0c9bSToomas Soome     if (!pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED_UUID, uuid, sizeof(uuid_t)) == -1)
18635ffb0c9bSToomas Soome     {
18645ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssdclientstub: Could not setsockopt() for UUID, no entitlements or process(uuid) invalid errno:%d (%s) ", errno, strerror(errno));
18655ffb0c9bSToomas Soome         // Free the hdr in case we return before calling deliver_request()
18665ffb0c9bSToomas Soome         if (hdr)
18675ffb0c9bSToomas Soome             free(hdr);
18685ffb0c9bSToomas Soome         DNSServiceRefDeallocate(*sdRef);
18695ffb0c9bSToomas Soome         *sdRef = NULL;
18705ffb0c9bSToomas Soome         return kDNSServiceErr_NoAuth;
18714b22b933Srs     }
18724b22b933Srs 
18735ffb0c9bSToomas Soome     put_uint32(pid, &ptr);
18745ffb0c9bSToomas Soome 
18755ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
18765ffb0c9bSToomas Soome     if (err)
18774b22b933Srs     {
18785ffb0c9bSToomas Soome         DNSServiceRefDeallocate(*sdRef);
18795ffb0c9bSToomas Soome         *sdRef = NULL;
18804b22b933Srs     }
18815ffb0c9bSToomas Soome     return err;
18825ffb0c9bSToomas Soome }
18835ffb0c9bSToomas Soome #elif TARGET_IPHONE_SIMULATOR // This hack is for Simulator platform only
18845ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid)
18855ffb0c9bSToomas Soome {
18865ffb0c9bSToomas Soome     (void) pid;
18875ffb0c9bSToomas Soome     (void) uuid;
18885ffb0c9bSToomas Soome     return DNSServiceCreateConnection(sdRef);
18895ffb0c9bSToomas Soome }
18905ffb0c9bSToomas Soome #endif
18914b22b933Srs 
18924b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
18935ffb0c9bSToomas Soome (
18945ffb0c9bSToomas Soome     DNSServiceRef sdRef,
18954b22b933Srs     DNSRecordRef                  *RecordRef,
18965ffb0c9bSToomas Soome     DNSServiceFlags flags,
18975ffb0c9bSToomas Soome     uint32_t interfaceIndex,
18984b22b933Srs     const char                    *fullname,
18995ffb0c9bSToomas Soome     uint16_t rrtype,
19005ffb0c9bSToomas Soome     uint16_t rrclass,
19015ffb0c9bSToomas Soome     uint16_t rdlen,
19024b22b933Srs     const void                    *rdata,
19035ffb0c9bSToomas Soome     uint32_t ttl,
19045ffb0c9bSToomas Soome     DNSServiceRegisterRecordReply callBack,
19054b22b933Srs     void                          *context
19065ffb0c9bSToomas Soome )
19075ffb0c9bSToomas Soome {
19085ffb0c9bSToomas Soome     char *ptr;
19094b22b933Srs     size_t len;
19104b22b933Srs     ipc_msg_hdr *hdr = NULL;
19114b22b933Srs     DNSRecordRef rref = NULL;
19125ffb0c9bSToomas Soome     DNSRecord **p;
19134b22b933Srs     int f1 = (flags & kDNSServiceFlagsShared) != 0;
19144b22b933Srs     int f2 = (flags & kDNSServiceFlagsUnique) != 0;
19154b22b933Srs     if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
19164b22b933Srs 
19175ffb0c9bSToomas Soome     if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny())
19185ffb0c9bSToomas Soome         flags |= kDNSServiceFlagsIncludeP2P;
19195ffb0c9bSToomas Soome 
1920c65ebfc7SToomas Soome     if (!sdRef || !RecordRef || !fullname || (!rdata && rdlen) || !callBack)
1921c65ebfc7SToomas Soome     {
1922c65ebfc7SToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with NULL parameter");
1923c65ebfc7SToomas Soome         return kDNSServiceErr_BadParam;
1924c65ebfc7SToomas Soome     }
19255ffb0c9bSToomas Soome 
19265ffb0c9bSToomas Soome     if (!DNSServiceRefValid(sdRef))
19275ffb0c9bSToomas Soome     {
19285ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
19295ffb0c9bSToomas Soome         return kDNSServiceErr_BadReference;
19305ffb0c9bSToomas Soome     }
19315ffb0c9bSToomas Soome 
19325ffb0c9bSToomas Soome     if (sdRef->op != connection_request && sdRef->op != connection_delegate_request)
19335ffb0c9bSToomas Soome     {
19345ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op);
19354b22b933Srs         return kDNSServiceErr_BadReference;
19365ffb0c9bSToomas Soome     }
19375ffb0c9bSToomas Soome 
19384b22b933Srs     *RecordRef = NULL;
19394b22b933Srs 
19405ffb0c9bSToomas Soome     len = sizeof(DNSServiceFlags);
19414b22b933Srs     len += 2 * sizeof(uint32_t);  // interfaceIndex, ttl
19424b22b933Srs     len += 3 * sizeof(uint16_t);  // rrtype, rrclass, rdlen
19434b22b933Srs     len += strlen(fullname) + 1;
19444b22b933Srs     len += rdlen;
19454b22b933Srs 
19465ffb0c9bSToomas Soome     // Bump up the uid. Normally for shared operations (kDNSServiceFlagsShareConnection), this
19475ffb0c9bSToomas Soome     // is done in ConnectToServer. For DNSServiceRegisterRecord, ConnectToServer has already
19485ffb0c9bSToomas Soome     // been called. As multiple DNSServiceRegisterRecords can be multiplexed over a single
19495ffb0c9bSToomas Soome     // connection, we need a way to demultiplex the response so that the callback corresponding
19505ffb0c9bSToomas Soome     // to the right DNSServiceRegisterRecord instance can be called. Use the same mechanism that
19515ffb0c9bSToomas Soome     // is used by kDNSServiceFlagsShareConnection. create_hdr copies the uid value to ipc
19525ffb0c9bSToomas Soome     // hdr->client_context which will be returned in the ipc response.
19535ffb0c9bSToomas Soome     if (++sdRef->uid.u32[0] == 0)
19545ffb0c9bSToomas Soome         ++sdRef->uid.u32[1];
19555ffb0c9bSToomas Soome     hdr = create_hdr(reg_record_request, &len, &ptr, 1, sdRef);
19565ffb0c9bSToomas Soome     if (!hdr) return kDNSServiceErr_NoMemory;
19575ffb0c9bSToomas Soome 
19584b22b933Srs     put_flags(flags, &ptr);
19595ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
19604b22b933Srs     put_string(fullname, &ptr);
19615ffb0c9bSToomas Soome     put_uint16(rrtype, &ptr);
19625ffb0c9bSToomas Soome     put_uint16(rrclass, &ptr);
19635ffb0c9bSToomas Soome     put_uint16(rdlen, &ptr);
19644b22b933Srs     put_rdata(rdlen, rdata, &ptr);
19655ffb0c9bSToomas Soome     put_uint32(ttl, &ptr);
19664b22b933Srs 
19675ffb0c9bSToomas Soome     rref = malloc(sizeof(DNSRecord));
19685ffb0c9bSToomas Soome     if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; }
19695ffb0c9bSToomas Soome     rref->AppContext = context;
19705ffb0c9bSToomas Soome     rref->AppCallback = callBack;
19714b22b933Srs     rref->record_index = sdRef->max_index++;
19724b22b933Srs     rref->sdr = sdRef;
19735ffb0c9bSToomas Soome     rref->recnext = NULL;
19744b22b933Srs     *RecordRef = rref;
19755ffb0c9bSToomas Soome     // Remember the uid that we are sending across so that we can match
19765ffb0c9bSToomas Soome     // when the response comes back.
19775ffb0c9bSToomas Soome     rref->uid = sdRef->uid;
19784b22b933Srs     hdr->reg_index = rref->record_index;
19794b22b933Srs 
19805ffb0c9bSToomas Soome     p = &(sdRef)->rec;
19815ffb0c9bSToomas Soome     while (*p) p = &(*p)->recnext;
19825ffb0c9bSToomas Soome     *p = rref;
19834b22b933Srs 
19845ffb0c9bSToomas Soome     return deliver_request(hdr, sdRef);     // Will free hdr for us
19855ffb0c9bSToomas Soome }
19864b22b933Srs 
19875ffb0c9bSToomas Soome // sdRef returned by DNSServiceRegister()
19884b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceAddRecord
19895ffb0c9bSToomas Soome (
19905ffb0c9bSToomas Soome     DNSServiceRef sdRef,
19914b22b933Srs     DNSRecordRef    *RecordRef,
19925ffb0c9bSToomas Soome     DNSServiceFlags flags,
19935ffb0c9bSToomas Soome     uint16_t rrtype,
19945ffb0c9bSToomas Soome     uint16_t rdlen,
19954b22b933Srs     const void      *rdata,
19965ffb0c9bSToomas Soome     uint32_t ttl
19975ffb0c9bSToomas Soome )
19985ffb0c9bSToomas Soome {
19994b22b933Srs     ipc_msg_hdr *hdr;
20004b22b933Srs     size_t len = 0;
20014b22b933Srs     char *ptr;
20024b22b933Srs     DNSRecordRef rref;
20035ffb0c9bSToomas Soome     DNSRecord **p;
20045ffb0c9bSToomas Soome 
2005c65ebfc7SToomas Soome     if (!sdRef || !RecordRef || (!rdata && rdlen))
2006c65ebfc7SToomas Soome     {
2007c65ebfc7SToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL parameter");
2008c65ebfc7SToomas Soome         return kDNSServiceErr_BadParam;
2009c65ebfc7SToomas Soome     }
20105ffb0c9bSToomas Soome     if (sdRef->op != reg_service_request)
20115ffb0c9bSToomas Soome     {
20125ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with non-DNSServiceRegister DNSServiceRef %p %d", sdRef, sdRef->op);
20135ffb0c9bSToomas Soome         return kDNSServiceErr_BadReference;
20145ffb0c9bSToomas Soome     }
20154b22b933Srs 
20165ffb0c9bSToomas Soome     if (!DNSServiceRefValid(sdRef))
20175ffb0c9bSToomas Soome     {
20185ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
20194b22b933Srs         return kDNSServiceErr_BadReference;
20205ffb0c9bSToomas Soome     }
20215ffb0c9bSToomas Soome 
20224b22b933Srs     *RecordRef = NULL;
20234b22b933Srs 
20245ffb0c9bSToomas Soome     len += 2 * sizeof(uint16_t);  // rrtype, rdlen
20254b22b933Srs     len += rdlen;
20264b22b933Srs     len += sizeof(uint32_t);
20274b22b933Srs     len += sizeof(DNSServiceFlags);
20284b22b933Srs 
20295ffb0c9bSToomas Soome     hdr = create_hdr(add_record_request, &len, &ptr, 1, sdRef);
20305ffb0c9bSToomas Soome     if (!hdr) return kDNSServiceErr_NoMemory;
20314b22b933Srs     put_flags(flags, &ptr);
20325ffb0c9bSToomas Soome     put_uint16(rrtype, &ptr);
20335ffb0c9bSToomas Soome     put_uint16(rdlen, &ptr);
20344b22b933Srs     put_rdata(rdlen, rdata, &ptr);
20355ffb0c9bSToomas Soome     put_uint32(ttl, &ptr);
20364b22b933Srs 
20375ffb0c9bSToomas Soome     rref = malloc(sizeof(DNSRecord));
20385ffb0c9bSToomas Soome     if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; }
20395ffb0c9bSToomas Soome     rref->AppContext = NULL;
20405ffb0c9bSToomas Soome     rref->AppCallback = NULL;
20414b22b933Srs     rref->record_index = sdRef->max_index++;
20424b22b933Srs     rref->sdr = sdRef;
20435ffb0c9bSToomas Soome     rref->recnext = NULL;
20444b22b933Srs     *RecordRef = rref;
20454b22b933Srs     hdr->reg_index = rref->record_index;
20464b22b933Srs 
20475ffb0c9bSToomas Soome     p = &(sdRef)->rec;
20485ffb0c9bSToomas Soome     while (*p) p = &(*p)->recnext;
20495ffb0c9bSToomas Soome     *p = rref;
20505ffb0c9bSToomas Soome 
20515ffb0c9bSToomas Soome     return deliver_request(hdr, sdRef);     // Will free hdr for us
20524b22b933Srs }
20534b22b933Srs 
20545ffb0c9bSToomas Soome // DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord
20554b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord
20565ffb0c9bSToomas Soome (
20575ffb0c9bSToomas Soome     DNSServiceRef sdRef,
20585ffb0c9bSToomas Soome     DNSRecordRef RecordRef,
20595ffb0c9bSToomas Soome     DNSServiceFlags flags,
20605ffb0c9bSToomas Soome     uint16_t rdlen,
20614b22b933Srs     const void      *rdata,
20625ffb0c9bSToomas Soome     uint32_t ttl
20635ffb0c9bSToomas Soome )
20645ffb0c9bSToomas Soome {
20654b22b933Srs     ipc_msg_hdr *hdr;
20664b22b933Srs     size_t len = 0;
20674b22b933Srs     char *ptr;
20684b22b933Srs 
2069c65ebfc7SToomas Soome     if (!sdRef || (!rdata && rdlen))
2070c65ebfc7SToomas Soome     {
2071c65ebfc7SToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with NULL parameter");
2072c65ebfc7SToomas Soome         return kDNSServiceErr_BadParam;
2073c65ebfc7SToomas Soome     }
20745ffb0c9bSToomas Soome 
20755ffb0c9bSToomas Soome     if (!DNSServiceRefValid(sdRef))
20765ffb0c9bSToomas Soome     {
20775ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
20785ffb0c9bSToomas Soome         return kDNSServiceErr_BadReference;
20795ffb0c9bSToomas Soome     }
20805ffb0c9bSToomas Soome 
20815ffb0c9bSToomas Soome     // Note: RecordRef is allowed to be NULL
20824b22b933Srs 
20834b22b933Srs     len += sizeof(uint16_t);
20844b22b933Srs     len += rdlen;
20854b22b933Srs     len += sizeof(uint32_t);
20864b22b933Srs     len += sizeof(DNSServiceFlags);
20874b22b933Srs 
20885ffb0c9bSToomas Soome     hdr = create_hdr(update_record_request, &len, &ptr, 1, sdRef);
20895ffb0c9bSToomas Soome     if (!hdr) return kDNSServiceErr_NoMemory;
20904b22b933Srs     hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX;
20914b22b933Srs     put_flags(flags, &ptr);
20925ffb0c9bSToomas Soome     put_uint16(rdlen, &ptr);
20934b22b933Srs     put_rdata(rdlen, rdata, &ptr);
20945ffb0c9bSToomas Soome     put_uint32(ttl, &ptr);
20955ffb0c9bSToomas Soome     return deliver_request(hdr, sdRef);     // Will free hdr for us
20965ffb0c9bSToomas Soome }
20974b22b933Srs 
20984b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord
20995ffb0c9bSToomas Soome (
21005ffb0c9bSToomas Soome     DNSServiceRef sdRef,
21015ffb0c9bSToomas Soome     DNSRecordRef RecordRef,
21025ffb0c9bSToomas Soome     DNSServiceFlags flags
21035ffb0c9bSToomas Soome )
21045ffb0c9bSToomas Soome {
21054b22b933Srs     ipc_msg_hdr *hdr;
21064b22b933Srs     size_t len = 0;
21074b22b933Srs     char *ptr;
21084b22b933Srs     DNSServiceErrorType err;
21094b22b933Srs 
21105ffb0c9bSToomas Soome     if (!sdRef)            { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
21115ffb0c9bSToomas Soome     if (!RecordRef)        { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSRecordRef");  return kDNSServiceErr_BadParam; }
21125ffb0c9bSToomas Soome     if (!sdRef->max_index) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with bad DNSServiceRef");  return kDNSServiceErr_BadReference; }
21135ffb0c9bSToomas Soome 
21145ffb0c9bSToomas Soome     if (!DNSServiceRefValid(sdRef))
21155ffb0c9bSToomas Soome     {
21165ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
21174b22b933Srs         return kDNSServiceErr_BadReference;
21185ffb0c9bSToomas Soome     }
21194b22b933Srs 
21204b22b933Srs     len += sizeof(flags);
21215ffb0c9bSToomas Soome     hdr = create_hdr(remove_record_request, &len, &ptr, 1, sdRef);
21225ffb0c9bSToomas Soome     if (!hdr) return kDNSServiceErr_NoMemory;
21234b22b933Srs     hdr->reg_index = RecordRef->record_index;
21244b22b933Srs     put_flags(flags, &ptr);
21255ffb0c9bSToomas Soome     err = deliver_request(hdr, sdRef);      // Will free hdr for us
21265ffb0c9bSToomas Soome     if (!err)
21275ffb0c9bSToomas Soome     {
21285ffb0c9bSToomas Soome         // This RecordRef could have been allocated in DNSServiceRegisterRecord or DNSServiceAddRecord.
21295ffb0c9bSToomas Soome         // If so, delink from the list before freeing
21305ffb0c9bSToomas Soome         DNSRecord **p = &sdRef->rec;
21315ffb0c9bSToomas Soome         while (*p && *p != RecordRef) p = &(*p)->recnext;
21325ffb0c9bSToomas Soome         if (*p) *p = RecordRef->recnext;
21335ffb0c9bSToomas Soome         free(RecordRef);
21344b22b933Srs     }
21355ffb0c9bSToomas Soome     return err;
21365ffb0c9bSToomas Soome }
21374b22b933Srs 
21384b22b933Srs DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
21395ffb0c9bSToomas Soome (
21405ffb0c9bSToomas Soome     DNSServiceFlags flags,
21415ffb0c9bSToomas Soome     uint32_t interfaceIndex,
21425ffb0c9bSToomas Soome     const char      *fullname,
21435ffb0c9bSToomas Soome     uint16_t rrtype,
21445ffb0c9bSToomas Soome     uint16_t rrclass,
21455ffb0c9bSToomas Soome     uint16_t rdlen,
21465ffb0c9bSToomas Soome     const void      *rdata
21475ffb0c9bSToomas Soome )
21485ffb0c9bSToomas Soome {
2149c65ebfc7SToomas Soome     DNSServiceErrorType err;
21504b22b933Srs     char *ptr;
21514b22b933Srs     size_t len;
21524b22b933Srs     ipc_msg_hdr *hdr;
2153c65ebfc7SToomas Soome     DNSServiceOp *tmp = NULL;
2154c65ebfc7SToomas Soome 
2155c65ebfc7SToomas Soome     if (!fullname || (!rdata && rdlen)) return kDNSServiceErr_BadParam;
21565ffb0c9bSToomas Soome 
2157c65ebfc7SToomas Soome     err = ConnectToServer(&tmp, flags, reconfirm_record_request, NULL, NULL, NULL);
21585ffb0c9bSToomas Soome     if (err) return err;
21594b22b933Srs 
21604b22b933Srs     len = sizeof(DNSServiceFlags);
21614b22b933Srs     len += sizeof(uint32_t);
21624b22b933Srs     len += strlen(fullname) + 1;
21634b22b933Srs     len += 3 * sizeof(uint16_t);
21644b22b933Srs     len += rdlen;
21655ffb0c9bSToomas Soome     hdr = create_hdr(reconfirm_record_request, &len, &ptr, 0, tmp);
21665ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
21674b22b933Srs 
21684b22b933Srs     put_flags(flags, &ptr);
21695ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
21704b22b933Srs     put_string(fullname, &ptr);
21715ffb0c9bSToomas Soome     put_uint16(rrtype, &ptr);
21725ffb0c9bSToomas Soome     put_uint16(rrclass, &ptr);
21735ffb0c9bSToomas Soome     put_uint16(rdlen, &ptr);
21744b22b933Srs     put_rdata(rdlen, rdata, &ptr);
21755ffb0c9bSToomas Soome 
21765ffb0c9bSToomas Soome     err = deliver_request(hdr, tmp);        // Will free hdr for us
21774b22b933Srs     DNSServiceRefDeallocate(tmp);
21785ffb0c9bSToomas Soome     return err;
21795ffb0c9bSToomas Soome }
21805ffb0c9bSToomas Soome 
21815ffb0c9bSToomas Soome 
21825ffb0c9bSToomas Soome static void handle_port_mapping_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
21835ffb0c9bSToomas Soome {
21845ffb0c9bSToomas Soome     union { uint32_t l; u_char b[4]; } addr;
21855ffb0c9bSToomas Soome     uint8_t protocol;
21865ffb0c9bSToomas Soome     union { uint16_t s; u_char b[2]; } internalPort;
21875ffb0c9bSToomas Soome     union { uint16_t s; u_char b[2]; } externalPort;
21885ffb0c9bSToomas Soome     uint32_t ttl;
21895ffb0c9bSToomas Soome 
21905ffb0c9bSToomas Soome     if (!data || data + 13 > end) goto fail;
21915ffb0c9bSToomas Soome 
21925ffb0c9bSToomas Soome     addr.b[0] = *data++;
21935ffb0c9bSToomas Soome     addr.b[1] = *data++;
21945ffb0c9bSToomas Soome     addr.b[2] = *data++;
21955ffb0c9bSToomas Soome     addr.b[3] = *data++;
21965ffb0c9bSToomas Soome     protocol          = *data++;
21975ffb0c9bSToomas Soome     internalPort.b[0] = *data++;
21985ffb0c9bSToomas Soome     internalPort.b[1] = *data++;
21995ffb0c9bSToomas Soome     externalPort.b[0] = *data++;
22005ffb0c9bSToomas Soome     externalPort.b[1] = *data++;
22015ffb0c9bSToomas Soome     ttl               = get_uint32(&data, end);
22025ffb0c9bSToomas Soome     if (!data) goto fail;
22035ffb0c9bSToomas Soome 
22045ffb0c9bSToomas Soome     ((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, addr.l, protocol, internalPort.s, externalPort.s, ttl, sdr->AppContext);
22055ffb0c9bSToomas Soome     return;
22065ffb0c9bSToomas Soome     // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
22075ffb0c9bSToomas Soome 
22085ffb0c9bSToomas Soome     fail :
22095ffb0c9bSToomas Soome     syslog(LOG_WARNING, "dnssd_clientstub handle_port_mapping_response: error reading result from daemon");
22105ffb0c9bSToomas Soome }
22115ffb0c9bSToomas Soome 
22125ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate
22135ffb0c9bSToomas Soome (
22145ffb0c9bSToomas Soome     DNSServiceRef                       *sdRef,
22155ffb0c9bSToomas Soome     DNSServiceFlags flags,
22165ffb0c9bSToomas Soome     uint32_t interfaceIndex,
22175ffb0c9bSToomas Soome     uint32_t protocol,                                /* TCP and/or UDP */
22185ffb0c9bSToomas Soome     uint16_t internalPortInNetworkByteOrder,
22195ffb0c9bSToomas Soome     uint16_t externalPortInNetworkByteOrder,
22205ffb0c9bSToomas Soome     uint32_t ttl,                                     /* time to live in seconds */
22215ffb0c9bSToomas Soome     DNSServiceNATPortMappingReply callBack,
22225ffb0c9bSToomas Soome     void                                *context      /* may be NULL */
22235ffb0c9bSToomas Soome )
22245ffb0c9bSToomas Soome {
22255ffb0c9bSToomas Soome     char *ptr;
22265ffb0c9bSToomas Soome     size_t len;
22275ffb0c9bSToomas Soome     ipc_msg_hdr *hdr;
22285ffb0c9bSToomas Soome     union { uint16_t s; u_char b[2]; } internalPort = { internalPortInNetworkByteOrder };
22295ffb0c9bSToomas Soome     union { uint16_t s; u_char b[2]; } externalPort = { externalPortInNetworkByteOrder };
22305ffb0c9bSToomas Soome 
2231*3b436d06SToomas Soome     DNSServiceErrorType err = ConnectToServer(sdRef, flags, port_mapping_request, handle_port_mapping_response, (void *)callBack, context);
22325ffb0c9bSToomas Soome     if (err) return err;    // On error ConnectToServer leaves *sdRef set to NULL
22335ffb0c9bSToomas Soome 
22345ffb0c9bSToomas Soome     len = sizeof(flags);
22355ffb0c9bSToomas Soome     len += sizeof(interfaceIndex);
22365ffb0c9bSToomas Soome     len += sizeof(protocol);
22375ffb0c9bSToomas Soome     len += sizeof(internalPort);
22385ffb0c9bSToomas Soome     len += sizeof(externalPort);
22395ffb0c9bSToomas Soome     len += sizeof(ttl);
22405ffb0c9bSToomas Soome 
22415ffb0c9bSToomas Soome     hdr = create_hdr(port_mapping_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
22425ffb0c9bSToomas Soome     if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
22435ffb0c9bSToomas Soome 
22445ffb0c9bSToomas Soome     put_flags(flags, &ptr);
22455ffb0c9bSToomas Soome     put_uint32(interfaceIndex, &ptr);
22465ffb0c9bSToomas Soome     put_uint32(protocol, &ptr);
22475ffb0c9bSToomas Soome     *ptr++ = internalPort.b[0];
22485ffb0c9bSToomas Soome     *ptr++ = internalPort.b[1];
22495ffb0c9bSToomas Soome     *ptr++ = externalPort.b[0];
22505ffb0c9bSToomas Soome     *ptr++ = externalPort.b[1];
22515ffb0c9bSToomas Soome     put_uint32(ttl, &ptr);
22525ffb0c9bSToomas Soome 
22535ffb0c9bSToomas Soome     err = deliver_request(hdr, *sdRef);     // Will free hdr for us
22545ffb0c9bSToomas Soome     if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
22555ffb0c9bSToomas Soome     return err;
22565ffb0c9bSToomas Soome }
22575ffb0c9bSToomas Soome 
22585ffb0c9bSToomas Soome #if _DNS_SD_LIBDISPATCH
22595ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue
22605ffb0c9bSToomas Soome (
22615ffb0c9bSToomas Soome     DNSServiceRef service,
22625ffb0c9bSToomas Soome     dispatch_queue_t queue
22635ffb0c9bSToomas Soome )
22645ffb0c9bSToomas Soome {
22655ffb0c9bSToomas Soome     int dnssd_fd  = DNSServiceRefSockFD(service);
22665ffb0c9bSToomas Soome     if (dnssd_fd == dnssd_InvalidSocket) return kDNSServiceErr_BadParam;
22675ffb0c9bSToomas Soome     if (!queue)
22685ffb0c9bSToomas Soome     {
22695ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub: DNSServiceSetDispatchQueue dispatch queue NULL");
22705ffb0c9bSToomas Soome         return kDNSServiceErr_BadParam;
22715ffb0c9bSToomas Soome     }
22725ffb0c9bSToomas Soome     if (service->disp_queue)
22735ffb0c9bSToomas Soome     {
22745ffb0c9bSToomas Soome         syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch queue set already");
22755ffb0c9bSToomas Soome         return kDNSServiceErr_BadParam;
22765ffb0c9bSToomas Soome     }
22775ffb0c9bSToomas Soome     if (service->disp_source)
22785ffb0c9bSToomas Soome     {
22795ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch source set already");
22805ffb0c9bSToomas Soome         return kDNSServiceErr_BadParam;
22815ffb0c9bSToomas Soome     }
22825ffb0c9bSToomas Soome     service->disp_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, dnssd_fd, 0, queue);
22835ffb0c9bSToomas Soome     if (!service->disp_source)
22845ffb0c9bSToomas Soome     {
22855ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch_source_create failed");
22865ffb0c9bSToomas Soome         return kDNSServiceErr_NoMemory;
22875ffb0c9bSToomas Soome     }
22885ffb0c9bSToomas Soome     service->disp_queue = queue;
22895ffb0c9bSToomas Soome     dispatch_source_set_event_handler(service->disp_source, ^{DNSServiceProcessResult(service);});
22905ffb0c9bSToomas Soome     dispatch_source_set_cancel_handler(service->disp_source, ^{dnssd_close(dnssd_fd);});
22915ffb0c9bSToomas Soome     dispatch_resume(service->disp_source);
22925ffb0c9bSToomas Soome     return kDNSServiceErr_NoError;
22935ffb0c9bSToomas Soome }
22945ffb0c9bSToomas Soome #endif // _DNS_SD_LIBDISPATCH
22955ffb0c9bSToomas Soome 
22965ffb0c9bSToomas Soome #if !defined(_WIN32)
22975ffb0c9bSToomas Soome 
22985ffb0c9bSToomas Soome static void DNSSD_API SleepKeepaliveCallback(DNSServiceRef sdRef, DNSRecordRef rec, const DNSServiceFlags flags,
22995ffb0c9bSToomas Soome                                              DNSServiceErrorType errorCode, void *context)
23005ffb0c9bSToomas Soome {
23015ffb0c9bSToomas Soome     SleepKAContext *ka = (SleepKAContext *)context;
23025ffb0c9bSToomas Soome     (void)rec;      // Unused
23035ffb0c9bSToomas Soome     (void)flags;    // Unused
23045ffb0c9bSToomas Soome 
23055ffb0c9bSToomas Soome     if (sdRef->kacontext != context)
23065ffb0c9bSToomas Soome         syslog(LOG_WARNING, "SleepKeepaliveCallback context mismatch");
23075ffb0c9bSToomas Soome 
23085ffb0c9bSToomas Soome     if (ka->AppCallback)
23095ffb0c9bSToomas Soome         ((DNSServiceSleepKeepaliveReply)ka->AppCallback)(sdRef, errorCode, ka->AppContext);
23105ffb0c9bSToomas Soome }
23115ffb0c9bSToomas Soome 
23125ffb0c9bSToomas Soome DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive
23135ffb0c9bSToomas Soome (
23145ffb0c9bSToomas Soome     DNSServiceRef                       *sdRef,
23155ffb0c9bSToomas Soome     DNSServiceFlags flags,
23165ffb0c9bSToomas Soome     int fd,
23175ffb0c9bSToomas Soome     unsigned int timeout,
23185ffb0c9bSToomas Soome     DNSServiceSleepKeepaliveReply callBack,
23195ffb0c9bSToomas Soome     void                                *context
23205ffb0c9bSToomas Soome )
23215ffb0c9bSToomas Soome {
23225ffb0c9bSToomas Soome     char source_str[INET6_ADDRSTRLEN];
23235ffb0c9bSToomas Soome     char target_str[INET6_ADDRSTRLEN];
23245ffb0c9bSToomas Soome     struct sockaddr_storage lss;
23255ffb0c9bSToomas Soome     struct sockaddr_storage rss;
23265ffb0c9bSToomas Soome     socklen_t len1, len2;
23275ffb0c9bSToomas Soome     unsigned int len, proxyreclen;
23285ffb0c9bSToomas Soome     char buf[256];
23295ffb0c9bSToomas Soome     DNSServiceErrorType err;
23305ffb0c9bSToomas Soome     DNSRecordRef record = NULL;
23315ffb0c9bSToomas Soome     char name[10];
23325ffb0c9bSToomas Soome     char recname[128];
23335ffb0c9bSToomas Soome     SleepKAContext *ka;
23345ffb0c9bSToomas Soome     unsigned int i, unique;
23355ffb0c9bSToomas Soome 
23365ffb0c9bSToomas Soome 
23375ffb0c9bSToomas Soome     (void) flags; //unused
23385ffb0c9bSToomas Soome     if (!timeout) return kDNSServiceErr_BadParam;
23395ffb0c9bSToomas Soome 
23405ffb0c9bSToomas Soome 
23415ffb0c9bSToomas Soome     len1 = sizeof(lss);
23425ffb0c9bSToomas Soome     if (getsockname(fd, (struct sockaddr *)&lss, &len1) < 0)
23435ffb0c9bSToomas Soome     {
23445ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getsockname %d\n", errno);
23455ffb0c9bSToomas Soome         return kDNSServiceErr_BadParam;
23465ffb0c9bSToomas Soome     }
23475ffb0c9bSToomas Soome 
23485ffb0c9bSToomas Soome     len2 = sizeof(rss);
23495ffb0c9bSToomas Soome     if (getpeername(fd, (struct sockaddr *)&rss, &len2) < 0)
23505ffb0c9bSToomas Soome     {
23515ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSleepKeepalive: getpeername %d\n", errno);
23525ffb0c9bSToomas Soome         return kDNSServiceErr_BadParam;
23535ffb0c9bSToomas Soome     }
23545ffb0c9bSToomas Soome 
23555ffb0c9bSToomas Soome     if (len1 != len2)
23565ffb0c9bSToomas Soome     {
23575ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSleepKeepalive local/remote info not same");
23585ffb0c9bSToomas Soome         return kDNSServiceErr_Unknown;
23595ffb0c9bSToomas Soome     }
23605ffb0c9bSToomas Soome 
23615ffb0c9bSToomas Soome     unique = 0;
23625ffb0c9bSToomas Soome     if (lss.ss_family == AF_INET)
23635ffb0c9bSToomas Soome     {
23645ffb0c9bSToomas Soome         struct sockaddr_in *sl = (struct sockaddr_in *)&lss;
23655ffb0c9bSToomas Soome         struct sockaddr_in *sr = (struct sockaddr_in *)&rss;
23665ffb0c9bSToomas Soome         unsigned char *ptr = (unsigned char *)&sl->sin_addr;
23675ffb0c9bSToomas Soome 
23685ffb0c9bSToomas Soome         if (!inet_ntop(AF_INET, (const void *)&sr->sin_addr, target_str, sizeof (target_str)))
23695ffb0c9bSToomas Soome         {
23705ffb0c9bSToomas Soome             syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote info failed %d", errno);
23715ffb0c9bSToomas Soome             return kDNSServiceErr_Unknown;
23725ffb0c9bSToomas Soome         }
23735ffb0c9bSToomas Soome         if (!inet_ntop(AF_INET, (const void *)&sl->sin_addr, source_str, sizeof (source_str)))
23745ffb0c9bSToomas Soome         {
23755ffb0c9bSToomas Soome             syslog(LOG_WARNING, "DNSServiceSleepKeepalive local info failed %d", errno);
23765ffb0c9bSToomas Soome             return kDNSServiceErr_Unknown;
23775ffb0c9bSToomas Soome         }
23785ffb0c9bSToomas Soome         // Sum of all bytes in the local address and port should result in a unique
23795ffb0c9bSToomas Soome         // number in the local network
23805ffb0c9bSToomas Soome         for (i = 0; i < sizeof(struct in_addr); i++)
23815ffb0c9bSToomas Soome             unique += ptr[i];
23825ffb0c9bSToomas Soome         unique += sl->sin_port;
23835ffb0c9bSToomas Soome         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));
23845ffb0c9bSToomas Soome     }
23855ffb0c9bSToomas Soome     else
23865ffb0c9bSToomas Soome     {
23875ffb0c9bSToomas Soome         struct sockaddr_in6 *sl6 = (struct sockaddr_in6 *)&lss;
23885ffb0c9bSToomas Soome         struct sockaddr_in6 *sr6 = (struct sockaddr_in6 *)&rss;
23895ffb0c9bSToomas Soome         unsigned char *ptr = (unsigned char *)&sl6->sin6_addr;
23905ffb0c9bSToomas Soome 
23915ffb0c9bSToomas Soome         if (!inet_ntop(AF_INET6, (const void *)&sr6->sin6_addr, target_str, sizeof (target_str)))
23925ffb0c9bSToomas Soome         {
23935ffb0c9bSToomas Soome             syslog(LOG_WARNING, "DNSServiceSleepKeepalive remote6 info failed %d", errno);
23945ffb0c9bSToomas Soome             return kDNSServiceErr_Unknown;
23955ffb0c9bSToomas Soome         }
23965ffb0c9bSToomas Soome         if (!inet_ntop(AF_INET6, (const void *)&sl6->sin6_addr, source_str, sizeof (source_str)))
23975ffb0c9bSToomas Soome         {
23985ffb0c9bSToomas Soome             syslog(LOG_WARNING, "DNSServiceSleepKeepalive local6 info failed %d", errno);
23995ffb0c9bSToomas Soome             return kDNSServiceErr_Unknown;
24005ffb0c9bSToomas Soome         }
24015ffb0c9bSToomas Soome         for (i = 0; i < sizeof(struct in6_addr); i++)
24025ffb0c9bSToomas Soome             unique += ptr[i];
24035ffb0c9bSToomas Soome         unique += sl6->sin6_port;
24045ffb0c9bSToomas Soome         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));
24055ffb0c9bSToomas Soome     }
24065ffb0c9bSToomas Soome 
24075ffb0c9bSToomas Soome     if (len >= (sizeof(buf) - 1))
24085ffb0c9bSToomas Soome     {
24095ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit local/remote info");
24105ffb0c9bSToomas Soome         return kDNSServiceErr_Unknown;
24115ffb0c9bSToomas Soome     }
24125ffb0c9bSToomas Soome     // Include the NULL byte also in the first byte. The total length of the record includes the
24135ffb0c9bSToomas Soome     // first byte also.
24145ffb0c9bSToomas Soome     buf[0] = len + 1;
24155ffb0c9bSToomas Soome     proxyreclen = len + 2;
24165ffb0c9bSToomas Soome 
24175ffb0c9bSToomas Soome     len = snprintf(name, sizeof(name), "%u", unique);
24185ffb0c9bSToomas Soome     if (len >= sizeof(name))
24195ffb0c9bSToomas Soome     {
24205ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit unique");
24215ffb0c9bSToomas Soome         return kDNSServiceErr_Unknown;
24225ffb0c9bSToomas Soome     }
24235ffb0c9bSToomas Soome 
24245ffb0c9bSToomas Soome     len = snprintf(recname, sizeof(recname), "%s.%s", name, "_keepalive._dns-sd._udp.local");
24255ffb0c9bSToomas Soome     if (len >= sizeof(recname))
24265ffb0c9bSToomas Soome     {
24275ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSleepKeepalive could not fit name");
24285ffb0c9bSToomas Soome         return kDNSServiceErr_Unknown;
24295ffb0c9bSToomas Soome     }
24305ffb0c9bSToomas Soome 
24315ffb0c9bSToomas Soome     ka = malloc(sizeof(SleepKAContext));
24325ffb0c9bSToomas Soome     if (!ka) return kDNSServiceErr_NoMemory;
2433*3b436d06SToomas Soome     ka->AppCallback = (void *)callBack;
24345ffb0c9bSToomas Soome     ka->AppContext = context;
24355ffb0c9bSToomas Soome 
24365ffb0c9bSToomas Soome     err = DNSServiceCreateConnection(sdRef);
24375ffb0c9bSToomas Soome     if (err)
24385ffb0c9bSToomas Soome     {
24395ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection");
24405ffb0c9bSToomas Soome         free(ka);
24415ffb0c9bSToomas Soome         return err;
24424b22b933Srs     }
24434b22b933Srs 
24445ffb0c9bSToomas Soome     // we don't care about the "record". When sdRef gets deallocated later, it will be freed too
24455ffb0c9bSToomas Soome     err = DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, 0, recname,
24465ffb0c9bSToomas Soome                                    kDNSServiceType_NULL,  kDNSServiceClass_IN, proxyreclen, buf,  kDNSServiceInterfaceIndexAny, SleepKeepaliveCallback, ka);
24475ffb0c9bSToomas Soome     if (err)
24485ffb0c9bSToomas Soome     {
24495ffb0c9bSToomas Soome         syslog(LOG_WARNING, "DNSServiceSleepKeepalive cannot create connection");
24505ffb0c9bSToomas Soome         free(ka);
24515ffb0c9bSToomas Soome         return err;
24525ffb0c9bSToomas Soome     }
24535ffb0c9bSToomas Soome     (*sdRef)->kacontext = ka;
24545ffb0c9bSToomas Soome     return kDNSServiceErr_NoError;
24555ffb0c9bSToomas Soome }
24565ffb0c9bSToomas Soome #endif
2457