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