From 62a059abdb0ac1063882f3321ea3c37ef0393748 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 14 Apr 2026 21:17:46 +0300 Subject: [PATCH] add sockets to gui --- src/control_server.c | 72 ++++++++++++++++++++++++++++- tools/etcpmon/etcpmon_client.c | 71 +++++++++++++++++++++++++++-- tools/etcpmon/etcpmon_client.h | 25 +++++++--- tools/etcpmon/etcpmon_gui.c | 78 ++++++++++++++++++++++++++------ tools/etcpmon/etcpmon_gui.h | 14 ++++-- tools/etcpmon/etcpmon_protocol.h | 30 ++++++++++++ 6 files changed, 259 insertions(+), 31 deletions(-) diff --git a/src/control_server.c b/src/control_server.c index c144f8aa..6bb91f71 100644 --- a/src/control_server.c +++ b/src/control_server.c @@ -88,6 +88,7 @@ static void handle_client_data(struct control_server* server, struct control_cli static void close_client(struct control_server* server, struct control_client* client); static void send_conn_list(struct control_server* server, struct control_client* client, uint8_t seq_id); +static void send_socket_list(struct control_server* server, struct control_client* client, uint8_t seq_id); static void send_metrics(struct control_server* server, struct control_client* client, uint8_t seq_id); static void send_error(struct control_client* client, uint8_t error_code, const char* msg, uint8_t seq_id); @@ -609,7 +610,7 @@ static void handle_client_data(struct control_server* server, struct control_cli close_client(server, client); return; } - if (hdr->type < ETCPMON_CMD_LIST_CONN || hdr->type > ETCPMON_CMD_DISCONNECT) { + if (hdr->type < ETCPMON_CMD_LIST_CONN || hdr->type > ETCPMON_CMD_LIST_SOCKETS) { DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Invalid command type from client: 0x%02X", hdr->type); close_client(server, client); return; @@ -676,6 +677,10 @@ static void handle_client_data(struct control_server* server, struct control_cli } break; + case ETCPMON_CMD_LIST_SOCKETS: + send_socket_list(server, client, req_seq); + break; + case ETCPMON_CMD_DISCONNECT: if (server->log_file) { fprintf(server->log_file, "%llu: [LOG] Client requested disconnect\n", @@ -759,6 +764,71 @@ static void send_conn_list(struct control_server* server, struct control_client* u_free(buffer); } +static void send_socket_list(struct control_server* server, struct control_client* client, uint8_t seq_id) { + struct UTUN_INSTANCE* instance = server->instance; + + uint8_t count = 0; + struct ETCP_SOCKET* sock = instance->etcp_sockets; + while (sock && count < ETCPMON_MAX_CONNECTIONS) { + count++; + sock = sock->next; + } + + uint16_t rsp_size = ETCPMON_SOCKET_LIST_SIZE(count); + uint8_t* buffer = (uint8_t*)u_malloc(rsp_size); + if (!buffer) { + DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Failed to allocate socket list buffer"); + return; + } + + struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer; + etcpmon_build_header(hdr, sizeof(struct etcpmon_rsp_socket_list) + + count * sizeof(struct etcpmon_socket_info), + ETCPMON_RSP_SOCKET_LIST, + seq_id); + + struct etcpmon_rsp_socket_list* rsp = (struct etcpmon_rsp_socket_list*)(buffer + sizeof(*hdr)); + rsp->count = count; + + struct etcpmon_socket_info* info = (struct etcpmon_socket_info*)(buffer + sizeof(*hdr) + sizeof(*rsp)); + sock = instance->etcp_sockets; + for (uint8_t i = 0; i < count && sock; i++) { + strncpy(info[i].name, sock->name, sizeof(info[i].name) - 1); + info[i].name[sizeof(info[i].name) - 1] = '\0'; + info[i].is_ipv6 = (sock->local_addr.ss_family == AF_INET6) ? 1 : 0; + if (sock->local_addr.ss_family == AF_INET) { + struct sockaddr_in* sin = (struct sockaddr_in*)&sock->local_addr; + inet_ntop(AF_INET, &sin->sin_addr, info[i].ip, sizeof(info[i].ip)); + info[i].port = ntohs(sin->sin_port); + } else if (sock->local_addr.ss_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&sock->local_addr; + inet_ntop(AF_INET6, &sin6->sin6_addr, info[i].ip, sizeof(info[i].ip)); + info[i].port = ntohs(sin6->sin6_port); + } else { + strncpy(info[i].ip, "unknown", sizeof(info[i].ip) - 1); + info[i].ip[sizeof(info[i].ip) - 1] = '\0'; + info[i].port = 0; + } + info[i].nat_type = sock->nat_type; + sock = sock->next; + } + + if (server->log_file) { + log_hex_data(server->log_file, "TX", buffer, rsp_size); + fprintf(server->log_file, "%llu: [LOG] Sent RSP_SOCKET_LIST count=%d\n", + (unsigned long long)get_timestamp_ms(), count); + fflush(server->log_file); + } + +#ifdef _WIN32 + send(client->fd, (const char*)buffer, rsp_size, 0); +#else + send(client->fd, buffer, rsp_size, 0); +#endif + + u_free(buffer); +} + static void send_metrics(struct control_server* server, struct control_client* client, uint8_t seq_id) { struct UTUN_INSTANCE* instance = server->instance; diff --git a/tools/etcpmon/etcpmon_client.c b/tools/etcpmon/etcpmon_client.c index dd1177c8..709cc301 100644 --- a/tools/etcpmon/etcpmon_client.c +++ b/tools/etcpmon/etcpmon_client.c @@ -87,6 +87,10 @@ void etcpmon_client_cleanup(struct etcpmon_client* client) { free(client->conn_list); client->conn_list = NULL; } + if (client->socket_list) { + free(client->socket_list); + client->socket_list = NULL; + } if (client->last_metrics) { free(client->last_metrics); client->last_metrics = NULL; @@ -299,6 +303,32 @@ int etcpmon_client_select_connection(struct etcpmon_client* client, uint64_t pee return 0; } +int etcpmon_client_request_socket_list(struct etcpmon_client* client) { + if (!client || client->sock == INVALID_SOCKET) return -1; + + struct etcpmon_msg_header hdr; + etcpmon_build_header(&hdr, 0, ETCPMON_CMD_LIST_SOCKETS, 0); + + if (client->log_file) { + log_hex_data(client->log_file, "TX", (const uint8_t*)&hdr, sizeof(hdr)); + fprintf(client->log_file, "%llu: [LOG] CMD_LIST_SOCKETS (seq=0)\n", + (unsigned long long)get_timestamp_ms()); + fflush(client->log_file); + } + + int sent = send(client->sock, (const char*)&hdr, sizeof(hdr), 0); + if (sent != sizeof(hdr)) { + if (client->log_file) { + fprintf(client->log_file, "%llu: [ERROR] Failed to send CMD_LIST_SOCKETS: %d\n", + (unsigned long long)get_timestamp_ms(), WSAGetLastError()); + fflush(client->log_file); + } + return -1; + } + + return 0; +} + int etcpmon_client_request_metrics(struct etcpmon_client* client) { if (!client || client->sock == INVALID_SOCKET) return -1; @@ -440,9 +470,38 @@ static void handle_response(struct etcpmon_client* client, uint8_t msg_type, break; } + case ETCPMON_RSP_SOCKET_LIST: { + if (payload_size < sizeof(struct etcpmon_rsp_socket_list)) break; + + struct etcpmon_rsp_socket_list* rsp = (struct etcpmon_rsp_socket_list*)payload; + uint8_t count = rsp->count; + + if (payload_size < sizeof(*rsp) + count * sizeof(struct etcpmon_socket_info)) break; + + if (client->socket_list) { + free(client->socket_list); + } + client->socket_list = (struct etcpmon_socket_info*)malloc(count * sizeof(struct etcpmon_socket_info)); + if (client->socket_list) { + memcpy(client->socket_list, payload + sizeof(*rsp), count * sizeof(struct etcpmon_socket_info)); + client->socket_count = count; + + if (client->log_file) { + fprintf(client->log_file, "%llu: [LOG] RSP_SOCKET_LIST count=%d\n", + (unsigned long long)get_timestamp_ms(), count); + fflush(client->log_file); + } + + if (client->on_socket_list) { + client->on_socket_list(client->socket_list, count, client->user_data); + } + } + break; + } + case ETCPMON_RSP_METRICS: { if (payload_size < sizeof(struct etcpmon_rsp_metrics)) break; - + struct etcpmon_rsp_metrics* rsp = (struct etcpmon_rsp_metrics*)payload; uint8_t links_count = rsp->etcp.links_count; @@ -765,16 +824,18 @@ void etcpmon_client_set_callbacks(struct etcpmon_client* client, void (*on_connected)(void*), void (*on_disconnected)(void*), void (*on_conn_list)(struct etcpmon_conn_info*, uint8_t, void*), - void (*on_metrics)(struct etcpmon_rsp_metrics*, - struct etcpmon_link_metrics*, - uint8_t, void*), + void (*on_socket_list)(struct etcpmon_socket_info*, uint8_t, void*), + void (*on_metrics)(struct etcpmon_rsp_metrics*, + struct etcpmon_link_metrics*, + uint8_t, void*), void (*on_error)(const char*, void*), void* user_data) { if (!client) return; - + client->on_connected = on_connected; client->on_disconnected = on_disconnected; client->on_conn_list = on_conn_list; + client->on_socket_list = on_socket_list; client->on_metrics = on_metrics; client->on_error = on_error; client->user_data = user_data; diff --git a/tools/etcpmon/etcpmon_client.h b/tools/etcpmon/etcpmon_client.h index e25f441a..f44389b6 100644 --- a/tools/etcpmon/etcpmon_client.h +++ b/tools/etcpmon/etcpmon_client.h @@ -65,7 +65,11 @@ struct etcpmon_client { /* Connection info cache */ struct etcpmon_conn_info* conn_list; uint8_t conn_count; - + + /* Socket info cache */ + struct etcpmon_socket_info* socket_list; + uint8_t socket_count; + /* Latest metrics */ struct etcpmon_rsp_metrics* last_metrics; struct etcpmon_link_metrics* last_links; @@ -89,9 +93,10 @@ struct etcpmon_client { void (*on_connected)(void* user_data); void (*on_disconnected)(void* user_data); void (*on_conn_list)(struct etcpmon_conn_info* list, uint8_t count, void* user_data); - void (*on_metrics)(struct etcpmon_rsp_metrics* metrics, - struct etcpmon_link_metrics* links, - uint8_t links_count, void* user_data); + void (*on_socket_list)(struct etcpmon_socket_info* list, uint8_t count, void* user_data); + void (*on_metrics)(struct etcpmon_rsp_metrics* metrics, + struct etcpmon_link_metrics* links, + uint8_t links_count, void* user_data); void (*on_error)(const char* msg, void* user_data); void* user_data; @@ -128,6 +133,11 @@ int etcpmon_client_is_connected(const struct etcpmon_client* client); */ int etcpmon_client_request_list(struct etcpmon_client* client); +/* Request local socket list + * Returns 0 on success, -1 on error + */ +int etcpmon_client_request_socket_list(struct etcpmon_client* client); + /* Select connection to monitor * Returns 0 on success, -1 on error */ @@ -160,9 +170,10 @@ void etcpmon_client_set_callbacks(struct etcpmon_client* client, void (*on_connected)(void*), void (*on_disconnected)(void*), void (*on_conn_list)(struct etcpmon_conn_info*, uint8_t, void*), - void (*on_metrics)(struct etcpmon_rsp_metrics*, - struct etcpmon_link_metrics*, - uint8_t, void*), + void (*on_socket_list)(struct etcpmon_socket_info*, uint8_t, void*), + void (*on_metrics)(struct etcpmon_rsp_metrics*, + struct etcpmon_link_metrics*, + uint8_t, void*), void (*on_error)(const char*, void*), void* user_data); diff --git a/tools/etcpmon/etcpmon_gui.c b/tools/etcpmon/etcpmon_gui.c index 323261fe..159f4198 100644 --- a/tools/etcpmon/etcpmon_gui.c +++ b/tools/etcpmon/etcpmon_gui.c @@ -36,8 +36,9 @@ static void UpdateUIState(struct etcpmon_app* app); static void on_connected(void* user_data); static void on_disconnected(void* user_data); static void on_conn_list(struct etcpmon_conn_info* list, uint8_t count, void* user_data); -static void on_metrics(struct etcpmon_rsp_metrics* metrics, - struct etcpmon_link_metrics* links, +static void on_socket_list(struct etcpmon_socket_info* list, uint8_t count, void* user_data); +static void on_metrics(struct etcpmon_rsp_metrics* metrics, + struct etcpmon_link_metrics* links, uint8_t links_count, void* user_data); static void on_error(const char* msg, void* user_data); @@ -131,9 +132,9 @@ HWND etcpmon_gui_create_window(struct etcpmon_app* app) { app->client = (struct etcpmon_client*)malloc(sizeof(struct etcpmon_client)); if (app->client) { etcpmon_client_init(app->client); - etcpmon_client_set_callbacks(app->client, + etcpmon_client_set_callbacks(app->client, on_connected, on_disconnected, - on_conn_list, on_metrics, on_error, + on_conn_list, on_socket_list, on_metrics, on_error, app); } @@ -198,7 +199,16 @@ static void CreateControls(struct etcpmon_app* app) { app->hListConnections = CreateWindowExA(WS_EX_CLIENTEDGE, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | LBS_NOINTEGRALHEIGHT, x, y + 20, 300, 120, hWnd, (HMENU)IDC_LIST_CONNECTIONS, hInst, NULL); - + + /* Sockets list */ + CreateWindowExA(0, "STATIC", "Sockets:", + WS_CHILD | WS_VISIBLE, + x + 310, y, 100, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + + app->hListSockets = CreateWindowExA(WS_EX_CLIENTEDGE, "LISTBOX", "", + WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | LBS_NOINTEGRALHEIGHT, + x + 310, y + 20, 300, 120, hWnd, (HMENU)IDC_LIST_SOCKETS, hInst, NULL); + /* Graph area */ y += 140; app->hGraphWnd = CreateWindowExA( @@ -790,6 +800,7 @@ static void OnTimer(struct etcpmon_app* app) { if (app->need_initial_request && app->isConnected) { etcpmon_client_request_list(app->client); + etcpmon_client_request_socket_list(app->client); app->need_initial_request = 0; } @@ -837,23 +848,28 @@ static void on_disconnected(void* user_data) { static void on_conn_list(struct etcpmon_conn_info* list, uint8_t count, void* user_data) { struct etcpmon_app* app = (struct etcpmon_app*)user_data; - + etcpmon_gui_update_conn_list(app, list, count); - + /* Автоматически выбираем первое соединение (только один раз) */ if (count > 0 && app->last_selected_peer_id == 0) { uint64_t first_id = list[0].peer_node_id; app->last_selected_peer_id = first_id; - + etcpmon_client_select_connection(app->client, first_id); - + /* Выделяем первую строку в списке */ SendMessage(app->hListConnections, LB_SETCURSEL, 0, 0); - + etcpmon_gui_set_status(app, "Auto-selected first connection"); } } +static void on_socket_list(struct etcpmon_socket_info* list, uint8_t count, void* user_data) { + struct etcpmon_app* app = (struct etcpmon_app*)user_data; + etcpmon_gui_update_socket_list(app, list, count); +} + static void on_metrics(struct etcpmon_rsp_metrics* metrics, struct etcpmon_link_metrics* links, uint8_t links_count, void* user_data) { @@ -904,26 +920,58 @@ void etcpmon_gui_set_status(struct etcpmon_app* app, const char* text) { if (!app || !app->hWndStatus) return; SendMessageA(app->hWndStatus, SB_SETTEXTA, 0, (LPARAM)text); } -void etcpmon_gui_update_conn_list(struct etcpmon_app* app, - struct etcpmon_conn_info* list, +void etcpmon_gui_update_conn_list(struct etcpmon_app* app, + struct etcpmon_conn_info* list, uint8_t count) { if (!app || !app->hListConnections) return; - + /* Clear existing items */ SendMessage(app->hListConnections, LB_RESETCONTENT, 0, 0); - + /* Add new items */ for (uint8_t i = 0; i < count; i++) { char display[64]; snprintf(display, sizeof(display), "%s (%016llX)", list[i].name, (unsigned long long)list[i].peer_node_id); - + int idx = (int)SendMessageA(app->hListConnections, LB_ADDSTRING, 0, (LPARAM)display); if (idx != LB_ERR) { SendMessage(app->hListConnections, LB_SETITEMDATA, idx, (LPARAM)list[i].peer_node_id); } } } + +static const char* nat_type_to_string(uint8_t nat_type) { + switch (nat_type) { + case NAT_TYPE_OPEN: return "Open"; + case NAT_TYPE_RESTRICTED: return "Restricted"; + case NAT_VERIFIED_OPEN: return "Verified-Open"; + case NAT_VERIFIED_RESTRICTED: return "Verified-Restricted"; + case NAT_VERIFIED_DIRECT: return "Direct"; + default: return "Unknown"; + } +} + +void etcpmon_gui_update_socket_list(struct etcpmon_app* app, + struct etcpmon_socket_info* list, + uint8_t count) { + if (!app || !app->hListSockets) return; + + SendMessage(app->hListSockets, LB_RESETCONTENT, 0, 0); + + for (uint8_t i = 0; i < count; i++) { + char display[128]; + const char* nat_str = nat_type_to_string(list[i].nat_type); + if (list[i].is_ipv6) { + snprintf(display, sizeof(display), "[%s]:%d | NAT: %s", + list[i].ip, list[i].port, nat_str); + } else { + snprintf(display, sizeof(display), "%s:%d | NAT: %s", + list[i].ip, list[i].port, nat_str); + } + SendMessageA(app->hListSockets, LB_ADDSTRING, 0, (LPARAM)display); + } +} /* Обновляет EDIT только если текст действительно изменился → убирает моргание */ static BOOL UpdateEditIfChanged(HWND hDlg, int nIDDlgItem, const char* fmt, ...) { diff --git a/tools/etcpmon/etcpmon_gui.h b/tools/etcpmon/etcpmon_gui.h index cc2d8109..880bda1c 100644 --- a/tools/etcpmon/etcpmon_gui.h +++ b/tools/etcpmon/etcpmon_gui.h @@ -17,6 +17,7 @@ /* Client structure is defined in etcpmon_client.h */ struct etcpmon_client; struct etcpmon_conn_info; +struct etcpmon_socket_info; struct etcpmon_rsp_metrics; struct etcpmon_link_metrics; @@ -37,6 +38,7 @@ extern "C" { #define IDC_BTN_CONNECT 102 #define IDC_BTN_DISCONNECT 103 #define IDC_LIST_CONNECTIONS 104 +#define IDC_LIST_SOCKETS 106 #define IDC_TIMER_UPDATE 105 /* ETCP Metrics control IDs */ @@ -187,7 +189,8 @@ struct etcpmon_app { HWND hBtnConnect; HWND hBtnDisconnect; HWND hListConnections; - + HWND hListSockets; + /* ETCP metrics controls */ HWND hEditEtcpName; HWND hEditEtcpRttLast; @@ -321,10 +324,15 @@ void etcpmon_gui_cleanup(struct etcpmon_app* app); void etcpmon_gui_set_status(struct etcpmon_app* app, const char* text); /* Update connection list */ -void etcpmon_gui_update_conn_list(struct etcpmon_app* app, - struct etcpmon_conn_info* list, +void etcpmon_gui_update_conn_list(struct etcpmon_app* app, + struct etcpmon_conn_info* list, uint8_t count); +/* Update socket list */ +void etcpmon_gui_update_socket_list(struct etcpmon_app* app, + struct etcpmon_socket_info* list, + uint8_t count); + /* Update metrics display */ void etcpmon_gui_update_metrics(struct etcpmon_app* app, struct etcpmon_rsp_metrics* metrics, diff --git a/tools/etcpmon/etcpmon_protocol.h b/tools/etcpmon/etcpmon_protocol.h index 5996eb46..d5338e0b 100644 --- a/tools/etcpmon/etcpmon_protocol.h +++ b/tools/etcpmon/etcpmon_protocol.h @@ -22,6 +22,15 @@ extern "C" { * ============================================================================ */ #define ETCPMON_PROTOCOL_VERSION 1 + +/* NAT types (mirrored from etcp_connections.h for monitor protocol) */ +#define NAT_TYPE_UNKNOWN 0 +#define NAT_TYPE_OPEN 1 +#define NAT_TYPE_RESTRICTED 2 +#define NAT_VERIFIED_UNKNOWN 4 +#define NAT_VERIFIED_OPEN 5 +#define NAT_VERIFIED_RESTRICTED 6 +#define NAT_VERIFIED_DIRECT 7 #define ETCPMON_MAX_MSG_SIZE 4096 #define ETCPMON_MAX_CONN_NAME 32 #define ETCPMON_MAX_CONNECTIONS 256 @@ -39,10 +48,12 @@ extern "C" { #define ETCPMON_CMD_SELECT_CONN 0x02 /* Select connection by peer_node_id */ #define ETCPMON_CMD_GET_METRICS 0x03 /* Request metrics for selected */ #define ETCPMON_CMD_DISCONNECT 0x04 /* Disconnect from monitoring */ +#define ETCPMON_CMD_LIST_SOCKETS 0x05 /* Request local socket list */ /* Server -> Client responses */ #define ETCPMON_RSP_CONN_LIST 0x81 /* Connection list response */ #define ETCPMON_RSP_METRICS 0x82 /* Metrics response */ +#define ETCPMON_RSP_SOCKET_LIST 0x83 /* Socket list response */ #define ETCPMON_RSP_ERROR 0xFF /* Error response */ /* Error codes for RSP_ERROR */ @@ -94,6 +105,20 @@ struct etcpmon_rsp_conn_list { /* Followed by count * struct etcpmon_conn_info */ }; +/* RSP_SOCKET_LIST: List of local sockets */ +struct etcpmon_socket_info { + char name[ETCPMON_MAX_CONN_NAME]; /* Null-terminated socket name */ + char ip[46]; /* Null-terminated IP address string */ + uint16_t port; /* Port number (host byte order) */ + uint8_t is_ipv6; /* 1 = IPv6, 0 = IPv4 */ + uint8_t nat_type; /* NAT type (NAT_TYPE_*, NAT_VERIFIED_*) */ +}; + +struct etcpmon_rsp_socket_list { + uint8_t count; /* Number of sockets */ + /* Followed by count * struct etcpmon_socket_info */ +}; + /* RSP_METRICS: Full metrics for ETCP connection and its links */ /* ETCP Connection metrics */ @@ -250,6 +275,11 @@ struct etcpmon_rsp_error { (sizeof(struct etcpmon_msg_header) + sizeof(struct etcpmon_rsp_conn_list) + \ (count) * sizeof(struct etcpmon_conn_info)) +/* Calculate size of socket list response */ +#define ETCPMON_SOCKET_LIST_SIZE(count) \ + (sizeof(struct etcpmon_msg_header) + sizeof(struct etcpmon_rsp_socket_list) + \ + (count) * sizeof(struct etcpmon_socket_info)) + /* Calculate size of metrics response */ #define ETCPMON_METRICS_SIZE(links_count) \ (sizeof(struct etcpmon_msg_header) + sizeof(struct etcpmon_rsp_metrics) + \