public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
* [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support
@ 2010-06-24 11:03 Michael Goldish
  2010-06-24 11:03 ` [KVM-AUTOTEST PATCH] [RFC] KVM test: add python client for rss file transfer services Michael Goldish
  2010-06-24 11:25 ` [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support Yaniv Kaul
  0 siblings, 2 replies; 5+ messages in thread
From: Michael Goldish @ 2010-06-24 11:03 UTC (permalink / raw)
  To: autotest, kvm; +Cc: Michael Goldish

Enable RSS to send/receive files and directory trees (recursively).

See protocol details in rss.cpp.

Signed-off-by: Michael Goldish <mgoldish@redhat.com>
---
 client/tests/kvm/deps/rss.cpp | 1429 ++++++++++++++++++++++++++++-------------
 1 files changed, 970 insertions(+), 459 deletions(-)

diff --git a/client/tests/kvm/deps/rss.cpp b/client/tests/kvm/deps/rss.cpp
index 66d9a5b..b2f6049 100644
--- a/client/tests/kvm/deps/rss.cpp
+++ b/client/tests/kvm/deps/rss.cpp
@@ -1,459 +1,970 @@
-// Simple remote shell server
-// Author: Michael Goldish <mgoldish@redhat.com>
-// Much of the code here was adapted from Microsoft code samples.
-
-// Usage: rss.exe [port]
-// If no port is specified the default is 22.
-
-#define _WIN32_WINNT 0x0500
-
-#include <windows.h>
-#include <winsock2.h>
-#include <stdio.h>
-
-#pragma comment(lib, "ws2_32.lib")
-
-int port = 22;
-
-HWND hMainWindow = NULL;
-HWND hTextBox = NULL;
-
-struct client_info {
-    SOCKET socket;
-    sockaddr_in addr;
-    int pid;
-    HWND hwnd;
-    HANDLE hJob;
-    HANDLE hChildOutputRead;
-    HANDLE hThreadChildToSocket;
-};
-
-void ExitOnError(char *message, BOOL winsock = 0)
-{
-    LPVOID system_message;
-    char buffer[512];
-
-    int error_code;
-    if (winsock)
-        error_code = WSAGetLastError();
-    else
-        error_code = GetLastError();
-
-    WSACleanup();
-
-    FormatMessage(
-        FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
-        NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-        (LPTSTR)&system_message, 0, NULL);
-
-    sprintf(buffer,
-            "%s!\n"
-            "Error code = %d\n"
-            "Error message = %s",
-            message, error_code, (char *)system_message);
-
-    MessageBox(hMainWindow, buffer, "Error", MB_OK | MB_ICONERROR);
-
-    LocalFree(system_message);
-    ExitProcess(1);
-}
-
-void AppendMessage(char *message)
-{
-    int length = GetWindowTextLength(hTextBox);
-    SendMessage(hTextBox, EM_SETSEL, (WPARAM)length, (LPARAM)length);
-    SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)message);
-}
-
-void FormatStringForPrinting(char *dst, char *src, int size)
-{
-    int j = 0;
-
-    for (int i = 0; i < size && src[i]; i++) {
-        if (src[i] == '\n') {
-            dst[j++] = '\\';
-            dst[j++] = 'n';
-        } else if (src[i] == '\r') {
-            dst[j++] = '\\';
-            dst[j++] = 'r';
-        } else if (src[i] == '\t') {
-            dst[j++] = '\\';
-            dst[j++] = 't';
-        } else if (src[i] == '\\') {
-            dst[j++] = '\\';
-            dst[j++] = '\\';
-        } else dst[j++] = src[i];
-    }
-    dst[j] = 0;
-}
-
-char* GetClientIPAddress(client_info *ci)
-{
-    char *address = inet_ntoa(ci->addr.sin_addr);
-    if (address)
-        return address;
-    else
-        return "unknown";
-}
-
-DWORD WINAPI ChildToSocket(LPVOID client_info_ptr)
-{
-    char buffer[1024], message[1024];
-    client_info ci;
-    DWORD bytes_read;
-    int bytes_sent;
-
-    memcpy(&ci, client_info_ptr, sizeof(ci));
-
-    while (1) {
-        // Read data from the child's STDOUT/STDERR pipes
-        if (!ReadFile(ci.hChildOutputRead,
-                      buffer, sizeof(buffer),
-                      &bytes_read, NULL) || !bytes_read) {
-            if (GetLastError() == ERROR_BROKEN_PIPE)
-                break; // Pipe done -- normal exit path
-            else
-                ExitOnError("ReadFile failed"); // Something bad happened
-        }
-        // Send data to the client
-        bytes_sent = send(ci.socket, buffer, bytes_read, 0);
-        /*
-        // Make sure all the data was sent
-        if (bytes_sent != bytes_read) {
-            sprintf(message,
-                    "ChildToSocket: bytes read (%d) != bytes sent (%d)",
-                    bytes_read, bytes_sent);
-            ExitOnError(message, 1);
-        }
-        */
-    }
-
-    AppendMessage("Child exited\r\n");
-    shutdown(ci.socket, SD_BOTH);
-
-    return 0;
-}
-
-DWORD WINAPI SocketToChild(LPVOID client_info_ptr)
-{
-    char buffer[256], formatted_buffer[768];
-    char message[1024], client_info_str[256];
-    client_info ci;
-    DWORD bytes_written;
-    int bytes_received;
-
-    memcpy(&ci, client_info_ptr, sizeof(ci));
-
-    sprintf(client_info_str, "address %s, port %d",
-            GetClientIPAddress(&ci), ci.addr.sin_port);
-
-    sprintf(message, "New client connected (%s)\r\n", client_info_str);
-    AppendMessage(message);
-
-    while (1) {
-        // Receive data from the socket
-        ZeroMemory(buffer, sizeof(buffer));
-        bytes_received = recv(ci.socket, buffer, sizeof(buffer), 0);
-        if (bytes_received <= 0)
-            break;
-        // Report the data received
-        FormatStringForPrinting(formatted_buffer, buffer, sizeof(buffer));
-        sprintf(message, "Client (%s) entered text: \"%s\"\r\n",
-                client_info_str, formatted_buffer);
-        AppendMessage(message);
-        // Send the data as a series of WM_CHAR messages to the console window
-        for (int i=0; i<bytes_received; i++) {
-            SendMessage(ci.hwnd, WM_CHAR, (WPARAM)buffer[i], 0);
-            SendMessage(ci.hwnd, WM_SETFOCUS, 0, 0);
-        }
-    }
-
-    sprintf(message, "Client disconnected (%s)\r\n", client_info_str);
-    AppendMessage(message);
-
-    // Attempt to terminate the child's process tree:
-    // Using taskkill (where available)
-    sprintf(buffer, "taskkill /PID %d /T /F", ci.pid);
-    system(buffer);
-    // .. and using TerminateJobObject()
-    TerminateJobObject(ci.hJob, 0);
-    // Wait for the ChildToSocket thread to terminate
-    WaitForSingleObject(ci.hThreadChildToSocket, 10000);
-    // In case the thread refuses to exit -- terminate it
-    TerminateThread(ci.hThreadChildToSocket, 0);
-    // Close the socket
-    shutdown(ci.socket, SD_BOTH);
-    closesocket(ci.socket);
-
-    // Close unnecessary handles
-    CloseHandle(ci.hJob);
-    CloseHandle(ci.hThreadChildToSocket);
-    CloseHandle(ci.hChildOutputRead);
-
-    AppendMessage("SocketToChild thread exited\r\n");
-
-    return 0;
-}
-
-void PrepAndLaunchRedirectedChild(client_info *ci,
-                                  HANDLE hChildStdOut,
-                                  HANDLE hChildStdErr)
-{
-    PROCESS_INFORMATION pi;
-    STARTUPINFO si;
-
-    // Allocate a new console for the child
-    HWND hwnd = GetForegroundWindow();
-    FreeConsole();
-    AllocConsole();
-    ShowWindow(GetConsoleWindow(), SW_HIDE);
-    if (hwnd)
-        SetForegroundWindow(hwnd);
-
-    // Set up the start up info struct.
-    ZeroMemory(&si, sizeof(STARTUPINFO));
-    si.cb = sizeof(STARTUPINFO);
-    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
-    si.hStdOutput = hChildStdOut;
-    si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
-    si.hStdError  = hChildStdErr;
-    // Use this if you want to hide the child:
-    si.wShowWindow = SW_HIDE;
-    // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
-    // use the wShowWindow flags.
-
-    // Launch the process that you want to redirect.
-    if (!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE,
-                       0, NULL, "C:\\", &si, &pi))
-        ExitOnError("CreateProcess failed");
-
-    // Close any unnecessary handles.
-    if (!CloseHandle(pi.hThread))
-        ExitOnError("CloseHandle failed");
-
-    // Keep the process ID
-    ci->pid = pi.dwProcessId;
-    // Assign the process to a newly created JobObject
-    ci->hJob = CreateJobObject(NULL, NULL);
-    AssignProcessToJobObject(ci->hJob, pi.hProcess);
-    // Keep the console window's handle
-    ci->hwnd = GetConsoleWindow();
-
-    // Detach from the child's console
-    FreeConsole();
-}
-
-void SpawnSession(client_info *ci)
-{
-    HANDLE hOutputReadTmp, hOutputRead, hOutputWrite;
-    HANDLE hErrorWrite;
-    SECURITY_ATTRIBUTES sa;
-
-    // Set up the security attributes struct.
-    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
-    sa.lpSecurityDescriptor = NULL;
-    sa.bInheritHandle = TRUE;
-
-    // Create the child output pipe.
-    if (!CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0))
-        ExitOnError("CreatePipe failed");
-
-    // Create a duplicate of the output write handle for the std error
-    // write handle. This is necessary in case the child application
-    // closes one of its std output handles.
-    if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
-                         GetCurrentProcess(), &hErrorWrite, 0,
-                         TRUE, DUPLICATE_SAME_ACCESS))
-        ExitOnError("DuplicateHandle failed");
-
-    // Create new output read handle and the input write handles. Set
-    // the Properties to FALSE. Otherwise, the child inherits the
-    // properties and, as a result, non-closeable handles to the pipes
-    // are created.
-    if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
-                         GetCurrentProcess(),
-                         &hOutputRead, // Address of new handle.
-                         0, FALSE, // Make it uninheritable.
-                         DUPLICATE_SAME_ACCESS))
-        ExitOnError("DuplicateHandle failed");
-
-    // Close inheritable copies of the handles you do not want to be
-    // inherited.
-    if (!CloseHandle(hOutputReadTmp))
-        ExitOnError("CloseHandle failed");
-
-    PrepAndLaunchRedirectedChild(ci, hOutputWrite, hErrorWrite);
-
-    ci->hChildOutputRead = hOutputRead;
-
-    // Close pipe handles (do not continue to modify the parent).
-    // You need to make sure that no handles to the write end of the
-    // output pipe are maintained in this process or else the pipe will
-    // not close when the child process exits and the ReadFile will hang.
-    if (!CloseHandle(hOutputWrite)) ExitOnError("CloseHandle failed");
-    if (!CloseHandle(hErrorWrite)) ExitOnError("CloseHandle failed");
-}
-
-DWORD WINAPI ListenThread(LPVOID param)
-{
-    WSADATA wsaData;
-    SOCKET ListenSocket = INVALID_SOCKET;
-    sockaddr_in addr;
-    int result, addrlen;
-    client_info ci;
-    HANDLE hThread;
-
-    // Initialize Winsock
-    result = WSAStartup(MAKEWORD(2,2), &wsaData);
-    if (result)
-        ExitOnError("Winsock initialization failed");
-
-    // Create socket
-    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (ListenSocket == INVALID_SOCKET)
-        ExitOnError("Socket creation failed", 1);
-
-    // Bind the socket
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = htonl(INADDR_ANY);
-    addr.sin_port = htons(port);
-
-    result = bind(ListenSocket, (sockaddr *)&addr, sizeof(addr));
-    if (result == SOCKET_ERROR)
-        ExitOnError("bind failed", 1);
-
-    // Start listening for incoming connections
-    result = listen(ListenSocket, SOMAXCONN);
-    if (result == SOCKET_ERROR)
-        ExitOnError("listen failed", 1);
-
-    // Inform the user
-    AppendMessage("Waiting for clients to connect...\r\n");
-
-    while (1) {
-        addrlen = sizeof(ci.addr);
-        ci.socket = accept(ListenSocket, (sockaddr *)&ci.addr, &addrlen);
-        if (ci.socket == INVALID_SOCKET) {
-            if (WSAGetLastError() == WSAEINTR)
-                break;
-            else
-                ExitOnError("accept failed", 1);
-        }
-
-        // Under heavy load, spawning cmd.exe might take a while, so tell the
-        // client to be patient
-        char *message = "Please wait...\r\n";
-        send(ci.socket, message, strlen(message), 0);
-        // Spawn a new redirected cmd.exe process
-        SpawnSession(&ci);
-        // Start transferring data from the child process to the client
-        hThread = CreateThread(NULL, 0, ChildToSocket, (LPVOID)&ci, 0, NULL);
-        if (!hThread)
-            ExitOnError("Could not create ChildToSocket thread");
-        ci.hThreadChildToSocket = hThread;
-        // ... and from the client to the child process
-        hThread = CreateThread(NULL, 0, SocketToChild, (LPVOID)&ci, 0, NULL);
-        if (!hThread)
-            ExitOnError("Could not create SocketToChild thread");
-    }
-
-    return 0;
-}
-
-LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
-    RECT rect;
-    HANDLE hListenThread;
-
-    switch (msg) {
-        case WM_CREATE:
-            // Create text box
-            GetClientRect(hwnd, &rect);
-            hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
-                                      "EDIT", "",
-                                      WS_CHILD|WS_VISIBLE|WS_VSCROLL|
-                                      ES_MULTILINE|ES_AUTOVSCROLL,
-                                      20, 20,
-                                      rect.right - 40,
-                                      rect.bottom - 40,
-                                      hwnd,
-                                      NULL,
-                                      GetModuleHandle(NULL),
-                                      NULL);
-            if (!hTextBox)
-                ExitOnError("Could not create text box");
-
-            // Set the font
-            SendMessage(hTextBox, WM_SETFONT,
-                        (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
-                        MAKELPARAM(FALSE, 0));
-
-            // Start the listening thread
-            hListenThread =
-                CreateThread(NULL, 0, ListenThread, NULL, 0, NULL);
-            if (!hListenThread)
-                ExitOnError("Could not create server thread");
-            break;
-
-        case WM_DESTROY:
-            WSACleanup();
-            PostQuitMessage(0);
-            break;
-
-        default:
-            return DefWindowProc(hwnd, msg, wParam, lParam);
-    }
-
-    return 0;
-}
-
-int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
-                   LPSTR lpCmdLine, int nShowCmd)
-{
-    WNDCLASSEX wc;
-    MSG msg;
-
-    if (strlen(lpCmdLine))
-        sscanf(lpCmdLine, "%d", &port);
-
-    // Make sure the firewall is disabled
-    system("netsh firewall set opmode disable");
-
-    // Create the window class
-    wc.cbSize        = sizeof(WNDCLASSEX);
-    wc.style         = CS_HREDRAW | CS_VREDRAW;
-    wc.lpfnWndProc   = WndProc;
-    wc.cbClsExtra    = 0;
-    wc.cbWndExtra    = 0;
-    wc.hInstance     = hInstance;
-    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
-    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
-    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
-    wc.lpszMenuName  = NULL;
-    wc.lpszClassName = "RemoteShellServerWindowClass";
-    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
-
-    if (!RegisterClassEx(&wc))
-        ExitOnError("Could not register window class");
-
-    // Create the main window
-    hMainWindow =
-        CreateWindow("RemoteShellServerWindowClass",
-                     "Remote Shell Server",
-                     WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
-                     20, 20, 500, 300,
-                     NULL, NULL, hInstance, NULL);
-    if (!hMainWindow)
-        ExitOnError("Could not create window");
-
-    ShowWindow(hMainWindow, SW_SHOWMINNOACTIVE);
-    UpdateWindow(hMainWindow);
-
-    // Main message loop
-    while (GetMessage(&msg, NULL, 0, 0)) {
-        TranslateMessage(&msg);
-        DispatchMessage(&msg);
-    }
-
-    ExitProcess(0);
-}
+// Simple remote shell server (and file transfer server)
+// Author: Michael Goldish <mgoldish@redhat.com>
+// Much of the code here was adapted from Microsoft code samples.
+
+// Usage: rss.exe [shell port] [file transfer port]
+// If no shell port is specified the default is 22.
+// If no file transfer port is specified the default is 23.
+
+// Definitions:
+// A 'msg' is a 32 bit integer.
+// A 'packet' is a 64 bit unsigned integer followed by a string of bytes.
+// The 64 bit integer indicates the length of the string.
+
+// Protocol for file transfers:
+//
+// When uploading files to the server:
+// 1. The client connects.
+// 2. The server sends RSS_MAGIC.
+// 3. The client sends RSS_SET_PATH, followed by a packet (as defined above)
+//    containing the path (in the server's filesystem) where files are to be
+//    stored.  The 'current path' is set to the path given.
+// Uploading a file (optional, can be repeated many times):
+//   4. The client sends RSS_CREATE_FILE, followed by a packet containing the
+//      filename (filename only, without a path), followed by a packet
+//      containing the file's contents.  The file is stored in the current
+//      path.
+// Uploading a directory (optional, can be repeated many times):
+//   5. The client sends RSS_CREATE_DIR, followed by a packet containing the
+//      name of the directory to be created (directory name only, without a
+//      path).  The directory is created in the current path.  Then, the
+//      'current path' becomes the path of that new directory.
+//   6. Steps 4-5 may be repeated to upload files and directories to the new
+//      directory.
+//   7. The client sends RSS_LEAVE_DIR, and the current path is set to the path
+//      of its parent directory (e.g. C:\Foobar -> C:\).
+// 8. The client sends RSS_DONE and waits for a response.
+// 9. The server sends RSS_OK to indicate that it's still listening.
+// 10. Steps 3-9 are repeated as many times as necessary.
+// If a critical error occurs at any time, the server may send RSS_ERROR
+// followed by a packet containing an error message, and the connection is
+// closed.
+//
+// When downloading files from the server:
+// 1. The client connects.
+// 2. The server sends RSS_MAGIC.
+// 3. The client sends RSS_SET_PATH, followed by a packet (as defined above)
+//    containing a path (in the server's filesystem) or a wildcard pattern
+//    indicating the files/directories the client wants to download.
+// The server then searches the given path.  For every file found:
+//   4. The server sends RSS_CREATE_FILE, followed by a packet containing the
+//      filename (filename only, without a path), followed by a packet
+//      containing the file's contents.
+// For every directory found:
+//   5. The server sends RSS_CREATE_DIR, followed by a packet containing the
+//      name of the directory to be created (directory name only, without a
+//      path).
+//   6. Steps 4-5 are repeated as many times as necessary.
+//   7. The server sends RSS_LEAVE_DIR.
+// 8. The server sends RSS_DONE.
+// 9. Steps 3-8 are repeated as many times as necessary.
+// If a critical error occurs, the server may send RSS_ERROR followed by a
+// packet containing an error message, and the connection is closed.
+// RSS_ERROR may only be sent when the client expects a msg.
+
+#define _WIN32_WINNT 0x0500
+
+#include <winsock2.h>
+#include <windows.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <shlwapi.h>
+
+#pragma comment(lib, "ws2_32.lib")
+#pragma comment(lib, "shlwapi.lib")
+
+#define TEXTBOX_LIMIT 32767
+
+// Constants for file transfer server
+#define RSS_MAGIC           0x525353
+#define RSS_OK              1
+#define RSS_ERROR           2
+#define RSS_UPLOAD          3
+#define RSS_DOWNLOAD        4
+#define RSS_SET_PATH        5
+#define RSS_CREATE_FILE     6
+#define RSS_CREATE_DIR      7
+#define RSS_LEAVE_DIR       8
+#define RSS_DONE            9
+
+// Globals
+int shell_port = 22;
+int file_transfer_port = 23;
+
+HWND hMainWindow = NULL;
+HWND hTextBox = NULL;
+
+struct client_info {
+    SOCKET socket;
+    sockaddr_in addr;
+    char addr_str[256];
+    int pid;
+    HWND hwnd;
+    HANDLE hJob;
+    HANDLE hChildOutputRead;
+    HANDLE hThreadChildToSocket;
+};
+
+void ExitOnError(char *message, BOOL winsock = FALSE)
+{
+    LPVOID system_message;
+    char buffer[512];
+    int error_code;
+
+    if (winsock)
+        error_code = WSAGetLastError();
+    else
+        error_code = GetLastError();
+    WSACleanup();
+
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                  FORMAT_MESSAGE_FROM_SYSTEM,
+                  NULL,
+                  error_code,
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                  (LPTSTR)&system_message,
+                  0,
+                  NULL);
+    sprintf(buffer,
+            "%s!\n"
+            "Error code = %d\n"
+            "Error message = %s",
+            message, error_code, (char *)system_message);
+    MessageBox(hMainWindow, buffer, "Error", MB_OK | MB_ICONERROR);
+
+    LocalFree(system_message);
+    ExitProcess(1);
+}
+
+void _vAppendMessage(char *message, va_list args)
+{
+    char str[512] = {0};
+
+    vsnprintf(str, sizeof(str) - 1, message, args);
+    strncat(str, "\r\n", sizeof(str) - 1 - strlen(str));
+
+    int length = GetWindowTextLength(hTextBox);
+    SendMessage(hTextBox, EM_SETSEL, (WPARAM)length, (LPARAM)length);
+    SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)str);
+}
+
+// Append text to the textbox
+// (if the textbox is full, do nothing)
+void _AppendMessage(char *message, ...)
+{
+    va_list args;
+
+    va_start(args, message);
+    _vAppendMessage(message, args);
+    va_end(args);
+}
+
+// Append text to the textbox
+// (if the textbox is full or nearly full, remove old text first)
+void AppendMessage(char *message, ...)
+{
+    va_list args;
+
+    int length = GetWindowTextLength(hTextBox);
+    if (length > TEXTBOX_LIMIT - 512) {
+        SendMessage(hTextBox, EM_SETSEL, (WPARAM)0,
+                    (LPARAM)(TEXTBOX_LIMIT / 2));
+        SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)"...");
+        if (length == TEXTBOX_LIMIT) _AppendMessage("...");
+    }
+    va_start(args, message);
+    _vAppendMessage(message, args);
+    va_end(args);
+}
+
+void FormatStringForPrinting(char *dst, char *src, int size)
+{
+    int j = 0;
+
+    for (int i = 0; i < size && src[i]; i++) {
+        if (src[i] == '\n') {
+            dst[j++] = '\\';
+            dst[j++] = 'n';
+        } else if (src[i] == '\r') {
+            dst[j++] = '\\';
+            dst[j++] = 'r';
+        } else if (src[i] == '\t') {
+            dst[j++] = '\\';
+            dst[j++] = 't';
+        } else if (src[i] == '\\') {
+            dst[j++] = '\\';
+            dst[j++] = '\\';
+        } else dst[j++] = src[i];
+    }
+    dst[j] = 0;
+}
+
+char* GetClientIPAddress(client_info *ci)
+{
+    char *address = inet_ntoa(ci->addr.sin_addr);
+    if (address)
+        return address;
+    else
+        return "unknown";
+}
+
+// Read a small chunk of data into a buffer
+BOOL Receive(SOCKET socket, char *buffer, int len)
+{
+    while (len > 0) {
+        int bytes_received = recv(socket, buffer, len, 0);
+        if (bytes_received <= 0)
+            return FALSE;
+        buffer += bytes_received;
+        len -= bytes_received;
+    }
+    return TRUE;
+}
+
+// Send data from a buffer
+BOOL Send(SOCKET socket, char *buffer, int len)
+{
+    while (len > 0) {
+        int bytes_sent = send(socket, buffer, len, 0);
+        if (bytes_sent <= 0)
+            return FALSE;
+        buffer += bytes_sent;
+        len -= bytes_sent;
+    }
+    return TRUE;
+}
+
+/*-------------
+ * Shell server
+ *-------------*/
+
+DWORD WINAPI ChildToSocket(LPVOID client_info_ptr)
+{
+    client_info *ci = (client_info *)client_info_ptr;
+    char buffer[1024];
+    DWORD bytes_read;
+
+    while (1) {
+        // Read data from the child's STDOUT/STDERR pipes
+        if (!ReadFile(ci->hChildOutputRead,
+                      buffer, sizeof(buffer),
+                      &bytes_read, NULL) || !bytes_read) {
+            if (GetLastError() == ERROR_BROKEN_PIPE)
+                break; // Pipe done -- normal exit path
+            else
+                ExitOnError("ReadFile failed"); // Something bad happened
+        }
+        // Send data to the client
+        Send(ci->socket, buffer, bytes_read);
+    }
+
+    AppendMessage("Child exited");
+    closesocket(ci->socket);
+    return 0;
+}
+
+DWORD WINAPI SocketToChild(LPVOID client_info_ptr)
+{
+    client_info *ci = (client_info *)client_info_ptr;
+    char buffer[256], formatted_buffer[768];
+    int bytes_received;
+
+    AppendMessage("Shell server: new client connected (%s)", ci->addr_str);
+
+    while (1) {
+        // Receive data from the socket
+        ZeroMemory(buffer, sizeof(buffer));
+        bytes_received = recv(ci->socket, buffer, sizeof(buffer), 0);
+        if (bytes_received <= 0)
+            break;
+        // Report the data received
+        FormatStringForPrinting(formatted_buffer, buffer, sizeof(buffer));
+        _AppendMessage("Client (%s) entered text: \"%s\"",
+                       ci->addr_str, formatted_buffer);
+        // Send the data as a series of WM_CHAR messages to the console window
+        for (int i = 0; i < bytes_received; i++) {
+            SendMessage(ci->hwnd, WM_CHAR, (WPARAM)buffer[i], 0);
+            SendMessage(ci->hwnd, WM_SETFOCUS, 0, 0);
+        }
+    }
+
+    AppendMessage("Shell server: client disconnected (%s)", ci->addr_str);
+
+    // Attempt to terminate the child's process tree:
+    // Using taskkill (where available)
+    sprintf(buffer, "taskkill /PID %d /T /F", ci->pid);
+    system(buffer);
+    // .. and using TerminateJobObject()
+    TerminateJobObject(ci->hJob, 0);
+    // Wait for the ChildToSocket thread to terminate
+    WaitForSingleObject(ci->hThreadChildToSocket, 10000);
+    // In case the thread refuses to exit, terminate it
+    TerminateThread(ci->hThreadChildToSocket, 0);
+    // Close the socket
+    closesocket(ci->socket);
+
+    // Free resources
+    CloseHandle(ci->hJob);
+    CloseHandle(ci->hThreadChildToSocket);
+    CloseHandle(ci->hChildOutputRead);
+    free(ci);
+
+    AppendMessage("SocketToChild thread exited");
+    return 0;
+}
+
+void PrepAndLaunchRedirectedChild(client_info *ci,
+                                  HANDLE hChildStdOut,
+                                  HANDLE hChildStdErr)
+{
+    PROCESS_INFORMATION pi;
+    STARTUPINFO si;
+
+    // Allocate a new console for the child
+    HWND hwnd = GetForegroundWindow();
+    FreeConsole();
+    AllocConsole();
+    ShowWindow(GetConsoleWindow(), SW_HIDE);
+    if (hwnd)
+        SetForegroundWindow(hwnd);
+
+    // Set up the start up info struct.
+    ZeroMemory(&si, sizeof(STARTUPINFO));
+    si.cb = sizeof(STARTUPINFO);
+    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+    si.hStdOutput = hChildStdOut;
+    si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
+    si.hStdError  = hChildStdErr;
+    // Use this if you want to hide the child:
+    si.wShowWindow = SW_HIDE;
+    // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
+    // use the wShowWindow flags.
+
+    // Launch the process that you want to redirect.
+    if (!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE,
+                       0, NULL, "C:\\", &si, &pi))
+        ExitOnError("CreateProcess failed");
+
+    // Close any unnecessary handles.
+    if (!CloseHandle(pi.hThread))
+        ExitOnError("CloseHandle failed");
+
+    // Keep the process ID
+    ci->pid = pi.dwProcessId;
+    // Assign the process to a newly created JobObject
+    ci->hJob = CreateJobObject(NULL, NULL);
+    AssignProcessToJobObject(ci->hJob, pi.hProcess);
+    // Keep the console window's handle
+    ci->hwnd = GetConsoleWindow();
+
+    // Detach from the child's console
+    FreeConsole();
+}
+
+void SpawnSession(client_info *ci)
+{
+    HANDLE hOutputReadTmp, hOutputRead, hOutputWrite;
+    HANDLE hErrorWrite;
+    SECURITY_ATTRIBUTES sa;
+
+    // Set up the security attributes struct.
+    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = TRUE;
+
+    // Create the child output pipe.
+    if (!CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0))
+        ExitOnError("CreatePipe failed");
+
+    // Create a duplicate of the output write handle for the std error
+    // write handle. This is necessary in case the child application
+    // closes one of its std output handles.
+    if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
+                         GetCurrentProcess(), &hErrorWrite, 0,
+                         TRUE, DUPLICATE_SAME_ACCESS))
+        ExitOnError("DuplicateHandle failed");
+
+    // Create new output read handle and the input write handles. Set
+    // the Properties to FALSE. Otherwise, the child inherits the
+    // properties and, as a result, non-closeable handles to the pipes
+    // are created.
+    if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
+                         GetCurrentProcess(),
+                         &hOutputRead, // Address of new handle.
+                         0, FALSE, // Make it uninheritable.
+                         DUPLICATE_SAME_ACCESS))
+        ExitOnError("DuplicateHandle failed");
+
+    // Close inheritable copies of the handles you do not want to be
+    // inherited.
+    if (!CloseHandle(hOutputReadTmp))
+        ExitOnError("CloseHandle failed");
+
+    PrepAndLaunchRedirectedChild(ci, hOutputWrite, hErrorWrite);
+
+    ci->hChildOutputRead = hOutputRead;
+
+    // Close pipe handles (do not continue to modify the parent).
+    // You need to make sure that no handles to the write end of the
+    // output pipe are maintained in this process or else the pipe will
+    // not close when the child process exits and the ReadFile will hang.
+    if (!CloseHandle(hOutputWrite)) ExitOnError("CloseHandle failed");
+    if (!CloseHandle(hErrorWrite)) ExitOnError("CloseHandle failed");
+}
+
+SOCKET PrepListenSocket(int port)
+{
+    sockaddr_in addr;
+    linger l;
+    int result;
+
+    // Create socket
+    SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (ListenSocket == INVALID_SOCKET)
+        ExitOnError("Socket creation failed", TRUE);
+
+    // Enable lingering
+    l.l_linger = 10;
+    l.l_onoff = 1;
+    setsockopt(ListenSocket, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l));
+
+    // Bind the socket
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    addr.sin_port = htons(port);
+
+    result = bind(ListenSocket, (sockaddr *)&addr, sizeof(addr));
+    if (result == SOCKET_ERROR)
+        ExitOnError("bind failed", TRUE);
+
+    // Start listening for incoming connections
+    result = listen(ListenSocket, SOMAXCONN);
+    if (result == SOCKET_ERROR)
+        ExitOnError("listen failed", TRUE);
+
+    return ListenSocket;
+}
+
+DWORD WINAPI ShellListenThread(LPVOID param)
+{
+    client_info _ci, *ci;
+    HANDLE hThread;
+
+    SOCKET ListenSocket = PrepListenSocket(shell_port);
+
+    // Inform the user
+    AppendMessage("Shell server: waiting for clients to connect...");
+
+    while (1) {
+        int addrlen = sizeof(_ci.addr);
+        _ci.socket = accept(ListenSocket, (sockaddr *)&_ci.addr, &addrlen);
+        if (_ci.socket == INVALID_SOCKET) {
+            if (WSAGetLastError() == WSAEINTR)
+                break;
+            else
+                ExitOnError("accept failed", TRUE);
+        }
+
+        if (!(ci = (client_info *)malloc(sizeof(client_info))))
+            ExitOnError("Could not allocate client_info struct");
+        memcpy(ci, &_ci, sizeof(client_info));
+        sprintf(ci->addr_str, "%s:%d", GetClientIPAddress(ci),
+                ci->addr.sin_port);
+
+        // Under heavy load, spawning cmd.exe might take a while, so tell the
+        // client to be patient
+        char *message = "Please wait...\r\n";
+        Send(ci->socket, message, strlen(message));
+        // Spawn a new redirected cmd.exe process
+        SpawnSession(ci);
+        // Start transferring data from the child process to the client
+        hThread = CreateThread(NULL, 0, ChildToSocket, (LPVOID)ci, 0, NULL);
+        if (!hThread)
+            ExitOnError("Could not create ChildToSocket thread");
+        ci->hThreadChildToSocket = hThread;
+        // ... and from the client to the child process
+        hThread = CreateThread(NULL, 0, SocketToChild, (LPVOID)ci, 0, NULL);
+        if (!hThread)
+            ExitOnError("Could not create SocketToChild thread");
+    }
+
+    return 0;
+}
+
+/*---------------------
+ * File transfer server
+ *---------------------*/
+
+#define BUFSIZE 65536
+
+// Receive up to 4GB into a file
+BOOL ReceiveIntoFile(SOCKET socket, char *filename, DWORD len)
+{
+    char *buffer = (char *)malloc(BUFSIZE);
+    if (!buffer) return FALSE;
+
+    FILE *fp = fopen(filename, "wb");
+    if (!fp) {
+        free(buffer);
+        return FALSE;
+    }
+
+    while (len > 0) {
+        int bytes_received = recv(socket, buffer, min(BUFSIZE, len), 0);
+        if (bytes_received <= 0)
+            break;
+        if (fwrite(buffer, bytes_received, 1, fp) < 1)
+            break;
+        len -= bytes_received;
+    }
+
+    fclose(fp);
+    free(buffer);
+    return len == 0;
+}
+
+// Send data from a file (unlimited size)
+BOOL SendFromFile(SOCKET socket, char *filename)
+{
+    char *buffer = (char *)malloc(BUFSIZE);
+    if (!buffer) return FALSE;
+
+    FILE *fp = fopen(filename, "rb");
+    if (!fp) {
+        free(buffer);
+        return FALSE;
+    }
+
+    while (!feof(fp)) {
+        int bytes_read = fread(buffer, 1, BUFSIZE, fp);
+        if (bytes_read <= 0)
+            break;
+        if (!Send(socket, buffer, bytes_read)) {
+            fclose(fp);
+            free(buffer);
+            return FALSE;
+        }
+    }
+
+    BOOL success = feof(fp);
+    fclose(fp);
+    free(buffer);
+    return success;
+}
+
+BOOL ReceivePacket(SOCKET socket, char *buffer, DWORD max_size)
+{
+    DWORD packet_size = 0, padding;
+
+    if (!Receive(socket, (char *)&packet_size, 4)) return FALSE;
+    if (!Receive(socket, (char *)&padding, 4)) return FALSE;
+    if (packet_size > max_size) return FALSE;
+    return Receive(socket, buffer, packet_size);
+}
+
+BOOL ReceiveStrPacket(SOCKET socket, char *buffer, DWORD max_size)
+{
+    memset(buffer, 0, max_size);
+    return ReceivePacket(socket, buffer, max_size - 1);
+}
+
+BOOL SendPacket(SOCKET socket, char *buffer, DWORD len)
+{
+    DWORD padding = 0;
+
+    if (!Send(socket, (char *)&len, 4)) return FALSE;
+    if (!Send(socket, (char *)&padding, 4)) return FALSE;
+    return Send(socket, buffer, len);
+}
+
+BOOL ReceivePacketIntoFile(SOCKET socket, char *filename)
+{
+    DWORD packet_size = 0, padding;
+
+    if (!Receive(socket, (char *)&packet_size, 4)) return FALSE;
+    if (!Receive(socket, (char *)&padding, 4)) return FALSE;
+    return ReceiveIntoFile(socket, filename, packet_size);
+}
+
+BOOL SendPacketFromFile(SOCKET socket, char *filename,
+                        DWORD size_low, DWORD size_high)
+{
+    if (!Send(socket, (char *)&size_low, 4)) return FALSE;
+    if (!Send(socket, (char *)&size_high, 4)) return FALSE;
+    return SendFromFile(socket, filename);
+}
+
+BOOL SendMsg(SOCKET socket, int msg)
+{
+    return Send(socket, (char *)&msg, 4);
+}
+
+int TerminateTransfer(client_info *ci, char *message)
+{
+    AppendMessage(message);
+    AppendMessage("File transfer server: client disconnected (%s)",
+                  ci->addr_str);
+    closesocket(ci->socket);
+    free(ci);
+    return 0;
+}
+
+int TerminateWithError(client_info *ci, char *message)
+{
+    SendMsg(ci->socket, RSS_ERROR);
+    SendPacket(ci->socket, message, strlen(message));
+    return TerminateTransfer(ci, message);
+}
+
+int ReceiveThread(client_info *ci)
+{
+    char path[512], filename[512];
+    int msg = 0;
+
+    AppendMessage("Client (%s) wants to upload files", ci->addr_str);
+
+    while (1) {
+        if (!Receive(ci->socket, (char *)&msg, 4))
+            return TerminateTransfer(ci, "Could not receive further msgs");
+
+        switch (msg) {
+        case RSS_SET_PATH:
+            if (!ReceiveStrPacket(ci->socket, path, sizeof(path)))
+                return TerminateWithError(ci,
+                    "RSS_SET_PATH: could not receive path");
+            _AppendMessage("Client (%s) set path to %s", ci->addr_str, path);
+            break;
+
+        case RSS_CREATE_FILE:
+            if (!ReceiveStrPacket(ci->socket, filename, sizeof(filename)))
+                return TerminateWithError(ci,
+                    "RSS_CREATE_FILE: could not receive filename");
+            if (PathIsDirectory(path))
+                PathAppend(path, filename);
+            _AppendMessage("Client (%s) is uploading %s", ci->addr_str, path);
+            if (!ReceivePacketIntoFile(ci->socket, path))
+                return TerminateWithError(ci,
+                    "RSS_CREATE_FILE: error receiving or writing file "
+                    "contents");
+            PathAppend(path, "..");
+            break;
+
+        case RSS_CREATE_DIR:
+            if (!ReceiveStrPacket(ci->socket, filename, sizeof(filename)))
+                return TerminateWithError(ci,
+                    "RSS_CREATE_DIR: could not receive dirname");
+            if (PathIsDirectory(path))
+                PathAppend(path, filename);
+            _AppendMessage("Entering dir %s", path);
+            if (PathFileExists(path)) {
+                if (!PathIsDirectory(path))
+                    return TerminateWithError(ci,
+                        "RSS_CREATE_DIR: path exists and is not a directory");
+            } else {
+                if (!CreateDirectory(path, NULL))
+                    return TerminateWithError(ci,
+                        "RSS_CREATE_DIR: could not create directory");
+            }
+            break;
+
+        case RSS_LEAVE_DIR:
+            PathAppend(path, "..");
+            _AppendMessage("Returning to dir %s", path);
+            break;
+
+        case RSS_DONE:
+            if (!SendMsg(ci->socket, RSS_OK))
+                return TerminateTransfer(ci,
+                    "RSS_DONE: could not send OK msg");
+            break;
+
+        default:
+            return TerminateWithError(ci, "Received unexpected msg");
+        }
+    }
+}
+
+// Given a path or a pattern with wildcards, send files or directory trees to
+// the client
+int SendFiles(client_info *ci, char *pattern)
+{
+    char path[MAX_PATH];
+    WIN32_FIND_DATA ffd;
+    FILE *fp;
+
+    HANDLE hFind = FindFirstFile(pattern, &ffd);
+    if (hFind == INVALID_HANDLE_VALUE) {
+        // If a weird error occurred (like failure to list directory contents
+        // due to insufficient permissions) print a warning and continue.
+        if (GetLastError() != ERROR_FILE_NOT_FOUND)
+            AppendMessage("WARNING: FindFirstFile failed on pattern %s",
+                          pattern);
+        return 1;
+    }
+
+    strncpy(path, pattern, sizeof(path) - 1);
+    PathAppend(path, "..");
+
+    do {
+        if (ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+            continue;
+        if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+            // Directory
+            if (!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, ".."))
+                continue;
+            PathAppend(path, ffd.cFileName);
+            _AppendMessage("Entering dir %s", path);
+            PathAppend(path, "*");
+            if (!SendMsg(ci->socket, RSS_CREATE_DIR)) {
+                FindClose(hFind);
+                return TerminateTransfer(ci,
+                    "Could not send RSS_CREATE_DIR msg");
+            }
+            if (!SendPacket(ci->socket, ffd.cFileName,
+                            strlen(ffd.cFileName))) {
+                FindClose(hFind);
+                return TerminateTransfer(ci, "Could not send dirname");
+            }
+            if (!SendFiles(ci, path)) {
+                FindClose(hFind);
+                return 0;
+            }
+            if (!SendMsg(ci->socket, RSS_LEAVE_DIR)) {
+                FindClose(hFind);
+                return TerminateTransfer(ci,
+                    "Could not send RSS_LEAVE_DIR msg");
+            }
+            PathAppend(path, "..");
+            PathAppend(path, "..");
+            _AppendMessage("Returning to dir %s", path);
+        } else {
+            // File
+            PathAppend(path, ffd.cFileName);
+            _AppendMessage("Client (%s) is downloading %s",
+                           ci->addr_str, path);
+            // Make sure the file is readable
+            fp = fopen(path, "rb");
+            if (fp) fclose(fp);
+            else {
+                AppendMessage("WARNING: could not read file %s", path);
+                PathAppend(path, "..");
+                continue;
+            }
+            if (!SendMsg(ci->socket, RSS_CREATE_FILE)) {
+                FindClose(hFind);
+                return TerminateTransfer(ci,
+                    "Could not send RSS_CREATE_FILE msg");
+            }
+            if (!SendPacket(ci->socket, ffd.cFileName,
+                            strlen(ffd.cFileName))) {
+                FindClose(hFind);
+                return TerminateTransfer(ci, "Could not send filename");
+            }
+            if (!SendPacketFromFile(ci->socket, path,
+                                    ffd.nFileSizeLow, ffd.nFileSizeHigh)) {
+                FindClose(hFind);
+                return TerminateTransfer(ci, "Could not send file contents");
+            }
+            PathAppend(path, "..");
+        }
+    } while (FindNextFile(hFind, &ffd));
+
+    if (GetLastError() == ERROR_NO_MORE_FILES) {
+        FindClose(hFind);
+        return 1;
+    } else {
+        FindClose(hFind);
+        return TerminateWithError(ci, "FindNextFile failed");
+    }
+}
+
+int SendThread(client_info *ci)
+{
+    char pattern[512];
+    int msg = 0;
+
+    AppendMessage("Client (%s) wants to download files", ci->addr_str);
+
+    while (1) {
+        if (!Receive(ci->socket, (char *)&msg, 4))
+            return TerminateTransfer(ci, "Could not receive further msgs");
+
+        switch (msg) {
+        case RSS_SET_PATH:
+            if (!ReceiveStrPacket(ci->socket, pattern, sizeof(pattern)))
+                return TerminateWithError(ci,
+                    "RSS_SET_PATH: could not receive path");
+            AppendMessage("Client (%s) asked for %s", ci->addr_str, pattern);
+            PathRemoveBackslash(pattern);
+            if (!SendFiles(ci, pattern))
+                return 0;
+            if (!SendMsg(ci->socket, RSS_DONE))
+                return TerminateTransfer(ci,
+                    "RSS_SET_PATH: could not send RSS_DONE msg");
+            break;
+
+        default:
+            return TerminateWithError(ci, "Received unexpected msg");
+        }
+    }
+}
+
+DWORD WINAPI TransferThreadEntry(LPVOID client_info_ptr)
+{
+    client_info *ci = (client_info *)client_info_ptr;
+    int msg = 0;
+
+    AppendMessage("File transfer server: new client connected (%s)",
+                  ci->addr_str);
+
+    if (!SendMsg(ci->socket, RSS_MAGIC))
+        return TerminateTransfer(ci, "Could not send greeting message");
+    if (!Receive(ci->socket, (char *)&msg, 4))
+        return TerminateTransfer(ci, "Error receiving msg");
+
+    if (msg == RSS_UPLOAD)
+        return ReceiveThread(ci);
+    else if (msg == RSS_DOWNLOAD)
+        return SendThread(ci);
+    return TerminateWithError(ci, "Received unexpected msg");
+}
+
+DWORD WINAPI FileTransferListenThread(LPVOID param)
+{
+    client_info _ci, *ci;
+    HANDLE hThread;
+
+    SOCKET ListenSocket = PrepListenSocket(file_transfer_port);
+
+    // Inform the user
+    AppendMessage("File transfer server: waiting for clients to connect...");
+
+    while (1) {
+        int addrlen = sizeof(_ci.addr);
+        _ci.socket = accept(ListenSocket, (sockaddr *)&_ci.addr, &addrlen);
+        if (_ci.socket == INVALID_SOCKET) {
+            if (WSAGetLastError() == WSAEINTR)
+                break;
+            else
+                ExitOnError("accept failed", TRUE);
+        }
+
+        if (!(ci = (client_info *)malloc(sizeof(client_info))))
+            ExitOnError("Could not allocate client_info struct");
+        memcpy(ci, &_ci, sizeof(client_info));
+        sprintf(ci->addr_str, "%s:%d", GetClientIPAddress(ci),
+                ci->addr.sin_port);
+        hThread = CreateThread(NULL, 0, TransferThreadEntry, (LPVOID)ci, 0,
+                               NULL);
+        if (!hThread)
+            ExitOnError("Could not create MainTransferThread");
+    }
+
+    return 0;
+}
+
+/*--------------------
+ * WndProc and WinMain
+ *--------------------*/
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    RECT rect;
+    WSADATA wsaData;
+
+    switch (msg) {
+    case WM_CREATE:
+        // Create text box
+        GetClientRect(hwnd, &rect);
+        hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
+                                  "EDIT", "",
+                                  WS_CHILD | WS_VISIBLE | WS_VSCROLL |
+                                  ES_MULTILINE | ES_AUTOVSCROLL,
+                                  20, 20,
+                                  rect.right - 40,
+                                  rect.bottom - 40,
+                                  hwnd,
+                                  NULL,
+                                  GetModuleHandle(NULL),
+                                  NULL);
+        if (!hTextBox)
+            ExitOnError("Could not create text box");
+        // Set font
+        SendMessage(hTextBox, WM_SETFONT,
+                    (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
+                    MAKELPARAM(FALSE, 0));
+        // Set size limit
+        SendMessage(hTextBox, EM_LIMITTEXT, (WPARAM)TEXTBOX_LIMIT, (LPARAM)0);
+        // Initialize Winsock
+        if (WSAStartup(MAKEWORD(2,2), &wsaData))
+            ExitOnError("Winsock initialization failed");
+        // Start the listening threads
+        if (!CreateThread(NULL, 0, ShellListenThread, NULL, 0, NULL))
+            ExitOnError("Could not create shell server thread");
+        if (!CreateThread(NULL, 0, FileTransferListenThread, NULL, 0, NULL))
+            ExitOnError("Could not create file transfer server thread");
+        break;
+
+    case WM_DESTROY:
+        if (WSACleanup())
+            ExitOnError("WSACleanup failed");
+        PostQuitMessage(0);
+        break;
+
+    default:
+        return DefWindowProc(hwnd, msg, wParam, lParam);
+    }
+
+    return 0;
+}
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+                   LPSTR lpCmdLine, int nShowCmd)
+{
+    WNDCLASSEX wc;
+    MSG msg;
+
+    if (strlen(lpCmdLine))
+        sscanf(lpCmdLine, "%d %d", &shell_port, &file_transfer_port);
+
+    // Make sure the firewall is disabled
+    system("netsh firewall set opmode disable");
+
+    // Create the window class
+    wc.cbSize        = sizeof(WNDCLASSEX);
+    wc.style         = CS_HREDRAW | CS_VREDRAW;
+    wc.lpfnWndProc   = WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
+    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
+    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
+    wc.lpszMenuName  = NULL;
+    wc.lpszClassName = "RemoteShellServerWindowClass";
+    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
+
+    if (!RegisterClassEx(&wc))
+        ExitOnError("Could not register window class");
+
+    // Create the main window
+    hMainWindow =
+        CreateWindow("RemoteShellServerWindowClass",
+                     "Remote Shell Server",
+                     WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
+                     20, 20, 600, 400,
+                     NULL, NULL, hInstance, NULL);
+    if (!hMainWindow)
+        ExitOnError("Could not create window");
+
+    ShowWindow(hMainWindow, SW_SHOWMINNOACTIVE);
+    UpdateWindow(hMainWindow);
+
+    // Main message loop
+    while (GetMessage(&msg, NULL, 0, 0)) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+
+    ExitProcess(0);
+}
-- 
1.5.4.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [KVM-AUTOTEST PATCH] [RFC] KVM test: add python client for rss file transfer services
  2010-06-24 11:03 [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support Michael Goldish
@ 2010-06-24 11:03 ` Michael Goldish
  2010-06-24 11:25 ` [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support Yaniv Kaul
  1 sibling, 0 replies; 5+ messages in thread
From: Michael Goldish @ 2010-06-24 11:03 UTC (permalink / raw)
  To: autotest, kvm; +Cc: Michael Goldish

For usage details see docstrings in rss_file_transfer.py.
For protocol details see deps/rss.cpp.

Signed-off-by: Michael Goldish <mgoldish@redhat.com>
---
 client/tests/kvm/rss_file_transfer.py |  412 +++++++++++++++++++++++++++++++++
 1 files changed, 412 insertions(+), 0 deletions(-)
 create mode 100644 client/tests/kvm/rss_file_transfer.py

diff --git a/client/tests/kvm/rss_file_transfer.py b/client/tests/kvm/rss_file_transfer.py
new file mode 100644
index 0000000..3d35df7
--- /dev/null
+++ b/client/tests/kvm/rss_file_transfer.py
@@ -0,0 +1,412 @@
+#!/usr/bin/python
+"""
+Client for file transfer services offered by RSS (Remote Shell Server).
+
+@author: Michael Goldish (mgoldish@redhat.com)
+@copyright: 2008-2010 Red Hat Inc.
+"""
+
+import socket, struct, time, sys, os, glob
+
+# Globals
+BUFSIZE = 65536
+
+# Protocol message constants
+RSS_MAGIC           = 0x525353
+RSS_OK              = 1
+RSS_ERROR           = 2
+RSS_UPLOAD          = 3
+RSS_DOWNLOAD        = 4
+RSS_SET_PATH        = 5
+RSS_CREATE_FILE     = 6
+RSS_CREATE_DIR      = 7
+RSS_LEAVE_DIR       = 8
+RSS_DONE            = 9
+
+# See rss.cpp for protocol details.
+
+
+class FileTransferError(Exception):
+    pass
+
+
+class FileTransferConnectError(FileTransferError):
+    pass
+
+
+class FileTransferTimeoutError(FileTransferError):
+    pass
+
+
+class FileTransferProtocolError(FileTransferError):
+    pass
+
+
+class FileTransferSendError(FileTransferError):
+    pass
+
+
+class FileTransferServerError(FileTransferError):
+    pass
+
+
+class FileTransferClient(object):
+    """
+    Connect to a RSS (remote shell server) and transfer files.
+    """
+
+    def __init__(self, address, port, timeout=10):
+        """
+        Connect to a server.
+
+        @param address: The server's address
+        @param port: The server's port
+        @param timeout: Time duration to wait for connection to succeed
+        @raise FileTransferConnectError: Raised if the connection fails
+        @raise FileTransferProtocolError: Raised if an incorrect magic number
+                is received
+        """
+        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self._socket.settimeout(timeout)
+        try:
+            self._socket.connect((address, port))
+        except socket.error:
+            raise FileTransferConnectError("Could not connect to server")
+        if self._receive_msg(timeout) != RSS_MAGIC:
+            raise FileTransferProtocolError("Received wrong magic number")
+
+
+    def __del__(self):
+        self.close()
+
+
+    def close(self):
+        """
+        Close the connection.
+        """
+        self._socket.close()
+
+
+    def _send(self, str):
+        try:
+            self._socket.sendall(str)
+        except socket.error:
+            raise FileTransferSendError("Could not send data to server")
+
+
+    def _receive(self, size, timeout=10):
+        s = ""
+        end_time = time.time() + timeout
+        while size:
+            try:
+                self._socket.settimeout(max(0, end_time - time.time()))
+                data = self._socket.recv(min(BUFSIZE, size))
+            except socket.timeout:
+                raise FileTransferTimeoutError("Timeout expired while "
+                                               "receiving data from server")
+            except socket.error:
+                raise FileTransferProtocolError("Error receiving data from "
+                                                "server")
+            if not data:
+                raise FileTransferProtocolError("Connection closed "
+                                                "unexpectedly")
+            s += data
+            size -= len(data)
+        return s
+
+
+    def _send_packet(self, str):
+        self._send(struct.pack("=Q", len(str)))
+        self._send(str)
+
+
+    def _receive_packet(self, timeout=10):
+        size = struct.unpack("=Q", self._receive(8))[0]
+        return self._receive(size, timeout)
+
+
+    def _send_packet_from_file(self, filename, timeout=30):
+        self._send(struct.pack("=Q", os.path.getsize(filename)))
+        f = open(filename, "rb")
+        try:
+            end_time = time.time() + timeout
+            while time.time() < end_time:
+                data = f.read(BUFSIZE)
+                if not data:
+                    break
+                self._send(data)
+            else:
+                raise FileTransferTimeoutError("Timeout expired while sending "
+                                               "file %s" % filename)
+        finally:
+            f.close()
+
+
+    def _receive_packet_into_file(self, filename, timeout=30):
+        size = struct.unpack("=Q", self._receive(8))[0]
+        f = open(filename, "wb")
+        try:
+            end_time = time.time() + timeout
+            while size:
+                try:
+                    self._socket.settimeout(max(0, end_time - time.time()))
+                    data = self._socket.recv(min(BUFSIZE, size))
+                except socket.timeout:
+                    raise FileTransferTimeoutError("Timeout expired while "
+                                                   "receiving file %s" %
+                                                   filename)
+                except socket.error:
+                    raise FileTransferProtocolError("Error receiving file %s" %
+                                                    filename)
+                if not data:
+                    raise FileTransferProtocolError("Connection closed "
+                                                    "unexpectedly while "
+                                                    "receiving file %s" %
+                                                    filename)
+                f.write(data)
+                size -= len(data)
+        finally:
+            f.close()
+
+
+    def _send_msg(self, msg, timeout=10):
+        self._send(struct.pack("=i", msg))
+
+
+    def _receive_msg(self, timeout=10):
+        s = self._receive(4, timeout)
+        return struct.unpack("=i", s)[0]
+
+
+    def _handle_transfer_error(self):
+        # Save original exception
+        e = sys.exc_info()
+        try:
+            # See if we can get an error message
+            msg = self._receive_msg()
+        except FileTransferError:
+            # No error message -- re-raise original exception
+            raise e[0], e[1], e[2]
+        if msg == RSS_ERROR:
+            errmsg = self._receive_packet()
+            raise FileTransferServerError("Server said: %s" % errmsg)
+        raise e[0], e[1], e[2]
+
+
+class FileUploadClient(FileTransferClient):
+    """
+    Connect to a RSS (remote shell server) and upload files or directory trees.
+    """
+
+    def __init__(self, address, port, timeout=10):
+        """
+        Connect to a server.
+
+        @param address: The server's address
+        @param port: The server's port
+        @param timeout: Time duration to wait for connection to succeed
+        @raise FileTransferConnectError: Raised if the connection fails
+        @raise FileTransferProtocolError: Raised if an incorrect magic number
+                is received
+        @raise FileTransferSendError: Raised if the RSS_UPLOAD message cannot
+                be sent to the server
+        """
+        super(FileUploadClient, self).__init__(address, port, timeout)
+        self._send_msg(RSS_UPLOAD)
+
+
+    def _upload_file(self, path, end_time):
+        if os.path.isfile(path):
+            self._send_msg(RSS_CREATE_FILE)
+            self._send_packet(os.path.basename(path))
+            self._send_packet_from_file(path, max(0, end_time - time.time()))
+        elif os.path.isdir(path):
+            self._send_msg(RSS_CREATE_DIR)
+            self._send_packet(os.path.basename(path))
+            for filename in os.listdir(path):
+                self._upload_file(os.path.join(path, filename), end_time)
+            self._send_msg(RSS_LEAVE_DIR)
+
+
+    def upload(self, src_pattern, dst_path, timeout=600):
+        """
+        Send files or directory trees to the server.
+        The semantics of src_pattern and dst_path are similar to those of scp.
+        For example, the following are OK:
+            src_pattern='/tmp/foo.txt', dst_path='C:\\'
+                (copies a single file)
+            src_pattern='/usr/', dst_path='C:\\Windows\\'
+                (copies a directory tree recursively)
+            src_pattern='/usr/*', dst_path='C:\\Windows\\'
+                (copies all files and directory trees under /usr/)
+        The following is not OK:
+            src_pattern='/tmp/foo.txt', dst_path='C:\\Windows\\*'
+                (wildcards are only allowed in src_pattern)
+
+        @param src_pattern: A path or wildcard pattern specifying the files or
+                directories to send to the server
+        @param dst_path: A path in the server's filesystem where the files will
+                be saved
+        @param timeout: Time duration in seconds to wait for the transfer to
+                complete
+        @raise FileTransferTimeoutError: Raised if timeout expires
+        @raise FileTransferServerError: Raised if something goes wrong and the
+                server sends an informative error message to the client
+        @note: Other exceptions can be raised.
+        """
+        end_time = time.time() + timeout
+        try:
+            try:
+                self._send_msg(RSS_SET_PATH)
+                self._send_packet(dst_path)
+                for filename in glob.iglob(src_pattern):
+                    self._upload_file(os.path.abspath(filename), end_time)
+                self._send_msg(RSS_DONE)
+            except FileTransferTimeoutError:
+                raise
+            except FileTransferError:
+                self._handle_transfer_error()
+            else:
+                # Look for RSS_OK or RSS_ERROR
+                msg = self._receive_msg(max(0, end_time - time.time()))
+                if msg == RSS_OK:
+                    return
+                elif msg == RSS_ERROR:
+                    errmsg = self._receive_packet()
+                    raise FileTransferServerError("Server said: %s" % errmsg)
+                else:
+                    # Neither RSS_OK nor RSS_ERROR found
+                    raise FileTransferProtocolError
+        except:
+            # In any case, if the transfer failed, close the connection
+            self.close()
+            raise
+
+
+class FileDownloadClient(FileTransferClient):
+    """
+    Connect to a RSS (remote shell server) and download files or directory trees.
+    """
+
+    def __init__(self, address, port, timeout=10):
+        """
+        Connect to a server.
+
+        @param address: The server's address
+        @param port: The server's port
+        @param timeout: Time duration to wait for connection to succeed
+        @raise FileTransferConnectError: Raised if the connection fails
+        @raise FileTransferProtocolError: Raised if an incorrect magic number
+                is received
+        @raise FileTransferSendError: Raised if the RSS_UPLOAD message cannot
+                be sent to the server
+        """
+        super(FileDownloadClient, self).__init__(address, port, timeout)
+        self._send_msg(RSS_DOWNLOAD)
+
+
+    def download(self, src_pattern, dst_path, timeout=600):
+        """
+        Receive files or directory trees from the server.
+        The semantics of src_pattern and dst_path are similar to those of scp.
+        For example, the following are OK:
+            src_pattern='C:\\foo.txt', dst_path='/tmp'
+                (receives a single file)
+            src_pattern='C:\\Windows', dst_path='/tmp'
+                (receives a directory tree recursively)
+            src_pattern='C:\\Windows\\*', dst_path='/tmp'
+                (receives all files and directory trees under C:\\Windows)
+        The following is not OK:
+            src_pattern='C:\\Windows', dst_path='/tmp/*'
+                (wildcards are only allowed in src_pattern)
+
+        @param src_pattern: A path or wildcard pattern specifying the files or
+                directories, in the server's filesystem, that will be sent to
+                the client
+        @param dst_path: A path in the local filesystem where the files will
+                be saved
+        @param timeout: Time duration in seconds to wait for the transfer to
+                complete
+        @raise FileTransferTimeoutError: Raised if timeout expires
+        @raise FileTransferServerError: Raised if something goes wrong and the
+                server sends an informative error message to the client
+        @note: Other exceptions can be raised.
+        """
+        dst_path = os.path.abspath(dst_path)
+        end_time = time.time() + timeout
+        try:
+            try:
+                self._send_msg(RSS_SET_PATH)
+                self._send_packet(src_pattern)
+            except FileTransferError:
+                self._handle_transfer_error()
+            while True:
+                msg = self._receive_msg()
+                if msg == RSS_CREATE_FILE:
+                    # Receive filename and file contents
+                    filename = self._receive_packet()
+                    if os.path.isdir(dst_path):
+                        dst_path = os.path.join(dst_path, filename)
+                    self._receive_packet_into_file(
+                            dst_path, max(0, end_time - time.time()))
+                    dst_path = os.path.dirname(dst_path)
+                elif msg == RSS_CREATE_DIR:
+                    # Receive dirname and create the directory
+                    dirname = self._receive_packet()
+                    if os.path.isdir(dst_path):
+                        dst_path = os.path.join(dst_path, dirname)
+                    if not os.path.isdir(dst_path):
+                        os.mkdir(dst_path)
+                elif msg == RSS_LEAVE_DIR:
+                    # Return to parent dir
+                    dst_path = os.path.dirname(dst_path)
+                elif msg == RSS_DONE:
+                    # Transfer complete
+                    break
+                elif msg == RSS_ERROR:
+                    # Receive error message and abort
+                    errmsg = self._receive_packet()
+                    raise FileTransferServerError("Server said: %s" % errmsg)
+                else:
+                    # Unexpected msg
+                    raise FileTransferProtocolError("Received unexpected msg")
+        except:
+            # In any case, if the transfer failed, close the connection
+            self.close()
+            raise
+
+
+def main():
+    import optparse
+
+    usage = "usage: %prog [options] address port src_pattern dst_path"
+    parser = optparse.OptionParser(usage=usage)
+    parser.add_option("-d", "--download",
+                      action="store_true", dest="download",
+                      help="download files from server")
+    parser.add_option("-u", "--upload",
+                      action="store_true", dest="upload",
+                      help="upload files to server")
+    parser.add_option("-t", "--timeout",
+                      type="int", dest="timeout", default=3600,
+                      help="transfer timeout")
+    options, args = parser.parse_args()
+    if options.download == options.upload:
+        parser.error("you must specify either -d or -u")
+    if len(args) != 4:
+        parser.error("incorrect number of arguments")
+    address, port, src_pattern, dst_path = args
+    port = int(port)
+
+    if options.download:
+        client = FileDownloadClient(address, port)
+        client.download(src_pattern, dst_path, timeout=options.timeout)
+        client.close()
+    elif options.upload:
+        client = FileUploadClient(address, port)
+        client.upload(src_pattern, dst_path, timeout=options.timeout)
+        client.close()
+
+
+if __name__ == "__main__":
+    main()
-- 
1.5.4.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support
  2010-06-24 11:03 [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support Michael Goldish
  2010-06-24 11:03 ` [KVM-AUTOTEST PATCH] [RFC] KVM test: add python client for rss file transfer services Michael Goldish
@ 2010-06-24 11:25 ` Yaniv Kaul
  2010-06-24 11:37   ` Lucas Meneghel Rodrigues
  2010-06-24 12:23   ` Michael Goldish
  1 sibling, 2 replies; 5+ messages in thread
From: Yaniv Kaul @ 2010-06-24 11:25 UTC (permalink / raw)
  To: Michael Goldish; +Cc: autotest, kvm

  On 6/24/2010 2:03 PM, Michael Goldish wrote:
> Enable RSS to send/receive files and directory trees (recursively).

Are you slowly developing a competitor to STAF 
(http://staf.sourceforge.net/) ?
And re. the file transfer protocol, why not use TFTP, instead of 
creating your own protocol?
Y.

> See protocol details in rss.cpp.
>
> Signed-off-by: Michael Goldish<mgoldish@redhat.com>
> ---
>   client/tests/kvm/deps/rss.cpp | 1429 ++++++++++++++++++++++++++++-------------
>   1 files changed, 970 insertions(+), 459 deletions(-)
>
> diff --git a/client/tests/kvm/deps/rss.cpp b/client/tests/kvm/deps/rss.cpp
> index 66d9a5b..b2f6049 100644
> --- a/client/tests/kvm/deps/rss.cpp
> +++ b/client/tests/kvm/deps/rss.cpp
> @@ -1,459 +1,970 @@
> -// Simple remote shell server
> -// Author: Michael Goldish<mgoldish@redhat.com>
> -// Much of the code here was adapted from Microsoft code samples.
> -
> -// Usage: rss.exe [port]
> -// If no port is specified the default is 22.
> -
> -#define _WIN32_WINNT 0x0500
> -
> -#include<windows.h>
> -#include<winsock2.h>
> -#include<stdio.h>
> -
> -#pragma comment(lib, "ws2_32.lib")
> -
> -int port = 22;
> -
> -HWND hMainWindow = NULL;
> -HWND hTextBox = NULL;
> -
> -struct client_info {
> -    SOCKET socket;
> -    sockaddr_in addr;
> -    int pid;
> -    HWND hwnd;
> -    HANDLE hJob;
> -    HANDLE hChildOutputRead;
> -    HANDLE hThreadChildToSocket;
> -};
> -
> -void ExitOnError(char *message, BOOL winsock = 0)
> -{
> -    LPVOID system_message;
> -    char buffer[512];
> -
> -    int error_code;
> -    if (winsock)
> -        error_code = WSAGetLastError();
> -    else
> -        error_code = GetLastError();
> -
> -    WSACleanup();
> -
> -    FormatMessage(
> -        FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
> -        NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
> -        (LPTSTR)&system_message, 0, NULL);
> -
> -    sprintf(buffer,
> -            "%s!\n"
> -            "Error code = %d\n"
> -            "Error message = %s",
> -            message, error_code, (char *)system_message);
> -
> -    MessageBox(hMainWindow, buffer, "Error", MB_OK | MB_ICONERROR);
> -
> -    LocalFree(system_message);
> -    ExitProcess(1);
> -}
> -
> -void AppendMessage(char *message)
> -{
> -    int length = GetWindowTextLength(hTextBox);
> -    SendMessage(hTextBox, EM_SETSEL, (WPARAM)length, (LPARAM)length);
> -    SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)message);
> -}
> -
> -void FormatStringForPrinting(char *dst, char *src, int size)
> -{
> -    int j = 0;
> -
> -    for (int i = 0; i<  size&&  src[i]; i++) {
> -        if (src[i] == '\n') {
> -            dst[j++] = '\\';
> -            dst[j++] = 'n';
> -        } else if (src[i] == '\r') {
> -            dst[j++] = '\\';
> -            dst[j++] = 'r';
> -        } else if (src[i] == '\t') {
> -            dst[j++] = '\\';
> -            dst[j++] = 't';
> -        } else if (src[i] == '\\') {
> -            dst[j++] = '\\';
> -            dst[j++] = '\\';
> -        } else dst[j++] = src[i];
> -    }
> -    dst[j] = 0;
> -}
> -
> -char* GetClientIPAddress(client_info *ci)
> -{
> -    char *address = inet_ntoa(ci->addr.sin_addr);
> -    if (address)
> -        return address;
> -    else
> -        return "unknown";
> -}
> -
> -DWORD WINAPI ChildToSocket(LPVOID client_info_ptr)
> -{
> -    char buffer[1024], message[1024];
> -    client_info ci;
> -    DWORD bytes_read;
> -    int bytes_sent;
> -
> -    memcpy(&ci, client_info_ptr, sizeof(ci));
> -
> -    while (1) {
> -        // Read data from the child's STDOUT/STDERR pipes
> -        if (!ReadFile(ci.hChildOutputRead,
> -                      buffer, sizeof(buffer),
> -&bytes_read, NULL) || !bytes_read) {
> -            if (GetLastError() == ERROR_BROKEN_PIPE)
> -                break; // Pipe done -- normal exit path
> -            else
> -                ExitOnError("ReadFile failed"); // Something bad happened
> -        }
> -        // Send data to the client
> -        bytes_sent = send(ci.socket, buffer, bytes_read, 0);
> -        /*
> -        // Make sure all the data was sent
> -        if (bytes_sent != bytes_read) {
> -            sprintf(message,
> -                    "ChildToSocket: bytes read (%d) != bytes sent (%d)",
> -                    bytes_read, bytes_sent);
> -            ExitOnError(message, 1);
> -        }
> -        */
> -    }
> -
> -    AppendMessage("Child exited\r\n");
> -    shutdown(ci.socket, SD_BOTH);
> -
> -    return 0;
> -}
> -
> -DWORD WINAPI SocketToChild(LPVOID client_info_ptr)
> -{
> -    char buffer[256], formatted_buffer[768];
> -    char message[1024], client_info_str[256];
> -    client_info ci;
> -    DWORD bytes_written;
> -    int bytes_received;
> -
> -    memcpy(&ci, client_info_ptr, sizeof(ci));
> -
> -    sprintf(client_info_str, "address %s, port %d",
> -            GetClientIPAddress(&ci), ci.addr.sin_port);
> -
> -    sprintf(message, "New client connected (%s)\r\n", client_info_str);
> -    AppendMessage(message);
> -
> -    while (1) {
> -        // Receive data from the socket
> -        ZeroMemory(buffer, sizeof(buffer));
> -        bytes_received = recv(ci.socket, buffer, sizeof(buffer), 0);
> -        if (bytes_received<= 0)
> -            break;
> -        // Report the data received
> -        FormatStringForPrinting(formatted_buffer, buffer, sizeof(buffer));
> -        sprintf(message, "Client (%s) entered text: \"%s\"\r\n",
> -                client_info_str, formatted_buffer);
> -        AppendMessage(message);
> -        // Send the data as a series of WM_CHAR messages to the console window
> -        for (int i=0; i<bytes_received; i++) {
> -            SendMessage(ci.hwnd, WM_CHAR, (WPARAM)buffer[i], 0);
> -            SendMessage(ci.hwnd, WM_SETFOCUS, 0, 0);
> -        }
> -    }
> -
> -    sprintf(message, "Client disconnected (%s)\r\n", client_info_str);
> -    AppendMessage(message);
> -
> -    // Attempt to terminate the child's process tree:
> -    // Using taskkill (where available)
> -    sprintf(buffer, "taskkill /PID %d /T /F", ci.pid);
> -    system(buffer);
> -    // .. and using TerminateJobObject()
> -    TerminateJobObject(ci.hJob, 0);
> -    // Wait for the ChildToSocket thread to terminate
> -    WaitForSingleObject(ci.hThreadChildToSocket, 10000);
> -    // In case the thread refuses to exit -- terminate it
> -    TerminateThread(ci.hThreadChildToSocket, 0);
> -    // Close the socket
> -    shutdown(ci.socket, SD_BOTH);
> -    closesocket(ci.socket);
> -
> -    // Close unnecessary handles
> -    CloseHandle(ci.hJob);
> -    CloseHandle(ci.hThreadChildToSocket);
> -    CloseHandle(ci.hChildOutputRead);
> -
> -    AppendMessage("SocketToChild thread exited\r\n");
> -
> -    return 0;
> -}
> -
> -void PrepAndLaunchRedirectedChild(client_info *ci,
> -                                  HANDLE hChildStdOut,
> -                                  HANDLE hChildStdErr)
> -{
> -    PROCESS_INFORMATION pi;
> -    STARTUPINFO si;
> -
> -    // Allocate a new console for the child
> -    HWND hwnd = GetForegroundWindow();
> -    FreeConsole();
> -    AllocConsole();
> -    ShowWindow(GetConsoleWindow(), SW_HIDE);
> -    if (hwnd)
> -        SetForegroundWindow(hwnd);
> -
> -    // Set up the start up info struct.
> -    ZeroMemory(&si, sizeof(STARTUPINFO));
> -    si.cb = sizeof(STARTUPINFO);
> -    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
> -    si.hStdOutput = hChildStdOut;
> -    si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
> -    si.hStdError  = hChildStdErr;
> -    // Use this if you want to hide the child:
> -    si.wShowWindow = SW_HIDE;
> -    // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
> -    // use the wShowWindow flags.
> -
> -    // Launch the process that you want to redirect.
> -    if (!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE,
> -                       0, NULL, "C:\\",&si,&pi))
> -        ExitOnError("CreateProcess failed");
> -
> -    // Close any unnecessary handles.
> -    if (!CloseHandle(pi.hThread))
> -        ExitOnError("CloseHandle failed");
> -
> -    // Keep the process ID
> -    ci->pid = pi.dwProcessId;
> -    // Assign the process to a newly created JobObject
> -    ci->hJob = CreateJobObject(NULL, NULL);
> -    AssignProcessToJobObject(ci->hJob, pi.hProcess);
> -    // Keep the console window's handle
> -    ci->hwnd = GetConsoleWindow();
> -
> -    // Detach from the child's console
> -    FreeConsole();
> -}
> -
> -void SpawnSession(client_info *ci)
> -{
> -    HANDLE hOutputReadTmp, hOutputRead, hOutputWrite;
> -    HANDLE hErrorWrite;
> -    SECURITY_ATTRIBUTES sa;
> -
> -    // Set up the security attributes struct.
> -    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
> -    sa.lpSecurityDescriptor = NULL;
> -    sa.bInheritHandle = TRUE;
> -
> -    // Create the child output pipe.
> -    if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa, 0))
> -        ExitOnError("CreatePipe failed");
> -
> -    // Create a duplicate of the output write handle for the std error
> -    // write handle. This is necessary in case the child application
> -    // closes one of its std output handles.
> -    if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
> -                         GetCurrentProcess(),&hErrorWrite, 0,
> -                         TRUE, DUPLICATE_SAME_ACCESS))
> -        ExitOnError("DuplicateHandle failed");
> -
> -    // Create new output read handle and the input write handles. Set
> -    // the Properties to FALSE. Otherwise, the child inherits the
> -    // properties and, as a result, non-closeable handles to the pipes
> -    // are created.
> -    if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
> -                         GetCurrentProcess(),
> -&hOutputRead, // Address of new handle.
> -                         0, FALSE, // Make it uninheritable.
> -                         DUPLICATE_SAME_ACCESS))
> -        ExitOnError("DuplicateHandle failed");
> -
> -    // Close inheritable copies of the handles you do not want to be
> -    // inherited.
> -    if (!CloseHandle(hOutputReadTmp))
> -        ExitOnError("CloseHandle failed");
> -
> -    PrepAndLaunchRedirectedChild(ci, hOutputWrite, hErrorWrite);
> -
> -    ci->hChildOutputRead = hOutputRead;
> -
> -    // Close pipe handles (do not continue to modify the parent).
> -    // You need to make sure that no handles to the write end of the
> -    // output pipe are maintained in this process or else the pipe will
> -    // not close when the child process exits and the ReadFile will hang.
> -    if (!CloseHandle(hOutputWrite)) ExitOnError("CloseHandle failed");
> -    if (!CloseHandle(hErrorWrite)) ExitOnError("CloseHandle failed");
> -}
> -
> -DWORD WINAPI ListenThread(LPVOID param)
> -{
> -    WSADATA wsaData;
> -    SOCKET ListenSocket = INVALID_SOCKET;
> -    sockaddr_in addr;
> -    int result, addrlen;
> -    client_info ci;
> -    HANDLE hThread;
> -
> -    // Initialize Winsock
> -    result = WSAStartup(MAKEWORD(2,2),&wsaData);
> -    if (result)
> -        ExitOnError("Winsock initialization failed");
> -
> -    // Create socket
> -    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> -    if (ListenSocket == INVALID_SOCKET)
> -        ExitOnError("Socket creation failed", 1);
> -
> -    // Bind the socket
> -    addr.sin_family = AF_INET;
> -    addr.sin_addr.s_addr = htonl(INADDR_ANY);
> -    addr.sin_port = htons(port);
> -
> -    result = bind(ListenSocket, (sockaddr *)&addr, sizeof(addr));
> -    if (result == SOCKET_ERROR)
> -        ExitOnError("bind failed", 1);
> -
> -    // Start listening for incoming connections
> -    result = listen(ListenSocket, SOMAXCONN);
> -    if (result == SOCKET_ERROR)
> -        ExitOnError("listen failed", 1);
> -
> -    // Inform the user
> -    AppendMessage("Waiting for clients to connect...\r\n");
> -
> -    while (1) {
> -        addrlen = sizeof(ci.addr);
> -        ci.socket = accept(ListenSocket, (sockaddr *)&ci.addr,&addrlen);
> -        if (ci.socket == INVALID_SOCKET) {
> -            if (WSAGetLastError() == WSAEINTR)
> -                break;
> -            else
> -                ExitOnError("accept failed", 1);
> -        }
> -
> -        // Under heavy load, spawning cmd.exe might take a while, so tell the
> -        // client to be patient
> -        char *message = "Please wait...\r\n";
> -        send(ci.socket, message, strlen(message), 0);
> -        // Spawn a new redirected cmd.exe process
> -        SpawnSession(&ci);
> -        // Start transferring data from the child process to the client
> -        hThread = CreateThread(NULL, 0, ChildToSocket, (LPVOID)&ci, 0, NULL);
> -        if (!hThread)
> -            ExitOnError("Could not create ChildToSocket thread");
> -        ci.hThreadChildToSocket = hThread;
> -        // ... and from the client to the child process
> -        hThread = CreateThread(NULL, 0, SocketToChild, (LPVOID)&ci, 0, NULL);
> -        if (!hThread)
> -            ExitOnError("Could not create SocketToChild thread");
> -    }
> -
> -    return 0;
> -}
> -
> -LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
> -{
> -    RECT rect;
> -    HANDLE hListenThread;
> -
> -    switch (msg) {
> -        case WM_CREATE:
> -            // Create text box
> -            GetClientRect(hwnd,&rect);
> -            hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
> -                                      "EDIT", "",
> -                                      WS_CHILD|WS_VISIBLE|WS_VSCROLL|
> -                                      ES_MULTILINE|ES_AUTOVSCROLL,
> -                                      20, 20,
> -                                      rect.right - 40,
> -                                      rect.bottom - 40,
> -                                      hwnd,
> -                                      NULL,
> -                                      GetModuleHandle(NULL),
> -                                      NULL);
> -            if (!hTextBox)
> -                ExitOnError("Could not create text box");
> -
> -            // Set the font
> -            SendMessage(hTextBox, WM_SETFONT,
> -                        (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
> -                        MAKELPARAM(FALSE, 0));
> -
> -            // Start the listening thread
> -            hListenThread =
> -                CreateThread(NULL, 0, ListenThread, NULL, 0, NULL);
> -            if (!hListenThread)
> -                ExitOnError("Could not create server thread");
> -            break;
> -
> -        case WM_DESTROY:
> -            WSACleanup();
> -            PostQuitMessage(0);
> -            break;
> -
> -        default:
> -            return DefWindowProc(hwnd, msg, wParam, lParam);
> -    }
> -
> -    return 0;
> -}
> -
> -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
> -                   LPSTR lpCmdLine, int nShowCmd)
> -{
> -    WNDCLASSEX wc;
> -    MSG msg;
> -
> -    if (strlen(lpCmdLine))
> -        sscanf(lpCmdLine, "%d",&port);
> -
> -    // Make sure the firewall is disabled
> -    system("netsh firewall set opmode disable");
> -
> -    // Create the window class
> -    wc.cbSize        = sizeof(WNDCLASSEX);
> -    wc.style         = CS_HREDRAW | CS_VREDRAW;
> -    wc.lpfnWndProc   = WndProc;
> -    wc.cbClsExtra    = 0;
> -    wc.cbWndExtra    = 0;
> -    wc.hInstance     = hInstance;
> -    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
> -    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
> -    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
> -    wc.lpszMenuName  = NULL;
> -    wc.lpszClassName = "RemoteShellServerWindowClass";
> -    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
> -
> -    if (!RegisterClassEx(&wc))
> -        ExitOnError("Could not register window class");
> -
> -    // Create the main window
> -    hMainWindow =
> -        CreateWindow("RemoteShellServerWindowClass",
> -                     "Remote Shell Server",
> -                     WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
> -                     20, 20, 500, 300,
> -                     NULL, NULL, hInstance, NULL);
> -    if (!hMainWindow)
> -        ExitOnError("Could not create window");
> -
> -    ShowWindow(hMainWindow, SW_SHOWMINNOACTIVE);
> -    UpdateWindow(hMainWindow);
> -
> -    // Main message loop
> -    while (GetMessage(&msg, NULL, 0, 0)) {
> -        TranslateMessage(&msg);
> -        DispatchMessage(&msg);
> -    }
> -
> -    ExitProcess(0);
> -}
> +// Simple remote shell server (and file transfer server)
> +// Author: Michael Goldish<mgoldish@redhat.com>
> +// Much of the code here was adapted from Microsoft code samples.
> +
> +// Usage: rss.exe [shell port] [file transfer port]
> +// If no shell port is specified the default is 22.
> +// If no file transfer port is specified the default is 23.
> +
> +// Definitions:
> +// A 'msg' is a 32 bit integer.
> +// A 'packet' is a 64 bit unsigned integer followed by a string of bytes.
> +// The 64 bit integer indicates the length of the string.
> +
> +// Protocol for file transfers:
> +//
> +// When uploading files to the server:
> +// 1. The client connects.
> +// 2. The server sends RSS_MAGIC.
> +// 3. The client sends RSS_SET_PATH, followed by a packet (as defined above)
> +//    containing the path (in the server's filesystem) where files are to be
> +//    stored.  The 'current path' is set to the path given.
> +// Uploading a file (optional, can be repeated many times):
> +//   4. The client sends RSS_CREATE_FILE, followed by a packet containing the
> +//      filename (filename only, without a path), followed by a packet
> +//      containing the file's contents.  The file is stored in the current
> +//      path.
> +// Uploading a directory (optional, can be repeated many times):
> +//   5. The client sends RSS_CREATE_DIR, followed by a packet containing the
> +//      name of the directory to be created (directory name only, without a
> +//      path).  The directory is created in the current path.  Then, the
> +//      'current path' becomes the path of that new directory.
> +//   6. Steps 4-5 may be repeated to upload files and directories to the new
> +//      directory.
> +//   7. The client sends RSS_LEAVE_DIR, and the current path is set to the path
> +//      of its parent directory (e.g. C:\Foobar ->  C:\).
> +// 8. The client sends RSS_DONE and waits for a response.
> +// 9. The server sends RSS_OK to indicate that it's still listening.
> +// 10. Steps 3-9 are repeated as many times as necessary.
> +// If a critical error occurs at any time, the server may send RSS_ERROR
> +// followed by a packet containing an error message, and the connection is
> +// closed.
> +//
> +// When downloading files from the server:
> +// 1. The client connects.
> +// 2. The server sends RSS_MAGIC.
> +// 3. The client sends RSS_SET_PATH, followed by a packet (as defined above)
> +//    containing a path (in the server's filesystem) or a wildcard pattern
> +//    indicating the files/directories the client wants to download.
> +// The server then searches the given path.  For every file found:
> +//   4. The server sends RSS_CREATE_FILE, followed by a packet containing the
> +//      filename (filename only, without a path), followed by a packet
> +//      containing the file's contents.
> +// For every directory found:
> +//   5. The server sends RSS_CREATE_DIR, followed by a packet containing the
> +//      name of the directory to be created (directory name only, without a
> +//      path).
> +//   6. Steps 4-5 are repeated as many times as necessary.
> +//   7. The server sends RSS_LEAVE_DIR.
> +// 8. The server sends RSS_DONE.
> +// 9. Steps 3-8 are repeated as many times as necessary.
> +// If a critical error occurs, the server may send RSS_ERROR followed by a
> +// packet containing an error message, and the connection is closed.
> +// RSS_ERROR may only be sent when the client expects a msg.
> +
> +#define _WIN32_WINNT 0x0500
> +
> +#include<winsock2.h>
> +#include<windows.h>
> +#include<stdio.h>
> +#include<stdarg.h>
> +#include<shlwapi.h>
> +
> +#pragma comment(lib, "ws2_32.lib")
> +#pragma comment(lib, "shlwapi.lib")
> +
> +#define TEXTBOX_LIMIT 32767
> +
> +// Constants for file transfer server
> +#define RSS_MAGIC           0x525353
> +#define RSS_OK              1
> +#define RSS_ERROR           2
> +#define RSS_UPLOAD          3
> +#define RSS_DOWNLOAD        4
> +#define RSS_SET_PATH        5
> +#define RSS_CREATE_FILE     6
> +#define RSS_CREATE_DIR      7
> +#define RSS_LEAVE_DIR       8
> +#define RSS_DONE            9
> +
> +// Globals
> +int shell_port = 22;
> +int file_transfer_port = 23;
> +
> +HWND hMainWindow = NULL;
> +HWND hTextBox = NULL;
> +
> +struct client_info {
> +    SOCKET socket;
> +    sockaddr_in addr;
> +    char addr_str[256];
> +    int pid;
> +    HWND hwnd;
> +    HANDLE hJob;
> +    HANDLE hChildOutputRead;
> +    HANDLE hThreadChildToSocket;
> +};
> +
> +void ExitOnError(char *message, BOOL winsock = FALSE)
> +{
> +    LPVOID system_message;
> +    char buffer[512];
> +    int error_code;
> +
> +    if (winsock)
> +        error_code = WSAGetLastError();
> +    else
> +        error_code = GetLastError();
> +    WSACleanup();
> +
> +    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
> +                  FORMAT_MESSAGE_FROM_SYSTEM,
> +                  NULL,
> +                  error_code,
> +                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
> +                  (LPTSTR)&system_message,
> +                  0,
> +                  NULL);
> +    sprintf(buffer,
> +            "%s!\n"
> +            "Error code = %d\n"
> +            "Error message = %s",
> +            message, error_code, (char *)system_message);
> +    MessageBox(hMainWindow, buffer, "Error", MB_OK | MB_ICONERROR);
> +
> +    LocalFree(system_message);
> +    ExitProcess(1);
> +}
> +
> +void _vAppendMessage(char *message, va_list args)
> +{
> +    char str[512] = {0};
> +
> +    vsnprintf(str, sizeof(str) - 1, message, args);
> +    strncat(str, "\r\n", sizeof(str) - 1 - strlen(str));
> +
> +    int length = GetWindowTextLength(hTextBox);
> +    SendMessage(hTextBox, EM_SETSEL, (WPARAM)length, (LPARAM)length);
> +    SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)str);
> +}
> +
> +// Append text to the textbox
> +// (if the textbox is full, do nothing)
> +void _AppendMessage(char *message, ...)
> +{
> +    va_list args;
> +
> +    va_start(args, message);
> +    _vAppendMessage(message, args);
> +    va_end(args);
> +}
> +
> +// Append text to the textbox
> +// (if the textbox is full or nearly full, remove old text first)
> +void AppendMessage(char *message, ...)
> +{
> +    va_list args;
> +
> +    int length = GetWindowTextLength(hTextBox);
> +    if (length>  TEXTBOX_LIMIT - 512) {
> +        SendMessage(hTextBox, EM_SETSEL, (WPARAM)0,
> +                    (LPARAM)(TEXTBOX_LIMIT / 2));
> +        SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)"...");
> +        if (length == TEXTBOX_LIMIT) _AppendMessage("...");
> +    }
> +    va_start(args, message);
> +    _vAppendMessage(message, args);
> +    va_end(args);
> +}
> +
> +void FormatStringForPrinting(char *dst, char *src, int size)
> +{
> +    int j = 0;
> +
> +    for (int i = 0; i<  size&&  src[i]; i++) {
> +        if (src[i] == '\n') {
> +            dst[j++] = '\\';
> +            dst[j++] = 'n';
> +        } else if (src[i] == '\r') {
> +            dst[j++] = '\\';
> +            dst[j++] = 'r';
> +        } else if (src[i] == '\t') {
> +            dst[j++] = '\\';
> +            dst[j++] = 't';
> +        } else if (src[i] == '\\') {
> +            dst[j++] = '\\';
> +            dst[j++] = '\\';
> +        } else dst[j++] = src[i];
> +    }
> +    dst[j] = 0;
> +}
> +
> +char* GetClientIPAddress(client_info *ci)
> +{
> +    char *address = inet_ntoa(ci->addr.sin_addr);
> +    if (address)
> +        return address;
> +    else
> +        return "unknown";
> +}
> +
> +// Read a small chunk of data into a buffer
> +BOOL Receive(SOCKET socket, char *buffer, int len)
> +{
> +    while (len>  0) {
> +        int bytes_received = recv(socket, buffer, len, 0);
> +        if (bytes_received<= 0)
> +            return FALSE;
> +        buffer += bytes_received;
> +        len -= bytes_received;
> +    }
> +    return TRUE;
> +}
> +
> +// Send data from a buffer
> +BOOL Send(SOCKET socket, char *buffer, int len)
> +{
> +    while (len>  0) {
> +        int bytes_sent = send(socket, buffer, len, 0);
> +        if (bytes_sent<= 0)
> +            return FALSE;
> +        buffer += bytes_sent;
> +        len -= bytes_sent;
> +    }
> +    return TRUE;
> +}
> +
> +/*-------------
> + * Shell server
> + *-------------*/
> +
> +DWORD WINAPI ChildToSocket(LPVOID client_info_ptr)
> +{
> +    client_info *ci = (client_info *)client_info_ptr;
> +    char buffer[1024];
> +    DWORD bytes_read;
> +
> +    while (1) {
> +        // Read data from the child's STDOUT/STDERR pipes
> +        if (!ReadFile(ci->hChildOutputRead,
> +                      buffer, sizeof(buffer),
> +&bytes_read, NULL) || !bytes_read) {
> +            if (GetLastError() == ERROR_BROKEN_PIPE)
> +                break; // Pipe done -- normal exit path
> +            else
> +                ExitOnError("ReadFile failed"); // Something bad happened
> +        }
> +        // Send data to the client
> +        Send(ci->socket, buffer, bytes_read);
> +    }
> +
> +    AppendMessage("Child exited");
> +    closesocket(ci->socket);
> +    return 0;
> +}
> +
> +DWORD WINAPI SocketToChild(LPVOID client_info_ptr)
> +{
> +    client_info *ci = (client_info *)client_info_ptr;
> +    char buffer[256], formatted_buffer[768];
> +    int bytes_received;
> +
> +    AppendMessage("Shell server: new client connected (%s)", ci->addr_str);
> +
> +    while (1) {
> +        // Receive data from the socket
> +        ZeroMemory(buffer, sizeof(buffer));
> +        bytes_received = recv(ci->socket, buffer, sizeof(buffer), 0);
> +        if (bytes_received<= 0)
> +            break;
> +        // Report the data received
> +        FormatStringForPrinting(formatted_buffer, buffer, sizeof(buffer));
> +        _AppendMessage("Client (%s) entered text: \"%s\"",
> +                       ci->addr_str, formatted_buffer);
> +        // Send the data as a series of WM_CHAR messages to the console window
> +        for (int i = 0; i<  bytes_received; i++) {
> +            SendMessage(ci->hwnd, WM_CHAR, (WPARAM)buffer[i], 0);
> +            SendMessage(ci->hwnd, WM_SETFOCUS, 0, 0);
> +        }
> +    }
> +
> +    AppendMessage("Shell server: client disconnected (%s)", ci->addr_str);
> +
> +    // Attempt to terminate the child's process tree:
> +    // Using taskkill (where available)
> +    sprintf(buffer, "taskkill /PID %d /T /F", ci->pid);
> +    system(buffer);
> +    // .. and using TerminateJobObject()
> +    TerminateJobObject(ci->hJob, 0);
> +    // Wait for the ChildToSocket thread to terminate
> +    WaitForSingleObject(ci->hThreadChildToSocket, 10000);
> +    // In case the thread refuses to exit, terminate it
> +    TerminateThread(ci->hThreadChildToSocket, 0);
> +    // Close the socket
> +    closesocket(ci->socket);
> +
> +    // Free resources
> +    CloseHandle(ci->hJob);
> +    CloseHandle(ci->hThreadChildToSocket);
> +    CloseHandle(ci->hChildOutputRead);
> +    free(ci);
> +
> +    AppendMessage("SocketToChild thread exited");
> +    return 0;
> +}
> +
> +void PrepAndLaunchRedirectedChild(client_info *ci,
> +                                  HANDLE hChildStdOut,
> +                                  HANDLE hChildStdErr)
> +{
> +    PROCESS_INFORMATION pi;
> +    STARTUPINFO si;
> +
> +    // Allocate a new console for the child
> +    HWND hwnd = GetForegroundWindow();
> +    FreeConsole();
> +    AllocConsole();
> +    ShowWindow(GetConsoleWindow(), SW_HIDE);
> +    if (hwnd)
> +        SetForegroundWindow(hwnd);
> +
> +    // Set up the start up info struct.
> +    ZeroMemory(&si, sizeof(STARTUPINFO));
> +    si.cb = sizeof(STARTUPINFO);
> +    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
> +    si.hStdOutput = hChildStdOut;
> +    si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
> +    si.hStdError  = hChildStdErr;
> +    // Use this if you want to hide the child:
> +    si.wShowWindow = SW_HIDE;
> +    // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
> +    // use the wShowWindow flags.
> +
> +    // Launch the process that you want to redirect.
> +    if (!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE,
> +                       0, NULL, "C:\\",&si,&pi))
> +        ExitOnError("CreateProcess failed");
> +
> +    // Close any unnecessary handles.
> +    if (!CloseHandle(pi.hThread))
> +        ExitOnError("CloseHandle failed");
> +
> +    // Keep the process ID
> +    ci->pid = pi.dwProcessId;
> +    // Assign the process to a newly created JobObject
> +    ci->hJob = CreateJobObject(NULL, NULL);
> +    AssignProcessToJobObject(ci->hJob, pi.hProcess);
> +    // Keep the console window's handle
> +    ci->hwnd = GetConsoleWindow();
> +
> +    // Detach from the child's console
> +    FreeConsole();
> +}
> +
> +void SpawnSession(client_info *ci)
> +{
> +    HANDLE hOutputReadTmp, hOutputRead, hOutputWrite;
> +    HANDLE hErrorWrite;
> +    SECURITY_ATTRIBUTES sa;
> +
> +    // Set up the security attributes struct.
> +    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
> +    sa.lpSecurityDescriptor = NULL;
> +    sa.bInheritHandle = TRUE;
> +
> +    // Create the child output pipe.
> +    if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa, 0))
> +        ExitOnError("CreatePipe failed");
> +
> +    // Create a duplicate of the output write handle for the std error
> +    // write handle. This is necessary in case the child application
> +    // closes one of its std output handles.
> +    if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
> +                         GetCurrentProcess(),&hErrorWrite, 0,
> +                         TRUE, DUPLICATE_SAME_ACCESS))
> +        ExitOnError("DuplicateHandle failed");
> +
> +    // Create new output read handle and the input write handles. Set
> +    // the Properties to FALSE. Otherwise, the child inherits the
> +    // properties and, as a result, non-closeable handles to the pipes
> +    // are created.
> +    if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
> +                         GetCurrentProcess(),
> +&hOutputRead, // Address of new handle.
> +                         0, FALSE, // Make it uninheritable.
> +                         DUPLICATE_SAME_ACCESS))
> +        ExitOnError("DuplicateHandle failed");
> +
> +    // Close inheritable copies of the handles you do not want to be
> +    // inherited.
> +    if (!CloseHandle(hOutputReadTmp))
> +        ExitOnError("CloseHandle failed");
> +
> +    PrepAndLaunchRedirectedChild(ci, hOutputWrite, hErrorWrite);
> +
> +    ci->hChildOutputRead = hOutputRead;
> +
> +    // Close pipe handles (do not continue to modify the parent).
> +    // You need to make sure that no handles to the write end of the
> +    // output pipe are maintained in this process or else the pipe will
> +    // not close when the child process exits and the ReadFile will hang.
> +    if (!CloseHandle(hOutputWrite)) ExitOnError("CloseHandle failed");
> +    if (!CloseHandle(hErrorWrite)) ExitOnError("CloseHandle failed");
> +}
> +
> +SOCKET PrepListenSocket(int port)
> +{
> +    sockaddr_in addr;
> +    linger l;
> +    int result;
> +
> +    // Create socket
> +    SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> +    if (ListenSocket == INVALID_SOCKET)
> +        ExitOnError("Socket creation failed", TRUE);
> +
> +    // Enable lingering
> +    l.l_linger = 10;
> +    l.l_onoff = 1;
> +    setsockopt(ListenSocket, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l));
> +
> +    // Bind the socket
> +    addr.sin_family = AF_INET;
> +    addr.sin_addr.s_addr = htonl(INADDR_ANY);
> +    addr.sin_port = htons(port);
> +
> +    result = bind(ListenSocket, (sockaddr *)&addr, sizeof(addr));
> +    if (result == SOCKET_ERROR)
> +        ExitOnError("bind failed", TRUE);
> +
> +    // Start listening for incoming connections
> +    result = listen(ListenSocket, SOMAXCONN);
> +    if (result == SOCKET_ERROR)
> +        ExitOnError("listen failed", TRUE);
> +
> +    return ListenSocket;
> +}
> +
> +DWORD WINAPI ShellListenThread(LPVOID param)
> +{
> +    client_info _ci, *ci;
> +    HANDLE hThread;
> +
> +    SOCKET ListenSocket = PrepListenSocket(shell_port);
> +
> +    // Inform the user
> +    AppendMessage("Shell server: waiting for clients to connect...");
> +
> +    while (1) {
> +        int addrlen = sizeof(_ci.addr);
> +        _ci.socket = accept(ListenSocket, (sockaddr *)&_ci.addr,&addrlen);
> +        if (_ci.socket == INVALID_SOCKET) {
> +            if (WSAGetLastError() == WSAEINTR)
> +                break;
> +            else
> +                ExitOnError("accept failed", TRUE);
> +        }
> +
> +        if (!(ci = (client_info *)malloc(sizeof(client_info))))
> +            ExitOnError("Could not allocate client_info struct");
> +        memcpy(ci,&_ci, sizeof(client_info));
> +        sprintf(ci->addr_str, "%s:%d", GetClientIPAddress(ci),
> +                ci->addr.sin_port);
> +
> +        // Under heavy load, spawning cmd.exe might take a while, so tell the
> +        // client to be patient
> +        char *message = "Please wait...\r\n";
> +        Send(ci->socket, message, strlen(message));
> +        // Spawn a new redirected cmd.exe process
> +        SpawnSession(ci);
> +        // Start transferring data from the child process to the client
> +        hThread = CreateThread(NULL, 0, ChildToSocket, (LPVOID)ci, 0, NULL);
> +        if (!hThread)
> +            ExitOnError("Could not create ChildToSocket thread");
> +        ci->hThreadChildToSocket = hThread;
> +        // ... and from the client to the child process
> +        hThread = CreateThread(NULL, 0, SocketToChild, (LPVOID)ci, 0, NULL);
> +        if (!hThread)
> +            ExitOnError("Could not create SocketToChild thread");
> +    }
> +
> +    return 0;
> +}
> +
> +/*---------------------
> + * File transfer server
> + *---------------------*/
> +
> +#define BUFSIZE 65536
> +
> +// Receive up to 4GB into a file
> +BOOL ReceiveIntoFile(SOCKET socket, char *filename, DWORD len)
> +{
> +    char *buffer = (char *)malloc(BUFSIZE);
> +    if (!buffer) return FALSE;
> +
> +    FILE *fp = fopen(filename, "wb");
> +    if (!fp) {
> +        free(buffer);
> +        return FALSE;
> +    }
> +
> +    while (len>  0) {
> +        int bytes_received = recv(socket, buffer, min(BUFSIZE, len), 0);
> +        if (bytes_received<= 0)
> +            break;
> +        if (fwrite(buffer, bytes_received, 1, fp)<  1)
> +            break;
> +        len -= bytes_received;
> +    }
> +
> +    fclose(fp);
> +    free(buffer);
> +    return len == 0;
> +}
> +
> +// Send data from a file (unlimited size)
> +BOOL SendFromFile(SOCKET socket, char *filename)
> +{
> +    char *buffer = (char *)malloc(BUFSIZE);
> +    if (!buffer) return FALSE;
> +
> +    FILE *fp = fopen(filename, "rb");
> +    if (!fp) {
> +        free(buffer);
> +        return FALSE;
> +    }
> +
> +    while (!feof(fp)) {
> +        int bytes_read = fread(buffer, 1, BUFSIZE, fp);
> +        if (bytes_read<= 0)
> +            break;
> +        if (!Send(socket, buffer, bytes_read)) {
> +            fclose(fp);
> +            free(buffer);
> +            return FALSE;
> +        }
> +    }
> +
> +    BOOL success = feof(fp);
> +    fclose(fp);
> +    free(buffer);
> +    return success;
> +}
> +
> +BOOL ReceivePacket(SOCKET socket, char *buffer, DWORD max_size)
> +{
> +    DWORD packet_size = 0, padding;
> +
> +    if (!Receive(socket, (char *)&packet_size, 4)) return FALSE;
> +    if (!Receive(socket, (char *)&padding, 4)) return FALSE;
> +    if (packet_size>  max_size) return FALSE;
> +    return Receive(socket, buffer, packet_size);
> +}
> +
> +BOOL ReceiveStrPacket(SOCKET socket, char *buffer, DWORD max_size)
> +{
> +    memset(buffer, 0, max_size);
> +    return ReceivePacket(socket, buffer, max_size - 1);
> +}
> +
> +BOOL SendPacket(SOCKET socket, char *buffer, DWORD len)
> +{
> +    DWORD padding = 0;
> +
> +    if (!Send(socket, (char *)&len, 4)) return FALSE;
> +    if (!Send(socket, (char *)&padding, 4)) return FALSE;
> +    return Send(socket, buffer, len);
> +}
> +
> +BOOL ReceivePacketIntoFile(SOCKET socket, char *filename)
> +{
> +    DWORD packet_size = 0, padding;
> +
> +    if (!Receive(socket, (char *)&packet_size, 4)) return FALSE;
> +    if (!Receive(socket, (char *)&padding, 4)) return FALSE;
> +    return ReceiveIntoFile(socket, filename, packet_size);
> +}
> +
> +BOOL SendPacketFromFile(SOCKET socket, char *filename,
> +                        DWORD size_low, DWORD size_high)
> +{
> +    if (!Send(socket, (char *)&size_low, 4)) return FALSE;
> +    if (!Send(socket, (char *)&size_high, 4)) return FALSE;
> +    return SendFromFile(socket, filename);
> +}
> +
> +BOOL SendMsg(SOCKET socket, int msg)
> +{
> +    return Send(socket, (char *)&msg, 4);
> +}
> +
> +int TerminateTransfer(client_info *ci, char *message)
> +{
> +    AppendMessage(message);
> +    AppendMessage("File transfer server: client disconnected (%s)",
> +                  ci->addr_str);
> +    closesocket(ci->socket);
> +    free(ci);
> +    return 0;
> +}
> +
> +int TerminateWithError(client_info *ci, char *message)
> +{
> +    SendMsg(ci->socket, RSS_ERROR);
> +    SendPacket(ci->socket, message, strlen(message));
> +    return TerminateTransfer(ci, message);
> +}
> +
> +int ReceiveThread(client_info *ci)
> +{
> +    char path[512], filename[512];
> +    int msg = 0;
> +
> +    AppendMessage("Client (%s) wants to upload files", ci->addr_str);
> +
> +    while (1) {
> +        if (!Receive(ci->socket, (char *)&msg, 4))
> +            return TerminateTransfer(ci, "Could not receive further msgs");
> +
> +        switch (msg) {
> +        case RSS_SET_PATH:
> +            if (!ReceiveStrPacket(ci->socket, path, sizeof(path)))
> +                return TerminateWithError(ci,
> +                    "RSS_SET_PATH: could not receive path");
> +            _AppendMessage("Client (%s) set path to %s", ci->addr_str, path);
> +            break;
> +
> +        case RSS_CREATE_FILE:
> +            if (!ReceiveStrPacket(ci->socket, filename, sizeof(filename)))
> +                return TerminateWithError(ci,
> +                    "RSS_CREATE_FILE: could not receive filename");
> +            if (PathIsDirectory(path))
> +                PathAppend(path, filename);
> +            _AppendMessage("Client (%s) is uploading %s", ci->addr_str, path);
> +            if (!ReceivePacketIntoFile(ci->socket, path))
> +                return TerminateWithError(ci,
> +                    "RSS_CREATE_FILE: error receiving or writing file "
> +                    "contents");
> +            PathAppend(path, "..");
> +            break;
> +
> +        case RSS_CREATE_DIR:
> +            if (!ReceiveStrPacket(ci->socket, filename, sizeof(filename)))
> +                return TerminateWithError(ci,
> +                    "RSS_CREATE_DIR: could not receive dirname");
> +            if (PathIsDirectory(path))
> +                PathAppend(path, filename);
> +            _AppendMessage("Entering dir %s", path);
> +            if (PathFileExists(path)) {
> +                if (!PathIsDirectory(path))
> +                    return TerminateWithError(ci,
> +                        "RSS_CREATE_DIR: path exists and is not a directory");
> +            } else {
> +                if (!CreateDirectory(path, NULL))
> +                    return TerminateWithError(ci,
> +                        "RSS_CREATE_DIR: could not create directory");
> +            }
> +            break;
> +
> +        case RSS_LEAVE_DIR:
> +            PathAppend(path, "..");
> +            _AppendMessage("Returning to dir %s", path);
> +            break;
> +
> +        case RSS_DONE:
> +            if (!SendMsg(ci->socket, RSS_OK))
> +                return TerminateTransfer(ci,
> +                    "RSS_DONE: could not send OK msg");
> +            break;
> +
> +        default:
> +            return TerminateWithError(ci, "Received unexpected msg");
> +        }
> +    }
> +}
> +
> +// Given a path or a pattern with wildcards, send files or directory trees to
> +// the client
> +int SendFiles(client_info *ci, char *pattern)
> +{
> +    char path[MAX_PATH];
> +    WIN32_FIND_DATA ffd;
> +    FILE *fp;
> +
> +    HANDLE hFind = FindFirstFile(pattern,&ffd);
> +    if (hFind == INVALID_HANDLE_VALUE) {
> +        // If a weird error occurred (like failure to list directory contents
> +        // due to insufficient permissions) print a warning and continue.
> +        if (GetLastError() != ERROR_FILE_NOT_FOUND)
> +            AppendMessage("WARNING: FindFirstFile failed on pattern %s",
> +                          pattern);
> +        return 1;
> +    }
> +
> +    strncpy(path, pattern, sizeof(path) - 1);
> +    PathAppend(path, "..");
> +
> +    do {
> +        if (ffd.dwFileAttributes&  FILE_ATTRIBUTE_REPARSE_POINT)
> +            continue;
> +        if (ffd.dwFileAttributes&  FILE_ATTRIBUTE_DIRECTORY) {
> +            // Directory
> +            if (!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, ".."))
> +                continue;
> +            PathAppend(path, ffd.cFileName);
> +            _AppendMessage("Entering dir %s", path);
> +            PathAppend(path, "*");
> +            if (!SendMsg(ci->socket, RSS_CREATE_DIR)) {
> +                FindClose(hFind);
> +                return TerminateTransfer(ci,
> +                    "Could not send RSS_CREATE_DIR msg");
> +            }
> +            if (!SendPacket(ci->socket, ffd.cFileName,
> +                            strlen(ffd.cFileName))) {
> +                FindClose(hFind);
> +                return TerminateTransfer(ci, "Could not send dirname");
> +            }
> +            if (!SendFiles(ci, path)) {
> +                FindClose(hFind);
> +                return 0;
> +            }
> +            if (!SendMsg(ci->socket, RSS_LEAVE_DIR)) {
> +                FindClose(hFind);
> +                return TerminateTransfer(ci,
> +                    "Could not send RSS_LEAVE_DIR msg");
> +            }
> +            PathAppend(path, "..");
> +            PathAppend(path, "..");
> +            _AppendMessage("Returning to dir %s", path);
> +        } else {
> +            // File
> +            PathAppend(path, ffd.cFileName);
> +            _AppendMessage("Client (%s) is downloading %s",
> +                           ci->addr_str, path);
> +            // Make sure the file is readable
> +            fp = fopen(path, "rb");
> +            if (fp) fclose(fp);
> +            else {
> +                AppendMessage("WARNING: could not read file %s", path);
> +                PathAppend(path, "..");
> +                continue;
> +            }
> +            if (!SendMsg(ci->socket, RSS_CREATE_FILE)) {
> +                FindClose(hFind);
> +                return TerminateTransfer(ci,
> +                    "Could not send RSS_CREATE_FILE msg");
> +            }
> +            if (!SendPacket(ci->socket, ffd.cFileName,
> +                            strlen(ffd.cFileName))) {
> +                FindClose(hFind);
> +                return TerminateTransfer(ci, "Could not send filename");
> +            }
> +            if (!SendPacketFromFile(ci->socket, path,
> +                                    ffd.nFileSizeLow, ffd.nFileSizeHigh)) {
> +                FindClose(hFind);
> +                return TerminateTransfer(ci, "Could not send file contents");
> +            }
> +            PathAppend(path, "..");
> +        }
> +    } while (FindNextFile(hFind,&ffd));
> +
> +    if (GetLastError() == ERROR_NO_MORE_FILES) {
> +        FindClose(hFind);
> +        return 1;
> +    } else {
> +        FindClose(hFind);
> +        return TerminateWithError(ci, "FindNextFile failed");
> +    }
> +}
> +
> +int SendThread(client_info *ci)
> +{
> +    char pattern[512];
> +    int msg = 0;
> +
> +    AppendMessage("Client (%s) wants to download files", ci->addr_str);
> +
> +    while (1) {
> +        if (!Receive(ci->socket, (char *)&msg, 4))
> +            return TerminateTransfer(ci, "Could not receive further msgs");
> +
> +        switch (msg) {
> +        case RSS_SET_PATH:
> +            if (!ReceiveStrPacket(ci->socket, pattern, sizeof(pattern)))
> +                return TerminateWithError(ci,
> +                    "RSS_SET_PATH: could not receive path");
> +            AppendMessage("Client (%s) asked for %s", ci->addr_str, pattern);
> +            PathRemoveBackslash(pattern);
> +            if (!SendFiles(ci, pattern))
> +                return 0;
> +            if (!SendMsg(ci->socket, RSS_DONE))
> +                return TerminateTransfer(ci,
> +                    "RSS_SET_PATH: could not send RSS_DONE msg");
> +            break;
> +
> +        default:
> +            return TerminateWithError(ci, "Received unexpected msg");
> +        }
> +    }
> +}
> +
> +DWORD WINAPI TransferThreadEntry(LPVOID client_info_ptr)
> +{
> +    client_info *ci = (client_info *)client_info_ptr;
> +    int msg = 0;
> +
> +    AppendMessage("File transfer server: new client connected (%s)",
> +                  ci->addr_str);
> +
> +    if (!SendMsg(ci->socket, RSS_MAGIC))
> +        return TerminateTransfer(ci, "Could not send greeting message");
> +    if (!Receive(ci->socket, (char *)&msg, 4))
> +        return TerminateTransfer(ci, "Error receiving msg");
> +
> +    if (msg == RSS_UPLOAD)
> +        return ReceiveThread(ci);
> +    else if (msg == RSS_DOWNLOAD)
> +        return SendThread(ci);
> +    return TerminateWithError(ci, "Received unexpected msg");
> +}
> +
> +DWORD WINAPI FileTransferListenThread(LPVOID param)
> +{
> +    client_info _ci, *ci;
> +    HANDLE hThread;
> +
> +    SOCKET ListenSocket = PrepListenSocket(file_transfer_port);
> +
> +    // Inform the user
> +    AppendMessage("File transfer server: waiting for clients to connect...");
> +
> +    while (1) {
> +        int addrlen = sizeof(_ci.addr);
> +        _ci.socket = accept(ListenSocket, (sockaddr *)&_ci.addr,&addrlen);
> +        if (_ci.socket == INVALID_SOCKET) {
> +            if (WSAGetLastError() == WSAEINTR)
> +                break;
> +            else
> +                ExitOnError("accept failed", TRUE);
> +        }
> +
> +        if (!(ci = (client_info *)malloc(sizeof(client_info))))
> +            ExitOnError("Could not allocate client_info struct");
> +        memcpy(ci,&_ci, sizeof(client_info));
> +        sprintf(ci->addr_str, "%s:%d", GetClientIPAddress(ci),
> +                ci->addr.sin_port);
> +        hThread = CreateThread(NULL, 0, TransferThreadEntry, (LPVOID)ci, 0,
> +                               NULL);
> +        if (!hThread)
> +            ExitOnError("Could not create MainTransferThread");
> +    }
> +
> +    return 0;
> +}
> +
> +/*--------------------
> + * WndProc and WinMain
> + *--------------------*/
> +
> +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
> +{
> +    RECT rect;
> +    WSADATA wsaData;
> +
> +    switch (msg) {
> +    case WM_CREATE:
> +        // Create text box
> +        GetClientRect(hwnd,&rect);
> +        hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
> +                                  "EDIT", "",
> +                                  WS_CHILD | WS_VISIBLE | WS_VSCROLL |
> +                                  ES_MULTILINE | ES_AUTOVSCROLL,
> +                                  20, 20,
> +                                  rect.right - 40,
> +                                  rect.bottom - 40,
> +                                  hwnd,
> +                                  NULL,
> +                                  GetModuleHandle(NULL),
> +                                  NULL);
> +        if (!hTextBox)
> +            ExitOnError("Could not create text box");
> +        // Set font
> +        SendMessage(hTextBox, WM_SETFONT,
> +                    (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
> +                    MAKELPARAM(FALSE, 0));
> +        // Set size limit
> +        SendMessage(hTextBox, EM_LIMITTEXT, (WPARAM)TEXTBOX_LIMIT, (LPARAM)0);
> +        // Initialize Winsock
> +        if (WSAStartup(MAKEWORD(2,2),&wsaData))
> +            ExitOnError("Winsock initialization failed");
> +        // Start the listening threads
> +        if (!CreateThread(NULL, 0, ShellListenThread, NULL, 0, NULL))
> +            ExitOnError("Could not create shell server thread");
> +        if (!CreateThread(NULL, 0, FileTransferListenThread, NULL, 0, NULL))
> +            ExitOnError("Could not create file transfer server thread");
> +        break;
> +
> +    case WM_DESTROY:
> +        if (WSACleanup())
> +            ExitOnError("WSACleanup failed");
> +        PostQuitMessage(0);
> +        break;
> +
> +    default:
> +        return DefWindowProc(hwnd, msg, wParam, lParam);
> +    }
> +
> +    return 0;
> +}
> +
> +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
> +                   LPSTR lpCmdLine, int nShowCmd)
> +{
> +    WNDCLASSEX wc;
> +    MSG msg;
> +
> +    if (strlen(lpCmdLine))
> +        sscanf(lpCmdLine, "%d %d",&shell_port,&file_transfer_port);
> +
> +    // Make sure the firewall is disabled
> +    system("netsh firewall set opmode disable");
> +
> +    // Create the window class
> +    wc.cbSize        = sizeof(WNDCLASSEX);
> +    wc.style         = CS_HREDRAW | CS_VREDRAW;
> +    wc.lpfnWndProc   = WndProc;
> +    wc.cbClsExtra    = 0;
> +    wc.cbWndExtra    = 0;
> +    wc.hInstance     = hInstance;
> +    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
> +    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
> +    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
> +    wc.lpszMenuName  = NULL;
> +    wc.lpszClassName = "RemoteShellServerWindowClass";
> +    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
> +
> +    if (!RegisterClassEx(&wc))
> +        ExitOnError("Could not register window class");
> +
> +    // Create the main window
> +    hMainWindow =
> +        CreateWindow("RemoteShellServerWindowClass",
> +                     "Remote Shell Server",
> +                     WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
> +                     20, 20, 600, 400,
> +                     NULL, NULL, hInstance, NULL);
> +    if (!hMainWindow)
> +        ExitOnError("Could not create window");
> +
> +    ShowWindow(hMainWindow, SW_SHOWMINNOACTIVE);
> +    UpdateWindow(hMainWindow);
> +
> +    // Main message loop
> +    while (GetMessage(&msg, NULL, 0, 0)) {
> +        TranslateMessage(&msg);
> +        DispatchMessage(&msg);
> +    }
> +
> +    ExitProcess(0);
> +}


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support
  2010-06-24 11:25 ` [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support Yaniv Kaul
@ 2010-06-24 11:37   ` Lucas Meneghel Rodrigues
  2010-06-24 12:23   ` Michael Goldish
  1 sibling, 0 replies; 5+ messages in thread
From: Lucas Meneghel Rodrigues @ 2010-06-24 11:37 UTC (permalink / raw)
  To: Yaniv Kaul; +Cc: autotest, kvm

On Thu, 2010-06-24 at 14:25 +0300, Yaniv Kaul wrote:
> On 6/24/2010 2:03 PM, Michael Goldish wrote:
> > Enable RSS to send/receive files and directory trees (recursively).
> 
> Are you slowly developing a competitor to STAF 
> (http://staf.sourceforge.net/) ?
> And re. the file transfer protocol, why not use TFTP, instead of 
> creating your own protocol?
> Y.

About this, some time ago, after analyzing all the options at the time,
we found out that having our own, small, maintainable application was a
better choice, since it fits fully into our current model of running
tests.

Also, it's very straightforward to get rss.exe installed and working
under pretty much all windows guests out there. It all boils down to a
matter of convenience.

> > See protocol details in rss.cpp.
> >
> > Signed-off-by: Michael Goldish<mgoldish@redhat.com>
> > ---
> >   client/tests/kvm/deps/rss.cpp | 1429 ++++++++++++++++++++++++++++-------------
> >   1 files changed, 970 insertions(+), 459 deletions(-)
> >
> > diff --git a/client/tests/kvm/deps/rss.cpp b/client/tests/kvm/deps/rss.cpp
> > index 66d9a5b..b2f6049 100644
> > --- a/client/tests/kvm/deps/rss.cpp
> > +++ b/client/tests/kvm/deps/rss.cpp
> > @@ -1,459 +1,970 @@
> > -// Simple remote shell server
> > -// Author: Michael Goldish<mgoldish@redhat.com>
> > -// Much of the code here was adapted from Microsoft code samples.
> > -
> > -// Usage: rss.exe [port]
> > -// If no port is specified the default is 22.
> > -
> > -#define _WIN32_WINNT 0x0500
> > -
> > -#include<windows.h>
> > -#include<winsock2.h>
> > -#include<stdio.h>
> > -
> > -#pragma comment(lib, "ws2_32.lib")
> > -
> > -int port = 22;
> > -
> > -HWND hMainWindow = NULL;
> > -HWND hTextBox = NULL;
> > -
> > -struct client_info {
> > -    SOCKET socket;
> > -    sockaddr_in addr;
> > -    int pid;
> > -    HWND hwnd;
> > -    HANDLE hJob;
> > -    HANDLE hChildOutputRead;
> > -    HANDLE hThreadChildToSocket;
> > -};
> > -
> > -void ExitOnError(char *message, BOOL winsock = 0)
> > -{
> > -    LPVOID system_message;
> > -    char buffer[512];
> > -
> > -    int error_code;
> > -    if (winsock)
> > -        error_code = WSAGetLastError();
> > -    else
> > -        error_code = GetLastError();
> > -
> > -    WSACleanup();
> > -
> > -    FormatMessage(
> > -        FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
> > -        NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
> > -        (LPTSTR)&system_message, 0, NULL);
> > -
> > -    sprintf(buffer,
> > -            "%s!\n"
> > -            "Error code = %d\n"
> > -            "Error message = %s",
> > -            message, error_code, (char *)system_message);
> > -
> > -    MessageBox(hMainWindow, buffer, "Error", MB_OK | MB_ICONERROR);
> > -
> > -    LocalFree(system_message);
> > -    ExitProcess(1);
> > -}
> > -
> > -void AppendMessage(char *message)
> > -{
> > -    int length = GetWindowTextLength(hTextBox);
> > -    SendMessage(hTextBox, EM_SETSEL, (WPARAM)length, (LPARAM)length);
> > -    SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)message);
> > -}
> > -
> > -void FormatStringForPrinting(char *dst, char *src, int size)
> > -{
> > -    int j = 0;
> > -
> > -    for (int i = 0; i<  size&&  src[i]; i++) {
> > -        if (src[i] == '\n') {
> > -            dst[j++] = '\\';
> > -            dst[j++] = 'n';
> > -        } else if (src[i] == '\r') {
> > -            dst[j++] = '\\';
> > -            dst[j++] = 'r';
> > -        } else if (src[i] == '\t') {
> > -            dst[j++] = '\\';
> > -            dst[j++] = 't';
> > -        } else if (src[i] == '\\') {
> > -            dst[j++] = '\\';
> > -            dst[j++] = '\\';
> > -        } else dst[j++] = src[i];
> > -    }
> > -    dst[j] = 0;
> > -}
> > -
> > -char* GetClientIPAddress(client_info *ci)
> > -{
> > -    char *address = inet_ntoa(ci->addr.sin_addr);
> > -    if (address)
> > -        return address;
> > -    else
> > -        return "unknown";
> > -}
> > -
> > -DWORD WINAPI ChildToSocket(LPVOID client_info_ptr)
> > -{
> > -    char buffer[1024], message[1024];
> > -    client_info ci;
> > -    DWORD bytes_read;
> > -    int bytes_sent;
> > -
> > -    memcpy(&ci, client_info_ptr, sizeof(ci));
> > -
> > -    while (1) {
> > -        // Read data from the child's STDOUT/STDERR pipes
> > -        if (!ReadFile(ci.hChildOutputRead,
> > -                      buffer, sizeof(buffer),
> > -&bytes_read, NULL) || !bytes_read) {
> > -            if (GetLastError() == ERROR_BROKEN_PIPE)
> > -                break; // Pipe done -- normal exit path
> > -            else
> > -                ExitOnError("ReadFile failed"); // Something bad happened
> > -        }
> > -        // Send data to the client
> > -        bytes_sent = send(ci.socket, buffer, bytes_read, 0);
> > -        /*
> > -        // Make sure all the data was sent
> > -        if (bytes_sent != bytes_read) {
> > -            sprintf(message,
> > -                    "ChildToSocket: bytes read (%d) != bytes sent (%d)",
> > -                    bytes_read, bytes_sent);
> > -            ExitOnError(message, 1);
> > -        }
> > -        */
> > -    }
> > -
> > -    AppendMessage("Child exited\r\n");
> > -    shutdown(ci.socket, SD_BOTH);
> > -
> > -    return 0;
> > -}
> > -
> > -DWORD WINAPI SocketToChild(LPVOID client_info_ptr)
> > -{
> > -    char buffer[256], formatted_buffer[768];
> > -    char message[1024], client_info_str[256];
> > -    client_info ci;
> > -    DWORD bytes_written;
> > -    int bytes_received;
> > -
> > -    memcpy(&ci, client_info_ptr, sizeof(ci));
> > -
> > -    sprintf(client_info_str, "address %s, port %d",
> > -            GetClientIPAddress(&ci), ci.addr.sin_port);
> > -
> > -    sprintf(message, "New client connected (%s)\r\n", client_info_str);
> > -    AppendMessage(message);
> > -
> > -    while (1) {
> > -        // Receive data from the socket
> > -        ZeroMemory(buffer, sizeof(buffer));
> > -        bytes_received = recv(ci.socket, buffer, sizeof(buffer), 0);
> > -        if (bytes_received<= 0)
> > -            break;
> > -        // Report the data received
> > -        FormatStringForPrinting(formatted_buffer, buffer, sizeof(buffer));
> > -        sprintf(message, "Client (%s) entered text: \"%s\"\r\n",
> > -                client_info_str, formatted_buffer);
> > -        AppendMessage(message);
> > -        // Send the data as a series of WM_CHAR messages to the console window
> > -        for (int i=0; i<bytes_received; i++) {
> > -            SendMessage(ci.hwnd, WM_CHAR, (WPARAM)buffer[i], 0);
> > -            SendMessage(ci.hwnd, WM_SETFOCUS, 0, 0);
> > -        }
> > -    }
> > -
> > -    sprintf(message, "Client disconnected (%s)\r\n", client_info_str);
> > -    AppendMessage(message);
> > -
> > -    // Attempt to terminate the child's process tree:
> > -    // Using taskkill (where available)
> > -    sprintf(buffer, "taskkill /PID %d /T /F", ci.pid);
> > -    system(buffer);
> > -    // .. and using TerminateJobObject()
> > -    TerminateJobObject(ci.hJob, 0);
> > -    // Wait for the ChildToSocket thread to terminate
> > -    WaitForSingleObject(ci.hThreadChildToSocket, 10000);
> > -    // In case the thread refuses to exit -- terminate it
> > -    TerminateThread(ci.hThreadChildToSocket, 0);
> > -    // Close the socket
> > -    shutdown(ci.socket, SD_BOTH);
> > -    closesocket(ci.socket);
> > -
> > -    // Close unnecessary handles
> > -    CloseHandle(ci.hJob);
> > -    CloseHandle(ci.hThreadChildToSocket);
> > -    CloseHandle(ci.hChildOutputRead);
> > -
> > -    AppendMessage("SocketToChild thread exited\r\n");
> > -
> > -    return 0;
> > -}
> > -
> > -void PrepAndLaunchRedirectedChild(client_info *ci,
> > -                                  HANDLE hChildStdOut,
> > -                                  HANDLE hChildStdErr)
> > -{
> > -    PROCESS_INFORMATION pi;
> > -    STARTUPINFO si;
> > -
> > -    // Allocate a new console for the child
> > -    HWND hwnd = GetForegroundWindow();
> > -    FreeConsole();
> > -    AllocConsole();
> > -    ShowWindow(GetConsoleWindow(), SW_HIDE);
> > -    if (hwnd)
> > -        SetForegroundWindow(hwnd);
> > -
> > -    // Set up the start up info struct.
> > -    ZeroMemory(&si, sizeof(STARTUPINFO));
> > -    si.cb = sizeof(STARTUPINFO);
> > -    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
> > -    si.hStdOutput = hChildStdOut;
> > -    si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
> > -    si.hStdError  = hChildStdErr;
> > -    // Use this if you want to hide the child:
> > -    si.wShowWindow = SW_HIDE;
> > -    // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
> > -    // use the wShowWindow flags.
> > -
> > -    // Launch the process that you want to redirect.
> > -    if (!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE,
> > -                       0, NULL, "C:\\",&si,&pi))
> > -        ExitOnError("CreateProcess failed");
> > -
> > -    // Close any unnecessary handles.
> > -    if (!CloseHandle(pi.hThread))
> > -        ExitOnError("CloseHandle failed");
> > -
> > -    // Keep the process ID
> > -    ci->pid = pi.dwProcessId;
> > -    // Assign the process to a newly created JobObject
> > -    ci->hJob = CreateJobObject(NULL, NULL);
> > -    AssignProcessToJobObject(ci->hJob, pi.hProcess);
> > -    // Keep the console window's handle
> > -    ci->hwnd = GetConsoleWindow();
> > -
> > -    // Detach from the child's console
> > -    FreeConsole();
> > -}
> > -
> > -void SpawnSession(client_info *ci)
> > -{
> > -    HANDLE hOutputReadTmp, hOutputRead, hOutputWrite;
> > -    HANDLE hErrorWrite;
> > -    SECURITY_ATTRIBUTES sa;
> > -
> > -    // Set up the security attributes struct.
> > -    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
> > -    sa.lpSecurityDescriptor = NULL;
> > -    sa.bInheritHandle = TRUE;
> > -
> > -    // Create the child output pipe.
> > -    if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa, 0))
> > -        ExitOnError("CreatePipe failed");
> > -
> > -    // Create a duplicate of the output write handle for the std error
> > -    // write handle. This is necessary in case the child application
> > -    // closes one of its std output handles.
> > -    if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
> > -                         GetCurrentProcess(),&hErrorWrite, 0,
> > -                         TRUE, DUPLICATE_SAME_ACCESS))
> > -        ExitOnError("DuplicateHandle failed");
> > -
> > -    // Create new output read handle and the input write handles. Set
> > -    // the Properties to FALSE. Otherwise, the child inherits the
> > -    // properties and, as a result, non-closeable handles to the pipes
> > -    // are created.
> > -    if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
> > -                         GetCurrentProcess(),
> > -&hOutputRead, // Address of new handle.
> > -                         0, FALSE, // Make it uninheritable.
> > -                         DUPLICATE_SAME_ACCESS))
> > -        ExitOnError("DuplicateHandle failed");
> > -
> > -    // Close inheritable copies of the handles you do not want to be
> > -    // inherited.
> > -    if (!CloseHandle(hOutputReadTmp))
> > -        ExitOnError("CloseHandle failed");
> > -
> > -    PrepAndLaunchRedirectedChild(ci, hOutputWrite, hErrorWrite);
> > -
> > -    ci->hChildOutputRead = hOutputRead;
> > -
> > -    // Close pipe handles (do not continue to modify the parent).
> > -    // You need to make sure that no handles to the write end of the
> > -    // output pipe are maintained in this process or else the pipe will
> > -    // not close when the child process exits and the ReadFile will hang.
> > -    if (!CloseHandle(hOutputWrite)) ExitOnError("CloseHandle failed");
> > -    if (!CloseHandle(hErrorWrite)) ExitOnError("CloseHandle failed");
> > -}
> > -
> > -DWORD WINAPI ListenThread(LPVOID param)
> > -{
> > -    WSADATA wsaData;
> > -    SOCKET ListenSocket = INVALID_SOCKET;
> > -    sockaddr_in addr;
> > -    int result, addrlen;
> > -    client_info ci;
> > -    HANDLE hThread;
> > -
> > -    // Initialize Winsock
> > -    result = WSAStartup(MAKEWORD(2,2),&wsaData);
> > -    if (result)
> > -        ExitOnError("Winsock initialization failed");
> > -
> > -    // Create socket
> > -    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> > -    if (ListenSocket == INVALID_SOCKET)
> > -        ExitOnError("Socket creation failed", 1);
> > -
> > -    // Bind the socket
> > -    addr.sin_family = AF_INET;
> > -    addr.sin_addr.s_addr = htonl(INADDR_ANY);
> > -    addr.sin_port = htons(port);
> > -
> > -    result = bind(ListenSocket, (sockaddr *)&addr, sizeof(addr));
> > -    if (result == SOCKET_ERROR)
> > -        ExitOnError("bind failed", 1);
> > -
> > -    // Start listening for incoming connections
> > -    result = listen(ListenSocket, SOMAXCONN);
> > -    if (result == SOCKET_ERROR)
> > -        ExitOnError("listen failed", 1);
> > -
> > -    // Inform the user
> > -    AppendMessage("Waiting for clients to connect...\r\n");
> > -
> > -    while (1) {
> > -        addrlen = sizeof(ci.addr);
> > -        ci.socket = accept(ListenSocket, (sockaddr *)&ci.addr,&addrlen);
> > -        if (ci.socket == INVALID_SOCKET) {
> > -            if (WSAGetLastError() == WSAEINTR)
> > -                break;
> > -            else
> > -                ExitOnError("accept failed", 1);
> > -        }
> > -
> > -        // Under heavy load, spawning cmd.exe might take a while, so tell the
> > -        // client to be patient
> > -        char *message = "Please wait...\r\n";
> > -        send(ci.socket, message, strlen(message), 0);
> > -        // Spawn a new redirected cmd.exe process
> > -        SpawnSession(&ci);
> > -        // Start transferring data from the child process to the client
> > -        hThread = CreateThread(NULL, 0, ChildToSocket, (LPVOID)&ci, 0, NULL);
> > -        if (!hThread)
> > -            ExitOnError("Could not create ChildToSocket thread");
> > -        ci.hThreadChildToSocket = hThread;
> > -        // ... and from the client to the child process
> > -        hThread = CreateThread(NULL, 0, SocketToChild, (LPVOID)&ci, 0, NULL);
> > -        if (!hThread)
> > -            ExitOnError("Could not create SocketToChild thread");
> > -    }
> > -
> > -    return 0;
> > -}
> > -
> > -LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
> > -{
> > -    RECT rect;
> > -    HANDLE hListenThread;
> > -
> > -    switch (msg) {
> > -        case WM_CREATE:
> > -            // Create text box
> > -            GetClientRect(hwnd,&rect);
> > -            hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
> > -                                      "EDIT", "",
> > -                                      WS_CHILD|WS_VISIBLE|WS_VSCROLL|
> > -                                      ES_MULTILINE|ES_AUTOVSCROLL,
> > -                                      20, 20,
> > -                                      rect.right - 40,
> > -                                      rect.bottom - 40,
> > -                                      hwnd,
> > -                                      NULL,
> > -                                      GetModuleHandle(NULL),
> > -                                      NULL);
> > -            if (!hTextBox)
> > -                ExitOnError("Could not create text box");
> > -
> > -            // Set the font
> > -            SendMessage(hTextBox, WM_SETFONT,
> > -                        (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
> > -                        MAKELPARAM(FALSE, 0));
> > -
> > -            // Start the listening thread
> > -            hListenThread =
> > -                CreateThread(NULL, 0, ListenThread, NULL, 0, NULL);
> > -            if (!hListenThread)
> > -                ExitOnError("Could not create server thread");
> > -            break;
> > -
> > -        case WM_DESTROY:
> > -            WSACleanup();
> > -            PostQuitMessage(0);
> > -            break;
> > -
> > -        default:
> > -            return DefWindowProc(hwnd, msg, wParam, lParam);
> > -    }
> > -
> > -    return 0;
> > -}
> > -
> > -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
> > -                   LPSTR lpCmdLine, int nShowCmd)
> > -{
> > -    WNDCLASSEX wc;
> > -    MSG msg;
> > -
> > -    if (strlen(lpCmdLine))
> > -        sscanf(lpCmdLine, "%d",&port);
> > -
> > -    // Make sure the firewall is disabled
> > -    system("netsh firewall set opmode disable");
> > -
> > -    // Create the window class
> > -    wc.cbSize        = sizeof(WNDCLASSEX);
> > -    wc.style         = CS_HREDRAW | CS_VREDRAW;
> > -    wc.lpfnWndProc   = WndProc;
> > -    wc.cbClsExtra    = 0;
> > -    wc.cbWndExtra    = 0;
> > -    wc.hInstance     = hInstance;
> > -    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
> > -    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
> > -    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
> > -    wc.lpszMenuName  = NULL;
> > -    wc.lpszClassName = "RemoteShellServerWindowClass";
> > -    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
> > -
> > -    if (!RegisterClassEx(&wc))
> > -        ExitOnError("Could not register window class");
> > -
> > -    // Create the main window
> > -    hMainWindow =
> > -        CreateWindow("RemoteShellServerWindowClass",
> > -                     "Remote Shell Server",
> > -                     WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
> > -                     20, 20, 500, 300,
> > -                     NULL, NULL, hInstance, NULL);
> > -    if (!hMainWindow)
> > -        ExitOnError("Could not create window");
> > -
> > -    ShowWindow(hMainWindow, SW_SHOWMINNOACTIVE);
> > -    UpdateWindow(hMainWindow);
> > -
> > -    // Main message loop
> > -    while (GetMessage(&msg, NULL, 0, 0)) {
> > -        TranslateMessage(&msg);
> > -        DispatchMessage(&msg);
> > -    }
> > -
> > -    ExitProcess(0);
> > -}
> > +// Simple remote shell server (and file transfer server)
> > +// Author: Michael Goldish<mgoldish@redhat.com>
> > +// Much of the code here was adapted from Microsoft code samples.
> > +
> > +// Usage: rss.exe [shell port] [file transfer port]
> > +// If no shell port is specified the default is 22.
> > +// If no file transfer port is specified the default is 23.
> > +
> > +// Definitions:
> > +// A 'msg' is a 32 bit integer.
> > +// A 'packet' is a 64 bit unsigned integer followed by a string of bytes.
> > +// The 64 bit integer indicates the length of the string.
> > +
> > +// Protocol for file transfers:
> > +//
> > +// When uploading files to the server:
> > +// 1. The client connects.
> > +// 2. The server sends RSS_MAGIC.
> > +// 3. The client sends RSS_SET_PATH, followed by a packet (as defined above)
> > +//    containing the path (in the server's filesystem) where files are to be
> > +//    stored.  The 'current path' is set to the path given.
> > +// Uploading a file (optional, can be repeated many times):
> > +//   4. The client sends RSS_CREATE_FILE, followed by a packet containing the
> > +//      filename (filename only, without a path), followed by a packet
> > +//      containing the file's contents.  The file is stored in the current
> > +//      path.
> > +// Uploading a directory (optional, can be repeated many times):
> > +//   5. The client sends RSS_CREATE_DIR, followed by a packet containing the
> > +//      name of the directory to be created (directory name only, without a
> > +//      path).  The directory is created in the current path.  Then, the
> > +//      'current path' becomes the path of that new directory.
> > +//   6. Steps 4-5 may be repeated to upload files and directories to the new
> > +//      directory.
> > +//   7. The client sends RSS_LEAVE_DIR, and the current path is set to the path
> > +//      of its parent directory (e.g. C:\Foobar ->  C:\).
> > +// 8. The client sends RSS_DONE and waits for a response.
> > +// 9. The server sends RSS_OK to indicate that it's still listening.
> > +// 10. Steps 3-9 are repeated as many times as necessary.
> > +// If a critical error occurs at any time, the server may send RSS_ERROR
> > +// followed by a packet containing an error message, and the connection is
> > +// closed.
> > +//
> > +// When downloading files from the server:
> > +// 1. The client connects.
> > +// 2. The server sends RSS_MAGIC.
> > +// 3. The client sends RSS_SET_PATH, followed by a packet (as defined above)
> > +//    containing a path (in the server's filesystem) or a wildcard pattern
> > +//    indicating the files/directories the client wants to download.
> > +// The server then searches the given path.  For every file found:
> > +//   4. The server sends RSS_CREATE_FILE, followed by a packet containing the
> > +//      filename (filename only, without a path), followed by a packet
> > +//      containing the file's contents.
> > +// For every directory found:
> > +//   5. The server sends RSS_CREATE_DIR, followed by a packet containing the
> > +//      name of the directory to be created (directory name only, without a
> > +//      path).
> > +//   6. Steps 4-5 are repeated as many times as necessary.
> > +//   7. The server sends RSS_LEAVE_DIR.
> > +// 8. The server sends RSS_DONE.
> > +// 9. Steps 3-8 are repeated as many times as necessary.
> > +// If a critical error occurs, the server may send RSS_ERROR followed by a
> > +// packet containing an error message, and the connection is closed.
> > +// RSS_ERROR may only be sent when the client expects a msg.
> > +
> > +#define _WIN32_WINNT 0x0500
> > +
> > +#include<winsock2.h>
> > +#include<windows.h>
> > +#include<stdio.h>
> > +#include<stdarg.h>
> > +#include<shlwapi.h>
> > +
> > +#pragma comment(lib, "ws2_32.lib")
> > +#pragma comment(lib, "shlwapi.lib")
> > +
> > +#define TEXTBOX_LIMIT 32767
> > +
> > +// Constants for file transfer server
> > +#define RSS_MAGIC           0x525353
> > +#define RSS_OK              1
> > +#define RSS_ERROR           2
> > +#define RSS_UPLOAD          3
> > +#define RSS_DOWNLOAD        4
> > +#define RSS_SET_PATH        5
> > +#define RSS_CREATE_FILE     6
> > +#define RSS_CREATE_DIR      7
> > +#define RSS_LEAVE_DIR       8
> > +#define RSS_DONE            9
> > +
> > +// Globals
> > +int shell_port = 22;
> > +int file_transfer_port = 23;
> > +
> > +HWND hMainWindow = NULL;
> > +HWND hTextBox = NULL;
> > +
> > +struct client_info {
> > +    SOCKET socket;
> > +    sockaddr_in addr;
> > +    char addr_str[256];
> > +    int pid;
> > +    HWND hwnd;
> > +    HANDLE hJob;
> > +    HANDLE hChildOutputRead;
> > +    HANDLE hThreadChildToSocket;
> > +};
> > +
> > +void ExitOnError(char *message, BOOL winsock = FALSE)
> > +{
> > +    LPVOID system_message;
> > +    char buffer[512];
> > +    int error_code;
> > +
> > +    if (winsock)
> > +        error_code = WSAGetLastError();
> > +    else
> > +        error_code = GetLastError();
> > +    WSACleanup();
> > +
> > +    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
> > +                  FORMAT_MESSAGE_FROM_SYSTEM,
> > +                  NULL,
> > +                  error_code,
> > +                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
> > +                  (LPTSTR)&system_message,
> > +                  0,
> > +                  NULL);
> > +    sprintf(buffer,
> > +            "%s!\n"
> > +            "Error code = %d\n"
> > +            "Error message = %s",
> > +            message, error_code, (char *)system_message);
> > +    MessageBox(hMainWindow, buffer, "Error", MB_OK | MB_ICONERROR);
> > +
> > +    LocalFree(system_message);
> > +    ExitProcess(1);
> > +}
> > +
> > +void _vAppendMessage(char *message, va_list args)
> > +{
> > +    char str[512] = {0};
> > +
> > +    vsnprintf(str, sizeof(str) - 1, message, args);
> > +    strncat(str, "\r\n", sizeof(str) - 1 - strlen(str));
> > +
> > +    int length = GetWindowTextLength(hTextBox);
> > +    SendMessage(hTextBox, EM_SETSEL, (WPARAM)length, (LPARAM)length);
> > +    SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)str);
> > +}
> > +
> > +// Append text to the textbox
> > +// (if the textbox is full, do nothing)
> > +void _AppendMessage(char *message, ...)
> > +{
> > +    va_list args;
> > +
> > +    va_start(args, message);
> > +    _vAppendMessage(message, args);
> > +    va_end(args);
> > +}
> > +
> > +// Append text to the textbox
> > +// (if the textbox is full or nearly full, remove old text first)
> > +void AppendMessage(char *message, ...)
> > +{
> > +    va_list args;
> > +
> > +    int length = GetWindowTextLength(hTextBox);
> > +    if (length>  TEXTBOX_LIMIT - 512) {
> > +        SendMessage(hTextBox, EM_SETSEL, (WPARAM)0,
> > +                    (LPARAM)(TEXTBOX_LIMIT / 2));
> > +        SendMessage(hTextBox, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)"...");
> > +        if (length == TEXTBOX_LIMIT) _AppendMessage("...");
> > +    }
> > +    va_start(args, message);
> > +    _vAppendMessage(message, args);
> > +    va_end(args);
> > +}
> > +
> > +void FormatStringForPrinting(char *dst, char *src, int size)
> > +{
> > +    int j = 0;
> > +
> > +    for (int i = 0; i<  size&&  src[i]; i++) {
> > +        if (src[i] == '\n') {
> > +            dst[j++] = '\\';
> > +            dst[j++] = 'n';
> > +        } else if (src[i] == '\r') {
> > +            dst[j++] = '\\';
> > +            dst[j++] = 'r';
> > +        } else if (src[i] == '\t') {
> > +            dst[j++] = '\\';
> > +            dst[j++] = 't';
> > +        } else if (src[i] == '\\') {
> > +            dst[j++] = '\\';
> > +            dst[j++] = '\\';
> > +        } else dst[j++] = src[i];
> > +    }
> > +    dst[j] = 0;
> > +}
> > +
> > +char* GetClientIPAddress(client_info *ci)
> > +{
> > +    char *address = inet_ntoa(ci->addr.sin_addr);
> > +    if (address)
> > +        return address;
> > +    else
> > +        return "unknown";
> > +}
> > +
> > +// Read a small chunk of data into a buffer
> > +BOOL Receive(SOCKET socket, char *buffer, int len)
> > +{
> > +    while (len>  0) {
> > +        int bytes_received = recv(socket, buffer, len, 0);
> > +        if (bytes_received<= 0)
> > +            return FALSE;
> > +        buffer += bytes_received;
> > +        len -= bytes_received;
> > +    }
> > +    return TRUE;
> > +}
> > +
> > +// Send data from a buffer
> > +BOOL Send(SOCKET socket, char *buffer, int len)
> > +{
> > +    while (len>  0) {
> > +        int bytes_sent = send(socket, buffer, len, 0);
> > +        if (bytes_sent<= 0)
> > +            return FALSE;
> > +        buffer += bytes_sent;
> > +        len -= bytes_sent;
> > +    }
> > +    return TRUE;
> > +}
> > +
> > +/*-------------
> > + * Shell server
> > + *-------------*/
> > +
> > +DWORD WINAPI ChildToSocket(LPVOID client_info_ptr)
> > +{
> > +    client_info *ci = (client_info *)client_info_ptr;
> > +    char buffer[1024];
> > +    DWORD bytes_read;
> > +
> > +    while (1) {
> > +        // Read data from the child's STDOUT/STDERR pipes
> > +        if (!ReadFile(ci->hChildOutputRead,
> > +                      buffer, sizeof(buffer),
> > +&bytes_read, NULL) || !bytes_read) {
> > +            if (GetLastError() == ERROR_BROKEN_PIPE)
> > +                break; // Pipe done -- normal exit path
> > +            else
> > +                ExitOnError("ReadFile failed"); // Something bad happened
> > +        }
> > +        // Send data to the client
> > +        Send(ci->socket, buffer, bytes_read);
> > +    }
> > +
> > +    AppendMessage("Child exited");
> > +    closesocket(ci->socket);
> > +    return 0;
> > +}
> > +
> > +DWORD WINAPI SocketToChild(LPVOID client_info_ptr)
> > +{
> > +    client_info *ci = (client_info *)client_info_ptr;
> > +    char buffer[256], formatted_buffer[768];
> > +    int bytes_received;
> > +
> > +    AppendMessage("Shell server: new client connected (%s)", ci->addr_str);
> > +
> > +    while (1) {
> > +        // Receive data from the socket
> > +        ZeroMemory(buffer, sizeof(buffer));
> > +        bytes_received = recv(ci->socket, buffer, sizeof(buffer), 0);
> > +        if (bytes_received<= 0)
> > +            break;
> > +        // Report the data received
> > +        FormatStringForPrinting(formatted_buffer, buffer, sizeof(buffer));
> > +        _AppendMessage("Client (%s) entered text: \"%s\"",
> > +                       ci->addr_str, formatted_buffer);
> > +        // Send the data as a series of WM_CHAR messages to the console window
> > +        for (int i = 0; i<  bytes_received; i++) {
> > +            SendMessage(ci->hwnd, WM_CHAR, (WPARAM)buffer[i], 0);
> > +            SendMessage(ci->hwnd, WM_SETFOCUS, 0, 0);
> > +        }
> > +    }
> > +
> > +    AppendMessage("Shell server: client disconnected (%s)", ci->addr_str);
> > +
> > +    // Attempt to terminate the child's process tree:
> > +    // Using taskkill (where available)
> > +    sprintf(buffer, "taskkill /PID %d /T /F", ci->pid);
> > +    system(buffer);
> > +    // .. and using TerminateJobObject()
> > +    TerminateJobObject(ci->hJob, 0);
> > +    // Wait for the ChildToSocket thread to terminate
> > +    WaitForSingleObject(ci->hThreadChildToSocket, 10000);
> > +    // In case the thread refuses to exit, terminate it
> > +    TerminateThread(ci->hThreadChildToSocket, 0);
> > +    // Close the socket
> > +    closesocket(ci->socket);
> > +
> > +    // Free resources
> > +    CloseHandle(ci->hJob);
> > +    CloseHandle(ci->hThreadChildToSocket);
> > +    CloseHandle(ci->hChildOutputRead);
> > +    free(ci);
> > +
> > +    AppendMessage("SocketToChild thread exited");
> > +    return 0;
> > +}
> > +
> > +void PrepAndLaunchRedirectedChild(client_info *ci,
> > +                                  HANDLE hChildStdOut,
> > +                                  HANDLE hChildStdErr)
> > +{
> > +    PROCESS_INFORMATION pi;
> > +    STARTUPINFO si;
> > +
> > +    // Allocate a new console for the child
> > +    HWND hwnd = GetForegroundWindow();
> > +    FreeConsole();
> > +    AllocConsole();
> > +    ShowWindow(GetConsoleWindow(), SW_HIDE);
> > +    if (hwnd)
> > +        SetForegroundWindow(hwnd);
> > +
> > +    // Set up the start up info struct.
> > +    ZeroMemory(&si, sizeof(STARTUPINFO));
> > +    si.cb = sizeof(STARTUPINFO);
> > +    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
> > +    si.hStdOutput = hChildStdOut;
> > +    si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
> > +    si.hStdError  = hChildStdErr;
> > +    // Use this if you want to hide the child:
> > +    si.wShowWindow = SW_HIDE;
> > +    // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
> > +    // use the wShowWindow flags.
> > +
> > +    // Launch the process that you want to redirect.
> > +    if (!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE,
> > +                       0, NULL, "C:\\",&si,&pi))
> > +        ExitOnError("CreateProcess failed");
> > +
> > +    // Close any unnecessary handles.
> > +    if (!CloseHandle(pi.hThread))
> > +        ExitOnError("CloseHandle failed");
> > +
> > +    // Keep the process ID
> > +    ci->pid = pi.dwProcessId;
> > +    // Assign the process to a newly created JobObject
> > +    ci->hJob = CreateJobObject(NULL, NULL);
> > +    AssignProcessToJobObject(ci->hJob, pi.hProcess);
> > +    // Keep the console window's handle
> > +    ci->hwnd = GetConsoleWindow();
> > +
> > +    // Detach from the child's console
> > +    FreeConsole();
> > +}
> > +
> > +void SpawnSession(client_info *ci)
> > +{
> > +    HANDLE hOutputReadTmp, hOutputRead, hOutputWrite;
> > +    HANDLE hErrorWrite;
> > +    SECURITY_ATTRIBUTES sa;
> > +
> > +    // Set up the security attributes struct.
> > +    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
> > +    sa.lpSecurityDescriptor = NULL;
> > +    sa.bInheritHandle = TRUE;
> > +
> > +    // Create the child output pipe.
> > +    if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa, 0))
> > +        ExitOnError("CreatePipe failed");
> > +
> > +    // Create a duplicate of the output write handle for the std error
> > +    // write handle. This is necessary in case the child application
> > +    // closes one of its std output handles.
> > +    if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
> > +                         GetCurrentProcess(),&hErrorWrite, 0,
> > +                         TRUE, DUPLICATE_SAME_ACCESS))
> > +        ExitOnError("DuplicateHandle failed");
> > +
> > +    // Create new output read handle and the input write handles. Set
> > +    // the Properties to FALSE. Otherwise, the child inherits the
> > +    // properties and, as a result, non-closeable handles to the pipes
> > +    // are created.
> > +    if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
> > +                         GetCurrentProcess(),
> > +&hOutputRead, // Address of new handle.
> > +                         0, FALSE, // Make it uninheritable.
> > +                         DUPLICATE_SAME_ACCESS))
> > +        ExitOnError("DuplicateHandle failed");
> > +
> > +    // Close inheritable copies of the handles you do not want to be
> > +    // inherited.
> > +    if (!CloseHandle(hOutputReadTmp))
> > +        ExitOnError("CloseHandle failed");
> > +
> > +    PrepAndLaunchRedirectedChild(ci, hOutputWrite, hErrorWrite);
> > +
> > +    ci->hChildOutputRead = hOutputRead;
> > +
> > +    // Close pipe handles (do not continue to modify the parent).
> > +    // You need to make sure that no handles to the write end of the
> > +    // output pipe are maintained in this process or else the pipe will
> > +    // not close when the child process exits and the ReadFile will hang.
> > +    if (!CloseHandle(hOutputWrite)) ExitOnError("CloseHandle failed");
> > +    if (!CloseHandle(hErrorWrite)) ExitOnError("CloseHandle failed");
> > +}
> > +
> > +SOCKET PrepListenSocket(int port)
> > +{
> > +    sockaddr_in addr;
> > +    linger l;
> > +    int result;
> > +
> > +    // Create socket
> > +    SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> > +    if (ListenSocket == INVALID_SOCKET)
> > +        ExitOnError("Socket creation failed", TRUE);
> > +
> > +    // Enable lingering
> > +    l.l_linger = 10;
> > +    l.l_onoff = 1;
> > +    setsockopt(ListenSocket, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l));
> > +
> > +    // Bind the socket
> > +    addr.sin_family = AF_INET;
> > +    addr.sin_addr.s_addr = htonl(INADDR_ANY);
> > +    addr.sin_port = htons(port);
> > +
> > +    result = bind(ListenSocket, (sockaddr *)&addr, sizeof(addr));
> > +    if (result == SOCKET_ERROR)
> > +        ExitOnError("bind failed", TRUE);
> > +
> > +    // Start listening for incoming connections
> > +    result = listen(ListenSocket, SOMAXCONN);
> > +    if (result == SOCKET_ERROR)
> > +        ExitOnError("listen failed", TRUE);
> > +
> > +    return ListenSocket;
> > +}
> > +
> > +DWORD WINAPI ShellListenThread(LPVOID param)
> > +{
> > +    client_info _ci, *ci;
> > +    HANDLE hThread;
> > +
> > +    SOCKET ListenSocket = PrepListenSocket(shell_port);
> > +
> > +    // Inform the user
> > +    AppendMessage("Shell server: waiting for clients to connect...");
> > +
> > +    while (1) {
> > +        int addrlen = sizeof(_ci.addr);
> > +        _ci.socket = accept(ListenSocket, (sockaddr *)&_ci.addr,&addrlen);
> > +        if (_ci.socket == INVALID_SOCKET) {
> > +            if (WSAGetLastError() == WSAEINTR)
> > +                break;
> > +            else
> > +                ExitOnError("accept failed", TRUE);
> > +        }
> > +
> > +        if (!(ci = (client_info *)malloc(sizeof(client_info))))
> > +            ExitOnError("Could not allocate client_info struct");
> > +        memcpy(ci,&_ci, sizeof(client_info));
> > +        sprintf(ci->addr_str, "%s:%d", GetClientIPAddress(ci),
> > +                ci->addr.sin_port);
> > +
> > +        // Under heavy load, spawning cmd.exe might take a while, so tell the
> > +        // client to be patient
> > +        char *message = "Please wait...\r\n";
> > +        Send(ci->socket, message, strlen(message));
> > +        // Spawn a new redirected cmd.exe process
> > +        SpawnSession(ci);
> > +        // Start transferring data from the child process to the client
> > +        hThread = CreateThread(NULL, 0, ChildToSocket, (LPVOID)ci, 0, NULL);
> > +        if (!hThread)
> > +            ExitOnError("Could not create ChildToSocket thread");
> > +        ci->hThreadChildToSocket = hThread;
> > +        // ... and from the client to the child process
> > +        hThread = CreateThread(NULL, 0, SocketToChild, (LPVOID)ci, 0, NULL);
> > +        if (!hThread)
> > +            ExitOnError("Could not create SocketToChild thread");
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +/*---------------------
> > + * File transfer server
> > + *---------------------*/
> > +
> > +#define BUFSIZE 65536
> > +
> > +// Receive up to 4GB into a file
> > +BOOL ReceiveIntoFile(SOCKET socket, char *filename, DWORD len)
> > +{
> > +    char *buffer = (char *)malloc(BUFSIZE);
> > +    if (!buffer) return FALSE;
> > +
> > +    FILE *fp = fopen(filename, "wb");
> > +    if (!fp) {
> > +        free(buffer);
> > +        return FALSE;
> > +    }
> > +
> > +    while (len>  0) {
> > +        int bytes_received = recv(socket, buffer, min(BUFSIZE, len), 0);
> > +        if (bytes_received<= 0)
> > +            break;
> > +        if (fwrite(buffer, bytes_received, 1, fp)<  1)
> > +            break;
> > +        len -= bytes_received;
> > +    }
> > +
> > +    fclose(fp);
> > +    free(buffer);
> > +    return len == 0;
> > +}
> > +
> > +// Send data from a file (unlimited size)
> > +BOOL SendFromFile(SOCKET socket, char *filename)
> > +{
> > +    char *buffer = (char *)malloc(BUFSIZE);
> > +    if (!buffer) return FALSE;
> > +
> > +    FILE *fp = fopen(filename, "rb");
> > +    if (!fp) {
> > +        free(buffer);
> > +        return FALSE;
> > +    }
> > +
> > +    while (!feof(fp)) {
> > +        int bytes_read = fread(buffer, 1, BUFSIZE, fp);
> > +        if (bytes_read<= 0)
> > +            break;
> > +        if (!Send(socket, buffer, bytes_read)) {
> > +            fclose(fp);
> > +            free(buffer);
> > +            return FALSE;
> > +        }
> > +    }
> > +
> > +    BOOL success = feof(fp);
> > +    fclose(fp);
> > +    free(buffer);
> > +    return success;
> > +}
> > +
> > +BOOL ReceivePacket(SOCKET socket, char *buffer, DWORD max_size)
> > +{
> > +    DWORD packet_size = 0, padding;
> > +
> > +    if (!Receive(socket, (char *)&packet_size, 4)) return FALSE;
> > +    if (!Receive(socket, (char *)&padding, 4)) return FALSE;
> > +    if (packet_size>  max_size) return FALSE;
> > +    return Receive(socket, buffer, packet_size);
> > +}
> > +
> > +BOOL ReceiveStrPacket(SOCKET socket, char *buffer, DWORD max_size)
> > +{
> > +    memset(buffer, 0, max_size);
> > +    return ReceivePacket(socket, buffer, max_size - 1);
> > +}
> > +
> > +BOOL SendPacket(SOCKET socket, char *buffer, DWORD len)
> > +{
> > +    DWORD padding = 0;
> > +
> > +    if (!Send(socket, (char *)&len, 4)) return FALSE;
> > +    if (!Send(socket, (char *)&padding, 4)) return FALSE;
> > +    return Send(socket, buffer, len);
> > +}
> > +
> > +BOOL ReceivePacketIntoFile(SOCKET socket, char *filename)
> > +{
> > +    DWORD packet_size = 0, padding;
> > +
> > +    if (!Receive(socket, (char *)&packet_size, 4)) return FALSE;
> > +    if (!Receive(socket, (char *)&padding, 4)) return FALSE;
> > +    return ReceiveIntoFile(socket, filename, packet_size);
> > +}
> > +
> > +BOOL SendPacketFromFile(SOCKET socket, char *filename,
> > +                        DWORD size_low, DWORD size_high)
> > +{
> > +    if (!Send(socket, (char *)&size_low, 4)) return FALSE;
> > +    if (!Send(socket, (char *)&size_high, 4)) return FALSE;
> > +    return SendFromFile(socket, filename);
> > +}
> > +
> > +BOOL SendMsg(SOCKET socket, int msg)
> > +{
> > +    return Send(socket, (char *)&msg, 4);
> > +}
> > +
> > +int TerminateTransfer(client_info *ci, char *message)
> > +{
> > +    AppendMessage(message);
> > +    AppendMessage("File transfer server: client disconnected (%s)",
> > +                  ci->addr_str);
> > +    closesocket(ci->socket);
> > +    free(ci);
> > +    return 0;
> > +}
> > +
> > +int TerminateWithError(client_info *ci, char *message)
> > +{
> > +    SendMsg(ci->socket, RSS_ERROR);
> > +    SendPacket(ci->socket, message, strlen(message));
> > +    return TerminateTransfer(ci, message);
> > +}
> > +
> > +int ReceiveThread(client_info *ci)
> > +{
> > +    char path[512], filename[512];
> > +    int msg = 0;
> > +
> > +    AppendMessage("Client (%s) wants to upload files", ci->addr_str);
> > +
> > +    while (1) {
> > +        if (!Receive(ci->socket, (char *)&msg, 4))
> > +            return TerminateTransfer(ci, "Could not receive further msgs");
> > +
> > +        switch (msg) {
> > +        case RSS_SET_PATH:
> > +            if (!ReceiveStrPacket(ci->socket, path, sizeof(path)))
> > +                return TerminateWithError(ci,
> > +                    "RSS_SET_PATH: could not receive path");
> > +            _AppendMessage("Client (%s) set path to %s", ci->addr_str, path);
> > +            break;
> > +
> > +        case RSS_CREATE_FILE:
> > +            if (!ReceiveStrPacket(ci->socket, filename, sizeof(filename)))
> > +                return TerminateWithError(ci,
> > +                    "RSS_CREATE_FILE: could not receive filename");
> > +            if (PathIsDirectory(path))
> > +                PathAppend(path, filename);
> > +            _AppendMessage("Client (%s) is uploading %s", ci->addr_str, path);
> > +            if (!ReceivePacketIntoFile(ci->socket, path))
> > +                return TerminateWithError(ci,
> > +                    "RSS_CREATE_FILE: error receiving or writing file "
> > +                    "contents");
> > +            PathAppend(path, "..");
> > +            break;
> > +
> > +        case RSS_CREATE_DIR:
> > +            if (!ReceiveStrPacket(ci->socket, filename, sizeof(filename)))
> > +                return TerminateWithError(ci,
> > +                    "RSS_CREATE_DIR: could not receive dirname");
> > +            if (PathIsDirectory(path))
> > +                PathAppend(path, filename);
> > +            _AppendMessage("Entering dir %s", path);
> > +            if (PathFileExists(path)) {
> > +                if (!PathIsDirectory(path))
> > +                    return TerminateWithError(ci,
> > +                        "RSS_CREATE_DIR: path exists and is not a directory");
> > +            } else {
> > +                if (!CreateDirectory(path, NULL))
> > +                    return TerminateWithError(ci,
> > +                        "RSS_CREATE_DIR: could not create directory");
> > +            }
> > +            break;
> > +
> > +        case RSS_LEAVE_DIR:
> > +            PathAppend(path, "..");
> > +            _AppendMessage("Returning to dir %s", path);
> > +            break;
> > +
> > +        case RSS_DONE:
> > +            if (!SendMsg(ci->socket, RSS_OK))
> > +                return TerminateTransfer(ci,
> > +                    "RSS_DONE: could not send OK msg");
> > +            break;
> > +
> > +        default:
> > +            return TerminateWithError(ci, "Received unexpected msg");
> > +        }
> > +    }
> > +}
> > +
> > +// Given a path or a pattern with wildcards, send files or directory trees to
> > +// the client
> > +int SendFiles(client_info *ci, char *pattern)
> > +{
> > +    char path[MAX_PATH];
> > +    WIN32_FIND_DATA ffd;
> > +    FILE *fp;
> > +
> > +    HANDLE hFind = FindFirstFile(pattern,&ffd);
> > +    if (hFind == INVALID_HANDLE_VALUE) {
> > +        // If a weird error occurred (like failure to list directory contents
> > +        // due to insufficient permissions) print a warning and continue.
> > +        if (GetLastError() != ERROR_FILE_NOT_FOUND)
> > +            AppendMessage("WARNING: FindFirstFile failed on pattern %s",
> > +                          pattern);
> > +        return 1;
> > +    }
> > +
> > +    strncpy(path, pattern, sizeof(path) - 1);
> > +    PathAppend(path, "..");
> > +
> > +    do {
> > +        if (ffd.dwFileAttributes&  FILE_ATTRIBUTE_REPARSE_POINT)
> > +            continue;
> > +        if (ffd.dwFileAttributes&  FILE_ATTRIBUTE_DIRECTORY) {
> > +            // Directory
> > +            if (!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, ".."))
> > +                continue;
> > +            PathAppend(path, ffd.cFileName);
> > +            _AppendMessage("Entering dir %s", path);
> > +            PathAppend(path, "*");
> > +            if (!SendMsg(ci->socket, RSS_CREATE_DIR)) {
> > +                FindClose(hFind);
> > +                return TerminateTransfer(ci,
> > +                    "Could not send RSS_CREATE_DIR msg");
> > +            }
> > +            if (!SendPacket(ci->socket, ffd.cFileName,
> > +                            strlen(ffd.cFileName))) {
> > +                FindClose(hFind);
> > +                return TerminateTransfer(ci, "Could not send dirname");
> > +            }
> > +            if (!SendFiles(ci, path)) {
> > +                FindClose(hFind);
> > +                return 0;
> > +            }
> > +            if (!SendMsg(ci->socket, RSS_LEAVE_DIR)) {
> > +                FindClose(hFind);
> > +                return TerminateTransfer(ci,
> > +                    "Could not send RSS_LEAVE_DIR msg");
> > +            }
> > +            PathAppend(path, "..");
> > +            PathAppend(path, "..");
> > +            _AppendMessage("Returning to dir %s", path);
> > +        } else {
> > +            // File
> > +            PathAppend(path, ffd.cFileName);
> > +            _AppendMessage("Client (%s) is downloading %s",
> > +                           ci->addr_str, path);
> > +            // Make sure the file is readable
> > +            fp = fopen(path, "rb");
> > +            if (fp) fclose(fp);
> > +            else {
> > +                AppendMessage("WARNING: could not read file %s", path);
> > +                PathAppend(path, "..");
> > +                continue;
> > +            }
> > +            if (!SendMsg(ci->socket, RSS_CREATE_FILE)) {
> > +                FindClose(hFind);
> > +                return TerminateTransfer(ci,
> > +                    "Could not send RSS_CREATE_FILE msg");
> > +            }
> > +            if (!SendPacket(ci->socket, ffd.cFileName,
> > +                            strlen(ffd.cFileName))) {
> > +                FindClose(hFind);
> > +                return TerminateTransfer(ci, "Could not send filename");
> > +            }
> > +            if (!SendPacketFromFile(ci->socket, path,
> > +                                    ffd.nFileSizeLow, ffd.nFileSizeHigh)) {
> > +                FindClose(hFind);
> > +                return TerminateTransfer(ci, "Could not send file contents");
> > +            }
> > +            PathAppend(path, "..");
> > +        }
> > +    } while (FindNextFile(hFind,&ffd));
> > +
> > +    if (GetLastError() == ERROR_NO_MORE_FILES) {
> > +        FindClose(hFind);
> > +        return 1;
> > +    } else {
> > +        FindClose(hFind);
> > +        return TerminateWithError(ci, "FindNextFile failed");
> > +    }
> > +}
> > +
> > +int SendThread(client_info *ci)
> > +{
> > +    char pattern[512];
> > +    int msg = 0;
> > +
> > +    AppendMessage("Client (%s) wants to download files", ci->addr_str);
> > +
> > +    while (1) {
> > +        if (!Receive(ci->socket, (char *)&msg, 4))
> > +            return TerminateTransfer(ci, "Could not receive further msgs");
> > +
> > +        switch (msg) {
> > +        case RSS_SET_PATH:
> > +            if (!ReceiveStrPacket(ci->socket, pattern, sizeof(pattern)))
> > +                return TerminateWithError(ci,
> > +                    "RSS_SET_PATH: could not receive path");
> > +            AppendMessage("Client (%s) asked for %s", ci->addr_str, pattern);
> > +            PathRemoveBackslash(pattern);
> > +            if (!SendFiles(ci, pattern))
> > +                return 0;
> > +            if (!SendMsg(ci->socket, RSS_DONE))
> > +                return TerminateTransfer(ci,
> > +                    "RSS_SET_PATH: could not send RSS_DONE msg");
> > +            break;
> > +
> > +        default:
> > +            return TerminateWithError(ci, "Received unexpected msg");
> > +        }
> > +    }
> > +}
> > +
> > +DWORD WINAPI TransferThreadEntry(LPVOID client_info_ptr)
> > +{
> > +    client_info *ci = (client_info *)client_info_ptr;
> > +    int msg = 0;
> > +
> > +    AppendMessage("File transfer server: new client connected (%s)",
> > +                  ci->addr_str);
> > +
> > +    if (!SendMsg(ci->socket, RSS_MAGIC))
> > +        return TerminateTransfer(ci, "Could not send greeting message");
> > +    if (!Receive(ci->socket, (char *)&msg, 4))
> > +        return TerminateTransfer(ci, "Error receiving msg");
> > +
> > +    if (msg == RSS_UPLOAD)
> > +        return ReceiveThread(ci);
> > +    else if (msg == RSS_DOWNLOAD)
> > +        return SendThread(ci);
> > +    return TerminateWithError(ci, "Received unexpected msg");
> > +}
> > +
> > +DWORD WINAPI FileTransferListenThread(LPVOID param)
> > +{
> > +    client_info _ci, *ci;
> > +    HANDLE hThread;
> > +
> > +    SOCKET ListenSocket = PrepListenSocket(file_transfer_port);
> > +
> > +    // Inform the user
> > +    AppendMessage("File transfer server: waiting for clients to connect...");
> > +
> > +    while (1) {
> > +        int addrlen = sizeof(_ci.addr);
> > +        _ci.socket = accept(ListenSocket, (sockaddr *)&_ci.addr,&addrlen);
> > +        if (_ci.socket == INVALID_SOCKET) {
> > +            if (WSAGetLastError() == WSAEINTR)
> > +                break;
> > +            else
> > +                ExitOnError("accept failed", TRUE);
> > +        }
> > +
> > +        if (!(ci = (client_info *)malloc(sizeof(client_info))))
> > +            ExitOnError("Could not allocate client_info struct");
> > +        memcpy(ci,&_ci, sizeof(client_info));
> > +        sprintf(ci->addr_str, "%s:%d", GetClientIPAddress(ci),
> > +                ci->addr.sin_port);
> > +        hThread = CreateThread(NULL, 0, TransferThreadEntry, (LPVOID)ci, 0,
> > +                               NULL);
> > +        if (!hThread)
> > +            ExitOnError("Could not create MainTransferThread");
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +/*--------------------
> > + * WndProc and WinMain
> > + *--------------------*/
> > +
> > +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
> > +{
> > +    RECT rect;
> > +    WSADATA wsaData;
> > +
> > +    switch (msg) {
> > +    case WM_CREATE:
> > +        // Create text box
> > +        GetClientRect(hwnd,&rect);
> > +        hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
> > +                                  "EDIT", "",
> > +                                  WS_CHILD | WS_VISIBLE | WS_VSCROLL |
> > +                                  ES_MULTILINE | ES_AUTOVSCROLL,
> > +                                  20, 20,
> > +                                  rect.right - 40,
> > +                                  rect.bottom - 40,
> > +                                  hwnd,
> > +                                  NULL,
> > +                                  GetModuleHandle(NULL),
> > +                                  NULL);
> > +        if (!hTextBox)
> > +            ExitOnError("Could not create text box");
> > +        // Set font
> > +        SendMessage(hTextBox, WM_SETFONT,
> > +                    (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
> > +                    MAKELPARAM(FALSE, 0));
> > +        // Set size limit
> > +        SendMessage(hTextBox, EM_LIMITTEXT, (WPARAM)TEXTBOX_LIMIT, (LPARAM)0);
> > +        // Initialize Winsock
> > +        if (WSAStartup(MAKEWORD(2,2),&wsaData))
> > +            ExitOnError("Winsock initialization failed");
> > +        // Start the listening threads
> > +        if (!CreateThread(NULL, 0, ShellListenThread, NULL, 0, NULL))
> > +            ExitOnError("Could not create shell server thread");
> > +        if (!CreateThread(NULL, 0, FileTransferListenThread, NULL, 0, NULL))
> > +            ExitOnError("Could not create file transfer server thread");
> > +        break;
> > +
> > +    case WM_DESTROY:
> > +        if (WSACleanup())
> > +            ExitOnError("WSACleanup failed");
> > +        PostQuitMessage(0);
> > +        break;
> > +
> > +    default:
> > +        return DefWindowProc(hwnd, msg, wParam, lParam);
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
> > +                   LPSTR lpCmdLine, int nShowCmd)
> > +{
> > +    WNDCLASSEX wc;
> > +    MSG msg;
> > +
> > +    if (strlen(lpCmdLine))
> > +        sscanf(lpCmdLine, "%d %d",&shell_port,&file_transfer_port);
> > +
> > +    // Make sure the firewall is disabled
> > +    system("netsh firewall set opmode disable");
> > +
> > +    // Create the window class
> > +    wc.cbSize        = sizeof(WNDCLASSEX);
> > +    wc.style         = CS_HREDRAW | CS_VREDRAW;
> > +    wc.lpfnWndProc   = WndProc;
> > +    wc.cbClsExtra    = 0;
> > +    wc.cbWndExtra    = 0;
> > +    wc.hInstance     = hInstance;
> > +    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
> > +    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
> > +    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
> > +    wc.lpszMenuName  = NULL;
> > +    wc.lpszClassName = "RemoteShellServerWindowClass";
> > +    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
> > +
> > +    if (!RegisterClassEx(&wc))
> > +        ExitOnError("Could not register window class");
> > +
> > +    // Create the main window
> > +    hMainWindow =
> > +        CreateWindow("RemoteShellServerWindowClass",
> > +                     "Remote Shell Server",
> > +                     WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
> > +                     20, 20, 600, 400,
> > +                     NULL, NULL, hInstance, NULL);
> > +    if (!hMainWindow)
> > +        ExitOnError("Could not create window");
> > +
> > +    ShowWindow(hMainWindow, SW_SHOWMINNOACTIVE);
> > +    UpdateWindow(hMainWindow);
> > +
> > +    // Main message loop
> > +    while (GetMessage(&msg, NULL, 0, 0)) {
> > +        TranslateMessage(&msg);
> > +        DispatchMessage(&msg);
> > +    }
> > +
> > +    ExitProcess(0);
> > +}
> 
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support
  2010-06-24 11:25 ` [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support Yaniv Kaul
  2010-06-24 11:37   ` Lucas Meneghel Rodrigues
@ 2010-06-24 12:23   ` Michael Goldish
  1 sibling, 0 replies; 5+ messages in thread
From: Michael Goldish @ 2010-06-24 12:23 UTC (permalink / raw)
  To: Yaniv Kaul; +Cc: autotest, kvm

On 06/24/2010 02:25 PM, Yaniv Kaul wrote:
>  On 6/24/2010 2:03 PM, Michael Goldish wrote:
>> Enable RSS to send/receive files and directory trees (recursively).
> 
> Are you slowly developing a competitor to STAF
> (http://staf.sourceforge.net/) ?

I think STAF offers much more stuff.  However, rss is well suited to
freshly-installed guests (no setup whatsoever) and offers an interactive
command shell, which is useful for our tests.

> And re. the file transfer protocol, why not use TFTP, instead of
> creating your own protocol?
> Y.

Can TFTP send/receive directory trees recursively?
Also, I got the impression that TFTP isn't as trivial as it sounds, but
I might be wrong.  If it can do what we need, maybe it's a good idea to
give it a try.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2010-06-24 12:23 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-06-24 11:03 [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support Michael Goldish
2010-06-24 11:03 ` [KVM-AUTOTEST PATCH] [RFC] KVM test: add python client for rss file transfer services Michael Goldish
2010-06-24 11:25 ` [KVM-AUTOTEST PATCH] [RFC] KVM test: rss.cpp: add file transfer support Yaniv Kaul
2010-06-24 11:37   ` Lucas Meneghel Rodrigues
2010-06-24 12:23   ` Michael Goldish

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox