simplifying transport plugin API
[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_NO
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   struct GNUNET_MessageHeader hdr;
255   if (buf == NULL)
256     {
257 #if DEBUG_TOPOLOGY
258       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
259                   "Core told us that our attempt to connect failed.\n");
260 #endif
261       return 0;
262     }
263 #if DEBUG_TOPOLOGY
264   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
265               "Sending dummy message to establish connection.\n");
266 #endif
267   hdr.size = htons (sizeof (struct GNUNET_MessageHeader));
268   hdr.type = htons (GNUNET_MESSAGE_TYPE_TOPOLOGY_DUMMY);
269   memcpy (buf, &hdr, sizeof (struct GNUNET_MessageHeader));
270   return sizeof (struct GNUNET_MessageHeader);
271 }
272
273
274 /**
275  * Try to connect to the specified peer.
276  *
277  * @param peer who we should try to connect to
278  * @param pos entry in our friend list; NULL if not in friend list yet
279  */
280 static void
281 attempt_connect (const struct GNUNET_PeerIdentity *peer,
282                  struct PeerList *pos)
283 {
284   if (pos == NULL)
285     {
286       pos = friends;
287       while (pos != NULL)
288         {
289           if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
290             break;
291           pos = pos->next;
292         }
293     }
294   if (pos == NULL)
295     {
296       pos = GNUNET_malloc (sizeof(struct PeerList));
297       pos->id = *peer;
298       pos->next = friends;
299       friends = pos;
300     }
301   if (GNUNET_YES == pos->is_friend)
302     pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT_FRIEND);
303   else
304     pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT);
305 #if DEBUG_TOPOLOGY
306   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
307               "Asking core to connect to `%s'\n",
308               GNUNET_i2s (peer));
309 #endif
310   GNUNET_CORE_notify_transmit_ready (handle,
311                                      0 /* priority */,
312                                      GNUNET_TIME_UNIT_MINUTES,
313                                      peer,
314                                      sizeof(struct GNUNET_MessageHeader),
315                                      &ready_callback,
316                                      NULL);
317 }
318
319
320 /**
321  * Is this peer one of our friends?
322  */
323 static int
324 is_friend (const struct GNUNET_PeerIdentity * peer)
325 {
326   struct PeerList *pos;
327
328   pos = friends;
329   while (pos != NULL)
330     {
331       if ( (GNUNET_YES == pos->is_friend) &&
332            (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
333         {
334 #if DEBUG_TOPOLOGY
335           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336                       "Determined that `%s' is a friend\n",
337                       GNUNET_i2s (peer));
338 #endif    
339           return GNUNET_YES;
340         }
341       pos = pos->next;
342     }
343   return GNUNET_NO;
344 }
345
346
347 /**
348  * Check if an additional connection from the given peer is allowed.
349  */
350 static int
351 is_connection_allowed (const struct GNUNET_PeerIdentity * peer)
352 {
353   if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
354     return GNUNET_SYSERR;       /* disallow connections to self */
355   if (is_friend (peer))
356     return GNUNET_OK;
357   if (GNUNET_YES == friends_only)
358     {
359 #if DEBUG_TOPOLOGY
360       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
361                   "Determined that `%s' is not allowed to connect (not a friend)\n",
362                   GNUNET_i2s (peer));
363 #endif       
364       return GNUNET_SYSERR;
365     }
366   if (friend_count >= minimum_friend_count)
367     return GNUNET_OK;
368 #if DEBUG_TOPOLOGY
369   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370               "Determined that `%s' is not allowed to connect (not enough connected friends)\n",
371               GNUNET_i2s (peer));
372 #endif       
373   return GNUNET_SYSERR;
374 }
375
376
377 /**
378  * Method called whenever a peer connects.
379  *
380  * @param cls closure
381  * @param peer peer identity this notification is about
382  */
383 static void connect_notify (void *cls,
384                             const struct
385                             GNUNET_PeerIdentity * peer)
386 {
387   struct PeerList *pos;
388
389 #if DEBUG_TOPOLOGY
390   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
391               "Core told us that we are connecting to `%s'\n",
392               GNUNET_i2s (peer));
393 #endif       
394   connection_count++;
395   pos = friends;
396   while (pos != NULL)
397     {
398       if ( (GNUNET_YES == pos->is_friend) &&
399            (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
400         {
401           GNUNET_assert (GNUNET_NO == pos->is_connected);
402           pos->is_connected = GNUNET_YES;
403           pos->blacklisted_until.value = 0; /* remove blacklisting */
404           friend_count++;
405           return;
406         }
407       pos = pos->next;
408     }
409   pos = GNUNET_malloc (sizeof(struct PeerList));
410   pos->id = *peer;
411   pos->is_connected = GNUNET_YES;
412   pos->next = friends;
413   friends = pos;
414   if (GNUNET_OK != is_connection_allowed (peer))
415     {
416 #if DEBUG_TOPOLOGY
417       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418                   "Connection to `%s' is forbidden, forcing disconnect!\n",
419                   GNUNET_i2s (peer));
420 #endif       
421       force_disconnect (peer);
422     }
423 }
424
425
426 /**
427  * Disconnect from all non-friends (we're below quota).
428  */
429 static void
430 drop_non_friends ()
431 {
432   struct PeerList *pos;
433
434   pos = friends;
435   while (pos != NULL)
436     {
437       if (GNUNET_NO == pos->is_friend)
438         {
439           GNUNET_assert (GNUNET_YES == pos->is_connected);
440 #if DEBUG_TOPOLOGY
441           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442                       "Connection to `%s' is not from a friend, forcing disconnect!\n",
443                       GNUNET_i2s (&pos->id));
444 #endif       
445           force_disconnect (&pos->id);
446         }
447       pos = pos->next;
448     }
449 }
450
451
452 /**
453  * Method called whenever a peer disconnects.
454  *
455  * @param cls closure
456  * @param peer peer identity this notification is about
457  */
458 static void disconnect_notify (void *cls,
459                                const struct
460                                GNUNET_PeerIdentity * peer)
461 {
462   struct PeerList *pos;
463   struct PeerList *prev;
464  
465 #if DEBUG_TOPOLOGY
466   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
467               "Core told us that we disconnected from `%s'\n",
468               GNUNET_i2s (peer));
469 #endif       
470   connection_count--;
471   pos = friends;
472   prev = NULL;
473   while (pos != NULL)
474     {
475       if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
476         {
477           GNUNET_assert (GNUNET_YES == pos->is_connected);
478           pos->is_connected = GNUNET_NO;
479           if (GNUNET_YES == pos->is_friend)
480             {
481               friend_count--;
482               if (friend_count < minimum_friend_count)
483                 {
484                   /* disconnect from all non-friends */
485 #if DEBUG_TOPOLOGY
486                   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
487                               "Not enough friendly connections, dropping all non-friend connections\n");
488 #endif       
489                   drop_non_friends ();
490                   attempt_connect (peer, pos);
491                 }
492             }
493           else
494             {
495               /* free entry */
496               if (prev == NULL)
497                 friends = pos->next;
498               else
499                 prev->next = pos->next;
500               GNUNET_free (pos);
501             }
502           return;
503         }
504       prev = pos;
505       pos = pos->next;
506     }
507   GNUNET_break (0);
508 }
509
510
511 /**
512  * Find more peers that we should connect to and ask the
513  * core to establish connections.
514  */
515 static void
516 find_more_peers (void *cls,
517                  const struct GNUNET_SCHEDULER_TaskContext *tc);
518
519
520 /**
521  * Determine when we should try again to find more peers and
522  * schedule the task.
523  */
524 static void
525 schedule_peer_search ()
526 {
527   struct GNUNET_TIME_Relative delay;
528
529   /* Typically, we try again every 15 minutes; the minimum period is
530      15s; if we are above the connection target, we reduce re-trying
531      by the square of how much we are above; so for example, with 200%
532      of the connection target we would only look for more peers once
533      every hour (after all, we're quite busy processing twice as many
534      connections as we intended to have); similarly, if we are at only
535      25% of our connectivity goal, we will try 16x as hard to connect
536      (so roughly once a minute, plus the 15s minimum delay */
537   delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
538                                          15 + 15 * 60 * connection_count * connection_count / target_connection_count / target_connection_count);
539 #if DEBUG_TOPOLOGY
540   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541               "Will try to find more peers in %llums\n",
542               (unsigned long long) delay.value);
543 #endif 
544   GNUNET_SCHEDULER_add_delayed (sched,
545                                 delay,
546                                 &find_more_peers,
547                                 NULL);
548 }
549
550
551
552
553 /**
554  * Iterator called on each address.
555  *
556  * @param cls flag that we will set if we see any addresses
557  * @param tname name of the transport
558  * @param expiration when will the given address expire
559  * @param addr the address of the peer
560  * @param addrlen number of bytes in addr
561  * @return GNUNET_SYSERR always, to terminate iteration
562  */
563 static int
564 address_iterator (void *cls,
565                   const char *tname,
566                   struct GNUNET_TIME_Absolute expiration,
567                   const void *addr, size_t addrlen)
568 {
569   int *flag = cls;
570   *flag = GNUNET_YES;
571   return GNUNET_SYSERR;
572 }
573
574
575 /**
576  * We've gotten a HELLO from another peer.
577  * Consider it for advertising.
578  */
579 static void
580 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
581 {
582   int have_address;
583   struct GNUNET_PeerIdentity pid;
584   struct HelloList *pos;
585   uint16_t size;
586
587   have_address = GNUNET_NO;
588   GNUNET_HELLO_iterate_addresses (hello,
589                                   GNUNET_NO,
590                                   &address_iterator,
591                                   &have_address);
592   if (GNUNET_NO == have_address)
593     return; /* no point in advertising this one... */
594   GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
595   pos = hellos;
596   while (pos != NULL)
597     {
598       if (0 == memcmp (&pos->id,
599                        &pid,
600                        sizeof(struct GNUNET_PeerIdentity)))
601         return; /* duplicate, at least "mostly" */
602       pos = pos->next;
603     }
604 #if DEBUG_TOPOLOGY
605   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606               "Found `%s' from peer `%s' for advertising\n",
607               "HELLO",
608               GNUNET_i2s (&pid));
609 #endif 
610   size = GNUNET_HELLO_size (hello);
611   pos = GNUNET_malloc (sizeof(struct HelloList) + size);
612   pos->msg = (struct GNUNET_HELLO_Message*) &pos[1];
613   memcpy (&pos->msg, hello, size);
614   pos->id = pid;
615   pos->expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
616   /* 2^{-5} chance of not sending a HELLO to a peer is
617      acceptably small (if the filter is 50% full);
618      64 bytes of memory are small compared to the rest
619      of the data structure and would only really become
620      "useless" once a HELLO has been passed on to ~100
621      other peers, which is likely more than enough in
622      any case; hence 64, 5 as bloomfilter parameters. */
623   pos->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, 64, 5);
624   /* never send a peer its own HELLO */
625   GNUNET_CONTAINER_bloomfilter_add (pos->filter, &pos->id.hashPubKey);
626   pos->next = hellos;
627   hellos = pos;
628 }
629
630
631 /**
632  * Peerinfo calls this function to let us know about a
633  * possible peer that we might want to connect to.
634  */
635 static void
636 process_peer (void *cls,
637               const struct GNUNET_PeerIdentity *peer,
638               const struct GNUNET_HELLO_Message *hello,
639               uint32_t trust)
640 {
641   struct PeerList *pos;
642
643   if (peer == NULL)
644     {
645       /* last call, schedule 'find_more_peers' again... */
646       if (0 != (GNUNET_SCHEDULER_get_reason (sched) & GNUNET_SCHEDULER_REASON_SHUTDOWN))
647         {
648 #if DEBUG_TOPOLOGY
649           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
650                       "Received shutdown request, stopping search for peers to connect to.\n");
651 #endif
652           return;
653         }
654       schedule_peer_search ();
655       return;
656     }
657   if (hello == NULL)
658     {
659       /* no HELLO known; can not connect, ignore! */
660       return;
661     }
662   if (0 == memcmp (&my_identity,
663                    peer, sizeof (struct GNUNET_PeerIdentity)))
664     return;  /* that's me! */
665
666   consider_for_advertising (hello);
667 #if DEBUG_TOPOLOGY
668   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
669               "Considering connecting to peer `%s'\n",
670               GNUNET_i2s (peer));
671 #endif 
672   pos = friends;
673   while (pos != NULL)
674     {
675       if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
676         {
677           if (GNUNET_YES == pos->is_connected)
678             {
679 #if DEBUG_TOPOLOGY
680               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
681                           "Already connected to peer `%s'\n",
682                           GNUNET_i2s (peer));
683 #endif 
684               return;
685             }
686           if (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value > 0)
687             {
688 #if DEBUG_TOPOLOGY
689               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
690                           "Already tried peer `%s' recently\n",
691                           GNUNET_i2s (peer));
692 #endif 
693               return; /* peer still blacklisted */
694             }
695           if (GNUNET_YES == pos->is_friend)
696             {
697               attempt_connect (peer, pos);
698               return;
699             }
700         }
701       pos = pos->next;
702     }
703   if ( (GNUNET_YES == friends_only) ||    
704        (friend_count < minimum_friend_count) )
705     {
706 #if DEBUG_TOPOLOGY
707       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
708                   "Peer `%s' is not a friend, and we currently only connect to friends\n",
709                   GNUNET_i2s (peer));
710 #endif 
711       return;
712     }
713   attempt_connect (peer, NULL);
714 }
715
716
717 /**
718  * Try to add more friends to our connection set.
719  */
720 static void
721 try_add_friends ()
722 {
723   struct PeerList *pos;
724
725 #if DEBUG_TOPOLOGY
726   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727               "Considering all of our friends for new connections\n");
728 #endif 
729   pos = friends;
730   while (pos != NULL)
731     {
732       if ( (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value == 0) &&
733            (GNUNET_YES == pos->is_friend) &&
734            (GNUNET_YES != pos->is_connected) )
735         attempt_connect (&pos->id, pos);
736       pos = pos->next;
737     }
738 }
739
740
741 /**
742  * Discard peer entries for blacklisted peers
743  * where the blacklisting has expired.
744  */
745 static void
746 discard_old_blacklist_entries ()
747 {
748   struct PeerList *pos;
749   struct PeerList *next;
750   struct PeerList *prev;
751
752   next = friends;
753   prev = NULL;
754   while (NULL != (pos = next))
755     {
756       next = pos->next;
757       if ( (GNUNET_NO == pos->is_friend) &&
758            (GNUNET_NO == pos->is_connected) &&
759            (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
760         {
761           /* delete 'pos' from list */
762 #if DEBUG_TOPOLOGY
763           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764                       "Deleting peer `%s' from our list (not connected, not a friend and blacklist expired)\n",
765                       GNUNET_i2s (&pos->id));
766 #endif  
767           if (prev == NULL)
768             friends = next;
769           else
770             prev->next = next;
771           GNUNET_free (pos);
772         }
773       else
774         {
775           prev = pos;
776         }
777     }
778 }
779
780
781 /**
782  * Find more peers that we should connect to and ask the
783  * core to establish connections.
784  */
785 static void
786 find_more_peers (void *cls,
787                  const struct GNUNET_SCHEDULER_TaskContext *tc)
788 {
789   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
790     {
791 #if DEBUG_TOPOLOGY
792       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
793                   "Received shutdown request, stopping search for peers to connect to.\n");
794 #endif
795       return;
796     }
797   discard_old_blacklist_entries ();
798   if (connection_count <= target_connection_count)
799     {
800       schedule_peer_search ();
801       return;
802     }
803   if ( (GNUNET_YES == friends_only) ||
804        (friend_count < minimum_friend_count) )
805     {
806       try_add_friends ();
807       schedule_peer_search ();
808       return;
809     }
810 #if DEBUG_TOPOLOGY
811   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812               "Got sufficient (%u/%u, %u friends) number of connections, won't try to create more.\n",
813               connection_count,
814               target_connection_count,
815               friend_count);
816 #endif  
817   GNUNET_PEERINFO_for_all (cfg,
818                            sched,
819                            NULL,
820                            0, GNUNET_TIME_UNIT_FOREVER_REL,
821                            &process_peer, NULL);
822 }
823
824
825 /**
826  * Function called after GNUNET_CORE_connect has succeeded
827  * (or failed for good).
828  *
829  * @param cls closure
830  * @param server handle to the server, NULL if we failed
831  * @param my_id ID of this peer, NULL if we failed
832  * @param publicKey public key of this peer, NULL if we failed
833  */
834 static void
835 core_init (void *cls,
836            struct GNUNET_CORE_Handle * server,
837            const struct GNUNET_PeerIdentity *
838            my_id,
839            const struct
840            GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
841            publicKey)
842 {
843   if (server == NULL)
844     {
845       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
846                   _("Failed to connect to core service, can not manage topology!\n"));
847       GNUNET_SCHEDULER_shutdown (sched);
848       return;
849     }
850   handle = server;
851   my_identity = *my_id;
852 #if DEBUG_TOPOLOGY
853   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
854               "I am peer `%s'\n",
855               GNUNET_i2s (my_id));
856 #endif  
857   if (autoconnect)
858     GNUNET_SCHEDULER_add_delayed (sched,
859                                   GNUNET_TIME_UNIT_SECONDS /* give core time to tell us about existing connections */,
860                                   &find_more_peers,
861                                   NULL);
862 }
863
864
865 /**
866  * gnunet-daemon-topology command line options.
867  */
868 static struct GNUNET_GETOPT_CommandLineOption options[] = {
869   GNUNET_GETOPT_OPTION_END
870 };
871
872
873 /**
874  * Read the friends file.
875  */
876 static void
877 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
878 {
879   char *fn;
880   char *data;
881   size_t pos;
882   GNUNET_HashCode hc;
883   struct stat frstat;
884   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
885   unsigned int entries_found;
886   struct PeerList *fl;
887
888   if (GNUNET_OK !=
889       GNUNET_CONFIGURATION_get_value_filename (cfg,
890                                                "TOPOLOGY",
891                                                "FRIENDS",
892                                                &fn))
893     {
894       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
895                   _("Option `%s' in section `%s' not specified!\n"),
896                   "FRIENDS",
897                   "TOPOLOGY");
898       return;
899     }
900   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
901     GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
902         | GNUNET_DISK_PERM_USER_WRITE);
903   if (0 != STAT (fn, &frstat))
904     {
905       if ((friends_only) || (minimum_friend_count > 0))
906         {
907           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
908                       _("Could not read friends list `%s'\n"), fn);
909           GNUNET_free (fn);
910           return;
911         }
912     }
913   if (frstat.st_size == 0)
914     {
915       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
916                   _("Friends file `%s' is empty.\n"),
917                   fn);
918       GNUNET_free (fn);
919       return;
920     }
921   data = GNUNET_malloc_large (frstat.st_size);
922   if (frstat.st_size !=
923       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
924     {
925       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
926                   _("Failed to read friends list from `%s'\n"), fn);
927       GNUNET_free (fn);
928       GNUNET_free (data);
929       return;
930     }
931   entries_found = 0;
932   pos = 0;
933   while ((pos < frstat.st_size) && isspace (data[pos]))
934     pos++;
935   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
936          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
937     {
938       memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
939       if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
940         {
941           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
942                       _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
943                       (unsigned long long) pos);
944           pos++;
945           while ((pos < frstat.st_size) && (!isspace (data[pos])))
946             pos++;
947           continue;
948         }
949       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
950       if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &hc))
951         {
952           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
953                       _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
954                       (unsigned long long) pos,
955                       &enc);
956         }
957       else
958         {
959           entries_found++;
960           fl = GNUNET_malloc (sizeof(struct PeerList));
961           fl->is_friend = GNUNET_YES;
962           fl->id.hashPubKey = hc;
963           fl->next = friends;
964           friends = fl;
965 #if DEBUG_TOPOLOGY
966           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967                       "Found friend `%s' in configuration\n",
968                       GNUNET_i2s (&fl->id));
969 #endif       
970         }
971       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
972       while ((pos < frstat.st_size) && isspace (data[pos]))
973         pos++;
974     }
975   GNUNET_free (data);
976   GNUNET_free (fn);
977   if ( (minimum_friend_count > entries_found) &&
978        (friends_only == GNUNET_NO) )
979     {
980       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
981                   _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
982     }
983   if ( (minimum_friend_count > target_connection_count) &&
984        (friends_only == GNUNET_NO) )
985     {
986       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
987                   _("More friendly connections required than target total number of connections.\n"));
988     }
989 }
990
991
992 /**
993  * This function is called whenever an encrypted HELLO message is
994  * received.
995  *
996  * @param cls closure
997  * @param other the other peer involved (sender or receiver, NULL
998  *        for loopback messages where we are both sender and receiver)
999  * @param message the actual HELLO message
1000  * @return GNUNET_OK to keep the connection open,
1001  *         GNUNET_SYSERR to close it (signal serious error)
1002  */
1003 static int
1004 handle_encrypted_hello (void *cls,
1005                         const struct GNUNET_PeerIdentity * other,
1006                         const struct GNUNET_MessageHeader *
1007                         message)
1008 {
1009 #if DEBUG_TOPOLOGY
1010   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1011               "Received encrypted `%s' from peer `%s'",
1012               "HELLO",
1013               GNUNET_i2s (other));
1014 #endif  
1015   if (transport != NULL)
1016     GNUNET_TRANSPORT_offer_hello (transport,
1017                                   message);
1018   return GNUNET_OK;
1019 }
1020
1021
1022 /**
1023  * Peerinfo calls this function to let us know about a
1024  * possible peer that we might want to connect to.
1025  *
1026  * @param cls unused
1027  * @param peer NULL for the end of the list, otherwise a peer identity
1028  * @param hello a HELLO for a peer, or NULL
1029  * @param trust how much do we trust the given peer?
1030  */
1031 static void
1032 gather_hello_callback (void *cls,
1033                        const struct GNUNET_PeerIdentity *peer,
1034                        const struct GNUNET_HELLO_Message *hello,
1035                        uint32_t trust)
1036 {
1037   if (peer == NULL)
1038     {
1039       hello_gathering_active = GNUNET_NO;
1040       return;
1041     }
1042 #if DEBUG_TOPOLOGY
1043   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1044               "Received `%s' for peer `%s'",
1045               "HELLO",
1046               GNUNET_i2s (peer));
1047 #endif  
1048   if (hello != NULL)
1049     consider_for_advertising (hello);
1050 }
1051
1052
1053 /**
1054  * Function to fill send buffer with HELLO.
1055  *
1056  * @param cls unused
1057  * @param receiver the receiver of the message
1058  * @param position is the reference to the
1059  *        first unused position in the buffer where GNUnet is building
1060  *        the message
1061  * @param padding is the number of bytes left in that buffer.
1062  * @return the number of bytes written to
1063  *   that buffer (must be a positive number).
1064  */
1065 static unsigned int
1066 hello_advertising (void *cls,
1067                    const struct GNUNET_PeerIdentity *
1068                    receiver,
1069                    void *position, 
1070                    size_t padding)
1071 {
1072   struct PeerList *pl;
1073   struct HelloList *pos;
1074   struct HelloList *prev;
1075   struct HelloList *next;
1076   uint16_t size;
1077
1078 #if DEBUG_TOPOLOGY
1079   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1080               "Data solicited for `%s', considering sending `%s's",
1081               GNUNET_i2s (receiver),
1082               "HELLO");
1083 #endif  
1084   pl = friends;
1085   while (pl != NULL)
1086     {
1087       if (0 == memcmp (&pl->id, receiver, sizeof (struct GNUNET_PeerIdentity)))
1088         break;
1089       pl = pl->next;
1090     }
1091   if (pl == NULL)
1092     {
1093       GNUNET_break (0);
1094       return 0;
1095     }
1096   /* find applicable HELLOs */
1097   prev = NULL;
1098   next = hellos;
1099   while (NULL != (pos = next))
1100     {
1101       next = pos->next;
1102       if (GNUNET_NO ==
1103           GNUNET_CONTAINER_bloomfilter_test (pos->filter,
1104                                              &receiver->hashPubKey))
1105         break;
1106       if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).value)
1107         {
1108           /* time to discard... */
1109           if (prev != NULL)
1110             prev->next = next;
1111           else
1112             hellos = next;
1113           GNUNET_CONTAINER_bloomfilter_free (pos->filter);
1114           GNUNET_free (pos);
1115         }
1116       else
1117         {
1118           prev = pos;
1119         }
1120     }
1121   if (pos != NULL)
1122     {
1123       size = GNUNET_HELLO_size (pos->msg);
1124       if (size < padding)
1125         {
1126           memcpy (position, pos->msg, size);
1127           GNUNET_CONTAINER_bloomfilter_add (pos->filter,
1128                                             &receiver->hashPubKey);
1129         }
1130       else
1131         {
1132           size = 0;
1133         }
1134 #if DEBUG_TOPOLOGY
1135       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1136                   "Sending %u bytes of  `%s's",
1137                   (unsigned int) size,
1138                   "HELLO");
1139 #endif  
1140       return size;
1141     }
1142   if ( (GNUNET_NO == hello_gathering_active) &&
1143        (GNUNET_TIME_absolute_get_duration (last_hello_gather_time).value >
1144         MIN_HELLO_GATHER_DELAY.value) )
1145     {
1146 #if DEBUG_TOPOLOGY
1147       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1148                   "Have no `%s's, trying to get some from `%s' for next time",
1149                   "HELLO",
1150                   "PEERINFO");
1151 #endif  
1152       hello_gathering_active = GNUNET_YES;
1153       last_hello_gather_time = GNUNET_TIME_absolute_get();
1154       GNUNET_PEERINFO_for_all (cfg,
1155                                sched,
1156                                NULL,
1157                                0, GNUNET_TIME_UNIT_FOREVER_REL,
1158                                &gather_hello_callback, NULL);
1159     }
1160   return 0;
1161 }
1162
1163
1164 /**
1165  * Last task run during shutdown.  Disconnects us from
1166  * the transport and core.
1167  */
1168 static void
1169 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1170 {
1171   struct PeerList *pl;
1172
1173   if (NULL != peerinfo_notify)
1174     {
1175       GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1176       peerinfo_notify = NULL;
1177     }
1178   GNUNET_TRANSPORT_disconnect (transport);
1179   transport = NULL;
1180   if (handle != NULL)
1181     {
1182       GNUNET_CORE_disconnect (handle);
1183       handle = NULL;
1184     }
1185   while (NULL != (pl = friends))
1186     {
1187       friends = pl->next;
1188       GNUNET_free (pl);
1189     }  
1190 }
1191
1192
1193 /**
1194  * Main function that will be run.
1195  *
1196  * @param cls closure
1197  * @param s the scheduler to use
1198  * @param args remaining command-line arguments
1199  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1200  * @param c configuration
1201  */
1202 static void
1203 run (void *cls,
1204      struct GNUNET_SCHEDULER_Handle * s,
1205      char *const *args,
1206      const char *cfgfile,
1207      const struct GNUNET_CONFIGURATION_Handle * c)
1208 {
1209   struct GNUNET_CORE_MessageHandler handlers[] =
1210     {
1211       { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1212       { NULL, 0, 0 }
1213     };
1214   unsigned long long opt;
1215
1216   sched = s;
1217   cfg = c;
1218   autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1219                                                       "TOPOLOGY",
1220                                                       "AUTOCONNECT");
1221   friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1222                                                        "TOPOLOGY",
1223                                                        "FRIENDS-ONLY");
1224   if (GNUNET_OK !=
1225       GNUNET_CONFIGURATION_get_value_number (cfg,
1226                                              "TOPOLOGY",
1227                                              "MINIMUM-FRIENDS",
1228                                              &opt))
1229     opt = 0;
1230   minimum_friend_count = (unsigned int) opt;
1231   if (GNUNET_OK !=
1232       GNUNET_CONFIGURATION_get_value_number (cfg,
1233                                              "TOPOLOGY",
1234                                              "TARGET-CONNECTION-COUNT",
1235                                              &opt))
1236     opt = 16;
1237   target_connection_count = (unsigned int) opt;
1238
1239   if ( (friends_only == GNUNET_YES) ||
1240        (minimum_friend_count > 0) )
1241     read_friends_file (cfg);
1242 #if DEBUG_TOPOLOGY
1243   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1244               "Topology would like %u connections with at least %u friends (%s)\n",
1245               target_connection_count,
1246               minimum_friend_count,
1247               autoconnect ? "autoconnect enabled" : "autoconnect disabled");
1248 #endif       
1249   transport = GNUNET_TRANSPORT_connect (sched,
1250                                         cfg,
1251                                         NULL,
1252                                         NULL,
1253                                         NULL,
1254                                         NULL);
1255   handle = GNUNET_CORE_connect (sched,
1256                                 cfg,
1257                                 GNUNET_TIME_UNIT_FOREVER_REL,
1258                                 NULL,
1259                                 &core_init,
1260                                 &connect_notify,
1261                                 &disconnect_notify,
1262                                 &hello_advertising,
1263                                 NULL, GNUNET_NO,
1264                                 NULL, GNUNET_NO,
1265                                 handlers);
1266   GNUNET_SCHEDULER_add_delayed (sched,
1267                                 GNUNET_TIME_UNIT_FOREVER_REL,
1268                                 &cleaning_task, NULL);
1269   if (NULL == transport)
1270     {
1271       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1272                   _("Failed to connect to `%s' service.\n"),
1273                   "transport");
1274       GNUNET_SCHEDULER_shutdown (sched);
1275       return;
1276     }
1277   if (NULL == handle)
1278     {
1279       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1280                   _("Failed to connect to `%s' service.\n"),
1281                   "core");
1282       GNUNET_SCHEDULER_shutdown (sched);
1283       return;
1284     }
1285   peerinfo_notify = GNUNET_PEERINFO_notify (cfg, sched,
1286                                             &process_peer,
1287                                             NULL);
1288 }
1289
1290
1291 /**
1292  * The main function for the topology daemon.
1293  *
1294  * @param argc number of arguments from the command line
1295  * @param argv command line arguments
1296  * @return 0 ok, 1 on error
1297  */
1298 int
1299 main (int argc, char *const *argv)
1300 {
1301   int ret;
1302
1303   ret = (GNUNET_OK ==
1304          GNUNET_PROGRAM_run (argc,
1305                              argv,
1306                              "topology",
1307                              _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
1308                              options,
1309                              &run, NULL)) ? 0 : 1;
1310   return ret;
1311 }
1312
1313 /* end of gnunet-daemon-topology.c */