From 54510aaae975c14801d3471eba43d7a9edabc518 Mon Sep 17 00:00:00 2001 From: Armands Arseniuss Skolmeisters Date: Fri, 17 Mar 2023 17:43:32 +0200 Subject: ..FG...PS. [ZBX-21795] handled ASCII escape sequences in telnet diff --git a/src/libs/zbxcomms/Makefile.am b/src/libs/zbxcomms/Makefile.am index 08504770bc0..ae7b7888d6a 100644 --- a/src/libs/zbxcomms/Makefile.am +++ b/src/libs/zbxcomms/Makefile.am @@ -5,6 +5,7 @@ noinst_LIBRARIES = libzbxcomms.a libzbxcomms_a_SOURCES = \ comms.h \ comms.c \ + telnet.h \ telnet.c \ tls.h \ tls.c \ diff --git a/src/libs/zbxcomms/telnet.c b/src/libs/zbxcomms/telnet.c index c6073971fc6..7f6e1317ea7 100644 --- a/src/libs/zbxcomms/telnet.c +++ b/src/libs/zbxcomms/telnet.c @@ -22,6 +22,7 @@ #include "log.h" #include "zbxstr.h" +#include "telnet.h" #define WAIT_READ 0 #define WAIT_WRITE 1 @@ -151,7 +152,7 @@ static ssize_t telnet_read(ZBX_SOCKET socket_fd, char *buf, size_t *buf_left, si if (1 > (rc = telnet_socket_read(socket_fd, &c1, 1))) break; - zabbix_log(LOG_LEVEL_DEBUG, "%s() c1:[%x=%c]", __func__, c1, isprint(c1) ? c1 : ' '); + zabbix_log(LOG_LEVEL_DEBUG, "%s() c1:[%x=%c]", __func__, c1, 0 != isprint(c1) ? c1 : ' '); switch (c1) { @@ -287,16 +288,56 @@ static void convert_unix_to_telnet_eol(const char *buf, size_t offset, char *out } } -static char telnet_lastchar(const char *buf, size_t offset) +static size_t telnet_strnrchr(const char *buf, size_t offset, char c, size_t n) { + for (size_t i = 1; n + 1 > i && i <= offset; i++) + { + if (buf[offset - i] == c) + return offset - i; + } + + return offset; +} + +char telnet_lastchar(const char *buf, size_t offset) +{ +#define ESC_MAX_LEN 6 +#define ESC_CHR 0x1B + size_t i, j; + while (0 < offset) { + /* look for escape sequence */ + if ((i = telnet_strnrchr(buf, offset, ESC_CHR, ESC_MAX_LEN)) != offset) + { + j = i; + if (0 != isalpha(buf[j + 1])) j += 2; /* ESC %c (C1 7/8bit control charaters) */ + else if (buf[j + 1] == '[') /* ESC [ (%d)* %c (CSI - Control sequence introducer)*/ + { + j += 2; + while (j < offset && (0 != isdigit(buf[j]) || ';' == buf[j])) j++; + if (j < offset && 0 != isalpha(buf[j])) + j++; + else + return buf[offset - 1]; + } + + if (j == offset) + { + /* move past escape sequence */ + offset = i; + continue; + } + } + offset--; if (' ' != buf[offset]) return buf[offset]; } return '\0'; +#undef ESC_MAX_LEN +#undef ESC_CHR } static int telnet_rm_echo(char *buf, size_t *offset, const char *echo, size_t len) diff --git a/src/libs/zbxcomms/telnet.h b/src/libs/zbxcomms/telnet.h new file mode 100644 index 00000000000..9049630e1a8 --- /dev/null +++ b/src/libs/zbxcomms/telnet.h @@ -0,0 +1,27 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +** This program 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 this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#ifndef ZABBIX_TELNET_H +#define ZABBIX_TELNET_H + +#include "zbxcommon.h" + +char telnet_lastchar(const char *buf, size_t offset); + +#endif /* ZABBIX_TELNET_H */ diff --git a/tests/libs/zbxcomms/Makefile.am b/tests/libs/zbxcomms/Makefile.am index d858ef5d181..629fd5f1d11 100644 --- a/tests/libs/zbxcomms/Makefile.am +++ b/tests/libs/zbxcomms/Makefile.am @@ -3,6 +3,7 @@ noinst_PROGRAMS = zbx_tcp_check_allowed_peers else noinst_PROGRAMS = zbx_tcp_check_allowed_peers_ipv4 endif +noinst_PROGRAMS += telnet_lastchar COMMON_SRC_FILES = \ ../../zbxmocktest.h @@ -74,3 +75,11 @@ zbx_tcp_check_allowed_peers_ipv4_LDFLAGS = @AGENT_LDFLAGS@ zbx_tcp_check_allowed_peers_ipv4_CFLAGS = $(COMMON_COMPILER_FLAGS) endif +telnet_lastchar_SOURCES = \ + telnet_lastchar.c \ + $(COMMON_SRC_FILES) + +telnet_lastchar_LDADD = \ + $(COMMON_LIB_FILES) + +telnet_lastchar_CFLAGS = $(COMMON_COMPILER_FLAGS) diff --git a/tests/libs/zbxcomms/telnet_lastchar.c b/tests/libs/zbxcomms/telnet_lastchar.c new file mode 100644 index 00000000000..1e4cc4b7bcc --- /dev/null +++ b/tests/libs/zbxcomms/telnet_lastchar.c @@ -0,0 +1,48 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program 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 2 of the License, or +** (at your option) any later version. +** +** This program 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 this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "zbxmocktest.h" +#include "zbxmockdata.h" +#include "zbxmockutil.h" + +#include "zbxstr.h" + +#include "../../../src/libs/zbxcomms/telnet.h" + +void zbx_mock_test_entry(void **state) +{ + const char *line, *expected_result; + uint64_t actual_result; + + ZBX_UNUSED(state); + + line = zbx_mock_get_parameter_string("in.buf"); + + expected_result = zbx_mock_get_parameter_string("out.value"); + + if (expected_result[0] != (actual_result = telnet_lastchar(line, strlen(line)))) + { + const char replace_char[2] = { 0x1b, 0 }; + char *str = zbx_string_replace(line, replace_char, "\\e"); + + fail_msg("Got %c instead of %c as a result \"%s\".", actual_result, expected_result[0], str); + + free(str); + } +} diff --git a/tests/libs/zbxcomms/telnet_lastchar.yaml b/tests/libs/zbxcomms/telnet_lastchar.yaml new file mode 100644 index 00000000000..cd3ddec94a7 --- /dev/null +++ b/tests/libs/zbxcomms/telnet_lastchar.yaml @@ -0,0 +1,99 @@ +--- +test case: Empty string +in: + buf: '' +out: + value: '' # null character +--- +test case: One character +in: + buf: '1' +out: + value: '1' +--- +test case: Seven characters +in: + buf: '7654321' +out: + value: '1' +--- +test case: One space +in: + buf: '7654321 ' +out: + value: '1' +--- +test case: Some space +in: + buf: '7654321 ' +out: + value: '1' +--- +test case: Only escape character +in: + buf: "7654321 \x1b" + offset: 11 +out: + value: "\x1b" +--- +test case: Two character escape sequence +in: + buf: "7654321 \x1bQ" + offset: 12 +out: + value: '1' +--- +test case: Unfinished multi-character escape sequence +in: + buf: "7654321 \x1b[" +out: + value: '[' +--- +test case: Unfinished multi-character escape sequence with digits +in: + buf: "7654321 \x1b[1;3" +out: + value: '3' +--- +test case: Multi-character escape sequence +in: + buf: "7654321 \x1b[K" +out: + value: '1' +--- +test case: Multi-character escape sequence with digits +in: + buf: "7654321 \x1b[54K" +out: + value: '1' +--- +test case: Multiple multi-character escape sequence +in: + buf: "7654321 \x1bQ\x1b[K" +out: + value: '1' +--- +test case: Multiple multi-character escape sequence with digits +in: + buf: "7654321 \x1b[12K\x1bQ\x1b[K" +out: + value: '1' +--- +test case: Multiple multi-character escape sequence with digits +in: + buf: "7654321 \x1b[12K \x1bQ\x1b[K" +out: + value: '1' +--- +test case: Unfinished multiple multi-character escape sequence with digits +in: + buf: "7654321 \x1b[12K \x1bQ\x1b[" +out: + value: '[' +--- +test case: Multiple multi-character escape sequence with digits +in: + buf: " \x1b[12K \x1bQ" +out: + value: '' +...