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