summaryrefslogtreecommitdiff
path: root/cli-chansession.c
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2007-01-11 03:05:30 +0000
committerMatt Johnston <matt@ucc.asn.au>2007-01-11 03:05:30 +0000
commit4ebcb4da9b179c9bc735698414524ccda5932568 (patch)
tree9ad32b57ddb21adc7906da94974efd721dc139f5 /cli-chansession.c
parenta8df079d41495a4b4f6288c2fbeea45e1963e3d5 (diff)
parent1fccffd891ce88522bb018de87faad1487a8b832 (diff)
downloaddropbear-4ebcb4da9b179c9bc735698414524ccda5932568.tar.gz
propagate from branch 'au.asn.ucc.matt.ltc.dropbear' (head ffd1015238ffcc959f6cd95176d96fcd0945a397)
to branch 'au.asn.ucc.matt.dropbear' (head 52ccb0ad0587a62bc64aecb939adbb76546aac16)
Diffstat (limited to 'cli-chansession.c')
-rw-r--r--cli-chansession.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/cli-chansession.c b/cli-chansession.c
new file mode 100644
index 0000000..fee8a22
--- /dev/null
+++ b/cli-chansession.c
@@ -0,0 +1,380 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#include "includes.h"
+#include "packet.h"
+#include "buffer.h"
+#include "session.h"
+#include "dbutil.h"
+#include "channel.h"
+#include "ssh.h"
+#include "runopts.h"
+#include "termcodes.h"
+#include "chansession.h"
+
+static void cli_closechansess(struct Channel *channel);
+static int cli_initchansess(struct Channel *channel);
+static void cli_chansessreq(struct Channel *channel);
+
+static void start_channel_request(struct Channel *channel, unsigned char *type);
+
+static void send_chansess_pty_req(struct Channel *channel);
+static void send_chansess_shell_req(struct Channel *channel);
+
+static void cli_tty_setup();
+
+const struct ChanType clichansess = {
+ 0, /* sepfds */
+ "session", /* name */
+ cli_initchansess, /* inithandler */
+ NULL, /* checkclosehandler */
+ cli_chansessreq, /* reqhandler */
+ cli_closechansess, /* closehandler */
+};
+
+static void cli_chansessreq(struct Channel *channel) {
+
+ unsigned char* type = NULL;
+ int wantreply;
+
+ TRACE(("enter cli_chansessreq"))
+
+ type = buf_getstring(ses.payload, NULL);
+ wantreply = buf_getbool(ses.payload);
+
+ if (strcmp(type, "exit-status") != 0) {
+ TRACE(("unknown request '%s'", type))
+ send_msg_channel_failure(channel);
+ goto out;
+ }
+
+ /* We'll just trust what they tell us */
+ cli_ses.retval = buf_getint(ses.payload);
+ TRACE(("got exit-status of '%d'", cli_ses.retval))
+
+out:
+ m_free(type);
+}
+
+
+/* If the main session goes, we close it up */
+static void cli_closechansess(struct Channel *UNUSED(channel)) {
+
+ /* This channel hasn't gone yet, so we have > 1 */
+ if (ses.chancount > 1) {
+ dropbear_log(LOG_INFO, "Waiting for other channels to close...");
+ }
+
+ cli_tty_cleanup(); /* Restore tty modes etc */
+
+}
+
+static void start_channel_request(struct Channel *channel,
+ unsigned char *type) {
+
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+ buf_putint(ses.writepayload, channel->remotechan);
+
+ buf_putstring(ses.writepayload, type, strlen(type));
+
+}
+
+/* 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() {
+
+ struct termios tio;
+
+ TRACE(("enter cli_pty_setup"))
+
+ if (cli_ses.tty_raw_mode == 1) {
+ TRACE(("leave cli_tty_setup: already in raw mode!"))
+ return;
+ }
+
+ if (tcgetattr(STDIN_FILENO, &tio) == -1) {
+ dropbear_exit("Failed to set raw TTY mode");
+ }
+
+ /* make a copy */
+ cli_ses.saved_tio = tio;
+
+ tio.c_iflag |= IGNPAR;
+ tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
+#ifdef IUCLC
+ tio.c_iflag &= ~IUCLC;
+#endif
+ tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
+#ifdef IEXTEN
+ tio.c_lflag &= ~IEXTEN;
+#endif
+ tio.c_oflag &= ~OPOST;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tio) == -1) {
+ dropbear_exit("Failed to set raw TTY mode");
+ }
+
+ cli_ses.tty_raw_mode = 1;
+ TRACE(("leave cli_tty_setup"))
+}
+
+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) {
+ dropbear_log(LOG_WARNING, "Failed restoring TTY");
+ } else {
+ cli_ses.tty_raw_mode = 0;
+ }
+
+ TRACE(("leave cli_tty_cleanup"))
+}
+
+static void put_termcodes() {
+
+ struct termios tio;
+ unsigned int sshcode;
+ const struct TermCode *termcode;
+ unsigned int value;
+ unsigned int mapcode;
+
+ unsigned int bufpos1, bufpos2;
+
+ TRACE(("enter put_termcodes"))
+
+ 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 - 4); /* 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 sigwinch_handler(int UNUSED(unused)) {
+
+ cli_ses.winchange = 1;
+
+}
+
+void cli_chansess_winchange() {
+
+ unsigned int i;
+ struct Channel *channel = NULL;
+
+ for (i = 0; i < ses.chansize; i++) {
+ channel = ses.channels[i];
+ if (channel != NULL && channel->type == &clichansess) {
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+ buf_putint(ses.writepayload, channel->remotechan);
+ buf_putstring(ses.writepayload, "window-change", 13);
+ buf_putbyte(ses.writepayload, 0); /* FALSE says the spec */
+ put_winsize();
+ encrypt_packet();
+ }
+ }
+ cli_ses.winchange = 0;
+}
+
+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();
+
+ /* Set up a window-change handler */
+ if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) {
+ dropbear_exit("signal error");
+ }
+ TRACE(("leave send_chansess_pty_req"))
+}
+
+static void send_chansess_shell_req(struct Channel *channel) {
+
+ unsigned char* reqtype = NULL;
+
+ TRACE(("enter send_chansess_shell_req"))
+
+ if (cli_opts.cmd) {
+ reqtype = "exec";
+ } else {
+ reqtype = "shell";
+ }
+
+ start_channel_request(channel, reqtype);
+
+ /* XXX TODO */
+ buf_putbyte(ses.writepayload, 0); /* Don't want replies */
+ if (cli_opts.cmd) {
+ buf_putstring(ses.writepayload, cli_opts.cmd, strlen(cli_opts.cmd));
+ }
+
+ encrypt_packet();
+ TRACE(("leave send_chansess_shell_req"))
+}
+
+static int cli_initchansess(struct Channel *channel) {
+
+
+ channel->writefd = STDOUT_FILENO;
+ setnonblocking(STDOUT_FILENO);
+
+ channel->readfd = STDIN_FILENO;
+ setnonblocking(STDIN_FILENO);
+
+ channel->errfd = STDERR_FILENO;
+ setnonblocking(STDERR_FILENO);
+
+ channel->extrabuf = cbuf_new(RECV_MAXWINDOW);
+
+ if (cli_opts.wantpty) {
+ send_chansess_pty_req(channel);
+ }
+
+ send_chansess_shell_req(channel);
+
+ if (cli_opts.wantpty) {
+ cli_tty_setup();
+ }
+
+ return 0; /* Success */
+
+}
+
+void cli_send_chansess_request() {
+
+ TRACE(("enter cli_send_chansess_request"))
+ if (send_msg_channel_open_init(STDIN_FILENO, &clichansess)
+ == DROPBEAR_FAILURE) {
+ dropbear_exit("Couldn't open initial channel");
+ }
+
+ /* No special channel request data */
+ encrypt_packet();
+ TRACE(("leave cli_send_chansess_request"))
+
+}