diff --git a/ssh.h b/ssh.h index 9c198109..670fdb68 100644 --- a/ssh.h +++ b/ssh.h @@ -1393,6 +1393,44 @@ enum { #define SSH2_EXTENDED_DATA_STDERR 1 /* 0x1 */ +enum { + /* TTY modes with opcodes defined consistently in the SSH specs. */ + #define TTYMODE_CHAR(name, val, index) SSH_TTYMODE_##name = val, + #define TTYMODE_FLAG(name, val, field, mask) SSH_TTYMODE_##name = val, + #include "sshttymodes.h" + #undef TTYMODE_CHAR + #undef TTYMODE_FLAG + + /* Modes encoded differently between SSH-1 and SSH-2, for which we + * make up our own dummy opcodes to avoid confusion. */ + TTYMODE_dummy = 255, + TTYMODE_ISPEED, TTYMODE_OSPEED, + + /* Limiting value that we can use as an array bound below */ + TTYMODE_LIMIT, + + /* The real opcodes for terminal speeds. */ + TTYMODE_ISPEED_SSH1 = 192, + TTYMODE_OSPEED_SSH1 = 193, + TTYMODE_ISPEED_SSH2 = 128, + TTYMODE_OSPEED_SSH2 = 129, + + /* And the opcode that ends a list. */ + TTYMODE_END_OF_LIST = 0 +}; + +struct ssh_ttymodes { + /* A boolean per mode, indicating whether it's set. */ + int have_mode[TTYMODE_LIMIT]; + + /* The actual value for each mode. */ + unsigned mode_val[TTYMODE_LIMIT]; +}; + +struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf); +void write_ttymodes_to_packet(BinarySink *bs, int ssh_version, + struct ssh_ttymodes modes); + const char *ssh1_pkt_type(int type); const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type); int ssh2_pkt_type_code_valid(unsigned type); @@ -1429,11 +1467,6 @@ enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_LOG2_ENUM) }; 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, Seat *seat, Conf *conf, - int ssh_version, int ospeed, int ispeed); - /* Shared system for allocating local SSH channel ids. Expects to be * passed a tree full of structs that have a field called 'localid' of * type unsigned, and will check that! */ diff --git a/ssh1connection.c b/ssh1connection.c index 34ba5bff..c7e284b6 100644 --- a/ssh1connection.c +++ b/ssh1connection.c @@ -28,7 +28,6 @@ struct ssh1_connection_state { int got_pty; int echoedit; - int ospeed, ispeed; int stdout_throttling; int session_ready; int session_eof_pending, session_eof_sent, session_terminated; @@ -709,11 +708,6 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl) s->portfwdmgr_configured = TRUE; if (!conf_get_int(s->conf, CONF_nopty)) { - /* Unpick the terminal-speed string. */ - /* XXX perhaps we should allow no speeds to be sent. */ - s->ospeed = 38400; s->ispeed = 38400; /* last-resort defaults */ - sscanf(conf_get_str(s->conf, CONF_termspeed), "%d,%d", - &s->ospeed, &s->ispeed); /* Send the pty request. */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_REQUEST_PTY); put_stringz(pktout, conf_get_str(s->conf, CONF_termtype)); @@ -723,14 +717,13 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl) s->term_height_orig = s->term_height; put_uint32(pktout, 0); /* width in pixels */ put_uint32(pktout, 0); /* height in pixels */ - write_ttymodes_to_packet_from_conf( - BinarySink_UPCAST(pktout), s->ppl.seat, s->conf, - 1, s->ospeed, s->ispeed); + write_ttymodes_to_packet( + BinarySink_UPCAST(pktout), 1, + get_ttymodes_from_conf(s->ppl.seat, s->conf)); pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh1_connection_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_SUCCESS) { - ppl_logevent(("Allocated pty (ospeed %dbps, ispeed %dbps)", - s->ospeed, s->ispeed)); + ppl_logevent(("Allocated pty")); s->got_pty = TRUE; } else if (pktin->type == SSH1_SMSG_FAILURE) { ppl_printf(("Server refused to allocate pty\r\n")); diff --git a/ssh2connection.c b/ssh2connection.c index 1307a6b1..c0b3e365 100644 --- a/ssh2connection.c +++ b/ssh2connection.c @@ -1593,13 +1593,8 @@ static void ssh2channel_request_pty( { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; - int ospeed, ispeed; strbuf *modebuf; - ospeed = ispeed = 38400; /* last-resort defaults */ - sscanf(conf_get_str(conf, CONF_termspeed), "%d,%d", - &ospeed, &ispeed); - PktOut *pktout = ssh2_chanreq_init( c, "pty-req", want_reply ? ssh2_channel_response : NULL, NULL); put_stringz(pktout, conf_get_str(conf, CONF_termtype)); @@ -1608,9 +1603,9 @@ static void ssh2channel_request_pty( put_uint32(pktout, 0); /* pixel width */ put_uint32(pktout, 0); /* pixel height */ modebuf = strbuf_new(); - write_ttymodes_to_packet_from_conf( - BinarySink_UPCAST(modebuf), s->ppl.seat, conf, - 2, ospeed, ispeed); + write_ttymodes_to_packet( + BinarySink_UPCAST(modebuf), 2, + get_ttymodes_from_conf(s->ppl.seat, conf)); put_stringsb(pktout, modebuf); pq_push(s->ppl.out_pq, pktout); } diff --git a/sshcommon.c b/sshcommon.c index bd718dee..51595de5 100644 --- a/sshcommon.c +++ b/sshcommon.c @@ -367,101 +367,47 @@ void chan_no_request_response(Channel *chan, int success) } /* ---------------------------------------------------------------------- - * Common routine to marshal tty modes into an SSH packet. + * Common routines for handling SSH tty modes. */ -void write_ttymodes_to_packet_from_conf( - BinarySink *bs, Seat *seat, Conf *conf, - int ssh_version, int ospeed, int ispeed) +static unsigned real_ttymode_opcode(unsigned our_opcode, int ssh_version) { - int i; + switch (our_opcode) { + case TTYMODE_ISPEED: + return ssh_version == 1 ? TTYMODE_ISPEED_SSH1 : TTYMODE_ISPEED_SSH2; + case TTYMODE_OSPEED: + return ssh_version == 1 ? TTYMODE_OSPEED_SSH1 : TTYMODE_OSPEED_SSH2; + default: + return our_opcode; + } +} - /* - * 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 { +struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf) +{ + struct ssh_ttymodes modes; + size_t i; + + static const struct mode_name_type { 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 } + enum { TYPE_CHAR, TYPE_BOOL } type; + } modes_names_types[] = { + #define TTYMODE_CHAR(name, val, index) { #name, val, TYPE_CHAR }, + #define TTYMODE_FLAG(name, val, field, mask) { #name, val, TYPE_BOOL }, + #include "sshttymodes.h" + #undef TTYMODE_CHAR + #undef TTYMODE_FLAG }; - /* 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, + memset(&modes, 0, sizeof(modes)); - SSH_TTY_OP_END = 0 - }; - - for (i = 0; i < lenof(ssh_ttymodes); i++) { - const struct ssh_ttymode *mode = ssh_ttymodes + i; + for (i = 0; i < lenof(modes_names_types); i++) { + const struct mode_name_type *mode = &modes_names_types[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. */ + if (!sval) + sval = "N"; /* just in case */ /* * sval[0] can be @@ -488,7 +434,7 @@ void write_ttymodes_to_packet_from_conf( unsigned ival = 0; switch (mode->type) { - case TTY_OP_CHAR: + case TYPE_CHAR: if (*sval) { char *next = NULL; /* We know ctrlparse won't write to the string, so @@ -500,7 +446,7 @@ void write_ttymodes_to_packet_from_conf( ival = 255; /* special value meaning "don't set" */ } break; - case TTY_OP_BOOL: + case TYPE_BOOL: if (stricmp(sval, "yes") == 0 || stricmp(sval, "on") == 0 || stricmp(sval, "true") == 0 || @@ -518,30 +464,48 @@ void write_ttymodes_to_packet_from_conf( 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); + modes.have_mode[mode->opcode] = TRUE; + modes.mode_val[mode->opcode] = 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); + { + unsigned ospeed, ispeed; + + /* Unpick the terminal-speed config string. */ + ospeed = ispeed = 38400; /* last-resort defaults */ + sscanf(conf_get_str(conf, CONF_termspeed), "%u,%u", &ospeed, &ispeed); + /* Currently we unconditionally set these */ + modes.have_mode[TTYMODE_ISPEED] = TRUE; + modes.mode_val[TTYMODE_ISPEED] = ispeed; + modes.have_mode[TTYMODE_OSPEED] = TRUE; + modes.mode_val[TTYMODE_OSPEED] = ospeed; + } + + return modes; +} + +void write_ttymodes_to_packet(BinarySink *bs, int ssh_version, + struct ssh_ttymodes modes) +{ + unsigned i; + + for (i = 0; i < TTYMODE_LIMIT; i++) { + if (modes.have_mode[i]) { + unsigned val = modes.mode_val[i]; + unsigned opcode = real_ttymode_opcode(i, ssh_version); + + put_byte(bs, opcode); + if (ssh_version == 1 && opcode >= 1 && opcode <= 127) + put_byte(bs, val); + else + put_uint32(bs, val); + } + } + + put_byte(bs, TTYMODE_END_OF_LIST); } /* ---------------------------------------------------------------------- diff --git a/sshttymodes.h b/sshttymodes.h new file mode 100644 index 00000000..8fffe1c5 --- /dev/null +++ b/sshttymodes.h @@ -0,0 +1,179 @@ +/* + * List of SSH terminal modes, indicating whether SSH types them as + * char or boolean, and if they're boolean, which POSIX flags field of + * a termios structure they appear in, and what bit mask removes them + * (e.g. CS7 and CS8 aren't single bits). + * + * Sources: RFC 4254, SSH-1 RFC-1.2.31, POSIX 2017, and the Linux + * termios manpage for flags not specified by POSIX. + * + * This is a separate header file rather than my usual style of a + * parametric list macro, because in this case I need to be able to + * #ifdef out each mode in case it's not defined on a particular + * target system. + * + * If you want only the locally defined modes, #define + * TTYMODES_LOCAL_ONLY before including this header. + */ +#if !defined TTYMODES_LOCAL_ONLY || defined VINTR +TTYMODE_CHAR(INTR, 1, VINTR) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VQUIT +TTYMODE_CHAR(QUIT, 2, VQUIT) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VERASE +TTYMODE_CHAR(ERASE, 3, VERASE) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VKILL +TTYMODE_CHAR(KILL, 4, VKILL) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VEOF +TTYMODE_CHAR(EOF, 5, VEOF) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VEOL +TTYMODE_CHAR(EOL, 6, VEOL) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VEOL2 +TTYMODE_CHAR(EOL2, 7, VEOL2) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VSTART +TTYMODE_CHAR(START, 8, VSTART) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VSTOP +TTYMODE_CHAR(STOP, 9, VSTOP) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VSUSP +TTYMODE_CHAR(SUSP, 10, VSUSP) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VDSUSP +TTYMODE_CHAR(DSUSP, 11, VDSUSP) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VREPRINT +TTYMODE_CHAR(REPRINT, 12, VREPRINT) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VWERASE +TTYMODE_CHAR(WERASE, 13, VWERASE) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VLNEXT +TTYMODE_CHAR(LNEXT, 14, VLNEXT) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VFLUSH +TTYMODE_CHAR(FLUSH, 15, VFLUSH) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VSWTCH +TTYMODE_CHAR(SWTCH, 16, VSWTCH) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VSTATUS +TTYMODE_CHAR(STATUS, 17, VSTATUS) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined VDISCARD +TTYMODE_CHAR(DISCARD, 18, VDISCARD) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IGNPAR +TTYMODE_FLAG(IGNPAR, 30, i, IGNPAR) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined PARMRK +TTYMODE_FLAG(PARMRK, 31, i, PARMRK) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined INPCK +TTYMODE_FLAG(INPCK, 32, i, INPCK) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ISTRIP +TTYMODE_FLAG(ISTRIP, 33, i, ISTRIP) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined INLCR +TTYMODE_FLAG(INLCR, 34, i, INLCR) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IGNCR +TTYMODE_FLAG(IGNCR, 35, i, IGNCR) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ICRNL +TTYMODE_FLAG(ICRNL, 36, i, ICRNL) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IUCLC +TTYMODE_FLAG(IUCLC, 37, i, IUCLC) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IXON +TTYMODE_FLAG(IXON, 38, i, IXON) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IXANY +TTYMODE_FLAG(IXANY, 39, i, IXANY) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IXOFF +TTYMODE_FLAG(IXOFF, 40, i, IXOFF) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IMAXBEL +TTYMODE_FLAG(IMAXBEL, 41, i, IMAXBEL) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IUTF8 +TTYMODE_FLAG(IUTF8, 42, i, IUTF8) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ISIG +TTYMODE_FLAG(ISIG, 50, l, ISIG) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ICANON +TTYMODE_FLAG(ICANON, 51, l, ICANON) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined XCASE +TTYMODE_FLAG(XCASE, 52, l, XCASE) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ECHO +TTYMODE_FLAG(ECHO, 53, l, ECHO) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ECHOE +TTYMODE_FLAG(ECHOE, 54, l, ECHOE) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ECHOK +TTYMODE_FLAG(ECHOK, 55, l, ECHOK) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ECHONL +TTYMODE_FLAG(ECHONL, 56, l, ECHONL) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined NOFLSH +TTYMODE_FLAG(NOFLSH, 57, l, NOFLSH) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined TOSTOP +TTYMODE_FLAG(TOSTOP, 58, l, TOSTOP) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined IEXTEN +TTYMODE_FLAG(IEXTEN, 59, l, IEXTEN) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ECHOCTL +TTYMODE_FLAG(ECHOCTL, 60, l, ECHOCTL) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ECHOKE +TTYMODE_FLAG(ECHOKE, 61, l, ECHOKE) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined PENDIN +TTYMODE_FLAG(PENDIN, 62, l, PENDIN) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined OPOST +TTYMODE_FLAG(OPOST, 70, o, OPOST) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined OLCUC +TTYMODE_FLAG(OLCUC, 71, o, OLCUC) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ONLCR +TTYMODE_FLAG(ONLCR, 72, o, ONLCR) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined OCRNL +TTYMODE_FLAG(OCRNL, 73, o, OCRNL) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ONOCR +TTYMODE_FLAG(ONOCR, 74, o, ONOCR) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined ONLRET +TTYMODE_FLAG(ONLRET, 75, o, ONLRET) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined CS7 +TTYMODE_FLAG(CS7, 90, c, CSIZE) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined CS8 +TTYMODE_FLAG(CS8, 91, c, CSIZE) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined PARENB +TTYMODE_FLAG(PARENB, 92, c, PARENB) +#endif +#if !defined TTYMODES_LOCAL_ONLY || defined PARODD +TTYMODE_FLAG(PARODD, 93, c, PARODD) +#endif