+ prevent bug https://www.gnunet.org/bugs/view.php?id=1868
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 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 transport/gnunet-service-transport.c
23  * @brief
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_transport_service.h"
30 #include "gnunet_peerinfo_service.h"
31 #include "gnunet_ats_service.h"
32 #include "gnunet-service-transport.h"
33 #include "gnunet-service-transport_blacklist.h"
34 #include "gnunet-service-transport_clients.h"
35 #include "gnunet-service-transport_hello.h"
36 #include "gnunet-service-transport_neighbours.h"
37 #include "gnunet-service-transport_plugins.h"
38 #include "gnunet-service-transport_validation.h"
39 #include "transport.h"
40
41 /* globals */
42
43 /**
44  * Statistics handle.
45  */
46 struct GNUNET_STATISTICS_Handle *GST_stats;
47
48 /**
49  * Configuration handle.
50  */
51 const struct GNUNET_CONFIGURATION_Handle *GST_cfg;
52
53 /**
54  * Configuration handle.
55  */
56 struct GNUNET_PeerIdentity GST_my_identity;
57
58 /**
59  * Handle to peerinfo service.
60  */
61 struct GNUNET_PEERINFO_Handle *GST_peerinfo;
62
63 /**
64  * Our public key.
65  */
66 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded GST_my_public_key;
67
68 /**
69  * Our private key.
70  */
71 struct GNUNET_CRYPTO_RsaPrivateKey *GST_my_private_key;
72
73 /**
74  * ATS handle.
75  */
76 struct GNUNET_ATS_SchedulingHandle *GST_ats;
77
78
79 /**
80  * Transmit our HELLO message to the given (connected) neighbour.
81  *
82  * @param cls the 'HELLO' message
83  * @param target a connected neighbour
84  * @param ats performance information (unused)
85  * @param ats_count number of records in ats (unused)
86  * @param transport plugin
87  * @param addr address
88  * @param addrlen address length
89  */
90 static void
91 transmit_our_hello (void *cls, const struct GNUNET_PeerIdentity *target,
92                     const struct GNUNET_ATS_Information *ats,
93                     uint32_t ats_count,
94                     const char * transport,
95                     const void * addr,
96                     size_t addrlen)
97 {
98   const struct GNUNET_MessageHeader *hello = cls;
99
100   GST_neighbours_send (target, (const char *) hello, ntohs (hello->size),
101                        GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION, NULL, NULL);
102 }
103
104
105 /**
106  * My HELLO has changed. Tell everyone who should know.
107  *
108  * @param cls unused
109  * @param hello new HELLO
110  */
111 static void
112 process_hello_update (void *cls, const struct GNUNET_MessageHeader *hello)
113 {
114   GST_clients_broadcast (hello, GNUNET_NO);
115   GST_neighbours_iterate (&transmit_our_hello, (void *) hello);
116 }
117
118
119
120 /**
121  * We received some payload.  Prepare to pass it on to our clients. 
122  *
123  * @param peer (claimed) identity of the other peer
124  * @param message the message, never NULL
125  * @param ats performance information
126  * @param ats_count number of records in ats
127  * @return how long the plugin should wait until receiving more data
128  */
129 static struct GNUNET_TIME_Relative
130 process_payload (const struct GNUNET_PeerIdentity *peer,
131                  const struct GNUNET_MessageHeader *message,
132                  const struct GNUNET_ATS_Information *ats,
133                  uint32_t ats_count)
134 {
135   struct GNUNET_TIME_Relative ret;
136   int do_forward;
137   struct InboundMessage *im;
138   size_t msg_size = ntohs (message->size);
139   size_t size = sizeof (struct InboundMessage) + msg_size + sizeof (struct GNUNET_ATS_Information) * ats_count;
140   char buf[size];
141   struct GNUNET_ATS_Information *ap;
142   
143   ret = GNUNET_TIME_UNIT_ZERO;
144   do_forward = GNUNET_SYSERR;
145   ret =
146     GST_neighbours_calculate_receive_delay (peer,
147                                             msg_size,
148                                             &do_forward);
149
150   if (!GST_neighbours_test_connected (peer))
151   {
152
153     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Discarded %u bytes type %u payload from peer `%s'\n",
154                       msg_size,
155                       ntohs (message->type),
156                       GNUNET_i2s (peer));
157
158     GNUNET_STATISTICS_update (GST_stats,
159                               gettext_noop ("# bytes payload discarded due to not connected peer "),
160                               msg_size,
161                               GNUNET_NO);
162     return ret;
163   }
164
165   if (do_forward != GNUNET_YES)
166     return ret;
167   im = (struct InboundMessage*) buf;    
168   im->header.size = htons (size);
169   im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
170   im->ats_count = htonl (ats_count);
171   im->peer = *peer;
172   ap = (struct GNUNET_ATS_Information*) &im[1];
173   memcpy (ap, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
174   memcpy (&ap[ats_count], message, ntohs (message->size));
175
176   GST_clients_broadcast (&im->header, GNUNET_YES);
177
178   return ret;
179 }
180
181
182 /**
183  * Function called by the transport for each received message.
184  * This function should also be called with "NULL" for the
185  * message to signal that the other peer disconnected.
186  *
187  * @param cls closure, const char* with the name of the plugin we received the message from
188  * @param peer (claimed) identity of the other peer
189  * @param message the message, NULL if we only care about
190  *                learning about the delay until we should receive again -- FIXME!
191  * @param ats performance information
192  * @param ats_count number of records in ats
193  * @param session identifier used for this session (NULL for plugins
194  *                that do not offer bi-directional communication to the sender
195  *                using the same "connection")
196  * @param sender_address binary address of the sender (if we established the
197  *                connection or are otherwise sure of it; should be NULL
198  *                for inbound TCP/UDP connections since it it not clear
199  *                that we could establish ourselves a connection to that
200  *                IP address and get the same system)
201  * @param sender_address_len number of bytes in sender_address
202  * @return how long the plugin should wait until receiving more data
203  *         (plugins that do not support this, can ignore the return value)
204  */
205 static struct GNUNET_TIME_Relative
206 plugin_env_receive_callback (void *cls, const struct GNUNET_PeerIdentity *peer,
207                              const struct GNUNET_MessageHeader *message,
208                              const struct GNUNET_ATS_Information *ats,
209                              uint32_t ats_count, struct Session *session,
210                              const char *sender_address,
211                              uint16_t sender_address_len)
212 {
213   const char *plugin_name = cls;
214   struct GNUNET_TIME_Relative ret;
215   uint16_t type;
216   
217   ret = GNUNET_TIME_UNIT_ZERO;
218   if (NULL == message)
219     goto end;
220   type = ntohs (message->type);
221 #if DEBUG_TRANSPORT
222
223   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received Message with type %u\n", type);
224 #endif
225
226   switch (type)
227   {
228   case GNUNET_MESSAGE_TYPE_HELLO:
229     GST_validation_handle_hello (message);
230     return ret;
231   case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
232 #if DEBUG_TRANSPORT
233     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
234                 "Processing `%s' from `%s'\n", "PING",
235                 (sender_address != NULL) ? GST_plugins_a2s (plugin_name,
236                                                             sender_address,
237                                                             sender_address_len)
238                 : "<inbound>");
239 #endif
240     GST_validation_handle_ping (peer, message, plugin_name, session,
241                                 sender_address, sender_address_len);
242     break;
243   case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
244 #if DEBUG_TRANSPORT
245     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
246                 "Processing `%s' from `%s'\n", "PONG",
247                 (sender_address != NULL) ? GST_plugins_a2s (plugin_name,
248                                                             sender_address,
249                                                             sender_address_len)
250                 : "<inbound>");
251 #endif
252     GST_validation_handle_pong (peer, message);
253     break;
254   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT:
255     GST_neighbours_handle_connect (message,
256                                    peer,
257                                    plugin_name, sender_address, sender_address_len,
258                                    session, ats, ats_count);
259     break;
260   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK:
261     GST_neighbours_handle_connect_ack (message,
262                                    peer,
263                                    plugin_name, sender_address, sender_address_len,
264                                    session, ats, ats_count);
265     break;
266   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
267     GST_neighbours_handle_ack (message,
268                                    peer,
269                                    plugin_name, sender_address, sender_address_len,
270                                    session, ats, ats_count);
271     break;
272   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
273     GST_neighbours_handle_disconnect_message (peer, message);
274     break;
275   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
276     GST_neighbours_keepalive (peer);
277     break;
278   default:
279     /* should be payload */
280     ret = process_payload (peer,
281                            message,
282                            ats, ats_count);
283     break;
284   }
285  end:
286 #if 1
287   /* FIXME: this should not be needed, and not sure it's good to have it, but without
288      this connections seem to go extra-slow */
289   GNUNET_ATS_address_update (GST_ats, peer,
290                              plugin_name, sender_address, sender_address_len,
291                              session,
292                              ats, ats_count);  
293 #endif
294 #if DEBUG_TRANSPORT
295   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296               "Allowing receive from peer %s to continue in %llu ms\n",
297               GNUNET_i2s (peer),
298               (unsigned long long) ret.rel_value);
299 #endif
300   return ret;
301 }
302
303
304 /**
305  * Function that will be called for each address the transport
306  * is aware that it might be reachable under.  Update our HELLO.
307  *
308  * @param cls name of the plugin (const char*)
309  * @param add_remove should the address added (YES) or removed (NO) from the
310  *                   set of valid addresses?
311  * @param addr one of the addresses of the host
312  *        the specific address format depends on the transport
313  * @param addrlen length of the address
314  */
315 static void
316 plugin_env_address_change_notification (void *cls, int add_remove,
317                                         const void *addr, size_t addrlen)
318 {
319   const char *plugin_name = cls;
320
321   GST_hello_modify_addresses (add_remove, plugin_name, addr, addrlen);
322 }
323
324
325 /**
326  * Function that will be called whenever the plugin internally
327  * cleans up a session pointer and hence the service needs to
328  * discard all of those sessions as well.  Plugins that do not
329  * use sessions can simply omit calling this function and always
330  * use NULL wherever a session pointer is needed.  This function
331  * should be called BEFORE a potential "TransmitContinuation"
332  * from the "TransmitFunction".
333  *
334  * @param cls closure
335  * @param peer which peer was the session for
336  * @param session which session is being destoyed
337  */
338 static void
339 plugin_env_session_end (void *cls, const struct GNUNET_PeerIdentity *peer,
340                         struct Session *session)
341 {
342 #if DEBUG_TRANSPORT
343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344               "Session %X to peer `%s' ended \n",
345               session, GNUNET_i2s (peer));
346 #endif
347   if (NULL != session)
348     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO  | GNUNET_ERROR_TYPE_BULK,
349                      "transport-ats",
350                      "Telling ATS to destroy session %p from peer %s\n",
351                      session,              
352                      GNUNET_i2s (peer));
353   GNUNET_ATS_address_destroyed (GST_ats, peer, NULL, NULL, 0, session);
354   GST_neighbours_session_terminated (peer, session);
355 }
356
357
358 /**
359  * Function called by ATS to notify the callee that the
360  * assigned bandwidth or address for a given peer was changed.  If the
361  * callback is called with address/bandwidth assignments of zero, the
362  * ATS disconnect function will still be called once the disconnect
363  * actually happened.
364  *
365  * @param cls closure
366  * @param peer identity of the peer
367  * @param plugin_name name of the transport plugin, NULL to disconnect
368  * @param session session to use (if available)
369  * @param plugin_addr address to use (if available)
370  * @param plugin_addr_len number of bytes in addr
371  * @param bandwidth_out assigned outbound bandwidth for the connection, 0 to disconnect from peer
372  * @param bandwidth_in assigned inbound bandwidth for the connection, 0 to disconnect from peer
373  */
374 static void
375 ats_request_address_change (void *cls, const struct GNUNET_PeerIdentity *peer,
376                             const char *plugin_name,
377                             const void *plugin_addr, size_t plugin_addr_len,
378                             struct Session *session,
379                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
380                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
381                             const struct GNUNET_ATS_Information * ats,
382                             uint32_t ats_count)
383 {
384   uint32_t bw_in = ntohl (bandwidth_in.value__);
385   uint32_t bw_out = ntohl (bandwidth_out.value__);
386
387   /* ATS tells me to disconnect from peer*/
388   if ((bw_in == 0) && (bw_out == 0))
389   {
390 #if DEBUG_TRANSPORT
391     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
392                 "ATS tells me to disconnect from peer `%s'\n",
393                 GNUNET_i2s (peer));
394 #endif
395     GST_neighbours_force_disconnect(peer);
396     return;
397   }
398   /* will never return GNUNET_YES since connection is to be established */
399   GST_neighbours_switch_to_address_3way (peer, plugin_name, plugin_addr,
400                                     plugin_addr_len, session, ats, ats_count,
401                                     bandwidth_in, bandwidth_out);
402 }
403
404
405 /**
406  * Function called to notify transport users that another
407  * peer connected to us.
408  *
409  * @param cls closure
410  * @param peer the peer that connected
411  * @param ats performance data
412  * @param ats_count number of entries in ats
413  */
414 static void
415 neighbours_connect_notification (void *cls,
416                                  const struct GNUNET_PeerIdentity *peer,
417                                  const struct GNUNET_ATS_Information
418                                  *ats, uint32_t ats_count)
419 {
420   size_t len = sizeof (struct ConnectInfoMessage) +
421       ats_count * sizeof (struct GNUNET_ATS_Information);
422   char buf[len];
423   struct ConnectInfoMessage *connect_msg = (struct ConnectInfoMessage *) buf;
424   struct GNUNET_ATS_Information *ap;
425
426   connect_msg->header.size = htons (sizeof (buf));
427   connect_msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
428   connect_msg->ats_count = htonl (ats_count);
429   connect_msg->id = *peer;
430   ap = (struct GNUNET_ATS_Information *) &connect_msg[1];
431   memcpy (ap, ats,
432           ats_count * sizeof (struct GNUNET_ATS_Information));
433   GST_clients_broadcast (&connect_msg->header, GNUNET_NO);
434 }
435
436
437 /**
438  * Function called to notify transport users that another
439  * peer disconnected from us.
440  *
441  * @param cls closure
442  * @param peer the peer that disconnected
443  */
444 static void
445 neighbours_disconnect_notification (void *cls,
446                                     const struct GNUNET_PeerIdentity *peer)
447 {
448   struct DisconnectInfoMessage disconnect_msg;
449
450   disconnect_msg.header.size = htons (sizeof (struct DisconnectInfoMessage));
451   disconnect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
452   disconnect_msg.reserved = htonl (0);
453   disconnect_msg.peer = *peer;
454   GST_clients_broadcast (&disconnect_msg.header, GNUNET_NO);
455 }
456
457
458 /**
459  * Function called when the service shuts down.  Unloads our plugins
460  * and cancels pending validations.
461  *
462  * @param cls closure, unused
463  * @param tc task context (unused)
464  */
465 static void
466 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
467 {
468   GST_validation_stop ();
469   GST_neighbours_stop ();
470   GST_plugins_unload ();
471
472   GNUNET_ATS_scheduling_done (GST_ats);
473   GST_ats = NULL;
474   GST_clients_stop ();
475   GST_blacklist_stop ();
476   GST_hello_stop ();
477
478   if (GST_peerinfo != NULL)
479   {
480     GNUNET_PEERINFO_disconnect (GST_peerinfo);
481     GST_peerinfo = NULL;
482   }
483   if (GST_stats != NULL)
484   {
485     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
486     GST_stats = NULL;
487   }
488   if (GST_my_private_key != NULL)
489   {
490     GNUNET_CRYPTO_rsa_key_free (GST_my_private_key);
491     GST_my_private_key = NULL;
492   }
493 }
494
495
496 /**
497  * Initiate transport service.
498  *
499  * @param cls closure
500  * @param server the initialized server
501  * @param c configuration to use
502  */
503 static void
504 run (void *cls, struct GNUNET_SERVER_Handle *server,
505      const struct GNUNET_CONFIGURATION_Handle *c)
506 {
507   char *keyfile;
508
509   /* setup globals */
510   GST_cfg = c;
511   if (GNUNET_OK !=
512       GNUNET_CONFIGURATION_get_value_filename (c, "GNUNETD", "HOSTKEY",
513                                                &keyfile))
514   {
515     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
516                 _
517                 ("Transport service is lacking key configuration settings.  Exiting.\n"));
518     GNUNET_SCHEDULER_shutdown ();
519     return;
520   }
521   GST_my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
522   GNUNET_free (keyfile);
523   if (GST_my_private_key == NULL)
524   {
525     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
526                 _("Transport service could not access hostkey.  Exiting.\n"));
527     GNUNET_SCHEDULER_shutdown ();
528     return;
529   }
530   GST_stats = GNUNET_STATISTICS_create ("transport", c);
531   GST_peerinfo = GNUNET_PEERINFO_connect (c);
532   GNUNET_CRYPTO_rsa_key_get_public (GST_my_private_key, &GST_my_public_key);
533   GNUNET_CRYPTO_hash (&GST_my_public_key, sizeof (GST_my_public_key),
534                       &GST_my_identity.hashPubKey);
535   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
536                                 NULL);
537   if (GST_peerinfo == NULL)
538   {
539     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
540                 _("Could not access PEERINFO service.  Exiting.\n"));
541     GNUNET_SCHEDULER_shutdown ();
542     return;
543   }
544
545   /* start subsystems */
546   GST_hello_start (&process_hello_update, NULL);
547   GST_blacklist_start (server);
548   GST_plugins_load (&plugin_env_receive_callback,
549                     &plugin_env_address_change_notification,
550                     &plugin_env_session_end);
551   GST_ats = GNUNET_ATS_scheduling_init (GST_cfg, &ats_request_address_change, NULL);
552   GST_neighbours_start (NULL, &neighbours_connect_notification,
553                         &neighbours_disconnect_notification);
554   GST_clients_start (server);
555   GST_validation_start ();
556 }
557
558
559 /**
560  * The main function for the transport service.
561  *
562  * @param argc number of arguments from the command line
563  * @param argv command line arguments
564  * @return 0 ok, 1 on error
565  */
566 int
567 main (int argc, char *const *argv)
568 {
569   return (GNUNET_OK ==
570           GNUNET_SERVICE_run (argc, argv, "transport",
571                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
572 }
573
574 /* end of file gnunet-service-transport-new.c */