1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
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.)
37 Change History (most recent first):
39 $Log: NetMonitor.c,v $
40 Revision 1.71 2004/12/16 20:17:11 cheshire
41 <rdar://problem/3324626> Cache memory management improvements
43 Revision 1.70 2004/12/04 02:13:20 cheshire
44 Pass proper record type in GetLargeResourceRecord() calls
46 Revision 1.69 2004/11/30 22:37:01 cheshire
47 Update copyright dates and add "Mode: C; tab-width: 4" headers
49 Revision 1.68 2004/10/23 01:16:01 cheshire
50 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
52 Revision 1.67 2004/10/16 00:17:00 cheshire
53 <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
55 Revision 1.66 2004/09/17 00:31:52 cheshire
56 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
58 Revision 1.65 2004/09/16 01:58:22 cheshire
61 Revision 1.64 2004/06/15 02:39:47 cheshire
62 When displaying error message, only show command name, not entire path
64 Revision 1.63 2004/05/18 23:51:26 cheshire
65 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
67 Revision 1.62 2004/03/16 18:24:25 cheshire
68 If packet destination was not multicast, then display it
70 Revision 1.61 2004/02/20 09:36:46 cheshire
71 Also show TTL in packet header, if it's not 255
73 Revision 1.60 2004/02/07 02:11:35 cheshire
74 Make mDNSNetMonitor smarter about sending targeted unicast HINFO queries
76 Revision 1.59 2004/02/03 21:42:55 cheshire
77 Add constants kReportTopServices and kReportTopHosts
79 Revision 1.58 2004/01/27 20:15:23 cheshire
80 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
82 Revision 1.57 2004/01/24 23:59:42 cheshire
83 Change to use mDNSVal16() instead of shifting and ORing
85 Revision 1.56 2004/01/24 05:25:34 cheshire
86 mDNSNetMonitor now uses the new ability to send unicast queries so that
87 it causes less perturbation of the network traffic it's monitoring.
89 Revision 1.55 2003/12/23 00:21:31 cheshire
90 Send HINFO queries to determine the mDNSResponder version of each host
92 Revision 1.54 2003/12/17 01:06:39 cheshire
93 Also show host name along with HINFO data
95 Revision 1.53 2003/12/17 00:51:22 cheshire
96 Changed mDNSNetMonitor and mDNSIdentify to link the object files
97 instead of #including the "DNSCommon.c" "uDNS.c" and source files
99 Revision 1.52 2003/12/17 00:21:51 cheshire
100 If we received one, display host's HINFO record in final summary
102 Revision 1.51 2003/12/13 03:05:28 ksekar
103 <rdar://problem/3192548>: DynDNS: Unicast query of service records
105 Revision 1.50 2003/12/08 20:47:02 rpantos
106 Add support for mDNSResponder on Linux.
108 Revision 1.49 2003/10/30 19:38:56 cheshire
109 Fix warning on certain compilers
111 Revision 1.48 2003/10/30 19:30:00 cheshire
112 Fix warnings on certain compilers
114 Revision 1.47 2003/09/05 18:49:57 cheshire
115 Add total packet size to display
117 Revision 1.46 2003/09/05 02:33:48 cheshire
118 Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window
120 Revision 1.45 2003/09/04 00:16:20 cheshire
121 Only show count of unique source addresses seen on network if we're not filtering
123 Revision 1.44 2003/09/02 22:13:28 cheshire
124 Show total host count in final summary table
126 Revision 1.43 2003/09/02 21:42:52 cheshire
127 Improved alignment of final summary table with v6 addresses
129 Revision 1.42 2003/09/02 20:59:24 cheshire
130 Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
132 Revision 1.41 2003/08/29 22:05:44 cheshire
133 Also count subsequent KA packets for the purposes of statistics counting
135 Revision 1.40 2003/08/29 16:43:24 cheshire
136 Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
138 Revision 1.39 2003/08/28 02:07:48 vlubet
139 Added "per hosts" statistics
141 Revision 1.38 2003/08/20 22:41:42 cheshire
142 Also display total multicast packet count
144 Revision 1.37 2003/08/20 22:32:08 cheshire
145 Error in DisplayQuery: Authorities come *after* Answers, not before
147 Revision 1.36 2003/08/18 23:20:10 cheshire
148 RDLength moved from the RData to the ResourceRecord object.
150 Revision 1.35 2003/08/15 20:17:28 cheshire
151 "LargeResourceRecord" changed to "LargeCacheRecord"
153 Revision 1.34 2003/08/14 02:19:55 cheshire
154 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
156 Revision 1.33 2003/08/12 19:56:26 cheshire
159 Revision 1.32 2003/08/06 18:57:01 cheshire
162 Revision 1.31 2003/08/06 02:05:12 cheshire
163 Add ability to give a list of hosts to monitor
165 Revision 1.30 2003/08/05 23:56:26 cheshire
166 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
167 (Right now mDNSPosix.c just reports 255 -- we should fix this)
169 Revision 1.29 2003/08/05 00:43:12 cheshire
170 Report errors encountered while processing authority section
172 Revision 1.28 2003/07/29 22:51:08 cheshire
173 Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them
175 Revision 1.27 2003/07/29 22:48:04 cheshire
176 Completed support for decoding packets containing oversized resource records
178 Revision 1.26 2003/07/19 03:25:23 cheshire
179 Change to make use of new GetLargeResourceRecord() call, for handling larger records
181 Revision 1.25 2003/07/18 00:13:23 cheshire
182 Remove erroneous trailing '\' from TXT record display
184 Revision 1.24 2003/07/17 17:10:51 cheshire
185 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
186 Further work: distinguish between PM and PU
188 Revision 1.23 2003/07/16 22:20:23 cheshire
189 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
190 Made mDNSNetMonitor distinguish between QM and QU in its logging output
192 Revision 1.22 2003/07/02 21:19:58 cheshire
193 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
195 Revision 1.21 2003/06/18 05:48:41 cheshire
198 Revision 1.20 2003/06/06 22:18:22 cheshire
199 Add extra space in Q output to line it up with RR output
201 Revision 1.19 2003/06/06 21:05:04 cheshire
202 Display state of cache-flush bit on additional records
204 Revision 1.18 2003/06/06 20:01:30 cheshire
205 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
206 (Global search-and-replace; no functional change to code execution.)
208 Revision 1.17 2003/06/06 14:26:50 cheshire
209 Explicitly #include <time.h> for the benefit of certain Linux distributions
211 Revision 1.16 2003/05/29 21:56:36 cheshire
213 Distinguish modern multicast queries from legacy multicast queries
214 In addition to record counts, display packet counts of queries, legacy queries, and responses
215 Include TTL in RR display
217 Revision 1.15 2003/05/29 20:03:57 cheshire
218 Various improvements:
219 Display start and end time, average rates in packets-per-minute,
220 show legacy queries as -LQ-, improve display of TXT and unknown records
222 Revision 1.14 2003/05/26 04:45:42 cheshire
223 Limit line length when printing super-long TXT records
225 Revision 1.13 2003/05/26 03:21:29 cheshire
226 Tidy up address structure naming:
227 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
228 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
229 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
231 Revision 1.12 2003/05/26 03:01:28 cheshire
232 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
234 Revision 1.11 2003/05/26 00:48:13 cheshire
235 If mDNS packet contains a non-zero message ID, then display it.
237 Revision 1.10 2003/05/22 01:10:32 cheshire
238 Indicate when the TC bit is set on a query packet
240 Revision 1.9 2003/05/21 03:56:00 cheshire
241 Improve display of Probe queries
243 Revision 1.8 2003/05/09 21:41:56 cheshire
244 Track deletion/goodbye packets as separate category
246 Revision 1.7 2003/05/07 00:16:01 cheshire
247 More detailed decoding of Resource Records
249 Revision 1.6 2003/05/05 21:16:16 cheshire
250 <rdar://problem/3241281> Change timenow from a local variable to a structure member
252 Revision 1.5 2003/04/19 01:16:22 cheshire
253 Add filter option, to monitor only packets from a single specified source address
255 Revision 1.4 2003/04/18 00:45:21 cheshire
256 Distinguish announcements (AN) from deletions (DE)
258 Revision 1.3 2003/04/15 18:26:01 cheshire
259 Added timestamps and help information
261 Revision 1.2 2003/04/04 20:42:02 cheshire
262 Fix broken statistics counting
264 Revision 1.1 2003/04/04 01:37:14 cheshire
269 //*************************************************************************************************************
270 // Incorporate mDNS.c functionality
272 // We want to use much of the functionality provided by "mDNS.c",
273 // except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
274 #define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
276 #undef mDNSCoreReceive
278 //*************************************************************************************************************
281 #include <stdio.h> // For printf()
282 #include <stdlib.h> // For malloc()
283 #include <string.h> // For bcopy()
284 #include <time.h> // For "struct tm" etc.
285 #include <signal.h> // For SIGINT, SIGTERM
286 #include <netdb.h> // For gethostbyname()
287 #include <sys/socket.h> // For AF_INET, AF_INET6, etc.
288 #include <arpa/inet.h> // For inet_addr()
289 #include <netinet/in.h> // For INADDR_NONE
291 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
292 #include "ExampleClientApp.h"
294 //*************************************************************************************************************
295 // Types and structures
299 // Primitive operations
303 // These are meta-categories;
304 // Query and Answer operations are actually subdivided into two classes:
305 // Browse query/answer and
306 // Resolve query/answer
310 // The "Browse" variants of query/answer
315 // The "Resolve" variants of query/answer
323 typedef struct ActivityStat_struct ActivityStat
;
324 struct ActivityStat_struct
330 int stat
[OP_NumTypes
];
333 typedef struct FilterList_struct FilterList
;
334 struct FilterList_struct
340 //*************************************************************************************************************
343 #define kReportTopServices 15
344 #define kReportTopHosts 15
346 //*************************************************************************************************************
349 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
350 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
352 struct timeval tv_start
, tv_end
, tv_interval
;
354 static FilterList
*Filters
;
355 #define ExactlyOneFilter (Filters && !Filters->next)
357 static int NumPktQ
, NumPktL
, NumPktR
, NumPktB
; // Query/Legacy/Response/Bad
358 static int NumProbes
, NumGoodbyes
, NumQuestions
, NumLegacy
, NumAnswers
, NumAdditionals
;
360 static ActivityStat
*stats
;
362 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
364 //*************************************************************************************************************
367 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
368 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
369 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
372 unsigned char buffer
[512];
374 va_start(ptr
,format
);
375 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
377 printf("%s", buffer
);
381 //*************************************************************************************************************
384 // Would benefit from a hash
388 HostPkt_Q
= 0, // Query
389 HostPkt_L
= 1, // Legacy Query
390 HostPkt_R
= 2, // Response
391 HostPkt_B
= 3, // Bad
398 unsigned long pkts
[HostPkt_NumTypes
];
399 unsigned long totalops
;
400 unsigned long stat
[OP_NumTypes
];
403 UTF8str255 HIHardware
;
404 UTF8str255 HISoftware
;
409 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
418 static HostList IPv4HostList
= { 0, 0, 0 };
419 static HostList IPv6HostList
= { 0, 0, 0 };
421 mDNSlocal HostEntry
*FindHost(const mDNSAddr
*addr
, HostList
* list
)
425 for (i
= 0; i
< list
->num
; i
++)
427 HostEntry
*entry
= list
->hosts
+ i
;
428 if (mDNSSameAddress(addr
, &entry
->addr
))
435 mDNSlocal HostEntry
*AddHost(const mDNSAddr
*addr
, HostList
* list
)
439 if (list
->num
>= list
->max
)
441 long newMax
= list
->max
+ 64;
442 HostEntry
*newHosts
= realloc(list
->hosts
, newMax
* sizeof(HostEntry
));
443 if (newHosts
== NULL
)
446 list
->hosts
= newHosts
;
449 entry
= list
->hosts
+ list
->num
++;
452 for (i
=0; i
<HostPkt_NumTypes
; i
++) entry
->pkts
[i
] = 0;
454 for (i
=0; i
<OP_NumTypes
; i
++) entry
->stat
[i
] = 0;
455 entry
->hostname
.c
[0] = 0;
456 entry
->revname
.c
[0] = 0;
457 entry
->HIHardware
.c
[0] = 0;
458 entry
->HISoftware
.c
[0] = 0;
459 entry
->NumQueries
= 0;
461 if (entry
->addr
.type
== mDNSAddrType_IPv4
)
463 mDNSv4Addr ip
= entry
->addr
.ip
.v4
;
465 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.", ip
.b
[3], ip
.b
[2], ip
.b
[1], ip
.b
[0]);
466 MakeDomainNameFromDNSNameString(&entry
->revname
, buffer
);
472 mDNSlocal HostEntry
*GotPacketFromHost(const mDNSAddr
*addr
, HostPkt_Type t
, mDNSOpaque16 id
)
474 if (ExactlyOneFilter
) return(NULL
);
477 HostList
*list
= (addr
->type
== mDNSAddrType_IPv4
) ? &IPv4HostList
: &IPv6HostList
;
478 HostEntry
*entry
= FindHost(addr
, list
);
479 if (!entry
) entry
= AddHost(addr
, list
);
480 if (!entry
) return(NULL
);
481 // Don't count our own interrogation packets
482 if (id
.NotAnInteger
!= 0xFFFF) entry
->pkts
[t
]++;
487 mDNSlocal
void RecordHostInfo(HostEntry
*entry
, const ResourceRecord
*const pktrr
)
489 if (!entry
->hostname
.c
[0])
491 if (pktrr
->rrtype
== kDNSType_A
|| pktrr
->rrtype
== kDNSType_AAAA
)
493 // Should really check that the rdata in the address record matches the source address of this packet
494 entry
->NumQueries
= 0;
495 AssignDomainName(&entry
->hostname
, pktrr
->name
);
498 if (pktrr
->rrtype
== kDNSType_PTR
)
499 if (SameDomainName(&entry
->revname
, pktrr
->name
))
501 entry
->NumQueries
= 0;
502 AssignDomainName(&entry
->hostname
, &pktrr
->rdata
->u
.name
);
505 else if (pktrr
->rrtype
== kDNSType_HINFO
)
507 RDataBody
*rd
= &pktrr
->rdata
->u
;
508 mDNSu8
*rdend
= (mDNSu8
*)rd
+ pktrr
->rdlength
;
509 mDNSu8
*hw
= rd
->txt
.c
;
510 mDNSu8
*sw
= hw
+ 1 + (mDNSu32
)hw
[0];
511 if (sw
+ 1 + sw
[0] <= rdend
)
513 AssignDomainName(&entry
->hostname
, pktrr
->name
);
514 mDNSPlatformMemCopy(hw
, entry
->HIHardware
.c
, 1 + (mDNSu32
)hw
[0]);
515 mDNSPlatformMemCopy(sw
, entry
->HISoftware
.c
, 1 + (mDNSu32
)sw
[0]);
520 mDNSlocal
void SendUnicastQuery(mDNS
*const m
, HostEntry
*entry
, domainname
*name
, mDNSu16 rrtype
, mDNSInterfaceID InterfaceID
)
522 const mDNSOpaque16 id
= { { 0xFF, 0xFF } };
524 mDNSu8
*qptr
= query
.data
;
525 const mDNSu8
*const limit
= query
.data
+ sizeof(query
.data
);
526 const mDNSAddr
*target
= &entry
->addr
;
527 InitializeDNSMessage(&query
.h
, id
, QueryFlags
);
528 qptr
= putQuestion(&query
, qptr
, limit
, name
, rrtype
, kDNSClass_IN
);
529 entry
->LastQuery
= m
->timenow
;
532 // Note: When there are multiple mDNSResponder agents running on a single machine
533 // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder)
534 // it is possible that unicast queries may not go to the primary system responder.
535 // We try the first query using unicast, but if that doesn't work we try again via multicast.
536 if (entry
->NumQueries
> 2)
538 target
= &AllDNSLinkGroup_v4
;
542 //mprintf("%#a Q\n", target);
543 InterfaceID
= mDNSInterface_Any
; // Send query from our unicast reply socket
544 m
->ExpectUnicastResponse
= m
->timenow
;
547 mDNSSendDNSMessage(&mDNSStorage
, &query
, qptr
, InterfaceID
, target
, MulticastDNSPort
, -1, mDNSNULL
);
550 mDNSlocal
void AnalyseHost(mDNS
*const m
, HostEntry
*entry
, const mDNSInterfaceID InterfaceID
)
552 // If we've done four queries without answer, give up
553 if (entry
->NumQueries
>= 4) return;
555 // If we've done a query in the last second, give the host a chance to reply before trying again
556 if (entry
->NumQueries
&& m
->timenow
- entry
->LastQuery
< mDNSPlatformOneSecond
) return;
558 // If we don't know the host name, try to find that first
559 if (!entry
->hostname
.c
[0])
561 if (entry
->revname
.c
[0])
563 SendUnicastQuery(m
, entry
, &entry
->revname
, kDNSType_PTR
, InterfaceID
);
564 //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries);
567 // If we have the host name but no HINFO, now ask for that
568 else if (!entry
->HIHardware
.c
[0])
570 SendUnicastQuery(m
, entry
, &entry
->hostname
, kDNSType_HINFO
, InterfaceID
);
571 //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries);
575 mDNSlocal
int CompareHosts(const void *p1
, const void *p2
)
577 return (int)(HostEntryTotalPackets((HostEntry
*)p2
) - HostEntryTotalPackets((HostEntry
*)p1
));
580 mDNSlocal
void ShowSortedHostList(HostList
*list
, int max
)
582 HostEntry
*e
, *end
= &list
->hosts
[(max
< list
->num
) ? max
: list
->num
];
583 qsort(list
->hosts
, list
->num
, sizeof(HostEntry
), CompareHosts
);
584 if (list
->num
) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner
, " Pkts Query LegacyQ Response");
585 for (e
= &list
->hosts
[0]; e
< end
; e
++)
587 int len
= mprintf("%#-25a", &e
->addr
);
588 if (len
> 25) mprintf("\n%25s", "");
589 mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e
->totalops
,
590 e
->stat
[OP_probe
], e
->stat
[OP_goodbye
],
591 e
->stat
[OP_browseq
], e
->stat
[OP_browsea
],
592 e
->stat
[OP_resolveq
], e
->stat
[OP_resolvea
]);
593 mprintf(" %8lu %8lu %8lu %8lu",
594 HostEntryTotalPackets(e
), e
->pkts
[HostPkt_Q
], e
->pkts
[HostPkt_L
], e
->pkts
[HostPkt_R
]);
595 if (e
->pkts
[HostPkt_B
]) mprintf("Bad: %8lu", e
->pkts
[HostPkt_B
]);
597 if (!e
->HISoftware
.c
[0] && e
->NumQueries
> 2)
598 mDNSPlatformMemCopy("\x0E*** Jaguar ***", &e
->HISoftware
, 15);
599 if (e
->hostname
.c
[0] || e
->HIHardware
.c
[0] || e
->HISoftware
.c
[0])
600 mprintf("%##-45s %#-14s %#s\n", e
->hostname
.c
, e
->HIHardware
.c
, e
->HISoftware
.c
);
604 //*************************************************************************************************************
605 // Receive and process packets
607 mDNSexport mDNSBool
ExtractServiceType(const domainname
*const fqdn
, domainname
*const srvtype
)
610 const mDNSu8
*src
= fqdn
->c
;
611 mDNSu8
*dst
= srvtype
->c
;
614 if (len
== 0 || len
>= 0x40) return(mDNSfalse
);
615 if (src
[1] != '_') src
+= 1 + len
;
618 if (len
== 0 || len
>= 0x40 || src
[1] != '_') return(mDNSfalse
);
619 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
622 if (len
== 0 || len
>= 0x40 || src
[1] != '_') return(mDNSfalse
);
623 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
625 *dst
++ = 0; // Put the null root label on the end of the service type
630 mDNSlocal
void recordstat(HostEntry
*entry
, domainname
*fqdn
, int op
, mDNSu16 rrtype
)
632 ActivityStat
**s
= &stats
;
637 if (rrtype
== kDNSType_SRV
|| rrtype
== kDNSType_TXT
) op
= op
- OP_browsegroup
+ OP_resolvegroup
;
638 else if (rrtype
!= kDNSType_PTR
) return;
641 if (!ExtractServiceType(fqdn
, &srvtype
)) return;
643 while (*s
&& !SameDomainName(&(*s
)->srvtype
, &srvtype
)) s
= &(*s
)->next
;
647 *s
= malloc(sizeof(ActivityStat
));
650 (*s
)->srvtype
= srvtype
;
653 for (i
=0; i
<OP_NumTypes
; i
++) (*s
)->stat
[i
] = 0;
665 mDNSlocal
void printstats(int max
)
669 for (i
=0; i
<max
; i
++)
672 ActivityStat
*s
, *m
= NULL
;
673 for (s
= stats
; s
; s
=s
->next
)
674 if (!s
->printed
&& max
< s
->totalops
)
675 { m
= s
; max
= s
->totalops
; }
677 m
->printed
= mDNStrue
;
678 if (i
==0) mprintf("%-25s%s\n", "Service Type", OPBanner
);
679 mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m
->srvtype
.c
, m
->totalops
, m
->stat
[OP_probe
],
680 m
->stat
[OP_goodbye
], m
->stat
[OP_browseq
], m
->stat
[OP_browsea
], m
->stat
[OP_resolveq
], m
->stat
[OP_resolvea
]);
684 mDNSlocal
const mDNSu8
*FindUpdate(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*ptr
, const mDNSu8
*const end
,\
685 DNSQuestion
*q
, LargeCacheRecord
*pkt
)
688 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
690 const mDNSu8
*p2
= ptr
;
691 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, kDNSRecordTypePacketAuth
, pkt
);
693 if (ResourceRecordAnswersQuestion(&pkt
->r
.resrec
, q
)) return(p2
);
698 mDNSlocal
void DisplayTimestamp(void)
702 gettimeofday(&tv
, NULL
);
703 localtime_r((time_t*)&tv
.tv_sec
, &tm
);
704 mprintf("\n%d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv
.tv_usec
);
707 mDNSlocal
void DisplayPacketHeader(const DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSAddr
*dstaddr
)
709 const char *const ptype
= (msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
) ? "-R- " :
710 (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
) ? "-Q- " : "-LQ-";
713 mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
714 srcaddr
, ptype
, msg
->h
.numQuestions
, msg
->h
.numAnswers
, msg
->h
.numAuthorities
, msg
->h
.numAdditionals
, end
- (mDNSu8
*)msg
);
716 if (msg
->h
.id
.NotAnInteger
) mprintf(" ID:%u", mDNSVal16(msg
->h
.id
));
718 if (!mDNSAddrIsDNSMulticast(dstaddr
)) mprintf(" To: %#a", dstaddr
);
720 if (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)
722 if (msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
) mprintf(" Truncated");
723 else mprintf(" Truncated (KA list continues in next packet)");
728 mDNSlocal
void DisplayResourceRecord(const mDNSAddr
*const srcaddr
, const char *const op
, const ResourceRecord
*const pktrr
)
730 static const char hexchars
[16] = "0123456789ABCDEF";
732 char buffer
[MaxWidth
+8];
735 RDataBody
*rd
= &pktrr
->rdata
->u
;
736 mDNSu8
*rdend
= (mDNSu8
*)rd
+ pktrr
->rdlength
;
737 int n
= mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr
, op
, DNSTypeName(pktrr
->rrtype
), pktrr
->rroriginalttl
, pktrr
->name
->c
);
739 switch(pktrr
->rrtype
)
741 case kDNSType_A
: n
+= mprintf("%.4a", &rd
->ipv4
); break;
742 case kDNSType_PTR
: n
+= mprintf("%##.*s", MaxWidth
- n
, rd
->name
.c
); break;
743 case kDNSType_HINFO
:// same as kDNSType_TXT below
745 mDNSu8
*t
= rd
->txt
.c
;
746 while (t
< rdend
&& t
[0] && p
< buffer
+MaxWidth
)
749 for (i
=1; i
<=t
[0] && p
< buffer
+MaxWidth
; i
++)
751 if (t
[i
] == '\\') *p
++ = '\\';
752 if (t
[i
] >= ' ') *p
++ = t
[i
];
758 *p
++ = hexchars
[t
[i
] >> 4];
759 *p
++ = hexchars
[t
[i
] & 0xF];
763 if (t
< rdend
&& t
[0]) { *p
++ = '\\'; *p
++ = ' '; }
766 n
+= mprintf("%.*s", MaxWidth
- n
, buffer
);
768 case kDNSType_AAAA
: n
+= mprintf("%.16a", &rd
->ipv6
); break;
769 case kDNSType_SRV
: n
+= mprintf("%##s:%d", rd
->srv
.target
.c
, mDNSVal16(rd
->srv
.port
)); break;
771 mDNSu8
*s
= rd
->data
;
772 while (s
< rdend
&& p
< buffer
+MaxWidth
)
774 if (*s
== '\\') *p
++ = '\\';
775 if (*s
>= ' ') *p
++ = *s
;
781 *p
++ = hexchars
[*s
>> 4];
782 *p
++ = hexchars
[*s
& 0xF];
787 n
+= mprintf("%.*s", MaxWidth
- n
, buffer
);
794 mDNSlocal
void HexDump(const mDNSu8
*ptr
, const mDNSu8
*const end
)
800 if (&ptr
[i
] < end
) mprintf("%02X ", ptr
[i
]);
803 if (&ptr
[i
] < end
) mprintf("%c", ptr
[i
] <= ' ' || ptr
[i
] >= 126 ? '.' : ptr
[i
]);
809 mDNSlocal
void DisplayError(const mDNSAddr
*srcaddr
, const mDNSu8
*ptr
, const mDNSu8
*const end
, char *msg
)
811 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr
, msg
);
815 mDNSlocal
void DisplayQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
,
816 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSInterfaceID InterfaceID
)
819 const mDNSu8
*ptr
= msg
->data
;
820 const mDNSu8
*auth
= LocateAuthorities(msg
, end
);
821 mDNSBool MQ
= (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
);
822 HostEntry
*entry
= GotPacketFromHost(srcaddr
, MQ
? HostPkt_Q
: HostPkt_L
, msg
->h
.id
);
823 LargeCacheRecord pkt
;
825 DisplayPacketHeader(msg
, end
, srcaddr
, srcport
, dstaddr
);
826 if (msg
->h
.id
.NotAnInteger
!= 0xFFFF)
828 if (MQ
) NumPktQ
++; else NumPktL
++;
831 for (i
=0; i
<msg
->h
.numQuestions
; i
++)
834 mDNSu8
*p2
= (mDNSu8
*)getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
835 mDNSu16 ucbit
= q
.qclass
& kDNSQClass_UnicastResponse
;
836 q
.qclass
&= ~kDNSQClass_UnicastResponse
;
837 if (!p2
) { DisplayError(srcaddr
, ptr
, end
, "QUESTION"); return; }
839 p2
= (mDNSu8
*)FindUpdate(m
, msg
, auth
, end
, &q
, &pkt
);
843 DisplayResourceRecord(srcaddr
, ucbit
? "(PU)" : "(PM)", &pkt
.r
.resrec
);
844 recordstat(entry
, &q
.qname
, OP_probe
, q
.qtype
);
845 p2
= (mDNSu8
*)skipDomainName(msg
, p2
, end
);
846 // Having displayed this update record, clear type and class so we don't display the same one again.
847 p2
[0] = p2
[1] = p2
[2] = p2
[3] = 0;
851 const char *ptype
= ucbit
? "(QU)" : "(QM)";
852 if (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
) NumQuestions
++;
853 else { NumLegacy
++; ptype
= "(LQ)"; }
854 mprintf("%#-16a %-5s %-5s %##s\n", srcaddr
, ptype
, DNSTypeName(q
.qtype
), q
.qname
.c
);
855 if (msg
->h
.id
.NotAnInteger
!= 0xFFFF) recordstat(entry
, &q
.qname
, OP_query
, q
.qtype
);
859 for (i
=0; i
<msg
->h
.numAnswers
; i
++)
861 const mDNSu8
*ep
= ptr
;
862 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAns
, &pkt
);
863 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "KNOWN ANSWER"); return; }
864 DisplayResourceRecord(srcaddr
, "(KA)", &pkt
.r
.resrec
);
866 // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
867 // the same as a single query, to more accurately reflect the burden on the network
868 // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
869 if (msg
->h
.numQuestions
== 0 && i
== 0)
870 recordstat(entry
, pkt
.r
.resrec
.name
, OP_query
, pkt
.r
.resrec
.rrtype
);
873 for (i
=0; i
<msg
->h
.numAuthorities
; i
++)
875 const mDNSu8
*ep
= ptr
;
876 ptr
= skipResourceRecord(msg
, ptr
, end
);
877 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "AUTHORITY"); return; }
880 if (entry
) AnalyseHost(m
, entry
, InterfaceID
);
883 mDNSlocal
void DisplayResponse(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*end
,
884 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSInterfaceID InterfaceID
)
887 const mDNSu8
*ptr
= msg
->data
;
888 HostEntry
*entry
= GotPacketFromHost(srcaddr
, HostPkt_R
, msg
->h
.id
);
889 LargeCacheRecord pkt
;
891 DisplayPacketHeader(msg
, end
, srcaddr
, srcport
, dstaddr
);
892 if (msg
->h
.id
.NotAnInteger
!= 0xFFFF) NumPktR
++;
894 for (i
=0; i
<msg
->h
.numQuestions
; i
++)
897 const mDNSu8
*ep
= ptr
;
898 ptr
= getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
899 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "QUESTION"); return; }
900 if (mDNSAddrIsDNSMulticast(dstaddr
))
901 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr
, DNSTypeName(q
.qtype
), q
.qname
.c
);
903 mprintf("%#-16a (Q) %-5s %##s\n", srcaddr
, DNSTypeName(q
.qtype
), q
.qname
.c
);
906 for (i
=0; i
<msg
->h
.numAnswers
; i
++)
908 const mDNSu8
*ep
= ptr
;
909 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAns
, &pkt
);
910 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "ANSWER"); return; }
911 if (pkt
.r
.resrec
.rroriginalttl
)
914 DisplayResourceRecord(srcaddr
, (pkt
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "(AN)" : "(AN+)", &pkt
.r
.resrec
);
915 if (msg
->h
.id
.NotAnInteger
!= 0xFFFF) recordstat(entry
, pkt
.r
.resrec
.name
, OP_answer
, pkt
.r
.resrec
.rrtype
);
916 if (entry
) RecordHostInfo(entry
, &pkt
.r
.resrec
);
921 DisplayResourceRecord(srcaddr
, "(DE)", &pkt
.r
.resrec
);
922 recordstat(entry
, pkt
.r
.resrec
.name
, OP_goodbye
, pkt
.r
.resrec
.rrtype
);
926 for (i
=0; i
<msg
->h
.numAuthorities
; i
++)
928 const mDNSu8
*ep
= ptr
;
929 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAuth
, &pkt
);
930 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "AUTHORITY"); return; }
931 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
932 srcaddr
, DNSTypeName(pkt
.r
.resrec
.rrtype
), pkt
.r
.resrec
.name
->c
);
935 for (i
=0; i
<msg
->h
.numAdditionals
; i
++)
937 const mDNSu8
*ep
= ptr
;
938 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAdd
, &pkt
);
939 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "ADDITIONAL"); return; }
941 DisplayResourceRecord(srcaddr
, (pkt
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "(AD)" : "(AD+)", &pkt
.r
.resrec
);
942 if (entry
) RecordHostInfo(entry
, &pkt
.r
.resrec
);
945 if (entry
) AnalyseHost(m
, entry
, InterfaceID
);
948 mDNSlocal
void ProcessUnicastResponse(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*end
, const mDNSAddr
*srcaddr
, const mDNSInterfaceID InterfaceID
)
951 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
952 HostEntry
*entry
= GotPacketFromHost(srcaddr
, HostPkt_R
, msg
->h
.id
);
953 //mprintf("%#a R\n", srcaddr);
955 for (i
=0; i
<msg
->h
.numAnswers
+ msg
->h
.numAuthorities
+ msg
->h
.numAdditionals
; i
++)
957 LargeCacheRecord pkt
;
958 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAns
, &pkt
);
959 if (pkt
.r
.resrec
.rroriginalttl
&& entry
) RecordHostInfo(entry
, &pkt
.r
.resrec
);
963 mDNSlocal mDNSBool
AddressMatchesFilterList(const mDNSAddr
*srcaddr
)
966 if (!Filters
) return(srcaddr
->type
== mDNSAddrType_IPv4
);
967 for (f
=Filters
; f
; f
=f
->next
) if (mDNSSameAddress(srcaddr
, &f
->FilterAddr
)) return(mDNStrue
);
971 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
972 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
)
974 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
975 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
976 const mDNSu8 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
977 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
979 (void)dstaddr
; // Unused
980 (void)dstport
; // Unused
982 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
983 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
984 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
985 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
986 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
988 // For now we're only interested in monitoring IPv4 traffic.
989 // All IPv6 packets should just be duplicates of the v4 packets.
990 if (AddressMatchesFilterList(srcaddr
))
993 if (!mDNSAddrIsDNSMulticast(dstaddr
))
995 if (QR_OP
== StdQ
) mprintf("Unicast query from %#a\n", srcaddr
);
996 else if (QR_OP
== StdR
) ProcessUnicastResponse(m
, msg
, end
, srcaddr
, InterfaceID
);
1000 if (QR_OP
== StdQ
) DisplayQuery (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, InterfaceID
);
1001 else if (QR_OP
== StdR
) DisplayResponse (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, InterfaceID
);
1004 debugf("Unknown DNS packet type %02X%02X (ignored)", msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1]);
1005 GotPacketFromHost(srcaddr
, HostPkt_B
, msg
->h
.id
);
1013 mDNSlocal mStatus
mDNSNetMonitor(void)
1016 int h
, m
, s
, mul
, div
, TotPkt
;
1019 mStatus status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
1020 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
1021 mDNS_Init_DontAdvertiseLocalAddresses
,
1022 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
1023 if (status
) return(status
);
1025 gettimeofday(&tv_start
, NULL
);
1026 mDNSPosixListenForSignalInEventLoop(SIGINT
);
1027 mDNSPosixListenForSignalInEventLoop(SIGTERM
);
1031 struct timeval timeout
= { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
1032 mDNSBool gotSomething
;
1033 mDNSPosixRunEventLoopOnce(&mDNSStorage
, &timeout
, &signals
, &gotSomething
);
1035 while ( !( sigismember( &signals
, SIGINT
) || sigismember( &signals
, SIGTERM
)));
1037 // Now display final summary
1038 TotPkt
= NumPktQ
+ NumPktL
+ NumPktR
;
1039 gettimeofday(&tv_end
, NULL
);
1040 tv_interval
= tv_end
;
1041 if (tv_start
.tv_usec
> tv_interval
.tv_usec
)
1042 { tv_interval
.tv_usec
+= 1000000; tv_interval
.tv_sec
--; }
1043 tv_interval
.tv_sec
-= tv_start
.tv_sec
;
1044 tv_interval
.tv_usec
-= tv_start
.tv_usec
;
1045 h
= (tv_interval
.tv_sec
/ 3600);
1046 m
= (tv_interval
.tv_sec
% 3600) / 60;
1047 s
= (tv_interval
.tv_sec
% 60);
1048 if (tv_interval
.tv_sec
> 10)
1051 div
= tv_interval
.tv_sec
;
1056 div
= tv_interval
.tv_sec
* 1000 + tv_interval
.tv_usec
/ 1000;
1057 if (div
== 0) div
=1;
1061 localtime_r((time_t*)&tv_start
.tv_sec
, &tm
);
1062 mprintf("Started %3d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv_start
.tv_usec
);
1063 localtime_r((time_t*)&tv_end
.tv_sec
, &tm
);
1064 mprintf("End %3d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv_end
.tv_usec
);
1065 mprintf("Captured for %3d:%02d:%02d.%06d\n", h
, m
, s
, tv_interval
.tv_usec
);
1066 if (!Filters
) mprintf("Unique source addresses seen on network: %ld\n", IPv4HostList
.num
+ IPv6HostList
.num
);
1068 mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ
, NumPktQ
* mul
/ div
);
1069 mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL
, NumPktL
* mul
/ div
);
1070 mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR
, NumPktR
* mul
/ div
);
1071 mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt
, TotPkt
* mul
/ div
);
1073 mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes
, NumProbes
* mul
/ div
);
1074 mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes
, NumGoodbyes
* mul
/ div
);
1075 mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions
, NumQuestions
* mul
/ div
);
1076 mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy
, NumLegacy
* mul
/ div
);
1077 mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers
, NumAnswers
* mul
/ div
);
1078 mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals
, NumAdditionals
* mul
/ div
);
1080 printstats(kReportTopServices
);
1082 if (!ExactlyOneFilter
)
1084 ShowSortedHostList(&IPv4HostList
, kReportTopHosts
);
1085 ShowSortedHostList(&IPv6HostList
, kReportTopHosts
);
1088 mDNS_Close(&mDNSStorage
);
1092 mDNSexport
int main(int argc
, char **argv
)
1094 const char *progname
= strrchr(argv
[0], '/') ? strrchr(argv
[0], '/') + 1 : argv
[0];
1098 setlinebuf(stdout
); // Want to see lines as they appear, not block buffered
1100 for (i
=1; i
<argc
; i
++)
1106 a
.type
= mDNSAddrType_IPv4
;
1108 if (inet_pton(AF_INET
, argv
[i
], &s4
) == 1)
1109 a
.ip
.v4
.NotAnInteger
= s4
.s_addr
;
1110 else if (inet_pton(AF_INET6
, argv
[i
], &s6
) == 1)
1112 a
.type
= mDNSAddrType_IPv6
;
1113 bcopy(&s6
, &a
.ip
.v6
, sizeof(a
.ip
.v6
));
1117 struct hostent
*h
= gethostbyname(argv
[i
]);
1118 if (h
) a
.ip
.v4
.NotAnInteger
= *(long*)h
->h_addr
;
1122 f
= malloc(sizeof(*f
));
1128 status
= mDNSNetMonitor();
1129 if (status
) { fprintf(stderr
, "%s: mDNSNetMonitor failed %ld\n", progname
, status
); return(status
); }
1133 fprintf(stderr
, "\nmDNS traffic monitor\n");
1134 fprintf(stderr
, "Usage: %s (<host>)\n", progname
);
1135 fprintf(stderr
, "Optional <host> parameter displays only packets from that host\n");
1137 fprintf(stderr
, "\nPer-packet header output:\n");
1138 fprintf(stderr
, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
1139 fprintf(stderr
, "-R- Multicast Response packet containing answers/announcements\n");
1140 fprintf(stderr
, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
1141 fprintf(stderr
, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
1143 fprintf(stderr
, "\nPer-record display:\n");
1144 fprintf(stderr
, "(PM) Probe Question (new service starting), requesting multicast response\n");
1145 fprintf(stderr
, "(PU) Probe Question (new service starting), requesting unicast response\n");
1146 fprintf(stderr
, "(DE) Deletion/Goodbye (service going away)\n");
1147 fprintf(stderr
, "(LQ) Legacy Query Question\n");
1148 fprintf(stderr
, "(QM) Query Question, requesting multicast response\n");
1149 fprintf(stderr
, "(QU) Query Question, requesting unicast response\n");
1150 fprintf(stderr
, "(KA) Known Answer (information querier already knows)\n");
1151 fprintf(stderr
, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
1152 fprintf(stderr
, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
1153 fprintf(stderr
, "(AD) Unique Additional Record Set (entire RR Set)\n");
1154 fprintf(stderr
, "(AD+) Additional records (add to existing RR Set members)\n");
1156 fprintf(stderr
, "\nFinal summary, sorted by service type:\n");
1157 fprintf(stderr
, "Probe Probes for this service type starting up\n");
1158 fprintf(stderr
, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
1159 fprintf(stderr
, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
1160 fprintf(stderr
, "BrowseA Browse answers/announcments advertising instances of this service\n");
1161 fprintf(stderr
, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
1162 fprintf(stderr
, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
1163 fprintf(stderr
, "\n");