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