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