-
ca0f4bfa
dap_chain_net_node_list.c 12.37 KiB
/*
* Authors:
* Dmitriy Gerasimov <naeper@demlabs.net>
* Cellframe https://cellframe.net
* DeM Labs Inc. https://demlabs.net
* Copyright (c) 2017-2019
* All rights reserved.
This file is part of CellFrame SDK the open source project
CellFrame SDK 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.
CellFrame SDK 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 CellFrame SDK based project. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dap_chain_net_node_list.h"
#include "http_status_code.h"
#include "dap_chain_net_balancer.h"
#include "dap_client.h"
#include "dap_client_http.h"
#define LOG_TAG "dap_chain_net_node_list"
/**
* @brief server function, makes handshake and add node to node list
*
* @param dap_http_simple_t *a_http_simple, void *a_arg
* @return void
* send value
* 1 - Node addr successfully added to node list
* 2 - Can't add this addres to node list
* 3 - Can't calculate hash for addr
* 4 - Can't do handshake
* 5 - Already exists
*/
void dap_chain_net_node_check_http_issue_link(dap_http_simple_t *a_http_simple, void *a_arg)
{
log_it(L_DEBUG,"Proc enc http request");
http_status_code_t *l_return_code = (http_status_code_t *)a_arg;
if (strcmp(a_http_simple->http_client->url_path, DAP_NODE_LIST_URI_HASH)) {
log_it(L_ERROR, "Wrong path '%s' in the request to dap_chain_net_node_list module",
a_http_simple->http_client->url_path);
*l_return_code = Http_Status_BadRequest;
return;
}
int l_protocol_version = 0;
char l_issue_method = 0;
uint64_t addr = 0;
uint32_t ipv4 = 0;
uint16_t port = 0;
const char l_net_token[] = "net=";
sscanf(a_http_simple->http_client->in_query_string, "version=%d,method=%c,addr=%lu,ipv4=%d,port=%hu,net=",
&l_protocol_version, &l_issue_method, &addr, &ipv4, &port);
if (l_protocol_version != 1 || l_issue_method != 'r') {
log_it(L_ERROR, "Unsupported protocol version/method in the request to dap_chain_net_node_list module");
*l_return_code = Http_Status_MethodNotAllowed;
return;
}
char *l_net_str = strstr(a_http_simple->http_client->in_query_string, l_net_token);
if (!l_net_str) {
log_it(L_ERROR, "Net name token not found in the request to dap_chain_net_node_list module");
*l_return_code = Http_Status_NotFound;
return;
}
l_net_str += strlen(l_net_token);
log_it(L_DEBUG, "HTTP Node check parser retrieve netname %s", l_net_str);
dap_chain_net_t *l_net = dap_chain_net_by_name(l_net_str);
dap_chain_node_info_t l_node_info = {
.hdr.address.uint64 = addr,
.hdr.ext_addr_v4.s_addr = ipv4,
.hdr.ext_port = port
};
uint8_t response = 0;
char *l_key = dap_chain_node_addr_to_hash_str(&l_node_info.hdr.address);
if(!l_key)
{
log_it(L_DEBUG, "Can't calculate hash for addr");
response = 3;
} else{
size_t node_info_size = 0;
dap_chain_node_info_t *l_node_inf_check;
l_node_inf_check = (dap_chain_node_info_t *) dap_global_db_get_sync(l_net->pub.gdb_nodes, l_key, &node_info_size, NULL, NULL);
if(l_node_inf_check)
{
log_it(L_DEBUG, "The node is already exists");
response = 5;
DAP_DELETE(l_node_inf_check);
}
else{
if(dap_chain_net_balancer_handshake(&l_node_info,l_net))
response = 1;
if(response)
{
size_t l_node_info_size = dap_chain_node_info_get_size(&l_node_info);
bool res = dap_global_db_set_sync(l_net->pub.gdb_nodes, l_key, (uint8_t*)&l_node_info, l_node_info_size, false) == 0;
if(res)
{
char l_node_addr_str[INET_ADDRSTRLEN]={};
inet_ntop(AF_INET, &l_node_info.hdr.ext_addr_v4, l_node_addr_str, INET_ADDRSTRLEN);
log_it(L_DEBUG, "Add address"NODE_ADDR_FP_STR" (%s) to node list",
NODE_ADDR_FP_ARGS_S(l_node_info.hdr.address),l_node_addr_str);
response = 1;
}
else
{
response = 2;
log_it(L_DEBUG, "Don't add this addres to node list");
}
}
else
{
log_it(L_DEBUG, "Can't do handshake");
response = 4;
}
}
}
DAP_DELETE(l_key);
*l_return_code = Http_Status_OK;
size_t l_data_send_size = sizeof(uint8_t);
dap_http_simple_reply(a_http_simple, &response, l_data_send_size);
}
static void s_net_node_link_prepare_success(void *a_response, size_t a_response_size, void *a_arg) {
struct node_link_request *l_node_list_request = (struct node_link_request *)a_arg;
pthread_mutex_lock(&l_node_list_request->wait_mutex);
l_node_list_request->response = *(uint8_t*)a_response;
pthread_cond_broadcast(&l_node_list_request->wait_cond);
pthread_mutex_unlock(&l_node_list_request->wait_mutex);
}
static void s_net_node_link_prepare_error(int a_error_code, void *a_arg){
struct node_link_request * l_node_list_request = (struct node_link_request *)a_arg;
dap_chain_node_info_t *l_node_info = l_node_list_request->link_info;
if (!l_node_info) {
log_it(L_WARNING, "Link prepare error, code %d", a_error_code);
return;
}
char l_node_addr_str[INET_ADDRSTRLEN]={};
inet_ntop(AF_INET, &l_node_info->hdr.ext_addr_v4, l_node_addr_str, INET_ADDRSTRLEN);
log_it(L_WARNING, "Link from "NODE_ADDR_FP_STR" (%s) prepare error with code %d",
NODE_ADDR_FP_ARGS_S(l_node_info->hdr.address), l_node_addr_str,a_error_code);
}
static struct node_link_request *s_node_list_request_init ()
{
struct node_link_request *l_node_list_request = DAP_NEW_Z(struct node_link_request);
if(!l_node_list_request){
return NULL;
}
l_node_list_request->worker = dap_events_worker_get_auto();
l_node_list_request->from_http = true;
l_node_list_request->response = 0;
pthread_condattr_t attr;
pthread_condattr_init(&attr);
#ifndef DAP_OS_DARWIN
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
#endif
pthread_cond_init(&l_node_list_request->wait_cond, &attr);
pthread_mutex_init(&l_node_list_request->wait_mutex, NULL);
return l_node_list_request;
}
static void s_node_list_request_deinit (struct node_link_request *a_node_list_request)
{
pthread_cond_destroy(&a_node_list_request->wait_cond);
pthread_mutex_destroy(&a_node_list_request->wait_mutex);
DAP_DEL_Z(a_node_list_request->link_info);
}
static int dap_chain_net_node_list_wait(struct node_link_request *a_node_list_request, int a_timeout_ms){
int ret = -1;
pthread_mutex_lock(&a_node_list_request->wait_mutex);
if(a_node_list_request->response)
{
pthread_mutex_unlock(&a_node_list_request->wait_mutex);
return 0;
}
struct timespec l_cond_timeout;
clock_gettime(CLOCK_REALTIME, &l_cond_timeout);
l_cond_timeout.tv_sec += a_timeout_ms/1000;
int l_ret_wait = pthread_cond_timedwait(&a_node_list_request->wait_cond, &a_node_list_request->wait_mutex, &l_cond_timeout);
if(!l_ret_wait) {
ret = a_node_list_request->response ? 0 : -2;
} else if(l_ret_wait == ETIMEDOUT) {
log_it(L_NOTICE,"Wait for status is stopped by timeout");
ret = -1;
} else if (l_ret_wait) {
char l_errbuf[128];
l_errbuf[0] = '\0';
strerror_r(l_ret_wait,l_errbuf,sizeof (l_errbuf));
log_it(L_ERROR, "Pthread condition timed wait returned \"%s\"(code %d)", l_errbuf, l_ret_wait);
ret = -3;
}
pthread_mutex_unlock(&a_node_list_request->wait_mutex);
return ret;
}
int dap_chain_net_node_list_request(dap_chain_net_t *a_net, dap_chain_node_info_t *a_link_node_request, bool a_sync)
{
dap_chain_node_info_t *l_link_node_info = dap_chain_net_balancer_link_from_cfg(a_net);
if (!l_link_node_info)
return false;
char l_node_addr_str[INET_ADDRSTRLEN] = {};
inet_ntop(AF_INET, &l_link_node_info->hdr.ext_addr_v4, l_node_addr_str, INET_ADDRSTRLEN);
log_it(L_DEBUG, "Start node list HTTP request to %s", l_node_addr_str);
struct node_link_request *l_node_list_request = s_node_list_request_init();
if(!l_node_list_request){
log_it(L_CRITICAL, "Memory allocation error");
DAP_DELETE(l_link_node_info);
return false;
}
l_node_list_request->net = a_net;
l_node_list_request->link_info = l_link_node_info;
int ret = 0;
char *l_request = dap_strdup_printf("%s/%s?version=1,method=r,addr=%lu,ipv4=%d,port=%hu,net=%s",
DAP_UPLINK_PATH_NODE_LIST,
DAP_NODE_LIST_URI_HASH,
a_link_node_request->hdr.address.uint64,
a_link_node_request->hdr.ext_addr_v4.s_addr,
a_link_node_request->hdr.ext_port,
a_net->pub.name);
ret = dap_client_http_request(l_node_list_request->worker,
l_node_addr_str,
l_link_node_info->hdr.ext_port,
"GET",
"text/text",
l_request,
NULL,
0,
NULL,
s_net_node_link_prepare_success,
s_net_node_link_prepare_error,
l_node_list_request,
NULL) == NULL;
DAP_DELETE(l_request);
if (a_sync) {
int rc = dap_chain_net_node_list_wait(l_node_list_request, 10000);
ret = ret ? 6 : rc ? 0 : l_node_list_request->response;
} else {
ret = 7;
}
s_node_list_request_deinit(l_node_list_request);
return ret;
}
static void s_node_list_callback_notify(dap_store_obj_t *a_obj, void *a_arg)
{
if (!a_arg || !a_obj || !a_obj->key)
return;
dap_chain_net_t *l_net = (dap_chain_net_t *)a_arg;
assert(l_net);
if (!dap_strcmp(a_obj->group, l_net->pub.gdb_nodes)) {
if (a_obj->value && a_obj->type == DAP_GLOBAL_DB_OPTYPE_ADD) {
dap_chain_node_info_t *l_node_info = (dap_chain_node_info_t *)a_obj->value;
if(l_node_info->info.owner_addr.uint64 == 0){
log_it(L_NOTICE, "Node %s removed, there is not pinners", a_obj->key);
dap_global_db_del_sync(a_obj->group, a_obj->key);
} else {
char l_node_ipv4_str[INET_ADDRSTRLEN]={ '\0' }, l_node_ipv6_str[INET6_ADDRSTRLEN]={ '\0' };
inet_ntop(AF_INET, &l_node_info->hdr.ext_addr_v4, l_node_ipv4_str, INET_ADDRSTRLEN);
inet_ntop(AF_INET6, &l_node_info->hdr.ext_addr_v6, l_node_ipv6_str, INET6_ADDRSTRLEN);
char l_ts[128] = { '\0' };
dap_gbd_time_to_str_rfc822(l_ts, sizeof(l_ts), a_obj->timestamp);
log_it(L_MSG, "Add node "NODE_ADDR_FP_STR" %s %s, pinned by "NODE_ADDR_FP_STR" at %s",
NODE_ADDR_FP_ARGS_S(l_node_info->hdr.address),
l_node_ipv4_str, dap_itoa(l_node_info->hdr.ext_port),
NODE_ADDR_FP_ARGS_S(l_node_info->info.owner_addr),
l_ts);
}
}
}
}
int dap_chain_net_node_list_init()
{
uint16_t l_net_count = 0;
dap_chain_net_t **l_net_list = dap_chain_net_list(&l_net_count);
for (uint16_t i = 0; i < l_net_count; i++) {
dap_chain_net_add_nodelist_notify_callback(l_net_list[i], s_node_list_callback_notify, l_net_list[i]);
}
return 0;
}