split up daemon-vpn in multiple files, remove exit-functionality
[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_core_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 = GNUNET_OS_start_process(helper_in, helper_out, "gnunet-helper-vpn", "gnunet-helper-vpn", NULL);
78
79     fh_from_helper = GNUNET_DISK_pipe_handle (helper_out, GNUNET_DISK_PIPE_END_READ);
80     fh_to_helper = GNUNET_DISK_pipe_handle (helper_in, GNUNET_DISK_PIPE_END_WRITE);
81
82     GNUNET_DISK_pipe_close_end(helper_out, GNUNET_DISK_PIPE_END_WRITE);
83     GNUNET_DISK_pipe_close_end(helper_in, GNUNET_DISK_PIPE_END_READ);
84
85     /* Tell the dns-service to rehijack the dns-port
86      * The routing-table gets flushed if an interface disappears.
87      */
88     restart_hijack = 1;
89     GNUNET_CLIENT_notify_transmit_ready(dns_connection, sizeof(struct GNUNET_MessageHeader), GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_query, NULL);
90
91     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, fh_from_helper, &helper_read, NULL);
92 }
93 /*}}}*/
94 /**
95  * Restart the helper-process
96  * {{{
97  */
98 void
99 restart_helper(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tskctx) {
100     // Kill the helper
101     GNUNET_OS_process_kill (helper_proc, SIGKILL);
102     GNUNET_OS_process_wait (helper_proc);
103     GNUNET_OS_process_close (helper_proc);
104     helper_proc = NULL;
105
106     GNUNET_DISK_pipe_close(helper_in);
107     GNUNET_DISK_pipe_close(helper_out);
108
109     /* Restart the helper */
110     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, start_helper_and_schedule, NULL);
111 }
112 /*}}}*/
113
114 /**
115  * Read from the helper-process
116  * {{{
117  */
118 void
119 helper_read(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx) {
120     /* no message can be bigger then 64k */
121     char buf[65535];
122
123     if (tsdkctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)
124       return;
125
126     int t = GNUNET_DISK_file_read(fh_from_helper, &buf, 65535);
127
128     /* On read-error, restart the helper */
129     if (t<=0) {
130         GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read error for header from vpn-helper: %m\n");
131         GNUNET_SCHEDULER_add_now(restart_helper, cls);
132         return;
133     }
134
135     /* FIXME */ GNUNET_SERVER_mst_receive(mst, NULL, buf, t, 0, 0);
136
137     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, fh_from_helper, &helper_read, NULL);
138 }
139 /*}}}*/
140
141 /**
142  * Send an dns-answer-packet to the helper
143  */
144 void
145 helper_write(void* cls, const struct GNUNET_SCHEDULER_TaskContext* tsdkctx) {
146     if (tsdkctx->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)
147       return;
148
149     struct answer_packet_list* ans = answer_proc_head;
150     size_t len = ntohs(ans->pkt.hdr.size);
151
152     GNUNET_assert(ans->pkt.subtype == GNUNET_DNS_ANSWER_TYPE_IP);
153
154     GNUNET_assert (20 == sizeof (struct ip_hdr));
155     GNUNET_assert (8 == sizeof (struct udp_pkt));
156     size_t data_len = len - sizeof(struct answer_packet) + 1;
157     size_t net_len = sizeof(struct ip_hdr) + sizeof(struct udp_dns) + data_len;
158     size_t pkt_len = sizeof(struct GNUNET_MessageHeader) + sizeof(struct pkt_tun) + net_len;
159
160     struct ip_udp_dns* pkt = alloca(pkt_len);
161     GNUNET_assert(pkt != NULL);
162     memset(pkt, 0, pkt_len);
163
164     /* set the gnunet-header */
165     pkt->shdr.size = htons(pkt_len);
166     pkt->shdr.type = htons(GNUNET_MESSAGE_TYPE_VPN_HELPER);
167
168     /* set the tun-header (no flags and ethertype of IPv4) */
169     pkt->tun.flags = 0;
170     pkt->tun.type = htons(0x0800);
171
172     /* set the ip-header */
173     pkt->ip_hdr.version = 4;
174     pkt->ip_hdr.hdr_lngth = 5;
175     pkt->ip_hdr.diff_serv = 0;
176     pkt->ip_hdr.tot_lngth = htons(net_len);
177     pkt->ip_hdr.ident = 0;
178     pkt->ip_hdr.flags = 0;
179     pkt->ip_hdr.frag_off = 0;
180     pkt->ip_hdr.ttl = 255;
181     pkt->ip_hdr.proto = 0x11; /* UDP */
182     pkt->ip_hdr.chks = 0; /* Will be calculated later*/
183     pkt->ip_hdr.sadr = ans->pkt.from;
184     pkt->ip_hdr.dadr = ans->pkt.to;
185
186     pkt->ip_hdr.chks = calculate_ip_checksum((uint16_t*)&pkt->ip_hdr, 5*4);
187
188     /* set the udp-header */
189     pkt->udp_dns.udp_hdr.spt = htons(53);
190     pkt->udp_dns.udp_hdr.dpt = ans->pkt.dst_port;
191     pkt->udp_dns.udp_hdr.len = htons(net_len - sizeof(struct ip_hdr));
192     pkt->udp_dns.udp_hdr.crc = 0; /* Optional for IPv4 */
193
194     memcpy(&pkt->udp_dns.data, ans->pkt.data, data_len);
195
196     GNUNET_CONTAINER_DLL_remove (answer_proc_head, answer_proc_tail, ans);
197     GNUNET_free(ans);
198
199     /* FIXME */ GNUNET_DISK_file_write(fh_to_helper, pkt, pkt_len);
200
201     /* if more packets are available, reschedule */
202     if (answer_proc_head != NULL)
203       GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
204                                        fh_to_helper,
205                                        &helper_write,
206                                        NULL);
207 }
208 /**
209  * Receive packets from the helper-process
210  */
211 void
212 message_token(void *cls,
213               void *client,
214               const struct GNUNET_MessageHeader *message) {
215     GNUNET_assert(ntohs(message->type) == GNUNET_MESSAGE_TYPE_VPN_HELPER);
216
217     struct tun_pkt *pkt_tun = (struct tun_pkt*) message;
218
219     /* ethertype is ipv6 */
220     if (ntohs(pkt_tun->tun.type) == 0x86dd)
221       {
222         struct ip6_pkt *pkt6 = (struct ip6_pkt*) message;
223         GNUNET_assert(pkt6->ip6_hdr.version == 6);
224         struct ip6_tcp *pkt6_tcp;
225         struct ip6_udp *pkt6_udp;
226         struct ip6_icmp *pkt6_icmp;
227         GNUNET_HashCode* key;
228
229         switch(pkt6->ip6_hdr.nxthdr)
230           {
231           case 0x06:
232             pkt6_tcp = (struct ip6_tcp*)pkt6;
233             break;
234           case 0x11:
235             pkt6_udp = (struct ip6_udp*)pkt6;
236             if ((key = address_mapping_exists(pkt6->ip6_hdr.dadr)) != NULL)
237               {
238                 struct map_entry* me = GNUNET_CONTAINER_multihashmap_get(hashmap, key);
239                 GNUNET_assert(me != NULL);
240                 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);
241                 GNUNET_free(key);
242                 if (me->desc.service_type & htonl(GNUNET_DNS_SERVICE_TYPE_UDP) &&
243                     (port_in_ports(me->desc.ports, pkt6_udp->udp_hdr.dpt) ||
244                      port_in_ports(me->additional_ports, pkt6_udp->udp_hdr.dpt)))
245                   {
246                     size_t size = sizeof(struct GNUNET_PeerIdentity) + sizeof(struct GNUNET_MessageHeader) + sizeof(GNUNET_HashCode) + ntohs(pkt6_udp->udp_hdr.len);
247                     struct GNUNET_PeerIdentity *cls = GNUNET_malloc(size);
248                     struct GNUNET_MessageHeader *hdr = (struct GNUNET_MessageHeader*)(cls+1);
249                     GNUNET_HashCode* hc = (GNUNET_HashCode*)(hdr + 1);
250                     memcpy(cls, &me->desc.peer, sizeof(struct GNUNET_PeerIdentity));
251                     memcpy(hc, &me->desc.service_descriptor, sizeof(GNUNET_HashCode));
252                     memcpy(hc+1, &pkt6_udp->udp_hdr, ntohs(pkt6_udp->udp_hdr.len));
253                     GNUNET_CORE_peer_request_connect(core_handle,
254                                         GNUNET_TIME_UNIT_FOREVER_REL,
255                                         (struct GNUNET_PeerIdentity*)&me->desc.peer,
256                                         send_udp_to_peer,
257                                         cls);
258                     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Queued to send to peer %x\n", *((unsigned int*)&me->desc.peer));
259                   }
260               }
261             break;
262           case 0x3a:
263             /* ICMPv6 */
264             pkt6_icmp = (struct ip6_icmp*)pkt6;
265             /* If this packet is an icmp-echo-request and a mapping exists, answer */
266             if (pkt6_icmp->icmp_hdr.type == 0x80 && (key = address_mapping_exists(pkt6->ip6_hdr.dadr)) != NULL)
267               {
268                 GNUNET_free(key);
269                 pkt6_icmp = GNUNET_malloc(ntohs(pkt6->shdr.size));
270                 memcpy(pkt6_icmp, pkt6, ntohs(pkt6->shdr.size));
271                 GNUNET_SCHEDULER_add_now(&send_icmp_response, pkt6_icmp);
272               }
273             break;
274           }
275       }
276     /* ethertype is ipv4 */
277     else if (ntohs(pkt_tun->tun.type) == 0x0800)
278       {
279         struct ip_pkt *pkt = (struct ip_pkt*) message;
280         struct ip_udp *udp = (struct ip_udp*) message;
281         GNUNET_assert(pkt->ip_hdr.version == 4);
282
283         /* Send dns-packets to the service-dns */
284         if (pkt->ip_hdr.proto == 0x11 && ntohs(udp->udp_hdr.dpt) == 53 )
285           {
286             /* 9 = 8 for the udp-header + 1 for the unsigned char data[1]; */
287             size_t len = sizeof(struct query_packet) + ntohs(udp->udp_hdr.len) - 9;
288
289             struct query_packet_list* query = GNUNET_malloc(len + 2*sizeof(struct query_packet_list*));
290             query->pkt.hdr.type = htons(GNUNET_MESSAGE_TYPE_LOCAL_QUERY_DNS);
291             query->pkt.hdr.size = htons(len);
292             query->pkt.orig_to = pkt->ip_hdr.dadr;
293             query->pkt.orig_from = pkt->ip_hdr.sadr;
294             query->pkt.src_port = udp->udp_hdr.spt;
295             memcpy(query->pkt.data, udp->data, ntohs(udp->udp_hdr.len) - 8);
296
297             GNUNET_CONTAINER_DLL_insert_after(head, tail, tail, query);
298
299             GNUNET_assert(head != NULL);
300
301             if (dns_connection != NULL)
302               GNUNET_CLIENT_notify_transmit_ready(dns_connection,
303                                                   len,
304                                                   GNUNET_TIME_UNIT_FOREVER_REL,
305                                                   GNUNET_YES,
306                                                   &send_query,
307                                                   NULL);
308           }
309       }
310 }
311
312 void write_to_helper(void* buf, size_t len)
313 {
314   (void)GNUNET_DISK_file_write(fh_to_helper, buf, len);
315 }
316
317 void schedule_helper_write(struct GNUNET_TIME_Relative time, void* cls)
318 {
319   GNUNET_SCHEDULER_add_write_file (time, fh_to_helper, &helper_write, cls);
320 }