More changes to the stream negotiation

yamux
John Jones 2017-10-25 12:28:53 -05:00
parent 9dd1dab8e4
commit 08a02f7956
24 changed files with 768 additions and 314 deletions

View File

@ -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;

View File

@ -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;
}
/**

View File

@ -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;
}

View File

@ -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);
out->private_key = (struct PrivateKey*)malloc(sizeof(struct PrivateKey));
libp2p_crypto_private_key_copy(private_key, out->private_key);
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;
}

View File

@ -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

View File

@ -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;
};
/***

View File

@ -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

View File

@ -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;
};

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -72,19 +72,21 @@ int test_helper_get_id_from_config(char* path, struct PrivateKey** private_ptr,
memcpy(*peer_ptr, ptr, length);
(*peer_ptr)[length] = 0;
// private key
ptr = strstr(contents, "PrivKey\":");
if (ptr == NULL)
goto exit;
ptr += 9;
ptr = strstr(ptr, "\"");
ptr++;
if (private_ptr != NULL) {
// private key
ptr = strstr(contents, "PrivKey\":");
if (ptr == NULL)
goto exit;
ptr += 9;
ptr = strstr(ptr, "\"");
ptr++;
end = strstr(ptr, "\"");
end[0] = 0;
end = strstr(ptr, "\"");
end[0] = 0;
// turn the encoded private key into a struct
*private_ptr = base64ToPrivateKey(ptr);
// turn the encoded private key into a struct
*private_ptr = base64ToPrivateKey(ptr);
}
retVal = 1;
exit:

View File

@ -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]);
}
else
test_wanted = argv[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;
}
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;
}