diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f3049b..5c0dc9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ endif() project ("shell-server") # Добавьте источник в исполняемый файл этого проекта. -add_executable (shell-server "main.c" "server.c" "server.h") +add_executable (shell-server "main.c" "server.c" "server.h" "client.c" "client.h") if (CMAKE_VERSION VERSION_GREATER 3.12) set_property(TARGET shell-server PROPERTY CXX_STANDARD 20) diff --git a/main.c b/main.c index c422661..e7477bb 100644 --- a/main.c +++ b/main.c @@ -3,31 +3,38 @@ #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"); +int _tmain(int argc, char *argv[]) { if (argc == 2) { - if (lstrcmpi(argv[1], TEXT("-c")) == 0) { - return 0; - } - else if (lstrcmpi(argv[1], TEXT("-s")) == 0) { + if (lstrcmpi(argv[1], TEXT("-s")) == 0) { StartShellServer(); return 0; } else { - PrintHelpMessage(); - return -1; + goto help_message; } } + else if (argc == 3) { + if (lstrcmpi(argv[1], TEXT("-c")) == 0) { + StartShellClient(argv[2]); + return 0; + } + // TODO: implement service + else if (lstrcmpi(argv[1], TEXT("-s")) == 0 && lstrcmpi(argv[2], TEXT("-service")) == 0) { + // CreateService + return 0; + } + else { + goto help_message; + } + } else { - PrintHelpMessage(); - return -1; + goto help_message; } return 0; +help_message: + printf("Wrong usage\nUsage: %s [-c {remote ip} | -s [-service]]\n", argv[0]); + return -1; } diff --git a/server.c b/server.c index 53b6759..d406ad8 100644 --- a/server.c +++ b/server.c @@ -1,77 +1,32 @@ #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; +typedef struct PipeThreadInfo_t { + HANDLE Pipe; + SOCKET ClientSocket; +} PipeThreadInfo; -SOCKET ClientSocket = INVALID_SOCKET; - -//HANDLE g_hInputFile = NULL; +void CreateChildProcess(HANDLE g_hChildStd_IN_Rd, HANDLE g_hChildStd_OUT_Wr); +DWORD WINAPI WorkWithClient(LPVOID lpParam); +DWORD WINAPI WriteToPipe(LPVOID lpParam); +DWORD WINAPI ReadFromPipe(LPVOID lpParam); +void ErrorExit(PCTSTR); 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; + int iResult; + + SOCKET ClientSocket = INVALID_SOCKET; SOCKET ListenSocket = INVALID_SOCKET; - int iResult; + /* + * Initialize listening socket + */ // Initialize Winsock iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); @@ -125,22 +80,87 @@ void CreateSocket() { return 1; } - // Accepting a connection - ClientSocket = INVALID_SOCKET; + /* + * Process client connection + */ + for (;;) { + // Accepting a client 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; + ClientSocket = accept(ListenSocket, NULL, NULL); + if (ClientSocket == INVALID_SOCKET) { + printf("accept failed: %d\n", WSAGetLastError()); + break; + } + printf("Client connected\n"); + // Create separate thread to process client connection + CreateThread(NULL, 0, WorkWithClient, ClientSocket, 0, NULL); } - // No longer needed + + /* + * Finalization + */ + // TODO: properly close all handles closesocket(ListenSocket); + WSACleanup(); } -void CreateChildProcess() +DWORD WINAPI WorkWithClient(LPVOID lpParam) { + /* + * Client variables + */ + // Pipe handles for child STDIN/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; + + SECURITY_ATTRIBUTES saAttr; + HANDLE PipeThreads[2]; + + SOCKET ClientSocket = (SOCKET)lpParam; + + /* + * Initialize pipes + */ + // 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")); + + /* + * Create child process cmd.exe + */ + CreateChildProcess(g_hChildStd_IN_Rd, g_hChildStd_OUT_Wr); + + // Create threads for STDIN/STDOUT + PipeThreadInfo WriteThreadInfo = { g_hChildStd_IN_Wr, ClientSocket }; + PipeThreadInfo ReadThreadInfo = { g_hChildStd_OUT_Rd, ClientSocket }; + PipeThreads[0] = CreateThread(NULL, 0, WriteToPipe, &WriteThreadInfo, 0, NULL); + PipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &ReadThreadInfo, 0, NULL); + + WaitForMultipleObjects(2, PipeThreads, TRUE, INFINITE); + + printf("\n->Client disconnected.\n"); +} + +void CreateChildProcess(HANDLE g_hChildStd_IN_Rd, HANDLE g_hChildStd_OUT_Wr) // Create a child process that uses the previously created pipes for STDIN and STDOUT. { TCHAR szCmdline[] = TEXT("C:\\Windows\\System32\\cmd.exe"); @@ -195,36 +215,31 @@ void CreateChildProcess() } } -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 WINAPI WriteToPipe(LPVOID lpParam) { DWORD dwRead, dwWritten; - //CHAR chBuf[BUFSIZE] = "dir\r\n"; BOOL bSuccess = FALSE; int i = 0; + SOCKET ClientSocket = ((PipeThreadInfo*)lpParam)->ClientSocket; + HANDLE g_hChildStd_IN_Wr = ((PipeThreadInfo*)lpParam)->Pipe; + char recvbuf[DEFAULT_BUFLEN]; int iResult, iSendResult; int recvbuflen = DEFAULT_BUFLEN; - + + // Change codepage to UTF-8 + //CHAR chcpCommand[] = "chcp 65001\n"; + //bSuccess = WriteFile(g_hChildStd_IN_Wr, chcpCommand, strlen(chcpCommand), &dwWritten, NULL); + //if (!bSuccess) goto WriteToPipe_end; 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); + bSuccess = WriteFile(g_hChildStd_IN_Wr, recvbuf, iResult, &dwWritten, NULL); + if (!bSuccess) break; + printf("Received command: %s\n"); } else if (iResult == 0) printf("Connection closing...\n"); @@ -234,40 +249,34 @@ void WriteToPipe(void) 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. - +WriteToPipe_end: + // Closing STDIN => cmd.exe exit 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 WINAPI ReadFromPipe(LPVOID lpParam) { - DWORD dwRead, dwWritten; - CHAR chBuf[BUFSIZE]; + DWORD dwRead; + CHAR chBuf[BUFSIZE + 1]; + BOOL bSuccess = FALSE; - HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + //HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + SOCKET ClientSocket = ((PipeThreadInfo*)lpParam)->ClientSocket; + HANDLE g_hChildStd_OUT_Rd = ((PipeThreadInfo*)lpParam)->Pipe; int iSendResult; + for (;;) { bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); if (!bSuccess || dwRead == 0) break; + printf("STDOUT: %s\n", chBuf); + + chBuf[dwRead] = '\0'; iSendResult = send(ClientSocket, chBuf, dwRead, 0); if (iSendResult == SOCKET_ERROR) { @@ -276,11 +285,7 @@ void ReadFromPipe(void) WSACleanup(); return 1; } - // Write to console - bSuccess = WriteFile(hParentStdOut, chBuf, - dwRead, &dwWritten, NULL); if (!bSuccess) break; - break; } } diff --git a/server.h b/server.h index 249948d..f1f32fc 100644 --- a/server.h +++ b/server.h @@ -12,11 +12,7 @@ #include #include +#define DEFAULT_PORT "50113" + // 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 +void StartShellServer(); \ No newline at end of file