1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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
15 * limitations under the License.
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.)
30 Change History (most recent first):
33 Revision 1.36 2006/08/14 23:24:46 cheshire
34 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
36 Revision 1.35 2006/06/12 18:22:42 cheshire
37 <rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
39 Revision 1.34 2004/12/16 20:17:11 cheshire
40 <rdar://problem/3324626> Cache memory management improvements
42 Revision 1.33 2004/11/30 22:37:00 cheshire
43 Update copyright dates and add "Mode: C; tab-width: 4" headers
45 Revision 1.32 2004/10/19 21:33:21 cheshire
46 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
47 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
48 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
50 Revision 1.31 2004/10/16 00:17:00 cheshire
51 <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
53 Revision 1.30 2004/09/21 23:29:51 cheshire
54 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
56 Revision 1.29 2004/09/17 01:08:53 cheshire
57 Renamed 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.
62 Revision 1.28 2004/09/17 00:31:52 cheshire
63 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
65 Revision 1.27 2004/09/16 01:58:22 cheshire
68 Revision 1.26 2004/08/24 21:55:07 cheshire
69 Don't try to build IPv6 code on systems that don't have IPv6
71 Revision 1.25 2004/07/20 23:42:37 cheshire
72 Update to use only "_services._dns-sd._udp.local." meta-query for service enumeration
74 Revision 1.24 2004/06/15 02:39:47 cheshire
75 When displaying error message, only show command name, not entire path
77 Revision 1.23 2004/05/18 23:51:26 cheshire
78 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
80 Revision 1.22 2004/04/20 22:43:28 cheshire
81 Use _services._dns-sd._udp query, as documented in
82 <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd-02.txt>
84 Revision 1.21 2004/01/28 21:38:57 cheshire
85 Also ask target host for _services._mdns._udp.local. list
87 Revision 1.20 2004/01/28 19:04:38 cheshire
88 Fix Ctrl-C handling when multiple targets are specified
90 Revision 1.19 2004/01/28 03:49:30 cheshire
91 Enhanced mDNSIdentify to make use of new targeted-query capability
93 Revision 1.18 2004/01/27 19:06:51 cheshire
94 Remove workaround for WWDC 2003 bug; no one has run that buggy build for a long time
96 Revision 1.17 2004/01/22 03:57:00 cheshire
97 Use the new meta-interface mDNSInterface_ForceMCast. This restores mDNSIdentify's
98 ability to use multicast queries with non-link-local target addresses, like 17.x.x.x.
100 Revision 1.16 2004/01/22 00:03:32 cheshire
101 Add while() loop so that a list of targets may be specified on the command line
103 Revision 1.15 2004/01/21 21:55:06 cheshire
104 Don't need to wait for timeout once we've got the information we wanted
106 Revision 1.14 2003/12/17 00:51:22 cheshire
107 Changed mDNSNetMonitor and mDNSIdentify to link the object files
108 instead of #including the "DNSCommon.c" "uDNS.c" and source files
110 Revision 1.13 2003/12/13 03:05:28 ksekar
111 <rdar://problem/3192548>: DynDNS: Unicast query of service records
113 Revision 1.12 2003/11/14 21:27:09 cheshire
114 <rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
115 Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
117 Revision 1.11 2003/10/30 19:26:38 cheshire
118 Fix warnings on certain compilers
120 Revision 1.10 2003/09/02 20:38:57 cheshire
121 #include <signal.h> for Linux
123 Revision 1.9 2003/08/14 23:57:46 cheshire
124 Report if there is no answer at all from the target host
126 Revision 1.8 2003/08/14 02:19:55 cheshire
127 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
129 Revision 1.7 2003/08/12 19:56:26 cheshire
132 Revision 1.6 2003/08/06 01:46:18 cheshire
133 Distinguish no answer from partial answer
135 Revision 1.5 2003/08/05 23:56:26 cheshire
136 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
137 (Right now mDNSPosix.c just reports 255 -- we should fix this)
139 Revision 1.4 2003/08/04 17:24:48 cheshire
140 Combine the three separate A/AAAA/HINFO queries into a single qtype "ANY" query
142 Revision 1.3 2003/08/04 17:14:08 cheshire
143 Do both AAAA queries in parallel
145 Revision 1.2 2003/08/02 02:25:13 cheshire
146 Multiple improvements: Now displays host's name, and all v4 and v6 addresses, as well as HINFO record
148 Revision 1.1 2003/08/01 02:20:02 cheshire
149 Add mDNSIdentify tool, used to discover what version of mDNSResponder a particular host is running
153 //*************************************************************************************************************
154 // Incorporate mDNS.c functionality
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
160 #undef mDNSCoreReceive
162 //*************************************************************************************************************
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>
176 #include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
177 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
178 #include "ExampleClientApp.h"
180 //*************************************************************************************************************
183 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
184 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
185 #define RR_CACHE_SIZE 500
186 static CacheEntity gRRCache
[RR_CACHE_SIZE
];
188 static volatile int StopNow
; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
189 static volatile int NumAnswers
, NumAddr
, NumAAAA
, NumHINFO
;
190 static char hostname
[MAX_ESCAPED_DOMAIN_NAME
], hardware
[256], software
[256];
191 static mDNSAddr lastsrc
, hostaddr
, target
;
192 static mDNSOpaque16 lastid
, id
;
194 //*************************************************************************************************************
197 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
198 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
199 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
202 unsigned char buffer
[512];
204 va_start(ptr
,format
);
205 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
207 printf("%s", buffer
);
211 //*************************************************************************************************************
214 mDNSexport
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
,
216 const mDNSInterfaceID InterfaceID
)
218 (void)dstaddr
; // Unused
219 // Snag copy of header ID, then call through
223 // We *want* to allow off-net unicast responses here.
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
);
228 static void NameCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
231 (void)question
; // Unused
232 (void)AddRecord
;// Unused
233 if (!id
.NotAnInteger
) id
= lastid
;
234 if (answer
->rrtype
== kDNSType_PTR
|| answer
->rrtype
== kDNSType_CNAME
)
236 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, hostname
);
238 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
242 static void InfoCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
245 (void)question
; // Unused
246 (void)AddRecord
;// Unused
247 if (answer
->rrtype
== kDNSType_A
)
249 if (!id
.NotAnInteger
) id
= lastid
;
252 mprintf("%##s %s %.4a\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv4
);
253 hostaddr
.type
= mDNSAddrType_IPv4
; // Prefer v4 target to v6 target, for now
254 hostaddr
.ip
.v4
= answer
->rdata
->u
.ipv4
;
256 else if (answer
->rrtype
== kDNSType_AAAA
)
258 if (!id
.NotAnInteger
) id
= lastid
;
261 mprintf("%##s %s %.16a\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv6
);
262 if (!hostaddr
.type
) // Prefer v4 target to v6 target, for now
264 hostaddr
.type
= mDNSAddrType_IPv6
;
265 hostaddr
.ip
.v6
= answer
->rdata
->u
.ipv6
;
268 else if (answer
->rrtype
== kDNSType_HINFO
)
270 mDNSu8
*p
= answer
->rdata
->u
.data
;
271 strncpy(hardware
, (char*)(p
+1), p
[0]);
274 strncpy(software
, (char*)(p
+1), p
[0]);
280 // If we've got everything we're looking for, don't need to wait any more
281 if (NumHINFO
&& (NumAddr
|| NumAAAA
)) StopNow
= 1;
284 static void ServicesCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
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
))
296 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
301 mDNSexport
void WaitForAnswer(mDNS
*const m
, int seconds
)
304 gettimeofday(&end
, NULL
);
305 end
.tv_sec
+= seconds
;
312 struct timeval now
, remain
= end
;
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;
328 mDNSlocal mStatus
StartQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
330 if (qname
) MakeDomainNameFromDNSNameString(&q
->qname
, qname
);
331 q
->Target
= target
? *target
: zeroAddr
;
332 q
->TargetPort
= MulticastDNSPort
;
333 q
->TargetQID
= zeroID
;
334 q
->InterfaceID
= mDNSInterface_Any
;
336 q
->qclass
= kDNSClass_IN
;
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.
340 q
->QuestionCallback
= callback
;
341 q
->QuestionContext
= NULL
;
343 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
344 return(mDNS_StartQuery(&mDNSStorage
, q
));
347 mDNSlocal
void DoOneQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
349 mStatus status
= StartQuery(q
, qname
, qtype
, target
, callback
);
350 if (status
!= mStatus_NoError
)
354 WaitForAnswer(&mDNSStorage
, 4);
355 mDNS_StopQuery(&mDNSStorage
, q
);
359 mDNSlocal
int DoQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
361 DoOneQuery(q
, qname
, qtype
, target
, callback
);
362 if (StopNow
== 0 && target
&& target
->type
)
364 mprintf("%##s %s Trying multicast\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
365 DoOneQuery(q
, qname
, qtype
, NULL
, callback
);
367 if (StopNow
== 0 && NumAnswers
== 0)
368 mprintf("%##s %s *** No Answer ***\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
372 mDNSlocal
void HandleSIG(int signal
)
374 (void)signal
; // Unused
380 mDNSexport
int main(int argc
, char **argv
)
382 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
392 if (argc
< 2) goto usage
;
394 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
395 mDNS_DebugMode
= mDNStrue
;
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
);
402 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %d\n", (int)status
); return(status
); }
404 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
405 signal(SIGTERM
, HandleSIG
);
407 while (this_arg
< argc
)
409 char *arg
= argv
[this_arg
++];
410 if (this_arg
> 2) printf("\n");
412 lastid
= id
= zeroID
;
413 hostaddr
= target
= zeroAddr
;
414 hostname
[0] = hardware
[0] = software
[0] = 0;
415 NumAddr
= NumAAAA
= NumHINFO
= 0;
417 if (inet_pton(AF_INET
, arg
, &s4
) == 1)
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;
428 else if (inet_pton(AF_INET6
, arg
, &s6
) == 1)
431 mDNSu8
*p
= (mDNSu8
*)&s6
;
432 for (i
= 0; i
< 16; i
++)
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] = '.';
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;
448 strcpy(hostname
, arg
);
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;
454 if (hardware
[0] || software
[0])
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
;
461 StartQuery(&q1
, "_services._dns-sd._udp.local.", kDNSQType_ANY
, &target
, ServicesCallback
);
462 WaitForAnswer(&mDNSStorage
, 4);
463 mDNS_StopQuery(&mDNSStorage
, &q1
);
464 if (StopNow
== 2) break;
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");
474 printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
477 mDNS_Close(&mDNSStorage
);
481 fprintf(stderr
, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname
);