PageRenderTime 28ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/client/virt/deps/rss.cpp

https://gitlab.com/libvirt/autotest
C++ | 1011 lines | 846 code | 54 blank | 111 comment | 50 complexity | c29a19886861a9a79d91f1a7549ce248 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0
  1. // Simple remote shell server (and file transfer server)
  2. // Author: Michael Goldish <mgoldish@redhat.com>
  3. // Much of the code here was adapted from Microsoft code samples.
  4. // Usage: rss.exe [shell port] [file transfer port]
  5. // If no shell port is specified the default is 10022.
  6. // If no file transfer port is specified the default is 10023.
  7. // Definitions:
  8. // A 'msg' is a 32 bit integer.
  9. // A 'packet' is a 32 bit unsigned integer followed by a string of bytes.
  10. // The 32 bit integer indicates the length of the string.
  11. // Protocol for file transfers:
  12. //
  13. // When uploading files/directories to the server:
  14. // 1. The client connects.
  15. // 2. The server sends RSS_MAGIC.
  16. // 3. The client sends the chunk size for file transfers (a 32 bit integer
  17. // between 512 and 1048576 indicating the size in bytes).
  18. // 4. The client sends RSS_SET_PATH, followed by a packet (as defined above)
  19. // containing the path (in the server's filesystem) where files and/or
  20. // directories are to be stored.
  21. // Uploading a file (optional, can be repeated many times):
  22. // 5. The client sends RSS_CREATE_FILE, followed by a packet containing the
  23. // filename (filename only, without a path), followed by a series of
  24. // packets (called chunks) containing the file's contents. The size of
  25. // each chunk is the size set by the client in step 3, except for the
  26. // last chunk, which must be smaller.
  27. // Uploading a directory (optional, can be repeated many times):
  28. // 6. The client sends RSS_CREATE_DIR, followed by a packet containing the
  29. // name of the directory to be created (directory name only, without a
  30. // path).
  31. // 7. The client uploads files and directories to the new directory (using
  32. // steps 5, 6, 8).
  33. // 8. The client sends RSS_LEAVE_DIR.
  34. // 9. The client sends RSS_DONE and waits for a response.
  35. // 10. The server sends RSS_OK to indicate that it's still listening.
  36. // 11. Steps 4-10 are repeated as many times as necessary.
  37. // 12. The client disconnects.
  38. // If a critical error occurs at any time, the server may send RSS_ERROR
  39. // followed by a packet containing an error message, and the connection is
  40. // closed.
  41. //
  42. // When downloading files from the server:
  43. // 1. The client connects.
  44. // 2. The server sends RSS_MAGIC.
  45. // 3. The client sends the chunk size for file transfers (a 32 bit integer
  46. // between 512 and 1048576 indicating the size in bytes).
  47. // 4. The client sends RSS_SET_PATH, followed by a packet (as defined above)
  48. // containing a path (in the server's filesystem) or a wildcard pattern
  49. // indicating the files/directories the client wants to download.
  50. // The server then searches the given path. For every file found:
  51. // 5. The server sends RSS_CREATE_FILE, followed by a packet containing the
  52. // filename (filename only, without a path), followed by a series of
  53. // packets (called chunks) containing the file's contents. The size of
  54. // each chunk is the size set by the client in step 3, except for the
  55. // last chunk, which must be smaller.
  56. // For every directory found:
  57. // 6. The server sends RSS_CREATE_DIR, followed by a packet containing the
  58. // name of the directory to be created (directory name only, without a
  59. // path).
  60. // 7. The server sends files and directories located inside the directory
  61. // (using steps 5, 6, 8).
  62. // 8. The server sends RSS_LEAVE_DIR.
  63. // 9. The server sends RSS_DONE.
  64. // 10. Steps 4-9 are repeated as many times as necessary.
  65. // 11. The client disconnects.
  66. // If a critical error occurs, the server may send RSS_ERROR followed by a
  67. // packet containing an error message, and the connection is closed.
  68. // RSS_ERROR may be sent only when the client expects a msg.
  69. #define _WIN32_WINNT 0x0500
  70. #include <winsock2.h>
  71. #include <windows.h>
  72. #include <stdio.h>
  73. #include <stdarg.h>
  74. #include <shlwapi.h>
  75. #pragma comment(lib, "ws2_32.lib")
  76. #pragma comment(lib, "shlwapi.lib")
  77. #define TEXTBOX_LIMIT 262144
  78. // Constants for file transfer server
  79. #define RSS_MAGIC 0x525353
  80. #define RSS_OK 1
  81. #define RSS_ERROR 2
  82. #define RSS_UPLOAD 3
  83. #define RSS_DOWNLOAD 4
  84. #define RSS_SET_PATH 5
  85. #define RSS_CREATE_FILE 6
  86. #define RSS_CREATE_DIR 7
  87. #define RSS_LEAVE_DIR 8
  88. #define RSS_DONE 9
  89. // Globals
  90. int shell_port = 10022;
  91. int file_transfer_port = 10023;
  92. HWND hMainWindow = NULL;
  93. HWND hTextBox = NULL;
  94. char text_buffer[8192] = {0};
  95. int text_size = 0;
  96. CRITICAL_SECTION critical_section;
  97. FILE *log_file;
  98. struct client_info {
  99. SOCKET socket;
  100. char addr_str[256];
  101. int pid;
  102. HWND hwnd;
  103. HANDLE hJob;
  104. HANDLE hChildOutputRead;
  105. HANDLE hThreadChildToSocket;
  106. char *chunk_buffer;
  107. int chunk_size;
  108. };
  109. /*-----------------
  110. * Shared functions
  111. *-----------------*/
  112. void ExitOnError(const char *message, BOOL winsock = FALSE)
  113. {
  114. LPVOID system_message;
  115. char buffer[512];
  116. int error_code;
  117. if (winsock)
  118. error_code = WSAGetLastError();
  119. else
  120. error_code = GetLastError();
  121. WSACleanup();
  122. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  123. FORMAT_MESSAGE_FROM_SYSTEM,
  124. NULL,
  125. error_code,
  126. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  127. (LPTSTR)&system_message,
  128. 0,
  129. NULL);
  130. sprintf(buffer,
  131. "%s!\n"
  132. "Error code = %d\n"
  133. "Error message = %s",
  134. message, error_code, (char *)system_message);
  135. MessageBox(hMainWindow, buffer, "Error", MB_OK | MB_ICONERROR);
  136. LocalFree(system_message);
  137. ExitProcess(1);
  138. }
  139. void FlushTextBuffer()
  140. {
  141. if (!text_size) return;
  142. // Clear the text box if it contains too much text
  143. int len = GetWindowTextLength(hTextBox);
  144. while (len > TEXTBOX_LIMIT - sizeof(text_buffer)) {
  145. SendMessage(hTextBox, EM_SETSEL, 0, TEXTBOX_LIMIT * 1/4);
  146. SendMessage(hTextBox, EM_REPLACESEL, FALSE, (LPARAM)"...");
  147. len = GetWindowTextLength(hTextBox);
  148. }
  149. // Append the contents of text_buffer to the text box
  150. SendMessage(hTextBox, EM_SETSEL, len, len);
  151. SendMessage(hTextBox, EM_REPLACESEL, FALSE, (LPARAM)text_buffer);
  152. // Clear text_buffer
  153. text_buffer[0] = 0;
  154. text_size = 0;
  155. // Make sure the log file's buffer is flushed as well
  156. if (log_file)
  157. fflush(log_file);
  158. }
  159. void AppendMessage(const char *message, ...)
  160. {
  161. va_list args;
  162. char str[512] = {0};
  163. va_start(args, message);
  164. vsnprintf(str, sizeof(str) - 3, message, args);
  165. va_end(args);
  166. strcat(str, "\r\n");
  167. int len = strlen(str);
  168. EnterCriticalSection(&critical_section);
  169. // Write message to the log file
  170. if (log_file)
  171. fwrite(str, len, 1, log_file);
  172. // Flush the text buffer if necessary
  173. if (text_size + len + 1 > sizeof(text_buffer))
  174. FlushTextBuffer();
  175. // Append message to the text buffer
  176. strcpy(text_buffer + text_size, str);
  177. text_size += len;
  178. LeaveCriticalSection(&critical_section);
  179. }
  180. // Flush the text buffer every 250 ms
  181. DWORD WINAPI UpdateTextBox(LPVOID client_info_ptr)
  182. {
  183. while (1) {
  184. Sleep(250);
  185. EnterCriticalSection(&critical_section);
  186. FlushTextBuffer();
  187. LeaveCriticalSection(&critical_section);
  188. }
  189. return 0;
  190. }
  191. void FormatStringForPrinting(char *dst, const char *src, int size)
  192. {
  193. int j = 0;
  194. for (int i = 0; i < size && src[i]; i++) {
  195. if (src[i] == '\n') {
  196. dst[j++] = '\\';
  197. dst[j++] = 'n';
  198. } else if (src[i] == '\r') {
  199. dst[j++] = '\\';
  200. dst[j++] = 'r';
  201. } else if (src[i] == '\t') {
  202. dst[j++] = '\\';
  203. dst[j++] = 't';
  204. } else if (src[i] == '\\') {
  205. dst[j++] = '\\';
  206. dst[j++] = '\\';
  207. } else dst[j++] = src[i];
  208. }
  209. dst[j] = 0;
  210. }
  211. SOCKET PrepareListenSocket(int port)
  212. {
  213. sockaddr_in addr;
  214. linger l;
  215. int result;
  216. // Create socket
  217. SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  218. if (ListenSocket == INVALID_SOCKET)
  219. ExitOnError("Socket creation failed", TRUE);
  220. // Enable lingering
  221. l.l_linger = 10;
  222. l.l_onoff = 1;
  223. setsockopt(ListenSocket, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l));
  224. // Bind the socket
  225. addr.sin_family = AF_INET;
  226. addr.sin_addr.s_addr = htonl(INADDR_ANY);
  227. addr.sin_port = htons(port);
  228. result = bind(ListenSocket, (sockaddr *)&addr, sizeof(addr));
  229. if (result == SOCKET_ERROR)
  230. ExitOnError("bind failed", TRUE);
  231. // Start listening for incoming connections
  232. result = listen(ListenSocket, SOMAXCONN);
  233. if (result == SOCKET_ERROR)
  234. ExitOnError("listen failed", TRUE);
  235. return ListenSocket;
  236. }
  237. client_info* Accept(SOCKET ListenSocket)
  238. {
  239. sockaddr_in addr;
  240. int addrlen = sizeof(addr);
  241. // Accept the connection
  242. SOCKET socket = accept(ListenSocket, (sockaddr *)&addr, &addrlen);
  243. if (socket == INVALID_SOCKET) {
  244. if (WSAGetLastError() == WSAEINTR)
  245. return NULL;
  246. else
  247. ExitOnError("accept failed", TRUE);
  248. }
  249. // Allocate a new client_info struct
  250. client_info *ci = (client_info *)calloc(1, sizeof(client_info));
  251. if (!ci)
  252. ExitOnError("Could not allocate client_info struct");
  253. // Populate the new struct
  254. ci->socket = socket;
  255. const char *address = inet_ntoa(addr.sin_addr);
  256. if (!address) address = "unknown";
  257. sprintf(ci->addr_str, "%s:%d", address, addr.sin_port);
  258. return ci;
  259. }
  260. // Read a given number of bytes into a buffer
  261. BOOL Receive(SOCKET socket, char *buffer, int len)
  262. {
  263. while (len > 0) {
  264. int bytes_received = recv(socket, buffer, len, 0);
  265. if (bytes_received <= 0)
  266. return FALSE;
  267. buffer += bytes_received;
  268. len -= bytes_received;
  269. }
  270. return TRUE;
  271. }
  272. // Send a given number of bytes from a buffer
  273. BOOL Send(SOCKET socket, const char *buffer, int len)
  274. {
  275. while (len > 0) {
  276. int bytes_sent = send(socket, buffer, len, 0);
  277. if (bytes_sent <= 0)
  278. return FALSE;
  279. buffer += bytes_sent;
  280. len -= bytes_sent;
  281. }
  282. return TRUE;
  283. }
  284. /*-------------
  285. * Shell server
  286. *-------------*/
  287. DWORD WINAPI ChildToSocket(LPVOID client_info_ptr)
  288. {
  289. client_info *ci = (client_info *)client_info_ptr;
  290. char buffer[1024];
  291. DWORD bytes_read;
  292. while (1) {
  293. // Read data from the child's STDOUT/STDERR pipes
  294. if (!ReadFile(ci->hChildOutputRead,
  295. buffer, sizeof(buffer),
  296. &bytes_read, NULL) || !bytes_read) {
  297. if (GetLastError() == ERROR_BROKEN_PIPE)
  298. break; // Pipe done -- normal exit path
  299. else
  300. ExitOnError("ReadFile failed"); // Something bad happened
  301. }
  302. // Send data to the client
  303. Send(ci->socket, buffer, bytes_read);
  304. }
  305. AppendMessage("Child exited");
  306. closesocket(ci->socket);
  307. return 0;
  308. }
  309. DWORD WINAPI SocketToChild(LPVOID client_info_ptr)
  310. {
  311. client_info *ci = (client_info *)client_info_ptr;
  312. char buffer[256], formatted_buffer[768];
  313. int bytes_received;
  314. AppendMessage("Shell server: new client connected (%s)", ci->addr_str);
  315. while (1) {
  316. // Receive data from the socket
  317. ZeroMemory(buffer, sizeof(buffer));
  318. bytes_received = recv(ci->socket, buffer, sizeof(buffer), 0);
  319. if (bytes_received <= 0)
  320. break;
  321. // Report the data received
  322. FormatStringForPrinting(formatted_buffer, buffer, sizeof(buffer));
  323. AppendMessage("Client (%s) entered text: \"%s\"",
  324. ci->addr_str, formatted_buffer);
  325. // Send the data as a series of WM_CHAR messages to the console window
  326. for (int i = 0; i < bytes_received; i++) {
  327. SendMessage(ci->hwnd, WM_CHAR, buffer[i], 0);
  328. SendMessage(ci->hwnd, WM_SETFOCUS, 0, 0);
  329. }
  330. }
  331. AppendMessage("Shell server: client disconnected (%s)", ci->addr_str);
  332. // Attempt to terminate the child's process tree:
  333. // Using taskkill (where available)
  334. sprintf(buffer, "taskkill /PID %d /T /F", ci->pid);
  335. system(buffer);
  336. // .. and using TerminateJobObject()
  337. TerminateJobObject(ci->hJob, 0);
  338. // Wait for the ChildToSocket thread to terminate
  339. WaitForSingleObject(ci->hThreadChildToSocket, 10000);
  340. // In case the thread refuses to exit, terminate it
  341. TerminateThread(ci->hThreadChildToSocket, 0);
  342. // Close the socket
  343. closesocket(ci->socket);
  344. // Free resources
  345. CloseHandle(ci->hJob);
  346. CloseHandle(ci->hThreadChildToSocket);
  347. CloseHandle(ci->hChildOutputRead);
  348. free(ci);
  349. AppendMessage("SocketToChild thread exited");
  350. return 0;
  351. }
  352. void PrepAndLaunchRedirectedChild(client_info *ci,
  353. HANDLE hChildStdOut,
  354. HANDLE hChildStdErr)
  355. {
  356. PROCESS_INFORMATION pi;
  357. STARTUPINFO si;
  358. // Allocate a new console for the child
  359. HWND hwnd = GetForegroundWindow();
  360. FreeConsole();
  361. AllocConsole();
  362. ShowWindow(GetConsoleWindow(), SW_HIDE);
  363. if (hwnd)
  364. SetForegroundWindow(hwnd);
  365. // Set up the start up info struct.
  366. ZeroMemory(&si, sizeof(STARTUPINFO));
  367. si.cb = sizeof(STARTUPINFO);
  368. si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  369. si.hStdOutput = hChildStdOut;
  370. si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  371. si.hStdError = hChildStdErr;
  372. // Use this if you want to hide the child:
  373. si.wShowWindow = SW_HIDE;
  374. // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
  375. // use the wShowWindow flags.
  376. // Launch the process that you want to redirect.
  377. if (!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE,
  378. 0, NULL, "C:\\", &si, &pi))
  379. ExitOnError("CreateProcess failed");
  380. // Close any unnecessary handles.
  381. if (!CloseHandle(pi.hThread))
  382. ExitOnError("CloseHandle failed");
  383. // Keep the process ID
  384. ci->pid = pi.dwProcessId;
  385. // Assign the process to a newly created JobObject
  386. ci->hJob = CreateJobObject(NULL, NULL);
  387. AssignProcessToJobObject(ci->hJob, pi.hProcess);
  388. // Keep the console window's handle
  389. ci->hwnd = GetConsoleWindow();
  390. // Detach from the child's console
  391. FreeConsole();
  392. }
  393. void SpawnSession(client_info *ci)
  394. {
  395. HANDLE hOutputReadTmp, hOutputRead, hOutputWrite;
  396. HANDLE hErrorWrite;
  397. SECURITY_ATTRIBUTES sa;
  398. // Set up the security attributes struct.
  399. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  400. sa.lpSecurityDescriptor = NULL;
  401. sa.bInheritHandle = TRUE;
  402. // Create the child output pipe.
  403. if (!CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0))
  404. ExitOnError("CreatePipe failed");
  405. // Create a duplicate of the output write handle for the std error
  406. // write handle. This is necessary in case the child application
  407. // closes one of its std output handles.
  408. if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
  409. GetCurrentProcess(), &hErrorWrite, 0,
  410. TRUE, DUPLICATE_SAME_ACCESS))
  411. ExitOnError("DuplicateHandle failed");
  412. // Create new output read handle and the input write handles. Set
  413. // the Properties to FALSE. Otherwise, the child inherits the
  414. // properties and, as a result, non-closeable handles to the pipes
  415. // are created.
  416. if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
  417. GetCurrentProcess(),
  418. &hOutputRead, // Address of new handle.
  419. 0, FALSE, // Make it uninheritable.
  420. DUPLICATE_SAME_ACCESS))
  421. ExitOnError("DuplicateHandle failed");
  422. // Close inheritable copies of the handles you do not want to be
  423. // inherited.
  424. if (!CloseHandle(hOutputReadTmp))
  425. ExitOnError("CloseHandle failed");
  426. PrepAndLaunchRedirectedChild(ci, hOutputWrite, hErrorWrite);
  427. ci->hChildOutputRead = hOutputRead;
  428. // Close pipe handles (do not continue to modify the parent).
  429. // You need to make sure that no handles to the write end of the
  430. // output pipe are maintained in this process or else the pipe will
  431. // not close when the child process exits and the ReadFile will hang.
  432. if (!CloseHandle(hOutputWrite)) ExitOnError("CloseHandle failed");
  433. if (!CloseHandle(hErrorWrite)) ExitOnError("CloseHandle failed");
  434. }
  435. DWORD WINAPI ShellListenThread(LPVOID param)
  436. {
  437. HANDLE hThread;
  438. SOCKET ListenSocket = PrepareListenSocket(shell_port);
  439. // Inform the user
  440. AppendMessage("Shell server: waiting for clients to connect...");
  441. while (1) {
  442. client_info *ci = Accept(ListenSocket);
  443. if (!ci) break;
  444. // Under heavy load, spawning cmd.exe might take a while, so tell the
  445. // client to be patient
  446. const char *message = "Please wait...\r\n";
  447. Send(ci->socket, message, strlen(message));
  448. // Spawn a new redirected cmd.exe process
  449. SpawnSession(ci);
  450. // Start transferring data from the child process to the client
  451. hThread = CreateThread(NULL, 0, ChildToSocket, (LPVOID)ci, 0, NULL);
  452. if (!hThread)
  453. ExitOnError("Could not create ChildToSocket thread");
  454. ci->hThreadChildToSocket = hThread;
  455. // ... and from the client to the child process
  456. hThread = CreateThread(NULL, 0, SocketToChild, (LPVOID)ci, 0, NULL);
  457. if (!hThread)
  458. ExitOnError("Could not create SocketToChild thread");
  459. }
  460. return 0;
  461. }
  462. /*---------------------
  463. * File transfer server
  464. *---------------------*/
  465. int ReceivePacket(SOCKET socket, char *buffer, DWORD max_size)
  466. {
  467. DWORD packet_size = 0;
  468. if (!Receive(socket, (char *)&packet_size, 4))
  469. return -1;
  470. if (packet_size > max_size)
  471. return -1;
  472. if (!Receive(socket, buffer, packet_size))
  473. return -1;
  474. return packet_size;
  475. }
  476. int ReceiveStrPacket(SOCKET socket, char *buffer, DWORD max_size)
  477. {
  478. memset(buffer, 0, max_size);
  479. return ReceivePacket(socket, buffer, max_size - 1);
  480. }
  481. BOOL SendPacket(SOCKET socket, const char *buffer, DWORD len)
  482. {
  483. if (!Send(socket, (char *)&len, 4))
  484. return FALSE;
  485. return Send(socket, buffer, len);
  486. }
  487. BOOL SendMsg(SOCKET socket, DWORD msg)
  488. {
  489. return Send(socket, (char *)&msg, 4);
  490. }
  491. // Send data from a file
  492. BOOL SendFileChunks(client_info *ci, const char *filename)
  493. {
  494. FILE *fp = fopen(filename, "rb");
  495. if (!fp) return FALSE;
  496. while (1) {
  497. int bytes_read = fread(ci->chunk_buffer, 1, ci->chunk_size, fp);
  498. if (!SendPacket(ci->socket, ci->chunk_buffer, bytes_read))
  499. break;
  500. if (bytes_read < ci->chunk_size) {
  501. if (ferror(fp))
  502. break;
  503. else {
  504. fclose(fp);
  505. return TRUE;
  506. }
  507. }
  508. }
  509. fclose(fp);
  510. return FALSE;
  511. }
  512. // Receive data into a file
  513. BOOL ReceiveFileChunks(client_info *ci, const char *filename)
  514. {
  515. FILE *fp = fopen(filename, "wb");
  516. if (!fp) return FALSE;
  517. while (1) {
  518. int bytes_received = ReceivePacket(ci->socket, ci->chunk_buffer,
  519. ci->chunk_size);
  520. if (bytes_received < 0)
  521. break;
  522. if (bytes_received > 0)
  523. if (fwrite(ci->chunk_buffer, bytes_received, 1, fp) < 1)
  524. break;
  525. if (bytes_received < ci->chunk_size) {
  526. fclose(fp);
  527. return TRUE;
  528. }
  529. }
  530. fclose(fp);
  531. return FALSE;
  532. }
  533. BOOL ExpandPath(char *path, int max_size)
  534. {
  535. char temp[512];
  536. int result;
  537. PathRemoveBackslash(path);
  538. result = ExpandEnvironmentStrings(path, temp, sizeof(temp));
  539. if (result == 0 || result > sizeof(temp))
  540. return FALSE;
  541. strncpy(path, temp, max_size - 1);
  542. return TRUE;
  543. }
  544. int TerminateTransfer(client_info *ci, const char *message)
  545. {
  546. AppendMessage(message);
  547. AppendMessage("File transfer server: client disconnected (%s)",
  548. ci->addr_str);
  549. closesocket(ci->socket);
  550. free(ci->chunk_buffer);
  551. free(ci);
  552. return 0;
  553. }
  554. int TerminateWithError(client_info *ci, const char *message)
  555. {
  556. SendMsg(ci->socket, RSS_ERROR);
  557. SendPacket(ci->socket, message, strlen(message));
  558. return TerminateTransfer(ci, message);
  559. }
  560. int ReceiveThread(client_info *ci)
  561. {
  562. char path[512], filename[512];
  563. DWORD msg;
  564. AppendMessage("Client (%s) wants to upload files", ci->addr_str);
  565. while (1) {
  566. if (!Receive(ci->socket, (char *)&msg, 4))
  567. return TerminateTransfer(ci, "Could not receive further msgs");
  568. switch (msg) {
  569. case RSS_SET_PATH:
  570. if (ReceiveStrPacket(ci->socket, path, sizeof(path)) < 0)
  571. return TerminateWithError(ci,
  572. "RSS_SET_PATH: could not receive path, or path too long");
  573. AppendMessage("Client (%s) set path to %s", ci->addr_str, path);
  574. if (!ExpandPath(path, sizeof(path)))
  575. return TerminateWithError(ci,
  576. "RSS_SET_PATH: error expanding environment strings");
  577. break;
  578. case RSS_CREATE_FILE:
  579. if (ReceiveStrPacket(ci->socket, filename, sizeof(filename)) < 0)
  580. return TerminateWithError(ci,
  581. "RSS_CREATE_FILE: could not receive filename");
  582. if (PathIsDirectory(path))
  583. PathAppend(path, filename);
  584. AppendMessage("Client (%s) is uploading %s", ci->addr_str, path);
  585. if (!ReceiveFileChunks(ci, path))
  586. return TerminateWithError(ci,
  587. "RSS_CREATE_FILE: error receiving or writing file "
  588. "contents");
  589. PathAppend(path, "..");
  590. break;
  591. case RSS_CREATE_DIR:
  592. if (ReceiveStrPacket(ci->socket, filename, sizeof(filename)) < 0)
  593. return TerminateWithError(ci,
  594. "RSS_CREATE_DIR: could not receive dirname");
  595. if (PathIsDirectory(path))
  596. PathAppend(path, filename);
  597. AppendMessage("Entering dir %s", path);
  598. if (PathFileExists(path)) {
  599. if (!PathIsDirectory(path))
  600. return TerminateWithError(ci,
  601. "RSS_CREATE_DIR: path exists and is not a directory");
  602. } else {
  603. if (!CreateDirectory(path, NULL))
  604. return TerminateWithError(ci,
  605. "RSS_CREATE_DIR: could not create directory");
  606. }
  607. break;
  608. case RSS_LEAVE_DIR:
  609. PathAppend(path, "..");
  610. AppendMessage("Returning to dir %s", path);
  611. break;
  612. case RSS_DONE:
  613. if (!SendMsg(ci->socket, RSS_OK))
  614. return TerminateTransfer(ci,
  615. "RSS_DONE: could not send OK msg");
  616. break;
  617. default:
  618. return TerminateWithError(ci, "Received unexpected msg");
  619. }
  620. }
  621. }
  622. // Given a path or a pattern with wildcards, send files or directory trees to
  623. // the client
  624. int SendFiles(client_info *ci, const char *pattern)
  625. {
  626. char path[512];
  627. WIN32_FIND_DATA ffd;
  628. HANDLE hFind = FindFirstFile(pattern, &ffd);
  629. if (hFind == INVALID_HANDLE_VALUE) {
  630. // If a weird error occurred (like failure to list directory contents
  631. // due to insufficient permissions) print a warning and continue.
  632. if (GetLastError() != ERROR_FILE_NOT_FOUND)
  633. AppendMessage("WARNING: FindFirstFile failed on pattern %s",
  634. pattern);
  635. return 1;
  636. }
  637. strncpy(path, pattern, sizeof(path) - 1);
  638. PathAppend(path, "..");
  639. do {
  640. if (ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
  641. continue;
  642. if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  643. // Directory
  644. if (!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, ".."))
  645. continue;
  646. PathAppend(path, ffd.cFileName);
  647. AppendMessage("Entering dir %s", path);
  648. PathAppend(path, "*");
  649. if (!SendMsg(ci->socket, RSS_CREATE_DIR)) {
  650. FindClose(hFind);
  651. return TerminateTransfer(ci,
  652. "Could not send RSS_CREATE_DIR msg");
  653. }
  654. if (!SendPacket(ci->socket, ffd.cFileName,
  655. strlen(ffd.cFileName))) {
  656. FindClose(hFind);
  657. return TerminateTransfer(ci, "Could not send dirname");
  658. }
  659. if (!SendFiles(ci, path)) {
  660. FindClose(hFind);
  661. return 0;
  662. }
  663. if (!SendMsg(ci->socket, RSS_LEAVE_DIR)) {
  664. FindClose(hFind);
  665. return TerminateTransfer(ci,
  666. "Could not send RSS_LEAVE_DIR msg");
  667. }
  668. PathAppend(path, "..");
  669. PathAppend(path, "..");
  670. AppendMessage("Returning to dir %s", path);
  671. } else {
  672. // File
  673. PathAppend(path, ffd.cFileName);
  674. AppendMessage("Client (%s) is downloading %s", ci->addr_str, path);
  675. // Make sure the file is readable
  676. FILE *fp = fopen(path, "rb");
  677. if (fp) fclose(fp);
  678. else {
  679. AppendMessage("WARNING: could not read file %s", path);
  680. PathAppend(path, "..");
  681. continue;
  682. }
  683. if (!SendMsg(ci->socket, RSS_CREATE_FILE)) {
  684. FindClose(hFind);
  685. return TerminateTransfer(ci,
  686. "Could not send RSS_CREATE_FILE msg");
  687. }
  688. if (!SendPacket(ci->socket, ffd.cFileName,
  689. strlen(ffd.cFileName))) {
  690. FindClose(hFind);
  691. return TerminateTransfer(ci, "Could not send filename");
  692. }
  693. if (!SendFileChunks(ci, path)) {
  694. FindClose(hFind);
  695. return TerminateTransfer(ci, "Could not send file contents");
  696. }
  697. PathAppend(path, "..");
  698. }
  699. } while (FindNextFile(hFind, &ffd));
  700. if (GetLastError() == ERROR_NO_MORE_FILES) {
  701. FindClose(hFind);
  702. return 1;
  703. } else {
  704. FindClose(hFind);
  705. return TerminateWithError(ci, "FindNextFile failed");
  706. }
  707. }
  708. int SendThread(client_info *ci)
  709. {
  710. char pattern[512];
  711. DWORD msg;
  712. AppendMessage("Client (%s) wants to download files", ci->addr_str);
  713. while (1) {
  714. if (!Receive(ci->socket, (char *)&msg, 4))
  715. return TerminateTransfer(ci, "Could not receive further msgs");
  716. switch (msg) {
  717. case RSS_SET_PATH:
  718. if (ReceiveStrPacket(ci->socket, pattern, sizeof(pattern)) < 0)
  719. return TerminateWithError(ci,
  720. "RSS_SET_PATH: could not receive path, or path too long");
  721. AppendMessage("Client (%s) asked for %s", ci->addr_str, pattern);
  722. if (!ExpandPath(pattern, sizeof(pattern)))
  723. return TerminateWithError(ci,
  724. "RSS_SET_PATH: error expanding environment strings");
  725. if (!SendFiles(ci, pattern))
  726. return 0;
  727. if (!SendMsg(ci->socket, RSS_DONE))
  728. return TerminateTransfer(ci,
  729. "RSS_SET_PATH: could not send RSS_DONE msg");
  730. break;
  731. default:
  732. return TerminateWithError(ci, "Received unexpected msg");
  733. }
  734. }
  735. }
  736. DWORD WINAPI TransferThreadEntry(LPVOID client_info_ptr)
  737. {
  738. client_info *ci = (client_info *)client_info_ptr;
  739. DWORD msg;
  740. AppendMessage("File transfer server: new client connected (%s)",
  741. ci->addr_str);
  742. if (!SendMsg(ci->socket, RSS_MAGIC))
  743. return TerminateTransfer(ci, "Could not send greeting message");
  744. if (!Receive(ci->socket, (char *)&ci->chunk_size, 4))
  745. return TerminateTransfer(ci, "Error receiving chunk size");
  746. AppendMessage("Client (%s) set chunk size to %d", ci->addr_str,
  747. ci->chunk_size);
  748. if (ci->chunk_size > 1048576 || ci->chunk_size < 512)
  749. return TerminateWithError(ci, "Client set invalid chunk size");
  750. if (!(ci->chunk_buffer = (char *)malloc(ci->chunk_size)))
  751. return TerminateWithError(ci, "Memory allocation error");
  752. if (!Receive(ci->socket, (char *)&msg, 4))
  753. return TerminateTransfer(ci, "Error receiving msg");
  754. if (msg == RSS_UPLOAD)
  755. return ReceiveThread(ci);
  756. else if (msg == RSS_DOWNLOAD)
  757. return SendThread(ci);
  758. return TerminateWithError(ci, "Received unexpected msg");
  759. }
  760. DWORD WINAPI FileTransferListenThread(LPVOID param)
  761. {
  762. SOCKET ListenSocket = PrepareListenSocket(file_transfer_port);
  763. // Inform the user
  764. AppendMessage("File transfer server: waiting for clients to connect...");
  765. while (1) {
  766. client_info *ci = Accept(ListenSocket);
  767. if (!ci) break;
  768. if (!CreateThread(NULL, 0, TransferThreadEntry, (LPVOID)ci, 0, NULL))
  769. ExitOnError("Could not create file transfer thread");
  770. }
  771. return 0;
  772. }
  773. /*--------------------
  774. * WndProc and WinMain
  775. *--------------------*/
  776. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  777. {
  778. RECT rect;
  779. WSADATA wsaData;
  780. SYSTEMTIME lt;
  781. char log_filename[256];
  782. switch (msg) {
  783. case WM_CREATE:
  784. // Create text box
  785. GetClientRect(hwnd, &rect);
  786. hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
  787. "EDIT", "",
  788. WS_CHILD | WS_VISIBLE | WS_VSCROLL |
  789. ES_MULTILINE | ES_AUTOVSCROLL,
  790. 20, 20,
  791. rect.right - 40,
  792. rect.bottom - 40,
  793. hwnd,
  794. NULL,
  795. GetModuleHandle(NULL),
  796. NULL);
  797. if (!hTextBox)
  798. ExitOnError("Could not create text box");
  799. // Set font
  800. SendMessage(hTextBox, WM_SETFONT,
  801. (WPARAM)GetStockObject(DEFAULT_GUI_FONT),
  802. MAKELPARAM(FALSE, 0));
  803. // Set size limit
  804. SendMessage(hTextBox, EM_LIMITTEXT, TEXTBOX_LIMIT, 0);
  805. // Initialize critical section object for text buffer access
  806. InitializeCriticalSection(&critical_section);
  807. // Open log file
  808. GetLocalTime(&lt);
  809. sprintf(log_filename, "rss_%02d-%02d-%02d_%02d-%02d-%02d.log",
  810. lt.wYear, lt.wMonth, lt.wDay,
  811. lt.wHour, lt.wMinute, lt.wSecond);
  812. log_file = fopen(log_filename, "wb");
  813. // Create text box update thread
  814. if (!CreateThread(NULL, 0, UpdateTextBox, NULL, 0, NULL))
  815. ExitOnError("Could not create text box update thread");
  816. // Initialize Winsock
  817. if (WSAStartup(MAKEWORD(2, 2), &wsaData))
  818. ExitOnError("Winsock initialization failed");
  819. // Start the listening threads
  820. if (!CreateThread(NULL, 0, ShellListenThread, NULL, 0, NULL))
  821. ExitOnError("Could not create shell server listen thread");
  822. if (!CreateThread(NULL, 0, FileTransferListenThread, NULL, 0, NULL))
  823. ExitOnError("Could not create file transfer server listen thread");
  824. break;
  825. case WM_SIZE:
  826. MoveWindow(hTextBox, 20, 20,
  827. LOWORD(lParam) - 40, HIWORD(lParam) - 40, TRUE);
  828. break;
  829. case WM_DESTROY:
  830. if (WSACleanup())
  831. ExitOnError("WSACleanup failed");
  832. PostQuitMessage(0);
  833. break;
  834. default:
  835. return DefWindowProc(hwnd, msg, wParam, lParam);
  836. }
  837. return 0;
  838. }
  839. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  840. LPSTR lpCmdLine, int nShowCmd)
  841. {
  842. WNDCLASSEX wc;
  843. MSG msg;
  844. char title[256];
  845. if (strlen(lpCmdLine))
  846. sscanf(lpCmdLine, "%d %d", &shell_port, &file_transfer_port);
  847. sprintf(title, "Remote Shell Server (listening on ports %d, %d)",
  848. shell_port, file_transfer_port);
  849. // Create the window class
  850. wc.cbSize = sizeof(WNDCLASSEX);
  851. wc.style = CS_HREDRAW | CS_VREDRAW;
  852. wc.lpfnWndProc = WndProc;
  853. wc.cbClsExtra = 0;
  854. wc.cbWndExtra = 0;
  855. wc.hInstance = hInstance;
  856. wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  857. wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
  858. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  859. wc.lpszMenuName = NULL;
  860. wc.lpszClassName = "RemoteShellServerWindowClass";
  861. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  862. if (!RegisterClassEx(&wc))
  863. ExitOnError("Could not register window class");
  864. // Create the main window
  865. hMainWindow =
  866. CreateWindow("RemoteShellServerWindowClass", title,
  867. WS_OVERLAPPEDWINDOW,
  868. 20, 20, 600, 400,
  869. NULL, NULL, hInstance, NULL);
  870. if (!hMainWindow)
  871. ExitOnError("Could not create window");
  872. ShowWindow(hMainWindow, SW_SHOWMINNOACTIVE);
  873. UpdateWindow(hMainWindow);
  874. // Main message loop
  875. while (GetMessage(&msg, NULL, 0, 0)) {
  876. TranslateMessage(&msg);
  877. DispatchMessage(&msg);
  878. }
  879. ExitProcess(0);
  880. }