1 /* -*- Mode: C; tab-width: 4 -*- 2 * 3 * Copyright (c) 2002-2008 Apple Inc. All rights reserved. 4 * 5 * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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 * Formatting notes: 41 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion 42 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>, 43 * but for the sake of brevity here I will say just this: Curly braces are not syntactially 44 * part of an "if" statement; they are the beginning and ending markers of a compound statement; 45 * therefore common sense dictates that if they are part of a compound statement then they 46 * should be indented to the same level as everything else in that compound statement. 47 * Indenting curly braces at the same level as the "if" implies that curly braces are 48 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;" 49 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't 50 * understand why variable y is not of type "char*" just proves the point that poor code 51 * layout leads people to unfortunate misunderstandings about how the C language really works.) 52 53 To build this tool, copy and paste the following into a command line: 54 55 OS X: 56 gcc dns-sd.c -o dns-sd 57 58 POSIX systems: 59 gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd 60 61 Windows: 62 cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib 63 (may require that you run a Visual Studio script such as vsvars32.bat first) 64 */ 65 66 // For testing changes to dnssd_clientstub.c, uncomment this line and the code will be compiled 67 // with an embedded copy of the client stub instead of linking the system library version at runtime. 68 // This also useful to work around link errors when you're working on an older version of Mac OS X, 69 // and trying to build a newer version of the "dns-sd" command which uses new API entry points that 70 // aren't in the system's /usr/lib/libSystem.dylib. 71 //#define TEST_NEW_CLIENTSTUB 1 72 73 // When building mDNSResponder for Mac OS X 10.4 and earlier, /usr/lib/libSystem.dylib is built using its own private 74 // copy of dnssd_clientstub.c, which is old and doesn't have all the entry points defined in the latest version, so 75 // when we're building dns-sd.c on Mac OS X 10.4 or earlier, we automatically set TEST_NEW_CLIENTSTUB so that we'll 76 // embed a copy of the latest dnssd_clientstub.c instead of trying to link to the incomplete version in libSystem.dylib 77 #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1040 78 #define TEST_NEW_CLIENTSTUB 1 79 #endif 80 81 #include <ctype.h> 82 #include <stdio.h> // For stdout, stderr 83 #include <stdlib.h> // For exit() 84 #include <string.h> // For strlen(), strcpy() 85 #include <errno.h> // For errno, EINTR 86 #include <time.h> 87 #include <sys/types.h> // For u_char 88 89 #ifdef _WIN32 90 #include <winsock2.h> 91 #include <ws2tcpip.h> 92 #include <Iphlpapi.h> 93 #include <process.h> 94 typedef int pid_t; 95 #define getpid _getpid 96 #define strcasecmp _stricmp 97 #define snprintf _snprintf 98 static const char kFilePathSep = '\\'; 99 #ifndef HeapEnableTerminationOnCorruption 100 # define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1 101 #endif 102 #if !defined(IFNAMSIZ) 103 #define IFNAMSIZ 16 104 #endif 105 #define if_nametoindex if_nametoindex_win 106 #define if_indextoname if_indextoname_win 107 108 typedef PCHAR (WINAPI * if_indextoname_funcptr_t)(ULONG index, PCHAR name); 109 typedef ULONG (WINAPI * if_nametoindex_funcptr_t)(PCSTR name); 110 111 unsigned if_nametoindex_win(const char *ifname) 112 { 113 HMODULE library; 114 unsigned index = 0; 115 116 // Try and load the IP helper library dll 117 if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL ) 118 { 119 if_nametoindex_funcptr_t if_nametoindex_funcptr; 120 121 // On Vista and above there is a Posix like implementation of if_nametoindex 122 if ((if_nametoindex_funcptr = (if_nametoindex_funcptr_t) GetProcAddress(library, "if_nametoindex")) != NULL ) 123 { 124 index = if_nametoindex_funcptr(ifname); 125 } 126 127 FreeLibrary(library); 128 } 129 130 return index; 131 } 132 133 char * if_indextoname_win( unsigned ifindex, char *ifname) 134 { 135 HMODULE library; 136 char * name = NULL; 137 138 // Try and load the IP helper library dll 139 if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL ) 140 { 141 if_indextoname_funcptr_t if_indextoname_funcptr; 142 143 // On Vista and above there is a Posix like implementation of if_indextoname 144 if ((if_indextoname_funcptr = (if_indextoname_funcptr_t) GetProcAddress(library, "if_indextoname")) != NULL ) 145 { 146 name = if_indextoname_funcptr(ifindex, ifname); 147 } 148 149 FreeLibrary(library); 150 } 151 152 return name; 153 } 154 155 static size_t _sa_len(const struct sockaddr *addr) 156 { 157 if (addr->sa_family == AF_INET) return (sizeof(struct sockaddr_in)); 158 else if (addr->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6)); 159 else return (sizeof(struct sockaddr)); 160 } 161 162 # define SA_LEN(addr) (_sa_len(addr)) 163 164 #else 165 #include <unistd.h> // For getopt() and optind 166 #include <netdb.h> // For getaddrinfo() 167 #include <sys/time.h> // For struct timeval 168 #include <sys/socket.h> // For AF_INET 169 #include <netinet/in.h> // For struct sockaddr_in() 170 #include <arpa/inet.h> // For inet_addr() 171 #include <net/if.h> // For if_nametoindex() 172 static const char kFilePathSep = '/'; 173 // #ifndef NOT_HAVE_SA_LEN 174 // #define SA_LEN(addr) ((addr)->sa_len) 175 // #else 176 #define SA_LEN(addr) (((addr)->sa_family == AF_INET6)? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) 177 // #endif 178 #endif 179 180 #if (TEST_NEW_CLIENTSTUB && !defined(__APPLE_API_PRIVATE)) 181 #define __APPLE_API_PRIVATE 1 182 #endif 183 184 // DNSServiceSetDispatchQueue is not supported on 10.6 & prior 185 #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) 186 #undef _DNS_SD_LIBDISPATCH 187 #endif 188 #include "dns_sd.h" 189 #include "ClientCommon.h" 190 191 #if TEST_NEW_CLIENTSTUB 192 #include "../mDNSShared/dnssd_ipc.c" 193 #include "../mDNSShared/dnssd_clientlib.c" 194 #include "../mDNSShared/dnssd_clientstub.c" 195 #endif 196 197 // The "+0" is to cope with the case where _DNS_SD_H is defined but empty (e.g. on Mac OS X 10.4 and earlier) 198 #if _DNS_SD_H+0 >= 116 199 #define HAS_NAT_PMP_API 1 200 #define HAS_ADDRINFO_API 1 201 #else 202 #define kDNSServiceFlagsReturnIntermediates 0 203 #endif 204 205 //************************************************************************************************************* 206 // Globals 207 208 typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; 209 210 static int operation; 211 static uint32_t opinterface = kDNSServiceInterfaceIndexAny; 212 static DNSServiceRef client = NULL; 213 static DNSServiceRef client_pa = NULL; // DNSServiceRef for RegisterProxyAddressRecord 214 static DNSServiceRef sc1, sc2, sc3; // DNSServiceRefs for kDNSServiceFlagsShareConnection testing 215 216 static int num_printed; 217 static char addtest = 0; 218 static DNSRecordRef record = NULL; 219 static char myhinfoW[14] = "\002PC\012Windows XP"; 220 static char myhinfoX[ 9] = "\003Mac\004OS X"; 221 static char updatetest[3] = "\002AA"; 222 static char bigNULL[8192]; // 8K is maximum rdata we support 223 224 #if _DNS_SD_LIBDISPATCH 225 dispatch_queue_t main_queue; 226 dispatch_source_t timer_source; 227 #endif 228 229 // Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this 230 #define LONG_TIME 100000000 231 232 static volatile int stopNow = 0; 233 static volatile int timeOut = LONG_TIME; 234 235 #if _DNS_SD_LIBDISPATCH 236 #define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) \ 237 if (main_queue && (E) == kDNSServiceErr_ServiceNotRunning) { fprintf(stderr, "Error code %d\n", (E)); exit(0); } 238 #else 239 #define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) 240 #endif 241 242 //************************************************************************************************************* 243 // Supporting Utility Functions 244 245 static uint16_t GetRRType(const char *s) 246 { 247 if (!strcasecmp(s, "A" )) return(kDNSServiceType_A); 248 else if (!strcasecmp(s, "NS" )) return(kDNSServiceType_NS); 249 else if (!strcasecmp(s, "MD" )) return(kDNSServiceType_MD); 250 else if (!strcasecmp(s, "MF" )) return(kDNSServiceType_MF); 251 else if (!strcasecmp(s, "CNAME" )) return(kDNSServiceType_CNAME); 252 else if (!strcasecmp(s, "SOA" )) return(kDNSServiceType_SOA); 253 else if (!strcasecmp(s, "MB" )) return(kDNSServiceType_MB); 254 else if (!strcasecmp(s, "MG" )) return(kDNSServiceType_MG); 255 else if (!strcasecmp(s, "MR" )) return(kDNSServiceType_MR); 256 else if (!strcasecmp(s, "NULL" )) return(kDNSServiceType_NULL); 257 else if (!strcasecmp(s, "WKS" )) return(kDNSServiceType_WKS); 258 else if (!strcasecmp(s, "PTR" )) return(kDNSServiceType_PTR); 259 else if (!strcasecmp(s, "HINFO" )) return(kDNSServiceType_HINFO); 260 else if (!strcasecmp(s, "MINFO" )) return(kDNSServiceType_MINFO); 261 else if (!strcasecmp(s, "MX" )) return(kDNSServiceType_MX); 262 else if (!strcasecmp(s, "TXT" )) return(kDNSServiceType_TXT); 263 else if (!strcasecmp(s, "RP" )) return(kDNSServiceType_RP); 264 else if (!strcasecmp(s, "AFSDB" )) return(kDNSServiceType_AFSDB); 265 else if (!strcasecmp(s, "X25" )) return(kDNSServiceType_X25); 266 else if (!strcasecmp(s, "ISDN" )) return(kDNSServiceType_ISDN); 267 else if (!strcasecmp(s, "RT" )) return(kDNSServiceType_RT); 268 else if (!strcasecmp(s, "NSAP" )) return(kDNSServiceType_NSAP); 269 else if (!strcasecmp(s, "NSAP_PTR")) return(kDNSServiceType_NSAP_PTR); 270 else if (!strcasecmp(s, "SIG" )) return(kDNSServiceType_SIG); 271 else if (!strcasecmp(s, "KEY" )) return(kDNSServiceType_KEY); 272 else if (!strcasecmp(s, "PX" )) return(kDNSServiceType_PX); 273 else if (!strcasecmp(s, "GPOS" )) return(kDNSServiceType_GPOS); 274 else if (!strcasecmp(s, "AAAA" )) return(kDNSServiceType_AAAA); 275 else if (!strcasecmp(s, "LOC" )) return(kDNSServiceType_LOC); 276 else if (!strcasecmp(s, "NXT" )) return(kDNSServiceType_NXT); 277 else if (!strcasecmp(s, "EID" )) return(kDNSServiceType_EID); 278 else if (!strcasecmp(s, "NIMLOC" )) return(kDNSServiceType_NIMLOC); 279 else if (!strcasecmp(s, "SRV" )) return(kDNSServiceType_SRV); 280 else if (!strcasecmp(s, "ATMA" )) return(kDNSServiceType_ATMA); 281 else if (!strcasecmp(s, "NAPTR" )) return(kDNSServiceType_NAPTR); 282 else if (!strcasecmp(s, "KX" )) return(kDNSServiceType_KX); 283 else if (!strcasecmp(s, "CERT" )) return(kDNSServiceType_CERT); 284 else if (!strcasecmp(s, "A6" )) return(kDNSServiceType_A6); 285 else if (!strcasecmp(s, "DNAME" )) return(kDNSServiceType_DNAME); 286 else if (!strcasecmp(s, "SINK" )) return(kDNSServiceType_SINK); 287 else if (!strcasecmp(s, "OPT" )) return(kDNSServiceType_OPT); 288 else if (!strcasecmp(s, "TKEY" )) return(kDNSServiceType_TKEY); 289 else if (!strcasecmp(s, "TSIG" )) return(kDNSServiceType_TSIG); 290 else if (!strcasecmp(s, "IXFR" )) return(kDNSServiceType_IXFR); 291 else if (!strcasecmp(s, "AXFR" )) return(kDNSServiceType_AXFR); 292 else if (!strcasecmp(s, "MAILB" )) return(kDNSServiceType_MAILB); 293 else if (!strcasecmp(s, "MAILA" )) return(kDNSServiceType_MAILA); 294 else if (!strcasecmp(s, "ANY" )) return(kDNSServiceType_ANY); 295 else return(atoi(s)); 296 } 297 298 #if HAS_NAT_PMP_API | HAS_ADDRINFO_API 299 static DNSServiceProtocol GetProtocol(const char *s) 300 { 301 if (!strcasecmp(s, "v4" )) return(kDNSServiceProtocol_IPv4); 302 else if (!strcasecmp(s, "v6" )) return(kDNSServiceProtocol_IPv6); 303 else if (!strcasecmp(s, "v4v6" )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); 304 else if (!strcasecmp(s, "v6v4" )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); 305 else if (!strcasecmp(s, "udp" )) return(kDNSServiceProtocol_UDP); 306 else if (!strcasecmp(s, "tcp" )) return(kDNSServiceProtocol_TCP); 307 else if (!strcasecmp(s, "udptcp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); 308 else if (!strcasecmp(s, "tcpudp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); 309 else return(atoi(s)); 310 } 311 #endif 312 313 //************************************************************************************************************* 314 // Sample callback functions for each of the operation types 315 316 static void printtimestamp(void) 317 { 318 struct tm tm; 319 int ms; 320 #ifdef _WIN32 321 SYSTEMTIME sysTime; 322 time_t uct = time(NULL); 323 tm = *localtime(&uct); 324 GetLocalTime(&sysTime); 325 ms = sysTime.wMilliseconds; 326 #else 327 struct timeval tv; 328 gettimeofday(&tv, NULL); 329 localtime_r((time_t*)&tv.tv_sec, &tm); 330 ms = tv.tv_usec/1000; 331 #endif 332 printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); 333 } 334 335 #define DomainMsg(X) (((X) & kDNSServiceFlagsDefault) ? "(Default)" : \ 336 ((X) & kDNSServiceFlagsAdd) ? "Added" : "Removed") 337 338 #define MAX_LABELS 128 339 340 static void DNSSD_API enum_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, 341 DNSServiceErrorType errorCode, const char *replyDomain, void *context) 342 { 343 DNSServiceFlags partialflags = flags & ~(kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault); 344 int labels = 0, depth = 0, i, initial = 0; 345 char text[64]; 346 const char *label[MAX_LABELS]; 347 348 (void)sdref; // Unused 349 (void)ifIndex; // Unused 350 (void)context; // Unused 351 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 352 353 // 1. Print the header 354 if (num_printed++ == 0) printf("Timestamp Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing"); 355 printtimestamp(); 356 if (errorCode) 357 printf("Error code %d\n", errorCode); 358 else if (!*replyDomain) 359 printf("Error: No reply domain\n"); 360 else 361 { 362 printf("%-10s", DomainMsg(flags)); 363 printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : ""); 364 if (partialflags) printf("Flags: %4X ", partialflags); 365 else printf(" "); 366 367 // 2. Count the labels 368 while (replyDomain && *replyDomain && labels < MAX_LABELS) 369 { 370 label[labels++] = replyDomain; 371 replyDomain = GetNextLabel(replyDomain, text); 372 } 373 374 // 3. Decide if we're going to clump the last two or three labels (e.g. "apple.com", or "nicta.com.au") 375 if (labels >= 3 && replyDomain - label[labels-1] <= 3 && label[labels-1] - label[labels-2] <= 4) initial = 3; 376 else if (labels >= 2 && replyDomain - label[labels-1] <= 4) initial = 2; 377 else initial = 1; 378 labels -= initial; 379 380 // 4. Print the initial one-, two- or three-label clump 381 for (i=0; i<initial; i++) 382 { 383 GetNextLabel(label[labels+i], text); 384 if (i>0) printf("."); 385 printf("%s", text); 386 } 387 printf("\n"); 388 389 // 5. Print the remainder of the hierarchy 390 for (depth=0; depth<labels; depth++) 391 { 392 printf(" "); 393 for (i=0; i<=depth; i++) printf("- "); 394 GetNextLabel(label[labels-1-depth], text); 395 printf("> %s\n", text); 396 } 397 } 398 399 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 400 } 401 402 static int CopyLabels(char *dst, const char *lim, const char **srcp, int labels) 403 { 404 const char *src = *srcp; 405 while (*src != '.' || --labels > 0) 406 { 407 if (*src == '\\') *dst++ = *src++; // Make sure "\." doesn't confuse us 408 if (!*src || dst >= lim) return -1; 409 *dst++ = *src++; 410 if (!*src || dst >= lim) return -1; 411 } 412 *dst++ = 0; 413 *srcp = src + 1; // skip over final dot 414 return 0; 415 } 416 417 static void DNSSD_API zonedata_resolve(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, 418 const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txt, void *context) 419 { 420 union { uint16_t s; u_char b[2]; } port = { opaqueport }; 421 uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; 422 423 const char *p = fullname; 424 char n[kDNSServiceMaxDomainName]; 425 char t[kDNSServiceMaxDomainName]; 426 427 const unsigned char *max = txt + txtLen; 428 429 (void)sdref; // Unused 430 (void)ifIndex; // Unused 431 (void)context; // Unused 432 433 //if (!(flags & kDNSServiceFlagsAdd)) return; 434 if (errorCode) { printf("Error code %d\n", errorCode); return; } 435 436 if (CopyLabels(n, n + kDNSServiceMaxDomainName, &p, 3)) return; // Fetch name+type 437 p = fullname; 438 if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 1)) return; // Skip first label 439 if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 2)) return; // Fetch next two labels (service type) 440 441 if (num_printed++ == 0) 442 { 443 printf("\n"); 444 printf("; To direct clients to browse a different domain, substitute that domain in place of '@'\n"); 445 printf("%-47s PTR %s\n", "lb._dns-sd._udp", "@"); 446 printf("\n"); 447 printf("; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.\n"); 448 printf("; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local\n"); 449 printf("; names with the correct fully-qualified (unicast) domain name of the target host offering the service.\n"); 450 } 451 452 printf("\n"); 453 printf("%-47s PTR %s\n", t, n); 454 printf("%-47s SRV 0 0 %d %s ; Replace with unicast FQDN of target host\n", n, PortAsNumber, hosttarget); 455 printf("%-47s TXT ", n); 456 457 while (txt < max) 458 { 459 const unsigned char *const end = txt + 1 + txt[0]; 460 txt++; // Skip over length byte 461 printf(" \""); 462 while (txt<end) 463 { 464 if (*txt == '\\' || *txt == '\"') printf("\\"); 465 printf("%c", *txt++); 466 } 467 printf("\""); 468 } 469 printf("\n"); 470 471 DNSServiceRefDeallocate(sdref); 472 free(context); 473 474 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 475 } 476 477 static void DNSSD_API zonedata_browse(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, 478 const char *replyName, const char *replyType, const char *replyDomain, void *context) 479 { 480 DNSServiceRef *newref; 481 482 (void)sdref; // Unused 483 (void)context; // Unused 484 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 485 486 if (!(flags & kDNSServiceFlagsAdd)) return; 487 if (errorCode) { printf("Error code %d\n", errorCode); return; } 488 489 newref = malloc(sizeof(*newref)); 490 *newref = client; 491 DNSServiceResolve(newref, kDNSServiceFlagsShareConnection, ifIndex, replyName, replyType, replyDomain, zonedata_resolve, newref); 492 } 493 494 static void DNSSD_API browse_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, 495 const char *replyName, const char *replyType, const char *replyDomain, void *context) 496 { 497 char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; 498 (void)sdref; // Unused 499 (void)context; // Unused 500 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 501 502 if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-25s %s\n", "Domain", "Service Type", "Instance Name"); 503 printtimestamp(); 504 if (errorCode) printf("Error code %d\n", errorCode); 505 else printf("%s%6X%3d %-25s %-25s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName); 506 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 507 508 // To test selective cancellation of operations of shared sockets, 509 // cancel the current operation when we've got a multiple of five results 510 //if (operation == 'S' && num_printed % 5 == 0) DNSServiceRefDeallocate(sdref); 511 } 512 513 static void ShowTXTRecord(uint16_t txtLen, const unsigned char *txtRecord) 514 { 515 const unsigned char *ptr = txtRecord; 516 const unsigned char *max = txtRecord + txtLen; 517 while (ptr < max) 518 { 519 const unsigned char *const end = ptr + 1 + ptr[0]; 520 if (end > max) { printf("<< invalid data >>"); break; } 521 if (++ptr < end) printf(" "); // As long as string is non-empty, begin with a space 522 while (ptr<end) 523 { 524 // We'd like the output to be shell-friendly, so that it can be copied and pasted unchanged into a "dns-sd -R" command. 525 // However, this is trickier than it seems. Enclosing a string in double quotes doesn't necessarily make it 526 // shell-safe, because shells still expand variables like $foo even when they appear inside quoted strings. 527 // Enclosing a string in single quotes is better, but when using single quotes even backslash escapes are ignored, 528 // meaning there's simply no way to represent a single quote (or apostrophe) inside a single-quoted string. 529 // The only remaining solution is not to surround the string with quotes at all, but instead to use backslash 530 // escapes to encode spaces and all other known shell metacharacters. 531 // (If we've missed any known shell metacharacters, please let us know.) 532 // In addition, non-printing ascii codes (0-31) are displayed as \xHH, using a two-digit hex value. 533 // Because '\' is itself a shell metacharacter (the shell escape character), it has to be escaped as "\\" to survive 534 // the round-trip to the shell and back. This means that a single '\' is represented here as EIGHT backslashes: 535 // The C compiler eats half of them, resulting in four appearing in the output. 536 // The shell parses those four as a pair of "\\" sequences, passing two backslashes to the "dns-sd -R" command. 537 // The "dns-sd -R" command interprets this single "\\" pair as an escaped literal backslash. Sigh. 538 if (strchr(" &;`'\"|*?~<>^()[]{}$", *ptr)) printf("\\"); 539 if (*ptr == '\\') printf("\\\\\\\\"); 540 else if (*ptr >= ' ' ) printf("%c", *ptr); 541 else printf("\\\\x%02X", *ptr); 542 ptr++; 543 } 544 } 545 } 546 547 static void DNSSD_API resolve_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, 548 const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txtRecord, void *context) 549 { 550 union { uint16_t s; u_char b[2]; } port = { opaqueport }; 551 uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; 552 553 (void)sdref; // Unused 554 (void)ifIndex; // Unused 555 (void)context; // Unused 556 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 557 558 if (errorCode) 559 printf("Error code %d\n", errorCode); 560 else 561 { 562 printtimestamp(); 563 printf("%s can be reached at %s:%u (interface %d)", fullname, hosttarget, PortAsNumber, ifIndex); 564 if (flags) printf(" Flags: %X", flags); 565 // Don't show degenerate TXT records containing nothing but a single empty string 566 if (txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); } 567 printf("\n"); 568 } 569 570 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 571 } 572 573 static void myTimerCallBack(void) 574 { 575 DNSServiceErrorType err = kDNSServiceErr_Unknown; 576 577 switch (operation) 578 { 579 case 'A': 580 { 581 switch (addtest) 582 { 583 case 0: printf("Adding Test HINFO record\n"); 584 err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_HINFO, sizeof(myhinfoW), &myhinfoW[0], 0); 585 addtest = 1; 586 break; 587 case 1: printf("Updating Test HINFO record\n"); 588 err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 0); 589 addtest = 2; 590 break; 591 case 2: printf("Removing Test HINFO record\n"); 592 err = DNSServiceRemoveRecord(client, record, 0); 593 addtest = 0; 594 break; 595 } 596 } 597 break; 598 599 case 'U': 600 { 601 if (updatetest[1] != 'Z') updatetest[1]++; 602 else updatetest[1] = 'A'; 603 updatetest[0] = 3 - updatetest[0]; 604 updatetest[2] = updatetest[1]; 605 printtimestamp(); 606 printf("Updating Test TXT record to %c\n", updatetest[1]); 607 err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 0); 608 } 609 break; 610 611 case 'N': 612 { 613 printf("Adding big NULL record\n"); 614 err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0); 615 if (err) printf("Failed: %d\n", err); else printf("Succeeded\n"); 616 timeOut = LONG_TIME; 617 #if _DNS_SD_LIBDISPATCH 618 if (timer_source) 619 dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), 620 (uint64_t)timeOut * NSEC_PER_SEC, 0); 621 #endif 622 } 623 break; 624 } 625 626 if (err != kDNSServiceErr_NoError) 627 { 628 fprintf(stderr, "DNSService add/update/remove failed %ld\n", (long int)err); 629 stopNow = 1; 630 } 631 } 632 633 static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode, 634 const char *name, const char *regtype, const char *domain, void *context) 635 { 636 (void)sdref; // Unused 637 (void)flags; // Unused 638 (void)context; // Unused 639 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 640 641 printtimestamp(); 642 printf("Got a reply for service %s.%s%s: ", name, regtype, domain); 643 644 if (errorCode == kDNSServiceErr_NoError) 645 { 646 if (flags & kDNSServiceFlagsAdd) printf("Name now registered and active\n"); 647 else printf("Name registration removed\n"); 648 if (operation == 'A' || operation == 'U' || operation == 'N') 649 { 650 timeOut = 5; 651 #if _DNS_SD_LIBDISPATCH 652 if (timer_source) 653 dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), 654 (uint64_t)timeOut * NSEC_PER_SEC, 0); 655 #endif 656 } 657 } 658 else if (errorCode == kDNSServiceErr_NameConflict) 659 { 660 printf("Name in use, please choose another\n"); 661 exit(-1); 662 } 663 else 664 printf("Error %d\n", errorCode); 665 666 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 667 } 668 669 // Output the wire-format domainname pointed to by rd 670 static int snprintd(char *p, int max, const unsigned char **rd) 671 { 672 const char *const buf = p; 673 const char *const end = p + max; 674 while (**rd) { p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); *rd += 1 + **rd; } 675 *rd += 1; // Advance over the final zero byte 676 return(p-buf); 677 } 678 679 static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, 680 const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) 681 { 682 char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; 683 const unsigned char *rd = rdata; 684 const unsigned char *end = (const unsigned char *) rdata + rdlen; 685 char rdb[1000] = "", *p = rdb; 686 int unknowntype = 0; 687 688 (void)sdref; // Unused 689 (void)flags; // Unused 690 (void)ifIndex; // Unused 691 (void)ttl; // Unused 692 (void)context; // Unused 693 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 694 695 if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); 696 printtimestamp(); 697 698 if (!errorCode) 699 { 700 switch (rrtype) 701 { 702 case kDNSServiceType_A: 703 snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); 704 break; 705 706 case kDNSServiceType_NS: 707 case kDNSServiceType_CNAME: 708 case kDNSServiceType_PTR: 709 case kDNSServiceType_DNAME: 710 p += snprintd(p, sizeof(rdb), &rd); 711 break; 712 713 case kDNSServiceType_SOA: 714 p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname 715 p += snprintf(p, rdb + sizeof(rdb) - p, " "); 716 p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname 717 p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", 718 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])); 719 break; 720 721 case kDNSServiceType_AAAA: 722 snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", 723 rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], 724 rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); 725 break; 726 727 case kDNSServiceType_SRV: 728 p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port 729 ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); 730 rd += 6; 731 p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host 732 break; 733 734 default : snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); unknowntype = 1; break; 735 } 736 } 737 738 printf("%s%6X%3d %-30s%4d%4d %s", op, flags, ifIndex, fullname, rrtype, rrclass, rdb); 739 if (unknowntype) while (rd < end) printf(" %02X", *rd++); 740 if (errorCode) 741 { 742 if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record"); 743 else if (errorCode == kDNSServiceErr_Timeout) 744 { 745 printf("No Such Record\n"); 746 printf("Query Timed Out\n"); 747 exit(1); 748 } 749 } 750 printf("\n"); 751 752 if (operation == 'C') 753 if (flags & kDNSServiceFlagsAdd) 754 DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata); 755 756 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 757 } 758 759 #if HAS_NAT_PMP_API 760 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) 761 { 762 (void)sdref; // Unused 763 (void)flags; // Unused 764 (void)context; // Unused 765 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 766 767 if (num_printed++ == 0) printf("Timestamp if %-20s %-15s %-15s %-15s %-6s\n", "External Address", "Protocol", "Internal Port", "External Port", "TTL"); 768 printtimestamp(); 769 if (errorCode && errorCode != kDNSServiceErr_DoubleNAT) printf("Error code %d\n", errorCode); 770 else 771 { 772 const unsigned char *digits = (const unsigned char *)&publicAddress; 773 char addr[256]; 774 775 snprintf(addr, sizeof(addr), "%d.%d.%d.%d", digits[0], digits[1], digits[2], digits[3]); 776 printf("%-4d %-20s %-15d %-15d %-15d %-6d%s\n", ifIndex, addr, protocol, ntohs(privatePort), ntohs(publicPort), ttl, errorCode == kDNSServiceErr_DoubleNAT ? " Double NAT" : ""); 777 } 778 779 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 780 } 781 #endif 782 783 #if HAS_ADDRINFO_API 784 static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context) 785 { 786 char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; 787 char addr[256] = ""; 788 (void) sdref; 789 (void) context; 790 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 791 792 if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-44s %s\n", "Hostname", "Address", "TTL"); 793 printtimestamp(); 794 795 if (address && address->sa_family == AF_INET) 796 { 797 const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr; 798 snprintf(addr, sizeof(addr), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]); 799 } 800 else if (address && address->sa_family == AF_INET6) 801 { 802 char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE 803 const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *)address; 804 const unsigned char *b = (const unsigned char * )&s6->sin6_addr; 805 if (!if_indextoname(s6->sin6_scope_id, if_name)) 806 snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id); 807 snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s", 808 b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7], 809 b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name); 810 } 811 812 printf("%s%6X%3d %-25s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl); 813 if (errorCode) 814 { 815 if (errorCode == kDNSServiceErr_NoSuchRecord) printf(" No Such Record"); 816 else printf(" Error code %d", errorCode); 817 } 818 printf("\n"); 819 820 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 821 } 822 #endif 823 824 //************************************************************************************************************* 825 // The main test function 826 827 static void HandleEvents(void) 828 #if _DNS_SD_LIBDISPATCH 829 { 830 main_queue = dispatch_get_main_queue(); 831 if (client) DNSServiceSetDispatchQueue(client, main_queue); 832 if (client_pa) DNSServiceSetDispatchQueue(client_pa, main_queue); 833 if (operation == 'A' || operation == 'U' || operation == 'N') 834 { 835 timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_queue); 836 if (timer_source) 837 { 838 // Start the timer "timeout" seconds into the future and repeat it every "timeout" seconds 839 dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC), 840 (uint64_t)timeOut * NSEC_PER_SEC, 0); 841 dispatch_source_set_event_handler(timer_source, ^{myTimerCallBack();}); 842 dispatch_resume(timer_source); 843 } 844 } 845 dispatch_main(); 846 } 847 #else 848 { 849 int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1; 850 int dns_sd_fd2 = client_pa ? DNSServiceRefSockFD(client_pa) : -1; 851 int nfds = dns_sd_fd + 1; 852 fd_set readfds; 853 struct timeval tv; 854 int result; 855 856 if (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1; 857 858 while (!stopNow) 859 { 860 // 1. Set up the fd_set as usual here. 861 // This example client has no file descriptors of its own, 862 // but a real application would call FD_SET to add them to the set here 863 FD_ZERO(&readfds); 864 865 // 2. Add the fd for our client(s) to the fd_set 866 if (client ) FD_SET(dns_sd_fd , &readfds); 867 if (client_pa) FD_SET(dns_sd_fd2, &readfds); 868 869 // 3. Set up the timeout. 870 tv.tv_sec = timeOut; 871 tv.tv_usec = 0; 872 873 result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv); 874 if (result > 0) 875 { 876 DNSServiceErrorType err = kDNSServiceErr_NoError; 877 if (client && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(client ); 878 else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa); 879 if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } 880 } 881 else if (result == 0) 882 myTimerCallBack(); 883 else 884 { 885 printf("select() returned %d errno %d %s\n", result, errno, strerror(errno)); 886 if (errno != EINTR) stopNow = 1; 887 } 888 } 889 } 890 #endif 891 892 static int getfirstoption(int argc, char **argv, const char *optstr, int *pOptInd) 893 // Return the recognized option in optstr and the option index of the next arg. 894 #if NOT_HAVE_GETOPT 895 { 896 int i; 897 for (i=1; i < argc; i++) 898 { 899 if (argv[i][0] == '-' && &argv[i][1] && 900 NULL != strchr(optstr, argv[i][1])) 901 { 902 *pOptInd = i + 1; 903 return argv[i][1]; 904 } 905 } 906 return -1; 907 } 908 #else 909 { 910 int o = getopt(argc, (char *const *)argv, optstr); 911 *pOptInd = optind; 912 return o; 913 } 914 #endif 915 916 static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef rec, const DNSServiceFlags flags, 917 DNSServiceErrorType errorCode, void *context) 918 { 919 char *name = (char *)context; 920 921 (void)service; // Unused 922 (void)rec; // Unused 923 (void)flags; // Unused 924 EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); 925 926 printtimestamp(); 927 printf("Got a reply for record %s: ", name); 928 929 switch (errorCode) 930 { 931 case kDNSServiceErr_NoError: printf("Name now registered and active\n"); break; 932 case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); exit(-1); 933 default: printf("Error %d\n", errorCode); break; 934 } 935 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 936 // DNSServiceRemoveRecord(service, rec, 0); to test record removal 937 938 #if 0 // To test updating of individual records registered via DNSServiceRegisterRecord 939 if (!errorCode) 940 { 941 int x = 0x11111111; 942 printf("Updating\n"); 943 DNSServiceUpdateRecord(service, rec, 0, sizeof(x), &x, 0); 944 } 945 #endif 946 947 if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); 948 } 949 950 static void getip(const char *const name, struct sockaddr_storage *result) 951 { 952 struct addrinfo *addrs = NULL; 953 int err = getaddrinfo(name, NULL, NULL, &addrs); 954 if (err) fprintf(stderr, "getaddrinfo error %d for %s", err, name); 955 else memcpy(result, addrs->ai_addr, SA_LEN(addrs->ai_addr)); 956 if (addrs) freeaddrinfo(addrs); 957 } 958 959 static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip, DNSServiceFlags flags) 960 { 961 // Call getip() after the call DNSServiceCreateConnection(). 962 // On the Win32 platform, WinSock must be initialized for getip() to succeed. 963 // Any DNSService* call will initialize WinSock for us, so we make sure 964 // DNSServiceCreateConnection() is called before getip() is. 965 struct sockaddr_storage hostaddr; 966 getip(ip, &hostaddr); 967 flags |= kDNSServiceFlagsUnique; 968 if (hostaddr.ss_family == AF_INET) 969 return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host, 970 kDNSServiceType_A, kDNSServiceClass_IN, 4, &((struct sockaddr_in *)&hostaddr)->sin_addr, 240, MyRegisterRecordCallback, (void*)host)); 971 else if (hostaddr.ss_family == AF_INET6) 972 return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host, 973 kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host)); 974 else return(kDNSServiceErr_BadParam); 975 } 976 977 #define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ 978 ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ 979 ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : 0) 980 981 #define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1])) 982 983 static DNSServiceErrorType RegisterService(DNSServiceRef *sdref, 984 const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags) 985 { 986 uint16_t PortAsNumber = atoi(port); 987 Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; 988 unsigned char txt[2048] = ""; 989 unsigned char *ptr = txt; 990 int i; 991 992 if (nam[0] == '.' && nam[1] == 0) nam = ""; // We allow '.' on the command line as a synonym for empty string 993 if (dom[0] == '.' && dom[1] == 0) dom = ""; // We allow '.' on the command line as a synonym for empty string 994 995 printf("Registering Service %s.%s%s%s", nam[0] ? nam : "<<Default>>", typ, dom[0] ? "." : "", dom); 996 if (host && *host) printf(" host %s", host); 997 printf(" port %s", port); 998 999 if (argc) 1000 { 1001 for (i = 0; i < argc; i++) 1002 { 1003 const char *p = argv[i]; 1004 *ptr = 0; 1005 while (*p && *ptr < 255 && ptr + 1 + *ptr < txt+sizeof(txt)) 1006 { 1007 if (p[0] != '\\' || p[1] == 0) { ptr[++*ptr] = *p; p+=1; } 1008 else if (p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3])) { ptr[++*ptr] = HexPair(p+2); p+=4; } 1009 else { ptr[++*ptr] = p[1]; p+=2; } 1010 } 1011 ptr += 1 + *ptr; 1012 } 1013 printf(" TXT"); 1014 ShowTXTRecord(ptr-txt, txt); 1015 } 1016 printf("\n"); 1017 1018 //flags |= kDNSServiceFlagsAllowRemoteQuery; 1019 //flags |= kDNSServiceFlagsNoAutoRename; 1020 1021 return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL)); 1022 } 1023 1024 #define TypeBufferSize 80 1025 static char *gettype(char *buffer, char *typ) 1026 { 1027 if (!typ || !*typ || (typ[0] == '.' && typ[1] == 0)) typ = "_http._tcp"; 1028 if (!strchr(typ, '.')) { snprintf(buffer, TypeBufferSize, "%s._tcp", typ); typ = buffer; } 1029 return(typ); 1030 } 1031 1032 int main(int argc, char **argv) 1033 { 1034 DNSServiceErrorType err; 1035 char buffer[TypeBufferSize], *typ, *dom; 1036 int opi; 1037 DNSServiceFlags flags = 0; 1038 1039 // Extract the program name from argv[0], which by convention contains the path to this executable. 1040 // Note that this is just a voluntary convention, not enforced by the kernel -- 1041 // the process calling exec() can pass bogus data in argv[0] if it chooses to. 1042 const char *a0 = strrchr(argv[0], kFilePathSep) + 1; 1043 if (a0 == (const char *)1) a0 = argv[0]; 1044 1045 #if defined(_WIN32) 1046 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); 1047 #endif 1048 1049 #if TEST_NEW_CLIENTSTUB 1050 printf("Using embedded copy of dnssd_clientstub instead of system library\n"); 1051 if (sizeof(argv) == 8) printf("Running in 64-bit mode\n"); 1052 #endif 1053 1054 // Test code for TXTRecord functions 1055 //TXTRecordRef txtRecord; 1056 //TXTRecordCreate(&txtRecord, 0, NULL); 1057 //TXTRecordSetValue(&txtRecord, "aaa", 1, "b"); 1058 //printf("%d\n", TXTRecordContainsKey(TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), "Aaa")); 1059 1060 if (argc > 1 && !strcmp(argv[1], "-lo")) 1061 { 1062 argc--; 1063 argv++; 1064 opinterface = kDNSServiceInterfaceIndexLocalOnly; 1065 printf("Using LocalOnly\n"); 1066 } 1067 1068 if (argc > 1 && (!strcmp(argv[1], "-p2p") || !strcmp(argv[1], "-P2P"))) 1069 { 1070 argc--; 1071 argv++; 1072 opinterface = kDNSServiceInterfaceIndexP2P; 1073 printf("Using P2P\n"); 1074 } 1075 1076 if (argc > 1 && !strcasecmp(argv[1], "-includep2p")) 1077 { 1078 argc--; 1079 argv++; 1080 flags |= kDNSServiceFlagsIncludeP2P; 1081 printf("Including P2P\n"); 1082 } 1083 1084 if (argc > 2 && !strcmp(argv[1], "-i")) 1085 { 1086 opinterface = if_nametoindex(argv[2]); 1087 if (!opinterface) opinterface = atoi(argv[2]); 1088 if (!opinterface) { fprintf(stderr, "Unknown interface %s\n", argv[2]); goto Fail; } 1089 argc -= 2; 1090 argv += 2; 1091 } 1092 1093 if (argc < 2) goto Fail; // Minimum command line is the command name and one argument 1094 operation = getfirstoption(argc, argv, "EFBZLlRPQqtCAUNTMISV" 1095 #if HAS_NAT_PMP_API 1096 "X" 1097 #endif 1098 #if HAS_ADDRINFO_API 1099 "G" 1100 #endif 1101 , &opi); 1102 if (operation == -1) goto Fail; 1103 1104 if (opinterface) printf("Using interface %d\n", opinterface); 1105 1106 switch (operation) 1107 { 1108 case 'E': printf("Looking for recommended registration domains:\n"); 1109 err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, opinterface, enum_reply, NULL); 1110 break; 1111 1112 case 'F': printf("Looking for recommended browsing domains:\n"); 1113 err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, opinterface, enum_reply, NULL); 1114 //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL); 1115 //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "bonjour.nicta.com.au.", NULL); 1116 //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "ibm.com.", NULL); 1117 //enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL); 1118 break; 1119 1120 case 'B': typ = (argc < opi+1) ? "" : argv[opi+0]; 1121 dom = (argc < opi+2) ? "" : argv[opi+1]; // Missing domain argument is the same as empty string i.e. use system default(s) 1122 typ = gettype(buffer, typ); 1123 if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string 1124 printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); 1125 err = DNSServiceBrowse(&client, flags, opinterface, typ, dom, browse_reply, NULL); 1126 break; 1127 1128 case 'Z': typ = (argc < opi+1) ? "" : argv[opi+0]; 1129 dom = (argc < opi+2) ? "" : argv[opi+1]; // Missing domain argument is the same as empty string i.e. use system default(s) 1130 typ = gettype(buffer, typ); 1131 if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string 1132 printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); 1133 err = DNSServiceCreateConnection(&client); 1134 sc1 = client; 1135 err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL); 1136 break; 1137 1138 case 'l': 1139 case 'L': { 1140 DNSServiceFlags rflags = 0; 1141 if (argc < opi+2) goto Fail; 1142 typ = (argc < opi+2) ? "" : argv[opi+1]; 1143 dom = (argc < opi+3) ? "local" : argv[opi+2]; 1144 typ = gettype(buffer, typ); 1145 if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local" 1146 printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom); 1147 if (operation == 'l') rflags |= kDNSServiceFlagsWakeOnResolve; 1148 err = DNSServiceResolve(&client, rflags, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL); 1149 break; 1150 } 1151 1152 case 'R': if (argc < opi+4) goto Fail; 1153 typ = (argc < opi+2) ? "" : argv[opi+1]; 1154 dom = (argc < opi+3) ? "" : argv[opi+2]; 1155 typ = gettype(buffer, typ); 1156 if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string 1157 err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags); 1158 break; 1159 1160 case 'P': if (argc < opi+6) goto Fail; 1161 err = DNSServiceCreateConnection(&client_pa); 1162 if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } 1163 err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5], flags); 1164 if (err) break; 1165 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); 1166 break; 1167 1168 case 't': 1169 case 'q': 1170 case 'Q': 1171 case 'C': { 1172 uint16_t rrtype, rrclass; 1173 flags |= kDNSServiceFlagsReturnIntermediates; 1174 if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable; 1175 if (operation == 't') flags |= (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout); 1176 if (argc < opi+1) goto Fail; 1177 rrtype = (argc <= opi+1) ? kDNSServiceType_A : GetRRType(argv[opi+1]); 1178 rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]); 1179 if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery; 1180 err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL); 1181 break; 1182 } 1183 1184 case 'A': 1185 case 'U': 1186 case 'N': { 1187 Opaque16 registerPort = { { 0x12, 0x34 } }; 1188 static const char TXT[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; 1189 printf("Registering Service Test._testupdate._tcp.local.\n"); 1190 err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testupdate._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL); 1191 break; 1192 } 1193 1194 case 'T': { 1195 Opaque16 registerPort = { { 0x23, 0x45 } }; 1196 char TXT[1024]; 1197 unsigned int i; 1198 for (i=0; i<sizeof(TXT); i++) 1199 if ((i & 0x1F) == 0) TXT[i] = 0x1F; else TXT[i] = 'A' + (i >> 5); 1200 printf("Registering Service Test._testlargetxt._tcp.local.\n"); 1201 err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testlargetxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT), TXT, reg_reply, NULL); 1202 break; 1203 } 1204 1205 case 'M': { 1206 pid_t pid = getpid(); 1207 Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; 1208 static const char TXT1[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String"; 1209 static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String" "\xC" "Sixth String"; 1210 printf("Registering Service Test._testdualtxt._tcp.local.\n"); 1211 err = DNSServiceRegister(&client, flags, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL); 1212 if (!err) err = DNSServiceAddRecord(client, &record, flags, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0); 1213 break; 1214 } 1215 1216 case 'I': { 1217 pid_t pid = getpid(); 1218 Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; 1219 static const char TXT[] = "\x09" "Test Data"; 1220 printf("Registering Service Test._testtxt._tcp.local.\n"); 1221 err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); 1222 if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0); 1223 break; 1224 } 1225 1226 #if HAS_NAT_PMP_API 1227 case 'X': { 1228 if (argc == opi) // If no arguments, just fetch IP address 1229 err = DNSServiceNATPortMappingCreate(&client, 0, 0, 0, 0, 0, 0, port_mapping_create_reply, NULL); 1230 else if (argc >= opi+2 && atoi(argv[opi+0]) == 0) 1231 { 1232 DNSServiceProtocol prot = GetProtocol(argv[opi+0]); // Must specify TCP or UDP 1233 uint16_t IntPortAsNumber = atoi(argv[opi+1]); // Must specify internal port 1234 uint16_t ExtPortAsNumber = (argc < opi+3) ? 0 : atoi(argv[opi+2]); // Optional desired external port 1235 uint32_t ttl = (argc < opi+4) ? 0 : atoi(argv[opi+3]); // Optional desired lease lifetime 1236 Opaque16 intp = { { IntPortAsNumber >> 8, IntPortAsNumber & 0xFF } }; 1237 Opaque16 extp = { { ExtPortAsNumber >> 8, ExtPortAsNumber & 0xFF } }; 1238 err = DNSServiceNATPortMappingCreate(&client, 0, 0, prot, intp.NotAnInteger, extp.NotAnInteger, ttl, port_mapping_create_reply, NULL); 1239 } 1240 else goto Fail; 1241 break; 1242 } 1243 #endif 1244 1245 #if HAS_ADDRINFO_API 1246 case 'G': { 1247 if (argc != opi+2) goto Fail; 1248 else err = DNSServiceGetAddrInfo(&client, kDNSServiceFlagsReturnIntermediates, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL); 1249 break; 1250 } 1251 #endif 1252 1253 case 'S': { 1254 Opaque16 registerPort = { { 0x23, 0x45 } }; // 9029 decimal 1255 unsigned char txtrec[16] = "\xF" "/path=test.html"; 1256 DNSRecordRef rec; 1257 unsigned char nulrec[4] = "1234"; 1258 1259 err = DNSServiceCreateConnection(&client); 1260 if (err) { fprintf(stderr, "DNSServiceCreateConnection failed %ld\n", (long int)err); return (-1); } 1261 1262 sc1 = client; 1263 err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, "_http._tcp", "", browse_reply, NULL); 1264 if (err) { fprintf(stderr, "DNSServiceBrowse _http._tcp failed %ld\n", (long int)err); return (-1); } 1265 1266 sc2 = client; 1267 err = DNSServiceBrowse(&sc2, kDNSServiceFlagsShareConnection, opinterface, "_ftp._tcp", "", browse_reply, NULL); 1268 if (err) { fprintf(stderr, "DNSServiceBrowse _ftp._tcp failed %ld\n", (long int)err); return (-1); } 1269 1270 sc3 = client; 1271 err = DNSServiceRegister(&sc3, kDNSServiceFlagsShareConnection, opinterface, "kDNSServiceFlagsShareConnection", 1272 "_http._tcp", "local", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL); 1273 if (err) { fprintf(stderr, "SharedConnection DNSServiceRegister failed %ld\n", (long int)err); return (-1); } 1274 1275 err = DNSServiceUpdateRecord(sc3, NULL, 0, sizeof(txtrec), txtrec, 0); 1276 if (err) { fprintf(stderr, "SharedConnection DNSServiceUpdateRecord failed %ld\n", (long int)err); return (-1); } 1277 1278 err = DNSServiceAddRecord(sc3, &rec, 0, kDNSServiceType_NULL, sizeof(nulrec), nulrec, 0); 1279 if (err) { fprintf(stderr, "SharedConnection DNSServiceAddRecord failed %ld\n", (long int)err); return (-1); } 1280 1281 err = DNSServiceRemoveRecord(sc3, rec, 0); 1282 if (err) { fprintf(stderr, "SharedConnection DNSServiceRemoveRecord failed %ld\n", (long int)err); return (-1); } 1283 1284 break; 1285 } 1286 1287 case 'V': { 1288 uint32_t v; 1289 uint32_t size = sizeof(v); 1290 err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size); 1291 if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err); 1292 else printf("Currently running daemon (system service) is version %d.%d\n", v / 10000, v / 100 % 100); 1293 exit(0); 1294 } 1295 1296 default: goto Fail; 1297 } 1298 1299 if (!client || err != kDNSServiceErr_NoError) { fprintf(stderr, "DNSService call failed %ld\n", (long int)err); return (-1); } 1300 HandleEvents(); 1301 1302 // Be sure to deallocate the DNSServiceRef when you're finished 1303 if (client ) DNSServiceRefDeallocate(client ); 1304 if (client_pa) DNSServiceRefDeallocate(client_pa); 1305 return 0; 1306 1307 Fail: 1308 fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", a0); 1309 fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", a0); 1310 fprintf(stderr, "%s -B <Type> <Domain> (Browse for services instances)\n", a0); 1311 fprintf(stderr, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", a0); 1312 fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", a0); 1313 fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...] (Proxy)\n", a0); 1314 fprintf(stderr, "%s -Z <Type> <Domain> (Output results in Zone File format)\n", a0); 1315 fprintf(stderr, "%s -Q <FQDN> <rrtype> <rrclass> (Generic query for any record type)\n", a0); 1316 fprintf(stderr, "%s -C <FQDN> <rrtype> <rrclass> (Query; reconfirming each result)\n", a0); 1317 #if HAS_NAT_PMP_API 1318 fprintf(stderr, "%s -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL> (NAT Port Mapping)\n", a0); 1319 #endif 1320 #if HAS_ADDRINFO_API 1321 fprintf(stderr, "%s -G v4/v6/v4v6 <Hostname> (Get address information for hostname)\n", a0); 1322 #endif 1323 fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", a0); 1324 1325 fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", a0); 1326 fprintf(stderr, "%s -U (Test updating a TXT record)\n", a0); 1327 fprintf(stderr, "%s -N (Test adding a large NULL record)\n", a0); 1328 fprintf(stderr, "%s -T (Test creating a large TXT record)\n", a0); 1329 fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", a0); 1330 fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", a0); 1331 fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", a0); 1332 return 0; 1333 } 1334 1335 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion 1336 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" 1337 // To expand "version" to its value before making the string, use STRINGIFY(version) instead 1338 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s 1339 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) 1340 1341 // NOT static -- otherwise the compiler may optimize it out 1342 // The "@(#) " pattern is a special prefix the "what" command looks for 1343 const char VersionString_SCCS[] = "@(#) dns-sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; 1344 1345 #if _BUILDING_XCODE_PROJECT_ 1346 // If the process crashes, then this string will be magically included in the automatically-generated crash log 1347 const char *__crashreporter_info__ = VersionString_SCCS + 5; 1348 asm(".desc ___crashreporter_info__, 0x10"); 1349 #endif 1350