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