From 771202516bdc87718e08bcfebb5ed44ce10d6282 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Al=D0=B5x=D0=B0nder=20Lysik=D0=BEv?=
 <alexander.lysikov@demlabs.net>
Date: Mon, 22 Jun 2020 21:10:50 +0500
Subject: [PATCH] move vpn client from net to vpn module added hooks

---
 modules/net/CMakeLists.txt                    |   2 +-
 modules/net/dap_chain_node_cli.c              |   5 -
 modules/net/dap_chain_node_cli_cmd.c          | 160 -----------------
 modules/net/include/dap_chain_node_cli_cmd.h  |   3 -
 .../vpn/dap_chain_net_srv_vpn_cdb_auth.c      |  82 ++++++++-
 .../service/vpn/dap_chain_net_srv_vpn_cmd.c   | 170 ++++++++++++++++++
 .../service/vpn/dap_chain_net_srv_vpn_cmd.h   |   4 +
 .../service/vpn/dap_chain_net_vpn_client.c    |   7 +
 8 files changed, 262 insertions(+), 171 deletions(-)
 create mode 100644 modules/service/vpn/dap_chain_net_srv_vpn_cmd.c
 create mode 100644 modules/service/vpn/dap_chain_net_srv_vpn_cmd.h

diff --git a/modules/net/CMakeLists.txt b/modules/net/CMakeLists.txt
index 990616476d..9c3c877a0e 100644
--- a/modules/net/CMakeLists.txt
+++ b/modules/net/CMakeLists.txt
@@ -45,7 +45,7 @@ endif()
 
 if(UNIX)
     target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_client dap_stream_ch_chain dap_stream_ch_chain_net dap_chain
-      dap_chain_crypto dap_chain_wallet dap_chain_net_srv dap_chain_net_srv_vpn dap_chain_mempool dap_chain_global_db dap_chain_net_srv_stake
+      dap_chain_wallet dap_chain_net_srv dap_chain_mempool dap_chain_global_db dap_chain_net_srv_stake
       resolv
       )
 endif()
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index 8ff96efa9d..958af35a70 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -948,11 +948,6 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
     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] -net <net name>\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" );
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index afbd4469e6..809d073a55 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -77,7 +77,6 @@
 #include "dap_chain_net_srv.h"
 #ifndef _WIN32
 #include "dap_chain_net_news.h"
-#include "dap_chain_net_vpn_client.h"
 #endif
 #include "dap_chain_cell.h"
 
@@ -3800,162 +3799,3 @@ int com_news(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply)
     dap_chain_node_cli_set_reply_text(a_str_reply, "News added from %s successfully", l_from);
     return 0;
 }
