]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - mDNSPosix/NetMonitor.c
mDNSResponder-1096.100.3.tar.gz
[apple/mdnsresponder.git] / mDNSPosix / NetMonitor.c
index da3e63ea32a0e54474542f8802a6682169d19616..5a93de6c778b76b9f56581a42f21fe3e765fb603 100644 (file)
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
- * Formatting notes:
- * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
- * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
- * but for the sake of brevity here I will say just this: Curly braces are not syntactially
- * part of an "if" statement; they are the beginning and ending markers of a compound statement;
- * therefore common sense dictates that if they are part of a compound statement then they
- * should be indented to the same level as everything else in that compound statement.
- * Indenting curly braces at the same level as the "if" implies that curly braces are
- * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
- * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
- * understand why variable y is not of type "char*" just proves the point that poor code
- * layout leads people to unfortunate misunderstandings about how the C language really works.)
-
-    Change History (most recent first):
-
-$Log: NetMonitor.c,v $
-Revision 1.82  2006/08/14 23:24:46  cheshire
-Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
-
-Revision 1.81  2006/07/06 00:01:44  cheshire
-<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
-Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
-
-Revision 1.80  2006/06/12 18:22:42  cheshire
-<rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
-
-Revision 1.79  2006/04/26 20:48:33  cheshire
-Make final count of unique source addresses show IPv4 and IPv6 counts separately
-
-Revision 1.78  2006/04/25 00:42:24  cheshire
-Add ability to specify a single interface index to capture on,
-e.g. typically "-i 4" for Ethernet and "-i 5" for AirPort
-
-Revision 1.77  2006/03/02 21:50:45  cheshire
-Removed strange backslash at the end of a line
-
-Revision 1.76  2006/02/23 23:38:43  cheshire
-<rdar://problem/4427969> On FreeBSD 4 "arpa/inet.h" requires "netinet/in.h" be included first
-
-Revision 1.75  2006/01/05 22:33:58  cheshire
-Use IFNAMSIZ (more portable) instead of IF_NAMESIZE
-
-Revision 1.74  2005/12/02 20:08:39  cheshire
-Update "No HINFO" message
-
-Revision 1.73  2005/12/02 19:19:53  cheshire
-<rdar://problem/4331590> Include interface index and name in mDNSNetMonitor output
-
-Revision 1.72  2005/11/07 01:47:45  cheshire
-<rdar://problem/4331590> Include interface index in mDNSNetMonitor output
-
-Revision 1.71  2004/12/16 20:17:11  cheshire
-<rdar://problem/3324626> Cache memory management improvements
-
-Revision 1.70  2004/12/04 02:13:20  cheshire
-Pass proper record type in GetLargeResourceRecord() calls
-
-Revision 1.69  2004/11/30 22:37:01  cheshire
-Update copyright dates and add "Mode: C; tab-width: 4" headers
-
-Revision 1.68  2004/10/23 01:16:01  cheshire
-<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
-
-Revision 1.67  2004/10/16 00:17:00  cheshire
-<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
-
-Revision 1.66  2004/09/17 00:31:52  cheshire
-For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
-
-Revision 1.65  2004/09/16 01:58:22  cheshire
-Fix compiler warnings
-
-Revision 1.64  2004/06/15 02:39:47  cheshire
-When displaying error message, only show command name, not entire path
-
-Revision 1.63  2004/05/18 23:51:26  cheshire
-Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
-
-Revision 1.62  2004/03/16 18:24:25  cheshire
-If packet destination was not multicast, then display it
-
-Revision 1.61  2004/02/20 09:36:46  cheshire
-Also show TTL in packet header, if it's not 255
-
-Revision 1.60  2004/02/07 02:11:35  cheshire
-Make mDNSNetMonitor smarter about sending targeted unicast HINFO queries
-
-Revision 1.59  2004/02/03 21:42:55  cheshire
-Add constants kReportTopServices and kReportTopHosts
-
-Revision 1.58  2004/01/27 20:15:23  cheshire
-<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
-
-Revision 1.57  2004/01/24 23:59:42  cheshire
-Change to use mDNSVal16() instead of shifting and ORing
-
-Revision 1.56  2004/01/24 05:25:34  cheshire
-mDNSNetMonitor now uses the new ability to send unicast queries so that
-it causes less perturbation of the network traffic it's monitoring.
-
-Revision 1.55  2003/12/23 00:21:31  cheshire
-Send HINFO queries to determine the mDNSResponder version of each host
-
-Revision 1.54  2003/12/17 01:06:39  cheshire
-Also show host name along with HINFO data
-
-Revision 1.53  2003/12/17 00:51:22  cheshire
-Changed mDNSNetMonitor and mDNSIdentify to link the object files
-instead of #including the "DNSCommon.c" "uDNS.c" and source files
-
-Revision 1.52  2003/12/17 00:21:51  cheshire
-If we received one, display host's HINFO record in final summary
-
-Revision 1.51  2003/12/13 03:05:28  ksekar
-<rdar://problem/3192548>: DynDNS: Unicast query of service records
-
-Revision 1.50  2003/12/08 20:47:02  rpantos
-Add support for mDNSResponder on Linux.
-
-Revision 1.49  2003/10/30 19:38:56  cheshire
-Fix warning on certain compilers
-
-Revision 1.48  2003/10/30 19:30:00  cheshire
-Fix warnings on certain compilers
-
-Revision 1.47  2003/09/05 18:49:57  cheshire
-Add total packet size to display
-
-Revision 1.46  2003/09/05 02:33:48  cheshire
-Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window
-
-Revision 1.45  2003/09/04 00:16:20  cheshire
-Only show count of unique source addresses seen on network if we're not filtering
-
-Revision 1.44  2003/09/02 22:13:28  cheshire
-Show total host count in final summary table
-
-Revision 1.43  2003/09/02 21:42:52  cheshire
-Improved alignment of final summary table with v6 addresses
-
-Revision 1.42  2003/09/02 20:59:24  cheshire
-Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
-
-Revision 1.41  2003/08/29 22:05:44  cheshire
-Also count subsequent KA packets for the purposes of statistics counting
-
-Revision 1.40  2003/08/29 16:43:24  cheshire
-Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
-
-Revision 1.39  2003/08/28 02:07:48  vlubet
-Added "per hosts" statistics
-
-Revision 1.38  2003/08/20 22:41:42  cheshire
-Also display total multicast packet count
-
-Revision 1.37  2003/08/20 22:32:08  cheshire
-Error in DisplayQuery: Authorities come *after* Answers, not before
-
-Revision 1.36  2003/08/18 23:20:10  cheshire
-RDLength moved from the RData to the ResourceRecord object.
-
-Revision 1.35  2003/08/15 20:17:28  cheshire
-"LargeResourceRecord" changed to "LargeCacheRecord"
-
-Revision 1.34  2003/08/14 02:19:55  cheshire
-<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
-
-Revision 1.33  2003/08/12 19:56:26  cheshire
-Update to APSL 2.0
-
-Revision 1.32  2003/08/06 18:57:01  cheshire
-Update comments
-
-Revision 1.31  2003/08/06 02:05:12  cheshire
-Add ability to give a list of hosts to monitor
-
-Revision 1.30  2003/08/05 23:56:26  cheshire
-Update code to compile with the new mDNSCoreReceive() function that requires a TTL
-(Right now mDNSPosix.c just reports 255 -- we should fix this)
-
-Revision 1.29  2003/08/05 00:43:12  cheshire
-Report errors encountered while processing authority section
-
-Revision 1.28  2003/07/29 22:51:08  cheshire
-Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them
-
-Revision 1.27  2003/07/29 22:48:04  cheshire
-Completed support for decoding packets containing oversized resource records
-
-Revision 1.26  2003/07/19 03:25:23  cheshire
-Change to make use of new GetLargeResourceRecord() call, for handling larger records
-
-Revision 1.25  2003/07/18 00:13:23  cheshire
-Remove erroneous trailing '\' from TXT record display
-
-Revision 1.24  2003/07/17 17:10:51  cheshire
-<rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
-Further work: distinguish between PM and PU
-
-Revision 1.23  2003/07/16 22:20:23  cheshire
-<rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
-Made mDNSNetMonitor distinguish between QM and QU in its logging output
-
-Revision 1.22  2003/07/02 21:19:58  cheshire
-<rdar://problem/3313413> Update copyright notices, etc., in source code comments
-
-Revision 1.21  2003/06/18 05:48:41  cheshire
-Fix warnings
-
-Revision 1.20  2003/06/06 22:18:22  cheshire
-Add extra space in Q output to line it up with RR output
-
-Revision 1.19  2003/06/06 21:05:04  cheshire
-Display state of cache-flush bit on additional records
-
-Revision 1.18  2003/06/06 20:01:30  cheshire
-For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
-(Global search-and-replace; no functional change to code execution.)
-
-Revision 1.17  2003/06/06 14:26:50  cheshire
-Explicitly #include <time.h> for the benefit of certain Linux distributions
-
-Revision 1.16  2003/05/29 21:56:36  cheshire
-More improvements:
-Distinguish modern multicast queries from legacy multicast queries
-In addition to record counts, display packet counts of queries, legacy queries, and responses
-Include TTL in RR display
-
-Revision 1.15  2003/05/29 20:03:57  cheshire
-Various improvements:
-Display start and end time, average rates in packets-per-minute,
-show legacy queries as -LQ-, improve display of TXT and unknown records
-
-Revision 1.14  2003/05/26 04:45:42  cheshire
-Limit line length when printing super-long TXT records
-
-Revision 1.13  2003/05/26 03:21:29  cheshire
-Tidy up address structure naming:
-mDNSIPAddr         => mDNSv4Addr (for consistency with mDNSv6Addr)
-mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
-mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
-
-Revision 1.12  2003/05/26 03:01:28  cheshire
-<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
-
-Revision 1.11  2003/05/26 00:48:13  cheshire
-If mDNS packet contains a non-zero message ID, then display it.
-
-Revision 1.10  2003/05/22 01:10:32  cheshire
-Indicate when the TC bit is set on a query packet
-
-Revision 1.9  2003/05/21 03:56:00  cheshire
-Improve display of Probe queries
-
-Revision 1.8  2003/05/09 21:41:56  cheshire
-Track deletion/goodbye packets as separate category
-
-Revision 1.7  2003/05/07 00:16:01  cheshire
-More detailed decoding of Resource Records
-
-Revision 1.6  2003/05/05 21:16:16  cheshire
-<rdar://problem/3241281> Change timenow from a local variable to a structure member
-
-Revision 1.5  2003/04/19 01:16:22  cheshire
-Add filter option, to monitor only packets from a single specified source address
-
-Revision 1.4  2003/04/18 00:45:21  cheshire
-Distinguish announcements (AN) from deletions (DE)
-
-Revision 1.3  2003/04/15 18:26:01  cheshire
-Added timestamps and help information
-
-Revision 1.2  2003/04/04 20:42:02  cheshire
-Fix broken statistics counting
-
-Revision 1.1  2003/04/04 01:37:14  cheshire
-Added NetMonitor.c
-
  */
 
 //*************************************************************************************************************
