Initial commit

Первая версия программы - реализовано создание дочернего процесса
cmd.exe, создание нужных пайпов и черновая реализация работы по сети
This commit is contained in:
Nihonium 2024-11-28 01:46:38 +03:00
commit 4c6087fe9d
5 changed files with 454 additions and 0 deletions

21
CMakeLists.txt Normal file
View file

@ -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 "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>: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: Добавьте тесты и целевые объекты, если это необходимо.

61
CMakePresets.json Normal file
View file

@ -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"
}
}
]
}

33
main.c Normal file
View file

@ -0,0 +1,33 @@
#include <stdio.h>
#include <tchar.h>
#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;
}

317
server.c Normal file
View file

@ -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);
}

22
server.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#pragma comment(lib, "Ws2_32.lib")
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <strsafe.h>
#include <winsock2.h>
#include <ws2tcpip.h>
// Defined functions
void StartShellServer();
void CreatePipes();
void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PCTSTR);
void CreateSocket();