1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
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.)
37 Change History (most recent first):
40 Revision 1.34 2004/12/16 20:17:11 cheshire
41 <rdar://problem/3324626> Cache memory management improvements
43 Revision 1.33 2004/11/30 22:37:00 cheshire
44 Update copyright dates and add "Mode: C; tab-width: 4" headers
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
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
54 Revision 1.30 2004/09/21 23:29:51 cheshire
55 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
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.
63 Revision 1.28 2004/09/17 00:31:52 cheshire
64 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
66 Revision 1.27 2004/09/16 01:58:22 cheshire
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
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
75 Revision 1.24 2004/06/15 02:39:47 cheshire
76 When displaying error message, only show command name, not entire path
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
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>
85 Revision 1.21 2004/01/28 21:38:57 cheshire
86 Also ask target host for _services._mdns._udp.local. list
88 Revision 1.20 2004/01/28 19:04:38 cheshire
89 Fix Ctrl-C handling when multiple targets are specified
91 Revision 1.19 2004/01/28 03:49:30 cheshire
92 Enhanced mDNSIdentify to make use of new targeted-query capability
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
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.
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
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
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
111 Revision 1.13 2003/12/13 03:05:28 ksekar
112 <rdar://problem/3192548>: DynDNS: Unicast query of service records
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.
118 Revision 1.11 2003/10/30 19:26:38 cheshire
119 Fix warnings on certain compilers
121 Revision 1.10 2003/09/02 20:38:57 cheshire
122 #include <signal.h> for Linux
124 Revision 1.9 2003/08/14 23:57:46 cheshire
125 Report if there is no answer at all from the target host
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
130 Revision 1.7 2003/08/12 19:56:26 cheshire
133 Revision 1.6 2003/08/06 01:46:18 cheshire
134 Distinguish no answer from partial answer
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)
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
143 Revision 1.3 2003/08/04 17:14:08 cheshire
144 Do both AAAA queries in parallel
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
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
154 //*************************************************************************************************************
155 // Incorporate mDNS.c functionality
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
161 #undef mDNSCoreReceive
163 //*************************************************************************************************************
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>
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"
181 //*************************************************************************************************************
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
];
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
;
195 //*************************************************************************************************************
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
, ...)
203 unsigned char buffer
[512];
205 va_start(ptr
,format
);
206 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
208 printf("%s", buffer
);
212 //*************************************************************************************************************
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
)
219 (void)dstaddr
; // Unused
220 // Snag copy of header ID, then call through
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
);
229 static void NameCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
232 (void)question
; // Unused
233 (void)AddRecord
;// Unused
234 if (!id
.NotAnInteger
) id
= lastid
;
235 if (answer
->rrtype
== kDNSType_PTR
|| answer
->rrtype
== kDNSType_CNAME
)
237 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, hostname
);
239 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
243 static void InfoCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
246 (void)question
; // Unused
247 (void)AddRecord
;// Unused
248 if (answer
->rrtype
== kDNSType_A
)
250 if (!id
.NotAnInteger
) id
= lastid
;
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
;
257 else if (answer
->rrtype
== kDNSType_AAAA
)
259 if (!id
.NotAnInteger
) id
= lastid
;
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
265 hostaddr
.type
= mDNSAddrType_IPv6
;
266 hostaddr
.ip
.v6
= answer
->rdata
->u
.ipv6
;
269 else if (answer
->rrtype
== kDNSType_HINFO
)
271 mDNSu8
*p
= answer
->rdata
->u
.data
;
272 strncpy(hardware
, (char*)(p
+1), p
[0]);
275 strncpy(software
, (char*)(p
+1), p
[0]);
281 // If we've got everything we're looking for, don't need to wait any more
282 if (NumHINFO
&& (NumAddr
|| NumAAAA
)) StopNow
= 1;
285 static void ServicesCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
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
))
297 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
302 mDNSexport
void WaitForAnswer(mDNS
*const m
, int seconds
)
305 gettimeofday(&end
, NULL
);
306 end
.tv_sec
+= seconds
;
313 struct timeval now
, remain
= end
;
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;
329 mDNSlocal mStatus
StartQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
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
;
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
;
344 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
345 return(mDNS_StartQuery(&mDNSStorage
, q
));
348 mDNSlocal
void DoOneQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
350 mStatus status
= StartQuery(q
, qname
, qtype
, target
, callback
);
351 if (status
!= mStatus_NoError
)
355 WaitForAnswer(&mDNSStorage
, 4);
356 mDNS_StopQuery(&mDNSStorage
, q
);
360 mDNSlocal
int DoQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
362 DoOneQuery(q
, qname
, qtype
, target
, callback
);
363 if (StopNow
== 0 && target
&& target
->type
)
365 mprintf("%##s %s Trying multicast\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
366 DoOneQuery(q
, qname
, qtype
, NULL
, callback
);
368 if (StopNow
== 0 && NumAnswers
== 0)
369 mprintf("%##s %s *** No Answer ***\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
373 mDNSlocal
void HandleSIG(int signal
)
375 (void)signal
; // Unused
381 mDNSexport
int main(int argc
, char **argv
)
383 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
393 if (argc
< 2) goto usage
;
395 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
396 mDNS_DebugMode
= mDNStrue
;
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
); }
405 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
406 signal(SIGTERM
, HandleSIG
);
408 while (this_arg
< argc
)
410 char *arg
= argv
[this_arg
++];
411 if (this_arg
> 2) printf("\n");
413 lastid
= id
= zeroID
;
414 hostaddr
= target
= zeroAddr
;
415 hostname
[0] = hardware
[0] = software
[0] = 0;
416 NumAddr
= NumAAAA
= NumHINFO
= 0;
418 if (inet_pton(AF_INET
, arg
, &s4
) == 1)
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;
429 else if (inet_pton(AF_INET6
, arg
, &s6
) == 1)
432 mDNSu8
*p
= (mDNSu8
*)&s6
;
433 for (i
= 0; i
< 16; i
++)
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] = '.';
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;
449 strcpy(hostname
, arg
);
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;
455 if (hardware
[0] || software
[0])
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;
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");
475 printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
478 mDNS_Close(&mDNSStorage
);
482 fprintf(stderr
, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname
);