-
Dmitriy A. Gerasimov authored1746405e
dap_chain_node_cli.c 43.40 KiB
/*
* Authors:
* Dmitriy A. Gerasimov <gerasimov.dmitriy@demlabs.net>
* Alexander Lysikov <alexander.lysikov@demlabs.net>
* DeM Labs Inc. https://demlabs.net
* Kelvin Project https://github.com/kelvinblockchain
* Copyright (c) 2019
* All rights reserved.
This file is part of DAP (Deus Applications Prototypes) the open source project
DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DAP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with any DAP based project. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
//#include <glib.h>
#ifndef _WIN32
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
//#include <unistd.h> // for close
#include <fcntl.h>
//#include <sys/poll.h>
//#include <sys/select.h>
#include <netinet/in.h>
#include <sys/un.h>
//#define closesocket close
//typedef int SOCKET;
//#define SOCKET_ERROR -1 // for win32 = (-1)
//#define INVALID_SOCKET -1 // for win32 = (SOCKET)(~0)
// for Windows
#else
#include <winsock2.h>
#include <windows.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#include <io.h>
#endif
#include <pthread.h>
#include "iputils/iputils.h"
#include "dap_common.h"
#include "dap_config.h"
#include "dap_strfuncs.h"
#include "dap_list.h"
#include "dap_chain_node_cli_cmd.h"
#include "dap_chain_node_client.h"
#include "dap_chain_node_cli.h"
//#include "dap_chain_node_cli.h"
#define LOG_TAG "chain_node_cli"
#define MAX_CONSOLE_CLIENTS 16
static SOCKET server_sockfd = -1; // network or local unix
uint32_t l_listen_port = 0;
#ifdef _WIN32
#define poll WSAPoll
#endif
static dap_chain_node_cmd_item_t * s_commands = NULL;
/**
* Wait for data
* timeout - timeout in ms
* [Specifying a negative value in timeout means an infinite timeout.]
* [Specifying a timeout of zero causes poll() to return immediately, even if no file descriptors are ready.]
* return zero if the time limit expired
* return: >0 if data is present to read
* return: -1 if error
*/
static int s_poll( int socket, int timeout )
{
struct pollfd fds;
int res;
fds.fd = socket;
// POLLIN - received data
// POLLNVAL - closed the socket on our side
// POLLHUP - closed the socket on another side (does not work! Received POLLIN and the next reading returns 0 bytes)
fds.events = POLLIN; // | | POLLNVAL | POLLHUP | POLLERR | POLLPRI
res = poll(&fds, 1, timeout);
// since POLLIN=(POLLRDNORM | POLLRDBAND), then maybe revents=POLLRDNORM
if(res == 1 && !(fds.revents & POLLIN)) //if(res==1 && fds.revents!=POLLIN)
return -1;
return res;
}
/**
* Check socket for validity
*/
static bool is_valid_socket(SOCKET sock)
{
struct pollfd fds;
fds.fd = sock;
fds.events = POLLIN;
// return: -1 err, 0 timeout, 1 waited
int count_desc = poll(&fds, 1, 0);
// error
if(count_desc == -1)
return false;
// event with an error code
if(count_desc > 0)
{
// feature of disconnection under Windows
// under Windows, with socket closed fds.revents=POLLHUP, in Unix fds.events = POLLIN
if(fds.revents & (POLLERR | POLLHUP | POLLNVAL))
return false;
// feature of disconnection under Unix (QNX)
// under Windows, with socket closed res = 0, in Unix res = -1
char buf[2];
long res = recv(sock, buf, 1, MSG_PEEK); // MSG_PEEK The data is treated as unread and the next recv() function shall still return this data.
if(res < 0)
return false;
// data in the buffer must be(count_desc>0), but read 0 bytes(res=0)
if(!res && (fds.revents & POLLIN))
return false;
}
return true;
}
/**
* Read from socket
*
* timeout in milliseconds
* return the number of read bytes (-1 err or -2 timeout)
*/
long s_recv(SOCKET sock, unsigned char *buf, size_t bufsize, int timeout)
{
struct pollfd fds;
long res;
fds.fd = sock;
fds.events = POLLIN; // | POLLNVAL | POLLHUP | POLLERR | POLLPRI;// | POLLRDNORM;//POLLOUT |
res = poll(&fds, 1, timeout);
if(res == 1 && !(fds.revents & POLLIN))
return -1;
if(!res) // timeout
return -2;
if(res < 1) {
return -1;
}
// res = read(sock, (char*) buf, bufsize);
res = recv(sock, (char*) buf, bufsize, 0); //MSG_WAITALL
if(res <= 0) { //EINTR=4 ENOENT=2 EINVAL=22 ECONNRESET=254
printf("[s_recv] recv()=%ld errno=%d\n", res, errno);
}
return res;
}
/**
* Reading from the socket till arrival the specified string
*
* stop_str - string to which reading will continue
* del_stop_str - удалять ли строку для поиска в конце
* timeout - in ms
* return: string (if waited for final characters) or NULL, if the string requires deletion
*/
char* s_get_next_str( SOCKET nSocket, int *dwLen, const char *stop_str, bool del_stop_str, int timeout )
{
bool bSuccess = false;
long nRecv = 0; // count of bytes received
size_t stop_str_len = (stop_str) ? strlen(stop_str) : 0;
// if there is nothing to look for
if(!stop_str_len)
return NULL;
size_t lpszBuffer_len = 256;
char *lpszBuffer = DAP_NEW_Z_SIZE(char, lpszBuffer_len);
// received string will not be larger than MAX_REPLY_LEN
while(1) //nRecv < MAX_REPLY_LEN)
{
// read one byte
long ret = s_recv(nSocket, (unsigned char *) (lpszBuffer + nRecv), 1, timeout);
//int ret = recv(nSocket,lpszBuffer+nRecv,1, 0);
if(ret <= 0)
{
break;
}
nRecv += ret;
//printf("**debug** socket=%d read %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv));
while((nRecv + 1) >= (long) lpszBuffer_len)
{
lpszBuffer_len *= 2;
lpszBuffer = (char*) realloc(lpszBuffer, lpszBuffer_len);
}
// search for the required string
if(nRecv >= (long) stop_str_len) {
// found the required string
if(!strncasecmp(lpszBuffer + nRecv - stop_str_len, stop_str, stop_str_len)) {
bSuccess = true;
break;
}
}
};
// end reading
if(bSuccess) {
// delete the searched string
if(del_stop_str) {
lpszBuffer[nRecv - (long) stop_str_len] = '\0';
if(dwLen)
*dwLen =(int) nRecv - (int) stop_str_len;
}
else {
lpszBuffer[nRecv] = '\0';
if(dwLen)
*dwLen = (int) nRecv;
}
lpszBuffer = DAP_REALLOC(lpszBuffer,(size_t) *dwLen + 1);
return lpszBuffer;
}
// in case of an error or missing string
if(dwLen)
*dwLen = 0;
free(lpszBuffer);
return NULL;
}
/**
* threading function for processing a request from a client
*/
static void* thread_one_client_func(void *args)
{
SOCKET newsockfd = (SOCKET) (intptr_t) args;
log_it(L_INFO, "new connection sockfd=%d", newsockfd);
int str_len, marker = 0;
int timeout = 5000; // 5 sec
int argc = 0;
dap_list_t *cmd_param_list = NULL;
while(1)
{
// wait data from client
int is_data = s_poll(newsockfd, timeout);
// timeout
if(!is_data)
continue;
// error (may be socket closed)
if(is_data < 0)
break;
int is_valid = is_valid_socket(newsockfd);
if(!is_valid)
{
break;
}
// receiving http header
char *str_header = s_get_next_str(newsockfd, &str_len, "\r\n", true, timeout);
// bad format
if(!str_header)
break;
if(str_header && strlen(str_header) == 0) {
marker++;
if(marker == 1)
continue;
}
// filling parameters of command
if(marker == 1) {
cmd_param_list = dap_list_append(cmd_param_list, str_header);
//printf("g_list_append argc=%d command=%s ", argc, str_header);
argc++;
}
else
free(str_header);
if(marker == 2) {
dap_list_t *list = cmd_param_list;
// form command
unsigned int argc = dap_list_length(list);
// command is found
if(argc >= 1) {
int l_verbose = 0;
char *cmd_name = list->data;
list = dap_list_next(list);
// execute command
char *str_cmd = dap_strdup_printf("%s", cmd_name);
dap_chain_node_cmd_item_t *l_cmd = dap_chain_node_cli_cmd_find(cmd_name);
int res = -1;
char *str_reply = NULL;
if(l_cmd){
while(list) {
char *str_cmd_prev = str_cmd;
str_cmd = dap_strdup_printf("%s;%s", str_cmd, list->data);
list = dap_list_next(list);
DAP_DELETE(str_cmd_prev);
}
log_it(L_INFO, "execute command=%s", str_cmd);
// exec command
char **l_argv = dap_strsplit(str_cmd, ";", -1);
// Call the command function
if(l_cmd && l_argv && l_cmd->func)
res = (*(l_cmd->func))(argc, l_argv, l_cmd->arg_func, &str_reply);
else if (l_cmd){
log_it(L_WARNING,"NULL arguments for input for command \"%s\"", str_cmd);
}else {
log_it(L_WARNING,"No function for command \"%s\" but it registred?!", str_cmd);
}
// find '-verbose' command
l_verbose = dap_chain_node_cli_find_option_val(l_argv, 1, argc, "-verbose", NULL);
dap_strfreev(l_argv);
} else {
str_reply = dap_strdup_printf("can't recognize command=%s", str_cmd);
log_it(L_ERROR, str_reply);
}
char *reply_body;
if(l_verbose)
reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, (str_reply) ? str_reply : "");
else
reply_body = dap_strdup_printf("%d\r\n%s\r\n", res, (str_reply) ? str_reply : "");
// return the result of the command function
char *reply_str = dap_strdup_printf("HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n\r\n"
"%s",
strlen(reply_body), reply_body);
/*int ret = */ send(newsockfd, reply_str, strlen(reply_str) ,0);
DAP_DELETE(str_reply);
DAP_DELETE(reply_str);
DAP_DELETE(reply_body);
DAP_DELETE(str_cmd);
}
dap_list_free_full(cmd_param_list, free);
break;
}
}
// close connection
int cs = closesocket(newsockfd);
log_it(L_INFO, "close connection=%d sockfd=%d", cs, newsockfd);
return NULL;
}
#ifdef _WIN32
char *p_get_next_str( HANDLE hPipe, int *dwLen, const char *stop_str, bool del_stop_str, int timeout )
{
bool bSuccess = false;
long nRecv = 0; // count of bytes received
size_t stop_str_len = (stop_str) ? strlen(stop_str) : 0;
// if there is nothing to look for
if(!stop_str_len)
return NULL;
size_t lpszBuffer_len = 256;
char *lpszBuffer = DAP_NEW_Z_SIZE(char, lpszBuffer_len);
// received string will not be larger than MAX_REPLY_LEN
while( 1 ) //nRecv < MAX_REPLY_LEN)
{
long ret = 0;
// read one byte
// long ret = s_recv( nSocket, (unsigned char *) (lpszBuffer + nRecv), 1, timeout);
bSuccess = ReadFile( hPipe, lpszBuffer + nRecv,
lpszBuffer_len - nRecv, (LPDWORD)&ret, NULL );
//int ret = recv(nSocket,lpszBuffer+nRecv,1, 0);
if ( ret <= 0 || !bSuccess )
break;
nRecv += ret;
//printf("**debug** socket=%d read %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv));
while((nRecv + 1) >= (long) lpszBuffer_len)
{
lpszBuffer_len *= 2;
lpszBuffer = (char*) realloc(lpszBuffer, lpszBuffer_len);
}
// search for the required string
if(nRecv >= (long) stop_str_len) {
// found the required string
if(!strncasecmp(lpszBuffer + nRecv - stop_str_len, stop_str, stop_str_len)) {
bSuccess = true;
break;
}
}
};
// end reading
if(bSuccess) {
// delete the searched string
if(del_stop_str) {
lpszBuffer[nRecv - (long) stop_str_len] = '\0';
if(dwLen)
*dwLen =(int) nRecv - (int) stop_str_len;
}
else {
lpszBuffer[nRecv] = '\0';
if(dwLen)
*dwLen = (int) nRecv;
}
lpszBuffer = DAP_REALLOC(lpszBuffer,(size_t) *dwLen + 1);
return lpszBuffer;
}
// in case of an error or missing string
if(dwLen)
*dwLen = 0;
free(lpszBuffer);
return NULL;
}
/**
* threading function for processing a request from a client
*/
static void *thread_pipe_client_func( void *args )
{
HANDLE hPipe = (HANDLE)args;
// SOCKET newsockfd = (SOCKET) (intptr_t) args;
log_it(L_INFO, "new connection pipe = %X", hPipe );
int str_len, marker = 0;
int timeout = 5000; // 5 sec
int argc = 0;
dap_list_t *cmd_param_list = NULL;
while( 1 )
{
// wait data from client
// int is_data = s_poll( newsockfd, timeout );
// timeout
// if(!is_data)
// continue;
// error (may be socket closed)
// if(is_data < 0)
// break;
// int is_valid = is_valid_socket(newsockfd);
// if(!is_valid)
// {
// break;
// }
// receiving http header
char *str_header = p_get_next_str( hPipe, &str_len, "\r\n", true, timeout );
// bad format
if(!str_header)
break;
if ( str_header && strlen(str_header) == 0) {
marker++;
if(marker == 1)
continue;
}
// filling parameters of command
if ( marker == 1 ) {
cmd_param_list = dap_list_append( cmd_param_list, str_header );
//printf("g_list_append argc=%d command=%s ", argc, str_header);
argc ++;
}
else
free( str_header );
if ( marker == 2 ) {
dap_list_t *list = cmd_param_list;
// form command
unsigned int argc = dap_list_length( list );
// command is found
if ( argc >= 1) {
int l_verbose = 0;
char *cmd_name = list->data;
list = dap_list_next( list );
// execute command
char *str_cmd = dap_strdup_printf( "%s", cmd_name );
dap_chain_node_cmd_item_t *l_cmd = dap_chain_node_cli_cmd_find( cmd_name );
int res = -1;
char *str_reply = NULL;
if ( l_cmd ) {
while( list ) {
str_cmd = dap_strdup_printf( "%s;%s", str_cmd, list->data );
list = dap_list_next(list);
}
log_it(L_INFO, "execute command = %s", str_cmd );
// exec command
char **l_argv = dap_strsplit( str_cmd, ";", -1 );
// Call the command function
if ( l_cmd && l_argv && l_cmd->func )
res = (* (l_cmd->func))( argc, l_argv, NULL, &str_reply );
else if ( l_cmd ) {
log_it(L_WARNING,"NULL arguments for input for command \"%s\"", str_cmd );
}else {
log_it(L_WARNING,"No function for command \"%s\" but it registred?!", str_cmd );
}
// find '-verbose' command
l_verbose = dap_chain_node_cli_find_option_val( l_argv, 1, argc, "-verbose", NULL );
dap_strfreev( l_argv );
} else {
str_reply = dap_strdup_printf("can't recognize command = %s", str_cmd );
log_it( L_ERROR, str_reply );
}
char *reply_body;
if(l_verbose)
reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, (str_reply) ? str_reply : "");
else
reply_body = dap_strdup_printf("%d\r\n%s\r\n", res, (str_reply) ? str_reply : "");
// return the result of the command function
char *reply_str = dap_strdup_printf( "HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n\r\n"
"%s",
strlen(reply_body), reply_body );
int ret;// = send( newsockfd, reply_str, strlen(reply_str) ,0 );
WriteFile( hPipe, reply_str, strlen(reply_str), (LPDWORD)&ret, NULL );
DAP_DELETE(str_reply);
DAP_DELETE(reply_str);
DAP_DELETE(reply_body);
DAP_DELETE(str_cmd);
}
dap_list_free_full(cmd_param_list, free);
break;
}
}
// close connection
// int cs = closesocket(newsockfd);
log_it( L_INFO, "close connection pipe = %X", hPipe );
FlushFileBuffers( hPipe );
DisconnectNamedPipe( hPipe );
CloseHandle( hPipe );
return NULL;
}
/**
* main threading server function pipe win32
*/
static void* thread_pipe_func( void *args )
{
BOOL fConnected = FALSE;
pthread_t threadId;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
static const char *cPipeName = "\\\\.\\pipe\\node_cli.pipe";
for (;;)
{
/// printf( "\nPipe Server: Main thread awaiting client connection on %s\n", lpszPipename );
hPipe = CreateNamedPipe(
cPipeName, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
4096, // output buffer size
4096, // input buffer size
0, // client time-out
NULL ); // default security attribute
if ( hPipe == INVALID_HANDLE_VALUE ) {
log_it( L_ERROR, "CreateNamedPipe failed, GLE = %d.\n", GetLastError() );
return NULL;
}
fConnected = ConnectNamedPipe( hPipe, NULL ) ? TRUE : ( GetLastError() == ERROR_PIPE_CONNECTED );
if ( fConnected )
{
log_it( L_INFO, "Client %X connected, creating a processing thread.\n", hPipe );
pthread_create( &threadId, NULL, thread_pipe_client_func, hPipe );
pthread_detach( threadId );
}
else
CloseHandle( hPipe );
}
return NULL;
}
#endif
/**
* main threading server function
*/
static void* thread_main_func(void *args)
{
SOCKET sockfd = (SOCKET) (intptr_t) args;
SOCKET newsockfd;
log_it( L_INFO, "Server start socket = %s", dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path") );
// wait of clients
while(1)
{
pthread_t threadId;
struct sockaddr_in peer;
socklen_t size = sizeof(peer);
// received a new connection request
if((newsockfd = accept(sockfd, (struct sockaddr*) &peer, &size)) == (SOCKET) -1) {
log_it(L_ERROR, "new connection break newsockfd=%d", newsockfd);
break;
}
// create child thread for a client connection
pthread_create(&threadId, NULL, thread_one_client_func, (void*) (intptr_t) newsockfd);
// in order to thread not remain in state "dead" after completion
pthread_detach(threadId);
};
// close connection
int cs = closesocket(sockfd);
log_it(L_INFO, "Exit server thread=%d socket=%d", cs, sockfd);
return NULL;
}
/**
* Write text to reply string
*/
void dap_chain_node_cli_set_reply_text(char **str_reply, const char *str, ...)
{
if(str_reply) {
if(*str_reply) {
assert(! *str_reply );
DAP_DELETE(*str_reply);
*str_reply = NULL;
}
va_list args;
va_start(args, str);
*str_reply = dap_strdup_vprintf(str, args); //*str_reply = dap_strdup(str);
va_end(args);
}
}
/**
* find option value
*
* return index of string in argv, or 0 if not found
*/
int dap_chain_node_cli_find_option_val( char** argv, int arg_start, int arg_end, const char *opt_name, const char **opt_value)
{
int arg_index = arg_start;
const char *arg_string;
while(arg_index < arg_end)
{
char * l_argv_cur = argv[arg_index];
arg_string = l_argv_cur;
// find opt_name
if(arg_string && opt_name && arg_string[0] && opt_name[0] && !strcmp(arg_string, opt_name)) {
// find opt_value
if(opt_value) {
arg_string = argv[++arg_index];
if(arg_string) {
*opt_value = arg_string;
return arg_index;
}
}
else
// need only opt_name
return arg_index;
}
arg_index++;
}
return 0;
}
/**
* @brief s_cmd_item_create
* @param a_name
* @param func
* @param a_arg_func
* @param doc
* @param doc_ex
* @return
*/
void dap_chain_node_cli_cmd_item_create(const char * a_name, cmdfunc_t *a_func, void *a_arg_func, const char *a_doc, const char *a_doc_ex)
{
dap_chain_node_cmd_item_t *l_cmd_item = DAP_NEW_Z(dap_chain_node_cmd_item_t);
dap_snprintf(l_cmd_item->name,sizeof (l_cmd_item->name),"%s",a_name);
l_cmd_item->doc = strdup( a_doc);
l_cmd_item->doc_ex = strdup( a_doc_ex);
l_cmd_item->func = a_func;
l_cmd_item->arg_func = a_arg_func;
HASH_ADD_STR(s_commands,name,l_cmd_item);
log_it(L_DEBUG,"Added command %s",l_cmd_item->name);
}
/**
* @brief dap_chain_node_cli_command_get_first
* @return
*/
dap_chain_node_cmd_item_t* dap_chain_node_cli_cmd_get_first()
{
return s_commands;
}
/**
* @brief dap_chain_node_cli_command_find
* @param a_name
* @return
*/
dap_chain_node_cmd_item_t* dap_chain_node_cli_cmd_find(const char *a_name)
{
dap_chain_node_cmd_item_t *l_cmd_item = NULL;
HASH_FIND_STR(s_commands,a_name,l_cmd_item);
return l_cmd_item;
}
/**
* Initialization of the server side of the interaction
* with the console kelvin-node-cli
*
* return 0 if OK, -1 error
*/
int dap_chain_node_cli_init(dap_config_t * g_config)
{
#ifndef _WIN32
struct sockaddr_un l_server_addr={0};
l_server_addr.sun_family = AF_UNIX;
snprintf(l_server_addr.sun_path,sizeof(l_server_addr.sun_path), "%s", dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path") );
#else
pthread_t threadId;
#endif
struct sockaddr_in server_addr;
SOCKET sockfd = -1;
bool l_conserver_enabled = dap_config_get_item_bool_default( g_config, "conserver", "enabled", true );
if ( !l_conserver_enabled ) {
log_it( L_WARNING, "Console Server is dissabled." );
return 0;
}
#ifdef __WIN32
WSADATA wsaData;
int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (ret != 0) {
log_it(L_CRITICAL, "Couldn't init Winsock DLL, error: %d", ret);
return 2;
}
#endif
dap_chain_node_cli_cmd_item_create("global_db", com_global_db, NULL, "Work with global database",
"global_db cells add -cell <cell id> \n"
"global_db flush \n\n"
// "global_db wallet_info set -addr <wallet address> -cell <cell id> \n\n"
);
dap_chain_node_cli_cmd_item_create("node", com_node, NULL, "Work with node",
"node add -net <net name> -addr {<node address> | -alias <node alias>} {-port <port>} -cell <cell id> {-ipv4 <ipv4 external address> | -ipv6 <ipv6 external address>}\n\n"
"node del -net <net name> -addr <node address> | -alias <node alias>\n\n"
"node link {add|del} -net <net name> {-addr <node address> | -alias <node alias>} -link <node address>\n\n"
"node alias -addr <node address> -alias <node alias>\n\n"
"node connect {<node address> | -alias <node alias> | auto}\n\n"
"node handshake {<node address> | -alias <node alias>}\n"
"node dump -net <net name> [ -addr <node address> | -alias <node alias>] [-full]\n\n"
);
dap_chain_node_cli_cmd_item_create ("ping", com_ping, NULL, "Send ICMP ECHO_REQUEST to network hosts",
"ping [-c <count>] host\n");
dap_chain_node_cli_cmd_item_create ("traceroute", com_traceroute, NULL, "Print the hops and time of packets trace to network host",
"traceroute host\n");
dap_chain_node_cli_cmd_item_create ("tracepath", com_tracepath, NULL, "Traces path to a network host along this path",
"tracepath host\n");
dap_chain_node_cli_cmd_item_create ("help", com_help, NULL, "Description of command parameters",
"help [<command>]\n"
"\tObtain help for <command> or get the total list of the commands\n"
);
dap_chain_node_cli_cmd_item_create ("?", com_help, NULL, "Synonym for \"help\"",
"? [<command>]\n"
"\tObtain help for <command> or get the total list of the commands\n"
);
dap_chain_node_cli_cmd_item_create("wallet", com_tx_wallet, NULL, "Wallet operations",
"wallet [new -w <wallet_name> [-sign <sign_type>] [-restore <hex value>] [-net <net_name>] [-force]| list | info -addr <addr> -w <wallet_name> -net <net_name>]\n");
// Token commands
dap_chain_node_cli_cmd_item_create ("token_update", com_token_update, NULL, "Token update",
"\nPrivate token update\n"
"\t token_update -net <net name> -chain <chain name> -token <token ticker> -type private [-<Param name 1> <Param Value 1>] [-Param name 2> <Param Value 2>] ...[-<Param Name N> <Param Value N>]\n"
"\t Update private token <token ticker> for <netname>:<chain name> with"
"\t custom parameters list <Param 1>, <Param 2>...<Param N>."
"\n"
"==Params==\n"
"General:\n"
"\t -flags_set <value>:\t Set list of flags from <value> to token declaration\n"
"\t -flags_unset <value>:\t Unset list of flags from <value> from token declaration\n"
"\t -total_supply <value>:\t Set total supply - emission's maximum - to the <value>\n"
"\t -signs_valid <value>:\t Set valid signatures count's minimum\n"
"\t -signs_add <value>:\t Add signature's pkey fingerprint to the list of owners\n"
"\t -signs_remove <value>:\t Remove signature's pkey fingerprint from the owners\n"
"\nDatum type allowed/blocked updates:\n"
"\t -datum_type_allowed_add <value>:\t Add allowed datum type(s)\n"
"\t -datum_type_allowed_remove <value>:\t Remove datum type(s) from allowed\n"
"\t -datum_type_allowed_clear:\t Remove all datum types from allowed\n"
"\t -datum_type_blocked_add <value>:\t Add blocked datum type(s)\n"
"\t -datum_type_blocked_remove <value>:\t Remove datum type(s) from blocked\n"
"\t -datum_type_blocked_clear:\t Remove all datum types from blocked\n"
"\nTx receiver addresses allowed/blocked updates:\n"
"\t -tx_receiver_allowed_add <value>:\t Add allowed tx receiver(s)\n"
"\t -tx_receiver_allowed_remove <value>:\t Remove tx receiver(s) from allowed\n"
"\t -tx_receiver_allowed_clear:\t Remove all tx receivers from allowed\n"
"\t -tx_receiver_blocked_add <value>:\t Add blocked tx receiver(s)\n"
"\t -tx_receiver_blocked_remove <value>:\t Remove tx receiver(s) from blocked\n"
"\t -tx_receiver_blocked_clear:\t Remove all tx receivers from blocked\n"
"\nTx sender addresses allowed/blocked updates:\n"
"\t -tx_sender_allowed_add <value>:\t Add allowed tx sender(s)\n"
"\t -tx_sender_allowed_remove <value>:\t Remove tx sender(s) from allowed\n"
"\t -tx_sender_allowed_clear:\t Remove all tx senders from allowed\n"
"\t -tx_sender_blocked_add <value>:\t Add allowed tx sender(s)\n"
"\t -tx_sender_blocked_remove <value>:\t Remove tx sender(s) from blocked\n"
"\t -tx_sender_blocked_clear:\t Remove all tx sender(s) from blocked\n"
"\n"
"==Flags=="
"\t ALL_BLOCKED:\t Blocked all permissions, usefull add it first and then add allows what you want to allow\n"
"\t ALL_ALLOWED:\t Allowed all permissions if not blocked them. Be careful with this mode\n"
"\t ALL_FROZEN:\t All permissions are temprorary frozen\n"
"\t ALL_UNFROZEN:\t Unfrozen permissions\n"
"\t STATIC_ALL:\t No token manipulations after declarations at all. Token declares staticly and can't variabed after\n"
"\t STATIC_FLAGS:\t No token manipulations after declarations with flags\n"
"\t STATIC_PERMISSIONS_ALL:\t No all permissions lists manipulations after declarations\n"
"\t STATIC_PERMISSIONS_DATUM_TYPE:\t No datum type permissions lists manipulations after declarations\n"
"\t STATIC_PERMISSIONS_TX_SENDER:\t No tx sender permissions lists manipulations after declarations\n"
"\t STATIC_PERMISSIONS_TX_RECEIVER:\t No tx receiver permissions lists manipulations after declarations\n"
"\n"
);
// Token commands
dap_chain_node_cli_cmd_item_create ("token_decl", com_token_decl, NULL, "Token declaration",
"Simple token declaration:\n"
"\t token_decl -net <net name> -chain <chain name> -token <token ticker> -total_supply <total supply> -signs_total <sign total> -signs_emission <signs for emission> -certs <certs list>\n"
"\t\ Declare new simple token for <netname>:<chain name> with ticker <token ticker>, maximum emission <total supply> and <signs for emission> from <signs total> signatures on valid emission\n"
"\nExtended private token declaration\n"
"\t token_decl -net <net name> -chain <chain name> -token <token ticker> -type private -flags [<Flag 1>][,<Flag 2>]...[,<Flag N>]... [-<Param name 1> <Param Value 1>] [-Param name 2> <Param Value 2>] ...[-<Param Name N> <Param Value N>]\n"
"\t Declare new token for <netname>:<chain name> with ticker <token ticker>, flags <Flag 1>,<Flag2>...<Flag N>"
"\t and custom parameters list <Param 1>, <Param 2>...<Param N>."
"\n"
"==Flags=="
"\t ALL_BLOCKED:\t Blocked all permissions, usefull add it first and then add allows what you want to allow\n"
"\t ALL_ALLOWED:\t Allowed all permissions if not blocked them. Be careful with this mode\n"
"\t ALL_FROZEN:\t All permissions are temprorary frozen\n"
"\t ALL_UNFROZEN:\t Unfrozen permissions\n"
"\t STATIC_ALL:\t No token manipulations after declarations at all. Token declares staticly and can't variabed after\n"
"\t STATIC_FLAGS:\t No token manipulations after declarations with flags\n"
"\t STATIC_PERMISSIONS_ALL:\t No all permissions lists manipulations after declarations\n"
"\t STATIC_PERMISSIONS_DATUM_TYPE:\t No datum type permissions lists manipulations after declarations\n"
"\t STATIC_PERMISSIONS_TX_SENDER:\t No tx sender permissions lists manipulations after declarations\n"
"\t STATIC_PERMISSIONS_TX_RECEIVER:\t No tx receiver permissions lists manipulations after declarations\n"
"\n"
"==Params==\n"
"General:\n"
"\t -flags <value>:\t List of flags from <value> to token declaration\n"
"\t -total_supply <value>:\t Set total supply - emission's maximum - to the <value>\n"
"\t -signs_valid <value>:\t Set valid signatures count's minimum\n"
"\t -signs <value>:\t Signature's fingerprint list\n"
"\nDatum type allowed/blocked:\n"
"\t -datum_type_allowed <value>:\t Set allowed datum type(s)\n"
"\t -datum_type_blocked <value>:\t Set blocked datum type(s)\n"
"\nTx receiver addresses allowed/blocked:\n"
"\t -tx_receiver_allowed <value>:\t Set allowed tx receiver(s)\n"
"\t -tx_receiver_blocked <value>:\t Set blocked tx receiver(s)\n"
"\nTx sender addresses allowed/blocked:\n"
"\t -tx_sender_allowed <value>:\t Set allowed tx sender(s)\n"
"\t -tx_sender_blocked <value>:\t Set allowed tx sender(s)\n"
"\n"
);
/// -------- General tsd types ----
// Flags set/unsed
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_SET_FLAGS 0x0001
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_UNSET_FLAGS 0x0002
// Total supply limits
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SUPPLY 0x0003
// Set total signs count value to set to be valid
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_VALID 0x0004
// Add owner signature's pkey fingerprint
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_ADD 0x0006
// Remove owner signature by pkey fingerprint
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TOTAL_SIGNS_REMOVE 0x0007
/// ------- Permissions list flags, grouped by update-remove-clear operations --------
// Allowed datum types list update, remove or clear
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_UPDATE 0x0010
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_REMOVE 0x0011
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_ALLOWED_CLEAR 0x0012
// Blocked datum types list update, remove or clear
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_UPDATE 0x0013
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_REMOVE 0x0014
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_DATUM_TYPE_BLOCKED_CLEAR 0x0015
//Allowed tx receiver addres list update, remove or clear
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_UPDATE 0x0014
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_REMOVE 0x0015
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_ALLOWED_CLEAR 0x0016
//Blocked tx receiver addres list update, remove or clear
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_UPDATE 0x0017
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_REMOVE 0x0018
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_RECEIVER_BLOCKED_CLEAR 0x0019
//Allowed tx sender addres list update, remove or clear
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_UPDATE 0x0020
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_REMOVE 0x0021
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_ALLOWED_CLEAR 0x0022
//Blocked tx sender addres list update, remove or clear
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_UPDATE 0x0023
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_REMOVE 0x0024
#define DAP_CHAIN_DATUM_TOKEN_TSD_TYPE_TX_SENDER_BLOCKED_CLEAR 0x0025
dap_chain_node_cli_cmd_item_create ("token_decl_sign", com_token_decl_sign, NULL, "Token declaration add sign",
"token_decl_sign -net <net name> -chain <chain name> -datum <datum_hash> -certs <certs list>\n"
"\t Sign existent <datum hash> in mempool with <certs list>\n"
);
dap_chain_node_cli_cmd_item_create ("token_emit", com_token_emit, NULL, "Token emission",
"token_emit -net <net name> -chain_emission <chain for emission> -chain_base_tx <chain for base tx> -addr <addr> -token <token ticker> -certs <cert> -emission_value <val>\n");
dap_chain_node_cli_cmd_item_create ("mempool_list", com_mempool_list, NULL, "List mempool entries for selected chain network and chain id",
"mempool_list -net <net name> -chain <chain name>\n");
dap_chain_node_cli_cmd_item_create ("mempool_proc", com_mempool_proc, NULL, "Proc mempool entries for selected chain network and chain id",
"mempool_proc -net <net name> -chain <chain name>\n");
dap_chain_node_cli_cmd_item_create ("mempool_delete", com_mempool_delete, NULL, "Delete datum with hash <datum hash>",
"mempool_delete -net <net name> -chain <chain name> -datum <datum hash>\n");
dap_chain_node_cli_cmd_item_create ("mempool_add_ca", com_mempool_add_ca, NULL,
"Add pubic certificate into the mempool to prepare its way to chains",
"mempool_add_ca -net <net name> [-chain <chain name>] -ca_name <Certificate name>\n");
// Transaction commands
dap_chain_node_cli_cmd_item_create ("tx_create", com_tx_create, NULL, "Make transaction",
"tx_create -net <net name> -chain <chain name> -from_wallet <name> -to_addr <addr> -token <token ticker> -value <value> [-fee <addr> -value_fee <val>]\n" );
dap_chain_node_cli_cmd_item_create ("tx_cond_create", com_tx_cond_create, NULL, "Make cond transaction",
"tx_cond_create todo\n" );
dap_chain_node_cli_cmd_item_create ("tx_verify", com_tx_verify, NULL, "Verifing transaction",
"tx_verify -wallet <wallet name> \n" );
// Transaction history
dap_chain_node_cli_cmd_item_create("tx_history", com_tx_history, NULL, "Transaction history (for address or by hash)",
"tx_history [-addr <addr> | -w <wallet name> | -tx <tx_hash>] -net <net name> -chain <chain name>\n");
// vpn client
dap_chain_node_cli_cmd_item_create ("vpn_client", com_vpn_client, NULL, "VPN client control",
"vpn_client [start -addr <server address> -port <server port>| stop | status]\n");
// Log
dap_chain_node_cli_cmd_item_create ("print_log", com_print_log, NULL, "Print log info",
"print_log [ts_after <timestamp >] [limit <line numbers>]\n" );
// Statisticss
dap_chain_node_cli_cmd_item_create("stats", com_stats, NULL, "Print statistics",
"stats cpu");
// Exit
dap_chain_node_cli_cmd_item_create ("exit", com_exit, NULL, "Stop application and exit",
"exit\n" );
#ifndef _WIN32
// News
//dap_chain_node_cli_cmd_item_create("news", com_news, NULL, "Add News for VPN clients. Language code is a text code like \"en\", \"ru\", \"fr\"",
// "news [-text <news text> | -file <filename with news>] -lang <language code> \n");
#endif
// create thread for waiting of clients
pthread_t l_thread_id;
l_listen_port = dap_config_get_item_uint16_default( g_config, "conserver", "listen_port_tcp",0);
const char * l_listen_unix_socket_path = dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path");
const char * l_listen_unix_socket_permissions_str = dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_permissions");
mode_t l_listen_unix_socket_permissions = 0770;
if ( l_listen_unix_socket_path && l_listen_unix_socket_permissions ) {
if ( l_listen_unix_socket_permissions_str ) {
dap_sscanf(l_listen_unix_socket_permissions_str,"%ou", &l_listen_unix_socket_permissions );
}
log_it( L_INFO, "Console interace on path %s (%04o) ", l_listen_unix_socket_path, l_listen_unix_socket_permissions );
#ifndef _WIN32
if ( server_sockfd >= 0 ) {
dap_chain_node_cli_delete( );
server_sockfd = 0;
}
// create socket
sockfd = socket( AF_UNIX, SOCK_STREAM, 0 );
if( sockfd == INVALID_SOCKET )
return -1;
//int gdsg = sizeof(struct sockaddr_un);
if ( access( l_listen_unix_socket_path , R_OK) != -1 )
unlink( l_listen_unix_socket_path );
// connecting the address with a socket
if( bind(sockfd, (const struct sockaddr*) &l_server_addr, sizeof(struct sockaddr_un)) == SOCKET_ERROR) {
// errno = EACCES 13 Permission denied
if ( errno == EACCES ) // EACCES=13
log_it( L_ERROR, "Server can't start(err=%d). Can't create file=%s [Permission denied]", errno,
l_listen_unix_socket_path );
else
log_it( L_ERROR, "Server can't start(err=%d). May be problem with file=%s?", errno, l_listen_unix_socket_path );
closesocket( sockfd );
return -2;
}
chmod(l_listen_unix_socket_path,l_listen_unix_socket_permissions);
#else
// Sleep( 3000 );
if( pthread_create(&threadId, NULL, thread_pipe_func, (void*) (intptr_t) sockfd) != 0 ) {
closesocket( sockfd );
return -7;
}
return 0;
#endif
}
else if (l_listen_port ){
const char *l_listen_addr_str = dap_config_get_item_str(g_config, "conserver", "listen_address");
log_it( L_INFO, "Console interace on addr %s port %u ", l_listen_addr_str, l_listen_port );
server_addr.sin_family = AF_INET;
#ifdef _WIN32
struct in_addr _in_addr = { { .S_addr = htonl(INADDR_LOOPBACK) } };
server_addr.sin_addr = _in_addr;
server_addr.sin_port = l_listen_port;
#else
inet_pton( AF_INET, l_listen_addr_str, &server_addr.sin_addr );
server_addr.sin_port = htons( (uint16_t)l_listen_port );
#endif
// create socket
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET ) {
#ifdef __WIN32
_set_errno(WSAGetLastError());
#endif
log_it( L_ERROR, "Console Server: can't create socket, err %d", errno );
return -3;
}
// connecting the address with a socket
if ( bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == SOCKET_ERROR ) {
#ifdef __WIN32
_set_errno(WSAGetLastError());
#endif
log_it( L_ERROR, "Console Server: can't bind socket, err %d", errno );
closesocket( sockfd );
return -4;
}
}else {
log_it (L_INFO, "Not defined console interface");
return 0;
}
// turn on reception of connections
if( listen(sockfd, MAX_CONSOLE_CLIENTS) == SOCKET_ERROR )
return -5;
if( pthread_create(&l_thread_id, NULL, thread_main_func, (void*) (intptr_t) sockfd) != 0 ) {
closesocket( sockfd );
return -6;
}
// in order to thread not remain in state "dead" after completion
pthread_detach( l_thread_id );
server_sockfd = sockfd;
return 0;
}
/**
* Deinitialization of the server side
*
*/
void dap_chain_node_cli_delete(void)
{
if(server_sockfd >= 0)
closesocket(server_sockfd);
#ifdef __WIN32
WSACleanup();
#endif
// deinit client for handshake
dap_chain_node_client_deinit();
}