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