changeset 40:b4874d772210

- 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
author Matt Johnston <matt@ucc.asn.au>
date Sun, 01 Aug 2004 08:54:01 +0000
parents 0883c0906870
children 18eccbfb9641
files chansession.h cli-auth.c cli-chansession.c cli-main.c cli-runopts.c cli-session.c dbutil.c options.h runopts.h session.h signkey.c svr-chansession.c
diffstat 12 files changed, 255 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/chansession.h	Fri Jul 30 12:29:53 2004 +0000
+++ b/chansession.h	Sun Aug 01 08:54:01 2004 +0000
@@ -39,7 +39,6 @@
 	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 @@
 		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;
 
--- a/cli-auth.c	Fri Jul 30 12:29:53 2004 +0000
+++ b/cli-auth.c	Sun Aug 01 08:54:01 2004 +0000
@@ -7,6 +7,8 @@
 #include "packet.h"
 #include "runopts.h"
 
+#undef DROPBEAR_PUBKEY_AUTH
+
 void cli_authinitialise() {
 
 	memset(&ses.authstate, 0, sizeof(ses.authstate));
--- a/cli-chansession.c	Fri Jul 30 12:29:53 2004 +0000
+++ b/cli-chansession.c	Sun Aug 01 08:54:01 2004 +0000
@@ -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_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 @@
 
 }
 
-
 /* 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 @@
 	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 @@
 	TRACE(("leave cli_tty_cleanup"));
 }
 
+static void put_termcodes() {
+
+	TRACE(("enter put_termcodes"));
+
+	struct termios tio;
+	unsigned int sshcode;
+	const struct TermCode *termcode;
+	unsigned int value;
+	unsigned int mapcode;
+
+	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;
+	}
+
+	bufpos1 = ses.writepayload->pos;
+	buf_putint(ses.writepayload, 0); /* A placeholder for the final length */
+
+	/* 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* termmodes = "\0";
 	unsigned char* term = NULL;
-	int termc = 80, termr = 25, termw = 0, termh = 0; /* XXX TODO matt */
 
 	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";
+		term = "vt100"; /* Seems a safe default */
 	}
-
-	/* 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 */
 
-	buf_putstring(ses.writepayload, termmodes, 1); /* XXX TODO */
-	//m_free(termmodes);
+	/* Window size */
+	put_winsize();
+
+	/* Terminal mode encoding */
+	put_termcodes();
 
 	encrypt_packet();
 	TRACE(("leave send_chansess_pty_req"));
@@ -171,7 +265,6 @@
 		send_chansess_pty_req(channel);
 	}
 
-	cli_opts.cmd = "df";
 	send_chansess_shell_req(channel);
 
 	if (cli_opts.wantpty) {
--- a/cli-main.c	Fri Jul 30 12:29:53 2004 +0000
+++ b/cli-main.c	Sun Aug 01 08:54:01 2004 +0000
@@ -61,9 +61,12 @@
 				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 @@
 
 	vsnprintf(printbuf, sizeof(printbuf), format, param);
 
-	fprintf(stderr, "Dropbear: %s\n", printbuf);
+	fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
 
 }
--- a/cli-runopts.c	Fri Jul 30 12:29:53 2004 +0000
+++ b/cli-runopts.c	Sun Aug 01 08:54:01 2004 +0000
@@ -55,11 +55,12 @@
 	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
--- a/cli-session.c	Fri Jul 30 12:29:53 2004 +0000
+++ b/cli-session.c	Sun Aug 01 08:54:01 2004 +0000
@@ -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 @@
 			cli_ses.state = SESSION_RUNNING;
 			return;
 
+		case SESSION_RUNNING:
+			if (ses.chancount < 1) {
+				cli_finished();
+			}
+			return;
+
 		/* XXX more here needed */
 
 
@@ -174,6 +183,26 @@
 
 }
 
+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() {
 
--- a/dbutil.c	Fri Jul 30 12:29:53 2004 +0000
+++ b/dbutil.c	Sun Aug 01 08:54:01 2004 +0000
@@ -103,6 +103,7 @@
 #ifdef DEBUG_TRACE
 void dropbear_trace(const char* format, ...) {
 
+#if 0
 	va_list param;
 
 	va_start(param, format);
@@ -110,6 +111,7 @@
 	vfprintf(stderr, format, param);
 	fprintf(stderr, "\n");
 	va_end(param);
+#endif
 }
 #endif /* DEBUG_TRACE */
 
--- a/options.h	Fri Jul 30 12:29:53 2004 +0000
+++ b/options.h	Sun Aug 01 08:54:01 2004 +0000
@@ -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
--- a/runopts.h	Fri Jul 30 12:29:53 2004 +0000
+++ b/runopts.h	Sun Aug 01 08:54:01 2004 +0000
@@ -79,6 +79,7 @@
 /* Uncompleted XXX matt */
 typedef struct cli_runopts {
 
+	char *progname;
 	char *remotehost;
 	char *remoteport;
 
--- a/session.h	Fri Jul 30 12:29:53 2004 +0000
+++ b/session.h	Sun Aug 01 08:54:01 2004 +0000
@@ -55,6 +55,7 @@
 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 {
 
--- a/signkey.c	Fri Jul 30 12:29:53 2004 +0000
+++ b/signkey.c	Sun Aug 01 08:54:01 2004 +0000
@@ -53,7 +53,6 @@
 	unsigned int len;
 
 	TRACE(("enter buf_get_pub_key"));
-	printhex(buf_getptr(buf, 0x99), 0x99);
 
 	ident = buf_getstring(buf, &len);
 
--- a/svr-chansession.c	Fri Jul 30 12:29:53 2004 +0000
+++ b/svr-chansession.c	Sun Aug 01 08:54:01 2004 +0000
@@ -56,6 +56,7 @@
 
 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 @@
 	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 @@
  * 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 @@
 
 	unsigned int termlen;
 	unsigned char namebuf[65];
-	struct termios termio;
 
 	TRACE(("enter sessionpty"));
 	chansess->term = buf_getstring(ses.payload, &termlen);
@@ -408,10 +493,6 @@
 		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 @@
 	}
 
 	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;
-
-		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;
+	/* Set up the rows/col counts */
+	sessionwinchange(chansess);
 
-				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;