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.44 2009/01/13 05:31:34 mkrochma
34 <rdar://problem/6491367> Replace bzero, bcopy with mDNSPlatformMemZero, mDNSPlatformMemCopy, memset, memcpy
36 Revision 1.43 2008/09/05 22:51:21 cheshire
37 Minor cleanup to bring code in sync with TOT, and make "_services" metaquery work again
39 Revision 1.42 2007/07/27 19:30:41 cheshire
40 Changed mDNSQuestionCallback parameter from mDNSBool to QC_result,
41 to properly reflect tri-state nature of the possible responses
43 Revision 1.41 2007/04/16 20:49:39 cheshire
44 Fix compile errors for mDNSPosix build
46 Revision 1.40 2007/02/28 01:51:22 cheshire
47 Added comment about reverse-order IP address
49 Revision 1.39 2007/01/05 08:30:51 cheshire
50 Trim excessive "$Log" checkin history from before 2006
51 (checkin history still available via "cvs log ..." of course)
53 Revision 1.38 2007/01/04 20:57:48 cheshire
54 Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates)
56 Revision 1.37 2006/10/27 01:32:08 cheshire
57 Set ReturnIntermed to mDNStrue
59 Revision 1.36 2006/08/14 23:24:46 cheshire
60 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
62 Revision 1.35 2006/06/12 18:22:42 cheshire
63 <rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
67 //*************************************************************************************************************
68 // Incorporate mDNS.c functionality
70 // We want to use the functionality provided by "mDNS.c",
71 // except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
72 #define mDNSCoreReceive __MDNS__mDNSCoreReceive
74 #undef mDNSCoreReceive
76 //*************************************************************************************************************
83 #include <sys/socket.h>
84 #include <netinet/in.h>
85 #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
86 #include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
87 #include <arpa/inet.h>
90 #include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
91 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
92 #include "ExampleClientApp.h"
94 //*************************************************************************************************************
97 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
98 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
99 #define RR_CACHE_SIZE 500
100 static CacheEntity gRRCache
[RR_CACHE_SIZE
];
101 mDNSexport
const char ProgramName
[] = "mDNSIdentify";
103 static volatile int StopNow
; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
104 static volatile int NumAnswers
, NumAddr
, NumAAAA
, NumHINFO
;
105 static char hostname
[MAX_ESCAPED_DOMAIN_NAME
], hardware
[256], software
[256];
106 static mDNSAddr lastsrc
, hostaddr
, target
;
107 static mDNSOpaque16 lastid
, id
;
109 //*************************************************************************************************************
112 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
113 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
114 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
117 unsigned char buffer
[512];
119 va_start(ptr
,format
);
120 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
122 printf("%s", buffer
);
126 //*************************************************************************************************************
129 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
130 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*const dstaddr
, const mDNSIPPort dstport
,
131 const mDNSInterfaceID InterfaceID
)
133 (void)dstaddr
; // Unused
134 // Snag copy of header ID, then call through
138 // We *want* to allow off-net unicast responses here.
139 // For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
140 __MDNS__mDNSCoreReceive(m
, msg
, end
, srcaddr
, srcport
, &AllDNSLinkGroup_v4
, dstport
, InterfaceID
);
143 mDNSlocal
void NameCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
146 (void)question
; // Unused
147 (void)AddRecord
;// Unused
148 if (!id
.NotAnInteger
) id
= lastid
;
149 if (answer
->rrtype
== kDNSType_PTR
|| answer
->rrtype
== kDNSType_CNAME
)
151 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, hostname
);
153 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
157 mDNSlocal
void InfoCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
160 (void)question
; // Unused
161 (void)AddRecord
;// Unused
162 if (answer
->rrtype
== kDNSType_A
)
164 if (!id
.NotAnInteger
) id
= lastid
;
167 mprintf("%##s %s %.4a\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv4
);
168 hostaddr
.type
= mDNSAddrType_IPv4
; // Prefer v4 target to v6 target, for now
169 hostaddr
.ip
.v4
= answer
->rdata
->u
.ipv4
;
171 else if (answer
->rrtype
== kDNSType_AAAA
)
173 if (!id
.NotAnInteger
) id
= lastid
;
176 mprintf("%##s %s %.16a\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv6
);
177 if (!hostaddr
.type
) // Prefer v4 target to v6 target, for now
179 hostaddr
.type
= mDNSAddrType_IPv6
;
180 hostaddr
.ip
.v6
= answer
->rdata
->u
.ipv6
;
183 else if (answer
->rrtype
== kDNSType_HINFO
)
185 mDNSu8
*p
= answer
->rdata
->u
.data
;
186 strncpy(hardware
, (char*)(p
+1), p
[0]);
189 strncpy(software
, (char*)(p
+1), p
[0]);
195 // If we've got everything we're looking for, don't need to wait any more
196 if (/*NumHINFO && */ (NumAddr
|| NumAAAA
)) StopNow
= 1;
199 mDNSlocal
void ServicesCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
202 (void)question
; // Unused
203 (void)AddRecord
;// Unused
204 // Right now the mDNSCore targeted-query code is incomplete --
205 // it issues targeted queries, but accepts answers from anywhere
206 // For now, we'll just filter responses here so we don't get confused by responses from someone else
207 if (answer
->rrtype
== kDNSType_PTR
&& mDNSSameAddress(&lastsrc
, &target
))
210 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
214 mDNSlocal
void WaitForAnswer(mDNS
*const m
, int seconds
)
217 gettimeofday(&end
, NULL
);
218 end
.tv_sec
+= seconds
;
225 struct timeval now
, remain
= end
;
229 gettimeofday(&now
, NULL
);
230 if (remain
.tv_usec
< now
.tv_usec
) { remain
.tv_usec
+= 1000000; remain
.tv_sec
--; }
231 if (remain
.tv_sec
< now
.tv_sec
)
233 if (!NumAnswers
) printf("No response after %d seconds\n", seconds
);
236 remain
.tv_usec
-= now
.tv_usec
;
237 remain
.tv_sec
-= now
.tv_sec
;
238 mDNSPosixGetFDSet(m
, &nfds
, &readfds
, &remain
);
239 result
= select(nfds
, &readfds
, NULL
, NULL
, &remain
);
240 if (result
>= 0) mDNSPosixProcessFDSet(m
, &readfds
);
241 else if (errno
!= EINTR
) StopNow
= 2;
245 mDNSlocal mStatus
StartQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
248 if (qname
) MakeDomainNameFromDNSNameString(&q
->qname
, qname
);
249 q
->InterfaceID
= mDNSInterface_Any
;
250 q
->Target
= target
? *target
: zeroAddr
;
251 q
->TargetPort
= MulticastDNSPort
;
252 q
->TargetQID
= zeroID
;
254 q
->qclass
= kDNSClass_IN
;
255 q
->LongLived
= mDNSfalse
;
256 q
->ExpectUnique
= mDNSfalse
; // Don't want to stop after the first response packet
257 q
->ForceMCast
= mDNStrue
; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
258 q
->ReturnIntermed
= mDNStrue
;
259 q
->QuestionCallback
= callback
;
260 q
->QuestionContext
= NULL
;
262 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
263 return(mDNS_StartQuery(&mDNSStorage
, q
));
266 mDNSlocal
void DoOneQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
268 mStatus status
= StartQuery(q
, qname
, qtype
, target
, callback
);
269 if (status
!= mStatus_NoError
)
273 WaitForAnswer(&mDNSStorage
, 4);
274 mDNS_StopQuery(&mDNSStorage
, q
);
278 mDNSlocal
int DoQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
280 DoOneQuery(q
, qname
, qtype
, target
, callback
);
281 if (StopNow
== 0 && NumAnswers
== 0 && target
&& target
->type
)
283 mprintf("%##s %s Trying multicast\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
284 DoOneQuery(q
, qname
, qtype
, NULL
, callback
);
286 if (StopNow
== 0 && NumAnswers
== 0)
287 mprintf("%##s %s *** No Answer ***\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
291 mDNSlocal
void HandleSIG(int signal
)
293 (void)signal
; // Unused
299 mDNSexport
int main(int argc
, char **argv
)
301 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
311 if (argc
< 2) goto usage
;
313 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
314 mDNS_DebugMode
= mDNStrue
;
316 // Initialise the mDNS core.
317 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
318 gRRCache
, RR_CACHE_SIZE
,
319 mDNS_Init_DontAdvertiseLocalAddresses
,
320 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
321 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %d\n", (int)status
); return(status
); }
323 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
324 signal(SIGTERM
, HandleSIG
);
326 while (this_arg
< argc
)
328 char *arg
= argv
[this_arg
++];
329 if (this_arg
> 2) printf("\n");
331 lastid
= id
= zeroID
;
332 hostaddr
= target
= zeroAddr
;
333 hostname
[0] = hardware
[0] = software
[0] = 0;
334 NumAddr
= NumAAAA
= NumHINFO
= 0;
336 if (inet_pton(AF_INET
, arg
, &s4
) == 1)
338 mDNSu8
*p
= (mDNSu8
*)&s4
;
339 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
340 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.", p
[3], p
[2], p
[1], p
[0]);
341 printf("%s\n", buffer
);
342 target
.type
= mDNSAddrType_IPv4
;
343 target
.ip
.v4
.NotAnInteger
= s4
.s_addr
;
344 DoQuery(&q
, buffer
, kDNSType_PTR
, &target
, NameCallback
);
345 if (StopNow
== 2) break;
348 else if (inet_pton(AF_INET6
, arg
, &s6
) == 1)
351 mDNSu8
*p
= (mDNSu8
*)&s6
;
352 for (i
= 0; i
< 16; i
++)
354 static const char hexValues
[] = "0123456789ABCDEF";
355 buffer
[i
* 4 ] = hexValues
[p
[15-i
] & 0x0F];
356 buffer
[i
* 4 + 1] = '.';
357 buffer
[i
* 4 + 2] = hexValues
[p
[15-i
] >> 4];
358 buffer
[i
* 4 + 3] = '.';
360 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
361 target
.type
= mDNSAddrType_IPv6
;
362 mDNSPlatformMemCopy(&target
.ip
.v6
, &s6
, sizeof(target
.ip
.v6
));
363 DoQuery(&q
, buffer
, kDNSType_PTR
, &target
, NameCallback
);
364 if (StopNow
== 2) break;
368 strcpy(hostname
, arg
);
370 // Now we have the host name; get its A, AAAA, and HINFO
371 if (hostname
[0]) DoQuery(&q
, hostname
, kDNSQType_ANY
, &target
, InfoCallback
);
372 if (StopNow
== 2) break;
374 if (hardware
[0] || software
[0])
376 printf("HINFO Hardware: %s\n", hardware
);
377 printf("HINFO Software: %s\n", software
);
379 else if (NumAnswers
) printf("%s has no HINFO record\n", hostname
);
380 else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
384 // Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers
385 mDNS
*const m
= &mDNSStorage
;
389 FORALL_CACHERECORDS(slot
, cg
, rr
) mDNS_PurgeCacheResourceRecord(m
, rr
);
390 if (target
.type
== 0) target
= hostaddr
; // Make sure the services query is targeted
391 DoQuery(&q
, "_services._dns-sd._udp.local.", kDNSType_PTR
, &target
, ServicesCallback
);
392 if (StopNow
== 2) break;
396 mDNS_Close(&mDNSStorage
);
400 fprintf(stderr
, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname
);