From 69ede6fef29a45d342245245da56b6cb0e305f57 Mon Sep 17 00:00:00 2001 From: bagajjal Date: Tue, 18 Jul 2017 12:31:30 -0700 Subject: [PATCH] Terminal fixes (#182) Terminal code cleanup and control sequence handling.. PowerShell/Win32-OpenSSH#799 Picking the user32/kernel32 from the systemdirectory. PowerShell/Win32-OpenSSH#814 Console settings are not restored properly PowerShell/Win32-OpenSSH#813 Fix the clearscreen issue while connecting to non-windows (UNIX/LINUX) servers. PowerShell/Win32-OpenSSH#807 vi arrow keys are not working PowerShell/Win32-OpenSSH#806 Fix the nopty unix ssh session. PowerShell/Win32-OpenSSH#805 Fix emacs issue PowerShell/Win32-OpenSSH#802 --- contrib/win32/win32compat/console.c | 70 +++--- contrib/win32/win32compat/console.h | 12 +- contrib/win32/win32compat/shell-host.c | 273 +++++++++++++++-------- contrib/win32/win32compat/termio.c | 44 +++- contrib/win32/win32compat/tncon.c | 2 +- contrib/win32/win32compat/tncon.h | 2 +- contrib/win32/win32compat/tnnet.c | 28 ++- contrib/win32/win32compat/win32_sshtty.c | 14 +- 8 files changed, 285 insertions(+), 160 deletions(-) diff --git a/contrib/win32/win32compat/console.c b/contrib/win32/win32compat/console.c index fdd51d2aa..b0b447af5 100644 --- a/contrib/win32/win32compat/console.c +++ b/contrib/win32/win32compat/console.c @@ -42,7 +42,8 @@ #include "ansiprsr.h" HANDLE hOutputConsole = NULL; -DWORD dwSavedAttributes = 0; +DWORD stdin_dwSavedAttributes = 0; +DWORD stdout_dwSavedAttributes = 0; WORD wStartingAttributes = 0; int ScreenX; @@ -72,8 +73,8 @@ int in_raw_mode = 0; char *consoleTitle = "OpenSSH SSH client"; /* Used to enter the raw mode */ -int -ConEnterRawMode(DWORD OutputHandle, BOOL fSmartInit) +void +ConEnterRawMode() { DWORD dwAttributes = 0; DWORD dwRet = 0; @@ -81,22 +82,22 @@ ConEnterRawMode(DWORD OutputHandle, BOOL fSmartInit) CONSOLE_SCREEN_BUFFER_INFO csbi; static bool bFirstConInit = true; - hOutputConsole = GetStdHandle(OutputHandle); + hOutputConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hOutputConsole == INVALID_HANDLE_VALUE) { dwRet = GetLastError(); error("GetStdHandle on OutputHandle failed with %d\n", dwRet); - return dwRet; + return; } - if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwSavedAttributes)) { + if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &stdin_dwSavedAttributes)) { dwRet = GetLastError(); error("GetConsoleMode on STD_INPUT_HANDLE failed with %d\n", dwRet); - return dwRet; + return; } SetConsoleTitle(consoleTitle); - dwAttributes = dwSavedAttributes; + dwAttributes = stdin_dwSavedAttributes; dwAttributes &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT); dwAttributes |= ENABLE_WINDOW_INPUT; @@ -104,17 +105,17 @@ ConEnterRawMode(DWORD OutputHandle, BOOL fSmartInit) if (!SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), dwAttributes)) { /* Windows NT */ dwRet = GetLastError(); error("SetConsoleMode on STD_INPUT_HANDLE failed with %d\n", dwRet); - return dwRet; + return; } - if (!GetConsoleMode(hOutputConsole, &dwAttributes)) { + if (!GetConsoleMode(hOutputConsole, &stdout_dwSavedAttributes)) { dwRet = GetLastError(); error("GetConsoleMode on hOutputConsole failed with %d\n", dwRet); - return dwRet; - + return; } - dwAttributes |= (DWORD)ENABLE_VIRTUAL_TERMINAL_PROCESSING; + dwAttributes = stdout_dwSavedAttributes; + dwAttributes |= (DWORD)ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; char *envValue = NULL; _dupenv_s(&envValue, NULL, "SSH_TERM_CONHOST_PARSER"); @@ -140,7 +141,7 @@ ConEnterRawMode(DWORD OutputHandle, BOOL fSmartInit) SavedViewRect = csbi.srWindow; debug("console doesn't support the ansi parsing"); } else { - ConMoveCursorTop(csbi); + ConSaveViewRect(); debug("console supports the ansi parsing"); } @@ -150,26 +151,18 @@ ConEnterRawMode(DWORD OutputHandle, BOOL fSmartInit) ScrollBottom = ConVisibleWindowHeight(); in_raw_mode = 1; - - return 0; } /* Used to Uninitialize the Console */ -int +void ConExitRawMode() { - CONSOLE_SCREEN_BUFFER_INFO consoleInfo; - in_raw_mode = 0; - if (hOutputConsole == NULL || !GetConsoleScreenBufferInfo(hOutputConsole, &consoleInfo)) - return 0; - - SetConsoleMode(hOutputConsole, dwSavedAttributes); - - return 0; + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), stdin_dwSavedAttributes); + SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), stdout_dwSavedAttributes); } /* Used to exit the raw mode */ -int +void ConUnInitWithRestore() { DWORD dwWritten; @@ -177,19 +170,19 @@ ConUnInitWithRestore() CONSOLE_SCREEN_BUFFER_INFO consoleInfo; if (hOutputConsole == NULL) - return 0; + return; if (!GetConsoleScreenBufferInfo(hOutputConsole, &consoleInfo)) - return 0; + return; - SetConsoleMode(hOutputConsole, dwSavedAttributes); + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), stdin_dwSavedAttributes); + SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), stdout_dwSavedAttributes); Coord = consoleInfo.dwCursorPosition; Coord.X = 0; DWORD dwNumChar = (consoleInfo.dwSize.Y - consoleInfo.dwCursorPosition.Y) * consoleInfo.dwSize.X; FillConsoleOutputCharacter(hOutputConsole, ' ', dwNumChar, Coord, &dwWritten); FillConsoleOutputAttribute(hOutputConsole, wStartingAttributes, dwNumChar, Coord, &dwWritten); SetConsoleTextAttribute(hOutputConsole, wStartingAttributes); - return 0; } BOOL @@ -1571,16 +1564,17 @@ ConSaveWindowsState() } void -ConMoveCursorTop(CONSOLE_SCREEN_BUFFER_INFO csbi) +ConMoveCursorTopOfVisibleWindow() { - /* Windows server at first sends the "cls" after the connection is established. - * Since we don't want to loose any data on the console, we would like to scroll down - * the visible window. - */ - int offset = csbi.dwCursorPosition.Y - csbi.srWindow.Top; - ConMoveVisibleWindow(offset); + CONSOLE_SCREEN_BUFFER_INFO csbi; + int offset; - ConSaveViewRect(); + if (GetConsoleScreenBufferInfo(hOutputConsole, &csbi)) { + offset = csbi.dwCursorPosition.Y - csbi.srWindow.Top; + ConMoveVisibleWindow(offset); + + ConSaveViewRect(); + } } HANDLE diff --git a/contrib/win32/win32compat/console.h b/contrib/win32/win32compat/console.h index 46c73bf01..9d44e5bcb 100644 --- a/contrib/win32/win32compat/console.h +++ b/contrib/win32/win32compat/console.h @@ -83,11 +83,15 @@ #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 #endif +#ifndef DISABLE_NEWLINE_AUTO_RETURN +#define DISABLE_NEWLINE_AUTO_RETURN 0x8 +#endif + typedef void * SCREEN_HANDLE; -int ConEnterRawMode(DWORD OutputHandle, BOOL fSmartInit); -int ConUnInitWithRestore(); -int ConExitRawMode(); +void ConEnterRawMode(); +void ConUnInitWithRestore(); +void ConExitRawMode(); BOOL ConIsRedirected(HANDLE hInput); HANDLE GetConsoleOutputHandle(); HANDLE GetConsoleInputHandle(); @@ -141,6 +145,6 @@ void ConSaveWindowsState(); void ConMoveVisibleWindow(int offset); int is_cursor_at_lastline_of_visible_window(); void ConGetCursorPosition(int *x, int *y); -void ConMoveCursorTop(CONSOLE_SCREEN_BUFFER_INFO csbi); +void ConMoveCursorTopOfVisibleWindow(); HANDLE get_console_handle(FILE *, DWORD *); #endif diff --git a/contrib/win32/win32compat/shell-host.c b/contrib/win32/win32compat/shell-host.c index 019a657a5..113adbf16 100644 --- a/contrib/win32/win32compat/shell-host.c +++ b/contrib/win32/win32compat/shell-host.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "misc_internal.h" #include "inc\utf.h" @@ -92,9 +94,10 @@ struct key_translation { int in_key_len; } key_translation; -/* All the substrings (Ex- "\x1b") should be in the end, otherwise ProcessIncomingKeys() will not work as expected */ +/* All the substrings should be in the end, otherwise ProcessIncomingKeys() will not work as expected */ struct key_translation keys[] = { { L"\r", VK_RETURN, L'\r' , 0}, + { L"\n", VK_RETURN, L'\r' , 0 }, { L"\b", VK_BACK, L'\b' , 0}, { L"\x7f", VK_BACK, L'\b' , 0}, { L"\t", VK_TAB, L'\t' , 0}, @@ -102,9 +105,9 @@ struct key_translation keys[] = { { L"\x1b[B", VK_DOWN, 0 , 0}, { L"\x1b[C", VK_RIGHT, 0 , 0}, { L"\x1b[D", VK_LEFT, 0 , 0}, - { L"\x1b[F", VK_END, 0 , 0 }, /* KeyPad END */ - { L"\x1b[H", VK_HOME, 0 , 0 }, /* KeyPad HOME */ - { L"\x1b[Z", 0, 0 , 0 }, /* ignore Shift+TAB */ + { L"\x1b[F", VK_END, 0 , 0}, /* KeyPad END */ + { L"\x1b[H", VK_HOME, 0 , 0}, /* KeyPad HOME */ + { L"\x1b[Z", 0, 0 , 0}, /* ignore Shift+TAB */ { L"\x1b[1~", VK_HOME, 0 , 0}, { L"\x1b[2~", VK_INSERT, 0 , 0}, { L"\x1b[3~", VK_DELETE, 0 , 0}, @@ -123,15 +126,21 @@ struct key_translation keys[] = { { L"\x1b[21~", VK_F10, 0 , 0}, { L"\x1b[23~", VK_F11, 0 , 0}, { L"\x1b[24~", VK_F12, 0 , 0}, - { L"\x1bOP", VK_F1, 0 , 0 }, - { L"\x1bOQ", VK_F2, 0 , 0 }, - { L"\x1bOR", VK_F3, 0 , 0 }, - { L"\x1bOS", VK_F4, 0 , 0 }, - { L"\x1b", VK_ESCAPE, L'\x1b' , 0} + { L"\x1bOA", VK_UP, 0 , 0}, + { L"\x1bOB", VK_DOWN, 0 , 0}, + { L"\x1bOC", VK_RIGHT, 0 , 0}, + { L"\x1bOD", VK_LEFT, 0 , 0}, + { L"\x1bOF", VK_END, 0 , 0}, /* KeyPad END */ + { L"\x1bOH", VK_HOME, 0 , 0}, /* KeyPad HOME */ + { L"\x1bOP", VK_F1, 0 , 0}, + { L"\x1bOQ", VK_F2, 0 , 0}, + { L"\x1bOR", VK_F3, 0 , 0}, + { L"\x1bOS", VK_F4, 0 , 0} }; static SHORT lastX = 0; static SHORT lastY = 0; +static wchar_t system32_path[PATH_MAX]; static wchar_t cmd_exe_path[PATH_MAX]; SHORT currentLine = 0; @@ -201,27 +210,58 @@ ConSRWidth() return consoleBufferInfo.srWindow.Right; } +struct key_translation * +FindKeyTransByMask(wchar_t prefix, const wchar_t * value, int vlen, wchar_t suffix) +{ + struct key_translation *k = NULL; + for (int i = 0; i < ARRAYSIZE(keys); i++) { + k = &keys[i]; + if (k->in_key_len < vlen + 2) continue; + if (k->in[0] != L'\033') continue; + if (k->in[1] != prefix) continue; + if (k->in[vlen + 2] != suffix) continue; + + if (vlen <= 1 && value[0] == k->in[2]) + return k; + if (vlen > 1 && wcsncmp(&k->in[2], value, vlen) == 0) + return k; + } + + return NULL; +} + +int +GetVirtualKeyByMask(wchar_t prefix, const wchar_t * value, int vlen, wchar_t suffix) +{ + struct key_translation * pk = FindKeyTransByMask(prefix, value, vlen, suffix); + return pk ? pk->vk : 0; +} + /* * This function will handle the console keystrokes. */ -void -SendKeyStroke(HANDLE hInput, int keyStroke, wchar_t character) +void +SendKeyStrokeEx(HANDLE hInput, int vKey, wchar_t character, DWORD ctrlState, BOOL keyDown) { DWORD wr = 0; INPUT_RECORD ir; ir.EventType = KEY_EVENT; - ir.Event.KeyEvent.bKeyDown = TRUE; - ir.Event.KeyEvent.wRepeatCount = 1; - ir.Event.KeyEvent.wVirtualKeyCode = keyStroke; - ir.Event.KeyEvent.wVirtualScanCode = 0; - ir.Event.KeyEvent.dwControlKeyState = 0; + ir.Event.KeyEvent.bKeyDown = keyDown; + ir.Event.KeyEvent.wRepeatCount = 0; + ir.Event.KeyEvent.wVirtualKeyCode = vKey; + ir.Event.KeyEvent.wVirtualScanCode = MapVirtualKeyA(vKey, MAPVK_VK_TO_VSC); + ir.Event.KeyEvent.dwControlKeyState = ctrlState; ir.Event.KeyEvent.uChar.UnicodeChar = character; WriteConsoleInputW(hInput, &ir, 1, &wr); +} - ir.Event.KeyEvent.bKeyDown = FALSE; - WriteConsoleInputW(hInput, &ir, 1, &wr); +void +SendKeyStroke(HANDLE hInput, int keyStroke, wchar_t character) +{ + SendKeyStrokeEx(hInput, keyStroke, character, 0, TRUE); + SendKeyStrokeEx(hInput, keyStroke, character, 0, FALSE); } void @@ -231,26 +271,80 @@ initialize_keylen() keys[i].in_key_len = (int) wcslen(keys[i].in); } +int +ProcessCtrlSequence(wchar_t *buf, int buf_len) +{ + int vkey = 0; + /* Decode special keys when pressed CTRL key */ + if (buf[0] == L'\033' && buf[1] == L'[' && buf[buf_len - 3] == L';' && buf[buf_len - 2] == L'5') { + if (buf[buf_len - 1] == L'~') { + /* VK_DELETE, VK_PGDN, VK_PGUP */ + if (!vkey && buf_len == 6) + vkey = GetVirtualKeyByMask(L'[', &buf[2], 1, L'~'); + + /* VK_F1 ... VK_F12 */ + if (!vkey && buf_len == 7) + vkey = GetVirtualKeyByMask(L'[', &buf[2], 2, L'~'); + } else { + /* VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN */ + if (!vkey && buf_len == 6 && buf[2] == L'1') + vkey = GetVirtualKeyByMask(L'[', &buf[5], 1, 0); + + /* VK_F1 ... VK_F4 */ + if (!vkey && buf_len == 6 && buf[2] == L'1' && isalpha(buf[5])) + vkey = GetVirtualKeyByMask(L'O', &buf[5], 1, 0); + } + if (vkey) { + SendKeyStrokeEx(child_in, VK_CONTROL, 0, LEFT_CTRL_PRESSED, TRUE); + SendKeyStrokeEx(child_in, vkey, 0, LEFT_CTRL_PRESSED, TRUE); + SendKeyStrokeEx(child_in, VK_CONTROL, 0, 0, FALSE); + SendKeyStrokeEx(child_in, vkey, 0, 0, FALSE); + } + } + + return vkey; +} + void ProcessIncomingKeys(char * ansikey) { + int buf_len = 0; + const int MAX_CTRL_SEQ_LEN = 7; + const wchar_t *ESC_SEQ = L"\x1b"; wchar_t *buf = utf8_to_utf16(ansikey); - + if (!buf) { printf("\nFailed to deserialize the client data, error:%d\n", GetLastError()); exit(255); } loop: - while (buf && (wcslen(buf) > 0)) { + while (buf && ((buf_len=(int)wcslen(buf)) > 0)) { for (int j = 0; j < ARRAYSIZE(keys); j++) { - if ( (wcslen(buf) >= keys[j].in_key_len) && (wcsncmp(buf, keys[j].in, keys[j].in_key_len) == 0) ) { + if ( (buf_len >= keys[j].in_key_len) && (wcsncmp(buf, keys[j].in, keys[j].in_key_len) == 0) ) { SendKeyStroke(child_in, keys[j].vk, keys[j].out); buf += keys[j].in_key_len; goto loop; } } + /* Decode special keys when pressed CTRL key. CTRL sequences can be of size 6 or 7. */ + if ((buf_len >= MAX_CTRL_SEQ_LEN) && ProcessCtrlSequence(buf, MAX_CTRL_SEQ_LEN)) { + buf += MAX_CTRL_SEQ_LEN; + goto loop; + } + + if ((buf_len >= (MAX_CTRL_SEQ_LEN - 1)) && ProcessCtrlSequence(buf, MAX_CTRL_SEQ_LEN - 1)) { + buf += (MAX_CTRL_SEQ_LEN - 1); + goto loop; + } + + if(wcsncmp(buf, ESC_SEQ, wcslen(ESC_SEQ)) == 0) { + SendKeyStroke(child_in, VK_ESCAPE, L'\x1b'); + buf += wcslen(ESC_SEQ); + goto loop; + } + if (*buf == L'\x3') /*Ctrl+C - Raise Ctrl+C*/ GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); else @@ -460,10 +554,13 @@ SendBuffer(HANDLE hInput, CHAR_INFO *buffer, DWORD bufferSize) } void -CalculateAndSetCursor(HANDLE hInput, UINT x, UINT y) +CalculateAndSetCursor(HANDLE hInput, short x, short y, BOOL scroll) { + if (scroll && y > currentLine) + for (short n = currentLine; n < y; n++) + SendLF(hInput); - SendSetCursor(pipe_out, x + 1, y + 1); + SendSetCursor(hInput, x + 1, y + 1); currentLine = y; } @@ -486,16 +583,16 @@ SizeWindow(HANDLE hInput) matchingFont.FontWeight = FW_NORMAL; wcscpy_s(matchingFont.FaceName, LF_FACESIZE, L"Consolas"); - bSuccess = __SetCurrentConsoleFontEx(child_out, FALSE, &matchingFont); + bSuccess = __SetCurrentConsoleFontEx(hInput, FALSE, &matchingFont); /* This information is the live screen */ ZeroMemory(&consoleInfo, sizeof(consoleInfo)); consoleInfo.cbSize = sizeof(consoleInfo); - bSuccess = GetConsoleScreenBufferInfoEx(child_out, &consoleInfo); + bSuccess = GetConsoleScreenBufferInfoEx(hInput, &consoleInfo); /* Get the largest size we can size the console window to */ - coordScreen = GetLargestConsoleWindowSize(child_out); + coordScreen = GetLargestConsoleWindowSize(hInput); /* Define the new console window size and scroll position */ if (inputSi.dwXCountChars == 0 || inputSi.dwYCountChars == 0) { @@ -507,18 +604,18 @@ SizeWindow(HANDLE hInput) srWindowRect.Bottom = min((SHORT)inputSi.dwYCountChars, coordScreen.Y) - 1; srWindowRect.Left = srWindowRect.Top = (SHORT)0; - /* Define the new console buffer size to be the maximum possible */ - coordScreen.X = 100; + /* Define the new console buffer history to be the maximum possible */ + coordScreen.X = srWindowRect.Right + 1; /* buffer width must be equ window width */ coordScreen.Y = 9999; - if (SetConsoleWindowInfo(child_out, TRUE, &srWindowRect)) - bSuccess = SetConsoleScreenBufferSize(child_out, coordScreen); + if (SetConsoleWindowInfo(hInput, TRUE, &srWindowRect)) + bSuccess = SetConsoleScreenBufferSize(hInput, coordScreen); else { - if (SetConsoleScreenBufferSize(child_out, coordScreen)) - bSuccess = SetConsoleWindowInfo(child_out, TRUE, &srWindowRect); + if (SetConsoleScreenBufferSize(hInput, coordScreen)) + bSuccess = SetConsoleWindowInfo(hInput, TRUE, &srWindowRect); } - bSuccess = GetConsoleScreenBufferInfoEx(child_out, &consoleInfo); + bSuccess = GetConsoleScreenBufferInfoEx(hInput, &consoleInfo); } DWORD WINAPI @@ -543,20 +640,25 @@ ProcessEvent(void *p) HWND hwnd; LONG idObject; LONG idChild; + CHAR_INFO pBuffer[MAX_EXPECTED_BUFFER_SIZE] = {0,}; + DWORD bufferSize; + SMALL_RECT readRect; + COORD coordBufSize; + COORD coordBufCoord; if (!p) return ERROR_INVALID_PARAMETER; consoleEvent* current = (consoleEvent *)p; - if (current) { - event = current->event; - hwnd = current->hwnd; - idObject = current->idObject; - idChild = current->idChild; - } else + if (!current) return ERROR_INVALID_PARAMETER; + event = current->event; + hwnd = current->hwnd; + idObject = current->idObject; + idChild = current->idChild; + if (event < EVENT_CONSOLE_CARET || event > EVENT_CONSOLE_LAYOUT) return ERROR_INVALID_PARAMETER; @@ -586,14 +688,15 @@ ProcessEvent(void *p) lastX = co.X; lastY = co.Y; - SendSetCursor(pipe_out, lastX + 1, lastY + 1); + if (lastX == 0 && lastY > currentLine) + CalculateAndSetCursor(pipe_out, lastX, lastY, TRUE); + else + SendSetCursor(pipe_out, lastX + 1, lastY + 1); break; } case EVENT_CONSOLE_UPDATE_REGION: { - SMALL_RECT readRect; - readRect.Top = HIWORD(idObject); readRect.Left = LOWORD(idObject); readRect.Bottom = HIWORD(idChild); @@ -617,8 +720,7 @@ ProcessEvent(void *p) } } - /* Figure out the buffer size */ - COORD coordBufSize; + /* Figure out the buffer size */ coordBufSize.Y = readRect.Bottom - readRect.Top + 1; coordBufSize.X = readRect.Right - readRect.Left + 1; @@ -632,7 +734,7 @@ ProcessEvent(void *p) return ERROR_INVALID_PARAMETER; /* Compute buffer size */ - DWORD bufferSize = coordBufSize.X * coordBufSize.Y; + bufferSize = coordBufSize.X * coordBufSize.Y; if (bufferSize > MAX_EXPECTED_BUFFER_SIZE) { if (!bStartup) { SendClearScreen(pipe_out); @@ -641,38 +743,22 @@ ProcessEvent(void *p) } return ERROR_SUCCESS; } - - /* Create the screen scrape buffer */ - CHAR_INFO *pBuffer = (PCHAR_INFO)malloc(sizeof(CHAR_INFO) * bufferSize); - if (!pBuffer) - return ERROR_INSUFFICIENT_BUFFER; - - /* The top left destination cell of the temporary buffer is row 0, col 0 */ - COORD coordBufCoord; + + /* The top left destination cell of the temporary buffer is row 0, col 0 */ coordBufCoord.X = 0; coordBufCoord.Y = 0; /* Copy the block from the screen buffer to the temp. buffer */ - if (!ReadConsoleOutput(child_out, pBuffer, coordBufSize, coordBufCoord, &readRect)) { - DWORD dwError = GetLastError(); - - free(pBuffer); - return dwError; - } - - if (readRect.Top > currentLine) - for (SHORT n = currentLine; n < readRect.Top; n++) - SendLF(pipe_out); + if (!ReadConsoleOutput(child_out, pBuffer, coordBufSize, coordBufCoord, &readRect)) + return GetLastError(); /* Set cursor location based on the reported location from the message */ - CalculateAndSetCursor(pipe_out, readRect.Left, readRect.Top); + CalculateAndSetCursor(pipe_out, readRect.Left, readRect.Top, TRUE); /* Send the entire block */ SendBuffer(pipe_out, pBuffer, bufferSize); lastViewPortY = ViewPortY; - lastLineLength = readRect.Left; - - free(pBuffer); + lastLineLength = readRect.Left; break; } @@ -683,38 +769,27 @@ ProcessEvent(void *p) wX = LOWORD(idObject); wY = HIWORD(idObject); - SMALL_RECT readRect; readRect.Top = wY; readRect.Bottom = wY; readRect.Left = wX; readRect.Right = ConSRWidth(); /* Set cursor location based on the reported location from the message */ - CalculateAndSetCursor(pipe_out, wX, wY); - - COORD coordBufSize; + CalculateAndSetCursor(pipe_out, wX, wY, TRUE); + coordBufSize.Y = readRect.Bottom - readRect.Top + 1; coordBufSize.X = readRect.Right - readRect.Left + 1; + bufferSize = coordBufSize.X * coordBufSize.Y; /* The top left destination cell of the temporary buffer is row 0, col 0 */ - COORD coordBufCoord; coordBufCoord.X = 0; coordBufCoord.Y = 0; - int pBufferSize = coordBufSize.X * coordBufSize.Y; - /* Send the one character. Note that a CR doesn't end up here */ - CHAR_INFO *pBuffer = (PCHAR_INFO)malloc(sizeof(CHAR_INFO) * pBufferSize); - if (!pBuffer) - return ERROR_INSUFFICIENT_BUFFER; /* Copy the block from the screen buffer to the temp. buffer */ - if (!ReadConsoleOutput(child_out, pBuffer, coordBufSize, coordBufCoord, &readRect)) { - DWORD dwError = GetLastError(); - free(pBuffer); - return dwError; - } + if (!ReadConsoleOutput(child_out, pBuffer, coordBufSize, coordBufCoord, &readRect)) + return GetLastError(); - SendBuffer(pipe_out, pBuffer, pBufferSize); - free(pBuffer); + SendBuffer(pipe_out, pBuffer, bufferSize); break; } @@ -951,13 +1026,9 @@ cleanup: wchar_t * w32_cmd_path() { - ZeroMemory(cmd_exe_path, PATH_MAX); - if (!GetSystemDirectory(cmd_exe_path, sizeof(cmd_exe_path))) { - printf("GetSystemDirectory failed"); - exit(255); - } + wcsncpy_s(cmd_exe_path, (sizeof(cmd_exe_path)/sizeof(wchar_t)), system32_path, wcslen(system32_path)+1); + wcscat_s(cmd_exe_path, (sizeof(cmd_exe_path)/sizeof(wchar_t)), L"\\cmd.exe"); - wcscat_s(cmd_exe_path, sizeof(cmd_exe_path), L"\\cmd.exe"); return cmd_exe_path; } @@ -972,14 +1043,21 @@ start_with_pty(wchar_t *command) DWORD dwStatus; HANDLE hEventHook = NULL; HMODULE hm_kernel32 = NULL, hm_user32 = NULL; + wchar_t kernel32_dll_path[PATH_MAX]={0,}, user32_dll_path[PATH_MAX]={0,}; if(cmd == NULL) { printf("ssh-shellhost is out of memory"); exit(255); } - - if ((hm_kernel32 = LoadLibraryW(L"kernel32.dll")) == NULL || - (hm_user32 = LoadLibraryW(L"user32.dll")) == NULL || + + wcsncpy_s(kernel32_dll_path, (sizeof(kernel32_dll_path)/sizeof(wchar_t)), system32_path, wcslen(system32_path)+1); + wcscat_s(kernel32_dll_path, (sizeof(kernel32_dll_path)/sizeof(wchar_t)), L"\\kernel32.dll"); + + wcsncpy_s(user32_dll_path, (sizeof(user32_dll_path)/sizeof(wchar_t)), system32_path, wcslen(system32_path)+1); + wcscat_s(user32_dll_path, (sizeof(user32_dll_path)/sizeof(wchar_t)), L"\\user32.dll"); + + if ((hm_kernel32 = LoadLibraryW(kernel32_dll_path)) == NULL || + (hm_user32 = LoadLibraryW(user32_dll_path)) == NULL || (__SetCurrentConsoleFontEx = (__t_SetCurrentConsoleFontEx)GetProcAddress(hm_kernel32, "SetCurrentConsoleFontEx")) == NULL || (__UnhookWinEvent = (__t_UnhookWinEvent)GetProcAddress(hm_user32, "UnhookWinEvent")) == NULL || (__SetWinEventHook = (__t_SetWinEventHook)GetProcAddress(hm_user32, "SetWinEventHook")) == NULL) { @@ -1310,9 +1388,6 @@ cleanup: return child_exit_code; } -#include -#include - static void* xmalloc(size_t size) { void* ptr; if ((ptr = malloc(size)) == NULL) { @@ -1447,6 +1522,12 @@ wmain(int ac, wchar_t **av) free(cmd_utf8); } + ZeroMemory(system32_path, sizeof(system32_path) / sizeof(wchar_t)); + if (!GetSystemDirectory(system32_path, sizeof(system32_path)/sizeof(wchar_t))) { + printf("GetSystemDirectory failed"); + exit(255); + } + if (pty_requested) return start_with_pty(cmd); else diff --git a/contrib/win32/win32compat/termio.c b/contrib/win32/win32compat/termio.c index 786c0fed2..3a14812bb 100644 --- a/contrib/win32/win32compat/termio.c +++ b/contrib/win32/win32compat/termio.c @@ -50,6 +50,7 @@ #define TERM_IO_BUF_SIZE 2048 extern int in_raw_mode; +BOOL isFirstTime = TRUE; struct io_status { DWORD to_transfer; @@ -84,11 +85,44 @@ ReadThread(_In_ LPVOID lpParameter) debug5("TermRead thread, io:%p", pio); memset(&read_status, 0, sizeof(read_status)); if (FILETYPE(pio) == FILE_TYPE_CHAR) { - while (nBytesReturned == 0) { - nBytesReturned = ReadConsoleForTermEmul(WINHANDLE(pio), - pio->read_details.buf, pio->read_details.buf_size); + if (in_raw_mode) { + while (nBytesReturned == 0) { + nBytesReturned = ReadConsoleForTermEmul(WINHANDLE(pio), + pio->read_details.buf, pio->read_details.buf_size); + } + read_status.transferred = nBytesReturned; + } else { + if (isFirstTime) { + isFirstTime = false; + + DWORD dwAttributes; + if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwAttributes)) + error("GetConsoleMode on STD_INPUT_HANDLE failed with %d\n", GetLastError()); + + dwAttributes |= (ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); + + if (!SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), dwAttributes)) + error("SetConsoleMode on STD_INPUT_HANDLE failed with %d\n", GetLastError()); + } + + if (!ReadFile(WINHANDLE(pio), pio->read_details.buf, + pio->read_details.buf_size, &read_status.transferred, NULL)) { + read_status.error = GetLastError(); + debug("ReadThread - ReadFile failed %d, io:%p", GetLastError(), pio); + } + + char *p = NULL; + if (p = strstr(pio->read_details.buf, "\r\n")) + *p++ = '\n'; + else if (p = strstr(pio->read_details.buf, "\r")) + *p++ = '\n'; + + if (p) { + *p = '\0'; + pio->read_details.buf_size = (DWORD)strlen(pio->read_details.buf); + read_status.transferred = pio->read_details.buf_size; + } } - read_status.transferred = nBytesReturned; } else { if (!ReadFile(WINHANDLE(pio), pio->read_details.buf, pio->read_details.buf_size, &read_status.transferred, NULL)) { @@ -221,7 +255,7 @@ syncio_close(struct w32_io* pio) /* If io is pending, let worker threads exit. */ if (pio->read_details.pending) { /* For console - the read thread is blocked so terminate it. */ - if (FILETYPE(pio) == FILE_TYPE_CHAR) + if (FILETYPE(pio) == FILE_TYPE_CHAR && in_raw_mode) TerminateThread(pio->read_overlapped.hEvent, 0); else WaitForSingleObject(pio->read_overlapped.hEvent, INFINITE); diff --git a/contrib/win32/win32compat/tncon.c b/contrib/win32/win32compat/tncon.c index 6d171c8b6..7dc2170c3 100644 --- a/contrib/win32/win32compat/tncon.c +++ b/contrib/win32/win32compat/tncon.c @@ -155,7 +155,7 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) switch (InputRecord.Event.KeyEvent.uChar.UnicodeChar) { case 0xd: if (pParams->nReceiveCRLF == ENUM_LF) - NetWriteString2(pParams->Socket, "\r", 1, 0); + NetWriteString2(pParams->Socket, "\n", 1, 0); else NetWriteString2(pParams->Socket, "\r\n", 2, 0); break; diff --git a/contrib/win32/win32compat/tncon.h b/contrib/win32/win32compat/tncon.h index c36c02b4b..aa82369cf 100644 --- a/contrib/win32/win32compat/tncon.h +++ b/contrib/win32/win32compat/tncon.h @@ -54,7 +54,7 @@ #define PREV_KEY "\x1b[5~" #define NEXT_KEY "\x1b[6~" #define SHIFT_TAB_KEY "\x1b[~" -#define ESCAPE_KEY "\x1b" +#define ESCAPE_KEY "\x1b" #define BACKSPACE_KEY "\b" // VT100 Function Key's diff --git a/contrib/win32/win32compat/tnnet.c b/contrib/win32/win32compat/tnnet.c index 39b0fc8b6..5c53ebeb4 100644 --- a/contrib/win32/win32compat/tnnet.c +++ b/contrib/win32/win32compat/tnnet.c @@ -41,6 +41,8 @@ #define dwBuffer 4096 extern BOOL isAnsiParsingRequired; +extern bool gbVTAppMode; +BOOL isFirstPacket = TRUE; /* * Server will always be returning a sequence of ANSI control characters which the client @@ -51,14 +53,34 @@ extern BOOL isAnsiParsingRequired; void processBuffer(HANDLE handle, char *buf, size_t len, unsigned char **respbuf, size_t *resplen) { - unsigned char* pszNewHead = NULL; - unsigned char* pszHead = NULL; - unsigned char* pszTail = NULL; + unsigned char *pszNewHead = NULL; + unsigned char *pszHead = NULL; + unsigned char *pszTail = NULL; + const char *applicationModeSeq = "\x1b[?1h"; + const int applicationModeSeqLen = (int)strlen(applicationModeSeq); + const char *normalModeSeq = "\x1b[?1l"; + const int normalModeSeqLen = (int)strlen(normalModeSeq); + const char *clsSeq = "\x1b[2J"; if (len == 0) return; if (false == isAnsiParsingRequired) { + if(isFirstPacket) { + isFirstPacket = FALSE; + /* Windows server at first sends the "cls" after the connection is established. + * There is a bug in the conhost which causes the visible window data to loose so to + * mitigate that issue we need to first move the visible window so that the cursor is at the top of the visible window. + */ + if (strstr(buf, clsSeq)) + ConMoveCursorTopOfVisibleWindow(); + } + + if(len >= applicationModeSeqLen && strstr(buf, applicationModeSeq)) + gbVTAppMode = true; + else if(len >= normalModeSeqLen && strstr(buf, normalModeSeq)) + gbVTAppMode = false; + /* Console has the capability to parse so pass the raw buffer to console directly */ ConRestoreViewRect(); /* Restore the visible window, otherwise WriteConsoleW() gets messy */ wchar_t* t = utf8_to_utf16(buf); diff --git a/contrib/win32/win32compat/win32_sshtty.c b/contrib/win32/win32compat/win32_sshtty.c index 84e80ca50..ab5f2f8b8 100644 --- a/contrib/win32/win32compat/win32_sshtty.c +++ b/contrib/win32/win32compat/win32_sshtty.c @@ -4,27 +4,17 @@ #include #include "..\..\..\sshpty.h" - static struct termios _saved_tio; static int _in_raw_mode = 0; - /* * TTY raw mode routines for Windows */ -int ConEnterRawMode(DWORD OutputHandle, BOOL fSmartInit); -int ConExitRawMode(void); - struct termios term_settings; -/* - * TODO - clean this up for Windows, ConInit should return previous terminal - * settings that need to be stored in term_settings - */ - struct termios * - get_saved_tio(void) { +get_saved_tio(void) { memset(&term_settings, 0, sizeof(term_settings)); return &term_settings; } @@ -36,5 +26,5 @@ leave_raw_mode(int quiet) { void enter_raw_mode(int quiet) { - ConEnterRawMode(STD_OUTPUT_HANDLE, TRUE); + ConEnterRawMode(); }