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