From 12abb953945f19b4aab6ca56dde0d2acd1c692c7 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Wed, 19 Sep 2018 20:36:40 +0100 Subject: [PATCH] Move the ttymode formatter into sshcommon.c. While I'm at it, I've brought it all into a single function: the parsing of data from Conf, the list of modes, and even the old callback system for writing to the destination buffer is now a simple if statement that formats mode parameters as byte or uint32 depending on SSH version. Also, the terminal speeds and the end byte are part of the same setup, so it's all together in one place instead of scattered all over ssh.c. --- ssh.c | 190 ++-------------------------------------------------- ssh.h | 5 ++ sshcommon.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 184 deletions(-) diff --git a/ssh.c b/ssh.c index 7a2b26fa..3d91a672 100644 --- a/ssh.c +++ b/ssh.c @@ -50,111 +50,6 @@ static const char *const ssh2_disconnect_reasons[] = { #define DH_MIN_SIZE 1024 #define DH_MAX_SIZE 8192 -/* - * Codes for terminal modes. - * Most of these are the same in SSH-1 and SSH-2. - * This list is derived from RFC 4254 and - * SSH-1 RFC-1.2.31. - */ -static const struct ssh_ttymode { - const char* const mode; - int opcode; - enum { TTY_OP_CHAR, TTY_OP_BOOL } type; -} ssh_ttymodes[] = { - /* "V" prefix discarded for special characters relative to SSH specs */ - { "INTR", 1, TTY_OP_CHAR }, - { "QUIT", 2, TTY_OP_CHAR }, - { "ERASE", 3, TTY_OP_CHAR }, - { "KILL", 4, TTY_OP_CHAR }, - { "EOF", 5, TTY_OP_CHAR }, - { "EOL", 6, TTY_OP_CHAR }, - { "EOL2", 7, TTY_OP_CHAR }, - { "START", 8, TTY_OP_CHAR }, - { "STOP", 9, TTY_OP_CHAR }, - { "SUSP", 10, TTY_OP_CHAR }, - { "DSUSP", 11, TTY_OP_CHAR }, - { "REPRINT", 12, TTY_OP_CHAR }, - { "WERASE", 13, TTY_OP_CHAR }, - { "LNEXT", 14, TTY_OP_CHAR }, - { "FLUSH", 15, TTY_OP_CHAR }, - { "SWTCH", 16, TTY_OP_CHAR }, - { "STATUS", 17, TTY_OP_CHAR }, - { "DISCARD", 18, TTY_OP_CHAR }, - { "IGNPAR", 30, TTY_OP_BOOL }, - { "PARMRK", 31, TTY_OP_BOOL }, - { "INPCK", 32, TTY_OP_BOOL }, - { "ISTRIP", 33, TTY_OP_BOOL }, - { "INLCR", 34, TTY_OP_BOOL }, - { "IGNCR", 35, TTY_OP_BOOL }, - { "ICRNL", 36, TTY_OP_BOOL }, - { "IUCLC", 37, TTY_OP_BOOL }, - { "IXON", 38, TTY_OP_BOOL }, - { "IXANY", 39, TTY_OP_BOOL }, - { "IXOFF", 40, TTY_OP_BOOL }, - { "IMAXBEL", 41, TTY_OP_BOOL }, - { "IUTF8", 42, TTY_OP_BOOL }, - { "ISIG", 50, TTY_OP_BOOL }, - { "ICANON", 51, TTY_OP_BOOL }, - { "XCASE", 52, TTY_OP_BOOL }, - { "ECHO", 53, TTY_OP_BOOL }, - { "ECHOE", 54, TTY_OP_BOOL }, - { "ECHOK", 55, TTY_OP_BOOL }, - { "ECHONL", 56, TTY_OP_BOOL }, - { "NOFLSH", 57, TTY_OP_BOOL }, - { "TOSTOP", 58, TTY_OP_BOOL }, - { "IEXTEN", 59, TTY_OP_BOOL }, - { "ECHOCTL", 60, TTY_OP_BOOL }, - { "ECHOKE", 61, TTY_OP_BOOL }, - { "PENDIN", 62, TTY_OP_BOOL }, /* XXX is this a real mode? */ - { "OPOST", 70, TTY_OP_BOOL }, - { "OLCUC", 71, TTY_OP_BOOL }, - { "ONLCR", 72, TTY_OP_BOOL }, - { "OCRNL", 73, TTY_OP_BOOL }, - { "ONOCR", 74, TTY_OP_BOOL }, - { "ONLRET", 75, TTY_OP_BOOL }, - { "CS7", 90, TTY_OP_BOOL }, - { "CS8", 91, TTY_OP_BOOL }, - { "PARENB", 92, TTY_OP_BOOL }, - { "PARODD", 93, TTY_OP_BOOL } -}; - -/* Miscellaneous other tty-related constants. */ -#define SSH_TTY_OP_END 0 -/* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */ -#define SSH1_TTY_OP_ISPEED 192 -#define SSH1_TTY_OP_OSPEED 193 -#define SSH2_TTY_OP_ISPEED 128 -#define SSH2_TTY_OP_OSPEED 129 - -/* Helper functions for parsing tty-related config. */ -static unsigned int ssh_tty_parse_specchar(char *s) -{ - unsigned int ret; - if (*s) { - char *next = NULL; - ret = ctrlparse(s, &next); - if (!next) ret = s[0]; - } else { - ret = 255; /* special value meaning "don't set" */ - } - return ret; -} -static unsigned int ssh_tty_parse_boolean(char *s) -{ - if (stricmp(s, "yes") == 0 || - stricmp(s, "on") == 0 || - stricmp(s, "true") == 0 || - stricmp(s, "+") == 0) - return 1; /* true */ - else if (stricmp(s, "no") == 0 || - stricmp(s, "off") == 0 || - stricmp(s, "false") == 0 || - stricmp(s, "-") == 0) - return 0; /* false */ - else - return (atoi(s) != 0); -} - /* Safely convert rekey_time to unsigned long minutes */ static unsigned long rekey_mins(int rekey_time, unsigned long def) { @@ -870,41 +765,6 @@ static void bomb_out(Ssh ssh, char *text) #define bombout(msg) bomb_out(ssh, dupprintf msg) -/* Helper function for common bits of parsing ttymodes. */ -static void parse_ttymodes( - BinarySink *bs, Ssh ssh, - void (*do_mode)(BinarySink *, const struct ssh_ttymode *, char *)) -{ - int i; - const struct ssh_ttymode *mode; - char *val; - - for (i = 0; i < lenof(ssh_ttymodes); i++) { - mode = ssh_ttymodes + i; - /* Every mode known to the current version of the code should be - * mentioned; this was ensured when settings were loaded. */ - val = conf_get_str_str(ssh->conf, CONF_ttymodes, mode->mode); - - /* - * val[0] can be - * - 'V', indicating that an explicit value follows it; - * - 'A', indicating that we should pass the value through from - * the local environment via get_ttymode; or - * - 'N', indicating that we should explicitly not send this - * mode. - */ - if (val[0] == 'A') { - val = get_ttymode(ssh->frontend, mode->mode); - if (val) { - do_mode(bs, mode, val); - sfree(val); - } - } else if (val[0] == 'V') { - do_mode(bs, mode, val + 1); /* skip the 'V' */ - } /* else 'N', or something from the future we don't understand */ - } -} - static int ssh_channelcmp(void *av, void *bv) { struct ssh_channel *a = (struct ssh_channel *) av; @@ -3363,22 +3223,6 @@ static void ssh1_smsg_exit_status(Ssh ssh, PktIn *pktin) ssh_disconnect(ssh, NULL, NULL, 0, TRUE); } -/* Helper function to deal with sending tty modes for REQUEST_PTY */ -static void ssh1_send_ttymode(BinarySink *bs, - const struct ssh_ttymode *mode, char *val) -{ - put_byte(bs, mode->opcode); - - switch (mode->type) { - case TTY_OP_CHAR: - put_byte(bs, ssh_tty_parse_specchar(val)); - break; - case TTY_OP_BOOL: - put_byte(bs, ssh_tty_parse_boolean(val)); - break; - } -} - static int (ssh_agent_forwarding_permitted)(ConnectionLayer *cl) { Ssh ssh = FROMFIELD(cl, struct ssh_tag, cl); @@ -3478,12 +3322,9 @@ static void do_ssh1_connection(void *vctx) put_uint32(pkt, ssh->term_width); put_uint32(pkt, 0); /* width in pixels */ put_uint32(pkt, 0); /* height in pixels */ - parse_ttymodes(BinarySink_UPCAST(pkt), ssh, ssh1_send_ttymode); - put_byte(pkt, SSH1_TTY_OP_ISPEED); - put_uint32(pkt, ssh->ispeed); - put_byte(pkt, SSH1_TTY_OP_OSPEED); - put_uint32(pkt, ssh->ospeed); - put_byte(pkt, SSH_TTY_OP_END); + write_ttymodes_to_packet_from_conf( + BinarySink_UPCAST(pkt), ssh->frontend, ssh->conf, + 1, ssh->ospeed, ssh->ispeed); ssh_pkt_write(ssh, pkt); ssh->state = SSH_STATE_INTERMED; crMaybeWaitUntilV((pktin = pq_pop(&ssh->pq_ssh1_connection)) != NULL); @@ -6825,22 +6666,6 @@ static void ssh2_msg_userauth_banner(Ssh ssh, PktIn *pktin) } } -/* Helper function to deal with sending tty modes for "pty-req" */ -static void ssh2_send_ttymode(BinarySink *bs, - const struct ssh_ttymode *mode, char *val) -{ - put_byte(bs, mode->opcode); - - switch (mode->type) { - case TTY_OP_CHAR: - put_uint32(bs, ssh_tty_parse_specchar(val)); - break; - case TTY_OP_BOOL: - put_uint32(bs, ssh_tty_parse_boolean(val)); - break; - } -} - static void ssh2_setup_x11(struct ssh_channel *c, PktIn *pktin, void *ctx) { @@ -6935,12 +6760,9 @@ static void ssh2_setup_pty(struct ssh_channel *c, PktIn *pktin, put_uint32(pktout, 0); /* pixel height */ { strbuf *modebuf = strbuf_new(); - parse_ttymodes(BinarySink_UPCAST(modebuf), ssh, ssh2_send_ttymode); - put_byte(modebuf, SSH2_TTY_OP_ISPEED); - put_uint32(modebuf, ssh->ispeed); - put_byte(modebuf, SSH2_TTY_OP_OSPEED); - put_uint32(modebuf, ssh->ospeed); - put_byte(modebuf, SSH_TTY_OP_END); + write_ttymodes_to_packet_from_conf( + BinarySink_UPCAST(modebuf), ssh->frontend, ssh->conf, + 2, ssh->ospeed, ssh->ispeed); put_stringsb(pktout, modebuf); } ssh2_pkt_send(ssh, pktout); diff --git a/ssh.h b/ssh.h index f4ef94ef..bfeb20df 100644 --- a/ssh.h +++ b/ssh.h @@ -1321,3 +1321,8 @@ enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_LOG2_ENUM) }; #define TMP_DECLARE_REAL_ENUM(thing) thing = 1 << log2_##thing, enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_REAL_ENUM) }; #undef TMP_DECLARE_REAL_ENUM + +/* Shared function that writes tty modes into a pty request */ +void write_ttymodes_to_packet_from_conf( + BinarySink *bs, Frontend *frontend, Conf *conf, + int ssh_version, int ospeed, int ispeed); diff --git a/sshcommon.c b/sshcommon.c index ec471544..024b64c7 100644 --- a/sshcommon.c +++ b/sshcommon.c @@ -4,7 +4,9 @@ */ #include +#include +#include "putty.h" #include "ssh.h" #include "sshchan.h" @@ -286,3 +288,181 @@ int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof) { return FALSE; /* default: never proactively ask for a close */ } + +/* ---------------------------------------------------------------------- + * Common routine to marshal tty modes into an SSH packet. + */ + +void write_ttymodes_to_packet_from_conf( + BinarySink *bs, Frontend *frontend, Conf *conf, + int ssh_version, int ospeed, int ispeed) +{ + int i; + + /* + * Codes for terminal modes. + * Most of these are the same in SSH-1 and SSH-2. + * This list is derived from RFC 4254 and + * SSH-1 RFC-1.2.31. + */ + static const struct ssh_ttymode { + const char *mode; + int opcode; + enum { TTY_OP_CHAR, TTY_OP_BOOL } type; + } ssh_ttymodes[] = { + /* "V" prefix discarded for special characters relative to SSH specs */ + { "INTR", 1, TTY_OP_CHAR }, + { "QUIT", 2, TTY_OP_CHAR }, + { "ERASE", 3, TTY_OP_CHAR }, + { "KILL", 4, TTY_OP_CHAR }, + { "EOF", 5, TTY_OP_CHAR }, + { "EOL", 6, TTY_OP_CHAR }, + { "EOL2", 7, TTY_OP_CHAR }, + { "START", 8, TTY_OP_CHAR }, + { "STOP", 9, TTY_OP_CHAR }, + { "SUSP", 10, TTY_OP_CHAR }, + { "DSUSP", 11, TTY_OP_CHAR }, + { "REPRINT", 12, TTY_OP_CHAR }, + { "WERASE", 13, TTY_OP_CHAR }, + { "LNEXT", 14, TTY_OP_CHAR }, + { "FLUSH", 15, TTY_OP_CHAR }, + { "SWTCH", 16, TTY_OP_CHAR }, + { "STATUS", 17, TTY_OP_CHAR }, + { "DISCARD", 18, TTY_OP_CHAR }, + { "IGNPAR", 30, TTY_OP_BOOL }, + { "PARMRK", 31, TTY_OP_BOOL }, + { "INPCK", 32, TTY_OP_BOOL }, + { "ISTRIP", 33, TTY_OP_BOOL }, + { "INLCR", 34, TTY_OP_BOOL }, + { "IGNCR", 35, TTY_OP_BOOL }, + { "ICRNL", 36, TTY_OP_BOOL }, + { "IUCLC", 37, TTY_OP_BOOL }, + { "IXON", 38, TTY_OP_BOOL }, + { "IXANY", 39, TTY_OP_BOOL }, + { "IXOFF", 40, TTY_OP_BOOL }, + { "IMAXBEL", 41, TTY_OP_BOOL }, + { "IUTF8", 42, TTY_OP_BOOL }, + { "ISIG", 50, TTY_OP_BOOL }, + { "ICANON", 51, TTY_OP_BOOL }, + { "XCASE", 52, TTY_OP_BOOL }, + { "ECHO", 53, TTY_OP_BOOL }, + { "ECHOE", 54, TTY_OP_BOOL }, + { "ECHOK", 55, TTY_OP_BOOL }, + { "ECHONL", 56, TTY_OP_BOOL }, + { "NOFLSH", 57, TTY_OP_BOOL }, + { "TOSTOP", 58, TTY_OP_BOOL }, + { "IEXTEN", 59, TTY_OP_BOOL }, + { "ECHOCTL", 60, TTY_OP_BOOL }, + { "ECHOKE", 61, TTY_OP_BOOL }, + { "PENDIN", 62, TTY_OP_BOOL }, /* XXX is this a real mode? */ + { "OPOST", 70, TTY_OP_BOOL }, + { "OLCUC", 71, TTY_OP_BOOL }, + { "ONLCR", 72, TTY_OP_BOOL }, + { "OCRNL", 73, TTY_OP_BOOL }, + { "ONOCR", 74, TTY_OP_BOOL }, + { "ONLRET", 75, TTY_OP_BOOL }, + { "CS7", 90, TTY_OP_BOOL }, + { "CS8", 91, TTY_OP_BOOL }, + { "PARENB", 92, TTY_OP_BOOL }, + { "PARODD", 93, TTY_OP_BOOL } + }; + + /* Miscellaneous other tty-related constants. */ + enum { + /* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */ + SSH1_TTY_OP_ISPEED = 192, + SSH1_TTY_OP_OSPEED = 193, + SSH2_TTY_OP_ISPEED = 128, + SSH2_TTY_OP_OSPEED = 129, + + SSH_TTY_OP_END = 0 + }; + + for (i = 0; i < lenof(ssh_ttymodes); i++) { + const struct ssh_ttymode *mode = ssh_ttymodes + i; + const char *sval = conf_get_str_str(conf, CONF_ttymodes, mode->mode); + char *to_free = NULL; + + /* Every mode known to the current version of the code should be + * mentioned; this was ensured when settings were loaded. */ + + /* + * sval[0] can be + * - 'V', indicating that an explicit value follows it; + * - 'A', indicating that we should pass the value through from + * the local environment via get_ttymode; or + * - 'N', indicating that we should explicitly not send this + * mode. + */ + if (sval[0] == 'A') { + sval = to_free = get_ttymode(frontend, mode->mode); + } else if (sval[0] == 'V') { + sval++; /* skip the 'V' */ + } else { + /* else 'N', or something from the future we don't understand */ + continue; + } + + if (sval) { + /* + * Parse the string representation of the tty mode + * into the integer value it will take on the wire. + */ + unsigned ival = 0; + + switch (mode->type) { + case TTY_OP_CHAR: + if (*sval) { + char *next = NULL; + /* We know ctrlparse won't write to the string, so + * casting away const is ugly but allowable. */ + ival = ctrlparse((char *)sval, &next); + if (!next) + ival = sval[0]; + } else { + ival = 255; /* special value meaning "don't set" */ + } + break; + case TTY_OP_BOOL: + if (stricmp(sval, "yes") == 0 || + stricmp(sval, "on") == 0 || + stricmp(sval, "true") == 0 || + stricmp(sval, "+") == 0) + ival = 1; /* true */ + else if (stricmp(sval, "no") == 0 || + stricmp(sval, "off") == 0 || + stricmp(sval, "false") == 0 || + stricmp(sval, "-") == 0) + ival = 0; /* false */ + else + ival = (atoi(sval) != 0); + break; + default: + assert(0 && "Bad mode->type"); + } + + /* + * And write it into the output packet. The parameter + * value is formatted as a byte in SSH-1, but a uint32 + * in SSH-2. + */ + put_byte(bs, mode->opcode); + if (ssh_version == 1) + put_byte(bs, ival); + else + put_uint32(bs, ival); + } + + sfree(to_free); + } + + /* + * Finish off with the terminal speeds (which are formatted as + * uint32 in both protocol versions) and the end marker. + */ + put_byte(bs, ssh_version == 1 ? SSH1_TTY_OP_ISPEED : SSH2_TTY_OP_ISPEED); + put_uint32(bs, ispeed); + put_byte(bs, ssh_version == 1 ? SSH1_TTY_OP_OSPEED : SSH2_TTY_OP_OSPEED); + put_uint32(bs, ospeed); + put_byte(bs, SSH_TTY_OP_END); +}