Browse Source

Validate etcpmon response strings and close failed streams

master
evgeny 1 week ago
parent
commit
d62916edf0
  1. 2
      tools/etcpmon/Makefile
  2. 45
      tools/etcpmon/etcpmon_client.c
  3. 29
      tools/etcpmon/test_etcpmon.c

2
tools/etcpmon/Makefile

@ -24,6 +24,8 @@ $(TARGET): $(OBJS)
@echo Linking $@
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
$(OBJS): $(wildcard *.h)
%.o: %.c
@echo Compiling $<
$(CC) $(CFLAGS) -c -o $@ $<

45
tools/etcpmon/etcpmon_client.c

@ -468,6 +468,9 @@ int etcpmon_client_send_request(struct etcpmon_client* client,
(unsigned long long)get_timestamp_ms(), type, sent, total_size, sent == SOCKET_ERROR ? WSAGetLastError() : 0);
fflush(client->log_file);
}
/* После частичной отправки следующий пакет уже нельзя писать в этот поток. */
etcpmon_client_disconnect(client);
if (client->on_disconnected) client->on_disconnected(client->user_data);
return -1;
}
@ -626,7 +629,8 @@ static void handle_response(struct etcpmon_client* client, uint8_t msg_type,
}
case ETCPMON_RSP_ERROR: {
if (payload_size >= sizeof(struct etcpmon_rsp_error)) {
if (payload_size > sizeof(struct etcpmon_rsp_error) &&
memchr(payload + sizeof(struct etcpmon_rsp_error), 0, payload_size - sizeof(struct etcpmon_rsp_error))) {
struct etcpmon_rsp_error* rsp = (struct etcpmon_rsp_error*)payload;
const char* msg = (const char*)(payload + sizeof(*rsp));
@ -639,12 +643,15 @@ static void handle_response(struct etcpmon_client* client, uint8_t msg_type,
if (client->on_error) {
client->on_error(msg, client->user_data);
}
} else if (client->log_file) {
fprintf(client->log_file, "[ERROR] Invalid RSP_ERROR string: size=%u\n", payload_size);
}
break;
}
case ETCPMON_RSP_ACTION_RESULT: {
if (payload_size >= sizeof(struct etcpmon_rsp_action_result)) {
if (payload_size > sizeof(struct etcpmon_rsp_action_result) &&
memchr(payload + sizeof(struct etcpmon_rsp_action_result), 0, payload_size - sizeof(struct etcpmon_rsp_action_result))) {
const char* text = (const char*)(payload + sizeof(struct etcpmon_rsp_action_result));
if (client->log_file) {
@ -656,15 +663,23 @@ static void handle_response(struct etcpmon_client* client, uint8_t msg_type,
if (client->on_action_result) {
client->on_action_result(text, client->user_data);
}
} else if (client->log_file) {
fprintf(client->log_file, "[ERROR] Invalid RSP_ACTION_RESULT string: size=%u\n", payload_size);
}
break;
}
case ETCPMON_RSP_DEBUG_CONFIG: {
if (payload_size >= 2) {
const uint8_t* names_end = payload_size > 2 ? memchr(payload + 2, 0, payload_size - 2) : NULL;
if (!names_end || payload[1] > ETCPMON_MAX_DEBUG_CATEGORIES ||
(size_t)(names_end - payload) + 1 + payload[1] != payload_size) {
if (client->log_file) fprintf(client->log_file, "[ERROR] Invalid RSP_DEBUG_CONFIG: size=%u\n", payload_size);
break;
}
{
client->debug_global_level = payload[0];
client->debug_category_count = payload[1];
uint16_t names_len = (uint16_t)strlen((const char*)(payload + 2)) + 1;
uint16_t names_len = (uint16_t)(names_end - payload - 2) + 1;
if (2 + names_len <= payload_size) {
size_t copy_len = names_len < sizeof(client->debug_category_names) ? names_len : sizeof(client->debug_category_names) - 1;
memcpy(client->debug_category_names, payload + 2, copy_len);
@ -1000,8 +1015,10 @@ int etcpmon_client_send_debug_config(struct etcpmon_client* client,
memcpy(payload + 2, levels, count);
uint16_t payload_size = 2 + count;
struct etcpmon_msg_header hdr;
etcpmon_build_header(&hdr, payload_size, ETCPMON_CMD_SET_DEBUG_CONFIG, 0);
uint8_t buffer[sizeof(struct etcpmon_msg_header) + sizeof(payload)];
struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer;
etcpmon_build_header(hdr, payload_size, ETCPMON_CMD_SET_DEBUG_CONFIG, 0);
memcpy(buffer + sizeof(*hdr), payload, payload_size);
if (client->log_file) {
fprintf(client->log_file, "%llu: [LOG] CMD_SET_DEBUG_CONFIG global=%d count=%d\n",
@ -1009,10 +1026,18 @@ int etcpmon_client_send_debug_config(struct etcpmon_client* client,
fflush(client->log_file);
}
int sent = send(client->sock, (const char*)&hdr, sizeof(hdr), 0);
if (sent != sizeof(hdr)) return -1;
sent = send(client->sock, (const char*)payload, payload_size, 0);
if (sent != payload_size) return -1;
int total_size = sizeof(*hdr) + payload_size;
int sent = send(client->sock, (const char*)buffer, total_size, 0);
if (sent != total_size) {
if (client->log_file) {
fprintf(client->log_file, "[ERROR] Failed to send debug config: sent=%d/%d error=%d\n",
sent, total_size, sent == SOCKET_ERROR ? WSAGetLastError() : 0);
fflush(client->log_file);
}
etcpmon_client_disconnect(client);
if (client->on_disconnected) client->on_disconnected(client->user_data);
return -1;
}
client->debug_global_level = global_level;
for (uint8_t i = 0; i < count; i++) client->debug_category_levels[i] = levels[i];
return 0;

29
tools/etcpmon/test_etcpmon.c

@ -129,6 +129,7 @@ int main(void) {
SendMessage(app.hListConnections, LB_SETCURSEL, 1, 0);
OnConnectionSelect(&app);
assert(client.selected_peer_id == list[1].peer_node_id);
assert(drain_commands(ETCPMON_CMD_SELECT_CONN) == 2);
struct etcpmon_conn_info reordered[3] = {list[1], list[2], list[0]};
strcpy(reordered[0].name, "renamed");
deliver_list(reordered, 3);
@ -202,6 +203,34 @@ int main(void) {
assert(client.pending_head == NULL && !client.connected && app.need_initial_request);
assert(SendMessage(app.hListConnections, LB_GETCOUNT, 0, 0) == 0);
puts("Malformed debug responses are rejected and socket snapshots are not capped at 16");
client.debug_config_dirty = 0;
const uint8_t invalid_names[] = {1, 0, 'x'};
handle_response(&client, ETCPMON_RSP_DEBUG_CONFIG, invalid_names, sizeof(invalid_names));
assert(!client.debug_config_dirty);
const uint8_t missing_levels[] = {1, 2, 'a', ',', 'b', 0, 1};
handle_response(&client, ETCPMON_RSP_DEBUG_CONFIG, missing_levels, sizeof(missing_levels));
assert(!client.debug_config_dirty);
const uint8_t valid_debug[] = {1, 2, 'a', ',', 'b', 0, 1, 2};
handle_response(&client, ETCPMON_RSP_DEBUG_CONFIG, valid_debug, sizeof(valid_debug));
assert(client.debug_config_dirty && client.debug_category_levels[1] == 2);
uint8_t socket_payload[1 + 17 * sizeof(struct etcpmon_socket_info)] = {17};
struct etcpmon_socket_info* sockets = (struct etcpmon_socket_info*)(socket_payload + 1);
for (int i = 0; i < 17; i++) {
strcpy(sockets[i].name, "socket");
strcpy(sockets[i].ip, "127.0.0.1");
}
handle_response(&client, ETCPMON_RSP_SOCKET_LIST, socket_payload, sizeof(socket_payload));
assert(client.socket_count == 17 && SendMessage(app.hListSockets, LB_GETCOUNT, 0, 0) == 17);
puts("A send failure clears pending requests and allows another connection");
closesocket(server_sock);
connect_client();
assert(etcpmon_client_send_request(&client, ETCPMON_CMD_ACTION, NULL, 0, request_callback, NULL) == 0);
assert(shutdown(client.sock, SD_SEND) == 0);
assert(etcpmon_client_send_request(&client, ETCPMON_CMD_ACTION, NULL, 0, request_callback, NULL) == -1);
assert(!client.connected && client.pending_head == NULL && client.pending_tail == NULL && app.need_initial_request);
etcpmon_client_cleanup(&client);
closesocket(server_sock);
closesocket(listener);

Loading…
Cancel
Save