2017-02-10 01:10:21 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
2017-02-10 02:53:58 +00:00
# include <string.h>
2017-02-10 01:10:21 +00:00
# include <unistd.h>
# include <pthread.h>
2017-04-04 01:54:03 +00:00
# include <netinet/in.h>
# include <arpa/inet.h>
2017-07-27 19:33:19 +00:00
# include "libp2p/conn/session.h"
# include "libp2p/net/multistream.h"
2017-02-10 01:10:21 +00:00
# include "libp2p/net/p2pnet.h"
2017-07-27 19:33:19 +00:00
# include "libp2p/nodeio/nodeio.h"
2017-02-23 16:16:23 +00:00
# include "libp2p/record/message.h"
2017-07-27 19:33:19 +00:00
# include "libp2p/routing/dht_protocol.h"
# include "libp2p/secio/secio.h"
2017-04-03 18:20:35 +00:00
# include "libp2p/utils/logger.h"
2017-02-10 01:10:21 +00:00
# include "ipfs/core/daemon.h"
2017-02-23 20:15:33 +00:00
# include "ipfs/routing/routing.h"
# include "ipfs/core/ipfs_node.h"
2017-03-19 19:40:16 +00:00
# include "ipfs/merkledag/merkledag.h"
# include "ipfs/merkledag/node.h"
2017-04-17 19:02:33 +00:00
# include "ipfs/util/thread_pool.h"
2017-02-10 01:10:21 +00:00
2017-02-10 02:53:58 +00:00
# define BUF_SIZE 4096
2017-05-11 18:53:52 +00:00
// this should be set to 5 for normal operation, perhaps higher for debugging purposes
# define DEFAULT_NETWORK_TIMEOUT 5
2017-04-17 16:58:47 +00:00
static int null_shutting_down = 0 ;
2017-03-30 18:59:31 +00:00
/***
* Compare incoming to see if they are requesting a protocol upgrade
* @ param incoming the incoming string
* @ param incoming_size the size of the incoming string
* @ param test the protocol string to compare it with ( i . e . " /secio " or " /nodeio "
* @ returns true ( 1 ) if there was a match , false ( 0 ) otherwise
*/
2017-07-17 19:38:13 +00:00
int protocol_compare ( const unsigned char * incoming , size_t incoming_size , const char * test ) {
2017-03-30 18:59:31 +00:00
int test_size = strlen ( test ) ;
if ( incoming_size > = test_size & & strncmp ( ( char * ) incoming , test , test_size ) = = 0 )
2017-03-19 19:40:16 +00:00
return 1 ;
return 0 ;
}
2017-03-30 18:59:31 +00:00
2017-07-17 19:38:13 +00:00
/***
* Handle the incoming request from a Multistream
* @ param incoming the incoming request
* @ param incoming_size the size of the request in bytes
* @ param session the session context
* @ param connection_param the connection parameters
* @ returns True ( 1 ) on success , False ( 0 ) on error
*/
int ipfs_null_marshal ( const unsigned char * incoming , size_t incoming_size , struct SessionContext * session , struct null_connection_params * connection_param ) {
if ( protocol_compare ( incoming , incoming_size , " /secio " ) ) {
libp2p_logger_debug ( " null " , " Attempting secure io connection... \n " ) ;
if ( ! libp2p_secio_handshake ( session , & connection_param - > local_node - > identity - > private_key , 1 ) ) {
// rejecting connection
libp2p_logger_debug ( " null " , " Secure IO connection failed \n " ) ;
return 0 ;
}
libp2p_logger_debug ( " null " , " Secure IO connection successful. \n " ) ;
} else if ( protocol_compare ( incoming , incoming_size , " /nodeio " ) ) {
libp2p_logger_debug ( " null " , " Attempting a nodeio connection. \n " ) ;
if ( ! libp2p_nodeio_handshake ( session ) ) {
return 0 ;
}
// loop through file requests
int _continue = 1 ;
while ( _continue ) {
unsigned char * hash ;
size_t hash_length = 0 ;
_continue = session - > default_stream - > read ( session , & hash , & hash_length , DEFAULT_NETWORK_TIMEOUT ) ;
if ( hash_length < 20 ) {
_continue = 0 ;
continue ;
}
else {
// try to get the Node
struct HashtableNode * node = NULL ;
if ( ! ipfs_merkledag_get ( hash , hash_length , & node , connection_param - > local_node - > repo ) ) {
_continue = 0 ;
continue ;
}
size_t results_size = ipfs_hashtable_node_protobuf_encode_size ( node ) ;
unsigned char results [ results_size ] ;
if ( ! ipfs_hashtable_node_protobuf_encode ( node , results , results_size , & results_size ) ) {
_continue = 0 ;
continue ;
}
// send it to the requestor
session - > default_stream - > write ( session , results , results_size ) ;
}
}
} else if ( protocol_compare ( incoming , incoming_size , " /ipfs/kad/ " ) ) {
libp2p_logger_log ( " null " , LOGLEVEL_DEBUG , " Attempting kademlia connection... \n " ) ;
if ( ! libp2p_routing_dht_handshake ( session ) ) {
libp2p_logger_log ( " null " , LOGLEVEL_DEBUG , " kademlia connection handshake failed \n " ) ;
return 0 ;
}
// this handles 1 transaction
libp2p_routing_dht_handle_message ( session , connection_param - > local_node - > peerstore , connection_param - > local_node - > providerstore ) ;
libp2p_logger_log ( " null " , LOGLEVEL_DEBUG , " kademlia message handled \n " ) ;
}
else {
libp2p_logger_error ( " null " , " There was a problem with this connection. It is nothing I can handle. Disconnecting. \n " ) ;
return 0 ;
}
return 1 ;
}
2017-02-23 20:15:33 +00:00
/**
2017-07-27 19:33:19 +00:00
* We ' ve received a connection . Find out what they want .
*
* @ param ptr a pointer to a null_connection_params struct
2017-02-23 20:15:33 +00:00
*/
2017-04-17 19:02:33 +00:00
void ipfs_null_connection ( void * ptr )
2017-02-10 01:10:21 +00:00
{
2017-07-27 19:33:19 +00:00
struct null_connection_params * connection_param = ( struct null_connection_params * ) ptr ;
2017-02-10 01:10:21 +00:00
2017-02-23 20:15:33 +00:00
// TODO: when should we exit the for loop and disconnect?
2017-07-27 19:33:19 +00:00
struct SessionContext * session = libp2p_session_context_new ( ) ;
if ( session = = NULL ) {
libp2p_logger_error ( " null " , " Unable to allocate SessionContext. Out of memory? \n " ) ;
return ;
}
session - > insecure_stream = libp2p_net_multistream_stream_new ( connection_param - > file_descriptor , connection_param - > ip , connection_param - > port ) ;
libp2p_logger_debug ( " null " , " %s null has a file descriptor of %d \n " , connection_param - > local_node - > identity - > peer_id , * ( ( int * ) session - > insecure_stream - > socket_descriptor ) ) ;
session - > default_stream = session - > insecure_stream ;
session - > datastore = connection_param - > local_node - > repo - > config - > datastore ;
session - > filestore = connection_param - > local_node - > repo - > config - > filestore ;
2017-02-23 20:15:33 +00:00
2017-04-04 01:54:03 +00:00
libp2p_logger_log ( " null " , LOGLEVEL_INFO , " Connection %d, count %d \n " , connection_param - > file_descriptor , * ( connection_param - > count ) ) ;
2017-02-10 01:10:21 +00:00
2017-07-27 19:33:19 +00:00
if ( libp2p_net_multistream_negotiate ( session ) ) {
2017-07-17 19:38:13 +00:00
// Someone has connected and successfully negotiated multistream. Now talk to them...
2017-02-23 20:15:33 +00:00
for ( ; ; ) {
2017-07-17 19:38:13 +00:00
// Wait for them to ask something...
2017-03-02 21:18:02 +00:00
unsigned char * results = NULL ;
2017-04-03 22:26:33 +00:00
size_t bytes_read = 0 ;
2017-07-27 19:33:19 +00:00
if ( null_shutting_down ) {
libp2p_logger_debug ( " null " , " %s null shutting down before read. \n " , connection_param - > local_node - > identity - > peer_id ) ;
// this service is shutting down. Ignore the request and exit the loop
2017-04-03 22:26:33 +00:00
break ;
2017-04-03 18:41:26 +00:00
}
2017-07-27 19:33:19 +00:00
if ( ! session - > default_stream - > read ( session , & results , & bytes_read , DEFAULT_NETWORK_TIMEOUT ) ) {
// the read was unsuccessful. We should close the connection.
libp2p_logger_debug ( " null " , " %s stream transaction read returned false. \n " , connection_param - > local_node - > identity - > peer_id ) ;
continue ;
}
2017-04-17 19:02:33 +00:00
if ( null_shutting_down ) {
2017-07-27 19:33:19 +00:00
libp2p_logger_debug ( " null " , " %s null shutting down after read. \n " , connection_param - > local_node - > identity - > peer_id ) ;
2017-07-17 19:38:13 +00:00
// this service is shutting down. Ignore the request and exit the loop
2017-04-17 19:02:33 +00:00
break ;
}
2017-07-17 19:38:13 +00:00
if ( bytes_read = = 0 ) {
// They did not ask for anything. There was a timeout. Wait again.
2017-04-17 19:02:33 +00:00
continue ;
}
2017-07-17 19:38:13 +00:00
// We actually got something. Process the request...
2017-04-03 22:26:33 +00:00
libp2p_logger_debug ( " null " , " Read %lu bytes from a stream tranaction \n " , bytes_read ) ;
2017-07-27 19:33:19 +00:00
int retVal = ipfs_null_marshal ( results , bytes_read , session , connection_param ) ;
2017-07-17 19:38:13 +00:00
free ( results ) ;
if ( ! retVal ) {
libp2p_logger_debug ( " null " , " ipfs_null_marshal returned false \n " ) ;
2017-04-17 19:02:33 +00:00
break ;
2017-02-23 20:15:33 +00:00
}
}
2017-04-03 17:42:35 +00:00
} else {
2017-04-03 18:20:35 +00:00
libp2p_logger_log ( " null " , LOGLEVEL_DEBUG , " Multistream negotiation failed \n " ) ;
2017-02-23 20:15:33 +00:00
}
2017-07-27 19:33:19 +00:00
libp2p_logger_debug ( " null " , " %s Freeing session context. \n " , connection_param - > local_node - > identity - > peer_id ) ;
2017-02-10 01:10:21 +00:00
( * ( connection_param - > count ) ) - - ; // update counter.
2017-04-17 19:02:33 +00:00
if ( connection_param - > ip ! = NULL )
free ( connection_param - > ip ) ;
2017-02-10 01:10:21 +00:00
free ( connection_param ) ;
2017-07-27 19:33:19 +00:00
libp2p_session_context_free ( session ) ;
2017-04-17 19:02:33 +00:00
return ;
2017-02-10 01:10:21 +00:00
}
2017-07-27 19:33:19 +00:00
/***
* Called by the daemon to listen for connections
* @ param ptr a pointer to an IpfsNodeListenParams struct
* @ returns nothing useful .
*/
2017-07-17 18:05:56 +00:00
void * ipfs_null_listen ( void * ptr )
2017-02-10 01:10:21 +00:00
{
int socketfd , s , count = 0 ;
2017-04-17 19:02:33 +00:00
threadpool thpool = thpool_init ( 25 ) ;
2017-02-22 15:56:11 +00:00
struct IpfsNodeListenParams * listen_param ;
2017-02-10 01:10:21 +00:00
struct null_connection_params * connection_param ;
2017-02-22 15:56:11 +00:00
listen_param = ( struct IpfsNodeListenParams * ) ptr ;
2017-02-10 01:10:21 +00:00
if ( ( socketfd = socket_listen ( socket_tcp4 ( ) , & ( listen_param - > ipv4 ) , & ( listen_param - > port ) ) ) < = 0 ) {
2017-04-27 20:52:20 +00:00
libp2p_logger_error ( " null " , " Failed to init null router. Address: %d, Port: %d \n " , listen_param - > ipv4 , listen_param - > port ) ;
2017-05-11 18:53:52 +00:00
return ( void * ) 2 ;
2017-02-10 01:10:21 +00:00
}
2017-05-11 18:53:52 +00:00
libp2p_logger_error ( " null " , " Ipfs listening on %d \n " , listen_param - > port ) ;
2017-02-10 01:10:21 +00:00
for ( ; ; ) {
2017-07-27 19:33:19 +00:00
libp2p_logger_debug ( " null " , " %s Attempting socket read \n " , listen_param - > local_node - > identity - > peer_id ) ;
2017-05-11 18:53:52 +00:00
int numDescriptors = socket_read_select4 ( socketfd , 2 ) ;
2017-04-17 16:58:47 +00:00
if ( null_shutting_down ) {
2017-07-27 19:33:19 +00:00
libp2p_logger_debug ( " null " , " %s null_listen shutting down. \n " , listen_param - > local_node - > identity - > peer_id ) ;
2017-04-17 16:58:47 +00:00
break ;
}
if ( numDescriptors > 0 ) {
s = socket_accept4 ( socketfd , & ( listen_param - > ipv4 ) , & ( listen_param - > port ) ) ;
if ( count > = CONNECTIONS ) { // limit reached.
close ( s ) ;
continue ;
}
count + + ;
connection_param = malloc ( sizeof ( struct null_connection_params ) ) ;
if ( connection_param ) {
connection_param - > file_descriptor = s ;
connection_param - > count = & count ;
connection_param - > local_node = listen_param - > local_node ;
connection_param - > port = listen_param - > port ;
connection_param - > ip = malloc ( INET_ADDRSTRLEN ) ;
if ( inet_ntop ( AF_INET , & ( listen_param - > ipv4 ) , connection_param - > ip , INET_ADDRSTRLEN ) = = NULL ) {
free ( connection_param - > ip ) ;
connection_param - > ip = NULL ;
connection_param - > port = 0 ;
}
// Create pthread for ipfs_null_connection.
2017-04-17 19:02:33 +00:00
thpool_add_work ( thpool , ipfs_null_connection , connection_param ) ;
2017-04-17 16:58:47 +00:00
}
}
2017-02-10 01:10:21 +00:00
}
2017-04-17 19:02:33 +00:00
thpool_destroy ( thpool ) ;
2017-05-11 19:30:52 +00:00
close ( socketfd ) ;
2017-02-10 01:10:21 +00:00
return ( void * ) 2 ;
}
2017-04-17 16:58:47 +00:00
int ipfs_null_shutdown ( ) {
null_shutting_down = 1 ;
return 1 ;
}