VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/ftp-server.cpp@ 82699

Last change on this file since 82699 was 82699, checked in by vboxsync, 5 years ago

IPRT/FTP: Renaming. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.9 KB
Line 
1/* $Id: ftp-server.cpp 82699 2020-01-09 14:43:25Z vboxsync $ */
2/** @file
3 * Generic FTP server (RFC 959) implementation.
4 */
5
6/*
7 * Copyright (C) 2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/**
28 * Known limitations so far:
29 * - UTF-8 support only.
30 * - No support for writing / modifying ("DELE", "MKD", "RMD", "STOR", ++).
31 * - No FTPS / SFTP support.
32 * - No passive mode ("PASV") support.
33 * - No proxy support.
34 * - No FXP support.
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_FTP
42#include <iprt/asm.h>
43#include <iprt/assert.h>
44#include <iprt/errcore.h>
45#include <iprt/ftp.h>
46#include <iprt/getopt.h>
47#include <iprt/mem.h>
48#include <iprt/log.h>
49#include <iprt/path.h>
50#include <iprt/poll.h>
51#include <iprt/socket.h>
52#include <iprt/string.h>
53#include <iprt/system.h>
54#include <iprt/tcp.h>
55
56#include "internal/magics.h"
57
58
59/*********************************************************************************************************************************
60* Structures and Typedefs *
61*********************************************************************************************************************************/
62/**
63 * Internal FTP server instance.
64 */
65typedef struct RTFTPSERVERINTERNAL
66{
67 /** Magic value. */
68 uint32_t u32Magic;
69 /** Callback table. */
70 RTFTPSERVERCALLBACKS Callbacks;
71 /** Pointer to TCP server instance. */
72 PRTTCPSERVER pTCPServer;
73 /** Number of currently connected clients. */
74 uint32_t cClients;
75} RTFTPSERVERINTERNAL;
76/** Pointer to an internal FTP server instance. */
77typedef RTFTPSERVERINTERNAL *PRTFTPSERVERINTERNAL;
78
79
80/*********************************************************************************************************************************
81* Defined Constants And Macros *
82*********************************************************************************************************************************/
83/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
84#define RTFTPSERVER_VALID_RETURN_RC(hFTPServer, a_rc) \
85 do { \
86 AssertPtrReturn((hFTPServer), (a_rc)); \
87 AssertReturn((hFTPServer)->u32Magic == RTFTPSERVER_MAGIC, (a_rc)); \
88 } while (0)
89
90/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
91#define RTFTPSERVER_VALID_RETURN(hFTPServer) RTFTPSERVER_VALID_RETURN_RC((hFTPServer), VERR_INVALID_HANDLE)
92
93/** Validates a handle and returns (void) if not valid. */
94#define RTFTPSERVER_VALID_RETURN_VOID(hFTPServer) \
95 do { \
96 AssertPtrReturnVoid(hFTPServer); \
97 AssertReturnVoid((hFTPServer)->u32Magic == RTFTPSERVER_MAGIC); \
98 } while (0)
99
100/** Supported FTP server command IDs.
101 * Alphabetically, named after their official command names. */
102typedef enum RTFTPSERVER_CMD
103{
104 /** Invalid command, do not use. Always must come first. */
105 RTFTPSERVER_CMD_INVALID = 0,
106 /** Aborts the current command on the server. */
107 RTFTPSERVER_CMD_ABOR,
108 /** Changes the current working directory. */
109 RTFTPSERVER_CMD_CDUP,
110 /** Changes the current working directory. */
111 RTFTPSERVER_CMD_CWD,
112 /** Lists a directory. */
113 RTFTPSERVER_CMD_LIST,
114 /** Sets the transfer mode. */
115 RTFTPSERVER_CMD_MODE,
116 /** Sends a nop ("no operation") to the server. */
117 RTFTPSERVER_CMD_NOOP,
118 /** Sets the password for authentication. */
119 RTFTPSERVER_CMD_PASS,
120 /** Sets the port to use for the data connection. */
121 RTFTPSERVER_CMD_PORT,
122 /** Gets the current working directory. */
123 RTFTPSERVER_CMD_PWD,
124 /** Terminates the session (connection). */
125 RTFTPSERVER_CMD_QUIT,
126 /** Retrieves a specific file. */
127 RTFTPSERVER_CMD_RETR,
128 /** Recursively gets a directory (and its contents). */
129 RTFTPSERVER_CMD_RGET,
130 /** Retrieves the current status of a transfer. */
131 RTFTPSERVER_CMD_STAT,
132 /** Gets the server's OS info. */
133 RTFTPSERVER_CMD_SYST,
134 /** Sets the (data) representation type. */
135 RTFTPSERVER_CMD_TYPE,
136 /** Sets the user name for authentication. */
137 RTFTPSERVER_CMD_USER,
138 /** End marker. */
139 RTFTPSERVER_CMD_LAST,
140 /** The usual 32-bit hack. */
141 RTFTPSERVER_CMD_32BIT_HACK = 0x7fffffff
142} RTFTPSERVER_CMD;
143
144/**
145 * Structure for maintaining an internal FTP server client.
146 */
147typedef struct RTFTPSERVERCLIENT
148{
149 /** Pointer to internal server state. */
150 PRTFTPSERVERINTERNAL pServer;
151 /** Socket handle the client is bound to. */
152 RTSOCKET hSocket;
153 /** Actual client state. */
154 RTFTPSERVERCLIENTSTATE State;
155} RTFTPSERVERCLIENT;
156/** Pointer to an internal FTP server client state. */
157typedef RTFTPSERVERCLIENT *PRTFTPSERVERCLIENT;
158
159/** Function pointer declaration for a specific FTP server command handler. */
160typedef DECLCALLBACK(int) FNRTFTPSERVERCMD(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs);
161/** Pointer to a FNRTFTPSERVERCMD(). */
162typedef FNRTFTPSERVERCMD *PFNRTFTPSERVERCMD;
163
164/** Handles a FTP server callback with no arguments and returns. */
165#define RTFTPSERVER_HANDLE_CALLBACK_RET(a_Name) \
166 do \
167 { \
168 PRTFTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
169 if (pCallbacks->a_Name) \
170 { \
171 RTFTPCALLBACKDATA Data = { &pClient->State, pCallbacks->pvUser, pCallbacks->cbUser }; \
172 return pCallbacks->a_Name(&Data); \
173 } \
174 } while (0)
175
176/** Handles a FTP server callback with arguments and returns. */
177#define RTFTPSERVER_HANDLE_CALLBACK_VA_RET(a_Name, ...) \
178 do \
179 { \
180 PRTFTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
181 if (pCallbacks->a_Name) \
182 { \
183 RTFTPCALLBACKDATA Data = { &pClient->State, pCallbacks->pvUser, pCallbacks->cbUser }; \
184 return pCallbacks->a_Name(&Data, __VA_ARGS__); \
185 } \
186 } while (0)
187
188/**
189 * Function prototypes for command handlers.
190 */
191static FNRTFTPSERVERCMD rtFtpServerHandleABOR;
192static FNRTFTPSERVERCMD rtFtpServerHandleCDUP;
193static FNRTFTPSERVERCMD rtFtpServerHandleCWD;
194static FNRTFTPSERVERCMD rtFtpServerHandleLIST;
195static FNRTFTPSERVERCMD rtFtpServerHandleMODE;
196static FNRTFTPSERVERCMD rtFtpServerHandleNOOP;
197static FNRTFTPSERVERCMD rtFtpServerHandlePORT;
198static FNRTFTPSERVERCMD rtFtpServerHandlePWD;
199static FNRTFTPSERVERCMD rtFtpServerHandleQUIT;
200static FNRTFTPSERVERCMD rtFtpServerHandleRETR;
201static FNRTFTPSERVERCMD rtFtpServerHandleRGET;
202static FNRTFTPSERVERCMD rtFtpServerHandleSTAT;
203static FNRTFTPSERVERCMD rtFtpServerHandleSYST;
204static FNRTFTPSERVERCMD rtFtpServerHandleTYPE;
205
206/**
207 * Structure for maintaining a single command entry for the command table.
208 */
209typedef struct RTFTPSERVER_CMD_ENTRY
210{
211 /** Command ID. */
212 RTFTPSERVER_CMD enmCmd;
213 /** Command represented as ASCII string. */
214 char szCmd[RTFTPSERVER_MAX_CMD_LEN];
215 /** Function pointer invoked to handle the command. */
216 PFNRTFTPSERVERCMD pfnCmd;
217} RTFTPSERVER_CMD_ENTRY;
218
219/**
220 * Table of handled commands.
221 */
222const RTFTPSERVER_CMD_ENTRY g_aCmdMap[] =
223{
224 { RTFTPSERVER_CMD_ABOR, "ABOR", rtFtpServerHandleABOR },
225 { RTFTPSERVER_CMD_CDUP, "CDUP", rtFtpServerHandleCDUP },
226 { RTFTPSERVER_CMD_CWD, "CWD", rtFtpServerHandleCWD },
227 { RTFTPSERVER_CMD_LIST, "LIST", rtFtpServerHandleLIST },
228 { RTFTPSERVER_CMD_MODE, "MODE", rtFtpServerHandleMODE },
229 { RTFTPSERVER_CMD_NOOP, "NOOP", rtFtpServerHandleNOOP },
230 { RTFTPSERVER_CMD_PORT, "PORT", rtFtpServerHandlePORT },
231 { RTFTPSERVER_CMD_PWD, "PWD", rtFtpServerHandlePWD },
232 { RTFTPSERVER_CMD_QUIT, "QUIT", rtFtpServerHandleQUIT },
233 { RTFTPSERVER_CMD_RETR, "RETR", rtFtpServerHandleRETR },
234 { RTFTPSERVER_CMD_RGET, "RGET", rtFtpServerHandleRGET },
235 { RTFTPSERVER_CMD_STAT, "STAT", rtFtpServerHandleSTAT },
236 { RTFTPSERVER_CMD_SYST, "SYST", rtFtpServerHandleSYST },
237 { RTFTPSERVER_CMD_TYPE, "TYPE", rtFtpServerHandleTYPE },
238 { RTFTPSERVER_CMD_LAST, "", NULL }
239};
240
241
242/*********************************************************************************************************************************
243* Protocol Functions *
244*********************************************************************************************************************************/
245
246/**
247 * Replies a (three digit) reply code back to the client.
248 *
249 * @returns VBox status code.
250 * @param pClient Client to reply to.
251 * @param enmReply Reply code to send.
252 */
253static int rtFtpServerSendReplyRc(PRTFTPSERVERCLIENT pClient, RTFTPSERVER_REPLY enmReply)
254{
255 char szReply[32];
256 RTStrPrintf2(szReply, sizeof(szReply), "%RU32\r\n", enmReply);
257
258 return RTTcpWrite(pClient->hSocket, szReply, strlen(szReply) + 1);
259}
260
261/**
262 * Replies a string back to the client.
263 *
264 * @returns VBox status code.
265 * @param pClient Client to reply to.
266 * @param pcszStr String to reply.
267 */
268static int rtFtpServerSendReplyStr(PRTFTPSERVERCLIENT pClient, const char *pcszStr)
269{
270 char *pszReply;
271 int rc = RTStrAPrintf(&pszReply, "%s\r\n", pcszStr);
272 if (RT_SUCCESS(rc))
273 {
274 rc = RTTcpWrite(pClient->hSocket, pszReply, strlen(pszReply) + 1);
275 RTStrFree(pszReply);
276 return rc;
277 }
278
279 return VERR_NO_MEMORY;
280}
281
282/**
283 * Looks up an user account.
284 *
285 * @returns VBox status code, or VERR_NOT_FOUND if user has not been found.
286 * @param pClient Client to look up user for.
287 * @param pcszUser User name to look up.
288 */
289static int rtFtpServerLookupUser(PRTFTPSERVERCLIENT pClient, const char *pcszUser)
290{
291 RTFTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnUserConnect, pcszUser);
292
293 return VERR_NOT_FOUND;
294}
295
296/**
297 * Handles the actual client authentication.
298 *
299 * @returns VBox status code, or VERR_ACCESS_DENIED if authentication failed.
300 * @param pClient Client to authenticate.
301 * @param pcszUser User name to authenticate with.
302 * @param pcszPassword Password to authenticate with.
303 */
304static int rtFtpServerAuthenticate(PRTFTPSERVERCLIENT pClient, const char *pcszUser, const char *pcszPassword)
305{
306 RTFTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnUserAuthenticate, pcszUser, pcszPassword);
307
308 return VERR_ACCESS_DENIED;
309}
310
311
312/*********************************************************************************************************************************
313* Command Protocol Handlers *
314*********************************************************************************************************************************/
315
316static int rtFtpServerHandleABOR(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
317{
318 RT_NOREF(pClient, cArgs, apcszArgs);
319
320 /** @todo Anything to do here? */
321 return VINF_SUCCESS;
322}
323
324static int rtFtpServerHandleCDUP(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
325{
326 RT_NOREF(pClient, cArgs, apcszArgs);
327
328 /** @todo Anything to do here? */
329 return VINF_SUCCESS;
330}
331
332static int rtFtpServerHandleCWD(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
333{
334 AssertPtrReturn(apcszArgs, VERR_INVALID_POINTER);
335
336 if (cArgs != 1)
337 return VERR_INVALID_PARAMETER;
338
339 RTFTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnPathSetCurrent, apcszArgs[0]);
340
341 return VERR_NOT_IMPLEMENTED;
342}
343
344static int rtFtpServerHandleLIST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
345{
346 RT_NOREF(cArgs, apcszArgs);
347
348 void *pvData = NULL;
349 size_t cbData = 0;
350
351 RTFTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnList, &pvData, &cbData);
352
353 return VERR_NOT_IMPLEMENTED;
354}
355
356static int rtFtpServerHandleMODE(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
357{
358 RT_NOREF(pClient, cArgs, apcszArgs);
359
360 /** @todo Anything to do here? */
361 return VINF_SUCCESS;
362}
363
364static int rtFtpServerHandleNOOP(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
365{
366 RT_NOREF(pClient, cArgs, apcszArgs);
367
368 /* Nothing to do here. */
369 return VINF_SUCCESS;
370}
371
372static int rtFtpServerHandlePORT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
373{
374 RT_NOREF(pClient, cArgs, apcszArgs);
375
376 /** @todo Anything to do here? */
377 return VINF_SUCCESS;
378}
379
380static int rtFtpServerHandlePWD(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
381{
382 RT_NOREF(cArgs, apcszArgs);
383
384#if 0
385 char *pszReply;
386 int rc = RTStrAPrintf(&pszReply, "%s\r\n", pClient->szCWD);
387 if (RT_SUCCESS(rc))
388 {
389 rc = RTTcpWrite(pClient->hSocket, pszReply, strlen(pszReply) + 1);
390 RTStrFree(pszReply);
391 return rc;
392 }
393
394 return VERR_NO_MEMORY;
395#endif
396
397 RT_NOREF(pClient);
398 return 0;
399}
400
401static int rtFtpServerHandleQUIT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
402{
403 RT_NOREF(pClient, cArgs, apcszArgs);
404
405 /** @todo Anything to do here? */
406 return VINF_SUCCESS;
407}
408
409static int rtFtpServerHandleRETR(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
410{
411 RT_NOREF(pClient, cArgs, apcszArgs);
412
413 /** @todo Anything to do here? */
414 return VINF_SUCCESS;
415}
416
417static int rtFtpServerHandleRGET(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
418{
419 RT_NOREF(pClient, cArgs, apcszArgs);
420
421 /** @todo Anything to do here? */
422 return VINF_SUCCESS;
423}
424
425static int rtFtpServerHandleSTAT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
426{
427 RT_NOREF(pClient, cArgs, apcszArgs);
428
429 /** @todo Anything to do here? */
430 return VINF_SUCCESS;
431}
432
433static int rtFtpServerHandleSYST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
434{
435 RT_NOREF(cArgs, apcszArgs);
436
437 char szOSInfo[64];
438 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szOSInfo, sizeof(szOSInfo));
439 if (RT_SUCCESS(rc))
440 rc = rtFtpServerSendReplyStr(pClient, szOSInfo);
441
442 return rc;
443}
444
445static int rtFtpServerHandleTYPE(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
446{
447 RT_NOREF(pClient, cArgs, apcszArgs);
448
449 /** @todo Anything to do here? */
450 return VINF_SUCCESS;
451}
452
453
454/*********************************************************************************************************************************
455* Internal server functions *
456*********************************************************************************************************************************/
457
458/**
459 * Handles the client's login procedure.
460 *
461 * @returns VBox status code.
462 * @param pClient Client to handle login procedure for.
463 */
464static int rtFtpServerDoLogin(PRTFTPSERVERCLIENT pClient)
465{
466 LogFlowFuncEnter();
467
468 /* Send welcome message. */
469 int rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_READY_FOR_NEW_USER);
470 if (RT_SUCCESS(rc))
471 {
472 size_t cbRead;
473
474 char szUser[64];
475 rc = RTTcpRead(pClient->hSocket, szUser, sizeof(szUser), &cbRead);
476 if (RT_SUCCESS(rc))
477 {
478 rc = rtFtpServerLookupUser(pClient, szUser);
479 if (RT_SUCCESS(rc))
480 {
481 rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_USERNAME_OKAY_NEED_PASSWORD);
482 if (RT_SUCCESS(rc))
483 {
484 char szPass[64];
485 rc = RTTcpRead(pClient->hSocket, szPass, sizeof(szPass), &cbRead);
486 {
487 if (RT_SUCCESS(rc))
488 {
489 rc = rtFtpServerAuthenticate(pClient, szUser, szPass);
490 if (RT_SUCCESS(rc))
491 {
492 rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_LOGGED_IN_PROCEED);
493 }
494 }
495 }
496 }
497 }
498 }
499 }
500
501 if (RT_FAILURE(rc))
502 {
503 int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_NOT_LOGGED_IN);
504 if (RT_SUCCESS(rc))
505 rc = rc2;
506 }
507
508 return rc;
509}
510
511/**
512 * Parses FTP command arguments handed in by the client.
513 *
514 * @returns VBox status code.
515 * @param pcszCmdParms Pointer to command arguments, if any. Can be NULL if no arguments are given.
516 * @param pcArgs Returns the number of parsed arguments, separated by a space (hex 0x20).
517 * @param ppapcszArgs Returns the string array of parsed arguments. Needs to be free'd with rtFtpServerCmdArgsFree().
518 */
519static int rtFtpServerCmdArgsParse(const char *pcszCmdParms, uint8_t *pcArgs, char ***ppapcszArgs)
520{
521 *pcArgs = 0;
522 *ppapcszArgs = NULL;
523
524 if (!pcszCmdParms) /* No parms given? Bail out early. */
525 return VINF_SUCCESS;
526
527 /** @todo Anything else to do here? */
528 /** @todo Check if quoting is correct. */
529
530 int cArgs = 0;
531 int rc = RTGetOptArgvFromString(ppapcszArgs, &cArgs, pcszCmdParms, RTGETOPTARGV_CNV_QUOTE_MS_CRT, " " /* Separators */);
532 if (RT_SUCCESS(rc))
533 {
534 if (cArgs <= UINT8_MAX)
535 {
536 *pcArgs = (uint8_t)cArgs;
537 }
538 else
539 rc = VERR_INVALID_PARAMETER;
540 }
541
542 return rc;
543}
544
545/**
546 * Frees a formerly argument string array parsed by rtFtpServerCmdArgsParse().
547 *
548 * @param ppapcszArgs Argument string array to free.
549 */
550static void rtFtpServerCmdArgsFree(char **ppapcszArgs)
551{
552 RTGetOptArgvFree(ppapcszArgs);
553}
554
555/**
556 * Main loop for processing client commands.
557 *
558 * @returns VBox status code.
559 * @param pClient Client to process commands for.
560 */
561static int rtFtpServerProcessCommands(PRTFTPSERVERCLIENT pClient)
562{
563 int rc;
564
565 for (;;)
566 {
567 size_t cbRead;
568 char szCmd[RTFTPSERVER_MAX_CMD_LEN];
569 rc = RTTcpRead(pClient->hSocket, szCmd, sizeof(szCmd), &cbRead);
570 if (RT_SUCCESS(rc))
571 {
572 /* Make sure to terminate the string in any case. */
573 szCmd[RTFTPSERVER_MAX_CMD_LEN - 1] = '\0';
574
575 /* A tiny bit of sanitation. */
576 RTStrStripL(szCmd);
577
578 /* First, terminate string by finding the command end marker (telnet style). */
579 /** @todo Not sure if this is entirely correct and/or needs tweaking; good enough for now as it seems. */
580 char *pszCmdEnd = RTStrIStr(szCmd, "\r\n");
581 if (pszCmdEnd)
582 *pszCmdEnd = '\0';
583
584 /* Second, determine if there is any parameters following. */
585 char *pszCmdParms = RTStrIStr(szCmd, " ");
586 if (pszCmdParms)
587 pszCmdParms++;
588
589 uint8_t cArgs = 0;
590 char **papszArgs = NULL;
591 rc = rtFtpServerCmdArgsParse(pszCmdParms, &cArgs, &papszArgs);
592 if (RT_SUCCESS(rc))
593 {
594 unsigned i = 0;
595 for (; i < RT_ELEMENTS(g_aCmdMap); i++)
596 {
597 if (!RTStrICmp(szCmd, g_aCmdMap[i].szCmd))
598 {
599 /* Save timestamp of last command sent. */
600 pClient->State.tsLastCmdMs = RTTimeMilliTS();
601
602 rc = g_aCmdMap[i].pfnCmd(pClient, cArgs, papszArgs);
603 break;
604 }
605 }
606
607 rtFtpServerCmdArgsFree(papszArgs);
608
609 if (i == RT_ELEMENTS(g_aCmdMap))
610 {
611 int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL);
612 if (RT_SUCCESS(rc))
613 rc = rc2;
614 }
615
616 if (g_aCmdMap[i].enmCmd == RTFTPSERVER_CMD_QUIT)
617 {
618 RTFTPSERVER_HANDLE_CALLBACK_RET(pfnOnUserDisconnect);
619 break;
620 }
621 }
622 else
623 {
624 int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
625 if (RT_SUCCESS(rc))
626 rc = rc2;
627 }
628 }
629 else
630 {
631 int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_RECOGNIZED);
632 if (RT_SUCCESS(rc))
633 rc = rc2;
634 }
635 }
636
637 return rc;
638}
639
640/**
641 * Resets the client's state.
642 *
643 * @param pState Client state to reset.
644 */
645static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState)
646{
647 pState->tsLastCmdMs = RTTimeMilliTS();
648}
649
650/**
651 * Per-client thread for serving the server's control connection.
652 *
653 * @returns VBox status code.
654 * @param hSocket Socket handle to use for the control connection.
655 * @param pvUser User-provided arguments. Of type PRTFTPSERVERINTERNAL.
656 */
657static DECLCALLBACK(int) rtFtpServerClientThread(RTSOCKET hSocket, void *pvUser)
658{
659 PRTFTPSERVERINTERNAL pThis = (PRTFTPSERVERINTERNAL)pvUser;
660 RTFTPSERVER_VALID_RETURN(pThis);
661
662 RTFTPSERVERCLIENT Client;
663 RT_ZERO(Client);
664
665 Client.pServer = pThis;
666 Client.hSocket = hSocket;
667
668 rtFtpServerClientStateReset(&Client.State);
669
670 int rc = rtFtpServerDoLogin(&Client);
671 if (RT_SUCCESS(rc))
672 {
673 ASMAtomicIncU32(&pThis->cClients);
674
675 rc = rtFtpServerProcessCommands(&Client);
676
677 ASMAtomicDecU32(&pThis->cClients);
678 }
679
680 return rc;
681}
682
683RTR3DECL(int) RTFtpServerCreate(PRTFTPSERVER phFTPServer, const char *pcszAddress, uint16_t uPort,
684 PRTFTPSERVERCALLBACKS pCallbacks)
685{
686 AssertPtrReturn(phFTPServer, VERR_INVALID_POINTER);
687 AssertPtrReturn(pcszAddress, VERR_INVALID_POINTER);
688 AssertReturn (uPort, VERR_INVALID_PARAMETER);
689 AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER);
690
691 int rc;
692
693 PRTFTPSERVERINTERNAL pThis = (PRTFTPSERVERINTERNAL)RTMemAllocZ(sizeof(RTFTPSERVERINTERNAL));
694 if (pThis)
695 {
696 pThis->u32Magic = RTFTPSERVER_MAGIC;
697 pThis->Callbacks = *pCallbacks;
698
699 rc = RTTcpServerCreate(pcszAddress, uPort, RTTHREADTYPE_DEFAULT, "ftpsrv",
700 rtFtpServerClientThread, pThis /* pvUser */, &pThis->pTCPServer);
701 }
702 else
703 rc = VERR_NO_MEMORY;
704
705 return rc;
706}
707
708RTR3DECL(int) RTFtpServerDestroy(RTFTPSERVER hFTPServer)
709{
710 if (hFTPServer == NIL_RTFTPSERVER)
711 return VINF_SUCCESS;
712
713 PRTFTPSERVERINTERNAL pThis = hFTPServer;
714 RTFTPSERVER_VALID_RETURN(pThis);
715
716 AssertPtr(pThis->pTCPServer);
717
718 int rc = RTTcpServerDestroy(pThis->pTCPServer);
719 if (RT_SUCCESS(rc))
720 {
721 pThis->u32Magic = RTFTPSERVER_MAGIC_DEAD;
722
723 RTMemFree(pThis);
724 }
725
726 return rc;
727}
728
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette