- fix
[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, "# transport addresses", 1, GNUNET_NO);
519   }
520   else if (GNUNET_NO == add_remove)
521   {
522     if (0 == addresses)
523       GNUNET_break (0);
524     else
525     {
526       addresses --;
527       GNUNET_STATISTICS_update (cfg, "# transport addresses", -1, GNUNET_NO);
528     }
529   }
530
531   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
532               "Transport now has %u addresses to communicate\n",
533               addresses);
534   GST_hello_modify_addresses (add_remove, address);
535 }
536
537
538 /**
539  * Function that will be called whenever the plugin internally
540  * cleans up a session pointer and hence the service needs to
541  * discard all of those sessions as well.  Plugins that do not
542  * use sessions can simply omit calling this function and always
543  * use NULL wherever a session pointer is needed.  This function
544  * should be called BEFORE a potential "TransmitContinuation"
545  * from the "TransmitFunction".
546  *
547  * @param cls closure
548  * @param address which address was the session for
549  * @param session which session is being destoyed
550  */
551 static void
552 plugin_env_session_end (void *cls,
553                         const struct GNUNET_HELLO_Address *address,
554                         struct Session *session)
555 {
556   struct SessionKiller *sk;
557
558   if (NULL == address)
559   {
560     GNUNET_break (0);
561     return;
562   }
563   if (NULL == session)
564   {
565     GNUNET_break (0);
566     return;
567   }
568   GNUNET_assert (strlen (address->transport_name) > 0);
569
570   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
571               "Notification from plugin about terminated session %p from peer `%s' address `%s'\n",
572               session,
573               GNUNET_i2s (&address->peer),
574               GST_plugins_a2s (address));
575
576   GST_neighbours_session_terminated (&address->peer, session);
577   GST_ats_del_session (address, session);
578   GST_blacklist_abort_matching (address, session);
579
580   for (sk = sk_head; NULL != sk; sk = sk->next)
581   {
582     if (sk->session == session)
583     {
584       GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk);
585       GNUNET_SCHEDULER_cancel (sk->task);
586       GNUNET_free(sk);
587       break;
588     }
589   }
590 }
591
592
593 /**
594  * Black list check result from blacklist check triggered when a
595  * plugin gave us a new session in #plugin_env_session_start().  If
596  * connection to the peer is disallowed, kill the session.
597  *
598  * @param cls NULL
599  * @param peer the peer
600  * @param address address associated with the request
601  * @param session session associated with the request
602  * @param result the result
603  */
604 static void
605 plugin_env_session_start_bl_check_cont (void *cls,
606                                         const struct GNUNET_PeerIdentity *peer,
607                                         const struct GNUNET_HELLO_Address *address,
608                                         struct Session *session,
609                                         int result)
610 {
611   if (GNUNET_OK != result)
612   {
613     kill_session (address->transport_name,
614                   session);
615     return;
616   }
617   if (GNUNET_YES !=
618       GNUNET_HELLO_address_check_option (address,
619                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
620   {
621     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
622                 "Informing verifier about inbound session's address `%s'\n",
623                 GST_plugins_a2s (address));
624     GST_validation_handle_address (address);
625   }
626 }
627
628
629 /**
630  * Plugin tells transport service about a new inbound session
631  *
632  * @param cls unused
633  * @param address the address
634  * @param session the new session
635  * @param scope network scope information
636  */
637 static void
638 plugin_env_session_start (void *cls,
639                           const struct GNUNET_HELLO_Address *address,
640                           struct Session *session,
641                           enum GNUNET_ATS_Network_Type scope)
642 {
643   struct GNUNET_ATS_Properties prop;
644
645   if (NULL == address)
646   {
647     GNUNET_break(0);
648     return;
649   }
650   if (NULL == session)
651   {
652     GNUNET_break(0);
653     return;
654   }
655   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
656               "Notification from plugin `%s' about new session from peer `%s' address `%s'\n",
657               address->transport_name,
658               GNUNET_i2s (&address->peer),
659               GST_plugins_a2s (address));
660   if (GNUNET_YES ==
661       GNUNET_HELLO_address_check_option (address,
662                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
663   {
664     /* inbound is always new, but outbound MAY already be known, but
665        for example for UNIX, we have symmetric connections and thus we
666        may not know the address yet; add if necessary! */
667     /* FIXME: maybe change API here so we just pass scope? */
668     memset (&prop, 0, sizeof (prop));
669     GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != scope);
670     prop.scope = scope;
671     GST_ats_add_inbound_address (address,
672                                  session,
673                                  &prop);
674   }
675   /* Do blacklist check if communication with this peer is allowed */
676   (void) GST_blacklist_test_allowed (&address->peer,
677                                      address->transport_name,
678                                      &plugin_env_session_start_bl_check_cont,
679                                      NULL,
680                                      address,
681                                      session);
682 }
683
684
685 /**
686  * Function called by ATS to notify the callee that the
687  * assigned bandwidth or address for a given peer was changed.  If the
688  * callback is called with address/bandwidth assignments of zero, the
689  * ATS disconnect function will still be called once the disconnect
690  * actually happened.
691  *
692  * @param cls closure
693  * @param peer the peer this address is intended for
694  * @param address address to use (for peer given in address)
695  * @param session session to use (if available)
696  * @param bandwidth_out assigned outbound bandwidth for the connection in NBO,
697  *      0 to disconnect from peer
698  * @param bandwidth_in assigned inbound bandwidth for the connection in NBO,
699  *      0 to disconnect from peer
700  * @param ats ATS information
701  * @param ats_count number of @a ats elements
702  */
703 static void
704 ats_request_address_change (void *cls,
705                             const struct GNUNET_PeerIdentity *peer,
706                             const struct GNUNET_HELLO_Address *address,
707                             struct Session *session,
708                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
709                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
710 {
711   uint32_t bw_in = ntohl (bandwidth_in.value__);
712   uint32_t bw_out = ntohl (bandwidth_out.value__);
713
714   if (NULL == peer)
715   {
716     /* ATS service died, all suggestions become invalid!
717        (but we'll keep using the allocations for a little
718        while, to keep going while ATS restarts) */
719     /* FIXME: We should drop all
720        connections now, as ATS won't explicitly tell
721        us and be unaware of ongoing resource allocations! */
722     return;
723   }
724   /* ATS tells me to disconnect from peer */
725   if ((0 == bw_in) && (0 == bw_out))
726   {
727     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
728                 "ATS tells me to disconnect from peer `%s'\n",
729                 GNUNET_i2s (peer));
730     GST_neighbours_force_disconnect (peer);
731     return;
732   }
733   GNUNET_assert (NULL != address);
734   GNUNET_STATISTICS_update (GST_stats,
735                             "# ATS suggestions received",
736                             1,
737                             GNUNET_NO);
738   GST_neighbours_switch_to_address (address,
739                                     session,
740                                     bandwidth_in,
741                                     bandwidth_out);
742 }
743
744
745 /**
746  * Function called when the service shuts down.  Unloads our plugins
747  * and cancels pending validations.
748  *
749  * @param cls closure, unused
750  * @param tc task context (unused)
751  */
752 static void
753 shutdown_task (void *cls,
754                const struct GNUNET_SCHEDULER_TaskContext *tc)
755 {
756   GST_neighbours_stop ();
757   GST_plugins_unload ();
758   GST_validation_stop ();
759   GST_ats_done ();
760   GNUNET_ATS_scheduling_done (GST_ats);
761   GST_ats = NULL;
762   GNUNET_ATS_connectivity_done (GST_ats_connect);
763   GST_ats_connect = NULL;
764   GNUNET_ATS_scanner_done (GST_is);
765   GST_is = NULL;
766   GST_clients_stop ();
767   GST_blacklist_stop ();
768   GST_hello_stop ();
769   GST_manipulation_stop ();
770
771   if (NULL != GST_peerinfo)
772   {
773     GNUNET_PEERINFO_disconnect (GST_peerinfo);
774     GST_peerinfo = NULL;
775   }
776   if (NULL != GST_stats)
777   {
778     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
779     GST_stats = NULL;
780   }
781   if (NULL != GST_my_private_key)
782   {
783     GNUNET_free(GST_my_private_key);
784     GST_my_private_key = NULL;
785   }
786   GST_server = NULL;
787 }
788
789
790 /**
791  * Initiate transport service.
792  *
793  * @param cls closure
794  * @param server the initialized server
795  * @param c configuration to use
796  */
797 static void
798 run (void *cls,
799      struct GNUNET_SERVER_Handle *server,
800      const struct GNUNET_CONFIGURATION_Handle *c)
801 {
802   char *keyfile;
803   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
804   long long unsigned int max_fd_cfg;
805   int max_fd_rlimit;
806   int max_fd;
807   int friend_only;
808
809   /* setup globals */
810   GST_cfg = c;
811   if (GNUNET_OK !=
812       GNUNET_CONFIGURATION_get_value_filename (c,
813                                                "PEER",
814                                                "PRIVATE_KEY",
815                                                &keyfile))
816   {
817     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
818         _("Transport service is lacking key configuration settings. Exiting.\n"));
819     GNUNET_SCHEDULER_shutdown ();
820     return;
821   }
822   if (GNUNET_OK !=
823       GNUNET_CONFIGURATION_get_value_time (c,
824                                            "transport",
825                                            "HELLO_EXPIRATION",
826                                            &hello_expiration))
827   {
828     hello_expiration = GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION;
829   }
830   GST_server = server;
831   pk = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
832   GNUNET_free (keyfile);
833   GNUNET_assert (NULL != pk);
834   GST_my_private_key = pk;
835
836   GST_stats = GNUNET_STATISTICS_create ("transport", GST_cfg);
837   GST_peerinfo = GNUNET_PEERINFO_connect (GST_cfg);
838   GNUNET_CRYPTO_eddsa_key_get_public (GST_my_private_key,
839                                       &GST_my_identity.public_key);
840   GNUNET_assert(NULL != GST_my_private_key);
841
842   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
843              "My identity is `%4s'\n",
844              GNUNET_i2s_full (&GST_my_identity));
845
846   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
847                                 &shutdown_task,
848                                 NULL);
849   if (NULL == GST_peerinfo)
850   {
851     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
852         _("Could not access PEERINFO service.  Exiting.\n"));
853     GNUNET_SCHEDULER_shutdown ();
854     return;
855   }
856
857   max_fd_rlimit = 0;
858   max_fd_cfg = 0;
859 #if HAVE_GETRLIMIT
860   struct rlimit r_file;
861   if (0 == getrlimit (RLIMIT_NOFILE, &r_file))
862   {
863     max_fd_rlimit = r_file.rlim_cur;
864     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
865         "Maximum number of open files was: %u/%u\n",
866         r_file.rlim_cur,
867         r_file.rlim_max);
868   }
869   max_fd_rlimit = (9 * max_fd_rlimit) / 10; /* Keep 10% for rest of transport */
870 #endif
871   GNUNET_CONFIGURATION_get_value_number (GST_cfg,
872                                          "transport",
873                                          "MAX_FD",
874                                          &max_fd_cfg);
875
876   if (max_fd_cfg > max_fd_rlimit)
877     max_fd = max_fd_cfg;
878   else
879     max_fd = max_fd_rlimit;
880   if (max_fd < DEFAULT_MAX_FDS)
881     max_fd = DEFAULT_MAX_FDS;
882
883   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884               "Limiting number of sockets to %u: validation %u, neighbors: %u\n",
885              max_fd, (max_fd / 3), (max_fd / 3) * 2);
886
887   friend_only = GNUNET_CONFIGURATION_get_value_yesno (GST_cfg,
888                                                       "topology",
889                                                       "FRIENDS-ONLY");
890   if (GNUNET_SYSERR == friend_only)
891     friend_only = GNUNET_NO; /* According to topology defaults */
892   /* start subsystems */
893   GST_blacklist_start (GST_server,
894                        GST_cfg,
895                        &GST_my_identity);
896   GST_is = GNUNET_ATS_scanner_init ();
897   GST_ats_connect = GNUNET_ATS_connectivity_init (GST_cfg);
898   GST_ats = GNUNET_ATS_scheduling_init (GST_cfg,
899                                         &ats_request_address_change,
900                                         NULL);
901   GST_ats_init ();
902   GST_manipulation_init ();
903   GST_plugins_load (&GST_manipulation_recv,
904                     &plugin_env_address_change_notification,
905                     &plugin_env_session_start,
906                     &plugin_env_session_end);
907   GST_hello_start (friend_only,
908                    &process_hello_update,
909                    NULL);
910   GST_neighbours_start ((max_fd / 3) * 2);
911   GST_clients_start (GST_server);
912   GST_validation_start ((max_fd / 3));
913 }
914
915
916 /**
917  * The main function for the transport service.
918  *
919  * @param argc number of arguments from the command line
920  * @param argv command line arguments
921  * @return 0 ok, 1 on error
922  */
923 int
924 main (int argc,
925       char * const *argv)
926 {
927   return
928       (GNUNET_OK
929           == GNUNET_SERVICE_run (argc, argv, "transport",
930               GNUNET_SERVICE_OPTION_NONE, &run, NULL )) ? 0 : 1;
931 }
932
933 /* end of file gnunet-service-transport.c */