]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/Identify.c
mDNSResponder-107.3.tar.gz
[apple/mdnsresponder.git] / mDNSPosix / Identify.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 *
24 * Formatting notes:
25 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
26 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
27 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
28 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
29 * therefore common sense dictates that if they are part of a compound statement then they
30 * should be indented to the same level as everything else in that compound statement.
31 * Indenting curly braces at the same level as the "if" implies that curly braces are
32 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
33 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
34 * understand why variable y is not of type "char*" just proves the point that poor code
35 * layout leads people to unfortunate misunderstandings about how the C language really works.)
36
37 Change History (most recent first):
38
39 $Log: Identify.c,v $
40 Revision 1.34 2004/12/16 20:17:11 cheshire
41 <rdar://problem/3324626> Cache memory management improvements
42
43 Revision 1.33 2004/11/30 22:37:00 cheshire
44 Update copyright dates and add "Mode: C; tab-width: 4" headers
45
46 Revision 1.32 2004/10/19 21:33:21 cheshire
47 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
48 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
49 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
50
51 Revision 1.31 2004/10/16 00:17:00 cheshire
52 <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
53
54 Revision 1.30 2004/09/21 23:29:51 cheshire
55 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
56
57 Revision 1.29 2004/09/17 01:08:53 cheshire
58 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
59 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
60 declared in that file are ONLY appropriate to single-address-space embedded applications.
61 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
62
63 Revision 1.28 2004/09/17 00:31:52 cheshire
64 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
65
66 Revision 1.27 2004/09/16 01:58:22 cheshire
67 Fix compiler warnings
68
69 Revision 1.26 2004/08/24 21:55:07 cheshire
70 Don't try to build IPv6 code on systems that don't have IPv6
71
72 Revision 1.25 2004/07/20 23:42:37 cheshire
73 Update to use only "_services._dns-sd._udp.local." meta-query for service enumeration
74
75 Revision 1.24 2004/06/15 02:39:47 cheshire
76 When displaying error message, only show command name, not entire path
77
78 Revision 1.23 2004/05/18 23:51:26 cheshire
79 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
80
81 Revision 1.22 2004/04/20 22:43:28 cheshire
82 Use _services._dns-sd._udp query, as documented in
83 <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd-02.txt>
84
85 Revision 1.21 2004/01/28 21:38:57 cheshire
86 Also ask target host for _services._mdns._udp.local. list
87
88 Revision 1.20 2004/01/28 19:04:38 cheshire
89 Fix Ctrl-C handling when multiple targets are specified
90
91 Revision 1.19 2004/01/28 03:49:30 cheshire
92 Enhanced mDNSIdentify to make use of new targeted-query capability
93
94 Revision 1.18 2004/01/27 19:06:51 cheshire
95 Remove workaround for WWDC 2003 bug; no one has run that buggy build for a long time
96
97 Revision 1.17 2004/01/22 03:57:00 cheshire
98 Use the new meta-interface mDNSInterface_ForceMCast. This restores mDNSIdentify's
99 ability to use multicast queries with non-link-local target addresses, like 17.x.x.x.
100
101 Revision 1.16 2004/01/22 00:03:32 cheshire
102 Add while() loop so that a list of targets may be specified on the command line
103
104 Revision 1.15 2004/01/21 21:55:06 cheshire
105 Don't need to wait for timeout once we've got the information we wanted
106
107 Revision 1.14 2003/12/17 00:51:22 cheshire
108 Changed mDNSNetMonitor and mDNSIdentify to link the object files
109 instead of #including the "DNSCommon.c" "uDNS.c" and source files
110
111 Revision 1.13 2003/12/13 03:05:28 ksekar
112 <rdar://problem/3192548>: DynDNS: Unicast query of service records
113
114 Revision 1.12 2003/11/14 21:27:09 cheshire
115 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
116 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
117
118 Revision 1.11 2003/10/30 19:26:38 cheshire
119 Fix warnings on certain compilers
120
121 Revision 1.10 2003/09/02 20:38:57 cheshire
122 #include <signal.h> for Linux
123
124 Revision 1.9 2003/08/14 23:57:46 cheshire
125 Report if there is no answer at all from the target host
126
127 Revision 1.8 2003/08/14 02:19:55 cheshire
128 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
129
130 Revision 1.7 2003/08/12 19:56:26 cheshire
131 Update to APSL 2.0
132
133 Revision 1.6 2003/08/06 01:46:18 cheshire
134 Distinguish no answer from partial answer
135
136 Revision 1.5 2003/08/05 23:56:26 cheshire
137 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
138 (Right now mDNSPosix.c just reports 255 -- we should fix this)
139
140 Revision 1.4 2003/08/04 17:24:48 cheshire
141 Combine the three separate A/AAAA/HINFO queries into a single qtype "ANY" query
142
143 Revision 1.3 2003/08/04 17:14:08 cheshire
144 Do both AAAA queries in parallel
145
146 Revision 1.2 2003/08/02 02:25:13 cheshire
147 Multiple improvements: Now displays host's name, and all v4 and v6 addresses, as well as HINFO record
148
149 Revision 1.1 2003/08/01 02:20:02 cheshire
150 Add mDNSIdentify tool, used to discover what version of mDNSResponder a particular host is running
151
152 */
153
154 //*************************************************************************************************************
155 // Incorporate mDNS.c functionality
156
157 // We want to use the functionality provided by "mDNS.c",
158 // except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
159 #define mDNSCoreReceive __MDNS__mDNSCoreReceive
160 #include "mDNS.c"
161 #undef mDNSCoreReceive
162
163 //*************************************************************************************************************
164 // Headers
165
166 #include <unistd.h>
167 #include <stdio.h>
168 #include <string.h>
169 #include <errno.h>
170 #include <sys/socket.h>
171 #include <netinet/in.h>
172 #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
173 #include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
174 #include <arpa/inet.h>
175 #include <signal.h>
176
177 #include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
178 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
179 #include "ExampleClientApp.h"
180
181 //*************************************************************************************************************
182 // Globals
183
184 static mDNS mDNSStorage; // mDNS core uses this to store its globals
185 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
186 #define RR_CACHE_SIZE 500
187 static CacheEntity gRRCache[RR_CACHE_SIZE];
188
189 static volatile int StopNow; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
190 static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO;
191 static char hostname[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256];
192 static mDNSAddr lastsrc, hostaddr, target;
193 static mDNSOpaque16 lastid, id;
194
195 //*************************************************************************************************************
196 // Utilities
197
198 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
199 mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
200 mDNSlocal mDNSu32 mprintf(const char *format, ...)
201 {
202 mDNSu32 length;
203 unsigned char buffer[512];
204 va_list ptr;
205 va_start(ptr,format);
206 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
207 va_end(ptr);
208 printf("%s", buffer);
209 return(length);
210 }
211
212 //*************************************************************************************************************
213 // Main code
214
215 mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
216 const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
217 const mDNSInterfaceID InterfaceID)
218 {
219 (void)dstaddr; // Unused
220 // Snag copy of header ID, then call through
221 lastid = msg->h.id;
222 lastsrc = *srcaddr;
223
224 // We *want* to allow off-net unicast responses here.
225 // For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
226 __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, &AllDNSLinkGroup_v4, dstport, InterfaceID);
227 }
228
229 static void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
230 {
231 (void)m; // Unused
232 (void)question; // Unused
233 (void)AddRecord;// Unused
234 if (!id.NotAnInteger) id = lastid;
235 if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME)
236 {
237 ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
238 StopNow = 1;
239 mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
240 }
241 }
242
243 static void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
244 {
245 (void)m; // Unused
246 (void)question; // Unused
247 (void)AddRecord;// Unused
248 if (answer->rrtype == kDNSType_A)
249 {
250 if (!id.NotAnInteger) id = lastid;
251 NumAnswers++;
252 NumAddr++;
253 mprintf("%##s %s %.4a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4);
254 hostaddr.type = mDNSAddrType_IPv4; // Prefer v4 target to v6 target, for now
255 hostaddr.ip.v4 = answer->rdata->u.ipv4;
256 }
257 else if (answer->rrtype == kDNSType_AAAA)
258 {
259 if (!id.NotAnInteger) id = lastid;
260 NumAnswers++;
261 NumAAAA++;
262 mprintf("%##s %s %.16a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6);
263 if (!hostaddr.type) // Prefer v4 target to v6 target, for now
264 {
265 hostaddr.type = mDNSAddrType_IPv6;
266 hostaddr.ip.v6 = answer->rdata->u.ipv6;
267 }
268 }
269 else if (answer->rrtype == kDNSType_HINFO)
270 {
271 mDNSu8 *p = answer->rdata->u.data;
272 strncpy(hardware, (char*)(p+1), p[0]);
273 hardware[p[0]] = 0;
274 p += 1 + p[0];
275 strncpy(software, (char*)(p+1), p[0]);
276 software[p[0]] = 0;
277 NumAnswers++;
278 NumHINFO++;
279 }
280
281 // If we've got everything we're looking for, don't need to wait any more
282 if (NumHINFO && (NumAddr || NumAAAA)) StopNow = 1;
283 }
284
285 static void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
286 {
287 (void)m; // Unused
288 (void)question; // Unused
289 (void)AddRecord;// Unused
290 // Right now the mDNSCore targeted-query code is incomplete --
291 // it issues targeted queries, but accepts answers from anywhere
292 // For now, we'll just filter responses here so we don't get confused by responses from someone else
293 if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target))
294 {
295 NumAnswers++;
296 NumAddr++;
297 mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
298 StopNow = 1;
299 }
300 }
301
302 mDNSexport void WaitForAnswer(mDNS *const m, int seconds)
303 {
304 struct timeval end;
305 gettimeofday(&end, NULL);
306 end.tv_sec += seconds;
307 StopNow = 0;
308 NumAnswers = 0;
309 while (!StopNow)
310 {
311 int nfds = 0;
312 fd_set readfds;
313 struct timeval now, remain = end;
314 int result;
315
316 FD_ZERO(&readfds);
317 gettimeofday(&now, NULL);
318 if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; }
319 if (remain.tv_sec < now.tv_sec) return;
320 remain.tv_usec -= now.tv_usec;
321 remain.tv_sec -= now.tv_sec;
322 mDNSPosixGetFDSet(m, &nfds, &readfds, &remain);
323 result = select(nfds, &readfds, NULL, NULL, &remain);
324 if (result >= 0) mDNSPosixProcessFDSet(m, &readfds);
325 else if (errno != EINTR) StopNow = 2;
326 }
327 }
328
329 mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
330 {
331 if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname);
332 q->Target = target ? *target : zeroAddr;
333 q->TargetPort = MulticastDNSPort;
334 q->TargetQID = zeroID;
335 q->InterfaceID = mDNSInterface_Any;
336 q->qtype = qtype;
337 q->qclass = kDNSClass_IN;
338 q->LongLived = mDNSfalse;
339 q->ExpectUnique = mDNStrue;
340 q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
341 q->QuestionCallback = callback;
342 q->QuestionContext = NULL;
343
344 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
345 return(mDNS_StartQuery(&mDNSStorage, q));
346 }
347
348 mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
349 {
350 mStatus status = StartQuery(q, qname, qtype, target, callback);
351 if (status != mStatus_NoError)
352 StopNow = 2;
353 else
354 {
355 WaitForAnswer(&mDNSStorage, 4);
356 mDNS_StopQuery(&mDNSStorage, q);
357 }
358 }
359
360 mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
361 {
362 DoOneQuery(q, qname, qtype, target, callback);
363 if (StopNow == 0 && target && target->type)
364 {
365 mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype));
366 DoOneQuery(q, qname, qtype, NULL, callback);
367 }
368 if (StopNow == 0 && NumAnswers == 0)
369 mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype));
370 return(StopNow);
371 }
372
373 mDNSlocal void HandleSIG(int signal)
374 {
375 (void)signal; // Unused
376 debugf("%s","");
377 debugf("HandleSIG");
378 StopNow = 2;
379 }
380
381 mDNSexport int main(int argc, char **argv)
382 {
383 const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
384 int this_arg = 1;
385 mStatus status;
386 struct in_addr s4;
387 #if HAVE_IPV6
388 struct in6_addr s6;
389 #endif
390 char buffer[256];
391 DNSQuestion q;
392
393 if (argc < 2) goto usage;
394
395 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
396 mDNS_DebugMode = mDNStrue;
397
398 // Initialise the mDNS core.
399 status = mDNS_Init(&mDNSStorage, &PlatformStorage,
400 gRRCache, RR_CACHE_SIZE,
401 mDNS_Init_DontAdvertiseLocalAddresses,
402 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
403 if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %ld\n", status); return(status); }
404
405 signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
406 signal(SIGTERM, HandleSIG);
407
408 while (this_arg < argc)
409 {
410 char *arg = argv[this_arg++];
411 if (this_arg > 2) printf("\n");
412
413 lastid = id = zeroID;
414 hostaddr = target = zeroAddr;
415 hostname[0] = hardware[0] = software[0] = 0;
416 NumAddr = NumAAAA = NumHINFO = 0;
417
418 if (inet_pton(AF_INET, arg, &s4) == 1)
419 {
420 mDNSu8 *p = (mDNSu8 *)&s4;
421 mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]);
422 printf("%s\n", buffer);
423 target.type = mDNSAddrType_IPv4;
424 target.ip.v4.NotAnInteger = s4.s_addr;
425 DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
426 if (StopNow == 2) break;
427 }
428 #if HAVE_IPV6
429 else if (inet_pton(AF_INET6, arg, &s6) == 1)
430 {
431 int i;
432 mDNSu8 *p = (mDNSu8 *)&s6;
433 for (i = 0; i < 16; i++)
434 {
435 static const char hexValues[] = "0123456789ABCDEF";
436 buffer[i * 4 ] = hexValues[p[15-i] & 0x0F];
437 buffer[i * 4 + 1] = '.';
438 buffer[i * 4 + 2] = hexValues[p[15-i] >> 4];
439 buffer[i * 4 + 3] = '.';
440 }
441 mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
442 target.type = mDNSAddrType_IPv6;
443 bcopy(&s6, &target.ip.v6, sizeof(target.ip.v6));
444 DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
445 if (StopNow == 2) break;
446 }
447 #endif
448 else
449 strcpy(hostname, arg);
450
451 // Now we have the host name; get its A, AAAA, and HINFO
452 if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback);
453 if (StopNow == 2) break;
454
455 if (hardware[0] || software[0])
456 {
457 DNSQuestion q1;
458 printf("HINFO Hardware: %s\n", hardware);
459 printf("HINFO Software: %s\n", software);
460 // We need to make sure the services query is targeted
461 if (target.type == 0) target = hostaddr;
462 StartQuery(&q1, "_services._dns-sd._udp.local.", kDNSQType_ANY, &target, ServicesCallback);
463 WaitForAnswer(&mDNSStorage, 4);
464 mDNS_StopQuery(&mDNSStorage, &q1);
465 if (StopNow == 2) break;
466 }
467 else if (NumAnswers)
468 {
469 printf("Host has no HINFO record; Best guess is ");
470 if (id.b[1]) printf("mDNSResponder-%d\n", id.b[1]);
471 else if (NumAAAA) printf("very early Panther build (mDNSResponder-33 or earlier)\n");
472 else printf("Jaguar version of mDNSResponder with no IPv6 support\n");
473 }
474 else
475 printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
476 }
477
478 mDNS_Close(&mDNSStorage);
479 return(0);
480
481 usage:
482 fprintf(stderr, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname);
483 return(-1);
484 }