c8fb646e562e150e9edc6815873feb226c34e948
[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   case GNUNET_DNSPARSER_TYPE_IXFR: return "IXFR";
74   case GNUNET_DNSPARSER_TYPE_AXFR: return "AXFR";
75   }
76   GNUNET_snprintf (buf, sizeof (buf), "%u", (unsigned int) type);
77   return buf;
78 }
79
80
81 /**
82  * Convert numeric DNS record class to a string.
83  *
84  * @param class class to convert
85  * @return class as string, only valid until the next call to this function
86  */
87 static const char *
88 get_class (uint16_t class)
89 {
90   static char buf[6];
91   switch (class)
92   {
93   case GNUNET_DNSPARSER_CLASS_INTERNET: return "IN";
94   case GNUNET_DNSPARSER_CLASS_CHAOS: return "CHAOS";
95   case GNUNET_DNSPARSER_CLASS_HESIOD: return "HESIOD";
96   }
97   GNUNET_snprintf (buf, sizeof (buf), "%u", (unsigned int) class);
98   return buf;
99 }
100
101
102 /**
103  * Output the given DNS query to stdout.
104  *
105  * @param query query to display.
106  */
107 static void
108 display_query (const struct GNUNET_DNSPARSER_Query *query)
109 {
110   fprintf (stdout,
111            "\t\t%s %s: %s\n",
112            get_class (query->class),
113            get_type (query->type),
114            query->name);
115 }
116
117
118 /**
119  * Output the given DNS query to stdout.
120  *
121  * @param query query to display.
122  */
123 static void
124 display_record (const struct GNUNET_DNSPARSER_Record *record)
125 {
126   const char *format;
127   char buf[INET6_ADDRSTRLEN];
128   char *tmp;
129   
130   tmp = NULL;
131   switch (record->type)
132   {
133   case GNUNET_DNSPARSER_TYPE_A:
134     if (record->data_len != sizeof (struct in_addr))
135       format = "<invalid>";
136     else
137       format = inet_ntop (AF_INET, record->data, buf, sizeof (buf));
138     break;
139   case GNUNET_DNSPARSER_TYPE_AAAA:
140     if (record->data_len != sizeof (struct in6_addr))
141       format = "<invalid>";
142     else
143       format = inet_ntop (AF_INET6, record->data, buf, sizeof (buf));
144     break;
145   case GNUNET_DNSPARSER_TYPE_CNAME:
146     tmp = GNUNET_strdup ("FIXME");
147   default:
148     format = "<payload>";
149     break;
150   }
151   fprintf (stdout,
152            "\t\t%s %s: %s = %s (%u bytes, %u s)\n",
153            get_class (record->class),
154            get_type (record->type),
155            record->name,
156            format,
157            (unsigned int) record->data_len,
158            (unsigned int) (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value / 1000));
159   GNUNET_free_non_null (tmp);
160 }
161
162
163 /**
164  * Signature of a function that is called whenever the DNS service
165  * encounters a DNS request and needs to do something with it.  The
166  * function has then the chance to generate or modify the response by
167  * calling one of the three "GNUNET_DNS_request_*" continuations.
168  *
169  * When a request is intercepted, this function is called first to
170  * give the client a chance to do the complete address resolution;
171  * "rdata" will be NULL for this first call for a DNS request, unless
172  * some other client has already filled in a response.
173  *
174  * If multiple clients exist, all of them are called before the global
175  * DNS.  The global DNS is only called if all of the clients'
176  * functions call GNUNET_DNS_request_forward.  Functions that call
177  * GNUNET_DNS_request_forward will be called again before a final
178  * response is returned to the application.  If any of the clients'
179  * functions call GNUNET_DNS_request_drop, the response is dropped.
180  *
181  * @param cls closure
182  * @param rh request handle to user for reply
183  * @param request_length number of bytes in request
184  * @param request udp payload of the DNS request
185  */
186 static void 
187 display_request (void *cls,
188                  struct GNUNET_DNS_RequestHandle *rh,
189                  size_t request_length,
190                  const char *request)
191 {
192   static const char *return_codes[] =
193     {
194       "No error", "Format error", "Server failure", "Name error",
195       "Not implemented", "Refused", "YXDomain", "YXRRset",
196       "NXRRset", "NOT AUTH", "NOT ZONE", "<invalid>",
197       "<invalid>", "<invalid>", "<invalid>", "<invalid>"
198     };
199   static const char *op_codes[] =
200     {
201       "Query", "Inverse query", "Status", "<invalid>",
202       "<invalid>", "<invalid>", "<invalid>", "<invalid>",
203       "<invalid>", "<invalid>", "<invalid>", "<invalid>",
204       "<invalid>", "<invalid>", "<invalid>", "<invalid>"
205     };
206   struct GNUNET_DNSPARSER_Packet *p;
207   unsigned int i;
208
209   p = GNUNET_DNSPARSER_parse (request, request_length);
210   if (NULL == p)
211   {
212     fprintf (stderr, "Received malformed DNS packet!\n");
213     // FIXME: drop instead?
214     GNUNET_DNS_request_forward (rh);
215     return;
216   }
217   fprintf (stdout,
218            "%s with ID: %5u Flags: %s%s%s%s%s%s Return Code: %s Opcode: %s\n",
219            p->flags.query_or_response ? "Response" : "Query",
220            p->id,
221            p->flags.recursion_desired ? "RD " : "",
222            p->flags.message_truncated ? "MT " : "",
223            p->flags.authoritative_answer ? "AA " : "",
224            p->flags.checking_disabled ? "CD " : "",
225            p->flags.authenticated_data ? "AD " : "",
226            p->flags.recursion_available ? "RA " : "",
227            return_codes[p->flags.return_code & 15],
228            op_codes[p->flags.opcode & 15]);  
229   if (p->num_queries > 0)
230     fprintf (stdout,
231              "\tQueries:\n");
232   for (i=0;i<p->num_queries;i++)
233     display_query (&p->queries[i]);
234   
235   if (p->num_answers > 0)
236     fprintf (stdout,
237              "\tAnswers:\n");
238   for (i=0;i<p->num_answers;i++)
239     display_record (&p->answers[i]);
240   fprintf (stdout, "\n");
241   GNUNET_DNSPARSER_free_packet (p);
242   GNUNET_DNS_request_forward (rh);
243 }
244
245
246 /**
247  * Shutdown.
248  */
249 static void
250 do_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
251 {
252   if (NULL != handle)
253   {
254     GNUNET_DNS_disconnect (handle);
255     handle = NULL;
256   }
257 }
258
259
260 /**
261  * Main function that will be run by the scheduler.
262  *
263  * @param cls closure
264  * @param args remaining command-line arguments
265  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
266  * @param cfg configuration
267  */
268 static void
269 run (void *cls, char *const *args, const char *cfgfile,
270      const struct GNUNET_CONFIGURATION_Handle *cfg)
271 {
272   handle =
273     GNUNET_DNS_connect (cfg, 
274                         GNUNET_DNS_FLAG_REQUEST_MONITOR | GNUNET_DNS_FLAG_RESPONSE_MONITOR,
275                         &display_request,
276                         NULL);
277   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
278                                 &do_disconnect, NULL);
279 }
280
281
282 int
283 main (int argc, char *const *argv)
284 {
285   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
286     {'s', "testoption", NULL,
287      gettext_noop ("not useful"),
288      0, &GNUNET_GETOPT_set_one, &benchmark_send},
289     GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
290     GNUNET_GETOPT_OPTION_END
291   };
292   return (GNUNET_OK ==
293           GNUNET_PROGRAM_run (argc, argv, "gnunet-dns-monitor",
294                               gettext_noop
295                               ("Monitor DNS queries."), options,
296                               &run, NULL)) ? ret : 1;
297 }
298
299
300 /* end of gnunet-dns-monitor.c */