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