]>
Commit | Line | Data |
---|---|---|
7f0064bd A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
f0cc3e7b | 3 | * Copyright (c) 2002-2018 Apple Inc. All rights reserved. |
c9b9ae52 | 4 | * |
67c8f8a1 A |
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 | |
83fb1e36 | 8 | * |
67c8f8a1 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
83fb1e36 | 10 | * |
67c8f8a1 A |
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 | |
c9b9ae52 | 15 | * limitations under the License. |
c9b9ae52 | 16 | * |
263eeeab | 17 | */ |
c9b9ae52 A |
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> | |
83fb1e36 A |
37 | #include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below |
38 | #include <netinet/ip.h> // For IPTOS_LOWDELAY etc. | |
c9b9ae52 A |
39 | #include <arpa/inet.h> |
40 | #include <signal.h> | |
41 | ||
83fb1e36 | 42 | #include "mDNSEmbeddedAPI.h" // Defines the interface to the mDNS core code |
c9b9ae52 A |
43 | #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform |
44 | #include "ExampleClientApp.h" | |
45 | ||
46 | //************************************************************************************************************* | |
47 | // Globals | |
48 | ||
f0cc3e7b | 49 | mDNS mDNSStorage; // mDNS core uses this to store its globals |
c9b9ae52 A |
50 | static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals |
51 | #define RR_CACHE_SIZE 500 | |
283ee3ff | 52 | static CacheEntity gRRCache[RR_CACHE_SIZE]; |
67c8f8a1 | 53 | mDNSexport const char ProgramName[] = "mDNSIdentify"; |
c9b9ae52 | 54 | |
83fb1e36 | 55 | static volatile int StopNow; // 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C |
c9b9ae52 | 56 | static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO; |
716635cc | 57 | static char hostname[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256]; |
8e92c31c | 58 | static mDNSAddr lastsrc, hostaddr, target; |
c9b9ae52 A |
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, ...) | |
83fb1e36 A |
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 | } | |
c9b9ae52 A |
77 | |
78 | //************************************************************************************************************* | |
79 | // Main code | |
80 | ||
81 | mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, | |
83fb1e36 A |
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 | } | |
c9b9ae52 | 94 | |
32bb7e43 | 95 | mDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) |
83fb1e36 A |
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 | } | |
c9b9ae52 | 108 | |
32bb7e43 | 109 | mDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) |
83fb1e36 A |
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 | } | |
8e92c31c | 150 | |
32bb7e43 | 151 | mDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) |
83fb1e36 A |
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 | } | |
c9b9ae52 | 165 | |
32bb7e43 | 166 | mDNSlocal void WaitForAnswer(mDNS *const m, int seconds) |
83fb1e36 A |
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; | |
f0cc3e7b | 176 | fd_set readfds, writefds; |
83fb1e36 A |
177 | struct timeval now, remain = end; |
178 | int result; | |
179 | ||
180 | FD_ZERO(&readfds); | |
f0cc3e7b | 181 | FD_ZERO(&writefds); |
83fb1e36 A |
182 | gettimeofday(&now, NULL); |
183 | if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; } | |
184 | if (remain.tv_sec < now.tv_sec) | |
185 | { | |
186 | if (!NumAnswers) printf("No response after %d seconds\n", seconds); | |
187 | return; | |
188 | } | |
189 | remain.tv_usec -= now.tv_usec; | |
190 | remain.tv_sec -= now.tv_sec; | |
f0cc3e7b A |
191 | mDNSPosixGetFDSet(m, &nfds, &readfds, &writefds, &remain); |
192 | result = select(nfds, &readfds, &writefds, NULL, &remain); | |
193 | if (result >= 0) mDNSPosixProcessFDSet(m, &readfds, &writefds); | |
83fb1e36 A |
194 | else if (errno != EINTR) StopNow = 2; |
195 | } | |
196 | } | |
c9b9ae52 | 197 | |
8e92c31c | 198 | mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) |
83fb1e36 A |
199 | { |
200 | lastsrc = zeroAddr; | |
201 | if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname); | |
202 | q->InterfaceID = mDNSInterface_Any; | |
203 | q->flags = 0; | |
204 | q->Target = target ? *target : zeroAddr; | |
205 | q->TargetPort = MulticastDNSPort; | |
206 | q->TargetQID = zeroID; | |
207 | q->qtype = qtype; | |
208 | q->qclass = kDNSClass_IN; | |
209 | q->LongLived = mDNSfalse; | |
210 | q->ExpectUnique = mDNSfalse; // Don't want to stop after the first response packet | |
211 | q->ForceMCast = mDNStrue; // Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa. | |
212 | q->ReturnIntermed = mDNStrue; | |
213 | q->SuppressUnusable = mDNSfalse; | |
83fb1e36 | 214 | q->AppendSearchDomains = 0; |
83fb1e36 A |
215 | q->TimeoutQuestion = 0; |
216 | q->ValidationRequired = 0; | |
217 | q->ValidatingResponse = 0; | |
218 | q->WakeOnResolve = 0; | |
f0cc3e7b | 219 | q->UseBackgroundTraffic = mDNSfalse; |
51601d48 | 220 | q->ProxyQuestion = 0; |
51601d48 | 221 | q->pid = mDNSPlatformGetPID(); |
83fb1e36 A |
222 | q->QuestionCallback = callback; |
223 | q->QuestionContext = NULL; | |
224 | ||
225 | //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype)); | |
226 | return(mDNS_StartQuery(&mDNSStorage, q)); | |
227 | } | |
c9b9ae52 | 228 | |
8e92c31c | 229 | mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) |
83fb1e36 A |
230 | { |
231 | mStatus status = StartQuery(q, qname, qtype, target, callback); | |
232 | if (status != mStatus_NoError) | |
233 | StopNow = 2; | |
234 | else | |
235 | { | |
236 | WaitForAnswer(&mDNSStorage, 4); | |
237 | mDNS_StopQuery(&mDNSStorage, q); | |
238 | } | |
239 | } | |
8e92c31c A |
240 | |
241 | mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback) | |
83fb1e36 A |
242 | { |
243 | DoOneQuery(q, qname, qtype, target, callback); | |
244 | if (StopNow == 0 && NumAnswers == 0 && target && target->type) | |
245 | { | |
246 | mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype)); | |
247 | DoOneQuery(q, qname, qtype, NULL, callback); | |
248 | } | |
249 | if (StopNow == 0 && NumAnswers == 0) | |
250 | mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype)); | |
251 | return(StopNow); | |
252 | } | |
c9b9ae52 A |
253 | |
254 | mDNSlocal void HandleSIG(int signal) | |
83fb1e36 A |
255 | { |
256 | (void)signal; // Unused | |
257 | debugf("%s",""); | |
258 | debugf("HandleSIG"); | |
259 | StopNow = 2; | |
260 | } | |
c9b9ae52 A |
261 | |
262 | mDNSexport int main(int argc, char **argv) | |
83fb1e36 A |
263 | { |
264 | const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; | |
265 | int this_arg = 1; | |
266 | mStatus status; | |
267 | struct in_addr s4; | |
7f0064bd | 268 | #if HAVE_IPV6 |
83fb1e36 | 269 | struct in6_addr s6; |
7f0064bd | 270 | #endif |
83fb1e36 A |
271 | char buffer[256]; |
272 | DNSQuestion q; | |
273 | ||
274 | if (argc < 2) goto usage; | |
275 | ||
276 | // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog | |
277 | mDNS_DebugMode = mDNStrue; | |
278 | ||
c9b9ae52 | 279 | // Initialise the mDNS core. |
83fb1e36 A |
280 | status = mDNS_Init(&mDNSStorage, &PlatformStorage, |
281 | gRRCache, RR_CACHE_SIZE, | |
282 | mDNS_Init_DontAdvertiseLocalAddresses, | |
283 | mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); | |
284 | if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); } | |
285 | ||
286 | signal(SIGINT, HandleSIG); // SIGINT is what you get for a Ctrl-C | |
287 | signal(SIGTERM, HandleSIG); | |
288 | ||
289 | while (this_arg < argc) | |
290 | { | |
291 | char *arg = argv[this_arg++]; | |
292 | if (this_arg > 2) printf("\n"); | |
293 | ||
294 | lastid = id = zeroID; | |
295 | hostaddr = target = zeroAddr; | |
296 | hostname[0] = hardware[0] = software[0] = 0; | |
297 | NumAddr = NumAAAA = NumHINFO = 0; | |
298 | ||
299 | if (inet_pton(AF_INET, arg, &s4) == 1) | |
300 | { | |
301 | mDNSu8 *p = (mDNSu8 *)&s4; | |
302 | // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code | |
303 | mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]); | |
304 | printf("%s\n", buffer); | |
305 | target.type = mDNSAddrType_IPv4; | |
306 | target.ip.v4.NotAnInteger = s4.s_addr; | |
307 | DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); | |
308 | if (StopNow == 2) break; | |
309 | } | |
7f0064bd | 310 | #if HAVE_IPV6 |
83fb1e36 A |
311 | else if (inet_pton(AF_INET6, arg, &s6) == 1) |
312 | { | |
313 | int i; | |
314 | mDNSu8 *p = (mDNSu8 *)&s6; | |
315 | for (i = 0; i < 16; i++) | |
316 | { | |
317 | static const char hexValues[] = "0123456789ABCDEF"; | |
318 | buffer[i * 4 ] = hexValues[p[15-i] & 0x0F]; | |
319 | buffer[i * 4 + 1] = '.'; | |
320 | buffer[i * 4 + 2] = hexValues[p[15-i] >> 4]; | |
321 | buffer[i * 4 + 3] = '.'; | |
322 | } | |
323 | mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); | |
324 | target.type = mDNSAddrType_IPv6; | |
325 | mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6)); | |
326 | DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback); | |
327 | if (StopNow == 2) break; | |
328 | } | |
7f0064bd | 329 | #endif |
9f221bca A |
330 | else |
331 | { | |
332 | if (strlen(arg) >= sizeof(hostname)) | |
333 | { | |
83fb1e36 A |
334 | fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname)); |
335 | goto usage; | |
336 | } | |
337 | strcpy(hostname, arg); | |
338 | } | |
339 | ||
340 | // Now we have the host name; get its A, AAAA, and HINFO | |
341 | if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback); | |
342 | if (StopNow == 2) break; | |
343 | ||
344 | if (hardware[0] || software[0]) | |
345 | { | |
346 | printf("HINFO Hardware: %s\n", hardware); | |
347 | printf("HINFO Software: %s\n", software); | |
348 | } | |
349 | else if (NumAnswers) printf("%s has no HINFO record\n", hostname); | |
350 | else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n"); | |
351 | ||
352 | if (NumAnswers) | |
353 | { | |
354 | // Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers | |
355 | mDNS *const m = &mDNSStorage; | |
356 | mDNSu32 slot; | |
357 | CacheGroup *cg; | |
358 | CacheRecord *rr; | |
359 | FORALL_CACHERECORDS(slot, cg, rr) | |
360 | { | |
361 | mDNS_PurgeCacheResourceRecord(m, rr); | |
362 | } | |
363 | if (target.type == 0) target = hostaddr; // Make sure the services query is targeted | |
364 | DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback); | |
365 | if (StopNow == 2) break; | |
366 | } | |
367 | } | |
368 | ||
369 | mDNS_Close(&mDNSStorage); | |
370 | return(0); | |
c9b9ae52 A |
371 | |
372 | usage: | |
83fb1e36 A |
373 | fprintf(stderr, "Usage: %s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname); |
374 | return(-1); | |
375 | } |