- 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:
Matt Johnston 2004-08-01 08:54:01 +00:00
parent cb071834da
commit 051b7454f8
12 changed files with 261 additions and 124 deletions

View File

@ -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;

View File

@ -7,6 +7,8 @@
#include "packet.h"
#include "runopts.h"
#undef DROPBEAR_PUBKEY_AUTH
void cli_authinitialise() {
memset(&ses.authstate, 0, sizeof(ses.authstate));

View File

@ -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) {

View File

@ -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);
}

View File

@ -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

View File

@ -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() {

View File

@ -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 */

View File

@ -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

View File

@ -79,6 +79,7 @@ void svr_getopts(int argc, char ** argv);
/* Uncompleted XXX matt */
typedef struct cli_runopts {
char *progname;
char *remotehost;
char *remoteport;

View File

@ -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 {

View File

@ -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);

View File

@ -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;