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