allow empty/NULL context message
[oweals/gnunet.git] / src / transport / gnunet-service-transport.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2010-2015 GNUnet e.V.
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                 (unsigned int) 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  */
254 static void
255 kill_session_task (void *cls)
256 {
257   struct GNUNET_ATS_SessionKiller *sk = cls;
258
259   sk->task = NULL;
260   GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk);
261   sk->plugin->disconnect_session (sk->plugin->cls, sk->session);
262   GNUNET_free(sk);
263 }
264
265
266 /**
267  * Force plugin to terminate session due to communication
268  * issue.
269  *
270  * @param plugin_name name of the plugin
271  * @param session session to termiante
272  */
273 static void
274 kill_session (const char *plugin_name,
275               struct GNUNET_ATS_Session *session)
276 {
277   struct GNUNET_TRANSPORT_PluginFunctions *plugin;
278   struct GNUNET_ATS_SessionKiller *sk;
279
280   for (sk = sk_head; NULL != sk; sk = sk->next)
281     if (sk->session == session)
282       return;
283   plugin = GST_plugins_find (plugin_name);
284   if (NULL == plugin)
285   {
286     GNUNET_break(0);
287     return;
288   }
289   /* need to issue disconnect asynchronously */
290   sk = GNUNET_new (struct GNUNET_ATS_SessionKiller);
291   sk->session = session;
292   sk->plugin = plugin;
293   sk->task = GNUNET_SCHEDULER_add_now (&kill_session_task, sk);
294   GNUNET_CONTAINER_DLL_insert (sk_head,
295                                sk_tail,
296                                sk);
297 }
298
299
300 /**
301  * Black list check result for try_connect call
302  * If connection to the peer is allowed request adddress and ???
303  *
304  * @param cls the message
305  * @param peer the peer
306  * @param address the address
307  * @param session the session
308  * @param result the result
309  */
310 static void
311 connect_bl_check_cont (void *cls,
312                        const struct GNUNET_PeerIdentity *peer,
313                        const struct GNUNET_HELLO_Address *address,
314                        struct GNUNET_ATS_Session *session,
315                        int result)
316 {
317   struct GNUNET_MessageHeader *msg = cls;
318
319   if (GNUNET_OK == result)
320   {
321     /* Blacklist allows to speak to this peer, forward SYN to neighbours  */
322     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
323                 "Received SYN message from peer `%s' at `%s'\n",
324                 GNUNET_i2s (peer),
325                 GST_plugins_a2s (address));
326     if (GNUNET_OK !=
327         GST_neighbours_handle_session_syn (msg,
328                                            peer))
329     {
330       GST_blacklist_abort_matching (address,
331                                     session);
332       kill_session (address->transport_name,
333                     session);
334     }
335     GNUNET_free (msg);
336     return;
337   }
338   GNUNET_free (msg);
339   if (GNUNET_SYSERR == result)
340     return; /* check was aborted, session destroyed */
341   /* Blacklist denies to speak to this peer */
342   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
343               "Discarding SYN message from `%s' due to denied blacklist check\n",
344               GNUNET_i2s (peer));
345   kill_session (address->transport_name,
346                 session);
347 }
348
349
350 /**
351  * Function called by the transport for each received message.
352  *
353  * @param cls closure, const char* with the name of the plugin we received the message from
354  * @param address address and (claimed) identity of the other peer
355  * @param message the message, NULL if we only care about
356  *                learning about the delay until we should receive again
357  * @param session identifier used for this session (NULL for plugins
358  *                that do not offer bi-directional communication to the sender
359  *                using the same "connection")
360  * @return how long the plugin should wait until receiving more data
361  *         (plugins that do not support this, can ignore the return value)
362  */
363 struct GNUNET_TIME_Relative
364 GST_receive_callback (void *cls,
365                       const struct GNUNET_HELLO_Address *address,
366                       struct GNUNET_ATS_Session *session,
367                       const struct GNUNET_MessageHeader *message)
368 {
369   const char *plugin_name = cls;
370   struct GNUNET_TIME_Relative ret;
371   uint16_t type;
372
373   ret = GNUNET_TIME_UNIT_ZERO;
374   if (NULL == message)
375     goto end;
376   type = ntohs (message->type);
377   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
378               "Received message with type %u from peer `%s'\n",
379               type,
380               GNUNET_i2s (&address->peer));
381
382   GNUNET_STATISTICS_update (GST_stats,
383                             gettext_noop ("# bytes total received"),
384                             ntohs (message->size),
385                             GNUNET_NO);
386   GST_neighbours_notify_data_recv (address,
387                                    message);
388   switch (type)
389   {
390   case GNUNET_MESSAGE_TYPE_HELLO_LEGACY:
391     /* Legacy HELLO message, discard  */
392     return ret;
393   case GNUNET_MESSAGE_TYPE_HELLO:
394     if (GNUNET_OK != GST_validation_handle_hello (message))
395     {
396       GNUNET_break_op (0);
397       GST_blacklist_abort_matching (address,
398                                     session);
399     }
400     return ret;
401   case GNUNET_MESSAGE_TYPE_TRANSPORT_PING:
402     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403                 "Processing PING from `%s'\n",
404                 GST_plugins_a2s (address));
405     if (GNUNET_OK !=
406         GST_validation_handle_ping (&address->peer,
407                                     message,
408                                     address,
409                                     session))
410     {
411       GST_blacklist_abort_matching (address,
412                                     session);
413       kill_session (plugin_name,
414                     session);
415     }
416     break;
417   case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG:
418     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
419                "Processing PONG from `%s'\n",
420                GST_plugins_a2s (address));
421     if (GNUNET_OK != GST_validation_handle_pong (&address->peer, message))
422     {
423       GNUNET_break_op (0);
424       GST_blacklist_abort_matching (address,
425                                     session);
426       kill_session (plugin_name, session);
427     }
428     break;
429   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN:
430     /* Do blacklist check if communication with this peer is allowed */
431     (void) GST_blacklist_test_allowed (&address->peer,
432                                        NULL,
433                                        &connect_bl_check_cont,
434                                        GNUNET_copy_message (message),
435                                        address,
436                                        session);
437     break;
438   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK:
439     if (GNUNET_OK !=
440         GST_neighbours_handle_session_syn_ack (message,
441                                                address,
442                                                session))
443     {
444       GST_blacklist_abort_matching (address, session);
445       kill_session (plugin_name, session);
446     }
447     break;
448   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK:
449     if (GNUNET_OK !=
450         GST_neighbours_handle_session_ack (message,
451                                            address,
452                                            session))
453     {
454       GNUNET_break_op(0);
455       GST_blacklist_abort_matching (address, session);
456       kill_session (plugin_name, session);
457     }
458     break;
459   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT:
460     GST_neighbours_handle_disconnect_message (&address->peer,
461                                               message);
462     break;
463   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA:
464     GST_neighbours_handle_quota_message (&address->peer,
465                                          message);
466     break;
467   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE:
468     GST_neighbours_keepalive (&address->peer,
469                               message);
470     break;
471   case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE:
472     GST_neighbours_keepalive_response (&address->peer,
473                                        message);
474     break;
475   default:
476     /* should be payload */
477     GNUNET_STATISTICS_update (GST_stats,
478                               gettext_noop ("# bytes payload received"),
479                               ntohs (message->size),
480                               GNUNET_NO);
481     ret = process_payload (address,
482                            session,
483                            message);
484     break;
485   }
486  end:
487   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488               "Allowing receive from peer %s to continue in %s\n",
489               GNUNET_i2s (&address->peer),
490               GNUNET_STRINGS_relative_time_to_string (ret,
491                                                       GNUNET_YES));
492   return ret;
493 }
494
495
496 /**
497  * Function that will be called for each address the transport
498  * is aware that it might be reachable under.  Update our HELLO.
499  *
500  * @param cls name of the plugin (const char*)
501  * @param add_remove should the address added (YES) or removed (NO) from the
502  *                   set of valid addresses?
503  * @param address the address to add or remove
504  */
505 static void
506 plugin_env_address_change_notification (void *cls,
507                                         int add_remove,
508                                         const struct GNUNET_HELLO_Address *address)
509 {
510   static int addresses = 0;
511   struct GNUNET_STATISTICS_Handle *cfg = GST_stats;
512
513   if (GNUNET_YES == add_remove)
514   {
515     addresses ++;
516     GNUNET_STATISTICS_update (cfg,
517                               "# transport addresses",
518                               1,
519                               GNUNET_NO);
520   }
521   else if (GNUNET_NO == add_remove)
522   {
523     if (0 == addresses)
524     {
525       GNUNET_break (0);
526     }
527     else
528     {
529       addresses --;
530       GNUNET_STATISTICS_update (cfg,
531                                 "# transport addresses",
532                                 -1,
533                                 GNUNET_NO);
534     }
535   }
536   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
537               "Transport now has %u addresses to communicate\n",
538               addresses);
539   GST_hello_modify_addresses (add_remove,
540                               address);
541 }
542
543
544 /**
545  * Function that will be called whenever the plugin internally
546  * cleans up a session pointer and hence the service needs to
547  * discard all of those sessions as well.  Plugins that do not
548  * use sessions can simply omit calling this function and always
549  * use NULL wherever a session pointer is needed.  This function
550  * should be called BEFORE a potential "TransmitContinuation"
551  * from the "TransmitFunction".
552  *
553  * @param cls closure
554  * @param address which address was the session for
555  * @param session which session is being destoyed
556  */
557 static void
558 plugin_env_session_end (void *cls,
559                         const struct GNUNET_HELLO_Address *address,
560                         struct GNUNET_ATS_Session *session)
561 {
562   struct GNUNET_ATS_SessionKiller *sk;
563
564   if (NULL == address)
565   {
566     GNUNET_break (0);
567     return;
568   }
569   if (NULL == session)
570   {
571     GNUNET_break (0);
572     return;
573   }
574   GNUNET_assert (strlen (address->transport_name) > 0);
575
576   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
577               "Notification from plugin about terminated session %p from peer `%s' address `%s'\n",
578               session,
579               GNUNET_i2s (&address->peer),
580               GST_plugins_a2s (address));
581
582   GST_neighbours_session_terminated (&address->peer, session);
583   GST_ats_del_session (address,
584                        session);
585   GST_blacklist_abort_matching (address, session);
586
587   for (sk = sk_head; NULL != sk; sk = sk->next)
588   {
589     if (sk->session == session)
590     {
591       GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk);
592       GNUNET_SCHEDULER_cancel (sk->task);
593       GNUNET_free(sk);
594       break;
595     }
596   }
597 }
598
599
600 /**
601  * Black list check result from blacklist check triggered when a
602  * plugin gave us a new session in #plugin_env_session_start().  If
603  * connection to the peer is disallowed, kill the session.
604  *
605  * @param cls NULL
606  * @param peer the peer
607  * @param address address associated with the request
608  * @param session session associated with the request
609  * @param result the result
610  */
611 static void
612 plugin_env_session_start_bl_check_cont (void *cls,
613                                         const struct GNUNET_PeerIdentity *peer,
614                                         const struct GNUNET_HELLO_Address *address,
615                                         struct GNUNET_ATS_Session *session,
616                                         int result)
617 {
618   if (GNUNET_OK != result)
619   {
620     kill_session (address->transport_name,
621                   session);
622     return;
623   }
624   if (GNUNET_YES !=
625       GNUNET_HELLO_address_check_option (address,
626                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
627   {
628     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629                 "Informing verifier about inbound session's address `%s'\n",
630                 GST_plugins_a2s (address));
631     GST_validation_handle_address (address);
632   }
633 }
634
635
636 /**
637  * Plugin tells transport service about a new inbound session
638  *
639  * @param cls unused
640  * @param address the address
641  * @param session the new session
642  * @param scope network scope information
643  */
644 static void
645 plugin_env_session_start (void *cls,
646                           const struct GNUNET_HELLO_Address *address,
647                           struct GNUNET_ATS_Session *session,
648                           enum GNUNET_ATS_Network_Type scope)
649 {
650   struct GNUNET_ATS_Properties prop;
651
652   if (NULL == address)
653   {
654     GNUNET_break(0);
655     return;
656   }
657   if (NULL == session)
658   {
659     GNUNET_break(0);
660     return;
661   }
662   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
663               "Notification from plugin `%s' about new session from peer `%s' address `%s'\n",
664               address->transport_name,
665               GNUNET_i2s (&address->peer),
666               GST_plugins_a2s (address));
667   if (GNUNET_YES ==
668       GNUNET_HELLO_address_check_option (address,
669                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
670   {
671     /* inbound is always new, but outbound MAY already be known, but
672        for example for UNIX, we have symmetric connections and thus we
673        may not know the address yet; add if necessary! */
674     /* FIXME: maybe change API here so we just pass scope? */
675     memset (&prop, 0, sizeof (prop));
676     GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != scope);
677     prop.scope = scope;
678     GST_ats_add_inbound_address (address,
679                                  session,
680                                  &prop);
681   }
682   /* Do blacklist check if communication with this peer is allowed */
683   (void) GST_blacklist_test_allowed (&address->peer,
684                                      address->transport_name,
685                                      &plugin_env_session_start_bl_check_cont,
686                                      NULL,
687                                      address,
688                                      session);
689 }
690
691
692 /**
693  * Function called by ATS to notify the callee that the
694  * assigned bandwidth or address for a given peer was changed.  If the
695  * callback is called with address/bandwidth assignments of zero, the
696  * ATS disconnect function will still be called once the disconnect
697  * actually happened.
698  *
699  * @param cls closure
700  * @param peer the peer this address is intended for
701  * @param address address to use (for peer given in address)
702  * @param session session to use (if available)
703  * @param bandwidth_out assigned outbound bandwidth for the connection in NBO,
704  *      0 to disconnect from peer
705  * @param bandwidth_in assigned inbound bandwidth for the connection in NBO,
706  *      0 to disconnect from peer
707  * @param ats ATS information
708  * @param ats_count number of @a ats elements
709  */
710 static void
711 ats_request_address_change (void *cls,
712                             const struct GNUNET_PeerIdentity *peer,
713                             const struct GNUNET_HELLO_Address *address,
714                             struct GNUNET_ATS_Session *session,
715                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
716                             struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
717 {
718   uint32_t bw_in = ntohl (bandwidth_in.value__);
719   uint32_t bw_out = ntohl (bandwidth_out.value__);
720
721   if (NULL == peer)
722   {
723     /* ATS service died, all suggestions become invalid!
724        (but we'll keep using the allocations for a little
725        while, to keep going while ATS restarts) */
726     /* FIXME: We should drop all
727        connections now, as ATS won't explicitly tell
728        us and be unaware of ongoing resource allocations! */
729     return;
730   }
731   /* ATS tells me to disconnect from peer */
732   if ((0 == bw_in) && (0 == bw_out))
733   {
734     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
735                 "ATS tells me to disconnect from peer `%s'\n",
736                 GNUNET_i2s (peer));
737     GST_neighbours_force_disconnect (peer);
738     return;
739   }
740   GNUNET_assert (NULL != address);
741   GNUNET_STATISTICS_update (GST_stats,
742                             "# ATS suggestions received",
743                             1,
744                             GNUNET_NO);
745   GST_neighbours_switch_to_address (address,
746                                     session,
747                                     bandwidth_in,
748                                     bandwidth_out);
749 }
750
751
752 /**
753  * Function called when the service shuts down.  Unloads our plugins
754  * and cancels pending validations.
755  *
756  * @param cls closure, unused
757  */
758 static void
759 shutdown_task (void *cls)
760 {
761   GST_neighbours_stop ();
762   GST_plugins_unload ();
763   GST_validation_stop ();
764   GST_ats_done ();
765   GNUNET_ATS_scheduling_done (GST_ats);
766   GST_ats = NULL;
767   GNUNET_ATS_connectivity_done (GST_ats_connect);
768   GST_ats_connect = NULL;
769   GNUNET_ATS_scanner_done (GST_is);
770   GST_is = NULL;
771   GST_clients_stop ();
772   GST_blacklist_stop ();
773   GST_hello_stop ();
774   GST_manipulation_stop ();
775
776   if (NULL != GST_peerinfo)
777   {
778     GNUNET_PEERINFO_disconnect (GST_peerinfo);
779     GST_peerinfo = NULL;
780   }
781   if (NULL != GST_stats)
782   {
783     GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO);
784     GST_stats = NULL;
785   }
786   if (NULL != GST_my_private_key)
787   {
788     GNUNET_free(GST_my_private_key);
789     GST_my_private_key = NULL;
790   }
791   GST_server = NULL;
792 }
793
794
795 /**
796  * Initiate transport service.
797  *
798  * @param cls closure
799  * @param server the initialized server
800  * @param c configuration to use
801  */
802 static void
803 run (void *cls,
804      struct GNUNET_SERVER_Handle *server,
805      const struct GNUNET_CONFIGURATION_Handle *c)
806 {
807   char *keyfile;
808   struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
809   long long unsigned int max_fd_cfg;
810   int max_fd_rlimit;
811   int max_fd;
812   int friend_only;
813
814   /* setup globals */
815   GST_cfg = c;
816   if (GNUNET_OK !=
817       GNUNET_CONFIGURATION_get_value_filename (c,
818                                                "PEER",
819                                                "PRIVATE_KEY",
820                                                &keyfile))
821   {
822     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
823         _("Transport service is lacking key configuration settings. Exiting.\n"));
824     GNUNET_SCHEDULER_shutdown ();
825     return;
826   }
827   if (GNUNET_OK !=
828       GNUNET_CONFIGURATION_get_value_time (c,
829                                            "transport",
830                                            "HELLO_EXPIRATION",
831                                            &hello_expiration))
832   {
833     hello_expiration = GNUNET_CONSTANTS_HELLO_ADDRESS_EXPIRATION;
834   }
835   GST_server = server;
836   pk = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile);
837   GNUNET_free (keyfile);
838   GNUNET_assert (NULL != pk);
839   GST_my_private_key = pk;
840
841   GST_stats = GNUNET_STATISTICS_create ("transport", GST_cfg);
842   GST_peerinfo = GNUNET_PEERINFO_connect (GST_cfg);
843   GNUNET_CRYPTO_eddsa_key_get_public (GST_my_private_key,
844                                       &GST_my_identity.public_key);
845   GNUNET_assert(NULL != GST_my_private_key);
846
847   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
848              "My identity is `%4s'\n",
849              GNUNET_i2s_full (&GST_my_identity));
850
851   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
852                                  NULL);
853   if (NULL == GST_peerinfo)
854   {
855     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
856         _("Could not access PEERINFO service.  Exiting.\n"));
857     GNUNET_SCHEDULER_shutdown ();
858     return;
859   }
860
861   max_fd_rlimit = 0;
862   max_fd_cfg = 0;
863 #if HAVE_GETRLIMIT
864   struct rlimit r_file;
865   if (0 == getrlimit (RLIMIT_NOFILE, &r_file))
866   {
867     max_fd_rlimit = r_file.rlim_cur;
868     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
869                 "Maximum number of open files was: %u/%u\n",
870                 (unsigned int) r_file.rlim_cur,
871                 (unsigned int) r_file.rlim_max);
872   }
873   max_fd_rlimit = (9 * max_fd_rlimit) / 10; /* Keep 10% for rest of transport */
874 #endif
875   GNUNET_CONFIGURATION_get_value_number (GST_cfg,
876                                          "transport",
877                                          "MAX_FD",
878                                          &max_fd_cfg);
879
880   if (max_fd_cfg > max_fd_rlimit)
881     max_fd = max_fd_cfg;
882   else
883     max_fd = max_fd_rlimit;
884   if (max_fd < DEFAULT_MAX_FDS)
885     max_fd = DEFAULT_MAX_FDS;
886
887   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
888               "Limiting number of sockets to %u: validation %u, neighbors: %u\n",
889              max_fd, (max_fd / 3), (max_fd / 3) * 2);
890
891   friend_only = GNUNET_CONFIGURATION_get_value_yesno (GST_cfg,
892                                                       "topology",
893                                                       "FRIENDS-ONLY");
894   if (GNUNET_SYSERR == friend_only)
895     friend_only = GNUNET_NO; /* According to topology defaults */
896   /* start subsystems */
897   GST_blacklist_start (GST_server,
898                        GST_cfg,
899                        &GST_my_identity);
900   GST_is = GNUNET_ATS_scanner_init ();
901   GST_ats_connect = GNUNET_ATS_connectivity_init (GST_cfg);
902   GST_ats = GNUNET_ATS_scheduling_init (GST_cfg,
903                                         &ats_request_address_change,
904                                         NULL);
905   GST_ats_init ();
906   GST_manipulation_init ();
907   GST_plugins_load (&GST_manipulation_recv,
908                     &plugin_env_address_change_notification,
909                     &plugin_env_session_start,
910                     &plugin_env_session_end);
911   GST_hello_start (friend_only,
912                    &process_hello_update,
913                    NULL);
914   GST_neighbours_start ((max_fd / 3) * 2);
915   GST_clients_start (GST_server);
916   GST_validation_start ((max_fd / 3));
917 }
918
919
920 /**
921  * The main function for the transport service.
922  *
923  * @param argc number of arguments from the command line
924  * @param argv command line arguments
925  * @return 0 ok, 1 on error
926  */
927 int
928 main (int argc,
929       char * const *argv)
930 {
931   return
932       (GNUNET_OK
933           == GNUNET_SERVICE_run (argc, argv, "transport",
934               GNUNET_SERVICE_OPTION_NONE, &run, NULL )) ? 0 : 1;
935 }
936
937 /* end of file gnunet-service-transport.c */