+ /* FIXME: create stat: routing loop-discard! */
+#if DEBUG_DV_PEER_NUMBERS
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "\n\n\nLoopy loo message\n\n\n");
+#endif
+
+#if DEBUG_DV_MESSAGES
+ direct_id = GNUNET_strdup(GNUNET_i2s(&dn->identity));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: DROPPING MESSAGE uid %u type %d, routing loop! Message immediately from %s!\n", my_short_id, ntohl(incoming->uid), ntohs(packed_message->type), direct_id);
+#endif
+ return GNUNET_OK;
+ }
+
+ /* At this point we have a message, and we need to forward it on to the
+ * next DV hop.
+ */
+ /* FIXME: Can't send message on, we have to behave.
+ * We have to tell core we have a message for the next peer, and let
+ * transport do transport selection on how to get this message to 'em */
+ /*ret = send_message (&destination,
+ &original_sender,
+ packed_message, DV_PRIORITY, DV_DELAY);*/
+
+#if DEBUG_DV_MESSAGES
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: FORWARD %s message for %s, uid %u, size %d type %d, cost %u!\n", my_short_id, "DV DATA", GNUNET_i2s(&destination), ntohl(incoming->uid), ntohs(packed_message->size), ntohs(packed_message->type), pos->cost);
+#endif
+
+ ret = send_message(&destination,
+ &original_sender,
+ NULL,
+ packed_message,
+ packed_message_size,
+ default_dv_priority,
+ ntohl(incoming->uid),
+ GNUNET_TIME_relative_get_forever());
+
+ if (ret != GNUNET_SYSERR)
+ return GNUNET_OK;
+ else
+ {
+#if DEBUG_MESSAGE_DROP
+ direct_id = GNUNET_strdup(GNUNET_i2s(&dn->identity));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: DROPPING MESSAGE type %d, forwarding failed! Message immediately from %s!\n", GNUNET_i2s(&my_identity), ntohs(((struct GNUNET_MessageHeader *)&incoming[1])->type), direct_id);
+#endif
+ return GNUNET_SYSERR;
+ }
+}
+
+#if DEBUG_DV
+/**
+ * Iterator over hash map entries.
+ *
+ * @param cls closure (NULL)
+ * @param key current key code
+ * @param value value in the hash map (DistantNeighbor)
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+int print_neighbors (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct DistantNeighbor *distant_neighbor = value;
+ char my_shortname[5];
+ char referrer_shortname[5];
+ memcpy(&my_shortname, GNUNET_i2s(&my_identity), 4);
+ my_shortname[4] = '\0';
+ memcpy(&referrer_shortname, GNUNET_i2s(&distant_neighbor->referrer->identity), 4);
+ referrer_shortname[4] = '\0';
+
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "`%s' %s: Peer `%s', distance %d, referrer `%s' pkey: %s\n", &my_shortname, "DV", GNUNET_i2s(&distant_neighbor->identity), distant_neighbor->cost, &referrer_shortname, distant_neighbor->pkey == NULL ? "no" : "yes");
+ return GNUNET_YES;
+}
+#endif
+
+/**
+ * Scheduled task which gossips about known direct peers to other connected
+ * peers. Will run until called with reason shutdown.
+ */
+static void
+neighbor_send_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct NeighborSendContext *send_context = cls;
+#if DEBUG_DV_GOSSIP_SEND
+ char * encPeerAbout;
+ char * encPeerTo;
+#endif
+ struct DistantNeighbor *about;
+ struct DirectNeighbor *to;
+ struct FastGossipNeighborList *about_list;
+
+ p2p_dv_MESSAGE_NeighborInfo *message;
+ struct PendingMessage *pending_message;
+
+ if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
+ {
+#if DEBUG_DV_GOSSIP
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: Called with reason shutdown, shutting down!\n",
+ GNUNET_i2s(&my_identity));
+#endif
+ return;
+ }
+
+ if (send_context->fast_gossip_list_head != NULL)
+ {
+ about_list = send_context->fast_gossip_list_head;
+ about = about_list->about;
+ GNUNET_CONTAINER_DLL_remove(send_context->fast_gossip_list_head,
+ send_context->fast_gossip_list_tail,
+ about_list);
+ GNUNET_free(about_list);
+ }
+ else
+ {
+ /* FIXME: this may become a problem, because the heap walk has only one internal "walker". This means
+ * that if two neighbor_send_tasks are operating in lockstep (which is quite possible, given default
+ * values for all connected peers) there may be a serious bias as to which peers get gossiped about!
+ * Probably the *best* way to fix would be to have an opaque pointer to the walk position passed as
+ * part of the walk_get_next call. Then the heap would have to keep a list of walks, or reset the walk
+ * whenever a modification has been detected. Yuck either way. Perhaps we could iterate over the heap
+ * once to get a list of peers to gossip about and gossip them over time... But then if one goes away
+ * in the mean time that becomes nasty. For now we'll just assume that the walking is done
+ * asynchronously enough to avoid major problems (-;
+ *
+ * NOTE: probably fixed once we decided send rate based on allowed bandwidth.
+ */
+ about = GNUNET_CONTAINER_heap_walk_get_next (neighbor_min_heap);
+ }
+ to = send_context->toNeighbor;
+
+ if ((about != NULL) && (to != about->referrer /* split horizon */ ) &&
+#if SUPPORT_HIDING
+ (about->hidden == GNUNET_NO) &&
+#endif
+ (to != NULL) &&
+ (0 != memcmp (&about->identity,
+ &to->identity, sizeof (struct GNUNET_PeerIdentity))) &&
+ (about->pkey != NULL))
+ {
+#if DEBUG_DV_GOSSIP_SEND
+ encPeerAbout = GNUNET_strdup(GNUNET_i2s(&about->identity));
+ encPeerTo = GNUNET_strdup(GNUNET_i2s(&to->identity));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: Sending info about peer %s id %u to directly connected peer %s\n",
+ GNUNET_i2s(&my_identity),
+ encPeerAbout, about->our_id, encPeerTo);
+ GNUNET_free(encPeerAbout);
+ GNUNET_free(encPeerTo);
+#endif
+ pending_message = GNUNET_malloc(sizeof(struct PendingMessage) + sizeof(p2p_dv_MESSAGE_NeighborInfo));
+ pending_message->msg = (struct GNUNET_MessageHeader *)&pending_message[1];
+ pending_message->importance = default_dv_priority;
+ pending_message->timeout = GNUNET_TIME_relative_get_forever();
+ memcpy(&pending_message->recipient, &to->identity, sizeof(struct GNUNET_PeerIdentity));
+ pending_message->msg_size = sizeof(p2p_dv_MESSAGE_NeighborInfo);
+ message = (p2p_dv_MESSAGE_NeighborInfo *)pending_message->msg;
+ message->header.size = htons (sizeof (p2p_dv_MESSAGE_NeighborInfo));
+ message->header.type = htons (GNUNET_MESSAGE_TYPE_DV_GOSSIP);
+ message->cost = htonl (about->cost);
+ message->neighbor_id = htonl (about->our_id);
+
+ memcpy (&message->pkey, about->pkey, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
+ memcpy (&message->neighbor,
+ &about->identity, sizeof (struct GNUNET_PeerIdentity));
+
+ GNUNET_CONTAINER_DLL_insert_after (core_pending_head,
+ core_pending_tail,
+ core_pending_tail,
+ pending_message);
+
+ if (core_transmit_handle == NULL)
+ core_transmit_handle = GNUNET_CORE_notify_transmit_ready(coreAPI, default_dv_priority, GNUNET_TIME_relative_get_forever(), &to->identity, sizeof(p2p_dv_MESSAGE_NeighborInfo), &core_transmit_notify, NULL);
+
+ }
+
+ if (send_context->fast_gossip_list_head != NULL) /* If there are other peers in the fast list, schedule right away */
+ {
+#if DEBUG_DV_PEER_NUMBERS
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "DV SERVICE: still in fast send mode\n");
+#endif
+ send_context->task = GNUNET_SCHEDULER_add_now(sched, &neighbor_send_task, send_context);
+ }
+ else
+ {
+#if DEBUG_DV_PEER_NUMBERS
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "DV SERVICE: entering slow send mode\n");
+#endif
+ send_context->task = GNUNET_SCHEDULER_add_delayed(sched, GNUNET_DV_DEFAULT_SEND_INTERVAL, &neighbor_send_task, send_context);
+ }
+
+ return;
+}
+
+
+/**
+ * Handle START-message. This is the first message sent to us
+ * by the client (can only be one!).
+ *
+ * @param cls closure (always NULL)
+ * @param client identification of the client
+ * @param message the actual message
+ */
+static void
+handle_start (void *cls,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+
+#if DEBUG_DV
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received `%s' request from client\n", "START");
+#endif
+
+ client_handle = client;
+
+ GNUNET_SERVER_client_keep(client_handle);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+#if UNSIMPLER
+/**
+ * Iterate over hash map entries for a distant neighbor,
+ * if direct neighbor matches context call send message
+ *
+ * @param cls closure, a DV_SendContext
+ * @param key current key code
+ * @param value value in the hash map
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+int send_iterator (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct DV_SendContext *send_context = cls;
+ struct DistantNeighbor *distant_neighbor = value;
+
+ if (memcmp(distant_neighbor->referrer, send_context->direct_peer, sizeof(struct GNUNET_PeerIdentity)) == 0) /* They match, send and free */
+ {
+ send_message_via(&my_identity, distant_neighbor, send_context);
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
+#endif
+
+/**
+ * Service server's handler for message send requests (which come
+ * bubbling up to us through the DV plugin).
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param message the actual message
+ */
+void handle_dv_send_message (void *cls,
+ struct GNUNET_SERVER_Client * client,
+ const struct GNUNET_MessageHeader * message)
+{
+ struct GNUNET_DV_SendMessage *send_msg;
+ struct GNUNET_DV_SendResultMessage *send_result_msg;
+ struct PendingMessage *pending_message;
+ size_t address_len;
+ size_t message_size;
+ struct GNUNET_PeerIdentity *destination;
+ struct GNUNET_PeerIdentity *direct;
+ struct GNUNET_MessageHeader *message_buf;
+ char *temp_pos;
+ int offset;
+ static struct GNUNET_CRYPTO_HashAsciiEncoded dest_hash;
+ struct DV_SendContext *send_context;
+#if DEBUG_DV_MESSAGES
+ char *cbuf;
+ struct GNUNET_MessageHeader *packed_message;
+#endif
+
+ if (client_handle == NULL)
+ {
+ client_handle = client;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: Setting initial client handle, never received `%s' message?\n", "dv", "START");
+ }
+ else if (client_handle != client)
+ {
+ client_handle = client;
+ /* What should we do in this case, assert fail or just log the warning? */
+#if DEBUG_DV
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: Setting client handle (was a different client!)!\n", "dv");
+#endif
+ }
+
+ GNUNET_assert(ntohs(message->size) > sizeof(struct GNUNET_DV_SendMessage));
+ send_msg = (struct GNUNET_DV_SendMessage *)message;
+
+ address_len = ntohl(send_msg->addrlen);
+ GNUNET_assert(address_len == sizeof(struct GNUNET_PeerIdentity) * 2);
+ message_size = ntohs(message->size) - sizeof(struct GNUNET_DV_SendMessage) - address_len;
+ destination = GNUNET_malloc(sizeof(struct GNUNET_PeerIdentity));
+ direct = GNUNET_malloc(sizeof(struct GNUNET_PeerIdentity));
+ message_buf = GNUNET_malloc(message_size);
+
+ temp_pos = (char *)&send_msg[1]; /* Set pointer to end of message */
+ offset = 0; /* Offset starts at zero */
+
+ memcpy(destination, &temp_pos[offset], sizeof(struct GNUNET_PeerIdentity));
+ offset += sizeof(struct GNUNET_PeerIdentity);
+
+ memcpy(direct, &temp_pos[offset], sizeof(struct GNUNET_PeerIdentity));
+ offset += sizeof(struct GNUNET_PeerIdentity);
+
+
+ memcpy(message_buf, &temp_pos[offset], message_size);
+ if (memcmp(&send_msg->target, destination, sizeof(struct GNUNET_PeerIdentity)) != 0)
+ {
+ GNUNET_CRYPTO_hash_to_enc (&destination->hashPubKey, &dest_hash); /* GNUNET_i2s won't properly work, need to hash one ourselves */
+ dest_hash.encoding[4] = '\0';
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s: asked to send message to `%s', but address is for `%s'!", "DV SERVICE", GNUNET_i2s(&send_msg->target), (const char *)&dest_hash.encoding);
+ }
+
+#if DEBUG_DV_MESSAGES
+ cbuf = (char *)message_buf;
+ offset = 0;
+ while(offset < message_size)
+ {
+ packed_message = (struct GNUNET_MessageHeader *)&cbuf[offset];
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "%s: DV PLUGIN SEND uid %u type %d to %s\n", my_short_id, ntohl(send_msg->uid), ntohs(packed_message->type), GNUNET_i2s(destination));
+ offset += ntohs(packed_message->size);
+ }
+ /*GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "%s: DV PLUGIN SEND uid %u type %d to %s\n", my_short_id, ntohl(send_msg->uid), ntohs(message_buf->type), GNUNET_i2s(destination));*/
+#endif
+ GNUNET_CRYPTO_hash_to_enc (&destination->hashPubKey, &dest_hash); /* GNUNET_i2s won't properly work, need to hash one ourselves */
+ dest_hash.encoding[4] = '\0';
+ send_context = GNUNET_malloc(sizeof(struct DV_SendContext));
+
+ send_result_msg = GNUNET_malloc(sizeof(struct GNUNET_DV_SendResultMessage));
+ send_result_msg->header.size = htons(sizeof(struct GNUNET_DV_SendResultMessage));
+ send_result_msg->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND_RESULT);
+ send_result_msg->uid = send_msg->uid; /* No need to ntohl->htonl this */
+
+ send_context->importance = ntohl(send_msg->priority);
+ send_context->timeout = send_msg->timeout;
+ send_context->direct_peer = direct;
+ send_context->distant_peer = destination;
+ send_context->message = message_buf;
+ send_context->message_size = message_size;
+ send_context->send_result = send_result_msg;
+#if DEBUG_DV_MESSAGES
+ send_context->uid = send_msg->uid;
+#endif
+
+ if (send_message_via(&my_identity, direct, send_context) != GNUNET_YES)
+ {
+ send_result_msg->result = htons(1);
+ pending_message = GNUNET_malloc(sizeof(struct PendingMessage) + sizeof(struct GNUNET_DV_SendResultMessage));
+ pending_message->msg = (struct GNUNET_MessageHeader *)&pending_message[1];
+ memcpy(&pending_message[1], send_result_msg, sizeof(struct GNUNET_DV_SendResultMessage));
+ GNUNET_free(send_result_msg);
+
+ GNUNET_CONTAINER_DLL_insert_after(plugin_pending_head, plugin_pending_tail, plugin_pending_tail, pending_message);
+
+ if (client_handle != NULL)
+ {
+ if (plugin_transmit_handle == NULL)
+ {
+ plugin_transmit_handle = GNUNET_SERVER_notify_transmit_ready (client_handle,
+ sizeof(struct GNUNET_DV_SendResultMessage),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &transmit_to_plugin, NULL);
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to queue message for plugin, must be one in progress already!!\n");
+ }
+ }
+ GNUNET_CRYPTO_hash_to_enc (&destination->hashPubKey, &dest_hash); /* GNUNET_i2s won't properly work, need to hash one ourselves */
+ dest_hash.encoding[4] = '\0';
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s DV SEND failed to send message to destination `%s' via `%s'\n", my_short_id, (const char *)&dest_hash.encoding, GNUNET_i2s(direct));
+ }
+
+ /* In bizarro world GNUNET_SYSERR indicates that we succeeded */
+#if UNSIMPLER
+ if (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_get_multiple(extended_neighbors, &destination->hashPubKey, &send_iterator, send_context))
+ {
+ send_result_msg->result = htons(1);
+ pending_message = GNUNET_malloc(sizeof(struct PendingMessage) + sizeof(struct GNUNET_DV_SendResultMessage));
+ pending_message->msg = (struct GNUNET_MessageHeader *)&pending_message[1];
+ memcpy(&pending_message[1], send_result_msg, sizeof(struct GNUNET_DV_SendResultMessage));
+ GNUNET_free(send_result_msg);
+
+ GNUNET_CONTAINER_DLL_insert_after(plugin_pending_head, plugin_pending_tail, plugin_pending_tail, pending_message);
+
+ if (client_handle != NULL)
+ {
+ if (plugin_transmit_handle == NULL)
+ {
+ plugin_transmit_handle = GNUNET_SERVER_notify_transmit_ready (client_handle,
+ sizeof(struct GNUNET_DV_SendResultMessage),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &transmit_to_plugin, NULL);
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to queue message for plugin, must be one in progress already!!\n");
+ }
+ }
+ GNUNET_CRYPTO_hash_to_enc (&destination->hashPubKey, &dest_hash); /* GNUNET_i2s won't properly work, need to hash one ourselves */
+ dest_hash.encoding[4] = '\0';
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s DV SEND failed to send message to destination `%s' via `%s'\n", my_short_id, (const char *)&dest_hash.encoding, GNUNET_i2s(direct));
+ }
+#endif
+ GNUNET_free(message_buf);
+ GNUNET_free(send_context);
+ GNUNET_free(direct);
+ GNUNET_free(destination);
+
+ GNUNET_SERVER_receive_done(client, GNUNET_OK);
+}
+
+/** Forward declarations **/
+static int handle_dv_gossip_message (void *cls,
+ const struct GNUNET_PeerIdentity *peer,
+ const struct GNUNET_MessageHeader *message,
+ struct GNUNET_TIME_Relative latency,
+ uint32_t distance);
+
+static int handle_dv_disconnect_message (void *cls,
+ const struct GNUNET_PeerIdentity *peer,
+ const struct GNUNET_MessageHeader *message,
+ struct GNUNET_TIME_Relative latency,
+ uint32_t distance);
+/** End forward declarations **/
+
+
+/**
+ * List of handlers for the messages understood by this
+ * service.
+ *
+ * Hmm... will we need to register some handlers with core and
+ * some handlers with our server here? Because core should be
+ * getting the incoming DV messages (from whichever lower level
+ * transport) and then our server should be getting messages
+ * from the dv_plugin, right?
+ */
+static struct GNUNET_CORE_MessageHandler core_handlers[] = {
+ {&handle_dv_data_message, GNUNET_MESSAGE_TYPE_DV_DATA, 0},
+ {&handle_dv_gossip_message, GNUNET_MESSAGE_TYPE_DV_GOSSIP, 0},
+ {&handle_dv_disconnect_message, GNUNET_MESSAGE_TYPE_DV_DISCONNECT, 0},
+ {NULL, 0, 0}
+};
+
+static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = {
+ {&handle_dv_send_message, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND, 0},
+ {&handle_start, NULL, GNUNET_MESSAGE_TYPE_DV_START, 0},
+ {NULL, NULL, 0, 0}
+};
+
+/**
+ * Free a DistantNeighbor node, including removing it
+ * from the referer's list.
+ */
+static void
+distant_neighbor_free (struct DistantNeighbor *referee)
+{
+ struct DirectNeighbor *referrer;
+
+ referrer = referee->referrer;
+ if (referrer != NULL)
+ {
+ GNUNET_CONTAINER_DLL_remove (referrer->referee_head,
+ referrer->referee_tail, referee);
+ }
+ GNUNET_CONTAINER_heap_remove_node (neighbor_max_heap, referee->max_loc);
+ GNUNET_CONTAINER_heap_remove_node (neighbor_min_heap, referee->min_loc);
+ GNUNET_CONTAINER_multihashmap_remove_all (extended_neighbors,
+ &referee->identity.hashPubKey);
+ GNUNET_free_non_null (referee->pkey);
+ GNUNET_free (referee);
+}
+
+/**
+ * Free a DirectNeighbor node, including removing it
+ * from the referer's list.
+ */
+static void
+direct_neighbor_free (struct DirectNeighbor *direct)
+{
+ struct NeighborSendContext *send_context;
+ struct FastGossipNeighborList *about_list;
+ struct FastGossipNeighborList *prev_about;
+
+ send_context = direct->send_context;
+
+ if (send_context->task != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel(sched, send_context->task);
+
+ about_list = send_context->fast_gossip_list_head;
+ while (about_list != NULL)
+ {
+ GNUNET_CONTAINER_DLL_remove(send_context->fast_gossip_list_head, send_context->fast_gossip_list_tail, about_list);
+ prev_about = about_list;
+ about_list = about_list->next;
+ GNUNET_free(prev_about);
+ }
+ GNUNET_free(send_context);
+ GNUNET_free(direct);
+}
+
+/**
+ * Multihashmap iterator for sending out disconnect messages
+ * for a peer.
+ *
+ * @param cls the peer that was disconnected
+ * @param key key value stored under
+ * @param value the direct neighbor to send disconnect to
+ *
+ * @return GNUNET_YES to continue iteration, GNUNET_NO to stop
+ */
+static int schedule_disconnect_messages (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct DisconnectContext *disconnect_context = cls;
+ struct DirectNeighbor *disconnected = disconnect_context->direct;
+ struct DirectNeighbor *notify = value;
+ struct PendingMessage *pending_message;
+ p2p_dv_MESSAGE_Disconnect *disconnect_message;
+
+ if (memcmp(¬ify->identity, &disconnected->identity, sizeof(struct GNUNET_PeerIdentity)) == 0)
+ return GNUNET_YES; /* Don't send disconnect message to peer that disconnected! */
+
+ pending_message = GNUNET_malloc(sizeof(struct PendingMessage) + sizeof(p2p_dv_MESSAGE_Disconnect));
+ pending_message->msg = (struct GNUNET_MessageHeader *)&pending_message[1];
+ pending_message->importance = default_dv_priority;
+ pending_message->timeout = GNUNET_TIME_relative_get_forever();
+ memcpy(&pending_message->recipient, ¬ify->identity, sizeof(struct GNUNET_PeerIdentity));
+ pending_message->msg_size = sizeof(p2p_dv_MESSAGE_Disconnect);
+ disconnect_message = (p2p_dv_MESSAGE_Disconnect *)pending_message->msg;
+ disconnect_message->header.size = htons (sizeof (p2p_dv_MESSAGE_Disconnect));
+ disconnect_message->header.type = htons (GNUNET_MESSAGE_TYPE_DV_DISCONNECT);
+ disconnect_message->peer_id = htonl(disconnect_context->distant->our_id);
+
+ GNUNET_CONTAINER_DLL_insert_after (core_pending_head,
+ core_pending_tail,
+ core_pending_tail,
+ pending_message);
+
+ if (core_transmit_handle == NULL)
+ core_transmit_handle = GNUNET_CORE_notify_transmit_ready(coreAPI, default_dv_priority, GNUNET_TIME_relative_get_forever(), ¬ify->identity, sizeof(p2p_dv_MESSAGE_Disconnect), &core_transmit_notify, NULL);
+
+ return GNUNET_YES;
+}
+
+/**
+ * Multihashmap iterator for freeing extended neighbors.
+ *
+ * @param cls NULL
+ * @param key key value stored under
+ * @param value the distant neighbor to be freed
+ *
+ * @return GNUNET_YES to continue iteration, GNUNET_NO to stop
+ */
+static int free_extended_neighbors (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct DistantNeighbor *distant = value;
+ distant_neighbor_free(distant);
+ return GNUNET_YES;
+}
+
+/**
+ * Multihashmap iterator for freeing direct neighbors.
+ *
+ * @param cls NULL
+ * @param key key value stored under
+ * @param value the direct neighbor to be freed
+ *
+ * @return GNUNET_YES to continue iteration, GNUNET_NO to stop
+ */
+static int free_direct_neighbors (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct DirectNeighbor *direct = value;
+ direct_neighbor_free(direct);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ * @param tc unused
+ */
+static void
+shutdown_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+#if DEBUG_DV
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "calling CORE_DISCONNECT\n");
+ GNUNET_CONTAINER_multihashmap_iterate(extended_neighbors, &print_neighbors, NULL);
+#endif
+ GNUNET_CONTAINER_multihashmap_iterate(extended_neighbors, &free_extended_neighbors, NULL);
+ GNUNET_CONTAINER_multihashmap_destroy(extended_neighbors);
+ GNUNET_CONTAINER_multihashmap_iterate(direct_neighbors, &free_direct_neighbors, NULL);
+ GNUNET_CONTAINER_multihashmap_destroy(direct_neighbors);
+
+ GNUNET_CONTAINER_heap_destroy(neighbor_max_heap);
+ GNUNET_CONTAINER_heap_destroy(neighbor_min_heap);
+
+ GNUNET_CORE_disconnect (coreAPI);
+ GNUNET_PEERINFO_disconnect(peerinfo_handle);
+ GNUNET_SERVER_mst_destroy(coreMST);
+ GNUNET_free_non_null(my_short_id);
+#if DEBUG_DV
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "CORE_DISCONNECT completed\n");
+#endif
+}
+
+/**
+ * To be called on core init/fail.
+ */
+void core_init (void *cls,
+ struct GNUNET_CORE_Handle * server,
+ const struct GNUNET_PeerIdentity *identity,
+ const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded * publicKey)
+{
+
+ if (server == NULL)
+ {
+ GNUNET_SCHEDULER_cancel(sched, cleanup_task);
+ GNUNET_SCHEDULER_add_now(sched, &shutdown_task, NULL);
+ return;
+ }
+#if DEBUG_DV
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: Core connection initialized, I am peer: %s\n", "dv", GNUNET_i2s(identity));
+#endif
+ memcpy(&my_identity, identity, sizeof(struct GNUNET_PeerIdentity));
+ my_short_id = GNUNET_strdup(GNUNET_i2s(&my_identity));
+ coreAPI = server;
+}
+
+
+#if PKEY_NO_NEIGHBOR_ON_ADD
+/**
+ * Iterator over hash map entries.
+ *
+ * @param cls closure
+ * @param key current key code
+ * @param value value in the hash map
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+static int add_pkey_to_extended (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey = cls;
+ struct DistantNeighbor *distant_neighbor = value;
+
+ if (distant_neighbor->pkey == NULL)
+ {
+ distant_neighbor->pkey = GNUNET_malloc(sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
+ memcpy(distant_neighbor->pkey, pkey, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
+ }
+
+ return GNUNET_YES;
+}
+#endif
+
+/**
+ * Iterator over hash map entries.
+ *
+ * @param cls closure
+ * @param key current key code
+ * @param value value in the hash map
+ * @return GNUNET_YES if we should continue to
+ * iterate,
+ * GNUNET_NO if not.
+ */
+static int update_matching_neighbors (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct NeighborUpdateInfo * update_info = cls;
+ struct DistantNeighbor *distant_neighbor = value;
+
+ if (update_info->referrer == distant_neighbor->referrer) /* Direct neighbor matches, update it's info and return GNUNET_NO */
+ {
+ /* same referrer, cost change! */
+ GNUNET_CONTAINER_heap_update_cost (neighbor_max_heap,
+ update_info->neighbor->max_loc, update_info->cost);
+ GNUNET_CONTAINER_heap_update_cost (neighbor_min_heap,
+ update_info->neighbor->min_loc, update_info->cost);
+ update_info->neighbor->last_activity = update_info->now;
+ update_info->neighbor->cost = update_info->cost;
+ update_info->neighbor->referrer_id = update_info->referrer_peer_id;
+ return GNUNET_NO;
+ }
+
+ return GNUNET_YES;
+}
+
+
+/**
+ * Iterate over all current direct peers, add DISTANT newly connected
+ * peer to the fast gossip list for that peer so we get DV routing
+ * information out as fast as possible!
+ *
+ * @param cls the newly connected neighbor we will gossip about
+ * @param key the hashcode of the peer
+ * @param value the direct neighbor we should gossip to
+ *
+ * @return GNUNET_YES to continue iteration, GNUNET_NO otherwise
+ */
+static int add_distant_all_direct_neighbors (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ struct DirectNeighbor *direct = (struct DirectNeighbor *)value;
+ struct DistantNeighbor *distant = (struct DistantNeighbor *)cls;
+ struct NeighborSendContext *send_context = direct->send_context;
+ struct FastGossipNeighborList *gossip_entry;
+#if DEBUG_DV
+ char *encPeerAbout;
+ char *encPeerTo;
+#endif
+
+ if (distant == NULL)
+ {
+ return GNUNET_YES;
+ }
+
+ if (memcmp(&direct->identity, &distant->identity, sizeof(struct GNUNET_PeerIdentity)) == 0)
+ {
+ return GNUNET_YES; /* Don't gossip to a peer about itself! */
+ }
+
+#if SUPPORT_HIDING
+ if (distant->hidden == GNUNET_YES)
+ return GNUNET_YES; /* This peer should not be gossipped about (hidden) */
+#endif
+ gossip_entry = GNUNET_malloc(sizeof(struct FastGossipNeighborList));
+ gossip_entry->about = distant;
+
+ GNUNET_CONTAINER_DLL_insert_after(send_context->fast_gossip_list_head,
+ send_context->fast_gossip_list_tail,
+ send_context->fast_gossip_list_tail,
+ gossip_entry);
+#if DEBUG_DV
+ encPeerAbout = GNUNET_strdup(GNUNET_i2s(&distant->identity));
+ encPeerTo = GNUNET_strdup(GNUNET_i2s(&direct->identity));
+
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "%s: Fast send info about peer %s id %u for directly connected peer %s\n",
+ GNUNET_i2s(&my_identity),
+ encPeerAbout, distant->our_id, encPeerTo);
+ GNUNET_free(encPeerAbout);
+ GNUNET_free(encPeerTo);
+#endif
+ /*if (send_context->task != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel(sched, send_context->task);*/
+
+ send_context->task = GNUNET_SCHEDULER_add_now(sched, &neighbor_send_task, send_context);
+ return GNUNET_YES;
+}
+
+/**
+ * Callback for hello address creation.
+ *
+ * @param cls closure, a struct HelloContext
+ * @param max maximum number of bytes that can be written to buf
+ * @param buf where to write the address information
+ *
+ * @return number of bytes written, 0 to signal the
+ * end of the iteration.
+ */
+static size_t
+generate_hello_address (void *cls, size_t max, void *buf)
+{
+ struct HelloContext *hello_context = cls;
+ char *addr_buffer;
+ size_t offset;
+ size_t size;
+ size_t ret;
+ char *addr1;
+ char *addr2;
+
+ if (hello_context->addresses_to_add == 0)
+ return 0;
+
+ /* Hello "address" will be concatenation of distant peer and direct peer identities */
+ size = 2 * sizeof(struct GNUNET_PeerIdentity);
+ GNUNET_assert(max >= size);
+
+ addr_buffer = GNUNET_malloc(size);
+ offset = 0;
+ /* Copy the distant peer identity to buffer */
+ memcpy(addr_buffer, &hello_context->distant_peer, sizeof(struct GNUNET_PeerIdentity));
+ offset += sizeof(struct GNUNET_PeerIdentity);
+ /* Copy the direct peer identity to buffer */
+ memcpy(&addr_buffer[offset], hello_context->direct_peer, sizeof(struct GNUNET_PeerIdentity));
+ addr1 = GNUNET_strdup(GNUNET_i2s(hello_context->direct_peer));
+ addr2 = GNUNET_strdup(GNUNET_i2s(&hello_context->distant_peer));
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "%s: GIVING HELLO for %s via %s to TRANSPORT\n", my_short_id, addr2, addr1);
+ GNUNET_free(addr1);
+ GNUNET_free(addr2);
+ ret = GNUNET_HELLO_add_address ("dv",
+ GNUNET_TIME_relative_to_absolute
+ (GNUNET_TIME_UNIT_HOURS), addr_buffer, size,
+ buf, max);
+
+ hello_context->addresses_to_add--;
+
+ GNUNET_free(addr_buffer);
+ return ret;
+}
+
+
+/**
+ * Handles when a peer is either added due to being newly connected
+ * or having been gossiped about, also called when the cost for a neighbor
+ * needs to be updated.
+ *
+ * @param peer identity of the peer whose info is being added/updated
+ * @param pkey public key of the peer whose info is being added/updated
+ * @param referrer_peer_id id to use when sending to 'peer'
+ * @param referrer if this is a gossiped peer, who did we hear it from?
+ * @param cost the cost of communicating with this peer via 'referrer'
+ *
+ * @return the added neighbor, the updated neighbor or NULL (neighbor
+ * not added)
+ */
+static struct DistantNeighbor *
+addUpdateNeighbor (const struct GNUNET_PeerIdentity * peer, struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *pkey,
+ unsigned int referrer_peer_id,
+ struct DirectNeighbor *referrer, unsigned int cost)
+{
+ struct DistantNeighbor *neighbor;
+ struct DistantNeighbor *max;
+ struct GNUNET_TIME_Absolute now;
+ struct NeighborUpdateInfo *neighbor_update;
+ struct HelloContext *hello_context;
+ struct GNUNET_HELLO_Message *hello_msg;
+ unsigned int our_id;
+
+#if DEBUG_DV_PEER_NUMBERS
+ char *encAbout;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s Received sender id (%u)!\n", "DV SERVICE", referrer_peer_id);
+#endif
+
+ now = GNUNET_TIME_absolute_get ();
+ neighbor = GNUNET_CONTAINER_multihashmap_get (extended_neighbors,
+ &peer->hashPubKey);
+ neighbor_update = GNUNET_malloc(sizeof(struct NeighborUpdateInfo));
+ neighbor_update->neighbor = neighbor;
+ neighbor_update->cost = cost;
+ neighbor_update->now = now;
+ neighbor_update->referrer = referrer;
+ neighbor_update->referrer_peer_id = referrer_peer_id;
+
+ if (neighbor != NULL)
+ {
+#if USE_PEER_ID
+ memcpy(&our_id, &neighbor->identity, sizeof(unsigned int));
+#else
+ our_id = neighbor->our_id;
+#endif
+ }
+ else
+ {
+#if USE_PEER_ID
+ memcpy(&our_id, peer, sizeof(unsigned int));
+#else
+ our_id = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, RAND_MAX - 1) + 1;
+#endif
+ }
+
+ /* Either we do not know this peer, or we already do but via a different immediate peer */
+ if ((neighbor == NULL) ||
+ (GNUNET_CONTAINER_multihashmap_get_multiple(extended_neighbors,
+ &peer->hashPubKey,
+ &update_matching_neighbors,
+ neighbor_update) != GNUNET_SYSERR))
+ {
+
+#if AT_MOST_ONE
+ if ((neighbor != NULL) && (cost < neighbor->cost)) /* New cost is less than old, remove old */
+ {
+ distant_neighbor_free(neighbor);
+ }
+ else if (neighbor != NULL) /* Only allow one DV connection to each peer */
+ {
+ return NULL;
+ }
+#endif
+ /* new neighbor! */
+ if (cost > fisheye_depth)
+ {
+ /* too costly */
+ GNUNET_free(neighbor_update);
+ return NULL;
+ }
+
+#if DEBUG_DV_PEER_NUMBERS
+ encAbout = GNUNET_strdup(GNUNET_i2s(peer));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: %s Chose NEW id (%u) for peer %s!\n", GNUNET_i2s(&my_identity), "DV SERVICE", our_id, encAbout);
+ GNUNET_free(encAbout);
+#endif
+
+ if (max_table_size <=
+ GNUNET_CONTAINER_multihashmap_size (extended_neighbors))
+ {
+ /* remove most expensive entry */
+ max = GNUNET_CONTAINER_heap_peek (neighbor_max_heap);
+ GNUNET_assert(max != NULL);
+ if (cost > max->cost)
+ {
+ /* new entry most expensive, don't create */
+ GNUNET_free(neighbor_update);
+ return NULL;
+ }
+ if (max->cost > 1)
+ {
+ /* only free if this is not a direct connection;
+ we could theoretically have more direct
+ connections than DV entries allowed total! */
+ distant_neighbor_free (max);
+ }
+ }
+
+ neighbor = GNUNET_malloc (sizeof (struct DistantNeighbor));
+ GNUNET_CONTAINER_DLL_insert (referrer->referee_head,
+ referrer->referee_tail, neighbor);
+ neighbor->max_loc = GNUNET_CONTAINER_heap_insert (neighbor_max_heap,
+ neighbor, cost);
+ neighbor->min_loc = GNUNET_CONTAINER_heap_insert (neighbor_min_heap,
+ neighbor, cost);
+ neighbor->referrer = referrer;
+ memcpy (&neighbor->identity, peer, sizeof (struct GNUNET_PeerIdentity));
+ if (pkey != NULL) /* pkey will be null on direct neighbor addition */
+ {
+ neighbor->pkey = GNUNET_malloc(sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
+ memcpy (neighbor->pkey, pkey, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded));
+ }
+ else
+ neighbor->pkey = pkey;
+
+ neighbor->last_activity = now;
+ neighbor->cost = cost;
+ neighbor->referrer_id = referrer_peer_id;
+ neighbor->our_id = our_id;
+ neighbor->hidden =
+ (cost == DIRECT_NEIGHBOR_COST) ? (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 4) ==
+ 0) : GNUNET_NO;
+
+ GNUNET_CONTAINER_multihashmap_put (extended_neighbors, &peer->hashPubKey,
+ neighbor,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+
+ if (cost != DIRECT_NEIGHBOR_COST)
+ {
+ /* Added neighbor, now send HELLO to transport */
+ hello_context = GNUNET_malloc(sizeof(struct HelloContext));
+ hello_context->direct_peer = &referrer->identity;
+ memcpy(&hello_context->distant_peer, peer, sizeof(struct GNUNET_PeerIdentity));
+ hello_context->addresses_to_add = 1;
+ hello_msg = GNUNET_HELLO_create(pkey, &generate_hello_address, hello_context);
+ GNUNET_assert(memcmp(hello_context->direct_peer, &hello_context->distant_peer, sizeof(struct GNUNET_PeerIdentity)) != 0);
+ send_to_plugin(hello_context->direct_peer, GNUNET_HELLO_get_header(hello_msg), GNUNET_HELLO_size(hello_msg), &hello_context->distant_peer, cost);
+ GNUNET_free(hello_context);
+ GNUNET_free(hello_msg);
+ }
+
+ }
+ else
+ {
+#if DEBUG_DV_GOSSIP
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: Already know peer %s distance %d, referrer id %d!\n", "dv", GNUNET_i2s(peer), cost, referrer_peer_id);
+#endif
+ }
+#if DEBUG_DV
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%s: Size of extended_neighbors is %d\n", "dv", GNUNET_CONTAINER_multihashmap_size(extended_neighbors));
+#endif
+
+ GNUNET_free(neighbor_update);
+ return neighbor;
+}
+
+
+/**
+ * Core handler for dv disconnect messages. These will be used
+ * by us to tell transport via the dv plugin that a peer can
+ * no longer be contacted by us via a certain address. We should
+ * then propagate these messages on, given that the distance to
+ * the peer indicates we would have gossiped about it to others.
+ *
+ * @param cls closure
+ * @param peer peer which sent the message (immediate sender)
+ * @param message the message
+ * @param latency the latency of the connection we received the message from
+ * @param distance the distance to the immediate peer
+ */
+static int handle_dv_disconnect_message (void *cls,
+ const struct GNUNET_PeerIdentity *peer,
+ const struct GNUNET_MessageHeader *message,
+ struct GNUNET_TIME_Relative latency,
+ uint32_t distance)
+{
+ struct DirectNeighbor *referrer;
+ struct DistantNeighbor *distant;
+ p2p_dv_MESSAGE_Disconnect *enc_message = (p2p_dv_MESSAGE_Disconnect *)message;
+
+ if (ntohs (message->size) < sizeof (p2p_dv_MESSAGE_Disconnect))
+ {
+ return GNUNET_SYSERR; /* invalid message */
+ }
+
+ referrer = GNUNET_CONTAINER_multihashmap_get (direct_neighbors,
+ &peer->hashPubKey);
+ if (referrer == NULL)
+ return GNUNET_OK;
+
+ distant = referrer->referee_head;
+ while (distant != NULL)
+ {
+ if (distant->referrer_id == ntohl(enc_message->peer_id))
+ {
+ distant_neighbor_free(distant);
+ }
+ distant = referrer->referee_head;