1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
19 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
20 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
21 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
22 * therefore common sense dictates that if they are part of a compound statement then they
23 * should be indented to the same level as everything else in that compound statement.
24 * Indenting curly braces at the same level as the "if" implies that curly braces are
25 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
26 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
27 * understand why variable y is not of type "char*" just proves the point that poor code
28 * layout leads people to unfortunate misunderstandings about how the C language really works.)
30 Change History (most recent first):
32 $Log: SamplemDNSClient.c,v $
33 Revision 1.56 2008/10/22 02:59:58 mkrochma
34 <rdar://problem/6309616> Fix errors compiling mDNS tool caused by BIND8 removal
36 Revision 1.55 2008/09/15 23:52:30 cheshire
37 <rdar://problem/6218902> mDNSResponder-177 fails to compile on Linux with .desc pseudo-op
38 Made __crashreporter_info__ symbol conditional, so we only use it for OS X build
40 Revision 1.54 2007/11/30 23:39:55 cheshire
41 Fixed compile warning: declaration of 'client' shadows a global declaration
43 Revision 1.53 2007/09/18 19:09:02 cheshire
44 <rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
46 Revision 1.52 2007/03/06 22:45:52 cheshire
48 <rdar://problem/4138615> argv buffer overflow issues
50 Revision 1.51 2007/02/13 18:56:45 cheshire
51 <rdar://problem/4993485> Mach mDNS tool inconsistent with UDS-based dns-sd tool
52 (missing domain should mean "system default(s)", not "local")
54 Revision 1.50 2007/01/05 08:30:47 cheshire
55 Trim excessive "$Log" checkin history from before 2006
56 (checkin history still available via "cvs log ..." of course)
58 Revision 1.49 2007/01/04 21:54:49 cheshire
59 Fix compile warnings related to the deprecated Mach-based API
61 Revision 1.48 2006/08/14 23:24:39 cheshire
62 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
64 Revision 1.47 2006/01/10 02:29:22 cheshire
65 <rdar://problem/4403861> Cosmetic IPv6 address display problem in mDNS test tool
70 #include <arpa/nameser.h>
71 #include <arpa/inet.h>
73 #include <CoreFoundation/CoreFoundation.h>
75 // We already know this tool is using the old deprecated API (that's its purpose)
76 // Since we compile with all warnings treated as errors, we have to turn off the warnings here or the project won't compile
77 #include <AvailabilityMacros.h>
78 #undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED
79 #define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED
80 #undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
81 #define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
83 #include <DNSServiceDiscovery/DNSServiceDiscovery.h>
85 //*************************************************************************************************************
88 typedef union { unsigned char b
[2]; unsigned short NotAnInteger
; } Opaque16
;
90 static char operation
;
91 static dns_service_discovery_ref client
= NULL
;
92 static int num_printed
;
93 static char addtest
= 0;
94 static DNSRecordReference record
;
95 static char myhinfo9
[11] = "\003Mac\006OS 9.2";
96 static char myhinfoX
[ 9] = "\003Mac\004OS X";
97 static char updatetest
[3] = "\002AA";
98 static char bigNULL
[4096];
100 //*************************************************************************************************************
101 // Supporting Utility Functions
103 // This code takes care of:
104 // 1. Extracting the mach_port_t from the dns_service_discovery_ref
105 // 2. Making a CFMachPortRef from it
106 // 3. Making a CFRunLoopSourceRef from that
107 // 4. Adding that source to the current RunLoop
108 // 5. and passing the resulting messages back to DNSServiceDiscovery_handleReply() for processing
110 // Code that's not based around a CFRunLoop will need its own mechanism to receive Mach messages
111 // from the mDNSResponder daemon and pass them to the DNSServiceDiscovery_handleReply() routine.
112 // (There is no way to automate this, because it varies depending on the application's existing
113 // event handling model.)
115 static void MyHandleMachMessage(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
117 (void)port
; // Unused
118 (void)size
; // Unused
119 (void)info
; // Unused
120 DNSServiceDiscovery_handleReply(msg
);
123 static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref c
)
125 mach_port_t port
= DNSServiceDiscoveryMachPort(c
);
130 CFMachPortContext context
= { 0, 0, NULL
, NULL
, NULL
};
131 Boolean shouldFreeInfo
;
132 CFMachPortRef cfMachPort
= CFMachPortCreateWithPort(kCFAllocatorDefault
, port
, MyHandleMachMessage
, &context
, &shouldFreeInfo
);
133 CFRunLoopSourceRef rls
= CFMachPortCreateRunLoopSource(NULL
, cfMachPort
, 0);
134 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls
, kCFRunLoopDefaultMode
);
140 //*************************************************************************************************************
141 // Sample callback functions for each of the operation types
143 static void printtimestamp(void)
147 gettimeofday(&tv
, NULL
);
148 localtime_r((time_t*)&tv
.tv_sec
, &tm
);
149 printf("%2d:%02d:%02d.%03d ", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv
.tv_usec
/1000);
152 #define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \
153 (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \
154 (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown")
156 static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType
, const char *replyDomain
,
157 DNSServiceDiscoveryReplyFlags flags
, void *context
)
159 (void)context
; // Unused
161 printf("Recommended Registration Domain %s %s", replyDomain
, DomainMsg(resultType
));
162 if (flags
) printf(" Flags: %X", flags
);
166 static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType
, const char *replyDomain
,
167 DNSServiceDiscoveryReplyFlags flags
, void *context
)
169 (void)context
; // Unused
171 printf("Recommended Browsing Domain %s %s", replyDomain
, DomainMsg(resultType
));
172 if (flags
) printf(" Flags: %X", flags
);
176 static void browse_reply(DNSServiceBrowserReplyResultType resultType
,
177 const char *replyName
, const char *replyType
, const char *replyDomain
, DNSServiceDiscoveryReplyFlags flags
, void *context
)
179 char *op
= (resultType
== DNSServiceBrowserReplyAddInstance
) ? "Add" : "Rmv";
180 (void)context
; // Unused
181 if (num_printed
++ == 0) printf("Timestamp A/R Flags %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name");
183 printf("%s%6X %-24s %-24s %s\n", op
, flags
, replyDomain
, replyType
, replyName
);
186 static void resolve_reply(struct sockaddr
*interface
, struct sockaddr
*address
, const char *txtRecord
, DNSServiceDiscoveryReplyFlags flags
, void *context
)
188 (void)interface
; // Unused
189 (void)context
; // Unused
190 if (address
->sa_family
!= AF_INET
&& address
->sa_family
!= AF_INET6
)
191 printf("Unknown address family %d\n", address
->sa_family
);
194 const char *src
= txtRecord
;
197 if (address
->sa_family
== AF_INET
)
199 struct sockaddr_in
*ip
= (struct sockaddr_in
*)address
;
200 union { uint32_t l
; u_char b
[4]; } addr
= { ip
->sin_addr
.s_addr
};
201 union { uint16_t s
; u_char b
[2]; } port
= { ip
->sin_port
};
202 uint16_t PortAsNumber
= ((uint16_t)port
.b
[0]) << 8 | port
.b
[1];
204 sprintf(ipstring
, "%d.%d.%d.%d", addr
.b
[0], addr
.b
[1], addr
.b
[2], addr
.b
[3]);
205 printf("Service can be reached at %-15s:%u", ipstring
, PortAsNumber
);
207 else if (address
->sa_family
== AF_INET6
)
209 struct sockaddr_in6
*ip6
= (struct sockaddr_in6
*)address
;
210 u_int8_t
*b
= ip6
->sin6_addr
.__u6_addr
.__u6_addr8
;
211 union { uint16_t s
; u_char b
[2]; } port
= { ip6
->sin6_port
};
212 uint16_t PortAsNumber
= ((uint16_t)port
.b
[0]) << 8 | port
.b
[1];
214 char ifname
[IF_NAMESIZE
+ 1] = "";
215 sprintf(ipstring
, "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
216 b
[0x0], b
[0x1], b
[0x2], b
[0x3], b
[0x4], b
[0x5], b
[0x6], b
[0x7],
217 b
[0x8], b
[0x9], b
[0xA], b
[0xB], b
[0xC], b
[0xD], b
[0xE], b
[0xF]);
218 if (ip6
->sin6_scope_id
) { ifname
[0] = '%'; if_indextoname(ip6
->sin6_scope_id
, &ifname
[1]); }
219 printf("%s%s:%u", ipstring
, ifname
, PortAsNumber
);
221 if (flags
) printf(" Flags: %X", flags
);
224 char txtInfo
[64]; // Display at most first 64 characters of TXT record
226 const char *const lim
= &txtInfo
[sizeof(txtInfo
)];
227 while (*src
&& dst
< lim
-1)
229 if (*src
== '\\') *dst
++ = '\\'; // '\' displays as "\\"
230 if (*src
>= ' ') *dst
++ = *src
++; // Display normal characters as-is
233 *dst
++ = '\\'; // Display a backslash
234 if (*src
== 1) *dst
++ = ' '; // String boundary displayed as "\ "
235 else // Other chararacters displayed as "\0xHH"
237 static const char hexchars
[16] = "0123456789ABCDEF";
240 *dst
++ = hexchars
[*src
>> 4];
241 *dst
++ = hexchars
[*src
& 0xF];
247 printf(" TXT %s", txtInfo
);
253 static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer
, void *info
)
255 (void)timer
; // Parameter not used
256 (void)info
; // Parameter not used
264 case 0: printf("Adding Test HINFO record\n");
265 record
= DNSServiceRegistrationAddRecord(client
, ns_t_hinfo
, sizeof(myhinfo9
), &myhinfo9
[0], 120);
268 case 1: printf("Updating Test HINFO record\n");
269 DNSServiceRegistrationUpdateRecord(client
, record
, sizeof(myhinfoX
), &myhinfoX
[0], 120);
272 case 2: printf("Removing Test HINFO record\n");
273 DNSServiceRegistrationRemoveRecord(client
, record
);
282 if (updatetest
[1] != 'Z') updatetest
[1]++;
283 else updatetest
[1] = 'A';
284 updatetest
[0] = 3 - updatetest
[0];
285 updatetest
[2] = updatetest
[1];
286 printf("Updating Test TXT record to %c\n", updatetest
[1]);
287 DNSServiceRegistrationUpdateRecord(client
, 0, 1+updatetest
[0], &updatetest
[0], 120);
293 printf("Adding big NULL record\n");
294 DNSServiceRegistrationAddRecord(client
, ns_t_null
, sizeof(bigNULL
), &bigNULL
[0], 120);
295 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
301 static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode
, void *context
)
303 (void)context
; // Unused
304 printf("Got a reply from the server: ");
307 case kDNSServiceDiscoveryNoError
: printf("Name now registered and active\n"); break;
308 case kDNSServiceDiscoveryNameConflict
: printf("Name in use, please choose another\n"); exit(-1);
309 default: printf("Error %d\n", errorCode
); return;
312 if (operation
== 'A' || operation
== 'U' || operation
== 'N')
314 CFRunLoopTimerContext myCFRunLoopTimerContext
= { 0, 0, NULL
, NULL
, NULL
};
315 CFRunLoopTimerRef timer
= CFRunLoopTimerCreate(kCFAllocatorDefault
,
316 CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1, // Next fire time, periodic interval, flags, and order
317 myCFRunLoopTimerCallBack
, &myCFRunLoopTimerContext
);
318 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
322 //*************************************************************************************************************
323 // The main test function
325 int main(int argc
, char **argv
)
327 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
329 setlinebuf(stdout
); // Want to see lines as they appear, not block buffered
331 if (argc
< 2) goto Fail
; // Minimum command line is the command name and one argument
332 operation
= getopt(argc
, (char * const *)argv
, "EFBLRAUNTMI");
333 if (operation
== -1) goto Fail
;
337 case 'E': printf("Looking for recommended registration domains:\n");
338 client
= DNSServiceDomainEnumerationCreate(1, regdom_reply
, nil
);
341 case 'F': printf("Looking for recommended browsing domains:\n");
342 client
= DNSServiceDomainEnumerationCreate(0, browsedom_reply
, nil
);
345 case 'B': if (argc
< optind
+1) goto Fail
;
346 d
= (argc
< optind
+2) ? "" : argv
[optind
+1]; // Missing domain argument is the same as empty string i.e. use system default(s)
347 if (d
[0] == '.' && d
[1] == 0) d
[0] = 0; // We allow '.' on the command line as a synonym for empty string
348 printf("Browsing for %s%s\n", argv
[optind
+0], d
);
349 client
= DNSServiceBrowserCreate(argv
[optind
+0], d
, browse_reply
, nil
);
352 case 'L': if (argc
< optind
+2) goto Fail
;
353 d
= (argc
< optind
+3) ? "" : argv
[optind
+2];
354 if (d
[0] == '.' && d
[1] == 0) d
= "local"; // We allow '.' on the command line as a synonym for "local"
355 printf("Lookup %s.%s%s\n", argv
[optind
+0], argv
[optind
+1], d
);
356 client
= DNSServiceResolverResolve(argv
[optind
+0], argv
[optind
+1], d
, resolve_reply
, nil
);
359 case 'R': if (argc
< optind
+4) goto Fail
;
361 char *nam
= argv
[optind
+0];
362 char *typ
= argv
[optind
+1];
363 char *dom
= argv
[optind
+2];
364 uint16_t PortAsNumber
= atoi(argv
[optind
+3]);
365 Opaque16 registerPort
= { { PortAsNumber
>> 8, PortAsNumber
& 0xFF } };
370 if (nam
[0] == '.' && nam
[1] == 0) nam
[0] = 0; // We allow '.' on the command line as a synonym for empty string
371 if (dom
[0] == '.' && dom
[1] == 0) dom
[0] = 0; // We allow '.' on the command line as a synonym for empty string
373 // Copy all the TXT strings into one C string separated by ASCII-1 delimiters
374 for (i
= optind
+4; i
< argc
; i
++)
376 int len
= strlen(argv
[i
]);
377 if (len
> 255 || ptr
+ len
+ 1 >= txt
+ sizeof(txt
)) break;
378 strcpy(ptr
, argv
[i
]);
382 if (ptr
> txt
) ptr
--;
385 printf("Registering Service %s.%s%s port %s %s\n", nam
, typ
, dom
, argv
[optind
+3], txt
);
386 client
= DNSServiceRegistrationCreate(nam
, typ
, dom
, registerPort
.NotAnInteger
, txt
, reg_reply
, nil
);
393 Opaque16 registerPort
= { { 0x12, 0x34 } };
394 static const char TXT
[] = "First String\001Second String\001Third String";
395 printf("Registering Service Test._testupdate._tcp.local.\n");
396 client
= DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort
.NotAnInteger
, TXT
, reg_reply
, nil
);
401 Opaque16 registerPort
= { { 0x23, 0x45 } };
404 for (i
=0; i
<sizeof(TXT
)-1; i
++)
405 if ((i
& 0x1F) == 0x1F) TXT
[i
] = 1; else TXT
[i
] = 'A' + (i
>> 5);
407 printf("Registering Service Test._testlargetxt._tcp.local.\n");
408 client
= DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort
.NotAnInteger
, TXT
, reg_reply
, nil
);
413 pid_t pid
= getpid();
414 Opaque16 registerPort
= { { pid
>> 8, pid
& 0xFF } };
415 static const char TXT1
[] = "First String\001Second String\001Third String";
416 static const char TXT2
[] = "\x0D" "Fourth String" "\x0C" "Fifth String" "\x0C" "Sixth String";
417 printf("Registering Service Test._testdualtxt._tcp.local.\n");
418 client
= DNSServiceRegistrationCreate("", "_testdualtxt._tcp.", "", registerPort
.NotAnInteger
, TXT1
, reg_reply
, nil
);
419 // use "sizeof(TXT2)-1" because we don't wan't the C compiler's null byte on the end of the string
420 record
= DNSServiceRegistrationAddRecord(client
, ns_t_txt
, sizeof(TXT2
)-1, TXT2
, 120);
425 pid_t pid
= getpid();
426 Opaque16 registerPort
= { { pid
>> 8, pid
& 0xFF } };
427 static const char TXT
[] = "\x09" "Test Data";
428 printf("Registering Service Test._testtxt._tcp.local.\n");
429 client
= DNSServiceRegistrationCreate("", "_testtxt._tcp.", "", registerPort
.NotAnInteger
, "", reg_reply
, nil
);
430 if (client
) DNSServiceRegistrationUpdateRecord(client
, 0, 1+TXT
[0], &TXT
[0], 120);
437 if (!client
) { fprintf(stderr
, "DNSService call failed\n"); return (-1); }
438 if (AddDNSServiceClientToRunLoop(client
) != 0) { fprintf(stderr
, "AddDNSServiceClientToRunLoop failed\n"); return (-1); }
439 printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client
));
442 // Be sure to deallocate the dns_service_discovery_ref when you're finished
443 // Note: What other cleanup has to be done here?
444 // We should probably invalidate, remove and release our CFRunLoopSourceRef?
445 DNSServiceDiscoveryDeallocate(client
);
451 fprintf(stderr
, "%s -E (Enumerate recommended registration domains)\n", progname
);
452 fprintf(stderr
, "%s -F (Enumerate recommended browsing domains)\n", progname
);
453 fprintf(stderr
, "%s -B <Type> <Domain> (Browse for services instances)\n", progname
);
454 fprintf(stderr
, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", progname
);
455 fprintf(stderr
, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", progname
);
456 fprintf(stderr
, "%s -A (Test Adding/Updating/Deleting a record)\n", progname
);
457 fprintf(stderr
, "%s -U (Test updating a TXT record)\n", progname
);
458 fprintf(stderr
, "%s -N (Test adding a large NULL record)\n", progname
);
459 fprintf(stderr
, "%s -T (Test creating a large TXT record)\n", progname
);
460 fprintf(stderr
, "%s -M (Test creating a registration with multiple TXT records)\n", progname
);
461 fprintf(stderr
, "%s -I (Test registering and then immediately updating TXT record)\n", progname
);
465 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
466 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
467 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
468 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
469 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
471 // NOT static -- otherwise the compiler may optimize it out
472 // The "@(#) " pattern is a special prefix the "what" command looks for
473 const char VersionString_SCCS
[] = "@(#) mDNS " STRINGIFY(mDNSResponderVersion
) " (" __DATE__
" " __TIME__
")";
475 #if _BUILDING_XCODE_PROJECT_
476 // If the process crashes, then this string will be magically included in the automatically-generated crash log
477 const char *__crashreporter_info__
= VersionString_SCCS
+ 5;
478 asm(".desc ___crashreporter_info__, 0x10");