fixing core API for early shutdown handling
[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       GNUNET_SCHEDULER_shutdown (sched);
813       return;
814     }
815   handle = server;
816   my_identity = *my_id;
817 #if DEBUG_TOPOLOGY
818   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819               "I am peer `%s'",
820               GNUNET_i2s (my_id));
821 #endif  
822   if (autoconnect)
823     GNUNET_SCHEDULER_add_delayed (sched,
824                                   GNUNET_TIME_UNIT_SECONDS /* give core time to tell us about existing connections */,
825                                   &find_more_peers,
826                                   NULL);
827 }
828
829
830 /**
831  * gnunet-daemon-topology command line options.
832  */
833 static struct GNUNET_GETOPT_CommandLineOption options[] = {
834   GNUNET_GETOPT_OPTION_END
835 };
836
837
838 /**
839  * Read the friends file.
840  */
841 static void
842 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
843 {
844   char *fn;
845   char *data;
846   size_t pos;
847   GNUNET_HashCode hc;
848   struct stat frstat;
849   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
850   unsigned int entries_found;
851   struct PeerList *fl;
852
853   if (GNUNET_OK !=
854       GNUNET_CONFIGURATION_get_value_filename (cfg,
855                                                "TOPOLOGY",
856                                                "FRIENDS",
857                                                &fn))
858     {
859       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
860                   _("Option `%s' in section `%s' not specified!\n"),
861                   "FRIENDS",
862                   "TOPOLOGY");
863       return;
864     }
865   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
866     GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
867         | GNUNET_DISK_PERM_USER_WRITE);
868   if (0 != STAT (fn, &frstat))
869     {
870       if ((friends_only) || (minimum_friend_count > 0))
871         {
872           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
873                       _("Could not read friends list `%s'\n"), fn);
874           GNUNET_free (fn);
875           return;
876         }
877     }
878   if (frstat.st_size == 0)
879     {
880       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
881                   _("Friends file `%s' is empty.\n"),
882                   fn);
883       GNUNET_free (fn);
884       return;
885     }
886   data = GNUNET_malloc_large (frstat.st_size);
887   if (frstat.st_size !=
888       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
889     {
890       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
891                   _("Failed to read friends list from `%s'\n"), fn);
892       GNUNET_free (fn);
893       GNUNET_free (data);
894       return;
895     }
896   entries_found = 0;
897   pos = 0;
898   while ((pos < frstat.st_size) && isspace (data[pos]))
899     pos++;
900   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
901          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
902     {
903       memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
904       if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
905         {
906           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
907                       _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
908                       (unsigned long long) pos);
909           pos++;
910           while ((pos < frstat.st_size) && (!isspace (data[pos])))
911             pos++;
912           continue;
913         }
914       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
915       if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &hc))
916         {
917           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
918                       _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
919                       (unsigned long long) pos,
920                       &enc);
921         }
922       else
923         {
924           entries_found++;
925           fl = GNUNET_malloc (sizeof(struct PeerList));
926           fl->is_friend = GNUNET_YES;
927           fl->id.hashPubKey = hc;
928           fl->next = friends;
929           friends = fl;
930 #if DEBUG_TOPOLOGY
931           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
932                       "Found friend `%s' in configuration\n",
933                       GNUNET_i2s (&fl->id));
934 #endif       
935         }
936       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
937       while ((pos < frstat.st_size) && isspace (data[pos]))
938         pos++;
939     }
940   GNUNET_free (data);
941   GNUNET_free (fn);
942   if ( (minimum_friend_count > entries_found) &&
943        (friends_only == GNUNET_NO) )
944     {
945       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
946                   _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
947     }
948   if ( (minimum_friend_count > target_connection_count) &&
949        (friends_only == GNUNET_NO) )
950     {
951       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
952                   _("More friendly connections required than target total number of connections.\n"));
953     }
954 }
955
956
957 /**
958  * This function is called whenever an encrypted HELLO message is
959  * received.
960  *
961  * @param cls closure
962  * @param other the other peer involved (sender or receiver, NULL
963  *        for loopback messages where we are both sender and receiver)
964  * @param message the actual HELLO message
965  * @return GNUNET_OK to keep the connection open,
966  *         GNUNET_SYSERR to close it (signal serious error)
967  */
968 static int
969 handle_encrypted_hello (void *cls,
970                         const struct GNUNET_PeerIdentity * other,
971                         const struct GNUNET_MessageHeader *
972                         message)
973 {
974 #if DEBUG_TOPOLOGY
975   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
976               "Received encrypted `%s' from peer `%s'",
977               "HELLO",
978               GNUNET_i2s (other));
979 #endif  
980   if (transport != NULL)
981     GNUNET_TRANSPORT_offer_hello (transport,
982                                   message);
983   return GNUNET_OK;
984 }
985
986
987 /**
988  * Peerinfo calls this function to let us know about a
989  * possible peer that we might want to connect to.
990  *
991  * @param cls unused
992  * @param peer NULL for the end of the list, otherwise a peer identity
993  * @param hello a HELLO for a peer, or NULL
994  * @param trust how much do we trust the given peer?
995  */
996 static void
997 gather_hello_callback (void *cls,
998                        const struct GNUNET_PeerIdentity *peer,
999                        const struct GNUNET_HELLO_Message *hello,
1000                        uint32_t trust)
1001 {
1002   if (peer == NULL)
1003     {
1004       hello_gathering_active = GNUNET_NO;
1005       return;
1006     }
1007 #if DEBUG_TOPOLOGY
1008   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1009               "Received `%s' for peer `%s'",
1010               "HELLO",
1011               GNUNET_i2s (peer));
1012 #endif  
1013   if (hello != NULL)
1014     consider_for_advertising (hello);
1015 }
1016
1017
1018 /**
1019  * Function to fill send buffer with HELLO.
1020  *
1021  * @param cls unused
1022  * @param receiver the receiver of the message
1023  * @param position is the reference to the
1024  *        first unused position in the buffer where GNUnet is building
1025  *        the message
1026  * @param padding is the number of bytes left in that buffer.
1027  * @return the number of bytes written to
1028  *   that buffer (must be a positive number).
1029  */
1030 static unsigned int
1031 hello_advertising (void *cls,
1032                    const struct GNUNET_PeerIdentity *
1033                    receiver,
1034                    void *position, 
1035                    size_t padding)
1036 {
1037   struct PeerList *pl;
1038   struct HelloList *pos;
1039   struct HelloList *prev;
1040   struct HelloList *next;
1041   uint16_t size;
1042
1043 #if DEBUG_TOPOLOGY
1044   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1045               "Data solicited for `%s', considering sending `%s's",
1046               GNUNET_i2s (receiver),
1047               "HELLO");
1048 #endif  
1049   pl = friends;
1050   while (pl != NULL)
1051     {
1052       if (0 == memcmp (&pl->id, receiver, sizeof (struct GNUNET_PeerIdentity)))
1053         break;
1054       pl = pl->next;
1055     }
1056   if (pl == NULL)
1057     {
1058       GNUNET_break (0);
1059       return 0;
1060     }
1061   /* find applicable HELLOs */
1062   prev = NULL;
1063   next = hellos;
1064   while (NULL != (pos = next))
1065     {
1066       next = pos->next;
1067       if (GNUNET_NO ==
1068           GNUNET_CONTAINER_bloomfilter_test (pos->filter,
1069                                              &receiver->hashPubKey))
1070         break;
1071       if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).value)
1072         {
1073           /* time to discard... */
1074           if (prev != NULL)
1075             prev->next = next;
1076           else
1077             hellos = next;
1078           GNUNET_CONTAINER_bloomfilter_free (pos->filter);
1079           GNUNET_free (pos);
1080         }
1081       else
1082         {
1083           prev = pos;
1084         }
1085     }
1086   if (pos != NULL)
1087     {
1088       size = GNUNET_HELLO_size (pos->msg);
1089       if (size < padding)
1090         {
1091           memcpy (position, pos->msg, size);
1092           GNUNET_CONTAINER_bloomfilter_add (pos->filter,
1093                                             &receiver->hashPubKey);
1094         }
1095       else
1096         {
1097           size = 0;
1098         }
1099 #if DEBUG_TOPOLOGY
1100       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1101                   "Sending %u bytes of  `%s's",
1102                   (unsigned int) size,
1103                   "HELLO");
1104 #endif  
1105       return size;
1106     }
1107   if ( (GNUNET_NO == hello_gathering_active) &&
1108        (GNUNET_TIME_absolute_get_duration (last_hello_gather_time).value >
1109         MIN_HELLO_GATHER_DELAY.value) )
1110     {
1111 #if DEBUG_TOPOLOGY
1112       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1113                   "Have no `%s's, trying to get some from `%s' for next time",
1114                   "HELLO",
1115                   "PEERINFO");
1116 #endif  
1117       hello_gathering_active = GNUNET_YES;
1118       last_hello_gather_time = GNUNET_TIME_absolute_get();
1119       GNUNET_PEERINFO_for_all (cfg,
1120                                sched,
1121                                NULL,
1122                                0, GNUNET_TIME_UNIT_FOREVER_REL,
1123                                &gather_hello_callback, NULL);
1124     }
1125   return 0;
1126 }
1127
1128
1129 /**
1130  * Last task run during shutdown.  Disconnects us from
1131  * the transport and core.
1132  */
1133 static void
1134 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1135 {
1136   struct PeerList *pl;
1137
1138   GNUNET_TRANSPORT_disconnect (transport);
1139   transport = NULL;
1140   if (handle != NULL)
1141     {
1142       GNUNET_CORE_disconnect (handle);
1143       handle = NULL;
1144     }
1145   while (NULL != (pl = friends))
1146     {
1147       friends = pl->next;
1148       GNUNET_free (pl);
1149     }
1150 }
1151
1152
1153 /**
1154  * Main function that will be run.
1155  *
1156  * @param cls closure
1157  * @param s the scheduler to use
1158  * @param args remaining command-line arguments
1159  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1160  * @param c configuration
1161  */
1162 static void
1163 run (void *cls,
1164      struct GNUNET_SCHEDULER_Handle * s,
1165      char *const *args,
1166      const char *cfgfile,
1167      const struct GNUNET_CONFIGURATION_Handle * c)
1168 {
1169   struct GNUNET_CORE_MessageHandler handlers[] =
1170     {
1171       { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1172       { NULL, 0, 0 }
1173     };
1174   unsigned long long opt;
1175
1176   sched = s;
1177   cfg = c;
1178   autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1179                                                       "TOPOLOGY",
1180                                                       "AUTOCONNECT");
1181   friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1182                                                        "TOPOLOGY",
1183                                                        "FRIENDS-ONLY");
1184   if (GNUNET_OK !=
1185       GNUNET_CONFIGURATION_get_value_number (cfg,
1186                                              "TOPOLOGY",
1187                                              "MINIMUM-FRIENDS",
1188                                              &opt))
1189     opt = 0;
1190   minimum_friend_count = (unsigned int) opt;
1191   if (GNUNET_OK !=
1192       GNUNET_CONFIGURATION_get_value_number (cfg,
1193                                              "TOPOLOGY",
1194                                              "TARGET-CONNECTION-COUNT",
1195                                              &opt))
1196     opt = 16;
1197   target_connection_count = (unsigned int) opt;
1198
1199   if ( (friends_only == GNUNET_YES) ||
1200        (minimum_friend_count > 0) )
1201     read_friends_file (cfg);
1202 #if DEBUG_TOPOLOGY
1203   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1204               "Topology would like %u connections with at least %u friends (%s)\n",
1205               target_connection_count,
1206               minimum_friend_count,
1207               autoconnect ? "autoconnect enabled" : "autoconnect disabled");
1208 #endif       
1209   transport = GNUNET_TRANSPORT_connect (sched,
1210                                         cfg,
1211                                         NULL,
1212                                         NULL,
1213                                         NULL,
1214                                         NULL);
1215   handle = GNUNET_CORE_connect (sched,
1216                                 cfg,
1217                                 GNUNET_TIME_UNIT_FOREVER_REL,
1218                                 NULL,
1219                                 &core_init,
1220                                 &connect_notify,
1221                                 &disconnect_notify,
1222                                 &hello_advertising,
1223                                 NULL, GNUNET_NO,
1224                                 NULL, GNUNET_NO,
1225                                 handlers);
1226   GNUNET_SCHEDULER_add_delayed (sched,
1227                                 GNUNET_TIME_UNIT_FOREVER_REL,
1228                                 &cleaning_task, NULL);
1229   if (NULL == transport)
1230     {
1231       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1232                   _("Failed to connect to `%s' service.\n"),
1233                   "transport");
1234       GNUNET_SCHEDULER_shutdown (sched);
1235       return;
1236     }
1237   if (NULL == handle)
1238     {
1239       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1240                   _("Failed to connect to `%s' service.\n"),
1241                   "core");
1242       GNUNET_SCHEDULER_shutdown (sched);
1243       return;
1244     }
1245 }
1246
1247
1248 /**
1249  * The main function for the topology daemon.
1250  *
1251  * @param argc number of arguments from the command line
1252  * @param argv command line arguments
1253  * @return 0 ok, 1 on error
1254  */
1255 int
1256 main (int argc, char *const *argv)
1257 {
1258   int ret;
1259
1260   ret = (GNUNET_OK ==
1261          GNUNET_PROGRAM_run (argc,
1262                              argv,
1263                              "topology",
1264                              _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
1265                              options,
1266                              &run, NULL)) ? 0 : 1;
1267   return ret;
1268 }
1269
1270 /* end of gnunet-daemon-topology.c */