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