@@ -300,71 +22,86 @@ Added NetMonitor.c
 // We want to use much of the functionality provided by "mDNS.c",
 // except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
 #define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
-#include "mDNS.c"
+#include "../mDNSCore/mDNS.c"
 #undef mDNSCoreReceive
 
 //*************************************************************************************************************
 // Headers
 
-#include <stdio.h>                     // For printf()
-#include <stdlib.h>                    // For malloc()
-#include <string.h>                    // For bcopy()
-#include <time.h>                      // For "struct tm" etc.
-#include <signal.h>                    // For SIGINT, SIGTERM
-#include <netdb.h>                     // For gethostbyname()
-#include <sys/socket.h>                // For AF_INET, AF_INET6, etc.
-#include <net/if.h>                    // For IF_NAMESIZE
-#include <netinet/in.h>                // For INADDR_NONE
-#include <arpa/inet.h>         // For inet_addr()
-
-#include "mDNSPosix.h"      // Defines the specific types needed to run mDNS on this platform
+#include <stdio.h>          // For printf()
+#include <stdlib.h>         // For malloc()
+#include <string.h>         // For strrchr(), strcmp()
+#include <time.h>           // For "struct tm" etc.
+#include <signal.h>         // For SIGINT, SIGTERM
+#if defined(WIN32)
+// Both mDNS.c and mDNSWin32.h declare UDPSocket_struct type resulting in a compile-time error, so
+// trick the compiler when including mDNSWin32.h
+#   define UDPSocket_struct _UDPSocket_struct
+#   include <mDNSEmbeddedAPI.h>
+#   include <mDNSWin32.h>
+#   include <PosixCompat.h>
+#   include <Poll.h>
+#   define IFNAMSIZ 256
+static HANDLE gStopEvent = INVALID_HANDLE_VALUE;
+static mDNSBool gRunning;
+static void CALLBACK StopNotification( HANDLE event, void *context ) { gRunning = mDNSfalse; }
+static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) { SetEvent( gStopEvent ); return TRUE; }
+void setlinebuf( FILE * fp ) {}
+#else
+#   include <netdb.h>           // For gethostbyname()
+#   include <sys/socket.h>      // For AF_INET, AF_INET6, etc.
+#   include <net/if.h>          // For IF_NAMESIZE
+#   include <netinet/in.h>      // For INADDR_NONE
+#   include <arpa/inet.h>       // For inet_addr()
+#   include "mDNSPosix.h"      // Defines the specific types needed to run mDNS on this platform
+#endif
 #include "ExampleClientApp.h"
 
 //*************************************************************************************************************
 // Types and structures
 
 enum
-       {
-       // Primitive operations
-       OP_probe        = 0,
-       OP_goodbye      = 1,
-
-       // These are meta-categories;
-       // Query and Answer operations are actually subdivided into two classes:
-       // Browse  query/answer and
-       // Resolve query/answer
-       OP_query        = 2,
-       OP_answer       = 3,
-
-       // The "Browse" variants of query/answer
-       OP_browsegroup  = 2,
-       OP_browseq      = 2,
-       OP_browsea      = 3,
-
-       // The "Resolve" variants of query/answer
-       OP_resolvegroup = 4,
-       OP_resolveq     = 4,
-       OP_resolvea     = 5,
-
-       OP_NumTypes = 6
-       };
+{
+    // Primitive operations
+    OP_probe        = 0,
+    OP_goodbye      = 1,
+
+    // These are meta-categories;
+    // Query and Answer operations are actually subdivided into two classes:
+    // Browse  query/answer and
+    // Resolve query/answer
+    OP_query        = 2,
+    OP_answer       = 3,
+
+    // The "Browse" variants of query/answer
+    OP_browsegroup  = 2,
+    OP_browseq      = 2,
+    OP_browsea      = 3,
+
+    // The "Resolve" variants of query/answer
+    OP_resolvegroup = 4,
+    OP_resolveq     = 4,
+    OP_resolvea     = 5,
+
+    OP_NumTypes = 6
+};
 
 typedef struct ActivityStat_struct ActivityStat;
 struct ActivityStat_struct
-       {
-       ActivityStat *next;
-       domainname srvtype;
-       int printed;
-       int totalops;
-       int stat[OP_NumTypes];
-       };
+{
+    ActivityStat *next;
+    domainname srvtype;
+    int printed;
+    int totalops;
+    int stat[OP_NumTypes];
+};
 
 typedef struct FilterList_struct FilterList;
 struct FilterList_struct
-       {
-       FilterList *next;
-       mDNSAddr FilterAddr;
-       };
+{
+    FilterList *next;
+    mDNSAddr FilterAddr;
+};
 
 //*************************************************************************************************************
 // Constants
@@ -375,37 +112,41 @@ struct FilterList_struct
 //*************************************************************************************************************
 // Globals
 
-static mDNS mDNSStorage;                                               // mDNS core uses this to store its globals
-static mDNS_PlatformSupport PlatformStorage;   // Stores this platform's globals
+mDNS mDNSStorage;                       // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage;    // Stores this platform's globals
+mDNSexport const char ProgramName[] = "mDNSNetMonitor";
 
 struct timeval tv_start, tv_end, tv_interval;
 static int FilterInterface = 0;
 static FilterList *Filters;
 #define ExactlyOneFilter (Filters && !Filters->next)
+static mDNSBool AddressType = mDNSAddrType_IPv4;
 
-static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad
+static int NumPktQ, NumPktL, NumPktR, NumPktB;  // Query/Legacy/Response/Bad
 static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
 
 static ActivityStat *stats;
 
 #define OPBanner "Total Ops   Probe   Goodbye  BrowseQ  BrowseA ResolveQ ResolveA"
 
+mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID);
+
 //*************************************************************************************************************
 // Utilities
 
 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
 mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
 mDNSlocal mDNSu32 mprintf(const char *format, ...)
-       {
-       mDNSu32 length;
-       unsigned char buffer[512];
-       va_list ptr;
-       va_start(ptr,format);
-       length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
-       va_end(ptr);
-       printf("%s", buffer);
-       return(length);
-       }
+{
+    mDNSu32 length;
+    unsigned char buffer[512];
+    va_list ptr;
+    va_start(ptr,format);
+    length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
+    va_end(ptr);
+    printf("%s", buffer);
+    return(length);
+}
 
 //*************************************************************************************************************
 // Host Address List
@@ -413,799 +154,909 @@ mDNSlocal mDNSu32 mprintf(const char *format, ...)
 // Would benefit from a hash
 
 typedef enum
-       {
-       HostPkt_Q        = 0,           // Query
-       HostPkt_L        = 1,           // Legacy Query
-       HostPkt_R        = 2,           // Response
-       HostPkt_B        = 3,           // Bad
-       HostPkt_NumTypes = 4
-       } HostPkt_Type;
+{
+    HostPkt_Q        = 0,       // Query
+    HostPkt_L        = 1,       // Legacy Query
+    HostPkt_R        = 2,       // Response
+    HostPkt_B        = 3,       // Bad
+    HostPkt_NumTypes = 4
+} HostPkt_Type;
 
 typedef struct
-       {
-       mDNSAddr addr;
-       unsigned long pkts[HostPkt_NumTypes];
-       unsigned long totalops;
-       unsigned long stat[OP_NumTypes];
-       domainname hostname;
-       domainname revname;
-       UTF8str255 HIHardware;
-       UTF8str255 HISoftware;
-       mDNSu32    NumQueries;
-       mDNSs32    LastQuery;
-       } HostEntry;
+{
+    mDNSAddr addr;
+    unsigned long pkts[HostPkt_NumTypes];
+    unsigned long totalops;
+    unsigned long stat[OP_NumTypes];
+    domainname hostname;
+    domainname revname;
+    UTF8str255 HIHardware;
+    UTF8str255 HISoftware;
+    mDNSu32 NumQueries;
+    mDNSs32 LastQuery;
+} HostEntry;
 
 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
 
 typedef struct
