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