]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/NetMonitor.c
8a90d249376ff988bc126df6a05de48742a092e2
[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.49 2003/10/30 19:38:56 cheshire
40 Fix warning on certain compilers
41
42 Revision 1.48 2003/10/30 19:30:00 cheshire
43 Fix warnings on certain compilers
44
45 Revision 1.47 2003/09/05 18:49:57 cheshire
46 Add total packet size to display
47
48 Revision 1.46 2003/09/05 02:33:48 cheshire
49 Set output to be line buffered, so you can redirect to a file and "tail -f" the file in another window
50
51 Revision 1.45 2003/09/04 00:16:20 cheshire
52 Only show count of unique source addresses seen on network if we're not filtering
53
54 Revision 1.44 2003/09/02 22:13:28 cheshire
55 Show total host count in final summary table
56
57 Revision 1.43 2003/09/02 21:42:52 cheshire
58 Improved alignment of final summary table with v6 addresses
59
60 Revision 1.42 2003/09/02 20:59:24 cheshire
61 Use bcopy() instead of non-portable "__u6_addr.__u6_addr32" fields.
62
63 Revision 1.41 2003/08/29 22:05:44 cheshire
64 Also count subsequent KA packets for the purposes of statistics counting
65
66 Revision 1.40 2003/08/29 16:43:24 cheshire
67 Also display breakdown of Probe/Goodbye/BrowseQ etc. for each host
68
69 Revision 1.39 2003/08/28 02:07:48 vlubet
70 Added "per hosts" statistics
71
72 Revision 1.38 2003/08/20 22:41:42 cheshire
73 Also display total multicast packet count
74
75 Revision 1.37 2003/08/20 22:32:08 cheshire
76 Error in DisplayQuery: Authorities come *after* Answers, not before
77
78 Revision 1.36 2003/08/18 23:20:10 cheshire
79 RDLength moved from the RData to the ResourceRecord object.
80
81 Revision 1.35 2003/08/15 20:17:28 cheshire
82 "LargeResourceRecord" changed to "LargeCacheRecord"
83
84 Revision 1.34 2003/08/14 02:19:55 cheshire
85 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
86
87 Revision 1.33 2003/08/12 19:56:26 cheshire
88 Update to APSL 2.0
89
90 Revision 1.32 2003/08/06 18:57:01 cheshire
91 Update comments
92
93 Revision 1.31 2003/08/06 02:05:12 cheshire
94 Add ability to give a list of hosts to monitor
95
96 Revision 1.30 2003/08/05 23:56:26 cheshire
97 Update code to compile with the new mDNSCoreReceive() function that requires a TTL
98 (Right now mDNSPosix.c just reports 255 -- we should fix this)
99
100 Revision 1.29 2003/08/05 00:43:12 cheshire
101 Report errors encountered while processing authority section
102
103 Revision 1.28 2003/07/29 22:51:08 cheshire
104 Added hexdump for packets we can't decode, so we can find out *why* we couldn't decode them
105
106 Revision 1.27 2003/07/29 22:48:04 cheshire
107 Completed support for decoding packets containing oversized resource records
108
109 Revision 1.26 2003/07/19 03:25:23 cheshire
110 Change to make use of new GetLargeResourceRecord() call, for handling larger records
111
112 Revision 1.25 2003/07/18 00:13:23 cheshire
113 Remove erroneous trailing '\' from TXT record display
114
115 Revision 1.24 2003/07/17 17:10:51 cheshire
116 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
117 Further work: distinguish between PM and PU
118
119 Revision 1.23 2003/07/16 22:20:23 cheshire
120 <rdar://problem/3315761> Implement "unicast response" request, using top bit of qclass
121 Made mDNSNetMonitor distinguish between QM and QU in its logging output
122
123 Revision 1.22 2003/07/02 21:19:58 cheshire
124 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
125
126 Revision 1.21 2003/06/18 05:48:41 cheshire
127 Fix warnings
128
129 Revision 1.20 2003/06/06 22:18:22 cheshire
130 Add extra space in Q output to line it up with RR output
131
132 Revision 1.19 2003/06/06 21:05:04 cheshire
133 Display state of cache-flush bit on additional records
134
135 Revision 1.18 2003/06/06 20:01:30 cheshire
136 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
137 (Global search-and-replace; no functional change to code execution.)
138
139 Revision 1.17 2003/06/06 14:26:50 cheshire
140 Explicitly #include <time.h> for the benefit of certain Linux distributions
141
142 Revision 1.16 2003/05/29 21:56:36 cheshire
143 More improvements:
144 Distinguish modern multicast queries from legacy multicast queries
145 In addition to record counts, display packet counts of queries, legacy queries, and responses
146 Include TTL in RR display
147
148 Revision 1.15 2003/05/29 20:03:57 cheshire
149 Various improvements:
150 Display start and end time, average rates in packets-per-minute,
151 show legacy queries as -LQ-, improve display of TXT and unknown records
152
153 Revision 1.14 2003/05/26 04:45:42 cheshire
154 Limit line length when printing super-long TXT records
155
156 Revision 1.13 2003/05/26 03:21:29 cheshire
157 Tidy up address structure naming:
158 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
159 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
160 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
161
162 Revision 1.12 2003/05/26 03:01:28 cheshire
163 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
164
165 Revision 1.11 2003/05/26 00:48:13 cheshire
166 If mDNS packet contains a non-zero message ID, then display it.
167
168 Revision 1.10 2003/05/22 01:10:32 cheshire
169 Indicate when the TC bit is set on a query packet
170
171 Revision 1.9 2003/05/21 03:56:00 cheshire
172 Improve display of Probe queries
173
174 Revision 1.8 2003/05/09 21:41:56 cheshire
175 Track deletion/goodbye packets as separate category
176
177 Revision 1.7 2003/05/07 00:16:01 cheshire
178 More detailed decoding of Resource Records
179
180 Revision 1.6 2003/05/05 21:16:16 cheshire
181 <rdar://problem/3241281> Change timenow from a local variable to a structure member
182
183 Revision 1.5 2003/04/19 01:16:22 cheshire
184 Add filter option, to monitor only packets from a single specified source address
185
186 Revision 1.4 2003/04/18 00:45:21 cheshire
187 Distinguish announcements (AN) from deletions (DE)
188
189 Revision 1.3 2003/04/15 18:26:01 cheshire
190 Added timestamps and help information
191
192 Revision 1.2 2003/04/04 20:42:02 cheshire
193 Fix broken statistics counting
194
195 Revision 1.1 2003/04/04 01:37:14 cheshire
196 Added NetMonitor.c
197
198 */
199
200 //*************************************************************************************************************
201 // Incorporate mDNS.c functionality
202
203 // We want to use much of the functionality provided by "mDNS.c",
204 // except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
205 #define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
206 #include "mDNS.c"
207 #undef mDNSCoreReceive
208
209 //*************************************************************************************************************
210 // Headers
211
212 #include <stdio.h> // For printf()
213 #include <stdlib.h> // For malloc()
214 #include <string.h> // For bcopy()
215 #include <time.h> // For "struct tm" etc.
216 #include <netdb.h> // For gethostbyname()
217 #include <sys/socket.h> // For AF_INET, AF_INET6, etc.
218 #include <arpa/inet.h> // For inet_addr()
219 #include <netinet/in.h> // For INADDR_NONE
220
221 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
222 #include "ExampleClientApp.h"
223
224 //*************************************************************************************************************
225 // Types and structures
226
227 enum
228 {
229 // Primitive operations
230 OP_probe = 0,
231 OP_goodbye = 1,
232
233 // These are meta-categories;
234 // Query and Answer operations are actually subdivided into two classes:
235 // Browse query/answer and
236 // Resolve query/answer
237 OP_query = 2,
238 OP_answer = 3,
239
240 // The "Browse" variants of query/answer
241 OP_browsegroup = 2,
242 OP_browseq = 2,
243 OP_browsea = 3,
244
245 // The "Resolve" variants of query/answer
246 OP_resolvegroup = 4,
247 OP_resolveq = 4,
248 OP_resolvea = 5,
249
250 OP_NumTypes = 6
251 };
252
253 typedef struct ActivityStat_struct ActivityStat;
254 struct ActivityStat_struct
255 {
256 ActivityStat *next;
257 domainname srvtype;
258 int printed;
259 int totalops;
260 int stat[OP_NumTypes];
261 };
262
263 typedef struct FilterList_struct FilterList;
264 struct FilterList_struct
265 {
266 FilterList *next;
267 mDNSAddr FilterAddr;
268 };
269
270 //*************************************************************************************************************
271 // Globals
272
273 static mDNS mDNSStorage; // mDNS core uses this to store its globals
274 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
275
276 struct timeval tv_start, tv_end, tv_interval;
277
278 static FilterList *Filters;
279 #define ExactlyOneFilter (Filters && !Filters->next)
280
281 static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad
282 static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
283
284 static ActivityStat *stats;
285
286 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
287
288 //*************************************************************************************************************
289 // Utilities
290
291 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
292 mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
293 mDNSlocal mDNSu32 mprintf(const char *format, ...)
294 {
295 mDNSu32 length;
296 unsigned char buffer[512];
297 va_list ptr;
298 va_start(ptr,format);
299 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
300 va_end(ptr);
301 printf("%s", buffer);
302 return(length);
303 }
304
305 //*************************************************************************************************************
306 // Host Address List
307 //
308 // Would benefit from a hash
309
310 typedef enum
311 {
312 HostPkt_Q = 0, // Query
313 HostPkt_L = 1, // Legacy Query
314 HostPkt_R = 2, // Response
315 HostPkt_B = 3, // Bad
316 HostPkt_NumTypes = 4
317 } HostPkt_Type;
318
319 typedef struct
320 {
321 mDNSAddr addr;
322 unsigned long pkts[HostPkt_NumTypes];
323 unsigned long totalops;
324 unsigned long stat[OP_NumTypes];
325 } HostEntry;
326
327 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
328
329 typedef struct
330 {
331 long num;
332 long max;
333 HostEntry *hosts;
334 } HostList;
335
336 static HostList IPv4HostList = { 0, 0, 0 };
337 static HostList IPv6HostList = { 0, 0, 0 };
338
339 mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList* list)
340 {
341 long i;
342
343 for (i = 0; i < list->num; i++)
344 {
345 HostEntry *entry = list->hosts + i;
346 if (mDNSSameAddress(addr, &entry->addr))
347 return entry;
348 }
349
350 return NULL;
351 }
352
353 mDNSlocal HostEntry *AddHost(HostList* list)
354 {
355 HostEntry *entry;
356 if (list->num >= list->max)
357 {
358 long newMax = list->max + 64;
359 HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
360 if (newHosts == NULL)
361 return NULL;
362 list->max = newMax;
363 list->hosts = newHosts;
364 }
365 entry = list->hosts + list->num++;
366 return(entry);
367 }
368
369 mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t)
370 {
371 if (ExactlyOneFilter) return(NULL);
372 else
373 {
374 HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
375 HostEntry *entry = FindHost(addr, list);
376 if (!entry)
377 {
378 int i;
379 entry = AddHost(list);
380 if (!entry) return(NULL);
381 entry->addr = *addr;
382 for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
383 entry->totalops = 0;
384 for (i=0; i<OP_NumTypes; i++) entry->stat[i] = 0;
385 }
386 entry->pkts[t]++;
387 return(entry);
388 }
389 }
390
391 mDNSlocal int CompareHosts(const void *p1, const void *p2)
392 {
393 return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
394 }
395
396 mDNSlocal void ShowSortedHostList(HostList *list, int max)
397 {
398 HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num];
399 qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts);
400 if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response");
401 for (e = &list->hosts[0]; e < end; e++)
402 {
403 int len = mprintf("%#-25a", &e->addr);
404 if (len > 25) mprintf("\n%25s", "");
405 mprintf("%8d %8d %8d %8d %8d %8d %8d", e->totalops,
406 e->stat[OP_probe], e->stat[OP_goodbye],
407 e->stat[OP_browseq], e->stat[OP_browsea],
408 e->stat[OP_resolveq], e->stat[OP_resolvea]);
409 mprintf(" %8lu %8lu %8lu %8lu",
410 HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]);
411 if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]);
412 mprintf("\n");
413 }
414 }
415
416 //*************************************************************************************************************
417 // Receive and process packets
418
419 mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
420 {
421 int i, len;
422 const mDNSu8 *src = fqdn->c;
423 mDNSu8 *dst = srvtype->c;
424
425 len = *src;
426 if (len == 0 || len >= 0x40) return(mDNSfalse);
427 if (src[1] != '_') src += 1 + len;
428
429 len = *src;
430 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
431 for (i=0; i<=len; i++) *dst++ = *src++;
432
433 len = *src;
434 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
435 for (i=0; i<=len; i++) *dst++ = *src++;
436
437 *dst++ = 0; // Put the null root label on the end of the service type
438
439 return(mDNStrue);
440 }
441
442 mDNSlocal void recordstat(HostEntry *entry, domainname *fqdn, int op, mDNSu16 rrtype)
443 {
444 ActivityStat **s = &stats;
445 domainname srvtype;
446
447 if (op != OP_probe)
448 {
449 if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
450 else if (rrtype != kDNSType_PTR) return;
451 }
452
453 if (!ExtractServiceType(fqdn, &srvtype)) return;
454
455 while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
456 if (!*s)
457 {
458 int i;
459 *s = malloc(sizeof(ActivityStat));
460 if (!*s) exit(-1);
461 (*s)->next = NULL;
462 (*s)->srvtype = srvtype;
463 (*s)->printed = 0;
464 (*s)->totalops = 0;
465 for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
466 }
467
468 (*s)->totalops++;
469 (*s)->stat[op]++;
470 if (entry)
471 {
472 entry->totalops++;
473 entry->stat[op]++;
474 }
475 }
476
477 mDNSlocal void printstats(int max)
478 {
479 int i;
480 for (i=0; i<max; i++)
481 {
482 int max = 0;
483 ActivityStat *s, *m = NULL;
484 for (s = stats; s; s=s->next)
485 if (!s->printed && max < s->totalops)
486 { m = s; max = s->totalops; }
487 if (!m) return;
488 m->printed = mDNStrue;
489 if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner);
490 mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", &m->srvtype, m->totalops, m->stat[OP_probe],
491 m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]);
492 }
493 }
494
495 mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end, DNSQuestion *q, LargeCacheRecord *pkt)
496 {
497 int i;
498 for (i = 0; i < query->h.numAuthorities; i++)
499 {
500 const mDNSu8 *p2 = ptr;
501 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, 0, pkt);
502 if (!ptr) break;
503 if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
504 }
505 return(mDNSNULL);
506 }
507
508 mDNSlocal void DisplayTimestamp(void)
509 {
510 struct timeval tv;
511 struct tm tm;
512 gettimeofday(&tv, NULL);
513 localtime_r((time_t*)&tv.tv_sec, &tm);
514 mprintf("\n%d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec);
515 }
516
517 mDNSlocal void DisplayPacketHeader(const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport)
518 {
519 const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " :
520 (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
521
522 DisplayTimestamp();
523 mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
524 srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg);
525
526 if (msg->h.id.NotAnInteger) mprintf(" ID:%u", ((mDNSu16)msg->h.id.b[0])<<8 | msg->h.id.b[1]);
527
528 if (msg->h.flags.b[0] & kDNSFlag0_TC)
529 {
530 if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated");
531 else mprintf(" Truncated (KA list continues in next packet)");
532 }
533 mprintf("\n");
534 }
535
536 mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
537 {
538 static const char hexchars[16] = "0123456789ABCDEF";
539 #define MaxWidth 132
540 char buffer[MaxWidth+8];
541 char *p = buffer;
542
543 RDataBody *rd = &pktrr->rdata->u;
544 mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
545 mDNSu32 n = mprintf("%#-16a %-5s %-5s%5d %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name.c);
546
547 switch(pktrr->rrtype)
548 {
549 case kDNSType_A: n += mprintf("%.4a", &rd->ip); break;
550 case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, &rd->name); break;
551 case kDNSType_HINFO:// same as kDNSType_TXT below
552 case kDNSType_TXT: {
553 mDNSu8 *t = rd->txt.c;
554 while (t < rdend && t[0] && p < buffer+MaxWidth)
555 {
556 int i;
557 for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
558 {
559 if (t[i] == '\\') *p++ = '\\';
560 if (t[i] >= ' ') *p++ = t[i];
561 else
562 {
563 *p++ = '\\';
564 *p++ = '0';
565 *p++ = 'x';
566 *p++ = hexchars[t[i] >> 4];
567 *p++ = hexchars[t[i] & 0xF];
568 }
569 }
570 t += 1+t[0];
571 if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
572 }
573 *p++ = 0;
574 n += mprintf("%.*s", MaxWidth - n, buffer);
575 } break;
576 case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break;
577 case kDNSType_SRV: n += mprintf("%##s:%d", &rd->srv.target, ((mDNSu16)rd->srv.port.b[0] << 8) | rd->srv.port.b[1]); break;
578 default: {
579 mDNSu8 *s = rd->data;
580 while (s < rdend && p < buffer+MaxWidth)
581 {
582 if (*s == '\\') *p++ = '\\';
583 if (*s >= ' ') *p++ = *s;
584 else
585 {
586 *p++ = '\\';
587 *p++ = '0';
588 *p++ = 'x';
589 *p++ = hexchars[*s >> 4];
590 *p++ = hexchars[*s & 0xF];
591 }
592 s++;
593 }
594 *p++ = 0;
595 n += mprintf("%.*s", MaxWidth - n, buffer);
596 } break;
597 }
598
599 mprintf("\n");
600 }
601
602 mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
603 {
604 while (ptr < end)
605 {
606 int i;
607 for (i=0; i<16; i++)
608 if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
609 else mprintf(" ");
610 for (i=0; i<16; i++)
611 if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
612 ptr += 16;
613 mprintf("\n");
614 }
615 }
616
617 mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
618 {
619 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
620 HexDump(ptr, end);
621 }
622
623 mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
624 {
625 int i;
626 const mDNSu8 *ptr = msg->data;
627 const mDNSu8 *auth = LocateAuthorities(msg, end);
628 mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger);
629 HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L);
630 LargeCacheRecord pkt;
631
632 DisplayPacketHeader(msg, end, srcaddr, srcport);
633 if (MQ) NumPktQ++; else NumPktL++;
634
635 for (i=0; i<msg->h.numQuestions; i++)
636 {
637 DNSQuestion q;
638 mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q);
639 mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse;
640 q.qclass &= ~kDNSQClass_UnicastResponse;
641 if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; }
642 ptr = p2;
643 p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
644 if (p2)
645 {
646 NumProbes++;
647 DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec);
648 recordstat(entry, &q.qname, OP_probe, q.qtype);
649 p2 = (mDNSu8 *)skipDomainName(msg, p2, end);
650 // Having displayed this update record, clear type and class so we don't display the same one again.
651 p2[0] = p2[1] = p2[2] = p2[3] = 0;
652 }
653 else
654 {
655 const char *ptype = ucbit ? "(QU)" : "(QM)";
656 if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++;
657 else { NumLegacy++; ptype = "(LQ)"; }
658 mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c);
659 recordstat(entry, &q.qname, OP_query, q.qtype);
660 }
661 }
662
663 for (i=0; i<msg->h.numAnswers; i++)
664 {
665 const mDNSu8 *ep = ptr;
666 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
667 if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; }
668 DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec);
669
670 // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
671 // the same as a single query, to more accurately reflect the burden on the network
672 // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
673 if (msg->h.numQuestions == 0 && i == 0)
674 recordstat(entry, &pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype);
675 }
676
677 for (i=0; i<msg->h.numAuthorities; i++)
678 {
679 const mDNSu8 *ep = ptr;
680 ptr = skipResourceRecord(msg, ptr, end);
681 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
682 }
683 }
684
685 mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
686 {
687 int i;
688 const mDNSu8 *ptr = msg->data;
689 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R);
690 LargeCacheRecord pkt;
691
692 DisplayPacketHeader(msg, end, srcaddr, srcport);
693 NumPktR++;
694
695 for (i=0; i<msg->h.numQuestions; i++)
696 {
697 DNSQuestion q;
698 const mDNSu8 *ep = ptr;
699 ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
700 if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
701 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
702 }
703
704 for (i=0; i<msg->h.numAnswers; i++)
705 {
706 const mDNSu8 *ep = ptr;
707 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
708 if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; }
709 if (pkt.r.resrec.rroriginalttl)
710 {
711 NumAnswers++;
712 DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec);
713 recordstat(entry, &pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype);
714 }
715 else
716 {
717 NumGoodbyes++;
718 DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
719 recordstat(entry, &pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
720 }
721 }
722
723 for (i=0; i<msg->h.numAuthorities; i++)
724 {
725 const mDNSu8 *ep = ptr;
726 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
727 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
728 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
729 srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name.c);
730 }
731
732 for (i=0; i<msg->h.numAdditionals; i++)
733 {
734 const mDNSu8 *ep = ptr;
735 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, 0, &pkt);
736 if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
737 NumAdditionals++;
738 DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)", &pkt.r.resrec);
739 }
740 }
741
742 mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
743 {
744 FilterList *f;
745 if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
746 for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
747 return(mDNSfalse);
748 }
749
750 mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
751 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSu8 ttl)
752 {
753 const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
754 const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
755 const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
756 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
757
758 (void)dstaddr; // Unused
759 (void)dstport; // Unused
760
761 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
762 msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
763 msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
764 msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
765 msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
766
767 if (ttl < 254)
768 {
769 debugf("** Apparent spoof mDNS %s packet from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
770 (QR_OP == StdQ) ? "Query" : (QR_OP == StdR) ? "Response" : "Unkown",
771 srcaddr, dstaddr, ttl, InterfaceID,
772 msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,",
773 msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,",
774 msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,",
775 msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s");
776 }
777
778 // For now we're only interested in monitoring IPv4 traffic.
779 // All IPv6 packets should just be duplicates of the v4 packets.
780 if (AddressMatchesFilterList(srcaddr))
781 {
782 if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, InterfaceID);
783 else if (QR_OP == StdR) DisplayResponse(m, msg, end, srcaddr, srcport, InterfaceID);
784 else
785 {
786 debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
787 GotPacketFromHost(srcaddr, HostPkt_B);
788 NumPktB++;
789 }
790 }
791 }
792
793 mDNSlocal mStatus mDNSNetMonitor(void)
794 {
795 struct tm tm;
796 int h, m, s, mul, div, TotPkt;
797
798 mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage,
799 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
800 mDNS_Init_DontAdvertiseLocalAddresses,
801 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
802 if (status) return(status);
803
804 gettimeofday(&tv_start, NULL);
805 ExampleClientEventLoop(&mDNSStorage); // Wait for user to hit Ctrl-C
806
807 // Now display final summary
808 TotPkt = NumPktQ + NumPktL + NumPktR;
809 gettimeofday(&tv_end, NULL);
810 tv_interval = tv_end;
811 if (tv_start.tv_usec > tv_interval.tv_usec)
812 { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; }
813 tv_interval.tv_sec -= tv_start.tv_sec;
814 tv_interval.tv_usec -= tv_start.tv_usec;
815 h = (tv_interval.tv_sec / 3600);
816 m = (tv_interval.tv_sec % 3600) / 60;
817 s = (tv_interval.tv_sec % 60);
818 if (tv_interval.tv_sec > 10)
819 {
820 mul = 60;
821 div = tv_interval.tv_sec;
822 }
823 else
824 {
825 mul = 60000;
826 div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
827 if (div == 0) div=1;
828 }
829
830 mprintf("\n\n");
831 localtime_r((time_t*)&tv_start.tv_sec, &tm);
832 mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec);
833 localtime_r((time_t*)&tv_end.tv_sec, &tm);
834 mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec);
835 mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec);
836 if (!Filters) mprintf("Unique source addresses seen on network: %d\n", IPv4HostList.num + IPv6HostList.num);
837 mprintf("\n");
838 mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div);
839 mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div);
840 mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div);
841 mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div);
842 mprintf("\n");
843 mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div);
844 mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div);
845 mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div);
846 mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div);
847 mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div);
848 mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div);
849 mprintf("\n");
850 printstats(15);
851
852 if (!ExactlyOneFilter)
853 {
854 ShowSortedHostList(&IPv4HostList, 15);
855 ShowSortedHostList(&IPv6HostList, 15);
856 }
857
858 mDNS_Close(&mDNSStorage);
859 return(0);
860 }
861
862 mDNSexport int main(int argc, char **argv)
863 {
864 int i;
865 mStatus status;
866
867 setlinebuf(stdout); // Want to see lines as they appear, not block buffered
868
869 for (i=1; i<argc; i++)
870 {
871 FilterList *f;
872 struct in_addr s4;
873 struct in6_addr s6;
874 mDNSAddr a;
875 a.type = mDNSAddrType_IPv4;
876
877 if (inet_pton(AF_INET, argv[i], &s4) == 1)
878 a.ip.v4.NotAnInteger = s4.s_addr;
879 else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
880 {
881 a.type = mDNSAddrType_IPv6;
882 bcopy(&s6, &a.ip.v6, sizeof(a.ip.v6));
883 }
884 else
885 {
886 struct hostent *h = gethostbyname(argv[i]);
887 if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
888 else goto usage;
889 }
890
891 f = malloc(sizeof(*f));
892 f->FilterAddr = a;
893 f->next = Filters;
894 Filters = f;
895 }
896
897 status = mDNSNetMonitor();
898 if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %ld\n", argv[0], status); return(status); }
899 return(0);
900
901 usage:
902 fprintf(stderr, "\nmDNS traffic monitor\n");
903 fprintf(stderr, "Usage: %s (<host>)\n", argv[0]);
904 fprintf(stderr, "Optional <host> parameter displays only packets from that host\n");
905
906 fprintf(stderr, "\nPer-packet header output:\n");
907 fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
908 fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n");
909 fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
910 fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
911
912 fprintf(stderr, "\nPer-record display:\n");
913 fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n");
914 fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n");
915 fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n");
916 fprintf(stderr, "(LQ) Legacy Query Question\n");
917 fprintf(stderr, "(QM) Query Question, requesting multicast response\n");
918 fprintf(stderr, "(QU) Query Question, requesting unicast response\n");
919 fprintf(stderr, "(KA) Known Answer (information querier already knows)\n");
920 fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
921 fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
922 fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n");
923 fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n");
924
925 fprintf(stderr, "\nFinal summary, sorted by service type:\n");
926 fprintf(stderr, "Probe Probes for this service type starting up\n");
927 fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
928 fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
929 fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n");
930 fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
931 fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
932 fprintf(stderr, "\n");
933 return(-1);
934 }