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.42 2007/07/27 19:30:41 cheshire
34 Changed mDNSQuestionCallback parameter from mDNSBool to QC_result,
35 to properly reflect tri-state nature of the possible responses
37 Revision 1.41 2007/04/16 20:49:39 cheshire
38 Fix compile errors for mDNSPosix build
40 Revision 1.40 2007/02/28 01:51:22 cheshire
41 Added comment about reverse-order IP address
43 Revision 1.39 2007/01/05 08:30:51 cheshire
44 Trim excessive "$Log" checkin history from before 2006
45 (checkin history still available via "cvs log ..." of course)
47 Revision 1.38 2007/01/04 20:57:48 cheshire
48 Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates)
50 Revision 1.37 2006/10/27 01:32:08 cheshire
51 Set ReturnIntermed to mDNStrue
53 Revision 1.36 2006/08/14 23:24:46 cheshire
54 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
56 Revision 1.35 2006/06/12 18:22:42 cheshire
57 <rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
61 //*************************************************************************************************************
62 // Incorporate mDNS.c functionality
64 // We want to use the functionality provided by "mDNS.c",
65 // except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
66 #define mDNSCoreReceive __MDNS__mDNSCoreReceive
68 #undef mDNSCoreReceive
70 //*************************************************************************************************************
77 #include <sys/socket.h>
78 #include <netinet/in.h>
79 #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
80 #include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
81 #include <arpa/inet.h>
84 #include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
85 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
86 #include "ExampleClientApp.h"
88 //*************************************************************************************************************
91 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
92 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
93 #define RR_CACHE_SIZE 500
94 static CacheEntity gRRCache
[RR_CACHE_SIZE
];
95 mDNSexport
const char ProgramName
[] = "mDNSIdentify";
97 static volatile int StopNow
; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
98 static volatile int NumAnswers
, NumAddr
, NumAAAA
, NumHINFO
;
99 static char hostname
[MAX_ESCAPED_DOMAIN_NAME
], hardware
[256], software
[256];
100 static mDNSAddr lastsrc
, hostaddr
, target
;
101 static mDNSOpaque16 lastid
, id
;
103 //*************************************************************************************************************
106 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
107 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
108 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
111 unsigned char buffer
[512];
113 va_start(ptr
,format
);
114 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
116 printf("%s", buffer
);
120 //*************************************************************************************************************
123 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
124 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*const dstaddr
, const mDNSIPPort dstport
,
125 const mDNSInterfaceID InterfaceID
)
127 (void)dstaddr
; // Unused
128 // Snag copy of header ID, then call through
132 // We *want* to allow off-net unicast responses here.
133 // For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
134 __MDNS__mDNSCoreReceive(m
, msg
, end
, srcaddr
, srcport
, &AllDNSLinkGroup_v4
, dstport
, InterfaceID
);
137 static void NameCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
140 (void)question
; // Unused
141 (void)AddRecord
;// Unused
142 if (!id
.NotAnInteger
) id
= lastid
;
143 if (answer
->rrtype
== kDNSType_PTR
|| answer
->rrtype
== kDNSType_CNAME
)
145 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, hostname
);
147 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
151 static void InfoCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
154 (void)question
; // Unused
155 (void)AddRecord
;// Unused
156 if (answer
->rrtype
== kDNSType_A
)
158 if (!id
.NotAnInteger
) id
= lastid
;
161 mprintf("%##s %s %.4a\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv4
);
162 hostaddr
.type
= mDNSAddrType_IPv4
; // Prefer v4 target to v6 target, for now
163 hostaddr
.ip
.v4
= answer
->rdata
->u
.ipv4
;
165 else if (answer
->rrtype
== kDNSType_AAAA
)
167 if (!id
.NotAnInteger
) id
= lastid
;
170 mprintf("%##s %s %.16a\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv6
);
171 if (!hostaddr
.type
) // Prefer v4 target to v6 target, for now
173 hostaddr
.type
= mDNSAddrType_IPv6
;
174 hostaddr
.ip
.v6
= answer
->rdata
->u
.ipv6
;
177 else if (answer
->rrtype
== kDNSType_HINFO
)
179 mDNSu8
*p
= answer
->rdata
->u
.data
;
180 strncpy(hardware
, (char*)(p
+1), p
[0]);
183 strncpy(software
, (char*)(p
+1), p
[0]);
189 // If we've got everything we're looking for, don't need to wait any more
190 if (NumHINFO
&& (NumAddr
|| NumAAAA
)) StopNow
= 1;
193 static void ServicesCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
196 (void)question
; // Unused
197 (void)AddRecord
;// Unused
198 // Right now the mDNSCore targeted-query code is incomplete --
199 // it issues targeted queries, but accepts answers from anywhere
200 // For now, we'll just filter responses here so we don't get confused by responses from someone else
201 if (answer
->rrtype
== kDNSType_PTR
&& mDNSSameAddress(&lastsrc
, &target
))
205 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
210 mDNSexport
void WaitForAnswer(mDNS
*const m
, int seconds
)
213 gettimeofday(&end
, NULL
);
214 end
.tv_sec
+= seconds
;
221 struct timeval now
, remain
= end
;
225 gettimeofday(&now
, NULL
);
226 if (remain
.tv_usec
< now
.tv_usec
) { remain
.tv_usec
+= 1000000; remain
.tv_sec
--; }
227 if (remain
.tv_sec
< now
.tv_sec
) return;
228 remain
.tv_usec
-= now
.tv_usec
;
229 remain
.tv_sec
-= now
.tv_sec
;
230 mDNSPosixGetFDSet(m
, &nfds
, &readfds
, &remain
);
231 result
= select(nfds
, &readfds
, NULL
, NULL
, &remain
);
232 if (result
>= 0) mDNSPosixProcessFDSet(m
, &readfds
);
233 else if (errno
!= EINTR
) StopNow
= 2;
237 mDNSlocal mStatus
StartQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
239 if (qname
) MakeDomainNameFromDNSNameString(&q
->qname
, qname
);
240 q
->Target
= target
? *target
: zeroAddr
;
241 q
->TargetPort
= MulticastDNSPort
;
242 q
->TargetQID
= zeroID
;
243 q
->InterfaceID
= mDNSInterface_Any
;
245 q
->qclass
= kDNSClass_IN
;
246 q
->LongLived
= mDNSfalse
;
247 q
->ExpectUnique
= mDNStrue
;
248 q
->ForceMCast
= mDNStrue
; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
249 q
->ReturnIntermed
= mDNStrue
;
250 q
->QuestionCallback
= callback
;
251 q
->QuestionContext
= NULL
;
253 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
254 return(mDNS_StartQuery(&mDNSStorage
, q
));
257 mDNSlocal
void DoOneQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
259 mStatus status
= StartQuery(q
, qname
, qtype
, target
, callback
);
260 if (status
!= mStatus_NoError
)
264 WaitForAnswer(&mDNSStorage
, 4);
265 mDNS_StopQuery(&mDNSStorage
, q
);
269 mDNSlocal
int DoQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
271 DoOneQuery(q
, qname
, qtype
, target
, callback
);
272 if (StopNow
== 0 && target
&& target
->type
)
274 mprintf("%##s %s Trying multicast\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
275 DoOneQuery(q
, qname
, qtype
, NULL
, callback
);
277 if (StopNow
== 0 && NumAnswers
== 0)
278 mprintf("%##s %s *** No Answer ***\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
282 mDNSlocal
void HandleSIG(int signal
)
284 (void)signal
; // Unused
290 mDNSexport
int main(int argc
, char **argv
)
292 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
302 if (argc
< 2) goto usage
;
304 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
305 mDNS_DebugMode
= mDNStrue
;
307 // Initialise the mDNS core.
308 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
309 gRRCache
, RR_CACHE_SIZE
,
310 mDNS_Init_DontAdvertiseLocalAddresses
,
311 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
312 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %d\n", (int)status
); return(status
); }
314 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
315 signal(SIGTERM
, HandleSIG
);
317 while (this_arg
< argc
)
319 char *arg
= argv
[this_arg
++];
320 if (this_arg
> 2) printf("\n");
322 lastid
= id
= zeroID
;
323 hostaddr
= target
= zeroAddr
;
324 hostname
[0] = hardware
[0] = software
[0] = 0;
325 NumAddr
= NumAAAA
= NumHINFO
= 0;
327 if (inet_pton(AF_INET
, arg
, &s4
) == 1)
329 mDNSu8
*p
= (mDNSu8
*)&s4
;
330 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
331 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.", p
[3], p
[2], p
[1], p
[0]);
332 printf("%s\n", buffer
);
333 target
.type
= mDNSAddrType_IPv4
;
334 target
.ip
.v4
.NotAnInteger
= s4
.s_addr
;
335 DoQuery(&q
, buffer
, kDNSType_PTR
, &target
, NameCallback
);
336 if (StopNow
== 2) break;
339 else if (inet_pton(AF_INET6
, arg
, &s6
) == 1)
342 mDNSu8
*p
= (mDNSu8
*)&s6
;
343 for (i
= 0; i
< 16; i
++)
345 static const char hexValues
[] = "0123456789ABCDEF";
346 buffer
[i
* 4 ] = hexValues
[p
[15-i
] & 0x0F];
347 buffer
[i
* 4 + 1] = '.';
348 buffer
[i
* 4 + 2] = hexValues
[p
[15-i
] >> 4];
349 buffer
[i
* 4 + 3] = '.';
351 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
352 target
.type
= mDNSAddrType_IPv6
;
353 bcopy(&s6
, &target
.ip
.v6
, sizeof(target
.ip
.v6
));
354 DoQuery(&q
, buffer
, kDNSType_PTR
, &target
, NameCallback
);
355 if (StopNow
== 2) break;
359 strcpy(hostname
, arg
);
361 // Now we have the host name; get its A, AAAA, and HINFO
362 if (hostname
[0]) DoQuery(&q
, hostname
, kDNSQType_ANY
, &target
, InfoCallback
);
363 if (StopNow
== 2) break;
365 if (hardware
[0] || software
[0])
368 printf("HINFO Hardware: %s\n", hardware
);
369 printf("HINFO Software: %s\n", software
);
370 // We need to make sure the services query is targeted
371 if (target
.type
== 0) target
= hostaddr
;
372 StartQuery(&q1
, "_services._dns-sd._udp.local.", kDNSQType_ANY
, &target
, ServicesCallback
);
373 WaitForAnswer(&mDNSStorage
, 4);
374 mDNS_StopQuery(&mDNSStorage
, &q1
);
375 if (StopNow
== 2) break;
379 printf("Host has no HINFO record; Best guess is ");
380 if (id
.b
[1]) printf("mDNSResponder-%d\n", id
.b
[1]);
381 else if (NumAAAA
) printf("very early Panther build (mDNSResponder-33 or earlier)\n");
382 else printf("Jaguar version of mDNSResponder with no IPv6 support\n");
385 printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
388 mDNS_Close(&mDNSStorage
);
392 fprintf(stderr
, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname
);