Skip to content

Commit 55f2ec6

Browse files
committed
Add pty mode support code
Pty mode code added so that sshd server can do remote echo, backspace processing. etc and ssh.exe client does not have to do local echo. We can enrich it in future for more features and allowing programs like powershell to run interactive. Pty mode is central for interactive use and will be built using Windows console instead of termios that Linux/Unix uses.
1 parent b72c36c commit 55f2ec6

File tree

5 files changed

+207
-6
lines changed

5 files changed

+207
-6
lines changed

channels.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2459,15 +2459,27 @@ channel_input_data(int type, u_int32_t seq, void *ctxt)
24592459
if ( data[0] == '\033' ) { // escape char octal 33, decimal 27
24602460
if ( (data[1] == '[') && (data[2]== '2') && (data[3]== '0') && ( data[4]== 'h' )) {
24612461
lftocrlf = 1;
2462+
data = data + 5 ; // we have processed the 5 bytes ESC sequence
2463+
data_len = data_len - 5;
24622464
}
24632465
}
24642466
}
24652467
#endif
24662468

24672469
if (c->datagram)
24682470
buffer_put_string(&c->output, data, data_len);
2469-
else
2471+
else {
2472+
#ifndef WIN32_FIXME
2473+
buffer_append(&c->output, data, data_len);
2474+
#else
24702475
buffer_append(&c->output, data, data_len);
2476+
if ( c->isatty ) {
2477+
buffer_append(&c->input, data, data_len); // we echo the data if it is sshd server and pty interactive mode
2478+
if ( (data_len ==1) && (data[0] == '\b') )
2479+
buffer_append(&c->input, " \b", 2); // for backspace, we need to send space and another backspace for visual erase
2480+
}
2481+
#endif
2482+
}
24712483
packet_check_eom();
24722484
return 0;
24732485
}