-       {
-       long            num;
-       long            max;
-       HostEntry       *hosts;
-       } HostList;
+{
+    long num;
+    long max;
+    HostEntry   *hosts;
+} HostList;
 
 static HostList IPv4HostList = { 0, 0, 0 };
 static HostList IPv6HostList = { 0, 0, 0 };
 
-mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList* list)
-       {
-       long    i;
-       
-       for (i = 0; i < list->num; i++)
-               {
-               HostEntry *entry = list->hosts + i;
-               if (mDNSSameAddress(addr, &entry->addr))
-                       return entry;
-               }
-
-       return NULL;
-       }
-       
-mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList* list)
-       {
-       int i;
-       HostEntry *entry;
-       if (list->num >= list->max)
-               {
-               long newMax = list->max + 64;
-               HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
-               if (newHosts == NULL)
-                       return NULL;
-               list->max = newMax;
-               list->hosts = newHosts;
-               }
-
-       entry = list->hosts + list->num++;
-
-       entry->addr = *addr;
-       for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
-       entry->totalops = 0;
-       for (i=0; i<OP_NumTypes;      i++) entry->stat[i] = 0;
-       entry->hostname.c[0] = 0;
-       entry->revname.c[0] = 0;
-       entry->HIHardware.c[0] = 0;
-       entry->HISoftware.c[0] = 0;
-       entry->NumQueries = 0;
-
-       if (entry->addr.type == mDNSAddrType_IPv4)
-               {
-               mDNSv4Addr ip = entry->addr.ip.v4;
-               char buffer[32];
-               mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]);
-               MakeDomainNameFromDNSNameString(&entry->revname, buffer);
-               }
-
-       return(entry);
-       }
+mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList *list)
+{
+    long i;
+
+    for (i = 0; i < list->num; i++)
+    {
+        HostEntry *entry = list->hosts + i;
+        if (mDNSSameAddress(addr, &entry->addr))
+            return entry;
+    }
+
+    return NULL;
+}
+
+mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList *list)
+{
+    int i;
+    HostEntry *entry;
+    if (list->num >= list->max)
+    {
+        long newMax = list->max + 64;
+        HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
+        if (newHosts == NULL)
+            return NULL;
+        list->max = newMax;
+        list->hosts = newHosts;
+    }
+
+    entry = list->hosts + list->num++;
+
+    entry->addr = *addr;
+    for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
+    entry->totalops = 0;
+    for (i=0; i<OP_NumTypes;      i++) entry->stat[i] = 0;
+    entry->hostname.c[0] = 0;
+    entry->revname.c[0] = 0;
+    entry->HIHardware.c[0] = 0;
+    entry->HISoftware.c[0] = 0;
+    entry->NumQueries = 0;
+
+    if (entry->addr.type == mDNSAddrType_IPv4)
+    {
+        mDNSv4Addr ip = entry->addr.ip.v4;
+        char buffer[32];
+        // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+        mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]);
+        MakeDomainNameFromDNSNameString(&entry->revname, buffer);
+    }
+
+    return(entry);
+}
 
 mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t, mDNSOpaque16 id)
-       {
-       if (ExactlyOneFilter) return(NULL);
-       else
-               {
-               HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
-               HostEntry *entry = FindHost(addr, list);
-               if (!entry) entry = AddHost(addr, list);
-               if (!entry) return(NULL);
-               // Don't count our own interrogation packets
-               if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++;
-               return(entry);
-               }
-       }
+{
+    if (ExactlyOneFilter) return(NULL);
+    else
+    {
+        HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
+        HostEntry *entry = FindHost(addr, list);
+        if (!entry) entry = AddHost(addr, list);
+        if (!entry) return(NULL);
+        // Don't count our own interrogation packets
+        if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++;
+        return(entry);
+    }
+}
 
 mDNSlocal void RecordHostInfo(HostEntry *entry, const ResourceRecord *const pktrr)
-       {
-       if (!entry->hostname.c[0])
-               {
-               if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA)
-                       {
-                       // Should really check that the rdata in the address record matches the source address of this packet
-                       entry->NumQueries = 0;
-                       AssignDomainName(&entry->hostname, pktrr->name);
-                       }
-
-               if (pktrr->rrtype == kDNSType_PTR)
-                       if (SameDomainName(&entry->revname, pktrr->name))
-                               {
-                               entry->NumQueries = 0;
-                               AssignDomainName(&entry->hostname, &pktrr->rdata->u.name);
-                               }
-               }
-       else if (pktrr->rrtype == kDNSType_HINFO)
-               {
-               RDataBody *rd = &pktrr->rdata->u;
-               mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
-               mDNSu8 *hw = rd->txt.c;
-               mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0];
-               if (sw + 1 + sw[0] <= rdend)
-                       {
-                       AssignDomainName(&entry->hostname, pktrr->name);
-                       mDNSPlatformMemCopy(hw, entry->HIHardware.c, 1 + (mDNSu32)hw[0]);
-                       mDNSPlatformMemCopy(sw, entry->HISoftware.c, 1 + (mDNSu32)sw[0]);
-                       }
-               }
-       }
+{
+    if (!entry->hostname.c[0])
+    {
+        if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA)
+        {
+            // Should really check that the rdata in the address record matches the source address of this packet
+            entry->NumQueries = 0;
+            AssignDomainName(&entry->hostname, pktrr->name);
+        }
+
+        if (pktrr->rrtype == kDNSType_PTR)
+            if (SameDomainName(&entry->revname, pktrr->name))
+            {
+                entry->NumQueries = 0;
+                AssignDomainName(&entry->hostname, &pktrr->rdata->u.name);
+            }
+    }
+    else if (pktrr->rrtype == kDNSType_HINFO)
+    {
+        RDataBody *rd = &pktrr->rdata->u;
+        mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
+        mDNSu8 *hw = rd->txt.c;
+        mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0];
+        if (sw + 1 + sw[0] <= rdend)
+        {
+            AssignDomainName(&entry->hostname, pktrr->name);
+            mDNSPlatformMemCopy(entry->HIHardware.c, hw, 1 + (mDNSu32)hw[0]);
+            mDNSPlatformMemCopy(entry->HISoftware.c, sw, 1 + (mDNSu32)sw[0]);
+        }
+    }
+}
 
 mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID)
-       {
-       const mDNSOpaque16 id = { { 0xFF, 0xFF } };
-       DNSMessage query;
-       mDNSu8       *qptr        = query.data;
-       const mDNSu8 *const limit = query.data + sizeof(query.data);
-       const mDNSAddr *target    = &entry->addr;
-       InitializeDNSMessage(&query.h, id, QueryFlags);
-       qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN);
-       entry->LastQuery = m->timenow;
-       entry->NumQueries++;
-
-       // Note: When there are multiple mDNSResponder agents running on a single machine
-       // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder)
-       // it is possible that unicast queries may not go to the primary system responder.
-       // We try the first query using unicast, but if that doesn't work we try again via multicast.
-       if (entry->NumQueries > 2)
-               {
-               target = &AllDNSLinkGroup_v4;
-               }
-       else
-               {
-               //mprintf("%#a Q\n", target);
-               InterfaceID = mDNSInterface_Any;        // Send query from our unicast reply socket
-               m->ExpectUnicastResponse = m->timenow;
-               }
-
-       mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, target, MulticastDNSPort, mDNSNULL, mDNSNULL);
-       }
+{
+    const mDNSOpaque16 id = { { 0xFF, 0xFF } };
+    DNSMessage query;
+    mDNSu8       *qptr        = query.data;
+    const mDNSu8 *const limit = query.data + sizeof(query.data);
+    const mDNSAddr *target    = &entry->addr;
+    InitializeDNSMessage(&query.h, id, QueryFlags);
+    qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN);
+    entry->LastQuery = m->timenow;
+    entry->NumQueries++;
+
+    // Note: When there are multiple mDNSResponder agents running on a single machine
+    // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder)
+    // it is possible that unicast queries may not go to the primary system responder.
+    // We try the first query using unicast, but if that doesn't work we try again via multicast.
+    if (entry->NumQueries > 2)
+    {
+        target = &AllDNSLinkGroup_v4;
+    }
+    else
+    {
+        //mprintf("%#a Q\n", target);
+        InterfaceID = mDNSInterface_Any;    // Send query from our unicast reply socket
+    }
+
+    mDNSSendDNSMessage(m, &query, qptr, InterfaceID, mDNSNULL, mDNSNULL, target, MulticastDNSPort, mDNSNULL, mDNSfalse);
+}
 
 mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID)
-       {
-       // If we've done four queries without answer, give up
-       if (entry->NumQueries >= 4) return;
-
-       // If we've done a query in the last second, give the host a chance to reply before trying again
-       if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return;
-
-       // If we don't know the host name, try to find that first
-       if (!entry->hostname.c[0])
-               {
-               if (entry->revname.c[0])
-                       {
-                       SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID);
-                       //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries);
-                       }
-               }
-       // If we have the host name but no HINFO, now ask for that
-       else if (!entry->HIHardware.c[0])
-               {
-               SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID);
-               //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries);
-               }
-       }
+{
+    // If we've done four queries without answer, give up
+    if (entry->NumQueries >= 4) return;
+
+    // If we've done a query in the last second, give the host a chance to reply before trying again
+    if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return;
+
+    // If we don't know the host name, try to find that first
+    if (!entry->hostname.c[0])
+    {
+        if (entry->revname.c[0])
+        {
+            SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID);
+            //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries);
+        }
+    }
+    // If we have the host name but no HINFO, now ask for that
+    else if (!entry->HIHardware.c[0])
+    {
+        SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID);
+        //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries);
+    }
+}
 
 mDNSlocal int CompareHosts(const void *p1, const void *p2)
