]>
Commit | Line | Data |
---|---|---|
67c8f8a1 A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
c9b9ae52 | 3 | * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. |
6528fe3e | 4 | * |
67c8f8a1 A |
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 | |
c9b9ae52 | 8 | * |
67c8f8a1 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
c9b9ae52 | 10 | * |
67c8f8a1 A |
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 | |
c9b9ae52 | 15 | * limitations under the License. |
c9b9ae52 | 16 | * |
6528fe3e A |
17 | * Formatting notes: |
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.) | |
263eeeab | 29 | */ |
6528fe3e A |
30 | |
31 | #include <libc.h> | |
32 | #include <arpa/nameser.h> | |
c9b9ae52 A |
33 | #include <arpa/inet.h> |
34 | #include <net/if.h> | |
6528fe3e | 35 | #include <CoreFoundation/CoreFoundation.h> |
7f0064bd A |
36 | |
37 | // We already know this tool is using the old deprecated API (that's its purpose) | |
38 | // Since we compile with all warnings treated as errors, we have to turn off the warnings here or the project won't compile | |
39 | #include <AvailabilityMacros.h> | |
40 | #undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED | |
41 | #define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED | |
67c8f8a1 A |
42 | #undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3 |
43 | #define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3 | |
44 | ||
6528fe3e A |
45 | #include <DNSServiceDiscovery/DNSServiceDiscovery.h> |
46 | ||
47 | //************************************************************************************************************* | |
48 | // Globals | |
49 | ||
50 | typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; | |
51 | ||
52 | static char operation; | |
53 | static dns_service_discovery_ref client = NULL; | |
c9b9ae52 | 54 | static int num_printed; |
6528fe3e A |
55 | static char addtest = 0; |
56 | static DNSRecordReference record; | |
57 | static char myhinfo9[11] = "\003Mac\006OS 9.2"; | |
58 | static char myhinfoX[ 9] = "\003Mac\004OS X"; | |
c9b9ae52 | 59 | static char updatetest[3] = "\002AA"; |
6528fe3e A |
60 | static char bigNULL[4096]; |
61 | ||
62 | //************************************************************************************************************* | |
63 | // Supporting Utility Functions | |
64 | // | |
65 | // This code takes care of: | |
66 | // 1. Extracting the mach_port_t from the dns_service_discovery_ref | |
67 | // 2. Making a CFMachPortRef from it | |
68 | // 3. Making a CFRunLoopSourceRef from that | |
69 | // 4. Adding that source to the current RunLoop | |
70 | // 5. and passing the resulting messages back to DNSServiceDiscovery_handleReply() for processing | |
71 | // | |
72 | // Code that's not based around a CFRunLoop will need its own mechanism to receive Mach messages | |
73 | // from the mDNSResponder daemon and pass them to the DNSServiceDiscovery_handleReply() routine. | |
74 | // (There is no way to automate this, because it varies depending on the application's existing | |
75 | // event handling model.) | |
76 | ||
77 | static void MyHandleMachMessage(CFMachPortRef port, void *msg, CFIndex size, void *info) | |
78 | { | |
67c8f8a1 A |
79 | (void)port; // Unused |
80 | (void)size; // Unused | |
81 | (void)info; // Unused | |
6528fe3e A |
82 | DNSServiceDiscovery_handleReply(msg); |
83 | } | |
84 | ||
030b743d | 85 | static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref c) |
67c8f8a1 | 86 | { |
030b743d | 87 | mach_port_t port = DNSServiceDiscoveryMachPort(c); |
67c8f8a1 A |
88 | if (!port) |
89 | return(-1); | |
90 | else | |
91 | { | |
92 | CFMachPortContext context = { 0, 0, NULL, NULL, NULL }; | |
93 | Boolean shouldFreeInfo; | |
94 | CFMachPortRef cfMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault, port, MyHandleMachMessage, &context, &shouldFreeInfo); | |
95 | CFRunLoopSourceRef rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0); | |
96 | CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); | |
97 | CFRelease(rls); | |
98 | return(0); | |
99 | } | |
100 | } | |
6528fe3e A |
101 | |
102 | //************************************************************************************************************* | |
103 | // Sample callback functions for each of the operation types | |
104 | ||
c9b9ae52 A |
105 | static void printtimestamp(void) |
106 | { | |
107 | struct timeval tv; | |
108 | struct tm tm; | |
109 | gettimeofday(&tv, NULL); | |
110 | localtime_r((time_t*)&tv.tv_sec, &tm); | |
8e92c31c | 111 | printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000); |
c9b9ae52 A |
112 | } |
113 | ||
6528fe3e | 114 | #define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \ |
67c8f8a1 A |
115 | (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \ |
116 | (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown") | |
6528fe3e A |
117 | |
118 | static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain, | |
67c8f8a1 | 119 | DNSServiceDiscoveryReplyFlags flags, void *context) |
6528fe3e | 120 | { |
67c8f8a1 | 121 | (void)context; // Unused |
c9b9ae52 | 122 | printtimestamp(); |
6528fe3e A |
123 | printf("Recommended Registration Domain %s %s", replyDomain, DomainMsg(resultType)); |
124 | if (flags) printf(" Flags: %X", flags); | |
125 | printf("\n"); | |
126 | } | |
127 | ||
128 | static void browsedom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain, | |
67c8f8a1 | 129 | DNSServiceDiscoveryReplyFlags flags, void *context) |
6528fe3e | 130 | { |
67c8f8a1 | 131 | (void)context; // Unused |
c9b9ae52 | 132 | printtimestamp(); |
6528fe3e A |
133 | printf("Recommended Browsing Domain %s %s", replyDomain, DomainMsg(resultType)); |
134 | if (flags) printf(" Flags: %X", flags); | |
135 | printf("\n"); | |
136 | } | |
137 | ||
138 | static void browse_reply(DNSServiceBrowserReplyResultType resultType, | |
67c8f8a1 | 139 | const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context) |
6528fe3e | 140 | { |
c9b9ae52 | 141 | char *op = (resultType == DNSServiceBrowserReplyAddInstance) ? "Add" : "Rmv"; |
67c8f8a1 | 142 | (void)context; // Unused |
8e92c31c | 143 | if (num_printed++ == 0) printf("Timestamp A/R Flags %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name"); |
c9b9ae52 | 144 | printtimestamp(); |
8e92c31c | 145 | printf("%s%6X %-24s %-24s %s\n", op, flags, replyDomain, replyType, replyName); |
6528fe3e A |
146 | } |
147 | ||
148 | static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context) | |
149 | { | |
67c8f8a1 A |
150 | (void)interface; // Unused |
151 | (void)context; // Unused | |
c9b9ae52 | 152 | if (address->sa_family != AF_INET && address->sa_family != AF_INET6) |
6528fe3e A |
153 | printf("Unknown address family %d\n", address->sa_family); |
154 | else | |
155 | { | |
67c8f8a1 A |
156 | const char *src = txtRecord; |
157 | printtimestamp(); | |
158 | ||
159 | if (address->sa_family == AF_INET) | |
160 | { | |
161 | struct sockaddr_in *ip = (struct sockaddr_in *)address; | |
162 | union { uint32_t l; u_char b[4]; } addr = { ip->sin_addr.s_addr }; | |
163 | union { uint16_t s; u_char b[2]; } port = { ip->sin_port }; | |
164 | uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; | |
165 | char ipstring[16]; | |
166 | sprintf(ipstring, "%d.%d.%d.%d", addr.b[0], addr.b[1], addr.b[2], addr.b[3]); | |
167 | printf("Service can be reached at %-15s:%u", ipstring, PortAsNumber); | |
168 | } | |
169 | else if (address->sa_family == AF_INET6) | |
170 | { | |
171 | struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)address; | |
172 | u_int8_t *b = ip6->sin6_addr.__u6_addr.__u6_addr8; | |
173 | union { uint16_t s; u_char b[2]; } port = { ip6->sin6_port }; | |
174 | uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1]; | |
175 | char ipstring[40]; | |
176 | char ifname[IF_NAMESIZE + 1] = ""; | |
177 | sprintf(ipstring, "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", | |
7df24c4d A |
178 | b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7], |
179 | b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF]); | |
67c8f8a1 A |
180 | if (ip6->sin6_scope_id) { ifname[0] = '%'; if_indextoname(ip6->sin6_scope_id, &ifname[1]); } |
181 | printf("%s%s:%u", ipstring, ifname, PortAsNumber); | |
182 | } | |
c9b9ae52 | 183 | if (flags) printf(" Flags: %X", flags); |
67c8f8a1 A |
184 | if (*src) |
185 | { | |
186 | char txtInfo[64]; // Display at most first 64 characters of TXT record | |
187 | char *dst = txtInfo; | |
188 | const char *const lim = &txtInfo[sizeof(txtInfo)]; | |
189 | while (*src && dst < lim-1) | |
190 | { | |
191 | if (*src == '\\') *dst++ = '\\'; // '\' displays as "\\" | |
192 | if (*src >= ' ') *dst++ = *src++; // Display normal characters as-is | |
193 | else | |
194 | { | |
195 | *dst++ = '\\'; // Display a backslash | |
196 | if (*src == 1) *dst++ = ' '; // String boundary displayed as "\ " | |
197 | else // Other chararacters displayed as "\0xHH" | |
198 | { | |
199 | static const char hexchars[16] = "0123456789ABCDEF"; | |
200 | *dst++ = '0'; | |
201 | *dst++ = 'x'; | |
202 | *dst++ = hexchars[*src >> 4]; | |
203 | *dst++ = hexchars[*src & 0xF]; | |
204 | } | |
c9b9ae52 | 205 | src++; |
67c8f8a1 A |
206 | } |
207 | } | |
208 | *dst++ = 0; | |
209 | printf(" TXT %s", txtInfo); | |
210 | } | |
6528fe3e A |
211 | printf("\n"); |
212 | } | |
213 | } | |
214 | ||
215 | static void myCFRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info) | |
216 | { | |
217 | (void)timer; // Parameter not used | |
218 | (void)info; // Parameter not used | |
67c8f8a1 A |
219 | |
220 | switch (operation) | |
221 | { | |
222 | case 'A': | |
223 | { | |
224 | switch (addtest) | |
225 | { | |
226 | case 0: printf("Adding Test HINFO record\n"); | |
32bb7e43 | 227 | record = DNSServiceRegistrationAddRecord(client, ns_t_hinfo, sizeof(myhinfo9), &myhinfo9[0], 120); |
67c8f8a1 A |
228 | addtest = 1; |
229 | break; | |
230 | case 1: printf("Updating Test HINFO record\n"); | |
231 | DNSServiceRegistrationUpdateRecord(client, record, sizeof(myhinfoX), &myhinfoX[0], 120); | |
232 | addtest = 2; | |
233 | break; | |
234 | case 2: printf("Removing Test HINFO record\n"); | |
235 | DNSServiceRegistrationRemoveRecord(client, record); | |
236 | addtest = 0; | |
237 | break; | |
238 | } | |
239 | } | |
240 | break; | |
241 | ||
242 | case 'U': | |
243 | { | |
244 | if (updatetest[1] != 'Z') updatetest[1]++; | |
245 | else updatetest[1] = 'A'; | |
246 | updatetest[0] = 3 - updatetest[0]; | |
247 | updatetest[2] = updatetest[1]; | |
248 | printf("Updating Test TXT record to %c\n", updatetest[1]); | |
249 | DNSServiceRegistrationUpdateRecord(client, 0, 1+updatetest[0], &updatetest[0], 120); | |
250 | } | |
251 | break; | |
252 | ||
253 | case 'N': | |
254 | { | |
255 | printf("Adding big NULL record\n"); | |
32bb7e43 | 256 | DNSServiceRegistrationAddRecord(client, ns_t_null, sizeof(bigNULL), &bigNULL[0], 120); |
67c8f8a1 A |
257 | CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); |
258 | } | |
259 | break; | |
260 | } | |
261 | } | |
6528fe3e A |
262 | |
263 | static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context) | |
264 | { | |
67c8f8a1 A |
265 | (void)context; // Unused |
266 | printf("Got a reply from the server: "); | |
267 | switch (errorCode) | |
268 | { | |
269 | case kDNSServiceDiscoveryNoError: printf("Name now registered and active\n"); break; | |
270 | case kDNSServiceDiscoveryNameConflict: printf("Name in use, please choose another\n"); exit(-1); | |
271 | default: printf("Error %d\n", errorCode); return; | |
272 | } | |
273 | ||
274 | if (operation == 'A' || operation == 'U' || operation == 'N') | |
275 | { | |
276 | CFRunLoopTimerContext myCFRunLoopTimerContext = { 0, 0, NULL, NULL, NULL }; | |
277 | CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, | |
278 | CFAbsoluteTimeGetCurrent() + 5.0, 5.0, 0, 1, // Next fire time, periodic interval, flags, and order | |
279 | myCFRunLoopTimerCallBack, &myCFRunLoopTimerContext); | |
280 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); | |
281 | } | |
6528fe3e A |
282 | } |
283 | ||
284 | //************************************************************************************************************* | |
285 | // The main test function | |
286 | ||
287 | int main(int argc, char **argv) | |
288 | { | |
7f0064bd | 289 | const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; |
030b743d | 290 | char *d; |
c9b9ae52 | 291 | setlinebuf(stdout); // Want to see lines as they appear, not block buffered |
6528fe3e A |
292 | |
293 | if (argc < 2) goto Fail; // Minimum command line is the command name and one argument | |
67c8f8a1 | 294 | operation = getopt(argc, (char * const *)argv, "EFBLRAUNTMI"); |
6528fe3e A |
295 | if (operation == -1) goto Fail; |
296 | ||
67c8f8a1 A |
297 | switch (operation) |
298 | { | |
299 | case 'E': printf("Looking for recommended registration domains:\n"); | |
300 | client = DNSServiceDomainEnumerationCreate(1, regdom_reply, nil); | |
301 | break; | |
302 | ||
303 | case 'F': printf("Looking for recommended browsing domains:\n"); | |
304 | client = DNSServiceDomainEnumerationCreate(0, browsedom_reply, nil); | |
305 | break; | |
306 | ||
307 | case 'B': if (argc < optind+1) goto Fail; | |
030b743d A |
308 | d = (argc < optind+2) ? "" : argv[optind+1]; // Missing domain argument is the same as empty string i.e. use system default(s) |
309 | if (d[0] == '.' && d[1] == 0) d[0] = 0; // We allow '.' on the command line as a synonym for empty string | |
310 | printf("Browsing for %s%s\n", argv[optind+0], d); | |
311 | client = DNSServiceBrowserCreate(argv[optind+0], d, browse_reply, nil); | |
67c8f8a1 A |
312 | break; |
313 | ||
314 | case 'L': if (argc < optind+2) goto Fail; | |
030b743d A |
315 | d = (argc < optind+3) ? "" : argv[optind+2]; |
316 | if (d[0] == '.' && d[1] == 0) d = "local"; // We allow '.' on the command line as a synonym for "local" | |
317 | printf("Lookup %s.%s%s\n", argv[optind+0], argv[optind+1], d); | |
318 | client = DNSServiceResolverResolve(argv[optind+0], argv[optind+1], d, resolve_reply, nil); | |
67c8f8a1 A |
319 | break; |
320 | ||
321 | case 'R': if (argc < optind+4) goto Fail; | |
322 | { | |
323 | char *nam = argv[optind+0]; | |
324 | char *typ = argv[optind+1]; | |
325 | char *dom = argv[optind+2]; | |
326 | uint16_t PortAsNumber = atoi(argv[optind+3]); | |
327 | Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } }; | |
328 | char txt[2048]; | |
329 | char *ptr = txt; | |
330 | int i; | |
331 | ||
332 | if (nam[0] == '.' && nam[1] == 0) nam[0] = 0; // We allow '.' on the command line as a synonym for empty string | |
333 | if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string | |
334 | ||
335 | // Copy all the TXT strings into one C string separated by ASCII-1 delimiters | |
336 | for (i = optind+4; i < argc; i++) | |
337 | { | |
338 | int len = strlen(argv[i]); | |
339 | if (len > 255 || ptr + len + 1 >= txt + sizeof(txt)) break; | |
340 | strcpy(ptr, argv[i]); | |
341 | ptr += len; | |
342 | *ptr++ = 1; | |
343 | } | |
344 | if (ptr > txt) ptr--; | |
345 | *ptr = 0; | |
346 | ||
347 | printf("Registering Service %s.%s%s port %s %s\n", nam, typ, dom, argv[optind+3], txt); | |
348 | client = DNSServiceRegistrationCreate(nam, typ, dom, registerPort.NotAnInteger, txt, reg_reply, nil); | |
349 | break; | |
350 | } | |
351 | ||
352 | case 'A': | |
353 | case 'U': | |
354 | case 'N': { | |
355 | Opaque16 registerPort = { { 0x12, 0x34 } }; | |
356 | static const char TXT[] = "First String\001Second String\001Third String"; | |
357 | printf("Registering Service Test._testupdate._tcp.local.\n"); | |
358 | client = DNSServiceRegistrationCreate("Test", "_testupdate._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil); | |
359 | break; | |
360 | } | |
361 | ||
362 | case 'T': { | |
363 | Opaque16 registerPort = { { 0x23, 0x45 } }; | |
364 | char TXT[1000]; | |
365 | unsigned int i; | |
366 | for (i=0; i<sizeof(TXT)-1; i++) | |
367 | if ((i & 0x1F) == 0x1F) TXT[i] = 1; else TXT[i] = 'A' + (i >> 5); | |
368 | TXT[i] = 0; | |
369 | printf("Registering Service Test._testlargetxt._tcp.local.\n"); | |
370 | client = DNSServiceRegistrationCreate("Test", "_testlargetxt._tcp.", "", registerPort.NotAnInteger, TXT, reg_reply, nil); | |
371 | break; | |
372 | } | |
373 | ||
374 | case 'M': { | |
375 | pid_t pid = getpid(); | |
376 | Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; | |
377 | static const char TXT1[] = "First String\001Second String\001Third String"; | |
378 | static const char TXT2[] = "\x0D" "Fourth String" "\x0C" "Fifth String" "\x0C" "Sixth String"; | |
379 | printf("Registering Service Test._testdualtxt._tcp.local.\n"); | |
380 | client = DNSServiceRegistrationCreate("", "_testdualtxt._tcp.", "", registerPort.NotAnInteger, TXT1, reg_reply, nil); | |
381 | // use "sizeof(TXT2)-1" because we don't wan't the C compiler's null byte on the end of the string | |
32bb7e43 | 382 | record = DNSServiceRegistrationAddRecord(client, ns_t_txt, sizeof(TXT2)-1, TXT2, 120); |
67c8f8a1 A |
383 | break; |
384 | } | |
385 | ||
386 | case 'I': { | |
387 | pid_t pid = getpid(); | |
388 | Opaque16 registerPort = { { pid >> 8, pid & 0xFF } }; | |
389 | static const char TXT[] = "\x09" "Test Data"; | |
390 | printf("Registering Service Test._testtxt._tcp.local.\n"); | |
391 | client = DNSServiceRegistrationCreate("", "_testtxt._tcp.", "", registerPort.NotAnInteger, "", reg_reply, nil); | |
392 | if (client) DNSServiceRegistrationUpdateRecord(client, 0, 1+TXT[0], &TXT[0], 120); | |
393 | break; | |
394 | } | |
395 | ||
396 | default: goto Exit; | |
397 | } | |
398 | ||
399 | if (!client) { fprintf(stderr, "DNSService call failed\n"); return (-1); } | |
400 | if (AddDNSServiceClientToRunLoop(client) != 0) { fprintf(stderr, "AddDNSServiceClientToRunLoop failed\n"); return (-1); } | |
401 | printf("Talking to DNS SD Daemon at Mach port %d\n", DNSServiceDiscoveryMachPort(client)); | |
6528fe3e | 402 | CFRunLoopRun(); |
67c8f8a1 A |
403 | |
404 | // Be sure to deallocate the dns_service_discovery_ref when you're finished | |
405 | // Note: What other cleanup has to be done here? | |
406 | // We should probably invalidate, remove and release our CFRunLoopSourceRef? | |
407 | DNSServiceDiscoveryDeallocate(client); | |
408 | ||
6528fe3e A |
409 | Exit: |
410 | return 0; | |
411 | ||
412 | Fail: | |
7f0064bd A |
413 | fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", progname); |
414 | fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", progname); | |
415 | fprintf(stderr, "%s -B <Type> <Domain> (Browse for services instances)\n", progname); | |
416 | fprintf(stderr, "%s -L <Name> <Type> <Domain> (Look up a service instance)\n", progname); | |
417 | fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", progname); | |
418 | fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", progname); | |
419 | fprintf(stderr, "%s -U (Test updating a TXT record)\n", progname); | |
420 | fprintf(stderr, "%s -N (Test adding a large NULL record)\n", progname); | |
421 | fprintf(stderr, "%s -T (Test creating a large TXT record)\n", progname); | |
422 | fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", progname); | |
423 | fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", progname); | |
6528fe3e A |
424 | return 0; |
425 | } | |
67c8f8a1 A |
426 | |
427 | // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion | |
428 | // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" | |
429 | // To expand "version" to its value before making the string, use STRINGIFY(version) instead | |
430 | #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s | |
431 | #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) | |
432 | ||
433 | // NOT static -- otherwise the compiler may optimize it out | |
434 | // The "@(#) " pattern is a special prefix the "what" command looks for | |
435 | const char VersionString_SCCS[] = "@(#) mDNS " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; | |
436 | ||
32bb7e43 | 437 | #if _BUILDING_XCODE_PROJECT_ |
67c8f8a1 A |
438 | // If the process crashes, then this string will be magically included in the automatically-generated crash log |
439 | const char *__crashreporter_info__ = VersionString_SCCS + 5; | |
440 | asm(".desc ___crashreporter_info__, 0x10"); | |
32bb7e43 | 441 | #endif |