contrib/win32/win32compat/socket.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2453,6 +2453,38 @@ int WSHELPread(int sfd, char *dst, unsigned int max)
24532453
case SFD_TYPE_CONSOLE:
24542454
{
24552455
ret = _read(sfd_to_fd(sfd), dst, max);
2456+
2457+
if (FD_ISSET(sfd_to_fd(sfd), &debug_sfds))
2458+
{
2459+
if (ret > 0)
2460+
{
2461+
dst[ret] = '\0';
2462+
2463+
debug("read[%d] len %d: %s", sfd_to_fd(sfd), ret, dst);
2464+
}
2465+
}
2466+
2467+
if (ret < 0)
2468+
{
2469+
error("read from pipe/console sfd [%d] failed with error code [%d]",
2470+
sfd, GetLastError());
2471+
}
2472+
2473+
break;
2474+
}
2475+
case 99:
2476+
{
2477+
ret = _getch();
2478+
if ( ( ret == 0) || (ret == 0xE0) ) {
2479+
dst[0] = ret ;
2480+
ret = _getch(); // function key or arrow key needs 2 calls, the first returning a 0 or 0xE0
2481+
dst[1] = ret;
2482+
ret = 2;
2483+
}
2484+
else {
2485+
dst[0] = ret;
2486+
ret = 1;
2487+
}
24562488

24572489
if (FD_ISSET(sfd_to_fd(sfd), &debug_sfds))
24582490
{

session.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,21 @@ do_exec_no_pty(Session *s, const char *command)
774774

775775
GetUserName(name, &size);
776776

777+
//if (!(s -> is_subsystem)) {
778+
// Send to the remote client ANSI/VT Sequence so that they send us CRLF in place of LF
779+
//Channel *c=channel_by_id ( s->chanid );
780+
//buffer_append(&c->input, "\033[20h", 5);
781+
//channel_output_poll();
782+
//}
783+
784+
//if (s ->ttyfd != -1) {
785+
// set the channel to tty interactive type
786+
// Channel *c=channel_by_id ( s->chanid );
787+
// c->isatty = 1;
788+
//}
789+
790+
if ( (s->term) && (s->term[0]) )
791+
SetEnvironmentVariable("TERM", s->term);
777792
/*
778793
* Create new process as other user using access token object.
779794
*/
@@ -869,8 +884,11 @@ do_exec_no_pty(Session *s, const char *command)
869884
*/
870885

871886
if (compat20)
872-
{
873-
session_set_fds(s, sockin[1], sockout[1], sockerr[1], s -> is_subsystem, 0);
887+
{
888+
if ( s->ttyfd == -1)
889+
session_set_fds(s, sockin[1], sockout[1], sockerr[1], s -> is_subsystem, 0);
890+
else
891+
session_set_fds(s, sockin[1], sockout[1], sockerr[1], s -> is_subsystem, 1); // tty interctive session
874892
}
875893
else
876894
{

sshpty.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,13 @@ pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, size_t namebuflen)
9191
#else
9292

9393
/*
94-
* Not implemented on Win32.
94+
* Simple console screen implementation in Win32 to give a Unix like pty for interactive sessions
9595
*/
96-
97-
return 0;
96+
*ttyfd = 0; // first ttyfd & ptyfd is indexed at 0
97+
*ptyfd = 0;
98+
strlcpy(namebuf, "console", namebuflen);
99+
return 1;
100+
//return 0;
98101

99102
#endif
100103
}

ttymodes.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,5 +505,141 @@ tty_parse_modes(int fd, int *n_bytes_ptr)
505505
/* Set the new modes for the terminal. */
506506
if (tcsetattr(fd, TCSANOW, &tio) == -1)
507507
logit("Setting tty modes failed: %.100s", strerror(errno));
508+
#else
509+
//struct termios tio;
510+
u_int tio[255]; // win32 dummy
511+
u_int tioFIELD ;
512+
int opcode, baud;
513+
int n_bytes = 0;
514+
int failure = 0;
515+
u_int (*get_arg)(void);
516+
int arg_size;
517+
518+
if (compat20) {
519+
*n_bytes_ptr = packet_get_int();
520+
if (*n_bytes_ptr == 0)
521+
return;
522+
get_arg = packet_get_int;
523+
arg_size = 4;
524+
} else {
525+
get_arg = packet_get_char;
526+
arg_size = 1;
527+
}
528+
529+
/*
530+
* Get old attributes for the terminal. We will modify these
531+
* flags. I am hoping that if there are any machine-specific
532+
* modes, they will initially have reasonable values.
533+
*/
534+
//if (tcgetattr(fd, &tio) == -1) {
535+
//logit("tcgetattr: %.100s", strerror(errno));
536+
//failure = -1;
537+
//}
538+
539+
for (;;) {
540+
n_bytes += 1;
541+
opcode = packet_get_char();
542+
switch (opcode) {
543+
case TTY_OP_END:
544+
goto set;
545+
546+
/* XXX: future conflict possible */
547+
case TTY_OP_ISPEED_PROTO1:
548+
case TTY_OP_ISPEED_PROTO2:
549+
n_bytes += 4;
550+
baud = packet_get_int();
551+
break;
552+
553+
/* XXX: future conflict possible */
554+
case TTY_OP_OSPEED_PROTO1:
555+
case TTY_OP_OSPEED_PROTO2:
556+
n_bytes += 4;
557+
baud = packet_get_int();
558+
break;
559+
560+
#define TTYCHAR(NAME, OP) \
561+
case OP: \
562+
n_bytes += arg_size; \
563+
tio[NAME] = special_char_decode(get_arg()); \
564+
break;
565+
#define TTYMODE(NAME, FIELD, OP) \
566+
case OP: \
567+
n_bytes += arg_size; \
568+
if (get_arg()) \
569+
tioFIELD |= NAME; \
570+
else \
571+
tioFIELD &= ~NAME; \
572+
break;
573+
574+
//#include "ttymodes.h"
575+
576+
#undef TTYCHAR
577+
#undef TTYMODE
578+
579+
default:
580+
debug("Ignoring unsupported tty mode opcode %d (0x%x)",
581+
opcode, opcode);
582+
if (!compat20) {
583+
/*
584+
* SSH1:
585+
* Opcodes 1 to 127 are defined to have
586+
* a one-byte argument.
587+
* Opcodes 128 to 159 are defined to have
588+
* an integer argument.
589+
*/
590+
if (opcode > 0 && opcode < 128) {
591+
n_bytes += 1;
592+
(void) packet_get_char();
593+
break;
594+
} else if (opcode >= 128 && opcode < 160) {
595+
n_bytes += 4;
596+
(void) packet_get_int();
597+
break;
598+
} else {
599+
/*
600+
* It is a truly undefined opcode (160 to 255).
601+
* We have no idea about its arguments. So we
602+
* must stop parsing. Note that some data
603+
* may be left in the packet; hopefully there
604+
* is nothing more coming after the mode data.
605+
*/
606+
logit("parse_tty_modes: unknown opcode %d",
607+
opcode);
608+
goto set;
609+
}
610+
} else {
611+
/*
612+
* SSH2:
613+
* Opcodes 1 to 159 are defined to have
614+
* a uint32 argument.
615+
* Opcodes 160 to 255 are undefined and
616+
* cause parsing to stop.
617+
*/
618+
if (opcode > 0 && opcode < 160) {
619+
n_bytes += 4;
620+
(void) packet_get_int();
621+
break;
622+
} else {
623+
logit("parse_tty_modes: unknown opcode %d",
624+
opcode);
625+
goto set;
626+
}
627+
}
628+
}
629+
}
630+
631+
set:
632+
if (*n_bytes_ptr != n_bytes) {
633+
*n_bytes_ptr = n_bytes;
634+
logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d",
635+
*n_bytes_ptr, n_bytes);
636+
return; /* Don't process bytes passed */
637+
}
638+
if (failure == -1)
639+
return; /* Packet parsed ok but tcgetattr() failed */
640+
641+
/* Set the new modes for the terminal. */
642+
//if (tcsetattr(fd, TCSANOW, &tio) == -1)
643+
//logit("Setting tty modes failed: %.100s", strerror(errno));
508644
#endif
509645
}

0 commit comments

Comments
 (0)