2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
27 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
28 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
29 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
30 * therefore common sense dictates that if they are part of a compound statement then they
31 * should be indented to the same level as everything else in that compound statement.
32 * Indenting curly braces at the same level as the "if" implies that curly braces are
33 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
34 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
35 * understand why variable y is not of type "char*" just proves the point that poor code
36 * layout leads people to unfortunate misunderstandings about how the C language really works.)
38 Change History (most recent first):
40 $Log: NetMonitor.c,v $
41 Revision 1.49 2003/10/30 19:38:56 cheshire
42 Fix warning on certain compilers
44 Revision 1.48 2003/10/30 19:30:00 cheshire
45 Fix warnings on certain compilers
47 Revision 1.47 2003/09/05 18:49:57 cheshire
48 Add total packet size to display
50 Revision 1.46 2003/09/05 02:33:48 cheshire
51 Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window
53 Revision 1.45 2003/09/04 00:16:20 cheshire
54 Only show count of unique source addresses seen on network if we're not filtering
56 Revision 1.44 2003/09/02 22:13:28 cheshire
57 Show total host count in final summary table
59 Revision 1.43 2003/09/02 21:42:52 cheshire
60 Improved alignment of final summary table with v6 addresses
62 Revision 1.42 2003/09/02 20:59:24 cheshire
63 Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
65 Revision 1.41 2003/08/29 22:05:44 cheshire
66 Also count subsequent KA packets for the purposes of statistics counting
68 Revision 1.40 2003/08/29 16:43:24 cheshire
69 Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
71 Revision 1.39 2003/08/28 02:07:48 vlubet
72 Added "per hosts" statistics
74 Revision 1.38 2003/08/20 22:41:42 cheshire
75 Also display total multicast packet count
77 Revision 1.37 2003/08/20 22:32:08 cheshire
78 Error in DisplayQuery: Authorities come *after* Answers, not before
80 Revision 1.36 2003/08/18 23:20:10 cheshire
81 RDLength moved from the RData to the ResourceRecord object.
83 Revision 1.35 2003/08/15 20:17:28 cheshire
84 "LargeResourceRecord" changed to "LargeCacheRecord"
86 Revision 1.34 2003/08/14 02:19:55 cheshire
87 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
89 Revision 1.33 2003/08/12 19:56:26 cheshire
92 Revision 1.32 2003/08/06 18:57:01 cheshire
95 Revision 1.31 2003/08/06 02:05:12 cheshire
96 Add ability to give a list of hosts to monitor
98 Revision 1.30 2003/08/05 23:56:26 cheshire
99 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
100 (Right now mDNSPosix.c just reports 255 -- we should fix this)
102 Revision 1.29 2003/08/05 00:43:12 cheshire
103 Report errors encountered while processing authority section
105 Revision 1.28 2003/07/29 22:51:08 cheshire
106 Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them
108 Revision 1.27 2003/07/29 22:48:04 cheshire
109 Completed support for decoding packets containing oversized resource records
111 Revision 1.26 2003/07/19 03:25:23 cheshire
112 Change to make use of new GetLargeResourceRecord() call, for handling larger records
114 Revision 1.25 2003/07/18 00:13:23 cheshire
115 Remove erroneous trailing '\' from TXT record display
117 Revision 1.24 2003/07/17 17:10:51 cheshire
118 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
119 Further work: distinguish between PM and PU
121 Revision 1.23 2003/07/16 22:20:23 cheshire
122 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
123 Made mDNSNetMonitor distinguish between QM and QU in its logging output
125 Revision 1.22 2003/07/02 21:19:58 cheshire
126 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
128 Revision 1.21 2003/06/18 05:48:41 cheshire
131 Revision 1.20 2003/06/06 22:18:22 cheshire
132 Add extra space in Q output to line it up with RR output
134 Revision 1.19 2003/06/06 21:05:04 cheshire
135 Display state of cache-flush bit on additional records
137 Revision 1.18 2003/06/06 20:01:30 cheshire
138 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
139 (Global search-and-replace; no functional change to code execution.)
141 Revision 1.17 2003/06/06 14:26:50 cheshire
142 Explicitly #include <time.h> for the benefit of certain Linux distributions
144 Revision 1.16 2003/05/29 21:56:36 cheshire
146 Distinguish modern multicast queries from legacy multicast queries
147 In addition to record counts, display packet counts of queries, legacy queries, and responses
148 Include TTL in RR display
150 Revision 1.15 2003/05/29 20:03:57 cheshire
151 Various improvements:
152 Display start and end time, average rates in packets-per-minute,
153 show legacy queries as -LQ-, improve display of TXT and unknown records
155 Revision 1.14 2003/05/26 04:45:42 cheshire
156 Limit line length when printing super-long TXT records
158 Revision 1.13 2003/05/26 03:21:29 cheshire
159 Tidy up address structure naming:
160 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
161 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
162 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
164 Revision 1.12 2003/05/26 03:01:28 cheshire
165 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
167 Revision 1.11 2003/05/26 00:48:13 cheshire
168 If mDNS packet contains a non-zero message ID, then display it.
170 Revision 1.10 2003/05/22 01:10:32 cheshire
171 Indicate when the TC bit is set on a query packet
173 Revision 1.9 2003/05/21 03:56:00 cheshire
174 Improve display of Probe queries
176 Revision 1.8 2003/05/09 21:41:56 cheshire
177 Track deletion/goodbye packets as separate category
179 Revision 1.7 2003/05/07 00:16:01 cheshire
180 More detailed decoding of Resource Records
182 Revision 1.6 2003/05/05 21:16:16 cheshire
183 <rdar://problem/3241281> Change timenow from a local variable to a structure member
185 Revision 1.5 2003/04/19 01:16:22 cheshire
186 Add filter option, to monitor only packets from a single specified source address
188 Revision 1.4 2003/04/18 00:45:21 cheshire
189 Distinguish announcements (AN) from deletions (DE)
191 Revision 1.3 2003/04/15 18:26:01 cheshire
192 Added timestamps and help information
194 Revision 1.2 2003/04/04 20:42:02 cheshire
195 Fix broken statistics counting
197 Revision 1.1 2003/04/04 01:37:14 cheshire
202 //*************************************************************************************************************
203 // Incorporate mDNS.c functionality
205 // We want to use much of the functionality provided by "mDNS.c",
206 // except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
207 #define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
209 #undef mDNSCoreReceive
211 //*************************************************************************************************************
214 #include <stdio.h> // For printf()
215 #include <stdlib.h> // For malloc()
216 #include <string.h> // For bcopy()
217 #include <time.h> // For "struct tm" etc.
218 #include <netdb.h> // For gethostbyname()
219 #include <sys/socket.h> // For AF_INET, AF_INET6, etc.
220 #include <arpa/inet.h> // For inet_addr()
221 #include <netinet/in.h> // For INADDR_NONE
223 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
224 #include "ExampleClientApp.h"
226 //*************************************************************************************************************
227 // Types and structures
231 // Primitive operations
235 // These are meta-categories;
236 // Query and Answer operations are actually subdivided into two classes:
237 // Browse query/answer and
238 // Resolve query/answer
242 // The "Browse" variants of query/answer
247 // The "Resolve" variants of query/answer
255 typedef struct ActivityStat_struct ActivityStat
;
256 struct ActivityStat_struct
262 int stat
[OP_NumTypes
];
265 typedef struct FilterList_struct FilterList
;
266 struct FilterList_struct
272 //*************************************************************************************************************
275 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
276 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
278 struct timeval tv_start
, tv_end
, tv_interval
;
280 static FilterList
*Filters
;
281 #define ExactlyOneFilter (Filters && !Filters->next)
283 static int NumPktQ
, NumPktL
, NumPktR
, NumPktB
; // Query/Legacy/Response/Bad
284 static int NumProbes
, NumGoodbyes
, NumQuestions
, NumLegacy
, NumAnswers
, NumAdditionals
;
286 static ActivityStat
*stats
;
288 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
290 //*************************************************************************************************************
293 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
294 mDNSlocal mDNSu32
mprintf(const char *format
, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
295 mDNSlocal mDNSu32
mprintf(const char *format
, ...)
298 unsigned char buffer
[512];
300 va_start(ptr
,format
);
301 length
= mDNS_vsnprintf((char *)buffer
, sizeof(buffer
), format
, ptr
);
303 printf("%s", buffer
);
307 //*************************************************************************************************************
310 // Would benefit from a hash
314 HostPkt_Q
= 0, // Query
315 HostPkt_L
= 1, // Legacy Query
316 HostPkt_R
= 2, // Response
317 HostPkt_B
= 3, // Bad
324 unsigned long pkts
[HostPkt_NumTypes
];
325 unsigned long totalops
;
326 unsigned long stat
[OP_NumTypes
];
329 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
338 static HostList IPv4HostList
= { 0, 0, 0 };
339 static HostList IPv6HostList
= { 0, 0, 0 };
341 mDNSlocal HostEntry
*FindHost(const mDNSAddr
*addr
, HostList
* list
)
345 for (i
= 0; i
< list
->num
; i
++)
347 HostEntry
*entry
= list
->hosts
+ i
;
348 if (mDNSSameAddress(addr
, &entry
->addr
))
355 mDNSlocal HostEntry
*AddHost(HostList
* list
)
358 if (list
->num
>= list
->max
)
360 long newMax
= list
->max
+ 64;
361 HostEntry
*newHosts
= realloc(list
->hosts
, newMax
* sizeof(HostEntry
));
362 if (newHosts
== NULL
)
365 list
->hosts
= newHosts
;
367 entry
= list
->hosts
+ list
->num
++;
371 mDNSlocal HostEntry
*GotPacketFromHost(const mDNSAddr
*addr
, HostPkt_Type t
)
373 if (ExactlyOneFilter
) return(NULL
);
376 HostList
*list
= (addr
->type
== mDNSAddrType_IPv4
) ? &IPv4HostList
: &IPv6HostList
;
377 HostEntry
*entry
= FindHost(addr
, list
);
381 entry
= AddHost(list
);
382 if (!entry
) return(NULL
);
384 for (i
=0; i
<HostPkt_NumTypes
; i
++) entry
->pkts
[i
] = 0;
386 for (i
=0; i
<OP_NumTypes
; i
++) entry
->stat
[i
] = 0;
393 mDNSlocal
int CompareHosts(const void *p1
, const void *p2
)
395 return (int)(HostEntryTotalPackets((HostEntry
*)p2
) - HostEntryTotalPackets((HostEntry
*)p1
));
398 mDNSlocal
void ShowSortedHostList(HostList
*list
, int max
)
400 HostEntry
*e
, *end
= &list
->hosts
[(max
< list
->num
) ? max
: list
->num
];
401 qsort(list
->hosts
, list
->num
, sizeof(HostEntry
), CompareHosts
);
402 if (list
->num
) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner
, " Pkts Query LegacyQ Response");
403 for (e
= &list
->hosts
[0]; e
< end
; e
++)
405 int len
= mprintf("%#-25a", &e
->addr
);
406 if (len
> 25) mprintf("\n%25s", "");
407 mprintf("%8d %8d %8d %8d %8d %8d %8d", e
->totalops
,
408 e
->stat
[OP_probe
], e
->stat
[OP_goodbye
],
409 e
->stat
[OP_browseq
], e
->stat
[OP_browsea
],
410 e
->stat
[OP_resolveq
], e
->stat
[OP_resolvea
]);
411 mprintf(" %8lu %8lu %8lu %8lu",
412 HostEntryTotalPackets(e
), e
->pkts
[HostPkt_Q
], e
->pkts
[HostPkt_L
], e
->pkts
[HostPkt_R
]);
413 if (e
->pkts
[HostPkt_B
]) mprintf("Bad: %8lu", e
->pkts
[HostPkt_B
]);
418 //*************************************************************************************************************
419 // Receive and process packets
421 mDNSexport mDNSBool
ExtractServiceType(const domainname
*const fqdn
, domainname
*const srvtype
)
424 const mDNSu8
*src
= fqdn
->c
;
425 mDNSu8
*dst
= srvtype
->c
;
428 if (len
== 0 || len
>= 0x40) return(mDNSfalse
);
429 if (src
[1] != '_') src
+= 1 + len
;
432 if (len
== 0 || len
>= 0x40 || src
[1] != '_') return(mDNSfalse
);
433 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
436 if (len
== 0 || len
>= 0x40 || src
[1] != '_') return(mDNSfalse
);
437 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
439 *dst
++ = 0; // Put the null root label on the end of the service type
444 mDNSlocal
void recordstat(HostEntry
*entry
, domainname
*fqdn
, int op
, mDNSu16 rrtype
)
446 ActivityStat
**s
= &stats
;
451 if (rrtype
== kDNSType_SRV
|| rrtype
== kDNSType_TXT
) op
= op
- OP_browsegroup
+ OP_resolvegroup
;
452 else if (rrtype
!= kDNSType_PTR
) return;
455 if (!ExtractServiceType(fqdn
, &srvtype
)) return;
457 while (*s
&& !SameDomainName(&(*s
)->srvtype
, &srvtype
)) s
= &(*s
)->next
;
461 *s
= malloc(sizeof(ActivityStat
));
464 (*s
)->srvtype
= srvtype
;
467 for (i
=0; i
<OP_NumTypes
; i
++) (*s
)->stat
[i
] = 0;
479 mDNSlocal
void printstats(int max
)
482 for (i
=0; i
<max
; i
++)
485 ActivityStat
*s
, *m
= NULL
;
486 for (s
= stats
; s
; s
=s
->next
)
487 if (!s
->printed
&& max
< s
->totalops
)
488 { m
= s
; max
= s
->totalops
; }
490 m
->printed
= mDNStrue
;
491 if (i
==0) mprintf("%-25s%s\n", "Service Type", OPBanner
);
492 mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", &m
->srvtype
, m
->totalops
, m
->stat
[OP_probe
],
493 m
->stat
[OP_goodbye
], m
->stat
[OP_browseq
], m
->stat
[OP_browsea
], m
->stat
[OP_resolveq
], m
->stat
[OP_resolvea
]);
497 mDNSlocal
const mDNSu8
*FindUpdate(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*ptr
, const mDNSu8
*const end
, DNSQuestion
*q
, LargeCacheRecord
*pkt
)
500 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
502 const mDNSu8
*p2
= ptr
;
503 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, 0, pkt
);
505 if (ResourceRecordAnswersQuestion(&pkt
->r
.resrec
, q
)) return(p2
);
510 mDNSlocal
void DisplayTimestamp(void)
514 gettimeofday(&tv
, NULL
);
515 localtime_r((time_t*)&tv
.tv_sec
, &tm
);
516 mprintf("\n%d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv
.tv_usec
);
519 mDNSlocal
void DisplayPacketHeader(const DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
)
521 const char *const ptype
= (msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
) ? "-R- " :
522 (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
) ? "-Q- " : "-LQ-";
525 mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
526 srcaddr
, ptype
, msg
->h
.numQuestions
, msg
->h
.numAnswers
, msg
->h
.numAuthorities
, msg
->h
.numAdditionals
, end
- (mDNSu8
*)msg
);
528 if (msg
->h
.id
.NotAnInteger
) mprintf(" ID:%u", ((mDNSu16
)msg
->h
.id
.b
[0])<<8 | msg
->h
.id
.b
[1]);
530 if (msg
->h
.flags
.b
[0] & kDNSFlag0_TC
)
532 if (msg
->h
.flags
.b
[0] & kDNSFlag0_QR_Response
) mprintf(" Truncated");
533 else mprintf(" Truncated (KA list continues in next packet)");
538 mDNSlocal
void DisplayResourceRecord(const mDNSAddr
*const srcaddr
, const char *const op
, const ResourceRecord
*const pktrr
)
540 static const char hexchars
[16] = "0123456789ABCDEF";
542 char buffer
[MaxWidth
+8];
545 RDataBody
*rd
= &pktrr
->rdata
->u
;
546 mDNSu8
*rdend
= (mDNSu8
*)rd
+ pktrr
->rdlength
;
547 mDNSu32 n
= mprintf("%#-16a %-5s %-5s%5d %##s -> ", srcaddr
, op
, DNSTypeName(pktrr
->rrtype
), pktrr
->rroriginalttl
, pktrr
->name
.c
);
549 switch(pktrr
->rrtype
)
551 case kDNSType_A
: n
+= mprintf("%.4a", &rd
->ip
); break;
552 case kDNSType_PTR
: n
+= mprintf("%##.*s", MaxWidth
- n
, &rd
->name
); break;
553 case kDNSType_HINFO
:// same as kDNSType_TXT below
555 mDNSu8
*t
= rd
->txt
.c
;
556 while (t
< rdend
&& t
[0] && p
< buffer
+MaxWidth
)
559 for (i
=1; i
<=t
[0] && p
< buffer
+MaxWidth
; i
++)
561 if (t
[i
] == '\\') *p
++ = '\\';
562 if (t
[i
] >= ' ') *p
++ = t
[i
];
568 *p
++ = hexchars
[t
[i
] >> 4];
569 *p
++ = hexchars
[t
[i
] & 0xF];
573 if (t
< rdend
&& t
[0]) { *p
++ = '\\'; *p
++ = ' '; }
576 n
+= mprintf("%.*s", MaxWidth
- n
, buffer
);
578 case kDNSType_AAAA
: n
+= mprintf("%.16a", &rd
->ipv6
); break;
579 case kDNSType_SRV
: n
+= mprintf("%##s:%d", &rd
->srv
.target
, ((mDNSu16
)rd
->srv
.port
.b
[0] << 8) | rd
->srv
.port
.b
[1]); break;
581 mDNSu8
*s
= rd
->data
;
582 while (s
< rdend
&& p
< buffer
+MaxWidth
)
584 if (*s
== '\\') *p
++ = '\\';
585 if (*s
>= ' ') *p
++ = *s
;
591 *p
++ = hexchars
[*s
>> 4];
592 *p
++ = hexchars
[*s
& 0xF];
597 n
+= mprintf("%.*s", MaxWidth
- n
, buffer
);
604 mDNSlocal
void HexDump(const mDNSu8
*ptr
, const mDNSu8
*const end
)
610 if (&ptr
[i
] < end
) mprintf("%02X ", ptr
[i
]);
613 if (&ptr
[i
] < end
) mprintf("%c", ptr
[i
] <= ' ' || ptr
[i
] >= 126 ? '.' : ptr
[i
]);
619 mDNSlocal
void DisplayError(const mDNSAddr
*srcaddr
, const mDNSu8
*ptr
, const mDNSu8
*const end
, char *msg
)
621 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr
, msg
);
625 mDNSlocal
void DisplayQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSInterfaceID InterfaceID
)
628 const mDNSu8
*ptr
= msg
->data
;
629 const mDNSu8
*auth
= LocateAuthorities(msg
, end
);
630 mDNSBool MQ
= (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
);
631 HostEntry
*entry
= GotPacketFromHost(srcaddr
, MQ
? HostPkt_Q
: HostPkt_L
);
632 LargeCacheRecord pkt
;
634 DisplayPacketHeader(msg
, end
, srcaddr
, srcport
);
635 if (MQ
) NumPktQ
++; else NumPktL
++;
637 for (i
=0; i
<msg
->h
.numQuestions
; i
++)
640 mDNSu8
*p2
= (mDNSu8
*)getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
641 mDNSu16 ucbit
= q
.qclass
& kDNSQClass_UnicastResponse
;
642 q
.qclass
&= ~kDNSQClass_UnicastResponse
;
643 if (!p2
) { DisplayError(srcaddr
, ptr
, end
, "QUESTION"); return; }
645 p2
= (mDNSu8
*)FindUpdate(m
, msg
, auth
, end
, &q
, &pkt
);
649 DisplayResourceRecord(srcaddr
, ucbit
? "(PU)" : "(PM)", &pkt
.r
.resrec
);
650 recordstat(entry
, &q
.qname
, OP_probe
, q
.qtype
);
651 p2
= (mDNSu8
*)skipDomainName(msg
, p2
, end
);
652 // Having displayed this update record, clear type and class so we don't display the same one again.
653 p2
[0] = p2
[1] = p2
[2] = p2
[3] = 0;
657 const char *ptype
= ucbit
? "(QU)" : "(QM)";
658 if (srcport
.NotAnInteger
== MulticastDNSPort
.NotAnInteger
) NumQuestions
++;
659 else { NumLegacy
++; ptype
= "(LQ)"; }
660 mprintf("%#-16a %-5s %-5s %##s\n", srcaddr
, ptype
, DNSTypeName(q
.qtype
), q
.qname
.c
);
661 recordstat(entry
, &q
.qname
, OP_query
, q
.qtype
);
665 for (i
=0; i
<msg
->h
.numAnswers
; i
++)
667 const mDNSu8
*ep
= ptr
;
668 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
669 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "KNOWN ANSWER"); return; }
670 DisplayResourceRecord(srcaddr
, "(KA)", &pkt
.r
.resrec
);
672 // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
673 // the same as a single query, to more accurately reflect the burden on the network
674 // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
675 if (msg
->h
.numQuestions
== 0 && i
== 0)
676 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_query
, pkt
.r
.resrec
.rrtype
);
679 for (i
=0; i
<msg
->h
.numAuthorities
; i
++)
681 const mDNSu8
*ep
= ptr
;
682 ptr
= skipResourceRecord(msg
, ptr
, end
);
683 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "AUTHORITY"); return; }
687 mDNSlocal
void DisplayResponse(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*end
, const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSInterfaceID InterfaceID
)
690 const mDNSu8
*ptr
= msg
->data
;
691 HostEntry
*entry
= GotPacketFromHost(srcaddr
, HostPkt_R
);
692 LargeCacheRecord pkt
;
694 DisplayPacketHeader(msg
, end
, srcaddr
, srcport
);
697 for (i
=0; i
<msg
->h
.numQuestions
; i
++)
700 const mDNSu8
*ep
= ptr
;
701 ptr
= getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
702 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "QUESTION"); return; }
703 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr
, DNSTypeName(q
.qtype
), q
.qname
.c
);
706 for (i
=0; i
<msg
->h
.numAnswers
; i
++)
708 const mDNSu8
*ep
= ptr
;
709 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
710 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "ANSWER"); return; }
711 if (pkt
.r
.resrec
.rroriginalttl
)
714 DisplayResourceRecord(srcaddr
, (pkt
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "(AN)" : "(AN+)", &pkt
.r
.resrec
);
715 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_answer
, pkt
.r
.resrec
.rrtype
);
720 DisplayResourceRecord(srcaddr
, "(DE)", &pkt
.r
.resrec
);
721 recordstat(entry
, &pkt
.r
.resrec
.name
, OP_goodbye
, pkt
.r
.resrec
.rrtype
);
725 for (i
=0; i
<msg
->h
.numAuthorities
; i
++)
727 const mDNSu8
*ep
= ptr
;
728 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
729 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "AUTHORITY"); return; }
730 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
731 srcaddr
, DNSTypeName(pkt
.r
.resrec
.rrtype
), pkt
.r
.resrec
.name
.c
);
734 for (i
=0; i
<msg
->h
.numAdditionals
; i
++)
736 const mDNSu8
*ep
= ptr
;
737 ptr
= GetLargeResourceRecord(m
, msg
, ptr
, end
, InterfaceID
, 0, &pkt
);
738 if (!ptr
) { DisplayError(srcaddr
, ep
, end
, "ADDITIONAL"); return; }
740 DisplayResourceRecord(srcaddr
, (pkt
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) ? "(AD)" : "(AD+)", &pkt
.r
.resrec
);
744 mDNSlocal mDNSBool
AddressMatchesFilterList(const mDNSAddr
*srcaddr
)
747 if (!Filters
) return(srcaddr
->type
== mDNSAddrType_IPv4
);
748 for (f
=Filters
; f
; f
=f
->next
) if (mDNSSameAddress(srcaddr
, &f
->FilterAddr
)) return(mDNStrue
);
752 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, DNSMessage
*const msg
, const mDNSu8
*const end
,
753 const mDNSAddr
*srcaddr
, mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, mDNSu8 ttl
)
755 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
756 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
757 const mDNSu8 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
758 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
760 (void)dstaddr
; // Unused
761 (void)dstport
; // Unused
763 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
764 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
765 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
766 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
767 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
771 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",
772 (QR_OP
== StdQ
) ? "Query" : (QR_OP
== StdR
) ? "Response" : "Unkown",
773 srcaddr
, dstaddr
, ttl
, InterfaceID
,
774 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
775 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
776 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
777 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
780 // For now we're only interested in monitoring IPv4 traffic.
781 // All IPv6 packets should just be duplicates of the v4 packets.
782 if (AddressMatchesFilterList(srcaddr
))
784 if (QR_OP
== StdQ
) DisplayQuery (m
, msg
, end
, srcaddr
, srcport
, InterfaceID
);
785 else if (QR_OP
== StdR
) DisplayResponse(m
, msg
, end
, srcaddr
, srcport
, InterfaceID
);
788 debugf("Unknown DNS packet type %02X%02X (ignored)", msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1]);
789 GotPacketFromHost(srcaddr
, HostPkt_B
);
795 mDNSlocal mStatus
mDNSNetMonitor(void)
798 int h
, m
, s
, mul
, div
, TotPkt
;
800 mStatus status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
801 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
802 mDNS_Init_DontAdvertiseLocalAddresses
,
803 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
804 if (status
) return(status
);
806 gettimeofday(&tv_start
, NULL
);
807 ExampleClientEventLoop(&mDNSStorage
); // Wait for user to hit Ctrl-C
809 // Now display final summary
810 TotPkt
= NumPktQ
+ NumPktL
+ NumPktR
;
811 gettimeofday(&tv_end
, NULL
);
812 tv_interval
= tv_end
;
813 if (tv_start
.tv_usec
> tv_interval
.tv_usec
)
814 { tv_interval
.tv_usec
+= 1000000; tv_interval
.tv_sec
--; }
815 tv_interval
.tv_sec
-= tv_start
.tv_sec
;
816 tv_interval
.tv_usec
-= tv_start
.tv_usec
;
817 h
= (tv_interval
.tv_sec
/ 3600);
818 m
= (tv_interval
.tv_sec
% 3600) / 60;
819 s
= (tv_interval
.tv_sec
% 60);
820 if (tv_interval
.tv_sec
> 10)
823 div
= tv_interval
.tv_sec
;
828 div
= tv_interval
.tv_sec
* 1000 + tv_interval
.tv_usec
/ 1000;
833 localtime_r((time_t*)&tv_start
.tv_sec
, &tm
);
834 mprintf("Started %3d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv_start
.tv_usec
);
835 localtime_r((time_t*)&tv_end
.tv_sec
, &tm
);
836 mprintf("End %3d:%02d:%02d.%06d\n", tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, tv_end
.tv_usec
);
837 mprintf("Captured for %3d:%02d:%02d.%06d\n", h
, m
, s
, tv_interval
.tv_usec
);
838 if (!Filters
) mprintf("Unique source addresses seen on network: %d\n", IPv4HostList
.num
+ IPv6HostList
.num
);
840 mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ
, NumPktQ
* mul
/ div
);
841 mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL
, NumPktL
* mul
/ div
);
842 mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR
, NumPktR
* mul
/ div
);
843 mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt
, TotPkt
* mul
/ div
);
845 mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes
, NumProbes
* mul
/ div
);
846 mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes
, NumGoodbyes
* mul
/ div
);
847 mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions
, NumQuestions
* mul
/ div
);
848 mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy
, NumLegacy
* mul
/ div
);
849 mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers
, NumAnswers
* mul
/ div
);
850 mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals
, NumAdditionals
* mul
/ div
);
854 if (!ExactlyOneFilter
)
856 ShowSortedHostList(&IPv4HostList
, 15);
857 ShowSortedHostList(&IPv6HostList
, 15);
860 mDNS_Close(&mDNSStorage
);
864 mDNSexport
int main(int argc
, char **argv
)
869 setlinebuf(stdout
); // Want to see lines as they appear, not block buffered
871 for (i
=1; i
<argc
; i
++)
877 a
.type
= mDNSAddrType_IPv4
;
879 if (inet_pton(AF_INET
, argv
[i
], &s4
) == 1)
880 a
.ip
.v4
.NotAnInteger
= s4
.s_addr
;
881 else if (inet_pton(AF_INET6
, argv
[i
], &s6
) == 1)
883 a
.type
= mDNSAddrType_IPv6
;
884 bcopy(&s6
, &a
.ip
.v6
, sizeof(a
.ip
.v6
));
888 struct hostent
*h
= gethostbyname(argv
[i
]);
889 if (h
) a
.ip
.v4
.NotAnInteger
= *(long*)h
->h_addr
;
893 f
= malloc(sizeof(*f
));
899 status
= mDNSNetMonitor();
900 if (status
) { fprintf(stderr
, "%s: mDNSNetMonitor failed %ld\n", argv
[0], status
); return(status
); }
904 fprintf(stderr
, "\nmDNS traffic monitor\n");
905 fprintf(stderr
, "Usage: %s (<host>)\n", argv
[0]);
906 fprintf(stderr
, "Optional <host> parameter displays only packets from that host\n");
908 fprintf(stderr
, "\nPer-packet header output:\n");
909 fprintf(stderr
, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
910 fprintf(stderr
, "-R- Multicast Response packet containing answers/announcements\n");
911 fprintf(stderr
, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
912 fprintf(stderr
, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
914 fprintf(stderr
, "\nPer-record display:\n");
915 fprintf(stderr
, "(PM) Probe Question (new service starting), requesting multicast response\n");
916 fprintf(stderr
, "(PU) Probe Question (new service starting), requesting unicast response\n");
917 fprintf(stderr
, "(DE) Deletion/Goodbye (service going away)\n");
918 fprintf(stderr
, "(LQ) Legacy Query Question\n");
919 fprintf(stderr
, "(QM) Query Question, requesting multicast response\n");
920 fprintf(stderr
, "(QU) Query Question, requesting unicast response\n");
921 fprintf(stderr
, "(KA) Known Answer (information querier already knows)\n");
922 fprintf(stderr
, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
923 fprintf(stderr
, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
924 fprintf(stderr
, "(AD) Unique Additional Record Set (entire RR Set)\n");
925 fprintf(stderr
, "(AD+) Additional records (add to existing RR Set members)\n");
927 fprintf(stderr
, "\nFinal summary, sorted by service type:\n");
928 fprintf(stderr
, "Probe Probes for this service type starting up\n");
929 fprintf(stderr
, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
930 fprintf(stderr
, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
931 fprintf(stderr
, "BrowseA Browse answers/announcments advertising instances of this service\n");
932 fprintf(stderr
, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
933 fprintf(stderr
, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
934 fprintf(stderr
, "\n");