fix dereferencing uninitialized rs/ws; assert non-NULL before dereferencing network...
[oweals/gnunet.git] / src / dns / gnunet-dns-redirector.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file src/dns/gnunet-dns-redirector.c
23  * @brief Tool to change DNS replies (for testing)
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dns_service.h"
30 #include "gnunet_dnsparser_lib.h"
31
32 /**
33  * Handle to DNS service.
34  */
35 static struct GNUNET_DNS_Handle *handle;
36
37 /**
38  * New target for A records.
39  */
40 static char *n4;
41
42 /**
43  * New target for AAAA records.
44  */
45 static char *n6;
46
47 /**
48  * Global return value (0 success).
49  */
50 static int ret;
51
52 /**
53  * Selected level of verbosity.
54  */
55 static unsigned int verbosity;
56
57
58 /**
59  * Modify the given DNS record.
60  *
61  * @param record record to modify
62  */
63 static void
64 modify_record (const struct GNUNET_DNSPARSER_Record *record)
65 {
66   char buf[INET6_ADDRSTRLEN];
67
68   switch (record->type)
69   {
70   case GNUNET_DNSPARSER_TYPE_A:
71     if (record->data.raw.data_len != sizeof (struct in_addr))
72       return;
73     if (NULL != n4)
74     {
75       if (verbosity > 1)
76         fprintf (stderr,
77                  "Changing A record from `%s' to `%s'\n",
78                  inet_ntop (AF_INET, record->data.raw.data, buf, sizeof (buf)),
79                  n4);
80       GNUNET_assert (1 == inet_pton (AF_INET, n4, record->data.raw.data));
81     }
82     break;
83   case GNUNET_DNSPARSER_TYPE_AAAA:
84     if (record->data.raw.data_len != sizeof (struct in6_addr))
85       return;
86     if (NULL != n6)
87     {
88       if (verbosity > 1)
89         fprintf (stderr,
90                  "Changing AAAA record from `%s' to `%s'\n",
91                  inet_ntop (AF_INET6, record->data.raw.data, buf, sizeof (buf)),
92                  n6);
93       GNUNET_assert (1 == inet_pton (AF_INET6, n6, record->data.raw.data));
94     }
95     break;
96   case GNUNET_DNSPARSER_TYPE_NS:
97   case GNUNET_DNSPARSER_TYPE_CNAME:
98   case GNUNET_DNSPARSER_TYPE_PTR:
99   case GNUNET_DNSPARSER_TYPE_SOA:
100   case GNUNET_DNSPARSER_TYPE_MX:
101   case GNUNET_DNSPARSER_TYPE_TXT:
102     break;
103   default:
104     break;
105   }
106 }
107
108
109 /**
110  * Signature of a function that is called whenever the DNS service
111  * encounters a DNS request and needs to do something with it.  The
112  * function has then the chance to generate or modify the response by
113  * calling one of the three "GNUNET_DNS_request_*" continuations.
114  *
115  * When a request is intercepted, this function is called first to
116  * give the client a chance to do the complete address resolution;
117  * "rdata" will be NULL for this first call for a DNS request, unless
118  * some other client has already filled in a response.
119  *
120  * If multiple clients exist, all of them are called before the global
121  * DNS.  The global DNS is only called if all of the clients'
122  * functions call GNUNET_DNS_request_forward.  Functions that call
123  * GNUNET_DNS_request_forward will be called again before a final
124  * response is returned to the application.  If any of the clients'
125  * functions call GNUNET_DNS_request_drop, the response is dropped.
126  *
127  * @param cls closure
128  * @param rh request handle to user for reply
129  * @param request_length number of bytes in request
130  * @param request udp payload of the DNS request
131  */
132 static void
133 modify_request (void *cls,
134                 struct GNUNET_DNS_RequestHandle *rh,
135                 size_t request_length,
136                 const char *request)
137 {
138   struct GNUNET_DNSPARSER_Packet *p;
139   unsigned int i;
140   char *buf;
141   size_t len;
142   int ret;
143
144   p = GNUNET_DNSPARSER_parse (request, request_length);
145   if (NULL == p)
146   {
147     fprintf (stderr, "Received malformed DNS packet, leaving it untouched\n");
148     GNUNET_DNS_request_forward (rh);
149     return;
150   }
151   for (i=0;i<p->num_answers;i++)
152     modify_record (&p->answers[i]);
153   buf = NULL;
154   ret = GNUNET_DNSPARSER_pack (p, 1024, &buf, &len);
155   GNUNET_DNSPARSER_free_packet (p);
156   if (GNUNET_OK != ret)
157   {
158     if (GNUNET_NO == ret)
159       fprintf (stderr,
160                "Modified DNS response did not fit, keeping old response\n");
161     else
162       GNUNET_break (0); /* our modifications should have been sane! */
163     GNUNET_DNS_request_forward (rh);
164   }
165   else
166   {
167     if (verbosity > 0)
168       fprintf (stdout,
169                "Injecting modified DNS response\n");
170     GNUNET_DNS_request_answer (rh, len, buf);
171   }
172   GNUNET_free_non_null (buf);
173 }
174
175
176 /**
177  * Shutdown.
178  */
179 static void
180 do_disconnect (void *cls)
181 {
182   if (NULL != handle)
183   {
184     GNUNET_DNS_disconnect (handle);
185     handle = NULL;
186   }
187 }
188
189
190 /**
191  * Main function that will be run by the scheduler.
192  *
193  * @param cls closure
194  * @param args remaining command-line arguments
195  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
196  * @param cfg configuration
197  */
198 static void
199 run (void *cls, char *const *args, const char *cfgfile,
200      const struct GNUNET_CONFIGURATION_Handle *cfg)
201 {
202   struct in_addr i4;
203   struct in6_addr i6;
204   if ( (n4 != NULL) &&
205        (1 != inet_pton (AF_INET, n4, &i4)) )
206   {
207     fprintf (stderr,
208              "`%s' is nto a valid IPv4 address!\n",
209              n4);
210     return;
211   }
212   if ( (n6 != NULL) &&
213        (1 != inet_pton (AF_INET6, n6, &i6)) )
214   {
215     fprintf (stderr,
216              "`%s' is nto a valid IPv6 address!\n",
217              n6);
218     return;
219   }
220
221   handle =
222     GNUNET_DNS_connect (cfg,
223                         GNUNET_DNS_FLAG_POST_RESOLUTION,
224                         &modify_request,
225                         NULL);
226   GNUNET_SCHEDULER_add_shutdown (&do_disconnect, NULL);
227 }
228
229
230 int
231 main (int argc, char *const *argv)
232 {
233   struct GNUNET_GETOPT_CommandLineOption options[] = {
234     GNUNET_GETOPT_option_string ('4',
235                                  "ipv4",
236                                  "IPV4",
237                                  gettext_noop ("set A records"),
238                                  &n4),
239
240     GNUNET_GETOPT_option_string ('6',
241                                  "ipv4",
242                                  "IPV6",
243                                  gettext_noop ("set AAAA records"),
244                                  &n6),
245
246     GNUNET_GETOPT_option_verbose (&verbosity),
247     GNUNET_GETOPT_OPTION_END
248   };
249
250   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
251     return 2;
252
253   ret = (GNUNET_OK ==
254          GNUNET_PROGRAM_run (argc, argv, "gnunet-dns-redirector",
255                              gettext_noop
256                              ("Change DNS replies to point elsewhere."), options,
257                              &run, NULL)) ? ret : 1;
258   GNUNET_free ((void*) argv);
259   return ret;
260 }
261
262
263 /* end of gnunet-dns-redirector.c */