From 0a8f4767deb731937115481af8846fec0c918d82 Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 17 Feb 2017 00:30:41 -0500 Subject: [PATCH] Implementation of protobuf for records, peers, and messages --- include/libp2p/record/message.h | 77 ++++++++++ include/libp2p/record/record.h | 15 ++ record/message.c | 241 +++++++++++++++++++++++++++++++- record/record.c | 42 ++++++ 4 files changed, 371 insertions(+), 4 deletions(-) diff --git a/include/libp2p/record/message.h b/include/libp2p/record/message.h index 2b64178..28bf448 100644 --- a/include/libp2p/record/message.h +++ b/include/libp2p/record/message.h @@ -1,5 +1,7 @@ #pragma once +#include "libp2p/record/record.h" + /** * protobuf stuff for Message and Peer * This is used for the KAD / DHT stuff @@ -42,3 +44,78 @@ struct Libp2pMessage { int32_t cluster_level_raw; // protobuf field 10 }; +/** + * create a new Peer struct + * @returns a struct or NULL if there was a problem + */ +struct Libp2pPeer* libp2p_message_peer_new(); + +/** + * frees resources from a peer struct + * @param in the peer to free + */ +void libp2p_message_peer_free(struct Libp2pPeer* in); + +/** + * Get an estimate of the necessary size of the buffer to protobuf a particular peer + * @param in the peer to examine + * @returns an approximation of the buffer size required (erring on the side of bigger) + */ +size_t libp2p_message_peer_protobuf_encode_size(struct Libp2pPeer* in); + +/** + * Encode the Peer into a buffer + * @param in the peer + * @param buffer where to put it + * @param max_buffer_size the maximum amount of memory reserved for the buffer + * @param bytes_written the number of bytes written to the buffer + * @returns true(1) on success, otherwise 0 + */ +int libp2p_message_peer_protobuf_encode(struct Libp2pPeer* in, unsigned char* buffer, size_t max_buffer_size, size_t* bytes_written); + +/** + * turn an array of bytes into a Peer + * @param in the protobuf formatted peer + * @param in_size the size of in + * @param out the new Peer + * @returns true(1) on success, otherwise false + */ +int libp2p_message_peer_protobuf_decode(unsigned char* in, size_t in_size, struct Libp2pPeer** out); + +/** + * create a new Libp2pMessage struct + * @returns a new Libp2pMessage with default settings + */ +struct Libp2pMessage* libp2p_message_new(); + +/** + * Deallocate memory from a Message struct + * @param in the struct + */ +void libp2p_message_free(struct Libp2pMessage* in); + +/** + * determine the size necessary for a message struct to be protobuf'd + * @param in the struct to be protobuf'd + * @returns the size required + */ +size_t libp2p_message_protobuf_encode_size(struct Libp2pMessage* in); + +/** + * Encode a Message into a protobuf + * @param in the message + * @param buffer the byte array that will hold the protobuf + * @param max_buffer_size the amount of memory previously reserved for buffer + * @param bytes_written will hold the number of bytes written to buffer + * @returns true(1) on success, otherwise false(0) + */ +int libp2p_message_protobuf_encode(struct Libp2pMessage* in, unsigned char* buffer, size_t max_buffer_size, size_t* bytes_written); + +/** + * turn a protobuf back into a message + * @param buffer the protobuf + * @param buffer_size the length of the buffer + * @param out the message + * @returns true(1) on success, otherwise false(0) + */ +int libp2p_message_protobuf_decode(unsigned char* buffer, size_t buffer_size, struct Libp2pMessage** out); diff --git a/include/libp2p/record/record.h b/include/libp2p/record/record.h index 4b95f25..eafc8bd 100644 --- a/include/libp2p/record/record.h +++ b/include/libp2p/record/record.h @@ -1,5 +1,8 @@ #pragma once +#include "libp2p/record/record.h" +#include "libp2p/crypto/rsa.h" + struct Libp2pRecord { // the key that references this record char* key; @@ -18,6 +21,18 @@ struct Libp2pRecord { size_t time_received_size; }; +/** + * Create a record with default settings + * @returns the newly allocated record struct + */ +struct Libp2pRecord* libp2p_record_new(); + +/** + * Free the resources from a record struct + * @param in the struct to free + */ +void libp2p_record_free(struct Libp2pRecord* in); + /** * Convert a Libp2pRecord into protobuf format * @param in the Libp2pRecord to convert diff --git a/record/message.c b/record/message.c index 60ac84f..b7e3409 100644 --- a/record/message.c +++ b/record/message.c @@ -111,12 +111,12 @@ int libp2p_message_peer_protobuf_decode(unsigned char* in, size_t in_size, struc pos += bytes_read; switch(field_no) { case (1): // id - if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&(ptr->id),&(ptr->id_size), &bytes_read) == 0) + if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&(ptr->id),&(ptr->id_size), &bytes_read)) goto exit; pos += bytes_read; break; case (2): { // array of bytes that is a multiaddress, put it in a vector - if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&vector.buffer, &vector.buffer_size, &bytes_read) == 0) + if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&vector.buffer, &vector.buffer_size, &bytes_read)) goto exit; pos += bytes_read; // now turn it into a byte array @@ -143,7 +143,7 @@ int libp2p_message_peer_protobuf_decode(unsigned char* in, size_t in_size, struc break; } case (3): // enum as varint - if (!protobuf_decode_varint(&in[pos], in_size - pos, (long long unsigned int*)&ptr->connection_type, &bytes_read) == 0) + if (!protobuf_decode_varint(&in[pos], in_size - pos, (long long unsigned int*)&ptr->connection_type, &bytes_read)) goto exit; pos += bytes_read; break; @@ -162,6 +162,239 @@ exit: if (vector.buffer != NULL) free(vector.buffer); return retVal; - } +/*** + * protobuf and other methods for Message + */ + +struct Libp2pMessage* libp2p_message_new() { + struct Libp2pMessage* out = (struct Libp2pMessage*)malloc(sizeof(struct Libp2pMessage)); + if (out != NULL) { + out->closer_peer_head = NULL; + out->cluster_level_raw = 0; + out->key = NULL; + out->key_size = 0; + out->message_type = MESSAGE_TYPE_PING; + out->provider_peer_head = NULL; + out->record = NULL; + } + return out; +} + +void libp2p_message_free(struct Libp2pMessage* in) { + struct Libp2pLinkedList* current = in->closer_peer_head; + while (current != NULL) { + struct Libp2pLinkedList* next = current->next; + libp2p_message_peer_free((struct Libp2pPeer*)next->item); + current = next; + } + if (in->key != NULL) + free(in->key); + current = in->provider_peer_head; + while (current != NULL) { + struct Libp2pLinkedList* next = current->next; + libp2p_message_peer_free((struct Libp2pPeer*)next->item); + current = next; + } + libp2p_record_free(in->record); + free(in); +} + +size_t libp2p_message_protobuf_encode_size(struct Libp2pMessage* in) { + // message type + size_t retVal = 11; + // clusterlevelraw + retVal += 11; + // key + retVal += in->key_size + 11; + // record + retVal += 11 + libp2p_record_protobuf_encode_size(in->record); + // closer peers + struct Libp2pLinkedList* current = in->closer_peer_head; + while (current != NULL) { + retVal += 11 + libp2p_message_peer_protobuf_encode_size((struct Libp2pPeer*)current->item); + current = current->next; + } + // provider peers + current = in->provider_peer_head; + while (current != NULL) { + retVal += 11 + libp2p_message_peer_protobuf_encode_size((struct Libp2pPeer*)current->item); + current = current->next; + } + return retVal; +} + +int libp2p_message_protobuf_encode(struct Libp2pMessage* in, unsigned char* buffer, size_t max_buffer_size, size_t* bytes_written) { + // data & data_size + size_t bytes_used = 0; + *bytes_written = 0; + int retVal = 0; + // field 1 + retVal = protobuf_encode_varint(1, WIRETYPE_VARINT, in->message_type, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); + if (retVal == 0) + return 0; + *bytes_written += bytes_used; + // field 2 + retVal = protobuf_encode_length_delimited(2, WIRETYPE_LENGTH_DELIMITED, in->key, in->key_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); + if (retVal == 0) + return 0; + *bytes_written += bytes_used; + // field 3 + size_t protobuf_size = libp2p_record_protobuf_encode_size(in->record); + unsigned char protobuf[protobuf_size]; + if (!libp2p_record_protobuf_encode(in->record, protobuf, protobuf_size, &protobuf_size)) + return 0; + retVal = protobuf_encode_length_delimited(3, WIRETYPE_LENGTH_DELIMITED, protobuf, protobuf_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); + if (retVal == 0) + return 0; + *bytes_written += bytes_used; + // field 8 (repeated) + struct Libp2pLinkedList* current = in->closer_peer_head; + while (current != NULL) { + struct Libp2pPeer* peer = (struct Libp2pPeer*)current->item; + protobuf_size = libp2p_message_peer_protobuf_encode_size(peer); + unsigned char* peer_buffer = (unsigned char*)malloc(protobuf_size); + if (peer_buffer == NULL) + return 0; + if (!libp2p_message_peer_protobuf_encode(peer, peer_buffer, protobuf_size, &protobuf_size)) { + free(peer_buffer); + return 0; + } + retVal = protobuf_encode_length_delimited(8, WIRETYPE_LENGTH_DELIMITED, peer_buffer, protobuf_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); + free(peer_buffer); + if (retVal == 0) + return 0; + *bytes_written += bytes_used; + current = current->next; + } + // field 9 (repeated) + current = in->provider_peer_head; + while (current != NULL) { + struct Libp2pPeer* peer = (struct Libp2pPeer*)current->item; + protobuf_size = libp2p_message_peer_protobuf_encode_size(peer); + unsigned char* peer_buffer = (unsigned char*)malloc(protobuf_size); + if (peer_buffer == NULL) + return 0; + if (!libp2p_message_peer_protobuf_encode(peer, peer_buffer, protobuf_size, &protobuf_size)) { + free(peer_buffer); + return 0; + } + retVal = protobuf_encode_length_delimited(9, WIRETYPE_LENGTH_DELIMITED, peer_buffer, protobuf_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); + free(peer_buffer); + if (retVal == 0) + return 0; + *bytes_written += bytes_used; + current = current->next; + } + // field 10 + retVal = protobuf_encode_varint(10, WIRETYPE_VARINT, in->cluster_level_raw, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); + if (retVal == 0) + return 0; + *bytes_written += bytes_used; + return 1; +} + +int libp2p_message_protobuf_decode(unsigned char* in, size_t in_size, struct Libp2pMessage** out) { + size_t pos = 0; + int retVal = 0; + size_t buffer_size = 0; + unsigned char* buffer = NULL; + struct Libp2pLinkedList* current_item = NULL; + struct Libp2pLinkedList* last_closer = NULL; + struct Libp2pLinkedList* last_provider = NULL; + + if ( (*out = (struct Libp2pMessage*)malloc(sizeof(struct Libp2pMessage))) == NULL) + goto exit; + + struct Libp2pMessage* ptr = *out; + + while(pos < in_size) { + size_t bytes_read = 0; + int field_no; + enum WireType field_type; + if (protobuf_decode_field_and_type(&in[pos], in_size, &field_no, &field_type, &bytes_read) == 0) { + goto exit; + } + pos += bytes_read; + switch(field_no) { + case (1): // message type + if (!protobuf_decode_varint(&in[pos], in_size - pos, (long long unsigned int*)&ptr->message_type , &bytes_read)) + goto exit; + pos += bytes_read; + break; + case (2): // key + if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&ptr->key, &ptr->key_size, &bytes_read)) + goto exit; + pos += bytes_read; + break; + case (3): // record + if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&buffer, &buffer_size, &bytes_read)) + goto exit; + // turn this back into a record + if (!libp2p_record_protobuf_decode(buffer, buffer_size, &ptr->record)) { + free(buffer); + buffer = NULL; + goto exit; + } + pos += bytes_read; + break; + case (8): // closer peers + if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&buffer, &buffer_size, &bytes_read)) + goto exit; + // turn this back into a peer + current_item = (struct Libp2pLinkedList*)malloc(sizeof(struct Libp2pLinkedList)); + if (current_item == NULL) + goto exit; + current_item->next = NULL; + if (!libp2p_message_peer_protobuf_decode(buffer, buffer_size, (struct Libp2pPeer**)¤t_item->item)) + goto exit; + free(buffer); + buffer = NULL; + if (ptr->closer_peer_head == NULL) { + ptr->closer_peer_head = current_item; + } else { + last_closer->next = current_item; + } + last_closer = current_item; + pos += bytes_read; + break; + case (9): // provider peers + if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&buffer, &buffer_size, &bytes_read)) + goto exit; + // turn this back into a peer + current_item = (struct Libp2pLinkedList*)malloc(sizeof(struct Libp2pLinkedList)); + if (current_item == NULL) + goto exit; + current_item->next = NULL; + if (!libp2p_message_peer_protobuf_decode(buffer, buffer_size, (struct Libp2pPeer**)¤t_item->item)) + goto exit; + free(buffer); + buffer = NULL; + if (ptr->provider_peer_head == NULL) { + ptr->provider_peer_head = current_item; + } else { + last_provider->next = current_item; + } + last_provider = current_item; + pos += bytes_read; + break; + case (10): // cluster level raw + if (!protobuf_decode_varint(&in[pos], in_size - pos, (long long unsigned int*)&ptr->cluster_level_raw , &bytes_read)) + goto exit; + pos += bytes_read; + break; + } + } + + retVal = 1; + +exit: + if (retVal == 0) { + free(*out); + *out = NULL; + } + if (buffer != NULL) + free(buffer); + return retVal; +} diff --git a/record/record.c b/record/record.c index ff4a3e0..1296d16 100644 --- a/record/record.c +++ b/record/record.c @@ -8,6 +8,48 @@ #include "mh/hashes.h" #include "mh/multihash.h" + +/** + * Create a record with default settings + * @returns the newly allocated record struct + */ +struct Libp2pRecord* libp2p_record_new() { + struct Libp2pRecord* out = (struct Libp2pRecord*)malloc(sizeof(struct Libp2pRecord)); + if (out != NULL) { + out->author = NULL; + out->author_size = 0; + out->key = NULL; + out->key_size = 0; + out->signature = NULL; + out->signature_size = 0; + out->time_received = NULL; + out->time_received_size = 0; + out->value = NULL; + out->value_size = 0; + } + return out; +} + +/** + * Free the resources from a record struct + * @param in the struct to free + */ +void libp2p_record_free(struct Libp2pRecord* in) { + if (in != NULL) { + if (in->author != NULL) + free(in->author); + if (in->key != NULL) + free(in->key); + if (in->signature != NULL) + free(in->signature); + if (in->time_received != NULL) + free(in->time_received); + if (in->value != NULL) + free(in->value); + free(in); + } +} + /** * Convert a Libp2pRecord into protobuf format * @param in the Libp2pRecord to convert