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