get the ip-addresses from the command-line
[oweals/gnunet.git] / src / vpn / gnunet-daemon-vpn-helper.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 Christian Grothoff
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-daemon-vpn-helper.c
23  * @brief
24  * @author Philipp Toelke
25  */
26 #include <platform.h>
27 #include <gnunet_common.h>
28 #include <gnunet_client_lib.h>
29 #include <gnunet_os_lib.h>
30 #include <gnunet_mesh_service.h>
31 #include <gnunet_protocols.h>
32 #include <gnunet_server_lib.h>
33 #include <gnunet_container_lib.h>
34 #include <block_dns.h>
35
36 #include "gnunet-daemon-vpn-dns.h"
37 #include "gnunet-daemon-vpn.h"
38 #include "gnunet-daemon-vpn-helper.h"
39 #include "gnunet-service-dns-p.h"
40 #include "gnunet-vpn-packet.h"
41
42 /**
43  * PipeHandle to receive data from the helper
44  */
45 static struct GNUNET_DISK_PipeHandle* helper_in;
46
47 /**
48  * PipeHandle to send data to the helper
49  */
50 static struct GNUNET_DISK_PipeHandle* helper_out;
51
52 /**
53  * FileHandle to receive data from the helper
54  */
55 static const struct GNUNET_DISK_FileHandle* fh_from_helper;
56
57 /**
58  * FileHandle to send data to the helper
59  */
60 static const struct GNUNET_DISK_FileHandle* fh_to_helper;
61
62 /**
63  * Start the helper-process
64  * {{{
65  */
66 void
67 start_helper_and_schedule(void *cls,
68                           const struct GNUNET_SCHEDULER_TaskContext *tc) {
69     if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
70       return;
71
72     helper_in = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO);
73     helper_out = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_NO, GNUNET_YES);
74
75     if (helper_in == NULL || helper_out == NULL) return;
76
77     helper_proc =
78       GNUNET_OS_start_process (helper_in, helper_out, "gnunet-helper-vpn",
79                                "gnunet-helper-vpn", "1234::1", "16", "10.10.10.1",
80                                "255.255.255.0");
81
82     fh_from_helper = GNUNET_DISK_pipe_handle (helper_out, GNUNET_DISK_PIPE_END_READ);
83     fh_to_helper = GNUNET_DISK_pipe_handle (helper_in, GNUNET_DISK_PIPE_END_WRITE);
84
85     GNUNET_DISK_pipe_close_end(helper_out, GNUNET_DISK_PIPE_END_WRITE);
86     GNUNET_DISK_pipe_close_end(helper_in, GNUNET_DISK_PIPE_END_READ);
87
88     /* Tell the dns-service to rehijack the dns-port
89      * The routing-table gets flushed if an interface disappears.
90      */
91     restart_hijack = 1;
92     GNUNET_CLIENT_notify_transmit_ready(dns_connection, sizeof(struct GNUNET_MessageHeader), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL);
93
94     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, fh_from_helper, &helper_read, NULL);
95 }
96 /*}}}*/
97 /**
98  * Restart the helper-process
99  * {{{
100  */
101 void
102 restart_helper(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tskctx) {
103     // Kill the helper
104     GNUNET_OS_process_kill (helper_proc, SIGKILL);
105     GNUNET_OS_process_wait (helper_proc);
106     GNUNET_OS_process_close (helper_proc);
107     helper_proc = NULL;
108
109     GNUNET_DISK_pipe_close(helper_in);
110     GNUNET_DISK_pipe_close(helper_out);
111
112     /* Restart the helper */
113     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, start_helper_and_schedule, NULL);
114 }
115 /*}}}*/
116
117 /**
118  * Read from the helper-process
119  * {{{
120  */
121 void
122 helper_read(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx) {
123     /* no message can be bigger then 64k */
124     char buf[65535];
125
126     if (tsdkctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)
127       return;
128
129     int t = GNUNET_DISK_file_read(fh_from_helper, &buf, 65535);
130
131     /* On read-error, restart the helper */
132     if (t<=0) {
133         GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read error for header from vpn-helper: %m\n");
134         GNUNET_SCHEDULER_add_now(restart_helper, cls);
135         return;
136     }
137
138     /* FIXME */ GNUNET_SERVER_mst_receive(mst, NULL, buf, t, 0, 0);
139
140     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, fh_from_helper, &helper_read, NULL);
141 }
142 /*}}}*/
143
144 /**
145  * Send an dns-answer-packet to the helper
146  */
147 void
148 helper_write(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx) {
149     if (tsdkctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)
150       return;
151
152     struct answer_packet_list* ans = answer_proc_head;
153     size_t len = ntohs(ans->pkt.hdr.size);
154
155     GNUNET_assert(ans->pkt.subtype == GNUNET_DNS_ANSWER_TYPE_IP);
156
157     GNUNET_assert (20 == sizeof (struct ip_hdr));
158     GNUNET_assert (8 == sizeof (struct udp_pkt));
159     size_t data_len = len - sizeof(struct answer_packet) + 1;
160     size_t net_len = sizeof(struct ip_hdr) + sizeof(struct udp_dns) + data_len;
161     size_t pkt_len = sizeof(struct GNUNET_MessageHeader) + sizeof(struct pkt_tun) + net_len;
162
163     struct ip_udp_dns* pkt = alloca(pkt_len);
164     GNUNET_assert(pkt != NULL);
165     memset(pkt, 0, pkt_len);
166
167     /* set the gnunet-header */
168     pkt->shdr.size = htons(pkt_len);
169     pkt->shdr.type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER);
170
171     /* set the tun-header (no flags and ethertype of IPv4) */
172     pkt->tun.flags = 0;
173     pkt->tun.type = htons(0x0800);
174
175     /* set the ip-header */
176     pkt->ip_hdr.version = 4;
177     pkt->ip_hdr.hdr_lngth = 5;
178     pkt->ip_hdr.diff_serv = 0;
179     pkt->ip_hdr.tot_lngth = htons(net_len);
180     pkt->ip_hdr.ident = 0;
181     pkt->ip_hdr.flags = 0;
182     pkt->ip_hdr.frag_off = 0;
183     pkt->ip_hdr.ttl = 255;
184     pkt->ip_hdr.proto = 0x11; /* UDP */
185     pkt->ip_hdr.chks = 0; /* Will be calculated later*/
186     pkt->ip_hdr.sadr = ans->pkt.from;
187     pkt->ip_hdr.dadr = ans->pkt.to;
188
189     pkt->ip_hdr.chks = calculate_ip_checksum((uint16_t*)&pkt->ip_hdr, 5*4);
190
191     /* set the udp-header */
192     pkt->udp_dns.udp_hdr.spt = htons(53);
193     pkt->udp_dns.udp_hdr.dpt = ans->pkt.dst_port;
194     pkt->udp_dns.udp_hdr.len = htons(net_len - sizeof(struct ip_hdr));
195     pkt->udp_dns.udp_hdr.crc = 0; /* Optional for IPv4 */
196
197     memcpy(&pkt->udp_dns.data, ans->pkt.data, data_len);
198
199     GNUNET_CONTAINER_DLL_remove (answer_proc_head, answer_proc_tail, ans);
200     GNUNET_free(ans);
201
202     /* FIXME */ GNUNET_DISK_file_write(fh_to_helper, pkt, pkt_len);
203
204     /* if more packets are available, reschedule */
205     if (answer_proc_head != NULL)
206       GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
207                                        fh_to_helper,
208                                        &helper_write,
209                                        NULL);
210 }
211 /**
212  * Receive packets from the helper-process
213  */
214 void
215 message_token(void *cls,
216               void *client,
217               const struct GNUNET_MessageHeader *message) {
218     GNUNET_assert(ntohs(message->type) == GNUNET_MESSAGE_TYPE_VPN_HELPER);
219
220     struct tun_pkt *pkt_tun = (struct tun_pkt*) message;
221
222     /* ethertype is ipv6 */
223     if (ntohs(pkt_tun->tun.type) == 0x86dd)
224       {
225         struct ip6_pkt *pkt6 = (struct ip6_pkt*) message;
226         GNUNET_assert(pkt6->ip6_hdr.version == 6);
227         struct ip6_tcp *pkt6_tcp;
228         struct ip6_udp *pkt6_udp;
229         struct ip6_icmp *pkt6_icmp;
230         GNUNET_HashCode* key;
231
232         switch(pkt6->ip6_hdr.nxthdr)
233           {
234           case 0x06:
235             pkt6_tcp = (struct ip6_tcp*)pkt6;
236             break;
237           case 0x11:
238             pkt6_udp = (struct ip6_udp*)pkt6;
239             if ((key = address_mapping_exists(pkt6->ip6_hdr.dadr)) != NULL)
240               {
241                 struct map_entry* me = GNUNET_CONTAINER_multihashmap_get(hashmap, key);
242                 GNUNET_assert(me != NULL);
243                 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Mapping exists; type: %d; UDP is %d; port: %x/%x!\n", me->desc.service_type, htonl(GNUNET_DNS_SERVICE_TYPE_UDP), pkt6_udp->udp_hdr.dpt, me->desc.ports);
244                 GNUNET_free(key);
245                 if (me->desc.service_type & htonl(GNUNET_DNS_SERVICE_TYPE_UDP) &&
246                     (port_in_ports(me->desc.ports, pkt6_udp->udp_hdr.dpt) ||
247                      port_in_ports(me->additional_ports, pkt6_udp->udp_hdr.dpt)))
248                   {
249                     size_t size = sizeof(struct GNUNET_MESH_Tunnel*) + sizeof(struct GNUNET_MessageHeader) + sizeof(GNUNET_HashCode) + ntohs(pkt6_udp->udp_hdr.len);
250                     struct GNUNET_MESH_Tunnel **cls = GNUNET_malloc(size);
251                     struct GNUNET_MessageHeader *hdr = (struct GNUNET_MessageHeader*)(cls+1);
252                     GNUNET_HashCode* hc = (GNUNET_HashCode*)(hdr + 1);
253
254                     memcpy(hc, &me->desc.service_descriptor, sizeof(GNUNET_HashCode));
255                     memcpy(hc+1, &pkt6_udp->udp_hdr, ntohs(pkt6_udp->udp_hdr.len));
256
257                     if (me->tunnel == NULL)
258                       {
259                         *cls = GNUNET_MESH_peer_request_connect_all(mesh_handle,
260                                                                     GNUNET_TIME_UNIT_FOREVER_REL,
261                                                                     1,
262                                                                     (struct GNUNET_PeerIdentity*)&me->desc.peer,
263                                                                     send_udp_to_peer,
264                                                                     NULL,
265                                                                     cls);
266                         me->tunnel = *cls;
267                       }
268                     else
269                       {
270                         *cls = me->tunnel;
271                         send_udp_to_peer(cls, (struct GNUNET_PeerIdentity*)1, NULL);
272                       }
273                     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Queued to send to peer %x\n", *((unsigned int*)&me->desc.peer));
274                   }
275               }
276             break;
277           case 0x3a:
278             /* ICMPv6 */
279             pkt6_icmp = (struct ip6_icmp*)pkt6;
280             /* If this packet is an icmp-echo-request and a mapping exists, answer */
281             if (pkt6_icmp->icmp_hdr.type == 0x80 && (key = address_mapping_exists(pkt6->ip6_hdr.dadr)) != NULL)
282               {
283                 GNUNET_free(key);
284                 pkt6_icmp = GNUNET_malloc(ntohs(pkt6->shdr.size));
285                 memcpy(pkt6_icmp, pkt6, ntohs(pkt6->shdr.size));
286                 GNUNET_SCHEDULER_add_now(&send_icmp_response, pkt6_icmp);
287               }
288             break;
289           }
290       }
291     /* ethertype is ipv4 */
292     else if (ntohs(pkt_tun->tun.type) == 0x0800)
293       {
294         struct ip_pkt *pkt = (struct ip_pkt*) message;
295         struct ip_udp *udp = (struct ip_udp*) message;
296         GNUNET_assert(pkt->ip_hdr.version == 4);
297
298         /* Send dns-packets to the service-dns */
299         if (pkt->ip_hdr.proto == 0x11 && ntohs(udp->udp_hdr.dpt) == 53 )
300           {
301             /* 9 = 8 for the udp-header + 1 for the unsigned char data[1]; */
302             size_t len = sizeof(struct query_packet) + ntohs(udp->udp_hdr.len) - 9;
303
304             struct query_packet_list* query = GNUNET_malloc(len + 2*sizeof(struct query_packet_list*));
305             query->pkt.hdr.type = htons(GNUNET_MESSAGE_TYPE_LOCAL_QUERY_DNS);
306             query->pkt.hdr.size = htons(len);
307             query->pkt.orig_to = pkt->ip_hdr.dadr;
308             query->pkt.orig_from = pkt->ip_hdr.sadr;
309             query->pkt.src_port = udp->udp_hdr.spt;
310             memcpy(query->pkt.data, udp->data, ntohs(udp->udp_hdr.len) - 8);
311
312             GNUNET_CONTAINER_DLL_insert_after(head, tail, tail, query);
313
314             GNUNET_assert(head != NULL);
315
316             if (dns_connection != NULL)
317               GNUNET_CLIENT_notify_transmit_ready(dns_connection,
318                                                   len,
319                                                   GNUNET_TIME_UNIT_FOREVER_REL,
320                                                   GNUNET_YES,
321                                                   &send_query,
322                                                   NULL);
323           }
324       }
325 }
326
327 void write_to_helper(void* buf, size_t len)
328 {
329   (void)GNUNET_DISK_file_write(fh_to_helper, buf, len);
330 }
331
332 void schedule_helper_write(struct GNUNET_TIME_Relative time, void* cls)
333 {
334   GNUNET_SCHEDULER_add_write_file (time, fh_to_helper, &helper_write, cls);
335 }