Robert Greenwalt | 47e4ceb | 2012-03-26 15:36:57 -0700 | [diff] [blame] | 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 | } |