-
-/**
- * vpn_client command
- *
- * VPN client control
- */
-int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply)
-{
-#ifndef _WIN32
-    enum {
-        CMD_NONE, CMD_INIT, CMD_START, CMD_STOP, CMD_STATUS
-    };
-    int l_arg_index = 1;
-    // find net
-    dap_chain_net_t *l_net = NULL;
-    if(dap_chain_node_cli_cmd_values_parse_net_chain(&l_arg_index, a_argc, a_argv, a_str_reply, NULL, &l_net) < 0)
-        return -2;
-
-    int cmd_num = CMD_NONE;
-    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "init", NULL)) {
-        cmd_num = CMD_INIT;
-    }
-    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "start", NULL)) {
-            cmd_num = CMD_START;
-        }
-    else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "stop", NULL)) {
-        cmd_num = CMD_STOP;
-    }
-    else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "status", NULL)) {
-        cmd_num = CMD_STATUS;
-    }
-    if(cmd_num == CMD_NONE) {
-        if(!a_argv[1])
-            dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameters");
-        else
-            dap_chain_node_cli_set_reply_text(a_str_reply, "parameter %s not recognized", a_argv[1]);
-        return -1;
-    }
-
-    switch (cmd_num)
-    {
-    case CMD_INIT: {
-            const char * l_str_token = NULL; // token name
-            const char * l_str_value_datoshi = NULL;
-            const char * l_str_wallet = NULL; // wallet name
-            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_str_wallet);
-            if(!l_str_wallet)
-                dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-w", &l_str_wallet);
-
-            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_str_token);
-            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_str_value_datoshi);
-
-            if(!l_str_wallet) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Wallet not defined, use -w <wallet_name> or -wallet <wallet_name> parameter");
-                break;
-            }
-            if(!l_str_token) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Token not defined, use -token <token_name> parameter");
-                break;
-            }
-            if(!l_str_value_datoshi) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi not defined, use -value <value of datoshi> parameter");
-                break;
-            }
-            uint64_t l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 10);
-            if(!l_a_value_datoshi)
-                l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 16);
-            if(!l_a_value_datoshi) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi have to be more then 0");
-                break;
-            }
-            int l_res = dap_chain_net_vpn_client_update(l_net, l_str_wallet, l_str_token, l_a_value_datoshi);
-            if(!l_res)
-                dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully");
-            else{
-                if(l_res==-3)
-                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully, but probably not enough founds in the wallet");
-                else
-                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not init");
-            }
-            return l_res;
-    }
-        break;
-    case CMD_START: {
-        const char * l_str_addr = NULL; // for example, "192.168.100.93"
-        const char * l_str_port = NULL; // for example, "8079"
-        dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr", &l_str_addr);
-        if(!l_str_addr) {
-            dap_chain_node_cli_set_reply_text(a_str_reply,
-                    "VPN server address not defined, use -addr <vpn server ipv4 address> parameter");
-            break;
-        }
-        dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-port", &l_str_port);
-        int l_srv_port = (l_str_port) ? (int) strtoll(l_str_port, 0, 10) : 0;
-        if(!l_srv_port) {
-            dap_chain_node_cli_set_reply_text(a_str_reply,
-                    "VPN server port not defined, use -port <vpn server port>  parameter");
-            break;
-        }
-        int l_res = dap_chain_net_vpn_client_start(l_net, l_str_addr, NULL, l_srv_port);
-        switch (l_res) {
-        case 0:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client started successfully");
-            break;
-        case 1:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client already started");
-            break;
-        case -2:
-        case -3:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't connect to VPN server");
-            break;
-        default:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't start VPN client");
-            break;
-        }
-        return l_res;
-    }
-        break;
-    case CMD_STOP: {
-        int res = dap_chain_net_vpn_client_stop();
-        if(!res)
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client stopped successfully");
-        else
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not stopped");
-        return res;
-    }
-        break;
-    case CMD_STATUS:
-        {
-        char *l_wallet_name = NULL, *l_str_token = NULL;
-        uint64_t l_value_datoshi = 0;
-        dap_chain_net_vpn_client_get_wallet_info(l_net, &l_wallet_name, &l_str_token, &l_value_datoshi);
-
-        const char *l_status_txt = "";
-        switch (dap_chain_net_vpn_client_status()) {
-        case VPN_CLIENT_STATUS_NOT_STARTED:
-            l_status_txt = "VPN client not started";
-            break;
-        case VPN_CLIENT_STATUS_STARTED:
-            l_status_txt = "VPN client started";
-            break;
-        case VPN_CLIENT_STATUS_STOPPED:
-            l_status_txt = "VPN client stopped";
-            break;
-        case VPN_CLIENT_STATUS_CONN_LOST:
-            l_status_txt = "VPN client lost connection";
-            break;
-        default:
-            l_status_txt = "VPN client status unknown";
-            break;
-        }
-        dap_chain_node_cli_set_reply_text(a_str_reply, "%s\nused:\nwallet:%s\nreceipt:%u*1e-9 %s", l_status_txt,
-                l_wallet_name, l_value_datoshi, l_str_token);
-        break;
-    }
-    }
-#endif
-    return 0;
-}
diff --git a/modules/net/include/dap_chain_node_cli_cmd.h b/modules/net/include/dap_chain_node_cli_cmd.h
index dc5f569568..19b73a6239 100644
--- a/modules/net/include/dap_chain_node_cli_cmd.h
+++ b/modules/net/include/dap_chain_node_cli_cmd.h
@@ -138,9 +138,6 @@ int com_exit(int argc, char ** argv, void *arg_func, char **str_reply);
 int com_news(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply);
 //#endif
 
