xref: /illumos-gate/usr/src/contrib/mDNSResponder/Clients/dns-sd.c (revision 472cd20d26008f77084ade4c2048159b98c2b705)
1 /* -*- Mode: C; tab-width: 4 -*-
2  *
3  * Copyright (c) 2002-2020 Apple Inc. All rights reserved.
4  *
5  * Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Inc.
6  * ("Apple") in consideration of your agreement to the following terms, and your
7  * use, installation, modification or redistribution of this Apple software
8  * constitutes acceptance of these terms.  If you do not agree with these terms,
9  * please do not use, install, modify or redistribute this Apple software.
10  *
11  * In consideration of your agreement to abide by the following terms, and subject
12  * to these terms, Apple grants you a personal, non-exclusive license, under Apple's
13  * copyrights in this original Apple software (the "Apple Software"), to use,
14  * reproduce, modify and redistribute the Apple Software, with or without
15  * modifications, in source and/or binary forms; provided that if you redistribute
16  * the Apple Software in its entirety and without modifications, you must retain
17  * this notice and the following text and disclaimers in all such redistributions of
18  * the Apple Software.  Neither the name, trademarks, service marks or logos of
19  * Apple Inc. may be used to endorse or promote products derived from the
20  * Apple Software without specific prior written permission from Apple.  Except as
21  * expressly stated in this notice, no other rights or licenses, express or implied,
22  * are granted by Apple herein, including but not limited to any patent rights that
23  * may be infringed by your derivative works or by other works in which the Apple
24  * Software may be incorporated.
25  *
26  * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
27  * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
28  * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
30  * COMBINATION WITH YOUR PRODUCTS.
31  *
32  * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
34  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
36  * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
37  * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
38  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39  *
40    To build this tool, copy and paste the following into a command line:
41 
42    OS X:
43    gcc dns-sd.c -o dns-sd
44 
45    POSIX systems:
46    gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd
47 
48    Windows:
49    cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib
50    (may require that you run a Visual Studio script such as vsvars32.bat first)
51  */
52 
53 // For testing changes to dnssd_clientstub.c, uncomment this line and the code will be compiled
54 // with an embedded copy of the client stub instead of linking the system library version at runtime.
55 // This also useful to work around link errors when you're working on an older version of Mac OS X,
56 // and trying to build a newer version of the "dns-sd" command which uses new API entry points that
57 // aren't in the system's /usr/lib/libSystem.dylib.
58 //#define TEST_NEW_CLIENTSTUB 1
59 
60 #include <ctype.h>
61 #include <stdio.h>          // For stdout, stderr
62 #include <stdlib.h>         // For exit()
63 #include <string.h>         // For strlen(), strcpy()
64 #include <errno.h>          // For errno, EINTR
65 #include <time.h>
66 #include <sys/types.h>      // For u_char
67 #ifdef APPLE_OSX_mDNSResponder
68 #include <inttypes.h>       // For PRId64
69 #endif // APPLE_OSX_mDNSResponder
70 
71 #if APPLE_OSX_mDNSResponder
72 #include <xpc/xpc.h>
73 #include "xpc_clients.h"
74 #endif
75 
76 #ifdef _WIN32
77     #include <winsock2.h>
78     #include <ws2tcpip.h>
79     #include <Iphlpapi.h>
80     #include <process.h>
81 typedef int pid_t;
82     #define getpid     _getpid
83     #define strcasecmp _stricmp
84     #define snprintf   _snprintf
85 static const char kFilePathSep = '\\';
86     #ifndef HeapEnableTerminationOnCorruption
87     #     define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
88     #endif
89     #if !defined(IFNAMSIZ)
90      #define IFNAMSIZ 16
91     #endif
92     #define if_nametoindex if_nametoindex_win
93     #define if_indextoname if_indextoname_win
94 
95 typedef PCHAR (WINAPI * if_indextoname_funcptr_t)(ULONG index, PCHAR name);
96 typedef ULONG (WINAPI * if_nametoindex_funcptr_t)(PCSTR name);
97 
if_nametoindex_win(const char * ifname)98 unsigned if_nametoindex_win(const char *ifname)
99 {
100     HMODULE library;
101     unsigned index = 0;
102 
103     // Try and load the IP helper library dll
104     if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL )
105     {
106         if_nametoindex_funcptr_t if_nametoindex_funcptr;
107 
108         // On Vista and above there is a Posix like implementation of if_nametoindex
109         if ((if_nametoindex_funcptr = (if_nametoindex_funcptr_t) GetProcAddress(library, "if_nametoindex")) != NULL )
110         {
111             index = if_nametoindex_funcptr(ifname);
112         }
113 
114         FreeLibrary(library);
115     }
116 
117     return index;
118 }
119 
if_indextoname_win(unsigned ifindex,char * ifname)120 char * if_indextoname_win( unsigned ifindex, char *ifname)
121 {
122     HMODULE library;
123     char * name = NULL;
124 
125     // Try and load the IP helper library dll
126     if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL )
127     {
128         if_indextoname_funcptr_t if_indextoname_funcptr;
129 
130         // On Vista and above there is a Posix like implementation of if_indextoname
131         if ((if_indextoname_funcptr = (if_indextoname_funcptr_t) GetProcAddress(library, "if_indextoname")) != NULL )
132         {
133             name = if_indextoname_funcptr(ifindex, ifname);
134         }
135 
136         FreeLibrary(library);
137     }
138 
139     return name;
140 }
141 
_sa_len(const struct sockaddr * addr)142 static size_t _sa_len(const struct sockaddr *addr)
143 {
144     if (addr->sa_family == AF_INET) return (sizeof(struct sockaddr_in));
145     else if (addr->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6));
146     else return (sizeof(struct sockaddr));
147 }
148 
149 #   define SA_LEN(addr) (_sa_len(addr))
150 
151 #else
152     #include <unistd.h>         // For getopt() and optind
153     #include <netdb.h>          // For getaddrinfo()
154     #include <sys/time.h>       // For struct timeval
155     #include <sys/socket.h>     // For AF_INET
156     #include <netinet/in.h>     // For struct sockaddr_in()
157     #include <arpa/inet.h>      // For inet_addr()
158     #include <net/if.h>         // For if_nametoindex()
159 static const char kFilePathSep = '/';
160 // #ifndef NOT_HAVE_SA_LEN
161 //  #define SA_LEN(addr) ((addr)->sa_len)
162 // #else
163     #define SA_LEN(addr) (((addr)->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))
164 // #endif
165 #endif
166 
167 #if (TEST_NEW_CLIENTSTUB && !defined(__APPLE_API_PRIVATE))
168 #define __APPLE_API_PRIVATE 1
169 #endif
170 
171 // DNSServiceSetDispatchQueue is not supported on 10.6 & prior
172 #if !TEST_NEW_CLIENTSTUB && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ % 10) <= 1060)
173 #undef _DNS_SD_LIBDISPATCH
174 #endif
175 #include "dns_sd.h"
176 #include "dns_sd_private.h"
177 #include "ClientCommon.h"
178 #include <stdarg.h>
179 
180 
181 #if TEST_NEW_CLIENTSTUB
182 #include "../mDNSShared/dnssd_ipc.c"
183 #include "../mDNSShared/dnssd_clientlib.c"
184 #include "../mDNSShared/dnssd_clientstub.c"
185 #endif
186 
187 #ifndef MIN
188 #define	MIN(x, y)	((x) < (y) ? (x) : (y))
189 #endif
190 //*************************************************************************************************************
191 // Globals
192 
193 #define DS_FIXED_SIZE   4
194 typedef struct
195 {
196     unsigned short keyTag;
197     unsigned char alg;
198     unsigned char digestType;
199     unsigned char  *digest;
200 } rdataDS;
201 
202 #define DNSKEY_FIXED_SIZE    4
203 typedef struct
204 {
205     unsigned short flags;
206     unsigned char proto;
207     unsigned char alg;
208     unsigned char *data;
209 } rdataDNSKey;
210 
211 //size of rdataRRSIG excluding signerName and signature (which are variable fields)
212 #define RRSIG_FIXED_SIZE      18
213 typedef struct
214 {
215     unsigned short typeCovered;
216     unsigned char alg;
217     unsigned char labels;
218     unsigned int origTTL;
219     unsigned int sigExpireTime;
220     unsigned int sigInceptTime;
221     unsigned short keyTag;
222     char signerName[256];
223     //unsigned char *signature
224 } rdataRRSig;
225 
226 #define RR_TYPE_SIZE 16
227 
228 typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
229 
230 static int operation;
231 static uint32_t opinterface = kDNSServiceInterfaceIndexAny;
232 static DNSServiceRef client    = NULL;
233 static DNSServiceRef client_pa = NULL;  // DNSServiceRef for RegisterProxyAddressRecord
234 static DNSServiceRef sc1, sc2, sc3;     // DNSServiceRefs for kDNSServiceFlagsShareConnection testing
235 
236 static int num_printed;
237 static char addtest = 0;
238 static DNSRecordRef record = NULL;
239 static char myhinfoW[14] = "\002PC\012Windows XP";
240 static char myhinfoX[ 9] = "\003Mac\004OS X";
241 static char updatetest[3] = "\002AA";
242 static char bigNULL[8192];  // 8K is maximum rdata we support
243 
244 #if _DNS_SD_LIBDISPATCH
245 dispatch_queue_t main_queue;
246 dispatch_source_t timer_source;
247 #endif
248 
249 // Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this
250 #define LONG_TIME 100000000
251 
252 static volatile int stopNow = 0;
253 static volatile int timeOut = LONG_TIME;
254 
255 #if _DNS_SD_LIBDISPATCH
256 #define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) \
257     if (main_queue && (E) == kDNSServiceErr_ServiceNotRunning) { fprintf(stderr, "Error code %d\n", (E)); exit(0); }
258 #else
259 #define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E)
260 #endif
261 
262 //*************************************************************************************************************
263 // Supporting Utility Functions
GetRRClass(const char * s)264 static uint16_t GetRRClass(const char *s)
265 {
266     if (!strcasecmp(s, "IN"))
267         return kDNSServiceClass_IN;
268     else
269         return(atoi(s));
270 }
271 
GetRRType(const char * s)272 static uint16_t GetRRType(const char *s)
273 {
274     if      (!strcasecmp(s, "A"       )) return(kDNSServiceType_A);
275     else if (!strcasecmp(s, "NS"      )) return(kDNSServiceType_NS);
276     else if (!strcasecmp(s, "MD"      )) return(kDNSServiceType_MD);
277     else if (!strcasecmp(s, "MF"      )) return(kDNSServiceType_MF);
278     else if (!strcasecmp(s, "CNAME"   )) return(kDNSServiceType_CNAME);
279     else if (!strcasecmp(s, "SOA"     )) return(kDNSServiceType_SOA);
280     else if (!strcasecmp(s, "MB"      )) return(kDNSServiceType_MB);
281     else if (!strcasecmp(s, "MG"      )) return(kDNSServiceType_MG);
282     else if (!strcasecmp(s, "MR"      )) return(kDNSServiceType_MR);
283     else if (!strcasecmp(s, "NULL"    )) return(kDNSServiceType_NULL);
284     else if (!strcasecmp(s, "WKS"     )) return(kDNSServiceType_WKS);
285     else if (!strcasecmp(s, "PTR"     )) return(kDNSServiceType_PTR);
286     else if (!strcasecmp(s, "HINFO"   )) return(kDNSServiceType_HINFO);
287     else if (!strcasecmp(s, "MINFO"   )) return(kDNSServiceType_MINFO);
288     else if (!strcasecmp(s, "MX"      )) return(kDNSServiceType_MX);
289     else if (!strcasecmp(s, "TXT"     )) return(kDNSServiceType_TXT);
290     else if (!strcasecmp(s, "RP"      )) return(kDNSServiceType_RP);
291     else if (!strcasecmp(s, "AFSDB"   )) return(kDNSServiceType_AFSDB);
292     else if (!strcasecmp(s, "X25"     )) return(kDNSServiceType_X25);
293     else if (!strcasecmp(s, "ISDN"    )) return(kDNSServiceType_ISDN);
294     else if (!strcasecmp(s, "RT"      )) return(kDNSServiceType_RT);
295     else if (!strcasecmp(s, "NSAP"    )) return(kDNSServiceType_NSAP);
296     else if (!strcasecmp(s, "NSAP_PTR")) return(kDNSServiceType_NSAP_PTR);
297     else if (!strcasecmp(s, "SIG"     )) return(kDNSServiceType_SIG);
298     else if (!strcasecmp(s, "KEY"     )) return(kDNSServiceType_KEY);
299     else if (!strcasecmp(s, "PX"      )) return(kDNSServiceType_PX);
300     else if (!strcasecmp(s, "GPOS"    )) return(kDNSServiceType_GPOS);
301     else if (!strcasecmp(s, "AAAA"    )) return(kDNSServiceType_AAAA);
302     else if (!strcasecmp(s, "LOC"     )) return(kDNSServiceType_LOC);
303     else if (!strcasecmp(s, "NXT"     )) return(kDNSServiceType_NXT);
304     else if (!strcasecmp(s, "EID"     )) return(kDNSServiceType_EID);
305     else if (!strcasecmp(s, "NIMLOC"  )) return(kDNSServiceType_NIMLOC);
306     else if (!strcasecmp(s, "SRV"     )) return(kDNSServiceType_SRV);
307     else if (!strcasecmp(s, "ATMA"    )) return(kDNSServiceType_ATMA);
308     else if (!strcasecmp(s, "NAPTR"   )) return(kDNSServiceType_NAPTR);
309     else if (!strcasecmp(s, "KX"      )) return(kDNSServiceType_KX);
310     else if (!strcasecmp(s, "CERT"    )) return(kDNSServiceType_CERT);
311     else if (!strcasecmp(s, "A6"      )) return(kDNSServiceType_A6);
312     else if (!strcasecmp(s, "DNAME"   )) return(kDNSServiceType_DNAME);
313     else if (!strcasecmp(s, "SINK"    )) return(kDNSServiceType_SINK);
314     else if (!strcasecmp(s, "OPT"     )) return(kDNSServiceType_OPT);
315     else if (!strcasecmp(s, "TKEY"    )) return(kDNSServiceType_TKEY);
316     else if (!strcasecmp(s, "TSIG"    )) return(kDNSServiceType_TSIG);
317     else if (!strcasecmp(s, "IXFR"    )) return(kDNSServiceType_IXFR);
318     else if (!strcasecmp(s, "AXFR"    )) return(kDNSServiceType_AXFR);
319     else if (!strcasecmp(s, "MAILB"   )) return(kDNSServiceType_MAILB);
320     else if (!strcasecmp(s, "MAILA"   )) return(kDNSServiceType_MAILA);
321     else if (!strcasecmp(s, "dnskey"  )) return(kDNSServiceType_DNSKEY);
322     else if (!strcasecmp(s, "ds"      )) return(kDNSServiceType_DS);
323     else if (!strcasecmp(s, "rrsig"   )) return(kDNSServiceType_RRSIG);
324     else if (!strcasecmp(s, "nsec"    )) return(kDNSServiceType_NSEC);
325     else if (!strcasecmp(s, "SVCB"    )) return(kDNSServiceType_SVCB);
326     else if (!strcasecmp(s, "HTTPS"   )) return(kDNSServiceType_HTTPS);
327     else if (!strcasecmp(s, "ANY"     )) return(kDNSServiceType_ANY);
328     else return(atoi(s));
329 }
330 
DNSTypeName(unsigned short rr_type)331 static char *DNSTypeName(unsigned short rr_type)
332 {
333     switch (rr_type)
334     {
335         case kDNSServiceType_A:          return("Addr");
336         case kDNSServiceType_NS:         return("NS");
337         case kDNSServiceType_MD:         return("MD");
338         case kDNSServiceType_MF:         return("MF");
339         case kDNSServiceType_CNAME:      return("CNAME");
340         case kDNSServiceType_SOA:        return("SOA");
341         case kDNSServiceType_MB:         return("MB");
342         case kDNSServiceType_MG:         return("MG");
343         case kDNSServiceType_MR:         return("MR");
344         case kDNSServiceType_NULL:       return("NULL");
345         case kDNSServiceType_WKS:        return("WKS");
346         case kDNSServiceType_PTR:        return("PTR");
347         case kDNSServiceType_HINFO:      return("HINFO");
348         case kDNSServiceType_MINFO:      return("MINFO");
349         case kDNSServiceType_MX:         return("MX");
350         case kDNSServiceType_TXT:        return("TXT");
351         case kDNSServiceType_RP:         return("RP");
352         case kDNSServiceType_AFSDB:      return("AFSDB");
353         case kDNSServiceType_X25:        return("X25");
354         case kDNSServiceType_ISDN:       return("ISDN");
355         case kDNSServiceType_RT:         return("RT");
356         case kDNSServiceType_NSAP:       return("NSAP");
357         case kDNSServiceType_NSAP_PTR:   return("NSAP_PTR");
358         case kDNSServiceType_SIG:        return("SIG");
359         case kDNSServiceType_KEY:        return("KEY");
360         case kDNSServiceType_PX:         return("PX");
361         case kDNSServiceType_GPOS:       return("GPOS");
362         case kDNSServiceType_AAAA:       return("AAAA");
363         case kDNSServiceType_LOC:        return("LOC");
364         case kDNSServiceType_NXT:        return("NXT");
365         case kDNSServiceType_EID:        return("EID");
366         case kDNSServiceType_NIMLOC:     return("NIMLOC");
367         case kDNSServiceType_SRV:        return("SRV");
368         case kDNSServiceType_ATMA:       return("ATMA");
369         case kDNSServiceType_NAPTR:      return("NAPTR");
370         case kDNSServiceType_KX:         return("KX");
371         case kDNSServiceType_CERT:       return("CERT");
372         case kDNSServiceType_A6:         return("A6");
373         case kDNSServiceType_DNAME:      return("DNAME");
374         case kDNSServiceType_SINK:       return("SINK");
375         case kDNSServiceType_OPT:        return("OPT");
376         case kDNSServiceType_APL:        return("APL");
377         case kDNSServiceType_DS:         return("DS");
378         case kDNSServiceType_SSHFP:      return("SSHFP");
379         case kDNSServiceType_IPSECKEY:   return("IPSECKEY");
380         case kDNSServiceType_RRSIG:      return("RRSIG");
381         case kDNSServiceType_NSEC:       return("NSEC");
382         case kDNSServiceType_DNSKEY:     return("DNSKEY");
383         case kDNSServiceType_DHCID:      return("DHCID");
384         case kDNSServiceType_NSEC3:      return("NSEC3");
385         case kDNSServiceType_NSEC3PARAM: return("NSEC3PARAM");
386         case kDNSServiceType_HIP:        return("HIP");
387         case kDNSServiceType_SPF:        return("SPF");
388         case kDNSServiceType_UINFO:      return("UINFO");
389         case kDNSServiceType_UID:        return("UID");
390         case kDNSServiceType_GID:        return("GID");
391         case kDNSServiceType_UNSPEC:     return("UNSPEC");
392         case kDNSServiceType_TKEY:       return("TKEY");
393         case kDNSServiceType_TSIG:       return("TSIG");
394         case kDNSServiceType_IXFR:       return("IXFR");
395         case kDNSServiceType_AXFR:       return("AXFR");
396         case kDNSServiceType_MAILB:      return("MAILB");
397         case kDNSServiceType_MAILA:      return("MAILA");
398         case kDNSServiceType_SVCB:       return("SVCB");
399         case kDNSServiceType_HTTPS:      return("HTTPS");
400         case kDNSServiceType_ANY:        return("ANY");
401         default:
402         {
403             static char buffer[RR_TYPE_SIZE];
404             snprintf(buffer, sizeof(buffer), "TYPE%d", rr_type);
405             return(buffer);
406         }
407     }
408 }
409 
swap16(unsigned short x)410 static unsigned short swap16(unsigned short x)
411 {
412     unsigned char *ptr = (unsigned char *)&x;
413     return (unsigned short)((unsigned short)ptr[0] << 8 | ptr[1]);
414 }
415 
swap32(unsigned int x)416 static unsigned int swap32(unsigned int x)
417 {
418     unsigned char *ptr = (unsigned char *)&x;
419     return (unsigned int)((unsigned int)ptr[0] << 24 | (unsigned int)ptr[1] << 16 | (unsigned int)ptr[2] << 8 | ptr[3]);
420 }
keytag(unsigned char * key,unsigned int keysize)421 static unsigned int keytag(unsigned char *key, unsigned int keysize)
422 {
423     unsigned long ac;
424     unsigned int i;
425 
426     for (ac = 0, i = 0; i < keysize; ++i)
427         ac += (i & 1) ? key[i] : key[i] << 8;
428     ac += (ac >> 16) & 0xFFFF;
429     return ac & 0xFFFF;
430 }
431 
432 // Base 64 encoding according to <https://tools.ietf.org/html/rfc4648#section-4>.
433 #define kBase64EncodingTable "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
434 
base64Encode(char * buffer,size_t buflen,void * rdata,size_t rdlen)435 static void base64Encode(char *buffer, size_t buflen, void *rdata, size_t rdlen)
436 {
437     const uint8_t *src = (const uint8_t *)rdata;
438     const uint8_t *const end = &src[rdlen];
439     char *dst = buffer;
440     const char *lim;
441 
442     if (buflen == 0) return;
443     lim = &buffer[buflen - 1];
444     while ((src < end) && (dst < lim))
445     {
446         uint32_t i;
447         const size_t rem = (size_t)(end - src);
448 
449         // Form a 24-bit input group. If less than 24 bits remain, pad with zero bits.
450         if (     rem >= 3) i = (src[0] << 16) | (src[1] << 8) | src[2]; // 24 bits are equal to 4 6-bit groups.
451         else if (rem == 2) i = (src[0] << 16) | (src[1] << 8);          // 16 bits are treated as 3 6-bit groups + 1 pad
452         else               i =  src[0] << 16;                           //  8 bits are treated as 2 6-bit groups + 2 pads
453 
454         // Encode each 6-bit group.
455                        *dst++ =              kBase64EncodingTable[(i >> 18) & 0x3F];
456         if (dst < lim) *dst++ =              kBase64EncodingTable[(i >> 12) & 0x3F];
457         if (dst < lim) *dst++ = (rem >= 2) ? kBase64EncodingTable[(i >>  6) & 0x3F] : '=';
458         if (dst < lim) *dst++ = (rem >= 3) ? kBase64EncodingTable[ i        & 0x3F] : '=';
459         src += (rem > 3) ? 3 : rem;
460     }
461     *dst = '\0';
462 }
463 
GetProtocol(const char * s)464 static DNSServiceProtocol GetProtocol(const char *s)
465 {
466     if      (!strcasecmp(s, "v4"      )) return(kDNSServiceProtocol_IPv4);
467     else if (!strcasecmp(s, "v6"      )) return(kDNSServiceProtocol_IPv6);
468     else if (!strcasecmp(s, "v4v6"    )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6);
469     else if (!strcasecmp(s, "v6v4"    )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6);
470     else if (!strcasecmp(s, "udp"     )) return(kDNSServiceProtocol_UDP);
471     else if (!strcasecmp(s, "tcp"     )) return(kDNSServiceProtocol_TCP);
472     else if (!strcasecmp(s, "udptcp"  )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP);
473     else if (!strcasecmp(s, "tcpudp"  )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP);
474     else return(atoi(s));
475 }
476 
477 
478 //*************************************************************************************************************
479 // Sample callback functions for each of the operation types
480 
481 #define printtimestamp() printtimestamp_F(stdout)
482 
printtimestamp_F(FILE * outstream)483 static void printtimestamp_F(FILE *outstream)
484 {
485     struct tm tm;
486     int ms;
487     static char date[16];
488     static char new_date[16];
489 #ifdef _WIN32
490     SYSTEMTIME sysTime;
491     time_t uct = time(NULL);
492     tm = *localtime(&uct);
493     GetLocalTime(&sysTime);
494     ms = sysTime.wMilliseconds;
495 #else
496     struct timeval tv;
497     gettimeofday(&tv, NULL);
498     localtime_r((time_t*)&tv.tv_sec, &tm);
499     ms = tv.tv_usec/1000;
500 #endif
501     strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm);
502     if (strncmp(date, new_date, sizeof(new_date)))
503     {
504         fprintf(outstream, "DATE: ---%s---\n", new_date); //display date only if it has changed
505         strncpy(date, new_date, sizeof(date));
506     }
507     fprintf(outstream, "%2d:%02d:%02d.%03d  ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms);
508 }
509 
510 // formating time to RFC 4034 format
FormatTime(unsigned long te,unsigned char * buf,int bufsize)511 static void FormatTime(unsigned long te, unsigned char *buf, int bufsize)
512 {
513     struct tm tmTime;
514 #ifdef _WIN32
515 	__time32_t t = (__time32_t) te;
516 	_gmtime32_s(&tmTime, &t);
517 #else
518     // Time since epoch : strftime takes "tm". Convert seconds to "tm" using
519     // gmtime_r first and then use strftime
520 	time_t t = (time_t)te;
521 	gmtime_r(&t, &tmTime);
522 #endif
523     strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime);
524 }
525 
print_usage(const char * arg0,int print_all)526 static void print_usage(const char *arg0, int print_all)
527 {
528     // Print the commonly used command line options.  These are listed in "the order they have been in historically".
529     fprintf(stderr, "%s -E                          (Enumerate recommended registration domains)\n", arg0);
530     fprintf(stderr, "%s -F                          (Enumerate recommended browsing     domains)\n", arg0);
531     fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...]         (Register a service)\n", arg0);
532     fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...] (Register Proxy)\n", arg0);
533     fprintf(stderr, "%s -B        <Type> <Domain>                 (Browse for service instances)\n", arg0);
534     fprintf(stderr, "%s -Z        <Type> <Domain>           (Output results in Zone File format)\n", arg0);
535     fprintf(stderr, "%s -L <Name> <Type> <Domain>        (Resolve (‘lookup’) a service instance)\n", arg0);
536     fprintf(stderr, "%s -Q <name> <rrtype> <rrclass>         (Generic query for any record type)\n", arg0);
537     fprintf(stderr, "%s -q <name> <rrtype> <rrclass>     (Generic query, using SuppressUnusable)\n", arg0);
538     fprintf(stderr, "%s -G v4/v6/v4v6 <hostname>          (Get address information for hostname)\n", arg0);
539     fprintf(stderr, "%s -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL>           (NAT Port Mapping)\n", arg0);
540     fprintf(stderr, "%s -H                               (Print usage for complete command list)\n", arg0);
541     fprintf(stderr, "%s -V            (Get version of currently running daemon / system service)\n", arg0);
542 #ifdef APPLE_OSX_mDNSResponder
543     fprintf(stderr, "%s -O [-compress|-stdout](Dump the state of mDNSResponder to file / STDOUT)\n", arg0);
544 #endif // APPLE_OSX_mDNSResponder
545 
546     if (print_all)  // Print all available options for dns-sd tool.  Keep these in alphabetical order for easier maintenance.
547     {
548         fprintf(stderr, "\n");
549         fprintf(stderr, "%s -A                              (Test Adding/Updating/Deleting a record)\n", arg0);
550         fprintf(stderr, "%s -C <name> <rrtype> <rrclass>           (Query; reconfirming each result)\n", arg0);
551         fprintf(stderr, "%s -I           (Test registering and then immediately updating TXT record)\n", arg0);
552         fprintf(stderr, "%s -N                                     (Test adding a large NULL record)\n", arg0);
553         fprintf(stderr, "%s -M              (Test creating a registration with multiple TXT records)\n", arg0);
554         fprintf(stderr, "%s -S                         (Test multiple operations on a shared socket)\n", arg0);
555         fprintf(stderr, "%s -T                                    (Test creating a large TXT record)\n", arg0);
556         fprintf(stderr, "%s -U                                          (Test updating a TXT record)\n", arg0);
557         fprintf(stderr, "%s -ble                                  (Use kDNSServiceInterfaceIndexBLE)\n", arg0);
558         fprintf(stderr, "%s -i <Interface>         (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0);
559         fprintf(stderr, "%s -includep2p                        (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0);
560         fprintf(stderr, "%s -includeAWDL                      (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0);
561         fprintf(stderr, "%s -intermediates            (Set kDNSServiceFlagsReturnIntermediates flag)\n", arg0);
562         fprintf(stderr, "%s -ku                               (Set kDNSServiceFlagsKnownUnique flag)\n", arg0);
563         fprintf(stderr, "%s -lo                          (Run dns-sd cmd using local only interface)\n", arg0);
564         fprintf(stderr, "%s -p2p                                  (Use kDNSServiceInterfaceIndexP2P)\n", arg0);
565         fprintf(stderr, "%s -tc                    (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0);
566         fprintf(stderr, "%s -test                                  (Run basic API input range tests)\n", arg0);
567         fprintf(stderr, "%s -t1                              (Set kDNSServiceFlagsThresholdOne flag)\n", arg0);
568         fprintf(stderr, "%s -tFinder                      (Set kDNSServiceFlagsThresholdFinder flag)\n", arg0);
569         fprintf(stderr, "%s -timeout                              (Set kDNSServiceFlagsTimeout flag)\n", arg0);
570         fprintf(stderr, "%s -unicastResponse              (Set kDNSServiceFlagsUnicastResponse flag)\n", arg0);
571         fprintf(stderr, "%s -autoTrigger                      (Set kDNSServiceFlagsAutoTrigger flag)\n", arg0);
572         fprintf(stderr, "%s -enableDNSSEC              (Enable DNSSEC validation for the '-Q' query)\n", arg0);
573     }
574 }
575 
576 #define DomainMsg(X) (((X) &kDNSServiceFlagsDefault) ? "(Default)" : \
577                       ((X) &kDNSServiceFlagsAdd)     ? "Added"     : "Removed")
578 
579 #define MAX_LABELS 128
580 
enum_reply(DNSServiceRef sdref,const DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * replyDomain,void * context)581 static void DNSSD_API enum_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex,
582                                  DNSServiceErrorType errorCode, const char *replyDomain, void *context)
583 {
584     DNSServiceFlags partialflags = flags & ~(kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault);
585     int labels = 0, depth = 0, i, initial = 0;
586     char text[64];
587     const char *label[MAX_LABELS];
588 
589     (void)sdref;        // Unused
590     (void)ifIndex;      // Unused
591     (void)context;      // Unused
592     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
593 
594     // 1. Print the header
595     if (num_printed++ == 0) printf("Timestamp     Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing");
596     printtimestamp();
597     if (errorCode)
598         printf("Error code %d\n", errorCode);
599     else if (!*replyDomain)
600         printf("Error: No reply domain\n");
601     else
602     {
603         printf("%-10s", DomainMsg(flags));
604         printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : "");
605         if (partialflags) printf("Flags: %4X  ", partialflags);
606         else printf("             ");
607 
608         // 2. Count the labels
609         while (replyDomain && *replyDomain && labels < MAX_LABELS)
610         {
611             label[labels++] = replyDomain;
612             replyDomain = GetNextLabel(replyDomain, text);
613         }
614 
615         // 3. Decide if we're going to clump the last two or three labels (e.g. "apple.com", or "nicta.com.au")
616         if      (labels >= 3 && replyDomain - label[labels-1] <= 3 && label[labels-1] - label[labels-2] <= 4) initial = 3;
617         else if (labels >= 2 && replyDomain - label[labels-1] <= 4) initial = 2;
618         else initial = 1;
619         labels -= initial;
620 
621         // 4. Print the initial one-, two- or three-label clump
622         for (i=0; i<initial; i++)
623         {
624             GetNextLabel(label[labels+i], text);
625             if (i>0) printf(".");
626             printf("%s", text);
627         }
628         printf("\n");
629 
630         // 5. Print the remainder of the hierarchy
631         for (depth=0; depth<labels; depth++)
632         {
633             printf("                                             ");
634             for (i=0; i<=depth; i++) printf("- ");
635             GetNextLabel(label[labels-1-depth], text);
636             printf("> %s\n", text);
637         }
638     }
639 
640     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
641 }
642 
CopyLabels(char * dst,const char * lim,const char ** srcp,int labels)643 static int CopyLabels(char *dst, const char *lim, const char **srcp, int labels)
644 {
645     const char *src = *srcp;
646     while (*src != '.' || --labels > 0)
647     {
648         if (*src == '\\') *dst++ = *src++;  // Make sure "\." doesn't confuse us
649         if (!*src || dst >= lim) return -1;
650         *dst++ = *src++;
651         if (!*src || dst >= lim) return -1;
652     }
653     *dst++ = 0;
654     *srcp = src + 1;    // skip over final dot
655     return 0;
656 }
657 
zonedata_resolve(DNSServiceRef sdref,const DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * fullname,const char * hosttarget,uint16_t opaqueport,uint16_t txtLen,const unsigned char * txt,void * context)658 static void DNSSD_API zonedata_resolve(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
659                                        const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txt, void *context)
660 {
661     union { uint16_t s; u_char b[2]; } port = { opaqueport };
662     uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
663 
664     const char *p = fullname;
665     char n[kDNSServiceMaxDomainName];
666     char t[kDNSServiceMaxDomainName];
667 
668     const unsigned char *max = txt + txtLen;
669 
670     (void)sdref;        // Unused
671     (void)ifIndex;      // Unused
672     (void)context;      // Unused
673 
674     //if (!(flags & kDNSServiceFlagsAdd)) return;
675     if (errorCode) { printf("Error code %d\n", errorCode); return; }
676 
677     if (CopyLabels(n, n + kDNSServiceMaxDomainName, &p, 3)) return;     // Fetch name+type
678     p = fullname;
679     if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 1)) return;     // Skip first label
680     if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 2)) return;     // Fetch next two labels (service type)
681 
682     if (num_printed++ == 0)
683     {
684         printf("\n");
685         printf("; To direct clients to browse a different domain, substitute that domain in place of '@'\n");
686         printf("%-47s PTR     %s\n", "lb._dns-sd._udp", "@");
687         printf("\n");
688         printf("; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.\n");
689         printf("; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local\n");
690         printf("; names with the correct fully-qualified (unicast) domain name of the target host offering the service.\n");
691     }
692 
693     printf("\n");
694     printf("%-47s PTR     %s\n", t, n);
695     printf("%-47s SRV     0 0 %d %s ; Replace with unicast FQDN of target host\n", n, PortAsNumber, hosttarget);
696     printf("%-47s TXT    ", n);
697 
698     while (txt < max)
699     {
700         const unsigned char *const end = txt + 1 + txt[0];
701         txt++;      // Skip over length byte
702         printf(" \"");
703         while (txt<end)
704         {
705             if (*txt == '\\' || *txt == '\"') printf("\\");
706             printf("%c", *txt++);
707         }
708         printf("\"");
709     }
710     printf("\n");
711 
712     DNSServiceRefDeallocate(sdref);
713     free(context);
714 
715     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
716 }
717 
zonedata_browse(DNSServiceRef sdref,const DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * replyName,const char * replyType,const char * replyDomain,void * context)718 static void DNSSD_API zonedata_browse(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
719                                       const char *replyName, const char *replyType, const char *replyDomain, void *context)
720 {
721     DNSServiceRef *newref;
722 
723     (void)sdref;        // Unused
724     (void)context;      // Unused
725     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
726 
727     if (!(flags & kDNSServiceFlagsAdd)) return;
728     if (errorCode) { printf("Error code %d\n", errorCode); return; }
729 
730     newref = malloc(sizeof(*newref));
731     *newref = client;
732     DNSServiceResolve(newref, kDNSServiceFlagsShareConnection, ifIndex, replyName, replyType, replyDomain, zonedata_resolve, newref);
733 }
734 
browse_reply(DNSServiceRef sdref,const DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * replyName,const char * replyType,const char * replyDomain,void * context)735 static void DNSSD_API browse_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
736                                    const char *replyName, const char *replyType, const char *replyDomain, void *context)
737 {
738     char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
739     (void)sdref;        // Unused
740     (void)context;      // Unused
741     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
742 
743     if (num_printed++ == 0) printf("Timestamp     A/R    Flags  if %-20s %-20s %s\n", "Domain", "Service Type", "Instance Name");
744     printtimestamp();
745     if (errorCode)
746         printf("Error code %d\n", errorCode);
747     else
748         printf("%s %8X %3d %-20s %-20s %s\n",
749                 op, flags, ifIndex, replyDomain, replyType, replyName);
750     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
751 
752     // To test selective cancellation of operations of shared sockets,
753     // cancel the current operation when we've got a multiple of five results
754     //if (operation == 'S' && num_printed % 5 == 0) DNSServiceRefDeallocate(sdref);
755 }
756 
ShowTXTRecord(uint16_t txtLen,const unsigned char * txtRecord)757 static void ShowTXTRecord(uint16_t txtLen, const unsigned char *txtRecord)
758 {
759     const unsigned char *ptr = txtRecord;
760     const unsigned char *max = txtRecord + txtLen;
761     while (ptr < max)
762     {
763         const unsigned char *const end = ptr + 1 + ptr[0];
764         if (end > max) { printf("<< invalid data >>"); break; }
765         if (++ptr < end) printf(" ");   // As long as string is non-empty, begin with a space
766         while (ptr<end)
767         {
768             // We'd like the output to be shell-friendly, so that it can be copied and pasted unchanged into a "dns-sd -R" command.
769             // However, this is trickier than it seems. Enclosing a string in double quotes doesn't necessarily make it
770             // shell-safe, because shells still expand variables like $foo even when they appear inside quoted strings.
771             // Enclosing a string in single quotes is better, but when using single quotes even backslash escapes are ignored,
772             // meaning there's simply no way to represent a single quote (or apostrophe) inside a single-quoted string.
773             // The only remaining solution is not to surround the string with quotes at all, but instead to use backslash
774             // escapes to encode spaces and all other known shell metacharacters.
775             // (If we've missed any known shell metacharacters, please let us know.)
776             // In addition, non-printing ascii codes (0-31) are displayed as \xHH, using a two-digit hex value.
777             // Because '\' is itself a shell metacharacter (the shell escape character), it has to be escaped as "\\" to survive
778             // the round-trip to the shell and back. This means that a single '\' is represented here as EIGHT backslashes:
779             // The C compiler eats half of them, resulting in four appearing in the output.
780             // The shell parses those four as a pair of "\\" sequences, passing two backslashes to the "dns-sd -R" command.
781             // The "dns-sd -R" command interprets this single "\\" pair as an escaped literal backslash. Sigh.
782             if (strchr(" &;`'\"|*?~<>^()[]{}$", *ptr)) printf("\\");
783             if      (*ptr == '\\') printf("\\\\\\\\");
784             else if (*ptr >= ' ' ) printf("%c",        *ptr);
785             else printf("\\\\x%02X", *ptr);
786             ptr++;
787         }
788     }
789 }
790 
resolve_reply(DNSServiceRef sdref,const DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * fullname,const char * hosttarget,uint16_t opaqueport,uint16_t txtLen,const unsigned char * txtRecord,void * context)791 static void DNSSD_API resolve_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
792                                     const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txtRecord, void *context)
793 {
794     union { uint16_t s; u_char b[2]; } port = { opaqueport };
795     uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
796 
797     (void)sdref;        // Unused
798     (void)ifIndex;      // Unused
799     (void)context;      // Unused
800     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
801 
802     printtimestamp();
803 
804     printf("%s ", fullname);
805 
806     if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record");
807     else if (errorCode) printf("error code %d\n", errorCode);
808     else printf("can be reached at %s:%u (interface %d)", hosttarget, PortAsNumber, ifIndex);
809 
810     if (flags) printf(" Flags: %X", flags);
811 
812     // Don't show degenerate TXT records containing nothing but a single empty string
813     if (!errorCode && txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); }
814 
815     printf("\n");
816 
817     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
818 }
819 
myTimerCallBack(void)820 static void myTimerCallBack(void)
821 {
822     DNSServiceErrorType err = kDNSServiceErr_Unknown;
823 
824     switch (operation)
825     {
826     case 'A':
827     {
828         switch (addtest)
829         {
830         case 0: printf("Adding Test HINFO record\n");
831             err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_HINFO, sizeof(myhinfoW), &myhinfoW[0], 0);
832             addtest = 1;
833             break;
834         case 1: printf("Updating Test HINFO record\n");
835             err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 0);
836             addtest = 2;
837             break;
838         case 2: printf("Removing Test HINFO record\n");
839             err = DNSServiceRemoveRecord(client, record, 0);
840             addtest = 0;
841             break;
842         }
843     }
844     break;
845 
846     case 'U':
847     {
848         if (updatetest[1] != 'Z') updatetest[1]++;
849         else updatetest[1] = 'A';
850         // The following line toggles the string length between 1 and 2 characters.
851         updatetest[0] = 3 - updatetest[0];
852         updatetest[2] = updatetest[1];
853         printtimestamp();
854         printf("Updating Test TXT record to %c\n", updatetest[1]);
855         err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 0);
856     }
857     break;
858 
859     case 'N':
860     {
861         printf("Adding big NULL record\n");
862         err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0);
863         if (err) printf("Failed: %d\n", err);else printf("Succeeded\n");
864         timeOut = LONG_TIME;
865 #if _DNS_SD_LIBDISPATCH
866         if (timer_source)
867             dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC),
868                                       (uint64_t)timeOut * NSEC_PER_SEC, 0);
869 #endif
870     }
871     break;
872     }
873 
874     if (err != kDNSServiceErr_NoError)
875     {
876         fprintf(stderr, "DNSService add/update/remove failed %ld\n", (long int)err);
877         stopNow = 1;
878     }
879 }
880 
reg_reply(DNSServiceRef sdref,const DNSServiceFlags flags,DNSServiceErrorType errorCode,const char * name,const char * regtype,const char * domain,void * context)881 static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode,
882                                 const char *name, const char *regtype, const char *domain, void *context)
883 {
884     (void)sdref;    // Unused
885     (void)flags;    // Unused
886     (void)context;  // Unused
887     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
888 
889     printtimestamp();
890     printf("Got a reply for service %s.%s%s: ", name, regtype, domain);
891 
892     if (errorCode == kDNSServiceErr_NoError)
893     {
894         if (flags & kDNSServiceFlagsAdd) printf("Name now registered and active\n");
895         else printf("Name registration removed\n");
896         if (operation == 'A' || operation == 'U' || operation == 'N')
897         {
898             timeOut = 5;
899 #if _DNS_SD_LIBDISPATCH
900             if (timer_source)
901                 dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC),
902                                           (uint64_t)timeOut * NSEC_PER_SEC, 0);
903 #endif
904         }
905     }
906     else if (errorCode == kDNSServiceErr_NameConflict)
907     {
908         printf("Name in use, please choose another\n");
909         exit(-1);
910     }
911     else
912         printf("Error %d\n", errorCode);
913 
914     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
915 }
916 
snprintf_safe(char * str,size_t size,const char * format,...)917 static int snprintf_safe(char *str, size_t size, const char *format, ...)
918 {
919     int length = 0;
920     va_list ptr;
921     va_start(ptr, format);
922     int result = vsnprintf(str, size, format, ptr);
923     va_end(ptr);
924     if (result > 0 && size > 0)
925     {
926         length = MIN((size_t)result, size-1);
927     }
928     return length;
929 }
930 
931 // Output the wire-format domainname pointed to by rd
snprintd(char * p,int max,const unsigned char ** rd)932 static int snprintd(char *p, int max, const unsigned char **rd)
933 {
934     const char *const buf = p;
935     const char *const end = p + max;
936     while (**rd)
937     {
938         p += snprintf_safe(p, end-p, "%.*s.", **rd, *rd+1);
939         *rd += 1 + **rd;
940     }
941     *rd += 1;   // Advance over the final zero byte
942     return(p-buf);
943 }
944 
ParseDNSSECRecords(uint16_t rrtype,char * rdb,size_t rdb_size,unsigned const char * rd,uint16_t rdlen)945 static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, size_t rdb_size, unsigned const char *rd, uint16_t rdlen)
946 {
947     char *p = rdb;
948     switch (rrtype)
949     {
950         case kDNSServiceType_DS:
951         {
952             unsigned char *ptr;
953             int i;
954             rdataDS *rrds = (rdataDS *)rd;
955             p += snprintf_safe(p, rdb + rdb_size - p, "%d  %d  %d  ",
956                           rrds->alg, swap16(rrds->keyTag), rrds->digestType);
957             ptr = (unsigned char *)(rd + DS_FIXED_SIZE);
958             for (i = 0; i < (rdlen - DS_FIXED_SIZE); i++)
959                 p += snprintf_safe(p, rdb + rdb_size - p, "%x", ptr[i]);
960             break;
961         }
962 
963         case kDNSServiceType_DNSKEY:
964         {
965             rdataDNSKey *rrkey = (rdataDNSKey *)rd;
966             p += snprintf_safe(p, rdb + rdb_size - p, "%d  %d  %d  %u ", swap16(rrkey->flags), rrkey->proto,
967                           rrkey->alg, (unsigned int)keytag((unsigned char *)rrkey, rdlen));
968             base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + DNSKEY_FIXED_SIZE), rdlen - DNSKEY_FIXED_SIZE);
969             break;
970         }
971 
972         case kDNSServiceType_NSEC:
973         {
974             unsigned char *next = (unsigned char *)rd;
975             int len, bitmaplen;
976             int win, wlen, type;
977             unsigned char *bmap;
978             char *l = NULL;
979 
980             l = p;
981             p += snprintd(p, rdb + rdb_size - p, &rd);
982             len = p - l + 1;
983 
984             bitmaplen = rdlen - len;
985             bmap = (unsigned char *)((unsigned char *)next + len);
986 
987             while (bitmaplen > 0)
988             {
989                 int i;
990 
991                 if (bitmaplen < 3)
992                 {
993                     printf("Case NSEC: malformed nsec, bitmaplen %d short\n", bitmaplen);
994                     break;
995                 }
996 
997                 win = *bmap++;
998                 wlen = *bmap++;
999                 bitmaplen -= 2;
1000                 if (bitmaplen < wlen || wlen < 1 || wlen > 32)
1001                 {
1002                     printf("Case NSEC: malformed nsec, bitmaplen %d wlen %d\n", bitmaplen, wlen);
1003                     break;
1004                 }
1005                 if (win < 0 || win >= 256)
1006                 {
1007                     printf("Case NSEC: malformed nsec, bad window win %d\n", win);
1008                     break;
1009                 }
1010                 type = win * 256;
1011                 for (i = 0; i < wlen * 8; i++)
1012                 {
1013                     if (bmap[i>>3] & (128 >> (i&7)))
1014                         p += snprintf_safe(p, rdb + rdb_size - p, " %s ", DNSTypeName(type + i));
1015                 }
1016                 bmap += wlen;
1017                 bitmaplen -= wlen;
1018             }
1019             break;
1020         }
1021 
1022         case kDNSServiceType_RRSIG:
1023         {
1024             rdataRRSig *rrsig = (rdataRRSig *)rd;
1025             unsigned char expTimeBuf[64];
1026             unsigned char inceptTimeBuf[64];
1027             unsigned long inceptClock;
1028             unsigned long expClock;
1029             const unsigned char *q = NULL;
1030             char *k = NULL;
1031             int len;
1032 
1033             expClock = (unsigned long)swap32(rrsig->sigExpireTime);
1034             FormatTime(expClock, expTimeBuf, sizeof(expTimeBuf));
1035 
1036             inceptClock = (unsigned long)swap32(rrsig->sigInceptTime);
1037             FormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf));
1038 
1039             p += snprintf_safe(p, rdb + rdb_size - p, " %-7s  %d  %d  %d  %s  %s  %7d  ",
1040                           DNSTypeName(swap16(rrsig->typeCovered)), rrsig->alg, rrsig->labels, swap32(rrsig->origTTL),
1041                           expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag));
1042 
1043             q = (const unsigned char *)&rrsig->signerName;
1044             k = p;
1045             p += snprintd(p, rdb + rdb_size - p, &q);
1046             len = p - k + 1;
1047 
1048             if ((&rdb[rdb_size] - p) >= 2)
1049             {
1050                 *p++ = ' ';
1051                 *p   = '\0';
1052             }
1053             base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + len + RRSIG_FIXED_SIZE), rdlen - (len + RRSIG_FIXED_SIZE));
1054             break;
1055         }
1056     }
1057     return;
1058 }
1059 
qr_reply(DNSServiceRef sdref,const DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * fullname,uint16_t rrtype,uint16_t rrclass,uint16_t rdlen,const void * rdata,uint32_t ttl,void * context)1060 static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
1061                                const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context)
1062 {
1063     char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
1064     const unsigned char *rd  = rdata;
1065     const unsigned char *end = (const unsigned char *) rdata + rdlen;
1066     char rdb[1000] = "0.0.0.0", *p = rdb;
1067     int unknowntype = 0;
1068     char dnssec_status[15] = "Unknown";
1069     char rr_type[RR_TYPE_SIZE];
1070     char rr_class[6];
1071     DNSServiceFlags check_flags = flags;//local flags for dnssec status checking
1072     int8_t enable_dnssec = ((check_flags & kDNSServiceFlagsEnableDNSSEC) != 0);
1073     static int8_t enabled_dnssec_before = -1;
1074 
1075     if (enabled_dnssec_before == -1) {
1076         enabled_dnssec_before = enable_dnssec;
1077     }
1078 
1079     (void)sdref;    // Unused
1080     (void)ifIndex;  // Unused
1081     (void)ttl;      // Unused
1082     (void)context;  // Unused
1083     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
1084 
1085     if (num_printed++ == 0)
1086     {
1087         printf("Timestamp     A/R    Flags if %-30s%-6s%-7s%s Rdata\n", "Name", "Type", "Class", enable_dnssec ? " DNSSECResult  " : "");
1088     }
1089     printtimestamp();
1090 
1091     switch (rrclass)
1092     {
1093         case kDNSServiceClass_IN:
1094             strncpy(rr_class, "IN", sizeof(rr_class));
1095             break;
1096         default:
1097             snprintf(rr_class, sizeof(rr_class), "%d", rrclass);
1098             break;
1099     }
1100     strncpy(rr_type, DNSTypeName(rrtype), sizeof(rr_type));
1101 
1102     if (!errorCode) //to avoid printing garbage in rdata
1103     {
1104         switch (rrtype)
1105         {
1106             case kDNSServiceType_A:
1107                 snprintf_safe(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]);
1108                 break;
1109 
1110             case kDNSServiceType_NS:
1111             case kDNSServiceType_CNAME:
1112             case kDNSServiceType_PTR:
1113             case kDNSServiceType_DNAME:
1114                 snprintd(p, sizeof(rdb), &rd);
1115                 break;
1116 
1117             case kDNSServiceType_SOA:
1118                 p += snprintd(p, rdb + sizeof(rdb) - p, &rd);           // mname
1119                 p += snprintf_safe(p, rdb + sizeof(rdb) - p, " ");
1120                 p += snprintd(p, rdb + sizeof(rdb) - p, &rd);           // rname
1121                      snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d",
1122                          ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4]));
1123                 break;
1124 
1125             case kDNSServiceType_AAAA:
1126                 snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
1127                     rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7],
1128                     rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]);
1129                 break;
1130 
1131             case kDNSServiceType_SRV:
1132                 p += snprintf_safe(p, rdb + sizeof(rdb) - p, "%d %d %d ",        // priority, weight, port
1133                          ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4)));
1134                 rd += 6;
1135                      snprintd(p, rdb + sizeof(rdb) - p, &rd);               // target host
1136                 break;
1137 
1138             case kDNSServiceType_DS:
1139             case kDNSServiceType_DNSKEY:
1140             case kDNSServiceType_NSEC:
1141             case kDNSServiceType_RRSIG:
1142                 ParseDNSSECRecords(rrtype, rdb, sizeof(rdb), rd, rdlen);
1143                 break;
1144 
1145             default:
1146                 snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : "");
1147                 unknowntype = 1;
1148                 break;
1149         }
1150     }
1151 
1152     if (check_flags & kDNSServiceFlagsSecure)
1153         strncpy(dnssec_status, "Secure            ", sizeof(dnssec_status));
1154     else if (check_flags & kDNSServiceFlagsInsecure)
1155         strncpy(dnssec_status, "Insecure          ", sizeof(dnssec_status));
1156     else if (check_flags & kDNSServiceFlagsIndeterminate)
1157         strncpy(dnssec_status, "Indeterminate     ", sizeof(dnssec_status));
1158     else if (check_flags & kDNSServiceFlagsBogus)
1159         strncpy(dnssec_status, "Bogus             ", sizeof(dnssec_status));
1160     else
1161         strncpy(dnssec_status, "                  ", sizeof(dnssec_status));
1162 
1163     printf("%s%9X%3d %-30s%-7s%-6s %s%s",
1164         op, flags, ifIndex, fullname, rr_type, rr_class, enabled_dnssec_before ? dnssec_status : "", rdb);
1165 
1166 
1167     if (unknowntype)
1168     {
1169         while (rd < end)
1170             printf(" %02X", *rd++);
1171     }
1172     if (errorCode)
1173     {
1174         if (errorCode == kDNSServiceErr_NoSuchRecord)
1175             printf("    No Such Record");
1176         else if (errorCode == kDNSServiceErr_NoAuth)
1177             printf("    No Authorization");
1178         else if (errorCode == kDNSServiceErr_Timeout)
1179         {
1180             printf("    No Such Record\n");
1181             printf("Query Timed Out\n");
1182             exit(1);
1183         }
1184     }
1185     printf("\n");
1186 
1187     if (operation == 'C')
1188         if (flags & kDNSServiceFlagsAdd)
1189             DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata);
1190 
1191     if (!(flags & kDNSServiceFlagsMoreComing))
1192         fflush(stdout);
1193 }
1194 
port_mapping_create_reply(DNSServiceRef sdref,DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,uint32_t publicAddress,uint32_t protocol,uint16_t privatePort,uint16_t publicPort,uint32_t ttl,void * context)1195 static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, uint32_t publicAddress, uint32_t protocol, uint16_t privatePort, uint16_t publicPort, uint32_t ttl, void *context)
1196 {
1197     (void)sdref;       // Unused
1198     (void)flags;       // Unused
1199     (void)context;     // Unused
1200     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
1201 
1202     if (num_printed++ == 0) printf("Timestamp     if   %-20s %-15s %-15s %-15s %-6s\n", "External Address", "Protocol", "Internal Port", "External Port", "TTL");
1203     printtimestamp();
1204     if (errorCode && errorCode != kDNSServiceErr_DoubleNAT) printf("Error code %d\n", errorCode);
1205     else
1206     {
1207         const unsigned char *digits = (const unsigned char *)&publicAddress;
1208         char addr[256];
1209 
1210         snprintf(addr, sizeof(addr), "%d.%d.%d.%d", digits[0], digits[1], digits[2], digits[3]);
1211         printf("%-4d %-20s %-15d %-15d %-15d %-6d%s\n", ifIndex, addr, protocol, ntohs(privatePort), ntohs(publicPort), ttl, errorCode == kDNSServiceErr_DoubleNAT ? " Double NAT" : "");
1212     }
1213 
1214     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
1215 }
1216 
addrinfo_reply(DNSServiceRef sdref,const DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * hostname,const struct sockaddr * address,uint32_t ttl,void * context)1217 static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context)
1218 {
1219     char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
1220     char addr[256] = "";
1221     char dnssec_status[15] = "Unknown";
1222     DNSServiceFlags check_flags = flags;
1223 	(void) sdref;
1224 	(void) context;
1225     unsigned char enable_dnssec = ((check_flags & kDNSServiceFlagsEnableDNSSEC) != 0);
1226 
1227     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
1228 
1229     if (num_printed++ == 0)
1230     {
1231         printf("Timestamp     A/R    Flags if %-38s %-44s %s%s\n", "Hostname", "Address", "TTL", enable_dnssec ? "DNSSECResult" : "");
1232     }
1233     printtimestamp();
1234 
1235     if (address && address->sa_family == AF_INET)
1236     {
1237         const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr;
1238         snprintf(addr, sizeof(addr), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
1239     }
1240     else if (address && address->sa_family == AF_INET6)
1241     {
1242         char if_name[IFNAMSIZ];     // Older Linux distributions don't define IF_NAMESIZE
1243         const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *)address;
1244         const unsigned char       *b  = (const unsigned char *      )&s6->sin6_addr;
1245         if (!if_indextoname(s6->sin6_scope_id, if_name))
1246             snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id);
1247         snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s",
1248             b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7],
1249             b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name);
1250     }
1251 
1252     if (enable_dnssec)
1253     {
1254         if (check_flags & kDNSServiceFlagsSecure)
1255             strncpy(dnssec_status, " Secure", sizeof(dnssec_status));
1256         else if (check_flags & kDNSServiceFlagsInsecure)
1257             strncpy(dnssec_status, " Insecure", sizeof(dnssec_status));
1258         else if (check_flags & kDNSServiceFlagsIndeterminate)
1259             strncpy(dnssec_status, " Indeterminate", sizeof(dnssec_status));
1260         else if (check_flags & kDNSServiceFlagsBogus)
1261             strncpy(dnssec_status, " Bogus", sizeof(dnssec_status));
1262     }
1263 
1264     printf("%s%9X%3d %-38s %-44s %d%s", op, flags, interfaceIndex, hostname, addr, ttl, enable_dnssec ? dnssec_status : "");
1265 
1266     if (errorCode)
1267     {
1268         if (errorCode == kDNSServiceErr_NoSuchRecord)
1269             printf("   No Such Record");
1270         else
1271             printf("   Error code %d", errorCode);
1272     }
1273     printf("\n");
1274 
1275     if (!(flags & kDNSServiceFlagsMoreComing))
1276         fflush(stdout);
1277 }
1278 
1279 //*************************************************************************************************************
1280 // The main test function
1281 
HandleEvents(void)1282 static void HandleEvents(void)
1283 #if _DNS_SD_LIBDISPATCH
1284 {
1285     main_queue = dispatch_get_main_queue();
1286     if (client) DNSServiceSetDispatchQueue(client, main_queue);
1287     if (client_pa) DNSServiceSetDispatchQueue(client_pa, main_queue);
1288     if (operation == 'A' || operation == 'U' || operation == 'N')
1289     {
1290         timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_queue);
1291         if (timer_source)
1292         {
1293             // Start the timer "timeout" seconds into the future and repeat it every "timeout" seconds
1294             dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC),
1295                                       (uint64_t)timeOut * NSEC_PER_SEC, 0);
1296             dispatch_source_set_event_handler(timer_source, ^{myTimerCallBack();});
1297             dispatch_resume(timer_source);
1298         }
1299     }
1300     dispatch_main();
1301 }
1302 #else
1303 {
1304     int dns_sd_fd  = client    ? DNSServiceRefSockFD(client   ) : -1;
1305     int dns_sd_fd2 = client_pa ? DNSServiceRefSockFD(client_pa) : -1;
1306     int nfds = dns_sd_fd + 1;
1307     fd_set readfds;
1308     struct timeval tv;
1309     int result;
1310 
1311     if (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1;
1312 
1313     while (!stopNow)
1314     {
1315         // 1. Set up the fd_set as usual here.
1316         // This example client has no file descriptors of its own,
1317         // but a real application would call FD_SET to add them to the set here
1318         FD_ZERO(&readfds);
1319 
1320         // 2. Add the fd for our client(s) to the fd_set
1321         if (client   ) FD_SET(dns_sd_fd, &readfds);
1322         if (client_pa) FD_SET(dns_sd_fd2, &readfds);
1323 
1324         // 3. Set up the timeout.
1325         tv.tv_sec  = timeOut;
1326         tv.tv_usec = 0;
1327 
1328         result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
1329         if (result > 0)
1330         {
1331             DNSServiceErrorType err = kDNSServiceErr_NoError;
1332             if      (client    && FD_ISSET(dns_sd_fd, &readfds)) err = DNSServiceProcessResult(client   );
1333             else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa);
1334             if (err) { printtimestamp_F(stderr); fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; }
1335         }
1336         else if (result == 0)
1337             myTimerCallBack();
1338         else
1339         {
1340             printf("select() returned %d errno %d %s\n", result, errno, strerror(errno));
1341             if (errno != EINTR) stopNow = 1;
1342         }
1343     }
1344 }
1345 #endif
1346 
getfirstoption(int argc,char ** argv,const char * optstr,int * pOptInd)1347 static int getfirstoption(int argc, char **argv, const char *optstr, int *pOptInd)
1348 // Return the recognized option in optstr and the option index of the next arg.
1349 #if NOT_HAVE_GETOPT
1350 {
1351     int i;
1352     for (i=1; i < argc; i++)
1353     {
1354         if (argv[i][0] == '-' && &argv[i][1] &&
1355             NULL != strchr(optstr, argv[i][1]))
1356         {
1357             *pOptInd = i + 1;
1358             return argv[i][1];
1359         }
1360     }
1361     return -1;
1362 }
1363 #else
1364 {
1365     int o = getopt(argc, (char *const *)argv, optstr);
1366     *pOptInd = optind;
1367     return o;
1368 }
1369 #endif
1370 
MyRegisterRecordCallback(DNSServiceRef service,DNSRecordRef rec,const DNSServiceFlags flags,DNSServiceErrorType errorCode,void * context)1371 static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef rec, const DNSServiceFlags flags,
1372                                                DNSServiceErrorType errorCode, void *context)
1373 {
1374     char *name = (char *)context;
1375 
1376     (void)service;  // Unused
1377     (void)rec;      // Unused
1378     (void)flags;    // Unused
1379     EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
1380 
1381     printtimestamp();
1382     printf("Got a reply for record %s: ", name);
1383 
1384     switch (errorCode)
1385     {
1386     case kDNSServiceErr_NoError:      printf("Name now registered and active\n"); break;
1387     case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); exit(-1);
1388     default:                          printf("Error %d\n", errorCode); break;
1389     }
1390     if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
1391 }
1392 
getip(const char * const name,struct sockaddr_storage * result)1393 static void getip(const char *const name, struct sockaddr_storage *result)
1394 {
1395     struct addrinfo *addrs = NULL;
1396     int err = getaddrinfo(name, NULL, NULL, &addrs);
1397     if (err) fprintf(stderr, "getaddrinfo error %d for %s", err, name);
1398     else memcpy(result, addrs->ai_addr, SA_LEN(addrs->ai_addr));
1399     if (addrs) freeaddrinfo(addrs);
1400 }
1401 
RegisterProxyAddressRecord(DNSServiceRef sdref,const char * host,const char * ip,DNSServiceFlags flags)1402 static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip, DNSServiceFlags flags)
1403 {
1404     // Call getip() after the call DNSServiceCreateConnection().
1405     // On the Win32 platform, WinSock must be initialized for getip() to succeed.
1406     // Any DNSService* call will initialize WinSock for us, so we make sure
1407     // DNSServiceCreateConnection() is called before getip() is.
1408     struct sockaddr_storage hostaddr;
1409     memset(&hostaddr, 0, sizeof(hostaddr));
1410     getip(ip, &hostaddr);
1411     flags |= kDNSServiceFlagsUnique;
1412     if (hostaddr.ss_family == AF_INET)
1413         return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host,
1414                                         kDNSServiceType_A,    kDNSServiceClass_IN,  4, &((struct sockaddr_in *)&hostaddr)->sin_addr,  240, MyRegisterRecordCallback, (void*)host));
1415     else if (hostaddr.ss_family == AF_INET6)
1416         return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host,
1417                                         kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host));
1418     else return(kDNSServiceErr_BadParam);
1419 }
1420 
1421 #define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0'     ) :  \
1422                     ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) :  \
1423                     ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : 0)
1424 
1425 #define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1]))
1426 
1427 #define MAXTXTRecordSize 8900
RegisterService(DNSServiceRef * sdref,const char * nam,const char * typ,const char * dom,const char * host,const char * port,int argc,char ** argv,DNSServiceFlags flags)1428 static DNSServiceErrorType RegisterService(DNSServiceRef *sdref,
1429                                            const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags)
1430 {
1431     uint16_t PortAsNumber = atoi(port);
1432     Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
1433     unsigned char txt[MAXTXTRecordSize];
1434     txt[0] = '\0';
1435     unsigned char *ptr = txt;
1436     int i;
1437 
1438     if (nam[0] == '.' && nam[1] == 0) nam = "";   // We allow '.' on the command line as a synonym for empty string
1439     if (dom[0] == '.' && dom[1] == 0) dom = "";   // We allow '.' on the command line as a synonym for empty string
1440 
1441     printf("Registering Service %s.%s%s%s", nam[0] ? nam : "<<Default>>", typ, dom[0] ? "." : "", dom);
1442     if (host && *host) printf(" host %s", host);
1443     printf(" port %s", port);
1444 
1445     if (argc)
1446     {
1447         for (i = 0; i < argc; i++)
1448         {
1449             const char *p = argv[i];
1450             if (ptr >= txt + sizeof(txt))
1451                 return kDNSServiceErr_BadParam;
1452             *ptr = 0;
1453             while (*p && *ptr < 255)
1454             {
1455                 if (ptr + 1 + *ptr >= txt + sizeof(txt))
1456                     return kDNSServiceErr_BadParam;
1457                 if      (p[0] != '\\' || p[1] == 0)                       { ptr[++*ptr] = *p;           p+=1; }
1458                 else if (p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3])) { ptr[++*ptr] = HexPair(p+2); p+=4; }
1459                 else                                                      { ptr[++*ptr] = p[1];         p+=2; }
1460             }
1461             ptr += 1 + *ptr;
1462         }
1463         printf(" TXT");
1464         ShowTXTRecord(ptr-txt, txt);
1465     }
1466     printf("\n");
1467 
1468     //flags |= kDNSServiceFlagsAllowRemoteQuery;
1469     //flags |= kDNSServiceFlagsNoAutoRename;
1470 
1471     return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL));
1472 }
1473 
1474 #define TypeBufferSize 80
gettype(char * buffer,char * typ)1475 static char *gettype(char *buffer, char *typ)
1476 {
1477     if (!typ || !*typ || (typ[0] == '.' && typ[1] == 0)) typ = "_http._tcp";
1478     if (!strchr(typ, '.')) { snprintf(buffer, TypeBufferSize, "%s._tcp", typ); typ = buffer; }
1479     return(typ);
1480 }
1481 
1482 // Do some basic tests to verify API handles > 63 byte strings gracefully with
1483 // a returned error code.
1484 
1485 #define STRING_64_BYTES "_123456789012345678901234567890123456789012345678901234567890123"
1486 
API_string_limit_test()1487 static int API_string_limit_test()
1488 {
1489     const char * regtype;
1490     DNSServiceRef sdRef = NULL;
1491     const char * longHost = STRING_64_BYTES ".local";
1492     const char * longDomain = "hostname." STRING_64_BYTES;
1493 
1494     printf("Testing for error returns when various strings are > 63 bytes.\n");
1495 
1496     printf("DNSServiceGetAddrInfo(), hostname = %s\n", longHost);
1497     if (DNSServiceGetAddrInfo(&sdRef, 0, 0, 0, longHost, addrinfo_reply, 0) == 0)
1498     {
1499         printf("DNSServiceGetAddrInfo(): expected error return\n");
1500         return 1;
1501     };
1502 
1503     printf("DNSServiceGetAddrInfo(), hostname = %s\n", longDomain);
1504     if (DNSServiceGetAddrInfo(&sdRef, 0, 0, 0, longDomain, addrinfo_reply, 0) == 0)
1505     {
1506         printf("DNSServiceGetAddrInfo(): expected error return\n");
1507         return 1;
1508     };
1509 
1510     printf("DNSServiceResolve(), name = %s\n", STRING_64_BYTES);
1511     if (DNSServiceResolve(&sdRef, 0, 0, STRING_64_BYTES, "_test._tcp", "local", resolve_reply, NULL) == 0)
1512     {
1513         printf("DNSServiceResolve(): expected error return\n");
1514         return 1;
1515     };
1516 
1517     regtype = STRING_64_BYTES "._tcp";
1518     printf("DNSServiceResolve(), regtype = %s\n", regtype);
1519     if (DNSServiceResolve(&sdRef, 0, 0, "instanceName", regtype, "local", resolve_reply, NULL) == 0)
1520     {
1521         printf("DNSServiceResolve(): expected error return\n");
1522         return 1;
1523     };
1524 
1525     printf("DNSServiceResolve(), domain = %s\n", STRING_64_BYTES);
1526     if (DNSServiceResolve(&sdRef, 0, 0, "instanceName", "_test._tcp", STRING_64_BYTES, resolve_reply, NULL) == 0)
1527     {
1528         printf("DNSServiceResolve(): expected error return\n");
1529         return 1;
1530     };
1531 
1532     printf("Testing for error returns when various strings are > 63 bytes: PASSED\n");
1533     return 0;
1534 }
1535 
API_NULL_input_test()1536 static int API_NULL_input_test()
1537 {
1538     printf("Running basic API input range tests with various pointer parameters set to NULL:\n");
1539 
1540     // Test that API's handle NULL pointers by returning an error when appropriate.
1541 
1542     // DNSServiceRefSockFD()
1543     if (DNSServiceRefSockFD(0) != -1)
1544     {
1545         printf("DNSServiceRefSockFD(): expected dnssd_InvalidSocket return\n");
1546         return 1;
1547     }
1548 
1549     // DNSServiceProcessResult()
1550     if (DNSServiceProcessResult(0) == 0)
1551     {
1552         printf("DNSServiceProcessResult(): expected error return\n");
1553         return 1;
1554     }
1555 
1556     // DNSServiceRefDeallocate(): no return value, just verify it doesn't crash
1557     DNSServiceRefDeallocate(0);
1558 
1559     // DNSServiceGetProperty()
1560     {
1561         uint32_t   result;
1562         uint32_t   size;
1563 
1564 	    if (    (DNSServiceGetProperty(                                0, &result, &size) == 0)
1565 	         || (DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion,       0, &size) == 0)
1566 	         || (DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &result, 0) == 0)
1567            )
1568 	    {
1569 	        printf("DNSServiceGetProperty(): expected error return\n");
1570 	        return 1;
1571 	    }
1572     }
1573 
1574     // DNSServiceResolve()
1575     {
1576 	    DNSServiceRef       sdRef;
1577 	    DNSServiceFlags     flags = 0;
1578 	    uint32_t            interfaceIndex = 0;
1579 	    const char          *name = "name";
1580 	    const char          *regtype = "_test._tcp";
1581 	    const char          *domain = "local";
1582 	    DNSServiceResolveReply callBack = 0;
1583 	    void                *context = 0;   // can be a NULL pointer
1584 
1585 	    if (    (DNSServiceResolve(    0,  flags, interfaceIndex, name, regtype, domain, callBack, context) == 0)
1586             ||  (DNSServiceResolve(&sdRef, flags, interfaceIndex,    0, regtype, domain, callBack, context) == 0)
1587             ||  (DNSServiceResolve(&sdRef, flags, interfaceIndex, name,       0, domain, callBack, context) == 0)
1588             ||  (DNSServiceResolve(&sdRef, flags, interfaceIndex, name, regtype,      0, callBack, context) == 0)
1589             ||  (DNSServiceResolve(&sdRef, flags, interfaceIndex, name, regtype, domain, callBack, context) == 0)
1590            )
1591 	    {
1592 	        printf("DNSServiceResolve(): expected error return\n");
1593 	        return 1;
1594 	    }
1595     }
1596 
1597     // DNSServiceQueryRecord()
1598     {
1599 	    DNSServiceRef       sdRef;
1600 	    DNSServiceFlags     flags = 0;
1601 	    uint32_t            interfaceIndex = 0;
1602 	    const char          *fullname = "fullname";
1603 	    uint16_t            rrtype = 0;
1604 	    uint16_t            rrclass = 0;
1605 	    DNSServiceQueryRecordReply callBack = 0;
1606 	    void                *context = 0;  /* may be NULL */
1607 
1608 	    if (    (DNSServiceQueryRecord(     0, flags, interfaceIndex, fullname, rrtype, rrclass, callBack, context) == 0)
1609 	        ||  (DNSServiceQueryRecord(&sdRef, flags, interfaceIndex, 0,        rrtype, rrclass, callBack, context) == 0)
1610 	        ||  (DNSServiceQueryRecord(&sdRef, flags, interfaceIndex, fullname, rrtype, rrclass,        0, context) == 0)
1611            )
1612 	    {
1613 	        printf("DNSServiceQueryRecord(): expected error return\n");
1614 	        return 1;
1615 	    }
1616     }
1617 
1618     // DNSServiceGetAddrInfo()
1619     {
1620 	    DNSServiceRef       sdRef;
1621 	    DNSServiceFlags     flags = 0;
1622 	    uint32_t            interfaceIndex = 0;
1623 	    DNSServiceProtocol  protocol = kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6;
1624 	    const char          *hostname = "host.local";
1625 	    DNSServiceGetAddrInfoReply callBack = 0;
1626 	    void                *context = 0;   // may be NULL
1627 
1628 	    if (    (DNSServiceGetAddrInfo(     0, flags, interfaceIndex, protocol, hostname, callBack, context) == 0)
1629             ||  (DNSServiceGetAddrInfo(&sdRef, flags, interfaceIndex, protocol,        0, callBack, context) == 0)
1630             ||  (DNSServiceGetAddrInfo(&sdRef, flags, interfaceIndex, protocol, hostname,        0, context) == 0)
1631            )
1632 	    {
1633 	        printf("DNSServiceGetAddrInfo(): expected error return\n");
1634 	        return 1;
1635 	    }
1636     }
1637 
1638     // DNSServiceBrowse()
1639     {
1640 	    DNSServiceRef       sdRef;
1641 	    DNSServiceFlags     flags = 0;
1642 	    uint32_t            interfaceIndex = 0;
1643 	    const char          *regtype = "_test._tcp";
1644 	    const char          *domain = 0;    /* may be NULL */
1645 	    DNSServiceBrowseReply callBack = 0;
1646 	    void                *context = 0;   /* may be NULL */
1647 
1648 	    if (    (DNSServiceBrowse(     0, flags, interfaceIndex, regtype, domain, callBack, context) == 0)
1649             ||  (DNSServiceBrowse(&sdRef, flags, interfaceIndex,       0, domain, callBack, context) == 0)
1650             ||  (DNSServiceBrowse(&sdRef, flags, interfaceIndex, regtype, domain,        0, context) == 0)
1651            )
1652 	    {
1653 	        printf("DNSServiceBrowse(): expected error return\n");
1654 	        return 1;
1655 	    }
1656     }
1657 
1658 #if APPLE_OSX_mDNSResponder
1659     // DNSServiceSetDefaultDomainForUser()
1660     if (DNSServiceSetDefaultDomainForUser(0, 0) == 0)
1661     {
1662         printf("DNSServiceSetDefaultDomainForUser(): expected error return\n");
1663         return 1;
1664     }
1665 #endif
1666 
1667     // DNSServiceRegister()
1668     {
1669 	    DNSServiceRef       sdRef;
1670 	    DNSServiceFlags     flags = 0;
1671 	    uint32_t            interfaceIndex = 0;
1672 	    const char          *name = 0;         /* may be NULL */
1673 	    const char          *regtype = "_test._tcp";
1674 	    const char          *domain = 0;       /* may be NULL */
1675 	    const char          *host = 0;         /* may be NULL */
1676 	    uint16_t            port = 0x2211;     /* In network byte order */
1677 	    uint16_t            txtLen = 1;
1678 	    const void          *txtRecord = "\0";    /* may be NULL */
1679 	    DNSServiceRegisterReply callBack = 0;  /* may be NULL */
1680 	    void                *context = 0;      /* may be NULL */
1681 
1682 	    if (    (DNSServiceRegister(     0, flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, callBack, context) == 0)
1683             ||  (DNSServiceRegister(&sdRef, flags, interfaceIndex, name,       0, domain, host, port, txtLen, txtRecord, callBack, context) == 0)
1684            )
1685 	    {
1686 	        printf("DNSServiceRegister(): expected error return\n");
1687 	        return 1;
1688 	    }
1689     }
1690 
1691     // DNSServiceEnumerateDomains()
1692     {
1693 	    DNSServiceRef       sdRef;
1694 	    DNSServiceFlags     flags = 0;
1695 	    uint32_t            interfaceIndex = 0;
1696 	    DNSServiceDomainEnumReply callBack = 0;
1697 	    void                *context = 0;  /* may be NULL */
1698 
1699 	    if (    (DNSServiceEnumerateDomains(     0, flags, interfaceIndex, callBack, context) == 0)
1700             ||  (DNSServiceEnumerateDomains(&sdRef, flags, interfaceIndex,        0, context) == 0)
1701            )
1702 	    {
1703 	        printf("DNSServiceEnumerateDomains(): expected error return\n");
1704 	        return 1;
1705 	    }
1706     }
1707 
1708     // DNSServiceCreateConnection()
1709     if (DNSServiceCreateConnection(0) == 0)
1710     {
1711         printf("DNSServiceCreateConnection(): expected error return\n");
1712         return 1;
1713     }
1714 
1715 #if APPLE_OSX_mDNSResponder
1716     // DNSServiceCreateDelegateConnection()
1717     if (DNSServiceCreateDelegateConnection(0, 0, 0) == 0)
1718     {
1719         printf("DNSServiceCreateDelegateConnection(): expected error return\n");
1720         return 1;
1721     }
1722 #endif
1723 
1724     // DNSServiceRegisterRecord()
1725     {
1726 	    DNSServiceRef       sdRef;
1727 	    DNSRecordRef        RecordRef;
1728 	    DNSServiceFlags     flags = 0;
1729 	    uint32_t            interfaceIndex = 0;
1730 	    const char          *fullname = "test1._test._tcp.local";
1731 	    uint16_t            rrtype = kDNSServiceType_TXT;
1732 	    uint16_t            rrclass = kDNSServiceClass_IN;
1733 	    uint16_t            rdlen = 1;
1734 	    const void          *rdata = "\0";
1735 	    uint32_t            ttl = 0;
1736 	    DNSServiceRegisterRecordReply callBack = 0;
1737 	    void                *context = 0;    /* may be NULL */
1738 
1739         // Need an initialize sdRef
1740         if (DNSServiceCreateConnection(&sdRef))
1741         {
1742 	        printf("DNSServiceCreateConnection(): failed\n");
1743 	        return 1;
1744         }
1745 
1746 	    if (    (DNSServiceRegisterRecord(     0, &RecordRef, flags, interfaceIndex, fullname, rrtype, rrclass, rdlen, rdata, ttl, callBack, context) == 0)
1747 	        ||  (DNSServiceRegisterRecord(sdRef, &RecordRef, flags, interfaceIndex,         0, rrtype, rrclass, rdlen, rdata, ttl, callBack, context) == 0)
1748 	        ||  (DNSServiceRegisterRecord(sdRef, &RecordRef, flags, interfaceIndex,  fullname, rrtype, rrclass, rdlen,     0, ttl, callBack, context) == 0)
1749 	        ||  (DNSServiceRegisterRecord(sdRef, &RecordRef, flags, interfaceIndex,  fullname, rrtype, rrclass, rdlen, rdata, ttl,        0, context) == 0)
1750            )
1751 	    {
1752 	        printf("DNSServiceRegisterRecord(): expected error return\n");
1753 	        return 1;
1754 	    }
1755     }
1756 
1757     // DNSServiceAddRecord(), DNSServiceUpdateRecord(), and DNSServiceRemoveRecord() verify that they
1758     // get a valid DNSServiceRef returned from DNSServiceRegister()
1759     {
1760         DNSServiceErrorType err;
1761 	    Opaque16            registerPort = { { 0x12, 0x34 } };
1762 	    static const char   TXT[] = "\xC" "First String";
1763         DNSServiceRef       sdRef;
1764 
1765 	    DNSRecordRef        RecordRef;
1766 	    DNSServiceFlags     flags = 0;
1767 	    uint16_t            rrtype = kDNSServiceType_TXT;
1768 	    uint16_t            rdlen = 1;
1769 	    const void          *rdata = "\0";
1770 	    uint32_t            ttl = 100;
1771 
1772 	    err = DNSServiceRegister(&sdRef, 0, 0, "Test", "_test._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL);
1773         if (err)
1774         {
1775             printf("DNSServiceRegister() failed with: %d\n", err);
1776             return 1;
1777         }
1778 
1779 	    // DNSServiceAddRecord()
1780 	    if (    (DNSServiceAddRecord(    0, &RecordRef, flags, rrtype, rdlen, rdata, ttl) == 0)
1781 	        ||  (DNSServiceAddRecord(sdRef,          0, flags, rrtype, rdlen, rdata, ttl) == 0)
1782 	        ||  (DNSServiceAddRecord(sdRef, &RecordRef, flags, rrtype, rdlen,     0, ttl) == 0)
1783            )
1784 
1785 	    {
1786 	        printf("DNSServiceAddRecord(): expected error return\n");
1787 	        return 1;
1788 	    }
1789 
1790         // (rdlen == 0 && rdata == 0) should indicate a TXT with rdata containing only a 0 length byte.
1791         if (DNSServiceAddRecord(sdRef, &RecordRef, flags, rrtype, 0, 0, ttl) == kDNSServiceErr_BadParam)
1792         {
1793 	        printf("DNSServiceAddRecord(): with (rdlen == 0 && rdata == 0) returned kDNSServiceErr_BadParam\n");
1794 	        return 1;
1795         }
1796 
1797 	    // DNSServiceUpdateRecord()
1798         // Note, RecordRef can be NULL per explanation with declaration in dns_sd.h
1799 	    if (    (DNSServiceUpdateRecord(    0, RecordRef, flags, rdlen, rdata, ttl) == 0)
1800 	        ||  (DNSServiceUpdateRecord(sdRef, RecordRef, flags, rdlen,     0, ttl) == 0)
1801            )
1802 	    {
1803 	        printf("DNSServiceUpdateRecord(): expected error return\n");
1804 	        return 1;
1805 	    }
1806 
1807         // (rdlen == 0 && rdata == 0) should indicate a TXT with rdata containing only a 0 length byte.
1808         if (DNSServiceUpdateRecord(sdRef, RecordRef, flags, 0, 0, ttl) == kDNSServiceErr_BadParam)
1809         {
1810 	        printf("DNSServiceUpdateRecord(): with (rdlen == 0 && rdata == 0) returned kDNSServiceErr_BadParam\n");
1811 	        return 1;
1812         }
1813 
1814 	    // DNSServiceRemoveRecord()
1815 	    if (    (DNSServiceRemoveRecord(    0, RecordRef, flags) == 0)
1816 	        ||  (DNSServiceRemoveRecord(sdRef,         0, flags) == 0)
1817            )
1818 	    {
1819 	        printf("DNSServiceRemoveRecord(): expected error return\n");
1820 	        return 1;
1821 	    }
1822 
1823         DNSServiceRefDeallocate(sdRef);
1824     }
1825 
1826     // DNSServiceReconfirmRecord()
1827     {
1828 	    DNSServiceFlags     flags = 0;
1829 	    uint32_t            interfaceIndex = 0;
1830 	    const char          *fullname = "aaa._test._tcp.local";
1831 	    uint16_t            rrtype = kDNSServiceType_TXT;
1832 	    uint16_t            rrclass = kDNSServiceClass_IN;
1833 	    uint16_t            rdlen = 1;
1834 	    const void          *rdata = "\0";
1835 
1836 	    if (    (DNSServiceReconfirmRecord(flags, interfaceIndex,        0, rrtype, rrclass, rdlen, rdata) == 0)
1837             ||  (DNSServiceReconfirmRecord(flags, interfaceIndex, fullname, rrtype, rrclass, rdlen,     0) == 0)
1838            )
1839 	    {
1840 	        printf("DNSServiceReconfirmRecord(): expected error return\n");
1841 	        return 1;
1842 	    }
1843         // (rdlen == 0 && rdata == 0) should indicate a TXT with rdata containing only a 0 length byte.
1844         if (DNSServiceReconfirmRecord(flags, interfaceIndex, fullname, rrtype, rrclass, 0, 0) == kDNSServiceErr_BadParam)
1845         {
1846 	        printf("DNSServiceReconfirmRecord(): with (rdlen == 0 && rdata == 0) returned kDNSServiceErr_BadParam\n");
1847 	        return 1;
1848         }
1849     }
1850 
1851 
1852     printf("Basic API input range tests: PASSED\n");
1853     return 0;
1854 }
1855 
API_input_range_test()1856 static int API_input_range_test()
1857 {
1858 
1859     if (API_string_limit_test())
1860         return 1;
1861 
1862     if (API_NULL_input_test())
1863         return 1;
1864 
1865     return 0;
1866 }
1867 
1868 #ifdef APPLE_OSX_mDNSResponder
1869 static void handle_state_dump_request(uint8_t if_compress_state_dump, uint8_t if_dump_to_stdout);
1870 #endif // APPLE_OSX_mDNSResponder
main(int argc,char ** argv)1871 int main(int argc, char **argv)
1872 {
1873     DNSServiceErrorType err;
1874     char buffer[TypeBufferSize], *typ, *dom;
1875     int opi;
1876     DNSServiceFlags flags = 0;
1877     unsigned char enable_dnssec = 0;
1878 
1879     // Extract the program name from argv[0], which by convention contains the path to this executable.
1880     // Note that this is just a voluntary convention, not enforced by the kernel --
1881     // the process calling exec() can pass bogus data in argv[0] if it chooses to.
1882     const char *a0 = strrchr(argv[0], kFilePathSep) + 1;
1883     if (a0 == (const char *)1) a0 = argv[0];
1884 
1885 #if defined(_WIN32)
1886     HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
1887 #endif
1888 
1889 #if TEST_NEW_CLIENTSTUB
1890     printf("Using embedded copy of dnssd_clientstub instead of system library\n");
1891     if (sizeof(argv) == 8) printf("Running in 64-bit mode\n");
1892 #endif
1893 
1894     // Test code for TXTRecord functions
1895     //TXTRecordRef txtRecord;
1896     //TXTRecordCreate(&txtRecord, 0, NULL);
1897     //TXTRecordSetValue(&txtRecord, "aaa", 1, "b");
1898     //printf("%d\n", TXTRecordContainsKey(TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), "Aaa"));
1899 
1900     while (argc > 1)
1901     {
1902         int entryCount;
1903 
1904         // record current argc to see if we process an argument in this pass
1905         entryCount = argc;
1906 
1907 	    if (argc > 1 && !strcmp(argv[1], "-test"))
1908 	    {
1909 	        argc--;
1910 	        argv++;
1911 	        return API_input_range_test();
1912 	    }
1913 
1914 	    if (argc > 1 && !strcmp(argv[1], "-lo"))
1915 	    {
1916 	        argc--;
1917 	        argv++;
1918 	        opinterface = kDNSServiceInterfaceIndexLocalOnly;
1919 	        printf("Using LocalOnly\n");
1920 	    }
1921 
1922 	    if (argc > 1 && (!strcasecmp(argv[1], "-p2p")))
1923 	    {
1924 	        argc--;
1925 	        argv++;
1926 	        opinterface = kDNSServiceInterfaceIndexP2P;
1927 	    }
1928 
1929 	    if (argc > 1 && (!strcasecmp(argv[1], "-ble")))
1930 	    {
1931 	        argc--;
1932 	        argv++;
1933 	        opinterface = kDNSServiceInterfaceIndexBLE;
1934 	    }
1935 
1936         if (argc > 1 && !strcasecmp(argv[1], "-allowexpired"))
1937         {
1938             argc--;
1939             argv++;
1940             flags |= kDNSServiceFlagsAllowExpiredAnswers;
1941             printf("Setting kDNSServiceFlagsAllowExpiredAnswers\n");
1942         }
1943 
1944 	    if (argc > 1 && !strcasecmp(argv[1], "-includep2p"))
1945 	    {
1946 	        argc--;
1947 	        argv++;
1948 	        flags |= kDNSServiceFlagsIncludeP2P;
1949 	        printf("Setting kDNSServiceFlagsIncludeP2P\n");
1950 	    }
1951 
1952 	    if (argc > 1 && !strcasecmp(argv[1], "-fmc"))
1953 	    {
1954 	        argc--;
1955 	        argv++;
1956 	        flags |= kDNSServiceFlagsForceMulticast;
1957 	        printf("Setting kDNSServiceFlagsForceMulticast flag for this request\n");
1958 	    }
1959 
1960 	    if (argc > 1 && !strcasecmp(argv[1], "-includeAWDL"))
1961 	    {
1962 	        argc--;
1963 	        argv++;
1964 	        flags |= kDNSServiceFlagsIncludeAWDL;
1965 	        printf("Setting kDNSServiceFlagsIncludeAWDL\n");
1966 	    }
1967 
1968         if (argc > 1 && !strcasecmp(argv[1], "-intermediates"))
1969         {
1970             argc--;
1971             argv++;
1972             flags |= kDNSServiceFlagsReturnIntermediates;
1973             printf("Setting kDNSServiceFlagsReturnIntermediates\n");
1974         }
1975 
1976         if (argc > 1 && !strcasecmp(argv[1], "-tc"))
1977 	    {
1978 	        argc--;
1979 	        argv++;
1980 	        flags |= kDNSServiceFlagsBackgroundTrafficClass;
1981 	        printf("Setting kDNSServiceFlagsBackgroundTrafficClass\n");
1982 	    }
1983 
1984 	    if (argc > 1 && !strcasecmp(argv[1], "-t1"))
1985 	    {
1986 	        argc--;
1987 	        argv++;
1988 	        flags |= kDNSServiceFlagsThresholdOne;
1989 	        printf("Setting kDNSServiceFlagsThresholdOne\n");
1990 	    }
1991 
1992 	    if (argc > 1 && !strcasecmp(argv[1], "-tFinder"))
1993 	    {
1994 	        argc--;
1995 	        argv++;
1996 	        flags |= kDNSServiceFlagsThresholdFinder;
1997 	        printf("Setting kDNSServiceFlagsThresholdFinder\n");
1998 	    }
1999 
2000 	    if (argc > 1 && !strcasecmp(argv[1], "-wo"))
2001 	    {
2002 	        argc--;
2003 	        argv++;
2004 	        flags |= kDNSServiceFlagsWakeOnlyService;
2005 	        printf("Setting kDNSServiceFlagsWakeOnlyService\n");
2006 	    }
2007 
2008 	    if (argc > 1 && !strcasecmp(argv[1], "-ku"))
2009 	    {
2010 	        argc--;
2011 	        argv++;
2012 	        flags |= kDNSServiceFlagsKnownUnique;
2013 	        printf("Setting kDNSServiceFlagsKnownUnique\n");
2014 	    }
2015 
2016 	    if (argc > 1 && !strcasecmp(argv[1], "-unicastResponse"))
2017 	    {
2018 	        argc--;
2019 	        argv++;
2020 	        flags |= kDNSServiceFlagsUnicastResponse;
2021 	        printf("Setting kDNSServiceFlagsUnicastResponse\n");
2022 	    }
2023 
2024 	    if (argc > 1 && !strcasecmp(argv[1], "-timeout"))
2025 	    {
2026 	        argc--;
2027 	        argv++;
2028 	        flags |= kDNSServiceFlagsTimeout;
2029 	        printf("Setting kDNSServiceFlagsTimeout\n");
2030 	    }
2031 
2032 	    if (argc > 1 && !strcasecmp(argv[1], "-autoTrigger"))
2033 	    {
2034 	        argc--;
2035 	        argv++;
2036 	        flags |= kDNSServiceFlagsAutoTrigger;
2037 	        printf("Setting kDNSServiceFlagsAutoTrigger\n");
2038 	    }
2039 
2040 	    if (argc > 1 && !strcasecmp(argv[1], "-enableDNSSEC"))
2041 	    {
2042 	        argc--;
2043 	        argv++;
2044             enable_dnssec = 1;
2045 	        printf("Enable DNSSEC validation for the '-Q' query\n");
2046 	    }
2047 
2048 	    if (argc > 2 && !strcmp(argv[1], "-i"))
2049 	    {
2050 	        opinterface = if_nametoindex(argv[2]);
2051 	        if (!opinterface) opinterface = atoi(argv[2]);
2052 	        if (!opinterface) { fprintf(stderr, "Unknown interface %s\n", argv[2]); goto Fail; }
2053 	        argc -= 2;
2054 	        argv += 2;
2055 	    }
2056 
2057         // Exit loop if if we didn't match one of the multi character options.
2058         if (argc == entryCount)
2059             break;
2060     }
2061 
2062     if (argc < 2) goto Fail;        // Minimum command line is the command name and one argument
2063     operation = getfirstoption(argc, argv, "ABCDEFHILMNPQRSTUVZhlq"
2064                                "X"
2065                                "Gg"
2066                                , &opi);
2067     if (operation == -1) goto Fail;
2068 
2069     if (opinterface) printf("Using interface %d\n", opinterface);
2070 
2071     switch (operation)
2072     {
2073     case 'E':   printf("Looking for recommended registration domains:\n");
2074         err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, opinterface, enum_reply, NULL);
2075         break;
2076 
2077     case 'F':   printf("Looking for recommended browsing domains:\n");
2078         err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, opinterface, enum_reply, NULL);
2079         //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL);
2080         //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "bonjour.nicta.com.au.", NULL);
2081         //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "ibm.com.", NULL);
2082         //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL);
2083         break;
2084 
2085     case 'B':   typ = (argc < opi+1) ? "" : argv[opi+0];
2086         dom = (argc < opi+2) ? "" : argv[opi+1];              // Missing domain argument is the same as empty string i.e. use system default(s)
2087         typ = gettype(buffer, typ);
2088         if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;               // We allow '.' on the command line as a synonym for empty string
2089         printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom);
2090         err = DNSServiceBrowse(&client, flags, opinterface, typ, dom, browse_reply, NULL);
2091         break;
2092 
2093     case 'Z':   typ = (argc < opi+1) ? "" : argv[opi+0];
2094         dom = (argc < opi+2) ? "" : argv[opi+1];              // Missing domain argument is the same as empty string i.e. use system default(s)
2095         typ = gettype(buffer, typ);
2096         if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;               // We allow '.' on the command line as a synonym for empty string
2097         printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom);
2098         err = DNSServiceCreateConnection(&client);
2099         if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); }
2100         sc1 = client;
2101         err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL);
2102         break;
2103 
2104     case 'l':
2105     case 'L':   {
2106         if (argc < opi+2) goto Fail;
2107         typ = (argc < opi+2) ? ""      : argv[opi+1];
2108         dom = (argc < opi+3) ? "local" : argv[opi+2];
2109         typ = gettype(buffer, typ);
2110         if (dom[0] == '.' && dom[1] == 0) dom = "local";               // We allow '.' on the command line as a synonym for "local"
2111         printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom);
2112         if (operation == 'l') flags |= kDNSServiceFlagsWakeOnResolve;
2113         err = DNSServiceResolve(&client, flags, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL);
2114         break;
2115     }
2116 
2117     case 'R':   if (argc < opi+4) goto Fail;
2118         typ = (argc < opi+2) ? "" : argv[opi+1];
2119         dom = (argc < opi+3) ? "" : argv[opi+2];
2120         typ = gettype(buffer, typ);
2121         if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;               // We allow '.' on the command line as a synonym for empty string
2122         err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags);
2123         break;
2124 
2125 
2126     case 'P':   if (argc < opi+6) goto Fail;
2127         err = DNSServiceCreateConnection(&client_pa);
2128         if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); }
2129         err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5], flags);
2130         if (err) break;
2131         err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags);
2132         break;
2133 
2134     case 'q':
2135     case 'Q':
2136     case 'C':   {
2137         uint16_t rrtype, rrclass;
2138         flags |= kDNSServiceFlagsReturnIntermediates;
2139         if (operation == 'q')
2140             flags |= kDNSServiceFlagsSuppressUnusable;
2141         if (enable_dnssec)
2142             flags |= kDNSServiceFlagsEnableDNSSEC;
2143         if (argc < opi+1)
2144             goto Fail;
2145         rrtype = (argc <= opi+1) ? kDNSServiceType_A  : GetRRType(argv[opi+1]);
2146         rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : GetRRClass(argv[opi+2]);
2147         if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR)
2148             flags |= kDNSServiceFlagsLongLivedQuery;
2149         err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL);
2150         break;
2151     }
2152 
2153     case 'A':
2154     case 'U':
2155     case 'N':   {
2156         Opaque16 registerPort = { { 0x12, 0x34 } };
2157         static const char TXT[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String";
2158         printf("Registering Service Test._testupdate._tcp.local.\n");
2159         err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testupdate._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL);
2160         break;
2161     }
2162 
2163     case 'T':   {
2164         Opaque16 registerPort = { { 0x23, 0x45 } };
2165         char TXT[1024];
2166         unsigned int i;
2167         for (i=0; i<sizeof(TXT); i++)
2168             if ((i & 0x1F) == 0) TXT[i] = 0x1F;else TXT[i] = 'A' + (i >> 5);
2169         printf("Registering Service Test._testlargetxt._tcp.local.\n");
2170         err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testlargetxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT), TXT, reg_reply, NULL);
2171         break;
2172     }
2173 
2174     case 'M':   {
2175         pid_t pid = getpid();
2176         Opaque16 registerPort = { { pid >> 8, pid & 0xFF } };
2177         static const char TXT1[] = "\xC" "First String"  "\xD" "Second String" "\xC" "Third String";
2178         static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String"  "\xC" "Sixth String";
2179         printf("Registering Service Test._testdualtxt._tcp.local.\n");
2180         err = DNSServiceRegister(&client, flags, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL);
2181         if (!err) err = DNSServiceAddRecord(client, &record, flags, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0);
2182         break;
2183     }
2184 
2185     case 'I':   {
2186         pid_t pid = getpid();
2187         Opaque16 registerPort = { { pid >> 8, pid & 0xFF } };
2188         static const char TXT[] = "\x09" "Test Data";
2189         printf("Registering Service Test._testtxt._tcp.local.\n");
2190         err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL);
2191         if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0);
2192         break;
2193     }
2194 
2195     case 'X':   {
2196         if (argc == opi)                // If no arguments, just fetch IP address
2197             err = DNSServiceNATPortMappingCreate(&client, 0, 0, 0, 0, 0, 0, port_mapping_create_reply, NULL);
2198         else if (argc >= opi+2 && atoi(argv[opi+0]) == 0)
2199         {
2200             DNSServiceProtocol prot  = GetProtocol(argv[opi+0]);                                    // Must specify TCP or UDP
2201             uint16_t IntPortAsNumber = atoi(argv[opi+1]);                                       // Must specify internal port
2202             uint16_t ExtPortAsNumber = (argc < opi+3) ? 0 : atoi(argv[opi+2]);              // Optional desired external port
2203             uint32_t ttl             = (argc < opi+4) ? 0 : atoi(argv[opi+3]);              // Optional desired lease lifetime
2204             Opaque16 intp = { { IntPortAsNumber >> 8, IntPortAsNumber & 0xFF } };
2205             Opaque16 extp = { { ExtPortAsNumber >> 8, ExtPortAsNumber & 0xFF } };
2206             err = DNSServiceNATPortMappingCreate(&client, 0, 0, prot, intp.NotAnInteger, extp.NotAnInteger, ttl, port_mapping_create_reply, NULL);
2207         }
2208         else goto Fail;
2209         break;
2210     }
2211 
2212     case 'G':   {
2213         flags |= kDNSServiceFlagsReturnIntermediates;
2214 
2215         if (argc != opi+2)
2216             goto Fail;
2217         else
2218             err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL);
2219         break;
2220     }
2221 
2222     case 'S':   {
2223         Opaque16 registerPort = { { 0x23, 0x45 } };                 // 9029 decimal
2224         unsigned char txtrec[16] = "\xF" "/path=test.html";
2225         DNSRecordRef rec;
2226         unsigned char nulrec[4] = "1234";
2227 
2228         err = DNSServiceCreateConnection(&client);
2229         if (err) { fprintf(stderr, "DNSServiceCreateConnection failed %ld\n", (long int)err); return (-1); }
2230 
2231         sc1 = client;
2232         err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, "_http._tcp", "", browse_reply, NULL);
2233         if (err) { fprintf(stderr, "DNSServiceBrowse _http._tcp failed %ld\n", (long int)err); return (-1); }
2234 
2235         sc2 = client;
2236         err = DNSServiceBrowse(&sc2, kDNSServiceFlagsShareConnection, opinterface, "_ftp._tcp", "", browse_reply, NULL);
2237         if (err) { fprintf(stderr, "DNSServiceBrowse _ftp._tcp failed %ld\n", (long int)err); return (-1); }
2238 
2239         sc3 = client;
2240         err = DNSServiceRegister(&sc3, kDNSServiceFlagsShareConnection, opinterface, "kDNSServiceFlagsShareConnection",
2241                                  "_http._tcp", "local", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL);
2242         if (err) { fprintf(stderr, "SharedConnection DNSServiceRegister failed %ld\n", (long int)err); return (-1); }
2243 
2244         err = DNSServiceUpdateRecord(sc3, NULL, 0, sizeof(txtrec), txtrec, 0);
2245         if (err) { fprintf(stderr, "SharedConnection DNSServiceUpdateRecord failed %ld\n", (long int)err); return (-1); }
2246 
2247         err = DNSServiceAddRecord(sc3, &rec, 0, kDNSServiceType_NULL, sizeof(nulrec), nulrec, 0);
2248         if (err) { fprintf(stderr, "SharedConnection DNSServiceAddRecord failed %ld\n", (long int)err); return (-1); }
2249 
2250         err = DNSServiceRemoveRecord(sc3, rec, 0);
2251         if (err) { fprintf(stderr, "SharedConnection DNSServiceRemoveRecord failed %ld\n", (long int)err); return (-1); }
2252 
2253         break;
2254     }
2255 
2256     case 'V':   {
2257         uint32_t v;
2258         uint32_t size = sizeof(v);
2259         err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size);
2260         if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err);
2261         else printf("Currently running daemon (system service) is version %d.%d.%d\n",  v / 10000, v / 100 % 100, v % 100);
2262         exit(0);
2263     }
2264 #ifdef APPLE_OSX_mDNSResponder
2265     case 'O': {
2266         // check if the user specifies the flag "-compress"
2267         uint8_t if_compress_state_dump = 0;
2268         uint8_t if_dump_to_stdout = 0;
2269 
2270         if (argc > opi+1) {
2271             printf("dns-sd: illegal option count\n");
2272             goto Fail;
2273         }
2274 
2275         if (argc == opi+1) {
2276             const char *param = argv[opi];
2277             if (strcasecmp("-compress", param) == 0) {
2278                 if_compress_state_dump = 1;
2279             } else if (strcasecmp("-stdout", param) == 0) {
2280                 if_dump_to_stdout = 1;
2281             } else {
2282                 printf("dns-sd: illegal option %s \n", param);
2283                 goto Fail;
2284             }
2285         }
2286         handle_state_dump_request(if_compress_state_dump, if_dump_to_stdout);
2287         err = kDNSServiceErr_NoError;
2288         break;
2289     }
2290 #endif // APPLE_OSX_mDNSResponder
2291 
2292     case 'H': goto Fail;
2293 
2294     default: goto Fail;
2295     }
2296 #ifdef APPLE_OSX_mDNSResponder
2297     // state dump does not need to create DNSServiceRef, so we can return directly here without cleaning up.
2298     if (operation == 'O')
2299         return 0;
2300 #endif // APPLE_OSX_mDNSResponder
2301 
2302     if (!client || err != kDNSServiceErr_NoError)
2303     {
2304         fprintf(stderr, "DNSService call failed %ld%s\n", (long int)err,
2305             (err == kDNSServiceErr_ServiceNotRunning) ? " (Service Not Running)" : "");
2306         return (-1);
2307     }
2308     printtimestamp();
2309     printf("...STARTING...\n");
2310     HandleEvents();
2311 
2312     // Be sure to deallocate the DNSServiceRef when you're finished
2313     if (client   ) DNSServiceRefDeallocate(client   );
2314     if (client_pa) DNSServiceRefDeallocate(client_pa);
2315     return 0;
2316 
2317 Fail:
2318     if (operation == 'H') print_usage(a0,1);
2319     else print_usage(a0,0);
2320     return 0;
2321 }
2322 
2323 #ifdef APPLE_OSX_mDNSResponder
2324 /*
2325  *  if_compress_state_dump and if_dump_to_stdout cannot be set at the same time.
2326  */
handle_state_dump_request(uint8_t if_compress_state_dump,uint8_t if_dump_to_stdout)2327 static void handle_state_dump_request(uint8_t if_compress_state_dump, uint8_t if_dump_to_stdout)
2328 {
2329     // create xpc connection to the xpc server for log utility
2330     xpc_connection_t log_utility_connection =  xpc_connection_create_mach_service(kDNSLogUtilityService, dispatch_get_main_queue(), XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
2331     xpc_connection_set_event_handler(log_utility_connection, ^(xpc_object_t event){
2332         printf("Connecting to %s, status: %s\n", kDNSLogUtilityService, xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
2333     });
2334     xpc_connection_resume(log_utility_connection);
2335 
2336     // set option for the state dump
2337     xpc_object_t xpc_dict = xpc_dictionary_create(NULL, NULL, 0);
2338     uint64_t dump_option;
2339     if (if_compress_state_dump) {
2340         dump_option = full_state_with_compression;
2341     }
2342     else if (if_dump_to_stdout) {
2343         // we pass the stdout directly to xpc server
2344         dump_option = full_state_to_stdout;
2345         xpc_dictionary_set_fd(xpc_dict, kDNSStateDumpFD, STDOUT_FILENO);
2346     }
2347     else {
2348         dump_option = full_state;
2349     }
2350 
2351     xpc_dictionary_set_uint64(xpc_dict, kDNSStateDump, dump_option);
2352 
2353     // send the request and handle the response from xpc server
2354     xpc_connection_send_message_with_reply(log_utility_connection, xpc_dict, dispatch_get_main_queue(), ^(xpc_object_t recv_msg){
2355         xpc_type_t msg_type = xpc_get_type(recv_msg);
2356 
2357         if (msg_type != XPC_TYPE_DICTIONARY) {
2358             printf("Received unexpected reply from daemon, error: \"%s\"\nUnexpected reply Contents:\n%s\n",
2359                    xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION), xpc_copy_description(recv_msg));
2360             exit(1);
2361         }
2362 
2363         // get the response dictionary
2364         uint32_t return_code = (uint32_t)xpc_dictionary_get_uint64(recv_msg, kDNSDaemonReply);
2365         if (return_code != kDNSMsg_NoError) {
2366             const char *error_description = xpc_dictionary_get_string(recv_msg, kDNSErrorDescription);
2367             printf("XPC service returns error, description: %s\n", error_description);
2368             exit(1);
2369         }
2370 
2371         // print the state information returned from the XPC server
2372         if (dump_option != full_state_to_stdout) {
2373             const char *path = xpc_dictionary_get_string(recv_msg, kDNSDumpFilePath);
2374             printf("State Dump Is Saved to: %s\n", path);
2375         }
2376 
2377         int64_t time_used = xpc_dictionary_get_int64(recv_msg, kDNSStateDumpTimeUsed);
2378         printf("             Time Used: %" PRId64 " ms\n", time_used);
2379 
2380         xpc_release(xpc_dict);
2381         exit(0);
2382     });
2383 
2384     dispatch_main();
2385 }
2386 #endif // APPLE_OSX_mDNSResponder
2387 
2388 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
2389 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
2390 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
2391 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
2392 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
2393 
2394 // NOT static -- otherwise the compiler may optimize it out
2395 // The "@(#) " pattern is a special prefix the "what" command looks for
2396 #ifndef MDNS_VERSIONSTR_NODTS
2397 const char VersionString_SCCS[] = "@(#) dns-sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
2398 #else
2399 const char VersionString_SCCS[] = "@(#) dns-sd " STRINGIFY(mDNSResponderVersion);
2400 #endif
2401 
2402 #if _BUILDING_XCODE_PROJECT_
2403 // If the process crashes, then this string will be magically included in the automatically-generated crash log
2404 const char *__crashreporter_info__ = VersionString_SCCS + 5;
2405 asm (".desc ___crashreporter_info__, 0x10");
2406 #endif
2407