From 4c6087fe9db057e6f93fa79118ffcd4ac0cd90dd Mon Sep 17 00:00:00 2001 From: nihonium Date: Thu, 28 Nov 2024 01:46:38 +0300 Subject: [PATCH] Initial commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Первая версия программы - реализовано создание дочернего процесса cmd.exe, создание нужных пайпов и черновая реализация работы по сети --- CMakeLists.txt | 21 +++ CMakePresets.json | 61 +++++++++ main.c | 33 +++++ server.c | 317 ++++++++++++++++++++++++++++++++++++++++++++++ server.h | 22 ++++ 5 files changed, 454 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 main.c create mode 100644 server.c create mode 100644 server.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5f3049b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +# CMakeList.txt: проект CMake для shell-server; включите исходный код и определения, +# укажите здесь логику для конкретного проекта. +# +cmake_minimum_required (VERSION 3.8) + +# Включение горячей перезагрузки для компиляторов MSVC, если поддерживается. +if (POLICY CMP0141) + cmake_policy(SET CMP0141 NEW) + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") +endif() + +project ("shell-server") + +# Добавьте источник в исполняемый файл этого проекта. +add_executable (shell-server "main.c" "server.c" "server.h") + +if (CMAKE_VERSION VERSION_GREATER 3.12) + set_property(TARGET shell-server PROPERTY CXX_STANDARD 20) +endif() + +# TODO: Добавьте тесты и целевые объекты, если это необходимо. diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..abf4065 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,61 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "windows-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x64-debug", + "displayName": "x64 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "x64-release", + "displayName": "x64 Release", + "inherits": "x64-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "x86-debug", + "displayName": "x86 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x86", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "x86-release", + "displayName": "x86 Release", + "inherits": "x86-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ] +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..c422661 --- /dev/null +++ b/main.c @@ -0,0 +1,33 @@ +#include +#include + +#include "server.h" + +void PrintHelpMessage() { + printf("Usage error: I know 3 arguments \n\t\"install\"\n\t\"delete\"\n\t\"app\"\n"); +} + +int _tmain(int argc, TCHAR *argv[]) { + printf("\n->Start of parent execution.\n"); + if (argc == 2) { + if (lstrcmpi(argv[1], TEXT("-c")) == 0) { + return 0; + } + else if (lstrcmpi(argv[1], TEXT("-s")) == 0) { + StartShellServer(); + return 0; + } + else { + PrintHelpMessage(); + return -1; + } + + } + else { + PrintHelpMessage(); + return -1; + } + + return 0; +} + diff --git a/server.c b/server.c new file mode 100644 index 0000000..53b6759 --- /dev/null +++ b/server.c @@ -0,0 +1,317 @@ +#include "server.h" + +#define BUFSIZE 4096 +#define DEFAULT_PORT "50113" +#define DEFAULT_BUFLEN 512 + +// Handles for child process STDIN and STDOUT +HANDLE g_hChildStd_IN_Rd = NULL; +HANDLE g_hChildStd_IN_Wr = NULL; +HANDLE g_hChildStd_OUT_Rd = NULL; +HANDLE g_hChildStd_OUT_Wr = NULL; + +SOCKET ClientSocket = INVALID_SOCKET; + +//HANDLE g_hInputFile = NULL; + +void StartShellServer() { + printf("\n->Start of shell server execution.\n"); + + CreatePipes(); + CreateSocket(); + + // Create the child process. + CreateChildProcess(); + + // TODO: split into threads + WriteToPipe(); + // Read from pipe that is the standard output for child process. + ReadFromPipe(); + + printf("\n->End of shell server execution.\n"); + + // The remaining open handles are cleaned up when this process terminates. + // To avoid resource leaks in a larger application, close handles explicitly. + +} + +void CreatePipes() { + SECURITY_ATTRIBUTES saAttr; + + // Set the bInheritHandle flag so pipe handles are inherited. + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // Create a pipe for the child process's STDOUT. + + if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) + ErrorExit(TEXT("StdoutRd CreatePipe")); + + // Ensure the read handle to the pipe for STDOUT is not inherited. + + if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) + ErrorExit(TEXT("Stdout SetHandleInformation")); + + // Create a pipe for the child process's STDIN. + + if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) + ErrorExit(TEXT("Stdin CreatePipe")); + + // Ensure the write handle to the pipe for STDIN is not inherited. + + if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) + ErrorExit(TEXT("Stdin SetHandleInformation")); +} + +// TODO: to review +void CreateSocket() { + WSADATA wsaData; + struct addrinfo* result = NULL, * ptr = NULL, hints; + SOCKET ListenSocket = INVALID_SOCKET; + + int iResult; + + // Initialize Winsock + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + printf("WSAStartup failed: %d\n", iResult); + return 1; + } + + // Creating socket + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + // Resolve the local address and port to be used by the server + iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); + if (iResult != 0) { + printf("getaddrinfo failed: %d\n", iResult); + WSACleanup(); + return 1; + } + + ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + + if (ListenSocket == INVALID_SOCKET) { + printf("Error at socket(): %ld\n", WSAGetLastError()); + freeaddrinfo(result); + WSACleanup(); + return 1; + } + + // Binding socket + // Setup the TCP listening socket + iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen); + if (iResult == SOCKET_ERROR) { + printf("bind failed with error: %d\n", WSAGetLastError()); + freeaddrinfo(result); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + // No longer needed + freeaddrinfo(result); + + // Listening socket + if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) { + printf("Listen failed with error: %ld\n", WSAGetLastError()); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + + // Accepting a connection + ClientSocket = INVALID_SOCKET; + + // Accept a client socket + ClientSocket = accept(ListenSocket, NULL, NULL); + if (ClientSocket == INVALID_SOCKET) { + printf("accept failed: %d\n", WSAGetLastError()); + closesocket(ListenSocket); + WSACleanup(); + return 1; + } + // No longer needed + closesocket(ListenSocket); +} + +void CreateChildProcess() +// Create a child process that uses the previously created pipes for STDIN and STDOUT. +{ + TCHAR szCmdline[] = TEXT("C:\\Windows\\System32\\cmd.exe"); + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + BOOL bSuccess = FALSE; + + // Set up members of the PROCESS_INFORMATION structure. + + ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); + + // Set up members of the STARTUPINFO structure. + // This structure specifies the STDIN and STDOUT handles for redirection. + + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = g_hChildStd_OUT_Wr; + siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; + siStartInfo.hStdInput = g_hChildStd_IN_Rd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + // Create the child process. + + bSuccess = CreateProcess(NULL, + szCmdline, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo); // receives PROCESS_INFORMATION + + // If an error occurs, exit the application. + if (!bSuccess) + ErrorExit(TEXT("CreateProcess")); + else + { + // Close handles to the child process and its primary thread. + // Some applications might keep these handles to monitor the status + // of the child process, for example. + + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + + // Close handles to the stdin and stdout pipes no longer needed by the child process. + // If they are not explicitly closed, there is no way to recognize that the child process has ended. + + CloseHandle(g_hChildStd_OUT_Wr); + CloseHandle(g_hChildStd_IN_Rd); + } +} + +void WriteToPipe(void) + +// Read from a file and write its contents to the pipe for the child's STDIN. +// Stop when there is no more data. +{ + DWORD dwRead, dwWritten; + //CHAR chBuf[BUFSIZE] = "dir\r\n"; + BOOL bSuccess = FALSE; + int i = 0; + + char recvbuf[DEFAULT_BUFLEN]; + int iResult, iSendResult; + int recvbuflen = DEFAULT_BUFLEN; + + + do + { + iResult = recv(ClientSocket, recvbuf, recvbuflen, 0); + if (iResult > 0) { + printf("Bytes received: %d\n", iResult); + + // Echo the buffer back to the sender + iSendResult = send(ClientSocket, recvbuf, iResult, 0); + if (iSendResult == SOCKET_ERROR) { + printf("send failed: %d\n", WSAGetLastError()); + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + printf("Bytes sent: %d\n", iSendResult); + } + else if (iResult == 0) + printf("Connection closing...\n"); + else { + printf("recv failed: %d\n", WSAGetLastError()); + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + + //dwRead = strlen(chBuf) + 1; + bSuccess = WriteFile(g_hChildStd_IN_Wr, recvbuf, iResult, &dwWritten, NULL); + if (!bSuccess) break; + //break; + //dwRead = strlen(chBuf) + 1; + //bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL); + //if (!bSuccess) break; + ReadFromPipe(); + } while (iResult > 0); + + // Close the pipe handle so the child process stops reading. + + if (!CloseHandle(g_hChildStd_IN_Wr)) + ErrorExit(TEXT("StdInWr CloseHandle")); +} + +void ReadFromPipe(void) + +// Read output from the child process's pipe for STDOUT +// and write to the parent process's pipe for STDOUT. +// Stop when there is no more data. +{ + DWORD dwRead, dwWritten; + CHAR chBuf[BUFSIZE]; + BOOL bSuccess = FALSE; + HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + + int iSendResult; + + for (;;) + { + bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); + if (!bSuccess || dwRead == 0) break; + + iSendResult = send(ClientSocket, chBuf, dwRead, 0); + if (iSendResult == SOCKET_ERROR) { + printf("send failed: %d\n", WSAGetLastError()); + closesocket(ClientSocket); + WSACleanup(); + return 1; + } + // Write to console + bSuccess = WriteFile(hParentStdOut, chBuf, + dwRead, &dwWritten, NULL); + if (!bSuccess) break; + break; + } +} + +void ErrorExit(PCTSTR lpszFunction) + +// Format a readable error message, display a message box, +// and exit from the application. +{ + LPVOID lpMsgBuf; + LPVOID lpDisplayBuf; + DWORD dw = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, NULL); + + lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, + (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); + StringCchPrintf((LPTSTR)lpDisplayBuf, + LocalSize(lpDisplayBuf) / sizeof(TCHAR), + TEXT("%s failed with error %d: %s"), + lpszFunction, dw, lpMsgBuf); + MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); + + LocalFree(lpMsgBuf); + LocalFree(lpDisplayBuf); + ExitProcess(1); +} \ No newline at end of file diff --git a/server.h b/server.h new file mode 100644 index 0000000..249948d --- /dev/null +++ b/server.h @@ -0,0 +1,22 @@ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#pragma comment(lib, "Ws2_32.lib") + +#include +#include +#include +#include + +#include +#include + +// Defined functions +void StartShellServer(); +void CreatePipes(); +void CreateChildProcess(void); +void WriteToPipe(void); +void ReadFromPipe(void); +void ErrorExit(PCTSTR); +void CreateSocket(); \ No newline at end of file