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