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); 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 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 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 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 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 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 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 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 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 262 mDNSlocal void HandleSIG(int signal) 263 { 264 (void)signal; // Unused 265 debugf("%s",""); 266 debugf("HandleSIG"); 267 StopNow = 2; 268 } 269 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