3aa518fa1797f3683880042409fd27b90dd99859
[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 Peer *peer;
1020   uint16_t size;
1021
1022   GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
1023   if (0 == memcmp (&pid,
1024                    &my_identity,
1025                    sizeof (struct GNUNET_PeerIdentity)))
1026     return; /* that's me! */
1027   have_address = GNUNET_NO;
1028   GNUNET_HELLO_iterate_addresses (hello,
1029                                   GNUNET_NO,
1030                                   &address_iterator,
1031                                   &have_address);
1032   if (GNUNET_NO == have_address)
1033     return; /* no point in advertising this one... */
1034   peer = GNUNET_CONTAINER_multihashmap_get (peers,
1035                                             &pid.hashPubKey);
1036   if (peer == NULL)
1037     peer = make_peer (&pid, hello, GNUNET_NO);
1038 #if DEBUG_TOPOLOGY
1039   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1040               "Found `%s' from peer `%s' for advertising\n",
1041               "HELLO",
1042               GNUNET_i2s (&pid));
1043 #endif 
1044   size = GNUNET_HELLO_size (hello);
1045   GNUNET_free_non_null (peer->hello);
1046   peer->hello = GNUNET_malloc (size);
1047   memcpy (peer->hello, hello, size);
1048   if (peer->filter != NULL)
1049     GNUNET_CONTAINER_bloomfilter_free (peer->filter);
1050   setup_filter (peer);
1051   /* since we have a new HELLO to pick from, re-schedule all
1052      HELLO requests that are not bound by the HELLO send rate! */
1053   GNUNET_CONTAINER_multihashmap_iterate (peers,
1054                                          &reschedule_hellos,
1055                                          peer);
1056 }
1057
1058
1059 /**
1060  * PEERINFO calls this function to let us know about a possible peer
1061  * that we might want to connect to.
1062  *
1063  * @param cls closure (not used)
1064  * @param peer potential peer to connect to
1065  * @param hello HELLO for this peer (or NULL)
1066  * @param trust how much we trust the peer (not used)
1067  */
1068 static void
1069 process_peer (void *cls,
1070               const struct GNUNET_PeerIdentity *peer,
1071               const struct GNUNET_HELLO_Message *hello,
1072               uint32_t trust)
1073 {
1074   struct Peer *pos;
1075
1076   GNUNET_assert (peer != NULL);
1077   if (0 == memcmp (&my_identity,
1078                    peer, sizeof (struct GNUNET_PeerIdentity)))
1079     return;  /* that's me! */
1080   if (hello == NULL)
1081     {
1082       /* free existing HELLO, if any */
1083       pos = GNUNET_CONTAINER_multihashmap_get (peers,
1084                                                &peer->hashPubKey);
1085       if (NULL != pos)
1086         {
1087           GNUNET_free_non_null (pos->hello);
1088           pos->hello = NULL;
1089           if (pos->filter != NULL)
1090             {
1091               GNUNET_CONTAINER_bloomfilter_free (pos->filter);
1092               pos->filter = NULL;
1093             }
1094           if ( (! pos->is_connected) &&
1095                (! pos->is_friend) &&
1096                (0 == GNUNET_TIME_absolute_get_remaining (pos->greylisted_until).value) )
1097             free_peer (NULL, &pos->pid.hashPubKey, pos);
1098         }
1099       return;
1100     }
1101   consider_for_advertising (hello);
1102   pos = GNUNET_CONTAINER_multihashmap_get (peers,
1103                                            &peer->hashPubKey);
1104   if (pos == NULL)
1105     pos = make_peer (peer, hello, GNUNET_NO);
1106   GNUNET_assert (NULL != pos);
1107   if (GNUNET_YES == pos->is_connected)
1108     {
1109 #if DEBUG_TOPOLOGY
1110       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1111                   "Already connected to peer `%s'\n",
1112                   GNUNET_i2s (peer));
1113 #endif 
1114       return;
1115     }
1116   if (GNUNET_TIME_absolute_get_remaining (pos->greylisted_until).value > 0)
1117     {
1118 #if DEBUG_TOPOLOGY
1119       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1120                   "Already tried peer `%s' recently\n",
1121                   GNUNET_i2s (peer));
1122 #endif 
1123       return; /* peer still greylisted */
1124     }
1125 #if DEBUG_TOPOLOGY
1126   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1127               "Considering connecting to peer `%s'\n",
1128               GNUNET_i2s (peer));
1129 #endif 
1130   attempt_connect (pos);
1131 }
1132
1133
1134 /**
1135  * Function called after GNUNET_CORE_connect has succeeded
1136  * (or failed for good).
1137  *
1138  * @param cls closure
1139  * @param server handle to the server, NULL if we failed
1140  * @param my_id ID of this peer, NULL if we failed
1141  * @param publicKey public key of this peer, NULL if we failed
1142  */
1143 static void
1144 core_init (void *cls,
1145            struct GNUNET_CORE_Handle * server,
1146            const struct GNUNET_PeerIdentity *
1147            my_id,
1148            const struct
1149            GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
1150            publicKey)
1151 {
1152   if (server == NULL)
1153     {
1154       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1155                   _("Failed to connect to core service, can not manage topology!\n"));
1156       GNUNET_SCHEDULER_shutdown (sched);
1157       return;
1158     }
1159   handle = server;
1160   my_identity = *my_id;
1161 #if DEBUG_TOPOLOGY
1162   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163               "I am peer `%s'\n",
1164               GNUNET_i2s (my_id));
1165 #endif  
1166   peerinfo_notify = GNUNET_PEERINFO_notify (cfg, sched,
1167                                             &process_peer,
1168                                             NULL);
1169 }
1170
1171
1172 /**
1173  * Read the friends file.
1174  */
1175 static void
1176 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
1177 {
1178   char *fn;
1179   char *data;
1180   size_t pos;
1181   struct GNUNET_PeerIdentity pid;
1182   struct stat frstat;
1183   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
1184   unsigned int entries_found;
1185   struct Peer *fl;
1186
1187   if (GNUNET_OK !=
1188       GNUNET_CONFIGURATION_get_value_filename (cfg,
1189                                                "TOPOLOGY",
1190                                                "FRIENDS",
1191                                                &fn))
1192     {
1193       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1194                   _("Option `%s' in section `%s' not specified!\n"),
1195                   "FRIENDS",
1196                   "TOPOLOGY");
1197       return;
1198     }
1199   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
1200     GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
1201         | GNUNET_DISK_PERM_USER_WRITE);
1202   if (0 != STAT (fn, &frstat))
1203     {
1204       if ((friends_only) || (minimum_friend_count > 0))
1205         {
1206           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1207                       _("Could not read friends list `%s'\n"), fn);
1208           GNUNET_free (fn);
1209           return;
1210         }
1211     }
1212   if (frstat.st_size == 0)
1213     {
1214       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1215                   _("Friends file `%s' is empty.\n"),
1216                   fn);
1217       GNUNET_free (fn);
1218       return;
1219     }
1220   data = GNUNET_malloc_large (frstat.st_size);
1221   if (frstat.st_size !=
1222       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
1223     {
1224       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1225                   _("Failed to read friends list from `%s'\n"), fn);
1226       GNUNET_free (fn);
1227       GNUNET_free (data);
1228       return;
1229     }
1230   entries_found = 0;
1231   pos = 0;
1232   while ((pos < frstat.st_size) && isspace (data[pos]))
1233     pos++;
1234   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
1235          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
1236     {
1237       memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
1238       if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
1239         {
1240           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1241                       _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
1242                       (unsigned long long) pos);
1243           pos++;
1244           while ((pos < frstat.st_size) && (!isspace (data[pos])))
1245             pos++;
1246           continue;
1247         }
1248       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
1249       if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
1250         {
1251           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1252                       _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
1253                       (unsigned long long) pos,
1254                       &enc);
1255         }
1256       else
1257         {
1258           if (0 != memcmp (&pid,
1259                            &my_identity,
1260                            sizeof (struct GNUNET_PeerIdentity)))
1261             {
1262               entries_found++;
1263               fl = make_peer (&pid,
1264                               NULL,
1265                               GNUNET_YES);
1266               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1267                           _("Found friend `%s' in configuration\n"),
1268                           GNUNET_i2s (&fl->pid));
1269             }
1270           else
1271             {
1272               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1273                           _("Found myself `%s' in friend list (useless, ignored)\n"),
1274                           GNUNET_i2s (&pid));
1275             }
1276         }
1277       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
1278       while ((pos < frstat.st_size) && isspace (data[pos]))
1279         pos++;
1280     }
1281   GNUNET_free (data);
1282   GNUNET_free (fn);
1283   GNUNET_STATISTICS_update (stats,
1284                             gettext_noop ("# friends in configuration"),
1285                             entries_found,
1286                             GNUNET_NO);
1287   if ( (minimum_friend_count > entries_found) &&
1288        (friends_only == GNUNET_NO) )
1289     {
1290       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1291                   _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
1292     }
1293   if ( (minimum_friend_count > target_connection_count) &&
1294        (friends_only == GNUNET_NO) )
1295     {
1296       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1297                   _("More friendly connections required than target total number of connections.\n"));
1298     }
1299 }
1300
1301
1302 /**
1303  * This function is called whenever an encrypted HELLO message is
1304  * received.
1305  *
1306  * @param cls closure
1307  * @param other the other peer involved (sender or receiver, NULL
1308  *        for loopback messages where we are both sender and receiver)
1309  * @param message the actual HELLO message
1310  * @param latency reported latency of the connection with 'other'
1311  * @param distance reported distance (DV) to 'other' 
1312  * @return GNUNET_OK to keep the connection open,
1313  *         GNUNET_SYSERR to close it (signal serious error)
1314  */
1315 static int
1316 handle_encrypted_hello (void *cls,
1317                         const struct GNUNET_PeerIdentity * other,
1318                         const struct GNUNET_MessageHeader *
1319                         message,
1320                         struct GNUNET_TIME_Relative latency,
1321                         uint32_t distance)
1322 {
1323   struct Peer *peer;
1324   struct GNUNET_PeerIdentity pid;
1325
1326 #if DEBUG_TOPOLOGY
1327   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1328               "Received encrypted `%s' from peer `%s'",
1329               "HELLO",
1330               GNUNET_i2s (other));
1331 #endif  
1332   if (GNUNET_OK !=
1333       GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message*) message,
1334                            &pid))
1335     {
1336       GNUNET_break_op (0);
1337       return GNUNET_SYSERR;
1338     }
1339   GNUNET_STATISTICS_update (stats,
1340                             gettext_noop ("# HELLO messages received"),
1341                             1,
1342                             GNUNET_NO);
1343   peer = GNUNET_CONTAINER_multihashmap_get (peers,
1344                                             &pid.hashPubKey);
1345   if ( (peer != NULL) &&
1346        (peer->is_blocked) )
1347     return GNUNET_OK; /* ignore: currently blocked */
1348   if ( (GNUNET_YES == friends_only) &&
1349        ( (peer == NULL) ||
1350          (GNUNET_YES != peer->is_friend) ) )
1351     return GNUNET_OK; /* ignore: not a friend */
1352   if (transport != NULL)
1353     GNUNET_TRANSPORT_offer_hello (transport,
1354                                   message);
1355   return GNUNET_OK;
1356 }
1357
1358
1359 /**
1360  * Function to fill send buffer with HELLO.
1361  *
1362  * @param cls 'struct Peer' of the target peer
1363  * @param size number of bytes available in buf
1364  * @param buf where the callee should write the message
1365  * @return number of bytes written to buf
1366  */
1367 static size_t
1368 hello_advertising_ready (void *cls,
1369                          size_t size,
1370                          void *buf)
1371 {
1372   struct Peer *pl = cls;
1373   struct FindAdvHelloContext fah;
1374   size_t want;
1375
1376   pl->hello_req = NULL;
1377   /* find applicable HELLOs */
1378   fah.peer = pl;
1379   fah.result = NULL;
1380   fah.max_size = size;
1381   fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
1382   GNUNET_CONTAINER_multihashmap_iterate (peers,
1383                                          &find_advertisable_hello,
1384                                          &fah);
1385   want = 0;
1386   if (fah.result != NULL)
1387     {
1388       want = GNUNET_HELLO_size (fah.result->hello);
1389       GNUNET_assert (want <= size);
1390       memcpy (buf, fah.result->hello, want);
1391       GNUNET_CONTAINER_bloomfilter_add (fah.result->filter,
1392                                         &pl->pid.hashPubKey);
1393 #if DEBUG_TOPOLOGY
1394       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1395                   "Sending `%s' with %u bytes",
1396                   "HELLO"
1397                   (unsigned int) want);
1398 #endif  
1399       GNUNET_STATISTICS_update (stats,
1400                                 gettext_noop ("# HELLO messages gossipped"),
1401                                 1,
1402                                 GNUNET_NO);    
1403     }
1404   pl->next_hello_allowed = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
1405   pl->hello_delay_task 
1406     = GNUNET_SCHEDULER_add_now (sched,
1407                                 &schedule_next_hello,
1408                                 pl);
1409   return want;
1410 }
1411
1412
1413 /**
1414  * Last task run during shutdown.  Disconnects us from
1415  * the transport and core.
1416  *
1417  * @param cls unused, NULL
1418  * @param tc scheduler context
1419  */
1420 static void
1421 cleaning_task (void *cls, 
1422                const struct GNUNET_SCHEDULER_TaskContext *tc)
1423 {
1424   struct DisconnectList *dl;
1425
1426   if (NULL != peerinfo_notify)
1427     {
1428       GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1429       peerinfo_notify = NULL;
1430     }
1431   GNUNET_TRANSPORT_disconnect (transport);
1432   transport = NULL;
1433   GNUNET_CONTAINER_multihashmap_iterate (peers,
1434                                          &free_peer,
1435                                          NULL);
1436   GNUNET_CONTAINER_multihashmap_destroy (peers);
1437   if (handle != NULL)
1438     {
1439       GNUNET_CORE_disconnect (handle);
1440       handle = NULL;
1441     }
1442   while (NULL != (dl = disconnect_head))
1443     {
1444       GNUNET_CONTAINER_DLL_remove (disconnect_head,
1445                                    disconnect_tail,
1446                                    dl);
1447       GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
1448       GNUNET_free (dl);
1449     }
1450   if (stats != NULL)
1451     {
1452       GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1453       stats = NULL;
1454     }
1455 }
1456
1457
1458 /**
1459  * Main function that will be run.
1460  *
1461  * @param cls closure
1462  * @param s the scheduler to use
1463  * @param args remaining command-line arguments
1464  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1465  * @param c configuration
1466  */
1467 static void
1468 run (void *cls,
1469      struct GNUNET_SCHEDULER_Handle * s,
1470      char *const *args,
1471      const char *cfgfile,
1472      const struct GNUNET_CONFIGURATION_Handle * c)
1473 {
1474   struct GNUNET_CORE_MessageHandler handlers[] =
1475     {
1476       { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1477       { NULL, 0, 0 }
1478     };
1479   unsigned long long opt;
1480
1481   sched = s;
1482   cfg = c;
1483   stats = GNUNET_STATISTICS_create (sched, "topology", cfg);
1484   autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1485                                                       "TOPOLOGY",
1486                                                       "AUTOCONNECT");
1487   friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1488                                                        "TOPOLOGY",
1489                                                        "FRIENDS-ONLY");
1490   if (GNUNET_OK !=
1491       GNUNET_CONFIGURATION_get_value_number (cfg,
1492                                              "TOPOLOGY",
1493                                              "MINIMUM-FRIENDS",
1494                                              &opt))
1495     opt = 0;
1496   minimum_friend_count = (unsigned int) opt;
1497   if (GNUNET_OK !=
1498       GNUNET_CONFIGURATION_get_value_number (cfg,
1499                                              "TOPOLOGY",
1500                                              "TARGET-CONNECTION-COUNT",
1501                                              &opt))
1502     opt = 16;
1503   target_connection_count = (unsigned int) opt;
1504   peers = GNUNET_CONTAINER_multihashmap_create (target_connection_count * 2);
1505
1506   if ( (friends_only == GNUNET_YES) ||
1507        (minimum_friend_count > 0) )
1508     read_friends_file (cfg);
1509 #if DEBUG_TOPOLOGY
1510   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1511               "Topology would like %u connections with at least %u friends (%s)\n",
1512               target_connection_count,
1513               minimum_friend_count,
1514               autoconnect ? "autoconnect enabled" : "autoconnect disabled");
1515 #endif       
1516   transport = GNUNET_TRANSPORT_connect (sched,
1517                                         cfg,
1518                                         NULL,
1519                                         NULL,
1520                                         NULL,
1521                                         NULL);
1522   handle = GNUNET_CORE_connect (sched,
1523                                 cfg,
1524                                 GNUNET_TIME_UNIT_FOREVER_REL,
1525                                 NULL,
1526                                 &core_init,
1527                                 NULL,
1528                                 &connect_notify,
1529                                 &disconnect_notify,
1530                                 NULL, GNUNET_NO,
1531                                 NULL, GNUNET_NO,
1532                                 handlers);
1533   GNUNET_SCHEDULER_add_delayed (sched,
1534                                 GNUNET_TIME_UNIT_FOREVER_REL,
1535                                 &cleaning_task, NULL);
1536   if (NULL == transport)
1537     {
1538       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1539                   _("Failed to connect to `%s' service.\n"),
1540                   "transport");
1541       GNUNET_SCHEDULER_shutdown (sched);
1542       return;
1543     }
1544   if (NULL == handle)
1545     {
1546       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1547                   _("Failed to connect to `%s' service.\n"),
1548                   "core");
1549       GNUNET_SCHEDULER_shutdown (sched);
1550       return;
1551     }
1552 }
1553
1554
1555 /**
1556  * gnunet-daemon-topology command line options.
1557  */
1558 static struct GNUNET_GETOPT_CommandLineOption options[] = {
1559   GNUNET_GETOPT_OPTION_END
1560 };
1561
1562
1563 /**
1564  * The main function for the topology daemon.
1565  *
1566  * @param argc number of arguments from the command line
1567  * @param argv command line arguments
1568  * @return 0 ok, 1 on error
1569  */
1570 int
1571 main (int argc, char *const *argv)
1572 {
1573   int ret;
1574
1575   ret = (GNUNET_OK ==
1576          GNUNET_PROGRAM_run (argc,
1577                              argv,
1578                              "topology",
1579                              _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
1580                              options,
1581                              &run, NULL)) ? 0 : 1;
1582   return ret;
1583 }
1584
1585 /* end of gnunet-daemon-topology.c */