]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/Identify.c
c2a033fe8592fa7a2d6699ce45451499eb79cbfa
[apple/mdnsresponder.git] / mDNSPosix / Identify.c
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 Change History (most recent first):
31
32 $Log: Identify.c,v $
33 Revision 1.44 2009/01/13 05:31:34 mkrochma
34 <rdar://problem/6491367> Replace bzero, bcopy with mDNSPlatformMemZero, mDNSPlatformMemCopy, memset, memcpy
35
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
38
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
42
43 Revision 1.41 2007/04/16 20:49:39 cheshire
44 Fix compile errors for mDNSPosix build
45
46 Revision 1.40 2007/02/28 01:51:22 cheshire
47 Added comment about reverse-order IP address
48
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)
52
53 Revision 1.38 2007/01/04 20:57:48 cheshire
54 Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates)
55
56 Revision 1.37 2006/10/27 01:32:08 cheshire
57 Set ReturnIntermed to mDNStrue
58
59 Revision 1.36 2006/08/14 23:24:46 cheshire
60 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
61
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
64
65 */
66
67 //*************************************************************************************************************
68 // Incorporate mDNS.c functionality
69
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
73 #include "mDNS.c"
74 #undef mDNSCoreReceive
75
76 //*************************************************************************************************************
77 // Headers
78
79 #include <unistd.h>
80 #include <stdio.h>
81 #include <string.h>
82 #include <errno.h>
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>
88 #include <signal.h>
89
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"
93
94 //*************************************************************************************************************
95 // Globals
96
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";
102
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;
108
109 //*************************************************************************************************************
110 // Utilities
111
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, ...)
115 {
116 mDNSu32 length;
117 unsigned char buffer[512];
118 va_list ptr;
119 va_start(ptr,format);
120 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
121 va_end(ptr);
122 printf("%s", buffer);
123 return(length);
124 }
125
126 //*************************************************************************************************************
127 // Main code
128
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)
132 {
133 (void)dstaddr; // Unused
134 // Snag copy of header ID, then call through
135 lastid = msg->h.id;
136 lastsrc = *srcaddr;
137
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);
141 }
142
143 mDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
144 {
145 (void)m; // Unused
146 (void)question; // Unused
147 (void)AddRecord;// Unused
148 if (!id.NotAnInteger) id = lastid;
149 if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME)
150 {
151 ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
152 StopNow = 1;
153 mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
154 }
155 }
156
157 mDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
158 {
159 (void)m; // Unused
160 (void)question; // Unused
161 (void)AddRecord;// Unused
162 if (answer->rrtype == kDNSType_A)
163 {
164 if (!id.NotAnInteger) id = lastid;
165 NumAnswers++;
166 NumAddr++;
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;
170 }
171 else if (answer->rrtype == kDNSType_AAAA)
172 {
173 if (!id.NotAnInteger) id = lastid;
174 NumAnswers++;
175 NumAAAA++;
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
178 {
179 hostaddr.type = mDNSAddrType_IPv6;
180 hostaddr.ip.v6 = answer->rdata->u.ipv6;
181 }
182 }
183 else if (answer->rrtype == kDNSType_HINFO)
184 {
185 mDNSu8 *p = answer->rdata->u.data;
186 strncpy(hardware, (char*)(p+1), p[0]);
187 hardware[p[0]] = 0;
188 p += 1 + p[0];
189 strncpy(software, (char*)(p+1), p[0]);
190 software[p[0]] = 0;
191 NumAnswers++;
192 NumHINFO++;
193 }
194
195 // If we've got everything we're looking for, don't need to wait any more
196 if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1;
197 }
198
199 mDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
200 {
201 (void)m; // Unused
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))
208 {
209 NumAnswers++;
210 mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
211 }
212 }
213
214 mDNSlocal void WaitForAnswer(mDNS *const m, int seconds)
215 {
216 struct timeval end;
217 gettimeofday(&end, NULL);
218 end.tv_sec += seconds;
219 StopNow = 0;
220 NumAnswers = 0;
221 while (!StopNow)
222 {
223 int nfds = 0;
224 fd_set readfds;
225 struct timeval now, remain = end;
226 int result;
227
228 FD_ZERO(&readfds);
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)
232 {
233 if (!NumAnswers) printf("No response after %d seconds\n", seconds);
234 return;
235 }
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;
242 }
243 }
244
245 mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
246 {
247 lastsrc = zeroAddr;
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;
253 q->qtype = qtype;
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;
261
262 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
263 return(mDNS_StartQuery(&mDNSStorage, q));
264 }
265
266 mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
267 {
268 mStatus status = StartQuery(q, qname, qtype, target, callback);
269 if (status != mStatus_NoError)
270 StopNow = 2;
271 else
272 {
273 WaitForAnswer(&mDNSStorage, 4);
274 mDNS_StopQuery(&mDNSStorage, q);
275 }
276 }
277
278 mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
279 {
280 DoOneQuery(q, qname, qtype, target, callback);
281 if (StopNow == 0 && NumAnswers == 0 && target && target->type)
282 {
283 mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype));
284 DoOneQuery(q, qname, qtype, NULL, callback);
285 }
286 if (StopNow == 0 && NumAnswers == 0)
287 mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype));
288 return(StopNow);
289 }
290
291 mDNSlocal void HandleSIG(int signal)
292 {
293 (void)signal; // Unused
294 debugf("%s","");
295 debugf("HandleSIG");
296 StopNow = 2;
297 }
298
299 mDNSexport int main(int argc, char **argv)
300 {
301 const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
302 int this_arg = 1;
303 mStatus status;
304 struct in_addr s4;
305 #if HAVE_IPV6
306 struct in6_addr s6;
307 #endif
308 char buffer[256];
309 DNSQuestion q;
310
311 if (argc < 2) goto usage;
312
313 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
314 mDNS_DebugMode = mDNStrue;
315
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); }
322
323 signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
324 signal(SIGTERM, HandleSIG);
325
326 while (this_arg < argc)
327 {
328 char *arg = argv[this_arg++];
329 if (this_arg > 2) printf("\n");
330
331 lastid = id = zeroID;
332 hostaddr = target = zeroAddr;
333 hostname[0] = hardware[0] = software[0] = 0;
334 NumAddr = NumAAAA = NumHINFO = 0;
335
336 if (inet_pton(AF_INET, arg, &s4) == 1)
337 {
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;
346 }
347 #if HAVE_IPV6
348 else if (inet_pton(AF_INET6, arg, &s6) == 1)
349 {
350 int i;
351 mDNSu8 *p = (mDNSu8 *)&s6;
352 for (i = 0; i < 16; i++)
353 {
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] = '.';
359 }
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;
365 }
366 #endif
367 else
368 strcpy(hostname, arg);
369
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;
373
374 if (hardware[0] || software[0])
375 {
376 printf("HINFO Hardware: %s\n", hardware);
377 printf("HINFO Software: %s\n", software);
378 }
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");
381
382 if (NumAnswers)
383 {
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;
386 mDNSu32 slot;
387 CacheGroup *cg;
388 CacheRecord *rr;
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;
393 }
394 }
395
396 mDNS_Close(&mDNSStorage);
397 return(0);
398
399 usage:
400 fprintf(stderr, "%s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname);
401 return(-1);
402 }