]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/SamplemDNSClient.c
mDNSResponder-333.10.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / SamplemDNSClient.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 *
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.)
29 */
30
31 #include <libc.h>
32 #include <arpa/nameser.h>
33 #include <arpa/inet.h>
34 #include <net/if.h>
35 #include <CoreFoundation/CoreFoundation.h>
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
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
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;
54 static int num_printed;
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";
59 static char updatetest[3] = "\002AA";
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 {
79 (void)port; // Unused
80 (void)size; // Unused
81 (void)info; // Unused
82 DNSServiceDiscovery_handleReply(msg);
83 }
84
85 static int AddDNSServiceClientToRunLoop(dns_service_discovery_ref c)
86 {
87 mach_port_t port = DNSServiceDiscoveryMachPort(c);
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 }
101
102 //*************************************************************************************************************
103 // Sample callback functions for each of the operation types
104
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);
111 printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec/1000);
112 }
113
114 #define DomainMsg(X) ((X) == DNSServiceDomainEnumerationReplyAddDomain ? "Added" : \
115 (X) == DNSServiceDomainEnumerationReplyAddDomainDefault ? "(Default)" : \
116 (X) == DNSServiceDomainEnumerationReplyRemoveDomain ? "Removed" : "Unknown")
117
118 static void regdom_reply(DNSServiceDomainEnumerationReplyResultType resultType, const char *replyDomain,
119 DNSServiceDiscoveryReplyFlags flags, void *context)
120 {
121 (void)context; // Unused
122 printtimestamp();
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,
129 DNSServiceDiscoveryReplyFlags flags, void *context)
130 {
131 (void)context; // Unused
132 printtimestamp();
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,
139 const char *replyName, const char *replyType, const char *replyDomain, DNSServiceDiscoveryReplyFlags flags, void *context)
140 {
141 char *op = (resultType == DNSServiceBrowserReplyAddInstance) ? "Add" : "Rmv";
142 (void)context; // Unused
143 if (num_printed++ == 0) printf("Timestamp A/R Flags %-24s %-24s %s\n", "Domain", "Service Type", "Instance Name");
144 printtimestamp();
145 printf("%s%6X %-24s %-24s %s\n", op, flags, replyDomain, replyType, replyName);
146 }
147
148 static void resolve_reply(struct sockaddr *interface, struct sockaddr *address, const char *txtRecord, DNSServiceDiscoveryReplyFlags flags, void *context)
149 {
150 (void)interface; // Unused
151 (void)context; // Unused
152 if (address->sa_family != AF_INET && address->sa_family != AF_INET6)
153 printf("Unknown address family %d\n", address->sa_family);
154 else
155 {
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",
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]);
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 }
183 if (flags) printf(" Flags: %X", flags);
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 }
205 src++;
206 }
207 }
208 *dst++ = 0;
209 printf(" TXT %s", txtInfo);
210 }
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
219
220 switch (operation)
221 {
222 case 'A':
223 {
224 switch (addtest)
225 {
226 case 0: printf("Adding Test HINFO record\n");
227 record = DNSServiceRegistrationAddRecord(client, ns_t_hinfo, sizeof(myhinfo9), &myhinfo9[0], 120);
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");
256 DNSServiceRegistrationAddRecord(client, ns_t_null, sizeof(bigNULL), &bigNULL[0], 120);
257 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
258 }
259 break;
260 }
261 }
262
263 static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode, void *context)
264 {
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 }
282 }
283
284 //*************************************************************************************************************
285 // The main test function
286
287 int main(int argc, char **argv)
288 {
289 const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
290 char *d;
291 setlinebuf(stdout); // Want to see lines as they appear, not block buffered
292
293 if (argc < 2) goto Fail; // Minimum command line is the command name and one argument
294 operation = getopt(argc, (char * const *)argv, "EFBLRAUNTMI");
295 if (operation == -1) goto Fail;
296
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;
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);
312 break;
313
314 case 'L': if (argc < optind+2) goto Fail;
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);
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
382 record = DNSServiceRegistrationAddRecord(client, ns_t_txt, sizeof(TXT2)-1, TXT2, 120);
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));
402 CFRunLoopRun();
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
409 Exit:
410 return 0;
411
412 Fail:
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);
424 return 0;
425 }
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
437 #if _BUILDING_XCODE_PROJECT_
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");
441 #endif