VirtualBox

Changeset 82732 in vbox for trunk


Ignore:
Timestamp:
Jan 14, 2020 9:53:20 AM (5 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
135675
Message:

IPRT/FTP: More work on data connection handling. bugref:9646

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/ftp.h

    r82731 r82732  
    7676typedef enum RTFTPSERVER_TRANSFER_MODE
    7777{
    78     RTFTPSERVER_TRANSFER_MODE_UNKNOWN = 0,
    79     RTFTPSERVER_TRANSFER_MODE_STREAM,
     78    /** Default if nothing else is set. */
     79    RTFTPSERVER_TRANSFER_MODE_STREAM = 0,
    8080    RTFTPSERVER_TRANSFER_MODE_BLOCK,
    8181    RTFTPSERVER_TRANSFER_MODE_COMPRESSED,
     
    8989typedef enum RTFTPSERVER_DATA_TYPE
    9090{
    91     RTFTPSERVER_DATA_TYPE_UNKNOWN = 0,
    92     RTFTPSERVER_DATA_TYPE_ASCII,
     91    /** Default if nothing else is set. */
     92    RTFTPSERVER_DATA_TYPE_ASCII = 0,
    9393    RTFTPSERVER_DATA_TYPE_EBCDIC,
    9494    RTFTPSERVER_DATA_TYPE_IMAGE,
     
    9999
    100100/**
     101 * Enumeration for defining the struct type.
     102 */
     103typedef enum RTFTPSERVER_STRUCT_TYPE
     104{
     105    /** Default if nothing else is set. */
     106    RTFTPSERVER_STRUCT_TYPE_FILE = 0,
     107    RTFTPSERVER_STRUCT_TYPE_RECORD,
     108    RTFTPSERVER_STRUCT_TYPE_PAGE,
     109    /** The usual 32-bit hack. */
     110    RTFTPSERVER_STRUCT_TYPE_32BIT_HACK = 0x7fffffff
     111} RTFTPSERVER_STRUCT_TYPE;
     112
     113/**
    101114 * Enumeration for FTP server reply codes.
    102115 *
     
    107120    /** Invalid reply type, do not use. */
    108121    RTFTPSERVER_REPLY_INVALID                        = 0,
     122    /** Command okay. */
     123    RTFTPSERVER_REPLY_FILE_STATUS_OKAY               = 150,
    109124    /** Command okay. */
    110125    RTFTPSERVER_REPLY_OKAY                           = 200,
     
    156171    /** Current set data type. */
    157172    RTFTPSERVER_DATA_TYPE       enmDataType;
     173    /** Current set struct type. */
     174    RTFTPSERVER_STRUCT_TYPE     enmStructType;
    158175} RTFTPSERVERCLIENTSTATE;
    159176/** Pointer to a FTP server client state. */
     
    182199typedef struct RTFTPSERVERCALLBACKS
    183200{
    184     /** User pointer to data. Optional and can be NULL. */
    185     void  *pvUser;
    186     /** Size (in bytes) of user data pointing at. Optional and can be 0. */
    187     size_t cbUser;
    188201    /**
    189202     * Callback which gets invoked when a user connected.
     
    208221     * @returns VBox status code.
    209222     * @param   pData           Pointer to generic callback data.
    210      */
    211     DECLCALLBACKMEMBER(int,  pfnOnUserDisconnect)(PRTFTPCALLBACKDATA pData);
     223     * @param   pcszUser        User name which disconnected.
     224     */
     225    DECLCALLBACKMEMBER(int,  pfnOnUserDisconnect)(PRTFTPCALLBACKDATA pData, const char *pcszUser);
     226    /**
     227     * Callback which gets invoked when the client wants to start reading or writing a file.
     228     *
     229     * @returns VBox status code.
     230     * @param   pData           Pointer to generic callback data.
     231     * @param   pcsszPath       Path of file to handle.
     232     * @param   fMode           File mode to use (IPRT stlye).
     233     * @param   ppvHandle       Opaque file handle only known to the callback implementation.
     234     */
    212235    DECLCALLBACKMEMBER(int,  pfnOnFileOpen)(PRTFTPCALLBACKDATA pData, const char *pcszPath, uint32_t fMode, void **ppvHandle);
     236    /**
     237     * Callback which gets invoked when the client wants to read from a file.
     238     *
     239     * @returns VBox status code.
     240     * @param   pData           Pointer to generic callback data.
     241     * @param   pvHandle        Opaque file handle only known to the callback implementation.
     242     * @param   pvBuf           Where to store the read file data.
     243     * @param   cbToRead        How much (in bytes) to read. Must at least supply the size of pvBuf.
     244     * @param   pcbRead         How much (in bytes) was read. Optional.
     245     */
    213246    DECLCALLBACKMEMBER(int,  pfnOnFileRead)(PRTFTPCALLBACKDATA pData, void *pvHandle, void *pvBuf, size_t cbToRead, size_t *pcbRead);
     247    /**
     248     * Callback which gets invoked when the client is done reading from or writing to a file.
     249     *
     250     * @returns VBox status code.
     251     * @param   pData           Pointer to generic callback data.
     252     * @param   ppvHandle       Opaque file handle only known to the callback implementation.
     253     */
    214254    DECLCALLBACKMEMBER(int,  pfnOnFileClose)(PRTFTPCALLBACKDATA pData, void *pvHandle);
    215255    /**
     
    278318 * @param   uPort               The port for creating a listening socket.
    279319 * @param   pCallbacks          Callback table to use.
     320 * @param   pvUser              Pointer to user-specific data. Optional.
     321 * @param   cbUser              Size of user-specific data. Optional.
    280322 */
    281323RTR3DECL(int) RTFtpServerCreate(PRTFTPSERVER phFTPServer, const char *pcszAddress, uint16_t uPort,
    282                                 PRTFTPSERVERCALLBACKS pCallbacks);
     324                                PRTFTPSERVERCALLBACKS pCallbacks, void *pvUser, size_t cbUser);
    283325
    284326/**
  • trunk/src/VBox/Runtime/generic/ftp-server.cpp

    r82728 r82732  
    2929 * Known limitations so far:
    3030 * - UTF-8 support only.
     31 * - Only supports ASCII + binary (image type) file streams for now.
    3132 * - No support for writing / modifying ("DELE", "MKD", "RMD", "STOR", ++).
    3233 * - No FTPS / SFTP support.
     
    4849#include <iprt/asm.h>
    4950#include <iprt/assert.h>
    50 #include <iprt/errcore.h>
     51#include <iprt/err.h>
    5152#include <iprt/file.h> /* For file mode flags. */
    5253#include <iprt/getopt.h>
     
    7778    /** Number of currently connected clients. */
    7879    uint32_t                cClients;
     80    /** Pointer to user-specific data. Optional. */
     81    void                   *pvUser;
     82    /** Size of user-specific data. Optional. */
     83    size_t                  cbUser;
    7984} RTFTPSERVERINTERNAL;
    8085/** Pointer to an internal FTP server instance. */
     
    134139    /** Retrieves the current status of a transfer. */
    135140    RTFTPSERVER_CMD_STAT,
     141    /** Sets the structure type to use. */
     142    RTFTPSERVER_CMD_STRU,
    136143    /** Gets the server's OS info. */
    137144    RTFTPSERVER_CMD_SYST,
     
    164171    /** Thread stop indicator. */
    165172    volatile bool               fStop;
     173    /** Thread stopped indicator. */
     174    volatile bool               fStopped;
     175    /** Overall result of data connection on stop. */
     176    int                         rc;
     177    /** For now we only support sending a single file per active data connection. */
    166178    char                        szFile[RTPATH_MAX];
    167179} RTFTPSERVERDATACONN;
     
    199211        if (pCallbacks->a_Name) \
    200212        { \
    201             RTFTPCALLBACKDATA Data = { &pClient->State, pCallbacks->pvUser, pCallbacks->cbUser }; \
     213            RTFTPCALLBACKDATA Data = { &pClient->State }; \
    202214            return pCallbacks->a_Name(&Data); \
    203215        } \
     
    213225        if (pCallbacks->a_Name) \
    214226        { \
    215             RTFTPCALLBACKDATA Data = { &pClient->State, pCallbacks->pvUser, pCallbacks->cbUser }; \
     227            RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
    216228            rc = pCallbacks->a_Name(&Data); \
    217229        } \
     
    227239        if (pCallbacks->a_Name) \
    228240        { \
    229             RTFTPCALLBACKDATA Data = { &pClient->State, pCallbacks->pvUser, pCallbacks->cbUser }; \
     241            RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
    230242            rc = pCallbacks->a_Name(&Data, __VA_ARGS__); \
    231243        } \
     
    241253        if (pCallbacks->a_Name) \
    242254        { \
    243             RTFTPCALLBACKDATA Data = { &pClient->State, pCallbacks->pvUser, pCallbacks->cbUser }; \
     255            RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
    244256            return pCallbacks->a_Name(&Data, __VA_ARGS__); \
    245257        } \
     
    253265*********************************************************************************************************************************/
    254266
    255 static int rtFtpServerDataPortOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort);
     267static void rtFtpServerDataConnReset(PRTFTPSERVERDATACONN pDataConn);
     268static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort);
     269static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState);
    256270
    257271/**
     
    271285static FNRTFTPSERVERCMD rtFtpServerHandleSIZE;
    272286static FNRTFTPSERVERCMD rtFtpServerHandleSTAT;
     287static FNRTFTPSERVERCMD rtFtpServerHandleSTRU;
    273288static FNRTFTPSERVERCMD rtFtpServerHandleSYST;
    274289static FNRTFTPSERVERCMD rtFtpServerHandleTYPE;
     
    307322    { RTFTPSERVER_CMD_SIZE,     "SIZE",         rtFtpServerHandleSIZE },
    308323    { RTFTPSERVER_CMD_STAT,     "STAT",         rtFtpServerHandleSTAT },
     324    { RTFTPSERVER_CMD_STRU,     "STRU",         rtFtpServerHandleSTRU },
    309325    { RTFTPSERVER_CMD_SYST,     "SYST",         rtFtpServerHandleSYST },
    310326    { RTFTPSERVER_CMD_TYPE,     "TYPE",         rtFtpServerHandleTYPE },
     
    543559 * @param   uPort               Port for the data connection.
    544560 */
    545 static int rtFtpServerDataPortOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort)
    546 {
    547     RT_NOREF(pAddr);
    548 
     561static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort)
     562{
     563    LogFlowFuncEnter();
     564
     565    /** @todo Implement IPv6 handling here. */
    549566    char szAddress[32];
    550567    const ssize_t cchAdddress = RTStrPrintf2(szAddress, sizeof(szAddress), "%RU8.%RU8.%RU8.%RU8",
     
    561578 * @param   pDataConn           Data connection to close.
    562579 */
    563 static int rtFtpServerDataPortClose(PRTFTPSERVERDATACONN pDataConn)
     580static int rtFtpServerDataConnClose(PRTFTPSERVERDATACONN pDataConn)
    564581{
    565582    int rc = VINF_SUCCESS;
     
    567584    if (pDataConn->hSocket != NIL_RTSOCKET)
    568585    {
    569         rc = RTTcpClientClose(pDataConn->hSocket);
    570         pDataConn->hSocket = NIL_RTSOCKET;
    571     }
    572 
    573     return rc;
     586        LogFlowFuncEnter();
     587
     588        rc = RTTcpFlush(pDataConn->hSocket);
     589        if (RT_SUCCESS(rc))
     590        {
     591            rc = RTTcpClientClose(pDataConn->hSocket);
     592            pDataConn->hSocket = NIL_RTSOCKET;
     593        }
     594    }
     595
     596    return rc;
     597}
     598
     599/**
     600 * Writes data to the data connection.
     601 *
     602 * @returns VBox status code.
     603 * @param   pDataConn           Data connection to write to.
     604 * @param   pvData              Data to write.
     605 * @param   cbData              Size (in bytes) of data to write.
     606 * @param   pcbWritten          How many bytes were written. Optional and unused atm.
     607 */
     608static int rtFtpServerDataConnWrite(PRTFTPSERVERDATACONN pDataConn, const void *pvData, size_t cbData, size_t *pcbWritten)
     609{
     610    RT_NOREF(pcbWritten);
     611
     612    return RTTcpWrite(pDataConn->hSocket, pvData, cbData);
    574613}
    575614
     
    590629    PRTFTPSERVERDATACONN pDataConn = &pClient->DataConn;
    591630
    592     int rc = rtFtpServerDataPortOpen(pDataConn, &pDataConn->Addr, pDataConn->uPort);
     631    LogFlowFuncEnter();
     632
     633    int rc = rtFtpServerDataConnOpen(pDataConn, &pDataConn->Addr, pDataConn->uPort);
    593634    if (RT_FAILURE(rc))
    594635        return rc;
     
    599640        return VERR_NO_MEMORY;
    600641
    601     pDataConn->fStop    = false;
     642    /* Set start indicator. */
    602643    pDataConn->fStarted = true;
    603644
     
    612653    if (RT_SUCCESS(rc))
    613654    {
     655        LogFlowFunc(("Transfer started\n"));
     656
    614657        do
    615658        {
    616659            size_t cbRead = 0;
    617660            RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileRead, pvHandle, pvBuf, cbBuf, &cbRead);
    618             if (RT_SUCCESS(rc))
    619                 rc = RTTcpWrite(pClient->DataConn.hSocket, pvBuf, cbRead);
     661            if (   RT_SUCCESS(rc)
     662                && cbRead)
     663            {
     664                rc = rtFtpServerDataConnWrite(pDataConn, pvBuf, cbRead, NULL /* pcbWritten */);
     665            }
    620666
    621667            if (   !cbRead
    622668                || ASMAtomicReadBool(&pDataConn->fStop))
     669            {
    623670                break;
     671            }
    624672        }
    625673        while (RT_SUCCESS(rc));
    626674
    627675        RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileClose, pvHandle);
    628     }
    629 
    630     rtFtpServerDataPortClose(&pClient->DataConn);
     676
     677        LogFlowFunc(("Transfer done\n"));
     678    }
     679
     680    rtFtpServerDataConnClose(pDataConn);
    631681
    632682    RTMemFree(pvBuf);
    633683    pvBuf = NULL;
    634684
     685    pDataConn->fStopped = true;
     686    pDataConn->rc       = rc;
     687
     688    LogFlowFuncLeaveRC(rc);
    635689    return rc;
    636690}
     
    683737    if (RT_SUCCESS(rc))
    684738    {
    685         if (pDataConn->hSocket != NIL_RTSOCKET)
    686         {
    687             RTTcpClientClose(pDataConn->hSocket);
    688             pDataConn->hSocket = NIL_RTSOCKET;
    689         }
    690 
    691         pDataConn->fStarted = false;
    692         pDataConn->hThread  = NIL_RTTHREAD;
     739        rtFtpServerDataConnClose(pDataConn);
     740        rtFtpServerDataConnReset(pDataConn);
    693741
    694742        rc = rcThread;
     
    696744
    697745    return rc;
     746}
     747
     748/**
     749 * Resets a data connection structure.
     750 *
     751 * @returns VBox status code.
     752 * @param   pDataConn           Data connection structure to reset.
     753 */
     754static void rtFtpServerDataConnReset(PRTFTPSERVERDATACONN pDataConn)
     755{
     756    LogFlowFuncEnter();
     757
     758    pDataConn->hSocket  = NIL_RTSOCKET;
     759    pDataConn->uPort    = 20; /* Default port to use. */
     760    pDataConn->hThread  = NIL_RTTHREAD;
     761    pDataConn->fStarted = false;
     762    pDataConn->fStop    = false;
     763    pDataConn->fStopped = false;
     764    pDataConn->rc       = VERR_IPE_UNINITIALIZED_STATUS;
    698765}
    699766
     
    814881
    815882    /* Only allow one data connection per client at a time. */
    816     rtFtpServerDataPortClose(&pClient->DataConn);
     883    rtFtpServerDataConnClose(&pClient->DataConn);
    817884
    818885    int rc = rtFtpParseHostAndPort(apcszArgs[0], &pClient->DataConn.Addr, &pClient->DataConn.uPort);
     
    863930    if (RT_SUCCESS(rc))
    864931    {
    865         rc = RTStrCopy(pClient->DataConn.szFile, sizeof(pClient->DataConn.szFile), pcszPath);
     932        rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_FILE_STATUS_OKAY);
    866933        if (RT_SUCCESS(rc))
    867934        {
    868             rc = rtFtpServerDataConnCreate(pClient, &pClient->DataConn);
     935            rc = RTStrCopy(pClient->DataConn.szFile, sizeof(pClient->DataConn.szFile), pcszPath);
    869936            if (RT_SUCCESS(rc))
    870937            {
    871                 rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
     938                rc = rtFtpServerDataConnCreate(pClient, &pClient->DataConn);
    872939            }
    873940        }
     
    9501017}
    9511018
     1019static int rtFtpServerHandleSTRU(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
     1020{
     1021    if (cArgs != 1)
     1022        return VERR_INVALID_PARAMETER;
     1023
     1024    const char *pcszType = apcszArgs[0];
     1025
     1026    int rc;
     1027
     1028    if (!RTStrICmp(pcszType, "F"))
     1029    {
     1030        pClient->State.enmStructType = RTFTPSERVER_STRUCT_TYPE_FILE;
     1031
     1032        rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
     1033    }
     1034    else
     1035        rc = VERR_NOT_IMPLEMENTED;
     1036
     1037    return rc;
     1038}
     1039
    9521040static int rtFtpServerHandleSYST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    9531041{
     
    9711059    int rc = VINF_SUCCESS;
    9721060
    973     if (!RTStrICmp(pcszType, "A")) /* ASCII (can be 7 or 8 bits). */
     1061    if (!RTStrICmp(pcszType, "A"))
    9741062    {
    9751063        pClient->State.enmDataType = RTFTPSERVER_DATA_TYPE_ASCII;
     
    9801068    }
    9811069    else /** @todo Support "E" (EBCDIC) and/or "L <size>" (custom)? */
    982         rc = VERR_INVALID_PARAMETER;
     1070        rc = VERR_NOT_IMPLEMENTED;
     1071
     1072    if (RT_SUCCESS(rc))
     1073        rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
    9831074
    9841075    return rc;
     
    9931084    AssertPtrReturn(pcszUser, VERR_INVALID_PARAMETER);
    9941085
    995     if (pClient->State.pszUser)
    996     {
    997         RTStrFree(pClient->State.pszUser);
    998         pClient->State.pszUser = NULL;
    999     }
     1086    rtFtpServerClientStateReset(&pClient->State);
    10001087
    10011088    int rc = rtFtpServerLookupUser(pClient, pcszUser);
     
    10691156
    10701157/**
    1071  * Main loop for processing client commands.
     1158 * Main function for processing client commands for the control connection.
    10721159 *
    10731160 * @returns VBox status code.
    10741161 * @param   pClient             Client to process commands for.
     1162 * @param   pcszCmd             Command string to parse and handle.
     1163 * @param   cbCmd               Size (in bytes) of command string.
     1164 */
     1165static int rtFtpServerProcessCommands(PRTFTPSERVERCLIENT pClient, char *pcszCmd, size_t cbCmd)
     1166{
     1167    /* Make sure to terminate the string in any case. */
     1168    pcszCmd[RT_MIN(RTFTPSERVER_MAX_CMD_LEN, cbCmd)] = '\0';
     1169
     1170    /* A tiny bit of sanitation. */
     1171    RTStrStripL(pcszCmd);
     1172
     1173    /* First, terminate string by finding the command end marker (telnet style). */
     1174    /** @todo Not sure if this is entirely correct and/or needs tweaking; good enough for now as it seems. */
     1175    char *pszCmdEnd = RTStrIStr(pcszCmd, "\r\n");
     1176    if (pszCmdEnd)
     1177        *pszCmdEnd = '\0';
     1178
     1179    int rcCmd = VINF_SUCCESS;
     1180
     1181    uint8_t cArgs     = 0;
     1182    char  **papszArgs = NULL;
     1183    int rc = rtFtpServerCmdArgsParse(pcszCmd, &cArgs, &papszArgs);
     1184    if (   RT_SUCCESS(rc)
     1185        && cArgs) /* At least the actual command (without args) must be present. */
     1186    {
     1187        unsigned i = 0;
     1188        for (; i < RT_ELEMENTS(g_aCmdMap); i++)
     1189        {
     1190            if (!RTStrICmp(papszArgs[0], g_aCmdMap[i].szCmd))
     1191            {
     1192                /* Save timestamp of last command sent. */
     1193                pClient->State.tsLastCmdMs = RTTimeMilliTS();
     1194
     1195                rcCmd = g_aCmdMap[i].pfnCmd(pClient, cArgs - 1, cArgs > 1 ? &papszArgs[1] : NULL);
     1196                break;
     1197            }
     1198        }
     1199
     1200        rtFtpServerCmdArgsFree(papszArgs);
     1201
     1202        if (i == RT_ELEMENTS(g_aCmdMap))
     1203        {
     1204            int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL);
     1205            if (RT_SUCCESS(rc))
     1206                rc = rc2;
     1207
     1208            return rc;
     1209        }
     1210
     1211        const bool fDisconnect =    g_aCmdMap[i].enmCmd == RTFTPSERVER_CMD_QUIT
     1212                                 || pClient->State.cFailedLoginAttempts >= 3; /** @todo Make this dynamic. */
     1213        if (fDisconnect)
     1214        {
     1215            int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CLOSING_CTRL_CONN);
     1216            if (RT_SUCCESS(rc))
     1217                rc = rc2;
     1218
     1219            RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnUserDisconnect, pClient->State.pszUser);
     1220            return rc;
     1221        }
     1222
     1223        switch (rcCmd)
     1224        {
     1225            case VERR_INVALID_PARAMETER:
     1226                RT_FALL_THROUGH();
     1227            case VERR_INVALID_POINTER:
     1228                rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
     1229                break;
     1230
     1231            case VERR_NOT_IMPLEMENTED:
     1232                rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL);
     1233                break;
     1234
     1235            default:
     1236                break;
     1237        }
     1238    }
     1239    else
     1240    {
     1241        int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
     1242        if (RT_SUCCESS(rc))
     1243            rc = rc2;
     1244    }
     1245
     1246    LogFlowFuncLeaveRC(rc);
     1247    return rc;
     1248}
     1249
     1250/**
     1251 * Main loop for processing client commands.
     1252 *
     1253 * @returns VBox status code.
     1254 * @param   pClient             Client to process commands for.
    10751255 */
    10761256static int rtFtpServerProcessCommands(PRTFTPSERVERCLIENT pClient)
     
    10781258    int rc;
    10791259
     1260    size_t cbRead;
     1261    char   szCmd[RTFTPSERVER_MAX_CMD_LEN + 1];
     1262
    10801263    for (;;)
    10811264    {
    1082         size_t cbRead;
    1083         char   szCmd[RTFTPSERVER_MAX_CMD_LEN];
    1084         rc = RTTcpRead(pClient->hSocket, szCmd, sizeof(szCmd), &cbRead);
     1265        rc = RTTcpSelectOne(pClient->hSocket, 200 /* ms */); /** @todo Can we improve here? Using some poll events or so? */
    10851266        if (RT_SUCCESS(rc))
    10861267        {
    1087             /* Make sure to terminate the string in any case. */
    1088             szCmd[RTFTPSERVER_MAX_CMD_LEN - 1] = '\0';
    1089 
    1090             /* A tiny bit of sanitation. */
    1091             RTStrStripL(szCmd);
    1092 
    1093             /* First, terminate string by finding the command end marker (telnet style). */
    1094             /** @todo Not sure if this is entirely correct and/or needs tweaking; good enough for now as it seems. */
    1095             char *pszCmdEnd = RTStrIStr(szCmd, "\r\n");
    1096             if (pszCmdEnd)
    1097                 *pszCmdEnd = '\0';
    1098 
    1099             int rcCmd = VINF_SUCCESS;
    1100 
    1101             uint8_t cArgs     = 0;
    1102             char  **papszArgs = NULL;
    1103             rc = rtFtpServerCmdArgsParse(szCmd, &cArgs, &papszArgs);
     1268            rc = RTTcpReadNB(pClient->hSocket, szCmd, sizeof(szCmd), &cbRead);
    11041269            if (   RT_SUCCESS(rc)
    1105                 && cArgs) /* At least the actual command (without args) must be present. */
     1270                && cbRead)
    11061271            {
    1107                 unsigned i = 0;
    1108                 for (; i < RT_ELEMENTS(g_aCmdMap); i++)
    1109                 {
    1110                     if (!RTStrICmp(papszArgs[0], g_aCmdMap[i].szCmd))
    1111                     {
    1112                         /* Save timestamp of last command sent. */
    1113                         pClient->State.tsLastCmdMs = RTTimeMilliTS();
    1114 
    1115                         rcCmd = g_aCmdMap[i].pfnCmd(pClient, cArgs - 1, cArgs > 1 ? &papszArgs[1] : NULL);
    1116                         break;
    1117                     }
    1118                 }
    1119 
    1120                 rtFtpServerCmdArgsFree(papszArgs);
    1121 
    1122                 if (i == RT_ELEMENTS(g_aCmdMap))
    1123                 {
    1124                     int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL);
    1125                     if (RT_SUCCESS(rc))
    1126                         rc = rc2;
    1127 
    1128                     continue;
    1129                 }
    1130 
    1131                 const bool fDisconnect =    g_aCmdMap[i].enmCmd == RTFTPSERVER_CMD_QUIT
    1132                                          || pClient->State.cFailedLoginAttempts >= 3; /** @todo Make this dynamic. */
    1133                 if (fDisconnect)
    1134                 {
    1135                     int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CLOSING_CTRL_CONN);
    1136                     if (RT_SUCCESS(rc))
    1137                         rc = rc2;
    1138 
    1139                     RTFTPSERVER_HANDLE_CALLBACK(pfnOnUserDisconnect);
    1140                     break;
    1141                 }
    1142 
    1143                 switch (rcCmd)
    1144                 {
    1145                     case VERR_INVALID_PARAMETER:
    1146                         RT_FALL_THROUGH();
    1147                     case VERR_INVALID_POINTER:
    1148                         rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
    1149                         break;
    1150 
    1151                     case VERR_NOT_IMPLEMENTED:
    1152                         rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL);
    1153                         break;
    1154 
    1155                     default:
    1156                         break;
    1157                 }
     1272                AssertBreakStmt(cbRead <= sizeof(szCmd), rc = VERR_BUFFER_OVERFLOW);
     1273                rc = rtFtpServerProcessCommands(pClient, szCmd, cbRead);
    11581274            }
    1159             else
     1275        }
     1276        else
     1277        {
     1278            if (rc == VERR_TIMEOUT)
     1279                rc = VINF_SUCCESS;
     1280
     1281            if (RT_FAILURE(rc))
     1282                break;
     1283
     1284            PRTFTPSERVERDATACONN pDataConn = &pClient->DataConn;
     1285
     1286            if (   ASMAtomicReadBool(&pDataConn->fStarted)
     1287                && ASMAtomicReadBool(&pDataConn->fStopped))
    11601288            {
    1161                 int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
     1289                Assert(pDataConn->rc != VERR_IPE_UNINITIALIZED_STATUS);
     1290
     1291                rc = rtFtpServerSendReplyRc(pClient, RT_SUCCESS(pDataConn->rc)
     1292                                                     ? RTFTPSERVER_REPLY_CLOSING_DATA_CONN
     1293                                                     : RTFTPSERVER_REPLY_CONN_CLOSED_TRANSFER_ABORTED);
     1294
     1295                int rc2 = rtFtpServerDataConnDestroy(pClient, pDataConn);
    11621296                if (RT_SUCCESS(rc))
    11631297                    rc = rc2;
    11641298            }
    11651299        }
    1166         else
    1167         {
    1168             int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_RECOGNIZED);
    1169             if (RT_SUCCESS(rc))
    1170                 rc = rc2;
    1171         }
    11721300    }
    11731301
     
    11771305        rc = rc2;
    11781306
     1307    LogFlowFuncLeaveRC(rc);
    11791308    return rc;
    11801309}
     
    11871316static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState)
    11881317{
     1318    LogFlowFuncEnter();
     1319
    11891320    RTStrFree(pState->pszUser);
    11901321    pState->pszUser = NULL;
    11911322
    1192     pState->tsLastCmdMs = RTTimeMilliTS();
     1323    pState->cFailedLoginAttempts = 0;
     1324    pState->tsLastCmdMs          = RTTimeMilliTS();
     1325    pState->enmDataType          = RTFTPSERVER_DATA_TYPE_ASCII;
     1326    pState->enmStructType        = RTFTPSERVER_STRUCT_TYPE_FILE;
    11931327}
    11941328
     
    12301364
    12311365RTR3DECL(int) RTFtpServerCreate(PRTFTPSERVER phFTPServer, const char *pcszAddress, uint16_t uPort,
    1232                                 PRTFTPSERVERCALLBACKS pCallbacks)
     1366                                PRTFTPSERVERCALLBACKS pCallbacks, void *pvUser, size_t cbUser)
    12331367{
    12341368    AssertPtrReturn(phFTPServer,  VERR_INVALID_POINTER);
     
    12361370    AssertReturn   (uPort,        VERR_INVALID_PARAMETER);
    12371371    AssertPtrReturn(pCallbacks,   VERR_INVALID_POINTER);
     1372    /* pvUser is optional. */
    12381373
    12391374    int rc;
     
    12441379        pThis->u32Magic  = RTFTPSERVER_MAGIC;
    12451380        pThis->Callbacks = *pCallbacks;
     1381        pThis->pvUser    = pvUser;
     1382        pThis->cbUser    = cbUser;
    12461383
    12471384        rc = RTTcpServerCreate(pcszAddress, uPort, RTTHREADTYPE_DEFAULT, "ftpsrv",
  • trunk/src/VBox/Runtime/tools/RTFTPServer.cpp

    r82727 r82732  
    186186}
    187187
    188 static DECLCALLBACK(int) onUserDisonnect(PRTFTPCALLBACKDATA pData)
     188static DECLCALLBACK(int) onUserDisonnect(PRTFTPCALLBACKDATA pData, const char *pcszUser)
    189189{
    190190    RT_NOREF(pData);
    191191
    192     RTPrintf("User disconnected\n");
     192    RTPrintf("User '%s' disconnected\n", pcszUser);
    193193
    194194    return VINF_SUCCESS;
     
    222222    Assert(pData->cbUser == sizeof(FTPSERVERDATA));
    223223
    224     return RTFileClose(pThis->hFile);
     224    int rc = RTFileClose(pThis->hFile);
     225    if (RT_SUCCESS(rc))
     226    {
     227        pThis->hFile = NIL_RTFILE;
     228    }
     229
     230    return rc;
    225231}
    226232
     
    406412        RT_ZERO(Callbacks);
    407413
    408         Callbacks.pvUser                = &g_FTPServerData;
    409         Callbacks.cbUser                = sizeof(g_FTPServerData);
    410 
    411414        Callbacks.pfnOnUserConnect      = onUserConnect;
    412415        Callbacks.pfnOnUserAuthenticate = onUserAuthenticate;
     
    423426
    424427        RTFTPSERVER hFTPServer;
    425         rc = RTFtpServerCreate(&hFTPServer, szAddress, uPort, &Callbacks);
     428        rc = RTFtpServerCreate(&hFTPServer, szAddress, uPort, &Callbacks,
     429                               &g_FTPServerData, sizeof(g_FTPServerData));
    426430        if (RT_SUCCESS(rc))
    427431        {
Note: See TracChangeset for help on using the changeset viewer.

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