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.
19 //*************************************************************************************************************
20 // Incorporate mDNS.c functionality
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
26 #undef mDNSCoreReceive
28 //*************************************************************************************************************
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>
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"
46 //*************************************************************************************************************
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";
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
;
61 //*************************************************************************************************************
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
, ...)
69 unsigned char buffer
[512];
72 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
78 //*************************************************************************************************************
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
)
85 (void)dstaddr
; // Unused
86 // Snag copy of header ID, then call through
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
);
95 mDNSlocal
void NameCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
98 (void)question
; // Unused
99 (void)AddRecord
; // Unused
100 if (!id
.NotAnInteger
) id
= lastid
;
101 if (answer
->rrtype
== kDNSType_PTR
|| answer
->rrtype
== kDNSType_CNAME
)
103 ConvertDomainNameToCString(&answer
->rdata
->u
.name
, hostname
);
105 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
109 mDNSlocal
void InfoCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
112 (void)question
; // Unused
113 (void)AddRecord
; // Unused
114 if (answer
->rrtype
== kDNSType_A
)
116 if (!id
.NotAnInteger
) id
= lastid
;
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
;
123 else if (answer
->rrtype
== kDNSType_AAAA
)
125 if (!id
.NotAnInteger
) id
= lastid
;
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
131 hostaddr
.type
= mDNSAddrType_IPv6
;
132 hostaddr
.ip
.v6
= answer
->rdata
->u
.ipv6
;
135 else if (answer
->rrtype
== kDNSType_HINFO
)
137 mDNSu8
*p
= answer
->rdata
->u
.data
;
138 strncpy(hardware
, (char*)(p
+1), p
[0]);
141 strncpy(software
, (char*)(p
+1), p
[0]);
147 // If we've got everything we're looking for, don't need to wait any more
148 if (/*NumHINFO && */ (NumAddr
|| NumAAAA
)) StopNow
= 1;
151 mDNSlocal
void ServicesCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
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
))
162 mprintf("%##s %s %##s\n", answer
->name
->c
, DNSTypeName(answer
->rrtype
), answer
->rdata
->u
.name
.c
);
166 mDNSlocal
void WaitForAnswer(mDNS
*const m
, int seconds
)
169 gettimeofday(&end
, NULL
);
170 end
.tv_sec
+= seconds
;
177 struct timeval now
, remain
= end
;
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
)
185 if (!NumAnswers
) printf("No response after %d seconds\n", seconds
);
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;
197 mDNSlocal mStatus
StartQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
200 if (qname
) MakeDomainNameFromDNSNameString(&q
->qname
, qname
);
201 q
->InterfaceID
= mDNSInterface_Any
;
203 q
->Target
= target
? *target
: zeroAddr
;
204 q
->TargetPort
= MulticastDNSPort
;
205 q
->TargetQID
= zeroID
;
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
;
228 //mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
229 return(mDNS_StartQuery(&mDNSStorage
, q
));
232 mDNSlocal
void DoOneQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
234 mStatus status
= StartQuery(q
, qname
, qtype
, target
, callback
);
235 if (status
!= mStatus_NoError
)
239 WaitForAnswer(&mDNSStorage
, 4);
240 mDNS_StopQuery(&mDNSStorage
, q
);
244 mDNSlocal
int DoQuery(DNSQuestion
*q
, char *qname
, mDNSu16 qtype
, const mDNSAddr
*target
, mDNSQuestionCallback callback
)
246 DoOneQuery(q
, qname
, qtype
, target
, callback
);
247 if (StopNow
== 0 && NumAnswers
== 0 && target
&& target
->type
)
249 mprintf("%##s %s Trying multicast\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
250 DoOneQuery(q
, qname
, qtype
, NULL
, callback
);
252 if (StopNow
== 0 && NumAnswers
== 0)
253 mprintf("%##s %s *** No Answer ***\n", q
->qname
.c
, DNSTypeName(q
->qtype
));
257 mDNSlocal
void HandleSIG(int signal
)
259 (void)signal
; // Unused
265 mDNSexport
int main(int argc
, char **argv
)
267 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
277 if (argc
< 2) goto usage
;
279 // Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
280 mDNS_DebugMode
= mDNStrue
;
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
); }
289 signal(SIGINT
, HandleSIG
); // SIGINT is what you get for a Ctrl-C
290 signal(SIGTERM
, HandleSIG
);
292 while (this_arg
< argc
)
294 char *arg
= argv
[this_arg
++];
295 if (this_arg
> 2) printf("\n");
297 lastid
= id
= zeroID
;
298 hostaddr
= target
= zeroAddr
;
299 hostname
[0] = hardware
[0] = software
[0] = 0;
300 NumAddr
= NumAAAA
= NumHINFO
= 0;
302 if (inet_pton(AF_INET
, arg
, &s4
) == 1)
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;
314 else if (inet_pton(AF_INET6
, arg
, &s6
) == 1)
317 mDNSu8
*p
= (mDNSu8
*)&s6
;
318 for (i
= 0; i
< 16; i
++)
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] = '.';
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;
335 if (strlen(arg
) >= sizeof(hostname
))
337 fprintf(stderr
, "hostname must be < %d characters\n", (int)sizeof(hostname
));
340 strcpy(hostname
, arg
);
343 // Now we have the host name; get its A, AAAA, and HINFO
344 if (hostname
[0]) DoQuery(&q
, hostname
, kDNSQType_ANY
, &target
, InfoCallback
);
345 if (StopNow
== 2) break;
347 if (hardware
[0] || software
[0])
349 printf("HINFO Hardware: %s\n", hardware
);
350 printf("HINFO Software: %s\n", software
);
352 else if (NumAnswers
) printf("%s has no HINFO record\n", hostname
);
353 else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
357 // Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers
358 mDNS
*const m
= &mDNSStorage
;
362 FORALL_CACHERECORDS(slot
, cg
, rr
)
364 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;
372 mDNS_Close(&mDNSStorage
);
376 fprintf(stderr
, "Usage: %s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname
);