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