* fixed incorrect doxygen commant fields in exit and vpn-helper-windows
[oweals/gnunet.git] / src / vpn / vpn_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 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/vpn_api.c
23  * @brief library to access the VPN service and tell it how to redirect traffic
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_vpn_service.h"
28 #include "vpn.h"
29
30
31 /**
32  * Opaque VPN handle
33  */
34 struct GNUNET_VPN_Handle
35 {
36   /**
37    * Configuration we use.
38    */
39   const struct GNUNET_CONFIGURATION_Handle *cfg;
40
41   /**
42    * Connection to VPN service.
43    */
44   struct GNUNET_CLIENT_Connection *client;
45
46   /**
47    * Active transmission request.
48    */
49   struct GNUNET_CLIENT_TransmitHandle *th;
50
51   /**
52    * Head of list of active redirection requests.
53    */
54   struct GNUNET_VPN_RedirectionRequest *rr_head;
55
56   /**
57    * Tail of list of active redirection requests.
58    */
59   struct GNUNET_VPN_RedirectionRequest *rr_tail;
60
61   /**
62    * Identifier of a reconnect task.
63    */
64   GNUNET_SCHEDULER_TaskIdentifier rt;
65
66   /**
67    * How long do we wait until we try to reconnect?
68    */
69   struct GNUNET_TIME_Relative backoff;
70
71   /**
72    * ID of the last request that was submitted to the service.
73    */
74   uint64_t request_id_gen;
75
76 };
77
78
79 /**
80  * Opaque redirection request handle.
81  */
82 struct GNUNET_VPN_RedirectionRequest
83 {
84   /**
85    * Element in DLL.
86    */
87   struct GNUNET_VPN_RedirectionRequest *next;
88
89   /**
90    * Element in DLL.
91    */
92   struct GNUNET_VPN_RedirectionRequest *prev;
93
94   /**
95    * Pointer to the VPN struct.
96    */
97   struct GNUNET_VPN_Handle *vh;
98
99   /**
100    * Target IP address for the redirection, or NULL for
101    * redirection to service.  Allocated after this struct.
102    */
103   const void *addr;
104
105   /**
106    * Function to call with the designated IP address.
107    */
108   GNUNET_VPN_AllocationCallback cb;
109   
110   /**
111    * Closure for 'cb'.
112    */
113   void *cb_cls;
114
115   /**
116    * For service redirection, identity of the peer offering the service.
117    */
118   struct GNUNET_PeerIdentity peer;
119
120   /**
121    * For service redirection, service descriptor.
122    */
123   struct GNUNET_HashCode serv;               
124
125   /**
126    * At what time should the created service mapping expire?
127    */
128   struct GNUNET_TIME_Absolute expiration_time;
129
130   /**
131    * non-zero if this request has been sent to the service.
132    */
133   uint64_t request_id;
134
135   /**
136    * Desired address family for the result.
137    */
138   int result_af;
139
140   /**
141    * Address family of 'addr'.  AF_INET or AF_INET6.
142    */
143   int addr_af;
144   
145   /**
146    * GNUNET_YES if we are to call the callback only after successful
147    * mesh tunnel creation.
148    */
149   int nac;
150   
151   /**
152    * For service redirection, IPPROT_UDP or IPPROTO_TCP.
153    */
154   uint8_t protocol;
155
156 };
157
158
159 /**
160  * Disconnect from the service (communication error) and reconnect later.
161  *
162  * @param vh handle to reconnect.
163  */
164 static void
165 reconnect (struct GNUNET_VPN_Handle *vh);
166
167
168 /**
169  * Function called when we receive a message from the VPN service.
170  *
171  * @param cls the 'struct GNUNET_VPN_Handle'
172  * @param msg message received, NULL on timeout or fatal error
173  */
174 static void 
175 receive_response (void *cls,
176                   const struct GNUNET_MessageHeader* msg)
177 {
178   struct GNUNET_VPN_Handle *vh = cls;
179   const struct RedirectToIpResponseMessage *rm;
180   struct GNUNET_VPN_RedirectionRequest *rr;
181   size_t msize;
182   size_t alen;
183   int af;
184
185   if (NULL == msg) 
186   {
187     reconnect (vh);
188     return;
189   }
190   if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP) ||
191        (sizeof (struct RedirectToIpResponseMessage) > (msize = ntohs (msg->size))) )
192   {
193     GNUNET_break (0);
194     reconnect (vh);
195     return;
196   }
197   rm = (const struct RedirectToIpResponseMessage *) msg;
198   af = (int) ntohl (rm->result_af);
199   switch (af)
200   {
201   case AF_UNSPEC:
202     alen = 0;
203     break;
204   case AF_INET:
205     alen = sizeof (struct in_addr);
206     break;
207   case AF_INET6:
208     alen = sizeof (struct in6_addr);
209     break;
210   default:
211     GNUNET_break (0);
212     reconnect (vh);
213     return;
214   }
215   if ( (msize != alen + sizeof (struct RedirectToIpResponseMessage)) ||
216        (0 == rm->request_id) )
217   {
218     GNUNET_break (0);
219     reconnect (vh);
220     return;
221   }  
222   GNUNET_CLIENT_receive (vh->client,
223                          &receive_response, vh,
224                          GNUNET_TIME_UNIT_FOREVER_REL);      
225   for (rr = vh->rr_head; NULL != rr; rr = rr->next)
226   {
227     if (rr->request_id == rm->request_id)
228     {
229       GNUNET_CONTAINER_DLL_remove (vh->rr_head,
230                                    vh->rr_tail,
231                                    rr);
232       rr->cb (rr->cb_cls,
233               af,
234               (af == AF_UNSPEC) ? NULL : &rm[1]);
235       GNUNET_free (rr);
236       break;
237     }
238   }
239 }
240
241
242 /**
243  * We're ready to transmit a request to the VPN service. Do it.
244  *
245  * @param cls the 'struct GNUNET_VPN_Handle*'
246  * @param size number of bytes available in buf
247  * @param buf where to copy the request
248  * @return number of bytes copied to 'buf'
249  */
250 static size_t
251 transmit_request (void *cls,
252                   size_t size,
253                   void *buf)
254 {
255   struct GNUNET_VPN_Handle *vh = cls;
256   struct GNUNET_VPN_RedirectionRequest *rr;
257   struct RedirectToIpRequestMessage rip;
258   struct RedirectToServiceRequestMessage rs;
259   char *cbuf;
260   size_t alen;
261   size_t ret;
262
263   vh->th = NULL;
264   /* find a pending request */
265   rr = vh->rr_head;
266   while ( (NULL != rr) &&
267           (0 != rr->request_id) )
268     rr = rr->next;
269   if (NULL == rr) 
270     return 0;
271   if (0 == size) 
272   {
273     reconnect (vh);
274     return 0;
275   }
276
277   /* if first request, start receive loop */
278   if (0 == vh->request_id_gen)
279     GNUNET_CLIENT_receive (vh->client,
280                            &receive_response, vh,
281                            GNUNET_TIME_UNIT_FOREVER_REL); 
282   if (NULL == rr->addr)
283   {
284     ret = sizeof (struct RedirectToServiceRequestMessage);
285     GNUNET_assert (ret <= size);
286     rs.header.size = htons ((uint16_t) ret);
287     rs.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE);
288     rs.nac = htonl (rr->nac);
289     rs.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
290     rs.protocol = htonl (rr->protocol);
291     rs.result_af = htonl (rr->result_af);
292     rs.target = rr->peer;
293     rs.service_descriptor = rr->serv;
294     rs.request_id = rr->request_id = ++vh->request_id_gen;    
295     memcpy (buf, &rs, sizeof (struct RedirectToServiceRequestMessage));
296   }
297   else
298   {
299     switch (rr->addr_af)
300     {
301     case AF_INET:
302       alen = sizeof (struct in_addr);
303       break;
304     case AF_INET6:
305       alen = sizeof (struct in6_addr);
306       break;
307     default:
308       GNUNET_assert (0);
309       return 0;
310     }
311     ret = alen + sizeof (struct RedirectToIpRequestMessage);
312     GNUNET_assert (ret <= size);
313     rip.header.size = htons ((uint16_t) ret);
314     rip.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP);
315     rip.nac = htonl (rr->nac);
316     rip.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
317     rip.result_af = htonl (rr->result_af);
318     rip.addr_af = htonl (rr->addr_af);
319     rip.request_id = rr->request_id = ++vh->request_id_gen;
320     cbuf = buf;
321     memcpy (cbuf, &rip, sizeof (struct RedirectToIpRequestMessage));
322     memcpy (&cbuf[sizeof (struct RedirectToIpRequestMessage)], rr->addr, alen);
323   }
324   /* test if there are more pending requests */
325   while ( (NULL != rr) &&
326           (0 != rr->request_id) )
327     rr = rr->next;
328   if (NULL != rr)
329     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
330                                                   sizeof (struct RedirectToServiceRequestMessage),
331                                                   GNUNET_TIME_UNIT_FOREVER_REL,
332                                                   GNUNET_NO,
333                                                   &transmit_request,
334                                                   vh);
335   return ret;
336 }
337
338
339 /**
340  * Add a request to our request queue and transmit it.
341  * 
342  * @param rr request to queue and transmit.
343  */
344 static void
345 queue_request (struct GNUNET_VPN_RedirectionRequest *rr)
346 {
347   struct GNUNET_VPN_Handle *vh;
348
349   vh = rr->vh;
350   GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head,
351                                     vh->rr_tail,
352                                     rr);
353   if ( (NULL == vh->th) &&
354        (NULL != vh->client) )
355     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
356                                                   sizeof (struct RedirectToServiceRequestMessage),
357                                                   GNUNET_TIME_UNIT_FOREVER_REL,
358                                                   GNUNET_NO,
359                                                   &transmit_request,
360                                                   vh);
361 }
362
363
364 /**
365  * Connect to the VPN service and start again to transmit our requests.
366  *
367  * @param cls the 'struct GNUNET_VPN_Handle *'
368  * @param tc scheduler context
369  */
370 static void
371 connect_task (void *cls,
372               const struct GNUNET_SCHEDULER_TaskContext *tc)
373 {
374   struct GNUNET_VPN_Handle *vh = cls;
375   
376   vh->rt = GNUNET_SCHEDULER_NO_TASK;
377   vh->client = GNUNET_CLIENT_connect ("vpn", vh->cfg);
378   GNUNET_assert (NULL != vh->client);
379   GNUNET_assert (NULL == vh->th);
380   if (NULL != vh->rr_head) 
381     vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
382                                                   sizeof (struct RedirectToServiceRequestMessage),
383                                                   GNUNET_TIME_UNIT_FOREVER_REL,
384                                                   GNUNET_NO,
385                                                   &transmit_request,
386                                                   vh);
387 }
388
389
390 /**
391  * Disconnect from the service (communication error) and reconnect later.
392  *
393  * @param vh handle to reconnect.
394  */
395 static void
396 reconnect (struct GNUNET_VPN_Handle *vh)
397 {
398   struct GNUNET_VPN_RedirectionRequest *rr;
399
400   if (NULL != vh->th)
401   {
402     GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
403     vh->th = NULL;
404   }  
405   GNUNET_CLIENT_disconnect (vh->client);
406   vh->client = NULL;
407   vh->request_id_gen = 0;
408   for (rr = vh->rr_head; NULL != rr; rr = rr->next)
409     rr->request_id = 0;
410   vh->backoff = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS,
411                                           GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply (vh->backoff, 2),
412                                                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)));
413   vh->rt = GNUNET_SCHEDULER_add_delayed (vh->backoff,
414                                          &connect_task, 
415                                          vh);
416 }
417
418
419 /**
420  * Cancel redirection request with the service.
421  *
422  * @param rr request to cancel
423  */
424 void
425 GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr)
426 {
427   struct GNUNET_VPN_Handle *vh;
428
429   vh = rr->vh;
430   GNUNET_CONTAINER_DLL_remove (vh->rr_head,
431                                vh->rr_tail,
432                                rr);
433   GNUNET_free (rr);
434 }
435
436
437 /**
438  * Tell the VPN that a forwarding to a particular peer offering a
439  * particular service is requested.  The VPN is to reserve a
440  * particular IP for the redirection and return it.  The VPN will
441  * begin the redirection as soon as possible and maintain it as long
442  * as it is actively used and keeping it is feasible.  Given resource
443  * limitations, the longest inactive mappings will be destroyed.
444  *
445  * @param vh VPN handle
446  * @param result_af desired address family for the returned allocation
447  *                  can also be AF_UNSPEC
448  * @param protocol protocol, IPPROTO_UDP or IPPROTO_TCP
449  * @param peer target peer for the redirection
450  * @param serv service descriptor to give to the peer
451  * @param nac GNUNET_YES to notify via callback only after completion of
452  *            the MESH-level connection,
453  *            GNUNET_NO to notify as soon as the IP has been reserved
454  * @param expiration_time at what time should the redirection expire?
455  *        (this should not impact connections that are active at that time)
456  * @param cb function to call with the IP
457  * @param cb_cls closure for cb
458  * @return handle to cancel the request (means the callback won't be
459  *         invoked anymore; the mapping may or may not be established
460  *         anyway)
461  */
462 struct GNUNET_VPN_RedirectionRequest *
463 GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh,
464                              int result_af,
465                              uint8_t protocol,
466                              const struct GNUNET_PeerIdentity *peer,
467                              const struct GNUNET_HashCode *serv,
468                              int nac,
469                              struct GNUNET_TIME_Absolute expiration_time,
470                              GNUNET_VPN_AllocationCallback cb,
471                              void *cb_cls)
472 {
473   struct GNUNET_VPN_RedirectionRequest *rr;
474
475   rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest));
476   rr->vh = vh;
477   rr->cb = cb;
478   rr->cb_cls = cb_cls;
479   rr->peer = *peer;
480   rr->serv = *serv;
481   rr->expiration_time = expiration_time;
482   rr->result_af = result_af;
483   rr->nac = nac;
484   rr->protocol = protocol;
485   queue_request (rr);
486   return rr;
487 }
488
489                 
490 /**
491  * Tell the VPN that forwarding to the Internet via some exit node is
492  * requested.  Note that both UDP and TCP traffic will be forwarded,
493  * but possibly to different exit nodes.  The VPN is to reserve a
494  * particular IP for the redirection and return it.  The VPN will
495  * begin the redirection as soon as possible and maintain it as long
496  * as it is actively used and keeping it is feasible.  Given resource
497  * limitations, the longest inactive mappings will be destroyed.
498  *
499  * @param vh VPN handle
500  * @param result_af desired address family for the returned allocation
501  * @param addr_af address family for 'addr', AF_INET or AF_INET6
502  * @param addr destination IP address on the Internet; destination
503  *             port is to be taken from the VPN packet itself
504  * @param nac GNUNET_YES to notify via callback only after completion of
505  *            the MESH-level connection,
506  *            GNUNET_NO to notify as soon as the IP has been reserved
507  * @param expiration_time at what time should the redirection expire?
508  *        (this should not impact connections that are active at that time)
509  * @param cb function to call with the IP
510  * @param cb_cls closure for cb
511  * @return handle to cancel the request (means the callback won't be
512  *         invoked anymore; the mapping may or may not be established
513  *         anyway)
514  */
515 struct GNUNET_VPN_RedirectionRequest *
516 GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh,
517                            int result_af,
518                            int addr_af,
519                            const void *addr,
520                            int nac,
521                            struct GNUNET_TIME_Absolute expiration_time,
522                            GNUNET_VPN_AllocationCallback cb,
523                            void *cb_cls)
524 {
525   struct GNUNET_VPN_RedirectionRequest *rr;
526   size_t alen;
527
528   switch (addr_af)
529   {
530   case AF_INET:
531     alen = sizeof (struct in_addr);
532     break;
533   case AF_INET6:
534     alen = sizeof (struct in6_addr);
535     break;
536   default:
537     GNUNET_break (0);
538     return NULL;
539   }
540   rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest) + alen);
541   rr->vh = vh;
542   rr->addr = &rr[1];
543   rr->cb = cb;
544   rr->cb_cls = cb_cls;
545   rr->expiration_time = expiration_time;
546   rr->result_af = result_af;
547   rr->addr_af = addr_af;
548   rr->nac = nac;
549   memcpy (&rr[1], addr, alen);
550   queue_request (rr);
551   return rr;
552 }
553
554
555 /**
556  * Connect to the VPN service
557  *
558  * @param cfg configuration to use
559  * @return VPN handle 
560  */
561 struct GNUNET_VPN_Handle *
562 GNUNET_VPN_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
563 {
564   struct GNUNET_VPN_Handle *vh;
565
566   vh = GNUNET_malloc (sizeof (struct GNUNET_VPN_Handle));
567   vh->cfg = cfg;
568   vh->client = GNUNET_CLIENT_connect ("vpn", cfg);
569   if (NULL == vh->client)
570   {
571     GNUNET_free (vh);
572     return NULL;
573   }
574   return vh;
575 }
576
577
578 /**
579  * Disconnect from the VPN service.
580  *
581  * @param vh VPN handle
582  */
583 void
584 GNUNET_VPN_disconnect (struct GNUNET_VPN_Handle *vh)
585 {
586   GNUNET_assert (NULL == vh->rr_head);
587   if (NULL != vh->th)
588   {
589     GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
590     vh->th = NULL;
591   }
592   if (NULL != vh->client)
593   {
594     GNUNET_CLIENT_disconnect (vh->client);
595     vh->client = NULL;
596   }
597   if (GNUNET_SCHEDULER_NO_TASK != vh->rt)
598   {
599     GNUNET_SCHEDULER_cancel (vh->rt);
600     vh->rt = GNUNET_SCHEDULER_NO_TASK;
601   }
602   GNUNET_free (vh);
603 }
604
605 /* end of vpn_api.c */