-// vpn_client command
-int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply);
-
 int com_mempool_delete(int argc, char ** argv, void *arg_func, char ** a_str_reply);
 int com_mempool_list(int argc, char ** argv, void *arg_func, char ** a_str_reply);
 int com_mempool_proc(int argc, char ** argv, void *arg_func, char ** a_str_reply);
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c b/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c
index e44fbaf2f5..7cb35668c6 100644
--- a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c
+++ b/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c
@@ -35,6 +35,7 @@
 #include "dap_common.h"
 #include "dap_string.h"
 #include "dap_strfuncs.h"
+#include "dap_file_utils.h"
 #include "dap_client_remote.h"
 
 #include "dap_http.h"
@@ -89,10 +90,51 @@ static char * s_salt_str = "Ijg24GAS56h3hg7hj245b";
 static bool s_is_registration_open = false;
 static bool s_mode_passwd = true;
 
+// hook paths
+static char *s_hook_user_create = NULL;
+static char *s_hook_user_login = NULL;
+static char *s_hook_user_update = NULL;
+static char *s_hook_user_delete = NULL;
+
+static char *s_hook_serial_generate = NULL;
+static char *s_hook_serial_login = NULL;
+static char *s_hook_serial_activate = NULL;
+static char *s_hook_serial_update = NULL;
+static char *s_hook_serial_delete = NULL;
+
 static int s_input_validation(const char * str);
 static void s_http_enc_proc(enc_http_delegate_t *a_delegate, void * a_arg);
 static void s_http_enc_proc_key(enc_http_delegate_t *a_delegate, void * a_arg);
 static void s_http_proc(dap_http_simple_t *a_http_simple, void * arg );
