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