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.33 2004/11/30 22:37:00 cheshire
41 Update copyright dates and add "Mode: C; tab-width: 4" headers
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
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
51 Revision 1.30 2004/09/21 23:29:51 cheshire
52 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
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.
60 Revision 1.28 2004/09/17 00:31:52 cheshire
61 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
63 Revision 1.27 2004/09/16 01:58:22 cheshire
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
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
72 Revision 1.24 2004/06/15 02:39:47 cheshire
73 When displaying error message, only show command name, not entire path
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
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>
82 Revision 1.21 2004/01/28 21:38:57 cheshire
83 Also ask target host for _services._mdns._udp.local. list
85 Revision 1.20 2004/01/28 19:04:38 cheshire
86 Fix Ctrl-C handling when multiple targets are specified
88 Revision 1.19 2004/01/28 03:49:30 cheshire
89 Enhanced mDNSIdentify to make use of new targeted-query capability
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
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.
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
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
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
108 Revision 1.13 2003/12/13 03:05:28 ksekar
109 <rdar://problem/3192548>: DynDNS: Unicast query of service records
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.
115 Revision 1.11 2003/10/30 19:26:38 cheshire
116 Fix warnings on certain compilers
118 Revision 1.10 2003/09/02 20:38:57 cheshire
119 #include <signal.h> for Linux
121 Revision 1.9 2003/08/14 23:57:46 cheshire
122 Report if there is no answer at all from the target host
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
127 Revision 1.7 2003/08/12 19:56:26 cheshire
130 Revision 1.6 2003/08/06 01:46:18 cheshire
131 Distinguish no answer from partial answer
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)
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
140 Revision 1.3 2003/08/04 17:14:08 cheshire
141 Do both AAAA queries in parallel
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
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
151 //*************************************************************************************************************
152 // Incorporate mDNS.c functionality
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
158 #undef mDNSCoreReceive
160 //*************************************************************************************************************
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>
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"
178 //*************************************************************************************************************
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
];
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
;
192 //*************************************************************************************************************
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
, ...)
200 unsigned char buffer
[512];
202 va_start(ptr
,format
);
203 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
205 printf("%s", buffer
);
209 //*************************************************************************************************************
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
)
216 (void)dstaddr
; // Unused
217 // Snag copy of header ID, then call through
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
);
226 static void NameCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
229 (void)question
; // Unused
230 (void)AddRecord
;// Unused
231 if (!id
.NotAnInteger
) id
= lastid
;
232 if (answer
->rrtype
== kDNSType_PTR
|| answer
->rrtype
== kDNSType_CNAME
)
234 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, hostname
);
236 mprintf("%##s %s %##s\n", answer
->name
.c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
240 static void InfoCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
243 (void)question
; // Unused
244 (void)AddRecord
;// Unused
245 if (answer
->rrtype
== kDNSType_A
)
247 if (!id
.NotAnInteger
) id
= lastid
;
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
;
254 else if (answer
->rrtype
== kDNSType_AAAA
)
256 if (!id
.NotAnInteger
) id
= lastid
;
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
262 hostaddr
.type
= mDNSAddrType_IPv6
;
263 hostaddr
.ip
.v6
= answer
->rdata
->u
.ipv6
;
266 else if (answer
->rrtype
== kDNSType_HINFO
)
268 mDNSu8
*p
= answer
->rdata
->u
.data
;
269 strncpy(hardware
, (char*)(p
+1), p
[0]);
272 strncpy(software
, (char*)(p
+1), p
[0]);
278 // If we've got everything we're looking for, don't need to wait any more
279 if (NumHINFO
&& (NumAddr
|| NumAAAA
)) StopNow
= 1;
282 static void ServicesCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
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
))
294 mprintf("%##s %s %##s\n", answer
->name
.c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
299 mDNSexport
void WaitForAnswer(mDNS
*const m
, int seconds
)
302 gettimeofday(&end
, NULL
);
303 end
.tv_sec
+= seconds
;
310 struct timeval now
, remain
= end
;
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;
326 mDNSlocal mStatus
StartQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
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
;
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
;
341 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
342 return(mDNS_StartQuery(&mDNSStorage
, q
));
345 mDNSlocal
void DoOneQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
347 mStatus status
= StartQuery(q
, qname
, qtype
, target
, callback
);
348 if (status
!= mStatus_NoError
)
352 WaitForAnswer(&mDNSStorage
, 4);
353 mDNS_StopQuery(&mDNSStorage
, q
);
357 mDNSlocal
int DoQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
359 DoOneQuery(q
, qname
, qtype
, target
, callback
);
360 if (StopNow
== 0 && target
&& target
->type
)
362 mprintf("%##s %s Trying multicast\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
363 DoOneQuery(q
, qname
, qtype
, NULL
, callback
);
365 if (StopNow
== 0 && NumAnswers
== 0)
366 mprintf("%##s %s *** No Answer ***\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
370 mDNSlocal
void HandleSIG(int signal
)
372 (void)signal
; // Unused
378 mDNSexport
int main(int argc
, char **argv
)
380 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
390 if (argc
< 2) goto usage
;
392 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
393 mDNS_DebugMode
= mDNStrue
;
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
); }
402 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
403 signal(SIGTERM
, HandleSIG
);
405 while (this_arg
< argc
)
407 char *arg
= argv
[this_arg
++];
408 if (this_arg
> 2) printf("\n");
410 lastid
= id
= zeroID
;
411 hostaddr
= target
= zeroAddr
;
412 hostname
[0] = hardware
[0] = software
[0] = 0;
413 NumAddr
= NumAAAA
= NumHINFO
= 0;
415 if (inet_pton(AF_INET
, arg
, &s4
) == 1)
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;
426 else if (inet_pton(AF_INET6
, arg
, &s6
) == 1)
429 mDNSu8
*p
= (mDNSu8
*)&s6
;
430 for (i
= 0; i
< 16; i
++)
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] = '.';
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;
446 strcpy(hostname
, arg
);
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;
452 if (hardware
[0] || software
[0])
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;
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");
472 printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
475 mDNS_Close(&mDNSStorage
);
479 fprintf(stderr
, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname
);