+
+static char *register_hook(const char *a_cfg_name)
+{
+    char *l_hook_path_ret = NULL;
+    const char *l_hook_path = dap_config_get_item_str(g_config, "cdb_auth", a_cfg_name);
+    if(dap_file_test(l_hook_path))
+        l_hook_path_ret = dap_strdup(l_hook_path);
+    else if(l_hook_path) {
+        log_it(L_WARNING, "file for %s = %s not found", a_cfg_name, l_hook_path);
+    }
+    return l_hook_path_ret;
+}
+
+static int run_hook(char *a_hook_path, char *a_format, ...)
+{
+    if(!a_hook_path)
+        return -1;
+    char *l_params = NULL;
+    va_list l_args;
+    va_start(l_args, a_hook_path);
+    l_params = dap_strdup_vprintf(a_format, l_args);
+    va_end(l_args);
+    char *l_cmd = dap_strdup_printf("%s %s", a_hook_path, l_params);
+    int l_ret = system(l_cmd);
+    DAP_DELETE(l_params);
+    DAP_DELETE(l_cmd);
+    return l_ret;
+}
+
 /**
  * @brief dap_chain_net_srv_vpn_cdb_auth_init
  * @param a_domain
@@ -132,7 +174,19 @@ int dap_chain_net_srv_vpn_cdb_auth_init (const char * a_domain, const char * a_m
     s_group_ts_last_login = dap_strdup_printf("%s.ts_last_login",s_group_users);
     s_group_ts_active_till = dap_strdup_printf("%s.ts_active_till",s_group_users);
 
-    return 0;
+    // load hook paths
+    s_hook_user_create = register_hook("hook_user_create");
+    s_hook_user_login = register_hook("hook_user_login");
+    s_hook_user_update = register_hook("hook_user_update");
+    s_hook_user_delete = register_hook("hook_user_delete");
+
+    s_hook_serial_generate = register_hook("hook_serial_generate");
+    s_hook_serial_login = register_hook("hook_serial_login");
+    s_hook_serial_activate = register_hook("hook_serial_activate");
+    s_hook_serial_update = register_hook("hook_serial_update");
+    s_hook_serial_delete = register_hook("s_hook_serial_delete");
+    //run_hook(s_hook_serial_update, "serial=%s active_days=%lld", l_serial.header.serial, l_active_days);
+
 }
 
 /**
@@ -487,6 +541,7 @@ int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial(const char *a_serial_str, int
                 if(i < l_serial_count - 1)
                     dap_string_append(l_keys, "\n");
             }
+            run_hook(s_hook_serial_generate, "serial=%s active_days=%lld", l_serial.header.serial, l_active_days);
         }
         dap_chain_node_cli_set_reply_text(a_str_reply, "generated new %s", l_keys->str);
         dap_string_free(l_keys, true);
@@ -519,6 +574,7 @@ int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial(const char *a_serial_str, int
                     DAP_DELETE(l_serial_key);
                     // save gdb
                     dap_chain_global_db_flush();
+                    run_hook(s_hook_serial_update, "serial=%s active_days=%lld", l_serial_key->header.serial, l_serial_key->header.activated ? "activated" : "inative", l_active_days);
                     return 0;
                 }
                 else{
@@ -531,6 +587,15 @@ int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial(const char *a_serial_str, int
             }
             return 0;
         }
+    }
+    else
+    // Command 'serial info'
+    if(!dap_strcmp(a_serial_str, "info")) {
+
+    }
+    // Command 'serial delete'
+    if(!dap_strcmp(a_serial_str, "delete")) {
+
     }
     else {
         dap_chain_node_cli_set_reply_text(a_str_reply, "unknown subcommand %s, use 'generate', 'list' or 'update'", a_serial_str);
@@ -594,8 +659,9 @@ int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user(const char *a_user_str, int a_ar
             dap_chain_global_db_gr_set(dap_strdup(l_login_str), l_time,sizeof (*l_time),s_group_ts_updated );
             l_time = NULL; // to prevent usage uleased memory that could be free in any moment
 
+            uint64_t l_active_days = 0;
             if ( l_active_days_str ){
-                uint64_t l_active_days = strtoull(l_active_days_str,NULL,10);
+                l_active_days = strtoull(l_active_days_str,NULL,10);
                 if ( l_active_days ){
                     l_time = DAP_NEW_Z(dap_chain_time_t);
                     *l_time = dap_chain_time_now() + (dap_chain_time_t) l_active_days*86400ull;
@@ -605,9 +671,17 @@ int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user(const char *a_user_str, int a_ar
             }
 
             if (l_is_user_create){
+                run_hook(s_hook_user_create, "login=%s pass=%s active_days=%lld first_name=%s last_name=%s email=%s", l_login_str, l_password_str, l_active_days,
+                        l_first_name_str ? l_first_name_str : "-",
+                        l_last_name_str ? l_last_name_str : "-",
+                        l_email_str ? l_email_str : "-");
                 dap_string_append_printf(l_ret_str,"OK: Created user '%s'\n",l_login_str );
                 l_ret = 0;
             }else if (l_is_user_update){
+                run_hook(s_hook_user_update, "login=%s pass=%s active_days=%lld first_name=%s last_name=%s email=%s", l_login_str, l_password_str, l_active_days,
+                                        l_first_name_str ? l_first_name_str : "-",
+                                        l_last_name_str ? l_last_name_str : "-",
+                                        l_email_str ? l_email_str : "-");
                 dap_string_append_printf(l_ret_str,"OK: Updated user '%s'\n",l_login_str );
                 l_ret = 0;
             }else{
@@ -647,6 +721,7 @@ int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user(const char *a_user_str, int a_ar
                 }
 
                 dap_string_append_printf(l_ret_str,"OK: Deleted user '%s'\n",l_login_str );
+                run_hook(s_hook_user_delete, "login=%s", l_login_str);
                 l_ret = 0;
             }else{
                 l_ret = -6;
@@ -872,6 +947,7 @@ static void s_http_enc_proc(enc_http_delegate_t *a_delegate, void * a_arg)
                         }
 
                         int l_login_result = dap_chain_net_srv_vpn_cdb_auth_check_login(l_login, l_password);
+                        run_hook(s_hook_user_login, "login=%s pass=%s result=%d", l_login, l_password, l_login_result);
                         switch (l_login_result) {
                         case 0: {
                             size_t l_tmp_size;
@@ -977,6 +1053,7 @@ static void s_http_enc_proc(enc_http_delegate_t *a_delegate, void * a_arg)
                         }
                         int l_login_result = dap_chain_net_srv_vpn_cdb_auth_check_serial(l_serial, l_pkey);
                         log_it(L_INFO, "Check serial '%s' with code %d (Ok=0)", l_serial, l_login_result);
+                        run_hook(s_hook_serial_login, "serial=%s result=%d", l_serial, l_login_result);
                         switch (l_login_result) {
                         case 0: {
                             size_t l_tmp_size;
@@ -1174,6 +1251,7 @@ static void s_http_enc_proc_key(enc_http_delegate_t *a_delegate, void * a_arg)
                             return;
                         }
                         int l_activate_result = dap_chain_net_srv_vpn_cdb_auth_activate_serial(l_serial_raw, l_serial, l_serial_sign, l_pkey);
+                        run_hook(s_hook_serial_activate, "serial=%s result=%d", l_serial, l_activate_result);
                         log_it(L_INFO, "Serial '%s' activated with code %d (Ok=0)", l_serial, l_activate_result);
                         switch (l_activate_result) {
                         case 0:
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cmd.c b/modules/service/vpn/dap_chain_net_srv_vpn_cmd.c
new file mode 100644
index 0000000000..01a18e9f0d
--- /dev/null
+++ b/modules/service/vpn/dap_chain_net_srv_vpn_cmd.c
@@ -0,0 +1,170 @@
+#include <dap_chain_node_cli.h>
+#include "dap_chain_node_cli_cmd.h"
+#include "dap_chain_net_srv_vpn_cmd.h"
+#include "dap_chain_net_vpn_client.h"
+
+/**
+ * vpn_client command
+ *
+ * VPN client control
+ */
+int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply)
+{
+#ifndef _WIN32
+    enum {
+        CMD_NONE, CMD_INIT, CMD_START, CMD_STOP, CMD_STATUS
+    };
+    int l_arg_index = 1;
+    // find net
+    dap_chain_net_t *l_net = NULL;
+    if(dap_chain_node_cli_cmd_values_parse_net_chain(&l_arg_index, a_argc, a_argv, a_str_reply, NULL, &l_net) < 0)
+        return -2;
+
+    int cmd_num = CMD_NONE;
+    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "init", NULL)) {
+        cmd_num = CMD_INIT;
+    }
+    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "start", NULL)) {
+            cmd_num = CMD_START;
+        }
+    else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "stop", NULL)) {
+        cmd_num = CMD_STOP;
+    }
+    else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "status", NULL)) {
+        cmd_num = CMD_STATUS;
+    }
+    if(cmd_num == CMD_NONE) {
+        if(!a_argv[1])
+            dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameters");
+        else
+            dap_chain_node_cli_set_reply_text(a_str_reply, "parameter %s not recognized", a_argv[1]);
+        return -1;
+    }
+
+    switch (cmd_num)
+    {
+    case CMD_INIT: {
+            const char * l_str_token = NULL; // token name
+            const char * l_str_value_datoshi = NULL;
+            const char * l_str_wallet = NULL; // wallet name
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_str_wallet);
+            if(!l_str_wallet)
+                dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-w", &l_str_wallet);
+
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_str_token);
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_str_value_datoshi);
+
+            if(!l_str_wallet) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Wallet not defined, use -w <wallet_name> or -wallet <wallet_name> parameter");
+                break;
+            }
+            if(!l_str_token) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Token not defined, use -token <token_name> parameter");
+                break;
+            }
+            if(!l_str_value_datoshi) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi not defined, use -value <value of datoshi> parameter");
+                break;
+            }
+            uint64_t l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 10);
+            if(!l_a_value_datoshi)
+                l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 16);
+            if(!l_a_value_datoshi) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi have to be more then 0");
+                break;
+            }
+            int l_res = dap_chain_net_vpn_client_update(l_net, l_str_wallet, l_str_token, l_a_value_datoshi);
+            if(!l_res)
+                dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully");
+            else{
+                if(l_res==-3)
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully, but probably not enough founds in the wallet");
+                else
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not init");
+            }
+            return l_res;
+    }
+        break;
+    case CMD_START: {
+        const char * l_str_addr = NULL; // for example, "192.168.100.93"
+        const char * l_str_port = NULL; // for example, "8079"
+        dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr", &l_str_addr);
+        if(!l_str_addr) {
+            dap_chain_node_cli_set_reply_text(a_str_reply,
+                    "VPN server address not defined, use -addr <vpn server ipv4 address> parameter");
+            break;
+        }
+        dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-port", &l_str_port);
+        int l_srv_port = (l_str_port) ? (int) strtoll(l_str_port, 0, 10) : 0;
+        if(!l_srv_port) {
+            dap_chain_node_cli_set_reply_text(a_str_reply,
+                    "VPN server port not defined, use -port <vpn server port>  parameter");
+            break;
+        }
+        int l_res = dap_chain_net_vpn_client_start(l_net, l_str_addr, NULL, l_srv_port);
+        switch (l_res) {
+        case 0:
+            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client started successfully");
+            break;
+        case 1:
+            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client already started");
+            break;
+        case -2:
+        case -3:
+            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't connect to VPN server");
+            break;
+        default:
+            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't start VPN client");
+            break;
+        }
+        return l_res;
+    }
+        break;
+    case CMD_STOP: {
+        int res = dap_chain_net_vpn_client_stop();
+        if(!res)
+            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client stopped successfully");
+        else
+            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not stopped");
+        return res;
+    }
+        break;
+    case CMD_STATUS:
+        {
+        char *l_wallet_name = NULL, *l_str_token = NULL;
+        uint64_t l_value_datoshi = 0;
+        dap_chain_net_vpn_client_get_wallet_info(l_net, &l_wallet_name, &l_str_token, &l_value_datoshi);
+
+        const char *l_status_txt = "";
+        switch (dap_chain_net_vpn_client_status()) {
+        case VPN_CLIENT_STATUS_NOT_STARTED:
+            l_status_txt = "VPN client not started";
+            break;
+        case VPN_CLIENT_STATUS_STARTED:
+            l_status_txt = "VPN client started";
+            break;
+        case VPN_CLIENT_STATUS_STOPPED:
+            l_status_txt = "VPN client stopped";
+            break;
+        case VPN_CLIENT_STATUS_CONN_LOST:
+            l_status_txt = "VPN client lost connection";
+            break;
+        default:
+            l_status_txt = "VPN client status unknown";
+            break;
+        }
+        dap_chain_node_cli_set_reply_text(a_str_reply, "%s\nused:\nwallet:%s\nreceipt:%u*1e-9 %s", l_status_txt,
+                l_wallet_name, l_value_datoshi, l_str_token);
+        break;
+    }
+    }
+#endif
+    return 0;
+}
+
+
+int dap_chain_net_srv_vpn_cmd_init()
+{
+
+
+}
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cmd.h b/modules/service/vpn/dap_chain_net_srv_vpn_cmd.h
new file mode 100644
index 0000000000..4ce2243e87
--- /dev/null
+++ b/modules/service/vpn/dap_chain_net_srv_vpn_cmd.h
@@ -0,0 +1,4 @@
+#pragma once
+
+// vpn_client command
+int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply);
diff --git a/modules/service/vpn/dap_chain_net_vpn_client.c b/modules/service/vpn/dap_chain_net_vpn_client.c
index 7a673697cd..9109e9320c 100644
--- a/modules/service/vpn/dap_chain_net_vpn_client.c
+++ b/modules/service/vpn/dap_chain_net_vpn_client.c
@@ -55,6 +55,7 @@
 #include "dap_stream_ch_chain_net_srv.h"
 //#include "dap_stream_ch_chain_net_srv.h"
 #include "dap_chain_net_vpn_client_tun.h"
+#include "dap_chain_net_srv_vpn_cmd.h"
 //#include "dap_chain_net_vpn_client_data.h"
 
 /*
@@ -724,6 +725,12 @@ void dap_chain_net_vpn_client_pkt_out(dap_stream_ch_t* a_ch)
 int dap_chain_net_vpn_client_init(dap_config_t * g_config)
 {
     pthread_mutex_init(&sf_socks_mutex, NULL);
+
+    // vpn client command
+    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] -net <net name>\n");
+
+
     return dap_chain_net_srv_client_vpn_init(g_config);
 }
 
-- 
GitLab