trying to fix #4003
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2010-2015 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., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19  */
20 /**
21  * @file transport/gnunet-service-transport.c
22  * @brief main for gnunet-service-transport
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_hello_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_ats.h"
34 #include "gnunet-service-transport_blacklist.h"
35 #include "gnunet-service-transport_clients.h"
36 #include "gnunet-service-transport_hello.h"
37 #include "gnunet-service-transport_neighbours.h"
38 #include "gnunet-service-transport_plugins.h"
39 #include "gnunet-service-transport_validation.h"
40 #include "gnunet-service-transport_manipulation.h"
41 #include "transport.h"
42
43
44 /**
45  * Information we need for an asynchronous session kill.
46  */
47 struct SessionKiller
48 {
49   /**
50    * Kept in a DLL.
51    */
52   struct SessionKiller *next;
53
54   /**
55    * Kept in a DLL.
56    */
57   struct SessionKiller *prev;
58
59   /**
60    * Session to kill.
61    */
62   struct Session *session;
63
64   /**
65    * Plugin for the session.
66    */
67   struct GNUNET_TRANSPORT_PluginFunctions *plugin;
68
69   /**
70    * The kill task.
71    */
72   struct GNUNET_SCHEDULER_Task *task;
73 };
74
75
76 /* globals */
77
78 /**
79  * Statistics handle.
80  */
81 struct GNUNET_STATISTICS_Handle *GST_stats;
82
83 /**
84  * Configuration handle.
85  */
86 const struct GNUNET_CONFIGURATION_Handle *GST_cfg;
87
88 /**
89  * Configuration handle.
90  */
91 struct GNUNET_PeerIdentity GST_my_identity;
92
93 /**
94  * Handle to peerinfo service.
95  */
96 struct GNUNET_PEERINFO_Handle *GST_peerinfo;
97
98 /**
99  * Handle to our service's server.
100  */
101 static struct GNUNET_SERVER_Handle *GST_server;
102
103 /**
104  * Our private key.
105  */
106 struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key;
107
108 /**
109  * ATS scheduling handle.
110  */
111 struct GNUNET_ATS_SchedulingHandle *GST_ats;
112
113 /**
114  * ATS connectivity handle.
115  */
116 struct GNUNET_ATS_ConnectivityHandle *GST_ats_connect;
117
118 /**
119  * Hello address expiration
120  */
121 struct GNUNET_TIME_Relative hello_expiration;
122
123 /**
124  * Head of DLL of asynchronous tasks to kill sessions.
125  */
126 static struct SessionKiller *sk_head;
127
128 /**
129  * Tail of DLL of asynchronous tasks to kill sessions.
130  */
131 static struct SessionKiller *sk_tail;
132
133 /**
134  * Interface scanner determines our LAN address range(s).
135  */
136 struct GNUNET_ATS_InterfaceScanner *GST_is;
137
138
139 /**
140  * Transmit our HELLO message to the given (connected) neighbour.
141  *
142  * @param cls the 'HELLO' message
143  * @param peer identity of the peer
144  * @param address the address
145  * @param state current state this peer is in
146  * @param state_timeout timeout for the current state of the peer
147  * @param bandwidth_in inbound quota in NBO
148  * @param bandwidth_out outbound quota in NBO
149  */
150 static void
151 transmit_our_hello (void *cls,
152                     const struct GNUNET_PeerIdentity *peer,
153                     const struct GNUNET_HELLO_Address *address,
154                     enum GNUNET_TRANSPORT_PeerState state,
155                     struct GNUNET_TIME_Absolute state_timeout,
156                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
157                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
158 {
159   const struct GNUNET_MessageHeader *hello = cls;
160
161   if (0 ==
162       memcmp (peer,
163               &GST_my_identity,
164               sizeof (struct GNUNET_PeerIdentity)))
165     return; /* not to ourselves */
166   if (GNUNET_NO == GST_neighbours_test_connected (peer))
167     return;
168
169   GST_neighbours_send (peer,
170                        hello,
171                        ntohs (hello->size),
172                        hello_expiration,
173                        NULL, NULL);
174 }
175
176
177 /**
178  * My HELLO has changed. Tell everyone who should know.
179  *
180  * @param cls unused
181  * @param hello new HELLO
182  */
183 static void
184 process_hello_update (void *cls,
185                       const struct GNUNET_MessageHeader *hello)
186 {
187   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188               "Broadcasting HELLO to clients\n");
189   GST_clients_broadcast (hello, GNUNET_NO);
190   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191               "Broadcasting HELLO to neighbours\n");
192   GST_neighbours_iterate (&transmit_our_hello,
193                           (void *) hello);
194 }
195
196
197 /**
198  * We received some payload.  Prepare to pass it on to our clients.
199  *
200  * @param address address and (claimed) identity of the other peer
201  * @param session identifier used for this session (NULL for plugins
202  *                that do not offer bi-directional communication to the sender
203  *                using the same "connection")
204  * @param message the message to process
205  * @return how long the plugin should wait until receiving more data
206  */
207 static struct GNUNET_TIME_Relative
208 process_payload (const struct GNUNET_HELLO_Address *address,
209                  struct Session *session,
210                  const struct GNUNET_MessageHeader *message)
211 {
212   struct GNUNET_TIME_Relative ret;
213   int do_forward;
214   struct InboundMessage *im;
215   size_t msg_size = ntohs (message->size);
216   size_t size = sizeof(struct InboundMessage) + msg_size;
217   char buf[size] GNUNET_ALIGN;
218
219   do_forward = GNUNET_SYSERR;
220   ret = GST_neighbours_calculate_receive_delay (&address->peer,
221                                                 msg_size,
222                                                 &do_forward);
223   if (! GST_neighbours_test_connected (&address->peer))
224   {
225     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
226                 "Discarded %u bytes type %u payload from peer `%s'\n",
227                 msg_size,
228                 ntohs (message->type),
229                 GNUNET_i2s (&address->peer));
230     GNUNET_STATISTICS_update (GST_stats, gettext_noop
231                               ("# bytes payload discarded due to not connected peer"),
232                               msg_size,
233                               GNUNET_NO);
234     return ret;
235   }
236
237   if (GNUNET_YES != do_forward)
238     return ret;
239   im = (struct InboundMessage *) buf;
240   im->header.size = htons (size);
241   im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV);
242   im->peer = address->peer;
243   memcpy (&im[1], message, ntohs (message->size));
244   GST_clients_broadcast (&im->header, GNUNET_YES);
245   return ret;
246 }
247
248
249 /**
250  * Task to asynchronously terminate a session.
251  *
252  * @param cls the `struct SessionKiller` with the information for the kill
253  * @param tc scheduler context
254  */
255 static void
256 kill_session_task (void *cls,
257                    const struct GNUNET_SCHEDULER_TaskContext *tc)
258 {
259   struct SessionKiller *sk = cls;
260
261   sk->task = NULL;
262   GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk);
263   sk->plugin->disconnect_session (sk->plugin->cls, sk->session);
264   GNUNET_free(sk);
265 }
266
267
268 /**
269  * Force plugin to terminate session due to communication
270  * issue.
271  *
272  * @param plugin_name name of the plugin
273  * @param session session to termiante
274  */
275 static void
276 kill_session (const char *plugin_name,
277               struct Session *session)
278 {
279   struct GNUNET_TRANSPORT_PluginFunctions *plugin;
280   struct SessionKiller *sk;
281
282   for (sk = sk_head; NULL != sk; sk = sk->next)
283     if (sk->session == session)
284       return;
285   plugin = GST_plugins_find (plugin_name);
286   if (NULL == plugin)
287   {
288     GNUNET_break(0);
289     return;
290   }
291   /* need to issue disconnect asynchronously */
292   sk = GNUNET_new (struct SessionKiller);
293   sk->session = session;
294   sk->plugin = plugin;
295   sk->task = GNUNET_SCHEDULER_add_now (&kill_session_task, sk);
296   GNUNET_CONTAINER_DLL_insert (sk_head,
297                                sk_tail,
298                                sk);
299 }
300
301
302 /**
303  * Black list check result for try_connect call
304  * If connection to the peer is allowed request adddress and ???
305  *
306  * @param cls the message
307  * @param peer the peer
308  * @param address the address
309  * @param session the session
310  * @param result the result
311  */
312 static void
313 connect_bl_check_cont (void *cls,
314                        const struct GNUNET_PeerIdentity *peer,
315                        const struct GNUNET_HELLO_Address *address,
316                        struct Session *session,
317                        int result)
318 {
319   struct GNUNET_MessageHeader *msg = cls;
320
321   if (GNUNET_OK == result)
322   {
323     /* Blacklist allows to speak to this peer, forward SYN to neighbours  */
324     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
325                 "Received SYN message from peer `%s' at `%s'\n",
326                 GNUNET_i2s (peer),
327                 GST_plugins_a2s (address));
328     if (GNUNET_OK !=
329         GST_neighbours_handle_session_syn (msg,
330                                            peer))
331     {
332       GST_blacklist_abort_matching (address,
333                                     session);
334       kill_session (address->transport_name,
335                     session);
336     }
337     GNUNET_free (msg);
338     return;
339   }
340   GNUNET_free (msg);
341   if (GNUNET_SYSERR == result)
342     return; /* check was aborted, session destroyed */
343   /* Blacklist denies to speak to this peer */
344   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
345               "Discarding SYN message from `%s' due to denied blacklist check\n",
346               GNUNET_i2s (peer));
347   kill_session (address->transport_name,
348                 session);
349 }
350
351
352 /**
353  * Function called by the transport for each received message.
354  *
355  * @param cls closure, const char* with the name of the plugin we received the message from
356  * @param address address and (claimed) identity of the other peer
357  * @param message the message, NULL if we only care about
358  *                learning about the delay until we should receive again
359  * @param session identifier used for this session (NULL for plugins
360  *                that do not offer bi-directional communication to the sender
361  *                using the same "connection")
362  * @return how long the plugin should wait until receiving more data
363  *         (plugins that do not support this, can ignore the return value)
364  */
365 struct GNUNET_TIME_Relative
366 GST_receive_callback (void *cls,
367                       const struct GNUNET_HELLO_Address *address,
368                       struct Session *session,
369                       const struct GNUNET_MessageHeader *message)
370 {
371   const char *plugin_name = cls;
372   struct GNUNET_TIME_Relative ret;
373   uint16_t type;
374
375   ret = GNUNET_TIME_UNIT_ZERO;
376   if (NULL == message)
377     goto end;
378   type = ntohs (message->type);
379   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
380               "Received message with type %u from peer `%s'\n",
381               type,
382               GNUNET_i2s (&address->peer));
383
384   GNUNET_STATISTICS_update (GST_stats,
385                             gettext_noop ("# bytes total received"),
386                             ntohs (message->size),
387                             GNUNET_NO);
388   GST_neighbours_notify_data_recv (address,
389                                    message);
390   switch (type)
391   {
392   case GNUNET_MESSAGE_TYPE_HELLO_LEGACY:
393     /* Legacy HELLO message, discard  */
394     return ret;
395   case GNUNET_MESSAGE_TYPE_HELLO:
396     if (GNUNET_OK != GST_validation_handle_hello (message))
397     {
398       GNUNET_break_op (0);
399       GST_blacklist_abort_matching (address,
400                                     session);
401     }
402     return ret;
403   case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
404     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405                 "Processing PING from `%s'\n",
406                 GST_plugins_a2s (address));
407     if (GNUNET_OK !=
408         GST_validation_handle_ping (&address->peer,
409                                     message,
410                                     address,
411                                     session))
412     {
413       GST_blacklist_abort_matching (address,
414                                     session);
415       kill_session (plugin_name,
416                     session);
417     }
418     break;
419   case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
420     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
421                "Processing PONG from `%s'\n",
422                GST_plugins_a2s (address));
423     if (GNUNET_OK != GST_validation_handle_pong (&address->peer, message))
424     {
425       GNUNET_break_op (0);
426       GST_blacklist_abort_matching (address,
427                                     session);
428       kill_session (plugin_name, session);
429     }
430     break;
431   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN:
432     /* Do blacklist check if communication with this peer is allowed */
433     (void) GST_blacklist_test_allowed (&address->peer,
434                                        NULL,
435                                        &connect_bl_check_cont,
436                                        GNUNET_copy_message (message),
437                                        address,
438                                        session);
439     break;
440   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK:
441     if (GNUNET_OK !=
442         GST_neighbours_handle_session_syn_ack (message,
443                                                address,
444                                                session))
445     {
446       GST_blacklist_abort_matching (address, session);
447       kill_session (plugin_name, session);
448     }
449     break;
450   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
451     if (GNUNET_OK !=
452         GST_neighbours_handle_session_ack (message,
453                                            address,
454                                            session))
455     {
456       GNUNET_break_op(0);
457       GST_blacklist_abort_matching (address, session);
458       kill_session (plugin_name, session);
459     }
460     break;
461   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
462     GST_neighbours_handle_disconnect_message (&address->peer,
463                                               message);
464     break;
465   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA:
466     GST_neighbours_handle_quota_message (&address->peer,
467                                          message);
468     break;
469   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
470     GST_neighbours_keepalive (&address->peer,
471                               message);
472     break;
473   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE:
474     GST_neighbours_keepalive_response (&address->peer,
475                                        message);
476     break;
477   default:
478     /* should be payload */
479     GNUNET_STATISTICS_update (GST_stats,
480                               gettext_noop ("# bytes payload received"),
481                               ntohs (message->size),
482                               GNUNET_NO);
483     ret = process_payload (address,
484                            session,
485                            message);
486     break;
487   }
488  end:
489   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490               "Allowing receive from peer %s to continue in %s\n",
491               GNUNET_i2s (&address->peer),
492               GNUNET_STRINGS_relative_time_to_string (ret,
493                                                       GNUNET_YES));
494   return ret;
495 }
496
497
498 /**
499  * Function that will be called for each address the transport
500  * is aware that it might be reachable under.  Update our HELLO.
501  *
502  * @param cls name of the plugin (const char*)
503  * @param add_remove should the address added (YES) or removed (NO) from the
504  *                   set of valid addresses?
505  * @param address the address to add or remove
506  */
507 static void
508 plugin_env_address_change_notification (void *cls,
509                                         int add_remove,
510                                         const struct GNUNET_HELLO_Address *address)
511 {
512   static int addresses = 0;
513   struct GNUNET_STATISTICS_Handle *cfg = GST_stats;
514
515   if (GNUNET_YES == add_remove)
516   {
517     addresses ++;
518     GNUNET_STATISTICS_update (cfg,
519                               "# transport addresses",
520                               1,
521                               GNUNET_NO);
522   }
523   else if (GNUNET_NO == add_remove)
524   {
525     if (0 == addresses)
526     {
527       GNUNET_break (0);
528     }
529     else
530     {
531       addresses --;
532       GNUNET_STATISTICS_update (cfg,
533                                 "# transport addresses",
534                                 -1,
535                                 GNUNET_NO);
536     }
537   }
538   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
539               "Transport now has %u addresses to communicate\n",
540               addresses);
541   GST_hello_modify_addresses (add_remove,
542                               address);
543 }
544
545
546 /**
547  * Function that will be called whenever the plugin internally
548  * cleans up a session pointer and hence the service needs to
549  * discard all of those sessions as well.  Plugins that do not
550  * use sessions can simply omit calling this function and always
551  * use NULL wherever a session pointer is needed.  This function
552  * should be called BEFORE a potential "TransmitContinuation"
553  * from the "TransmitFunction".
554  *
555  * @param cls closure
556  * @param address which address was the session for
557  * @param session which session is being destoyed
558  */
559 static void
560 plugin_env_session_end (void *cls,
561                         const struct GNUNET_HELLO_Address *address,
562                         struct Session *session)
563 {
564   struct SessionKiller *sk;
565
566   if (NULL == address)
567   {
568     GNUNET_break (0);
569     return;
570   }
571   if (NULL == session)
572   {
573     GNUNET_break (0);
574     return;
575   }
576   GNUNET_assert (strlen (address->transport_name) > 0);
577
578   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
579               "Notification from plugin about terminated session %p from peer `%s' address `%s'\n",
580               session,
581               GNUNET_i2s (&address->peer),
582               GST_plugins_a2s (address));
583
584   GST_neighbours_session_terminated (&address->peer, session);
585   GST_ats_del_session (address, session);
586   GST_blacklist_abort_matching (address, session);
587
588   for (sk = sk_head; NULL != sk; sk = sk->next)
589   {
590     if (sk->session == session)
591     {
592       GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk);
593       GNUNET_SCHEDULER_cancel (sk->task);
594       GNUNET_free(sk);
595       break;
596     }
597   }
598 }
599
600
601 /**
602  * Black list check result from blacklist check triggered when a
603  * plugin gave us a new session in #plugin_env_session_start().  If
604  * connection to the peer is disallowed, kill the session.
605  *
606  * @param cls NULL
607  * @param peer the peer
608  * @param address address associated with the request
609  * @param session session associated with the request
610  * @param result the result
611  */
612 static void
613 plugin_env_session_start_bl_check_cont (void *cls,
614                                         const struct GNUNET_PeerIdentity *peer,
615                                         const struct GNUNET_HELLO_Address *address,
616                                         struct Session *session,
617                                         int result)
618 {
619   if (GNUNET_OK != result)
620   {
621     kill_session (address->transport_name,
622                   session);
623     return;
624   }
625   if (GNUNET_YES !=
626       GNUNET_HELLO_address_check_option (address,
627                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
628   {
629     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
630                 "Informing verifier about inbound session's address `%s'\n",
631                 GST_plugins_a2s (address));
632     GST_validation_handle_address (address);
633   }
634 }
635
636
637 /**
638  * Plugin tells transport service about a new inbound session
639  *
640  * @param cls unused
641  * @param address the address
642  * @param session the new session
643  * @param scope network scope information
644  */
645 static void
646 plugin_env_session_start (void *cls,
647                           const struct GNUNET_HELLO_Address *address,
648                           struct Session *session,
649                           enum GNUNET_ATS_Network_Type scope)
650 {
651   struct GNUNET_ATS_Properties prop;
652
653   if (NULL == address)
654   {
655     GNUNET_break(0);
656     return;
657   }
658   if (NULL == session)
659   {
660     GNUNET_break(0);
661     return;
662   }
663   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
664               "Notification from plugin `%s' about new session from peer `%s' address `%s'\n",
665               address->transport_name,
666               GNUNET_i2s (&address->peer),
667               GST_plugins_a2s (address));
668   if (GNUNET_YES ==
669       GNUNET_HELLO_address_check_option (address,
670                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
671   {
672     /* inbound is always new, but outbound MAY already be known, but
673        for example for UNIX, we have symmetric connections and thus we
674        may not know the address yet; add if necessary! */
675     /* FIXME: maybe change API here so we just pass scope? */
676     memset (&prop, 0, sizeof (prop));
677     GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != scope);
678     prop.scope = scope;
679     GST_ats_add_inbound_address (address,
680                                  session,
681                                  &prop);
682   }
683   /* Do blacklist check if communication with this peer is allowed */
684   (void) GST_blacklist_test_allowed (&address->peer,
685                                      address->transport_name,
686                                      &plugin_env_session_start_bl_check_cont,
687                                      NULL,
688                                      address,
689                                      session);
690 }
691
692
693 /**
694  * Function called by ATS to notify the callee that the
695  * assigned bandwidth or address for a given peer was changed.  If the
696  * callback is called with address/bandwidth assignments of zero, the
697  * ATS disconnect function will still be called once the disconnect
698  * actually happened.
699  *
700  * @param cls closure
701  * @param peer the peer this address is intended for
702  * @param address address to use (for peer given in address)
703  * @param session session to use (if available)
704  * @param bandwidth_out assigned outbound bandwidth for the connection in NBO,
705  *      0 to disconnect from peer
706  * @param bandwidth_in assigned inbound bandwidth for the connection in NBO,
707  *      0 to disconnect from peer
708  * @param ats ATS information
709  * @param ats_count number of @a ats elements
710  */
711 static void
712 ats_request_address_change (void *cls,
713                             const struct GNUNET_PeerIdentity *peer,
714                             const struct GNUNET_HELLO_Address *address,
715                             struct Session *session,
716                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
717                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
718 {
719   uint32_t bw_in = ntohl (bandwidth_in.value__);
720   uint32_t bw_out = ntohl (bandwidth_out.value__);
721
722   if (NULL == peer)
723   {
724     /* ATS service died, all suggestions become invalid!
725        (but we'll keep using the allocations for a little
726        while, to keep going while ATS restarts) */
727     /* FIXME: We should drop all
728        connections now, as ATS won't explicitly tell
729        us and be unaware of ongoing resource allocations! */
730     return;
731   }
732   /* ATS tells me to disconnect from peer */
733   if ((0 == bw_in) && (0 == bw_out))
734   {
735     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
736                 "ATS tells me to disconnect from peer `%s'\n",
737                 GNUNET_i2s (peer));
738     GST_neighbours_force_disconnect (peer);
739     return;
740   }
741   GNUNET_assert (NULL != address);
742   GNUNET_STATISTICS_update (GST_stats,
743                             "# ATS suggestions received",
744                             1,
745                             GNUNET_NO);
746   GST_neighbours_switch_to_address (address,
747                                     session,
748                                     bandwidth_in,
749                                     bandwidth_out);
750 }
751
752
753 /**
754  * Function called when the service shuts down.  Unloads our plugins
755  * and cancels pending validations.
756  *
757  * @param cls closure, unused
758  * @param tc task context (unused)
759  */
760 static void
761 shutdown_task (void *cls,
762                const struct GNUNET_SCHEDULER_TaskContext *tc)
763 {
764   GST_neighbours_stop ();
765   GST_plugins_unload ();
766   GST_validation_stop ();
767   GST_ats_done ();
768   GNUNET_ATS_scheduling_done (GST_ats);
769   GST_ats = NULL;
770   GNUNET_ATS_connectivity_done (GST_ats_connect);
771   GST_ats_connect = NULL;
772   GNUNET_ATS_scanner_done (GST_is);
773   GST_is = NULL;
774   GST_clients_stop ();
775   GST_blacklist_stop ();
776   GST_hello_stop ();
777   GST_manipulation_stop ();
778
779   if (NULL != GST_peerinfo)
780   {
781     GNUNET_PEERINFO_disconnect (GST_peerinfo);
782     GST_peerinfo = NULL;
783   }
784   if (NULL != GST_stats)
785   {
786     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
787     GST_stats = NULL;
788   }
789   if (NULL != GST_my_private_key)
790   {
791     GNUNET_free(GST_my_private_key);
792     GST_my_private_key = NULL;
793   }
794   GST_server = NULL;
795 }
796
797
798 /**
799  * Initiate transport service.
800  *
801  * @param cls closure
802  * @param server the initialized server
803  * @param c configuration to use
804  */
805 static void
806 run (void *cls,
807      struct GNUNET_SERVER_Handle *server,
808      const struct GNUNET_CONFIGURATION_Handle *c)
809 {
810   char *keyfile;
811   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
812   long long unsigned int max_fd_cfg;
813   int max_fd_rlimit;
814   int max_fd;
815   int friend_only;
816
817   /* setup globals */
818   GST_cfg = c;
819   if (GNUNET_OK !=
820       GNUNET_CONFIGURATION_get_value_filename (c,
821                                                "PEER",
822                                                "PRIVATE_KEY",
823                                                &keyfile))
824   {
825     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
826         _("Transport service is lacking key configuration settings. Exiting.\n"));
827     GNUNET_SCHEDULER_shutdown ();
828     return;
829   }
830   if (GNUNET_OK !=
831       GNUNET_CONFIGURATION_get_value_time (c,
832                                            "transport",
833                                            "HELLO_EXPIRATION",
834                                            &hello_expiration))
835   {
836     hello_expiration = GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION;
837   }
838   GST_server = server;
839   pk = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
840   GNUNET_free (keyfile);
841   GNUNET_assert (NULL != pk);
842   GST_my_private_key = pk;
843
844   GST_stats = GNUNET_STATISTICS_create ("transport", GST_cfg);
845   GST_peerinfo = GNUNET_PEERINFO_connect (GST_cfg);
846   GNUNET_CRYPTO_eddsa_key_get_public (GST_my_private_key,
847                                       &GST_my_identity.public_key);
848   GNUNET_assert(NULL != GST_my_private_key);
849
850   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
851              "My identity is `%4s'\n",
852              GNUNET_i2s_full (&GST_my_identity));
853
854   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
855                                 &shutdown_task,
856                                 NULL);
857   if (NULL == GST_peerinfo)
858   {
859     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
860         _("Could not access PEERINFO service.  Exiting.\n"));
861     GNUNET_SCHEDULER_shutdown ();
862     return;
863   }
864
865   max_fd_rlimit = 0;
866   max_fd_cfg = 0;
867 #if HAVE_GETRLIMIT
868   struct rlimit r_file;
869   if (0 == getrlimit (RLIMIT_NOFILE, &r_file))
870   {
871     max_fd_rlimit = r_file.rlim_cur;
872     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
873         "Maximum number of open files was: %u/%u\n",
874         r_file.rlim_cur,
875         r_file.rlim_max);
876   }
877   max_fd_rlimit = (9 * max_fd_rlimit) / 10; /* Keep 10% for rest of transport */
878 #endif
879   GNUNET_CONFIGURATION_get_value_number (GST_cfg,
880                                          "transport",
881                                          "MAX_FD",
882                                          &max_fd_cfg);
883
884   if (max_fd_cfg > max_fd_rlimit)
885     max_fd = max_fd_cfg;
886   else
887     max_fd = max_fd_rlimit;
888   if (max_fd < DEFAULT_MAX_FDS)
889     max_fd = DEFAULT_MAX_FDS;
890
891   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
892               "Limiting number of sockets to %u: validation %u, neighbors: %u\n",
893              max_fd, (max_fd / 3), (max_fd / 3) * 2);
894
895   friend_only = GNUNET_CONFIGURATION_get_value_yesno (GST_cfg,
896                                                       "topology",
897                                                       "FRIENDS-ONLY");
898   if (GNUNET_SYSERR == friend_only)
899     friend_only = GNUNET_NO; /* According to topology defaults */
900   /* start subsystems */
901   GST_blacklist_start (GST_server,
902                        GST_cfg,
903                        &GST_my_identity);
904   GST_is = GNUNET_ATS_scanner_init ();
905   GST_ats_connect = GNUNET_ATS_connectivity_init (GST_cfg);
906   GST_ats = GNUNET_ATS_scheduling_init (GST_cfg,
907                                         &ats_request_address_change,
908                                         NULL);
909   GST_ats_init ();
910   GST_manipulation_init ();
911   GST_plugins_load (&GST_manipulation_recv,
912                     &plugin_env_address_change_notification,
913                     &plugin_env_session_start,
914                     &plugin_env_session_end);
915   GST_hello_start (friend_only,
916                    &process_hello_update,
917                    NULL);
918   GST_neighbours_start ((max_fd / 3) * 2);
919   GST_clients_start (GST_server);
920   GST_validation_start ((max_fd / 3));
921 }
922
923
924 /**
925  * The main function for the transport service.
926  *
927  * @param argc number of arguments from the command line
928  * @param argv command line arguments
929  * @return 0 ok, 1 on error
930  */
931 int
932 main (int argc,
933       char * const *argv)
934 {
935   return
936       (GNUNET_OK
937           == GNUNET_SERVICE_run (argc, argv, "transport",
938               GNUNET_SERVICE_OPTION_NONE, &run, NULL )) ? 0 : 1;
939 }
940
941 /* end of file gnunet-service-transport.c */