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