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.)
31 //*************************************************************************************************************
32 // Incorporate mDNS.c functionality
34 // We want to use the functionality provided by "mDNS.c",
35 // except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
36 #define mDNSCoreReceive __MDNS__mDNSCoreReceive
38 #undef mDNSCoreReceive
40 //*************************************************************************************************************
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
50 #include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
51 #include <arpa/inet.h>
54 #include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
55 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
56 #include "ExampleClientApp.h"
58 //*************************************************************************************************************
61 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
62 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
63 #define RR_CACHE_SIZE 500
64 static CacheEntity gRRCache
[RR_CACHE_SIZE
];
65 mDNSexport
const char ProgramName
[] = "mDNSIdentify";
67 static volatile int StopNow
; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
68 static volatile int NumAnswers
, NumAddr
, NumAAAA
, NumHINFO
;
69 static char hostname
[MAX_ESCAPED_DOMAIN_NAME
], hardware
[256], software
[256];
70 static mDNSAddr lastsrc
, hostaddr
, target
;
71 static mDNSOpaque16 lastid
, id
;
73 //*************************************************************************************************************
76 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
77 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
78 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
81 unsigned char buffer
[512];
84 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
90 //*************************************************************************************************************
93 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
94 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*const dstaddr
, const mDNSIPPort dstport
,
95 const mDNSInterfaceID InterfaceID
)
97 (void)dstaddr
; // Unused
98 // Snag copy of header ID, then call through
102 // We *want* to allow off-net unicast responses here.
103 // For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
104 __MDNS__mDNSCoreReceive(m
, msg
, end
, srcaddr
, srcport
, &AllDNSLinkGroup_v4
, dstport
, InterfaceID
);
107 mDNSlocal
void NameCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
110 (void)question
; // Unused
111 (void)AddRecord
;// Unused
112 if (!id
.NotAnInteger
) id
= lastid
;
113 if (answer
->rrtype
== kDNSType_PTR
|| answer
->rrtype
== kDNSType_CNAME
)
115 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, hostname
);
117 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
121 mDNSlocal
void InfoCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
124 (void)question
; // Unused
125 (void)AddRecord
;// Unused
126 if (answer
->rrtype
== kDNSType_A
)
128 if (!id
.NotAnInteger
) id
= lastid
;
131 mprintf("%##s %s %.4a\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv4
);
132 hostaddr
.type
= mDNSAddrType_IPv4
; // Prefer v4 target to v6 target, for now
133 hostaddr
.ip
.v4
= answer
->rdata
->u
.ipv4
;
135 else if (answer
->rrtype
== kDNSType_AAAA
)
137 if (!id
.NotAnInteger
) id
= lastid
;
140 mprintf("%##s %s %.16a\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), &answer
->rdata
->u
.ipv6
);
141 if (!hostaddr
.type
) // Prefer v4 target to v6 target, for now
143 hostaddr
.type
= mDNSAddrType_IPv6
;
144 hostaddr
.ip
.v6
= answer
->rdata
->u
.ipv6
;
147 else if (answer
->rrtype
== kDNSType_HINFO
)
149 mDNSu8
*p
= answer
->rdata
->u
.data
;
150 strncpy(hardware
, (char*)(p
+1), p
[0]);
153 strncpy(software
, (char*)(p
+1), p
[0]);
159 // If we've got everything we're looking for, don't need to wait any more
160 if (/*NumHINFO && */ (NumAddr
|| NumAAAA
)) StopNow
= 1;
163 mDNSlocal
void ServicesCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
166 (void)question
; // Unused
167 (void)AddRecord
;// Unused
168 // Right now the mDNSCore targeted-query code is incomplete --
169 // it issues targeted queries, but accepts answers from anywhere
170 // For now, we'll just filter responses here so we don't get confused by responses from someone else
171 if (answer
->rrtype
== kDNSType_PTR
&& mDNSSameAddress(&lastsrc
, &target
))
174 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
178 mDNSlocal
void WaitForAnswer(mDNS
*const m
, int seconds
)
181 gettimeofday(&end
, NULL
);
182 end
.tv_sec
+= seconds
;
189 struct timeval now
, remain
= end
;
193 gettimeofday(&now
, NULL
);
194 if (remain
.tv_usec
< now
.tv_usec
) { remain
.tv_usec
+= 1000000; remain
.tv_sec
--; }
195 if (remain
.tv_sec
< now
.tv_sec
)
197 if (!NumAnswers
) printf("No response after %d seconds\n", seconds
);
200 remain
.tv_usec
-= now
.tv_usec
;
201 remain
.tv_sec
-= now
.tv_sec
;
202 mDNSPosixGetFDSet(m
, &nfds
, &readfds
, &remain
);
203 result
= select(nfds
, &readfds
, NULL
, NULL
, &remain
);
204 if (result
>= 0) mDNSPosixProcessFDSet(m
, &readfds
);
205 else if (errno
!= EINTR
) StopNow
= 2;
209 mDNSlocal mStatus
StartQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
212 if (qname
) MakeDomainNameFromDNSNameString(&q
->qname
, qname
);
213 q
->InterfaceID
= mDNSInterface_Any
;
214 q
->Target
= target
? *target
: zeroAddr
;
215 q
->TargetPort
= MulticastDNSPort
;
216 q
->TargetQID
= zeroID
;
218 q
->qclass
= kDNSClass_IN
;
219 q
->LongLived
= mDNSfalse
;
220 q
->ExpectUnique
= mDNSfalse
; // Don't want to stop after the first response packet
221 q
->ForceMCast
= mDNStrue
; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
222 q
->ReturnIntermed
= mDNStrue
;
223 q
->SuppressUnusable
= mDNSfalse
;
224 q
->SearchListIndex
= 0;
225 q
->AppendSearchDomains
= 0;
226 q
->RetryWithSearchDomains
= mDNSfalse
;
227 q
->TimeoutQuestion
= 0;
228 q
->WakeOnResolve
= 0;
229 q
->qnameOrig
= mDNSNULL
;
230 q
->QuestionCallback
= callback
;
231 q
->QuestionContext
= NULL
;
233 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
234 return(mDNS_StartQuery(&mDNSStorage
, q
));
237 mDNSlocal
void DoOneQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
239 mStatus status
= StartQuery(q
, qname
, qtype
, target
, callback
);
240 if (status
!= mStatus_NoError
)
244 WaitForAnswer(&mDNSStorage
, 4);
245 mDNS_StopQuery(&mDNSStorage
, q
);
249 mDNSlocal
int DoQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
251 DoOneQuery(q
, qname
, qtype
, target
, callback
);
252 if (StopNow
== 0 && NumAnswers
== 0 && target
&& target
->type
)
254 mprintf("%##s %s Trying multicast\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
255 DoOneQuery(q
, qname
, qtype
, NULL
, callback
);
257 if (StopNow
== 0 && NumAnswers
== 0)
258 mprintf("%##s %s *** No Answer ***\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
262 mDNSlocal
void HandleSIG(int signal
)
264 (void)signal
; // Unused
270 mDNSexport
int main(int argc
, char **argv
)
272 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
282 if (argc
< 2) goto usage
;
284 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
285 mDNS_DebugMode
= mDNStrue
;
287 // Initialise the mDNS core.
288 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
289 gRRCache
, RR_CACHE_SIZE
,
290 mDNS_Init_DontAdvertiseLocalAddresses
,
291 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
292 if (status
) { fprintf(stderr
, "Daemon start: mDNS_Init failed %d\n", (int)status
); return(status
); }
294 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
295 signal(SIGTERM
, HandleSIG
);
297 while (this_arg
< argc
)
299 char *arg
= argv
[this_arg
++];
300 if (this_arg
> 2) printf("\n");
302 lastid
= id
= zeroID
;
303 hostaddr
= target
= zeroAddr
;
304 hostname
[0] = hardware
[0] = software
[0] = 0;
305 NumAddr
= NumAAAA
= NumHINFO
= 0;
307 if (inet_pton(AF_INET
, arg
, &s4
) == 1)
309 mDNSu8
*p
= (mDNSu8
*)&s4
;
310 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
311 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.", p
[3], p
[2], p
[1], p
[0]);
312 printf("%s\n", buffer
);
313 target
.type
= mDNSAddrType_IPv4
;
314 target
.ip
.v4
.NotAnInteger
= s4
.s_addr
;
315 DoQuery(&q
, buffer
, kDNSType_PTR
, &target
, NameCallback
);
316 if (StopNow
== 2) break;
319 else if (inet_pton(AF_INET6
, arg
, &s6
) == 1)
322 mDNSu8
*p
= (mDNSu8
*)&s6
;
323 for (i
= 0; i
< 16; i
++)
325 static const char hexValues
[] = "0123456789ABCDEF";
326 buffer
[i
* 4 ] = hexValues
[p
[15-i
] & 0x0F];
327 buffer
[i
* 4 + 1] = '.';
328 buffer
[i
* 4 + 2] = hexValues
[p
[15-i
] >> 4];
329 buffer
[i
* 4 + 3] = '.';
331 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
332 target
.type
= mDNSAddrType_IPv6
;
333 mDNSPlatformMemCopy(&target
.ip
.v6
, &s6
, sizeof(target
.ip
.v6
));
334 DoQuery(&q
, buffer
, kDNSType_PTR
, &target
, NameCallback
);
335 if (StopNow
== 2) break;
339 strcpy(hostname
, arg
);
341 // Now we have the host name; get its A, AAAA, and HINFO
342 if (hostname
[0]) DoQuery(&q
, hostname
, kDNSQType_ANY
, &target
, InfoCallback
);
343 if (StopNow
== 2) break;
345 if (hardware
[0] || software
[0])
347 printf("HINFO Hardware: %s\n", hardware
);
348 printf("HINFO Software: %s\n", software
);
350 else if (NumAnswers
) printf("%s has no HINFO record\n", hostname
);
351 else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
355 // Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers
356 mDNS
*const m
= &mDNSStorage
;
360 FORALL_CACHERECORDS(slot
, cg
, rr
) mDNS_PurgeCacheResourceRecord(m
, rr
);
361 if (target
.type
== 0) target
= hostaddr
; // Make sure the services query is targeted
362 DoQuery(&q
, "_services._dns-sd._udp.local.", kDNSType_PTR
, &target
, ServicesCallback
);
363 if (StopNow
== 2) break;
367 mDNS_Close(&mDNSStorage
);
371 fprintf(stderr
, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname
);