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