# HG changeset patch # User Matt Johnston # Date 1091190593 0 # Node ID 0883c0906870d3a2a9be3a4fa6afb9e95ef8a19a # Parent 5c6f9d27ea1c4b8db3717cc968525bd9fff0d4b6 tty raw mode support works mostly adding cli-{chansession,runopts}.c which were missing diff -r 5c6f9d27ea1c -r 0883c0906870 cli-chansession.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-chansession.c Fri Jul 30 12:29:53 2004 +0000 @@ -0,0 +1,198 @@ +#include "includes.h" +#include "packet.h" +#include "buffer.h" +#include "session.h" +#include "dbutil.h" +#include "channel.h" +#include "ssh.h" +#include "runopts.h" + +static void cli_closechansess(struct Channel *channel); +static int cli_initchansess(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(); +static void cli_tty_cleanup(); + +static const struct ChanType clichansess = { + 0, /* sepfds */ + "session", /* name */ + cli_initchansess, /* inithandler */ + NULL, /* checkclosehandler */ + NULL, /* reqhandler */ + cli_closechansess, /* closehandler */ +}; + +/* If the main session goes, we close it up */ +static void cli_closechansess(struct Channel *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")); +} + +static 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")); + } + + 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 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"); + + term = getenv("TERM"); + if (term == NULL) { + term = "vt100"; + } + + /* 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); + + encrypt_packet(); + 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->infd = STDOUT_FILENO; + //channel->outfd = STDIN_FILENO; + //channel->errfd = STDERR_FILENO; + + if (cli_opts.wantpty) { + send_chansess_pty_req(channel); + } + + cli_opts.cmd = "df"; + 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")); + +} diff -r 5c6f9d27ea1c -r 0883c0906870 cli-runopts.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cli-runopts.c Fri Jul 30 12:29:53 2004 +0000 @@ -0,0 +1,202 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * 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 "runopts.h" +#include "signkey.h" +#include "buffer.h" +#include "dbutil.h" +#include "algo.h" + +cli_runopts cli_opts; /* GLOBAL */ + +static void printhelp(const char * progname); + +static void printhelp(const char * progname) { + + fprintf(stderr, "Dropbear client v%s\n" + "Usage: %s [options] user@host[:port]\n" + "Options are:\n" + "user Remote username\n" + "host Remote host\n" + "port Remote port\n" + ,DROPBEAR_VERSION, progname); +} + +void cli_getopts(int argc, char ** argv) { + + unsigned int i; + char ** next = 0; + + uid_t uid; + struct passwd *pw; + + char* userhostarg = NULL; + + /* see printhelp() for options */ + cli_opts.remotehost = NULL; + cli_opts.remoteport = NULL; + cli_opts.username = NULL; + cli_opts.cmd = NULL; + cli_opts.wantpty = 0; + opts.nolocaltcp = 0; + opts.noremotetcp = 0; + /* not yet + opts.ipv4 = 1; + opts.ipv6 = 1; + */ + + if (argc != 2) { + printhelp(argv[0]); + exit(EXIT_FAILURE); + } + + /* We'll be editing it, should probably make a copy */ + userhostarg = m_strdup(argv[1]); + + cli_opts.remotehost = strchr(userhostarg, '@'); + if (cli_opts.remotehost == NULL) { + /* no username portion, the cli-auth.c code can figure the local + * user's name */ + cli_opts.remotehost = userhostarg; + } else { + cli_opts.remotehost[0] = '\0'; /* Split the user/host */ + cli_opts.remotehost++; + cli_opts.username = userhostarg; + } + + if (cli_opts.username == NULL) { + uid = getuid(); + + pw = getpwuid(uid); + if (pw == NULL || pw->pw_name == NULL) { + dropbear_exit("Couldn't find username for current user"); + } + + cli_opts.username = m_strdup(pw->pw_name); + } + + if (cli_opts.remotehost[0] == '\0') { + dropbear_exit("Bad hostname argument"); + } + + cli_opts.remoteport = strchr(cli_opts.remotehost, ':'); + if (cli_opts.remoteport == NULL) { + cli_opts.remoteport = "22"; + } else { + cli_opts.remoteport[0] = '\0'; + cli_opts.remoteport++; + } + +#if 0 + for (i = 1; i < (unsigned int)argc; i++) { + if (next) { + *next = argv[i]; + if (*next == NULL) { + dropbear_exit("Invalid null argument"); + } + next = 0x00; + continue; + } + + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'b': + next = &svr_opts.bannerfile; + break; +#ifdef DROPBEAR_DSS + case 'd': + next = &svr_opts.dsskeyfile; + break; +#endif +#ifdef DROPBEAR_RSA + case 'r': + next = &svr_opts.rsakeyfile; + break; +#endif + case 'F': + svr_opts.forkbg = 0; + break; +#ifndef DISABLE_SYSLOG + case 'E': + svr_opts.usingsyslog = 0; + break; +#endif +#ifndef DISABLE_LOCALTCPFWD + case 'j': + opts.nolocaltcp = 1; + break; +#endif +#ifndef DISABLE_REMOTETCPFWD + case 'k': + opts.noremotetcp = 1; + break; +#endif + case 'p': + if (portnum < DROPBEAR_MAX_PORTS) { + portstring[portnum] = NULL; + next = &portstring[portnum]; + portnum++; + } + break; +#ifdef DO_MOTD + /* motd is displayed by default, -m turns it off */ + case 'm': + svr_opts.domotd = 0; + break; +#endif + case 'w': + svr_opts.norootlogin = 1; + break; +#ifdef DROPBEAR_PASSWORD_AUTH + case 's': + svr_opts.noauthpass = 1; + break; + case 'g': + svr_opts.norootpass = 1; + break; +#endif + case 'h': + printhelp(argv[0]); + exit(EXIT_FAILURE); + break; + /* + case '4': + svr_opts.ipv4 = 0; + break; + case '6': + svr_opts.ipv6 = 0; + break; + */ + default: + fprintf(stderr, "Unknown argument %s\n", argv[i]); + printhelp(argv[0]); + exit(EXIT_FAILURE); + break; + } + } + } +#endif + +} diff -r 5c6f9d27ea1c -r 0883c0906870 cli-session.c --- a/cli-session.c Fri Jul 30 11:27:52 2004 +0000 +++ b/cli-session.c Fri Jul 30 12:29:53 2004 +0000 @@ -76,6 +76,8 @@ cli_ses.state = STATE_NOTHING; cli_ses.kex_state = KEX_NOTHING; + cli_ses.tty_raw_mode = 0; + /* For printing "remote host closed" for the user */ ses.remoteclosed = cli_remoteclosed; ses.buf_match_algo = cli_buf_match_algo; diff -r 5c6f9d27ea1c -r 0883c0906870 session.h --- a/session.h Fri Jul 30 11:27:52 2004 +0000 +++ b/session.h Fri Jul 30 12:29:53 2004 +0000 @@ -208,6 +208,9 @@ int something; /* XXX */ unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */ + int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */ + struct termios saved_tio; + }; /* Global structs storing the state */