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