More changes to the stream negotiation
This commit is contained in:
parent
9dd1dab8e4
commit
08a02f7956
24 changed files with 768 additions and 314 deletions
4
Makefile
4
Makefile
|
@ -2,6 +2,8 @@
|
|||
DEBUG = true
|
||||
export DEBUG
|
||||
|
||||
LINKER_FLAGS=
|
||||
|
||||
OBJS = \
|
||||
conn/*.o \
|
||||
crypto/*.o \
|
||||
|
@ -19,7 +21,7 @@ OBJS = \
|
|||
yamux/*.o
|
||||
|
||||
link:
|
||||
ar rcs libp2p.a $(OBJS)
|
||||
ar rcs libp2p.a $(OBJS) $(LINKER_FLAGS)
|
||||
|
||||
compile:
|
||||
cd conn; make all;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Functions for handling the local dialer
|
||||
*/
|
||||
|
||||
#include "libp2p/crypto/encoding/x509.h"
|
||||
#include "libp2p/conn/dialer.h"
|
||||
#include "libp2p/conn/connection.h"
|
||||
#include "libp2p/conn/transport_dialer.h"
|
||||
|
@ -17,16 +18,33 @@ struct TransportDialer* libp2p_conn_tcp_transport_dialer_new();
|
|||
|
||||
/**
|
||||
* Create a Dialer with the specified local information
|
||||
* @param peer the local peer
|
||||
* @param peerstore the local peerstore
|
||||
* @param private_key the local private key
|
||||
* @returns a new Dialer struct
|
||||
*/
|
||||
struct Dialer* libp2p_conn_dialer_new(struct Libp2pPeer* peer, struct RsaPrivateKey* private_key) {
|
||||
struct Dialer* libp2p_conn_dialer_new(struct Libp2pPeer* peer, struct Peerstore* peerstore, struct PrivateKey* private_key) {
|
||||
int success = 0;
|
||||
struct Dialer* dialer = (struct Dialer*)malloc(sizeof(struct Dialer));
|
||||
if (dialer != NULL) {
|
||||
dialer->peerstore = peerstore;
|
||||
dialer->peer_id = malloc(peer->id_size + 1);
|
||||
memset(dialer->peer_id, 0, peer->id_size + 1);
|
||||
if (dialer->peer_id != NULL) {
|
||||
strncpy(dialer->peer_id, peer->id, peer->id_size);
|
||||
dialer->private_key = private_key;
|
||||
// convert private key to rsa private key
|
||||
struct RsaPrivateKey* rsa_private_key = libp2p_crypto_rsa_rsa_private_key_new();
|
||||
if (!libp2p_crypto_encoding_x509_der_to_private_key(private_key->data, private_key->data_size, rsa_private_key)) {
|
||||
libp2p_crypto_rsa_rsa_private_key_free(rsa_private_key);
|
||||
libp2p_conn_dialer_free(dialer);
|
||||
return NULL;
|
||||
}
|
||||
if (!libp2p_crypto_rsa_private_key_fill_public_key(rsa_private_key)) {
|
||||
libp2p_crypto_rsa_rsa_private_key_free(rsa_private_key);
|
||||
libp2p_conn_dialer_free(dialer);
|
||||
return NULL;
|
||||
}
|
||||
dialer->private_key = rsa_private_key;
|
||||
//TODO: build transport dialers
|
||||
dialer->transport_dialers = NULL;
|
||||
dialer->fallback_dialer = libp2p_conn_tcp_transport_dialer_new(dialer->peer_id, private_key);
|
||||
|
@ -45,7 +63,7 @@ struct Dialer* libp2p_conn_dialer_new(struct Libp2pPeer* peer, struct RsaPrivate
|
|||
void libp2p_conn_dialer_free(struct Dialer* in) {
|
||||
if (in != NULL) {
|
||||
free(in->peer_id);
|
||||
//libp2p_crypto_private_key_free(in->private_key);
|
||||
libp2p_crypto_rsa_rsa_private_key_free(in->private_key);
|
||||
if (in->transport_dialers != NULL) {
|
||||
struct Libp2pLinkedList* current = in->transport_dialers;
|
||||
while(current != NULL) {
|
||||
|
@ -83,6 +101,8 @@ struct Stream* libp2p_conn_dialer_get_connection(const struct Dialer* dialer, co
|
|||
* @returns true(1) on success, false(0) otherwise
|
||||
*/
|
||||
int libp2p_conn_dialer_join_swarm(const struct Dialer* dialer, struct Libp2pPeer* peer, int timeout_secs) {
|
||||
if (dialer == NULL || peer == NULL)
|
||||
return 0;
|
||||
// find the right Multiaddress
|
||||
struct Libp2pLinkedList* current_entry = peer->addr_head;
|
||||
struct Stream* conn_stream = NULL;
|
||||
|
@ -90,19 +110,24 @@ int libp2p_conn_dialer_join_swarm(const struct Dialer* dialer, struct Libp2pPeer
|
|||
struct MultiAddress* ma = current_entry->item;
|
||||
conn_stream = libp2p_conn_dialer_get_connection(dialer, ma);
|
||||
if (conn_stream != NULL) {
|
||||
if (peer->sessionContext == NULL) {
|
||||
peer->sessionContext = libp2p_session_context_new();
|
||||
}
|
||||
peer->sessionContext->insecure_stream = conn_stream;
|
||||
peer->sessionContext->default_stream = conn_stream;
|
||||
peer->sessionContext->port = multiaddress_get_ip_port(ma);
|
||||
multiaddress_get_ip_address(ma, &peer->sessionContext->host);
|
||||
break;
|
||||
}
|
||||
current_entry = current_entry->next;
|
||||
}
|
||||
if (conn_stream == NULL)
|
||||
return 0;
|
||||
peer->sessionContext->insecure_stream = conn_stream;
|
||||
peer->sessionContext->default_stream = conn_stream;
|
||||
// multistream
|
||||
struct Stream* new_stream = libp2p_net_multistream_stream_new(conn_stream);
|
||||
if (new_stream != NULL) {
|
||||
// secio over multistream
|
||||
new_stream = libp2p_secio_stream_new(new_stream);
|
||||
new_stream = libp2p_secio_stream_new(new_stream, peer, dialer->peerstore, dialer->private_key);
|
||||
if (new_stream != NULL) {
|
||||
peer->sessionContext->default_stream = new_stream;
|
||||
// multistream over secio
|
||||
|
@ -116,12 +141,13 @@ int libp2p_conn_dialer_join_swarm(const struct Dialer* dialer, struct Libp2pPeer
|
|||
// identity over yamux
|
||||
// kademlia over yamux
|
||||
// circuit relay over yamux
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,7 +37,6 @@ struct SessionContext* libp2p_session_context_new() {
|
|||
context->shared_key = NULL;
|
||||
context->shared_key_size = 0;
|
||||
context->traffic_type = TCP;
|
||||
context->last_comm_epoch = 0;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,12 @@ struct TransportDialer* libp2p_conn_transport_dialer_new(char* peer_id, struct P
|
|||
if (out != NULL) {
|
||||
out->peer_id = malloc(strlen(peer_id) + 1);
|
||||
strcpy(out->peer_id, peer_id);
|
||||
if (private_key != NULL) {
|
||||
out->private_key = (struct PrivateKey*)malloc(sizeof(struct PrivateKey));
|
||||
libp2p_crypto_private_key_copy(private_key, out->private_key);
|
||||
} else {
|
||||
out->private_key = NULL;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ struct Dialer {
|
|||
*/
|
||||
char* peer_id; // the local peer ID as null terminated string
|
||||
struct RsaPrivateKey* private_key; // used to initiate secure connections, can be NULL, and connections will not be secured
|
||||
struct Peerstore* peerstore; // used by secio to add peers to the collection
|
||||
|
||||
/**
|
||||
* A linked list of transport dialers. A transport dialer can be selected
|
||||
|
@ -42,7 +43,7 @@ struct Dialer {
|
|||
* @param private_key the local private key
|
||||
* @returns a new Dialer struct
|
||||
*/
|
||||
struct Dialer* libp2p_conn_dialer_new(struct Libp2pPeer* peer, struct RsaPrivateKey* private_key);
|
||||
struct Dialer* libp2p_conn_dialer_new(struct Libp2pPeer* peer, struct Peerstore* peerstore, struct PrivateKey* private_key);
|
||||
|
||||
/**
|
||||
* free resources from the Dialer struct
|
||||
|
|
|
@ -62,7 +62,6 @@ struct SessionContext {
|
|||
struct StretchedKey* remote_stretched_key;
|
||||
unsigned char* remote_ephemeral_public_key;
|
||||
size_t remote_ephemeral_public_key_size;
|
||||
unsigned long long last_comm_epoch;
|
||||
};
|
||||
|
||||
/***
|
||||
|
|
|
@ -83,10 +83,10 @@ struct Stream* libp2p_net_multistream_connect_with_timeout(const char* hostname,
|
|||
* NOTE: the SessionContext should already contain the connected stream. If not, use
|
||||
* libp2p_net_multistream_connect instead of this method.
|
||||
*
|
||||
* @param session the struct Session, which contains all the context info
|
||||
* @param ctx the MultistreamContext
|
||||
* @returns true(1) on success, or false(0)
|
||||
*/
|
||||
int libp2p_net_multistream_negotiate(struct SessionContext* session);
|
||||
int libp2p_net_multistream_negotiate(struct MultistreamContext* ctx);
|
||||
|
||||
/**
|
||||
* Expect to read a message, and follow its instructions
|
||||
|
|
|
@ -31,6 +31,7 @@ void libp2p_stream_message_free(struct StreamMessage* msg);
|
|||
*/
|
||||
struct ConnectionContext {
|
||||
int socket_descriptor;
|
||||
unsigned long long last_comm_epoch;
|
||||
struct SessionContext* session_context;
|
||||
};
|
||||
|
||||
|
|
|
@ -140,3 +140,9 @@ int libp2p_peer_protobuf_encode_with_alloc(struct Libp2pPeer* in, unsigned char*
|
|||
*/
|
||||
int libp2p_peer_protobuf_decode(unsigned char* in, size_t in_size, struct Libp2pPeer** out);
|
||||
|
||||
/***
|
||||
* Get the last time we communicated with this peer as an epoch
|
||||
* @param peer the peer to examine
|
||||
* @returns the last time we communicated with this peer (0 indicates never in this session, or disconnected)
|
||||
*/
|
||||
unsigned long long libp2p_peer_last_comm(const struct Libp2pPeer* peer);
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "libp2p/crypto/rsa.h"
|
||||
#include "libp2p/conn/session.h"
|
||||
|
||||
struct Exchange {
|
||||
unsigned char* epubkey;
|
||||
size_t epubkey_size;
|
||||
|
@ -35,3 +38,13 @@ int libp2p_secio_exchange_protobuf_encode(struct Exchange* in, unsigned char* bu
|
|||
* @returns true(1) on success, otherwise false(0)
|
||||
*/
|
||||
int libp2p_secio_exchange_protobuf_decode(unsigned char* buffer, size_t max_buffer_length, struct Exchange** out);
|
||||
|
||||
/***
|
||||
* Build an exchange object based on passed in values
|
||||
* @param local_session the SessionContext
|
||||
* @param private_key the local RsaPrivateKey
|
||||
* @param bytes_to_be_signed the bytes that should be signed
|
||||
* @param bytes_size the length of bytes_to_be_signed
|
||||
* @returns an Exchange object or NULL
|
||||
*/
|
||||
struct Exchange* libp2p_secio_exchange_build(struct SessionContext* local_session, struct RsaPrivateKey* private_key, const char* bytes_to_be_signed, size_t bytes_size);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "libp2p/crypto/rsa.h"
|
||||
|
||||
struct Propose {
|
||||
unsigned char* rand;
|
||||
size_t rand_size;
|
||||
|
@ -51,3 +53,15 @@ int libp2p_secio_propose_protobuf_encode(struct Propose* in, unsigned char* buff
|
|||
* @returns true(1) on success, otherwise false(0)
|
||||
*/
|
||||
int libp2p_secio_propose_protobuf_decode(const unsigned char* buffer, size_t max_buffer_length, struct Propose** out);
|
||||
|
||||
/***
|
||||
* Build a propose structure for sending to the remote client
|
||||
* @param nonce a 16 byte nonce, previously defined
|
||||
* @param rsa_key the local RSA key
|
||||
* @param supportedExchanges a comma separated list of supported exchange protocols
|
||||
* @param supportedCiphers a comma separated list of supported ciphers
|
||||
* @param supportedHashes a comma separated list of supported hashes
|
||||
* @returns an initialized Propose struct, or NULL
|
||||
*/
|
||||
struct Propose* libp2p_secio_propose_build(unsigned char nonce[16], struct RsaPrivateKey* rsa_key,
|
||||
const char* supportedExchanges, const char* supportedCiphers, const char* supportedHashes);
|
||||
|
|
|
@ -23,9 +23,12 @@ struct Libp2pProtocolHandler* libp2p_secio_build_protocol_handler(struct RsaPriv
|
|||
* Initiates a secio handshake. Use this method when you want to initiate a secio
|
||||
* session. This should not be used to respond to incoming secio requests
|
||||
* @param parent_stream the parent stream
|
||||
* @param remote_peer the remote peer
|
||||
* @param peerstore the peerstore
|
||||
* @param rsa_private_key the local private key
|
||||
* @returns a Secio Stream
|
||||
*/
|
||||
struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream);
|
||||
struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream, struct Libp2pPeer* remote_peer, struct Peerstore* peerstore, struct RsaPrivateKey* rsa_private_key);
|
||||
|
||||
/***
|
||||
* Initiates a secio handshake. Use this method when you want to initiate a secio
|
||||
|
|
|
@ -21,9 +21,14 @@ int libp2p_net_connection_close(void* stream_context) {
|
|||
if (stream_context == NULL)
|
||||
return 0;
|
||||
struct ConnectionContext* ctx = (struct ConnectionContext*)stream_context;
|
||||
if (close(ctx->socket_descriptor) == 0)
|
||||
// everything was okay
|
||||
if (ctx != NULL) {
|
||||
if (ctx->socket_descriptor > 0) {
|
||||
close(ctx->socket_descriptor);
|
||||
}
|
||||
free(ctx);
|
||||
ctx = NULL;
|
||||
return 1;
|
||||
}
|
||||
// something went wrong
|
||||
return 0;
|
||||
}
|
||||
|
@ -125,6 +130,12 @@ struct Stream* libp2p_net_connection_new(int fd, char* ip, int port) {
|
|||
struct ConnectionContext* ctx = (struct ConnectionContext*) malloc(sizeof(struct ConnectionContext));
|
||||
if (ctx != NULL) {
|
||||
out->stream_context = ctx;
|
||||
ctx->socket_descriptor = fd;
|
||||
if (!socket_connect4_with_timeout(ctx->socket_descriptor, hostname_to_ip(ip), port, 10) == 0) {
|
||||
// unable to connect
|
||||
libp2p_stream_free(out);
|
||||
out = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
|
|
@ -190,7 +190,7 @@ int libp2p_net_multistream_peek(void* stream_context) {
|
|||
if (parent_stream == NULL)
|
||||
return -1;
|
||||
|
||||
return parent_stream->peek(parent_stream);
|
||||
return parent_stream->peek(parent_stream->stream_context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,19 +211,19 @@ int libp2p_net_multistream_write(void* stream_context, struct StreamMessage* inc
|
|||
varint_encode(incoming->data_size, &varint[0], 12, &varint_size);
|
||||
// now put the size with the data
|
||||
struct StreamMessage outgoing;
|
||||
outgoing.data = (uint8_t*) malloc(varint_size + incoming->data_size);
|
||||
outgoing.data_size = varint_size + incoming->data_size;
|
||||
outgoing.data = (uint8_t*) malloc(outgoing.data_size);
|
||||
if (outgoing.data == NULL) {
|
||||
return 0;
|
||||
}
|
||||
memset(outgoing.data, 0, incoming->data_size + varint_size);
|
||||
memset(outgoing.data, 0, outgoing.data_size);
|
||||
memcpy(outgoing.data, varint, varint_size);
|
||||
memcpy(&outgoing.data[varint_size], incoming->data, incoming->data_size);
|
||||
// now ship it
|
||||
num_bytes = parent_stream->write(parent_stream, &outgoing);
|
||||
if (num_bytes > 0) {
|
||||
// update the last time we communicated
|
||||
multistream_context->session_context->last_comm_epoch = os_utils_gmtime();
|
||||
}
|
||||
num_bytes = parent_stream->write(parent_stream->stream_context, &outgoing);
|
||||
// subtract the varint if all went well
|
||||
if (num_bytes == outgoing.data_size)
|
||||
num_bytes = incoming->data_size;
|
||||
free(outgoing.data);
|
||||
}
|
||||
|
||||
|
@ -244,6 +244,7 @@ int libp2p_net_multistream_read(void* stream_context, struct StreamMessage** res
|
|||
|
||||
// find out the length
|
||||
uint8_t varint[12];
|
||||
memset(varint, 0, 12);
|
||||
size_t num_bytes_requested = 0;
|
||||
size_t varint_length = 0;
|
||||
for(int i = 0; i < 12; i++) {
|
||||
|
@ -353,10 +354,10 @@ struct Stream* libp2p_net_multistream_connect_with_timeout(const char* hostname,
|
|||
* NOTE: the SessionContext should already contain the connected stream. If not, use
|
||||
* libp2p_net_multistream_connect instead of this method.
|
||||
*
|
||||
* @param session the struct Session, which contains all the context info
|
||||
* @param ctx a MultistreamContext
|
||||
* @returns true(1) on success, or false(0)
|
||||
*/
|
||||
int libp2p_net_multistream_negotiate(struct SessionContext* session) {
|
||||
int libp2p_net_multistream_negotiate(struct MultistreamContext* ctx) {
|
||||
const char* protocolID = "/multistream/1.0.0\n";
|
||||
struct StreamMessage outgoing;
|
||||
struct StreamMessage* results = NULL;
|
||||
|
@ -364,13 +365,13 @@ int libp2p_net_multistream_negotiate(struct SessionContext* session) {
|
|||
// send the protocol id
|
||||
outgoing.data = (uint8_t*)protocolID;
|
||||
outgoing.data_size = strlen(protocolID);
|
||||
if (!libp2p_net_multistream_write(session, &outgoing))
|
||||
if (!libp2p_net_multistream_write(ctx, &outgoing))
|
||||
goto exit;
|
||||
// expect the same back
|
||||
libp2p_net_multistream_read(session, &results, multistream_default_timeout);
|
||||
libp2p_net_multistream_read(ctx, &results, multistream_default_timeout);
|
||||
if (results == NULL || results->data_size == 0)
|
||||
goto exit;
|
||||
if (strncmp((char*)results, protocolID, strlen(protocolID)) != 0)
|
||||
if (strncmp((char*)results->data, protocolID, strlen(protocolID)) != 0)
|
||||
goto exit;
|
||||
retVal = 1;
|
||||
exit:
|
||||
|
@ -401,6 +402,21 @@ struct Stream* libp2p_net_multistream_stream_new(struct Stream* parent_stream) {
|
|||
out->write = libp2p_net_multistream_write;
|
||||
out->peek = libp2p_net_multistream_peek;
|
||||
out->address = parent_stream->address;
|
||||
// build MultistreamContext
|
||||
struct MultistreamContext* ctx = (struct MultistreamContext*) malloc(sizeof(struct MultistreamContext));
|
||||
if (ctx == NULL) {
|
||||
libp2p_net_multistream_stream_free(out);
|
||||
return NULL;
|
||||
}
|
||||
out->stream_context = ctx;
|
||||
ctx->stream = out;
|
||||
ctx->handlers = NULL;
|
||||
ctx->session_context = NULL;
|
||||
// attempt to negotiate multistream protocol
|
||||
if (!libp2p_net_multistream_negotiate(ctx)) {
|
||||
libp2p_net_multistream_stream_free(out);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
28
net/socket.c
28
net/socket.c
|
@ -120,15 +120,14 @@ int socket_connect4(int s, uint32_t ip, uint16_t port) {
|
|||
int socket_connect4_with_timeout(int s, uint32_t ip, uint16_t port, int timeout_secs)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
//long args; // fctl args with O_NONBLOCK
|
||||
//long orig_args; // fctl args;
|
||||
long args; // fctl args with O_NONBLOCK
|
||||
long orig_args; // fctl args;
|
||||
|
||||
memset(&sa, 0, sizeof sa);
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
sa.sin_addr.s_addr = ip;
|
||||
|
||||
/*
|
||||
// set to non blocking
|
||||
orig_args = fcntl(s, F_GETFL, NULL);
|
||||
if (orig_args < 0) {
|
||||
|
@ -148,36 +147,33 @@ int socket_connect4_with_timeout(int s, uint32_t ip, uint16_t port, int timeout_
|
|||
libp2p_logger_debug("socket", "Socket already non-blocking during connect.\n");
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// connect
|
||||
int retVal = connect(s, (struct sockaddr *) &sa, sizeof sa);
|
||||
if (retVal == -1 && errno == EINPROGRESS) {
|
||||
int tried_secs = 0;
|
||||
while (retVal == -1 && errno == EINPROGRESS && tried_secs < timeout_secs) {
|
||||
libp2p_logger_debug("socket", "Socket connect unsuccessful. Waiting to try again.\n");
|
||||
// wait for timeout
|
||||
sleep(timeout_secs);
|
||||
sleep(1);
|
||||
tried_secs++;
|
||||
retVal = connect(s, (struct sockaddr *) &sa, sizeof sa);
|
||||
if (retVal == -1 && errno == EALREADY) {
|
||||
libp2p_logger_debug("socket", "Socket connect completed.\n");
|
||||
retVal = 0;
|
||||
} else {
|
||||
libp2p_logger_debug("socket", "Socket connect worked on second try.\n");
|
||||
}
|
||||
} else {
|
||||
if (retVal == -1) {
|
||||
libp2p_logger_debug("socket", "Socket connect failed with error %d.\n", errno);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if ( retVal == 0 && args != orig_args) {
|
||||
if (retVal == -1) {
|
||||
libp2p_logger_debug("socket", "Socket connect failed with error %d.\n", errno);
|
||||
}
|
||||
|
||||
if (args != orig_args) {
|
||||
// set back to blocking
|
||||
libp2p_logger_debug("socket", "Setting socket back to blocking.\n");
|
||||
args = fcntl(s, F_GETFL, NULL);
|
||||
args &= (~O_NONBLOCK);
|
||||
fcntl(s, F_SETFL, args);
|
||||
}
|
||||
*/
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "multiaddr/multiaddr.h"
|
||||
#include "libp2p/net/stream.h"
|
||||
|
||||
struct Stream* libp2p_stream_new() {
|
||||
|
@ -20,6 +21,14 @@ struct Stream* libp2p_stream_new() {
|
|||
|
||||
void libp2p_stream_free(struct Stream* stream) {
|
||||
if (stream != NULL) {
|
||||
if (stream->socket_mutex != NULL) {
|
||||
free(stream->socket_mutex);
|
||||
stream->socket_mutex = NULL;
|
||||
}
|
||||
if (stream->address != NULL) {
|
||||
multiaddress_free(stream->address);
|
||||
stream->address = NULL;
|
||||
}
|
||||
free(stream);
|
||||
}
|
||||
}
|
||||
|
|
18
peer/peer.c
18
peer/peer.c
|
@ -369,3 +369,21 @@ int libp2p_peer_compare(const struct Libp2pPeer* a, const struct Libp2pPeer* b)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
* Get the last time we communicated with this peer as an epoch
|
||||
* @param peer the peer to examine
|
||||
* @returns the last time we communicated with this peer (0 indicates never in this session, or disconnected)
|
||||
*/
|
||||
unsigned long long libp2p_peer_last_comm(const struct Libp2pPeer* peer) {
|
||||
unsigned long long retVal = 0;
|
||||
if (peer != NULL) {
|
||||
if (peer->sessionContext != NULL) {
|
||||
if (peer->sessionContext->insecure_stream != NULL) {
|
||||
struct ConnectionContext* ctx = (struct ConnectionContext*)peer->sessionContext->insecure_stream->stream_context;
|
||||
retVal = ctx->last_comm_epoch;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "libp2p/secio/exchange.h"
|
||||
#include "libp2p/crypto/ephemeral.h"
|
||||
#include "protobuf.h"
|
||||
|
||||
// epubkey signature
|
||||
|
@ -108,3 +109,38 @@ exit:
|
|||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/***
|
||||
* Forward declaration
|
||||
*/
|
||||
int libp2p_secio_sign(struct PrivateKey* priv, const char* bytes_to_send, size_t bytes_size, uint8_t** signature, size_t* signature_size);
|
||||
|
||||
/***
|
||||
* Build an exchange object based on passed in values
|
||||
* @param local_session the SessionContext
|
||||
* @param private_key the local RsaPrivateKey
|
||||
* @param bytes_to_be_signed the bytes that should be signed
|
||||
* @param bytes_size the length of bytes_to_be_signed
|
||||
* @returns an Exchange object or NULL
|
||||
*/
|
||||
struct Exchange* libp2p_secio_exchange_build(struct SessionContext* local_session, struct RsaPrivateKey* private_key, const char* bytes_to_be_signed, size_t bytes_size) {
|
||||
struct Exchange* exchange_out = libp2p_secio_exchange_new();
|
||||
if (exchange_out != NULL) {
|
||||
// don't send the first byte (to stay compatible with GO version)
|
||||
exchange_out->epubkey = (unsigned char*)malloc(local_session->ephemeral_private_key->public_key->bytes_size - 1);
|
||||
if (exchange_out->epubkey == NULL) {
|
||||
libp2p_secio_exchange_free(exchange_out);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(exchange_out->epubkey, &local_session->ephemeral_private_key->public_key->bytes[1], local_session->ephemeral_private_key->public_key->bytes_size - 1);
|
||||
exchange_out->epubkey_size = local_session->ephemeral_private_key->public_key->bytes_size - 1;
|
||||
|
||||
struct PrivateKey* priv = libp2p_crypto_private_key_new();
|
||||
priv->type = KEYTYPE_RSA;
|
||||
priv->data = (unsigned char*)private_key->der;
|
||||
priv->data_size = private_key->der_length;
|
||||
libp2p_secio_sign(priv, bytes_to_be_signed, bytes_size, &exchange_out->signature, &exchange_out->signature_size);
|
||||
free(priv);
|
||||
}
|
||||
return exchange_out;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "protobuf.h"
|
||||
#include "libp2p/secio/propose.h"
|
||||
#include "libp2p/crypto/key.h"
|
||||
|
||||
// rand pubkey exchanges ciphers hashes
|
||||
enum WireType secio_propose_message_fields[] = { WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED };
|
||||
|
@ -159,3 +160,54 @@ exit:
|
|||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/***
|
||||
* Build a propose structure for sending to the remote client
|
||||
* @param nonce a 16 byte nonce, previously defined
|
||||
* @param rsa_key the local RSA key
|
||||
* @param supportedExchanges a comma separated list of supported exchange protocols
|
||||
* @param supportedCiphers a comma separated list of supported ciphers
|
||||
* @param supportedHashes a comma separated list of supported hashes
|
||||
* @returns an initialized Propose struct, or NULL
|
||||
*/
|
||||
struct Propose* libp2p_secio_propose_build(unsigned char nonce[16], struct RsaPrivateKey* rsa_key,
|
||||
const char* supportedExchanges, const char* supportedCiphers, const char* supportedHashes) {
|
||||
struct Propose* propose = libp2p_secio_propose_new();
|
||||
if (propose != NULL) {
|
||||
// nonce
|
||||
propose->rand = malloc(16);
|
||||
memcpy(propose->rand, nonce, 16);
|
||||
propose->rand_size = 16;
|
||||
// ciphers, exchanges, hashes
|
||||
propose->ciphers_size = strlen(supportedCiphers);
|
||||
propose->ciphers = malloc(propose->ciphers_size + 1);
|
||||
strcpy(propose->ciphers, supportedCiphers);
|
||||
propose->exchanges_size = strlen(supportedExchanges);
|
||||
propose->exchanges = malloc(propose->exchanges_size + 1);
|
||||
strcpy(propose->exchanges, supportedExchanges);
|
||||
propose->hashes_size = strlen(supportedHashes);
|
||||
propose->hashes = malloc(propose->hashes_size + 1);
|
||||
strcpy(propose->hashes, supportedHashes);
|
||||
// key
|
||||
struct PublicKey pub_key;
|
||||
pub_key.type = KEYTYPE_RSA;
|
||||
pub_key.data_size = rsa_key->public_key_length;
|
||||
pub_key.data = malloc(pub_key.data_size);
|
||||
memcpy(pub_key.data, rsa_key->public_key_der, rsa_key->public_key_length);
|
||||
propose->public_key_size = libp2p_crypto_public_key_protobuf_encode_size(&pub_key);
|
||||
propose->public_key = malloc(propose->public_key_size);
|
||||
if (propose->public_key == NULL) {
|
||||
free(pub_key.data);
|
||||
libp2p_secio_propose_free(propose);
|
||||
return NULL;
|
||||
}
|
||||
if (libp2p_crypto_public_key_protobuf_encode(&pub_key, propose->public_key, propose->public_key_size, &propose->public_key_size) == 0) {
|
||||
free(pub_key.data);
|
||||
libp2p_secio_propose_free(propose);
|
||||
return NULL;
|
||||
}
|
||||
free(pub_key.data);
|
||||
}
|
||||
return propose;
|
||||
|
||||
}
|
||||
|
|
351
secio/secio.c
351
secio/secio.c
|
@ -73,9 +73,12 @@ int libp2p_secio_shutdown(void* context) {
|
|||
* Initiates a secio handshake. Use this method when you want to initiate a secio
|
||||
* session. This should not be used to respond to incoming secio requests
|
||||
* @param parent_stream the parent stream
|
||||
* @param remote_peer the remote peer
|
||||
* @param peerstore the peerstore
|
||||
* @param rsa_private_key the local private key
|
||||
* @returns a Secio Stream
|
||||
*/
|
||||
struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream) {
|
||||
struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream, struct Libp2pPeer* remote_peer, struct Peerstore* peerstore, struct RsaPrivateKey* rsa_private_key) {
|
||||
struct Stream* new_stream = libp2p_stream_new();
|
||||
if (new_stream != NULL) {
|
||||
struct SecioContext* ctx = (struct SecioContext*) malloc(sizeof(struct SecioContext));
|
||||
|
@ -85,6 +88,10 @@ struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream) {
|
|||
return NULL;
|
||||
}
|
||||
new_stream->stream_context = ctx;
|
||||
ctx->stream = new_stream;
|
||||
ctx->session_context = remote_peer->sessionContext;
|
||||
ctx->peer_store = peerstore;
|
||||
ctx->private_key = rsa_private_key;
|
||||
new_stream->parent_stream = parent_stream;
|
||||
if (!libp2p_secio_send_protocol(ctx)
|
||||
|| !libp2p_secio_receive_protocol(ctx)
|
||||
|
@ -555,6 +562,173 @@ int libp2p_secio_receive_protocol(struct SecioContext* ctx) {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate down the tree of streams to get the raw socket descriptor
|
||||
* @param stream the stream
|
||||
* @returns the raw socket descriptor
|
||||
*/
|
||||
int libp2p_secio_get_socket_descriptor(struct Stream* stream) {
|
||||
struct Stream* current = stream;
|
||||
while (current->parent_stream != NULL)
|
||||
current = current->parent_stream;
|
||||
struct ConnectionContext* ctx = current->stream_context;
|
||||
return ctx->socket_descriptor;
|
||||
}
|
||||
|
||||
/***
|
||||
* Write bytes to an unencrypted stream
|
||||
* @param session the session information
|
||||
* @param bytes the bytes to write
|
||||
* @param data_length the number of bytes to write
|
||||
* @returns the number of bytes written
|
||||
*/
|
||||
int libp2p_secio_unencrypted_write(struct Stream* secio_stream, struct StreamMessage* msg) {
|
||||
int num_bytes = 0;
|
||||
|
||||
int socket_descriptor = libp2p_secio_get_socket_descriptor(secio_stream);
|
||||
|
||||
if (msg != NULL && msg->data_size > 0) { // only do this is if there is something to send
|
||||
// first send the size
|
||||
uint32_t size = htonl(msg->data_size);
|
||||
char* size_as_char = (char*)&size;
|
||||
int left = 4;
|
||||
int written = 0;
|
||||
int written_this_time = 0;
|
||||
do {
|
||||
written_this_time = socket_write(socket_descriptor, &size_as_char[written], left, 0);
|
||||
if (written_this_time < 0) {
|
||||
written_this_time = 0;
|
||||
if ( (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
||||
// TODO: use epoll or select to wait for socket to be writable
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
left = left - written_this_time;
|
||||
} while (left > 0);
|
||||
// then send the actual data
|
||||
left = msg->data_size;
|
||||
written = 0;
|
||||
do {
|
||||
written_this_time = socket_write(socket_descriptor, (char*)&msg->data[written], left, 0);
|
||||
if (written_this_time < 0) {
|
||||
written_this_time = 0;
|
||||
if ( (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
||||
// TODO: use epoll or select to wait for socket to be writable
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
left = left - written_this_time;
|
||||
written += written_this_time;
|
||||
} while (left > 0);
|
||||
num_bytes = written;
|
||||
} // there was something to send
|
||||
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
/***
|
||||
* Read bytes from the incoming stream
|
||||
* @param session the session information
|
||||
* @param results where to put the bytes read
|
||||
* @param results_size the size of the results
|
||||
* @returns the number of bytes read
|
||||
*/
|
||||
int libp2p_secio_unencrypted_read(struct Stream* secio_stream, struct StreamMessage** msg, int timeout_secs) {
|
||||
uint32_t buffer_size = 0;
|
||||
|
||||
if (secio_stream == NULL) {
|
||||
libp2p_logger_error("secio", "Attempted unencrypted read on invalid session.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_descriptor = libp2p_secio_get_socket_descriptor(secio_stream);
|
||||
|
||||
// first read the 4 byte integer
|
||||
char* size = (char*)&buffer_size;
|
||||
int left = 4;
|
||||
int read = 0;
|
||||
int read_this_time = 0;
|
||||
int time_left = timeout_secs;
|
||||
do {
|
||||
read_this_time = socket_read(socket_descriptor, &size[read], 1, 0, timeout_secs);
|
||||
if (read_this_time <= 0) {
|
||||
if ( errno == EINPROGRESS ) {
|
||||
time_left--;
|
||||
sleep(1);
|
||||
if (time_left == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
|
||||
// TODO: use epoll or select to wait for socket to be writable
|
||||
libp2p_logger_debug("secio", "Attempted read, but got EAGAIN or EWOULDBLOCK. Code %d.\n", errno);
|
||||
return 0;
|
||||
} else {
|
||||
// is this really an error?
|
||||
if (errno != 0) {
|
||||
libp2p_logger_error("secio", "Error in libp2p_secio_unencrypted_read: %s\n", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
libp2p_logger_error("secio", "Error in libp2p_secio_unencrypted_read: 0 bytes read, but errno shows no error. Trying again.\n");
|
||||
}
|
||||
} else {
|
||||
left = left - read_this_time;
|
||||
read += read_this_time;
|
||||
}
|
||||
} while (left > 0);
|
||||
buffer_size = ntohl(buffer_size);
|
||||
if (buffer_size == 0) {
|
||||
libp2p_logger_error("secio", "unencrypted read buffer size is 0.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// now read the number of bytes we've found, minus the 4 that we just read
|
||||
left = buffer_size;
|
||||
read = 0;
|
||||
read_this_time = 0;
|
||||
*msg = libp2p_stream_message_new();
|
||||
struct StreamMessage* m = *msg;
|
||||
if (m == NULL) {
|
||||
libp2p_logger_error("secio", "Unable to allocate memory for the incoming message. Size: %ulld", left);
|
||||
return 0;
|
||||
}
|
||||
m->data_size = left;
|
||||
m->data = (uint8_t*) malloc(left);
|
||||
unsigned char* ptr = m->data;
|
||||
time_left = timeout_secs;
|
||||
do {
|
||||
read_this_time = socket_read(socket_descriptor, (char*)&ptr[read], left, 0, timeout_secs);
|
||||
if (read_this_time < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
sleep(1);
|
||||
time_left--;
|
||||
if (time_left == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
read_this_time = 0;
|
||||
if ( (errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
||||
// TODO: use epoll or select to wait for socket to be writable
|
||||
} else {
|
||||
libp2p_logger_error("secio", "read from socket returned %d.\n", errno);
|
||||
return 0;
|
||||
}
|
||||
} else if (read_this_time == 0) {
|
||||
// socket_read returned 0, which it shouldn't
|
||||
libp2p_logger_error("secio", "socket_read returned 0 trying to read from stream %d.\n", socket_descriptor);
|
||||
return 0;
|
||||
}
|
||||
left = left - read_this_time;
|
||||
} while (left > 0);
|
||||
|
||||
m->data_size = buffer_size;
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize state for the sha256 stream cipher
|
||||
* @param session the SessionContext struct that contains the variables to initialize
|
||||
|
@ -642,7 +816,7 @@ int libp2p_secio_encrypted_write(void* stream_context, struct StreamMessage* byt
|
|||
return 0;
|
||||
}
|
||||
|
||||
int retVal = parent_stream->write(parent_stream->stream_context, &outgoing);
|
||||
int retVal = libp2p_secio_unencrypted_write(parent_stream, &outgoing);
|
||||
if (!retVal) {
|
||||
libp2p_logger_error("secio", "secio_unencrypted_write returned false\n");
|
||||
}
|
||||
|
@ -732,7 +906,7 @@ int libp2p_secio_encrypted_read(void* stream_context, struct StreamMessage** byt
|
|||
// reader uses the remote cipher and mac
|
||||
// read the data
|
||||
struct StreamMessage* msg = NULL;
|
||||
if (!parent_stream->read(parent_stream->stream_context, &msg, timeout_secs)) {
|
||||
if (!libp2p_secio_unencrypted_read(parent_stream, &msg, 10)) {
|
||||
libp2p_logger_error("secio", "Unencrypted_read returned false.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
@ -744,6 +918,55 @@ int libp2p_secio_encrypted_read(void* stream_context, struct StreamMessage** byt
|
|||
return retVal;
|
||||
}
|
||||
|
||||
struct Libp2pPeer* libp2p_secio_get_peer_or_add(struct Peerstore* peerstore, struct SessionContext* local_session) {
|
||||
struct Libp2pPeer* remote_peer = NULL;
|
||||
|
||||
// see if we already have this peer
|
||||
if (peerstore != NULL)
|
||||
remote_peer = libp2p_peerstore_get_peer(peerstore, (unsigned char*)local_session->remote_peer_id, strlen(local_session->remote_peer_id));
|
||||
if (remote_peer == NULL) {
|
||||
remote_peer = libp2p_peer_new();
|
||||
// put peer information in Libp2pPeer struct
|
||||
remote_peer->id_size = strlen(local_session->remote_peer_id);
|
||||
if (remote_peer->id_size > 0) {
|
||||
remote_peer->id = malloc(remote_peer->id_size + 1);
|
||||
if (remote_peer->id != NULL) {
|
||||
memcpy(remote_peer->id, local_session->remote_peer_id, remote_peer->id_size);
|
||||
remote_peer->id[remote_peer->id_size] = 0;
|
||||
}
|
||||
}
|
||||
remote_peer->sessionContext = local_session;
|
||||
// add a multiaddress to the peer (if we have what we need)
|
||||
char url[100];
|
||||
sprintf(url, "/ip4/%s/tcp/%d/ipfs/%s", local_session->host, local_session->port, libp2p_peer_id_to_string(remote_peer));
|
||||
struct MultiAddress* ma = multiaddress_new_from_string(url);
|
||||
if (ma == NULL) {
|
||||
libp2p_logger_error("secio", "Unable to generate MultiAddress from [%s].\n", url);
|
||||
libp2p_peer_free(remote_peer);
|
||||
return NULL;
|
||||
} else {
|
||||
libp2p_logger_debug("secio", "Adding %s to peer %s.\n", url, libp2p_peer_id_to_string(remote_peer));
|
||||
struct Libp2pLinkedList* ma_ll = libp2p_utils_linked_list_new();
|
||||
ma_ll->item = ma;
|
||||
remote_peer->addr_head = ma_ll;
|
||||
}
|
||||
if (peerstore != NULL) {
|
||||
libp2p_logger_debug("secio", "New connection. Adding Peer to Peerstore.\n");
|
||||
libp2p_peerstore_add_peer(peerstore, remote_peer);
|
||||
}
|
||||
} else {
|
||||
// we already had one in the peerstore
|
||||
if (remote_peer->sessionContext != local_session) {
|
||||
// clean up old session context
|
||||
libp2p_logger_debug("secio", "Same remote connected. Replacing SessionContext.\n");
|
||||
libp2p_session_context_free(remote_peer->sessionContext);
|
||||
remote_peer->sessionContext = local_session;
|
||||
}
|
||||
}
|
||||
remote_peer->connection_type = CONNECTION_TYPE_CONNECTED;
|
||||
return remote_peer;
|
||||
}
|
||||
|
||||
/***
|
||||
* performs initial communication over an insecure channel to share
|
||||
* keys, IDs, and initiate connection. This is a framed messaging system
|
||||
|
@ -755,7 +978,7 @@ int libp2p_secio_encrypted_read(void* stream_context, struct StreamMessage** byt
|
|||
*/
|
||||
int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
||||
int retVal = 0;
|
||||
size_t results_size = 0, bytes_written = 0;
|
||||
size_t bytes_written = 0;
|
||||
struct StreamMessage* incoming = NULL;
|
||||
struct StreamMessage outgoing; // used for outgoing messages
|
||||
unsigned char* propose_in_bytes = NULL; // the remote protobuf
|
||||
|
@ -774,8 +997,6 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
char* char_buffer = NULL;
|
||||
size_t char_buffer_length = 0;
|
||||
struct StretchedKey* k1 = NULL, *k2 = NULL;
|
||||
struct PrivateKey* priv = NULL;
|
||||
struct PublicKey pub_key = {0};
|
||||
struct Libp2pPeer* remote_peer = NULL;
|
||||
|
||||
struct SessionContext* local_session = secio_context->session_context;
|
||||
|
@ -792,38 +1013,8 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
}
|
||||
|
||||
// Build the proposal to be sent to the new connection:
|
||||
propose_out = libp2p_secio_propose_new();
|
||||
libp2p_secio_propose_set_property((void**)&propose_out->rand, &propose_out->rand_size, local_session->local_nonce, 16);
|
||||
|
||||
// public key - protobuf it and stick it in propose_out
|
||||
pub_key.type = KEYTYPE_RSA;
|
||||
pub_key.data_size = private_key->public_key_length;
|
||||
pub_key.data = malloc(pub_key.data_size);
|
||||
memcpy(pub_key.data, private_key->public_key_der, private_key->public_key_length);
|
||||
results_size = libp2p_crypto_public_key_protobuf_encode_size(&pub_key);
|
||||
results = malloc(results_size);
|
||||
if (results == NULL) {
|
||||
free(pub_key.data);
|
||||
goto exit;
|
||||
}
|
||||
if (libp2p_crypto_public_key_protobuf_encode(&pub_key, results, results_size, &results_size) == 0) {
|
||||
free(pub_key.data);
|
||||
goto exit;
|
||||
}
|
||||
free(pub_key.data);
|
||||
|
||||
propose_out->public_key_size = results_size;
|
||||
propose_out->public_key = malloc(results_size);
|
||||
memcpy(propose_out->public_key, results, results_size);
|
||||
free(results);
|
||||
results = NULL;
|
||||
results_size = 0;
|
||||
// supported exchanges
|
||||
libp2p_secio_propose_set_property((void**)&propose_out->exchanges, &propose_out->exchanges_size, SupportedExchanges, strlen(SupportedExchanges));
|
||||
// supported ciphers
|
||||
libp2p_secio_propose_set_property((void**)&propose_out->ciphers, &propose_out->ciphers_size, SupportedCiphers, strlen(SupportedCiphers));
|
||||
// supported hashes
|
||||
libp2p_secio_propose_set_property((void**)&propose_out->hashes, &propose_out->hashes_size, SupportedHashes, strlen(SupportedHashes));
|
||||
propose_out = libp2p_secio_propose_build(local_session->local_nonce, private_key,
|
||||
SupportedExchanges, SupportedCiphers, SupportedHashes);
|
||||
|
||||
// protobuf the proposal
|
||||
propose_out_size = libp2p_secio_propose_protobuf_encode_size(propose_out);
|
||||
|
@ -831,19 +1022,18 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
if (libp2p_secio_propose_protobuf_encode(propose_out, propose_out_bytes, propose_out_size, &propose_out_size) == 0)
|
||||
goto exit;
|
||||
|
||||
// now send the protocol and Propose struct
|
||||
// now send the Propose struct
|
||||
outgoing.data = propose_out_bytes;
|
||||
outgoing.data_size = propose_out_size;
|
||||
bytes_written = secio_context->stream->parent_stream->write(secio_context->stream->parent_stream->stream_context, &outgoing);
|
||||
bytes_written = libp2p_secio_unencrypted_write(secio_context->stream, &outgoing);
|
||||
|
||||
if (bytes_written != propose_out_size) {
|
||||
libp2p_logger_error("secio", "Sent propose_out, but did not write the correct number of bytes. Should be %d but was %d.\n", propose_out_size, bytes_written);
|
||||
} else {
|
||||
//libp2p_logger_debug("secio", "Sent propose out.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// try to get the Propse struct from the remote peer
|
||||
bytes_written = secio_context->stream->parent_stream->read(secio_context->stream->parent_stream->stream_context, &incoming, 10);
|
||||
// try to get the Propose struct from the remote peer
|
||||
bytes_written = libp2p_secio_unencrypted_read(secio_context->stream, &incoming, 10);
|
||||
if (bytes_written <= 0) {
|
||||
libp2p_logger_error("secio", "Unable to get the remote's Propose struct.\n");
|
||||
goto exit;
|
||||
|
@ -851,7 +1041,7 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
//libp2p_logger_debug("secio", "Received their propose struct.\n");
|
||||
}
|
||||
|
||||
if (!libp2p_secio_propose_protobuf_decode(incoming->data, incoming->data_size -1, &propose_in)) {
|
||||
if (!libp2p_secio_propose_protobuf_decode(incoming->data, incoming->data_size, &propose_in)) {
|
||||
libp2p_logger_error("secio", "Unable to un-protobuf the remote's Propose struct\n");
|
||||
goto exit;
|
||||
}
|
||||
|
@ -862,50 +1052,16 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
if (propose_in->rand_size != 16)
|
||||
goto exit;
|
||||
memcpy(local_session->remote_nonce, propose_in->rand, 16);
|
||||
|
||||
// get public key and put it in a struct PublicKey
|
||||
if (!libp2p_crypto_public_key_protobuf_decode(propose_in->public_key, propose_in->public_key_size, &public_key))
|
||||
goto exit;
|
||||
|
||||
// generate their peer id
|
||||
libp2p_crypto_public_key_to_peer_id(public_key, &local_session->remote_peer_id);
|
||||
|
||||
// see if we already have this peer
|
||||
int new_peer = 0;
|
||||
remote_peer = libp2p_peerstore_get_peer(peerstore, (unsigned char*)local_session->remote_peer_id, strlen(local_session->remote_peer_id));
|
||||
if (remote_peer == NULL) {
|
||||
remote_peer = libp2p_peer_new();
|
||||
new_peer = 1;
|
||||
// put peer information in Libp2pPeer struct
|
||||
remote_peer->id_size = strlen(local_session->remote_peer_id);
|
||||
if (remote_peer->id_size > 0) {
|
||||
remote_peer->id = malloc(remote_peer->id_size + 1);
|
||||
if (remote_peer->id != NULL) {
|
||||
memcpy(remote_peer->id, local_session->remote_peer_id, remote_peer->id_size);
|
||||
remote_peer->id[remote_peer->id_size] = 0;
|
||||
}
|
||||
}
|
||||
remote_peer->sessionContext = local_session;
|
||||
// add a multiaddress to the peer (if we have what we need)
|
||||
char url[100];
|
||||
sprintf(url, "/ip4/%s/tcp/%d/ipfs/%s", local_session->host, local_session->port, libp2p_peer_id_to_string(remote_peer));
|
||||
struct MultiAddress* ma = multiaddress_new_from_string(url);
|
||||
if (ma == NULL) {
|
||||
libp2p_logger_error("secio", "Unable to generate MultiAddress from [%s].\n", url);
|
||||
goto exit;
|
||||
} else {
|
||||
libp2p_logger_debug("secio", "Adding %s to peer %s.\n", url, libp2p_peer_id_to_string(remote_peer));
|
||||
struct Libp2pLinkedList* ma_ll = libp2p_utils_linked_list_new();
|
||||
ma_ll->item = ma;
|
||||
remote_peer->addr_head = ma_ll;
|
||||
}
|
||||
} else {
|
||||
if (remote_peer->sessionContext != local_session) {
|
||||
// clean up old session context
|
||||
libp2p_logger_debug("secio", "Same remote connected. Replacing SessionContext.\n");
|
||||
libp2p_session_context_free(remote_peer->sessionContext);
|
||||
remote_peer->sessionContext = local_session;
|
||||
}
|
||||
}
|
||||
remote_peer->connection_type = CONNECTION_TYPE_CONNECTED;
|
||||
// pull the peer from the peerstore if it is there
|
||||
remote_peer = libp2p_secio_get_peer_or_add(peerstore, local_session);
|
||||
|
||||
// negotiate encryption parameters NOTE: SelectBest must match, otherwise this won't work
|
||||
// first determine order
|
||||
|
@ -934,25 +1090,9 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
memcpy(&char_buffer[propose_in_size + propose_out_size], &local_session->ephemeral_private_key->public_key->bytes[1], local_session->ephemeral_private_key->public_key->bytes_size-1);
|
||||
|
||||
// send Exchange packet
|
||||
exchange_out = libp2p_secio_exchange_new();
|
||||
exchange_out = libp2p_secio_exchange_build(local_session, private_key, char_buffer, char_buffer_length);
|
||||
if (exchange_out == NULL)
|
||||
goto exit;
|
||||
// don't send the first byte (to stay compatible with GO version)
|
||||
exchange_out->epubkey = (unsigned char*)malloc(local_session->ephemeral_private_key->public_key->bytes_size - 1);
|
||||
if (exchange_out->epubkey == NULL)
|
||||
goto exit;
|
||||
memcpy(exchange_out->epubkey, &local_session->ephemeral_private_key->public_key->bytes[1], local_session->ephemeral_private_key->public_key->bytes_size - 1);
|
||||
exchange_out->epubkey_size = local_session->ephemeral_private_key->public_key->bytes_size - 1;
|
||||
|
||||
priv = libp2p_crypto_private_key_new();
|
||||
priv->type = KEYTYPE_RSA;
|
||||
priv->data = (unsigned char*)private_key->der;
|
||||
priv->data_size = private_key->der_length;
|
||||
libp2p_secio_sign(priv, char_buffer, char_buffer_length, &exchange_out->signature, &exchange_out->signature_size);
|
||||
free(char_buffer);
|
||||
char_buffer = NULL;
|
||||
// yes, this is an improper disposal, but it gets the job done without fuss
|
||||
free(priv);
|
||||
|
||||
exchange_out_protobuf_size = libp2p_secio_exchange_protobuf_encode_size(exchange_out);
|
||||
exchange_out_protobuf = (unsigned char*)malloc(exchange_out_protobuf_size);
|
||||
|
@ -963,7 +1103,7 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
|
||||
outgoing.data = exchange_out_protobuf;
|
||||
outgoing.data_size = exchange_out_protobuf_size;
|
||||
bytes_written = secio_context->stream->parent_stream->write(secio_context->stream->parent_stream, &outgoing);
|
||||
bytes_written = libp2p_secio_unencrypted_write(secio_context->stream, &outgoing);
|
||||
if (exchange_out_protobuf_size != bytes_written) {
|
||||
libp2p_logger_error("secio", "Unable to write exchange_out\n");
|
||||
goto exit;
|
||||
|
@ -976,13 +1116,13 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
|
||||
// receive Exchange packet
|
||||
libp2p_logger_log("secio", LOGLEVEL_DEBUG, "Reading exchange packet\n");
|
||||
bytes_written = secio_context->stream->parent_stream->read(secio_context->stream->parent_stream->stream_context, &incoming, 10);
|
||||
bytes_written = libp2p_secio_unencrypted_read(secio_context->stream, &incoming, 10);
|
||||
if (bytes_written == 0) {
|
||||
libp2p_logger_error("secio", "unable to read exchange packet.\n");
|
||||
libp2p_peer_handle_connection_error(remote_peer);
|
||||
goto exit;
|
||||
} else {
|
||||
//libp2p_logger_debug("secio", "Read exchange packet.\n");
|
||||
libp2p_logger_debug("secio", "Read exchange packet.\n");
|
||||
}
|
||||
libp2p_secio_exchange_protobuf_decode(incoming->data, incoming->data_size, &exchange_in);
|
||||
libp2p_stream_message_free(incoming);
|
||||
|
@ -1093,11 +1233,6 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
|
|||
// set secure as default
|
||||
local_session->default_stream = local_session->secure_stream;
|
||||
|
||||
if (new_peer) {
|
||||
libp2p_logger_debug("secio", "New connection. Adding Peer to Peerstore.\n");
|
||||
libp2p_peerstore_add_peer(peerstore, remote_peer);
|
||||
}
|
||||
|
||||
retVal = 1;
|
||||
|
||||
//libp2p_logger_log("secio", LOGLEVEL_DEBUG, "Handshake complete\n");
|
||||
|
|
|
@ -13,7 +13,7 @@ OBJS = testit.o ../../c-protobuf/protobuf.o ../../c-protobuf/varint.o ../libp2p.
|
|||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
testit_libp2p: $(OBJS) $(DEPS)
|
||||
$(CC) -o $@ $(OBJS) $(LFLAGS) -lp2p -lm -lmultihash -lmultiaddr
|
||||
$(CC) -o $@ $(OBJS) $(LFLAGS) -lp2p -lm -lmultihash -lmultiaddr -lpthread
|
||||
|
||||
all_others:
|
||||
cd ../crypto; make all;
|
||||
|
|
|
@ -7,11 +7,12 @@
|
|||
int test_dialer_new() {
|
||||
int retVal = 0;
|
||||
char* peer_id = "QmQSDGgxSVTkHmtT25rTzQtc5C1Yg8SpGK3BTws8YsJ4x3";
|
||||
struct RsaPrivateKey* private_key = NULL;
|
||||
struct PrivateKey* private_key = NULL;
|
||||
struct Libp2pPeer* peer = libp2p_peer_new();
|
||||
peer->id = peer_id;
|
||||
peer->id = malloc(strlen(peer_id)+1);
|
||||
strcpy(peer->id, peer_id);
|
||||
peer->id_size = strlen(peer_id);
|
||||
struct Dialer* dialer = libp2p_conn_dialer_new(peer, private_key);
|
||||
struct Dialer* dialer = libp2p_conn_dialer_new(peer, NULL, private_key);
|
||||
if (dialer == NULL)
|
||||
goto exit;
|
||||
retVal = 1;
|
||||
|
@ -25,21 +26,19 @@ int test_dialer_dial() {
|
|||
int retVal = 0;
|
||||
char* config_dir = "/home/parallels/.ipfs/config";
|
||||
char* destination_string = "/ip4/192.210.179.217/tcp/4001/";
|
||||
char* peer_id = NULL;
|
||||
struct PrivateKey* private_key = NULL;
|
||||
struct Dialer* dialer = NULL;
|
||||
struct MultiAddress* destination_address = NULL;
|
||||
struct Stream* conn = NULL;
|
||||
struct Stream* stream = NULL;
|
||||
char* result = NULL;
|
||||
size_t result_size = 0;
|
||||
struct Libp2pPeer* peer = libp2p_peer_new();
|
||||
|
||||
test_helper_get_id_from_config(config_dir, &private_key, &peer->id);
|
||||
if (private_key == NULL)
|
||||
test_helper_get_id_from_config(config_dir, NULL, &peer->id);
|
||||
if (peer->id == NULL)
|
||||
goto exit;
|
||||
peer->id_size = strlen((char*)peer->id);
|
||||
|
||||
dialer = libp2p_conn_dialer_new(peer, NULL);
|
||||
dialer = libp2p_conn_dialer_new(peer, NULL, NULL);
|
||||
if (dialer == NULL)
|
||||
goto exit;
|
||||
|
||||
|
@ -48,8 +47,8 @@ int test_dialer_dial() {
|
|||
goto exit;
|
||||
|
||||
// now try to dial
|
||||
conn = libp2p_conn_dialer_get_connection(dialer, destination_address);
|
||||
if (conn == NULL)
|
||||
stream = libp2p_conn_dialer_get_connection(dialer, destination_address);
|
||||
if (stream == NULL)
|
||||
goto exit;
|
||||
|
||||
// clean up resources
|
||||
|
@ -57,12 +56,81 @@ int test_dialer_dial() {
|
|||
exit:
|
||||
if (result != NULL)
|
||||
free(result);
|
||||
free(peer_id);
|
||||
libp2p_peer_free(peer);
|
||||
multiaddress_free(destination_address);
|
||||
libp2p_conn_dialer_free(dialer);
|
||||
stream->close(stream->stream_context);
|
||||
libp2p_stream_free(stream);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int test_dialer_join_swarm() {
|
||||
int retVal = 0;
|
||||
|
||||
// we need a dialer and a peer
|
||||
struct Dialer* dialer = NULL;
|
||||
// this is a base64 encoded private key. It makes it easier to test if it is in base64 form
|
||||
// these were pulled from the GO version of ipfs
|
||||
char* orig_priv_key = "CAASqQkwggSlAgEAAoIBAQCuW+8vGUb2n4xOcfPZLmfVAy6GNJ0sYrD/hVXwxBU1aBas+8lfAuLwYJXPCVBg65wZWYEbbWCevLFjwB/oZyJA1J1g+HohggH8QvuDH164FtSbgyHFip2SPR7oUHgSWRqfKXRJsVW/SPCfEt59S8JH99Q747dU9fvZKpelE9aDLf5yI8nj29TDy3c1RpkxfUwfgnbeoCwsDnakFmVdoSEp3Lnt3JlI05qE0bgvkWAaelcXSNQCmZzDwXeMk9y221FnBkL4Vs3v2lKmjLx+Qr37P/t78T+VxsjnGHPhbZTIMIjwwON6568d0j25Bj9v6biiz8iXzBR4Fmz1CQ0mqU5BAgMBAAECggEAc6EYX/29Z/SrEaLUeiUiSsuPYQUnbrYMd4gvVDpVblOXJiTciJvbcFo9P04H9h6KKO2Ih23j86FjaqmQ/4jV2HSn4hUmuW4EbwzkyzJUmHTbjj5KeTzR/pd2Fc63skNROlg9fFmUagSvPm8/CYziTOP35bfAbyGqYXyzkJA1ZExVVSOi1zGVi+lnlI1fU2Aki5F7W7F/d2AQWsh7NXUwT7e6JP7TL+Gn4bWdn3NvluwAWTMgp6/It8OU1XPgu8OhdpZQWsMBqJwr79KGLbq2SZZXAw8O+ay1JQYmmmvYzwhdDgJwl+MOtf3NiqQWFzZP8RnlHGNcXlLHHPW0FB9H+QKBgQDirtBOqjCtND6m4hEfy6A24GcITYUBg1+AYQ7uM5uZl5u9AyxfG4bPxyrspz3yS0DOV4HNQ88iwmUE8+8ZHCLSY/YIp73Nk4m8t2s46CuI7Y5GrwCnh9xTMwaUrNx4IRTWyR3OxjQtUyrXtPR6uJ83FDenXvNi//Mrzp+myxX4wwKBgQDE6L8qiVA6n9k5dyUxxMUKJqynwPcBeC+wI85gr/9wwlRYDrgMYeH6/D5prZ3N5m8+zugVQQJKLfXBG0i8BRh5xLYFCZnV2O3NwvCdENlZJZrNNoz9jM3yRV+c7OdrclxDiN0bjGEBWv8GHutNFAwuUfMe0TMdfFYpM7gBHjEMqwKBgQCWHwOhNSCrdDARwSFqFyZxcUeKvhvZlrFGigCjS9Y+b6MaF+Ho0ogDTnlk5JUnwyKWBGnYEJI7CNZx40JzNKjzAHRN4xjV7mGHc0k1FLzQH9LbiMY8LMOC7gXrrFcNz4rHe8WbzLN9WNjEpfhK1b3Lcj4xP7ab17mpR1t/0HsqlQKBgQC3S6lYIUZLrCz7b0tyTqbU0jd6WQgVmBlcL5iXLH3uKxd0eQ8eh6diiZhXq0PwPQdlQhmMX12QS8QupAVK8Ltd7p05hzxqcmq7VTHCI8MPVxAI4zTPeVjko2tjmqu5u1TjkO2yDTTnnBs1SWbj8zt7itFz6G1ajzltVTV95OrnzQKBgQDEwZxnJA2vDJEDaJ82CiMiUAFzwsoK8hDvz63kOKeEW3/yESySnUbzpxDEjzYNsK74VaXHKCGI40fDRUqZxU/+qCrFf3xDfYS4r4wfFd2Jh+tn4NzSV/EhIr9KR/ZJW+TvGks+pWUJ3mhjPEvNtlt3M64/j2D0RP2aBQtoSpeezQ==";
|
||||
char* orig_peer_id = "QmRKm1d9kSCRpMFtLYpfhhCQ3DKuSSPJa3qn9wWXfwnWnY";
|
||||
char* remote_peer_id = "QmRjLCELimPe7aUdYRVNLD7UmB1CiJdJf8HLovKAB4KwmA";
|
||||
size_t orig_peer_id_size = strlen(orig_peer_id);
|
||||
struct PrivateKey* private_key = NULL;
|
||||
size_t decode_base64_size = 0;
|
||||
uint8_t* decode_base64 = NULL;
|
||||
struct Libp2pPeer* local_peer = NULL;
|
||||
struct Peerstore* peerstore = NULL;
|
||||
// we need connection information to an existing ipfs instance
|
||||
char* remote_swarm = "/ip4/10.211.55.2/tcp/4001";
|
||||
struct Libp2pPeer* remote_peer = NULL;
|
||||
struct MultiAddress* remote_ma = NULL;
|
||||
|
||||
// 1) take the private key and turn it back into bytes (decode base 64)
|
||||
decode_base64_size = libp2p_crypto_encoding_base64_decode_size(strlen(orig_priv_key));
|
||||
decode_base64 = (unsigned char*)malloc(decode_base64_size);
|
||||
memset(decode_base64, 0, decode_base64_size);
|
||||
|
||||
if (!libp2p_crypto_encoding_base64_decode((unsigned char*)orig_priv_key, strlen(orig_priv_key), &decode_base64[0], decode_base64_size, &decode_base64_size))
|
||||
goto exit;
|
||||
|
||||
if (!libp2p_crypto_private_key_protobuf_decode(decode_base64, decode_base64_size, &private_key))
|
||||
goto exit;
|
||||
|
||||
// 2) make the local peer
|
||||
local_peer = libp2p_peer_new();
|
||||
local_peer->id = orig_peer_id;
|
||||
local_peer->id_size = orig_peer_id_size;
|
||||
local_peer->is_local = 1;
|
||||
peerstore = libp2p_peerstore_new(local_peer);
|
||||
|
||||
// 3) make the dialer
|
||||
dialer = libp2p_conn_dialer_new(local_peer, peerstore, private_key);
|
||||
|
||||
// 4) make the remote peer
|
||||
remote_ma = multiaddress_new_from_string(remote_swarm);
|
||||
remote_peer = libp2p_peer_new();
|
||||
remote_peer->id_size = strlen(remote_peer_id);
|
||||
remote_peer->id = malloc(remote_peer->id_size);
|
||||
memcpy(remote_peer->id, remote_peer_id, remote_peer->id_size);
|
||||
remote_peer->addr_head = libp2p_utils_linked_list_new();
|
||||
remote_peer->addr_head->item = remote_ma;
|
||||
|
||||
// 5) attempt to dial
|
||||
if (!libp2p_conn_dialer_join_swarm(dialer, remote_peer, 10))
|
||||
goto exit;
|
||||
|
||||
retVal = 1;
|
||||
exit:
|
||||
if (decode_base64 != NULL)
|
||||
free(decode_base64);
|
||||
libp2p_peer_free(local_peer);
|
||||
libp2p_peerstore_free(peerstore);
|
||||
libp2p_conn_dialer_free(dialer);
|
||||
libp2p_crypto_private_key_free(private_key);
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
int test_dialer_dial_multistream() {
|
||||
int retVal = 0;
|
||||
char* config_dir = "/home/parallels/.ipfs/config";
|
||||
|
@ -82,7 +150,7 @@ int test_dialer_dial_multistream() {
|
|||
|
||||
peer->id_size = strlen((char*)peer->id);
|
||||
|
||||
dialer = libp2p_conn_dialer_new(peer, NULL);
|
||||
dialer = libp2p_conn_dialer_new(peer, NULL, NULL);
|
||||
if (dialer == NULL)
|
||||
goto exit;
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ int test_helper_get_id_from_config(char* path, struct PrivateKey** private_ptr,
|
|||
memcpy(*peer_ptr, ptr, length);
|
||||
(*peer_ptr)[length] = 0;
|
||||
|
||||
if (private_ptr != NULL) {
|
||||
// private key
|
||||
ptr = strstr(contents, "PrivKey\":");
|
||||
if (ptr == NULL)
|
||||
|
@ -85,6 +86,7 @@ int test_helper_get_id_from_config(char* path, struct PrivateKey** private_ptr,
|
|||
|
||||
// turn the encoded private key into a struct
|
||||
*private_ptr = base64ToPrivateKey(ptr);
|
||||
}
|
||||
|
||||
retVal = 1;
|
||||
exit:
|
||||
|
|
311
test/testit.c
311
test/testit.c
|
@ -15,150 +15,193 @@
|
|||
#include "test_peer.h"
|
||||
#include "libp2p/utils/logger.h"
|
||||
|
||||
const char* names[] = {
|
||||
"test_public_der_to_private_der",
|
||||
"test_mbedtls_varint_128_binary",
|
||||
"test_mbedtls_varint_128_string",
|
||||
"test_crypto_rsa_private_key_der",
|
||||
"test_crypto_rsa_signing",
|
||||
"test_crypto_rsa_public_key_to_peer_id",
|
||||
"test_crypto_x509_der_to_private2",
|
||||
"test_crypto_x509_der_to_private",
|
||||
"test_crypto_hashing_sha256",
|
||||
//"test_multihash_encode",
|
||||
//"test_multihash_decode",
|
||||
//"test_multihash_base58_encode_decode",
|
||||
//"test_multihash_base58_decode",
|
||||
//"test_multihash_size",
|
||||
"test_base58_encode_decode",
|
||||
"test_base58_size",
|
||||
"test_base58_max_size",
|
||||
"test_base58_peer_address",
|
||||
//"test_mbedtls_pk_write_key_der",
|
||||
//"test_crypto_rsa_sign",
|
||||
"test_crypto_encoding_base32_encode",
|
||||
"test_protobuf_private_key",
|
||||
"test_secio_handshake",
|
||||
"test_secio_handshake_go",
|
||||
"test_secio_encrypt_decrypt",
|
||||
"test_secio_exchange_protobuf_encode",
|
||||
"test_secio_encrypt_like_go",
|
||||
"test_multistream_connect",
|
||||
"test_multistream_get_list",
|
||||
"test_ephemeral_key_generate",
|
||||
"test_ephemeral_key_sign",
|
||||
"test_dialer_new",
|
||||
"test_dialer_dial",
|
||||
"test_dialer_dial_multistream",
|
||||
"test_record_protobuf",
|
||||
"test_record_make_put_record",
|
||||
"test_record_peer_protobuf",
|
||||
"test_record_message_protobuf",
|
||||
"test_peer",
|
||||
"test_peer_protobuf",
|
||||
"test_peerstore",
|
||||
"test_aes"
|
||||
struct test {
|
||||
int index;
|
||||
const char* name;
|
||||
int (*func)(void);
|
||||
int part_of_suite;
|
||||
struct test* next;
|
||||
};
|
||||
|
||||
int (*funcs[])(void) = {
|
||||
test_public_der_to_private_der,
|
||||
test_mbedtls_varint_128_binary,
|
||||
test_mbedtls_varint_128_string,
|
||||
test_crypto_rsa_private_key_der,
|
||||
test_crypto_rsa_signing,
|
||||
test_crypto_rsa_public_key_to_peer_id,
|
||||
test_crypto_x509_der_to_private2,
|
||||
test_crypto_x509_der_to_private,
|
||||
test_crypto_hashing_sha256,
|
||||
//test_multihash_encode,
|
||||
//test_multihash_decode,
|
||||
//test_multihash_base58_encode_decode,
|
||||
//test_multihash_base58_decode,
|
||||
//test_multihash_size,
|
||||
test_base58_encode_decode,
|
||||
test_base58_size,
|
||||
test_base58_max_size,
|
||||
test_base58_peer_address,
|
||||
//test_mbedtls_pk_write_key_der,
|
||||
//test_crypto_rsa_sign,
|
||||
test_crypto_encoding_base32_encode,
|
||||
test_protobuf_private_key,
|
||||
test_secio_handshake,
|
||||
test_secio_handshake_go,
|
||||
test_secio_encrypt_decrypt,
|
||||
test_secio_exchange_protobuf_encode,
|
||||
test_secio_encrypt_like_go,
|
||||
test_multistream_connect,
|
||||
test_multistream_get_list,
|
||||
test_ephemeral_key_generate,
|
||||
test_ephemeral_key_sign,
|
||||
test_dialer_new,
|
||||
test_dialer_dial,
|
||||
test_dialer_dial_multistream,
|
||||
test_record_protobuf,
|
||||
test_record_make_put_record,
|
||||
test_record_peer_protobuf,
|
||||
test_record_message_protobuf,
|
||||
test_peer,
|
||||
test_peer_protobuf,
|
||||
test_peerstore,
|
||||
test_aes
|
||||
};
|
||||
struct test* first_test = NULL;
|
||||
struct test* last_test = NULL;
|
||||
|
||||
int testit(const char* name, int (*func)(void)) {
|
||||
printf("Testing %s...\n", name);
|
||||
fprintf(stderr, "TESTING %s...\n", name);
|
||||
int retVal = func();
|
||||
if (retVal)
|
||||
printf("%s success!\n", name);
|
||||
fprintf(stderr, "%s success!\n", name);
|
||||
else
|
||||
printf("** Uh oh! %s failed.**\n", name);
|
||||
return retVal;
|
||||
fprintf(stderr, "** Uh oh! %s failed.**\n", name);
|
||||
return retVal == 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int counter = 0;
|
||||
int tests_ran = 0;
|
||||
char* test_wanted;
|
||||
int only_one = 0;
|
||||
if(argc > 1) {
|
||||
only_one = 1;
|
||||
if (argv[1][0] == '\'') { // some shells put quotes around arguments
|
||||
argv[1][strlen(argv[1])-1] = 0;
|
||||
test_wanted = &(argv[1][1]);
|
||||
int tear_down_test_collection() {
|
||||
struct test* current_test = first_test;
|
||||
while (current_test != NULL) {
|
||||
struct test* next_test = current_test->next;
|
||||
free(current_test);
|
||||
current_test = next_test;
|
||||
}
|
||||
else
|
||||
test_wanted = argv[1];
|
||||
}
|
||||
int array_length = sizeof(funcs) / sizeof(funcs[0]);
|
||||
int array2_length = sizeof(names) / sizeof(names[0]);
|
||||
if (array_length != array2_length) {
|
||||
printf("Test arrays are not of the same length. Funcs: %d, Names: %d\n", array_length, array2_length);
|
||||
}
|
||||
for (int i = 0; i < array_length; i++) {
|
||||
if (only_one) {
|
||||
const char* currName = names[i];
|
||||
if (strcmp(currName, test_wanted) == 0) {
|
||||
tests_ran++;
|
||||
counter += testit(names[i], funcs[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!only_one) {
|
||||
tests_ran++;
|
||||
counter += testit(names[i], funcs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (tests_ran == 0)
|
||||
printf("***** No tests found *****\n");
|
||||
else {
|
||||
if (tests_ran - counter > 0) {
|
||||
printf("***** There were %d failed test(s) (%d successful) *****\n", tests_ran - counter, counter);
|
||||
} else {
|
||||
printf("All %d tests passed\n", tests_ran);
|
||||
}
|
||||
}
|
||||
libp2p_logger_free();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int add_test(const char* name, int (*func)(void), int part_of_suite) {
|
||||
// create a new test
|
||||
struct test* t = (struct test*) malloc(sizeof(struct test));
|
||||
t->name = name;
|
||||
t->func = func;
|
||||
t->part_of_suite = part_of_suite;
|
||||
t->next = NULL;
|
||||
if (last_test == NULL)
|
||||
t->index = 0;
|
||||
else
|
||||
t->index = last_test->index + 1;
|
||||
// place it in the collection
|
||||
if (first_test == NULL) {
|
||||
first_test = t;
|
||||
} else {
|
||||
last_test->next = t;
|
||||
}
|
||||
last_test = t;
|
||||
if (last_test == NULL)
|
||||
return 0;
|
||||
return last_test->index;
|
||||
}
|
||||
|
||||
int build_test_collection() {
|
||||
add_test("test_public_der_to_private_der", test_public_der_to_private_der, 1);
|
||||
add_test("test_mbedtls_varint_128_binary", test_mbedtls_varint_128_binary, 1);
|
||||
add_test("test_mbedtls_varint_128_string", test_mbedtls_varint_128_string,1);
|
||||
add_test("test_crypto_rsa_private_key_der", test_crypto_rsa_private_key_der, 1);
|
||||
add_test("test_crypto_rsa_signing", test_crypto_rsa_signing, 1);
|
||||
add_test("test_crypto_rsa_public_key_to_peer_id", test_crypto_rsa_public_key_to_peer_id,1);
|
||||
add_test("test_crypto_x509_der_to_private2", test_crypto_x509_der_to_private2, 1);
|
||||
add_test("test_crypto_x509_der_to_private", test_crypto_x509_der_to_private,1);
|
||||
add_test("test_crypto_hashing_sha256", test_crypto_hashing_sha256,1);
|
||||
//add_test("test_multihash_encode", func,1);
|
||||
//add_test("test_multihash_decode", func,1);
|
||||
//add_test("test_multihash_base58_encode_decode", func,1);
|
||||
//add_test("test_multihash_base58_decode", func,1);
|
||||
//add_test("test_multihash_size", func,1);
|
||||
add_test("test_base58_encode_decode", test_base58_encode_decode,1);
|
||||
add_test("test_base58_size", test_base58_size,1);
|
||||
add_test("test_base58_max_size", test_base58_max_size,1);
|
||||
add_test("test_base58_peer_address", test_base58_peer_address,1);
|
||||
//add_test("test_mbedtls_pk_write_key_der", func,1);
|
||||
//add_test("test_crypto_rsa_sign", func,1);
|
||||
add_test("test_crypto_encoding_base32_encode", test_crypto_encoding_base32_encode,1);
|
||||
add_test("test_protobuf_private_key", test_protobuf_private_key,1);
|
||||
add_test("test_secio_handshake", test_secio_handshake,1);
|
||||
add_test("test_secio_handshake_go", test_secio_handshake_go,1);
|
||||
add_test("test_secio_encrypt_decrypt", test_secio_encrypt_decrypt,1);
|
||||
add_test("test_secio_exchange_protobuf_encode", test_secio_exchange_protobuf_encode,1);
|
||||
add_test("test_secio_encrypt_like_go", test_secio_encrypt_like_go,1);
|
||||
add_test("test_multistream_connect", test_multistream_connect,1);
|
||||
add_test("test_multistream_get_list", test_multistream_get_list,1);
|
||||
add_test("test_ephemeral_key_generate", test_ephemeral_key_generate,1);
|
||||
add_test("test_ephemeral_key_sign", test_ephemeral_key_sign,1);
|
||||
add_test("test_dialer_new", test_dialer_new,1);
|
||||
add_test("test_dialer_dial", test_dialer_dial,1);
|
||||
add_test("test_dialer_join_swarm", test_dialer_join_swarm, 1);
|
||||
add_test("test_dialer_dial_multistream", test_dialer_dial_multistream,1);
|
||||
add_test("test_record_protobuf", test_record_protobuf,1);
|
||||
add_test("test_record_make_put_record", test_record_make_put_record,1);
|
||||
add_test("test_record_peer_protobuf", test_record_peer_protobuf,1);
|
||||
add_test("test_record_message_protobuf", test_record_message_protobuf,1);
|
||||
add_test("test_peer", test_peer,1);
|
||||
add_test("test_peer_protobuf", test_peer_protobuf,1);
|
||||
add_test("test_peerstore", test_peerstore,1);
|
||||
add_test("test_aes", test_aes, 1);
|
||||
return 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pull the next test name from the command line
|
||||
* @param the count of arguments on the command line
|
||||
* @param argv the command line arguments
|
||||
* @param arg_number the current argument we want
|
||||
* @returns a null terminated string of the next test or NULL
|
||||
*/
|
||||
char* get_test(int argc, char** argv, int arg_number) {
|
||||
char* retVal = NULL;
|
||||
char* ptr = NULL;
|
||||
if (argc > arg_number) {
|
||||
ptr = argv[arg_number];
|
||||
if (ptr[0] == '\'')
|
||||
ptr++;
|
||||
retVal = ptr;
|
||||
ptr = strchr(retVal, '\'');
|
||||
if (ptr != NULL)
|
||||
ptr[0] = 0;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
struct test* get_test_by_index(int index) {
|
||||
struct test* current = first_test;
|
||||
while (current != NULL && current->index != index) {
|
||||
current = current->next;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
struct test* get_test_by_name(const char* name) {
|
||||
struct test* current = first_test;
|
||||
while (current != NULL && strcmp(current->name, name) != 0) {
|
||||
current = current->next;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* run certain tests or run all
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
int counter = 0;
|
||||
int tests_ran = 0;
|
||||
char* test_name_wanted = NULL;
|
||||
int certain_tests = 0;
|
||||
int current_test_arg = 1;
|
||||
if(argc > 1) {
|
||||
certain_tests = 1;
|
||||
}
|
||||
build_test_collection();
|
||||
if (certain_tests) {
|
||||
// certain tests were passed on the command line
|
||||
test_name_wanted = get_test(argc, argv, current_test_arg);
|
||||
while (test_name_wanted != NULL) {
|
||||
struct test* t = get_test_by_name(test_name_wanted);
|
||||
if (t != NULL) {
|
||||
tests_ran++;
|
||||
counter += testit(t->name, t->func);
|
||||
}
|
||||
test_name_wanted = get_test(argc, argv, ++current_test_arg);
|
||||
}
|
||||
} else {
|
||||
// run all tests that are part of this test suite
|
||||
struct test* current = first_test;
|
||||
while (current != NULL) {
|
||||
if (current->part_of_suite) {
|
||||
tests_ran++;
|
||||
counter += testit(current->name, current->func);
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
if (tests_ran == 0)
|
||||
fprintf(stderr, "***** No tests found *****\n");
|
||||
else {
|
||||
if (counter > 0) {
|
||||
fprintf(stderr, "***** There were %d failed (out of %d) test(s) *****\n", counter, tests_ran);
|
||||
} else {
|
||||
fprintf(stderr, "All %d tests passed\n", tests_ran);
|
||||
}
|
||||
}
|
||||
tear_down_test_collection();
|
||||
libp2p_logger_free();
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
return 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue