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 much of the functionality provided by "mDNS.c", 23// except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine 24#define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__ 25#include "mDNS.c" 26#undef mDNSCoreReceive 27 28//************************************************************************************************************* 29// Headers 30 31#include <stdio.h> // For printf() 32#include <stdlib.h> // For malloc() 33#include <string.h> // For strrchr(), strcmp() 34#include <time.h> // For "struct tm" etc. 35#include <signal.h> // For SIGINT, SIGTERM 36#if defined(WIN32) 37// Both mDNS.c and mDNSWin32.h declare UDPSocket_struct type resulting in a compile-time error, so 38// trick the compiler when including mDNSWin32.h 39# define UDPSocket_struct _UDPSocket_struct 40# include <mDNSEmbeddedAPI.h> 41# include <mDNSWin32.h> 42# include <PosixCompat.h> 43# include <Poll.h> 44# define IFNAMSIZ 256 45static HANDLE gStopEvent = INVALID_HANDLE_VALUE; 46static mDNSBool gRunning; 47static void CALLBACK StopNotification( HANDLE event, void *context ) { gRunning = mDNSfalse; } 48static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) { SetEvent( gStopEvent ); return TRUE; } 49void setlinebuf( FILE * fp ) {} 50#else 51# include <netdb.h> // For gethostbyname() 52# include <sys/socket.h> // For AF_INET, AF_INET6, etc. 53# include <net/if.h> // For IF_NAMESIZE 54# include <netinet/in.h> // For INADDR_NONE 55# include <arpa/inet.h> // For inet_addr() 56# include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform 57#endif 58#include "ExampleClientApp.h" 59 60//************************************************************************************************************* 61// Types and structures 62 63enum 64{ 65 // Primitive operations 66 OP_probe = 0, 67 OP_goodbye = 1, 68 69 // These are meta-categories; 70 // Query and Answer operations are actually subdivided into two classes: 71 // Browse query/answer and 72 // Resolve query/answer 73 OP_query = 2, 74 OP_answer = 3, 75 76 // The "Browse" variants of query/answer 77 OP_browsegroup = 2, 78 OP_browseq = 2, 79 OP_browsea = 3, 80 81 // The "Resolve" variants of query/answer 82 OP_resolvegroup = 4, 83 OP_resolveq = 4, 84 OP_resolvea = 5, 85 86 OP_NumTypes = 6 87}; 88 89typedef struct ActivityStat_struct ActivityStat; 90struct ActivityStat_struct 91{ 92 ActivityStat *next; 93 domainname srvtype; 94 int printed; 95 int totalops; 96 int stat[OP_NumTypes]; 97}; 98 99typedef struct FilterList_struct FilterList; 100struct FilterList_struct 101{ 102 FilterList *next; 103 mDNSAddr FilterAddr; 104}; 105 106//************************************************************************************************************* 107// Constants 108 109#define kReportTopServices 15 110#define kReportTopHosts 15 111 112//************************************************************************************************************* 113// Globals 114 115mDNS mDNSStorage; // mDNS core uses this to store its globals 116static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals 117mDNSexport const char ProgramName[] = "mDNSNetMonitor"; 118 119struct timeval tv_start, tv_end, tv_interval; 120static int FilterInterface = 0; 121static FilterList *Filters; 122#define ExactlyOneFilter (Filters && !Filters->next) 123 124static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad 125static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals; 126 127static ActivityStat *stats; 128 129#define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA" 130 131//************************************************************************************************************* 132// Utilities 133 134// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc. 135mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); 136mDNSlocal mDNSu32 mprintf(const char *format, ...) 137{ 138 mDNSu32 length; 139 unsigned char buffer[512]; 140 va_list ptr; 141 va_start(ptr,format); 142 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr); 143 va_end(ptr); 144 printf("%s", buffer); 145 return(length); 146} 147 148//************************************************************************************************************* 149// Host Address List 150// 151// Would benefit from a hash 152 153typedef enum 154{ 155 HostPkt_Q = 0, // Query 156 HostPkt_L = 1, // Legacy Query 157 HostPkt_R = 2, // Response 158 HostPkt_B = 3, // Bad 159 HostPkt_NumTypes = 4 160} HostPkt_Type; 161 162typedef struct 163{ 164 mDNSAddr addr; 165 unsigned long pkts[HostPkt_NumTypes]; 166 unsigned long totalops; 167 unsigned long stat[OP_NumTypes]; 168 domainname hostname; 169 domainname revname; 170 UTF8str255 HIHardware; 171 UTF8str255 HISoftware; 172 mDNSu32 NumQueries; 173 mDNSs32 LastQuery; 174} HostEntry; 175 176#define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B]) 177 178typedef struct 179{ 180 long num; 181 long max; 182 HostEntry *hosts; 183} HostList; 184 185static HostList IPv4HostList = { 0, 0, 0 }; 186static HostList IPv6HostList = { 0, 0, 0 }; 187 188mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList *list) 189{ 190 long i; 191 192 for (i = 0; i < list->num; i++) 193 { 194 HostEntry *entry = list->hosts + i; 195 if (mDNSSameAddress(addr, &entry->addr)) 196 return entry; 197 } 198 199 return NULL; 200} 201 202mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList *list) 203{ 204 int i; 205 HostEntry *entry; 206 if (list->num >= list->max) 207 { 208 long newMax = list->max + 64; 209 HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry)); 210 if (newHosts == NULL) 211 return NULL; 212 list->max = newMax; 213 list->hosts = newHosts; 214 } 215 216 entry = list->hosts + list->num++; 217 218 entry->addr = *addr; 219 for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0; 220 entry->totalops = 0; 221 for (i=0; i<OP_NumTypes; i++) entry->stat[i] = 0; 222 entry->hostname.c[0] = 0; 223 entry->revname.c[0] = 0; 224 entry->HIHardware.c[0] = 0; 225 entry->HISoftware.c[0] = 0; 226 entry->NumQueries = 0; 227 228 if (entry->addr.type == mDNSAddrType_IPv4) 229 { 230 mDNSv4Addr ip = entry->addr.ip.v4; 231 char buffer[32]; 232 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code 233 mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]); 234 MakeDomainNameFromDNSNameString(&entry->revname, buffer); 235 } 236 237 return(entry); 238} 239 240mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t, mDNSOpaque16 id) 241{ 242 if (ExactlyOneFilter) return(NULL); 243 else 244 { 245 HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList; 246 HostEntry *entry = FindHost(addr, list); 247 if (!entry) entry = AddHost(addr, list); 248 if (!entry) return(NULL); 249 // Don't count our own interrogation packets 250 if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++; 251 return(entry); 252 } 253} 254 255mDNSlocal void RecordHostInfo(HostEntry *entry, const ResourceRecord *const pktrr) 256{ 257 if (!entry->hostname.c[0]) 258 { 259 if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA) 260 { 261 // Should really check that the rdata in the address record matches the source address of this packet 262 entry->NumQueries = 0; 263 AssignDomainName(&entry->hostname, pktrr->name); 264 } 265 266 if (pktrr->rrtype == kDNSType_PTR) 267 if (SameDomainName(&entry->revname, pktrr->name)) 268 { 269 entry->NumQueries = 0; 270 AssignDomainName(&entry->hostname, &pktrr->rdata->u.name); 271 } 272 } 273 else if (pktrr->rrtype == kDNSType_HINFO) 274 { 275 RDataBody *rd = &pktrr->rdata->u; 276 mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; 277 mDNSu8 *hw = rd->txt.c; 278 mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0]; 279 if (sw + 1 + sw[0] <= rdend) 280 { 281 AssignDomainName(&entry->hostname, pktrr->name); 282 mDNSPlatformMemCopy(entry->HIHardware.c, hw, 1 + (mDNSu32)hw[0]); 283 mDNSPlatformMemCopy(entry->HISoftware.c, sw, 1 + (mDNSu32)sw[0]); 284 } 285 } 286} 287 288mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID) 289{ 290 const mDNSOpaque16 id = { { 0xFF, 0xFF } }; 291 DNSMessage query; 292 mDNSu8 *qptr = query.data; 293 const mDNSu8 *const limit = query.data + sizeof(query.data); 294 const mDNSAddr *target = &entry->addr; 295 InitializeDNSMessage(&query.h, id, QueryFlags); 296 qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN); 297 entry->LastQuery = m->timenow; 298 entry->NumQueries++; 299 300 // Note: When there are multiple mDNSResponder agents running on a single machine 301 // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder) 302 // it is possible that unicast queries may not go to the primary system responder. 303 // We try the first query using unicast, but if that doesn't work we try again via multicast. 304 if (entry->NumQueries > 2) 305 { 306 target = &AllDNSLinkGroup_v4; 307 } 308 else 309 { 310 //mprintf("%#a Q\n", target); 311 InterfaceID = mDNSInterface_Any; // Send query from our unicast reply socket 312 } 313 314 mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, mDNSNULL, target, MulticastDNSPort, mDNSNULL, mDNSNULL, mDNSfalse); 315} 316 317mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID) 318{ 319 // If we've done four queries without answer, give up 320 if (entry->NumQueries >= 4) return; 321 322 // If we've done a query in the last second, give the host a chance to reply before trying again 323 if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return; 324 325 // If we don't know the host name, try to find that first 326 if (!entry->hostname.c[0]) 327 { 328 if (entry->revname.c[0]) 329 { 330 SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID); 331 //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries); 332 } 333 } 334 // If we have the host name but no HINFO, now ask for that 335 else if (!entry->HIHardware.c[0]) 336 { 337 SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID); 338 //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries); 339 } 340} 341 342mDNSlocal int CompareHosts(const void *p1, const void *p2) 343{ 344 return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1)); 345} 346 347mDNSlocal void ShowSortedHostList(HostList *list, int max) 348{ 349 HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num]; 350 qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts); 351 if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response"); 352 for (e = &list->hosts[0]; e < end; e++) 353 { 354 int len = mprintf("%#-25a", &e->addr); 355 if (len > 25) mprintf("\n%25s", ""); 356 mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e->totalops, 357 e->stat[OP_probe], e->stat[OP_goodbye], 358 e->stat[OP_browseq], e->stat[OP_browsea], 359 e->stat[OP_resolveq], e->stat[OP_resolvea]); 360 mprintf(" %8lu %8lu %8lu %8lu", 361 HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]); 362 if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]); 363 mprintf("\n"); 364 if (!e->HISoftware.c[0] && e->NumQueries > 2) 365 mDNSPlatformMemCopy(&e->HISoftware, "\x27*** Unknown (Jaguar, Windows, etc.) ***", 0x28); 366 if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0]) 367 mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c); 368 } 369} 370 371//************************************************************************************************************* 372// Receive and process packets 373 374mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype) 375{ 376 int i, len; 377 const mDNSu8 *src = fqdn->c; 378 mDNSu8 *dst = srvtype->c; 379 380 len = *src; 381 if (len == 0 || len >= 0x40) return(mDNSfalse); 382 if (src[1] != '_') src += 1 + len; 383 384 len = *src; 385 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse); 386 for (i=0; i<=len; i++) *dst++ = *src++; 387 388 len = *src; 389 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse); 390 for (i=0; i<=len; i++) *dst++ = *src++; 391 392 *dst++ = 0; // Put the null root label on the end of the service type 393 394 return(mDNStrue); 395} 396 397mDNSlocal void recordstat(HostEntry *entry, const domainname *fqdn, int op, mDNSu16 rrtype) 398{ 399 ActivityStat **s = &stats; 400 domainname srvtype; 401 402 if (op != OP_probe) 403 { 404 if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup; 405 else if (rrtype != kDNSType_PTR) return; 406 } 407 408 if (!ExtractServiceType(fqdn, &srvtype)) return; 409 410 while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next; 411 if (!*s) 412 { 413 int i; 414 *s = malloc(sizeof(ActivityStat)); 415 if (!*s) exit(-1); 416 (*s)->next = NULL; 417 (*s)->srvtype = srvtype; 418 (*s)->printed = 0; 419 (*s)->totalops = 0; 420 for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0; 421 } 422 423 (*s)->totalops++; 424 (*s)->stat[op]++; 425 if (entry) 426 { 427 entry->totalops++; 428 entry->stat[op]++; 429 } 430} 431 432mDNSlocal void printstats(int max) 433{ 434 int i; 435 if (!stats) return; 436 for (i=0; i<max; i++) 437 { 438 int max = 0; 439 ActivityStat *s, *m = NULL; 440 for (s = stats; s; s=s->next) 441 if (!s->printed && max < s->totalops) 442 { m = s; max = s->totalops; } 443 if (!m) return; 444 m->printed = mDNStrue; 445 if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner); 446 mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.c, m->totalops, m->stat[OP_probe], 447 m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]); 448 } 449} 450 451mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end, 452 DNSQuestion *q, LargeCacheRecord *pkt) 453{ 454 int i; 455 for (i = 0; i < query->h.numAuthorities; i++) 456 { 457 const mDNSu8 *p2 = ptr; 458 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt); 459 if (!ptr) break; 460 if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2); 461 } 462 return(mDNSNULL); 463} 464 465mDNSlocal void DisplayPacketHeader(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) 466{ 467 const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " : 468 (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-"; 469 470 struct timeval tv; 471 struct tm tm; 472 const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse); 473 char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE 474 if_indextoname(index, if_name); 475 gettimeofday(&tv, NULL); 476 localtime_r((time_t*)&tv.tv_sec, &tm); 477 mprintf("\n%d:%02d:%02d.%06d Interface %d/%s\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec, index, if_name); 478 479 mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes", 480 srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg); 481 482 if (msg->h.id.NotAnInteger) mprintf(" ID:%u", mDNSVal16(msg->h.id)); 483 484 if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf(" To: %#a", dstaddr); 485 486 if (msg->h.flags.b[0] & kDNSFlag0_TC) 487 { 488 if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated"); 489 else mprintf(" Truncated (KA list continues in next packet)"); 490 } 491 mprintf("\n"); 492} 493 494mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr) 495{ 496 static const char hexchars[16] = "0123456789ABCDEF"; 497 #define MaxWidth 132 498 char buffer[MaxWidth+8]; 499 char *p = buffer; 500 501 RDataBody *rd = &pktrr->rdata->u; 502 mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength; 503 int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name->c); 504 505 if (pktrr->RecordType == kDNSRecordTypePacketNegative) { mprintf("**** ERROR: FAILED TO READ RDATA ****\n"); return; } 506 507 // The kDNSType_OPT case below just calls GetRRDisplayString_rdb 508 // Perhaps more (or all?) of the cases should do that? 509 switch(pktrr->rrtype) 510 { 511 case kDNSType_A: n += mprintf("%.4a", &rd->ipv4); break; 512 case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, rd->name.c); break; 513 case kDNSType_HINFO: // same as kDNSType_TXT below 514 case kDNSType_TXT: { 515 mDNSu8 *t = rd->txt.c; 516 while (t < rdend && t[0] && p < buffer+MaxWidth) 517 { 518 int i; 519 for (i=1; i<=t[0] && p < buffer+MaxWidth; i++) 520 { 521 if (t[i] == '\\') *p++ = '\\'; 522 if (t[i] >= ' ') *p++ = t[i]; 523 else 524 { 525 *p++ = '\\'; 526 *p++ = '0'; 527 *p++ = 'x'; 528 *p++ = hexchars[t[i] >> 4]; 529 *p++ = hexchars[t[i] & 0xF]; 530 } 531 } 532 t += 1+t[0]; 533 if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; } 534 } 535 *p++ = 0; 536 n += mprintf("%.*s", MaxWidth - n, buffer); 537 } break; 538 case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break; 539 case kDNSType_SRV: n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break; 540 case kDNSType_OPT: { 541 char b[MaxMsg]; 542 // Quick hack: we don't want the prefix that GetRRDisplayString_rdb puts at the start of its 543 // string, because it duplicates the name and rrtype we already display, so we compute the 544 // length of that prefix and strip that many bytes off the beginning of the string we display. 545 mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype)); 546 GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b); 547 n += mprintf("%.*s", MaxWidth - n, b + striplen); 548 } break; 549 case kDNSType_NSEC: { 550 char b[MaxMsg]; 551 // See the quick hack above 552 mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype)); 553 GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b); 554 n += mprintf("%s", b+striplen); 555 } break; 556 default: { 557 mDNSu8 *s = rd->data; 558 while (s < rdend && p < buffer+MaxWidth) 559 { 560 if (*s == '\\') *p++ = '\\'; 561 if (*s >= ' ') *p++ = *s; 562 else 563 { 564 *p++ = '\\'; 565 *p++ = '0'; 566 *p++ = 'x'; 567 *p++ = hexchars[*s >> 4]; 568 *p++ = hexchars[*s & 0xF]; 569 } 570 s++; 571 } 572 *p++ = 0; 573 n += mprintf("%.*s", MaxWidth - n, buffer); 574 } break; 575 } 576 577 mprintf("\n"); 578} 579 580mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end) 581{ 582 while (ptr < end) 583 { 584 int i; 585 for (i=0; i<16; i++) 586 if (&ptr[i] < end) mprintf("%02X ", ptr[i]); 587 else mprintf(" "); 588 for (i=0; i<16; i++) 589 if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]); 590 ptr += 16; 591 mprintf("\n"); 592 } 593} 594 595mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg) 596{ 597 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg); 598 HexDump(ptr, end); 599} 600 601mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, 602 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) 603{ 604 int i; 605 const mDNSu8 *ptr = msg->data; 606 const mDNSu8 *auth = LocateAuthorities(msg, end); 607 mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger); 608 HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id); 609 LargeCacheRecord pkt; 610 611 DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); 612 if (msg->h.id.NotAnInteger != 0xFFFF) 613 { 614 if (MQ) NumPktQ++;else NumPktL++; 615 } 616 617 for (i=0; i<msg->h.numQuestions; i++) 618 { 619 DNSQuestion q; 620 mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q); 621 mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse; 622 q.qclass &= ~kDNSQClass_UnicastResponse; 623 if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; } 624 ptr = p2; 625 p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt); 626 if (p2) 627 { 628 NumProbes++; 629 DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec); 630 recordstat(entry, &q.qname, OP_probe, q.qtype); 631 p2 = (mDNSu8 *)skipDomainName(msg, p2, end); 632 // Having displayed this update record, clear type and class so we don't display the same one again. 633 p2[0] = p2[1] = p2[2] = p2[3] = 0; 634 } 635 else 636 { 637 const char *ptype = ucbit ? "(QU)" : "(QM)"; 638 if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++; 639 else { NumLegacy++; ptype = "(LQ)"; } 640 mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c); 641 if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype); 642 } 643 } 644 645 for (i=0; i<msg->h.numAnswers; i++) 646 { 647 const mDNSu8 *ep = ptr; 648 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); 649 if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; } 650 DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec); 651 652 // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet 653 // the same as a single query, to more accurately reflect the burden on the network 654 // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.) 655 if (msg->h.numQuestions == 0 && i == 0) 656 recordstat(entry, pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype); 657 } 658 659 for (i=0; i<msg->h.numAuthorities; i++) 660 { 661 const mDNSu8 *ep = ptr; 662 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt); 663 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } 664 // After we display an Update record with its matching question (above) we zero out its type and class 665 // If any remain that haven't been zero'd out, display them here 666 if (pkt.r.resrec.rrtype || pkt.r.resrec.rrclass) DisplayResourceRecord(srcaddr, "(AU)", &pkt.r.resrec); 667 } 668 669 for (i=0; i<msg->h.numAdditionals; i++) 670 { 671 const mDNSu8 *ep = ptr; 672 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt); 673 if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } 674 DisplayResourceRecord(srcaddr, pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : "(AD)", &pkt.r.resrec); 675 } 676 677 if (entry) AnalyseHost(m, entry, InterfaceID); 678} 679 680mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, 681 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID) 682{ 683 int i; 684 const mDNSu8 *ptr = msg->data; 685 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); 686 LargeCacheRecord pkt; 687 688 DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); 689 if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++; 690 691 for (i=0; i<msg->h.numQuestions; i++) 692 { 693 DNSQuestion q; 694 const mDNSu8 *ep = ptr; 695 ptr = getQuestion(msg, ptr, end, InterfaceID, &q); 696 if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; } 697 if (mDNSAddrIsDNSMulticast(dstaddr)) 698 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); 699 else 700 mprintf("%#-16a (Q) %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c); 701 } 702 703 for (i=0; i<msg->h.numAnswers; i++) 704 { 705 const mDNSu8 *ep = ptr; 706 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); 707 if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; } 708 if (pkt.r.resrec.rroriginalttl) 709 { 710 NumAnswers++; 711 DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec); 712 if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype); 713 if (entry) RecordHostInfo(entry, &pkt.r.resrec); 714 } 715 else 716 { 717 NumGoodbyes++; 718 DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec); 719 recordstat(entry, pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype); 720 } 721 } 722 723 for (i=0; i<msg->h.numAuthorities; i++) 724 { 725 const mDNSu8 *ep = ptr; 726 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt); 727 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } 728 if (pkt.r.resrec.rrtype != kDNSType_NSEC3) 729 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n", 730 srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c); 731 } 732 733 for (i=0; i<msg->h.numAdditionals; i++) 734 { 735 const mDNSu8 *ep = ptr; 736 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt); 737 if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; } 738 NumAdditionals++; 739 DisplayResourceRecord(srcaddr, 740 pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", 741 &pkt.r.resrec); 742 if (entry) RecordHostInfo(entry, &pkt.r.resrec); 743 } 744 745 if (entry) AnalyseHost(m, entry, InterfaceID); 746} 747 748mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID) 749{ 750 int i; 751 const mDNSu8 *ptr = LocateAnswers(msg, end); 752 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id); 753 //mprintf("%#a R\n", srcaddr); 754 755 for (i=0; i<msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++) 756 { 757 LargeCacheRecord pkt; 758 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt); 759 if (ptr && pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec); 760 } 761} 762 763mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr) 764{ 765 FilterList *f; 766 if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4); 767 for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue); 768 return(mDNSfalse); 769} 770 771mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, 772 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) 773{ 774 const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; 775 const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; 776 const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); 777 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions; 778 int goodinterface = (FilterInterface == 0); 779 780 (void)dstaddr; // Unused 781 (void)dstport; // Unused 782 783 // Read the integer parts which are in IETF byte-order (MSB first, LSB second) 784 msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); 785 msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); 786 msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); 787 msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); 788 789 // For now we're only interested in monitoring IPv4 traffic. 790 // All IPv6 packets should just be duplicates of the v4 packets. 791 if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse)); 792 if (goodinterface && AddressMatchesFilterList(srcaddr)) 793 { 794 mDNS_Lock(m); 795 if (!mDNSAddrIsDNSMulticast(dstaddr)) 796 { 797 if (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr); 798 else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr, InterfaceID); 799 } 800 else 801 { 802 if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); 803 else if (QR_OP == StdR) DisplayResponse (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID); 804 else 805 { 806 debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]); 807 GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id); 808 NumPktB++; 809 } 810 } 811 mDNS_Unlock(m); 812 } 813} 814 815mDNSlocal mStatus mDNSNetMonitor(void) 816{ 817 struct tm tm; 818 int h, m, s, mul, div, TotPkt; 819#if !defined(WIN32) 820 sigset_t signals; 821#endif 822 823 mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage, 824 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, 825 mDNS_Init_DontAdvertiseLocalAddresses, 826 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); 827 if (status) return(status); 828 829 gettimeofday(&tv_start, NULL); 830 831#if defined( WIN32 ) 832 status = SetupInterfaceList(&mDNSStorage); 833 if (status) return(status); 834 gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 835 if (gStopEvent == INVALID_HANDLE_VALUE) return mStatus_UnknownErr; 836 mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL ); 837 if (!SetConsoleCtrlHandler(ConsoleControlHandler, TRUE)) return mStatus_UnknownErr; 838 gRunning = mDNStrue; while (gRunning) mDNSPoll( INFINITE ); 839 if (!SetConsoleCtrlHandler(ConsoleControlHandler, FALSE)) return mStatus_UnknownErr; 840 CloseHandle(gStopEvent); 841#else 842 mDNSPosixListenForSignalInEventLoop(SIGINT); 843 mDNSPosixListenForSignalInEventLoop(SIGTERM); 844 845 do 846 { 847 struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM 848 mDNSBool gotSomething; 849 mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething); 850 } 851 while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM))); 852#endif 853 854 // Now display final summary 855 TotPkt = NumPktQ + NumPktL + NumPktR; 856 gettimeofday(&tv_end, NULL); 857 tv_interval = tv_end; 858 if (tv_start.tv_usec > tv_interval.tv_usec) 859 { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; } 860 tv_interval.tv_sec -= tv_start.tv_sec; 861 tv_interval.tv_usec -= tv_start.tv_usec; 862 h = (tv_interval.tv_sec / 3600); 863 m = (tv_interval.tv_sec % 3600) / 60; 864 s = (tv_interval.tv_sec % 60); 865 if (tv_interval.tv_sec > 10) 866 { 867 mul = 60; 868 div = tv_interval.tv_sec; 869 } 870 else 871 { 872 mul = 60000; 873 div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000; 874 if (div == 0) div=1; 875 } 876 877 mprintf("\n\n"); 878 localtime_r((time_t*)&tv_start.tv_sec, &tm); 879 mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec); 880 localtime_r((time_t*)&tv_end.tv_sec, &tm); 881 mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec); 882 mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec); 883 if (!Filters) 884 { 885 mprintf("Unique source addresses seen on network:"); 886 if (IPv4HostList.num) mprintf(" %ld (IPv4)", IPv4HostList.num); 887 if (IPv6HostList.num) mprintf(" %ld (IPv6)", IPv6HostList.num); 888 if (!IPv4HostList.num && !IPv6HostList.num) mprintf(" None"); 889 mprintf("\n"); 890 } 891 mprintf("\n"); 892 mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div); 893 mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div); 894 mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div); 895 mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div); 896 mprintf("\n"); 897 mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div); 898 mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div); 899 mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div); 900 mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div); 901 mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div); 902 mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div); 903 mprintf("\n"); 904 printstats(kReportTopServices); 905 906 if (!ExactlyOneFilter) 907 { 908 ShowSortedHostList(&IPv4HostList, kReportTopHosts); 909 ShowSortedHostList(&IPv6HostList, kReportTopHosts); 910 } 911 912 mDNS_Close(&mDNSStorage); 913 return(0); 914} 915 916mDNSexport int main(int argc, char **argv) 917{ 918 const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]; 919 int i; 920 mStatus status; 921 922#if defined(WIN32) 923 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); 924#endif 925 926 setlinebuf(stdout); // Want to see lines as they appear, not block buffered 927 928 for (i=1; i<argc; i++) 929 { 930 if (i+1 < argc && !strcmp(argv[i], "-i") && atoi(argv[i+1])) 931 { 932 FilterInterface = atoi(argv[i+1]); 933 i += 2; 934 printf("Monitoring interface %d\n", FilterInterface); 935 } 936 else 937 { 938 struct in_addr s4; 939 struct in6_addr s6; 940 FilterList *f; 941 mDNSAddr a; 942 a.type = mDNSAddrType_IPv4; 943 944 if (inet_pton(AF_INET, argv[i], &s4) == 1) 945 a.ip.v4.NotAnInteger = s4.s_addr; 946 else if (inet_pton(AF_INET6, argv[i], &s6) == 1) 947 { 948 a.type = mDNSAddrType_IPv6; 949 mDNSPlatformMemCopy(&a.ip.v6, &s6, sizeof(a.ip.v6)); 950 } 951 else 952 { 953 struct hostent *h = gethostbyname(argv[i]); 954 if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr; 955 else goto usage; 956 } 957 958 f = malloc(sizeof(*f)); 959 f->FilterAddr = a; 960 f->next = Filters; 961 Filters = f; 962 } 963 } 964 965 status = mDNSNetMonitor(); 966 if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %d\n", progname, (int)status); return(status); } 967 return(0); 968 969usage: 970 fprintf(stderr, "\nmDNS traffic monitor\n"); 971 fprintf(stderr, "Usage: %s [-i index] [host]\n", progname); 972 fprintf(stderr, "Optional [-i index] parameter displays only packets from that interface index\n"); 973 fprintf(stderr, "Optional [host] parameter displays only packets from that host\n"); 974 975 fprintf(stderr, "\nPer-packet header output:\n"); 976 fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n"); 977 fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n"); 978 fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n"); 979 fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n"); 980 981 fprintf(stderr, "\nPer-record display:\n"); 982 fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n"); 983 fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n"); 984 fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n"); 985 fprintf(stderr, "(LQ) Legacy Query Question\n"); 986 fprintf(stderr, "(QM) Query Question, requesting multicast response\n"); 987 fprintf(stderr, "(QU) Query Question, requesting unicast response\n"); 988 fprintf(stderr, "(KA) Known Answer (information querier already knows)\n"); 989 fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n"); 990 fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n"); 991 fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n"); 992 fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n"); 993 994 fprintf(stderr, "\nFinal summary, sorted by service type:\n"); 995 fprintf(stderr, "Probe Probes for this service type starting up\n"); 996 fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n"); 997 fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n"); 998 fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n"); 999 fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n"); 1000 fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n"); 1001 fprintf(stderr, "\n"); 1002 return(-1); 1003} 1004