1d5b41522c3364f93aea23501ce7022741e1dac1
[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 }
1053
1054
1055 /**
1056  * Read the friends file.
1057  */
1058 static void
1059 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
1060 {
1061   char *fn;
1062   char *data;
1063   size_t pos;
1064   struct GNUNET_PeerIdentity pid;
1065   struct stat frstat;
1066   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
1067   unsigned int entries_found;
1068   struct PeerList *fl;
1069
1070   if (GNUNET_OK !=
1071       GNUNET_CONFIGURATION_get_value_filename (cfg,
1072                                                "TOPOLOGY",
1073                                                "FRIENDS",
1074                                                &fn))
1075     {
1076       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1077                   _("Option `%s' in section `%s' not specified!\n"),
1078                   "FRIENDS",
1079                   "TOPOLOGY");
1080       return;
1081     }
1082   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
1083     GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
1084         | GNUNET_DISK_PERM_USER_WRITE);
1085   if (0 != STAT (fn, &frstat))
1086     {
1087       if ((friends_only) || (minimum_friend_count > 0))
1088         {
1089           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1090                       _("Could not read friends list `%s'\n"), fn);
1091           GNUNET_free (fn);
1092           return;
1093         }
1094     }
1095   if (frstat.st_size == 0)
1096     {
1097       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1098                   _("Friends file `%s' is empty.\n"),
1099                   fn);
1100       GNUNET_free (fn);
1101       return;
1102     }
1103   data = GNUNET_malloc_large (frstat.st_size);
1104   if (frstat.st_size !=
1105       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
1106     {
1107       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1108                   _("Failed to read friends list from `%s'\n"), fn);
1109       GNUNET_free (fn);
1110       GNUNET_free (data);
1111       return;
1112     }
1113   entries_found = 0;
1114   pos = 0;
1115   while ((pos < frstat.st_size) && isspace (data[pos]))
1116     pos++;
1117   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
1118          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
1119     {
1120       memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
1121       if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
1122         {
1123           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1124                       _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
1125                       (unsigned long long) pos);
1126           pos++;
1127           while ((pos < frstat.st_size) && (!isspace (data[pos])))
1128             pos++;
1129           continue;
1130         }
1131       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
1132       if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
1133         {
1134           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1135                       _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
1136                       (unsigned long long) pos,
1137                       &enc);
1138         }
1139       else
1140         {
1141           if (0 != memcmp (&pid,
1142                            &my_identity,
1143                            sizeof (struct GNUNET_PeerIdentity)))
1144             {
1145               entries_found++;
1146               fl = make_peer (&pid,
1147                               NULL,
1148                               GNUNET_YES);
1149               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1150                           _("Found friend `%s' in configuration\n"),
1151                           GNUNET_i2s (&fl->id));
1152             }
1153           else
1154             {
1155               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1156                           _("Found myself `%s' in friend list (useless, ignored)\n"),
1157                           GNUNET_i2s (&pid));
1158             }
1159         }
1160       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
1161       while ((pos < frstat.st_size) && isspace (data[pos]))
1162         pos++;
1163     }
1164   GNUNET_free (data);
1165   GNUNET_free (fn);
1166   GNUNET_STATISTICS_update (stats,
1167                             gettext_noop ("# friends in configuration"),
1168                             entries_found,
1169                             GNUNET_NO);
1170   if ( (minimum_friend_count > entries_found) &&
1171        (friends_only == GNUNET_NO) )
1172     {
1173       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1174                   _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
1175     }
1176   if ( (minimum_friend_count > target_connection_count) &&
1177        (friends_only == GNUNET_NO) )
1178     {
1179       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1180                   _("More friendly connections required than target total number of connections.\n"));
1181     }
1182 }
1183
1184
1185 /**
1186  * This function is called whenever an encrypted HELLO message is
1187  * received.
1188  *
1189  * @param cls closure
1190  * @param other the other peer involved (sender or receiver, NULL
1191  *        for loopback messages where we are both sender and receiver)
1192  * @param message the actual HELLO message
1193  * @param latency reported latency of the connection with 'other'
1194  * @param distance reported distance (DV) to 'other' 
1195  * @return GNUNET_OK to keep the connection open,
1196  *         GNUNET_SYSERR to close it (signal serious error)
1197  */
1198 static int
1199 handle_encrypted_hello (void *cls,
1200                         const struct GNUNET_PeerIdentity * other,
1201                         const struct GNUNET_MessageHeader *
1202                         message,
1203                         struct GNUNET_TIME_Relative latency,
1204                         uint32_t distance)
1205 {
1206 #if DEBUG_TOPOLOGY
1207   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1208               "Received encrypted `%s' from peer `%s'",
1209               "HELLO",
1210               GNUNET_i2s (other));
1211 #endif  
1212   GNUNET_STATISTICS_update (stats,
1213                             gettext_noop ("# HELLO messages received"),
1214                             1,
1215                             GNUNET_NO);
1216   if (transport != NULL)
1217     GNUNET_TRANSPORT_offer_hello (transport,
1218                                   message);
1219   return GNUNET_OK;
1220 }
1221
1222
1223 /**
1224  * Function to fill send buffer with HELLO.
1225  *
1226  * @param cls 'struct PeerList' of the target peer
1227  * @param size number of bytes available in buf
1228  * @param buf where the callee should write the message
1229  * @return number of bytes written to buf
1230  */
1231 static size_t
1232 hello_advertising_ready (void *cls,
1233                          size_t size,
1234                          void *buf)
1235 {
1236   struct PeerList *pl = cls;
1237   struct PeerList *pos; 
1238   struct PeerList *next;
1239   uint16_t want;
1240   size_t hs;
1241
1242   pl->hello_req = NULL;
1243 #if DEBUG_TOPOLOGY
1244   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1245               "Data solicited for `%s', considering sending `%s'",
1246               GNUNET_i2s (&pl->id),
1247               "HELLO");
1248 #endif  
1249   /* find applicable HELLOs */
1250   next = peers;
1251   while (NULL != (pos = next))
1252     {
1253       next = pos->next;
1254       if (pos->hello == NULL)
1255         continue;
1256       if (0 == GNUNET_TIME_absolute_get_remaining (pos->filter_expiration).value)
1257         {
1258           /* time to discard... */
1259           GNUNET_CONTAINER_bloomfilter_free (pos->filter);
1260           setup_filter (pos);
1261         }
1262       if (GNUNET_NO ==
1263           GNUNET_CONTAINER_bloomfilter_test (pos->filter,
1264                                              &pl->id.hashPubKey))
1265         break;
1266     }
1267   want = 0;
1268   if (pos != NULL)
1269     {
1270       hs = GNUNET_HELLO_size (pos->hello);
1271       if (hs < size)
1272         {
1273           want = hs;
1274           memcpy (buf, pos->hello, want);
1275           GNUNET_CONTAINER_bloomfilter_add (pos->filter,
1276                                             &pl->id.hashPubKey);
1277 #if DEBUG_TOPOLOGY
1278           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1279                       "Sending %u bytes of `%s's",
1280                       (unsigned int) want,
1281                       "HELLO");
1282 #endif  
1283           GNUNET_STATISTICS_update (stats,
1284                                     gettext_noop ("# HELLO messages gossipped"),
1285                                     1,
1286                                     GNUNET_NO);
1287         }
1288     }
1289   pl->next_hello_allowed = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
1290   pl->hello_delay_task 
1291     = GNUNET_SCHEDULER_add_now (sched,
1292                                 &schedule_next_hello,
1293                                 pl);
1294   return want;
1295 }
1296
1297
1298 /**
1299  * Last task run during shutdown.  Disconnects us from
1300  * the transport and core.
1301  */
1302 static void
1303 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1304 {
1305   struct DisconnectList *dl;
1306
1307   if (NULL != peerinfo_notify)
1308     {
1309       GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1310       peerinfo_notify = NULL;
1311     }
1312   GNUNET_TRANSPORT_disconnect (transport);
1313   transport = NULL;
1314   while (NULL != peers)
1315     free_peer (peers);     
1316   if (handle != NULL)
1317     {
1318       GNUNET_CORE_disconnect (handle);
1319       handle = NULL;
1320     }
1321   while (NULL != (dl = disconnect_head))
1322     {
1323       GNUNET_CONTAINER_DLL_remove (disconnect_head,
1324                                    disconnect_tail,
1325                                    dl);
1326       GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
1327       GNUNET_free (dl);
1328     }
1329   if (stats != NULL)
1330     {
1331       GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
1332       stats = NULL;
1333     }
1334 }
1335
1336
1337 /**
1338  * Main function that will be run.
1339  *
1340  * @param cls closure
1341  * @param s the scheduler to use
1342  * @param args remaining command-line arguments
1343  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1344  * @param c configuration
1345  */
1346 static void
1347 run (void *cls,
1348      struct GNUNET_SCHEDULER_Handle * s,
1349      char *const *args,
1350      const char *cfgfile,
1351      const struct GNUNET_CONFIGURATION_Handle * c)
1352 {
1353   struct GNUNET_CORE_MessageHandler handlers[] =
1354     {
1355       { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1356       { NULL, 0, 0 }
1357     };
1358   unsigned long long opt;
1359
1360   sched = s;
1361   cfg = c;
1362   stats = GNUNET_STATISTICS_create (sched, "topology", cfg);
1363   autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1364                                                       "TOPOLOGY",
1365                                                       "AUTOCONNECT");
1366   friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1367                                                        "TOPOLOGY",
1368                                                        "FRIENDS-ONLY");
1369   if (GNUNET_OK !=
1370       GNUNET_CONFIGURATION_get_value_number (cfg,
1371                                              "TOPOLOGY",
1372                                              "MINIMUM-FRIENDS",
1373                                              &opt))
1374     opt = 0;
1375   minimum_friend_count = (unsigned int) opt;
1376   if (GNUNET_OK !=
1377       GNUNET_CONFIGURATION_get_value_number (cfg,
1378                                              "TOPOLOGY",
1379                                              "TARGET-CONNECTION-COUNT",
1380                                              &opt))
1381     opt = 16;
1382   target_connection_count = (unsigned int) opt;
1383
1384   if ( (friends_only == GNUNET_YES) ||
1385        (minimum_friend_count > 0) )
1386     read_friends_file (cfg);
1387 #if DEBUG_TOPOLOGY
1388   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1389               "Topology would like %u connections with at least %u friends (%s)\n",
1390               target_connection_count,
1391               minimum_friend_count,
1392               autoconnect ? "autoconnect enabled" : "autoconnect disabled");
1393 #endif       
1394   transport = GNUNET_TRANSPORT_connect (sched,
1395                                         cfg,
1396                                         NULL,
1397                                         NULL,
1398                                         NULL,
1399                                         NULL);
1400   handle = GNUNET_CORE_connect (sched,
1401                                 cfg,
1402                                 GNUNET_TIME_UNIT_FOREVER_REL,
1403                                 NULL,
1404                                 &core_init,
1405                                 NULL,
1406                                 &connect_notify,
1407                                 &disconnect_notify,
1408                                 NULL, GNUNET_NO,
1409                                 NULL, GNUNET_NO,
1410                                 handlers);
1411   GNUNET_SCHEDULER_add_delayed (sched,
1412                                 GNUNET_TIME_UNIT_FOREVER_REL,
1413                                 &cleaning_task, NULL);
1414   if (NULL == transport)
1415     {
1416       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1417                   _("Failed to connect to `%s' service.\n"),
1418                   "transport");
1419       GNUNET_SCHEDULER_shutdown (sched);
1420       return;
1421     }
1422   if (NULL == handle)
1423     {
1424       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1425                   _("Failed to connect to `%s' service.\n"),
1426                   "core");
1427       GNUNET_SCHEDULER_shutdown (sched);
1428       return;
1429     }
1430   peerinfo_notify = GNUNET_PEERINFO_notify (cfg, sched,
1431                                             &process_peer,
1432                                             NULL);
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 */