-       {
-       return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
-       }
+{
+    return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
+}
 
 mDNSlocal void ShowSortedHostList(HostList *list, int max)
-       {
-       HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num];
-       qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts);
-       if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, "    Pkts    Query   LegacyQ Response");
-       for (e = &list->hosts[0]; e < end; e++)
-               {
-               int len = mprintf("%#-25a", &e->addr);
-               if (len > 25) mprintf("\n%25s", "");
-               mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e->totalops,
-                       e->stat[OP_probe], e->stat[OP_goodbye],
-                       e->stat[OP_browseq], e->stat[OP_browsea],
-                       e->stat[OP_resolveq], e->stat[OP_resolvea]);
-               mprintf(" %8lu %8lu %8lu %8lu",
-                       HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]);
-               if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]);
-               mprintf("\n");
-               if (!e->HISoftware.c[0] && e->NumQueries > 2)
-                       mDNSPlatformMemCopy("\x27*** Unknown (Jaguar, Windows, etc.) ***", &e->HISoftware, 0x28);
-               if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0])
-                       mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c);
-               }
-       }
+{
+    HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num];
+    qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts);
+    if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, "    Pkts    Query   LegacyQ Response");
+    for (e = &list->hosts[0]; e < end; e++)
+    {
+        int len = mprintf("%#-25a", &e->addr);
+        if (len > 25) mprintf("\n%25s", "");
+        mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e->totalops,
+                e->stat[OP_probe], e->stat[OP_goodbye],
+                e->stat[OP_browseq], e->stat[OP_browsea],
+                e->stat[OP_resolveq], e->stat[OP_resolvea]);
+        mprintf(" %8lu %8lu %8lu %8lu",
+                HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]);
+        if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]);
+        mprintf("\n");
+        if (!e->HISoftware.c[0] && e->NumQueries > 2)
+            mDNSPlatformMemCopy(&e->HISoftware, "\x27*** Unknown (Jaguar, Windows, etc.) ***", 0x28);
+        if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0])
+            mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c);
+    }
+}
 
 //*************************************************************************************************************
 // Receive and process packets
 
-mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
-       {
-       int i, len;
-       const mDNSu8 *src = fqdn->c;
-       mDNSu8 *dst = srvtype->c;
-       
-       len = *src;
-       if (len == 0 || len >= 0x40) return(mDNSfalse);
-       if (src[1] != '_') src += 1 + len;
-       
-       len = *src;
-       if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
-       for (i=0; i<=len; i++) *dst++ = *src++;
-
-       len = *src;
-       if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
-       for (i=0; i<=len; i++) *dst++ = *src++;
-
-       *dst++ = 0;             // Put the null root label on the end of the service type
-
-       return(mDNStrue);
-       }
-
-mDNSlocal void recordstat(HostEntry *entry, domainname *fqdn, int op, mDNSu16 rrtype)
-       {
-       ActivityStat **s = &stats;
-       domainname srvtype;
-
-       if (op != OP_probe)
-               {
-               if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
-               else if (rrtype != kDNSType_PTR) return;
-               }
-       
-       if (!ExtractServiceType(fqdn, &srvtype)) return;
-
-       while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
-       if (!*s)
-               {
-               int i;
-               *s = malloc(sizeof(ActivityStat));
-               if (!*s) exit(-1);
-               (*s)->next     = NULL;
-               (*s)->srvtype  = srvtype;
-               (*s)->printed  = 0;
-               (*s)->totalops = 0;
-               for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
-               }
-
-       (*s)->totalops++;
-       (*s)->stat[op]++;
-       if (entry)
-               {
-               entry->totalops++;
-               entry->stat[op]++;
-               }
-       }
+mDNSlocal mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
+{
+    int i, len;
+    const mDNSu8 *src = fqdn->c;
+    mDNSu8 *dst = srvtype->c;
+
+    len = *src;
+    if (len == 0 || len >= 0x40) return(mDNSfalse);
+    if (src[1] != '_') src += 1 + len;
+
+    len = *src;
+    if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
+    for (i=0; i<=len; i++) *dst++ = *src++;
+
+    len = *src;
+    if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
+    for (i=0; i<=len; i++) *dst++ = *src++;
+
+    *dst++ = 0;     // Put the null root label on the end of the service type
+
+    return(mDNStrue);
+}
+
+mDNSlocal void recordstat(HostEntry *entry, const domainname *fqdn, int op, mDNSu16 rrtype)
+{
+    ActivityStat **s = &stats;
+    domainname srvtype;
+
+    if (op != OP_probe)
+    {
+        if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
+        else if (rrtype != kDNSType_PTR) return;
+    }
+
+    if (!ExtractServiceType(fqdn, &srvtype)) return;
+
+    while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
+    if (!*s)
+    {
+        int i;
+        *s = malloc(sizeof(ActivityStat));
+        if (!*s) exit(-1);
+        (*s)->next     = NULL;
+        (*s)->srvtype  = srvtype;
+        (*s)->printed  = 0;
+        (*s)->totalops = 0;
+        for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
+    }
+
+    (*s)->totalops++;
+    (*s)->stat[op]++;
+    if (entry)
+    {
+        entry->totalops++;
+        entry->stat[op]++;
+    }
+}
 
 mDNSlocal void printstats(int max)
-       {
-       int i;
-       if (!stats) return;
-       for (i=0; i<max; i++)
-               {
-               int max = 0;
-               ActivityStat *s, *m = NULL;
-               for (s = stats; s; s=s->next)
-                       if (!s->printed && max < s->totalops)
-                               { m = s; max = s->totalops; }
-               if (!m) return;
-               m->printed = mDNStrue;
-               if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner);
-               mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.c, m->totalops, m->stat[OP_probe],
-                       m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]);
-               }
-       }
+{
+    int i;
+    if (!stats) return;
+    for (i=0; i<max; i++)
+    {
+        int max_val = 0;
+        ActivityStat *s, *m = NULL;
+        for (s = stats; s; s=s->next)
+            if (!s->printed && max_val < s->totalops)
+            { m = s; max_val = s->totalops; }
+        if (!m) return;
+        m->printed = mDNStrue;
+        if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner);
+        mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.c, m->totalops, m->stat[OP_probe],
+                m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]);
+    }
+}
 
 mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end,
-       DNSQuestion *q, LargeCacheRecord *pkt)
-       {
-       int i;
-       for (i = 0; i < query->h.numAuthorities; i++)
-               {
-               const mDNSu8 *p2 = ptr;
-               ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt);
-               if (!ptr) break;
-               if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
-               }
-       return(mDNSNULL);
-       }
+                                   DNSQuestion *q, LargeCacheRecord *pkt)
+{
+    int i;
+    for (i = 0; i < query->h.numAuthorities; i++)
+    {
+        const mDNSu8 *p2 = ptr;
+        ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt);
+        if (!ptr) break;
+        if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
+    }
+    return(mDNSNULL);
+}
 
 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)
-       {
-       const char *const ptype =   (msg->h.flags.b[0] & kDNSFlag0_QR_Response)             ? "-R- " :
-                                                               (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
-
-       struct timeval tv;
-       struct tm tm;
-       const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID);
-       char if_name[IFNAMSIZ];         // Older Linux distributions don't define IF_NAMESIZE
-       if_indextoname(index, if_name);
-       gettimeofday(&tv, NULL);
-       localtime_r((time_t*)&tv.tv_sec, &tm);
-       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);
-
-       mprintf("%#-16a %s             Q:%3d  Ans:%3d  Auth:%3d  Add:%3d  Size:%5d bytes",
-               srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg);
-
-       if (msg->h.id.NotAnInteger) mprintf("  ID:%u", mDNSVal16(msg->h.id));
-
-       if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf("   To: %#a", dstaddr);
-
-       if (msg->h.flags.b[0] & kDNSFlag0_TC)
-               {
-               if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf("   Truncated");
-               else                                           mprintf("   Truncated (KA list continues in next packet)");
-               }
-       mprintf("\n");
-       }
+{
+    const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response)             ? "-R- " :
+                              (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
+    const unsigned length = end - (mDNSu8 *)msg;
+    struct timeval tv;
+    struct tm tm;
+    const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse);
+    char if_name[IFNAMSIZ];     // Older Linux distributions don't define IF_NAMESIZE
+    if_indextoname(index, if_name);
+    gettimeofday(&tv, NULL);
+    localtime_r((time_t*)&tv.tv_sec, &tm);
+    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);
+
+    mprintf("%#-16a %s             Q:%3d  Ans:%3d  Auth:%3d  Add:%3d  Size:%5d bytes",
+            srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, length);
+
+    if (msg->h.id.NotAnInteger) mprintf("  ID:%u", mDNSVal16(msg->h.id));
+
+    if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf("   To: %#a", dstaddr);
+
+    if (msg->h.flags.b[0] & kDNSFlag0_TC)
+    {
+        if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf("   Truncated");
+        else mprintf("   Truncated (KA list continues in next packet)");
+    }
+
+    mprintf("\n");
+
+    if (length < sizeof(DNSMessageHeader) + NormalMaxDNSMessageData - 192)
+        if (msg->h.flags.b[0] & kDNSFlag0_TC)
+            mprintf("%#-16a **** WARNING: Packet suspiciously small. Payload size (excluding IP and UDP headers)\n"
+                    "%#-16a **** should usually be closer to %d bytes before truncation becomes necessary.\n",
+                    srcaddr, srcaddr, sizeof(DNSMessageHeader) + NormalMaxDNSMessageData);
+}
+
+mDNSlocal void DisplaySizeCheck(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, int num_opts)
+{
+    const unsigned length = end - (mDNSu8 *)msg;
+    const int num_records = msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals - num_opts;
+
+    if (length > sizeof(DNSMessageHeader) + NormalMaxDNSMessageData)
+        if (num_records > 1)
+            mprintf("%#-16a **** ERROR: Oversized packet with %d records.\n"
+                    "%#-16a **** Many network devices cannot receive packets larger than %d bytes.\n"
+                    "%#-16a **** To minimize interoperability failures, oversized packets MUST be limited to a single resource record.\n",
+                    srcaddr, num_records, srcaddr, 40 + 8 + sizeof(DNSMessageHeader) + NormalMaxDNSMessageData, srcaddr);
+}
 
 mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
