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