1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 *
17 * Formatting notes:
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.)
29 */
30
31 //*************************************************************************************************************
32 // Incorporate mDNS.c functionality
33
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
37 #include "mDNS.c"
38 #undef mDNSCoreReceive
39
40 //*************************************************************************************************************
41 // Headers
42
43 #include <unistd.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <errno.h>
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>
52 #include <signal.h>
53
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"
57
58 //*************************************************************************************************************
59 // Globals
60
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";
66
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;
72
73 //*************************************************************************************************************
74 // Utilities
75
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);
mprintf(const char * format,...)78 mDNSlocal mDNSu32 mprintf(const char *format, ...)
79 {
80 mDNSu32 length;
81 unsigned char buffer[512];
82 va_list ptr;
83 va_start(ptr,format);
84 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
85 va_end(ptr);
86 printf("%s", buffer);
87 return(length);
88 }
89
90 //*************************************************************************************************************
91 // Main code
92
mDNSCoreReceive(mDNS * const m,DNSMessage * const msg,const mDNSu8 * const end,const mDNSAddr * const srcaddr,const mDNSIPPort srcport,const mDNSAddr * const dstaddr,const mDNSIPPort dstport,const mDNSInterfaceID InterfaceID)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)
96 {
97 (void)dstaddr; // Unused
98 // Snag copy of header ID, then call through
99 lastid = msg->h.id;
100 lastsrc = *srcaddr;
101
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);
105 }
106
NameCallback(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,QC_result AddRecord)107 mDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
108 {
109 (void)m; // Unused
110 (void)question; // Unused
111 (void)AddRecord;// Unused
112 if (!id.NotAnInteger) id = lastid;
113 if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME)
114 {
115 ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
116 StopNow = 1;
117 mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
118 }
119 }
120
InfoCallback(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,QC_result AddRecord)121 mDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
122 {
123 (void)m; // Unused
124 (void)question; // Unused
125 (void)AddRecord;// Unused
126 if (answer->rrtype == kDNSType_A)
127 {
128 if (!id.NotAnInteger) id = lastid;
129 NumAnswers++;
130 NumAddr++;
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;
134 }
135 else if (answer->rrtype == kDNSType_AAAA)
136 {
137 if (!id.NotAnInteger) id = lastid;
138 NumAnswers++;
139 NumAAAA++;
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
142 {
143 hostaddr.type = mDNSAddrType_IPv6;
144 hostaddr.ip.v6 = answer->rdata->u.ipv6;
145 }
146 }
147 else if (answer->rrtype == kDNSType_HINFO)
148 {
149 mDNSu8 *p = answer->rdata->u.data;
150 strncpy(hardware, (char*)(p+1), p[0]);
151 hardware[p[0]] = 0;
152 p += 1 + p[0];
153 strncpy(software, (char*)(p+1), p[0]);
154 software[p[0]] = 0;
155 NumAnswers++;
156 NumHINFO++;
157 }
158
159 // If we've got everything we're looking for, don't need to wait any more
160 if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1;
161 }
162
ServicesCallback(mDNS * const m,DNSQuestion * question,const ResourceRecord * const answer,QC_result AddRecord)163 mDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
164 {
165 (void)m; // Unused
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))
172 {
173 NumAnswers++;
174 mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
175 }
176 }
177
WaitForAnswer(mDNS * const m,int seconds)178 mDNSlocal void WaitForAnswer(mDNS *const m, int seconds)
179 {
180 struct timeval end;
181 gettimeofday(&end, NULL);
182 end.tv_sec += seconds;
183 StopNow = 0;
184 NumAnswers = 0;
185 while (!StopNow)
186 {
187 int nfds = 0;
188 fd_set readfds;
189 struct timeval now, remain = end;
190 int result;
191
192 FD_ZERO(&readfds);
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)
196 {
197 if (!NumAnswers) printf("No response after %d seconds\n", seconds);
198 return;
199 }
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;
206 }
207 }
208
StartQuery(DNSQuestion * q,char * qname,mDNSu16 qtype,const mDNSAddr * target,mDNSQuestionCallback callback)209 mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
210 {
211 lastsrc = zeroAddr;
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;
217 q->qtype = qtype;
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;
232
233 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
234 return(mDNS_StartQuery(&mDNSStorage, q));
235 }
236
DoOneQuery(DNSQuestion * q,char * qname,mDNSu16 qtype,const mDNSAddr * target,mDNSQuestionCallback callback)237 mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
238 {
239 mStatus status = StartQuery(q, qname, qtype, target, callback);
240 if (status != mStatus_NoError)
241 StopNow = 2;
242 else
243 {
244 WaitForAnswer(&mDNSStorage, 4);
245 mDNS_StopQuery(&mDNSStorage, q);
246 }
247 }
248
DoQuery(DNSQuestion * q,char * qname,mDNSu16 qtype,const mDNSAddr * target,mDNSQuestionCallback callback)249 mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
250 {
251 DoOneQuery(q, qname, qtype, target, callback);
252 if (StopNow == 0 && NumAnswers == 0 && target && target->type)
253 {
254 mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype));
255 DoOneQuery(q, qname, qtype, NULL, callback);
256 }
257 if (StopNow == 0 && NumAnswers == 0)
258 mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype));
259 return(StopNow);
260 }
261
HandleSIG(int signal)262 mDNSlocal void HandleSIG(int signal)
263 {
264 (void)signal; // Unused
265 debugf("%s","");
266 debugf("HandleSIG");
267 StopNow = 2;
268 }
269
main(int argc,char ** argv)270 mDNSexport int main(int argc, char **argv)
271 {
272 const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
273 int this_arg = 1;
274 mStatus status;
275 struct in_addr s4;
276 #if HAVE_IPV6
277 struct in6_addr s6;
278 #endif
279 char buffer[256];
280 DNSQuestion q;
281
282 if (argc < 2) goto usage;
283
284 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
285 mDNS_DebugMode = mDNStrue;
286
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); }
293
294 signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
295 signal(SIGTERM, HandleSIG);
296
297 while (this_arg < argc)
298 {
299 char *arg = argv[this_arg++];
300 if (this_arg > 2) printf("\n");
301
302 lastid = id = zeroID;
303 hostaddr = target = zeroAddr;
304 hostname[0] = hardware[0] = software[0] = 0;
305 NumAddr = NumAAAA = NumHINFO = 0;
306
307 if (inet_pton(AF_INET, arg, &s4) == 1)
308 {
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;
317 }
318 #if HAVE_IPV6
319 else if (inet_pton(AF_INET6, arg, &s6) == 1)
320 {
321 int i;
322 mDNSu8 *p = (mDNSu8 *)&s6;
323 for (i = 0; i < 16; i++)
324 {
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] = '.';
330 }
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;
336 }
337 #endif
338 else {
339 if (strlen(arg) >= sizeof(hostname)) {
340 fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname));
341 goto usage;
342 }
343 strcpy(hostname, arg);
344 }
345
346 // Now we have the host name; get its A, AAAA, and HINFO
347 if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback);
348 if (StopNow == 2) break;
349
350 if (hardware[0] || software[0])
351 {
352 printf("HINFO Hardware: %s\n", hardware);
353 printf("HINFO Software: %s\n", software);
354 }
355 else if (NumAnswers) printf("%s has no HINFO record\n", hostname);
356 else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
357
358 if (NumAnswers)
359 {
360 // Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers
361 mDNS *const m = &mDNSStorage;
362 mDNSu32 slot;
363 CacheGroup *cg;
364 CacheRecord *rr;
365 FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
366 if (target.type == 0) target = hostaddr; // Make sure the services query is targeted
367 DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback);
368 if (StopNow == 2) break;
369 }
370 }
371
372 mDNS_Close(&mDNSStorage);
373 return(0);
374
375 usage:
376 fprintf(stderr, "Usage: %s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname);
377 return(-1);
378 }
379