-       {
-       static const char hexchars[16] = "0123456789ABCDEF";
-       #define MaxWidth 132
-       char buffer[MaxWidth+8];
-       char *p = buffer;
-
-       RDataBody *rd = &pktrr->rdata->u;
-       mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
-       int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name->c);
-
-       switch(pktrr->rrtype)
-               {
-               case kDNSType_A:        n += mprintf("%.4a", &rd->ipv4); break;
-               case kDNSType_PTR:      n += mprintf("%##.*s", MaxWidth - n, rd->name.c); break;
-               case kDNSType_HINFO:// same as kDNSType_TXT below
-               case kDNSType_TXT:      {
-                                                       mDNSu8 *t = rd->txt.c;
-                                                       while (t < rdend && t[0] && p < buffer+MaxWidth)
-                                                               {
-                                                               int i;
-                                                               for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
-                                                                       {
-                                                                       if (t[i] == '\\') *p++ = '\\';
-                                                                       if (t[i] >= ' ') *p++ = t[i];
-                                                                       else
-                                                                               {
-                                                                               *p++ = '\\';
-                                                                               *p++ = '0';
-                                                                               *p++ = 'x';
-                                                                               *p++ = hexchars[t[i] >> 4];
-                                                                               *p++ = hexchars[t[i] & 0xF];
-                                                                               }
-                                                                       }
-                                                               t += 1+t[0];
-                                                               if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
-                                                               }
-                                                       *p++ = 0;
-                                                       n += mprintf("%.*s", MaxWidth - n, buffer);
-                                                       } break;
-               case kDNSType_AAAA:     n += mprintf("%.16a", &rd->ipv6); break;
-               case kDNSType_SRV:      n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break;
-               default:                        {
-                                                       mDNSu8 *s = rd->data;
-                                                       while (s < rdend && p < buffer+MaxWidth)
-                                                               {
-                                                               if (*s == '\\') *p++ = '\\';
-                                                               if (*s >= ' ') *p++ = *s;
-                                                               else
-                                                                       {
-                                                                       *p++ = '\\';
-                                                                       *p++ = '0';
-                                                                       *p++ = 'x';
-                                                                       *p++ = hexchars[*s >> 4];
-                                                                       *p++ = hexchars[*s & 0xF];
-                                                                       }
-                                                               s++;
-                                                               }
-                                                       *p++ = 0;
-                                                       n += mprintf("%.*s", MaxWidth - n, buffer);
-                                                       } break;
-               }
-       
-       mprintf("\n");
-       }
+{
+    static const char hexchars[16] = "0123456789ABCDEF";
+    #define MaxWidth 132
+    char buffer[MaxWidth+8];
+    char *p = buffer;
+
+    RDataBody *rd = &pktrr->rdata->u;
+    mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
+    int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name->c);
+
+    if (pktrr->RecordType == kDNSRecordTypePacketNegative) { mprintf("**** ERROR: FAILED TO READ RDATA ****\n"); return; }
+
+    // The kDNSType_OPT case below just calls GetRRDisplayString_rdb
+    // Perhaps more (or all?) of the cases should do that?
+    switch(pktrr->rrtype)
+    {
+    case kDNSType_A:    mprintf("%.4a", &rd->ipv4); break;
+    case kDNSType_PTR:  mprintf("%##.*s", MaxWidth - n, rd->name.c); break;
+    case kDNSType_HINFO:    // same as kDNSType_TXT below
+    case kDNSType_TXT:  {
+        mDNSu8 *t = rd->txt.c;
+        while (t < rdend && t[0] && p < buffer+MaxWidth)
+        {
+            int i;
+            for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
+            {
+                if (t[i] == '\\') *p++ = '\\';
+                if (t[i] >= ' ') *p++ = t[i];
+                else
+                {
+                    *p++ = '\\';
+                    *p++ = '0';
+                    *p++ = 'x';
+                    *p++ = hexchars[t[i] >> 4];
+                    *p++ = hexchars[t[i] & 0xF];
+                }
+            }
+            t += 1+t[0];
+            if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
+        }
+        *p++ = 0;
+        mprintf("%.*s", MaxWidth - n, buffer);
+    } break;
+    case kDNSType_AAAA: mprintf("%.16a", &rd->ipv6); break;
+    case kDNSType_SRV:  mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break;
+    case kDNSType_OPT:  {
+        char b[MaxMsg];
+        // Quick hack: we don't want the prefix that GetRRDisplayString_rdb puts at the start of its
+        // string, because it duplicates the name and rrtype we already display, so we compute the
+        // length of that prefix and strip that many bytes off the beginning of the string we display.
+        mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype));
+        GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b);
+        mprintf("%.*s", MaxWidth - n, b + striplen);
+    } break;
+    case kDNSType_NSEC: {
+        char b[MaxMsg];
+        // See the quick hack above
+        mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype));
+        GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b);
+        mprintf("%s", b+striplen);
+    } break;
+    default:            {
+        mDNSu8 *s = rd->data;
+        while (s < rdend && p < buffer+MaxWidth)
+        {
+            if (*s == '\\') *p++ = '\\';
+            if (*s >= ' ') *p++ = *s;
+            else
+            {
+                *p++ = '\\';
+                *p++ = '0';
+                *p++ = 'x';
+                *p++ = hexchars[*s >> 4];
+                *p++ = hexchars[*s & 0xF];
+            }
+            s++;
+        }
+        *p++ = 0;
+        mprintf("%.*s", MaxWidth - n, buffer);
+    } break;
+    }
+
+    mprintf("\n");
+}
 
 mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
-       {
-       while (ptr < end)
-               {
-               int i;
-               for (i=0; i<16; i++)
-                       if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
-                       else mprintf("   ");
-               for (i=0; i<16; i++)
-                       if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
-               ptr += 16;
-               mprintf("\n");
-               }
-       }
+{
+    while (ptr < end)
+    {
+        int i;
+        for (i=0; i<16; i++)
+            if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
+            else mprintf("   ");
+        for (i=0; i<16; i++)
+            if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
+        ptr += 16;
+        mprintf("\n");
+    }
+}
 
 mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
-       {
-       mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
-       HexDump(ptr, end);
-       }
+{
+    mprintf("%#-16a **** ERROR: FAILED TO READ %s ****\n", srcaddr, msg);
+    HexDump(ptr, end);
+}
 
 mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
