]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/NetMonitor.c
mDNSResponder-66.3.tar.gz
[apple/mdnsresponder.git] / mDNSPosix / NetMonitor.c
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 *
25 * Formatting notes:
26 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
27 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
28 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
29 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
30 * therefore common sense dictates that if they are part of a compound statement then they
31 * should be indented to the same level as everything else in that compound statement.
32 * Indenting curly braces at the same level as the "if" implies that curly braces are
33 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
34 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
35 * understand why variable y is not of type "char*" just proves the point that poor code
36 * layout leads people to unfortunate misunderstandings about how the C language really works.)
37
38 Change History (most recent first):
39
40 $Log: NetMonitor.c,v $
41 Revision 1.63 2004/05/18 23:51:26 cheshire
42 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
43
44 Revision 1.62 2004/03/16 18:24:25 cheshire
45 If packet destination was not multicast, then display it
46
47 Revision 1.61 2004/02/20 09:36:46 cheshire
48 Also show TTL in packet header, if it's not 255
49
50 Revision 1.60 2004/02/07 02:11:35 cheshire
51 Make mDNSNetMonitor smarter about sending targeted unicast HINFO queries
52
53 Revision 1.59 2004/02/03 21:42:55 cheshire
54 Add constants kReportTopServices and kReportTopHosts
55
56 Revision 1.58 2004/01/27 20:15:23 cheshire
57 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
58
59 Revision 1.57 2004/01/24 23:59:42 cheshire
60 Change to use mDNSVal16() instead of shifting and ORing
61
62 Revision 1.56 2004/01/24 05:25:34 cheshire
63 mDNSNetMonitor now uses the new ability to send unicast queries so that
64 it causes less perturbation of the network traffic it's monitoring.
65
66 Revision 1.55 2003/12/23 00:21:31 cheshire
67 Send HINFO queries to determine the mDNSResponder version of each host
68
69 Revision 1.54 2003/12/17 01:06:39 cheshire
70 Also show host name along with HINFO data
71
72 Revision 1.53 2003/12/17 00:51:22 cheshire
73 Changed mDNSNetMonitor and mDNSIdentify to link the object files
74 instead of #including the "DNSCommon.c" "uDNS.c" and source files
75
76 Revision 1.52 2003/12/17 00:21:51 cheshire
77 If we received one, display host's HINFO record in final summary
78
79 Revision 1.51 2003/12/13 03:05:28 ksekar
80 <rdar://problem/3192548>: DynDNS: Unicast query of service records
81
82 Revision 1.50 2003/12/08 20:47:02 rpantos
83 Add support for mDNSResponder on Linux.
84
85 Revision 1.49 2003/10/30 19:38:56 cheshire
86 Fix warning on certain compilers
87
88 Revision 1.48 2003/10/30 19:30:00 cheshire
89 Fix warnings on certain compilers
90
91 Revision 1.47 2003/09/05 18:49:57 cheshire
92 Add total packet size to display
93
94 Revision 1.46 2003/09/05 02:33:48 cheshire
95 Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window
96
97 Revision 1.45 2003/09/04 00:16:20 cheshire
98 Only show count of unique source addresses seen on network if we're not filtering
99
100 Revision 1.44 2003/09/02 22:13:28 cheshire
101 Show total host count in final summary table
102
103 Revision 1.43 2003/09/02 21:42:52 cheshire
104 Improved alignment of final summary table with v6 addresses
105
106 Revision 1.42 2003/09/02 20:59:24 cheshire
107 Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
108
109 Revision 1.41 2003/08/29 22:05:44 cheshire
110 Also count subsequent KA packets for the purposes of statistics counting
111
112 Revision 1.40 2003/08/29 16:43:24 cheshire
113 Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
114
115 Revision 1.39 2003/08/28 02:07:48 vlubet
116 Added "per hosts" statistics
117
118 Revision 1.38 2003/08/20 22:41:42 cheshire
119 Also display total multicast packet count
120
121 Revision 1.37 2003/08/20 22:32:08 cheshire
122 Error in DisplayQuery: Authorities come *after* Answers, not before
123
124 Revision 1.36 2003/08/18 23:20:10 cheshire
125 RDLength moved from the RData to the ResourceRecord object.
126
127 Revision 1.35 2003/08/15 20:17:28 cheshire
128 "LargeResourceRecord" changed to "LargeCacheRecord"
129
130 Revision 1.34 2003/08/14 02:19:55 cheshire
131 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
132
133 Revision 1.33 2003/08/12 19:56:26 cheshire
134 Update to APSL 2.0
135
136 Revision 1.32 2003/08/06 18:57:01 cheshire
137 Update comments
138
139 Revision 1.31 2003/08/06 02:05:12 cheshire
140 Add ability to give a list of hosts to monitor
141
142 Revision 1.30 2003/08/05 23:56:26 cheshire
143 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
144 (Right now mDNSPosix.c just reports 255 -- we should fix this)
145
146 Revision 1.29 2003/08/05 00:43:12 cheshire
147 Report errors encountered while processing authority section
148
149 Revision 1.28 2003/07/29 22:51:08 cheshire
150 Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them
151
152 Revision 1.27 2003/07/29 22:48:04 cheshire
153 Completed support for decoding packets containing oversized resource records
154
155 Revision 1.26 2003/07/19 03:25:23 cheshire
156 Change to make use of new GetLargeResourceRecord() call, for handling larger records
157
158 Revision 1.25 2003/07/18 00:13:23 cheshire
159 Remove erroneous trailing '\' from TXT record display
160
161 Revision 1.24 2003/07/17 17:10:51 cheshire
162 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
163 Further work: distinguish between PM and PU
164
165 Revision 1.23 2003/07/16 22:20:23 cheshire
166 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
167 Made mDNSNetMonitor distinguish between QM and QU in its logging output
168
169 Revision 1.22 2003/07/02 21:19:58 cheshire
170 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
171
172 Revision 1.21 2003/06/18 05:48:41 cheshire
173 Fix warnings
174
175 Revision 1.20 2003/06/06 22:18:22 cheshire
176 Add extra space in Q output to line it up with RR output
177
178 Revision 1.19 2003/06/06 21:05:04 cheshire
179 Display state of cache-flush bit on additional records
180
181 Revision 1.18 2003/06/06 20:01:30 cheshire
182 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
183 (Global search-and-replace; no functional change to code execution.)
184
185 Revision 1.17 2003/06/06 14:26:50 cheshire
186 Explicitly #include <time.h> for the benefit of certain Linux distributions
187
188 Revision 1.16 2003/05/29 21:56:36 cheshire
189 More improvements:
190 Distinguish modern multicast queries from legacy multicast queries
191 In addition to record counts, display packet counts of queries, legacy queries, and responses
192 Include TTL in RR display
193
194 Revision 1.15 2003/05/29 20:03:57 cheshire
195 Various improvements:
196 Display start and end time, average rates in packets-per-minute,
197 show legacy queries as -LQ-, improve display of TXT and unknown records
198
199 Revision 1.14 2003/05/26 04:45:42 cheshire
200 Limit line length when printing super-long TXT records
201
202 Revision 1.13 2003/05/26 03:21:29 cheshire
203 Tidy up address structure naming:
204 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
205 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
206 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
207
208 Revision 1.12 2003/05/26 03:01:28 cheshire
209 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
210
211 Revision 1.11 2003/05/26 00:48:13 cheshire
212 If mDNS packet contains a non-zero message ID, then display it.
213
214 Revision 1.10 2003/05/22 01:10:32 cheshire
215 Indicate when the TC bit is set on a query packet
216
217 Revision 1.9 2003/05/21 03:56:00 cheshire
218 Improve display of Probe queries
219
220 Revision 1.8 2003/05/09 21:41:56 cheshire
221 Track deletion/goodbye packets as separate category
222
223 Revision 1.7 2003/05/07 00:16:01 cheshire
224 More detailed decoding of Resource Records
225
226 Revision 1.6 2003/05/05 21:16:16 cheshire
227 <rdar://problem/3241281> Change timenow from a local variable to a structure member
228
229 Revision 1.5 2003/04/19 01:16:22 cheshire
230 Add filter option, to monitor only packets from a single specified source address
231
232 Revision 1.4 2003/04/18 00:45:21 cheshire
233 Distinguish announcements (AN) from deletions (DE)
234
235 Revision 1.3 2003/04/15 18:26:01 cheshire
236 Added timestamps and help information
237
238 Revision 1.2 2003/04/04 20:42:02 cheshire
239 Fix broken statistics counting
240
241 Revision 1.1 2003/04/04 01:37:14 cheshire
242 Added NetMonitor.c
243
244 */
245
246 //*************************************************************************************************************
247 // Incorporate mDNS.c functionality
248
249 // We want to use much of the functionality provided by "mDNS.c",
250 // except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
251 #define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
252 #include "mDNS.c"
253 #undef mDNSCoreReceive
254
255 //*************************************************************************************************************
256 // Headers
257
258 #include <stdio.h> // For printf()
259 #include <stdlib.h> // For malloc()
260 #include <string.h> // For bcopy()
261 #include <time.h> // For "struct tm" etc.
262 #include <signal.h> // For SIGINT, SIGTERM
263 #include <netdb.h> // For gethostbyname()
264 #include <sys/socket.h> // For AF_INET, AF_INET6, etc.
265 #include <arpa/inet.h> // For inet_addr()
266 #include <netinet/in.h> // For INADDR_NONE
267
268 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
269 #include "ExampleClientApp.h"
270
271 //*************************************************************************************************************
272 // Types and structures
273
274 enum
275 {
276 // Primitive operations
277 OP_probe = 0,
278 OP_goodbye = 1,
279
280 // These are meta-categories;
281 // Query and Answer operations are actually subdivided into two classes:
282 // Browse query/answer and
283 // Resolve query/answer
284 OP_query = 2,
285 OP_answer = 3,
286
287 // The "Browse" variants of query/answer
288 OP_browsegroup = 2,
289 OP_browseq = 2,
290 OP_browsea = 3,
291
292 // The "Resolve" variants of query/answer
293 OP_resolvegroup = 4,
294 OP_resolveq = 4,
295 OP_resolvea = 5,
296
297 OP_NumTypes = 6
298 };
299
300 typedef struct ActivityStat_struct ActivityStat;
301 struct ActivityStat_struct
302 {
303 ActivityStat *next;
304 domainname srvtype;
305 int printed;
306 int totalops;
307 int stat[OP_NumTypes];
308 };
309
310 typedef struct FilterList_struct FilterList;
311 struct FilterList_struct
312 {
313 FilterList *next;
314 mDNSAddr FilterAddr;
315 };
316
317 //*************************************************************************************************************
318 // Constants
319
320 #define kReportTopServices 15
321 #define kReportTopHosts 15
322
323 //*************************************************************************************************************
324 // Globals
325
326 static mDNS mDNSStorage; // mDNS core uses this to store its globals
327 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
328
329 struct timeval tv_start, tv_end, tv_interval;
330
331 static FilterList *Filters;
332 #define ExactlyOneFilter (Filters && !Filters->next)
333
334 static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad
335 static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
336
337 static ActivityStat *stats;
338
339 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
340
341 //*************************************************************************************************************
342 // Utilities
343
344 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
345 mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
346 mDNSlocal mDNSu32 mprintf(const char *format, ...)
347 {
348 mDNSu32 length;
349 unsigned char buffer[512];
350 va_list ptr;
351 va_start(ptr,format);
352 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
353 va_end(ptr);
354 printf("%s", buffer);
355 return(length);
356 }
357
358 //*************************************************************************************************************
359 // Host Address List
360 //
361 // Would benefit from a hash
362
363 typedef enum
364 {
365 HostPkt_Q = 0, // Query
366 HostPkt_L = 1, // Legacy Query
367 HostPkt_R = 2, // Response
368 HostPkt_B = 3, // Bad
369 HostPkt_NumTypes = 4
370 } HostPkt_Type;
371
372 typedef struct
373 {
374 mDNSAddr addr;
375 unsigned long pkts[HostPkt_NumTypes];
376 unsigned long totalops;
377 unsigned long stat[OP_NumTypes];
378 domainname hostname;
379 domainname revname;
380 UTF8str255 HIHardware;
381 UTF8str255 HISoftware;
382 mDNSu32 NumQueries;
383 mDNSs32 LastQuery;
384 } HostEntry;
385
386 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
387
388 typedef struct
389 {
390 long num;
391 long max;
392 HostEntry *hosts;
393 } HostList;
394
395 static HostList IPv4HostList = { 0, 0, 0 };
396 static HostList IPv6HostList = { 0, 0, 0 };
397
398 mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList* list)
399 {
400 long i;
401
402 for (i = 0; i < list->num; i++)
403 {
404 HostEntry *entry = list->hosts + i;
405 if (mDNSSameAddress(addr, &entry->addr))
406 return entry;
407 }
408
409 return NULL;
410 }
411
412 mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList* list)
413 {
414 int i;
415 HostEntry *entry;
416 if (list->num >= list->max)
417 {
418 long newMax = list->max + 64;
419 HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
420 if (newHosts == NULL)
421 return NULL;
422 list->max = newMax;
423 list->hosts = newHosts;
424 }
425
426 entry = list->hosts + list->num++;
427
428 entry->addr = *addr;
429 for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
430 entry->totalops = 0;
431 for (i=0; i<OP_NumTypes; i++) entry->stat[i] = 0;
432 entry->hostname.c[0] = 0;
433 entry->revname.c[0] = 0;
434 entry->HIHardware.c[0] = 0;
435 entry->HISoftware.c[0] = 0;
436 entry->NumQueries = 0;
437
438 if (entry->addr.type == mDNSAddrType_IPv4)
439 {
440 mDNSv4Addr ip = entry->addr.ip.v4;
441 char buffer[32];
442 mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]);
443 MakeDomainNameFromDNSNameString(&entry->revname, buffer);
444 }
445
446 return(entry);
447 }
448
449 mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t, mDNSOpaque16 id)
450 {
451 if (ExactlyOneFilter) return(NULL);
452 else
453 {
454 HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
455 HostEntry *entry = FindHost(addr, list);
456 if (!entry) entry = AddHost(addr, list);
457 if (!entry) return(NULL);
458 // Don't count our own interrogation packets
459 if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++;
460 return(entry);
461 }
462 }
463
464 mDNSlocal void RecordHostInfo(HostEntry *entry, const ResourceRecord *const pktrr)
465 {
466 if (!entry->hostname.c[0])
467 {
468 if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA)
469 {
470 // Should really check that the rdata in the address record matches the source address of this packet
471 entry->NumQueries = 0;
472 AssignDomainName(entry->hostname, pktrr->name);
473 }
474
475 if (pktrr->rrtype == kDNSType_PTR)
476 if (SameDomainName(&entry->revname, &pktrr->name))
477 {
478 entry->NumQueries = 0;
479 AssignDomainName(entry->hostname, pktrr->rdata->u.name);
480 }
481 }
482 else if (pktrr->rrtype == kDNSType_HINFO)
483 {
484 RDataBody *rd = &pktrr->rdata->u;
485 mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
486 mDNSu8 *hw = rd->txt.c;
487 mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0];
488 if (sw + 1 + sw[0] <= rdend)
489 {
490 AssignDomainName(entry->hostname, pktrr->name);
491 mDNSPlatformMemCopy(hw, entry->HIHardware.c, 1 + (mDNSu32)hw[0]);
492 mDNSPlatformMemCopy(sw, entry->HISoftware.c, 1 + (mDNSu32)sw[0]);
493 }
494 }
495 }
496
497 mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID)
498 {
499 const mDNSOpaque16 id = { { 0xFF, 0xFF } };
500 DNSMessage query;
501 mDNSu8 *qptr = query.data;
502 const mDNSu8 *const limit = query.data + sizeof(query.data);
503 const mDNSAddr *target = &entry->addr;
504 InitializeDNSMessage(&query.h, id, QueryFlags);
505 qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN);
506 entry->LastQuery = m->timenow;
507 entry->NumQueries++;
508
509 // Note: When there are multiple mDNSResponder agents running on a single machine
510 // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder)
511 // it is possible that unicast queries may not go to the primary system responder.
512 // We try the first query using unicast, but if that doesn't work we try again via multicast.
513 if (entry->NumQueries > 2)
514 {
515 target = &AllDNSLinkGroup_v4;
516 }
517 else
518 {
519 //mprintf("%#a Q\n", target);
520 InterfaceID = mDNSInterface_Any; // Send query from our unicast reply socket
521 m->ExpectUnicastResponse = m->timenow;
522 }
523
524 mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, target, MulticastDNSPort);
525 }
526
527 mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID)
528 {
529 // If we've done four queries without answer, give up
530 if (entry->NumQueries >= 4) return;
531
532 // If we've done a query in the last second, give the host a chance to reply before trying again
533 if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return;
534
535 // If we don't know the host name, try to find that first
536 if (!entry->hostname.c[0])
537 {
538 if (entry->revname.c[0])
539 {
540 SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID);
541 //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries);
542 }
543 }
544 // If we have the host name but no HINFO, now ask for that
545 else if (!entry->HIHardware.c[0])
546 {
547 SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID);
548 //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries);
549 }
550 }
551
552 mDNSlocal int CompareHosts(const void *p1, const void *p2)
553 {
554 return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
555 }
556
557 mDNSlocal void ShowSortedHostList(HostList *list, int max)
558 {
559 HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num];
560 qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts);
561 if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response");
562 for (e = &list->hosts[0]; e < end; e++)
563 {
564 int len = mprintf("%#-25a", &e->addr);
565 if (len > 25) mprintf("\n%25s", "");
566 mprintf("%8d %8d %8d %8d %8d %8d %8d", e->totalops,
567 e->stat[OP_probe], e->stat[OP_goodbye],
568 e->stat[OP_browseq], e->stat[OP_browsea],
569 e->stat[OP_resolveq], e->stat[OP_resolvea]);
570 mprintf(" %8lu %8lu %8lu %8lu",
571 HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]);
572 if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]);
573 mprintf("\n");
574 if (!e->HISoftware.c[0] && e->NumQueries > 2)
575 mDNSPlatformMemCopy("\x0E*** Jaguar ***", &e->HISoftware, 15);
576 if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0])
577 mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c);
578 }
579 }
580
581 //*************************************************************************************************************
582 // Receive and process packets
583
584 mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
585 {
586 int i, len;
587 const mDNSu8 *src = fqdn->c;
588 mDNSu8 *dst = srvtype->c;
589
590 len = *src;
591 if (len == 0 || len >= 0x40) return(mDNSfalse);
592 if (src[1] != '_') src += 1 + len;
593
594 len = *src;
595 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
596 for (i=0; i<=len; i++) *dst++ = *src++;
597
598 len = *src;
599 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
600 for (i=0; i<=len; i++) *dst++ = *src++;
601
602 *dst++ = 0; // Put the null root label on the end of the service type
603
604 return(mDNStrue);
605 }
606
607 mDNSlocal void recordstat(HostEntry *entry, domainname *fqdn, int op, mDNSu16 rrtype)
608 {
609 ActivityStat **s = &stats;
610 domainname srvtype;
611
612 if (op != OP_probe)
613 {
614 if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
615 else if (rrtype != kDNSType_PTR) return;
616 }
617
618 if (!ExtractServiceType(fqdn, &srvtype)) return;
619
620 while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
621 if (!*s)
622 {
623 int i;
624 *s = malloc(sizeof(ActivityStat));
625 if (!*s) exit(-1);
626 (*s)->next = NULL;
627 (*s)->srvtype = srvtype;
628 (*s)->printed = 0;
629 (*s)->totalops = 0;
630 for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
631 }
632
633 (*s)->totalops++;
634 (*s)->stat[op]++;
635 if (entry)
636 {
637 entry->totalops++;
638 entry->stat[op]++;
639 }
640 }
641
642 mDNSlocal void printstats(int max)
643 {
644 int i;
645 if (!stats) return;
646 for (i=0; i<max; i++)
647 {
648 int max = 0;
649 ActivityStat *s, *m = NULL;
650 for (s = stats; s; s=s->next)
651 if (!s->printed && max < s->totalops)
652 { m = s; max = s->totalops; }
653 if (!m) return;
654 m->printed = mDNStrue;
655 if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner);
656 mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", &m->srvtype, m->totalops, m->stat[OP_probe],
657 m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]);
658 }
659 }
660
661 mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end,\
662 DNSQuestion *q, LargeCacheRecord *pkt)
663 {
664 int i;
665 for (i = 0; i < query->h.numAuthorities; i++)
666 {
667 const mDNSu8 *p2 = ptr;
668 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, pkt);
669 if (!ptr) break;
670 if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
671 }
672 return(mDNSNULL);
673 }
674
675 mDNSlocal void DisplayTimestamp(void)
676 {
677 struct timeval tv;
678 struct tm tm;
679 gettimeofday(&tv, NULL);
680 localtime_r((time_t*)&tv.tv_sec, &tm);
681 mprintf("\n%d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec);
682 }
683
684 mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSu8 ttl)
685 {
686 const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " :
687 (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
688
689 DisplayTimestamp();
690 mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
691 srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg);
692
693 if (msg->h.id.NotAnInteger) mprintf(" ID:%u", mDNSVal16(msg->h.id));
694
695 if (ttl != 255) mprintf(" TTL:%u", ttl);
696
697 if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf(" To: %#a", dstaddr);
698
699 if (msg->h.flags.b[0] & kDNSFlag0_TC)
700 {
701 if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated");
702 else mprintf(" Truncated (KA list continues in next packet)");
703 }
704 mprintf("\n");
705 }
706
707 mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
708 {
709 static const char hexchars[16] = "0123456789ABCDEF";
710 #define MaxWidth 132
711 char buffer[MaxWidth+8];
712 char *p = buffer;
713
714 RDataBody *rd = &pktrr->rdata->u;
715 mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
716 mDNSu32 n = mprintf("%#-16a %-5s %-5s%5d %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name.c);
717
718 switch(pktrr->rrtype)
719 {
720 case kDNSType_A: n += mprintf("%.4a", &rd->ip); break;
721 case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, &rd->name); break;
722 case kDNSType_HINFO:// same as kDNSType_TXT below
723 case kDNSType_TXT: {
724 mDNSu8 *t = rd->txt.c;
725 while (t < rdend && t[0] && p < buffer+MaxWidth)
726 {
727 int i;
728 for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
729 {
730 if (t[i] == '\\') *p++ = '\\';
731 if (t[i] >= ' ') *p++ = t[i];
732 else
733 {
734 *p++ = '\\';
735 *p++ = '0';
736 *p++ = 'x';
737 *p++ = hexchars[t[i] >> 4];
738 *p++ = hexchars[t[i] & 0xF];
739 }
740 }
741 t += 1+t[0];
742 if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
743 }
744 *p++ = 0;
745 n += mprintf("%.*s", MaxWidth - n, buffer);
746 } break;
747 case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break;
748 case kDNSType_SRV: n += mprintf("%##s:%d", &rd->srv.target, mDNSVal16(rd->srv.port)); break;
749 default: {
750 mDNSu8 *s = rd->data;
751 while (s < rdend && p < buffer+MaxWidth)
752 {
753 if (*s == '\\') *p++ = '\\';
754 if (*s >= ' ') *p++ = *s;
755 else
756 {
757 *p++ = '\\';
758 *p++ = '0';
759 *p++ = 'x';
760 *p++ = hexchars[*s >> 4];
761 *p++ = hexchars[*s & 0xF];
762 }
763 s++;
764 }
765 *p++ = 0;
766 n += mprintf("%.*s", MaxWidth - n, buffer);
767 } break;
768 }
769
770 mprintf("\n");
771 }
772
773 mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
774 {
775 while (ptr < end)
776 {
777 int i;
778 for (i=0; i<16; i++)
779 if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
780 else mprintf(" ");
781 for (i=0; i<16; i++)
782 if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
783 ptr += 16;
784 mprintf("\n");
785 }
786 }
787
788 mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
789 {
790 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
791 HexDump(ptr, end);
792 }
793
794 mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
795 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID, mDNSu8 ttl)
796 {
797 int i;
798 const mDNSu8 *ptr = msg->data;
799 const mDNSu8 *auth = LocateAuthorities(msg, end);
800 mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger);
801 HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id);
802 LargeCacheRecord pkt;
803
804 DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr, ttl);
805 if (msg->h.id.NotAnInteger != 0xFFFF)
806 {
807 if (MQ) NumPktQ++; else NumPktL++;
808 }
809
810 for (i=0; i<msg->h.numQuestions; i++)
811 {
812 DNSQuestion q;
813 mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q);
814 mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse;
815 q.qclass &= ~kDNSQClass_UnicastResponse;
816 if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; }
817 ptr = p2;
818 p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
819 if (p2)
820 {
821 NumProbes++;
822 DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec);
823 recordstat(entry, &q.qname, OP_probe, q.qtype);
824 p2 = (mDNSu8 *)skipDomainName(msg, p2, end);
825 // Having displayed this update record, clear type and class so we don't display the same one again.
826 p2[0] = p2[1] = p2[2] = p2[3] = 0;
827 }
828 else
829 {
830 const char *ptype = ucbit ? "(QU)" : "(QM)";
831 if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++;
832 else { NumLegacy++; ptype = "(LQ)"; }
833 mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c);
834 if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype);
835 }
836 }
837
838 for (i=0; i<msg->h.numAnswers; i++)
839 {
840 const mDNSu8 *ep = ptr;
841 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
842 if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; }
843 DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec);
844
845 // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
846 // the same as a single query, to more accurately reflect the burden on the network
847 // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
848 if (msg->h.numQuestions == 0 && i == 0)
849 recordstat(entry, &pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype);
850 }
851
852 for (i=0; i<msg->h.numAuthorities; i++)
853 {
854 const mDNSu8 *ep = ptr;
855 ptr = skipResourceRecord(msg, ptr, end);
856 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
857 }
858
859 if (entry) AnalyseHost(m, entry, InterfaceID);
860 }
861
862 mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end,
863 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID, mDNSu8 ttl)
864 {
865 int i;
866 const mDNSu8 *ptr = msg->data;
867 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
868 LargeCacheRecord pkt;
869
870 DisplayPacketHeader(msg, end, srcaddr, srcport, dstaddr, ttl);
871 if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++;
872
873 for (i=0; i<msg->h.numQuestions; i++)
874 {
875 DNSQuestion q;
876 const mDNSu8 *ep = ptr;
877 ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
878 if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
879 if (mDNSAddrIsDNSMulticast(dstaddr))
880 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
881 else
882 mprintf("%#-16a (Q) %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
883 }
884
885 for (i=0; i<msg->h.numAnswers; i++)
886 {
887 const mDNSu8 *ep = ptr;
888 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
889 if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; }
890 if (pkt.r.resrec.rroriginalttl)
891 {
892 NumAnswers++;
893 DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec);
894 if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype);
895 if (entry) RecordHostInfo(entry, &pkt.r.resrec);
896 }
897 else
898 {
899 NumGoodbyes++;
900 DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
901 recordstat(entry, &pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
902 }
903 }
904
905 for (i=0; i<msg->h.numAuthorities; i++)
906 {
907 const mDNSu8 *ep = ptr;
908 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
909 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
910 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
911 srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name.c);
912 }
913
914 for (i=0; i<msg->h.numAdditionals; i++)
915 {
916 const mDNSu8 *ep = ptr;
917 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
918 if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
919 NumAdditionals++;
920 DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec);
921 if (entry) RecordHostInfo(entry, &pkt.r.resrec);
922 }
923
924 if (entry) AnalyseHost(m, entry, InterfaceID);
925 }
926
927 mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID)
928 {
929 int i;
930 const mDNSu8 *ptr = LocateAnswers(msg, end);
931 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
932 //mprintf("%#a R\n", srcaddr);
933
934 for (i=0; i<msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++)
935 {
936 LargeCacheRecord pkt;
937 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
938 if (pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec);
939 }
940 }
941
942 mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
943 {
944 FilterList *f;
945 if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
946 for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
947 return(mDNSfalse);
948 }
949
950 mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
951 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl)
952 {
953 const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
954 const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
955 const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
956 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
957
958 (void)dstaddr; // Unused
959 (void)dstport; // Unused
960
961 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
962 msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
963 msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
964 msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
965 msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
966
967 if (ttl < 254)
968 {
969 debugf("** Apparent spoof mDNS %s packet from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
970 (QR_OP == StdQ) ? "Query" : (QR_OP == StdR) ? "Response" : "Unkown",
971 srcaddr, dstaddr, ttl, InterfaceID,
972 msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,",
973 msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,",
974 msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,",
975 msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s");
976 }
977
978 // For now we're only interested in monitoring IPv4 traffic.
979 // All IPv6 packets should just be duplicates of the v4 packets.
980 if (AddressMatchesFilterList(srcaddr))
981 {
982 mDNS_Lock(m);
983 if (!mDNSAddrIsDNSMulticast(dstaddr))
984 {
985 if (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr);
986 else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr, InterfaceID);
987 }
988 else
989 {
990 if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID, ttl);
991 else if (QR_OP == StdR) DisplayResponse (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID, ttl);
992 else
993 {
994 debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
995 GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id);
996 NumPktB++;
997 }
998 }
999 mDNS_Unlock(m);
1000 }
1001 }
1002
1003 mDNSlocal mStatus mDNSNetMonitor(void)
1004 {
1005 struct tm tm;
1006 int h, m, s, mul, div, TotPkt;
1007 sigset_t signals;
1008
1009 mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage,
1010 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
1011 mDNS_Init_DontAdvertiseLocalAddresses,
1012 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
1013 if (status) return(status);
1014
1015 gettimeofday(&tv_start, NULL);
1016 mDNSPosixListenForSignalInEventLoop(SIGINT);
1017 mDNSPosixListenForSignalInEventLoop(SIGTERM);
1018
1019 do
1020 {
1021 struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
1022 mDNSBool gotSomething;
1023 mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
1024 }
1025 while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
1026
1027 // Now display final summary
1028 TotPkt = NumPktQ + NumPktL + NumPktR;
1029 gettimeofday(&tv_end, NULL);
1030 tv_interval = tv_end;
1031 if (tv_start.tv_usec > tv_interval.tv_usec)
1032 { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; }
1033 tv_interval.tv_sec -= tv_start.tv_sec;
1034 tv_interval.tv_usec -= tv_start.tv_usec;
1035 h = (tv_interval.tv_sec / 3600);
1036 m = (tv_interval.tv_sec % 3600) / 60;
1037 s = (tv_interval.tv_sec % 60);
1038 if (tv_interval.tv_sec > 10)
1039 {
1040 mul = 60;
1041 div = tv_interval.tv_sec;
1042 }
1043 else
1044 {
1045 mul = 60000;
1046 div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
1047 if (div == 0) div=1;
1048 }
1049
1050 mprintf("\n\n");
1051 localtime_r((time_t*)&tv_start.tv_sec, &tm);
1052 mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec);
1053 localtime_r((time_t*)&tv_end.tv_sec, &tm);
1054 mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec);
1055 mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec);
1056 if (!Filters) mprintf("Unique source addresses seen on network: %d\n", IPv4HostList.num + IPv6HostList.num);
1057 mprintf("\n");
1058 mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div);
1059 mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div);
1060 mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div);
1061 mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div);
1062 mprintf("\n");
1063 mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div);
1064 mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div);
1065 mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div);
1066 mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div);
1067 mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div);
1068 mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div);
1069 mprintf("\n");
1070 printstats(kReportTopServices);
1071
1072 if (!ExactlyOneFilter)
1073 {
1074 ShowSortedHostList(&IPv4HostList, kReportTopHosts);
1075 ShowSortedHostList(&IPv6HostList, kReportTopHosts);
1076 }
1077
1078 mDNS_Close(&mDNSStorage);
1079 return(0);
1080 }
1081
1082 mDNSexport int main(int argc, char **argv)
1083 {
1084 int i;
1085 mStatus status;
1086
1087 setlinebuf(stdout); // Want to see lines as they appear, not block buffered
1088
1089 for (i=1; i<argc; i++)
1090 {
1091 struct in_addr s4;
1092 struct in6_addr s6;
1093 FilterList *f;
1094 mDNSAddr a;
1095 a.type = mDNSAddrType_IPv4;
1096
1097 if (inet_pton(AF_INET, argv[i], &s4) == 1)
1098 a.ip.v4.NotAnInteger = s4.s_addr;
1099 else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
1100 {
1101 a.type = mDNSAddrType_IPv6;
1102 bcopy(&s6, &a.ip.v6, sizeof(a.ip.v6));
1103 }
1104 else
1105 {
1106 struct hostent *h = gethostbyname(argv[i]);
1107 if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
1108 else goto usage;
1109 }
1110
1111 f = malloc(sizeof(*f));
1112 f->FilterAddr = a;
1113 f->next = Filters;
1114 Filters = f;
1115 }
1116
1117 status = mDNSNetMonitor();
1118 if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", argv[0], status); return(status); }
1119 return(0);
1120
1121 usage:
1122 fprintf(stderr, "\nmDNS traffic monitor\n");
1123 fprintf(stderr, "Usage: %s (<host>)\n", argv[0]);
1124 fprintf(stderr, "Optional <host> parameter displays only packets from that host\n");
1125
1126 fprintf(stderr, "\nPer-packet header output:\n");
1127 fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
1128 fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n");
1129 fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
1130 fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
1131
1132 fprintf(stderr, "\nPer-record display:\n");
1133 fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n");
1134 fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n");
1135 fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n");
1136 fprintf(stderr, "(LQ) Legacy Query Question\n");
1137 fprintf(stderr, "(QM) Query Question, requesting multicast response\n");
1138 fprintf(stderr, "(QU) Query Question, requesting unicast response\n");
1139 fprintf(stderr, "(KA) Known Answer (information querier already knows)\n");
1140 fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
1141 fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
1142 fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n");
1143 fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n");
1144
1145 fprintf(stderr, "\nFinal summary, sorted by service type:\n");
1146 fprintf(stderr, "Probe Probes for this service type starting up\n");
1147 fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
1148 fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
1149 fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n");
1150 fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
1151 fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
1152 fprintf(stderr, "\n");
1153 return(-1);
1154 }