]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/NetMonitor.c
mDNSResponder-214.tar.gz
[apple/mdnsresponder.git] / mDNSPosix / NetMonitor.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * Formatting notes:
18 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
19 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
20 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
21 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
22 * therefore common sense dictates that if they are part of a compound statement then they
23 * should be indented to the same level as everything else in that compound statement.
24 * Indenting curly braces at the same level as the "if" implies that curly braces are
25 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
26 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
27 * understand why variable y is not of type "char*" just proves the point that poor code
28 * layout leads people to unfortunate misunderstandings about how the C language really works.)
29
30 Change History (most recent first):
31
32 $Log: NetMonitor.c,v $
33 Revision 1.96 2009/07/16 00:08:57 cheshire
34 Display any stray Update (Authority) records in query packets
35
36 Revision 1.95 2009/07/09 22:24:52 herscher
37 <rdar://problem/3775717> SDK: Port mDNSNetMonitor to Windows
38
39 Revision 1.94 2009/04/24 00:31:56 cheshire
40 <rdar://problem/3476350> Return negative answers when host knows authoritatively that no answer exists
41 Added code to display NSEC records
42
43 Revision 1.93 2009/01/13 05:31:34 mkrochma
44 <rdar://problem/6491367> Replace bzero, bcopy with mDNSPlatformMemZero, mDNSPlatformMemCopy, memset, memcpy
45
46 Revision 1.92 2009/01/11 03:20:06 mkrochma
47 <rdar://problem/5797526> Fixes from Igor Seleznev to get mdnsd working on Solaris
48
49 Revision 1.91 2008/11/13 22:08:07 cheshire
50 Show additional records in Query packets
51
52 Revision 1.90 2008/09/05 22:20:26 cheshire
53 <rdar://problem/3988320> Should use randomized source ports and transaction IDs to avoid DNS cache poisoning
54 Add "UDPSocket *src" parameter in mDNSPlatformSendUDP call
55
56 Revision 1.89 2007/05/17 19:12:42 cheshire
57 Tidy up code layout
58
59 Revision 1.88 2007/04/22 20:16:25 cheshire
60 Fix compiler errors (const parameter declarations)
61
62 Revision 1.87 2007/04/16 20:49:39 cheshire
63 Fix compile errors for mDNSPosix build
64
65 Revision 1.86 2007/03/22 18:31:48 cheshire
66 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
67
68 Revision 1.85 2007/02/28 01:51:22 cheshire
69 Added comment about reverse-order IP address
70
71 Revision 1.84 2007/01/05 08:30:52 cheshire
72 Trim excessive "$Log" checkin history from before 2006
73 (checkin history still available via "cvs log ..." of course)
74
75 Revision 1.83 2006/11/18 05:01:32 cheshire
76 Preliminary support for unifying the uDNS and mDNS code,
77 including caching of uDNS answers
78
79 Revision 1.82 2006/08/14 23:24:46 cheshire
80 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
81
82 Revision 1.81 2006/07/06 00:01:44 cheshire
83 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
84 Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
85
86 Revision 1.80 2006/06/12 18:22:42 cheshire
87 <rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
88
89 Revision 1.79 2006/04/26 20:48:33 cheshire
90 Make final count of unique source addresses show IPv4 and IPv6 counts separately
91
92 Revision 1.78 2006/04/25 00:42:24 cheshire
93 Add ability to specify a single interface index to capture on,
94 e.g. typically "-i 4" for Ethernet and "-i 5" for AirPort
95
96 Revision 1.77 2006/03/02 21:50:45 cheshire
97 Removed strange backslash at the end of a line
98
99 Revision 1.76 2006/02/23 23:38:43 cheshire
100 <rdar://problem/4427969> On FreeBSD 4 "arpa/inet.h" requires "netinet/in.h" be included first
101
102 Revision 1.75 2006/01/05 22:33:58 cheshire
103 Use IFNAMSIZ (more portable) instead of IF_NAMESIZE
104
105 */
106
107 //*************************************************************************************************************
108 // Incorporate mDNS.c functionality
109
110 // We want to use much of the functionality provided by "mDNS.c",
111 // except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
112 #define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
113 #include "mDNS.c"
114 #undef mDNSCoreReceive
115
116 //*************************************************************************************************************
117 // Headers
118
119 #include <stdio.h> // For printf()
120 #include <stdlib.h> // For malloc()
121 #include <string.h> // For strrchr(), strcmp()
122 #include <time.h> // For "struct tm" etc.
123 #include <signal.h> // For SIGINT, SIGTERM
124 #if defined(WIN32)
125 # include <mDNSEmbeddedAPI.h>
126 # include <mDNSWin32.h>
127 # include <uds_daemon.h>
128 # include <PosixCompat.h>
129 # include <Service.h>
130 # define IFNAMSIZ 256
131
132 // Stub these functions out
133 mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count) { return 0; }
134 mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) { return 0; }
135 mDNSexport void udsserver_handle_configchange(mDNS *const m) {}
136 mDNSexport int udsserver_exit(void) { return 0; }
137 void setlinebuf( FILE * fp ) {}
138 #else
139 # include <netdb.h> // For gethostbyname()
140 # include <sys/socket.h> // For AF_INET, AF_INET6, etc.
141 # include <net/if.h> // For IF_NAMESIZE
142 # include <netinet/in.h> // For INADDR_NONE
143 # include <arpa/inet.h> // For inet_addr()
144 # include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
145 #endif
146 #include "ExampleClientApp.h"
147
148 //*************************************************************************************************************
149 // Types and structures
150
151 enum
152 {
153 // Primitive operations
154 OP_probe = 0,
155 OP_goodbye = 1,
156
157 // These are meta-categories;
158 // Query and Answer operations are actually subdivided into two classes:
159 // Browse query/answer and
160 // Resolve query/answer
161 OP_query = 2,
162 OP_answer = 3,
163
164 // The "Browse" variants of query/answer
165 OP_browsegroup = 2,
166 OP_browseq = 2,
167 OP_browsea = 3,
168
169 // The "Resolve" variants of query/answer
170 OP_resolvegroup = 4,
171 OP_resolveq = 4,
172 OP_resolvea = 5,
173
174 OP_NumTypes = 6
175 };
176
177 typedef struct ActivityStat_struct ActivityStat;
178 struct ActivityStat_struct
179 {
180 ActivityStat *next;
181 domainname srvtype;
182 int printed;
183 int totalops;
184 int stat[OP_NumTypes];
185 };
186
187 typedef struct FilterList_struct FilterList;
188 struct FilterList_struct
189 {
190 FilterList *next;
191 mDNSAddr FilterAddr;
192 };
193
194 //*************************************************************************************************************
195 // Constants
196
197 #define kReportTopServices 15
198 #define kReportTopHosts 15
199
200 //*************************************************************************************************************
201 // Globals
202
203 mDNS mDNSStorage; // mDNS core uses this to store its globals
204 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
205 mDNSexport const char ProgramName[] = "mDNSNetMonitor";
206
207 struct timeval tv_start, tv_end, tv_interval;
208 static int FilterInterface = 0;
209 static FilterList *Filters;
210 #define ExactlyOneFilter (Filters && !Filters->next)
211
212 static int NumPktQ, NumPktL, NumPktR, NumPktB; // Query/Legacy/Response/Bad
213 static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
214
215 static ActivityStat *stats;
216
217 #define OPBanner "Total Ops Probe Goodbye BrowseQ BrowseA ResolveQ ResolveA"
218
219 //*************************************************************************************************************
220 // Utilities
221
222 // Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
223 mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
224 mDNSlocal mDNSu32 mprintf(const char *format, ...)
225 {
226 mDNSu32 length;
227 unsigned char buffer[512];
228 va_list ptr;
229 va_start(ptr,format);
230 length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
231 va_end(ptr);
232 printf("%s", buffer);
233 return(length);
234 }
235
236 //*************************************************************************************************************
237 // Host Address List
238 //
239 // Would benefit from a hash
240
241 typedef enum
242 {
243 HostPkt_Q = 0, // Query
244 HostPkt_L = 1, // Legacy Query
245 HostPkt_R = 2, // Response
246 HostPkt_B = 3, // Bad
247 HostPkt_NumTypes = 4
248 } HostPkt_Type;
249
250 typedef struct
251 {
252 mDNSAddr addr;
253 unsigned long pkts[HostPkt_NumTypes];
254 unsigned long totalops;
255 unsigned long stat[OP_NumTypes];
256 domainname hostname;
257 domainname revname;
258 UTF8str255 HIHardware;
259 UTF8str255 HISoftware;
260 mDNSu32 NumQueries;
261 mDNSs32 LastQuery;
262 } HostEntry;
263
264 #define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
265
266 typedef struct
267 {
268 long num;
269 long max;
270 HostEntry *hosts;
271 } HostList;
272
273 static HostList IPv4HostList = { 0, 0, 0 };
274 static HostList IPv6HostList = { 0, 0, 0 };
275
276 mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList *list)
277 {
278 long i;
279
280 for (i = 0; i < list->num; i++)
281 {
282 HostEntry *entry = list->hosts + i;
283 if (mDNSSameAddress(addr, &entry->addr))
284 return entry;
285 }
286
287 return NULL;
288 }
289
290 mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList *list)
291 {
292 int i;
293 HostEntry *entry;
294 if (list->num >= list->max)
295 {
296 long newMax = list->max + 64;
297 HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
298 if (newHosts == NULL)
299 return NULL;
300 list->max = newMax;
301 list->hosts = newHosts;
302 }
303
304 entry = list->hosts + list->num++;
305
306 entry->addr = *addr;
307 for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
308 entry->totalops = 0;
309 for (i=0; i<OP_NumTypes; i++) entry->stat[i] = 0;
310 entry->hostname.c[0] = 0;
311 entry->revname.c[0] = 0;
312 entry->HIHardware.c[0] = 0;
313 entry->HISoftware.c[0] = 0;
314 entry->NumQueries = 0;
315
316 if (entry->addr.type == mDNSAddrType_IPv4)
317 {
318 mDNSv4Addr ip = entry->addr.ip.v4;
319 char buffer[32];
320 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
321 mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]);
322 MakeDomainNameFromDNSNameString(&entry->revname, buffer);
323 }
324
325 return(entry);
326 }
327
328 mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t, mDNSOpaque16 id)
329 {
330 if (ExactlyOneFilter) return(NULL);
331 else
332 {
333 HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
334 HostEntry *entry = FindHost(addr, list);
335 if (!entry) entry = AddHost(addr, list);
336 if (!entry) return(NULL);
337 // Don't count our own interrogation packets
338 if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++;
339 return(entry);
340 }
341 }
342
343 mDNSlocal void RecordHostInfo(HostEntry *entry, const ResourceRecord *const pktrr)
344 {
345 if (!entry->hostname.c[0])
346 {
347 if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA)
348 {
349 // Should really check that the rdata in the address record matches the source address of this packet
350 entry->NumQueries = 0;
351 AssignDomainName(&entry->hostname, pktrr->name);
352 }
353
354 if (pktrr->rrtype == kDNSType_PTR)
355 if (SameDomainName(&entry->revname, pktrr->name))
356 {
357 entry->NumQueries = 0;
358 AssignDomainName(&entry->hostname, &pktrr->rdata->u.name);
359 }
360 }
361 else if (pktrr->rrtype == kDNSType_HINFO)
362 {
363 RDataBody *rd = &pktrr->rdata->u;
364 mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
365 mDNSu8 *hw = rd->txt.c;
366 mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0];
367 if (sw + 1 + sw[0] <= rdend)
368 {
369 AssignDomainName(&entry->hostname, pktrr->name);
370 mDNSPlatformMemCopy(entry->HIHardware.c, hw, 1 + (mDNSu32)hw[0]);
371 mDNSPlatformMemCopy(entry->HISoftware.c, sw, 1 + (mDNSu32)sw[0]);
372 }
373 }
374 }
375
376 mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID)
377 {
378 const mDNSOpaque16 id = { { 0xFF, 0xFF } };
379 DNSMessage query;
380 mDNSu8 *qptr = query.data;
381 const mDNSu8 *const limit = query.data + sizeof(query.data);
382 const mDNSAddr *target = &entry->addr;
383 InitializeDNSMessage(&query.h, id, QueryFlags);
384 qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN);
385 entry->LastQuery = m->timenow;
386 entry->NumQueries++;
387
388 // Note: When there are multiple mDNSResponder agents running on a single machine
389 // (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder)
390 // it is possible that unicast queries may not go to the primary system responder.
391 // We try the first query using unicast, but if that doesn't work we try again via multicast.
392 if (entry->NumQueries > 2)
393 {
394 target = &AllDNSLinkGroup_v4;
395 }
396 else
397 {
398 //mprintf("%#a Q\n", target);
399 InterfaceID = mDNSInterface_Any; // Send query from our unicast reply socket
400 }
401
402 mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, mDNSNULL, target, MulticastDNSPort, mDNSNULL, mDNSNULL);
403 }
404
405 mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID)
406 {
407 // If we've done four queries without answer, give up
408 if (entry->NumQueries >= 4) return;
409
410 // If we've done a query in the last second, give the host a chance to reply before trying again
411 if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return;
412
413 // If we don't know the host name, try to find that first
414 if (!entry->hostname.c[0])
415 {
416 if (entry->revname.c[0])
417 {
418 SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID);
419 //mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries);
420 }
421 }
422 // If we have the host name but no HINFO, now ask for that
423 else if (!entry->HIHardware.c[0])
424 {
425 SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID);
426 //mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries);
427 }
428 }
429
430 mDNSlocal int CompareHosts(const void *p1, const void *p2)
431 {
432 return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
433 }
434
435 mDNSlocal void ShowSortedHostList(HostList *list, int max)
436 {
437 HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num];
438 qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts);
439 if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, " Pkts Query LegacyQ Response");
440 for (e = &list->hosts[0]; e < end; e++)
441 {
442 int len = mprintf("%#-25a", &e->addr);
443 if (len > 25) mprintf("\n%25s", "");
444 mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e->totalops,
445 e->stat[OP_probe], e->stat[OP_goodbye],
446 e->stat[OP_browseq], e->stat[OP_browsea],
447 e->stat[OP_resolveq], e->stat[OP_resolvea]);
448 mprintf(" %8lu %8lu %8lu %8lu",
449 HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]);
450 if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]);
451 mprintf("\n");
452 if (!e->HISoftware.c[0] && e->NumQueries > 2)
453 mDNSPlatformMemCopy(&e->HISoftware, "\x27*** Unknown (Jaguar, Windows, etc.) ***", 0x28);
454 if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0])
455 mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c);
456 }
457 }
458
459 //*************************************************************************************************************
460 // Receive and process packets
461
462 mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
463 {
464 int i, len;
465 const mDNSu8 *src = fqdn->c;
466 mDNSu8 *dst = srvtype->c;
467
468 len = *src;
469 if (len == 0 || len >= 0x40) return(mDNSfalse);
470 if (src[1] != '_') src += 1 + len;
471
472 len = *src;
473 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
474 for (i=0; i<=len; i++) *dst++ = *src++;
475
476 len = *src;
477 if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
478 for (i=0; i<=len; i++) *dst++ = *src++;
479
480 *dst++ = 0; // Put the null root label on the end of the service type
481
482 return(mDNStrue);
483 }
484
485 mDNSlocal void recordstat(HostEntry *entry, const domainname *fqdn, int op, mDNSu16 rrtype)
486 {
487 ActivityStat **s = &stats;
488 domainname srvtype;
489
490 if (op != OP_probe)
491 {
492 if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
493 else if (rrtype != kDNSType_PTR) return;
494 }
495
496 if (!ExtractServiceType(fqdn, &srvtype)) return;
497
498 while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
499 if (!*s)
500 {
501 int i;
502 *s = malloc(sizeof(ActivityStat));
503 if (!*s) exit(-1);
504 (*s)->next = NULL;
505 (*s)->srvtype = srvtype;
506 (*s)->printed = 0;
507 (*s)->totalops = 0;
508 for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
509 }
510
511 (*s)->totalops++;
512 (*s)->stat[op]++;
513 if (entry)
514 {
515 entry->totalops++;
516 entry->stat[op]++;
517 }
518 }
519
520 mDNSlocal void printstats(int max)
521 {
522 int i;
523 if (!stats) return;
524 for (i=0; i<max; i++)
525 {
526 int max = 0;
527 ActivityStat *s, *m = NULL;
528 for (s = stats; s; s=s->next)
529 if (!s->printed && max < s->totalops)
530 { m = s; max = s->totalops; }
531 if (!m) return;
532 m->printed = mDNStrue;
533 if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner);
534 mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.c, m->totalops, m->stat[OP_probe],
535 m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]);
536 }
537 }
538
539 mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end,
540 DNSQuestion *q, LargeCacheRecord *pkt)
541 {
542 int i;
543 for (i = 0; i < query->h.numAuthorities; i++)
544 {
545 const mDNSu8 *p2 = ptr;
546 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt);
547 if (!ptr) break;
548 if (ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
549 }
550 return(mDNSNULL);
551 }
552
553 mDNSlocal void DisplayPacketHeader(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
554 {
555 const char *const ptype = (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "-R- " :
556 (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
557
558 struct timeval tv;
559 struct tm tm;
560 const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID);
561 char if_name[IFNAMSIZ]; // Older Linux distributions don't define IF_NAMESIZE
562 if_indextoname(index, if_name);
563 gettimeofday(&tv, NULL);
564 localtime_r((time_t*)&tv.tv_sec, &tm);
565 mprintf("\n%d:%02d:%02d.%06d Interface %d/%s\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec, index, if_name);
566
567 mprintf("%#-16a %s Q:%3d Ans:%3d Auth:%3d Add:%3d Size:%5d bytes",
568 srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg);
569
570 if (msg->h.id.NotAnInteger) mprintf(" ID:%u", mDNSVal16(msg->h.id));
571
572 if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf(" To: %#a", dstaddr);
573
574 if (msg->h.flags.b[0] & kDNSFlag0_TC)
575 {
576 if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf(" Truncated");
577 else mprintf(" Truncated (KA list continues in next packet)");
578 }
579 mprintf("\n");
580 }
581
582 mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
583 {
584 static const char hexchars[16] = "0123456789ABCDEF";
585 #define MaxWidth 132
586 char buffer[MaxWidth+8];
587 char *p = buffer;
588
589 RDataBody *rd = &pktrr->rdata->u;
590 mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
591 int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name->c);
592
593 switch(pktrr->rrtype)
594 {
595 case kDNSType_A: n += mprintf("%.4a", &rd->ipv4); break;
596 case kDNSType_PTR: n += mprintf("%##.*s", MaxWidth - n, rd->name.c); break;
597 case kDNSType_HINFO:// same as kDNSType_TXT below
598 case kDNSType_TXT: {
599 mDNSu8 *t = rd->txt.c;
600 while (t < rdend && t[0] && p < buffer+MaxWidth)
601 {
602 int i;
603 for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
604 {
605 if (t[i] == '\\') *p++ = '\\';
606 if (t[i] >= ' ') *p++ = t[i];
607 else
608 {
609 *p++ = '\\';
610 *p++ = '0';
611 *p++ = 'x';
612 *p++ = hexchars[t[i] >> 4];
613 *p++ = hexchars[t[i] & 0xF];
614 }
615 }
616 t += 1+t[0];
617 if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
618 }
619 *p++ = 0;
620 n += mprintf("%.*s", MaxWidth - n, buffer);
621 } break;
622 case kDNSType_AAAA: n += mprintf("%.16a", &rd->ipv6); break;
623 case kDNSType_SRV: n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break;
624 case kDNSType_NSEC: {
625 int i;
626 for (i=0; i<255; i++)
627 if (rd->nsec.bitmap[i>>3] & (128 >> (i&7)))
628 n += mprintf("%s ", DNSTypeName(i));
629 } break;
630 default: {
631 mDNSu8 *s = rd->data;
632 while (s < rdend && p < buffer+MaxWidth)
633 {
634 if (*s == '\\') *p++ = '\\';
635 if (*s >= ' ') *p++ = *s;
636 else
637 {
638 *p++ = '\\';
639 *p++ = '0';
640 *p++ = 'x';
641 *p++ = hexchars[*s >> 4];
642 *p++ = hexchars[*s & 0xF];
643 }
644 s++;
645 }
646 *p++ = 0;
647 n += mprintf("%.*s", MaxWidth - n, buffer);
648 } break;
649 }
650
651 mprintf("\n");
652 }
653
654 mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
655 {
656 while (ptr < end)
657 {
658 int i;
659 for (i=0; i<16; i++)
660 if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
661 else mprintf(" ");
662 for (i=0; i<16; i++)
663 if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
664 ptr += 16;
665 mprintf("\n");
666 }
667 }
668
669 mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
670 {
671 mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
672 HexDump(ptr, end);
673 }
674
675 mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
676 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
677 {
678 int i;
679 const mDNSu8 *ptr = msg->data;
680 const mDNSu8 *auth = LocateAuthorities(msg, end);
681 mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger);
682 HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id);
683 LargeCacheRecord pkt;
684
685 DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
686 if (msg->h.id.NotAnInteger != 0xFFFF)
687 {
688 if (MQ) NumPktQ++; else NumPktL++;
689 }
690
691 for (i=0; i<msg->h.numQuestions; i++)
692 {
693 DNSQuestion q;
694 mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q);
695 mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse;
696 q.qclass &= ~kDNSQClass_UnicastResponse;
697 if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; }
698 ptr = p2;
699 p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
700 if (p2)
701 {
702 NumProbes++;
703 DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec);
704 recordstat(entry, &q.qname, OP_probe, q.qtype);
705 p2 = (mDNSu8 *)skipDomainName(msg, p2, end);
706 // Having displayed this update record, clear type and class so we don't display the same one again.
707 p2[0] = p2[1] = p2[2] = p2[3] = 0;
708 }
709 else
710 {
711 const char *ptype = ucbit ? "(QU)" : "(QM)";
712 if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++;
713 else { NumLegacy++; ptype = "(LQ)"; }
714 mprintf("%#-16a %-5s %-5s %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c);
715 if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype);
716 }
717 }
718
719 for (i=0; i<msg->h.numAnswers; i++)
720 {
721 const mDNSu8 *ep = ptr;
722 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
723 if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; }
724 DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec);
725
726 // In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
727 // the same as a single query, to more accurately reflect the burden on the network
728 // (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
729 if (msg->h.numQuestions == 0 && i == 0)
730 recordstat(entry, pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype);
731 }
732
733 for (i=0; i<msg->h.numAuthorities; i++)
734 {
735 const mDNSu8 *ep = ptr;
736 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
737 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
738 // After we display an Update record with its matching question (above) we zero out its type and class
739 // If any remain that haven't been zero'd out, display them here
740 if (pkt.r.resrec.rrtype || pkt.r.resrec.rrclass) DisplayResourceRecord(srcaddr, "(AU)", &pkt.r.resrec);
741 }
742
743 for (i=0; i<msg->h.numAdditionals; i++)
744 {
745 const mDNSu8 *ep = ptr;
746 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
747 if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
748 DisplayResourceRecord(srcaddr, pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : "(AD)", &pkt.r.resrec);
749 }
750
751 if (entry) AnalyseHost(m, entry, InterfaceID);
752 }
753
754 mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end,
755 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
756 {
757 int i;
758 const mDNSu8 *ptr = msg->data;
759 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
760 LargeCacheRecord pkt;
761
762 DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
763 if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++;
764
765 for (i=0; i<msg->h.numQuestions; i++)
766 {
767 DNSQuestion q;
768 const mDNSu8 *ep = ptr;
769 ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
770 if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
771 if (mDNSAddrIsDNSMulticast(dstaddr))
772 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
773 else
774 mprintf("%#-16a (Q) %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
775 }
776
777 for (i=0; i<msg->h.numAnswers; i++)
778 {
779 const mDNSu8 *ep = ptr;
780 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
781 if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; }
782 if (pkt.r.resrec.rroriginalttl)
783 {
784 NumAnswers++;
785 DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec);
786 if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype);
787 if (entry) RecordHostInfo(entry, &pkt.r.resrec);
788 }
789 else
790 {
791 NumGoodbyes++;
792 DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
793 recordstat(entry, pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
794 }
795 }
796
797 for (i=0; i<msg->h.numAuthorities; i++)
798 {
799 const mDNSu8 *ep = ptr;
800 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
801 if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
802 mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
803 srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c);
804 }
805
806 for (i=0; i<msg->h.numAdditionals; i++)
807 {
808 const mDNSu8 *ep = ptr;
809 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
810 if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
811 NumAdditionals++;
812 DisplayResourceRecord(srcaddr,
813 pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)",
814 &pkt.r.resrec);
815 if (entry) RecordHostInfo(entry, &pkt.r.resrec);
816 }
817
818 if (entry) AnalyseHost(m, entry, InterfaceID);
819 }
820
821 mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID)
822 {
823 int i;
824 const mDNSu8 *ptr = LocateAnswers(msg, end);
825 HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
826 //mprintf("%#a R\n", srcaddr);
827
828 for (i=0; i<msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++)
829 {
830 LargeCacheRecord pkt;
831 ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
832 if (pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec);
833 }
834 }
835
836 mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
837 {
838 FilterList *f;
839 if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
840 for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
841 return(mDNSfalse);
842 }
843
844 mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
845 const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID)
846 {
847 const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
848 const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
849 const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
850 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
851 int goodinterface = (FilterInterface == 0);
852
853 (void)dstaddr; // Unused
854 (void)dstport; // Unused
855
856 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
857 msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
858 msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
859 msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
860 msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
861
862 // For now we're only interested in monitoring IPv4 traffic.
863 // All IPv6 packets should just be duplicates of the v4 packets.
864 if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID));
865 if (goodinterface && AddressMatchesFilterList(srcaddr))
866 {
867 mDNS_Lock(m);
868 if (!mDNSAddrIsDNSMulticast(dstaddr))
869 {
870 if (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr);
871 else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr, InterfaceID);
872 }
873 else
874 {
875 if (QR_OP == StdQ) DisplayQuery (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
876 else if (QR_OP == StdR) DisplayResponse (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
877 else
878 {
879 debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
880 GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id);
881 NumPktB++;
882 }
883 }
884 mDNS_Unlock(m);
885 }
886 }
887
888 mDNSlocal mStatus mDNSNetMonitor(void)
889 {
890 struct tm tm;
891 int h, m, s, mul, div, TotPkt;
892 #if !defined(WIN32)
893 sigset_t signals;
894 #endif
895
896 mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage,
897 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
898 mDNS_Init_DontAdvertiseLocalAddresses,
899 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
900 if (status) return(status);
901
902 gettimeofday(&tv_start, NULL);
903
904 #if defined( WIN32 )
905 RunDirect( 0, NULL );
906 #else
907 mDNSPosixListenForSignalInEventLoop(SIGINT);
908 mDNSPosixListenForSignalInEventLoop(SIGTERM);
909
910 do
911 {
912 struct timeval timeout = { 0x3FFFFFFF, 0 }; // wait until SIGINT or SIGTERM
913 mDNSBool gotSomething;
914 mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
915 }
916 while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
917 #endif
918
919 // Now display final summary
920 TotPkt = NumPktQ + NumPktL + NumPktR;
921 gettimeofday(&tv_end, NULL);
922 tv_interval = tv_end;
923 if (tv_start.tv_usec > tv_interval.tv_usec)
924 { tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; }
925 tv_interval.tv_sec -= tv_start.tv_sec;
926 tv_interval.tv_usec -= tv_start.tv_usec;
927 h = (tv_interval.tv_sec / 3600);
928 m = (tv_interval.tv_sec % 3600) / 60;
929 s = (tv_interval.tv_sec % 60);
930 if (tv_interval.tv_sec > 10)
931 {
932 mul = 60;
933 div = tv_interval.tv_sec;
934 }
935 else
936 {
937 mul = 60000;
938 div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
939 if (div == 0) div=1;
940 }
941
942 mprintf("\n\n");
943 localtime_r((time_t*)&tv_start.tv_sec, &tm);
944 mprintf("Started %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec);
945 localtime_r((time_t*)&tv_end.tv_sec, &tm);
946 mprintf("End %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec);
947 mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec);
948 if (!Filters)
949 {
950 mprintf("Unique source addresses seen on network:");
951 if (IPv4HostList.num) mprintf(" %ld (IPv4)", IPv4HostList.num);
952 if (IPv6HostList.num) mprintf(" %ld (IPv6)", IPv6HostList.num);
953 if (!IPv4HostList.num && !IPv6HostList.num) mprintf(" None");
954 mprintf("\n");
955 }
956 mprintf("\n");
957 mprintf("Modern Query Packets: %7d (avg%5d/min)\n", NumPktQ, NumPktQ * mul / div);
958 mprintf("Legacy Query Packets: %7d (avg%5d/min)\n", NumPktL, NumPktL * mul / div);
959 mprintf("Multicast Response Packets: %7d (avg%5d/min)\n", NumPktR, NumPktR * mul / div);
960 mprintf("Total Multicast Packets: %7d (avg%5d/min)\n", TotPkt, TotPkt * mul / div);
961 mprintf("\n");
962 mprintf("Total New Service Probes: %7d (avg%5d/min)\n", NumProbes, NumProbes * mul / div);
963 mprintf("Total Goodbye Announcements: %7d (avg%5d/min)\n", NumGoodbyes, NumGoodbyes * mul / div);
964 mprintf("Total Query Questions: %7d (avg%5d/min)\n", NumQuestions, NumQuestions * mul / div);
965 mprintf("Total Queries from Legacy Clients:%7d (avg%5d/min)\n", NumLegacy, NumLegacy * mul / div);
966 mprintf("Total Answers/Announcements: %7d (avg%5d/min)\n", NumAnswers, NumAnswers * mul / div);
967 mprintf("Total Additional Records: %7d (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div);
968 mprintf("\n");
969 printstats(kReportTopServices);
970
971 if (!ExactlyOneFilter)
972 {
973 ShowSortedHostList(&IPv4HostList, kReportTopHosts);
974 ShowSortedHostList(&IPv6HostList, kReportTopHosts);
975 }
976
977 mDNS_Close(&mDNSStorage);
978 return(0);
979 }
980
981 mDNSexport int main(int argc, char **argv)
982 {
983 const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
984 int i;
985 mStatus status;
986
987 setlinebuf(stdout); // Want to see lines as they appear, not block buffered
988
989 for (i=1; i<argc; i++)
990 {
991 if (i+1 < argc && !strcmp(argv[i], "-i") && atoi(argv[i+1]))
992 {
993 FilterInterface = atoi(argv[i+1]);
994 i += 2;
995 printf("Monitoring interface %d\n", FilterInterface);
996 }
997 else
998 {
999 struct in_addr s4;
1000 struct in6_addr s6;
1001 FilterList *f;
1002 mDNSAddr a;
1003 a.type = mDNSAddrType_IPv4;
1004
1005 if (inet_pton(AF_INET, argv[i], &s4) == 1)
1006 a.ip.v4.NotAnInteger = s4.s_addr;
1007 else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
1008 {
1009 a.type = mDNSAddrType_IPv6;
1010 mDNSPlatformMemCopy(&a.ip.v6, &s6, sizeof(a.ip.v6));
1011 }
1012 else
1013 {
1014 struct hostent *h = gethostbyname(argv[i]);
1015 if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
1016 else goto usage;
1017 }
1018
1019 f = malloc(sizeof(*f));
1020 f->FilterAddr = a;
1021 f->next = Filters;
1022 Filters = f;
1023 }
1024 }
1025
1026 status = mDNSNetMonitor();
1027 if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %d\n", progname, (int)status); return(status); }
1028 return(0);
1029
1030 usage:
1031 fprintf(stderr, "\nmDNS traffic monitor\n");
1032 fprintf(stderr, "Usage: %s (<host>)\n", progname);
1033 fprintf(stderr, "Optional <host> parameter displays only packets from that host\n");
1034
1035 fprintf(stderr, "\nPer-packet header output:\n");
1036 fprintf(stderr, "-Q- Multicast Query from mDNS client that accepts multicast responses\n");
1037 fprintf(stderr, "-R- Multicast Response packet containing answers/announcements\n");
1038 fprintf(stderr, "-LQ- Multicast Query from legacy client that does *not* listen for multicast responses\n");
1039 fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
1040
1041 fprintf(stderr, "\nPer-record display:\n");
1042 fprintf(stderr, "(PM) Probe Question (new service starting), requesting multicast response\n");
1043 fprintf(stderr, "(PU) Probe Question (new service starting), requesting unicast response\n");
1044 fprintf(stderr, "(DE) Deletion/Goodbye (service going away)\n");
1045 fprintf(stderr, "(LQ) Legacy Query Question\n");
1046 fprintf(stderr, "(QM) Query Question, requesting multicast response\n");
1047 fprintf(stderr, "(QU) Query Question, requesting unicast response\n");
1048 fprintf(stderr, "(KA) Known Answer (information querier already knows)\n");
1049 fprintf(stderr, "(AN) Unique Answer to question (or periodic announcment) (entire RR Set)\n");
1050 fprintf(stderr, "(AN+) Answer to question (or periodic announcment) (add to existing RR Set members)\n");
1051 fprintf(stderr, "(AD) Unique Additional Record Set (entire RR Set)\n");
1052 fprintf(stderr, "(AD+) Additional records (add to existing RR Set members)\n");
1053
1054 fprintf(stderr, "\nFinal summary, sorted by service type:\n");
1055 fprintf(stderr, "Probe Probes for this service type starting up\n");
1056 fprintf(stderr, "Goodbye Goodbye (deletion) packets for this service type shutting down\n");
1057 fprintf(stderr, "BrowseQ Browse questions from clients browsing to find a list of instances of this service\n");
1058 fprintf(stderr, "BrowseA Browse answers/announcments advertising instances of this service\n");
1059 fprintf(stderr, "ResolveQ Resolve questions from clients actively connecting to an instance of this service\n");
1060 fprintf(stderr, "ResolveA Resolve answers/announcments giving connection information for an instance of this service\n");
1061 fprintf(stderr, "\n");
1062 return(-1);
1063 }