diff --git a/CMakeLists.txt b/CMakeLists.txt index 39ee26ae704de55b51459d73f9a44b4b2dec1650..e1baa758096e7bba746a041c6681bde58999c154 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ endif() if (CELLFRAME_MODULES MATCHES "dap-sdk-net-client") set(DAPSDK_MODULES "core crypto io network-core network-link_manager network-client network-server") - set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_core dap_crypto dap_io dap_server dap_client m) + set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_core dap_crypto dap_io dap_server dap_client dap_dns_server m) if(NOT ANDROID) set(CELLFRAME_LIBS ${CELLFRAME_LIBS} pthread) endif() diff --git a/core/include/dap_common.h b/core/include/dap_common.h index d08bf97a6661e275864152f76d228743814d87de..88d1c07d392c94b7de39c62badff97c539de92a8 100755 --- a/core/include/dap_common.h +++ b/core/include/dap_common.h @@ -231,7 +231,7 @@ static inline void *s_vm_extend(const char *a_rtn_name, int a_rtn_line, void *a_ #define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) #define DAP_DEL_MULTY(...) dap_delete_multy(VA_NARGS(__VA_ARGS__), __VA_ARGS__) -#define DAP_DEL_Z(a) do if (a) { DAP_DELETE(a); (a) = NULL; } while (0); +#define DAP_DEL_Z(a) do { if (a) { DAP_DELETE(a); (a) = NULL; } } while (0); // a - pointer to alloc // t - type return pointer // s - size to alloc diff --git a/core/src/dap_config.c b/core/src/dap_config.c index 1c2c9cf296bdfac0e7fc1793e3d37aea923bf26c..2c273373092fc20e5a021c4505f80b70b62faa62 100644 --- a/core/src/dap_config.c +++ b/core/src/dap_config.c @@ -300,9 +300,13 @@ dap_config_t *dap_config_open(const char* a_file_path) { } log_it(L_DEBUG, "Looking for config name %s...", a_file_path); char l_path[MAX_PATH] = { '\0' }; + const char *l_suffix = ""; + size_t l_path_len = strlen(a_file_path); + if (l_path_len < 4 || strcmp(a_file_path + l_path_len - 4, ".cfg")) + l_suffix = ".cfg"; int l_pos = dap_strncmp(a_file_path, s_configs_path, strlen(s_configs_path) - 4) - ? snprintf(l_path, MAX_PATH, "%s/%s.cfg", s_configs_path, a_file_path) - : snprintf(l_path, MAX_PATH, "%s.cfg", a_file_path); + ? snprintf(l_path, MAX_PATH, "%s/%s%s", s_configs_path, a_file_path, l_suffix) + : snprintf(l_path, MAX_PATH, "%s%s", a_file_path, l_suffix); if (l_pos >= MAX_PATH) { log_it(L_ERROR, "Too long config name!"); diff --git a/io/include/dap_net.h b/io/include/dap_net.h index 252aadd0c955b814a4756445e460a32e27cb43cb..81d337fb79d6523e709255d82afc9df19461cc36 100644 --- a/io/include/dap_net.h +++ b/io/include/dap_net.h @@ -48,16 +48,25 @@ #define DAP_CFG_PARAM_SOCK_PERMISSIONS "listen-unix-socket-permissions" #define DAP_CFG_PARAM_LEGACY_PORT "listen-port-tcp" +typedef struct dap_link_info { + dap_stream_node_addr_t node_addr; + char uplink_addr[DAP_HOSTADDR_STRLEN]; + uint16_t uplink_port; +} DAP_ALIGN_PACKED dap_link_info_t; + +typedef struct dap_net_links { + uint64_t count_node; + byte_t nodes_info[]; +} DAP_ALIGN_PACKED dap_net_links_t; + #ifdef __cplusplus extern "C" { #endif - - int dap_net_resolve_host(const char *a_host, const char *a_port, bool a_numeric_only, struct sockaddr_storage *a_addr_out, int *a_family); int dap_net_parse_config_address(const char *a_src, char *a_addr, uint16_t *a_port, struct sockaddr_storage *a_saddr, int *a_family); long dap_net_recv(SOCKET sd, unsigned char *buf, size_t bufsize, int timeout); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/net/client/include/dap_client.h b/net/client/include/dap_client.h index 9d7ae83f74f1da0a9cc9a0daf441715ec33f9f1b..b8e3aa42f5718703b2b3ce750d4ffdbd23cd51e3 100644 --- a/net/client/include/dap_client.h +++ b/net/client/include/dap_client.h @@ -23,7 +23,7 @@ */ #pragma once -#include "dap_events.h" +#include "dap_net.h" #include <stdint.h> #include "dap_enc_key.h" #include "dap_stream.h" @@ -77,12 +77,6 @@ typedef void (*dap_client_callback_t) (dap_client_t *, void *); typedef void (*dap_client_callback_int_t) (dap_client_t *, void *, int); typedef void (*dap_client_callback_data_size_t) (dap_client_t *, void *, size_t); -typedef struct dap_link_info { - dap_stream_node_addr_t node_addr; - char uplink_addr[DAP_HOSTADDR_STRLEN]; - uint16_t uplink_port; -} DAP_ALIGN_PACKED dap_link_info_t; - /** * @brief The dap_client struct */ diff --git a/net/server/CMakeLists.txt b/net/server/CMakeLists.txt index 9d8ed804e823ef4ed5fcc56d12a69eed0aedeac5..deaf9312346492c81f5374e3a86f00f8a078d8bb 100644 --- a/net/server/CMakeLists.txt +++ b/net/server/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(cli_server) add_subdirectory(notify_server) add_subdirectory(http_server) add_subdirectory(enc_server) +add_subdirectory(dns_server) add_subdirectory(json_rpc) diff --git a/net/server/dns_server/CMakeLists.txt b/net/server/dns_server/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..354d1232f19c803b7586cfd3239e0c9b5755ca2c --- /dev/null +++ b/net/server/dns_server/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10) +project (dap_dns_server C) + +file(GLOB DAP_DNS_SRV_SRCS *.c) + +file(GLOB DAP_DNS_SRV_HEADERS include/*.h) + +add_library(${PROJECT_NAME} STATIC ${DAP_DNS_SRV_SRCS} ${DAP_DNS_SRV_HEADERS} ) + +target_link_libraries(${PROJECT_NAME} dap_core dap_io m dap_json_rpc) +target_include_directories(${PROJECT_NAME} PUBLIC include/ ) + +if(INSTALL_DAP_SDK) +set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "${DAP_DNS_SRV_HEADERS}") +INSTALL(TARGETS ${PROJECT_NAME} + LIBRARY DESTINATION lib/dap/net/server/dns_server/ + ARCHIVE DESTINATION lib/dap/net/server/dns_server/ + PUBLIC_HEADER DESTINATION include/dap/net/server/dns_server/ +) +endif() diff --git a/net/server/dns_server/dap_dns_client.c b/net/server/dns_server/dap_dns_client.c new file mode 100644 index 0000000000000000000000000000000000000000..8d10cd272285734f7bf77c0b0dd37c96801a685c --- /dev/null +++ b/net/server/dns_server/dap_dns_client.c @@ -0,0 +1,306 @@ +/* + * Authors: + * Roman Khlopkov <roman.khlopkov@demlabs.net> + * Dmitriy Gerasimov <dmitriy.gerasmiov@demlabs.net> + * DeM Labs Ltd https://demlabs.net + * DeM Labs Open source community https://gitlab.demlabs.net + * Copyright (c) 2021 + * All rights reserved. + + This file is part of DapChain SDK the open source project + + DapChain 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. + + DapChain 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 DapChain SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "dap_context.h" +#include "dap_timerfd.h" +#include "dap_net.h" +#include "dap_dns_server.h" +#include "dap_dns_client.h" + +#define LOG_TAG "dap_chain_node_dns_client" + +struct dns_client +{ + dap_events_socket_t * parent; + struct in_addr addr; + uint16_t port; + char *name; + dap_dns_buf_t dns_request; + byte_t buf[1024]; + + dap_dns_client_node_info_request_success_callback_t callback_success; + dap_dns_client_node_info_request_error_callback_t callback_error; + void * callbacks_arg; + + bool is_callbacks_called; +}; + +static void s_dns_client_esocket_read_callback(dap_events_socket_t * a_esocket, void * a_arg); +static void s_dns_client_esocket_error_callback(dap_events_socket_t * a_esocket, int a_error); +static bool s_dns_client_esocket_timeout_callback( void * a_arg); +static void s_dns_client_esocket_delete_callback(dap_events_socket_t * a_esocket, void * a_arg); +static void s_dns_client_esocket_worker_assign_callback(dap_events_socket_t * a_esocket, dap_worker_t * a_worker); + +/** + * @brief s_dns_client_esocket_read_callback + * @param a_esocket + * @param a_arg + */ +static void s_dns_client_esocket_read_callback(dap_events_socket_t * a_esocket, void * a_arg) +{ + (void) a_arg; + struct dns_client * l_dns_client = (struct dns_client*) a_esocket->_inheritor; + byte_t * l_buf = a_esocket->buf_in; + size_t l_recieved = a_esocket->buf_in_size; + size_t l_addr_point = DNS_REQUEST_SIZE + strlen(l_dns_client->name) + DNS_ANSWER_SIZE; + if (l_recieved < l_addr_point + sizeof(dap_link_info_t)) { + log_it(L_WARNING, "DNS answer incomplete"); + l_dns_client->callback_error(a_esocket->worker, l_dns_client->callbacks_arg, EIO); + l_dns_client->is_callbacks_called = true; + a_esocket->flags |= DAP_SOCK_SIGNAL_CLOSE; + a_esocket->buf_in_size = a_esocket->buf_out_size = 0; + return; + } + byte_t * l_cur = l_buf + 3 * sizeof(uint16_t); + int l_answers_count = ntohs(*(uint16_t *)l_cur); + if (l_answers_count != 1) { + log_it(L_WARNING, "Incorrect DNS answer format"); + l_dns_client->callback_error(a_esocket->worker, l_dns_client->callbacks_arg, EINVAL); + l_dns_client->is_callbacks_called = true; + a_esocket->flags |= DAP_SOCK_SIGNAL_CLOSE; + a_esocket->buf_in_size = a_esocket->buf_out_size = 0; + return; + } + + dap_net_links_t *l_link_full_node_list = DAP_NEW_Z_SIZE(dap_net_links_t,sizeof(dap_net_links_t) + sizeof(dap_link_info_t)); + l_cur = l_buf + l_addr_point; + *(dap_link_info_t *)l_link_full_node_list->nodes_info = *(dap_link_info_t *)l_cur; + l_link_full_node_list->count_node = 1; + + l_dns_client->callback_success(a_esocket->worker, l_link_full_node_list, l_dns_client->callbacks_arg); + l_dns_client->is_callbacks_called = true; + a_esocket->flags |= DAP_SOCK_SIGNAL_CLOSE; + a_esocket->buf_in_size = a_esocket->buf_out_size = 0; + DAP_DELETE(l_link_full_node_list); +} + +/** + * @brief s_dns_client_esocket_error_callback + * @param a_esocket + * @param a_error + */ +static void s_dns_client_esocket_error_callback(dap_events_socket_t * a_esocket, int a_error) +{ + struct dns_client * l_dns_client = (struct dns_client*) a_esocket->_inheritor; + log_it(L_ERROR,"DNS client esocket error %d", a_error); + l_dns_client->callback_error(a_esocket->worker, l_dns_client->callbacks_arg, a_error); + l_dns_client->is_callbacks_called = true; +} + +/** + * @brief s_dns_client_esocket_timeout_callback + * @param a_worker + * @param a_arg + * @return + */ +static bool s_dns_client_esocket_timeout_callback(void * a_arg) +{ + assert(a_arg); + dap_events_socket_uuid_t * l_es_uuid_ptr = (dap_events_socket_uuid_t *) a_arg; + assert(l_es_uuid_ptr); + + + dap_worker_t * l_worker = dap_worker_get_current(); // We're in own esocket context + assert(l_worker); + + dap_events_socket_t * l_es; + if((l_es = dap_context_find(l_worker->context,*l_es_uuid_ptr) ) != NULL){ // If we've not closed this esocket + struct dns_client * l_dns_client = (struct dns_client*) l_es->_inheritor; + log_it(L_WARNING,"DNS request timeout, bad network?"); + if(! l_dns_client->is_callbacks_called ){ + l_dns_client->callback_error(l_es->worker, l_dns_client->callbacks_arg, ETIMEDOUT); + l_dns_client->is_callbacks_called = true; + } + dap_events_socket_remove_and_delete_unsafe( l_es, false); + } + DAP_DEL_Z(l_es_uuid_ptr); + return false; +} + +/** + * @brief s_dns_client_esocket_delete_callback + * @param a_esocket + * @param a_arg + */ +static void s_dns_client_esocket_delete_callback(dap_events_socket_t * a_esocket, void * a_arg) +{ + (void) a_arg; + struct dns_client * l_dns_client = (struct dns_client*) a_esocket->_inheritor; + if(! l_dns_client->is_callbacks_called ) + l_dns_client->callback_error(a_esocket->worker, l_dns_client->callbacks_arg, EBUSY); + if(l_dns_client->name) + DAP_DELETE(l_dns_client->name); +} + +/** + * @brief s_dns_client_esocket_worker_assign_callback + * @param a_esocket + * @param a_worker + */ +static void s_dns_client_esocket_worker_assign_callback(dap_events_socket_t * a_esocket, dap_worker_t * a_worker) +{ + struct dns_client * l_dns_client = (struct dns_client*) a_esocket->_inheritor; + dap_events_socket_write_unsafe(a_esocket,l_dns_client->dns_request.data, l_dns_client->dns_request.size ); + + dap_events_socket_uuid_t * l_es_uuid_ptr = DAP_NEW_Z(dap_events_socket_uuid_t); + if (!l_es_uuid_ptr) { + log_it(L_CRITICAL, "%s", c_error_memory_alloc); + return; + } + *l_es_uuid_ptr = a_esocket->uuid; + dap_timerfd_start_on_worker(a_worker, dap_config_get_item_uint64_default(g_config,"dns_client","request_timeout",10)*1000, + s_dns_client_esocket_timeout_callback,l_es_uuid_ptr); + +} + +/** + * @brief dap_chain_node_info_dns_request + * @param a_addr + * @param a_port + * @param a_name + * @param a_result + * @param a_callback_success + * @param a_callback_error + * @param a_callbacks_arg + */ +int dap_chain_node_info_dns_request(dap_worker_t *a_worker, struct in_addr a_addr, uint16_t a_port, char *a_name, + dap_dns_client_node_info_request_success_callback_t a_callback_success, + dap_dns_client_node_info_request_error_callback_t a_callback_error,void * a_callbacks_arg) +{ + char l_addr_str[INET_ADDRSTRLEN] = {}; + inet_ntop(AF_INET, &a_addr, l_addr_str, INET_ADDRSTRLEN); + + log_it(L_INFO, "DNS request for bootstrap nodelist %s : %d, net %s", l_addr_str, a_port, a_name); + + struct dns_client * l_dns_client = DAP_NEW_Z(struct dns_client); + if(!l_dns_client) + return -1; + l_dns_client->name = dap_strdup(a_name); + l_dns_client->callback_error = a_callback_error; + l_dns_client->callback_success = a_callback_success; + l_dns_client->callbacks_arg = a_callbacks_arg; + l_dns_client->addr = a_addr; + dap_dns_buf_init(&l_dns_client->dns_request, (char *)l_dns_client->buf); + dap_dns_buf_put_uint16(&l_dns_client->dns_request, rand() % 0xFFFF); // ID + + + dap_dns_message_flags_t l_flags = {}; + dap_dns_buf_put_uint16(&l_dns_client->dns_request, l_flags.val); + dap_dns_buf_put_uint16(&l_dns_client->dns_request, 1); // we have only 1 question + dap_dns_buf_put_uint16(&l_dns_client->dns_request, 0); + dap_dns_buf_put_uint16(&l_dns_client->dns_request, 0); + dap_dns_buf_put_uint16(&l_dns_client->dns_request, 0); + + size_t l_ptr = 0; + uint8_t *l_cur = l_dns_client->buf + l_dns_client->dns_request.size; + size_t l_name_len = strlen(a_name); + for (size_t i = 0; i <= l_name_len; i++) + if (a_name[i] == '.' || a_name[i] == 0) { + *l_cur++ = i - l_ptr; + while (l_ptr < i) + *l_cur++ = a_name[l_ptr++]; + l_ptr++; + } + *l_cur++='\0'; + l_dns_client->dns_request.size = l_cur - l_dns_client->buf; + dap_dns_buf_put_uint16(&l_dns_client->dns_request, DNS_RECORD_TYPE_A); + dap_dns_buf_put_uint16(&l_dns_client->dns_request, DNS_CLASS_TYPE_IN); + + dap_events_socket_callbacks_t l_esocket_callbacks={}; + + l_esocket_callbacks.worker_assign_callback = s_dns_client_esocket_worker_assign_callback; + l_esocket_callbacks.delete_callback = s_dns_client_esocket_delete_callback; // Delete client callback + l_esocket_callbacks.read_callback = s_dns_client_esocket_read_callback; // Read function + l_esocket_callbacks.error_callback = s_dns_client_esocket_error_callback; // Error processing function + + dap_events_socket_t * l_esocket = dap_events_socket_create(DESCRIPTOR_TYPE_SOCKET_UDP,&l_esocket_callbacks); + strcpy(l_esocket->remote_addr_str, l_addr_str); + l_esocket->remote_port = a_port; + l_esocket->_inheritor = l_dns_client; + + dap_events_socket_assign_on_worker_mt(l_esocket, a_worker); + return 0; +} + + +/** + * @brief dap_dns_buf_init Initialize DNS parser buffer + * @param buf DNS buffer structure + * @param msg DNS message + * @return none + */ +void dap_dns_buf_init(dap_dns_buf_t *buf, char *msg) +{ + buf->data = msg; + buf->size = 0; +} + +/** + * @brief dap_dns_buf_get_uint16 Get uint16 from network order + * @param buf DNS buffer structure + * @return uint16 in host order + */ +uint16_t dap_dns_buf_get_uint16(dap_dns_buf_t *buf) +{ + char c; + c = buf->data[buf->size++]; + return c << 8 | buf->data[buf->size++]; +} + +/** + * @brief dap_dns_buf_put_uint16 Put uint16 to network order + * @param buf DNS buffer structure + * @param val uint16 in host order + * @return none + */ +void dap_dns_buf_put_uint16(dap_dns_buf_t *buf, uint16_t val) +{ + buf->data[buf->size++] = val >> 8; + buf->data[buf->size++] = val; +} + +/** + * @brief dap_dns_buf_put_uint32 Put uint32 to network order + * @param buf DNS buffer structure + * @param val uint32 in host order + * @return none + */ +void dap_dns_buf_put_uint32(dap_dns_buf_t *buf, uint32_t val) +{ + dap_dns_buf_put_uint16(buf, val >> 16); + dap_dns_buf_put_uint16(buf, val); +} + +/** + * @brief dap_dns_buf_put_uint64 Put uint64 to network order + * @param buf DNS buffer structure + * @param val uint64 in host order + * @return none + */ +void dap_dns_buf_put_uint64(dap_dns_buf_t *buf, uint64_t val) +{ + dap_dns_buf_put_uint32(buf, val >> 32); + dap_dns_buf_put_uint32(buf, val); +} diff --git a/net/server/dns_server/dap_dns_server.c b/net/server/dns_server/dap_dns_server.c new file mode 100644 index 0000000000000000000000000000000000000000..4ca786ff6847b9f2b50e6d2c52920f1f0d6e32e8 --- /dev/null +++ b/net/server/dns_server/dap_dns_server.c @@ -0,0 +1,269 @@ +/* + * Authors: + * Roman Khlopkov <roman.khlopkov@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * DeM Labs Open source community https://gitlab.demlabs.net + * Copyright (c) 2017-2020 + * All rights reserved. + + This file is part of DAP (Distributed Applications Platform) the open source project + + DAP (Distributed Applications Platform) 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 <errno.h> +#include "dap_dns_client.h" +#include "dap_dns_server.h" +#include "dap_events_socket.h" +#include "dap_common.h" +#include "dap_string.h" +#include "dap_client.h" + +#define LOG_TAG "dap_chain_node_dns_server" +#define BUF_SIZE 1024 + +static dap_dns_server_t *s_dns_server; +static char s_root_alias[] = "dnsroot"; + + +/** + * @brief dap_dns_zone_register Register DNS zone and set callback to handle it + * @param zone Name of zone to register + * @param callback Callback to handle DNS zone + * @return 0 if success, else return error code + */ +int dap_dns_zone_register(char *zone, dap_dns_zone_callback_t callback) { + dap_dns_zone_hash_t *new_zone = NULL; + HASH_FIND_STR(s_dns_server->hash_table, zone, new_zone); + if (new_zone == NULL) { // zone is not present + DAP_NEW_Z_RET_VAL(new_zone, dap_dns_zone_hash_t, DNS_ERROR_FAILURE, NULL); + new_zone->zone = dap_strdup(zone); + HASH_ADD_KEYPTR(hh, s_dns_server->hash_table, new_zone->zone, strlen(new_zone->zone), new_zone); + } // if zone present, just reassign callback + new_zone->callback = callback; + return DNS_ERROR_NONE; +} + +/** + * @brief dap_dns_zone_unregister Unregister DNS zone + * @param zone Name of zone to unregister + * @return 0 if success, else return error code + */ +int dap_dns_zone_unregister(char *zone) { + dap_dns_zone_hash_t *asked_zone = NULL; + HASH_FIND_STR(s_dns_server->hash_table, zone, asked_zone); + if (asked_zone == NULL) { + return DNS_ERROR_NAME; + } + HASH_DEL(s_dns_server->hash_table, asked_zone); + DAP_DELETE(asked_zone->zone); + DAP_DELETE(asked_zone); + return DNS_ERROR_NONE; +} + +/** + * @brief dap_dns_zone_find Find callback to registered DNS zone + * @param hostname Name of host for which the zone callback being searched + * @return Callback for registered DNS zone, else return NULL + */ +dap_dns_zone_callback_t dap_dns_zone_find(char *hostname) { + dap_dns_zone_hash_t *asked_zone = NULL; + HASH_FIND_STR(s_dns_server->hash_table, hostname, asked_zone); + if (asked_zone == NULL) { + if (!strcmp(hostname, &s_root_alias[0])) { + return NULL; + } + char *zone_up = strchr(hostname, '.'); + if (zone_up++ == NULL) { + zone_up = &s_root_alias[0]; + } + return dap_dns_zone_find(zone_up); + } else { + return asked_zone->callback; + } + return NULL; +} + +/** + * @brief dap_dns_client_read Read and parse incoming DNS message, send reply to it + * @param client DAP client remote structure + * @param arg Unused + * @return none + */ +void dap_dns_client_read(dap_events_socket_t *a_es, UNUSED_ARG void *a_arg) { +// sanity check + dap_return_if_pass(!a_es || a_es->buf_in_size < DNS_HEADER_SIZE); // Bad request +// memory alloc + int block_len = DNS_HEADER_SIZE; + dap_dns_buf_t *dns_message = NULL; + dap_dns_buf_t *dns_reply = NULL; + DAP_NEW_Z_RET(dns_message, dap_dns_buf_t, NULL); + DAP_NEW_Z_RET(dns_reply, dap_dns_buf_t, dns_message); + DAP_NEW_Z_SIZE_RET(dns_message->data, char, a_es->buf_in_size + 1, dns_message, dns_reply); + DAP_NEW_Z_SIZE_RET(dns_reply->data, char, block_len, dns_message->data, dns_message, dns_reply); +// func work + dns_message->data[a_es->buf_in_size] = 0; + dap_events_socket_pop_from_buf_in(a_es, dns_message->data, a_es->buf_in_size); + dns_message->size = 0; + + // Parse incoming DNS message + dns_reply->size = 0; + uint16_t val = dap_dns_buf_get_uint16(dns_message); // ID + dap_dns_buf_put_uint16(dns_reply, val); + val = dap_dns_buf_get_uint16(dns_message); // Flags + dns_reply->size += sizeof(uint16_t); // Put flags later + dap_dns_message_flags_t msg_flags; + msg_flags.val = val; + dap_dns_message_flags_bits_t *flags = &msg_flags.flags; + if (flags->qr) { // It's not request + goto cleanup; + } + flags->rcode = DNS_ERROR_NONE; + flags->qr = 1; // Response bit set + if (flags->tc) { // Truncated messages not supported yet + flags->rcode = DNS_ERROR_NOT_SUPPORTED; + } + flags->ra = 0; // Recursion not supported yet + flags->aa = 1; // Authoritative answer + uint16_t qdcount = dap_dns_buf_get_uint16(dns_message); + dap_dns_buf_put_uint16(dns_reply, qdcount); + val = dap_dns_buf_get_uint16(dns_message); // AN count + if (val) { // No other sections should present + goto cleanup; + } + dap_dns_buf_put_uint16(dns_reply, 1); // 1 answer section + val = dap_dns_buf_get_uint16(dns_message); // NS count + if (val) { // No other sections should present + goto cleanup; + } + dap_dns_buf_put_uint16(dns_reply, val); + val = dap_dns_buf_get_uint16(dns_message); // AR count + if (val) { // No other sections should present + goto cleanup; + } + dap_dns_buf_put_uint16(dns_reply, 1); // 1 aditional section + int dot_count = 0; + dap_string_t *dns_hostname = dap_string_new(""); + for (int i = 0; i < qdcount; i++) { + block_len = strlen(&dns_message->data[dns_message->size]) + 1 + 2 * sizeof(uint16_t); + dns_reply->data = DAP_REALLOC(dns_reply->data, dns_reply->size + block_len); + memcpy(&dns_reply->data[dns_reply->size], &dns_message->data[dns_message->size], block_len); + dns_reply->size += block_len; + if (flags->rcode) + break; + while (dns_message->size < dns_reply->size - 2 * sizeof(uint16_t)) { + uint8_t len = dns_message->data[dns_message->size++]; + if (len > DNS_MAX_DOMAIN_NAME_LEN) { + flags->rcode = DNS_ERROR_NAME; + break; + } + if (!len) { + break; + } + if (dot_count) { + if (dot_count > 3) { // Max three dots allowed + flags->rcode = DNS_ERROR_NAME; + break; + } + dap_string_append(dns_hostname, "."); + } + dap_string_append_len(dns_hostname, &dns_message->data[dns_message->size], len); + dns_message->size += len; + dot_count++; + if (dns_hostname->len >= DNS_MAX_HOSTNAME_LEN) { + flags->rcode = DNS_ERROR_NAME; + break; + } + } + val = dap_dns_buf_get_uint16(dns_message); // DNS record type + if (val != DNS_RECORD_TYPE_A) { // Only host address ipv4 + flags->rcode = DNS_ERROR_NOT_SUPPORTED; + break; + } + val = dap_dns_buf_get_uint16(dns_message); // DNS class type + if (val != DNS_CLASS_TYPE_IN) { // Internet only + flags->rcode = DNS_ERROR_NOT_SUPPORTED; + break; + } + if (dns_message->size != dns_reply->size) { + log_it(L_ERROR, "DNS parser pointer unequal, mptr = %u, rptr = %u", dns_message->size, dns_reply->size); + } + } + // Find link info + dap_link_info_t *l_link_info = NULL; + if (flags->rcode == DNS_ERROR_NONE) { + dap_dns_zone_callback_t callback = dap_dns_zone_find(dns_hostname->str); + if (callback) { + l_link_info = callback(dns_hostname->str); + } + } + if (l_link_info) { + // Compose DNS answer + block_len = DNS_ANSWER_SIZE + sizeof(dap_link_info_t); + dns_reply->data = DAP_REALLOC(dns_reply->data, dns_reply->size + block_len); + val = 0xc000 | DNS_HEADER_SIZE; // Link to host name + dap_dns_buf_put_uint16(dns_reply, val); + val = DNS_RECORD_TYPE_TXT; + dap_dns_buf_put_uint16(dns_reply, val); + val = DNS_CLASS_TYPE_IN; + dap_dns_buf_put_uint16(dns_reply, val); + uint32_t ttl = DNS_TIME_TO_LIVE; + dap_dns_buf_put_uint32(dns_reply, ttl); + val = sizeof(dap_link_info_t); + memcpy(dns_reply->data + dns_reply->size, l_link_info, sizeof(dap_link_info_t)); + DAP_DELETE(l_link_info); + } else if (flags->rcode == DNS_ERROR_NONE) { + flags->rcode = DNS_ERROR_NAME; + } + if (flags->rcode) { + dns_reply->data[7] = 0; // No answer section + } + // Set reply flags + dns_reply->data[2] = msg_flags.val >> 8; + dns_reply->data[3] = msg_flags.val; + // Send DNS reply + dap_events_socket_write_unsafe(a_es, dns_reply->data, dns_reply->size); + dap_string_free(dns_hostname, true); +cleanup: + DAP_DEL_MULTY(dns_reply->data, dns_message->data, dns_reply, dns_message); + return; +} + +void dap_dns_server_start(const char *a_cfg_section) +{ + DAP_NEW_Z_RET(s_dns_server, dap_dns_server_t, NULL); + dap_events_socket_callbacks_t l_cb = { .read_callback = dap_dns_client_read }; + s_dns_server->instance = dap_server_new(a_cfg_section, NULL, &l_cb); + if (!s_dns_server->instance) { + log_it(L_ERROR, "Can't start DNS server"); + DAP_DELETE(s_dns_server); + return; + } + log_it(L_NOTICE,"DNS server started"); +} + +void dap_dns_server_stop() { + if(!s_dns_server) + return; + + dap_dns_zone_hash_t *current_zone, *tmp; + HASH_ITER(hh, s_dns_server->hash_table, current_zone, tmp) { + // Clang bug at this, current_zone should change at every loop cycle + HASH_DEL(s_dns_server->hash_table, current_zone); + DAP_DELETE(current_zone->zone); + DAP_DELETE(current_zone); + } + dap_server_delete(s_dns_server->instance); + DAP_DELETE(s_dns_server); +} diff --git a/net/server/dns_server/include/dap_dns_client.h b/net/server/dns_server/include/dap_dns_client.h new file mode 100644 index 0000000000000000000000000000000000000000..6381229fb9935a632e7da15327cee0b7aa53d36d --- /dev/null +++ b/net/server/dns_server/include/dap_dns_client.h @@ -0,0 +1,53 @@ +/* + * Authors: + * Roman Khlopkov <roman.khlopkov@demlabs.net> + * Dmitriy Gerasimov <dmitriy.gerasmiov@demlabs.net> + * DeM Labs Ltd https://demlabs.net + * DeM Labs Open source community https://gitlab.demlabs.net + * Copyright (c) 2021 + * All rights reserved. + + This file is part of DapChain SDK the open source project + + DapChain 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. + + DapChain 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 DapChain SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include "dap_worker.h" +#include "dap_net.h" + +#define DNS_LISTEN_PORT 53 // UDP +#define DNS_LISTEN_PORT_STR "53" // UDP +#define DNS_REQUEST_SIZE 16 + +typedef struct _dap_dns_buf_t { + char *data; + uint32_t size; +} dap_dns_buf_t; + +// node info request callbacks +typedef void (*dap_dns_client_node_info_request_success_callback_t) (dap_worker_t *a_worker, dap_net_links_t *a_result, void *a_arg); +typedef void (*dap_dns_client_node_info_request_error_callback_t) (dap_worker_t *a_worker, void *a_arg, int a_errno); + +int dap_chain_node_info_dns_request(dap_worker_t *a_worker, struct in_addr a_addr, uint16_t a_port, char *a_name, + dap_dns_client_node_info_request_success_callback_t a_callback_success, + dap_dns_client_node_info_request_error_callback_t a_callback_error, void * a_callback_arg); + +void dap_dns_buf_init(dap_dns_buf_t *buf, char *msg); +void dap_dns_buf_put_uint64(dap_dns_buf_t *buf, uint64_t val); +void dap_dns_buf_put_uint32(dap_dns_buf_t *buf, uint32_t val); +void dap_dns_buf_put_uint16(dap_dns_buf_t *buf, uint16_t val); +uint16_t dap_dns_buf_get_uint16(dap_dns_buf_t *buf); diff --git a/net/server/dns_server/include/dap_dns_server.h b/net/server/dns_server/include/dap_dns_server.h new file mode 100644 index 0000000000000000000000000000000000000000..2fb7ea1ebf4eb1e32db1f5629ac3ad5b50ce1883 --- /dev/null +++ b/net/server/dns_server/include/dap_dns_server.h @@ -0,0 +1,124 @@ +/* + * Authors: + * Roman Khlopkov <roman.khlopkov@demlabs.net> + * Dmitriy Gerasimov <dmitriy.gerasmiov@demlabs.net> + * DeM Labs Ltd https://demlabs.net + * DeM Labs Open source community https://gitlab.demlabs.net + * Copyright (c) 2021 + * All rights reserved. + + This file is part of DapChain SDK the open source project + + DapChain 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. + + DapChain 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 DapChain SDK based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#ifdef _WIN32 +#include <pthread.h> +#endif +#include "dap_server.h" +#include "uthash.h" +#include "dap_client.h" + +#define DNS_TIME_TO_LIVE 600 // Seconds +#define DNS_HEADER_SIZE 12 +#define DNS_ANSWER_SIZE 10 +#define DNS_MAX_HOSTNAME_LEN 255 +#define DNS_MAX_DOMAIN_NAME_LEN 63 + +typedef enum _dap_dns_query_type_t { + DNS_QUERY_TYPE_STANDARD, + DNS_QUERY_TYPE_INVERSE, + DNS_QUERY_TYPE_STATUS +} dap_dns_query_type_t; + +typedef enum _dap_dns_error_t { + DNS_ERROR_NONE, // No error + DNS_ERROR_FORMAT, // DNS message parsing error + DNS_ERROR_FAILURE, // Internal server error + DNS_ERROR_NAME, // Only for authoritative servers. Name does not exist + DNS_ERROR_NOT_SUPPORTED,// This kind of query not implemented + DNS_ERROR_REFUSED // Operation refused +} dap_dns_error_t; + +typedef enum _dap_dns_record_type_t { + DNS_RECORD_TYPE_A = 1, // Host address + DNS_RECORD_TYPE_NS, // Authoritative name server + DNS_RECORD_TYPE_MD, // Mail destination (obsolete, use MX) + DNS_RECORD_TYPE_MF, // Mail forwarder (obsolete, use MX) + DNS_RECORD_TYPE_CNAME, // Canonical name of alias + DNS_RECORD_TYPE_SOA, // Marks a start of a zone of authority + DNS_RECORD_TYPE_MB, // Mailbox domain name (experimental) + DNS_RECORD_TYPE_MG, // Mail group member (experimental) + DNS_RECORD_TYPE_MR, // Mail rename domain name (experimental) + DNS_RECORD_TYPE_NULL, // NULL resource record (experimental) + DNS_RECORD_TYPE_WKS, // Well known services description + DNS_RECORD_TYPE_PTR, // Domain name pointer + DNS_RECORD_TYPE_HINFO, // Host information + DNS_RECORD_TYPE_MINFO, // Mail box or list information + DNS_RECORD_TYPE_MX, // Mail exchange + DNS_RECORD_TYPE_TXT, // Text strings + DNS_RECORD_TYPE_RP, // Responsible person + DNS_RECORD_TYPE_AXFR = 252, // A request for a transfer of an entire zone - QTYPE only + DNS_RECORD_TYPE_MAILB, // A request for mailbox-related records (MB, MG or MR) - QTYPE only + DNS_RECORD_TYPE_MAILA, // A request for mail agent RRs (obsolete - see MX) - QTYPE only + DNS_RECORD_TYPE_ANY // A request for all records - QTYPE only +} dap_dns_record_type_t; + +typedef enum _dap_dns_class_type_t { + DNS_CLASS_TYPE_IN = 1, // Internet + DNS_CLASS_TYPE_CS, // CSNET (obsolete) + DNS_CLASS_TYPE_CH, // CHAOS + DNS_CLASS_TYPE_HS, // Hesiod [Dyer 87] + DNS_CLASS_TYPE_ANY = 255 // Any class +} dap_dns_class_type_t; + +typedef struct _dap_dns_message_flags_bits_t { + int rcode : 4; // response code, answer only: 0 - no error, 1 - format error, 2 - server failure, 3 - name error, 4 - not supported, 5 - refused + int z : 3; // reserved, must be zero + int ra : 1; // 1 - recursion available (answer only) + int rd : 1; // 1 - recursion desired (query set, copied to answer) + int tc : 1; // 1 - message truncated + int aa : 1; // 1 - authoritative answer (answer only) + int opcode : 4; // type of query, copied to answer: 0 - standard, 1 - inverse, 2 - status, 3-15 - reserved + int qr : 1; // 0 - query, 1 - response +} dap_dns_message_flags_bits_t; + + +typedef union _dap_dns_message_flags_t { + dap_dns_message_flags_bits_t flags; + int val; +} dap_dns_message_flags_t; + +typedef dap_link_info_t *(*dap_dns_zone_callback_t) (const char *zonename); // Callback for DNS zone operations + +typedef struct _dap_dns_zone_hash_t { + char *zone; + dap_dns_zone_callback_t callback; + UT_hash_handle hh; +} dap_dns_zone_hash_t; + +typedef struct _dap_dns_server_t { + dap_server_t *instance; + dap_dns_zone_hash_t *hash_table; +} dap_dns_server_t; + + + +void dap_dns_server_start(const char* a_cfg_section); +void dap_dns_server_stop(); +int dap_dns_zone_register(char *zone, dap_dns_zone_callback_t callback); +int dap_dns_zone_unregister(char *zone); +