]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/NetMonitor.c
mDNSResponder-58.1.tar.gz
[apple/mdnsresponder.git] / mDNSPosix / NetMonitor.c
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 *
25 * Formatting notes:
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.)
37
38 Change History (most recent first):
39
40 $Log: NetMonitor.c,v $
41 Revision 1.49 2003/10/30 19:38:56 cheshire
42 Fix warning on certain compilers
43
44 Revision 1.48 2003/10/30 19:30:00 cheshire
45 Fix warnings on certain compilers
46
47 Revision 1.47 2003/09/05 18:49:57 cheshire
48 Add total packet size to display
49
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
52
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
55
56 Revision 1.44 2003/09/02 22:13:28 cheshire
57 Show total host count in final summary table
58
59 Revision 1.43 2003/09/02 21:42:52 cheshire
60 Improved alignment of final summary table with v6 addresses
61
62 Revision 1.42 2003/09/02 20:59:24 cheshire
63 Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
64
65 Revision 1.41 2003/08/29 22:05:44 cheshire
66 Also count subsequent KA packets for the purposes of statistics counting
67
68 Revision 1.40 2003/08/29 16:43:24 cheshire
69 Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
70
71 Revision 1.39 2003/08/28 02:07:48 vlubet
72 Added "per hosts" statistics
73
74 Revision 1.38 2003/08/20 22:41:42 cheshire
75 Also display total multicast packet count
76
77 Revision 1.37 2003/08/20 22:32:08 cheshire
78 Error in DisplayQuery: Authorities come *after* Answers, not before
79
80 Revision 1.36 2003/08/18 23:20:10 cheshire
81 RDLength moved from the RData to the ResourceRecord object.
82
83 Revision 1.35 2003/08/15 20:17:28 cheshire
84 "LargeResourceRecord" changed to "LargeCacheRecord"
85
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
88
89 Revision 1.33 2003/08/12 19:56:26 cheshire
90 Update to APSL 2.0
91
92 Revision 1.32 2003/08/06 18:57:01 cheshire
93 Update comments
94
95 Revision 1.31 2003/08/06 02:05:12 cheshire
96 Add ability to give a list of hosts to monitor
97
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)
101
102 Revision 1.29 2003/08/05 00:43:12 cheshire
103 Report errors encountered while processing authority section
104
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
107
108 Revision 1.27 2003/07/29 22:48:04 cheshire
109 Completed support for decoding packets containing oversized resource records
110
111 Revision 1.26 2003/07/19 03:25:23 cheshire
112 Change to make use of new GetLargeResourceRecord() call, for handling larger records
113
114 Revision 1.25 2003/07/18 00:13:23 cheshire
115 Remove erroneous trailing '\' from TXT record display
116
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
120
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
124
125 Revision 1.22 2003/07/02 21:19:58 cheshire
126 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
127
128 Revision 1.21 2003/06/18 05:48:41 cheshire
129 Fix warnings
130
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
133
134 Revision 1.19 2003/06/06 21:05:04 cheshire
135 Display state of cache-flush bit on additional records
136
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.)
140
141 Revision 1.17 2003/06/06 14:26:50 cheshire
142 Explicitly #include <time.h> for the benefit of certain Linux distributions
143
144 Revision 1.16 2003/05/29 21:56:36 cheshire
145 More improvements:
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
149
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
154
155 Revision 1.14 2003/05/26 04:45:42 cheshire
156 Limit line length when printing super-long TXT records
157
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
163
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
166
167 Revision 1.11 2003/05/26 00:48:13 cheshire
168 If mDNS packet contains a non-zero message ID, then display it.
169
170 Revision 1.10 2003/05/22 01:10:32 cheshire
171 Indicate when the TC bit is set on a query packet
172
173 Revision 1.9 2003/05/21 03:56:00 cheshire
174 Improve display of Probe queries
175
176 Revision 1.8 2003/05/09 21:41:56 cheshire
177 Track deletion/goodbye packets as separate category
178
179 Revision 1.7 2003/05/07 00:16:01 cheshire
180 More detailed decoding of Resource Records
181
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
184
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
187
188 Revision 1.4 2003/04/18 00:45:21 cheshire
189 Distinguish announcements (AN) from deletions (DE)
190
191 Revision 1.3 2003/04/15 18:26:01 cheshire
192 Added timestamps and help information
193
194 Revision 1.2 2003/04/04 20:42:02 cheshire
195 Fix broken statistics counting
196
197 Revision 1.1 2003/04/04 01:37:14 cheshire
198 Added NetMonitor.c
199
200 */
201
202 //*************************************************************************************************************
203 // Incorporate mDNS.c functionality
204
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__
208 #include "mDNS.c"
209 #undef mDNSCoreReceive
210
211 //*************************************************************************************************************
212 // Headers
213
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
222
223 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
224 #include "ExampleClientApp.h"
225
226 //*************************************************************************************************************
227 // Types and structures
228
229 enum
230 {
231 // Primitive operations
232 OP_probe = 0,
233 OP_goodbye = 1,
234
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
239 OP_query = 2,
240 OP_answer = 3,
241
242 // The "Browse" variants of query/answer
243 OP_browsegroup = 2,
244 OP_browseq = 2,
245 OP_browsea = 3,
246
247 // The "Resolve" variants of query/answer
248 OP_resolvegroup = 4,
249 OP_resolveq = 4,
250 OP_resolvea = 5,
251
252 OP_NumTypes = 6
253 };
254
255 typedef struct ActivityStat_struct ActivityStat;
256 struct ActivityStat_struct
257 {
258 ActivityStat *next;
259 domainname srvtype;
260 int printed;
261 int totalops;
262 int stat[OP_NumTypes];
263 };
264
265 typedef struct FilterList_struct FilterList;
266 struct FilterList_struct
267 {
268 FilterList *next;
269 mDNSAddr FilterAddr;
270 };
271
272 //*************************************************************************************************************
273 // Globals
274
275 static mDNS mDNSStorage; // mDNS core uses this to store its globals
276 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
277
278 struct timeval tv_start, tv_end, tv_interval;
279
280 static FilterList *Filters;
281 #define ExactlyOneFilter (Filters && !Filters->next)
282
283 static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad
284 static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
285
286 static ActivityStat *stats;
287
288 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
289
290 //*************************************************************************************************************
291 // Utilities
292
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, ...)
296 {
297 mDNSu32 length;
298 unsigned char buffer[512];
299 va_list ptr;
300 va_start(ptr,format);
301 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
302 va_end(ptr);
303 printf("%s", buffer);
304 return(length);
305 }
306
307 //*************************************************************************************************************
308 // Host Address List
309 //
310 // Would benefit from a hash
311
312 typedef enum
313 {
314 HostPkt_Q = 0, // Query
315 HostPkt_L = 1, // Legacy Query
316 HostPkt_R = 2, // Response
317 HostPkt_B = 3, // Bad
318 HostPkt_NumTypes = 4
319 } HostPkt_Type;
320
321 typedef struct
322 {
323 mDNSAddr addr;
324 unsigned long pkts[HostPkt_NumTypes];
325 unsigned long totalops;
326 unsigned long stat[OP_NumTypes];
327 } HostEntry;
328
329 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
330
331 typedef struct
332 {
333 long num;
334 long max;
335 HostEntry *hosts;
336 } HostList;
337
338 static HostList IPv4HostList = { 0, 0, 0 };
339 static HostList IPv6HostList = { 0, 0, 0 };
340
341 mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList* list)
342 {
343 long i;
344
345 for (i = 0; i < list->num; i++)
346 {
347 HostEntry *entry = list->hosts + i;
348 if (mDNSSameAddress(addr, &entry->addr))
349 return entry;
350 }
351
352 return NULL;
353 }
354
355 mDNSlocal HostEntry *AddHost(HostList* list)
356 {
357 HostEntry *entry;
358 if (list->num >= list->max)
359 {
360 long newMax = list->max + 64;
361 HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
362 if (newHosts == NULL)
363 return NULL;
364 list->max = newMax;
365 list->hosts = newHosts;
366 }
367 entry = list->hosts + list->num++;
368 return(entry);
369 }
370
371 mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t)
372 {
373 if (ExactlyOneFilter) return(NULL);
374 else
375 {
376 HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
377 HostEntry *entry = FindHost(addr, list);
378 if (!entry)
379 {
380 int i;
381 entry = AddHost(list);
382 if (!entry) return(NULL);
383 entry->addr = *addr;
384 for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
385 entry->totalops = 0;
386 for (i=0; i<OP_NumTypes; i++) entry->stat[i] = 0;
387 }
388 entry->pkts[t]++;
389 return(entry);
390 }
391 }
392
393 mDNSlocal int CompareHosts(const void *p1, const void *p2)
394 {
395 return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
396 }
397
398 mDNSlocal void ShowSortedHostList(HostList *list, int max)
399 {
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++)
404 {
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]);
414 mprintf("\n");
415 }
416 }
417
418 //*************************************************************************************************************
419 // Receive and process packets
420
421 mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
422 {
423 int i, len;
424 const mDNSu8 *src = fqdn->c;
425 mDNSu8 *dst = srvtype->c;
426
427 len = *src;
428 if (len == 0 || len >= 0x40) return(mDNSfalse);
429 if (src[1] != '_') src += 1 + len;
430
431 len = *src;
432 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
433 for (i=0; i<=len; i++) *dst++ = *src++;
434
435 len = *src;
436 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
437 for (i=0; i<=len; i++) *dst++ = *src++;
438
439 *dst++ = 0; // Put the null root label on the end of the service type
440
441 return(mDNStrue);
442 }
443
444 mDNSlocal void recordstat(HostEntry *entry, domainname *fqdn, int op, mDNSu16 rrtype)
445 {
446 ActivityStat **s = &stats;
447 domainname srvtype;
448
449 if (op != OP_probe)
450 {
451 if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
452 else if (rrtype != kDNSType_PTR) return;
453 }
454
455 if (!ExtractServiceType(fqdn, &srvtype)) return;
456
457 while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
458 if (!*s)
459 {
460 int i;
461 *s = malloc(sizeof(ActivityStat));
462 if (!*s) exit(-1);
463 (*s)->next = NULL;
464 (*s)->srvtype = srvtype;
465 (*s)->printed = 0;
466 (*s)->totalops = 0;
467 for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
468 }
469
470 (*s)->totalops++;
471 (*s)->stat[op]++;
472 if (entry)
473 {
474 entry->totalops++;
475 entry->stat[op]++;
476 }
477 }
478
479 mDNSlocal void printstats(int max)
480 {
481 int i;
482 for (i=0; i<max; i++)
483 {
484 int max = 0;
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; }
489 if (!m) return;
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]);
494 }
495 }
496
497 mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end, DNSQuestion *q, LargeCacheRecord *pkt)
498 {
499 int i;
500 for (i = 0; i < query->h.numAuthorities; i++)
501 {
502 const mDNSu8 *p2 = ptr;
503 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, pkt);
504 if (!ptr) break;
505 if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
506 }
507 return(mDNSNULL);
508 }
509
510 mDNSlocal void DisplayTimestamp(void)
511 {
512 struct timeval tv;
513 struct tm tm;
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);
517 }
518
519 mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport)
520 {
521 const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " :
522 (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
523
524 DisplayTimestamp();
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);
527
528 if (msg->h.id.NotAnInteger) mprintf(" ID:%u", ((mDNSu16)msg->h.id.b[0])<<8 | msg->h.id.b[1]);
529
530 if (msg->h.flags.b[0] & kDNSFlag0_TC)
531 {
532 if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated");
533 else mprintf(" Truncated (KA list continues in next packet)");
534 }
535 mprintf("\n");
536 }
537
538 mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
539 {
540 static const char hexchars[16] = "0123456789ABCDEF";
541 #define MaxWidth 132
542 char buffer[MaxWidth+8];
543 char *p = buffer;
544
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);
548
549 switch(pktrr->rrtype)
550 {
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
554 case kDNSType_TXT: {
555 mDNSu8 *t = rd->txt.c;
556 while (t < rdend && t[0] && p < buffer+MaxWidth)
557 {
558 int i;
559 for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
560 {
561 if (t[i] == '\\') *p++ = '\\';
562 if (t[i] >= ' ') *p++ = t[i];
563 else
564 {
565 *p++ = '\\';
566 *p++ = '0';
567 *p++ = 'x';
568 *p++ = hexchars[t[i] >> 4];
569 *p++ = hexchars[t[i] & 0xF];
570 }
571 }
572 t += 1+t[0];
573 if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
574 }
575 *p++ = 0;
576 n += mprintf("%.*s", MaxWidth - n, buffer);
577 } break;
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;
580 default: {
581 mDNSu8 *s = rd->data;
582 while (s < rdend && p < buffer+MaxWidth)
583 {
584 if (*s == '\\') *p++ = '\\';
585 if (*s >= ' ') *p++ = *s;
586 else
587 {
588 *p++ = '\\';
589 *p++ = '0';
590 *p++ = 'x';
591 *p++ = hexchars[*s >> 4];
592 *p++ = hexchars[*s & 0xF];
593 }
594 s++;
595 }
596 *p++ = 0;
597 n += mprintf("%.*s", MaxWidth - n, buffer);
598 } break;
599 }
600
601 mprintf("\n");
602 }
603
604 mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
605 {
606 while (ptr < end)
607 {
608 int i;
609 for (i=0; i<16; i++)
610 if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
611 else mprintf(" ");
612 for (i=0; i<16; i++)
613 if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
614 ptr += 16;
615 mprintf("\n");
616 }
617 }
618
619 mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
620 {
621 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
622 HexDump(ptr, end);
623 }
624
625 mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
626 {
627 int i;
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;
633
634 DisplayPacketHeader(msg, end, srcaddr, srcport);
635 if (MQ) NumPktQ++; else NumPktL++;
636
637 for (i=0; i<msg->h.numQuestions; i++)
638 {
639 DNSQuestion q;
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; }
644 ptr = p2;
645 p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
646 if (p2)
647 {
648 NumProbes++;
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;
654 }
655 else
656 {
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);
662 }
663 }
664
665 for (i=0; i<msg->h.numAnswers; i++)
666 {
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);
671
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);
677 }
678
679 for (i=0; i<msg->h.numAuthorities; i++)
680 {
681 const mDNSu8 *ep = ptr;
682 ptr = skipResourceRecord(msg, ptr, end);
683 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
684 }
685 }
686
687 mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
688 {
689 int i;
690 const mDNSu8 *ptr = msg->data;
691 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R);
692 LargeCacheRecord pkt;
693
694 DisplayPacketHeader(msg, end, srcaddr, srcport);
695 NumPktR++;
696
697 for (i=0; i<msg->h.numQuestions; i++)
698 {
699 DNSQuestion q;
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);
704 }
705
706 for (i=0; i<msg->h.numAnswers; i++)
707 {
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)
712 {
713 NumAnswers++;
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);
716 }
717 else
718 {
719 NumGoodbyes++;
720 DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
721 recordstat(entry, &pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
722 }
723 }
724
725 for (i=0; i<msg->h.numAuthorities; i++)
726 {
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);
732 }
733
734 for (i=0; i<msg->h.numAdditionals; i++)
735 {
736 const mDNSu8 *ep = ptr;
737 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
738 if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
739 NumAdditionals++;
740 DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec);
741 }
742 }
743
744 mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
745 {
746 FilterList *f;
747 if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
748 for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
749 return(mDNSfalse);
750 }
751
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)
754 {
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;
759
760 (void)dstaddr; // Unused
761 (void)dstport; // Unused
762
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]);
768
769 if (ttl < 254)
770 {
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");
778 }
779
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))
783 {
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);
786 else
787 {
788 debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
789 GotPacketFromHost(srcaddr, HostPkt_B);
790 NumPktB++;
791 }
792 }
793 }
794
795 mDNSlocal mStatus mDNSNetMonitor(void)
796 {
797 struct tm tm;
798 int h, m, s, mul, div, TotPkt;
799
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);
805
806 gettimeofday(&tv_start, NULL);
807 ExampleClientEventLoop(&mDNSStorage); // Wait for user to hit Ctrl-C
808
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)
821 {
822 mul = 60;
823 div = tv_interval.tv_sec;
824 }
825 else
826 {
827 mul = 60000;
828 div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
829 if (div == 0) div=1;
830 }
831
832 mprintf("\n\n");
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);
839 mprintf("\n");
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);
844 mprintf("\n");
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);
851 mprintf("\n");
852 printstats(15);
853
854 if (!ExactlyOneFilter)
855 {
856 ShowSortedHostList(&IPv4HostList, 15);
857 ShowSortedHostList(&IPv6HostList, 15);
858 }
859
860 mDNS_Close(&mDNSStorage);
861 return(0);
862 }
863
864 mDNSexport int main(int argc, char **argv)
865 {
866 int i;
867 mStatus status;
868
869 setlinebuf(stdout); // Want to see lines as they appear, not block buffered
870
871 for (i=1; i<argc; i++)
872 {
873 FilterList *f;
874 struct in_addr s4;
875 struct in6_addr s6;
876 mDNSAddr a;
877 a.type = mDNSAddrType_IPv4;
878
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)
882 {
883 a.type = mDNSAddrType_IPv6;
884 bcopy(&s6, &a.ip.v6, sizeof(a.ip.v6));
885 }
886 else
887 {
888 struct hostent *h = gethostbyname(argv[i]);
889 if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
890 else goto usage;
891 }
892
893 f = malloc(sizeof(*f));
894 f->FilterAddr = a;
895 f->next = Filters;
896 Filters = f;
897 }
898
899 status = mDNSNetMonitor();
900 if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", argv[0], status); return(status); }
901 return(0);
902
903 usage:
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");
907
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");
913
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");
926
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");
935 return(-1);
936 }