diff --git a/dap_chain_net_srv_vpn.c b/dap_chain_net_srv_vpn.c index ace1cc0b4002789bad79245237abd17e3d0f500e..de091e0eb1945a911984320e160c0d8f2bef4d58 100755 --- a/dap_chain_net_srv_vpn.c +++ b/dap_chain_net_srv_vpn.c @@ -8,31 +8,1073 @@ 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 (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. + 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/>. -*/ + 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 "dap_common.h" + //#include "dap_stream_ch_vpn.h" + #include "dap_chain_net_srv_vpn.h" + + #define LOG_TAG "chain_net_srv_vpn" + + int dap_chain_net_srv_vpn_init() + { + int l_res = dap_stream_ch_vpn_init("addr", "255.255.0.0"); + return 0; + } + + void dap_chain_net_srv_vpn_deinit() + { + dap_stream_ch_vpn_deinit(); + }*/ + +#include <sys/select.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/epoll.h> +#include <sys/un.h> +#include <sys/ioctl.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <pthread.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <errno.h> +#include <signal.h> + +#include <linux/if.h> +#include <linux/if_tun.h> + +#include "uthash.h" +#include "utlist.h" #include "dap_common.h" +#include "dap_strfuncs.h" +#include "dap_config.h" + +#include "dap_client_remote.h" +#include "dap_http_client.h" + +#include "dap_stream.h" +#include "dap_stream_ch.h" +#include "dap_stream_ch_proc.h" +#include "dap_stream_ch_pkt.h" + +#include "dap_chain_utxo.h"//datum_tx_cache.h #include "dap_chain_net_srv_vpn.h" -#define LOG_TAG "chain_net_srv_vpn" +#define LOG_TAG "srv_vpn" + +#define VPN_PACKET_OP_CODE_CONNECTED 0x000000a9 +#define VPN_PACKET_OP_CODE_CONNECT 0x000000aa +#define VPN_PACKET_OP_CODE_DISCONNECT 0x000000ab +#define VPN_PACKET_OP_CODE_SEND 0x000000ac +#define VPN_PACKET_OP_CODE_RECV 0x000000ad +#define VPN_PACKET_OP_CODE_PROBLEM 0x000000ae + +#define VPN_PROBLEM_CODE_NO_FREE_ADDR 0x00000001 +#define VPN_PROBLEM_CODE_TUNNEL_DOWN 0x00000002 +#define VPN_PROBLEM_CODE_PACKET_LOST 0x00000003 + +#define VPN_PACKET_OP_CODE_VPN_METADATA 0x000000b0 +#define VPN_PACKET_OP_CODE_VPN_RESERVED 0x000000b1 +#define VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST 0x000000b2 +#define VPN_PACKET_OP_CODE_VPN_ADDR_REPLY 0x000000b3 + +#define VPN_PACKET_OP_CODE_VPN_SEND 0x000000bc +#define VPN_PACKET_OP_CODE_VPN_RECV 0x000000bd + +#define SF_MAX_EVENTS 256 -int dap_chain_net_srv_vpn_init() +typedef struct ch_vpn_pkt { + struct { + int sock_id; // Client's socket id + uint32_t op_code; // Operation code + union { + struct { // L4 connect operation + uint32_t addr_size; + uint16_t port; + uint16_t padding; + } op_connect; + struct { // For data transmission, usualy for I/O functions + uint32_t data_size; + uint32_t padding; + } op_data; + struct { // We have a problem and we know that! + uint32_t code; // I hope we'll have no more than 4B+ problems, not I?? + uint32_t padding_padding_padding_damned_padding_nobody_nowhere_uses_this_fild_but_if_wil_change_me_pls_with_an_auto_rename; + } op_problem; + struct { + uint32_t padding1; + uint32_t padding2; + } raw; // Raw access to OP bytes + }; + }__attribute__((packed)) header; + uint8_t data[]; // Binary data nested by packet +}__attribute__((packed)) ch_vpn_pkt_t; + +/** + * @struct ch_vpn_socket_proxy + * @brief Internal data storage for single socket proxy functions. Usualy helpfull for\ + * port forwarding or for protecting single application's connection + * + **/ +typedef struct ch_vpn_socket_proxy { + int id; + int sock; + struct in_addr client_addr; // Used in raw L3 connections + pthread_mutex_t mutex; + dap_stream_ch_t * ch; + + bool signal_to_delete; + ch_vpn_pkt_t * pkt_out[100]; + size_t pkt_out_size; + + uint64_t bytes_sent; + uint64_t bytes_recieved; + + time_t time_created; + time_t time_lastused; + + UT_hash_handle hh; + UT_hash_handle hh2; + UT_hash_handle hh_sock; +} ch_vpn_socket_proxy_t; + +/** + * @struct dap_stream_ch_vpn + * @brief Object that creates for every remote channel client + * + * + **/ +typedef struct dap_stream_ch_vpn { - return 0; + pthread_mutex_t mutex; + ch_vpn_socket_proxy_t * socks; + int raw_l3_sock; + dap_chain_net_srv_uid_t srv_uid; // Unique ID for service. +} dap_stream_ch_vpn_t; + +typedef struct dap_stream_ch_vpn_remote_single { // + in_addr_t addr; +// pthread_mutex_t mutex; + dap_stream_ch_t * ch; + + uint64_t bytes_sent; + uint64_t bytes_recieved; + + UT_hash_handle hh; +} dap_stream_ch_vpn_remote_single_t; + +typedef struct vpn_local_network { + struct in_addr client_addr_last; + struct in_addr client_addr_mask; + struct in_addr client_addr_host; + struct in_addr client_addr; + int tun_ctl_fd; + int tun_fd; + struct ifreq ifr; + dap_stream_ch_vpn_remote_single_t * clients; // Remote clients identified by destination address + + ch_vpn_pkt_t * pkt_out[400]; + size_t pkt_out_size; + size_t pkt_out_rindex; + size_t pkt_out_windex; + pthread_mutex_t pkt_out_mutex; + + pthread_mutex_t clients_mutex; +} vpn_local_network_t; + +typedef struct list_addr_element { + struct in_addr addr; + struct list_addr_element *next; +} list_addr_element; + +static list_addr_element *list_addr_head = NULL; + +static ch_vpn_socket_proxy_t * sf_socks = NULL; +static ch_vpn_socket_proxy_t * sf_socks_client = NULL; +static pthread_mutex_t sf_socks_mutex; +static pthread_cond_t sf_socks_cond; +static int sf_socks_epoll_fd; +static pthread_t srv_sf_socks_pid; +static pthread_t srv_sf_socks_raw_pid; + +vpn_local_network_t *raw_server; + +#define CH_SF(a) ((dap_stream_ch_vpn_t *) ((a)->internal) ) + +void *srv_ch_sf_thread(void * arg); +void *srv_ch_sf_thread_raw(void *arg); +void srv_ch_sf_tun_create(); +void srv_ch_sf_tun_destroy(); + +void srv_ch_sf_new(dap_stream_ch_t* ch, void* arg); +void srv_ch_sf_delete(dap_stream_ch_t* ch, void* arg); +void srv_ch_sf_packet_in(dap_stream_ch_t* ch, void* arg); +void srv_ch_sf_packet_out(dap_stream_ch_t* ch, void* arg); + +//static int srv_ch_sf_raw_write(uint8_t op_code, const void * data, size_t data_size); +//void srv_stream_sf_disconnect(ch_vpn_socket_proxy_t * sf_sock); + +static const char *s_srv_vpn_addr, *s_srv_vpn_mask; + +/** + * @brief dap_stream_ch_vpn_init Init actions for VPN stream channel + * @param vpn_addr Zero if only client mode. Address if the node shares its local VPN + * @param vpn_mask Zero if only client mode. Mask if the node shares its local VPN + * @return 0 if everything is okay, lesser then zero if errors + */ +int dap_chain_net_srv_vpn_init(dap_config_t * g_config) +{ + const char *s_addr = dap_config_get_item_str(g_config, "vpn", "network_address"); + const char *s_mask = dap_config_get_item_str(g_config, "vpn", "network_mask"); + if(s_addr && s_mask) { + s_srv_vpn_addr = strdup(s_addr); + s_srv_vpn_mask = strdup(s_mask); + + raw_server = calloc(1, sizeof(vpn_local_network_t)); + pthread_mutex_init(&raw_server->clients_mutex, NULL); + pthread_mutex_init(&raw_server->pkt_out_mutex, NULL); + pthread_mutex_init(&sf_socks_mutex, NULL); + pthread_cond_init(&sf_socks_cond, NULL); + pthread_create(&srv_sf_socks_raw_pid, NULL, srv_ch_sf_thread_raw, NULL); + pthread_create(&srv_sf_socks_pid, NULL, srv_ch_sf_thread, NULL); + dap_stream_ch_proc_add(SERVICE_CHANNEL_ID, srv_ch_sf_new, srv_ch_sf_delete, srv_ch_sf_packet_in, + srv_ch_sf_packet_out); + return 0; + } + return -1; } +/** + * @brief ch_sf_deinit + */ void dap_chain_net_srv_vpn_deinit() { + pthread_mutex_destroy(&sf_socks_mutex); + pthread_cond_destroy(&sf_socks_cond); + free((char*) s_srv_vpn_addr); + free((char*) s_srv_vpn_mask); + if(raw_server) + free(raw_server); +} +void srv_ch_sf_tun_create() +{ + inet_aton(s_srv_vpn_addr, &raw_server->client_addr); + inet_aton(s_srv_vpn_mask, &raw_server->client_addr_mask); + raw_server->client_addr_host.s_addr = (raw_server->client_addr.s_addr | 0x01000000); // grow up some shit here! + raw_server->client_addr_last.s_addr = raw_server->client_addr_host.s_addr; + + if((raw_server->tun_ctl_fd = open("/dev/net/tun", O_RDWR)) < 0) { + log_it(L_ERROR, "Opening /dev/net/tun error: '%s'", strerror(errno)); + } else { + int err; + memset(&raw_server->ifr, 0, sizeof(raw_server->ifr)); + raw_server->ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + if((err = ioctl(raw_server->tun_ctl_fd, TUNSETIFF, (void *) &raw_server->ifr)) < 0) { + log_it(L_CRITICAL, "ioctl(TUNSETIFF) error: '%s' ", strerror(errno)); + close(raw_server->tun_ctl_fd); + raw_server->tun_ctl_fd = -1; + } else { + char buf[256]; + log_it(L_NOTICE, "Bringed up %s virtual network interface (%s/%s)", raw_server->ifr.ifr_name, + inet_ntoa(raw_server->client_addr_host), s_srv_vpn_mask); + raw_server->tun_fd = raw_server->tun_ctl_fd; // Looks yes, its so + snprintf(buf, sizeof(buf), "ip link set %s up", raw_server->ifr.ifr_name); + int res = system(buf); + snprintf(buf, sizeof(buf), "ip addr add %s/%s dev %s ", inet_ntoa(raw_server->client_addr_host), + s_srv_vpn_mask, + raw_server->ifr.ifr_name); + res = system(buf); + res = 0; + } + } + +} + +void srv_ch_sf_tun_destroy() +{ + close(raw_server->tun_fd); + raw_server->tun_fd = -1; } + +/** + * @brief stream_sf_new Callback to constructor of object of Ch + * @param ch + * @param arg + */ +void srv_ch_sf_new(dap_stream_ch_t* ch, void* arg) +{ + ch->internal = calloc(1, sizeof(dap_stream_ch_vpn_t)); + dap_stream_ch_vpn_t * sf = CH_SF(ch); + pthread_mutex_init(&sf->mutex, NULL); + sf->raw_l3_sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); + // + if(ch->stream->service_key) { + + char **l_str = dap_strsplit(ch->stream->service_key, ";", -1); + char *l_addr_base58 = NULL; + char *l_sign_txt = NULL; + if(dap_str_countv(l_str) == 2) { + l_addr_base58 = l_str[0]; + l_sign_txt = l_str[1]; + } + l_addr_base58 = + "EXh66KVCxChbKJLxXTwipYMooUpoGvpwpkcjpmGLbubwzqR2vVsH9HEgT2LcU2hDs2BTFkaNC8itE8nuCWxskVtRJG4iaubBDcRWAt2awtCVHAULffQGrwe8ocRCzS"; + l_sign_txt = "123"; + uint8_t *l_sign = l_sign_txt; + size_t l_sign_size = 0; + dap_chain_net_srv_abstract_t *l_cond = NULL; + uint64_t l_value = dap_chain_net_srv_client_auth(l_addr_base58, l_sign, l_sign_size, &l_cond); + + // add service + { + dap_chain_net_srv_t l_srv; + memcpy(&l_srv.srv_common, l_cond, sizeof(dap_chain_net_srv_abstract_t)); + dap_chain_net_srv_gen_uid(&l_srv.uid); + dap_chain_net_srv_add(&l_srv); + memcpy(&sf->srv_uid.raw, &l_srv.uid, sizeof(dap_chain_net_srv_uid_t)); // Unique ID for service. + } + + } +} + +/** + * @brief stream_sf_delete + * @param ch + * @param arg + */ +void srv_ch_sf_delete(dap_stream_ch_t* ch, void* arg) +{ + log_it(L_DEBUG, "ch_sf_delete() for %s", ch->stream->conn->hostaddr); + ch_vpn_socket_proxy_t * cur, *tmp; + dap_stream_ch_vpn_remote_single_t * raw_client = 0; + // in_addr_t raw_client_addr = CH_SF(ch)->tun_client_addr.s_addr; + in_addr_t raw_client_addr = ch->stream->session->tun_client_addr.s_addr; + + if(raw_client_addr) { + log_it(L_DEBUG, "ch_sf_delete() %s searching in hash table", + inet_ntoa(ch->stream->session->tun_client_addr)); + list_addr_element *el = (list_addr_element*) malloc(sizeof(list_addr_element)); + //el->addr = CH_SF(ch)->tun_client_addr; + el->addr = ch->stream->session->tun_client_addr; + LL_APPEND(list_addr_head, el); + // LL_FOREACH(list_addr_head,el) log_it(L_INFO,"addr = %s", inet_ntoa(el->addr)); + + pthread_mutex_lock(&raw_server->clients_mutex); + + HASH_FIND_INT(raw_server->clients, &raw_client_addr, raw_client); + if(raw_client) { + HASH_DEL(raw_server->clients, raw_client); + log_it(L_DEBUG, "ch_sf_delete() %s removed from hash table", + inet_ntoa(ch->stream->session->tun_client_addr)); + free(raw_client); + } else + log_it(L_DEBUG, "ch_sf_delete() %s is not present in raw sockets hash table", + inet_ntoa(ch->stream->session->tun_client_addr)); + + pthread_mutex_unlock(&raw_server->clients_mutex); + } + HASH_ITER(hh, CH_SF(ch)->socks , cur, tmp) + { + log_it(L_DEBUG, "delete socket: %i", cur->sock); + HASH_DEL(CH_SF(ch)->socks, cur); + if(cur) + free(cur); + } + pthread_mutex_unlock(&( CH_SF(ch)->mutex)); + if(CH_SF(ch)->raw_l3_sock) + close(CH_SF(ch)->raw_l3_sock); +} + +static void stream_sf_socket_delete(ch_vpn_socket_proxy_t * sf) +{ + close(sf->sock); + pthread_mutex_destroy(&(sf->mutex)); + if(sf) + free(sf); +} + +static void stream_sf_socket_ready_to_write(dap_stream_ch_t * ch, bool is_ready) +{ + pthread_mutex_lock(&ch->mutex); + ch->ready_to_write = is_ready; + if(is_ready) + ch->stream->conn_http->state_write = DAP_HTTP_CLIENT_STATE_DATA; + dap_client_remote_ready_to_write(ch->stream->conn, is_ready); + pthread_mutex_unlock(&ch->mutex); + +} + +static ch_vpn_pkt_t* srv_ch_sf_raw_read() +{ + ch_vpn_pkt_t*ret = NULL; + pthread_mutex_lock(&raw_server->pkt_out_mutex); + if(raw_server->pkt_out_rindex == (sizeof(raw_server->pkt_out) / sizeof(raw_server->pkt_out[0]))) { + raw_server->pkt_out_rindex = 0; // ring the buffer! + } + if((raw_server->pkt_out_rindex != raw_server->pkt_out_windex) || (raw_server->pkt_out_size == 0)) { + ret = raw_server->pkt_out[raw_server->pkt_out_rindex]; + raw_server->pkt_out_rindex++; + raw_server->pkt_out_size--; + } else + log_it(L_WARNING, "Packet drop on raw_read() operation, ring buffer is full"); + pthread_mutex_unlock(&raw_server->pkt_out_mutex); + return ret; +} + +/* + static int srv_ch_sf_raw_write(uint8_t op_code, const void * data, size_t data_size) + { + pthread_mutex_lock(&raw_server->pkt_out_mutex); + if(raw_server->pkt_out_windex == (sizeof(raw_server->pkt_out) / sizeof(raw_server->pkt_out[0]))) + raw_server->pkt_out_windex = 0; // ring the buffer! + if((raw_server->pkt_out_windex < raw_server->pkt_out_rindex) || (raw_server->pkt_out_size == 0)) { + ch_vpn_pkt_t * pkt = (ch_vpn_pkt_t *) calloc(1, data_size + sizeof(pkt->header)); + pkt->header.op_code = op_code; + pkt->header.sock_id = raw_server->tun_fd; + if(data_size > 0) { + pkt->header.op_data.data_size = data_size; + memcpy(pkt->data, data, data_size); + } + + raw_server->pkt_out[raw_server->pkt_out_windex] = pkt; + raw_server->pkt_out_windex++; + raw_server->pkt_out_size++; + pthread_mutex_unlock(&raw_server->pkt_out_mutex); + send_select_break(); + return raw_server->pkt_out_windex; + } else { + pthread_mutex_unlock(&raw_server->pkt_out_mutex); + log_it(L_WARNING, "Raw socket buffer overflow"); + return -1; + } + } + + static int srv_stream_sf_socket_write(ch_vpn_socket_proxy_t * sf, uint8_t op_code, const void * data, size_t data_size) + { + if(sf->pkt_out_size < (sizeof(sf->pkt_out) / sizeof(sf->pkt_out[0]))) { + ch_vpn_pkt_t * pkt = (ch_vpn_pkt_t *) calloc(1, data_size + sizeof(pkt->header)); + pkt->header.op_code = op_code; + pkt->header.sock_id = sf->id; + + switch (op_code) { + case VPN_PACKET_OP_CODE_RECV: { + pkt->header.op_data.data_size = data_size; + memcpy(pkt->data, data, data_size); + } + break; + default: { + log_it(L_ERROR, "Unprocessed opcode %u for write to sf socket", op_code); + free(pkt); + return -2; + } + } + sf->pkt_out[sf->pkt_out_size] = pkt; + sf->pkt_out_size++; + return sf->pkt_out_size; + } else + return -1; + }*/ + +/** + * @brief stream_sf_packet_in + * @param ch + * @param arg + */ +void srv_ch_sf_packet_in(dap_stream_ch_t* ch, void* arg) +{ + dap_stream_ch_pkt_t * pkt = (dap_stream_ch_pkt_t *) arg; + // log_it(L_DEBUG,"stream_sf_packet_in: channel packet hdr size %lu ( last bytes 0x%02x 0x%02x 0x%02x 0x%02x ) ", pkt->hdr.size, + // *((uint8_t *)pkt->data + pkt->hdr.size-4),*((uint8_t *)pkt->data + pkt->hdr.size-3) + // ,*((uint8_t *)pkt->data + pkt->hdr.size-2),*((uint8_t *)pkt->data + pkt->hdr.size-1) + // ); + + static bool client_connected = false; + ch_vpn_pkt_t * sf_pkt = (ch_vpn_pkt_t *) pkt->data; + + int remote_sock_id = sf_pkt->header.sock_id; + + //log_it(L_DEBUG, "Got SF packet with id %d op_code 0x%02x", remote_sock_id, sf_pkt->header.op_code); + if(sf_pkt->header.op_code >= 0xb0) { // Raw packets + switch (sf_pkt->header.op_code) { + case VPN_PACKET_OP_CODE_VPN_ADDR_REQUEST: { // Client request after L3 connection the new IP address + log_it(L_DEBUG, "Got SF packet with id %d op_code 0x%02x", remote_sock_id, sf_pkt->header.op_code); + struct in_addr n_addr = { 0 }; + + if(n_addr.s_addr == 0) { // If the addres still in the network + + pthread_mutex_lock(&raw_server->clients_mutex); + + int count_free_addr = -1; + list_addr_element *el; + LL_COUNT(list_addr_head, el, count_free_addr); + + dap_stream_ch_vpn_remote_single_t * n_client = (dap_stream_ch_vpn_remote_single_t*) calloc(1, + sizeof(dap_stream_ch_vpn_remote_single_t)); + n_client->ch = ch; + + if(count_free_addr > 0) + { + n_addr.s_addr = list_addr_head->addr.s_addr; + LL_DELETE(list_addr_head, list_addr_head); + } + else + { + n_addr.s_addr = ntohl(raw_server->client_addr_last.s_addr); + n_addr.s_addr++; + n_addr.s_addr = ntohl(n_addr.s_addr); + } + + n_client->addr = n_addr.s_addr; + raw_server->client_addr_last.s_addr = n_addr.s_addr; + ch->stream->session->tun_client_addr.s_addr = n_addr.s_addr; + HASH_ADD_INT(raw_server->clients, addr, n_client); + pthread_mutex_unlock(&raw_server->clients_mutex); + + log_it(L_NOTICE, "VPN client address %s leased", inet_ntoa(n_addr)); + log_it(L_INFO, "\tgateway %s", inet_ntoa(raw_server->client_addr_host)); + log_it(L_INFO, "\tmask %s", inet_ntoa(raw_server->client_addr_mask)); + log_it(L_INFO, "\taddr %s", inet_ntoa(raw_server->client_addr)); + log_it(L_INFO, "\tlast_addr %s", inet_ntoa(raw_server->client_addr_last)); + + ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, + sizeof(pkt_out->header) + sizeof(n_addr) + sizeof(raw_server->client_addr_host)); + pkt_out->header.sock_id = raw_server->tun_fd; + pkt_out->header.op_code = VPN_PACKET_OP_CODE_VPN_ADDR_REPLY; + pkt_out->header.op_data.data_size = sizeof(n_addr) + sizeof(raw_server->client_addr_host); + memcpy(pkt_out->data, &n_addr, sizeof(n_addr)); + memcpy(pkt_out->data + sizeof(n_addr), &raw_server->client_addr_host, + sizeof(raw_server->client_addr_host)); + dap_stream_ch_pkt_write(ch, DATA_CHANNEL_ID, pkt_out, + pkt_out->header.op_data.data_size + sizeof(pkt_out->header)); + stream_sf_socket_ready_to_write(ch, true); + + //ch_sf_raw_write(n_addr.s_addr,STREAM_SF_PACKET_OP_CODE_RAW_L3_ADDR_REPLY,&n_addr,sizeof(n_addr)); + } else { // All the network is filled with clients, can't lease a new address + log_it(L_WARNING, "All the network is filled with clients, can't lease a new address"); + ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header)); + pkt_out->header.sock_id = raw_server->tun_fd; + pkt_out->header.op_code = VPN_PACKET_OP_CODE_PROBLEM; + pkt_out->header.op_problem.code = VPN_PROBLEM_CODE_NO_FREE_ADDR; + dap_stream_ch_pkt_write(ch, DATA_CHANNEL_ID, pkt_out, + pkt_out->header.op_data.data_size + sizeof(pkt_out->header)); + stream_sf_socket_ready_to_write(ch, true); + } + } + break; + case VPN_PACKET_OP_CODE_VPN_SEND: { + struct in_addr in_saddr, in_daddr; + in_saddr.s_addr = ((struct iphdr*) sf_pkt->data)->saddr; + in_daddr.s_addr = ((struct iphdr*) sf_pkt->data)->daddr; + + char str_daddr[42], str_saddr[42]; + strncpy(str_saddr, inet_ntoa(in_saddr), sizeof(str_saddr)); + strncpy(str_daddr, inet_ntoa(in_daddr), sizeof(str_daddr)); + int ret; + //if( ch_sf_raw_write(STREAM_SF_PACKET_OP_CODE_RAW_SEND, sf_pkt->data, sf_pkt->op_data.data_size)<0){ + struct sockaddr_in sin = { 0 }; + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = in_daddr.s_addr; + + //if((ret=sendto(CH_SF(ch)->raw_l3_sock , sf_pkt->data,sf_pkt->header.op_data.data_size,0,(struct sockaddr *) &sin, sizeof (sin)))<0){ + if((ret = write(raw_server->tun_fd, sf_pkt->data, sf_pkt->header.op_data.data_size)) < 0) { + log_it(L_ERROR, "write() returned error %d : '%s'", ret, strerror(errno)); + //log_it(L_ERROR,"raw socket ring buffer overflowed"); + ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header)); + pkt_out->header.op_code = VPN_PACKET_OP_CODE_PROBLEM; + pkt_out->header.op_problem.code = VPN_PROBLEM_CODE_PACKET_LOST; + pkt_out->header.sock_id = raw_server->tun_fd; + dap_stream_ch_pkt_write(ch, DATA_CHANNEL_ID, pkt_out, + pkt_out->header.op_data.data_size + sizeof(pkt_out->header)); + stream_sf_socket_ready_to_write(ch, true); + } else { + log_it(L_DEBUG, "Raw IP packet daddr:%s saddr:%s %u from %d bytes sent to tun/tap interface", + str_saddr, str_daddr, sf_pkt->header.op_data.data_size, ret); + //log_it(L_DEBUG, "Raw IP sent %u bytes ", ret); + } + //} + } + break; + default: + log_it(L_WARNING, "Can't process SF type 0x%02x", sf_pkt->header.op_code); + } + } else { // All except CONNECT + ch_vpn_socket_proxy_t * sf_sock = NULL; + if(sf_pkt->header.op_code != VPN_PACKET_OP_CODE_CONNECT) { + pthread_mutex_lock(&( CH_SF(ch)->mutex)); + // log_it(L_DEBUG,"Looking in hash table with %d",remote_sock_id); + HASH_FIND_INT((CH_SF(ch)->socks), &remote_sock_id, sf_sock); + pthread_mutex_unlock(&( CH_SF(ch)->mutex)); + if(sf_sock != NULL) { + pthread_mutex_lock(&sf_sock->mutex); // Unlock it in your case as soon as possible to reduce lock time + sf_sock->time_lastused = time(NULL); + switch (sf_pkt->header.op_code) { + case VPN_PACKET_OP_CODE_SEND: { + if(client_connected == false) + { + log_it(L_WARNING, "Drop Packet! User not connected!"); // Client need send + pthread_mutex_unlock(&sf_socks_mutex); + break; + } + int ret; + if((ret = send(sf_sock->sock, sf_pkt->data, sf_pkt->header.op_data.data_size, 0)) < 0) { + log_it(L_INFO, "Disconnected from the remote host"); + pthread_mutex_unlock(&sf_sock->mutex); + pthread_mutex_lock(&( CH_SF(ch)->mutex)); + HASH_DEL(CH_SF(ch)->socks, sf_sock); + pthread_mutex_unlock(&( CH_SF(ch)->mutex)); + + pthread_mutex_lock(&sf_socks_mutex); + HASH_DELETE(hh2, sf_socks, sf_sock); + HASH_DELETE(hh_sock, sf_socks_client, sf_sock); + + struct epoll_event ev; + ev.data.fd = sf_sock->sock; + ev.events = EPOLLIN; + if(epoll_ctl(sf_socks_epoll_fd, EPOLL_CTL_DEL, sf_sock->sock, &ev) < 0) { + log_it(L_ERROR, "Can't remove sock_id %d from the epoll fd", remote_sock_id); + //stream_ch_pkt_write_f(ch,'i',"sock_id=%d op_code=0x%02x result=-2",sf_pkt->sock_id, sf_pkt->op_code); + } else { + log_it(L_NOTICE, "Removed sock_id %d from the the epoll fd", remote_sock_id); + //stream_ch_pkt_write_f(ch,'i',"sock_id=%d op_code=0x%02x result=0",sf_pkt->sock_id, sf_pkt->op_code); + } + pthread_mutex_unlock(&sf_socks_mutex); + + stream_sf_socket_delete(sf_sock); + } else { + sf_sock->bytes_sent += ret; + pthread_mutex_unlock(&sf_sock->mutex); + } + log_it(L_INFO, "Send action from %d sock_id (sf_packet size %lu, ch packet size %lu, have sent %d)" + , sf_sock->id, sf_pkt->header.op_data.data_size, pkt->hdr.size, ret); + } + break; + case VPN_PACKET_OP_CODE_DISCONNECT: { + log_it(L_INFO, "Disconnect action from %d sock_id", sf_sock->id); + + pthread_mutex_lock(&( CH_SF(ch)->mutex)); + HASH_DEL(CH_SF(ch)->socks, sf_sock); + pthread_mutex_unlock(&( CH_SF(ch)->mutex)); + + pthread_mutex_lock(&sf_socks_mutex); + HASH_DELETE(hh2, sf_socks, sf_sock); + HASH_DELETE(hh_sock, sf_socks_client, sf_sock); + struct epoll_event ev; + ev.data.fd = sf_sock->sock; + ev.events = EPOLLIN; + if(epoll_ctl(sf_socks_epoll_fd, EPOLL_CTL_DEL, sf_sock->sock, &ev) < 0) { + log_it(L_ERROR, "Can't remove sock_id %d to the epoll fd", remote_sock_id); + //stream_ch_pkt_write_f(ch,'i',"sock_id=%d op_code=%uc result=-2",sf_pkt->sock_id, sf_pkt->op_code); + } else { + log_it(L_NOTICE, "Removed sock_id %d from the epoll fd", remote_sock_id); + //stream_ch_pkt_write_f(ch,'i',"sock_id=%d op_code=%uc result=0",sf_pkt->sock_id, sf_pkt->op_code); + } + pthread_mutex_unlock(&sf_socks_mutex); + + pthread_mutex_unlock(&sf_sock->mutex); + stream_sf_socket_delete(sf_sock); + } + break; + default: { + log_it(L_WARNING, "Unprocessed op code 0x%02x", sf_pkt->header.op_code); + pthread_mutex_unlock(&sf_sock->mutex); + } + } + } else + log_it(L_WARNING, "Packet input: packet with sock_id %d thats not present in current stream channel", + remote_sock_id); + } else { + HASH_FIND_INT(CH_SF(ch)->socks, &remote_sock_id, sf_sock); + if(sf_sock) { + log_it(L_WARNING, "Socket id %d is already used, take another number for socket id", remote_sock_id); + } else { // Connect action + struct sockaddr_in remote_addr; + char addr_str[1024]; + size_t addr_str_size = + (sf_pkt->header.op_connect.addr_size > (sizeof(addr_str) - 1)) ? + (sizeof(addr_str) - 1) : + sf_pkt->header.op_connect.addr_size; + memset(&remote_addr, 0, sizeof(remote_addr)); + remote_addr.sin_family = AF_INET; + remote_addr.sin_port = htons(sf_pkt->header.op_connect.port); + + memcpy(addr_str, sf_pkt->data, addr_str_size); + addr_str[addr_str_size] = 0; + + log_it(L_DEBUG, "Connect action to %s:%u (addr_size %lu)", addr_str, sf_pkt->header.op_connect.port, + sf_pkt->header.op_connect.addr_size); + if(inet_pton(AF_INET, addr_str, &(remote_addr.sin_addr)) < 0) { + log_it(L_ERROR, "Wrong remote address '%s:%u'", addr_str, sf_pkt->header.op_connect.port); + } else { + int s; + if((s = socket(AF_INET, SOCK_STREAM, 0)) >= 0) { + log_it(L_DEBUG, "Socket is created (%d)", s); + if(connect(s, (struct sockaddr *) &remote_addr, sizeof(remote_addr)) >= 0) { + fcntl(s, F_SETFL, O_NONBLOCK); + log_it(L_INFO, "Remote address connected (%s:%u) with sock_id %d", addr_str, + sf_pkt->header.op_connect.port, remote_sock_id); + ch_vpn_socket_proxy_t * sf_sock = NULL; + sf_sock = DAP_NEW_Z(ch_vpn_socket_proxy_t); + sf_sock->id = remote_sock_id; + sf_sock->sock = s; + sf_sock->ch = ch; + pthread_mutex_init(&sf_sock->mutex, NULL); + + pthread_mutex_lock(&sf_socks_mutex); + pthread_mutex_lock(&( CH_SF(ch)->mutex)); + HASH_ADD_INT(CH_SF(ch)->socks, id, sf_sock); + log_it(L_DEBUG, "Added %d sock_id with sock %d to the hash table", sf_sock->id, + sf_sock->sock); + HASH_ADD(hh2, sf_socks, id, sizeof(sf_sock->id), sf_sock); + log_it(L_DEBUG, "Added %d sock_id with sock %d to the hash table", sf_sock->id, + sf_sock->sock); + HASH_ADD(hh_sock, sf_socks_client, sock, sizeof(int), sf_sock); + //log_it(L_DEBUG,"Added %d sock_id with sock %d to the socks hash table",sf->id,sf->sock); + pthread_mutex_unlock(&sf_socks_mutex); + pthread_mutex_unlock(&( CH_SF(ch)->mutex)); + + struct epoll_event ev; + ev.data.fd = s; + ev.events = EPOLLIN | EPOLLERR; + + if(epoll_ctl(sf_socks_epoll_fd, EPOLL_CTL_ADD, s, &ev) == -1) { + log_it(L_ERROR, "Can't add sock_id %d to the epoll fd", remote_sock_id); + //stream_ch_pkt_write_f(ch,'i',"sock_id=%d op_code=%uc result=-2",sf_pkt->sock_id, sf_pkt->op_code); + } else { + log_it(L_NOTICE, "Added sock_id %d with sock %d to the epoll fd", remote_sock_id, s); + log_it(L_NOTICE, "Send Connected packet to User"); + ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header)); + pkt_out->header.sock_id = remote_sock_id; + pkt_out->header.op_code = VPN_PACKET_OP_CODE_CONNECTED; + dap_stream_ch_pkt_write(ch, SERVICE_CHANNEL_ID, pkt_out, + pkt_out->header.op_data.data_size + sizeof(pkt_out->header)); + free(pkt_out); + client_connected = true; + } + stream_sf_socket_ready_to_write(ch, true); + } else { + log_it(L_INFO, "Can't connect to the remote server %s", addr_str); + dap_stream_ch_pkt_write_f(ch, 'i', "sock_id=%d op_code=%c result=-1", + sf_pkt->header.sock_id, sf_pkt->header.op_code); + stream_sf_socket_ready_to_write(ch, true); + } + } else { + log_it(L_ERROR, "Can't create the socket"); + } + } + } + } + } +} + +/** + * @brief stream_sf_disconnect + * @param sf + */ +void srv_stream_sf_disconnect(ch_vpn_socket_proxy_t * sf_sock) +{ + struct epoll_event ev; + ev.data.fd = sf_sock->sock; + ev.events = EPOLLIN | EPOLLERR; + if(epoll_ctl(sf_socks_epoll_fd, EPOLL_CTL_DEL, sf_sock->sock, &ev) == -1) { + log_it(L_ERROR, "Can't del sock_id %d from the epoll fd", sf_sock->id); + //stream_ch_pkt_write_f(sf->ch,'i',"sock_id=%d op_code=%uc result=-1",sf->id, STREAM_SF_PACKET_OP_CODE_RECV); + } else { + log_it(L_ERROR, "Removed sock_id %d from the epoll fd", sf_sock->id); + //stream_ch_pkt_write_f(sf->ch,'i',"sock_id=%d op_code=%uc result=0",sf->id, STREAM_SF_PACKET_OP_CODE_RECV); + } + + // Compise signal to disconnect to another side, with special opcode STREAM_SF_PACKET_OP_CODE_DISCONNECT + ch_vpn_pkt_t * pkt_out; + pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header) + 1); + pkt_out->header.op_code = VPN_PACKET_OP_CODE_DISCONNECT; + pkt_out->header.sock_id = sf_sock->id; + sf_sock->pkt_out[sf_sock->pkt_out_size] = pkt_out; + sf_sock->pkt_out_size++; + sf_sock->signal_to_delete = true; +} + +/** + + Socket forward + **/ + +void * srv_ch_sf_thread(void * arg) +{ + struct epoll_event ev, events[SF_MAX_EVENTS] = { 0 }; + //pthread_mutex_lock(&sf_socks_mutex); + sf_socks_epoll_fd = epoll_create(SF_MAX_EVENTS); + sigset_t sf_sigmask; + sigemptyset(&sf_sigmask); + sigaddset(&sf_sigmask, SIGUSR2); + + while(1) { + /*pthread_mutex_lock(&sf_socks_mutex); + if(sf_socks==NULL) + pthread_cond_wait(&sf_socks_cond,&sf_socks_mutex); + pthread_mutex_unlock(&sf_socks_mutex);*/ + int nfds = epoll_pwait(sf_socks_epoll_fd, events, SF_MAX_EVENTS, 10000, &sf_sigmask); + if(nfds < 0) { + //log_it(L_CRITICAL,"Can't run epoll_wait: %s",strerror(errno)); + continue; + } + if(nfds > 0) + log_it(L_DEBUG, "Epolled %d fd", nfds); + else + continue; + int n; + for(n = 0; n < nfds; ++n) { + int s = events[n].data.fd; + + ch_vpn_socket_proxy_t * sf = NULL; + pthread_mutex_lock(&sf_socks_mutex); + HASH_FIND(hh_sock, sf_socks_client, &s, sizeof(s), sf); + pthread_mutex_unlock(&sf_socks_mutex); + if(sf) { + if(events[n].events & EPOLLERR) { + log_it(L_NOTICE, "Socket id %d has EPOLLERR flag on", s); + pthread_mutex_lock(&(sf->mutex)); + srv_stream_sf_disconnect(sf); + pthread_mutex_unlock(&(sf->mutex)); + } else if(events[n].events & EPOLLIN) { + char buf[1000000]; + size_t buf_size; + ssize_t ret; + pthread_mutex_lock(&(sf->mutex)); + if(sf->pkt_out_size < ((sizeof(sf->pkt_out) / sizeof(sf->pkt_out[0])) - 1)) { + ret = recv(sf->sock, buf, sizeof(buf), 0); + //log_it(L_DEBUG,"recv() returned %d",ret); + if(ret > 0) { + buf_size = ret; + ch_vpn_pkt_t * pout; + pout = sf->pkt_out[sf->pkt_out_size] = (ch_vpn_pkt_t *) calloc(1, + buf_size + sizeof(pout->header)); + pout->header.op_code = VPN_PACKET_OP_CODE_RECV; + pout->header.sock_id = sf->id; + pout->header.op_data.data_size = buf_size; + memcpy(pout->data, buf, buf_size); + sf->pkt_out_size++; + pthread_mutex_unlock(&(sf->mutex)); + stream_sf_socket_ready_to_write(sf->ch, true); + } else { + log_it(L_NOTICE, + "Socket id %d returned error on recv() function - may be host has disconnected", s); + pthread_mutex_unlock(&(sf->mutex)); + stream_sf_socket_ready_to_write(sf->ch, true); + srv_stream_sf_disconnect(sf); + } + } else { + log_it(L_WARNING, "Can't receive data, full of stack"); + pthread_mutex_unlock(&(sf->mutex)); + } + } else { + log_it(L_WARNING, "Unprocessed flags 0x%08X", events[n].events); + } + } else { + if(epoll_ctl(sf_socks_epoll_fd, EPOLL_CTL_DEL, s, &ev) < 0) { + log_it(L_ERROR, "Can't remove sock_id %d to the epoll fd", s); + } else { + log_it(L_NOTICE, "Socket id %d is removed from the list", s); + } + } + } + //pthread_mutex_unlock(&sf_socks_mutex); + } +} + +/** + * + * + **/ +void* srv_ch_sf_thread_raw(void *arg) +{ + srv_ch_sf_tun_create(); + + if(raw_server->tun_fd <= 0) { + log_it(L_CRITICAL, "Tun/tap file descriptor is not initialized"); + return NULL; + } + /* if (fcntl(raw_server->tun_fd, F_SETFL, O_NONBLOCK) < 0){ ; + log_it(L_CRITICAL,"Can't switch tun/tap socket into the non-block mode"); + return NULL; + } + if (fcntl(raw_server->tun_fd, F_SETFD, FD_CLOEXEC) < 0){; + log_it(L_CRITICAL,"Can't switch tun/tap socket to not be passed across execs"); + return NULL; + } + */ + uint8_t *tmp_buf; + ssize_t tmp_buf_size; + static int tun_MTU = 100000; /// TODO Replace with detection of MTU size + + tmp_buf = (uint8_t *) calloc(1, tun_MTU); + tmp_buf_size = 0; + log_it(L_INFO, "Tun/tap thread starts with MTU = %d", tun_MTU); + + fd_set fds_read, fds_read_active; + + FD_ZERO(&fds_read); + FD_SET(raw_server->tun_fd, &fds_read); + FD_SET(get_select_breaker(), &fds_read); + /// Main cycle + do { + fds_read_active = fds_read; + int ret = select(FD_SETSIZE, &fds_read_active, NULL, NULL, NULL); + // + if(ret > 0) { + if(FD_ISSET(get_select_breaker(), &fds_read_active)) { // Smth to send + ch_vpn_pkt_t* pkt = srv_ch_sf_raw_read(); + if(pkt) { + int write_ret = write(raw_server->tun_fd, pkt->data, pkt->header.op_data.data_size); + if(write_ret > 0) { + log_it(L_DEBUG, "Wrote out %d bytes to the tun/tap interface", write_ret); + } else { + log_it(L_ERROR, "Tun/tap write %u bytes returned '%s' error, code (%d)", + pkt->header.op_data.data_size, strerror(errno), write_ret); + } + } + } + if(FD_ISSET(raw_server->tun_fd, &fds_read_active)) { + int read_ret = read(raw_server->tun_fd, tmp_buf, tun_MTU); + if(read_ret < 0) { + log_it(L_CRITICAL, "Tun/tap read returned '%s' error, code (%d)", strerror(errno), read_ret); + break; + } else { + struct iphdr *iph = (struct iphdr*) tmp_buf; + struct in_addr in_daddr, in_saddr; + in_daddr.s_addr = iph->daddr; + in_saddr.s_addr = iph->saddr; + char str_daddr[42], str_saddr[42]; + strncpy(str_saddr, inet_ntoa(in_saddr), sizeof(str_saddr)); + strncpy(str_daddr, inet_ntoa(in_daddr), sizeof(str_daddr)); + + if(iph->tot_len > (uint16_t) read_ret) { + log_it(L_INFO, "Tun/Tap interface returned only the fragment (tot_len =%u read_ret=%d) ", + iph->tot_len, read_ret); + } + if(iph->tot_len < (uint16_t) read_ret) { + log_it(L_WARNING, "Tun/Tap interface returned more then one packet (tot_len =%u read_ret=%d) ", + iph->tot_len, read_ret); + } + + log_it(L_DEBUG, "Read IP packet from tun/tap interface daddr=%s saddr=%s total_size = %d " + , str_daddr, str_saddr, read_ret); + dap_stream_ch_vpn_remote_single_t * raw_client = NULL; + pthread_mutex_lock(&raw_server->clients_mutex); + HASH_FIND_INT(raw_server->clients, &in_daddr.s_addr, raw_client); +// HASH_ADD_INT(CH_SF(ch)->socks, id, sf_sock ); +// HASH_DEL(CH_SF(ch)->socks,sf_sock); + if(raw_client) { // Is present in hash table such destination address + ch_vpn_pkt_t *pkt_out = (ch_vpn_pkt_t*) calloc(1, sizeof(pkt_out->header) + read_ret); + pkt_out->header.op_code = VPN_PACKET_OP_CODE_VPN_RECV; + pkt_out->header.sock_id = raw_server->tun_fd; + pkt_out->header.op_data.data_size = read_ret; + memcpy(pkt_out->data, tmp_buf, read_ret); + dap_stream_ch_pkt_write(raw_client->ch, DATA_CHANNEL_ID, pkt_out, + pkt_out->header.op_data.data_size + sizeof(pkt_out->header)); + stream_sf_socket_ready_to_write(raw_client->ch, true); + } else { + log_it(L_DEBUG, "No remote client for income IP packet with addr %s", inet_ntoa(in_daddr)); + } + pthread_mutex_unlock(&raw_server->clients_mutex); + } + }/*else { + log_it(L_CRITICAL,"select() has no tun handler in the returned set"); + break; + + }*/ + } else { + log_it(L_CRITICAL, "Select returned %d", ret); + break; + } + } while(1); + log_it(L_NOTICE, "Raw sockets listen thread is stopped"); + srv_ch_sf_tun_destroy(); + return NULL; +} + +/** + * @brief stream_sf_packet_out Packet Out Ch callback + * @param ch + * @param arg + */ +void srv_ch_sf_packet_out(dap_stream_ch_t* ch, void* arg) +{ + ch_vpn_socket_proxy_t * cur, *tmp; + bool isSmthOut = false; +// log_it(L_DEBUG,"Socket forwarding packet out callback: %u sockets in hashtable", HASH_COUNT(CH_SF(ch)->socks) ); + HASH_ITER(hh, CH_SF(ch)->socks , cur, tmp) + { + bool signalToBreak = false; + pthread_mutex_lock(&(cur->mutex)); + int i; + log_it(L_DEBUG, "Socket with id %d has %u packets in output buffer", cur->id, cur->pkt_out_size); + if(cur->pkt_out_size) { + for(i = 0; i < cur->pkt_out_size; i++) { + ch_vpn_pkt_t * pout = cur->pkt_out[i]; + if(pout) { + if(dap_stream_ch_pkt_write(ch, DATA_CHANNEL_ID, pout, + pout->header.op_data.data_size + sizeof(pout->header))) { + isSmthOut = true; + free(pout); + cur->pkt_out[i] = NULL; + } else { + log_it(L_WARNING, + "Buffer is overflowed, breaking cycle to let the upper level cycle drop data to the output socket"); + isSmthOut = true; + signalToBreak = true; + break; + } + } + } + } + + if(signalToBreak) { + pthread_mutex_unlock(&(cur->mutex)); + break; + } + cur->pkt_out_size = 0; + if(cur->signal_to_delete) { + log_it(L_NOTICE, "Socket id %d got signal to be deleted", cur->id); + pthread_mutex_lock(&( CH_SF(ch)->mutex)); + HASH_DEL(CH_SF(ch)->socks, cur); + pthread_mutex_unlock(&( CH_SF(ch)->mutex)); + + pthread_mutex_lock(&(sf_socks_mutex)); + HASH_DELETE(hh2, sf_socks, cur); + HASH_DELETE(hh_sock, sf_socks_client, cur); + pthread_mutex_unlock(&(sf_socks_mutex)); + + pthread_mutex_unlock(&(cur->mutex)); + stream_sf_socket_delete(cur); + } else + pthread_mutex_unlock(&(cur->mutex)); + } + ch->ready_to_write = isSmthOut; + if(isSmthOut) { + ch->stream->conn_http->state_write = DAP_HTTP_CLIENT_STATE_DATA; + } + dap_client_remote_ready_to_write(ch->stream->conn, isSmthOut); + +} + diff --git a/dap_chain_net_srv_vpn.h b/dap_chain_net_srv_vpn.h index c61803d30d13ae4d1ce5b0ca7598db2259c1b2d5..c8cee5fc1ca7a05b7547e258ded24299f1992795 100755 --- a/dap_chain_net_srv_vpn.h +++ b/dap_chain_net_srv_vpn.h @@ -22,5 +22,7 @@ along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. */ #pragma once -int dap_chain_net_srv_vpn_init(); +#include "dap_chain_net_srv.h" + +int dap_chain_net_srv_vpn_init(dap_config_t * g_config); void dap_chain_net_srv_vpn_deinit();