Robert Greenwalt | dd52342 | 2012-03-29 14:43:07 -0700 | [diff] [blame^] | 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 |