]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/NetMonitor.c
mDNSResponder-58.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 * 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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * Formatting notes:
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.)
35
36 Change History (most recent first):
37
38 $Log: NetMonitor.c,v $
39 Revision 1.47 2003/09/05 18:49:57 cheshire
40 Add total packet size to display
41
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
44
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
47
48 Revision 1.44 2003/09/02 22:13:28 cheshire
49 Show total host count in final summary table
50
51 Revision 1.43 2003/09/02 21:42:52 cheshire
52 Improved alignment of final summary table with v6 addresses
53
54 Revision 1.42 2003/09/02 20:59:24 cheshire
55 Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
56
57 Revision 1.41 2003/08/29 22:05:44 cheshire
58 Also count subsequent KA packets for the purposes of statistics counting
59
60 Revision 1.40 2003/08/29 16:43:24 cheshire
61 Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
62
63 Revision 1.39 2003/08/28 02:07:48 vlubet
64 Added "per hosts" statistics
65
66 Revision 1.38 2003/08/20 22:41:42 cheshire
67 Also display total multicast packet count
68
69 Revision 1.37 2003/08/20 22:32:08 cheshire
70 Error in DisplayQuery: Authorities come *after* Answers, not before
71
72 Revision 1.36 2003/08/18 23:20:10 cheshire
73 RDLength moved from the RData to the ResourceRecord object.
74
75 Revision 1.35 2003/08/15 20:17:28 cheshire
76 "LargeResourceRecord" changed to "LargeCacheRecord"
77
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
80
81 Revision 1.33 2003/08/12 19:56:26 cheshire
82 Update to APSL 2.0
83
84 Revision 1.32 2003/08/06 18:57:01 cheshire
85 Update comments
86
87 Revision 1.31 2003/08/06 02:05:12 cheshire
88 Add ability to give a list of hosts to monitor
89
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)
93
94 Revision 1.29 2003/08/05 00:43:12 cheshire
95 Report errors encountered while processing authority section
96
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
99
100 Revision 1.27 2003/07/29 22:48:04 cheshire
101 Completed support for decoding packets containing oversized resource records
102
103 Revision 1.26 2003/07/19 03:25:23 cheshire
104 Change to make use of new GetLargeResourceRecord() call, for handling larger records
105
106 Revision 1.25 2003/07/18 00:13:23 cheshire
107 Remove erroneous trailing '\' from TXT record display
108
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
112
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
116
117 Revision 1.22 2003/07/02 21:19:58 cheshire
118 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
119
120 Revision 1.21 2003/06/18 05:48:41 cheshire
121 Fix warnings
122
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
125
126 Revision 1.19 2003/06/06 21:05:04 cheshire
127 Display state of cache-flush bit on additional records
128
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.)
132
133 Revision 1.17 2003/06/06 14:26:50 cheshire
134 Explicitly #include <time.h> for the benefit of certain Linux distributions
135
136 Revision 1.16 2003/05/29 21:56:36 cheshire
137 More improvements:
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
141
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
146
147 Revision 1.14 2003/05/26 04:45:42 cheshire
148 Limit line length when printing super-long TXT records
149
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
155
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
158
159 Revision 1.11 2003/05/26 00:48:13 cheshire
160 If mDNS packet contains a non-zero message ID, then display it.
161
162 Revision 1.10 2003/05/22 01:10:32 cheshire
163 Indicate when the TC bit is set on a query packet
164
165 Revision 1.9 2003/05/21 03:56:00 cheshire
166 Improve display of Probe queries
167
168 Revision 1.8 2003/05/09 21:41:56 cheshire
169 Track deletion/goodbye packets as separate category
170
171 Revision 1.7 2003/05/07 00:16:01 cheshire
172 More detailed decoding of Resource Records
173
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
176
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
179
180 Revision 1.4 2003/04/18 00:45:21 cheshire
181 Distinguish announcements (AN) from deletions (DE)
182
183 Revision 1.3 2003/04/15 18:26:01 cheshire
184 Added timestamps and help information
185
186 Revision 1.2 2003/04/04 20:42:02 cheshire
187 Fix broken statistics counting
188
189 Revision 1.1 2003/04/04 01:37:14 cheshire
190 Added NetMonitor.c
191
192 */
193
194 //*************************************************************************************************************
195 // Incorporate mDNS.c functionality
196
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__
200 #include "mDNS.c"
201 #undef mDNSCoreReceive
202
203 //*************************************************************************************************************
204 // Headers
205
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
214
215 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
216 #include "ExampleClientApp.h"
217
218 //*************************************************************************************************************
219 // Types and structures
220
221 enum
222 {
223 // Primitive operations
224 OP_probe = 0,
225 OP_goodbye = 1,
226
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
231 OP_query = 2,
232 OP_answer = 3,
233
234 // The "Browse" variants of query/answer
235 OP_browsegroup = 2,
236 OP_browseq = 2,
237 OP_browsea = 3,
238
239 // The "Resolve" variants of query/answer
240 OP_resolvegroup = 4,
241 OP_resolveq = 4,
242 OP_resolvea = 5,
243
244 OP_NumTypes = 6
245 };
246
247 typedef struct ActivityStat_struct ActivityStat;
248 struct ActivityStat_struct
249 {
250 ActivityStat *next;
251 domainname srvtype;
252 int printed;
253 int totalops;
254 int stat[OP_NumTypes];
255 };
256
257 typedef struct FilterList_struct FilterList;
258 struct FilterList_struct
259 {
260 FilterList *next;
261 mDNSAddr FilterAddr;
262 };
263
264 //*************************************************************************************************************
265 // Globals
266
267 static mDNS mDNSStorage; // mDNS core uses this to store its globals
268 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
269
270 struct timeval tv_start, tv_end, tv_interval;
271
272 static FilterList *Filters;
273 #define ExactlyOneFilter (Filters && !Filters->next)
274
275 static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad
276 static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
277
278 static ActivityStat *stats;
279
280 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
281
282 //*************************************************************************************************************
283 // Utilities
284
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, ...)
288 {
289 mDNSu32 length;
290 unsigned char buffer[512];
291 va_list ptr;
292 va_start(ptr,format);
293 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
294 va_end(ptr);
295 printf("%s", buffer);
296 return(length);
297 }
298
299 //*************************************************************************************************************
300 // Host Address List
301 //
302 // Would benefit from a hash
303
304 typedef enum
305 {
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,
311 } HostPkt_Type;
312
313 typedef struct
314 {
315 mDNSAddr addr;
316 unsigned long pkts[HostPkt_NumTypes];
317 unsigned long totalops;
318 unsigned long stat[OP_NumTypes];
319 } HostEntry;
320
321 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
322
323 typedef struct
324 {
325 long num;
326 long max;
327 HostEntry *hosts;
328 } HostList;
329
330 static HostList IPv4HostList = { 0, 0, 0 };
331 static HostList IPv6HostList = { 0, 0, 0 };
332
333 mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList* list)
334 {
335 long i;
336
337 for (i = 0; i < list->num; i++)
338 {
339 HostEntry *entry = list->hosts + i;
340 if (mDNSSameAddress(addr, &entry->addr))
341 return entry;
342 }
343
344 return NULL;
345 }
346
347 mDNSlocal HostEntry *AddHost(HostList* list)
348 {
349 HostEntry *entry;
350 if (list->num >= list->max)
351 {
352 long newMax = list->max + 64;
353 HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
354 if (newHosts == NULL)
355 return NULL;
356 list->max = newMax;
357 list->hosts = newHosts;
358 }
359 entry = list->hosts + list->num++;
360 return(entry);
361 }
362
363 mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t)
364 {
365 if (ExactlyOneFilter) return(NULL);
366 else
367 {
368 HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
369 HostEntry *entry = FindHost(addr, list);
370 if (!entry)
371 {
372 int i;
373 entry = AddHost(list);
374 if (!entry) return(NULL);
375 entry->addr = *addr;
376 for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
377 entry->totalops = 0;
378 for (i=0; i<OP_NumTypes; i++) entry->stat[i] = 0;
379 }
380 entry->pkts[t]++;
381 return(entry);
382 }
383 }
384
385 mDNSlocal int CompareHosts(const void *p1, const void *p2)
386 {
387 return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
388 }
389
390 mDNSlocal void ShowSortedHostList(HostList *list, int max)
391 {
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++)
396 {
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]);
406 mprintf("\n");
407 }
408 }
409
410 //*************************************************************************************************************
411 // Receive and process packets
412
413 mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
414 {
415 int i, len;
416 const mDNSu8 *src = fqdn->c;
417 mDNSu8 *dst = srvtype->c;
418
419 len = *src;
420 if (len == 0 || len >= 0x40) return(mDNSfalse);
421 if (src[1] != '_') src += 1 + len;
422
423 len = *src;
424 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
425 for (i=0; i<=len; i++) *dst++ = *src++;
426
427 len = *src;
428 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
429 for (i=0; i<=len; i++) *dst++ = *src++;
430
431 *dst++ = 0; // Put the null root label on the end of the service type
432
433 return(mDNStrue);
434 }
435
436 mDNSlocal void recordstat(HostEntry *entry, domainname *fqdn, int op, mDNSu16 rrtype)
437 {
438 ActivityStat **s = &stats;
439 domainname srvtype;
440
441 if (op != OP_probe)
442 {
443 if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
444 else if (rrtype != kDNSType_PTR) return;
445 }
446
447 if (!ExtractServiceType(fqdn, &srvtype)) return;
448
449 while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
450 if (!*s)
451 {
452 int i;
453 *s = malloc(sizeof(ActivityStat));
454 if (!*s) exit(-1);
455 (*s)->next = NULL;
456 (*s)->srvtype = srvtype;
457 (*s)->printed = 0;
458 (*s)->totalops = 0;
459 for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
460 }
461
462 (*s)->totalops++;
463 (*s)->stat[op]++;
464 if (entry)
465 {
466 entry->totalops++;
467 entry->stat[op]++;
468 }
469 }
470
471 mDNSlocal void printstats(int max)
472 {
473 int i;
474 for (i=0; i<max; i++)
475 {
476 int max = 0;
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; }
481 if (!m) return;
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]);
486 }
487 }
488
489 mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end, DNSQuestion *q, LargeCacheRecord *pkt)
490 {
491 int i;
492 for (i = 0; i < query->h.numAuthorities; i++)
493 {
494 const mDNSu8 *p2 = ptr;
495 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, pkt);
496 if (!ptr) break;
497 if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
498 }
499 return(mDNSNULL);
500 }
501
502 mDNSlocal void DisplayTimestamp(void)
503 {
504 struct timeval tv;
505 struct tm tm;
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);
509 }
510
511 mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport)
512 {
513 const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " :
514 (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
515
516 DisplayTimestamp();
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);
519
520 if (msg->h.id.NotAnInteger) mprintf(" ID:%u", ((mDNSu16)msg->h.id.b[0])<<8 | msg->h.id.b[1]);
521
522 if (msg->h.flags.b[0] & kDNSFlag0_TC)
523 {
524 if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated");
525 else mprintf(" Truncated (KA list continues in next packet)");
526 }
527 mprintf("\n");
528 }
529
530 mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
531 {
532 static const char hexchars[16] = "0123456789ABCDEF";
533 #define MaxWidth 132
534 char buffer[MaxWidth+8];
535 char *p = buffer;
536
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);
540
541 switch(pktrr->rrtype)
542 {
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
546 case kDNSType_TXT: {
547 mDNSu8 *t = rd->txt.c;
548 while (t < rdend && t[0] && p < buffer+MaxWidth)
549 {
550 int i;
551 for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
552 {
553 if (t[i] == '\\') *p++ = '\\';
554 if (t[i] >= ' ') *p++ = t[i];
555 else
556 {
557 *p++ = '\\';
558 *p++ = '0';
559 *p++ = 'x';
560 *p++ = hexchars[t[i] >> 4];
561 *p++ = hexchars[t[i] & 0xF];
562 }
563 }
564 t += 1+t[0];
565 if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
566 }
567 *p++ = 0;
568 n += mprintf("%.*s", MaxWidth - n, buffer);
569 } break;
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;
572 default: {
573 mDNSu8 *s = rd->data;
574 while (s < rdend && p < buffer+MaxWidth)
575 {
576 if (*s == '\\') *p++ = '\\';
577 if (*s >= ' ') *p++ = *s;
578 else
579 {
580 *p++ = '\\';
581 *p++ = '0';
582 *p++ = 'x';
583 *p++ = hexchars[*s >> 4];
584 *p++ = hexchars[*s & 0xF];
585 }
586 s++;
587 }
588 *p++ = 0;
589 n += mprintf("%.*s", MaxWidth - n, buffer);
590 } break;
591 }
592
593 mprintf("\n");
594 }
595
596 mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
597 {
598 while (ptr < end)
599 {
600 int i;
601 for (i=0; i<16; i++)
602 if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
603 else mprintf(" ");
604 for (i=0; i<16; i++)
605 if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
606 ptr += 16;
607 mprintf("\n");
608 }
609 }
610
611 mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
612 {
613 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
614 HexDump(ptr, end);
615 }
616
617 mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
618 {
619 int i;
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;
625
626 DisplayPacketHeader(msg, end, srcaddr, srcport);
627 if (MQ) NumPktQ++; else NumPktL++;
628
629 for (i=0; i<msg->h.numQuestions; i++)
630 {
631 DNSQuestion q;
632 mDNSu8 *p2;
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);
639 if (p2)
640 {
641 NumProbes++;
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;
647 }
648 else
649 {
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);
655 }
656 }
657
658 for (i=0; i<msg->h.numAnswers; i++)
659 {
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);
664
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);
670 }
671
672 for (i=0; i<msg->h.numAuthorities; i++)
673 {
674 const mDNSu8 *ep = ptr;
675 ptr = skipResourceRecord(msg, ptr, end);
676 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
677 }
678 }
679
680 mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
681 {
682 int i;
683 const mDNSu8 *ptr = msg->data;
684 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R);
685 LargeCacheRecord pkt;
686
687 DisplayPacketHeader(msg, end, srcaddr, srcport);
688 NumPktR++;
689
690 for (i=0; i<msg->h.numQuestions; i++)
691 {
692 DNSQuestion q;
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);
697 }
698
699 for (i=0; i<msg->h.numAnswers; i++)
700 {
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)
705 {
706 NumAnswers++;
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);
709 }
710 else
711 {
712 NumGoodbyes++;
713 DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
714 recordstat(entry, &pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
715 }
716 }
717
718 for (i=0; i<msg->h.numAuthorities; i++)
719 {
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);
725 }
726
727 for (i=0; i<msg->h.numAdditionals; i++)
728 {
729 const mDNSu8 *ep = ptr;
730 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
731 if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
732 NumAdditionals++;
733 DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec);
734 }
735 }
736
737 mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
738 {
739 FilterList *f;
740 if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
741 for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
742 return(mDNSfalse);
743 }
744
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)
747 {
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);
751
752 (void)dstaddr; // Unused
753 (void)dstport; // Unused
754
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]);
761
762 if (ttl < 254)
763 {
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");
771 }
772
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))
776 {
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);
779 else
780 {
781 debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
782 GotPacketFromHost(srcaddr, HostPkt_B);
783 NumPktB++;
784 }
785 }
786 }
787
788 mDNSlocal mStatus mDNSNetMonitor(void)
789 {
790 struct tm tm;
791 int h, m, s, mul, div, TotPkt;
792
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);
798
799 gettimeofday(&tv_start, NULL);
800 ExampleClientEventLoop(&mDNSStorage); // Wait for user to hit Ctrl-C
801
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)
814 {
815 mul = 60;
816 div = tv_interval.tv_sec;
817 }
818 else
819 {
820 mul = 60000;
821 div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
822 if (div == 0) div=1;
823 }
824
825 mprintf("\n\n");
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);
832 mprintf("\n");
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);
837 mprintf("\n");
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);
844 mprintf("\n");
845 printstats(15);
846
847 if (!ExactlyOneFilter)
848 {
849 ShowSortedHostList(&IPv4HostList, 15);
850 ShowSortedHostList(&IPv6HostList, 15);
851 }
852
853 mDNS_Close(&mDNSStorage);
854 return(0);
855 }
856
857 mDNSexport int main(int argc, char **argv)
858 {
859 int i;
860 mStatus status;
861
862 setlinebuf(stdout); // Want to see lines as they appear, not block buffered
863
864 for (i=1; i<argc; i++)
865 {
866 struct in_addr s4;
867 struct in6_addr s6;
868 mDNSAddr a;
869 a.type = mDNSAddrType_IPv4;
870
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)
874 {
875 a.type = mDNSAddrType_IPv6;
876 bcopy(&s6, &a.ip.v6, sizeof(a.ip.v6));
877 }
878 else
879 {
880 struct hostent *h = gethostbyname(argv[i]);
881 if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
882 else goto usage;
883 }
884
885 FilterList *f = malloc(sizeof(*f));
886 f->FilterAddr = a;
887 f->next = Filters;
888 Filters = f;
889 }
890
891 status = mDNSNetMonitor();
892 if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", argv[0], status); return(status); }
893 return(0);
894
895 usage:
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");
899
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");
905
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");
918
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");
927 return(-1);
928 }