1
0
mirror of https://git.libssh.org/projects/libssh.git synced 2025-12-09 15:41:10 +03:00

Issue #157: Use the current TTY's settings by default.

When opening a PTY on the server, try to use the current TTY's settings
(i.e. based on STDIN). If that fails or STDIN isn't a TTY, use default
modes that avoid any character translation.

Don't rely on stdin to be a TTY (breaks CI). Instead, open a PTY and
temporarily use that as "fake" stdin.

Signed-off-by: Daniel Evers (daniel.evers@utimaco.com)
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
Daniel Evers
2023-10-28 16:16:11 +02:00
committed by Jakub Jelen
parent b5daac6772
commit cd6e84a6c3
6 changed files with 589 additions and 17 deletions

View File

@@ -47,6 +47,10 @@
# endif
#endif /* !defined(HAVE_STRTOULL) */
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
@@ -452,6 +456,10 @@ bool is_ssh_initialized(void);
#define SSH_ERRNO_MSG_MAX 1024
char *ssh_strerror(int err_num, char *buf, size_t buflen);
/** 55 defined options (5 bytes each) + terminator */
#define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1)
int encode_current_tty_opts(unsigned char *buf, size_t buflen);
#ifdef __cplusplus
}
#endif

View File

@@ -127,6 +127,7 @@ set(libssh_SRCS
socket.c
string.c
threads.c
ttyopts.c
wrapper.c
external/bcrypt_pbkdf.c
external/blowfish.c

View File

@@ -1991,9 +1991,18 @@ error:
int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
int col, int row)
{
/* default modes/options: none */
const unsigned char modes[1] = {0};
return ssh_channel_request_pty_size_modes(channel, terminal, col, row, modes, sizeof(modes));
/* use modes from the current TTY */
unsigned char modes_buf[SSH_TTY_MODES_MAX_BUFSIZE];
int rc = encode_current_tty_opts(modes_buf, sizeof(modes_buf));
if (rc < 0) {
return rc;
}
return ssh_channel_request_pty_size_modes(channel,
terminal,
col,
row,
modes_buf,
(size_t)rc);
}
/**

442
src/ttyopts.c Normal file
View File

@@ -0,0 +1,442 @@
/*
* ttyopts.c - encoding of TTY modes.
*
* This file is part of the SSH Library
*
* Copyright (c) 2023 by Utimaco TS GmbH <oss_committee@utimaco.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#include <libssh/priv.h>
#include <string.h>
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
/** Terminal mode opcodes */
enum {
TTY_OP_END = 0,
TTY_OP_VINTR = 1,
TTY_OP_VQUIT = 2,
TTY_OP_VERASE = 3,
TTY_OP_VKILL = 4,
TTY_OP_VEOF = 5,
TTY_OP_VEOL = 6,
TTY_OP_VEOL2 = 7,
TTY_OP_VSTART = 8,
TTY_OP_VSTOP = 9,
TTY_OP_VSUSP = 10,
TTY_OP_VDSUSP = 11,
TTY_OP_VREPRINT = 12,
TTY_OP_VWERASE = 13,
TTY_OP_VLNEXT = 14,
TTY_OP_VFLUSH = 15,
TTY_OP_VSWTC = 16,
TTY_OP_VSTATUS = 17,
TTY_OP_VDISCARD = 18,
TTY_OP_IGNPAR = 30,
TTY_OP_PARMRK = 31,
TTY_OP_INPCK = 32,
TTY_OP_ISTRIP = 33,
TTY_OP_INLCR = 34,
TTY_OP_IGNCR = 35,
TTY_OP_ICRNL = 36,
TTY_OP_IUCLC = 37,
TTY_OP_IXON = 38,
TTY_OP_IXANY = 39,
TTY_OP_IXOFF = 40,
TTY_OP_IMAXBEL = 41,
TTY_OP_IUTF8 = 42,
TTY_OP_ISIG = 50,
TTY_OP_ICANON = 51,
TTY_OP_XCASE = 52,
TTY_OP_ECHO = 53,
TTY_OP_ECHOE = 54,
TTY_OP_ECHOK = 55,
TTY_OP_ECHONL = 56,
TTY_OP_NOFLSH = 57,
TTY_OP_TOSTOP = 58,
TTY_OP_IEXTEN = 59,
TTY_OP_ECHOCTL = 60,
TTY_OP_ECHOKE = 61,
TTY_OP_PENDIN = 62,
TTY_OP_OPOST = 70,
TTY_OP_OLCUC = 71,
TTY_OP_ONLCR = 72,
TTY_OP_OCRNL = 73,
TTY_OP_ONOCR = 74,
TTY_OP_ONLRET = 75,
TTY_OP_CS7 = 90,
TTY_OP_CS8 = 91,
TTY_OP_PARENB = 92,
TTY_OP_PARODD = 93,
TTY_OP_ISPEED = 128,
TTY_OP_OSPEED = 129,
};
/**
* Encodes a single SSH terminal mode option into the buffer.
*
* @param[in] attr The mode's opcode value.
*
* @param[in] value The mode's value.
*
* @param[out] buf Destination buffer to encode into.
*
* @param[in] buflen The length of the buffer.
*
* @return number of bytes written to the buffer on success, -1 on
* error.
*/
static int
encode_termios_opt(unsigned char opcode,
uint32_t value,
unsigned char *buf,
size_t buflen)
{
int offset = 0;
/* always need 5 bytes */
if (buflen < 5) {
return -1;
}
/* 1 byte opcode */
buf[offset++] = opcode;
/* 4 bytes value (big endian) */
value = htonl(value);
memcpy(buf + offset, &value, sizeof(value));
offset += sizeof(value);
return offset;
}
#ifdef HAVE_TERMIOS_H
/** Converts a baudrate constant (Bxxxx) to a numeric value. */
static int
baud2speed(int baudrate)
{
switch (baudrate) {
default:
case B0:
return 0;
case B50:
return 50;
case B75:
return 75;
case B110:
return 110;
case B134:
return 134;
case B150:
return 150;
case B200:
return 200;
case B300:
return 300;
case B600:
return 600;
case B1200:
return 1200;
case B1800:
return 1800;
case B2400:
return 2400;
case B4800:
return 4800;
case B9600:
return 9600;
case B19200:
return 19200;
case B38400:
return 38400;
case B57600:
return 57600;
case B115200:
return 115200;
case B230400:
return 230400;
}
}
/**
* Encodes all terminal options from the given \c termios structure
* into the buffer.
*
* @param[in] attr The terminal options to encode.
*
* @param[out] buf Modes will be encoded into this buffer.
*
* @param[in] buflen The length of the buffer.
*
* @return number of bytes in the buffer on success, -1 on error.
*/
static int
encode_termios_opts(struct termios *attr, unsigned char *buf, size_t buflen)
{
unsigned int offset = 0;
int rc;
#define SSH_ENCODE_OPT(code, value) \
rc = encode_termios_opt(code, value, buf + offset, buflen - offset); \
if (rc < 0) { \
return rc; \
} else { \
offset += rc; \
}
#define SSH_ENCODE_INPUT_OPT(opt) \
SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_iflag & opt) ? 1 : 0)
SSH_ENCODE_INPUT_OPT(IGNPAR)
SSH_ENCODE_INPUT_OPT(PARMRK)
SSH_ENCODE_INPUT_OPT(INPCK)
SSH_ENCODE_INPUT_OPT(ISTRIP)
SSH_ENCODE_INPUT_OPT(INLCR)
SSH_ENCODE_INPUT_OPT(IGNCR)
SSH_ENCODE_INPUT_OPT(ICRNL)
SSH_ENCODE_INPUT_OPT(IUCLC)
SSH_ENCODE_INPUT_OPT(IXON)
SSH_ENCODE_INPUT_OPT(IXANY)
SSH_ENCODE_INPUT_OPT(IXOFF)
SSH_ENCODE_INPUT_OPT(IMAXBEL)
#ifdef IUTF8
SSH_ENCODE_INPUT_OPT(IUTF8)
#endif
#undef SSH_ENCODE_INPUT_OPT
#define SSH_ENCODE_OUTPUT_OPT(opt) \
SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_oflag & opt) ? 1 : 0)
SSH_ENCODE_OUTPUT_OPT(OPOST)
SSH_ENCODE_OUTPUT_OPT(OLCUC)
SSH_ENCODE_OUTPUT_OPT(ONLCR)
SSH_ENCODE_OUTPUT_OPT(OCRNL)
SSH_ENCODE_OUTPUT_OPT(ONOCR)
SSH_ENCODE_OUTPUT_OPT(ONLRET)
#undef SSH_ENCODE_OUTPUT_OPT
#define SSH_ENCODE_CONTROL_OPT(opt) \
SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_cflag & opt) ? 1 : 0)
SSH_ENCODE_CONTROL_OPT(CS7)
SSH_ENCODE_CONTROL_OPT(CS8)
SSH_ENCODE_CONTROL_OPT(PARENB)
SSH_ENCODE_CONTROL_OPT(PARODD)
#undef SSH_ENCODE_CONTROL_OPT
#define SSH_ENCODE_LOCAL_OPT(opt) \
SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_lflag & opt) ? 1 : 0)
SSH_ENCODE_LOCAL_OPT(ISIG)
SSH_ENCODE_LOCAL_OPT(ICANON)
SSH_ENCODE_LOCAL_OPT(XCASE)
SSH_ENCODE_LOCAL_OPT(ECHO)
SSH_ENCODE_LOCAL_OPT(ECHOE)
SSH_ENCODE_LOCAL_OPT(ECHOK)
SSH_ENCODE_LOCAL_OPT(ECHONL)
SSH_ENCODE_LOCAL_OPT(NOFLSH)
SSH_ENCODE_LOCAL_OPT(TOSTOP)
SSH_ENCODE_LOCAL_OPT(IEXTEN)
SSH_ENCODE_LOCAL_OPT(ECHOCTL)
SSH_ENCODE_LOCAL_OPT(ECHOKE)
SSH_ENCODE_LOCAL_OPT(PENDIN)
#undef SSH_ENCODE_LOCAL_OPT
#define SSH_ENCODE_CC_OPT(opt) SSH_ENCODE_OPT(TTY_OP_##opt, attr->c_cc[opt])
SSH_ENCODE_CC_OPT(VINTR)
SSH_ENCODE_CC_OPT(VQUIT)
SSH_ENCODE_CC_OPT(VERASE)
SSH_ENCODE_CC_OPT(VKILL)
SSH_ENCODE_CC_OPT(VEOF)
SSH_ENCODE_CC_OPT(VEOL)
SSH_ENCODE_CC_OPT(VEOL2)
SSH_ENCODE_CC_OPT(VSTART)
SSH_ENCODE_CC_OPT(VSTOP)
SSH_ENCODE_CC_OPT(VSUSP)
#ifdef VDSUSP
SSH_ENCODE_CC_OPT(VDSUSP)
#endif
SSH_ENCODE_CC_OPT(VREPRINT)
SSH_ENCODE_CC_OPT(VWERASE)
SSH_ENCODE_CC_OPT(VLNEXT)
#ifdef VFLUSH
SSH_ENCODE_CC_OPT(VFLUSH)
#endif
#ifdef VSWTC
SSH_ENCODE_CC_OPT(VSWTC)
#endif
#ifdef VSTATUS
SSH_ENCODE_CC_OPT(VSTATUS)
#endif
SSH_ENCODE_CC_OPT(VDISCARD)
#undef SSH_ENCODE_CC_OPT
SSH_ENCODE_OPT(TTY_OP_ISPEED, baud2speed(cfgetispeed(attr)))
SSH_ENCODE_OPT(TTY_OP_OSPEED, baud2speed(cfgetospeed(attr)))
#undef SSH_ENCODE_OPT
/* end of options */
if (buflen > offset) {
buf[offset++] = TTY_OP_END;
} else {
return -1;
}
return (int)offset;
}
#endif
/**
* Encodes a set of default options to ensure "sane" PTY behavior.
* This function intentionally doesn't use the \c termios structure
* to allow it to work on Windows as well.
*
* @param[out] buf Modes will be encoded into this buffer.
*
* @param[in] buflen The length of the buffer.
*
* @return number of bytes in the buffer on success, -1 on error.
*/
static int
encode_default_opts(unsigned char *buf, size_t buflen)
{
unsigned int offset = 0;
int rc;
#define SSH_ENCODE_OPT(code, value) \
rc = encode_termios_opt(code, value, buf + offset, buflen - offset); \
if (rc < 0) { \
return rc; \
} else { \
offset += rc; \
}
SSH_ENCODE_OPT(TTY_OP_VINTR, 003)
SSH_ENCODE_OPT(TTY_OP_VQUIT, 034)
SSH_ENCODE_OPT(TTY_OP_VERASE, 0177)
SSH_ENCODE_OPT(TTY_OP_VKILL, 025)
SSH_ENCODE_OPT(TTY_OP_VEOF, 0)
SSH_ENCODE_OPT(TTY_OP_VEOL, 0)
SSH_ENCODE_OPT(TTY_OP_VEOL2, 0)
SSH_ENCODE_OPT(TTY_OP_VSTART, 021)
SSH_ENCODE_OPT(TTY_OP_VSTOP, 023)
SSH_ENCODE_OPT(TTY_OP_VSUSP, 032)
SSH_ENCODE_OPT(TTY_OP_VDSUSP, 031)
SSH_ENCODE_OPT(TTY_OP_VREPRINT, 022)
SSH_ENCODE_OPT(TTY_OP_VWERASE, 027)
SSH_ENCODE_OPT(TTY_OP_VLNEXT, 026)
SSH_ENCODE_OPT(TTY_OP_VDISCARD, 017)
SSH_ENCODE_OPT(TTY_OP_IGNPAR, 0)
SSH_ENCODE_OPT(TTY_OP_PARMRK, 0)
SSH_ENCODE_OPT(TTY_OP_INPCK, 0)
SSH_ENCODE_OPT(TTY_OP_ISTRIP, 0)
SSH_ENCODE_OPT(TTY_OP_INLCR, 0)
SSH_ENCODE_OPT(TTY_OP_IGNCR, 0)
SSH_ENCODE_OPT(TTY_OP_ICRNL, 0)
SSH_ENCODE_OPT(TTY_OP_IUCLC, 0)
SSH_ENCODE_OPT(TTY_OP_IXON, 1)
SSH_ENCODE_OPT(TTY_OP_IXANY, 0)
SSH_ENCODE_OPT(TTY_OP_IXOFF, 0)
SSH_ENCODE_OPT(TTY_OP_IMAXBEL, 0)
SSH_ENCODE_OPT(TTY_OP_IUTF8, 1)
SSH_ENCODE_OPT(TTY_OP_ISIG, 1)
SSH_ENCODE_OPT(TTY_OP_ICANON, 1)
SSH_ENCODE_OPT(TTY_OP_XCASE, 0)
SSH_ENCODE_OPT(TTY_OP_ECHO, 1)
SSH_ENCODE_OPT(TTY_OP_ECHOE, 1)
SSH_ENCODE_OPT(TTY_OP_ECHOK, 1)
SSH_ENCODE_OPT(TTY_OP_ECHONL, 0)
SSH_ENCODE_OPT(TTY_OP_NOFLSH, 0)
SSH_ENCODE_OPT(TTY_OP_TOSTOP, 0)
SSH_ENCODE_OPT(TTY_OP_IEXTEN, 1)
SSH_ENCODE_OPT(TTY_OP_ECHOCTL, 0)
SSH_ENCODE_OPT(TTY_OP_ECHOKE, 1)
SSH_ENCODE_OPT(TTY_OP_PENDIN, 0)
SSH_ENCODE_OPT(TTY_OP_OPOST, 1)
SSH_ENCODE_OPT(TTY_OP_OLCUC, 0)
SSH_ENCODE_OPT(TTY_OP_ONLCR, 0)
SSH_ENCODE_OPT(TTY_OP_OCRNL, 0)
SSH_ENCODE_OPT(TTY_OP_ONOCR, 0)
SSH_ENCODE_OPT(TTY_OP_ONLRET, 0)
SSH_ENCODE_OPT(TTY_OP_CS7, 1)
SSH_ENCODE_OPT(TTY_OP_CS8, 1)
SSH_ENCODE_OPT(TTY_OP_PARENB, 0)
SSH_ENCODE_OPT(TTY_OP_PARODD, 0)
SSH_ENCODE_OPT(TTY_OP_ISPEED, 38400);
SSH_ENCODE_OPT(TTY_OP_OSPEED, 38400);
#undef SSH_ENCODE_OPT
/* end of options */
if (buflen > offset) {
buf[offset++] = TTY_OP_END;
} else {
return -1;
}
return (int)offset;
}
/**
* @ingroup libssh_misc
*
* @brief Encode the current TTY options as SSH modes.
*
* Call this function to determine the settings of the process' TTY and
* encode them as SSH Terminal Modes according to RFC 4254 section 8.
*
* If STDIN isn't connected to a TTY, this function fills the buffer with
* "sane" default modes.
*
* The encoded modes can be passed to \c ssh_channel_request_pty_size_modes .
*
* @code
* unsigned char modes_buf[SSH_TTY_MODES_MAX_BUFSIZE];
* encode_current_tty_opts(modes_buf, sizeof(modes_buf));
* @endcode
*
*
* @param[out] buf Modes will be encoded into this buffer.
*
* @param[in] buflen The length of the buffer.
*
* @return number of bytes in the buffer on success, -1 on error.
*/
int
encode_current_tty_opts(unsigned char *buf, size_t buflen)
{
#ifdef HAVE_TERMIOS_H
struct termios attr;
ZERO_STRUCT(attr);
if (isatty(STDIN_FILENO)) {
/* get local terminal attributes */
if (tcgetattr(STDIN_FILENO, &attr) < 0) {
perror("tcgetattr");
return -1;
}
return encode_termios_opts(&attr, buf, buflen);
}
#endif
/* use "sane" default attributes */
return encode_default_opts(buf, buflen);
}

