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