-       const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
-       {
-       int i;
-       const mDNSu8 *ptr = msg->data;
-       const mDNSu8 *auth = LocateAuthorities(msg, end);
-       mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger);
-       HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id);
-       LargeCacheRecord pkt;
-
-       DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
-       if (msg->h.id.NotAnInteger != 0xFFFF)
-               {
-               if (MQ) NumPktQ++; else NumPktL++;
-               }
-
-       for (i=0; i<msg->h.numQuestions; i++)
-               {
-               DNSQuestion q;
-               mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q);
-               mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse;
-               q.qclass &= ~kDNSQClass_UnicastResponse;
-               if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; }
-               ptr = p2;
-               p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
-               if (p2)
-                       {
-                       NumProbes++;
-                       DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec);
-                       recordstat(entry, &q.qname, OP_probe, q.qtype);
-                       p2 = (mDNSu8 *)skipDomainName(msg, p2, end);
-                       // Having displayed this update record, clear type and class so we don't display the same one again.
-                       p2[0] = p2[1] = p2[2] = p2[3] = 0;
-                       }
-               else
-                       {
-                       const char *ptype = ucbit ? "(QU)" : "(QM)";
-                       if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++;
-                       else { NumLegacy++; ptype = "(LQ)"; }
-                       mprintf("%#-16a %-5s %-5s      %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c);
-                       if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype);
-                       }
-               }
-
-       for (i=0; i<msg->h.numAnswers; i++)
-               {
-               const mDNSu8 *ep = ptr;
-               ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
-               if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; }
-               DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec);
-               
-               // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
-               // the same as a single query, to more accurately reflect the burden on the network
-               // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
-               if (msg->h.numQuestions == 0 && i == 0)
-                       recordstat(entry, pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype);
-               }
-
-       for (i=0; i<msg->h.numAuthorities; i++)
-               {
-               const mDNSu8 *ep = ptr;
-               ptr = skipResourceRecord(msg, ptr, end);
-               if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
-               }
-
-       if (entry) AnalyseHost(m, entry, InterfaceID);
-       }
+                            const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
+{
+    int i;
+    int num_opts = 0;
+    const mDNSu8 *ptr = msg->data;
+    const mDNSu8 *auth = LocateAuthorities(msg, end);
+    mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger);
+    HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id);
+    LargeCacheRecord pkt;
+
+    DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+    if (msg->h.id.NotAnInteger != 0xFFFF)
+    {
+        if (MQ) NumPktQ++; else NumPktL++;
+    }
+
+    for (i=0; i<msg->h.numQuestions; i++)
+    {
+        DNSQuestion q;
+        mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q);
+        mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse;
+        q.qclass &= ~kDNSQClass_UnicastResponse;
+        if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; }
+        ptr = p2;
+        p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
+        if (p2)
+        {
+            NumProbes++;
+            DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec);
+            recordstat(entry, &q.qname, OP_probe, q.qtype);
+            p2 = (mDNSu8 *)skipDomainName(msg, p2, end);
+            // Having displayed this update record, clear type and class so we don't display the same one again.
+            p2[0] = p2[1] = p2[2] = p2[3] = 0;
+        }
+        else
+        {
+            const char *ptype = ucbit ? "(QU)" : "(QM)";
+            if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++;
+            else { NumLegacy++; ptype = "(LQ)"; }
+            mprintf("%#-16a %-5s %-5s      %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c);
+            if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype);
+        }
+    }
+
+    for (i=0; i<msg->h.numAnswers; i++)
+    {
+        const mDNSu8 *ep = ptr;
+        ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+        if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; }
+        DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec);
+        if (pkt.r.resrec.rrtype == kDNSType_OPT)
+            { num_opts++; mprintf("%#-16a **** ERROR: OPT RECORD IN ANSWER SECTION ****\n", srcaddr); }
+
+        // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
+        // the same as a single query, to more accurately reflect the burden on the network
+        // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
+        if (msg->h.numQuestions == 0 && i == 0)
+            recordstat(entry, pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype);
+    }
+
+    for (i=0; i<msg->h.numAuthorities; i++)
+    {
+        const mDNSu8 *ep = ptr;
+        ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
+        if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
+        // After we display an Update record with its matching question (above) we zero out its type and class
+        // If any remain that haven't been zero'd out, display them here
+        if (pkt.r.resrec.rrtype || pkt.r.resrec.rrclass) DisplayResourceRecord(srcaddr, "(AU)", &pkt.r.resrec);
+        if (pkt.r.resrec.rrtype == kDNSType_OPT)
+            { num_opts++; mprintf("%#-16a **** ERROR: OPT RECORD IN AUTHORITY SECTION ****\n", srcaddr); }
+    }
+
+    for (i=0; i<msg->h.numAdditionals; i++)
+    {
+        const mDNSu8 *ep = ptr;
+        ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
+        if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
+        DisplayResourceRecord(srcaddr, pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : "(AD)", &pkt.r.resrec);
+        if (pkt.r.resrec.rrtype == kDNSType_OPT) num_opts++;
+    }
+
+    DisplaySizeCheck(msg, end, srcaddr, num_opts);
+
+    // We don't hexdump the DNSMessageHeader here because those six fields (id, flags, numQuestions, numAnswers, numAuthorities, numAdditionals)
+    // have already been swapped to host byte order and displayed, so including them in the hexdump is confusing
+    if (num_opts > 1) { mprintf("%#-16a **** ERROR: MULTIPLE OPT RECORDS ****\n", srcaddr); HexDump(msg->data, end); }
+
+    if (entry) AnalyseHost(m, entry, InterfaceID);
+}
 
 mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end,
-       const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
-       {
-       int i;
-       const mDNSu8 *ptr = msg->data;
-       HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
-       LargeCacheRecord pkt;
-
-       DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
-       if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++;
-
-       for (i=0; i<msg->h.numQuestions; i++)
-               {
-               DNSQuestion q;
-               const mDNSu8 *ep = ptr;
-               ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
-               if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
-               if (mDNSAddrIsDNSMulticast(dstaddr))
-                       mprintf("%#-16a (?)   **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
-               else
-                       mprintf("%#-16a (Q)   %-5s      %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
-               }
-
-       for (i=0; i<msg->h.numAnswers; i++)
-               {
-               const mDNSu8 *ep = ptr;
-               ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
-               if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; }
-               if (pkt.r.resrec.rroriginalttl)
-                       {
-                       NumAnswers++;
-                       DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec);
-                       if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype);
-                       if (entry) RecordHostInfo(entry, &pkt.r.resrec);
-                       }
-               else
-                       {
-                       NumGoodbyes++;
-                       DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
-                       recordstat(entry, pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
-                       }
-               }
-
-       for (i=0; i<msg->h.numAuthorities; i++)
-               {
-               const mDNSu8 *ep = ptr;
-               ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
-               if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
-               mprintf("%#-16a (?)  **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
-                       srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c);
-               }
-
-       for (i=0; i<msg->h.numAdditionals; i++)
-               {
-               const mDNSu8 *ep = ptr;
-               ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
-               if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
-               NumAdditionals++;
-               DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec);
-               if (entry) RecordHostInfo(entry, &pkt.r.resrec);
-               }
-       
-       if (entry) AnalyseHost(m, entry, InterfaceID);
-       }
+                               const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
+{
+    int i;
+    int num_opts = 0;
+    const mDNSu8 *ptr = msg->data;
+    HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
+    LargeCacheRecord pkt;
+
+    DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+    if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++;
+
+    for (i=0; i<msg->h.numQuestions; i++)
+    {
+        DNSQuestion q;
+        const mDNSu8 *ep = ptr;
+        ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
+        if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
+        if (mDNSAddrIsDNSMulticast(dstaddr))
+            mprintf("%#-16a (?)   **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
+        else
+            mprintf("%#-16a (Q)   %-5s      %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
+    }
+
+    for (i=0; i<msg->h.numAnswers; i++)
+    {
+        const mDNSu8 *ep = ptr;
+        ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+        if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; }
+        if (pkt.r.resrec.rroriginalttl)
+        {
+            NumAnswers++;
+            DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec);
+            if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype);
+            if (entry) RecordHostInfo(entry, &pkt.r.resrec);
+        }
+        else
+        {
+            NumGoodbyes++;
+            DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
+            recordstat(entry, pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
+        }
+        if (pkt.r.resrec.rrtype == kDNSType_OPT)
+            { num_opts++; mprintf("%#-16a **** ERROR: OPT RECORD IN ANSWER SECTION ****\n", srcaddr); }
+    }
+
+    for (i=0; i<msg->h.numAuthorities; i++)
+    {
+        const mDNSu8 *ep = ptr;
+        ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
+        if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
+        DisplayResourceRecord(srcaddr, "(AU)", &pkt.r.resrec);
+        if (pkt.r.resrec.rrtype == kDNSType_OPT)
+            { num_opts++; mprintf("%#-16a **** ERROR: OPT RECORD IN AUTHORITY SECTION ****\n", srcaddr); }
+        else if (pkt.r.resrec.rrtype != kDNSType_NSEC3)
+            mprintf("%#-16a (?)  **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
+                srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c);
+    }
+
+    for (i=0; i<msg->h.numAdditionals; i++)
+    {
+        const mDNSu8 *ep = ptr;
+        ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
+        if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
+        NumAdditionals++;
+        if (pkt.r.resrec.rrtype == kDNSType_OPT) num_opts++;
+        DisplayResourceRecord(srcaddr,
+                              pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)",
+                              &pkt.r.resrec);
+        if (entry) RecordHostInfo(entry, &pkt.r.resrec);
+    }
+
+    DisplaySizeCheck(msg, end, srcaddr, num_opts);
+
+    // We don't hexdump the DNSMessageHeader here because those six fields (id, flags, numQuestions, numAnswers, numAuthorities, numAdditionals)
+    // have already been swapped to host byte order and displayed, so including them in the hexdump is confusing
+    if (num_opts > 1) { mprintf("%#-16a **** ERROR: MULTIPLE OPT RECORDS ****\n", srcaddr); HexDump(msg->data, end); }
+
+    if (entry) AnalyseHost(m, entry, InterfaceID);
+}
 
 mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID)
-       {
-       int i;
-       const mDNSu8 *ptr = LocateAnswers(msg, end);
-       HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
-       //mprintf("%#a R\n", srcaddr);
-
-       for (i=0; i<msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++)
-               {
-               LargeCacheRecord pkt;
-               ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
-               if (pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec);
-               }
-       }
+{
+    int i;
+    const mDNSu8 *ptr = LocateAnswers(msg, end);
+    HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
+    //mprintf("%#a R\n", srcaddr);
+
+    for (i=0; i<msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++)
+    {
+        LargeCacheRecord pkt;
+        ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+        if (ptr && pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec);
+    }
+}
 
 mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
-       {
-       FilterList *f;
-       if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
-       for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
-       return(mDNSfalse);
-       }
+{
+    FilterList *f;
+    if (!Filters) return(srcaddr->type == AddressType);
+    for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
+    return(mDNSfalse);
+}
 
 mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
