2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
25 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
26 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
27 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
28 * therefore common sense dictates that if they are part of a compound statement then they
29 * should be indented to the same level as everything else in that compound statement.
30 * Indenting curly braces at the same level as the "if" implies that curly braces are
31 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
32 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
33 * understand why variable y is not of type "char*" just proves the point that poor code
34 * layout leads people to unfortunate misunderstandings about how the C language really works.)
36 Change History (most recent first):
38 $Log: NetMonitor.c,v $
39 Revision 1.49 2003/10/30 19:38:56 cheshire
40 Fix warning on certain compilers
42 Revision 1.48 2003/10/30 19:30:00 cheshire
43 Fix warnings on certain compilers
45 Revision 1.47 2003/09/05 18:49:57 cheshire
46 Add total packet size to display
48 Revision 1.46 2003/09/05 02:33:48 cheshire
49 Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window
51 Revision 1.45 2003/09/04 00:16:20 cheshire
52 Only show count of unique source addresses seen on network if we're not filtering
54 Revision 1.44 2003/09/02 22:13:28 cheshire
55 Show total host count in final summary table
57 Revision 1.43 2003/09/02 21:42:52 cheshire
58 Improved alignment of final summary table with v6 addresses
60 Revision 1.42 2003/09/02 20:59:24 cheshire
61 Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
63 Revision 1.41 2003/08/29 22:05:44 cheshire
64 Also count subsequent KA packets for the purposes of statistics counting
66 Revision 1.40 2003/08/29 16:43:24 cheshire
67 Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
69 Revision 1.39 2003/08/28 02:07:48 vlubet
70 Added "per hosts" statistics
72 Revision 1.38 2003/08/20 22:41:42 cheshire
73 Also display total multicast packet count
75 Revision 1.37 2003/08/20 22:32:08 cheshire
76 Error in DisplayQuery: Authorities come *after* Answers, not before
78 Revision 1.36 2003/08/18 23:20:10 cheshire
79 RDLength moved from the RData to the ResourceRecord object.
81 Revision 1.35 2003/08/15 20:17:28 cheshire
82 "LargeResourceRecord" changed to "LargeCacheRecord"
84 Revision 1.34 2003/08/14 02:19:55 cheshire
85 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
87 Revision 1.33 2003/08/12 19:56:26 cheshire
90 Revision 1.32 2003/08/06 18:57:01 cheshire
93 Revision 1.31 2003/08/06 02:05:12 cheshire
94 Add ability to give a list of hosts to monitor
96 Revision 1.30 2003/08/05 23:56:26 cheshire
97 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
98 (Right now mDNSPosix.c just reports 255 -- we should fix this)
100 Revision 1.29 2003/08/05 00:43:12 cheshire
101 Report errors encountered while processing authority section
103 Revision 1.28 2003/07/29 22:51:08 cheshire
104 Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them
106 Revision 1.27 2003/07/29 22:48:04 cheshire
107 Completed support for decoding packets containing oversized resource records
109 Revision 1.26 2003/07/19 03:25:23 cheshire
110 Change to make use of new GetLargeResourceRecord() call, for handling larger records
112 Revision 1.25 2003/07/18 00:13:23 cheshire
113 Remove erroneous trailing '\' from TXT record display
115 Revision 1.24 2003/07/17 17:10:51 cheshire
116 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
117 Further work: distinguish between PM and PU
119 Revision 1.23 2003/07/16 22:20:23 cheshire
120 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
121 Made mDNSNetMonitor distinguish between QM and QU in its logging output
123 Revision 1.22 2003/07/02 21:19:58 cheshire
124 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
126 Revision 1.21 2003/06/18 05:48:41 cheshire
129 Revision 1.20 2003/06/06 22:18:22 cheshire
130 Add extra space in Q output to line it up with RR output
132 Revision 1.19 2003/06/06 21:05:04 cheshire
133 Display state of cache-flush bit on additional records
135 Revision 1.18 2003/06/06 20:01:30 cheshire
136 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
137 (Global search-and-replace; no functional change to code execution.)
139 Revision 1.17 2003/06/06 14:26:50 cheshire
140 Explicitly #include <time.h> for the benefit of certain Linux distributions
142 Revision 1.16 2003/05/29 21:56:36 cheshire
144 Distinguish modern multicast queries from legacy multicast queries
145 In addition to record counts, display packet counts of queries, legacy queries, and responses
146 Include TTL in RR display
148 Revision 1.15 2003/05/29 20:03:57 cheshire
149 Various improvements:
150 Display start and end time, average rates in packets-per-minute,
151 show legacy queries as -LQ-, improve display of TXT and unknown records
153 Revision 1.14 2003/05/26 04:45:42 cheshire
154 Limit line length when printing super-long TXT records
156 Revision 1.13 2003/05/26 03:21:29 cheshire
157 Tidy up address structure naming:
158 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
159 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
160 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
162 Revision 1.12 2003/05/26 03:01:28 cheshire
163 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
165 Revision 1.11 2003/05/26 00:48:13 cheshire
166 If mDNS packet contains a non-zero message ID, then display it.
168 Revision 1.10 2003/05/22 01:10:32 cheshire
169 Indicate when the TC bit is set on a query packet
171 Revision 1.9 2003/05/21 03:56:00 cheshire
172 Improve display of Probe queries
174 Revision 1.8 2003/05/09 21:41:56 cheshire
175 Track deletion/goodbye packets as separate category
177 Revision 1.7 2003/05/07 00:16:01 cheshire
178 More detailed decoding of Resource Records
180 Revision 1.6 2003/05/05 21:16:16 cheshire
181 <rdar://problem/3241281> Change timenow from a local variable to a structure member
183 Revision 1.5 2003/04/19 01:16:22 cheshire
184 Add filter option, to monitor only packets from a single specified source address
186 Revision 1.4 2003/04/18 00:45:21 cheshire
187 Distinguish announcements (AN) from deletions (DE)
189 Revision 1.3 2003/04/15 18:26:01 cheshire
190 Added timestamps and help information
192 Revision 1.2 2003/04/04 20:42:02 cheshire
193 Fix broken statistics counting
195 Revision 1.1 2003/04/04 01:37:14 cheshire
200 //*************************************************************************************************************
201 // Incorporate mDNS.c functionality
203 // We want to use much of the functionality provided by "mDNS.c",
204 // except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
205 #define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
207 #undef mDNSCoreReceive
209 //*************************************************************************************************************
212 #include <stdio.h> // For printf()
213 #include <stdlib.h> // For malloc()
214 #include <string.h> // For bcopy()
215 #include <time.h> // For "struct tm" etc.
216 #include <netdb.h> // For gethostbyname()
217 #include <sys/socket.h> // For AF_INET, AF_INET6, etc.
218 #include <arpa/inet.h> // For inet_addr()
219 #include <netinet/in.h> // For INADDR_NONE
221 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
222 #include "ExampleClientApp.h"
224 //*************************************************************************************************************
225 // Types and structures
229 // Primitive operations
233 // These are meta-categories;
234 // Query and Answer operations are actually subdivided into two classes:
235 // Browse query/answer and
236 // Resolve query/answer
240 // The "Browse" variants of query/answer
245 // The "Resolve" variants of query/answer
253 typedef struct ActivityStat_struct ActivityStat
;
254 struct ActivityStat_struct
260 int stat
[OP_NumTypes
];
263 typedef struct FilterList_struct FilterList
;
264 struct FilterList_struct
270 //*************************************************************************************************************
273 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
274 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
276 struct timeval tv_start
, tv_end
, tv_interval
;
278 static FilterList
*Filters
;
279 #define ExactlyOneFilter (Filters && !Filters->next)
281 static int NumPktQ
, NumPktL
, NumPktR
, NumPktB
; // Query/Legacy/Response/Bad
282 static int NumProbes
, NumGoodbyes
, NumQuestions
, NumLegacy
, NumAnswers
, NumAdditionals
;
284 static ActivityStat
*stats
;
286 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
288 //*************************************************************************************************************
291 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
292 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
293 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
296 unsigned char buffer
[512];
298 va_start(ptr
,format
);
299 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
301 printf("%s", buffer
);
305 //*************************************************************************************************************
308 // Would benefit from a hash
312 HostPkt_Q
= 0, // Query
313 HostPkt_L
= 1, // Legacy Query
314 HostPkt_R
= 2, // Response
315 HostPkt_B
= 3, // Bad
322 unsigned long pkts
[HostPkt_NumTypes
];
323 unsigned long totalops
;
324 unsigned long stat
[OP_NumTypes
];
327 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
336 static HostList IPv4HostList
= { 0, 0, 0 };
337 static HostList IPv6HostList
= { 0, 0, 0 };
339 mDNSlocal HostEntry
*FindHost(const mDNSAddr
*addr
, HostList
* list
)
343 for (i
= 0; i
< list
->num
; i
++)
345 HostEntry
*entry
= list
->hosts
+ i
;
346 if (mDNSSameAddress(addr
, &entry
->addr
))
353 mDNSlocal HostEntry
*AddHost(HostList
* list
)
356 if (list
->num
>= list
->max
)
358 long newMax
= list
->max
+ 64;
359 HostEntry
*newHosts
= realloc(list
->hosts
, newMax
* sizeof(HostEntry
));
360 if (newHosts
== NULL
)
363 list
->hosts
= newHosts
;
365 entry
= list
->hosts
+ list
->num
++;
369 mDNSlocal HostEntry
*GotPacketFromHost(const mDNSAddr
*addr
, HostPkt_Type t
)
371 if (ExactlyOneFilter
) return(NULL
);
374 HostList
*list
= (addr
->type
== mDNSAddrType_IPv4
) ? &IPv4HostList
: &IPv6HostList
;
375 HostEntry
*entry
= FindHost(addr
, list
);
379 entry
= AddHost(list
);
380 if (!entry
) return(NULL
);
382 for (i
=0; i
<HostPkt_NumTypes
; i
++) entry
->pkts
[i
] = 0;
384 for (i
=0; i
<OP_NumTypes
; i
++) entry
->stat
[i
] = 0;
391 mDNSlocal
int CompareHosts(const void *p1
, const void *p2
)
393 return (int)(HostEntryTotalPackets((HostEntry
*)p2
) - HostEntryTotalPackets((HostEntry
*)p1
));
396 mDNSlocal
void ShowSortedHostList(HostList
*list
, int max
)
398 HostEntry
*e
, *end
= &list
->hosts
[(max
< list
->num
) ? max
: list
->num
];
399 qsort(list
->hosts
, list
->num
, sizeof(HostEntry
), CompareHosts
);
400 if (list
->num
) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner
, " Pkts Query LegacyQ Response");
401 for (e
= &list
->hosts
[0]; e
< end
; e
++)
403 int len
= mprintf("%#-25a", &e
->addr
);
404 if (len
> 25) mprintf("\n%25s", "");
405 mprintf("%8d %8d %8d %8d %8d %8d %8d", e
->totalops
,
406 e
->stat
[OP_probe
], e
->stat
[OP_goodbye
],
407 e
->stat
[OP_browseq
], e
->stat
[OP_browsea
],
408 e
->stat
[OP_resolveq
], e
->stat
[OP_resolvea
]);
409 mprintf(" %8lu %8lu %8lu %8lu",
410 HostEntryTotalPackets(e
), e
->pkts
[HostPkt_Q
], e
->pkts
[HostPkt_L
], e
->pkts
[HostPkt_R
]);
411 if (e
->pkts
[HostPkt_B
]) mprintf("Bad: %8lu", e
->pkts
[HostPkt_B
]);
416 //*************************************************************************************************************
417 // Receive and process packets
419 mDNSexport mDNSBool
ExtractServiceType(const domainname
*const fqdn
, domainname
*const srvtype
)
422 const mDNSu8
*src
= fqdn
->c
;
423 mDNSu8
*dst
= srvtype
->c
;
426 if (len
== 0 || len
>= 0x40) return(mDNSfalse
);
427 if (src
[1] != '_') src
+= 1 + len
;
430 if (len
== 0 || len
>= 0x40 || src
[1] != '_') return(mDNSfalse
);
431 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
434 if (len
== 0 || len
>= 0x40 || src
[1] != '_') return(mDNSfalse
);
435 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
437 *dst
++ = 0; // Put the null root label on the end of the service type
442 mDNSlocal
void recordstat(HostEntry
*entry
, domainname
*fqdn
, int op
, mDNSu16 rrtype
)
444 ActivityStat
**s
= &stats
;
449 if (rrtype
== kDNSType_SRV
|| rrtype
== kDNSType_TXT
) op
= op
- OP_browsegroup
+ OP_resolvegroup
;
450 else if (rrtype
!= kDNSType_PTR
) return;
453 if (!ExtractServiceType(fqdn
, &srvtype
)) return;
455 while (*s
&& !SameDomainName(&(*s
)->srvtype
, &srvtype
)) s
= &(*s
)->next
;
459 *s
= malloc(sizeof(ActivityStat
));
462 (*s
)->srvtype
= srvtype
;
465 for (i
=0; i
<OP_NumTypes
; i
++) (*s
)->stat
[i
] = 0;
477 mDNSlocal
void printstats(int max
)
480 for (i
=0; i
<max
; i
++)
483 ActivityStat
*s
, *m
= NULL
;
484 for (s
= stats
; s
; s
=s
->next
)
485 if (!s
->printed
&& max
< s
->totalops
)
486 { m
= s
; max
= s
->totalops
; }
488 m
->printed
= mDNStrue
;
489 if (i
==0) mprintf("%-25s%s\n", "Service Type", OPBanner
);
490 mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", &m
->srvtype
, m
->totalops
, m
->stat
[OP_probe
],
491 m
->stat
[OP_goodbye
], m
->stat
[OP_browseq
], m
->stat
[OP_browsea
], m
->stat
[OP_resolveq
], m
->stat
[OP_resolvea
]);
495 mDNSlocal
const mDNSu8
*FindUpdate(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*ptr
, const mDNSu8
*const end
, DNSQuestion
*q
, LargeCacheRecord
*pkt
)
498 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
500 const mDNSu8
*p2
= ptr
;
501 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, 0, pkt
);
503 if (ResourceRecordAnswersQuestion(&pkt
->r
.resrec
, q
)) return(p2
);
508 mDNSlocal
void DisplayTimestamp(void)
512 gettimeofday(&tv
, NULL
);
513 localtime_r((time_t*)&tv
.tv_sec
, &tm
);
514 mprintf("\n%d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv
.tv_usec
);
517 mDNSlocal
void DisplayPacketHeader(const DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
)
519 const char *const ptype
= (msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
) ? "-R- " :
520 (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
) ? "-Q- " : "-LQ-";
523 mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
524 srcaddr
, ptype
, msg
->h
.numQuestions
, msg
->h
.numAnswers
, msg
->h
.numAuthorities
, msg
->h
.numAdditionals
, end
- (mDNSu8
*)msg
);
526 if (msg
->h
.id
.NotAnInteger
) mprintf(" ID:%u", ((mDNSu16
)msg
->h
.id
.b
[0])<<8 | msg
->h
.id
.b
[1]);
528 if (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)
530 if (msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
) mprintf(" Truncated");
531 else mprintf(" Truncated (KA list continues in next packet)");
536 mDNSlocal
void DisplayResourceRecord(const mDNSAddr
*const srcaddr
, const char *const op
, const ResourceRecord
*const pktrr
)
538 static const char hexchars
[16] = "0123456789ABCDEF";
540 char buffer
[MaxWidth
+8];
543 RDataBody
*rd
= &pktrr
->rdata
->u
;
544 mDNSu8
*rdend
= (mDNSu8
*)rd
+ pktrr
->rdlength
;
545 mDNSu32 n
= mprintf("%#-16a %-5s %-5s%5d %##s -> ", srcaddr
, op
, DNSTypeName(pktrr
->rrtype
), pktrr
->rroriginalttl
, pktrr
->name
.c
);
547 switch(pktrr
->rrtype
)
549 case kDNSType_A
: n
+= mprintf("%.4a", &rd
->ip
); break;
550 case kDNSType_PTR
: n
+= mprintf("%##.*s", MaxWidth
- n
, &rd
->name
); break;
551 case kDNSType_HINFO
:// same as kDNSType_TXT below
553 mDNSu8
*t
= rd
->txt
.c
;
554 while (t
< rdend
&& t
[0] && p
< buffer
+MaxWidth
)
557 for (i
=1; i
<=t
[0] && p
< buffer
+MaxWidth
; i
++)
559 if (t
[i
] == '\\') *p
++ = '\\';
560 if (t
[i
] >= ' ') *p
++ = t
[i
];
566 *p
++ = hexchars
[t
[i
] >> 4];
567 *p
++ = hexchars
[t
[i
] & 0xF];
571 if (t
< rdend
&& t
[0]) { *p
++ = '\\'; *p
++ = ' '; }
574 n
+= mprintf("%.*s", MaxWidth
- n
, buffer
);
576 case kDNSType_AAAA
: n
+= mprintf("%.16a", &rd
->ipv6
); break;
577 case kDNSType_SRV
: n
+= mprintf("%##s:%d", &rd
->srv
.target
, ((mDNSu16
)rd
->srv
.port
.b
[0] << 8) | rd
->srv
.port
.b
[1]); break;
579 mDNSu8
*s
= rd
->data
;
580 while (s
< rdend
&& p
< buffer
+MaxWidth
)
582 if (*s
== '\\') *p
++ = '\\';
583 if (*s
>= ' ') *p
++ = *s
;
589 *p
++ = hexchars
[*s
>> 4];
590 *p
++ = hexchars
[*s
& 0xF];
595 n
+= mprintf("%.*s", MaxWidth
- n
, buffer
);
602 mDNSlocal
void HexDump(const mDNSu8
*ptr
, const mDNSu8
*const end
)
608 if (&ptr
[i
] < end
) mprintf("%02X ", ptr
[i
]);
611 if (&ptr
[i
] < end
) mprintf("%c", ptr
[i
] <= ' ' || ptr
[i
] >= 126 ? '.' : ptr
[i
]);
617 mDNSlocal
void DisplayError(const mDNSAddr
*srcaddr
, const mDNSu8
*ptr
, const mDNSu8
*const end
, char *msg
)
619 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr
, msg
);
623 mDNSlocal
void DisplayQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSInterfaceID InterfaceID
)
626 const mDNSu8
*ptr
= msg
->data
;
627 const mDNSu8
*auth
= LocateAuthorities(msg
, end
);
628 mDNSBool MQ
= (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
);
629 HostEntry
*entry
= GotPacketFromHost(srcaddr
, MQ
? HostPkt_Q
: HostPkt_L
);
630 LargeCacheRecord pkt
;
632 DisplayPacketHeader(msg
, end
, srcaddr
, srcport
);
633 if (MQ
) NumPktQ
++; else NumPktL
++;
635 for (i
=0; i
<msg
->h
.numQuestions
; i
++)
638 mDNSu8
*p2
= (mDNSu8
*)getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
639 mDNSu16 ucbit
= q
.qclass
& kDNSQClass_UnicastResponse
;
640 q
.qclass
&= ~kDNSQClass_UnicastResponse
;
641 if (!p2
) { DisplayError(srcaddr
, ptr
, end
, "QUESTION"); return; }
643 p2
= (mDNSu8
*)FindUpdate(m
, msg
, auth
, end
, &q
, &pkt
);
647 DisplayResourceRecord(srcaddr
, ucbit
? "(PU)" : "(PM)", &pkt
.r
.resrec
);
648 recordstat(entry
, &q
.qname
, OP_probe
, q
.qtype
);
649 p2
= (mDNSu8
*)skipDomainName(msg
, p2
, end
);
650 // Having displayed this update record, clear type and class so we don't display the same one again.
651 p2
[0] = p2
[1] = p2
[2] = p2
[3] = 0;
655 const char *ptype
= ucbit
? "(QU)" : "(QM)";
656 if (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
) NumQuestions
++;
657 else { NumLegacy
++; ptype
= "(LQ)"; }
658 mprintf("%#-16a %-5s %-5s %##s\n", srcaddr
, ptype
, DNSTypeName(q
.qtype
), q
.qname
.c
);
659 recordstat(entry
, &q
.qname
, OP_query
, q
.qtype
);
663 for (i
=0; i
<msg
->h
.numAnswers
; i
++)
665 const mDNSu8
*ep
= ptr
;
666 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
667 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "KNOWN ANSWER"); return; }
668 DisplayResourceRecord(srcaddr
, "(KA)", &pkt
.r
.resrec
);
670 // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
671 // the same as a single query, to more accurately reflect the burden on the network
672 // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
673 if (msg
->h
.numQuestions
== 0 && i
== 0)
674 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_query
, pkt
.r
.resrec
.rrtype
);
677 for (i
=0; i
<msg
->h
.numAuthorities
; i
++)
679 const mDNSu8
*ep
= ptr
;
680 ptr
= skipResourceRecord(msg
, ptr
, end
);
681 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "AUTHORITY"); return; }
685 mDNSlocal
void DisplayResponse(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSInterfaceID InterfaceID
)
688 const mDNSu8
*ptr
= msg
->data
;
689 HostEntry
*entry
= GotPacketFromHost(srcaddr
, HostPkt_R
);
690 LargeCacheRecord pkt
;
692 DisplayPacketHeader(msg
, end
, srcaddr
, srcport
);
695 for (i
=0; i
<msg
->h
.numQuestions
; i
++)
698 const mDNSu8
*ep
= ptr
;
699 ptr
= getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
700 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "QUESTION"); return; }
701 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr
, DNSTypeName(q
.qtype
), q
.qname
.c
);
704 for (i
=0; i
<msg
->h
.numAnswers
; i
++)
706 const mDNSu8
*ep
= ptr
;
707 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
708 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "ANSWER"); return; }
709 if (pkt
.r
.resrec
.rroriginalttl
)
712 DisplayResourceRecord(srcaddr
, (pkt
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "(AN)" : "(AN+)", &pkt
.r
.resrec
);
713 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_answer
, pkt
.r
.resrec
.rrtype
);
718 DisplayResourceRecord(srcaddr
, "(DE)", &pkt
.r
.resrec
);
719 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_goodbye
, pkt
.r
.resrec
.rrtype
);
723 for (i
=0; i
<msg
->h
.numAuthorities
; i
++)
725 const mDNSu8
*ep
= ptr
;
726 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
727 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "AUTHORITY"); return; }
728 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
729 srcaddr
, DNSTypeName(pkt
.r
.resrec
.rrtype
), pkt
.r
.resrec
.name
.c
);
732 for (i
=0; i
<msg
->h
.numAdditionals
; i
++)
734 const mDNSu8
*ep
= ptr
;
735 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
736 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "ADDITIONAL"); return; }
738 DisplayResourceRecord(srcaddr
, (pkt
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "(AD)" : "(AD+)", &pkt
.r
.resrec
);
742 mDNSlocal mDNSBool
AddressMatchesFilterList(const mDNSAddr
*srcaddr
)
745 if (!Filters
) return(srcaddr
->type
== mDNSAddrType_IPv4
);
746 for (f
=Filters
; f
; f
=f
->next
) if (mDNSSameAddress(srcaddr
, &f
->FilterAddr
)) return(mDNStrue
);
750 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
751 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, mDNSu8 ttl
)
753 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
754 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
755 const mDNSu8 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
756 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
758 (void)dstaddr
; // Unused
759 (void)dstport
; // Unused
761 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
762 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
763 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
764 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
765 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
769 debugf("** Apparent spoof mDNS %s packet from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
770 (QR_OP
== StdQ
) ? "Query" : (QR_OP
== StdR
) ? "Response" : "Unkown",
771 srcaddr
, dstaddr
, ttl
, InterfaceID
,
772 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
773 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
774 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
775 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
778 // For now we're only interested in monitoring IPv4 traffic.
779 // All IPv6 packets should just be duplicates of the v4 packets.
780 if (AddressMatchesFilterList(srcaddr
))
782 if (QR_OP
== StdQ
) DisplayQuery (m
, msg
, end
, srcaddr
, srcport
, InterfaceID
);
783 else if (QR_OP
== StdR
) DisplayResponse(m
, msg
, end
, srcaddr
, srcport
, InterfaceID
);
786 debugf("Unknown DNS packet type %02X%02X (ignored)", msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1]);
787 GotPacketFromHost(srcaddr
, HostPkt_B
);
793 mDNSlocal mStatus
mDNSNetMonitor(void)
796 int h
, m
, s
, mul
, div
, TotPkt
;
798 mStatus status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
799 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
800 mDNS_Init_DontAdvertiseLocalAddresses
,
801 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
802 if (status
) return(status
);
804 gettimeofday(&tv_start
, NULL
);
805 ExampleClientEventLoop(&mDNSStorage
); // Wait for user to hit Ctrl-C
807 // Now display final summary
808 TotPkt
= NumPktQ
+ NumPktL
+ NumPktR
;
809 gettimeofday(&tv_end
, NULL
);
810 tv_interval
= tv_end
;
811 if (tv_start
.tv_usec
> tv_interval
.tv_usec
)
812 { tv_interval
.tv_usec
+= 1000000; tv_interval
.tv_sec
--; }
813 tv_interval
.tv_sec
-= tv_start
.tv_sec
;
814 tv_interval
.tv_usec
-= tv_start
.tv_usec
;
815 h
= (tv_interval
.tv_sec
/ 3600);
816 m
= (tv_interval
.tv_sec
% 3600) / 60;
817 s
= (tv_interval
.tv_sec
% 60);
818 if (tv_interval
.tv_sec
> 10)
821 div
= tv_interval
.tv_sec
;
826 div
= tv_interval
.tv_sec
* 1000 + tv_interval
.tv_usec
/ 1000;
831 localtime_r((time_t*)&tv_start
.tv_sec
, &tm
);
832 mprintf("Started %3d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv_start
.tv_usec
);
833 localtime_r((time_t*)&tv_end
.tv_sec
, &tm
);
834 mprintf("End %3d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv_end
.tv_usec
);
835 mprintf("Captured for %3d:%02d:%02d.%06d\n", h
, m
, s
, tv_interval
.tv_usec
);
836 if (!Filters
) mprintf("Unique source addresses seen on network: %d\n", IPv4HostList
.num
+ IPv6HostList
.num
);
838 mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ
, NumPktQ
* mul
/ div
);
839 mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL
, NumPktL
* mul
/ div
);
840 mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR
, NumPktR
* mul
/ div
);
841 mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt
, TotPkt
* mul
/ div
);
843 mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes
, NumProbes
* mul
/ div
);
844 mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes
, NumGoodbyes
* mul
/ div
);
845 mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions
, NumQuestions
* mul
/ div
);
846 mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy
, NumLegacy
* mul
/ div
);
847 mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers
, NumAnswers
* mul
/ div
);
848 mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals
, NumAdditionals
* mul
/ div
);
852 if (!ExactlyOneFilter
)
854 ShowSortedHostList(&IPv4HostList
, 15);
855 ShowSortedHostList(&IPv6HostList
, 15);
858 mDNS_Close(&mDNSStorage
);
862 mDNSexport
int main(int argc
, char **argv
)
867 setlinebuf(stdout
); // Want to see lines as they appear, not block buffered
869 for (i
=1; i
<argc
; i
++)
875 a
.type
= mDNSAddrType_IPv4
;
877 if (inet_pton(AF_INET
, argv
[i
], &s4
) == 1)
878 a
.ip
.v4
.NotAnInteger
= s4
.s_addr
;
879 else if (inet_pton(AF_INET6
, argv
[i
], &s6
) == 1)
881 a
.type
= mDNSAddrType_IPv6
;
882 bcopy(&s6
, &a
.ip
.v6
, sizeof(a
.ip
.v6
));
886 struct hostent
*h
= gethostbyname(argv
[i
]);
887 if (h
) a
.ip
.v4
.NotAnInteger
= *(long*)h
->h_addr
;
891 f
= malloc(sizeof(*f
));
897 status
= mDNSNetMonitor();
898 if (status
) { fprintf(stderr
, "%s: mDNSNetMonitor failed %ld\n", argv
[0], status
); return(status
); }
902 fprintf(stderr
, "\nmDNS traffic monitor\n");
903 fprintf(stderr
, "Usage: %s (<host>)\n", argv
[0]);
904 fprintf(stderr
, "Optional <host> parameter displays only packets from that host\n");
906 fprintf(stderr
, "\nPer-packet header output:\n");
907 fprintf(stderr
, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
908 fprintf(stderr
, "-R- Multicast Response packet containing answers/announcements\n");
909 fprintf(stderr
, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
910 fprintf(stderr
, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
912 fprintf(stderr
, "\nPer-record display:\n");
913 fprintf(stderr
, "(PM) Probe Question (new service starting), requesting multicast response\n");
914 fprintf(stderr
, "(PU) Probe Question (new service starting), requesting unicast response\n");
915 fprintf(stderr
, "(DE) Deletion/Goodbye (service going away)\n");
916 fprintf(stderr
, "(LQ) Legacy Query Question\n");
917 fprintf(stderr
, "(QM) Query Question, requesting multicast response\n");
918 fprintf(stderr
, "(QU) Query Question, requesting unicast response\n");
919 fprintf(stderr
, "(KA) Known Answer (information querier already knows)\n");
920 fprintf(stderr
, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
921 fprintf(stderr
, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
922 fprintf(stderr
, "(AD) Unique Additional Record Set (entire RR Set)\n");
923 fprintf(stderr
, "(AD+) Additional records (add to existing RR Set members)\n");
925 fprintf(stderr
, "\nFinal summary, sorted by service type:\n");
926 fprintf(stderr
, "Probe Probes for this service type starting up\n");
927 fprintf(stderr
, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
928 fprintf(stderr
, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
929 fprintf(stderr
, "BrowseA Browse answers/announcments advertising instances of this service\n");
930 fprintf(stderr
, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
931 fprintf(stderr
, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
932 fprintf(stderr
, "\n");