mirror of
https://github.com/clearml/dropbear
synced 2025-04-02 12:06:15 +00:00
- Added terminal mode handling etc for the client, and window change
- Refactored the terminal-mode handling for the server - Improved session closing for the client --HG-- extra : convert_revision : 9d19b4f22c39798af5f3f24c2022f8caec4919e8
This commit is contained in:
parent
cb071834da
commit
051b7454f8
@ -39,7 +39,6 @@ struct ChanSess {
|
||||
int slave;
|
||||
unsigned char * tty;
|
||||
unsigned char * term;
|
||||
unsigned int termw, termh, termc, termr; /* width, height, col, rows */
|
||||
|
||||
/* exit details */
|
||||
int exited;
|
||||
@ -76,6 +75,9 @@ void send_msg_chansess_exitsignal(struct Channel * channel,
|
||||
struct ChanSess * chansess);
|
||||
void addnewvar(const char* param, const char* var);
|
||||
|
||||
void cli_send_chansess_request();
|
||||
void cli_tty_cleanup();
|
||||
|
||||
void svr_chansessinitialise();
|
||||
extern const struct ChanType svrchansess;
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include "packet.h"
|
||||
#include "runopts.h"
|
||||
|
||||
#undef DROPBEAR_PUBKEY_AUTH
|
||||
|
||||
void cli_authinitialise() {
|
||||
|
||||
memset(&ses.authstate, 0, sizeof(ses.authstate));
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "channel.h"
|
||||
#include "ssh.h"
|
||||
#include "runopts.h"
|
||||
#include "termcodes.h"
|
||||
|
||||
static void cli_closechansess(struct Channel *channel);
|
||||
static int cli_initchansess(struct Channel *channel);
|
||||
@ -16,7 +17,7 @@ static void send_chansess_pty_req(struct Channel *channel);
|
||||
static void send_chansess_shell_req(struct Channel *channel);
|
||||
|
||||
static void cli_tty_setup();
|
||||
static void cli_tty_cleanup();
|
||||
void cli_tty_cleanup();
|
||||
|
||||
static const struct ChanType clichansess = {
|
||||
0, /* sepfds */
|
||||
@ -50,7 +51,6 @@ static void start_channel_request(struct Channel *channel,
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Taken from OpenSSH's sshtty.c:
|
||||
* RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
|
||||
static void cli_tty_setup() {
|
||||
@ -91,12 +91,13 @@ static void cli_tty_setup() {
|
||||
TRACE(("leave cli_tty_setup"));
|
||||
}
|
||||
|
||||
static void cli_tty_cleanup() {
|
||||
void cli_tty_cleanup() {
|
||||
|
||||
TRACE(("enter cli_tty_cleanup"));
|
||||
|
||||
if (cli_ses.tty_raw_mode == 0) {
|
||||
TRACE(("leave cli_tty_cleanup: not in raw mode"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (tcsetattr(STDIN_FILENO, TCSADRAIN, &cli_ses.saved_tio) == -1) {
|
||||
@ -108,30 +109,123 @@ static void cli_tty_cleanup() {
|
||||
TRACE(("leave cli_tty_cleanup"));
|
||||
}
|
||||
|
||||
static void send_chansess_pty_req(struct Channel *channel) {
|
||||
static void put_termcodes() {
|
||||
|
||||
unsigned char* termmodes = "\0";
|
||||
unsigned char* term = NULL;
|
||||
int termc = 80, termr = 25, termw = 0, termh = 0; /* XXX TODO matt */
|
||||
TRACE(("enter put_termcodes"));
|
||||
|
||||
TRACE(("enter send_chansess_pty_req"));
|
||||
start_channel_request(channel, "pty-req");
|
||||
struct termios tio;
|
||||
unsigned int sshcode;
|
||||
const struct TermCode *termcode;
|
||||
unsigned int value;
|
||||
unsigned int mapcode;
|
||||
|
||||
term = getenv("TERM");
|
||||
if (term == NULL) {
|
||||
term = "vt100";
|
||||
unsigned int bufpos1, bufpos2;
|
||||
|
||||
if (tcgetattr(STDIN_FILENO, &tio) == -1) {
|
||||
dropbear_log(LOG_WARNING, "Failed reading termmodes");
|
||||
buf_putint(ses.writepayload, 1); /* Just the terminator */
|
||||
buf_putbyte(ses.writepayload, 0); /* TTY_OP_END */
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX TODO */
|
||||
buf_putbyte(ses.writepayload, 0); /* Don't want replies */
|
||||
buf_putstring(ses.writepayload, term, strlen(term));
|
||||
buf_putint(ses.writepayload, termc); /* Cols */
|
||||
buf_putint(ses.writepayload, termr); /* Rows */
|
||||
buf_putint(ses.writepayload, termw); /* Width */
|
||||
buf_putint(ses.writepayload, termh); /* Height */
|
||||
bufpos1 = ses.writepayload->pos;
|
||||
buf_putint(ses.writepayload, 0); /* A placeholder for the final length */
|
||||
|
||||
buf_putstring(ses.writepayload, termmodes, 1); /* XXX TODO */
|
||||
//m_free(termmodes);
|
||||
/* As with Dropbear server, we ignore baud rates for now */
|
||||
for (sshcode = 1; sshcode < MAX_TERMCODE; sshcode++) {
|
||||
|
||||
termcode = &termcodes[sshcode];
|
||||
mapcode = termcode->mapcode;
|
||||
|
||||
switch (termcode->type) {
|
||||
|
||||
case TERMCODE_NONE:
|
||||
continue;
|
||||
|
||||
case TERMCODE_CONTROLCHAR:
|
||||
value = tio.c_cc[mapcode];
|
||||
break;
|
||||
|
||||
case TERMCODE_INPUT:
|
||||
value = tio.c_iflag & mapcode;
|
||||
break;
|
||||
|
||||
case TERMCODE_OUTPUT:
|
||||
value = tio.c_oflag & mapcode;
|
||||
break;
|
||||
|
||||
case TERMCODE_LOCAL:
|
||||
value = tio.c_lflag & mapcode;
|
||||
break;
|
||||
|
||||
case TERMCODE_CONTROL:
|
||||
value = tio.c_cflag & mapcode;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* If we reach here, we have something to say */
|
||||
buf_putbyte(ses.writepayload, sshcode);
|
||||
buf_putint(ses.writepayload, value);
|
||||
}
|
||||
|
||||
buf_putbyte(ses.writepayload, 0); /* THE END, aka TTY_OP_END */
|
||||
|
||||
/* Put the string length at the start of the buffer */
|
||||
bufpos2 = ses.writepayload->pos;
|
||||
|
||||
buf_setpos(ses.writepayload, bufpos1); /* Jump back */
|
||||
buf_putint(ses.writepayload, bufpos2 - bufpos1); /* len(termcodes) */
|
||||
buf_setpos(ses.writepayload, bufpos2); /* Back where we were */
|
||||
|
||||
TRACE(("leave put_termcodes"));
|
||||
}
|
||||
|
||||
static void put_winsize() {
|
||||
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
|
||||
/* Some sane defaults */
|
||||
ws.ws_row = 25;
|
||||
ws.ws_col = 80;
|
||||
ws.ws_xpixel = 0;
|
||||
ws.ws_ypixel = 0;
|
||||
}
|
||||
|
||||
buf_putint(ses.writepayload, ws.ws_col); /* Cols */
|
||||
buf_putint(ses.writepayload, ws.ws_row); /* Rows */
|
||||
buf_putint(ses.writepayload, ws.ws_xpixel); /* Width */
|
||||
buf_putint(ses.writepayload, ws.ws_ypixel); /* Height */
|
||||
|
||||
}
|
||||
|
||||
static void send_chansess_pty_req(struct Channel *channel) {
|
||||
|
||||
unsigned char* term = NULL;
|
||||
|
||||
TRACE(("enter send_chansess_pty_req"));
|
||||
|
||||
start_channel_request(channel, "pty-req");
|
||||
|
||||
/* Don't want replies */
|
||||
buf_putbyte(ses.writepayload, 0);
|
||||
|
||||
/* Get the terminal */
|
||||
term = getenv("TERM");
|
||||
if (term == NULL) {
|
||||
term = "vt100"; /* Seems a safe default */
|
||||
}
|
||||
buf_putstring(ses.writepayload, term, strlen(term));
|
||||
|
||||
/* Window size */
|
||||
put_winsize();
|
||||
|
||||
/* Terminal mode encoding */
|
||||
put_termcodes();
|
||||
|
||||
encrypt_packet();
|
||||
TRACE(("leave send_chansess_pty_req"));
|
||||
@ -171,7 +265,6 @@ static int cli_initchansess(struct Channel *channel) {
|
||||
send_chansess_pty_req(channel);
|
||||
}
|
||||
|
||||
cli_opts.cmd = "df";
|
||||
send_chansess_shell_req(channel);
|
||||
|
||||
if (cli_opts.wantpty) {
|
||||
|
@ -61,9 +61,12 @@ static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
|
||||
cli_opts.remoteport, format);
|
||||
}
|
||||
|
||||
/* Do the cleanup first, since then the terminal will be reset */
|
||||
cli_session_cleanup();
|
||||
common_session_cleanup();
|
||||
|
||||
_dropbear_log(LOG_INFO, fmtbuf, param);
|
||||
|
||||
common_session_cleanup();
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
@ -73,6 +76,6 @@ static void cli_dropbear_log(int priority, const char* format, va_list param) {
|
||||
|
||||
vsnprintf(printbuf, sizeof(printbuf), format, param);
|
||||
|
||||
fprintf(stderr, "Dropbear: %s\n", printbuf);
|
||||
fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
|
||||
|
||||
}
|
||||
|
@ -55,11 +55,12 @@ void cli_getopts(int argc, char ** argv) {
|
||||
char* userhostarg = NULL;
|
||||
|
||||
/* see printhelp() for options */
|
||||
cli_opts.progname = argv[0];
|
||||
cli_opts.remotehost = NULL;
|
||||
cli_opts.remoteport = NULL;
|
||||
cli_opts.username = NULL;
|
||||
cli_opts.cmd = NULL;
|
||||
cli_opts.wantpty = 0;
|
||||
cli_opts.wantpty = 1;
|
||||
opts.nolocaltcp = 0;
|
||||
opts.noremotetcp = 0;
|
||||
/* not yet
|
||||
|
@ -9,10 +9,13 @@
|
||||
#include "channel.h"
|
||||
#include "random.h"
|
||||
#include "service.h"
|
||||
#include "runopts.h"
|
||||
#include "chansession.h"
|
||||
|
||||
static void cli_remoteclosed();
|
||||
static void cli_sessionloop();
|
||||
static void cli_session_init();
|
||||
static void cli_finished();
|
||||
|
||||
struct clientsession cli_ses; /* GLOBAL */
|
||||
|
||||
@ -163,6 +166,12 @@ static void cli_sessionloop() {
|
||||
cli_ses.state = SESSION_RUNNING;
|
||||
return;
|
||||
|
||||
case SESSION_RUNNING:
|
||||
if (ses.chancount < 1) {
|
||||
cli_finished();
|
||||
}
|
||||
return;
|
||||
|
||||
/* XXX more here needed */
|
||||
|
||||
|
||||
@ -174,6 +183,26 @@ static void cli_sessionloop() {
|
||||
|
||||
}
|
||||
|
||||
void cli_session_cleanup() {
|
||||
|
||||
if (!sessinitdone) {
|
||||
return;
|
||||
}
|
||||
cli_tty_cleanup();
|
||||
|
||||
}
|
||||
|
||||
static void cli_finished() {
|
||||
|
||||
cli_session_cleanup();
|
||||
common_session_cleanup();
|
||||
fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
|
||||
cli_opts.remotehost, cli_opts.remoteport);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* called when the remote side closes the connection */
|
||||
static void cli_remoteclosed() {
|
||||
|
||||
|
2
dbutil.c
2
dbutil.c
@ -103,6 +103,7 @@ void dropbear_log(int priority, const char* format, ...) {
|
||||
#ifdef DEBUG_TRACE
|
||||
void dropbear_trace(const char* format, ...) {
|
||||
|
||||
#if 0
|
||||
va_list param;
|
||||
|
||||
va_start(param, format);
|
||||
@ -110,6 +111,7 @@ void dropbear_trace(const char* format, ...) {
|
||||
vfprintf(stderr, format, param);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(param);
|
||||
#endif
|
||||
}
|
||||
#endif /* DEBUG_TRACE */
|
||||
|
||||
|
@ -111,7 +111,7 @@
|
||||
/* Authentication types to enable, at least one required.
|
||||
RFC Draft requires pubkey auth, and recommends password */
|
||||
#define DROPBEAR_PASSWORD_AUTH
|
||||
//#define DROPBEAR_PUBKEY_AUTH
|
||||
#define DROPBEAR_PUBKEY_AUTH
|
||||
|
||||
/* Random device to use - you must specify _one only_.
|
||||
* DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use
|
||||
|
@ -79,6 +79,7 @@ void svr_getopts(int argc, char ** argv);
|
||||
/* Uncompleted XXX matt */
|
||||
typedef struct cli_runopts {
|
||||
|
||||
char *progname;
|
||||
char *remotehost;
|
||||
char *remoteport;
|
||||
|
||||
|
@ -55,6 +55,7 @@ void svr_dropbear_log(int priority, const char* format, va_list param);
|
||||
void cli_session(int sock, char *remotehost);
|
||||
void cli_dropbear_exit(int exitcode, const char* format, va_list param);
|
||||
void cli_dropbear_log(int priority, const char* format, va_list param);
|
||||
void cli_session_cleanup();
|
||||
|
||||
struct key_context {
|
||||
|
||||
|
@ -53,7 +53,6 @@ int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter buf_get_pub_key"));
|
||||
printhex(buf_getptr(buf, 0x99), 0x99);
|
||||
|
||||
ident = buf_getstring(buf, &len);
|
||||
|
||||
|
@ -56,6 +56,7 @@ static void chansessionrequest(struct Channel *channel);
|
||||
|
||||
static void send_exitsignalstatus(struct Channel *channel);
|
||||
static int sesscheckclose(struct Channel *channel);
|
||||
static void get_termmodes(struct ChanSess *chansess);
|
||||
|
||||
|
||||
/* required to clear environment */
|
||||
@ -192,10 +193,6 @@ static int newchansess(struct Channel *channel) {
|
||||
chansess->slave = -1;
|
||||
chansess->tty = NULL;
|
||||
chansess->term = NULL;
|
||||
chansess->termw = 0;
|
||||
chansess->termh = 0;
|
||||
chansess->termc = 0;
|
||||
chansess->termr = 0;
|
||||
|
||||
chansess->exited = 0;
|
||||
|
||||
@ -376,22 +373,111 @@ static int sessionsignal(struct ChanSess *chansess) {
|
||||
* client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||
static int sessionwinchange(struct ChanSess *chansess) {
|
||||
|
||||
int termc, termr, termw, termh;
|
||||
|
||||
if (chansess->master < 0) {
|
||||
/* haven't got a pty yet */
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
chansess->termc = buf_getint(ses.payload);
|
||||
chansess->termr = buf_getint(ses.payload);
|
||||
chansess->termw = buf_getint(ses.payload);
|
||||
chansess->termh = buf_getint(ses.payload);
|
||||
termc = buf_getint(ses.payload);
|
||||
termr = buf_getint(ses.payload);
|
||||
termw = buf_getint(ses.payload);
|
||||
termh = buf_getint(ses.payload);
|
||||
|
||||
pty_change_window_size(chansess->master, chansess->termr, chansess->termc,
|
||||
chansess->termw, chansess->termh);
|
||||
pty_change_window_size(chansess->master, termr, termc, termw, termh);
|
||||
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
static void get_termmodes(struct ChanSess *chansess) {
|
||||
|
||||
struct termios termio;
|
||||
unsigned char opcode;
|
||||
unsigned int value;
|
||||
const struct TermCode * termcode;
|
||||
unsigned int len;
|
||||
|
||||
TRACE(("enter get_termmodes"));
|
||||
|
||||
/* Term modes */
|
||||
/* We'll ignore errors and continue if we can't set modes.
|
||||
* We're ignoring baud rates since they seem evil */
|
||||
if (tcgetattr(chansess->master, &termio) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
len = buf_getint(ses.payload);
|
||||
if (len != ses.payload->len - ses.payload->pos) {
|
||||
dropbear_exit("bad term mode string");
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
TRACE(("leave get_termmodes: empty terminal modes string"));
|
||||
}
|
||||
|
||||
while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) {
|
||||
|
||||
/* must be before checking type, so that value is consumed even if
|
||||
* we don't use it */
|
||||
value = buf_getint(ses.payload);
|
||||
|
||||
/* handle types of code */
|
||||
if (opcode > MAX_TERMCODE) {
|
||||
continue;
|
||||
}
|
||||
termcode = &termcodes[(unsigned int)opcode];
|
||||
|
||||
|
||||
switch (termcode->type) {
|
||||
|
||||
case TERMCODE_NONE:
|
||||
break;
|
||||
|
||||
case TERMCODE_CONTROLCHAR:
|
||||
termio.c_cc[termcode->mapcode] = value;
|
||||
break;
|
||||
|
||||
case TERMCODE_INPUT:
|
||||
if (value) {
|
||||
termio.c_iflag |= termcode->mapcode;
|
||||
} else {
|
||||
termio.c_iflag &= ~(termcode->mapcode);
|
||||
}
|
||||
break;
|
||||
|
||||
case TERMCODE_OUTPUT:
|
||||
if (value) {
|
||||
termio.c_oflag |= termcode->mapcode;
|
||||
} else {
|
||||
termio.c_oflag &= ~(termcode->mapcode);
|
||||
}
|
||||
break;
|
||||
|
||||
case TERMCODE_LOCAL:
|
||||
if (value) {
|
||||
termio.c_lflag |= termcode->mapcode;
|
||||
} else {
|
||||
termio.c_lflag &= ~(termcode->mapcode);
|
||||
}
|
||||
break;
|
||||
|
||||
case TERMCODE_CONTROL:
|
||||
if (value) {
|
||||
termio.c_cflag |= termcode->mapcode;
|
||||
} else {
|
||||
termio.c_cflag &= ~(termcode->mapcode);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
|
||||
dropbear_log(LOG_INFO, "error setting terminal attributes");
|
||||
}
|
||||
TRACE(("leave get_termmodes"));
|
||||
}
|
||||
|
||||
/* Set up a session pty which will be used to execute the shell or program.
|
||||
* The pty is allocated now, and kept for when the shell/program executes.
|
||||
* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
|
||||
@ -399,7 +485,6 @@ static int sessionpty(struct ChanSess * chansess) {
|
||||
|
||||
unsigned int termlen;
|
||||
unsigned char namebuf[65];
|
||||
struct termios termio;
|
||||
|
||||
TRACE(("enter sessionpty"));
|
||||
chansess->term = buf_getstring(ses.payload, &termlen);
|
||||
@ -408,10 +493,6 @@ static int sessionpty(struct ChanSess * chansess) {
|
||||
TRACE(("leave sessionpty: term len too long"));
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
chansess->termc = buf_getint(ses.payload);
|
||||
chansess->termr = buf_getint(ses.payload);
|
||||
chansess->termw = buf_getint(ses.payload);
|
||||
chansess->termh = buf_getint(ses.payload);
|
||||
|
||||
/* allocate the pty */
|
||||
assert(chansess->master == -1); /* haven't already got one */
|
||||
@ -426,89 +507,12 @@ static int sessionpty(struct ChanSess * chansess) {
|
||||
}
|
||||
|
||||
pty_setowner(ses.authstate.pw, chansess->tty);
|
||||
pty_change_window_size(chansess->master, chansess->termr, chansess->termc,
|
||||
chansess->termw, chansess->termh);
|
||||
|
||||
/* Term modes */
|
||||
/* We'll ignore errors and continue if we can't set modes.
|
||||
* We're ignoring baud rates since they seem evil */
|
||||
if (tcgetattr(chansess->master, &termio) == 0) {
|
||||
unsigned char opcode;
|
||||
unsigned int value;
|
||||
const struct TermCode * termcode;
|
||||
unsigned int len;
|
||||
/* Set up the rows/col counts */
|
||||
sessionwinchange(chansess);
|
||||
|
||||
len = buf_getint(ses.payload);
|
||||
if (len != ses.payload->len - ses.payload->pos) {
|
||||
dropbear_exit("bad term mode string");
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
TRACE(("empty terminal modes string"));
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
while (((opcode = buf_getbyte(ses.payload)) != 0x00) &&
|
||||
opcode <= 159) {
|
||||
|
||||
/* must be before checking type, so that value is consumed even if
|
||||
* we don't use it */
|
||||
value = buf_getint(ses.payload);
|
||||
|
||||
/* handle types of code */
|
||||
if (opcode > MAX_TERMCODE) {
|
||||
continue;
|
||||
}
|
||||
termcode = &termcodes[(unsigned int)opcode];
|
||||
|
||||
|
||||
switch (termcode->type) {
|
||||
|
||||
case TERMCODE_NONE:
|
||||
break;
|
||||
|
||||
case TERMCODE_CONTROLCHAR:
|
||||
termio.c_cc[termcode->mapcode] = value;
|
||||
break;
|
||||
|
||||
case TERMCODE_INPUT:
|
||||
if (value) {
|
||||
termio.c_iflag |= termcode->mapcode;
|
||||
} else {
|
||||
termio.c_iflag &= ~(termcode->mapcode);
|
||||
}
|
||||
break;
|
||||
|
||||
case TERMCODE_OUTPUT:
|
||||
if (value) {
|
||||
termio.c_oflag |= termcode->mapcode;
|
||||
} else {
|
||||
termio.c_oflag &= ~(termcode->mapcode);
|
||||
}
|
||||
break;
|
||||
|
||||
case TERMCODE_LOCAL:
|
||||
if (value) {
|
||||
termio.c_lflag |= termcode->mapcode;
|
||||
} else {
|
||||
termio.c_lflag &= ~(termcode->mapcode);
|
||||
}
|
||||
break;
|
||||
|
||||
case TERMCODE_CONTROL:
|
||||
if (value) {
|
||||
termio.c_cflag |= termcode->mapcode;
|
||||
} else {
|
||||
termio.c_cflag &= ~(termcode->mapcode);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
|
||||
dropbear_log(LOG_INFO, "error setting terminal attributes");
|
||||
}
|
||||
}
|
||||
/* Read the terminal modes */
|
||||
get_termmodes(chansess);
|
||||
|
||||
TRACE(("leave sessionpty"));
|
||||
return DROPBEAR_SUCCESS;
|
||||
|
Loading…
Reference in New Issue
Block a user