ff93f4145da9b871a18101ea53a78ad11f4b0079
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
1 /*
2      This file is part of GNUnet.
3      (C) 2007, 2008, 2009, 2010 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 2, 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 topology/gnunet-daemon-topology.c
23  * @brief code for maintaining the mesh topology
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - make sure that PEERINFO *also* notifies us when a HELLO expires
28  *   (otherwise the 'soft state' of this module does not work nicely)
29  * - CORE API's connect/disconnect API is not nice (we think we are
30  *   connected when we are not; disconnect using bandwidth-limiting
31  *   does not really work and is not nice)
32  * OPTIMIZATIONS:
33  * - move code to use hash table instead of linked list
34  * - instead of periodically discarding blacklisted entries,
35  *   simply add task that is triggered at the right time (earlier free,
36  *   more balanced load)
37  * - check if new HELLO learned is different from old HELLO
38  *   before resetting entire state!
39  */
40
41 #include <stdlib.h>
42 #include "platform.h"
43 #include "gnunet_core_service.h"
44 #include "gnunet_protocols.h"
45 #include "gnunet_peerinfo_service.h"
46 #include "gnunet_transport_service.h"
47 #include "gnunet_util_lib.h"
48
49
50 #define DEBUG_TOPOLOGY GNUNET_NO
51
52 /**
53  * For how long do we blacklist a peer after a failed connection
54  * attempt?
55  */
56 #define BLACKLIST_AFTER_ATTEMPT GNUNET_TIME_UNIT_HOURS
57
58 /**
59  * For how long do we blacklist a friend after a failed connection
60  * attempt?
61  */
62 #define BLACKLIST_AFTER_ATTEMPT_FRIEND GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
63
64 /**
65  * How often do we at most advertise any HELLO to a peer?
66  */
67 #define HELLO_ADVERTISEMENT_MIN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
68
69 /**
70  * How often do we at most advertise the same HELLO to the same peer?
71  */
72 #define HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
73
74
75 /**
76  * List of neighbours, friends and blacklisted peers.
77  */
78 struct PeerList
79 {
80
81   /**
82    * This is a linked list.
83    */
84   struct PeerList *next;
85
86   /**
87    * Our handle for the request to transmit HELLOs to this peer; NULL
88    * if no such request is pending.
89    */
90   struct GNUNET_CORE_TransmitHandle *hello_req;  
91
92   /**
93    * Our handle for the request to connect to this peer; NULL if no
94    * such request is pending.
95    */
96   struct GNUNET_CORE_TransmitHandle *connect_req;  
97
98   /**
99    * Pointer to the HELLO message of this peer; can be NULL.
100    */
101   struct GNUNET_HELLO_Message *hello;
102
103   /**
104    * Bloom filter used to mark which peers already got the HELLO
105    * from this peer.
106    */
107   struct GNUNET_CONTAINER_BloomFilter *filter;
108
109   /**
110    * Is this peer listed here because he is a friend?
111    */
112   int is_friend;
113
114   /**
115    * Are we connected to this peer right now?
116    */
117   int is_connected;
118
119   /**
120    * Until what time should we not try to connect again
121    * to this peer?
122    */
123   struct GNUNET_TIME_Absolute blacklisted_until;
124
125   /**
126    * Next time we are allowed to transmit a HELLO to this peer?
127    */
128   struct GNUNET_TIME_Absolute next_hello_allowed;
129
130   /**
131    * When should we reset the bloom filter of this entry?
132    */
133   struct GNUNET_TIME_Absolute filter_expiration;
134
135   /**
136    * ID of task we use to wait for the time to send the next HELLO
137    * to this peer.
138    */
139   GNUNET_SCHEDULER_TaskIdentifier hello_delay_task;
140
141   /**
142    * ID of the peer.
143    */
144   struct GNUNET_PeerIdentity id;
145
146 };
147
148
149 /**
150  * Entry in linked list of active 'disconnect' requests that we have issued.
151  */
152 struct DisconnectList
153 {
154   /**
155    * This is a doubly-linked list.
156    */
157   struct DisconnectList *next;
158
159   /**
160    * This is a doubly-linked list.
161    */
162   struct DisconnectList *prev;
163   
164   /**
165    * Our request handle.
166    */
167   struct GNUNET_CORE_PeerRequestHandle *rh;
168
169   /**
170    * Peer we tried to disconnect.
171    */
172   struct GNUNET_PeerIdentity peer;
173
174 };
175
176
177 /**
178  * Our peerinfo notification context.  We use notification
179  * to instantly learn about new peers as they are discovered.
180  */
181 static struct GNUNET_PEERINFO_NotifyContext *peerinfo_notify;
182
183 /**
184  * Our scheduler.
185  */
186 static struct GNUNET_SCHEDULER_Handle *sched;
187
188 /**
189  * Our configuration.
190  */
191 static const struct GNUNET_CONFIGURATION_Handle *cfg;
192
193 /**
194  * Handle to the core API.
195  */
196 static struct GNUNET_CORE_Handle *handle;
197
198 /**
199  * Handle to the transport API.
200  */
201 static struct GNUNET_TRANSPORT_Handle *transport;
202
203 /**
204  * Identity of this peer.
205  */
206 static struct GNUNET_PeerIdentity my_identity;
207
208 /**
209  * Linked list of all of our friends, all of our current neighbours
210  * and all peers for which we have HELLOs.  So pretty much everyone.
211  */
212 static struct PeerList *peers;
213
214 /**
215  * Flag to disallow non-friend connections (pure F2F mode).
216  */
217 static int friends_only;
218
219 /**
220  * Minimum number of friends to have in the
221  * connection set before we allow non-friends.
222  */
223 static unsigned int minimum_friend_count;
224
225 /**
226  * Number of peers (friends and others) that we are currently connected to.
227  */
228 static unsigned int connection_count;
229
230 /**
231  * Target number of connections.
232  */
233 static unsigned int target_connection_count;
234
235 /**
236  * Number of friends that we are currently connected to.
237  */
238 static unsigned int friend_count;
239
240 /**
241  * Should the topology daemon try to establish connections?
242  */
243 static int autoconnect;
244
245 /**
246  * Head of doubly-linked list of active 'disconnect' requests that we have issued.
247  */
248 static struct DisconnectList *disconnect_head;
249
250 /**
251  * Head of doubly-linked list of active 'disconnect' requests that we have issued.
252  */
253 static struct DisconnectList *disconnect_tail;
254
255
256 /**
257  * Function called once our request to 'disconnect' a peer
258  * has completed.
259  *
260  * @param cls our 'struct DisconnectList'
261  * @param tc unused
262  */
263 static void
264 disconnect_done (void *cls,
265                  const struct GNUNET_SCHEDULER_TaskContext *tc)
266 {
267   struct DisconnectList *dl = cls;
268
269   GNUNET_CONTAINER_DLL_remove (disconnect_head,
270                                disconnect_tail,
271                                dl);
272   GNUNET_free (dl);
273 }
274
275
276 /**
277  * Force a disconnect from the specified peer.  This is currently done by
278  * changing the bandwidth policy to 0 bytes per second.  
279  * FIXME: maybe we want a nicer CORE API for both connect and disconnect...
280  * FIXME: this policy change is never undone; how do we reconnect ever?
281  */
282 static void
283 force_disconnect (const struct GNUNET_PeerIdentity *peer)
284 {
285   struct DisconnectList *dl;
286
287   dl = GNUNET_malloc (sizeof (struct DisconnectList));
288   dl->peer = *peer;
289   GNUNET_CONTAINER_DLL_insert (disconnect_head,
290                                disconnect_tail,
291                                dl);
292   dl->rh = GNUNET_CORE_peer_request_disconnect (sched, cfg,                                             
293                                                 GNUNET_TIME_UNIT_FOREVER_REL,
294                                                 peer,
295                                                 &disconnect_done,
296                                                 dl);
297 }
298
299
300 /**
301  * Function called by core when our attempt to connect succeeded.
302  * Transmits a 'DUMMY' message to trigger the session key exchange.
303  * FIXME: this is an issue with the current CORE API.
304  */
305 static size_t
306 ready_callback (void *cls,
307                 size_t size, void *buf)
308 {
309   struct PeerList *pos = cls;
310   struct GNUNET_MessageHeader hdr;
311
312   pos->connect_req = NULL;
313   if (buf == NULL)
314     {
315 #if DEBUG_TOPOLOGY
316       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317                   "Core told us that our attempt to connect failed.\n");
318 #endif
319       return 0;
320     }
321 #if DEBUG_TOPOLOGY
322   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
323               "Sending dummy message to establish connection.\n");
324 #endif
325   hdr.size = htons (sizeof (struct GNUNET_MessageHeader));
326   hdr.type = htons (GNUNET_MESSAGE_TYPE_TOPOLOGY_DUMMY);
327   memcpy (buf, &hdr, sizeof (struct GNUNET_MessageHeader));
328   return sizeof (struct GNUNET_MessageHeader);
329 }
330
331
332 /**
333  * Try to connect to the specified peer.
334  *
335  * @param pos peer to connect to
336  */
337 static void
338 attempt_connect (struct PeerList *pos)
339 {
340   if (GNUNET_YES == pos->is_friend)
341     pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT_FRIEND);
342   else
343     pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT);
344 #if DEBUG_TOPOLOGY
345   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
346               "Asking core to connect to `%s'\n",
347               GNUNET_i2s (&pos->id));
348 #endif
349   pos->connect_req = GNUNET_CORE_notify_transmit_ready (handle,
350                                                         0 /* priority */,
351                                                         GNUNET_TIME_UNIT_MINUTES,
352                                                         &pos->id,
353                                                         sizeof(struct GNUNET_MessageHeader),
354                                                         &ready_callback,
355                                                         pos);
356 }
357
358
359 /**
360  * Find a peer in our linked list.  
361  * FIXME: should probably use a hash map instead.
362  */
363 struct PeerList *
364 find_peer (const struct GNUNET_PeerIdentity * peer)
365 {
366   struct PeerList *pos;
367
368   pos = peers;
369   while (pos != NULL)
370     {
371       if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
372         return pos;
373       pos = pos->next;
374     }
375   return NULL;
376 }
377
378
379 /**
380  * Check if an additional connection from the given peer is allowed.
381  */
382 static int
383 is_connection_allowed (struct PeerList *peer)
384 {
385   if (0 == memcmp (&my_identity, &peer->id, sizeof (struct GNUNET_PeerIdentity)))
386     return GNUNET_SYSERR;       /* disallow connections to self */
387   if (peer->is_friend)
388     return GNUNET_OK;
389   if (GNUNET_YES == friends_only)
390     {
391 #if DEBUG_TOPOLOGY
392       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393                   "Determined that `%s' is not allowed to connect (not a friend)\n",
394                   GNUNET_i2s (&peer->id));
395 #endif       
396       return GNUNET_SYSERR;
397     }
398   if (friend_count >= minimum_friend_count)
399     return GNUNET_OK;
400 #if DEBUG_TOPOLOGY
401   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402               "Determined that `%s' is not allowed to connect (not enough connected friends)\n",
403               GNUNET_i2s (&peer->id));
404 #endif       
405   return GNUNET_SYSERR;
406 }
407
408
409 /**
410  * Create a new entry in the peer list.
411  *
412  * @param peer identity of the new entry
413  * @param hello hello message, can be NULL
414  * @param is_friend is the new entry for a friend?
415  * @return the new entry
416  */
417 static struct PeerList *
418 make_peer (const struct
419            GNUNET_PeerIdentity * peer,
420            const struct GNUNET_HELLO_Message *hello,
421            int is_friend)
422 {
423   struct PeerList *ret;
424   
425   ret = GNUNET_malloc (sizeof (struct PeerList));
426   ret->id = *peer;
427   ret->is_friend = is_friend;
428   if (hello != NULL)
429     {
430       ret->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
431       memcpy (ret->hello, hello,
432               GNUNET_HELLO_size (hello));
433     }
434   ret->next = peers;
435   peers = ret;
436   return ret;
437 }
438
439
440 /**
441  * Free all resources associated with the given peer.
442  *
443  * @param peer peer to free
444  */
445 static void
446 free_peer (struct PeerList *peer)
447 {
448   struct PeerList *pos;
449   struct PeerList *prev;
450   
451   prev = NULL;
452   pos = peers;
453   while (peer != pos)
454     {
455       prev = pos;
456       pos = pos->next;
457     }
458   GNUNET_assert (pos != NULL);
459    if (prev == NULL)
460      peers = pos->next;
461    else
462      prev->next = pos->next;
463    if (pos->hello_req != NULL)
464      GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
465    if (pos->connect_req != NULL)
466      GNUNET_CORE_notify_transmit_ready_cancel (pos->connect_req);             
467    if (pos->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
468      GNUNET_SCHEDULER_cancel (sched,
469                               pos->hello_delay_task);
470    GNUNET_free (pos);
471 }
472
473
474 /**
475  * Setup bloom filter for the given peer entry.
476  *
477  * @param peer entry to initialize
478  */
479 static void
480 setup_filter (struct PeerList *peer)
481 {
482   /* 2^{-5} chance of not sending a HELLO to a peer is
483      acceptably small (if the filter is 50% full);
484      64 bytes of memory are small compared to the rest
485      of the data structure and would only really become
486      "useless" once a HELLO has been passed on to ~100
487      other peers, which is likely more than enough in
488      any case; hence 64, 5 as bloomfilter parameters. */
489   peer->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, 64, 5);
490   peer->filter_expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY);
491   /* never send a peer its own HELLO */
492   GNUNET_CONTAINER_bloomfilter_add (peer->filter, &peer->id.hashPubKey);
493 }
494
495
496 /**
497  * Function to fill send buffer with HELLO.
498  *
499  * @param cls 'struct PeerList' of the target peer
500  * @param size number of bytes available in buf
501  * @param buf where the callee should write the message
502  * @return number of bytes written to buf
503  */
504 static size_t
505 hello_advertising_ready (void *cls,
506                          size_t size,
507                          void *buf);
508
509
510 /**
511  * Calculate when we would like to send the next HELLO to this
512  * peer and ask for it.
513  *
514  * @param cls for which peer to schedule the HELLO
515  * @param tc task context
516  */
517 static void
518 schedule_next_hello (void *cls,
519                      const struct GNUNET_SCHEDULER_TaskContext *tc)
520 {
521   struct PeerList *pl = cls;
522   struct PeerList *pos;
523   struct PeerList *next;
524   uint16_t next_want;
525   struct GNUNET_TIME_Relative next_adv;
526   struct GNUNET_TIME_Relative rst_time;
527   
528   pl->hello_delay_task = GNUNET_SCHEDULER_NO_TASK;
529   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
530     return; /* we're out of here */
531   next_want = 0;
532   next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
533   /* find applicable HELLOs */
534   next = peers;
535   while (NULL != (pos = next))
536     {
537       next = pos->next;
538       if (pos->hello == NULL)
539         continue;
540       rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration);
541       if (0 == rst_time.value)
542         {
543           /* time to discard... */
544           GNUNET_CONTAINER_bloomfilter_free (pos->filter);
545           setup_filter (pos);
546         }
547       else
548         {
549           if (rst_time.value < next_adv.value)
550             next_want = GNUNET_HELLO_size (pos->hello);
551           next_adv = GNUNET_TIME_relative_min (rst_time,
552                                                next_adv);         
553         }
554       if (GNUNET_NO ==
555           GNUNET_CONTAINER_bloomfilter_test (pos->filter,
556                                              &pl->id.hashPubKey))
557         break;
558     }
559   if (pos != NULL)  
560     next_adv = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
561   if (next_adv.value == 0)
562     {
563       /* now! */
564       pl->hello_req = GNUNET_CORE_notify_transmit_ready (handle, 0,
565                                                          next_adv,
566                                                          &pl->id,
567                                                          next_want,
568                                                          &hello_advertising_ready,
569                                                          pl);
570       return;
571     }
572   pl->hello_delay_task 
573     = GNUNET_SCHEDULER_add_delayed (sched,
574                                     next_adv,
575                                     &schedule_next_hello,
576                                     pl);
577 }
578
579
580 /**
581  * Cancel existing requests for sending HELLOs to this peer
582  * and recalculate when we should send HELLOs to it based
583  * on our current state (something changed!).
584  */
585 static void
586 reschedule_hellos (struct PeerList *peer)
587 {
588   if (peer->hello_req != NULL)
589     {
590       GNUNET_CORE_notify_transmit_ready_cancel (peer->hello_req);
591       peer->hello_req = NULL;
592     }
593    if (peer->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
594      {
595        GNUNET_SCHEDULER_cancel (sched,
596                                 peer->hello_delay_task);
597        peer->hello_delay_task = GNUNET_SCHEDULER_NO_TASK;
598      }
599    peer->hello_delay_task 
600      = GNUNET_SCHEDULER_add_now (sched,
601                                  &schedule_next_hello,
602                                  peer);
603 }
604
605
606 /**
607  * Method called whenever a peer connects.
608  *
609  * @param cls closure
610  * @param peer peer identity this notification is about
611  * @param latency reported latency of the connection with 'other'
612  * @param distance reported distance (DV) to 'other' 
613  */
614 static void 
615 connect_notify (void *cls,
616                 const struct
617                 GNUNET_PeerIdentity * peer,
618                 struct GNUNET_TIME_Relative latency,
619                 uint32_t distance)
620 {
621   struct PeerList *pos;
622
623 #if DEBUG_TOPOLOGY
624   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625               "Core told us that we are connecting to `%s'\n",
626               GNUNET_i2s (peer));
627 #endif
628   connection_count++;
629   pos = find_peer (peer);
630   if (pos == NULL)    
631     {
632       pos = make_peer (peer, NULL, GNUNET_NO);
633       if (GNUNET_OK != is_connection_allowed (pos))
634         {
635           GNUNET_assert (pos->is_friend == GNUNET_NO);
636           pos->is_connected = GNUNET_YES;
637 #if DEBUG_TOPOLOGY
638           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639                       "Connection to `%s' is forbidden, forcing disconnect!\n",
640                       GNUNET_i2s (peer));
641 #endif       
642           force_disconnect (&pos->id);
643           return;
644         }
645     }
646   else
647     {
648       GNUNET_assert (GNUNET_NO == pos->is_connected);
649       pos->blacklisted_until.value = 0; /* remove blacklisting */
650     }
651   pos->is_connected = GNUNET_YES;
652   if (pos->is_friend)
653     friend_count++;
654   reschedule_hellos (pos);
655 }
656
657
658 /**
659  * Disconnect from all non-friends (we're below quota).
660  */
661 static void
662 drop_non_friends ()
663 {
664   struct PeerList *pos;
665
666   pos = peers;
667   while (pos != NULL)
668     {
669       if ( (GNUNET_NO == pos->is_friend) &&
670            (GNUNET_YES == pos->is_connected) )
671         {
672 #if DEBUG_TOPOLOGY
673           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
674                       "Connection to `%s' is not from a friend, forcing disconnect!\n",
675                       GNUNET_i2s (&pos->id));
676 #endif       
677           force_disconnect (&pos->id);
678         }
679       pos = pos->next;
680     }
681 }
682
683
684 /**
685  * Try to add more peers to our connection set.
686  */
687 static void
688 try_add_peers ()
689 {
690   struct PeerList *pos;
691
692   pos = peers;
693   while (pos != NULL)
694     {
695       if ( (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value == 0) &&
696            ( (GNUNET_YES == pos->is_friend) ||
697              (friend_count >= minimum_friend_count) ) &&
698            (GNUNET_YES != pos->is_connected) )
699         attempt_connect (pos);
700       pos = pos->next;
701     }
702 }
703
704
705 /**
706  * Method called whenever a peer disconnects.
707  *
708  * @param cls closure
709  * @param peer peer identity this notification is about
710  */
711 static void 
712 disconnect_notify (void *cls,
713                    const struct
714                    GNUNET_PeerIdentity * peer)
715 {
716   struct PeerList *pos;
717  
718 #if DEBUG_TOPOLOGY
719   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
720               "Core told us that we disconnected from `%s'\n",
721               GNUNET_i2s (peer));
722 #endif       
723   pos = find_peer (peer);
724   if (pos == NULL)
725     {
726       GNUNET_break (0);
727       return;
728     }
729   if (pos->is_connected != GNUNET_YES)
730     {
731       GNUNET_break (0);
732       return;
733     }
734   connection_count--;
735   if (pos->is_friend)
736     friend_count--; 
737   if ( (connection_count < target_connection_count) ||
738        (friend_count < minimum_friend_count) )
739     try_add_peers ();   
740   if (friend_count < minimum_friend_count)
741     {
742       /* disconnect from all non-friends */
743 #if DEBUG_TOPOLOGY
744       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745                   "Not enough friendly connections, dropping all non-friend connections\n");
746 #endif       
747       drop_non_friends ();
748     }
749 }
750
751
752 /**
753  * Iterator called on each address.
754  *
755  * @param cls flag that we will set if we see any addresses
756  * @param tname name of the transport
757  * @param expiration when will the given address expire
758  * @param addr the address of the peer
759  * @param addrlen number of bytes in addr
760  * @return GNUNET_SYSERR always, to terminate iteration
761  */
762 static int
763 address_iterator (void *cls,
764                   const char *tname,
765                   struct GNUNET_TIME_Absolute expiration,
766                   const void *addr, size_t addrlen)
767 {
768   int *flag = cls;
769   *flag = GNUNET_YES;
770   return GNUNET_SYSERR;
771 }
772
773
774 /**
775  * We've gotten a HELLO from another peer.  Consider it for
776  * advertising.
777  */
778 static void
779 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
780 {
781   int have_address;
782   struct GNUNET_PeerIdentity pid;
783   struct PeerList *peer;
784   struct PeerList *pos;
785   uint16_t size;
786
787   have_address = GNUNET_NO;
788   GNUNET_HELLO_iterate_addresses (hello,
789                                   GNUNET_NO,
790                                   &address_iterator,
791                                   &have_address);
792   if (GNUNET_NO == have_address)
793     return; /* no point in advertising this one... */
794   GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
795   peer = find_peer (&pid);
796   if (peer == NULL)
797     peer = make_peer (&pid, hello, GNUNET_NO);
798   // FIXME: check if 'hello' is any different from peer->hello?
799   GNUNET_free_non_null (peer->hello);
800 #if DEBUG_TOPOLOGY
801   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802               "Found `%s' from peer `%s' for advertising\n",
803               "HELLO",
804               GNUNET_i2s (&pid));
805 #endif 
806   size = GNUNET_HELLO_size (hello);
807   peer->hello = GNUNET_malloc (size);
808   memcpy (peer->hello, hello, size);
809   if (peer->filter != NULL)
810     GNUNET_CONTAINER_bloomfilter_free (peer->filter);
811   setup_filter (peer);
812   /* since we have a new HELLO to pick from, re-schedule all
813      HELLO requests that are not bound by the HELLO send rate! */
814   pos = peers;
815   while (NULL != pos)
816     {
817       if (pos != peer)  
818         {
819           if ( (pos->is_connected) &&
820                (GNUNET_TIME_absolute_get_remaining (pos->next_hello_allowed).value <= HELLO_ADVERTISEMENT_MIN_FREQUENCY.value) )
821             reschedule_hellos (pos);    
822         }
823       pos = pos->next;
824     }
825 }
826
827
828 /**
829  * Peerinfo calls this function to let us know about a possible peer
830  * that we might want to connect to.
831  */
832 static void
833 process_peer (void *cls,
834               const struct GNUNET_PeerIdentity *peer,
835               const struct GNUNET_HELLO_Message *hello,
836               uint32_t trust)
837 {
838   struct PeerList *pos;
839
840   GNUNET_assert (peer != NULL);
841   if (0 == memcmp (&my_identity,
842                    peer, sizeof (struct GNUNET_PeerIdentity)))
843     return;  /* that's me! */
844   if (hello == NULL)
845     {
846       /* free existing HELLO, if any */
847       if (NULL != (pos = find_peer (peer)))
848         {
849           GNUNET_free_non_null (pos->hello);
850           pos->hello = NULL;
851           if (pos->filter != NULL)
852             {
853               GNUNET_CONTAINER_bloomfilter_free (pos->filter);
854               pos->filter = NULL;
855             }
856           if ( (! pos->is_connected) &&
857                (! pos->is_friend) &&
858                (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
859             free_peer (pos);
860         }
861       return;
862     }
863   consider_for_advertising (hello);
864   pos = find_peer (peer);  
865   GNUNET_assert (NULL != pos);
866 #if DEBUG_TOPOLOGY
867   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
868               "Considering connecting to peer `%s'\n",
869               GNUNET_i2s (peer));
870 #endif 
871   if (GNUNET_YES == pos->is_connected)
872     {
873 #if DEBUG_TOPOLOGY
874       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
875                   "Already connected to peer `%s'\n",
876                   GNUNET_i2s (peer));
877 #endif 
878       return;
879     }
880   if (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value > 0)
881     {
882 #if DEBUG_TOPOLOGY
883       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884                   "Already tried peer `%s' recently\n",
885                   GNUNET_i2s (peer));
886 #endif 
887       return; /* peer still blacklisted */
888     }
889   if ( (GNUNET_YES == pos->is_friend) ||
890        (GNUNET_YES != friends_only) ||    
891        (friend_count >= minimum_friend_count) )
892     attempt_connect (pos);
893 }
894
895
896 /**
897  * Discard peer entries for blacklisted peers
898  * where the blacklisting has expired.
899  */
900 static void
901 discard_old_blacklist_entries (void *cls,
902                                const struct GNUNET_SCHEDULER_TaskContext *tc)
903 {
904   struct PeerList *pos;
905   struct PeerList *next;
906
907   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
908     return;
909   next = peers;
910   while (NULL != (pos = next))
911     {
912       next = pos->next;
913       if ( (GNUNET_NO == pos->is_friend) &&
914            (GNUNET_NO == pos->is_connected) &&
915            (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
916         free_peer (pos);
917     }
918   GNUNET_SCHEDULER_add_delayed (sched,
919                                 BLACKLIST_AFTER_ATTEMPT,
920                                 &discard_old_blacklist_entries,
921                                 NULL);
922 }
923
924
925 /**
926  * Function called after GNUNET_CORE_connect has succeeded
927  * (or failed for good).
928  *
929  * @param cls closure
930  * @param server handle to the server, NULL if we failed
931  * @param my_id ID of this peer, NULL if we failed
932  * @param publicKey public key of this peer, NULL if we failed
933  */
934 static void
935 core_init (void *cls,
936            struct GNUNET_CORE_Handle * server,
937            const struct GNUNET_PeerIdentity *
938            my_id,
939            const struct
940            GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
941            publicKey)
942 {
943   if (server == NULL)
944     {
945       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
946                   _("Failed to connect to core service, can not manage topology!\n"));
947       GNUNET_SCHEDULER_shutdown (sched);
948       return;
949     }
950   handle = server;
951   my_identity = *my_id;
952 #if DEBUG_TOPOLOGY
953   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
954               "I am peer `%s'\n",
955               GNUNET_i2s (my_id));
956 #endif  
957   GNUNET_SCHEDULER_add_delayed (sched,
958                                 BLACKLIST_AFTER_ATTEMPT,
959                                 &discard_old_blacklist_entries,
960                                 NULL);
961 }
962
963
964 /**
965  * Read the friends file.
966  */
967 static void
968 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
969 {
970   char *fn;
971   char *data;
972   size_t pos;
973   struct GNUNET_PeerIdentity pid;
974   struct stat frstat;
975   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
976   unsigned int entries_found;
977   struct PeerList *fl;
978
979   if (GNUNET_OK !=
980       GNUNET_CONFIGURATION_get_value_filename (cfg,
981                                                "TOPOLOGY",
982                                                "FRIENDS",
983                                                &fn))
984     {
985       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
986                   _("Option `%s' in section `%s' not specified!\n"),
987                   "FRIENDS",
988                   "TOPOLOGY");
989       return;
990     }
991   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
992     GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
993         | GNUNET_DISK_PERM_USER_WRITE);
994   if (0 != STAT (fn, &frstat))
995     {
996       if ((friends_only) || (minimum_friend_count > 0))
997         {
998           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
999                       _("Could not read friends list `%s'\n"), fn);
1000           GNUNET_free (fn);
1001           return;
1002         }
1003     }
1004   if (frstat.st_size == 0)
1005     {
1006       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1007                   _("Friends file `%s' is empty.\n"),
1008                   fn);
1009       GNUNET_free (fn);
1010       return;
1011     }
1012   data = GNUNET_malloc_large (frstat.st_size);
1013   if (frstat.st_size !=
1014       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
1015     {
1016       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1017                   _("Failed to read friends list from `%s'\n"), fn);
1018       GNUNET_free (fn);
1019       GNUNET_free (data);
1020       return;
1021     }
1022   entries_found = 0;
1023   pos = 0;
1024   while ((pos < frstat.st_size) && isspace (data[pos]))
1025     pos++;
1026   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
1027          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
1028     {
1029       memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
1030       if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
1031         {
1032           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1033                       _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
1034                       (unsigned long long) pos);
1035           pos++;
1036           while ((pos < frstat.st_size) && (!isspace (data[pos])))
1037             pos++;
1038           continue;
1039         }
1040       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
1041       if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
1042         {
1043           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1044                       _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
1045                       (unsigned long long) pos,
1046                       &enc);
1047         }
1048       else
1049         {
1050           entries_found++;
1051           fl = make_peer (&pid,
1052                           NULL,
1053                           GNUNET_YES);
1054           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1055                       _("Found friend `%s' in configuration\n"),
1056                       GNUNET_i2s (&fl->id));
1057         }
1058       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
1059       while ((pos < frstat.st_size) && isspace (data[pos]))
1060         pos++;
1061     }
1062   GNUNET_free (data);
1063   GNUNET_free (fn);
1064   if ( (minimum_friend_count > entries_found) &&
1065        (friends_only == GNUNET_NO) )
1066     {
1067       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1068                   _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
1069     }
1070   if ( (minimum_friend_count > target_connection_count) &&
1071        (friends_only == GNUNET_NO) )
1072     {
1073       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1074                   _("More friendly connections required than target total number of connections.\n"));
1075     }
1076 }
1077
1078
1079 /**
1080  * This function is called whenever an encrypted HELLO message is
1081  * received.
1082  *
1083  * @param cls closure
1084  * @param other the other peer involved (sender or receiver, NULL
1085  *        for loopback messages where we are both sender and receiver)
1086  * @param message the actual HELLO message
1087  * @param latency reported latency of the connection with 'other'
1088  * @param distance reported distance (DV) to 'other' 
1089  * @return GNUNET_OK to keep the connection open,
1090  *         GNUNET_SYSERR to close it (signal serious error)
1091  */
1092 static int
1093 handle_encrypted_hello (void *cls,
1094                         const struct GNUNET_PeerIdentity * other,
1095                         const struct GNUNET_MessageHeader *
1096                         message,
1097                         struct GNUNET_TIME_Relative latency,
1098                         uint32_t distance)
1099 {
1100 #if DEBUG_TOPOLOGY
1101   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102               "Received encrypted `%s' from peer `%s'",
1103               "HELLO",
1104               GNUNET_i2s (other));
1105 #endif  
1106   if (transport != NULL)
1107     GNUNET_TRANSPORT_offer_hello (transport,
1108                                   message);
1109   return GNUNET_OK;
1110 }
1111
1112
1113 /**
1114  * Function to fill send buffer with HELLO.
1115  *
1116  * @param cls 'struct PeerList' of the target peer
1117  * @param size number of bytes available in buf
1118  * @param buf where the callee should write the message
1119  * @return number of bytes written to buf
1120  */
1121 static size_t
1122 hello_advertising_ready (void *cls,
1123                          size_t size,
1124                          void *buf)
1125 {
1126   struct PeerList *pl = cls;
1127   struct PeerList *pos; 
1128   struct PeerList *next;
1129   uint16_t want;
1130   size_t hs;
1131
1132   pl->hello_req = NULL;
1133 #if DEBUG_TOPOLOGY
1134   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1135               "Data solicited for `%s', considering sending `%s'",
1136               GNUNET_i2s (&pl->id),
1137               "HELLO");
1138 #endif  
1139   /* find applicable HELLOs */
1140   next = peers;
1141   while (NULL != (pos = next))
1142     {
1143       next = pos->next;
1144       if (pos->hello == NULL)
1145         continue;
1146       if (0 == GNUNET_TIME_absolute_get_remaining (pos->filter_expiration).value)
1147         {
1148           /* time to discard... */
1149           GNUNET_CONTAINER_bloomfilter_free (pos->filter);
1150           setup_filter (pos);
1151         }
1152       if (GNUNET_NO ==
1153           GNUNET_CONTAINER_bloomfilter_test (pos->filter,
1154                                              &pl->id.hashPubKey))
1155         break;
1156     }
1157   want = 0;
1158   if (pos != NULL)
1159     {
1160       hs = GNUNET_HELLO_size (pos->hello);
1161       if (hs < size)
1162         {
1163           want = hs;
1164           memcpy (buf, pos->hello, want);
1165           GNUNET_CONTAINER_bloomfilter_add (pos->filter,
1166                                             &pl->id.hashPubKey);
1167           pl->next_hello_allowed = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
1168 #if DEBUG_TOPOLOGY
1169           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1170                       "Sending %u bytes of `%s's",
1171                       (unsigned int) want,
1172                       "HELLO");
1173 #endif  
1174         }
1175     }
1176   pl->hello_delay_task 
1177     = GNUNET_SCHEDULER_add_now (sched,
1178                                 &schedule_next_hello,
1179                                 pl);
1180   return want;
1181 }
1182
1183
1184 /**
1185  * Last task run during shutdown.  Disconnects us from
1186  * the transport and core.
1187  */
1188 static void
1189 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1190 {
1191   struct DisconnectList *dl;
1192
1193   if (NULL != peerinfo_notify)
1194     {
1195       GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1196       peerinfo_notify = NULL;
1197     }
1198   GNUNET_TRANSPORT_disconnect (transport);
1199   transport = NULL;
1200   while (NULL != peers)
1201     free_peer (peers);     
1202   if (handle != NULL)
1203     {
1204       GNUNET_CORE_disconnect (handle);
1205       handle = NULL;
1206     }
1207   while (NULL != (dl = disconnect_head))
1208     {
1209       GNUNET_CONTAINER_DLL_remove (disconnect_head,
1210                                    disconnect_tail,
1211                                    dl);
1212       GNUNET_CORE_peer_request_cancel (dl->rh);
1213       GNUNET_free (dl);
1214     }
1215 }
1216
1217
1218 /**
1219  * Main function that will be run.
1220  *
1221  * @param cls closure
1222  * @param s the scheduler to use
1223  * @param args remaining command-line arguments
1224  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1225  * @param c configuration
1226  */
1227 static void
1228 run (void *cls,
1229      struct GNUNET_SCHEDULER_Handle * s,
1230      char *const *args,
1231      const char *cfgfile,
1232      const struct GNUNET_CONFIGURATION_Handle * c)
1233 {
1234   struct GNUNET_CORE_MessageHandler handlers[] =
1235     {
1236       { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1237       { NULL, 0, 0 }
1238     };
1239   unsigned long long opt;
1240
1241   sched = s;
1242   cfg = c;
1243   autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1244                                                       "TOPOLOGY",
1245                                                       "AUTOCONNECT");
1246   friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1247                                                        "TOPOLOGY",
1248                                                        "FRIENDS-ONLY");
1249   if (GNUNET_OK !=
1250       GNUNET_CONFIGURATION_get_value_number (cfg,
1251                                              "TOPOLOGY",
1252                                              "MINIMUM-FRIENDS",
1253                                              &opt))
1254     opt = 0;
1255   minimum_friend_count = (unsigned int) opt;
1256   if (GNUNET_OK !=
1257       GNUNET_CONFIGURATION_get_value_number (cfg,
1258                                              "TOPOLOGY",
1259                                              "TARGET-CONNECTION-COUNT",
1260                                              &opt))
1261     opt = 16;
1262   target_connection_count = (unsigned int) opt;
1263
1264   if ( (friends_only == GNUNET_YES) ||
1265        (minimum_friend_count > 0) )
1266     read_friends_file (cfg);
1267 #if DEBUG_TOPOLOGY
1268   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1269               "Topology would like %u connections with at least %u friends (%s)\n",
1270               target_connection_count,
1271               minimum_friend_count,
1272               autoconnect ? "autoconnect enabled" : "autoconnect disabled");
1273 #endif       
1274   transport = GNUNET_TRANSPORT_connect (sched,
1275                                         cfg,
1276                                         NULL,
1277                                         NULL,
1278                                         NULL,
1279                                         NULL);
1280   handle = GNUNET_CORE_connect (sched,
1281                                 cfg,
1282                                 GNUNET_TIME_UNIT_FOREVER_REL,
1283                                 NULL,
1284                                 &core_init,
1285                                 NULL,
1286                                 &connect_notify,
1287                                 &disconnect_notify,
1288                                 NULL, GNUNET_NO,
1289                                 NULL, GNUNET_NO,
1290                                 handlers);
1291   GNUNET_SCHEDULER_add_delayed (sched,
1292                                 GNUNET_TIME_UNIT_FOREVER_REL,
1293                                 &cleaning_task, NULL);
1294   if (NULL == transport)
1295     {
1296       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1297                   _("Failed to connect to `%s' service.\n"),
1298                   "transport");
1299       GNUNET_SCHEDULER_shutdown (sched);
1300       return;
1301     }
1302   if (NULL == handle)
1303     {
1304       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1305                   _("Failed to connect to `%s' service.\n"),
1306                   "core");
1307       GNUNET_SCHEDULER_shutdown (sched);
1308       return;
1309     }
1310   peerinfo_notify = GNUNET_PEERINFO_notify (cfg, sched,
1311                                             &process_peer,
1312                                             NULL);
1313 }
1314
1315
1316 /**
1317  * gnunet-daemon-topology command line options.
1318  */
1319 static struct GNUNET_GETOPT_CommandLineOption options[] = {
1320   GNUNET_GETOPT_OPTION_END
1321 };
1322
1323
1324 /**
1325  * The main function for the topology daemon.
1326  *
1327  * @param argc number of arguments from the command line
1328  * @param argv command line arguments
1329  * @return 0 ok, 1 on error
1330  */
1331 int
1332 main (int argc, char *const *argv)
1333 {
1334   int ret;
1335
1336   ret = (GNUNET_OK ==
1337          GNUNET_PROGRAM_run (argc,
1338                              argv,
1339                              "topology",
1340                              _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
1341                              options,
1342                              &run, NULL)) ? 0 : 1;
1343   return ret;
1344 }
1345
1346 /* end of gnunet-daemon-topology.c */