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