]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/Identify.c
mDNSResponder-522.1.11.tar.gz
[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 */
18
19 //*************************************************************************************************************
20 // Incorporate mDNS.c functionality
21
22 // We want to use the functionality provided by "mDNS.c",
23 // except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
24 #define mDNSCoreReceive __MDNS__mDNSCoreReceive
25 #include "mDNS.c"
26 #undef mDNSCoreReceive
27
28 //*************************************************************************************************************
29 // Headers
30
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below
38 #include <netinet/ip.h> // For IPTOS_LOWDELAY etc.
39 #include <arpa/inet.h>
40 #include <signal.h>
41
42 #include "mDNSEmbeddedAPI.h" // Defines the interface to the mDNS core code
43 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
44 #include "ExampleClientApp.h"
45
46 //*************************************************************************************************************
47 // Globals
48
49 static mDNS mDNSStorage; // mDNS core uses this to store its globals
50 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
51 #define RR_CACHE_SIZE 500
52 static CacheEntity gRRCache[RR_CACHE_SIZE];
53 mDNSexport const char ProgramName[] = "mDNSIdentify";
54
55 static volatile int StopNow; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
56 static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO;
57 static char hostname[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256];
58 static mDNSAddr lastsrc, hostaddr, target;
59 static mDNSOpaque16 lastid, id;
60
61 //*************************************************************************************************************
62 // Utilities
63
64 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
65 mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
66 mDNSlocal mDNSu32 mprintf(const char *format, ...)
67 {
68 mDNSu32 length;
69 unsigned char buffer[512];
70 va_list ptr;
71 va_start(ptr,format);
72 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
73 va_end(ptr);
74 printf("%s", buffer);
75 return(length);
76 }
77
78 //*************************************************************************************************************
79 // Main code
80
81 mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
82 const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
83 const mDNSInterfaceID InterfaceID)
84 {
85 (void)dstaddr; // Unused
86 // Snag copy of header ID, then call through
87 lastid = msg->h.id;
88 lastsrc = *srcaddr;
89
90 // We *want* to allow off-net unicast responses here.
91 // For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
92 __MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, &AllDNSLinkGroup_v4, dstport, InterfaceID);
93 }
94
95 mDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
96 {
97 (void)m; // Unused
98 (void)question; // Unused
99 (void)AddRecord; // Unused
100 if (!id.NotAnInteger) id = lastid;
101 if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME)
102 {
103 ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
104 StopNow = 1;
105 mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
106 }
107 }
108
109 mDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
110 {
111 (void)m; // Unused
112 (void)question; // Unused
113 (void)AddRecord; // Unused
114 if (answer->rrtype == kDNSType_A)
115 {
116 if (!id.NotAnInteger) id = lastid;
117 NumAnswers++;
118 NumAddr++;
119 mprintf("%##s %s %.4a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4);
120 hostaddr.type = mDNSAddrType_IPv4; // Prefer v4 target to v6 target, for now
121 hostaddr.ip.v4 = answer->rdata->u.ipv4;
122 }
123 else if (answer->rrtype == kDNSType_AAAA)
124 {
125 if (!id.NotAnInteger) id = lastid;
126 NumAnswers++;
127 NumAAAA++;
128 mprintf("%##s %s %.16a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6);
129 if (!hostaddr.type) // Prefer v4 target to v6 target, for now
130 {
131 hostaddr.type = mDNSAddrType_IPv6;
132 hostaddr.ip.v6 = answer->rdata->u.ipv6;
133 }
134 }
135 else if (answer->rrtype == kDNSType_HINFO)
136 {
137 mDNSu8 *p = answer->rdata->u.data;
138 strncpy(hardware, (char*)(p+1), p[0]);
139 hardware[p[0]] = 0;
140 p += 1 + p[0];
141 strncpy(software, (char*)(p+1), p[0]);
142 software[p[0]] = 0;
143 NumAnswers++;
144 NumHINFO++;
145 }
146
147 // If we've got everything we're looking for, don't need to wait any more
148 if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1;
149 }
150
151 mDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
152 {
153 (void)m; // Unused
154 (void)question; // Unused
155 (void)AddRecord; // Unused
156 // Right now the mDNSCore targeted-query code is incomplete --
157 // it issues targeted queries, but accepts answers from anywhere
158 // For now, we'll just filter responses here so we don't get confused by responses from someone else
159 if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target))
160 {
161 NumAnswers++;
162 mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
163 }
164 }
165
166 mDNSlocal void WaitForAnswer(mDNS *const m, int seconds)
167 {
168 struct timeval end;
169 gettimeofday(&end, NULL);
170 end.tv_sec += seconds;
171 StopNow = 0;
172 NumAnswers = 0;
173 while (!StopNow)
174 {
175 int nfds = 0;
176 fd_set readfds;
177 struct timeval now, remain = end;
178 int result;
179
180 FD_ZERO(&readfds);
181 gettimeofday(&now, NULL);
182 if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; }
183 if (remain.tv_sec < now.tv_sec)
184 {
185 if (!NumAnswers) printf("No response after %d seconds\n", seconds);
186 return;
187 }
188 remain.tv_usec -= now.tv_usec;
189 remain.tv_sec -= now.tv_sec;
190 mDNSPosixGetFDSet(m, &nfds, &readfds, &remain);
191 result = select(nfds, &readfds, NULL, NULL, &remain);
192 if (result >= 0) mDNSPosixProcessFDSet(m, &readfds);
193 else if (errno != EINTR) StopNow = 2;
194 }
195 }
196
197 mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
198 {
199 lastsrc = zeroAddr;
200 if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname);
201 q->InterfaceID = mDNSInterface_Any;
202 q->flags = 0;
203 q->Target = target ? *target : zeroAddr;
204 q->TargetPort = MulticastDNSPort;
205 q->TargetQID = zeroID;
206 q->qtype = qtype;
207 q->qclass = kDNSClass_IN;
208 q->LongLived = mDNSfalse;
209 q->ExpectUnique = mDNSfalse; // Don't want to stop after the first response packet
210 q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
211 q->ReturnIntermed = mDNStrue;
212 q->SuppressUnusable = mDNSfalse;
213 q->SearchListIndex = 0;
214 q->AppendSearchDomains = 0;
215 q->RetryWithSearchDomains = mDNSfalse;
216 q->TimeoutQuestion = 0;
217 q->ValidationRequired = 0;
218 q->ValidatingResponse = 0;
219 q->WakeOnResolve = 0;
220 q->UseBackgroundTrafficClass = mDNSfalse;
221 q->ProxyQuestion = 0;
222 q->qnameOrig = mDNSNULL;
223 q->AnonInfo = mDNSNULL;
224 q->pid = mDNSPlatformGetPID();
225 q->QuestionCallback = callback;
226 q->QuestionContext = NULL;
227
228 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
229 return(mDNS_StartQuery(&mDNSStorage, q));
230 }
231
232 mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
233 {
234 mStatus status = StartQuery(q, qname, qtype, target, callback);
235 if (status != mStatus_NoError)
236 StopNow = 2;
237 else
238 {
239 WaitForAnswer(&mDNSStorage, 4);
240 mDNS_StopQuery(&mDNSStorage, q);
241 }
242 }
243
244 mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
245 {
246 DoOneQuery(q, qname, qtype, target, callback);
247 if (StopNow == 0 && NumAnswers == 0 && target && target->type)
248 {
249 mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype));
250 DoOneQuery(q, qname, qtype, NULL, callback);
251 }
252 if (StopNow == 0 && NumAnswers == 0)
253 mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype));
254 return(StopNow);
255 }
256
257 mDNSlocal void HandleSIG(int signal)
258 {
259 (void)signal; // Unused
260 debugf("%s","");
261 debugf("HandleSIG");
262 StopNow = 2;
263 }
264
265 mDNSexport int main(int argc, char **argv)
266 {
267 const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
268 int this_arg = 1;
269 mStatus status;
270 struct in_addr s4;
271 #if HAVE_IPV6
272 struct in6_addr s6;
273 #endif
274 char buffer[256];
275 DNSQuestion q;
276
277 if (argc < 2) goto usage;
278
279 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
280 mDNS_DebugMode = mDNStrue;
281
282 // Initialise the mDNS core.
283 status = mDNS_Init(&mDNSStorage, &PlatformStorage,
284 gRRCache, RR_CACHE_SIZE,
285 mDNS_Init_DontAdvertiseLocalAddresses,
286 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
287 if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
288
289 signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C
290 signal(SIGTERM, HandleSIG);
291
292 while (this_arg < argc)
293 {
294 char *arg = argv[this_arg++];
295 if (this_arg > 2) printf("\n");
296
297 lastid = id = zeroID;
298 hostaddr = target = zeroAddr;
299 hostname[0] = hardware[0] = software[0] = 0;
300 NumAddr = NumAAAA = NumHINFO = 0;
301
302 if (inet_pton(AF_INET, arg, &s4) == 1)
303 {
304 mDNSu8 *p = (mDNSu8 *)&s4;
305 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
306 mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]);
307 printf("%s\n", buffer);
308 target.type = mDNSAddrType_IPv4;
309 target.ip.v4.NotAnInteger = s4.s_addr;
310 DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
311 if (StopNow == 2) break;
312 }
313 #if HAVE_IPV6
314 else if (inet_pton(AF_INET6, arg, &s6) == 1)
315 {
316 int i;
317 mDNSu8 *p = (mDNSu8 *)&s6;
318 for (i = 0; i < 16; i++)
319 {
320 static const char hexValues[] = "0123456789ABCDEF";
321 buffer[i * 4 ] = hexValues[p[15-i] & 0x0F];
322 buffer[i * 4 + 1] = '.';
323 buffer[i * 4 + 2] = hexValues[p[15-i] >> 4];
324 buffer[i * 4 + 3] = '.';
325 }
326 mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
327 target.type = mDNSAddrType_IPv6;
328 mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6));
329 DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
330 if (StopNow == 2) break;
331 }
332 #endif
333 else {
334 if (strlen(arg) >= sizeof(hostname)) {
335 fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname));
336 goto usage;
337 }
338 strcpy(hostname, arg);
339 }
340
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;
344
345 if (hardware[0] || software[0])
346 {
347 printf("HINFO Hardware: %s\n", hardware);
348 printf("HINFO Software: %s\n", software);
349 }
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");
352
353 if (NumAnswers)
354 {
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;
357 mDNSu32 slot;
358 CacheGroup *cg;
359 CacheRecord *rr;
360 FORALL_CACHERECORDS(slot, cg, rr)
361 {
362 mDNS_PurgeCacheResourceRecord(m, rr);
363 }
364 if (target.type == 0) target = hostaddr; // Make sure the services query is targeted
365 DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback);
366 if (StopNow == 2) break;
367 }
368 }
369
370 mDNS_Close(&mDNSStorage);
371 return(0);
372
373 usage:
374 fprintf(stderr, "Usage: %s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname);
375 return(-1);
376 }