96d2a4959bd21e477ea9d3e6f9bc02ce0514bf80
[oweals/gnunet.git] / src / dns / gnunet-dns-monitor.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file src/dns/gnunet-dns-monitor.c
23  * @brief Tool to monitor DNS queries
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dns_service-new.h"
30 #include "gnunet_dnsparser_lib.h"
31
32 /**
33  * Handle to transport service.
34  */
35 static struct GNUNET_DNS_Handle *handle;
36
37 /**
38  * Option -s.
39  */
40 static int benchmark_send;
41
42 /**
43  * Global return value (0 success).
44  */
45 static int ret;
46
47 /**
48  * Selected level of verbosity.
49  */
50 static int verbosity;
51
52
53 /**
54  * Convert numeric DNS record type to a string.
55  *
56  * @param type type to convert
57  * @return type as string, only valid until the next call to this function
58  */
59 static const char *
60 get_type (uint16_t type)
61 {
62   static char buf[6];
63   switch (type)
64   {
65   case GNUNET_DNSPARSER_TYPE_A: return "A";
66   case GNUNET_DNSPARSER_TYPE_NS: return "NS";
67   case GNUNET_DNSPARSER_TYPE_CNAME: return "CNAME";
68   case GNUNET_DNSPARSER_TYPE_SOA: return "SOA";
69   case GNUNET_DNSPARSER_TYPE_PTR: return "PTR";
70   case GNUNET_DNSPARSER_TYPE_MX: return "MX";
71   case GNUNET_DNSPARSER_TYPE_TXT: return "TXT";
72   case GNUNET_DNSPARSER_TYPE_AAAA: return "AAAA";
73   }
74   GNUNET_snprintf (buf, sizeof (buf), "%u", (unsigned int) type);
75   return buf;
76 }
77
78
79 /**
80  * Convert numeric DNS record class to a string.
81  *
82  * @param class class to convert
83  * @return class as string, only valid until the next call to this function
84  */
85 static const char *
86 get_class (uint16_t class)
87 {
88   static char buf[6];
89   switch (class)
90   {
91   case GNUNET_DNSPARSER_CLASS_INTERNET: return "IN";
92   case GNUNET_DNSPARSER_CLASS_CHAOS: return "CHAOS";
93   case GNUNET_DNSPARSER_CLASS_HESIOD: return "HESIOD";
94   }
95   GNUNET_snprintf (buf, sizeof (buf), "%u", (unsigned int) class);
96   return buf;
97 }
98
99
100 /**
101  * Output the given DNS query to stdout.
102  *
103  * @param query query to display.
104  */
105 static void
106 display_query (const struct GNUNET_DNSPARSER_Query *query)
107 {
108   fprintf (stdout,
109            "\t\t%s %s: %s\n",
110            get_class (query->class),
111            get_type (query->type),
112            query->name);
113 }
114
115
116 /**
117  * Output the given DNS query to stdout.
118  *
119  * @param query query to display.
120  */
121 static void
122 display_record (const struct GNUNET_DNSPARSER_Record *record)
123 {
124   const char *format;
125   char buf[INET6_ADDRSTRLEN];
126   char *tmp;
127   
128   tmp = NULL;
129   switch (record->type)
130   {
131   case GNUNET_DNSPARSER_TYPE_A:
132     if (record->data_len != sizeof (struct in_addr))
133       format = "<invalid>";
134     else
135       format = inet_ntop (AF_INET, record->data.raw, buf, sizeof (buf));
136     break;
137   case GNUNET_DNSPARSER_TYPE_AAAA:
138     if (record->data_len != sizeof (struct in6_addr))
139       format = "<invalid>";
140     else
141       format = inet_ntop (AF_INET6, record->data.raw, buf, sizeof (buf));
142     break;
143   case GNUNET_DNSPARSER_TYPE_NS:
144   case GNUNET_DNSPARSER_TYPE_CNAME:
145   case GNUNET_DNSPARSER_TYPE_PTR:
146     format = record->data.hostname;
147     break;
148   case GNUNET_DNSPARSER_TYPE_SOA:
149     if (record->data.soa == NULL)
150       format = "<invalid>";
151     else
152     {
153       GNUNET_asprintf (&tmp,
154                        "origin: %s, mail: %s, serial = %u, refresh = %u s, retry = %u s, expire = %u s, minimum = %u s",
155                        record->data.soa->mname,
156                        record->data.soa->rname,
157                        (unsigned int) record->data.soa->serial,
158                        (unsigned int) record->data.soa->refresh,
159                        (unsigned int) record->data.soa->retry,
160                        (unsigned int) record->data.soa->expire,
161                        (unsigned int) record->data.soa->minimum_ttl);          
162       format = tmp;
163     }
164     break;
165   case GNUNET_DNSPARSER_TYPE_MX:
166     if (record->data.mx == NULL)
167       format = "<invalid>";
168     else
169     {
170       GNUNET_asprintf (&tmp,
171                        "%u: %s",
172                        record->data.mx->preference,
173                        record->data.mx->mxhost);
174       format = tmp;
175     }
176     break;
177   case GNUNET_DNSPARSER_TYPE_TXT:
178     GNUNET_asprintf (&tmp,
179                      "%.*s",
180                      (unsigned int) record->data_len,
181                      record->data.raw);
182     format = tmp;
183     break;
184   default:
185     format = "<payload>";
186     break;
187   }
188   fprintf (stdout,
189            "\t\t%s %s: %s = %s (%u bytes, %u s)\n",
190            get_class (record->class),
191            get_type (record->type),
192            record->name,
193            format,
194            (unsigned int) record->data_len,
195            (unsigned int) (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value / 1000));
196   GNUNET_free_non_null (tmp);
197 }
198
199
200 /**
201  * Signature of a function that is called whenever the DNS service
202  * encounters a DNS request and needs to do something with it.  The
203  * function has then the chance to generate or modify the response by
204  * calling one of the three "GNUNET_DNS_request_*" continuations.
205  *
206  * When a request is intercepted, this function is called first to
207  * give the client a chance to do the complete address resolution;
208  * "rdata" will be NULL for this first call for a DNS request, unless
209  * some other client has already filled in a response.
210  *
211  * If multiple clients exist, all of them are called before the global
212  * DNS.  The global DNS is only called if all of the clients'
213  * functions call GNUNET_DNS_request_forward.  Functions that call
214  * GNUNET_DNS_request_forward will be called again before a final
215  * response is returned to the application.  If any of the clients'
216  * functions call GNUNET_DNS_request_drop, the response is dropped.
217  *
218  * @param cls closure
219  * @param rh request handle to user for reply
220  * @param request_length number of bytes in request
221  * @param request udp payload of the DNS request
222  */
223 static void 
224 display_request (void *cls,
225                  struct GNUNET_DNS_RequestHandle *rh,
226                  size_t request_length,
227                  const char *request)
228 {
229   static const char *return_codes[] =
230     {
231       "No error", "Format error", "Server failure", "Name error",
232       "Not implemented", "Refused", "YXDomain", "YXRRset",
233       "NXRRset", "NOT AUTH", "NOT ZONE", "<invalid>",
234       "<invalid>", "<invalid>", "<invalid>", "<invalid>"
235     };
236   static const char *op_codes[] =
237     {
238       "Query", "Inverse query", "Status", "<invalid>",
239       "<invalid>", "<invalid>", "<invalid>", "<invalid>",
240       "<invalid>", "<invalid>", "<invalid>", "<invalid>",
241       "<invalid>", "<invalid>", "<invalid>", "<invalid>"
242     };
243   struct GNUNET_DNSPARSER_Packet *p;
244   unsigned int i;
245
246   p = GNUNET_DNSPARSER_parse (request, request_length);
247   if (NULL == p)
248   {
249     fprintf (stderr, "Received malformed DNS packet!\n");
250     // FIXME: drop instead?
251     GNUNET_DNS_request_forward (rh);
252     return;
253   }
254   fprintf (stdout,
255            "%s with ID: %5u Flags: %s%s%s%s%s%s, Return Code: %s, Opcode: %s\n",
256            p->flags.query_or_response ? "Response" : "Query",
257            p->id,
258            p->flags.recursion_desired ? "RD " : "",
259            p->flags.message_truncated ? "MT " : "",
260            p->flags.authoritative_answer ? "AA " : "",
261            p->flags.checking_disabled ? "CD " : "",
262            p->flags.authenticated_data ? "AD " : "",
263            p->flags.recursion_available ? "RA " : "",
264            return_codes[p->flags.return_code & 15],
265            op_codes[p->flags.opcode & 15]);  
266   if (p->num_queries > 0)
267     fprintf (stdout,
268              "\tQueries:\n");
269   for (i=0;i<p->num_queries;i++)
270     display_query (&p->queries[i]);
271   
272   if (p->num_answers > 0)
273     fprintf (stdout,
274              "\tAnswers:\n");
275   for (i=0;i<p->num_answers;i++)
276     display_record (&p->answers[i]);
277   fprintf (stdout, "\n");
278   GNUNET_DNSPARSER_free_packet (p);
279   GNUNET_DNS_request_forward (rh);
280 }
281
282
283 /**
284  * Shutdown.
285  */
286 static void
287 do_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
288 {
289   if (NULL != handle)
290   {
291     GNUNET_DNS_disconnect (handle);
292     handle = NULL;
293   }
294 }
295
296
297 /**
298  * Main function that will be run by the scheduler.
299  *
300  * @param cls closure
301  * @param args remaining command-line arguments
302  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
303  * @param cfg configuration
304  */
305 static void
306 run (void *cls, char *const *args, const char *cfgfile,
307      const struct GNUNET_CONFIGURATION_Handle *cfg)
308 {
309   handle =
310     GNUNET_DNS_connect (cfg, 
311                         GNUNET_DNS_FLAG_REQUEST_MONITOR | GNUNET_DNS_FLAG_RESPONSE_MONITOR,
312                         &display_request,
313                         NULL);
314   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
315                                 &do_disconnect, NULL);
316 }
317
318
319 int
320 main (int argc, char *const *argv)
321 {
322   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
323     {'s', "testoption", NULL,
324      gettext_noop ("not useful"),
325      0, &GNUNET_GETOPT_set_one, &benchmark_send},
326     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
327     GNUNET_GETOPT_OPTION_END
328   };
329   return (GNUNET_OK ==
330           GNUNET_PROGRAM_run (argc, argv, "gnunet-dns-monitor",
331                               gettext_noop
332                               ("Monitor DNS queries."), options,
333                               &run, NULL)) ? ret : 1;
334 }
335
336
337 /* end of gnunet-dns-monitor.c */