VirtualBox

Changeset 82770 in vbox for trunk/src/VBox/Runtime


Ignore:
Timestamp:
Jan 15, 2020 2:34:38 PM (5 years ago)
Author:
vboxsync
Message:

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

Location:
trunk/src/VBox/Runtime
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/generic/ftp-server.cpp

    r82737 r82770  
    155155} RTFTPSERVER_CMD;
    156156
     157struct RTFTPSERVERCLIENT;
     158
    157159/**
    158160 * Structure for maintaining a single data connection.
     
    160162typedef struct RTFTPSERVERDATACONN
    161163{
     164    /** Pointer to associated client of this data connection. */
     165    RTFTPSERVERCLIENT          *pClient;
    162166    /** Data connection IP. */
    163167    RTNETADDRIPV4               Addr;
     
    175179    /** Thread stopped indicator. */
    176180    volatile bool               fStopped;
    177     /** Overall result of data connection on stop. */
     181    /** Overall result when closing the data connection. */
    178182    int                         rc;
    179     /** For now we only support sending a single file per active data connection. */
    180     char                        szFile[RTPATH_MAX];
     183    /** Number of command arguments. */
     184    uint8_t                     cArgs;
     185    /** Command arguments array. Optional and can be NULL.
     186     *  Will be free'd by the data connection thread. */
     187    char**                      papszArgs;
    181188} RTFTPSERVERDATACONN;
    182189/** Pointer to a data connection struct. */
     
    196203    /** Data connection information.
    197204     *  At the moment we only allow one data connection per client at a time. */
    198     RTFTPSERVERDATACONN         DataConn;
     205    PRTFTPSERVERDATACONN        pDataConn;
    199206} RTFTPSERVERCLIENT;
    200207/** Pointer to an internal FTP server client state. */
     
    267274*********************************************************************************************************************************/
    268275
     276static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort);
    269277static void rtFtpServerDataConnReset(PRTFTPSERVERDATACONN pDataConn);
    270 static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort);
     278static int rtFtpServerDataConnStart(PRTFTPSERVERDATACONN pDataConn, PFNRTTHREAD pfnThread,
     279                                    uint8_t cArgs, const char * const *apcszArgs);
     280static int rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn);
     281
    271282static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState);
    272283
     
    293304static FNRTFTPSERVERCMD rtFtpServerHandleUSER;
    294305
    295 
    296306/**
    297307 * Structure for maintaining a single command entry for the command table.
     
    303313    /** Command represented as ASCII string. */
    304314    char               szCmd[RTFTPSERVER_MAX_CMD_LEN];
     315    /** Whether the commands needs a logged in (valid) user. */
     316    bool               fNeedsUser;
    305317    /** Function pointer invoked to handle the command. */
    306318    PFNRTFTPSERVERCMD  pfnCmd;
    307319} RTFTPSERVER_CMD_ENTRY;
     320/** Pointer to a command entry. */
     321typedef RTFTPSERVER_CMD_ENTRY *PRTFTPSERVER_CMD_ENTRY;
    308322
    309323/**
     
    312326const RTFTPSERVER_CMD_ENTRY g_aCmdMap[] =
    313327{
    314     { RTFTPSERVER_CMD_ABOR,     "ABOR",         rtFtpServerHandleABOR },
    315     { RTFTPSERVER_CMD_CDUP,     "CDUP",         rtFtpServerHandleCDUP },
    316     { RTFTPSERVER_CMD_CWD,      "CWD",          rtFtpServerHandleCWD  },
    317     { RTFTPSERVER_CMD_FEAT,     "FEAT",         rtFtpServerHandleFEAT },
    318     { RTFTPSERVER_CMD_LIST,     "LIST",         rtFtpServerHandleLIST },
    319     { RTFTPSERVER_CMD_MODE,     "MODE",         rtFtpServerHandleMODE },
    320     { RTFTPSERVER_CMD_NOOP,     "NOOP",         rtFtpServerHandleNOOP },
    321     { RTFTPSERVER_CMD_PASS,     "PASS",         rtFtpServerHandlePASS },
    322     { RTFTPSERVER_CMD_PORT,     "PORT",         rtFtpServerHandlePORT },
    323     { RTFTPSERVER_CMD_PWD,      "PWD",          rtFtpServerHandlePWD  },
    324     { RTFTPSERVER_CMD_QUIT,     "QUIT",         rtFtpServerHandleQUIT },
    325     { RTFTPSERVER_CMD_RETR,     "RETR",         rtFtpServerHandleRETR },
    326     { RTFTPSERVER_CMD_SIZE,     "SIZE",         rtFtpServerHandleSIZE },
    327     { RTFTPSERVER_CMD_STAT,     "STAT",         rtFtpServerHandleSTAT },
    328     { RTFTPSERVER_CMD_STRU,     "STRU",         rtFtpServerHandleSTRU },
    329     { RTFTPSERVER_CMD_SYST,     "SYST",         rtFtpServerHandleSYST },
    330     { RTFTPSERVER_CMD_TYPE,     "TYPE",         rtFtpServerHandleTYPE },
    331     { RTFTPSERVER_CMD_USER,     "USER",         rtFtpServerHandleUSER },
    332     { RTFTPSERVER_CMD_LAST,     "",             NULL }
     328    { RTFTPSERVER_CMD_ABOR,     "ABOR", true,  rtFtpServerHandleABOR },
     329    { RTFTPSERVER_CMD_CDUP,     "CDUP", true,  rtFtpServerHandleCDUP },
     330    { RTFTPSERVER_CMD_CWD,      "CWD",  true,  rtFtpServerHandleCWD  },
     331    { RTFTPSERVER_CMD_FEAT,     "FEAT", true,  rtFtpServerHandleFEAT },
     332    { RTFTPSERVER_CMD_LIST,     "LIST", true,  rtFtpServerHandleLIST },
     333    { RTFTPSERVER_CMD_MODE,     "MODE", true,  rtFtpServerHandleMODE },
     334    { RTFTPSERVER_CMD_NOOP,     "NOOP", true,  rtFtpServerHandleNOOP },
     335    { RTFTPSERVER_CMD_PASS,     "PASS", false, rtFtpServerHandlePASS },
     336    { RTFTPSERVER_CMD_PORT,     "PORT", true,  rtFtpServerHandlePORT },
     337    { RTFTPSERVER_CMD_PWD,      "PWD",  true,  rtFtpServerHandlePWD  },
     338    { RTFTPSERVER_CMD_QUIT,     "QUIT", false, rtFtpServerHandleQUIT },
     339    { RTFTPSERVER_CMD_RETR,     "RETR", true,  rtFtpServerHandleRETR },
     340    { RTFTPSERVER_CMD_SIZE,     "SIZE", true,  rtFtpServerHandleSIZE },
     341    { RTFTPSERVER_CMD_STAT,     "STAT", true,  rtFtpServerHandleSTAT },
     342    { RTFTPSERVER_CMD_STRU,     "STRU", true,  rtFtpServerHandleSTRU },
     343    { RTFTPSERVER_CMD_SYST,     "SYST", false, rtFtpServerHandleSYST },
     344    { RTFTPSERVER_CMD_TYPE,     "TYPE", true,  rtFtpServerHandleTYPE },
     345    { RTFTPSERVER_CMD_USER,     "USER", false, rtFtpServerHandleUSER },
     346    { RTFTPSERVER_CMD_LAST,     "",     false, NULL }
    333347};
    334348
     
    612626
    613627/**
     628 * Duplicates a command argument vector.
     629 *
     630 * @returns Duplicated argument vector or NULL if failed or no arguments given. Needs to be free'd with rtFtpCmdArgsFree().
     631 * @param   cArgs               Number of arguments in argument vector.
     632 * @param   papcszArgs          Pointer to argument vector to duplicate.
     633 */
     634static char** rtFtpCmdArgsDup(uint8_t cArgs, const char * const *apcszArgs)
     635{
     636    if (!cArgs)
     637        return NULL;
     638
     639    char **apcszArgsDup = (char **)RTMemAlloc(cArgs * sizeof(char *));
     640    if (!apcszArgsDup)
     641    {
     642        AssertFailed();
     643        return NULL;
     644    }
     645
     646    int rc2 = VINF_SUCCESS;
     647
     648    uint8_t i;
     649    for (i = 0; i < cArgs; i++)
     650    {
     651        apcszArgsDup[i] = RTStrDup(apcszArgs[i]);
     652        if (!apcszArgsDup[i])
     653            rc2 = VERR_NO_MEMORY;
     654    }
     655
     656    if (RT_FAILURE(rc2))
     657    {
     658        while (i--)
     659            RTStrFree(apcszArgsDup[i]);
     660
     661        RTMemFree(apcszArgsDup);
     662        return NULL;
     663    }
     664
     665    return apcszArgsDup;
     666}
     667
     668/**
     669 * Frees a command argument vector.
     670 *
     671 * @param   cArgs               Number of arguments in argument vector.
     672 * @param   papcszArgs          Pointer to argument vector to free.
     673 */
     674static void rtFtpCmdArgsFree(uint8_t cArgs, char **papcszArgs)
     675{
     676    while (cArgs--)
     677        RTStrFree(papcszArgs[cArgs]);
     678
     679    RTMemFree(papcszArgs);
     680}
     681
     682/**
    614683 * Opens a data connection to the client.
    615684 *
     
    674743
    675744/**
    676  * Thread serving a data connection.
     745 * Data connection thread for writing (sending) a file to the client.
    677746 *
    678747 * @returns VBox status code.
     
    680749 * @param   pvUser              Pointer to user-provided data. Of type PRTFTPSERVERCLIENT.
    681750 */
    682 static DECLCALLBACK(int) rtFtpServerDataConnThread(RTTHREAD ThreadSelf, void *pvUser)
     751static DECLCALLBACK(int) rtFtpServerDataConnFileWriteThread(RTTHREAD ThreadSelf, void *pvUser)
    683752{
    684753    RT_NOREF(ThreadSelf);
     
    687756    AssertPtr(pClient);
    688757
    689     PRTFTPSERVERDATACONN pDataConn = &pClient->DataConn;
     758    PRTFTPSERVERDATACONN pDataConn = pClient->pDataConn;
     759    AssertPtr(pDataConn);
    690760
    691761    LogFlowFuncEnter();
    692 
    693     int rc = rtFtpServerDataConnOpen(pDataConn, &pDataConn->Addr, pDataConn->uPort);
    694     if (RT_FAILURE(rc))
    695         return rc;
    696762
    697763    uint32_t cbBuf = _64K; /** @todo Improve this. */
     
    700766        return VERR_NO_MEMORY;
    701767
     768    int rc;
     769
    702770    /* Set start indicator. */
    703771    pDataConn->fStarted = true;
     
    705773    RTThreadUserSignal(RTThreadSelf());
    706774
    707     const char *pcszFile = pDataConn->szFile;
     775    AssertPtr(pDataConn->papszArgs);
     776    const char *pcszFile = pDataConn->papszArgs[0];
     777    AssertPtr(pcszFile);
    708778
    709779    void *pvHandle = NULL; /* Opaque handle known to the actual implementation. */
     
    738808    }
    739809
    740     rtFtpServerDataConnClose(pDataConn);
    741 
    742810    RTMemFree(pvBuf);
    743811    pvBuf = NULL;
     
    751819
    752820/**
    753  * Opens a data connection to the client.
     821 * Creates a data connection.
    754822 *
    755823 * @returns VBox status code.
    756  * @param   pClient             Client to open data connection for.
    757  * @param   pDataConn           Data connection to open.
    758  */
    759 static int rtFtpServerDataConnCreate(PRTFTPSERVERCLIENT pClient, PRTFTPSERVERDATACONN pDataConn)
    760 {
    761     int rc = RTThreadCreate(&pDataConn->hThread, rtFtpServerDataConnThread,
    762                             pClient, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
    763                             "ftpdata");
    764     if (RT_SUCCESS(rc))
    765     {
    766         int rc2 = RTThreadUserWait(pDataConn->hThread, 30 * 1000 /* Timeout in ms */);
    767         AssertRC(rc2);
    768 
    769         if (!pDataConn->fStarted) /* Did the thread indicate that it started correctly? */
    770             rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
    771     }
    772 
    773     return rc;
    774 }
    775 
    776 /**
    777  * Closes a data connection to the client.
     824 * @param   pClient             Client to create data connection for.
     825 * @param   ppDataConn          Where to return the (allocated) data connection.
     826 */
     827static int rtFtpServerDataConnCreate(PRTFTPSERVERCLIENT pClient, PRTFTPSERVERDATACONN *ppDataConn)
     828{
     829    if (pClient->pDataConn)
     830        return VERR_FTP_DATA_CONN_LIMIT_REACHED;
     831
     832    PRTFTPSERVERDATACONN pDataConn = (PRTFTPSERVERDATACONN)RTMemAllocZ(sizeof(RTFTPSERVERDATACONN));
     833    if (!pDataConn)
     834        return VERR_NO_MEMORY;
     835
     836    rtFtpServerDataConnReset(pDataConn);
     837
     838    pDataConn->pClient = pClient;
     839
     840    *ppDataConn = pDataConn;
     841
     842    LogFlowFuncLeaveRC(VINF_SUCCESS);
     843    return VINF_SUCCESS;
     844}
     845
     846/**
     847 * Starts a data connection.
    778848 *
    779849 * @returns VBox status code.
    780  * @param   pClient             Client to close data connection for.
    781  * @param   pDataConn           Data connection to close.
    782  */
    783 static int rtFtpServerDataConnDestroy(PRTFTPSERVERCLIENT pClient, PRTFTPSERVERDATACONN pDataConn)
    784 {
    785     RT_NOREF(pClient);
    786 
    787     if (pDataConn->hThread == NIL_RTTHREAD)
     850 * @param   pClient             Client to start data connection for.
     851 * @param   pfnThread           Pointer to thread function to use.
     852 * @param   cArgs               Number of arguments.
     853 * @param   apcszArgs           Array of arguments.
     854 */
     855static int rtFtpServerDataConnStart(PRTFTPSERVERDATACONN pDataConn, PFNRTTHREAD pfnThread,
     856                                    uint8_t cArgs, const char * const *apcszArgs)
     857{
     858    AssertPtrReturn(pDataConn, VERR_INVALID_POINTER);
     859    AssertPtrReturn(pfnThread, VERR_INVALID_POINTER);
     860
     861    AssertReturn(!pDataConn->fStarted, VERR_WRONG_ORDER);
     862    AssertReturn(!pDataConn->fStop,    VERR_WRONG_ORDER);
     863    AssertReturn(!pDataConn->fStopped, VERR_WRONG_ORDER);
     864
     865    int rc = VINF_SUCCESS;
     866
     867    if (cArgs)
     868    {
     869        pDataConn->papszArgs = rtFtpCmdArgsDup(cArgs, apcszArgs);
     870        if (!pDataConn->papszArgs)
     871            rc = VERR_NO_MEMORY;
     872    }
     873
     874    if (RT_SUCCESS(rc))
     875    {
     876        pDataConn->cArgs = cArgs;
     877
     878        rc = rtFtpServerDataConnOpen(pDataConn, &pDataConn->Addr, pDataConn->uPort);
     879        if (RT_SUCCESS(rc))
     880        {
     881            rc = RTThreadCreate(&pDataConn->hThread, pfnThread,
     882                                pDataConn->pClient, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
     883                                "ftpdata");
     884            if (RT_SUCCESS(rc))
     885            {
     886                int rc2 = RTThreadUserWait(pDataConn->hThread, 30 * 1000 /* Timeout in ms */);
     887                AssertRC(rc2);
     888
     889                if (!pDataConn->fStarted) /* Did the thread indicate that it started correctly? */
     890                    rc = VERR_FTP_DATA_CONN_INIT_FAILED;
     891            }
     892
     893            if (RT_FAILURE(rc))
     894                rtFtpServerDataConnClose(pDataConn);
     895        }
     896    }
     897
     898    if (RT_FAILURE(rc))
     899    {
     900        rtFtpCmdArgsFree(pDataConn->cArgs, pDataConn->papszArgs);
     901
     902        pDataConn->cArgs     = 0;
     903        pDataConn->papszArgs = NULL;
     904    }
     905
     906    LogFlowFuncLeaveRC(rc);
     907    return rc;
     908}
     909
     910/**
     911 * Destroys a data connection.
     912 *
     913 * @returns VBox status code.
     914 * @param   pDataConn           Data connection to destroy. The pointer is not valid anymore after successful return.
     915 */
     916static int rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn)
     917{
     918    if (!pDataConn)
    788919        return VINF_SUCCESS;
    789920
    790921    LogFlowFuncEnter();
    791922
    792     /* Set stop indicator. */
    793     pDataConn->fStop = true;
    794 
    795     int rcThread = VERR_WRONG_ORDER;
    796     int rc = RTThreadWait(pDataConn->hThread, 30 * 1000 /* Timeout in ms */, &rcThread);
     923    int rc = VINF_SUCCESS;
     924
     925    if (pDataConn->hThread != NIL_RTTHREAD)
     926    {
     927        /* Set stop indicator. */
     928        pDataConn->fStop = true;
     929
     930        int rcThread = VERR_WRONG_ORDER;
     931        rc = RTThreadWait(pDataConn->hThread, 30 * 1000 /* Timeout in ms */, &rcThread);
     932    }
     933
    797934    if (RT_SUCCESS(rc))
    798935    {
    799936        rtFtpServerDataConnClose(pDataConn);
    800         rtFtpServerDataConnReset(pDataConn);
    801 
    802         rc = rcThread;
    803     }
    804 
     937        rtFtpCmdArgsFree(pDataConn->cArgs, pDataConn->papszArgs);
     938
     939        RTMemFree(pDataConn);
     940        pDataConn = NULL;
     941
     942        /** @todo Also check / handle rcThread? */
     943    }
     944
     945    LogFlowFuncLeaveRC(rc);
    805946    return rc;
    806947}
     
    834975    RT_NOREF(cArgs, apcszArgs);
    835976
    836     int rc = rtFtpServerDataConnDestroy(pClient, &pClient->DataConn);
    837     if (RT_SUCCESS(rc))
     977    int rc = rtFtpServerDataConnDestroy(pClient->pDataConn);
     978    if (RT_SUCCESS(rc))
     979    {
     980        pClient->pDataConn = NULL;
     981
    838982        rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
     983    }
    839984
    840985    return rc;
     
    8851030}
    8861031
     1032/**
     1033 * Thread for handling the LIST command's output in a separate data connection.
     1034 *
     1035 * @returns VBox status code.
     1036 * @param   ThreadSelf          Thread handle. Unused.
     1037 * @param   pvUser              User-provided arguments. Of type PRTFTPSERVERCLIENT.
     1038 */
     1039static DECLCALLBACK(int) rtFtpServerDataConnListThread(RTTHREAD ThreadSelf, void *pvUser)
     1040{
     1041    RT_NOREF(ThreadSelf);
     1042
     1043    PRTFTPSERVERCLIENT pClient = (PRTFTPSERVERCLIENT)pvUser;
     1044    AssertPtr(pClient);
     1045
     1046    PRTFTPSERVERDATACONN pDataConn = pClient->pDataConn;
     1047    AssertPtr(pDataConn);
     1048
     1049    LogFlowFuncEnter();
     1050
     1051    uint32_t cbBuf = _64K; /** @todo Improve this. */
     1052    void *pvBuf = RTMemAlloc(cbBuf);
     1053    if (!pvBuf)
     1054        return VERR_NO_MEMORY;
     1055
     1056    int rc;
     1057
     1058    /* Set start indicator. */
     1059    pDataConn->fStarted = true;
     1060
     1061    RTThreadUserSignal(RTThreadSelf());
     1062
     1063    for (;;)
     1064    {
     1065        /* The first argument might indicate a directory to list.
     1066         * If no argument is given, the implementation must use the last directory set. */
     1067        size_t cbRead = 0;
     1068        RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnList,
     1069                                         pDataConn->cArgs == 1
     1070                                       ? pDataConn->papszArgs[0] : NULL, pvBuf, cbBuf, &cbRead);
     1071        if (RT_SUCCESS(rc))
     1072        {
     1073            int rc2 = rtFtpServerDataConnWrite(pDataConn, pvBuf, cbRead, NULL /* pcbWritten */);
     1074            AssertRC(rc2);
     1075
     1076            if (rc == VINF_EOF)
     1077                break;
     1078        }
     1079        else
     1080            break;
     1081
     1082        if (ASMAtomicReadBool(&pDataConn->fStop))
     1083            break;
     1084    }
     1085
     1086    RTMemFree(pvBuf);
     1087    pvBuf = NULL;
     1088
     1089    pDataConn->fStopped = true;
     1090    pDataConn->rc       = rc;
     1091
     1092    LogFlowFuncLeaveRC(rc);
     1093    return rc;
     1094}
     1095
    8871096static int rtFtpServerHandleLIST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    8881097{
    889     RT_NOREF(cArgs, apcszArgs);
    890 
    8911098    int rc;
    8921099
    893     void   *pvData = NULL;
    894     size_t  cbData = 0;
    895 
    896     /* The first argument might indicate a directory to list. */
    897     RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnList,
    898                                      cArgs == 1
    899                                    ? apcszArgs[0] : NULL, &pvData, &cbData);
    900 
    901     if (RT_SUCCESS(rc))
    902     {
    903         RTMemFree(pvData);
    904     }
     1100    /* Note: Data connection gets created when the PORT command was sent. */
     1101    if (pClient->pDataConn)
     1102    {
     1103        rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnListThread, cArgs, apcszArgs);
     1104    }
     1105    else
     1106        rc = VERR_FTP_DATA_CONN_NOT_FOUND;
     1107
     1108    int rc2 = rtFtpServerSendReplyRc(pClient,   RT_SUCCESS(rc)
     1109                                     ? RTFTPSERVER_REPLY_PATHNAME_OK
     1110                                     : RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
     1111    if (RT_SUCCESS(rc))
     1112        rc = rc2;
    9051113
    9061114    return rc;
     
    9551163        return rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
    9561164
    957     /* Only allow one data connection per client at a time. */
    958     rtFtpServerDataConnClose(&pClient->DataConn);
    959 
    960     int rc = rtFtpParseHostAndPort(apcszArgs[0], &pClient->DataConn.Addr, &pClient->DataConn.uPort);
    961     if (RT_SUCCESS(rc))
    962     {
    963         rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
    964     }
    965     else
    966         rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
    967 
    968     return rc;
    969 }
    970 
    971 static int rtFtpServerHandlePWD(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    972 {
    973     RT_NOREF(cArgs, apcszArgs);
    974 
    975     int rc;
    976 
    977     char szPWD[RTPATH_MAX];
    978 
    979     RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnPathGetCurrent, szPWD, sizeof(szPWD));
    980 
    981     if (RT_SUCCESS(rc))
    982        rc = rtFtpServerSendReplyStr(pClient, szPWD);
    983 
    984     return rc;
    985 }
    986 
    987 static int rtFtpServerHandleQUIT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    988 {
    989     RT_NOREF(cArgs, apcszArgs);
    990 
    991     return rtFtpServerDataConnDestroy(pClient, &pClient->DataConn);
    992 }
    993 
    994 static int rtFtpServerHandleRETR(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
    995 {
    996     if (cArgs != 1)
    997         return VERR_INVALID_PARAMETER;
    998 
    999     int rc;
    1000 
    1001     const char *pcszPath = apcszArgs[0];
    1002 
    1003     RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, pcszPath, NULL /* PRTFSOBJINFO */);
    1004 
    1005     if (RT_SUCCESS(rc))
    1006     {
    1007         rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_FILE_STATUS_OKAY);
     1165    PRTFTPSERVERDATACONN pDataConn;
     1166    int rc = rtFtpServerDataConnCreate(pClient, &pDataConn);
     1167    if (RT_SUCCESS(rc))
     1168    {
     1169        pClient->pDataConn = pDataConn;
     1170
     1171        rc = rtFtpParseHostAndPort(apcszArgs[0], &pDataConn->Addr, &pDataConn->uPort);
    10081172        if (RT_SUCCESS(rc))
    10091173        {
    1010             rc = RTStrCopy(pClient->DataConn.szFile, sizeof(pClient->DataConn.szFile), pcszPath);
     1174            rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
     1175        }
     1176    }
     1177
     1178    if (RT_FAILURE(rc))
     1179    {
     1180        int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
     1181        AssertRC(rc2);
     1182    }
     1183
     1184    return rc;
     1185}
     1186
     1187static int rtFtpServerHandlePWD(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
     1188{
     1189    RT_NOREF(cArgs, apcszArgs);
     1190
     1191    int rc;
     1192
     1193    char szPWD[RTPATH_MAX];
     1194
     1195    RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnPathGetCurrent, szPWD, sizeof(szPWD));
     1196
     1197    if (RT_SUCCESS(rc))
     1198       rc = rtFtpServerSendReplyRcEx(pClient, RTFTPSERVER_REPLY_PATHNAME_OK, "\"%s\"", szPWD); /* See RFC 959, APPENDIX II. */
     1199
     1200    return rc;
     1201}
     1202
     1203static int rtFtpServerHandleQUIT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
     1204{
     1205    RT_NOREF(cArgs, apcszArgs);
     1206
     1207    int rc = rtFtpServerDataConnDestroy(pClient->pDataConn);
     1208    if (RT_SUCCESS(rc))
     1209        pClient->pDataConn = NULL;
     1210
     1211    return rc;
     1212}
     1213
     1214static int rtFtpServerHandleRETR(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs)
     1215{
     1216    if (cArgs != 1) /* File name needs to be present. */
     1217        return VERR_INVALID_PARAMETER;
     1218
     1219    int rc;
     1220
     1221    const char *pcszPath = apcszArgs[0];
     1222
     1223    RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, pcszPath, NULL /* PRTFSOBJINFO */);
     1224
     1225    if (RT_SUCCESS(rc))
     1226    {
     1227        if (pClient->pDataConn)
     1228        {
     1229            /* Note: Data connection gets created when the PORT command was sent. */
     1230            rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnFileWriteThread, cArgs, apcszArgs);
    10111231            if (RT_SUCCESS(rc))
    1012             {
    1013                 rc = rtFtpServerDataConnCreate(pClient, &pClient->DataConn);
    1014             }
     1232                rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_FILE_STATUS_OKAY);
    10151233        }
     1234        else
     1235            rc = VERR_FTP_DATA_CONN_NOT_FOUND;
    10161236    }
    10171237
     
    12521472        *pszCmdEnd = '\0';
    12531473
     1474    /* Reply which gets sent back to the client. */
     1475    RTFTPSERVER_REPLY rcClient = RTFTPSERVER_REPLY_INVALID;
     1476
    12541477    int rcCmd = VINF_SUCCESS;
    12551478
     
    12651488        for (; i < RT_ELEMENTS(g_aCmdMap); i++)
    12661489        {
    1267             if (!RTStrICmp(papszArgs[0], g_aCmdMap[i].szCmd))
     1490            const RTFTPSERVER_CMD_ENTRY *pCmdEntry = &g_aCmdMap[i];
     1491
     1492            if (!RTStrICmp(papszArgs[0], pCmdEntry->szCmd))
    12681493            {
     1494                /* Some commands need a valid user before they can be executed. */
     1495                if (   pCmdEntry->fNeedsUser
     1496                    && pClient->State.pszUser == NULL)
     1497                {
     1498                    rcClient = RTFTPSERVER_REPLY_NOT_LOGGED_IN;
     1499                    break;
     1500                }
     1501
    12691502                /* Save timestamp of last command sent. */
    12701503                pClient->State.tsLastCmdMs = RTTimeMilliTS();
    12711504
    1272                 rcCmd = g_aCmdMap[i].pfnCmd(pClient, cArgs - 1, cArgs > 1 ? &papszArgs[1] : NULL);
     1505                /* Hand in arguments only without the actual command. */
     1506                rcCmd = pCmdEntry->pfnCmd(pClient, cArgs - 1, cArgs > 1 ? &papszArgs[1] : NULL);
     1507                if (RT_FAILURE(rcCmd))
     1508                {
     1509                    LogFunc(("Handling command '%s' failed with %Rrc\n", papszArgs[0], rcCmd));
     1510
     1511                    switch (rcCmd)
     1512                    {
     1513                        case VERR_INVALID_PARAMETER:
     1514                            RT_FALL_THROUGH();
     1515                        case VERR_INVALID_POINTER:
     1516                            rcClient = RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS;
     1517                            break;
     1518
     1519                        case VERR_NOT_IMPLEMENTED:
     1520                            rcClient = RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL;
     1521                            break;
     1522
     1523                        default:
     1524                            break;
     1525                    }
     1526                }
    12731527                break;
    12741528            }
     
    12791533        if (i == RT_ELEMENTS(g_aCmdMap))
    12801534        {
    1281             int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL);
    1282             if (RT_SUCCESS(rc))
    1283                 rc = rc2;
    1284 
    12851535            LogFlowFunc(("Command not implemented\n"));
    1286             return rc;
     1536            Assert(rcClient == RTFTPSERVER_REPLY_INVALID);
     1537            rcClient = RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL;
    12871538        }
    12881539
     
    12911542        if (fDisconnect)
    12921543        {
    1293             int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CLOSING_CTRL_CONN);
    1294             if (RT_SUCCESS(rc))
    1295                 rc = rc2;
    1296 
    12971544            RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnUserDisconnect, pClient->State.pszUser);
    1298             return rc;
     1545
     1546            Assert(rcClient == RTFTPSERVER_REPLY_INVALID);
     1547            rcClient = RTFTPSERVER_REPLY_CLOSING_CTRL_CONN;
    12991548        }
    1300 
    1301         switch (rcCmd)
    1302         {
    1303             case VERR_INVALID_PARAMETER:
    1304                 RT_FALL_THROUGH();
    1305             case VERR_INVALID_POINTER:
    1306                 rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
    1307                 break;
    1308 
    1309             case VERR_NOT_IMPLEMENTED:
    1310                 rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL);
    1311                 break;
    1312 
    1313             default:
    1314                 break;
    1315         }
    13161549    }
    13171550    else
    1318     {
    1319         int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
     1551        rcClient = RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS;
     1552
     1553    if (rcClient != RTFTPSERVER_REPLY_INVALID)
     1554    {
     1555        int rc2 = rtFtpServerSendReplyRc(pClient, rcClient);
    13201556        if (RT_SUCCESS(rc))
    13211557            rc = rc2;
     
    13591595            if (RT_FAILURE(rc))
    13601596                break;
    1361 
    1362             PRTFTPSERVERDATACONN pDataConn = &pClient->DataConn;
    1363 
    1364             if (   ASMAtomicReadBool(&pDataConn->fStarted)
    1365                 && ASMAtomicReadBool(&pDataConn->fStopped))
     1597        }
     1598
     1599        /*
     1600         * Handle data connection replies.
     1601         */
     1602        if (pClient->pDataConn)
     1603        {
     1604            if (   ASMAtomicReadBool(&pClient->pDataConn->fStarted)
     1605                && ASMAtomicReadBool(&pClient->pDataConn->fStopped))
    13661606            {
    1367                 Assert(pDataConn->rc != VERR_IPE_UNINITIALIZED_STATUS);
    1368 
    1369                 rc = rtFtpServerSendReplyRc(pClient, RT_SUCCESS(pDataConn->rc)
     1607                Assert(pClient->pDataConn->rc != VERR_IPE_UNINITIALIZED_STATUS);
     1608
     1609                rc = rtFtpServerSendReplyRc(pClient, RT_SUCCESS(pClient->pDataConn->rc)
    13701610                                                     ? RTFTPSERVER_REPLY_CLOSING_DATA_CONN
    13711611                                                     : RTFTPSERVER_REPLY_CONN_CLOSED_TRANSFER_ABORTED);
    13721612
    1373                 int rc2 = rtFtpServerDataConnDestroy(pClient, pDataConn);
     1613                int rc2 = rtFtpServerDataConnDestroy(pClient->pDataConn);
     1614                if (RT_SUCCESS(rc2))
     1615                    pClient->pDataConn = NULL;
     1616
    13741617                if (RT_SUCCESS(rc))
    13751618                    rc = rc2;
     
    13781621    }
    13791622
    1380     /* Make sure to close any open data connections. */
    1381     int rc2 = rtFtpServerDataConnDestroy(pClient, &pClient->DataConn);
     1623    /* Make sure to destroy all data connections. */
     1624    int rc2 = rtFtpServerDataConnDestroy(pClient->pDataConn);
     1625    if (RT_SUCCESS(rc2))
     1626        pClient->pDataConn = NULL;
     1627
    13821628    if (RT_SUCCESS(rc))
    13831629        rc = rc2;
  • trunk/src/VBox/Runtime/tools/RTFTPServer.cpp

    r82732 r82770  
    4747#include <iprt/assert.h>
    4848#include <iprt/ctype.h>
    49 #include <iprt/errcore.h>
     49#include <iprt/err.h>
    5050#include <iprt/file.h>
    5151#include <iprt/getopt.h>
     
    305305}
    306306
    307 static DECLCALLBACK(int) onList(PRTFTPCALLBACKDATA pData, const char *pcszPath, void **ppvData, size_t *pcbData)
    308 {
    309     RT_NOREF(pData, pcszPath, ppvData, pcbData);
    310 
    311     return VINF_SUCCESS;
     307static DECLCALLBACK(int) onList(PRTFTPCALLBACKDATA pData, const char *pcszPath, void *pvData, size_t cbData, size_t *pcbRead)
     308{
     309    RT_NOREF(pData, pcszPath, pvData, cbData, pcbRead);
     310
     311    return VINF_EOF;
    312312}
    313313
Note: See TracChangeset for help on using the changeset viewer.

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