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