2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
27 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
28 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
29 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
30 * therefore common sense dictates that if they are part of a compound statement then they
31 * should be indented to the same level as everything else in that compound statement.
32 * Indenting curly braces at the same level as the "if" implies that curly braces are
33 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
34 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
35 * understand why variable y is not of type "char*" just proves the point that poor code
36 * layout leads people to unfortunate misunderstandings about how the C language really works.)
38 Change History (most recent first):
41 Revision 1.23 2004/05/18 23:51:26 cheshire
42 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
44 Revision 1.22 2004/04/20 22:43:28 cheshire
45 Use _services._dns-sd._udp query, as documented in
46 <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd-02.txt>
48 Revision 1.21 2004/01/28 21:38:57 cheshire
49 Also ask target host for _services._mdns._udp.local. list
51 Revision 1.20 2004/01/28 19:04:38 cheshire
52 Fix Ctrl-C handling when multiple targets are specified
54 Revision 1.19 2004/01/28 03:49:30 cheshire
55 Enhanced mDNSIdentify to make use of new targeted-query capability
57 Revision 1.18 2004/01/27 19:06:51 cheshire
58 Remove workaround for WWDC 2003 bug; no one has run that buggy build for a long time
60 Revision 1.17 2004/01/22 03:57:00 cheshire
61 Use the new meta-interface mDNSInterface_ForceMCast. This restores mDNSIdentify's
62 ability to use multicast queries with non-link-local target addresses, like 17.x.x.x.
64 Revision 1.16 2004/01/22 00:03:32 cheshire
65 Add while() loop so that a list of targets may be specified on the command line
67 Revision 1.15 2004/01/21 21:55:06 cheshire
68 Don't need to wait for timeout once we've got the information we wanted
70 Revision 1.14 2003/12/17 00:51:22 cheshire
71 Changed mDNSNetMonitor and mDNSIdentify to link the object files
72 instead of #including the "DNSCommon.c" "uDNS.c" and source files
74 Revision 1.13 2003/12/13 03:05:28 ksekar
75 <rdar://problem/3192548>: DynDNS: Unicast query of service records
77 Revision 1.12 2003/11/14 21:27:09 cheshire
78 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
79 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
81 Revision 1.11 2003/10/30 19:26:38 cheshire
82 Fix warnings on certain compilers
84 Revision 1.10 2003/09/02 20:38:57 cheshire
85 #include <signal.h> for Linux
87 Revision 1.9 2003/08/14 23:57:46 cheshire
88 Report if there is no answer at all from the target host
90 Revision 1.8 2003/08/14 02:19:55 cheshire
91 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
93 Revision 1.7 2003/08/12 19:56:26 cheshire
96 Revision 1.6 2003/08/06 01:46:18 cheshire
97 Distinguish no answer from partial answer
99 Revision 1.5 2003/08/05 23:56:26 cheshire
100 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
101 (Right now mDNSPosix.c just reports 255 -- we should fix this)
103 Revision 1.4 2003/08/04 17:24:48 cheshire
104 Combine the three separate A/AAAA/HINFO queries into a single qtype "ANY" query
106 Revision 1.3 2003/08/04 17:14:08 cheshire
107 Do both AAAA queries in parallel
109 Revision 1.2 2003/08/02 02:25:13 cheshire
110 Multiple improvements: Now displays host's name, and all v4 and v6 addresses, as well as HINFO record
112 Revision 1.1 2003/08/01 02:20:02 cheshire
113 Add mDNSIdentify tool, used to discover what version of mDNSResponder a particular host is running
117 //*************************************************************************************************************
118 // Incorporate mDNS.c functionality
120 // We want to use the functionality provided by "mDNS.c",
121 // except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
122 #define mDNSCoreReceive __MDNS__mDNSCoreReceive
124 #undef mDNSCoreReceive
126 //*************************************************************************************************************
133 #include <sys/socket.h>
134 #include <netinet/in.h>
135 #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
136 #include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
137 #include <arpa/inet.h>
140 #include "mDNSClientAPI.h"// Defines the interface to the mDNS core code
141 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
142 #include "ExampleClientApp.h"
144 //*************************************************************************************************************
147 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
148 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
149 #define RR_CACHE_SIZE 500
150 static CacheRecord gRRCache
[RR_CACHE_SIZE
];
152 static volatile int StopNow
; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
153 static volatile int NumAnswers
, NumAddr
, NumAAAA
, NumHINFO
;
154 static char hostname
[MAX_ESCAPED_DOMAIN_NAME
], hardware
[256], software
[256];
155 static mDNSAddr lastsrc
, hostaddr
, target
;
156 static mDNSOpaque16 lastid
, id
;
158 //*************************************************************************************************************
161 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
162 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
163 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
166 unsigned char buffer
[512];
168 va_start(ptr
,format
);
169 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
171 printf("%s", buffer
);
175 //*************************************************************************************************************
178 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
179 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*const dstaddr
, const mDNSIPPort dstport
,
180 const mDNSInterfaceID InterfaceID
, mDNSu8 ttl
)
182 // Snag copy of header ID, then call through
186 // We *want* to allow off-net unicast responses here.
187 // For now, the simplest way to allow that is to smash the TTL to 255 so that mDNSCore doesn't reject the packet
189 __MDNS__mDNSCoreReceive(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
, ttl
);
192 static void NameCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
195 (void)question
; // Unused
196 (void)AddRecord
;// Unused
197 if (!id
.NotAnInteger
) id
= lastid
;
198 if (answer
->rrtype
== kDNSType_PTR
|| answer
->rrtype
== kDNSType_CNAME
)
200 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, hostname
);
202 mprintf("%##s %s %##s\n", answer
->name
.c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.name
.c
);
206 static void InfoCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
209 (void)question
; // Unused
210 (void)AddRecord
;// Unused
211 if (answer
->rrtype
== kDNSType_A
)
213 if (!id
.NotAnInteger
) id
= lastid
;
216 mprintf("%##s %s %.4a\n", answer
->name
.c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ip
);
217 hostaddr
.type
= mDNSAddrType_IPv4
; // Prefer v4 target to v6 target, for now
218 hostaddr
.ip
.v4
= answer
->rdata
->u
.ip
;
220 else if (answer
->rrtype
== kDNSType_AAAA
)
222 if (!id
.NotAnInteger
) id
= lastid
;
225 mprintf("%##s %s %.16a\n", answer
->name
.c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv6
);
226 if (!hostaddr
.type
) // Prefer v4 target to v6 target, for now
228 hostaddr
.type
= mDNSAddrType_IPv6
;
229 hostaddr
.ip
.v6
= answer
->rdata
->u
.ipv6
;
232 else if (answer
->rrtype
== kDNSType_HINFO
)
234 mDNSu8
*p
= answer
->rdata
->u
.data
;
235 strncpy(hardware
, (char*)(p
+1), p
[0]);
238 strncpy(software
, (char*)(p
+1), p
[0]);
244 // If we've got everything we're looking for, don't need to wait any more
245 if (NumHINFO
&& (NumAddr
|| NumAAAA
)) StopNow
= 1;
248 static void ServicesCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
251 (void)question
; // Unused
252 (void)AddRecord
;// Unused
253 // Right now the mDNSCore targeted-query code is incomplete --
254 // it issues targeted queries, but accepts answers from anywhere
255 // For now, we'll just filter responses here so we don't get confused by responses from someone else
256 if (answer
->rrtype
== kDNSType_PTR
&& mDNSSameAddress(&lastsrc
, &target
))
260 mprintf("%##s %s %##s\n", answer
->name
.c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.name
.c
);
265 mDNSexport
void WaitForAnswer(mDNS
*const m
, int seconds
)
268 gettimeofday(&end
, NULL
);
269 end
.tv_sec
+= seconds
;
276 struct timeval now
, remain
= end
;
280 gettimeofday(&now
, NULL
);
281 if (remain
.tv_usec
< now
.tv_usec
) { remain
.tv_usec
+= 1000000; remain
.tv_sec
--; }
282 if (remain
.tv_sec
< now
.tv_sec
) return;
283 remain
.tv_usec
-= now
.tv_usec
;
284 remain
.tv_sec
-= now
.tv_sec
;
285 mDNSPosixGetFDSet(m
, &nfds
, &readfds
, &remain
);
286 result
= select(nfds
, &readfds
, NULL
, NULL
, &remain
);
287 if (result
>= 0) mDNSPosixProcessFDSet(m
, &readfds
);
288 else if (errno
!= EINTR
) StopNow
= 2;
292 mDNSlocal mStatus
StartQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
294 if (qname
) MakeDomainNameFromDNSNameString(&q
->qname
, qname
);
295 q
->Target
= target
? *target
: zeroAddr
;
296 q
->TargetPort
= MulticastDNSPort
;
297 q
->TargetQID
= zeroID
;
298 q
->InterfaceID
= mDNSInterface_ForceMCast
;
300 q
->qclass
= kDNSClass_IN
;
301 q
->QuestionCallback
= callback
;
302 q
->QuestionContext
= NULL
;
304 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
305 return(mDNS_StartQuery(&mDNSStorage
, q
));
308 mDNSlocal
void DoOneQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
310 mStatus status
= StartQuery(q
, qname
, qtype
, target
, callback
);
311 if (status
!= mStatus_NoError
)
315 WaitForAnswer(&mDNSStorage
, 4);
316 mDNS_StopQuery(&mDNSStorage
, q
);
320 mDNSlocal
int DoQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
322 DoOneQuery(q
, qname
, qtype
, target
, callback
);
323 if (StopNow
== 0 && target
&& target
->type
)
325 mprintf("%##s %s Trying multicast\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
326 DoOneQuery(q
, qname
, qtype
, NULL
, callback
);
328 if (StopNow
== 0 && NumAnswers
== 0)
329 mprintf("%##s %s *** No Answer ***\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
333 mDNSlocal
void HandleSIG(int signal
)
335 (void)signal
; // Unused
341 mDNSexport
int main(int argc
, char **argv
)
350 if (argc
< 2) goto usage
;
352 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
353 mDNS_DebugMode
= mDNStrue
;
355 // Initialise the mDNS core.
356 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
357 gRRCache
, RR_CACHE_SIZE
,
358 mDNS_Init_DontAdvertiseLocalAddresses
,
359 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
360 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %ld\n", status
); return(status
); }
362 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
363 signal(SIGTERM
, HandleSIG
);
365 while (this_arg
< argc
)
367 char *arg
= argv
[this_arg
++];
368 if (this_arg
> 2) printf("\n");
370 lastid
= id
= zeroID
;
371 hostaddr
= target
= zeroAddr
;
372 hostname
[0] = hardware
[0] = software
[0] = 0;
373 NumAddr
= NumAAAA
= NumHINFO
= 0;
375 if (inet_pton(AF_INET
, arg
, &s4
) == 1)
377 mDNSu8
*p
= (mDNSu8
*)&s4
;
378 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.", p
[3], p
[2], p
[1], p
[0]);
379 printf("%s\n", buffer
);
380 target
.type
= mDNSAddrType_IPv4
;
381 target
.ip
.v4
.NotAnInteger
= s4
.s_addr
;
382 DoQuery(&q
, buffer
, kDNSType_PTR
, &target
, NameCallback
);
383 if (StopNow
== 2) break;
385 else if (inet_pton(AF_INET6
, arg
, &s6
) == 1)
388 mDNSu8
*p
= (mDNSu8
*)&s6
;
389 for (i
= 0; i
< 16; i
++)
391 static const char hexValues
[] = "0123456789ABCDEF";
392 buffer
[i
* 4 ] = hexValues
[p
[15-i
] & 0x0F];
393 buffer
[i
* 4 + 1] = '.';
394 buffer
[i
* 4 + 2] = hexValues
[p
[15-i
] >> 4];
395 buffer
[i
* 4 + 3] = '.';
397 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
398 target
.type
= mDNSAddrType_IPv6
;
399 bcopy(&s6
, &target
.ip
.v6
, sizeof(target
.ip
.v6
));
400 DoQuery(&q
, buffer
, kDNSType_PTR
, &target
, NameCallback
);
401 if (StopNow
== 2) break;
404 strcpy(hostname
, arg
);
406 // Now we have the host name; get its A, AAAA, and HINFO
407 if (hostname
[0]) DoQuery(&q
, hostname
, kDNSQType_ANY
, &target
, InfoCallback
);
408 if (StopNow
== 2) break;
410 if (hardware
[0] || software
[0])
413 printf("HINFO Hardware: %s\n", hardware
);
414 printf("HINFO Software: %s\n", software
);
415 // We need to make sure the services query is targeted
416 if (target
.type
== 0) target
= hostaddr
;
417 StartQuery(&q1
, "_services._mdns._udp.local.", kDNSQType_ANY
, &target
, ServicesCallback
);
418 StartQuery(&q2
, "_services._dns-sd._udp.local.", kDNSQType_ANY
, &target
, ServicesCallback
);
419 WaitForAnswer(&mDNSStorage
, 4);
420 mDNS_StopQuery(&mDNSStorage
, &q1
);
421 mDNS_StopQuery(&mDNSStorage
, &q2
);
422 if (StopNow
== 2) break;
426 printf("Host has no HINFO record; Best guess is ");
427 if (id
.b
[1]) printf("mDNSResponder-%d\n", id
.b
[1]);
428 else if (NumAAAA
) printf("very early Panther build (mDNSResponder-33 or earlier)\n");
429 else printf("Jaguar version of mDNSResponder with no IPv6 support\n");
432 printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
435 mDNS_Close(&mDNSStorage
);
439 fprintf(stderr
, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", argv
[0]);