get rid of the unneeded "closure"-struct
[oweals/gnunet.git] / src / vpn / gnunet-service-dns.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 vpn/gnunet-service-dns.c
23  * @author Philipp Toelke
24  */
25 #include "platform.h"
26 #include "gnunet_getopt_lib.h"
27 #include "gnunet_service_lib.h"
28 #include "gnunet_network_lib.h"
29 #include "gnunet_os_lib.h"
30 #include "gnunet-service-dns-p.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet-vpn-packet.h"
33 #include "gnunet-vpn-pretty-print.h"
34 #include "gnunet_container_lib.h"
35 #include "gnunet-dns-parser.h"
36 #include "gnunet_dht_service.h"
37 #include "gnunet_block_lib.h"
38 #include "block_dns.h"
39 #include "gnunet_crypto_lib.h"
40 #include "gnunet_signatures.h"
41
42 static struct GNUNET_SCHEDULER_Handle *sched;
43
44 static struct GNUNET_NETWORK_Handle *dnsout;
45
46 static struct GNUNET_DHT_Handle *dht;
47
48 static unsigned short dnsoutport;
49
50 static const struct GNUNET_CONFIGURATION_Handle *cfg;
51
52 static struct answer_packet_list *head;
53 static struct answer_packet_list *tail;
54
55 struct dns_query_id_state {
56         unsigned valid:1;
57         struct GNUNET_SERVER_Client* client;
58         unsigned local_ip:32;
59         unsigned remote_ip:32;
60         unsigned local_port:16;
61         char* name;
62         unsigned namelen:8;
63 };
64 static struct dns_query_id_state query_states[65536]; /* This is < 1.5MiB */
65
66 void hijack(unsigned short port) {
67         char port_s[6];
68
69         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Hijacking, port is %d\n", port);
70         snprintf(port_s, 6, "%d", port);
71         GNUNET_OS_start_process(NULL, NULL, "gnunet-helper-hijack-dns", "gnunet-hijack-dns", port_s, NULL);
72 }
73
74 void unhijack(unsigned short port) {
75         char port_s[6];
76
77         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "unHijacking, port is %d\n", port);
78         snprintf(port_s, 6, "%d", port);
79         GNUNET_OS_start_process(NULL, NULL, "gnunet-helper-hijack-dns", "gnunet-hijack-dns", "-d", port_s, NULL);
80 }
81
82 size_t send_answer(void* cls, size_t size, void* buf);
83
84 struct receive_dht_cls {
85   unsigned short id;
86   struct GNUNET_DHT_GetHandle* handle;
87 };
88
89 void receive_dht(void *cls,
90                  struct GNUNET_TIME_Absolute exp,
91                  const GNUNET_HashCode *key,
92                  const struct GNUNET_PeerIdentity *const *get_path,
93                  const struct GNUNET_PeerIdentity *const *put_path,
94                  enum GNUNET_BLOCK_Type type,
95                  size_t size,
96                  const void *data)
97 {
98   unsigned short id = ((struct receive_dht_cls*)cls)->id;
99   struct GNUNET_DHT_GetHandle* handle = ((struct receive_dht_cls*)cls)->handle;
100   GNUNET_free(cls);
101
102   GNUNET_assert(type == GNUNET_BLOCK_TYPE_DNS);
103
104   if (query_states[id].valid != GNUNET_YES) return;
105   query_states[id].valid = GNUNET_NO;
106
107   const struct GNUNET_DNS_Record* rec = data;
108   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got block of size %d, peer: %08x, desc: %08x\n", size, *((unsigned int*)&rec->peer), *((unsigned int*)&rec->service_descriptor));
109
110   size_t len = sizeof(struct answer_packet) - 1 \
111                + sizeof(struct dns_static) \
112                + query_states[id].namelen \
113                + sizeof(struct dns_query_line) \
114                + 2 /* To hold the pointer to the name */ \
115                + sizeof(struct dns_record_line) - 1 \
116                + 16;
117                ;
118   struct answer_packet_list* answer = GNUNET_malloc(len + 2*sizeof(struct answer_packet_list*));
119   memset(answer, 0, len + 2*sizeof(struct answer_packet_list*));
120
121   answer->pkt.hdr.type = htons(GNUNET_MESSAGE_TYPE_LOCAL_RESPONSE_DNS);
122   answer->pkt.hdr.size = htons(len);
123   answer->pkt.subtype = GNUNET_DNS_ANSWER_TYPE_SERVICE;
124
125   GNUNET_CRYPTO_hash(&rec->peer, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), &answer->pkt.peer);
126   memcpy(&answer->pkt.service_descriptor, &rec->service_descriptor, sizeof(GNUNET_HashCode));
127   memcpy(&answer->pkt.service_type, &rec->service_type, sizeof(answer->pkt.service_type));
128   memcpy(&answer->pkt.ports, &rec->ports, sizeof(answer->pkt.ports));
129
130   answer->pkt.from = query_states[id].remote_ip;
131
132   answer->pkt.to = query_states[id].local_ip;
133   answer->pkt.dst_port = query_states[id].local_port;
134
135   struct dns_pkt *dpkt = (struct dns_pkt*)answer->pkt.data;
136
137   dpkt->s.id = id;
138   dpkt->s.aa = 1;
139   dpkt->s.qr = 1;
140   dpkt->s.ra = 1;
141   dpkt->s.qdcount = htons(1);
142   dpkt->s.ancount = htons(1);
143
144   memcpy(dpkt->data, query_states[id].name, query_states[id].namelen);
145   GNUNET_free(query_states[id].name);
146
147
148   struct dns_query_line* dque = (struct dns_query_line*)(dpkt->data+(query_states[id].namelen));
149   dque->type = htons(28);
150   dque->class = htons(1);
151
152   char* anname = (char*)(dpkt->data+(query_states[id].namelen)+sizeof(struct dns_query_line));
153   memcpy(anname, (char[]){0xc0, 0x0c}, 2);
154
155   struct dns_record_line *drec_data = (struct dns_record_line*)(dpkt->data+(query_states[id].namelen)+sizeof(struct dns_query_line)+2);
156   drec_data->type = htons(28); /* AAAA */
157   drec_data->class = htons(1); /* IN */
158   drec_data->ttl = htonl(3600); /* FIXME: read from block */
159   drec_data->data_len = htons(16);
160
161   answer->pkt.addroffset = htons((unsigned short)((unsigned long)(&drec_data->data)-(unsigned long)(&answer->pkt)));
162
163   GNUNET_CONTAINER_DLL_insert_after(head, tail, tail, answer);
164
165   GNUNET_SERVER_notify_transmit_ready(query_states[id].client,
166                                       len,
167                                       GNUNET_TIME_UNIT_FOREVER_REL,
168                                       &send_answer,
169                                       query_states[id].client);
170
171   GNUNET_DHT_get_stop(handle);
172 }
173
174 /**
175  * This receives a GNUNET_MESSAGE_TYPE_REHIJACK and rehijacks the DNS
176  */
177 void rehijack(void *cls, struct GNUNET_SERVER_Client *client, const struct GNUNET_MessageHeader *message) {
178     unhijack(dnsoutport);
179     hijack(dnsoutport);
180 }
181
182 /**
183  * This receives the dns-payload from the daemon-vpn and sends it on over the udp-socket
184  */
185 void receive_query(void *cls, struct GNUNET_SERVER_Client *client, const struct GNUNET_MessageHeader *message)
186 {
187         struct query_packet* pkt = (struct query_packet*)message;
188         struct dns_pkt* dns = (struct dns_pkt*)pkt->data;
189         struct dns_pkt_parsed* pdns = parse_dns_packet(dns);
190
191         query_states[dns->s.id].valid = GNUNET_YES;
192         query_states[dns->s.id].client = client;
193         query_states[dns->s.id].local_ip = pkt->orig_from;
194         query_states[dns->s.id].local_port = pkt->src_port;
195         query_states[dns->s.id].remote_ip = pkt->orig_to;
196         query_states[dns->s.id].namelen = strlen((char*)dns->data) + 1;
197         query_states[dns->s.id].name = GNUNET_malloc(query_states[dns->s.id].namelen);
198         memcpy(query_states[dns->s.id].name, dns->data, query_states[dns->s.id].namelen);
199
200         if (pdns->queries[0]->namelen > 9 &&
201             0 == strncmp(pdns->queries[0]->name+(pdns->queries[0]->namelen - 9), ".gnunet.", 9)) {
202
203             GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Query for .gnunet!\n");
204             GNUNET_HashCode key;
205             GNUNET_CRYPTO_hash(pdns->queries[0]->name, pdns->queries[0]->namelen, &key);
206
207             GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Getting with key %08x, len is %d\n", *((unsigned int*)&key), pdns->queries[0]->namelen);
208
209             struct receive_dht_cls* cls = GNUNET_malloc(sizeof(struct receive_dht_cls));
210             cls->id = dns->s.id;
211
212             cls->handle = GNUNET_DHT_get_start(dht,
213                                  GNUNET_TIME_UNIT_MINUTES,
214                                  GNUNET_BLOCK_TYPE_DNS,
215                                  &key,
216                                  GNUNET_DHT_RO_NONE,
217                                  NULL,
218                                  0,
219                                  NULL,
220                                  0,
221                                  receive_dht,
222                                  cls);
223
224             goto out;
225         }
226
227         /* The query should be sent to the network */
228         GNUNET_free(pdns);
229
230         struct sockaddr_in dest;
231         memset(&dest, 0, sizeof dest);
232         dest.sin_port = htons(53);
233         dest.sin_addr.s_addr = pkt->orig_to;
234
235         /* int r = */ GNUNET_NETWORK_socket_sendto(dnsout, dns, ntohs(pkt->hdr.size) - sizeof(struct query_packet) + 1, (struct sockaddr*) &dest, sizeof dest);
236
237 out:
238         GNUNET_SERVER_receive_done(client, GNUNET_OK);
239 }
240
241 size_t send_answer(void* cls, size_t size, void* buf) {
242         struct answer_packet_list* query = head;
243         size_t len = ntohs(query->pkt.hdr.size);
244
245         GNUNET_assert(len <= size);
246
247         memcpy(buf, &query->pkt.hdr, len);
248
249         GNUNET_CONTAINER_DLL_remove (head, tail, query);
250
251         GNUNET_free(query);
252
253         if (head != NULL) {
254                 GNUNET_SERVER_notify_transmit_ready(cls, ntohs(head->pkt.hdr.size), GNUNET_TIME_UNIT_FOREVER_REL, &send_answer, cls);
255         }
256
257         return len;
258 }
259
260 static void read_response (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) {
261         unsigned char buf[65536];
262         struct dns_pkt* dns = (struct dns_pkt*)buf;
263
264         if (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)
265                 return;
266
267         struct sockaddr_in addr;
268         memset(&addr, 0, sizeof addr);
269         unsigned int addrlen = sizeof addr;
270
271         int r;
272         r = GNUNET_NETWORK_socket_recvfrom(dnsout, buf, 65536, (struct sockaddr*)&addr, &addrlen);
273
274         /* if (r < 0) TODO */
275
276         if (query_states[dns->s.id].valid == GNUNET_YES) {
277                 query_states[dns->s.id].valid = GNUNET_NO;
278
279                 size_t len = sizeof(struct answer_packet) + r - 1; /* 1 for the unsigned char data[1]; */
280                 struct answer_packet_list* answer = GNUNET_malloc(len + 2*sizeof(struct answer_packet_list*));
281                 answer->pkt.hdr.type = htons(GNUNET_MESSAGE_TYPE_LOCAL_RESPONSE_DNS);
282                 answer->pkt.hdr.size = htons(len);
283                 answer->pkt.subtype = GNUNET_DNS_ANSWER_TYPE_IP;
284                 answer->pkt.from = addr.sin_addr.s_addr;
285                 answer->pkt.to = query_states[dns->s.id].local_ip;
286                 answer->pkt.dst_port = query_states[dns->s.id].local_port;
287                 memcpy(answer->pkt.data, buf, r);
288
289                 GNUNET_CONTAINER_DLL_insert_after(head, tail, tail, answer);
290
291                 /* struct GNUNET_CONNECTION_TransmitHandle* th = */ GNUNET_SERVER_notify_transmit_ready(query_states[dns->s.id].client, len, GNUNET_TIME_UNIT_FOREVER_REL, &send_answer, query_states[dns->s.id].client);
292         }
293
294         GNUNET_SCHEDULER_add_read_net(sched, GNUNET_TIME_UNIT_FOREVER_REL, dnsout, &read_response, NULL);
295 }
296
297
298 /**
299  * Task run during shutdown.
300  *
301  * @param cls unused
302  * @param tc unused
303  */
304 static void
305 cleanup_task (void *cls,
306               const struct GNUNET_SCHEDULER_TaskContext *tc)
307 {
308         unhijack(dnsoutport);
309         GNUNET_DHT_disconnect(dht);
310 }
311
312 static void
313 publish_name (void *cls,
314              const struct GNUNET_SCHEDULER_TaskContext *tc)
315 {
316   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
317     return;
318
319   char* name = "philipptoelke.gnunet.";
320   size_t size = sizeof(struct GNUNET_DNS_Record);
321   struct GNUNET_DNS_Record data;
322   memset(&data, 0, size);
323
324   data.purpose.size = htonl(size - sizeof(struct GNUNET_CRYPTO_RsaSignature));
325   data.purpose.purpose = GNUNET_SIGNATURE_PURPOSE_DNS_RECORD;
326
327   GNUNET_CRYPTO_hash(name, strlen(name)+1, &data.service_descriptor);
328
329   char* keyfile;
330   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename(cfg, "GNUNETD",
331                                                            "HOSTKEY", &keyfile))
332     {
333       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "could not read keyfile-value\n");
334       if (keyfile != NULL) GNUNET_free(keyfile);
335       return;
336     }
337
338   struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file(keyfile);
339   GNUNET_free(keyfile);
340
341   GNUNET_CRYPTO_rsa_key_get_public(my_private_key, &data.peer);
342
343   data.expiration_time = GNUNET_TIME_relative_to_absolute(GNUNET_TIME_UNIT_HOURS);
344
345   /* Sign the block
346    */
347
348   if (GNUNET_OK != GNUNET_CRYPTO_rsa_sign(my_private_key,
349                                           &data.purpose,
350                                           &data.signature))
351     {
352       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "could not sign DNS_Record\n");
353       return;
354     }
355   GNUNET_free(my_private_key);
356
357   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Putting with key %08x\n", *((unsigned int*)&data.service_descriptor));
358
359   GNUNET_DHT_put(dht,
360                  &data.service_descriptor,
361                  GNUNET_DHT_RO_NONE,
362                  GNUNET_BLOCK_TYPE_DNS,
363                  size,
364                  (char*)&data,
365                  GNUNET_TIME_relative_to_absolute(GNUNET_TIME_UNIT_HOURS),
366                  GNUNET_TIME_UNIT_MINUTES,
367                  NULL,
368                  NULL);
369
370   GNUNET_SCHEDULER_add_delayed (sched, GNUNET_TIME_UNIT_HOURS, publish_name, NULL);
371 }
372
373 /**
374  * @param cls closure
375  * @param sched scheduler to use
376  * @param server the initialized server
377  * @param cfg configuration to use
378  */
379 static void
380 run (void *cls,
381      struct GNUNET_SCHEDULER_Handle *sched_,
382      struct GNUNET_SERVER_Handle *server,
383      const struct GNUNET_CONFIGURATION_Handle *cfg_)
384 {
385   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
386           /* callback, cls, type, size */
387     {&receive_query, NULL, GNUNET_MESSAGE_TYPE_LOCAL_QUERY_DNS, 0},
388     {&rehijack, NULL, GNUNET_MESSAGE_TYPE_REHIJACK, sizeof(struct GNUNET_MessageHeader)},
389     {NULL, NULL, 0, 0}
390   };
391
392   cfg = cfg_;
393   sched = sched_;
394
395   {
396   int i;
397   for (i = 0; i < 65536; i++) {
398     query_states[i].valid = GNUNET_NO;
399   }
400   }
401
402   dht = GNUNET_DHT_connect(sched, cfg, 1024);
403
404   struct sockaddr_in addr;
405
406   dnsout = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0);
407   if (dnsout == NULL)
408     return;
409   memset(&addr, 0, sizeof(struct sockaddr_in));
410
411   int err = GNUNET_NETWORK_socket_bind (dnsout,
412                                         (struct sockaddr*)&addr,
413                                         sizeof(struct sockaddr_in));
414
415   if (err != GNUNET_YES) {
416         GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Could not bind a port, exiting\n");
417         return;
418   }
419   socklen_t addrlen = sizeof(struct sockaddr_in);
420   err = getsockname(GNUNET_NETWORK_get_fd(dnsout),
421                     (struct sockaddr*) &addr,
422                     &addrlen);
423
424   dnsoutport = htons(addr.sin_port);
425
426   hijack(htons(addr.sin_port));
427
428   GNUNET_SCHEDULER_add_now (sched, publish_name, NULL);
429
430   GNUNET_SCHEDULER_add_read_net(sched, GNUNET_TIME_UNIT_FOREVER_REL, dnsout, &read_response, NULL);
431
432   GNUNET_SERVER_add_handlers (server, handlers);
433   GNUNET_SCHEDULER_add_delayed (sched,
434                   GNUNET_TIME_UNIT_FOREVER_REL,
435                   &cleanup_task,
436                   cls);
437 }
438
439 /**
440  * The main function for the dns service.
441  *
442  * @param argc number of arguments from the command line
443  * @param argv command line arguments
444  * @return 0 ok, 1 on error
445  */
446 int
447 main (int argc, char *const *argv)
448 {
449   return (GNUNET_OK ==
450           GNUNET_SERVICE_run (argc,
451                               argv,
452                               "dns",
453                               GNUNET_SERVICE_OPTION_NONE,
454                               &run, NULL)) ? 0 : 1;
455 }