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.47 2003/09/05 18:49:57 cheshire
40 Add total packet size to display
42 Revision 1.46 2003/09/05 02:33:48 cheshire
43 Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window
45 Revision 1.45 2003/09/04 00:16:20 cheshire
46 Only show count of unique source addresses seen on network if we're not filtering
48 Revision 1.44 2003/09/02 22:13:28 cheshire
49 Show total host count in final summary table
51 Revision 1.43 2003/09/02 21:42:52 cheshire
52 Improved alignment of final summary table with v6 addresses
54 Revision 1.42 2003/09/02 20:59:24 cheshire
55 Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
57 Revision 1.41 2003/08/29 22:05:44 cheshire
58 Also count subsequent KA packets for the purposes of statistics counting
60 Revision 1.40 2003/08/29 16:43:24 cheshire
61 Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
63 Revision 1.39 2003/08/28 02:07:48 vlubet
64 Added "per hosts" statistics
66 Revision 1.38 2003/08/20 22:41:42 cheshire
67 Also display total multicast packet count
69 Revision 1.37 2003/08/20 22:32:08 cheshire
70 Error in DisplayQuery: Authorities come *after* Answers, not before
72 Revision 1.36 2003/08/18 23:20:10 cheshire
73 RDLength moved from the RData to the ResourceRecord object.
75 Revision 1.35 2003/08/15 20:17:28 cheshire
76 "LargeResourceRecord" changed to "LargeCacheRecord"
78 Revision 1.34 2003/08/14 02:19:55 cheshire
79 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
81 Revision 1.33 2003/08/12 19:56:26 cheshire
84 Revision 1.32 2003/08/06 18:57:01 cheshire
87 Revision 1.31 2003/08/06 02:05:12 cheshire
88 Add ability to give a list of hosts to monitor
90 Revision 1.30 2003/08/05 23:56:26 cheshire
91 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
92 (Right now mDNSPosix.c just reports 255 -- we should fix this)
94 Revision 1.29 2003/08/05 00:43:12 cheshire
95 Report errors encountered while processing authority section
97 Revision 1.28 2003/07/29 22:51:08 cheshire
98 Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them
100 Revision 1.27 2003/07/29 22:48:04 cheshire
101 Completed support for decoding packets containing oversized resource records
103 Revision 1.26 2003/07/19 03:25:23 cheshire
104 Change to make use of new GetLargeResourceRecord() call, for handling larger records
106 Revision 1.25 2003/07/18 00:13:23 cheshire
107 Remove erroneous trailing '\' from TXT record display
109 Revision 1.24 2003/07/17 17:10:51 cheshire
110 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
111 Further work: distinguish between PM and PU
113 Revision 1.23 2003/07/16 22:20:23 cheshire
114 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
115 Made mDNSNetMonitor distinguish between QM and QU in its logging output
117 Revision 1.22 2003/07/02 21:19:58 cheshire
118 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
120 Revision 1.21 2003/06/18 05:48:41 cheshire
123 Revision 1.20 2003/06/06 22:18:22 cheshire
124 Add extra space in Q output to line it up with RR output
126 Revision 1.19 2003/06/06 21:05:04 cheshire
127 Display state of cache-flush bit on additional records
129 Revision 1.18 2003/06/06 20:01:30 cheshire
130 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
131 (Global search-and-replace; no functional change to code execution.)
133 Revision 1.17 2003/06/06 14:26:50 cheshire
134 Explicitly #include <time.h> for the benefit of certain Linux distributions
136 Revision 1.16 2003/05/29 21:56:36 cheshire
138 Distinguish modern multicast queries from legacy multicast queries
139 In addition to record counts, display packet counts of queries, legacy queries, and responses
140 Include TTL in RR display
142 Revision 1.15 2003/05/29 20:03:57 cheshire
143 Various improvements:
144 Display start and end time, average rates in packets-per-minute,
145 show legacy queries as -LQ-, improve display of TXT and unknown records
147 Revision 1.14 2003/05/26 04:45:42 cheshire
148 Limit line length when printing super-long TXT records
150 Revision 1.13 2003/05/26 03:21:29 cheshire
151 Tidy up address structure naming:
152 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
153 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
154 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
156 Revision 1.12 2003/05/26 03:01:28 cheshire
157 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
159 Revision 1.11 2003/05/26 00:48:13 cheshire
160 If mDNS packet contains a non-zero message ID, then display it.
162 Revision 1.10 2003/05/22 01:10:32 cheshire
163 Indicate when the TC bit is set on a query packet
165 Revision 1.9 2003/05/21 03:56:00 cheshire
166 Improve display of Probe queries
168 Revision 1.8 2003/05/09 21:41:56 cheshire
169 Track deletion/goodbye packets as separate category
171 Revision 1.7 2003/05/07 00:16:01 cheshire
172 More detailed decoding of Resource Records
174 Revision 1.6 2003/05/05 21:16:16 cheshire
175 <rdar://problem/3241281> Change timenow from a local variable to a structure member
177 Revision 1.5 2003/04/19 01:16:22 cheshire
178 Add filter option, to monitor only packets from a single specified source address
180 Revision 1.4 2003/04/18 00:45:21 cheshire
181 Distinguish announcements (AN) from deletions (DE)
183 Revision 1.3 2003/04/15 18:26:01 cheshire
184 Added timestamps and help information
186 Revision 1.2 2003/04/04 20:42:02 cheshire
187 Fix broken statistics counting
189 Revision 1.1 2003/04/04 01:37:14 cheshire
194 //*************************************************************************************************************
195 // Incorporate mDNS.c functionality
197 // We want to use much of the functionality provided by "mDNS.c",
198 // except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
199 #define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
201 #undef mDNSCoreReceive
203 //*************************************************************************************************************
206 #include <stdio.h> // For printf()
207 #include <stdlib.h> // For malloc()
208 #include <string.h> // For bcopy()
209 #include <time.h> // For "struct tm" etc.
210 #include <netdb.h> // For gethostbyname()
211 #include <sys/socket.h> // For AF_INET, AF_INET6, etc.
212 #include <arpa/inet.h> // For inet_addr()
213 #include <netinet/in.h> // For INADDR_NONE
215 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
216 #include "ExampleClientApp.h"
218 //*************************************************************************************************************
219 // Types and structures
223 // Primitive operations
227 // These are meta-categories;
228 // Query and Answer operations are actually subdivided into two classes:
229 // Browse query/answer and
230 // Resolve query/answer
234 // The "Browse" variants of query/answer
239 // The "Resolve" variants of query/answer
247 typedef struct ActivityStat_struct ActivityStat
;
248 struct ActivityStat_struct
254 int stat
[OP_NumTypes
];
257 typedef struct FilterList_struct FilterList
;
258 struct FilterList_struct
264 //*************************************************************************************************************
267 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
268 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
270 struct timeval tv_start
, tv_end
, tv_interval
;
272 static FilterList
*Filters
;
273 #define ExactlyOneFilter (Filters && !Filters->next)
275 static int NumPktQ
, NumPktL
, NumPktR
, NumPktB
; // Query/Legacy/Response/Bad
276 static int NumProbes
, NumGoodbyes
, NumQuestions
, NumLegacy
, NumAnswers
, NumAdditionals
;
278 static ActivityStat
*stats
;
280 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
282 //*************************************************************************************************************
285 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
286 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
287 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
290 unsigned char buffer
[512];
292 va_start(ptr
,format
);
293 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
295 printf("%s", buffer
);
299 //*************************************************************************************************************
302 // Would benefit from a hash
306 HostPkt_Q
= 0, // Query
307 HostPkt_L
= 1, // Legacy Query
308 HostPkt_R
= 2, // Response
309 HostPkt_B
= 3, // Bad
310 HostPkt_NumTypes
= 4,
316 unsigned long pkts
[HostPkt_NumTypes
];
317 unsigned long totalops
;
318 unsigned long stat
[OP_NumTypes
];
321 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
330 static HostList IPv4HostList
= { 0, 0, 0 };
331 static HostList IPv6HostList
= { 0, 0, 0 };
333 mDNSlocal HostEntry
*FindHost(const mDNSAddr
*addr
, HostList
* list
)
337 for (i
= 0; i
< list
->num
; i
++)
339 HostEntry
*entry
= list
->hosts
+ i
;
340 if (mDNSSameAddress(addr
, &entry
->addr
))
347 mDNSlocal HostEntry
*AddHost(HostList
* list
)
350 if (list
->num
>= list
->max
)
352 long newMax
= list
->max
+ 64;
353 HostEntry
*newHosts
= realloc(list
->hosts
, newMax
* sizeof(HostEntry
));
354 if (newHosts
== NULL
)
357 list
->hosts
= newHosts
;
359 entry
= list
->hosts
+ list
->num
++;
363 mDNSlocal HostEntry
*GotPacketFromHost(const mDNSAddr
*addr
, HostPkt_Type t
)
365 if (ExactlyOneFilter
) return(NULL
);
368 HostList
*list
= (addr
->type
== mDNSAddrType_IPv4
) ? &IPv4HostList
: &IPv6HostList
;
369 HostEntry
*entry
= FindHost(addr
, list
);
373 entry
= AddHost(list
);
374 if (!entry
) return(NULL
);
376 for (i
=0; i
<HostPkt_NumTypes
; i
++) entry
->pkts
[i
] = 0;
378 for (i
=0; i
<OP_NumTypes
; i
++) entry
->stat
[i
] = 0;
385 mDNSlocal
int CompareHosts(const void *p1
, const void *p2
)
387 return (int)(HostEntryTotalPackets((HostEntry
*)p2
) - HostEntryTotalPackets((HostEntry
*)p1
));
390 mDNSlocal
void ShowSortedHostList(HostList
*list
, int max
)
392 HostEntry
*e
, *end
= &list
->hosts
[(max
< list
->num
) ? max
: list
->num
];
393 qsort(list
->hosts
, list
->num
, sizeof(HostEntry
), CompareHosts
);
394 if (list
->num
) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner
, " Pkts Query LegacyQ Response");
395 for (e
= &list
->hosts
[0]; e
< end
; e
++)
397 int len
= mprintf("%#-25a", &e
->addr
);
398 if (len
> 25) mprintf("\n%25s", "");
399 mprintf("%8d %8d %8d %8d %8d %8d %8d", e
->totalops
,
400 e
->stat
[OP_probe
], e
->stat
[OP_goodbye
],
401 e
->stat
[OP_browseq
], e
->stat
[OP_browsea
],
402 e
->stat
[OP_resolveq
], e
->stat
[OP_resolvea
]);
403 mprintf(" %8lu %8lu %8lu %8lu",
404 HostEntryTotalPackets(e
), e
->pkts
[HostPkt_Q
], e
->pkts
[HostPkt_L
], e
->pkts
[HostPkt_R
]);
405 if (e
->pkts
[HostPkt_B
]) mprintf("Bad: %8lu", e
->pkts
[HostPkt_B
]);
410 //*************************************************************************************************************
411 // Receive and process packets
413 mDNSexport mDNSBool
ExtractServiceType(const domainname
*const fqdn
, domainname
*const srvtype
)
416 const mDNSu8
*src
= fqdn
->c
;
417 mDNSu8
*dst
= srvtype
->c
;
420 if (len
== 0 || len
>= 0x40) return(mDNSfalse
);
421 if (src
[1] != '_') src
+= 1 + len
;
424 if (len
== 0 || len
>= 0x40 || src
[1] != '_') return(mDNSfalse
);
425 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
428 if (len
== 0 || len
>= 0x40 || src
[1] != '_') return(mDNSfalse
);
429 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
431 *dst
++ = 0; // Put the null root label on the end of the service type
436 mDNSlocal
void recordstat(HostEntry
*entry
, domainname
*fqdn
, int op
, mDNSu16 rrtype
)
438 ActivityStat
**s
= &stats
;
443 if (rrtype
== kDNSType_SRV
|| rrtype
== kDNSType_TXT
) op
= op
- OP_browsegroup
+ OP_resolvegroup
;
444 else if (rrtype
!= kDNSType_PTR
) return;
447 if (!ExtractServiceType(fqdn
, &srvtype
)) return;
449 while (*s
&& !SameDomainName(&(*s
)->srvtype
, &srvtype
)) s
= &(*s
)->next
;
453 *s
= malloc(sizeof(ActivityStat
));
456 (*s
)->srvtype
= srvtype
;
459 for (i
=0; i
<OP_NumTypes
; i
++) (*s
)->stat
[i
] = 0;
471 mDNSlocal
void printstats(int max
)
474 for (i
=0; i
<max
; i
++)
477 ActivityStat
*s
, *m
= NULL
;
478 for (s
= stats
; s
; s
=s
->next
)
479 if (!s
->printed
&& max
< s
->totalops
)
480 { m
= s
; max
= s
->totalops
; }
482 m
->printed
= mDNStrue
;
483 if (i
==0) mprintf("%-25s%s\n", "Service Type", OPBanner
);
484 mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", &m
->srvtype
, m
->totalops
, m
->stat
[OP_probe
],
485 m
->stat
[OP_goodbye
], m
->stat
[OP_browseq
], m
->stat
[OP_browsea
], m
->stat
[OP_resolveq
], m
->stat
[OP_resolvea
]);
489 mDNSlocal
const mDNSu8
*FindUpdate(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*ptr
, const mDNSu8
*const end
, DNSQuestion
*q
, LargeCacheRecord
*pkt
)
492 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
494 const mDNSu8
*p2
= ptr
;
495 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, 0, pkt
);
497 if (ResourceRecordAnswersQuestion(&pkt
->r
.resrec
, q
)) return(p2
);
502 mDNSlocal
void DisplayTimestamp(void)
506 gettimeofday(&tv
, NULL
);
507 localtime_r((time_t*)&tv
.tv_sec
, &tm
);
508 mprintf("\n%d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv
.tv_usec
);
511 mDNSlocal
void DisplayPacketHeader(const DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
)
513 const char *const ptype
= (msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
) ? "-R- " :
514 (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
) ? "-Q- " : "-LQ-";
517 mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
518 srcaddr
, ptype
, msg
->h
.numQuestions
, msg
->h
.numAnswers
, msg
->h
.numAuthorities
, msg
->h
.numAdditionals
, end
- (mDNSu8
*)msg
);
520 if (msg
->h
.id
.NotAnInteger
) mprintf(" ID:%u", ((mDNSu16
)msg
->h
.id
.b
[0])<<8 | msg
->h
.id
.b
[1]);
522 if (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)
524 if (msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
) mprintf(" Truncated");
525 else mprintf(" Truncated (KA list continues in next packet)");
530 mDNSlocal
void DisplayResourceRecord(const mDNSAddr
*const srcaddr
, const char *const op
, const ResourceRecord
*const pktrr
)
532 static const char hexchars
[16] = "0123456789ABCDEF";
534 char buffer
[MaxWidth
+8];
537 RDataBody
*rd
= &pktrr
->rdata
->u
;
538 mDNSu8
*rdend
= (mDNSu8
*)rd
+ pktrr
->rdlength
;
539 mDNSu32 n
= mprintf("%#-16a %-5s %-5s%5d %##s -> ", srcaddr
, op
, DNSTypeName(pktrr
->rrtype
), pktrr
->rroriginalttl
, pktrr
->name
.c
);
541 switch(pktrr
->rrtype
)
543 case kDNSType_A
: n
+= mprintf("%.4a", &rd
->ip
); break;
544 case kDNSType_PTR
: n
+= mprintf("%##.*s", MaxWidth
- n
, &rd
->name
); break;
545 case kDNSType_HINFO
:// same as kDNSType_TXT below
547 mDNSu8
*t
= rd
->txt
.c
;
548 while (t
< rdend
&& t
[0] && p
< buffer
+MaxWidth
)
551 for (i
=1; i
<=t
[0] && p
< buffer
+MaxWidth
; i
++)
553 if (t
[i
] == '\\') *p
++ = '\\';
554 if (t
[i
] >= ' ') *p
++ = t
[i
];
560 *p
++ = hexchars
[t
[i
] >> 4];
561 *p
++ = hexchars
[t
[i
] & 0xF];
565 if (t
< rdend
&& t
[0]) { *p
++ = '\\'; *p
++ = ' '; }
568 n
+= mprintf("%.*s", MaxWidth
- n
, buffer
);
570 case kDNSType_AAAA
: n
+= mprintf("%.16a", &rd
->ipv6
); break;
571 case kDNSType_SRV
: n
+= mprintf("%##s:%d", &rd
->srv
.target
, ((mDNSu16
)rd
->srv
.port
.b
[0] << 8) | rd
->srv
.port
.b
[1]); break;
573 mDNSu8
*s
= rd
->data
;
574 while (s
< rdend
&& p
< buffer
+MaxWidth
)
576 if (*s
== '\\') *p
++ = '\\';
577 if (*s
>= ' ') *p
++ = *s
;
583 *p
++ = hexchars
[*s
>> 4];
584 *p
++ = hexchars
[*s
& 0xF];
589 n
+= mprintf("%.*s", MaxWidth
- n
, buffer
);
596 mDNSlocal
void HexDump(const mDNSu8
*ptr
, const mDNSu8
*const end
)
602 if (&ptr
[i
] < end
) mprintf("%02X ", ptr
[i
]);
605 if (&ptr
[i
] < end
) mprintf("%c", ptr
[i
] <= ' ' || ptr
[i
] >= 126 ? '.' : ptr
[i
]);
611 mDNSlocal
void DisplayError(const mDNSAddr
*srcaddr
, const mDNSu8
*ptr
, const mDNSu8
*const end
, char *msg
)
613 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr
, msg
);
617 mDNSlocal
void DisplayQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSInterfaceID InterfaceID
)
620 const mDNSu8
*ptr
= msg
->data
;
621 const mDNSu8
*auth
= LocateAuthorities(msg
, end
);
622 mDNSBool MQ
= (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
);
623 HostEntry
*entry
= GotPacketFromHost(srcaddr
, MQ
? HostPkt_Q
: HostPkt_L
);
624 LargeCacheRecord pkt
;
626 DisplayPacketHeader(msg
, end
, srcaddr
, srcport
);
627 if (MQ
) NumPktQ
++; else NumPktL
++;
629 for (i
=0; i
<msg
->h
.numQuestions
; i
++)
633 const mDNSu8
*ep
= ptr
;
634 ptr
= getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
635 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "QUESTION"); return; }
636 mDNSu16 ucbit
= q
.qclass
& kDNSQClass_UnicastResponse
;
637 q
.qclass
&= ~kDNSQClass_UnicastResponse
;
638 p2
= (mDNSu8
*)FindUpdate(m
, msg
, auth
, end
, &q
, &pkt
);
642 DisplayResourceRecord(srcaddr
, ucbit
? "(PU)" : "(PM)", &pkt
.r
.resrec
);
643 recordstat(entry
, &q
.qname
, OP_probe
, q
.qtype
);
644 p2
= (mDNSu8
*)skipDomainName(msg
, p2
, end
);
645 // Having displayed this update record, clear type and class so we don't display the same one again.
646 p2
[0] = p2
[1] = p2
[2] = p2
[3] = 0;
650 const char *ptype
= ucbit
? "(QU)" : "(QM)";
651 if (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
) NumQuestions
++;
652 else { NumLegacy
++; ptype
= "(LQ)"; }
653 mprintf("%#-16a %-5s %-5s %##s\n", srcaddr
, ptype
, DNSTypeName(q
.qtype
), q
.qname
.c
);
654 recordstat(entry
, &q
.qname
, OP_query
, q
.qtype
);
658 for (i
=0; i
<msg
->h
.numAnswers
; i
++)
660 const mDNSu8
*ep
= ptr
;
661 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
662 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "KNOWN ANSWER"); return; }
663 DisplayResourceRecord(srcaddr
, "(KA)", &pkt
.r
.resrec
);
665 // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
666 // the same as a single query, to more accurately reflect the burden on the network
667 // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
668 if (msg
->h
.numQuestions
== 0 && i
== 0)
669 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_query
, pkt
.r
.resrec
.rrtype
);
672 for (i
=0; i
<msg
->h
.numAuthorities
; i
++)
674 const mDNSu8
*ep
= ptr
;
675 ptr
= skipResourceRecord(msg
, ptr
, end
);
676 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "AUTHORITY"); return; }
680 mDNSlocal
void DisplayResponse(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSInterfaceID InterfaceID
)
683 const mDNSu8
*ptr
= msg
->data
;
684 HostEntry
*entry
= GotPacketFromHost(srcaddr
, HostPkt_R
);
685 LargeCacheRecord pkt
;
687 DisplayPacketHeader(msg
, end
, srcaddr
, srcport
);
690 for (i
=0; i
<msg
->h
.numQuestions
; i
++)
693 const mDNSu8
*ep
= ptr
;
694 ptr
= getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
695 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "QUESTION"); return; }
696 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr
, DNSTypeName(q
.qtype
), q
.qname
.c
);
699 for (i
=0; i
<msg
->h
.numAnswers
; i
++)
701 const mDNSu8
*ep
= ptr
;
702 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
703 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "ANSWER"); return; }
704 if (pkt
.r
.resrec
.rroriginalttl
)
707 DisplayResourceRecord(srcaddr
, (pkt
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "(AN)" : "(AN+)", &pkt
.r
.resrec
);
708 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_answer
, pkt
.r
.resrec
.rrtype
);
713 DisplayResourceRecord(srcaddr
, "(DE)", &pkt
.r
.resrec
);
714 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_goodbye
, pkt
.r
.resrec
.rrtype
);
718 for (i
=0; i
<msg
->h
.numAuthorities
; i
++)
720 const mDNSu8
*ep
= ptr
;
721 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
722 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "AUTHORITY"); return; }
723 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
724 srcaddr
, DNSTypeName(pkt
.r
.resrec
.rrtype
), pkt
.r
.resrec
.name
.c
);
727 for (i
=0; i
<msg
->h
.numAdditionals
; i
++)
729 const mDNSu8
*ep
= ptr
;
730 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
731 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "ADDITIONAL"); return; }
733 DisplayResourceRecord(srcaddr
, (pkt
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "(AD)" : "(AD+)", &pkt
.r
.resrec
);
737 mDNSlocal mDNSBool
AddressMatchesFilterList(const mDNSAddr
*srcaddr
)
740 if (!Filters
) return(srcaddr
->type
== mDNSAddrType_IPv4
);
741 for (f
=Filters
; f
; f
=f
->next
) if (mDNSSameAddress(srcaddr
, &f
->FilterAddr
)) return(mDNStrue
);
745 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
746 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, mDNSu8 ttl
)
748 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
749 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
750 const mDNSu8 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
752 (void)dstaddr
; // Unused
753 (void)dstport
; // Unused
755 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
756 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
757 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
758 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
759 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
760 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
764 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",
765 (QR_OP
== StdQ
) ? "Query" : (QR_OP
== StdR
) ? "Response" : "Unkown",
766 srcaddr
, dstaddr
, ttl
, InterfaceID
,
767 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
768 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
769 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
770 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
773 // For now we're only interested in monitoring IPv4 traffic.
774 // All IPv6 packets should just be duplicates of the v4 packets.
775 if (AddressMatchesFilterList(srcaddr
))
777 if (QR_OP
== StdQ
) DisplayQuery (m
, msg
, end
, srcaddr
, srcport
, InterfaceID
);
778 else if (QR_OP
== StdR
) DisplayResponse(m
, msg
, end
, srcaddr
, srcport
, InterfaceID
);
781 debugf("Unknown DNS packet type %02X%02X (ignored)", msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1]);
782 GotPacketFromHost(srcaddr
, HostPkt_B
);
788 mDNSlocal mStatus
mDNSNetMonitor(void)
791 int h
, m
, s
, mul
, div
, TotPkt
;
793 mStatus status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
794 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
795 mDNS_Init_DontAdvertiseLocalAddresses
,
796 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
797 if (status
) return(status
);
799 gettimeofday(&tv_start
, NULL
);
800 ExampleClientEventLoop(&mDNSStorage
); // Wait for user to hit Ctrl-C
802 // Now display final summary
803 TotPkt
= NumPktQ
+ NumPktL
+ NumPktR
;
804 gettimeofday(&tv_end
, NULL
);
805 tv_interval
= tv_end
;
806 if (tv_start
.tv_usec
> tv_interval
.tv_usec
)
807 { tv_interval
.tv_usec
+= 1000000; tv_interval
.tv_sec
--; }
808 tv_interval
.tv_sec
-= tv_start
.tv_sec
;
809 tv_interval
.tv_usec
-= tv_start
.tv_usec
;
810 h
= (tv_interval
.tv_sec
/ 3600);
811 m
= (tv_interval
.tv_sec
% 3600) / 60;
812 s
= (tv_interval
.tv_sec
% 60);
813 if (tv_interval
.tv_sec
> 10)
816 div
= tv_interval
.tv_sec
;
821 div
= tv_interval
.tv_sec
* 1000 + tv_interval
.tv_usec
/ 1000;
826 localtime_r((time_t*)&tv_start
.tv_sec
, &tm
);
827 mprintf("Started %3d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv_start
.tv_usec
);
828 localtime_r((time_t*)&tv_end
.tv_sec
, &tm
);
829 mprintf("End %3d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv_end
.tv_usec
);
830 mprintf("Captured for %3d:%02d:%02d.%06d\n", h
, m
, s
, tv_interval
.tv_usec
);
831 if (!Filters
) mprintf("Unique source addresses seen on network: %d\n", IPv4HostList
.num
+ IPv6HostList
.num
);
833 mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ
, NumPktQ
* mul
/ div
);
834 mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL
, NumPktL
* mul
/ div
);
835 mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR
, NumPktR
* mul
/ div
);
836 mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt
, TotPkt
* mul
/ div
);
838 mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes
, NumProbes
* mul
/ div
);
839 mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes
, NumGoodbyes
* mul
/ div
);
840 mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions
, NumQuestions
* mul
/ div
);
841 mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy
, NumLegacy
* mul
/ div
);
842 mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers
, NumAnswers
* mul
/ div
);
843 mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals
, NumAdditionals
* mul
/ div
);
847 if (!ExactlyOneFilter
)
849 ShowSortedHostList(&IPv4HostList
, 15);
850 ShowSortedHostList(&IPv6HostList
, 15);
853 mDNS_Close(&mDNSStorage
);
857 mDNSexport
int main(int argc
, char **argv
)
862 setlinebuf(stdout
); // Want to see lines as they appear, not block buffered
864 for (i
=1; i
<argc
; i
++)
869 a
.type
= mDNSAddrType_IPv4
;
871 if (inet_pton(AF_INET
, argv
[i
], &s4
) == 1)
872 a
.ip
.v4
.NotAnInteger
= s4
.s_addr
;
873 else if (inet_pton(AF_INET6
, argv
[i
], &s6
) == 1)
875 a
.type
= mDNSAddrType_IPv6
;
876 bcopy(&s6
, &a
.ip
.v6
, sizeof(a
.ip
.v6
));
880 struct hostent
*h
= gethostbyname(argv
[i
]);
881 if (h
) a
.ip
.v4
.NotAnInteger
= *(long*)h
->h_addr
;
885 FilterList
*f
= malloc(sizeof(*f
));
891 status
= mDNSNetMonitor();
892 if (status
) { fprintf(stderr
, "%s: mDNSNetMonitor failed %ld\n", argv
[0], status
); return(status
); }
896 fprintf(stderr
, "\nmDNS traffic monitor\n");
897 fprintf(stderr
, "Usage: %s (<host>)\n", argv
[0]);
898 fprintf(stderr
, "Optional <host> parameter displays only packets from that host\n");
900 fprintf(stderr
, "\nPer-packet header output:\n");
901 fprintf(stderr
, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
902 fprintf(stderr
, "-R- Multicast Response packet containing answers/announcements\n");
903 fprintf(stderr
, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
904 fprintf(stderr
, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
906 fprintf(stderr
, "\nPer-record display:\n");
907 fprintf(stderr
, "(PM) Probe Question (new service starting), requesting multicast response\n");
908 fprintf(stderr
, "(PU) Probe Question (new service starting), requesting unicast response\n");
909 fprintf(stderr
, "(DE) Deletion/Goodbye (service going away)\n");
910 fprintf(stderr
, "(LQ) Legacy Query Question\n");
911 fprintf(stderr
, "(QM) Query Question, requesting multicast response\n");
912 fprintf(stderr
, "(QU) Query Question, requesting unicast response\n");
913 fprintf(stderr
, "(KA) Known Answer (information querier already knows)\n");
914 fprintf(stderr
, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
915 fprintf(stderr
, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
916 fprintf(stderr
, "(AD) Unique Additional Record Set (entire RR Set)\n");
917 fprintf(stderr
, "(AD+) Additional records (add to existing RR Set members)\n");
919 fprintf(stderr
, "\nFinal summary, sorted by service type:\n");
920 fprintf(stderr
, "Probe Probes for this service type starting up\n");
921 fprintf(stderr
, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
922 fprintf(stderr
, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
923 fprintf(stderr
, "BrowseA Browse answers/announcments advertising instances of this service\n");
924 fprintf(stderr
, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
925 fprintf(stderr
, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
926 fprintf(stderr
, "\n");