View File

@@ -67,7 +67,7 @@ foreach(_CLI_TEST ${LIBSSH_CLIENT_TESTS})
add_cmocka_test(${_CLI_TEST}
SOURCES ${_CLI_TEST}.c
COMPILE_OPTIONS ${DEFAULT_C_COMPILE_FLAGS}
LINK_LIBRARIES ${TORTURE_LIBRARY}
LINK_LIBRARIES ${TORTURE_LIBRARY} util
)
if (OSX)

View File

@@ -27,8 +27,13 @@
#include <libssh/libssh.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <pty.h>
static int sshd_setup(void **state)
{
@@ -75,6 +80,26 @@ static int session_teardown(void **state)
return 0;
}
/* reads from the channel, expecting the given output */
static int check_channel_output(ssh_channel c, const char *expected)
{
char buffer[4096] = {0};
int nbytes;
nbytes = ssh_channel_read(c, buffer, sizeof(buffer) - 1, 0);
while (nbytes > 0) {
buffer[nbytes]='\0';
if (strstr(buffer, expected) != NULL)
{
return 1;
}
nbytes = ssh_channel_read(c, buffer, sizeof(buffer), 0);
}
return 0;
}
/* set explicit TTY modes and validate that the server uses them */
static void torture_request_pty_modes_translate_ocrnl(void **state)
{
const unsigned char modes[] = {
@@ -92,8 +117,6 @@ static void torture_request_pty_modes_translate_ocrnl(void **state)
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel c;
char buffer[4096] = {0};
int nbytes;
int rc;
int string_found = 0;
@@ -106,20 +129,103 @@ static void torture_request_pty_modes_translate_ocrnl(void **state)
rc = ssh_channel_request_pty_size_modes(c, "xterm", 80, 25, modes, sizeof(modes));
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(c, "echo -e '>TEST\\r\\n<'");
rc = ssh_channel_request_exec(c, "/bin/echo -e '>TEST\\r\\n<'");
assert_ssh_return_code(session, rc);
nbytes = ssh_channel_read(c, buffer, sizeof(buffer) - 1, 0);
while (nbytes > 0) {
buffer[nbytes]='\0';
/* expect 2 newline characters */
if (strstr(buffer, ">TEST\n\n<") != NULL) {
string_found = 1;
break;
}
/* expect 2 newline characters */
string_found = check_channel_output(c, ">TEST\n\n<");
assert_int_equal(string_found, 1);
nbytes = ssh_channel_read(c, buffer, sizeof(buffer), 0);
}
ssh_channel_close(c);
}
/* if stdin is a TTY, its modes are passed to the server */
static void torture_request_pty_modes_use_stdin_modes(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel c;
int rc;
int string_found = 0;
struct termios modes;
int stdin_backup_fd = -1;
int master_fd, slave_fd;
c = ssh_channel_new(session);
assert_non_null(c);
rc = ssh_channel_open_session(c);
assert_ssh_return_code(session, rc);
/* stdin must be a TTY, so open one and replace the FD */
stdin_backup_fd = dup(STDIN_FILENO);
rc = openpty(&master_fd, &slave_fd, NULL, NULL, NULL);
assert_int_equal(rc, 0);
dup2(master_fd, STDIN_FILENO);
assert_true(isatty(STDIN_FILENO));
/* translate NL to CRNL on output to see a noticeable effect */
memset(&modes, 0, sizeof(modes));
tcgetattr(STDIN_FILENO, &modes);
modes.c_oflag |= ONLCR;
modes.c_iflag &= ~(ICRNL | INLCR | IGNCR);
tcsetattr(STDIN_FILENO, TCSANOW, &modes);
rc = ssh_channel_request_pty_size(c, "xterm", 80, 25);
/* revert the changes to STDIN first! */
dup2(stdin_backup_fd, STDIN_FILENO);
close(stdin_backup_fd);
close(master_fd);
close(slave_fd);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(c, "/bin/echo -e '>TEST\\r\\n<'");
assert_ssh_return_code(session, rc);
/* expect 2 carriage return characters + newline */
string_found = check_channel_output(c, ">TEST\r\r\n<");
assert_int_equal(string_found, 1);
ssh_channel_close(c);
}
/* if stdin is NOT a TTY, default modes are passed to the server */
static void torture_request_pty_modes_use_default_modes(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
ssh_channel c;
int rc;
int string_found = 0;
int stdin_backup_fd = -1;
c = ssh_channel_new(session);
assert_non_null(c);
rc = ssh_channel_open_session(c);
assert_ssh_return_code(session, rc);
/* stdin must not a TTY - change the FD to something else */
stdin_backup_fd = dup(STDIN_FILENO);
close(STDIN_FILENO);
rc = open("/dev/null", O_RDONLY); // reuses FD 0 now
assert_int_equal(rc, STDIN_FILENO);
assert_false(isatty(STDIN_FILENO));
rc = ssh_channel_request_pty_size(c, "xterm", 80, 25);
/* revert the changes to STDIN first! */
dup2(stdin_backup_fd, STDIN_FILENO);
close(stdin_backup_fd);
assert_ssh_return_code(session, rc);
rc = ssh_channel_request_exec(c, "/bin/echo -e '>TEST\\r\\n<'");
assert_ssh_return_code(session, rc);
/* expect the input unmodified */
string_found = check_channel_output(c, ">TEST\r\n<");
assert_int_equal(string_found, 1);
ssh_channel_close(c);
@@ -132,6 +238,12 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_request_pty_modes_translate_ocrnl,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_request_pty_modes_use_stdin_modes,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_request_pty_modes_use_default_modes,
session_setup,
session_teardown),
};
ssh_init();