Implementation of protobuf for records, peers, and messages
This commit is contained in:
parent
029e3d800f
commit
0a8f4767de
4 changed files with 371 additions and 4 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
241
record/message.c
241
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue