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