-       const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID)
-       {
-       const mDNSu8 StdQ = kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery;
-       const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
-       const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
-       mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
-       int goodinterface = (FilterInterface == 0);
-
-       (void)dstaddr;  // Unused
-       (void)dstport;  // Unused
-       
-       // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
-       msg->h.numQuestions   = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
-       msg->h.numAnswers     = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
-       msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] <<  8 | ptr[5]);
-       msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] <<  8 | ptr[7]);
-
-       // For now we're only interested in monitoring IPv4 traffic.
-       // All IPv6 packets should just be duplicates of the v4 packets.
-       if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID));
-       if (goodinterface && AddressMatchesFilterList(srcaddr))
-               {
-               mDNS_Lock(m);
-               if (!mDNSAddrIsDNSMulticast(dstaddr))
-                       {
-                       if      (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr);
-                       else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr,                   InterfaceID);
-                       }
-               else
-                       {
-                       if      (QR_OP == StdQ) DisplayQuery          (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
-                       else if (QR_OP == StdR) DisplayResponse       (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
-                       else
-                               {
-                               debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
-                               GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id);
-                               NumPktB++;
-                               }
-                       }
-               mDNS_Unlock(m);
-               }
-       }
+                                const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID)
+{
+    const mDNSu8 StdQ = kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery;
+    const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+    const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+    mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
+    int goodinterface = (FilterInterface == 0);
+
+    (void)dstaddr;  // Unused
+    (void)dstport;  // Unused
+
+    // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+    msg->h.numQuestions   = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
+    msg->h.numAnswers     = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
+    msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] <<  8 | ptr[5]);
+    msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] <<  8 | ptr[7]);
+
+    // For now we're only interested in monitoring IPv4 traffic.
+    // All IPv6 packets should just be duplicates of the v4 packets.
+    if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse));
+    if (goodinterface && AddressMatchesFilterList(srcaddr))
+    {
+        mDNS_Lock(m);
+        if (!mDNSAddrIsDNSMulticast(dstaddr))
+        {
+            if      (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr);
+            else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr,                   InterfaceID);
+        }
+        else
+        {
+            if      (QR_OP == StdQ) DisplayQuery          (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+            else if (QR_OP == StdR) DisplayResponse       (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+            else
+            {
+                debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
+                GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id);
+                NumPktB++;
+            }
+        }
+        mDNS_Unlock(m);
+    }
+}
 
 mDNSlocal mStatus mDNSNetMonitor(void)
