c-libp2p/peer/peerstore.c

291 lines
9.3 KiB
C

#include <stdlib.h>
#include <string.h>
#include "libp2p/peer/peerstore.h"
#include "libp2p/utils/logger.h"
/***
* Creates a new PeerEntry struct
* @returns the newly allocated struct or NULL
*/
struct PeerEntry* libp2p_peer_entry_new() {
struct PeerEntry* out = (struct PeerEntry*)malloc(sizeof(struct PeerEntry));
if (out != NULL) {
out->peer = NULL;
}
return out;
}
/***
* Frees resources
* @param in the PeerEntry to free
*/
void libp2p_peer_entry_free(struct PeerEntry* in) {
if (in != NULL) {
libp2p_peer_free(in->peer);
free(in);
}
}
/***
* Copies a PeerEntry
* @param in the PeerEntry to copy
* @returns a newly allocated PeerEntry with the values from "in"
*/
struct PeerEntry* libp2p_peer_entry_copy(struct PeerEntry* in) {
struct PeerEntry* out = libp2p_peer_entry_new();
if (out != NULL) {
out->peer = libp2p_peer_copy(in->peer);
if (out->peer == NULL) {
free(out);
return NULL;
}
}
return out;
}
/**
* Creates a new empty peerstore
* @param peer_id the peer id as a null terminated string
* @returns an empty peerstore or NULL on error
*/
struct Peerstore* libp2p_peerstore_new(const struct Libp2pPeer* local_peer) {
struct Peerstore* out = (struct Peerstore*)malloc(sizeof(struct Peerstore));
if (out != NULL) {
out->head_entry = NULL;
out->last_entry = NULL;
// now add this peer as the first entry
libp2p_peerstore_add_peer(out, local_peer);
}
return out;
}
/**
* Deallocate resources used by the peerstore
* @param in the struct to deallocate
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_peerstore_free(struct Peerstore* in) {
if (in != NULL) {
struct Libp2pLinkedList* current = in->head_entry;
struct Libp2pLinkedList* next = NULL;
// first empty out the peer entries
while (current != NULL) {
next = current->next;
libp2p_peer_entry_free((struct PeerEntry*)current->item);
current->item = NULL;
current = next;
}
// now free the linked list entries
libp2p_utils_linked_list_free(in->head_entry);
// and finally the peerstore itself
free(in);
}
return 1;
}
/**
* Add a Peer to the Peerstore
* @param peerstore the peerstore to add the entry to
* @param peer_entry the entry to add
* @returns true(1) on success, otherwise false
*/
int libp2p_peerstore_add_peer_entry(struct Peerstore* peerstore, struct PeerEntry* peer_entry) {
if (peer_entry == NULL)
return 0;
struct Libp2pLinkedList* new_item = libp2p_utils_linked_list_new();
if (new_item == NULL)
return 0;
new_item->item = peer_entry;
if (peerstore->head_entry == NULL) {
peerstore->head_entry = new_item;
peerstore->last_entry = new_item;
} else {
peerstore->last_entry->next = new_item;
peerstore->last_entry = new_item;
}
return 1;
}
int libp2p_peerstore_update_addresses(struct Libp2pPeer* existing, const struct Libp2pPeer* incoming) {
struct Libp2pLinkedList* incoming_list_pos = incoming->addr_head;
while (incoming_list_pos != NULL) {
if (incoming_list_pos->item == NULL) {
libp2p_logger_error("peerstore", "update_addresses: incoming address list has null item.\n");
} else {
struct MultiAddress* incoming_ma = (struct MultiAddress*) incoming_list_pos->item;
// see if we have this multiaddress already
struct Libp2pLinkedList* existing_list_pos = existing->addr_head;
while (existing_list_pos != NULL) {
if (multiaddress_compare(incoming_ma, (struct MultiAddress*)existing_list_pos->item) == 0) {
// we found a match
break;
}
existing_list_pos = existing_list_pos->next;
}
if (existing_list_pos == NULL) {
// we didnt find a match. Add this multiaddress to the top of the existing peer's list
struct MultiAddress* ma_copy = multiaddress_copy(incoming_ma);
if (ma_copy == NULL) {
libp2p_logger_error("peerstore", "Attempted copy of multiaddress, but was null.");
} else {
struct Libp2pLinkedList* new_ma = libp2p_utils_linked_list_new();
new_ma->item = multiaddress_copy(incoming_list_pos->item);
new_ma->next = existing->addr_head;
existing->addr_head = new_ma;
}
}
}
incoming_list_pos = incoming_list_pos->next;
}
return 1;
}
/***
* Add a peer to the peerstore
* @param peerstore the peerstore to add the entry to
* @param peer the peer to add (will be wrapped in PeerEntry struct)
* @returns true(1) on success, otherwise false
*/
int libp2p_peerstore_add_peer(struct Peerstore* peerstore, const struct Libp2pPeer* peer) {
int retVal = 0;
char* ma_string = "";
if (peer != NULL && peer->addr_head != NULL && peer->addr_head->item != NULL) {
ma_string = ((struct MultiAddress*)peer->addr_head->item)->string;
}
// first check to see if it exists. If it does, return TRUE
struct PeerEntry* peer_entry = libp2p_peerstore_get_peer_entry(peerstore, (unsigned char*)peer->id, peer->id_size);
if (peer_entry != NULL) {
libp2p_logger_debug("peerstore", "Attempted to add %s to peerstore, but already there. Checking if we need to update addresses.\n", ma_string);
libp2p_peerstore_update_addresses(peer_entry->peer, peer);
return 1;
}
if (peer->id_size > 0) {
if (peer->addr_head != NULL && peer->addr_head->item != NULL) {
char* address = ((struct MultiAddress*)peer->addr_head->item)->string;
libp2p_logger_debug("peerstore", "Adding peer %s with address %s to peer store\n", libp2p_peer_id_to_string(peer), address);
} else {
libp2p_logger_debug("peerstore", "Adding peer %s with no addresses to peer store.\n", libp2p_peer_id_to_string(peer));
}
struct PeerEntry* peer_entry = libp2p_peer_entry_new();
if (peer_entry == NULL) {
libp2p_logger_error("peerstore", "Unable to allocate memory for new PeerEntry.\n");
return 0;
}
peer_entry->peer = libp2p_peer_copy(peer);
if (peer_entry->peer == NULL) {
libp2p_logger_error("peerstore", "Could not copy peer for PeerEntry.\n");
return 0;
}
retVal = libp2p_peerstore_add_peer_entry(peerstore, peer_entry);
libp2p_logger_debug("peerstore", "Adding peer %s to peerstore was a success\n", libp2p_peer_id_to_string(peer));
}
return retVal;
}
/**
* Retrieve a peer from the peerstore based on the peer id
* @param peerstore the peerstore to search
* @param peer_id the id to search for as a binary array
* @param peer_id_size the size of the binary array
* @returns the PeerEntry struct if found, otherwise NULL
*/
struct PeerEntry* libp2p_peerstore_get_peer_entry(struct Peerstore* peerstore, const unsigned char* peer_id, size_t peer_id_size) {
if (peer_id_size == 0 || peer_id == NULL || peerstore == NULL)
return NULL;
struct Libp2pLinkedList* current = peerstore->head_entry;
while(current != NULL) {
struct Libp2pPeer* peer = ((struct PeerEntry*)current->item)->peer;
if (peer->id_size == peer_id_size) {
if (memcmp(peer_id, peer->id, peer->id_size) == 0) {
return (struct PeerEntry*)current->item;
}
}
current = current->next;
}
return NULL;
}
/**
* Retrieve a peer from the peerstore based on the peer id
* @param peerstore the peerstore to search
* @param peer_id the id to search for as a binary array
* @param peer_id_size the size of the binary array
* @returns the Peer struct if found, otherwise NULL
*/
struct Libp2pPeer* libp2p_peerstore_get_peer(struct Peerstore* peerstore, const unsigned char* peer_id, size_t peer_id_size) {
struct PeerEntry* entry = libp2p_peerstore_get_peer_entry(peerstore, peer_id, peer_id_size);
if (entry == NULL)
return NULL;
return entry->peer;
}
/**
* Retrieves the local peer, which is always the first in the collection
* @param peerstore the peerstore
* @returns the first Libp2pPeer in the collection
*/
struct Libp2pPeer* libp2p_peerstore_get_local_peer(struct Peerstore* peerstore) {
struct Libp2pPeer* retVal = NULL;
if (peerstore != NULL && peerstore->head_entry != NULL && peerstore->head_entry->item != NULL) {
struct PeerEntry* entry = peerstore->head_entry->item;
retVal = entry->peer;
}
return retVal;
}
/***
* Look for a peer by id. If not found, add it to the peerstore
* @param peerstore the Peerstore
* @param peer_id the peer id
* @param peer_id_size the size of peer_id
* @returns a Peer struct, or NULL if error
*/
struct Libp2pPeer* libp2p_peerstore_get_or_add_peer_by_id(struct Peerstore* peerstore, const unsigned char* peer_id, size_t peer_id_size) {
if (peer_id_size == 0)
return NULL;
struct PeerEntry* entry = libp2p_peerstore_get_peer_entry(peerstore, peer_id, peer_id_size);
if (entry == NULL) {
// add it
struct Libp2pPeer* temp_peer = libp2p_peer_new();
temp_peer->id_size = peer_id_size;
temp_peer->id = (char*)peer_id;
libp2p_peerstore_add_peer(peerstore, temp_peer);
libp2p_peer_free(temp_peer);
entry = libp2p_peerstore_get_peer_entry(peerstore, peer_id, peer_id_size);
}
if (entry == NULL)
return NULL;
return entry->peer;
}
/**
* Look for this peer in the peerstore. If it is found, return a reference to that object.
* If it is not found, add it, and return a reference to the new copy
* @param peerstore the peerstore to search
* @param in the peer to search for
*/
struct Libp2pPeer* libp2p_peerstore_get_or_add_peer(struct Peerstore* peerstore, const struct Libp2pPeer* in) {
if (in == NULL)
return NULL;
struct Libp2pPeer* out = libp2p_peerstore_get_peer(peerstore, (unsigned char*)in->id, in->id_size);
if (out != NULL)
return out;
// we didn't find it. attempt to add
if (!libp2p_peerstore_add_peer(peerstore, in))
return NULL;
return libp2p_peerstore_get_peer(peerstore, (unsigned char*)in->id, in->id_size);
}