2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @file src/dns/gnunet-dns-monitor.c
21 * @brief Tool to monitor DNS queries
22 * @author Christian Grothoff
26 #include "gnunet_util_lib.h"
27 #include "gnunet_dns_service.h"
28 #include "gnunet_dnsparser_lib.h"
31 * Handle to transport service.
33 static struct GNUNET_DNS_Handle *handle;
38 static int inbound_only;
43 static int outbound_only;
46 * Global return value (0 success).
51 * Selected level of verbosity.
53 static unsigned int verbosity;
57 * Convert numeric DNS record type to a string.
59 * @param type type to convert
60 * @return type as string, only valid until the next call to this function
63 get_type (uint16_t type)
68 case GNUNET_DNSPARSER_TYPE_A: return "A";
69 case GNUNET_DNSPARSER_TYPE_NS: return "NS";
70 case GNUNET_DNSPARSER_TYPE_CNAME: return "CNAME";
71 case GNUNET_DNSPARSER_TYPE_SOA: return "SOA";
72 case GNUNET_DNSPARSER_TYPE_PTR: return "PTR";
73 case GNUNET_DNSPARSER_TYPE_MX: return "MX";
74 case GNUNET_DNSPARSER_TYPE_TXT: return "TXT";
75 case GNUNET_DNSPARSER_TYPE_AAAA: return "AAAA";
76 case GNUNET_DNSPARSER_TYPE_SRV: return "SRV";
78 GNUNET_snprintf (buf, sizeof (buf), "%u", (unsigned int) type);
84 * Convert numeric DNS record class to a string.
86 * @param class class to convert
87 * @return class as string, only valid until the next call to this function
90 get_class (uint16_t class)
95 case GNUNET_TUN_DNS_CLASS_INTERNET: return "IN";
96 case GNUNET_TUN_DNS_CLASS_CHAOS: return "CHAOS";
97 case GNUNET_TUN_DNS_CLASS_HESIOD: return "HESIOD";
99 GNUNET_snprintf (buf, sizeof (buf), "%u", (unsigned int) class);
105 * Output the given DNS query to stdout.
107 * @param query query to display.
110 display_query (const struct GNUNET_DNSPARSER_Query *query)
114 get_class (query->dns_traffic_class),
115 get_type (query->type),
121 * Output the given DNS record to stdout.
123 * @param record record to display.
126 display_record (const struct GNUNET_DNSPARSER_Record *record)
129 char buf[INET6_ADDRSTRLEN];
133 switch (record->type)
135 case GNUNET_DNSPARSER_TYPE_A:
136 if (record->data.raw.data_len != sizeof (struct in_addr))
137 format = "<invalid>";
139 format = inet_ntop (AF_INET, record->data.raw.data, buf, sizeof (buf));
141 case GNUNET_DNSPARSER_TYPE_AAAA:
142 if (record->data.raw.data_len != sizeof (struct in6_addr))
143 format = "<invalid>";
145 format = inet_ntop (AF_INET6, record->data.raw.data, buf, sizeof (buf));
147 case GNUNET_DNSPARSER_TYPE_NS:
148 case GNUNET_DNSPARSER_TYPE_CNAME:
149 case GNUNET_DNSPARSER_TYPE_PTR:
150 format = record->data.hostname;
152 case GNUNET_DNSPARSER_TYPE_SOA:
153 if (NULL == record->data.soa)
154 format = "<invalid>";
157 GNUNET_asprintf (&tmp,
158 "origin: %s, mail: %s, serial = %u, refresh = %u s, retry = %u s, expire = %u s, minimum = %u s",
159 record->data.soa->mname,
160 record->data.soa->rname,
161 (unsigned int) record->data.soa->serial,
162 (unsigned int) record->data.soa->refresh,
163 (unsigned int) record->data.soa->retry,
164 (unsigned int) record->data.soa->expire,
165 (unsigned int) record->data.soa->minimum_ttl);
169 case GNUNET_DNSPARSER_TYPE_MX:
170 if (record->data.mx == NULL)
171 format = "<invalid>";
174 GNUNET_asprintf (&tmp,
176 record->data.mx->preference,
177 record->data.mx->mxhost);
181 case GNUNET_DNSPARSER_TYPE_SRV:
182 if (NULL == record->data.srv)
183 format = "<invalid>";
186 GNUNET_asprintf (&tmp,
187 "priority %u, weight = %s, port = %u, target = %s",
188 (unsigned int) record->data.srv->priority,
189 (unsigned int) record->data.srv->weight,
190 (unsigned int) record->data.srv->port,
191 record->data.srv->target);
195 case GNUNET_DNSPARSER_TYPE_TXT:
196 GNUNET_asprintf (&tmp,
198 (unsigned int) record->data.raw.data_len,
199 record->data.raw.data);
203 format = "<payload>";
207 "\t\t%s %s: %s = %s (%u s)\n",
208 get_class (record->dns_traffic_class),
209 get_type (record->type),
212 (unsigned int) (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL));
213 GNUNET_free_non_null (tmp);
218 * Signature of a function that is called whenever the DNS service
219 * encounters a DNS request and needs to do something with it. The
220 * function has then the chance to generate or modify the response by
221 * calling one of the three "GNUNET_DNS_request_*" continuations.
223 * When a request is intercepted, this function is called first to
224 * give the client a chance to do the complete address resolution;
225 * "rdata" will be NULL for this first call for a DNS request, unless
226 * some other client has already filled in a response.
228 * If multiple clients exist, all of them are called before the global
229 * DNS. The global DNS is only called if all of the clients'
230 * functions call GNUNET_DNS_request_forward. Functions that call
231 * GNUNET_DNS_request_forward will be called again before a final
232 * response is returned to the application. If any of the clients'
233 * functions call GNUNET_DNS_request_drop, the response is dropped.
236 * @param rh request handle to user for reply
237 * @param request_length number of bytes in request
238 * @param request udp payload of the DNS request
241 display_request (void *cls,
242 struct GNUNET_DNS_RequestHandle *rh,
243 size_t request_length,
246 static const char *return_codes[] =
248 "No error", "Format error", "Server failure", "Name error",
249 "Not implemented", "Refused", "YXDomain", "YXRRset",
250 "NXRRset", "NOT AUTH", "NOT ZONE", "<invalid>",
251 "<invalid>", "<invalid>", "<invalid>", "<invalid>"
253 static const char *op_codes[] =
255 "Query", "Inverse query", "Status", "<invalid>",
256 "<invalid>", "<invalid>", "<invalid>", "<invalid>",
257 "<invalid>", "<invalid>", "<invalid>", "<invalid>",
258 "<invalid>", "<invalid>", "<invalid>", "<invalid>"
260 struct GNUNET_DNSPARSER_Packet *p;
263 p = GNUNET_DNSPARSER_parse (request, request_length);
266 fprintf (stderr, "Received malformed DNS packet!\n");
267 // FIXME: drop instead?
268 GNUNET_DNS_request_forward (rh);
272 "%s with ID: %5u Flags: %s%s%s%s%s%s, Return Code: %s, Opcode: %s\n",
273 p->flags.query_or_response ? "Response" : "Query",
275 p->flags.recursion_desired ? "RD " : "",
276 p->flags.message_truncated ? "MT " : "",
277 p->flags.authoritative_answer ? "AA " : "",
278 p->flags.checking_disabled ? "CD " : "",
279 p->flags.authenticated_data ? "AD " : "",
280 p->flags.recursion_available ? "RA " : "",
281 return_codes[p->flags.return_code & 15],
282 op_codes[p->flags.opcode & 15]);
283 if (p->num_queries > 0)
286 for (i=0;i<p->num_queries;i++)
287 display_query (&p->queries[i]);
289 if (p->num_answers > 0)
292 for (i=0;i<p->num_answers;i++)
293 display_record (&p->answers[i]);
294 fprintf (stdout, "\n");
295 GNUNET_DNSPARSER_free_packet (p);
296 GNUNET_DNS_request_forward (rh);
304 do_disconnect (void *cls)
308 GNUNET_DNS_disconnect (handle);
315 * Main function that will be run by the scheduler.
318 * @param args remaining command-line arguments
319 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
320 * @param cfg configuration
323 run (void *cls, char *const *args, const char *cfgfile,
324 const struct GNUNET_CONFIGURATION_Handle *cfg)
326 enum GNUNET_DNS_Flags flags;
328 flags = GNUNET_DNS_FLAG_REQUEST_MONITOR | GNUNET_DNS_FLAG_RESPONSE_MONITOR;
329 if (inbound_only | outbound_only)
332 flags |= GNUNET_DNS_FLAG_REQUEST_MONITOR;
334 flags |= GNUNET_DNS_FLAG_RESPONSE_MONITOR;
336 GNUNET_DNS_connect (cfg,
340 GNUNET_SCHEDULER_add_shutdown (&do_disconnect, NULL);
345 main (int argc, char *const *argv)
347 struct GNUNET_GETOPT_CommandLineOption options[] = {
349 GNUNET_GETOPT_option_flag ('i',
351 gettext_noop ("only monitor DNS queries"),
354 GNUNET_GETOPT_option_flag ('o',
356 gettext_noop ("only monitor DNS queries"),
359 GNUNET_GETOPT_option_verbose (&verbosity),
360 GNUNET_GETOPT_OPTION_END
363 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
366 GNUNET_PROGRAM_run (argc, argv, "gnunet-dns-monitor",
368 ("Monitor DNS queries."), options,
369 &run, NULL)) ? ret : 1;
370 GNUNET_free ((void*) argv);
375 /* end of gnunet-dns-monitor.c */