-dv work
[oweals/gnunet.git] / src / dv / plugin_transport_dv.c
1 /*
2      This file is part of GNUnet
3      (C) 2002--2013 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 dv/plugin_transport_dv.c
23  * @brief DV transport service, takes incoming DV requests and deals with
24  * the DV service
25  * @author Nathan Evans
26  * @author Christian Grothoff
27  */
28
29 #include "platform.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_connection_lib.h"
32 #include "gnunet_server_lib.h"
33 #include "gnunet_service_lib.h"
34 #include "gnunet_statistics_service.h"
35 #include "gnunet_dv_service.h"
36 #include "gnunet_transport_service.h"
37 #include "gnunet_transport_plugin.h"
38 #include "dv.h"
39
40
41 /**
42  * Encapsulation of all of the state of the plugin.
43  */
44 struct Plugin;
45
46
47 /**
48  * Session handle for connections.
49  */
50 struct Session
51 {
52
53   /**
54    * Mandatory session header.
55    */
56   struct SessionHeader header;
57
58   /**
59    * Pointer to the global plugin struct.
60    */
61   struct Plugin *plugin;
62
63   /**
64    * Continuation function to call once the transmission buffer
65    * has again space available.  NULL if there is no
66    * continuation to call.
67    */
68   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
69
70   /**
71    * Closure for transmit_cont.
72    */
73   void *transmit_cont_cls;
74
75   /**
76    * To whom are we talking to.
77    */
78   struct GNUNET_PeerIdentity sender;
79
80   /**
81    * Current distance to the given peer.
82    */
83   uint32_t distance;
84
85   /**
86    * Does the transport service know about this session (and we thus
87    * need to call 'session_end' when it is released?)
88    */
89   int active;
90
91 };
92
93
94 /**
95  * Encapsulation of all of the state of the plugin.
96  */
97 struct Plugin
98 {
99   /**
100    * Our environment.
101    */
102   struct GNUNET_TRANSPORT_PluginEnvironment *env;
103
104   /**
105    * Hash map of sessions (active and inactive).
106    */
107   struct GNUNET_CONTAINER_MultiHashMap *sessions;
108
109   /**
110    * Copy of the handler array where the closures are
111    * set to this struct's instance.
112    */
113   struct GNUNET_SERVER_MessageHandler *handlers;
114
115   /**
116    * Handle to the DV service
117    */
118   struct GNUNET_DV_ServiceHandle *dvh;
119
120 };
121
122
123 /**
124  * Handler for messages received from the DV service.
125  *
126  * @param cls closure with the plugin
127  * @param sender sender of the message
128  * @param distance how far did the message travel
129  * @param msg actual message payload 
130  */
131 static void
132 handle_dv_message_received (void *cls,
133                             const struct GNUNET_PeerIdentity *sender,
134                             uint32_t distance,
135                             const struct GNUNET_MessageHeader *msg)
136 {
137   struct Plugin *plugin = cls;
138   struct GNUNET_ATS_Information ats;
139   struct Session *session;
140
141   session = GNUNET_CONTAINER_multihashmap_get (plugin->sessions,
142                                                &sender->hashPubKey);
143   if (NULL == session)    
144   {
145     GNUNET_break (0);
146     return;
147   }
148   ats.type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
149   ats.value = htonl (distance);
150   session->active = GNUNET_YES;
151   plugin->env->receive (plugin->env->cls, sender,
152                         msg,
153                         &ats, 1,
154                         session, "", 0);
155 }
156
157
158 /**
159  * Function called if DV starts to be able to talk to a peer.
160  *
161  * @param cls closure with 'struct Plugin'
162  * @param peer newly connected peer
163  * @param distance distance to the peer
164  */
165 static void 
166 handle_dv_connect (void *cls,
167                    const struct GNUNET_PeerIdentity *peer,
168                    uint32_t distance)
169 {
170   struct Plugin *plugin = cls;
171   struct Session *session;
172
173   session = GNUNET_CONTAINER_multihashmap_get (plugin->sessions,
174                                                &peer->hashPubKey);
175   if (NULL != session)    
176   {
177     GNUNET_break (0);
178     session->distance = distance;
179     if (GNUNET_YES == session->active)
180       GNUNET_break (0); // FIXME: notify transport about distance change
181     return; /* nothing to do */  
182   }
183   session = GNUNET_malloc (sizeof (struct Session));
184   session->sender = *peer;
185   session->distance = distance;
186   GNUNET_assert (GNUNET_YES ==
187                  GNUNET_CONTAINER_multihashmap_put (plugin->sessions,
188                                                     &session->sender.hashPubKey,
189                                                     session,
190                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
191 }
192
193
194 /**
195  * Function called if DV distance to a peer is changed.
196  *
197  * @param cls closure with 'struct Plugin'
198  * @param peer connected peer
199  * @param distance new distance to the peer
200  */
201 static void 
202 handle_dv_distance_changed (void *cls,
203                             const struct GNUNET_PeerIdentity *peer,
204                             uint32_t distance)
205 {
206   struct Plugin *plugin = cls;
207   struct Session *session;
208
209   session = GNUNET_CONTAINER_multihashmap_get (plugin->sessions,
210                                                &peer->hashPubKey);
211   if (NULL == session)    
212   {
213     GNUNET_break (0);
214     handle_dv_connect (plugin, peer, distance);
215     return;
216   }
217   session->distance = distance;
218   if (GNUNET_YES == session->active)
219     GNUNET_break (0); // FIXME: notify transport about distance change
220 }
221
222
223 /**
224  * Function called if DV is no longer able to talk to a peer.
225  *
226  * @param cls closure with 'struct Plugin'
227  * @param peer peer that disconnected
228  */
229 static void 
230 handle_dv_disconnect (void *cls,
231                       const struct GNUNET_PeerIdentity *peer)
232 {
233   struct Plugin *plugin = cls;
234   struct Session *session;
235
236   session = GNUNET_CONTAINER_multihashmap_get (plugin->sessions,
237                                                &peer->hashPubKey);
238   if (NULL == session)    
239     return; /* nothing to do */  
240   GNUNET_break (0); // FIXME!
241 }
242
243
244 /**
245  * Function that can be used by the transport service to transmit
246  * a message using the plugin.
247  *
248  * @param cls closure
249  * @param session the session used
250  * @param priority how important is the message
251  * @param msgbuf the message to transmit
252  * @param msgbuf_size number of bytes in 'msgbuf'
253  * @param timeout when should we time out
254  * @param cont continuation to call once the message has
255  *        been transmitted (or if the transport is ready
256  *        for the next transmission call; or if the
257  *        peer disconnected...)
258  * @param cont_cls closure for cont
259  * @return number of bytes used (on the physical network, with overheads);
260  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
261  *         and does NOT mean that the message was not transmitted (DV)
262  */
263 static ssize_t
264 dv_plugin_send (void *cls, 
265                 struct Session *session,
266                 const char *msgbuf, size_t msgbuf_size, unsigned int priority,
267                 struct GNUNET_TIME_Relative timeout, 
268                 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
269 {
270   int ret = -1;
271
272   GNUNET_break (0); // FIXME!
273   return ret;
274 }
275
276
277 /**
278  * Function that can be used to force the plugin to disconnect
279  * from the given peer and cancel all previous transmissions
280  * (and their continuations).
281  *
282  * @param cls closure
283  * @param target peer from which to disconnect
284  */
285 static void
286 dv_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
287 {
288   struct Plugin *plugin = cls;
289   struct Session *session;
290
291   session = GNUNET_CONTAINER_multihashmap_get (plugin->sessions,
292                                                &target->hashPubKey);
293   if (NULL == session)    
294     return; /* nothing to do */  
295   session->transmit_cont = NULL;
296   session->transmit_cont_cls = NULL;
297   session->active = GNUNET_NO;
298 }
299
300
301 /**
302  * Convert the transports address to a nice, human-readable
303  * format.
304  *
305  * @param cls closure
306  * @param type name of the transport that generated the address
307  * @param addr one of the addresses of the host, NULL for the last address
308  *        the specific address format depends on the transport
309  * @param addrlen length of the address
310  * @param numeric should (IP) addresses be displayed in numeric form?
311  * @param timeout after how long should we give up?
312  * @param asc function to call on each string
313  * @param asc_cls closure for asc
314  */
315 static void
316 dv_plugin_address_pretty_printer (void *cls, const char *type, const void *addr,
317                                   size_t addrlen, int numeric,
318                                   struct GNUNET_TIME_Relative timeout,
319                                   GNUNET_TRANSPORT_AddressStringCallback asc,
320                                   void *asc_cls)
321 {
322   if ( (0 == addrlen) &&
323        (0 == strcmp (type, "dv")) )
324     asc (asc_cls, "dv");
325   asc (asc_cls, NULL);
326 }
327
328
329 /**
330  * Convert the DV address to a pretty string.
331  *
332  * @param cls closure
333  * @param addr the (hopefully) DV address
334  * @param addrlen the length of the address
335  *
336  * @return string representing the DV address
337  */
338 static const char *
339 dv_plugin_address_to_string (void *cls, const void *addr, size_t addrlen)
340 {
341   if (0 != addrlen)
342   {
343     GNUNET_break (0); /* malformed */
344     return NULL; 
345   }
346   return "dv";
347 }
348
349
350 /**
351  * Another peer has suggested an address for this peer and transport
352  * plugin.  Check that this could be a valid address.  This function
353  * is not expected to 'validate' the address in the sense of trying to
354  * connect to it but simply to see if the binary format is technically
355  * legal for establishing a connection to this peer (and make sure that
356  * the address really corresponds to our network connection/settings
357  * and not some potential man-in-the-middle).
358  *
359  * @param cls closure
360  * @param addr pointer to the address
361  * @param addrlen length of addr
362  * @return GNUNET_OK if this is a plausible address for this peer
363  *         and transport, GNUNET_SYSERR if not
364  *
365  */
366 static int
367 dv_plugin_check_address (void *cls, const void *addr, size_t addrlen)
368 {
369   if (0 != addrlen)
370     return GNUNET_SYSERR;
371   return GNUNET_OK;
372 }
373
374
375 /**
376  * Create a new session to transmit data to the target
377  * This session will used to send data to this peer and the plugin will
378  * notify us by calling the env->session_end function
379  *
380  * @param cls the plugin
381  * @param address the address
382  * @return the session if the address is valid, NULL otherwise
383  */
384 static struct Session * 
385 dv_get_session (void *cls,
386                 const struct GNUNET_HELLO_Address *address)
387 {
388   struct Plugin *plugin = cls;
389   struct Session *session;
390
391   if (0 != address->address_length)
392     return NULL;
393   session = GNUNET_CONTAINER_multihashmap_get (plugin->sessions,
394                                                &address->peer.hashPubKey);
395   if (NULL == session)
396     return NULL; /* not valid right now */
397   session->active = GNUNET_YES;
398   return session;
399 }
400
401
402 /**
403  * Function called to convert a string address to
404  * a binary address.
405  *
406  * @param cls closure ('struct Plugin*')
407  * @param addr string address
408  * @param addrlen length of the address including \0 termination
409  * @param buf location to store the buffer
410  *        If the function returns GNUNET_SYSERR, its contents are undefined.
411  * @param added length of created address
412  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
413  */
414 static int 
415 dv_plugin_string_to_address (void *cls,
416                              const char *addr,
417                              uint16_t addrlen,
418                              void **buf,
419                              size_t *added)
420 {
421   if ( (addrlen == 3) &&
422        (0 == strcmp ("dv", addr)) )
423   {
424     *added = 0;
425     return GNUNET_OK;
426   }
427   return GNUNET_SYSERR;
428 }
429
430
431 /**
432  * Entry point for the plugin.
433  */
434 void *
435 libgnunet_plugin_transport_dv_init (void *cls)
436 {
437   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
438   struct GNUNET_TRANSPORT_PluginFunctions *api;
439   struct Plugin *plugin;
440
441   plugin = GNUNET_malloc (sizeof (struct Plugin));
442   plugin->env = env;
443   plugin->sessions = GNUNET_CONTAINER_multihashmap_create (1024 * 8, GNUNET_YES);
444   plugin->dvh = GNUNET_DV_service_connect (env->cfg,
445                                            plugin,
446                                            &handle_dv_connect,
447                                            &handle_dv_distance_changed,
448                                            &handle_dv_disconnect,
449                                            &handle_dv_message_received);
450   if (NULL == plugin->dvh)
451   {
452     GNUNET_CONTAINER_multihashmap_destroy (plugin->sessions);
453     GNUNET_free (plugin);
454     return NULL;
455   }
456   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
457   api->cls = plugin;
458   api->send = &dv_plugin_send;
459   api->disconnect = &dv_plugin_disconnect;
460   api->address_pretty_printer = &dv_plugin_address_pretty_printer;
461   api->check_address = &dv_plugin_check_address;
462   api->address_to_string = &dv_plugin_address_to_string;
463   api->string_to_address = &dv_plugin_string_to_address;
464   api->get_session = dv_get_session;
465   return api;
466 }
467
468
469 /**
470  * Function called to free a session.
471  *
472  * @param cls NULL
473  * @param key unused
474  * @param value session to free
475  * @return GNUNET_OK (continue to iterate)
476  */
477 static int
478 free_session_iterator (void *cls,
479                        const struct GNUNET_HashCode *key,
480                        void *value)
481 {
482   struct Session *session = value;
483
484   // FIXME: still call transmit_cont's here!?
485   GNUNET_free (session);
486   return GNUNET_OK;
487 }
488
489
490 /**
491  * Exit point from the plugin.
492  */
493 void *
494 libgnunet_plugin_transport_dv_done (void *cls)
495 {
496   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
497   struct Plugin *plugin = api->cls;
498
499   GNUNET_DV_service_disconnect (plugin->dvh);
500   GNUNET_CONTAINER_multihashmap_iterate (plugin->sessions,
501                                          &free_session_iterator,
502                                          NULL);
503   GNUNET_CONTAINER_multihashmap_destroy (plugin->sessions);
504   GNUNET_free (plugin);
505   GNUNET_free (api);
506   return NULL;
507 }
508
509 /* end of plugin_transport_dv.c */