-       {
-       struct tm tm;
-       int h, m, s, mul, div, TotPkt;
-       sigset_t signals;
-       
-       mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage,
-               mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
-               mDNS_Init_DontAdvertiseLocalAddresses,
-               mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
-       if (status) return(status);
-
-       gettimeofday(&tv_start, NULL);
-       mDNSPosixListenForSignalInEventLoop(SIGINT);
-       mDNSPosixListenForSignalInEventLoop(SIGTERM);
-
-       do 
-               {
-               struct timeval  timeout = { 0x3FFFFFFF, 0 };    // wait until SIGINT or SIGTERM
-               mDNSBool                gotSomething;
-               mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
-               }
-       while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
-       
-       // Now display final summary
-       TotPkt = NumPktQ + NumPktL + NumPktR;
-       gettimeofday(&tv_end, NULL);
-       tv_interval = tv_end;
-       if (tv_start.tv_usec > tv_interval.tv_usec)
-               { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; }
-       tv_interval.tv_sec  -= tv_start.tv_sec;
-       tv_interval.tv_usec -= tv_start.tv_usec;
-       h = (tv_interval.tv_sec / 3600);
-       m = (tv_interval.tv_sec % 3600) / 60;
-       s = (tv_interval.tv_sec % 60);
-       if (tv_interval.tv_sec > 10)
-               {
-               mul = 60;
-               div = tv_interval.tv_sec;
-               }
-       else
-               {
-               mul = 60000;
-               div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
-               if (div == 0) div=1;
-               }
-
-       mprintf("\n\n");
-       localtime_r((time_t*)&tv_start.tv_sec, &tm);
-       mprintf("Started      %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec);
-       localtime_r((time_t*)&tv_end.tv_sec, &tm);
-       mprintf("End          %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec);
-       mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec);
-       if (!Filters)
-               {
-               mprintf("Unique source addresses seen on network:");
-               if (IPv4HostList.num) mprintf(" %ld (IPv4)", IPv4HostList.num);
-               if (IPv6HostList.num) mprintf(" %ld (IPv6)", IPv6HostList.num);
-               if (!IPv4HostList.num && !IPv6HostList.num) mprintf(" None");
-               mprintf("\n");
-               }
-       mprintf("\n");
-       mprintf("Modern Query        Packets:      %7d   (avg%5d/min)\n", NumPktQ,        NumPktQ        * mul / div);
-       mprintf("Legacy Query        Packets:      %7d   (avg%5d/min)\n", NumPktL,        NumPktL        * mul / div);
-       mprintf("Multicast Response  Packets:      %7d   (avg%5d/min)\n", NumPktR,        NumPktR        * mul / div);
-       mprintf("Total     Multicast Packets:      %7d   (avg%5d/min)\n", TotPkt,         TotPkt         * mul / div);
-       mprintf("\n");
-       mprintf("Total New Service Probes:         %7d   (avg%5d/min)\n", NumProbes,      NumProbes      * mul / div);
-       mprintf("Total Goodbye Announcements:      %7d   (avg%5d/min)\n", NumGoodbyes,    NumGoodbyes    * mul / div);
-       mprintf("Total Query Questions:            %7d   (avg%5d/min)\n", NumQuestions,   NumQuestions   * mul / div);
-       mprintf("Total Queries from Legacy Clients:%7d   (avg%5d/min)\n", NumLegacy,      NumLegacy      * mul / div);
-       mprintf("Total Answers/Announcements:      %7d   (avg%5d/min)\n", NumAnswers,     NumAnswers     * mul / div);
-       mprintf("Total Additional Records:         %7d   (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div);
-       mprintf("\n");
-       printstats(kReportTopServices);
-
-       if (!ExactlyOneFilter)
-               {
-               ShowSortedHostList(&IPv4HostList, kReportTopHosts);
-               ShowSortedHostList(&IPv6HostList, kReportTopHosts);
-               }
-
-       mDNS_Close(&mDNSStorage);
-       return(0);
-       }
+{
+    struct tm tm;
+    int h, m, s, mul, div, TotPkt;
+#if !defined(WIN32)
+    sigset_t signals;
+#endif
+
+    mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+                               mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+                               mDNS_Init_DontAdvertiseLocalAddresses,
+                               mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+    if (status) return(status);
+
+    gettimeofday(&tv_start, NULL);
+
+#if defined( WIN32 )
+    status = SetupInterfaceList(&mDNSStorage);
+    if (status) return(status);
+    gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (gStopEvent == INVALID_HANDLE_VALUE) return mStatus_UnknownErr;
+    mDNSPollRegisterEvent( gStopEvent, StopNotification, NULL );
+    if (!SetConsoleCtrlHandler(ConsoleControlHandler, TRUE)) return mStatus_UnknownErr;
+    gRunning = mDNStrue; while (gRunning) mDNSPoll( INFINITE );
+    if (!SetConsoleCtrlHandler(ConsoleControlHandler, FALSE)) return mStatus_UnknownErr;
+    CloseHandle(gStopEvent);
+#else
+    mDNSPosixListenForSignalInEventLoop(SIGINT);
+    mDNSPosixListenForSignalInEventLoop(SIGTERM);
+
+    do
+    {
+        struct timeval timeout = { FutureTime, 0 };     // wait until SIGINT or SIGTERM
+        mDNSBool gotSomething;
+        mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
+    }
+    while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
+#endif
+
+    // Now display final summary
+    TotPkt = NumPktQ + NumPktL + NumPktR;
+    gettimeofday(&tv_end, NULL);
+    tv_interval = tv_end;
+    if (tv_start.tv_usec > tv_interval.tv_usec)
+    { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; }
+    tv_interval.tv_sec  -= tv_start.tv_sec;
+    tv_interval.tv_usec -= tv_start.tv_usec;
+    h = (tv_interval.tv_sec / 3600);
+    m = (tv_interval.tv_sec % 3600) / 60;
+    s = (tv_interval.tv_sec % 60);
+    if (tv_interval.tv_sec > 10)
+    {
+        mul = 60;
+        div = tv_interval.tv_sec;
+    }
+    else
+    {
+        mul = 60000;
+        div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
+        if (div == 0) div=1;
+    }
+
+    mprintf("\n\n");
+    localtime_r((time_t*)&tv_start.tv_sec, &tm);
+    mprintf("Started      %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec);
+    localtime_r((time_t*)&tv_end.tv_sec, &tm);
+    mprintf("End          %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec);
+    mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec);
+    if (!Filters)
+    {
+        mprintf("Unique source addresses seen on network:");
+        if (IPv4HostList.num) mprintf(" %ld (IPv4)", IPv4HostList.num);
+        if (IPv6HostList.num) mprintf(" %ld (IPv6)", IPv6HostList.num);
+        if (!IPv4HostList.num && !IPv6HostList.num) mprintf(" None");
+        mprintf("\n");
+    }
+    mprintf("\n");
+    mprintf("Modern Query        Packets:      %7d   (avg%5d/min)\n", NumPktQ,        NumPktQ        * mul / div);
+    mprintf("Legacy Query        Packets:      %7d   (avg%5d/min)\n", NumPktL,        NumPktL        * mul / div);
+    mprintf("Multicast Response  Packets:      %7d   (avg%5d/min)\n", NumPktR,        NumPktR        * mul / div);
+    mprintf("Total     Multicast Packets:      %7d   (avg%5d/min)\n", TotPkt,         TotPkt         * mul / div);
+    mprintf("\n");
+    mprintf("Total New Service Probes:         %7d   (avg%5d/min)\n", NumProbes,      NumProbes      * mul / div);
+    mprintf("Total Goodbye Announcements:      %7d   (avg%5d/min)\n", NumGoodbyes,    NumGoodbyes    * mul / div);
+    mprintf("Total Query Questions:            %7d   (avg%5d/min)\n", NumQuestions,   NumQuestions   * mul / div);
+    mprintf("Total Queries from Legacy Clients:%7d   (avg%5d/min)\n", NumLegacy,      NumLegacy      * mul / div);
+    mprintf("Total Answers/Announcements:      %7d   (avg%5d/min)\n", NumAnswers,     NumAnswers     * mul / div);
+    mprintf("Total Additional Records:         %7d   (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div);
+    mprintf("\n");
+    printstats(kReportTopServices);
+
+    if (!ExactlyOneFilter)
+    {
+        ShowSortedHostList(&IPv4HostList, kReportTopHosts);
+        ShowSortedHostList(&IPv6HostList, kReportTopHosts);
+    }
+
+    mDNS_Close(&mDNSStorage);
+    return(0);
+}
 
 mDNSexport int main(int argc, char **argv)
-       {
-       const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
-       int i;
-       mStatus status;
-       
-       setlinebuf(stdout);                             // Want to see lines as they appear, not block buffered
-
-       for (i=1; i<argc; i++)
-               {
-               if (i+1 < argc && !strcmp(argv[i], "-i") && atoi(argv[i+1]))
-                       {
-                       FilterInterface = atoi(argv[i+1]);
-                       i += 2;
-                       printf("Monitoring interface %d\n", FilterInterface);
-                       }
-               else
-                       {
-                       struct in_addr s4;
-                       struct in6_addr s6;
-                       FilterList *f;
-                       mDNSAddr a;
-                       a.type = mDNSAddrType_IPv4;
-       
-                       if (inet_pton(AF_INET, argv[i], &s4) == 1)
-                               a.ip.v4.NotAnInteger = s4.s_addr;
-                       else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
-                               {
-                               a.type = mDNSAddrType_IPv6;
-                               bcopy(&s6, &a.ip.v6, sizeof(a.ip.v6));
-                               }
-                       else
-                               {
-                               struct hostent *h = gethostbyname(argv[i]);
-                               if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
-                               else goto usage;
-                               }
-                       
-                       f = malloc(sizeof(*f));
-                       f->FilterAddr = a;
-                       f->next = Filters;
-                       Filters = f;
+{
+    const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
+    int i;
+    mStatus status;
+
+#if defined(WIN32)
+    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+#endif
+
+    setlinebuf(stdout);             // Want to see lines as they appear, not block buffered
+
+    for (i=1; i<argc; i++)
+    {
+               if (i+1 < argc && !strcmp(argv[i], "-i"))
+        {
+                       FilterInterface = if_nametoindex(argv[i+1]);
+                       if (!FilterInterface) FilterInterface = atoi(argv[i+1]);
+                       if (!FilterInterface) {
+                               fprintf(stderr, "Unknown interface %s\n", argv[i+1]);
+                               goto usage;
                        }
-               }
-
-       status = mDNSNetMonitor();
-       if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %d\n", progname, (int)status); return(status); }
-       return(0);
+            printf("Monitoring interface %d/%s\n", FilterInterface, argv[i+1]);
+                       i += 1;
+        }
+        else if (!strcmp(argv[i], "-6"))
+        {
+            AddressType = mDNSAddrType_IPv6;
+            printf("Monitoring IPv6 traffic\n");
+        }
+        else
+        {
+            struct in_addr s4;
+            struct in6_addr s6;
+            FilterList *f;
+            mDNSAddr a;
+            a.type = mDNSAddrType_IPv4;
+
+            if (inet_pton(AF_INET, argv[i], &s4) == 1)
+                a.ip.v4.NotAnInteger = s4.s_addr;
+            else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
+            {
+                a.type = mDNSAddrType_IPv6;
+                mDNSPlatformMemCopy(&a.ip.v6, &s6, sizeof(a.ip.v6));
+            }
+            else
+            {
+                struct hostent *h = gethostbyname(argv[i]);
+                if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
+                else goto usage;
+            }
+
+            f = malloc(sizeof(*f));
+            f->FilterAddr = a;
+            f->next = Filters;
+            Filters = f;
+        }
+    }
+
+    status = mDNSNetMonitor();
+    if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %d\n", progname, (int)status); return(status); }
+    return(0);
 
 usage:
-       fprintf(stderr, "\nmDNS traffic monitor\n");
-       fprintf(stderr, "Usage: %s (<host>)\n", progname);
-       fprintf(stderr, "Optional <host> parameter displays only packets from that host\n");
-
-       fprintf(stderr, "\nPer-packet header output:\n");
-       fprintf(stderr, "-Q-            Multicast Query from mDNS client that accepts multicast responses\n");
-       fprintf(stderr, "-R-            Multicast Response packet containing answers/announcements\n");
-       fprintf(stderr, "-LQ-           Multicast Query from legacy client that does *not* listen for multicast responses\n");
-       fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
-
-       fprintf(stderr, "\nPer-record display:\n");
-       fprintf(stderr, "(PM)           Probe Question (new service starting), requesting multicast response\n");
-       fprintf(stderr, "(PU)           Probe Question (new service starting), requesting unicast response\n");
-       fprintf(stderr, "(DE)           Deletion/Goodbye (service going away)\n");
-       fprintf(stderr, "(LQ)           Legacy Query Question\n");
-       fprintf(stderr, "(QM)           Query Question, requesting multicast response\n");
-       fprintf(stderr, "(QU)           Query Question, requesting unicast response\n");
-       fprintf(stderr, "(KA)           Known Answer (information querier already knows)\n");
-       fprintf(stderr, "(AN)           Unique Answer to question (or periodic announcment) (entire RR Set)\n");
-       fprintf(stderr, "(AN+)          Answer to question (or periodic announcment) (add to existing RR Set members)\n");
-       fprintf(stderr, "(AD)           Unique Additional Record Set (entire RR Set)\n");
-       fprintf(stderr, "(AD+)          Additional records (add to existing RR Set members)\n");
-
-       fprintf(stderr, "\nFinal summary, sorted by service type:\n");
-       fprintf(stderr, "Probe          Probes for this service type starting up\n");
-       fprintf(stderr, "Goodbye        Goodbye (deletion) packets for this service type shutting down\n");
-       fprintf(stderr, "BrowseQ        Browse questions from clients browsing to find a list of instances of this service\n");
-       fprintf(stderr, "BrowseA        Browse answers/announcments advertising instances of this service\n");
-       fprintf(stderr, "ResolveQ       Resolve questions from clients actively connecting to an instance of this service\n");
-       fprintf(stderr, "ResolveA       Resolve answers/announcments giving connection information for an instance of this service\n");
-       fprintf(stderr, "\n");
-       return(-1);
-       }
+    fprintf(stderr, "\nmDNS traffic monitor\n");
+    fprintf(stderr, "Usage: %s [-i index] [-6] [host]\n", progname);
+    fprintf(stderr, "Optional [-i index] parameter displays only packets from that interface index\n");
+       fprintf(stderr, "Optional [-6] parameter displays only ipv6 packets (defaults to only ipv4 packets)\n");
+    fprintf(stderr, "Optional [host] parameter displays only packets from that host\n");
+
+    fprintf(stderr, "\nPer-packet header output:\n");
+    fprintf(stderr, "-Q-            Multicast Query from mDNS client that accepts multicast responses\n");
+    fprintf(stderr, "-R-            Multicast Response packet containing answers/announcements\n");
+    fprintf(stderr, "-LQ-           Multicast Query from legacy client that does *not* listen for multicast responses\n");
+    fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
+
+    fprintf(stderr, "\nPer-record display:\n");
+    fprintf(stderr, "(PM)           Probe Question (new service starting), requesting multicast response\n");
+    fprintf(stderr, "(PU)           Probe Question (new service starting), requesting unicast response\n");
+    fprintf(stderr, "(DE)           Deletion/Goodbye (service going away)\n");
+    fprintf(stderr, "(LQ)           Legacy Query Question\n");
+    fprintf(stderr, "(QM)           Query Question, requesting multicast response\n");
+    fprintf(stderr, "(QU)           Query Question, requesting unicast response\n");
+    fprintf(stderr, "(KA)           Known Answer (information querier already knows)\n");
+    fprintf(stderr, "(AN)           Unique Answer to question (or periodic announcment) (entire RR Set)\n");
+    fprintf(stderr, "(AN+)          Answer to question (or periodic announcment) (add to existing RR Set members)\n");
+    fprintf(stderr, "(AD)           Unique Additional Record Set (entire RR Set)\n");
+    fprintf(stderr, "(AD+)          Additional records (add to existing RR Set members)\n");
+
+    fprintf(stderr, "\nFinal summary, sorted by service type:\n");
+    fprintf(stderr, "Probe          Probes for this service type starting up\n");
+    fprintf(stderr, "Goodbye        Goodbye (deletion) packets for this service type shutting down\n");
+    fprintf(stderr, "BrowseQ        Browse questions from clients browsing to find a list of instances of this service\n");
+    fprintf(stderr, "BrowseA        Browse answers/announcments advertising instances of this service\n");
+    fprintf(stderr, "ResolveQ       Resolve questions from clients actively connecting to an instance of this service\n");
+    fprintf(stderr, "ResolveA       Resolve answers/announcments giving connection information for an instance of this service\n");
+    fprintf(stderr, "\n");
+    return(-1);
+}