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