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