- Timestamp:
- Jan 14, 2020 9:53:20 AM (5 years ago)
- svn:sync-xref-src-repo-rev:
- 135675
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/iprt/ftp.h
r82731 r82732 76 76 typedef enum RTFTPSERVER_TRANSFER_MODE 77 77 { 78 RTFTPSERVER_TRANSFER_MODE_UNKNOWN = 0,79 RTFTPSERVER_TRANSFER_MODE_STREAM ,78 /** Default if nothing else is set. */ 79 RTFTPSERVER_TRANSFER_MODE_STREAM = 0, 80 80 RTFTPSERVER_TRANSFER_MODE_BLOCK, 81 81 RTFTPSERVER_TRANSFER_MODE_COMPRESSED, … … 89 89 typedef enum RTFTPSERVER_DATA_TYPE 90 90 { 91 RTFTPSERVER_DATA_TYPE_UNKNOWN = 0,92 RTFTPSERVER_DATA_TYPE_ASCII ,91 /** Default if nothing else is set. */ 92 RTFTPSERVER_DATA_TYPE_ASCII = 0, 93 93 RTFTPSERVER_DATA_TYPE_EBCDIC, 94 94 RTFTPSERVER_DATA_TYPE_IMAGE, … … 99 99 100 100 /** 101 * Enumeration for defining the struct type. 102 */ 103 typedef 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 /** 101 114 * Enumeration for FTP server reply codes. 102 115 * … … 107 120 /** Invalid reply type, do not use. */ 108 121 RTFTPSERVER_REPLY_INVALID = 0, 122 /** Command okay. */ 123 RTFTPSERVER_REPLY_FILE_STATUS_OKAY = 150, 109 124 /** Command okay. */ 110 125 RTFTPSERVER_REPLY_OKAY = 200, … … 156 171 /** Current set data type. */ 157 172 RTFTPSERVER_DATA_TYPE enmDataType; 173 /** Current set struct type. */ 174 RTFTPSERVER_STRUCT_TYPE enmStructType; 158 175 } RTFTPSERVERCLIENTSTATE; 159 176 /** Pointer to a FTP server client state. */ … … 182 199 typedef struct RTFTPSERVERCALLBACKS 183 200 { 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;188 201 /** 189 202 * Callback which gets invoked when a user connected. … … 208 221 * @returns VBox status code. 209 222 * @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 */ 212 235 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 */ 213 246 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 */ 214 254 DECLCALLBACKMEMBER(int, pfnOnFileClose)(PRTFTPCALLBACKDATA pData, void *pvHandle); 215 255 /** … … 278 318 * @param uPort The port for creating a listening socket. 279 319 * @param pCallbacks Callback table to use. 320 * @param pvUser Pointer to user-specific data. Optional. 321 * @param cbUser Size of user-specific data. Optional. 280 322 */ 281 323 RTR3DECL(int) RTFtpServerCreate(PRTFTPSERVER phFTPServer, const char *pcszAddress, uint16_t uPort, 282 PRTFTPSERVERCALLBACKS pCallbacks );324 PRTFTPSERVERCALLBACKS pCallbacks, void *pvUser, size_t cbUser); 283 325 284 326 /** -
trunk/src/VBox/Runtime/generic/ftp-server.cpp
r82728 r82732 29 29 * Known limitations so far: 30 30 * - UTF-8 support only. 31 * - Only supports ASCII + binary (image type) file streams for now. 31 32 * - No support for writing / modifying ("DELE", "MKD", "RMD", "STOR", ++). 32 33 * - No FTPS / SFTP support. … … 48 49 #include <iprt/asm.h> 49 50 #include <iprt/assert.h> 50 #include <iprt/err core.h>51 #include <iprt/err.h> 51 52 #include <iprt/file.h> /* For file mode flags. */ 52 53 #include <iprt/getopt.h> … … 77 78 /** Number of currently connected clients. */ 78 79 uint32_t cClients; 80 /** Pointer to user-specific data. Optional. */ 81 void *pvUser; 82 /** Size of user-specific data. Optional. */ 83 size_t cbUser; 79 84 } RTFTPSERVERINTERNAL; 80 85 /** Pointer to an internal FTP server instance. */ … … 134 139 /** Retrieves the current status of a transfer. */ 135 140 RTFTPSERVER_CMD_STAT, 141 /** Sets the structure type to use. */ 142 RTFTPSERVER_CMD_STRU, 136 143 /** Gets the server's OS info. */ 137 144 RTFTPSERVER_CMD_SYST, … … 164 171 /** Thread stop indicator. */ 165 172 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. */ 166 178 char szFile[RTPATH_MAX]; 167 179 } RTFTPSERVERDATACONN; … … 199 211 if (pCallbacks->a_Name) \ 200 212 { \ 201 RTFTPCALLBACKDATA Data = { &pClient->State , pCallbacks->pvUser, pCallbacks->cbUser}; \213 RTFTPCALLBACKDATA Data = { &pClient->State }; \ 202 214 return pCallbacks->a_Name(&Data); \ 203 215 } \ … … 213 225 if (pCallbacks->a_Name) \ 214 226 { \ 215 RTFTPCALLBACKDATA Data = { &pClient->State, pC allbacks->pvUser, pCallbacks->cbUser }; \227 RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \ 216 228 rc = pCallbacks->a_Name(&Data); \ 217 229 } \ … … 227 239 if (pCallbacks->a_Name) \ 228 240 { \ 229 RTFTPCALLBACKDATA Data = { &pClient->State, pC allbacks->pvUser, pCallbacks->cbUser }; \241 RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \ 230 242 rc = pCallbacks->a_Name(&Data, __VA_ARGS__); \ 231 243 } \ … … 241 253 if (pCallbacks->a_Name) \ 242 254 { \ 243 RTFTPCALLBACKDATA Data = { &pClient->State, pC allbacks->pvUser, pCallbacks->cbUser }; \255 RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \ 244 256 return pCallbacks->a_Name(&Data, __VA_ARGS__); \ 245 257 } \ … … 253 265 *********************************************************************************************************************************/ 254 266 255 static int rtFtpServerDataPortOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort); 267 static void rtFtpServerDataConnReset(PRTFTPSERVERDATACONN pDataConn); 268 static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort); 269 static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState); 256 270 257 271 /** … … 271 285 static FNRTFTPSERVERCMD rtFtpServerHandleSIZE; 272 286 static FNRTFTPSERVERCMD rtFtpServerHandleSTAT; 287 static FNRTFTPSERVERCMD rtFtpServerHandleSTRU; 273 288 static FNRTFTPSERVERCMD rtFtpServerHandleSYST; 274 289 static FNRTFTPSERVERCMD rtFtpServerHandleTYPE; … … 307 322 { RTFTPSERVER_CMD_SIZE, "SIZE", rtFtpServerHandleSIZE }, 308 323 { RTFTPSERVER_CMD_STAT, "STAT", rtFtpServerHandleSTAT }, 324 { RTFTPSERVER_CMD_STRU, "STRU", rtFtpServerHandleSTRU }, 309 325 { RTFTPSERVER_CMD_SYST, "SYST", rtFtpServerHandleSYST }, 310 326 { RTFTPSERVER_CMD_TYPE, "TYPE", rtFtpServerHandleTYPE }, … … 543 559 * @param uPort Port for the data connection. 544 560 */ 545 static int rtFtpServerDataPortOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort) 546 { 547 RT_NOREF(pAddr); 548 561 static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort) 562 { 563 LogFlowFuncEnter(); 564 565 /** @todo Implement IPv6 handling here. */ 549 566 char szAddress[32]; 550 567 const ssize_t cchAdddress = RTStrPrintf2(szAddress, sizeof(szAddress), "%RU8.%RU8.%RU8.%RU8", … … 561 578 * @param pDataConn Data connection to close. 562 579 */ 563 static int rtFtpServerData PortClose(PRTFTPSERVERDATACONN pDataConn)580 static int rtFtpServerDataConnClose(PRTFTPSERVERDATACONN pDataConn) 564 581 { 565 582 int rc = VINF_SUCCESS; … … 567 584 if (pDataConn->hSocket != NIL_RTSOCKET) 568 585 { 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 */ 608 static 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); 574 613 } 575 614 … … 590 629 PRTFTPSERVERDATACONN pDataConn = &pClient->DataConn; 591 630 592 int rc = rtFtpServerDataPortOpen(pDataConn, &pDataConn->Addr, pDataConn->uPort); 631 LogFlowFuncEnter(); 632 633 int rc = rtFtpServerDataConnOpen(pDataConn, &pDataConn->Addr, pDataConn->uPort); 593 634 if (RT_FAILURE(rc)) 594 635 return rc; … … 599 640 return VERR_NO_MEMORY; 600 641 601 pDataConn->fStop = false;642 /* Set start indicator. */ 602 643 pDataConn->fStarted = true; 603 644 … … 612 653 if (RT_SUCCESS(rc)) 613 654 { 655 LogFlowFunc(("Transfer started\n")); 656 614 657 do 615 658 { 616 659 size_t cbRead = 0; 617 660 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 } 620 666 621 667 if ( !cbRead 622 668 || ASMAtomicReadBool(&pDataConn->fStop)) 669 { 623 670 break; 671 } 624 672 } 625 673 while (RT_SUCCESS(rc)); 626 674 627 675 RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileClose, pvHandle); 628 } 629 630 rtFtpServerDataPortClose(&pClient->DataConn); 676 677 LogFlowFunc(("Transfer done\n")); 678 } 679 680 rtFtpServerDataConnClose(pDataConn); 631 681 632 682 RTMemFree(pvBuf); 633 683 pvBuf = NULL; 634 684 685 pDataConn->fStopped = true; 686 pDataConn->rc = rc; 687 688 LogFlowFuncLeaveRC(rc); 635 689 return rc; 636 690 } … … 683 737 if (RT_SUCCESS(rc)) 684 738 { 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); 693 741 694 742 rc = rcThread; … … 696 744 697 745 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 */ 754 static 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; 698 765 } 699 766 … … 814 881 815 882 /* Only allow one data connection per client at a time. */ 816 rtFtpServerData PortClose(&pClient->DataConn);883 rtFtpServerDataConnClose(&pClient->DataConn); 817 884 818 885 int rc = rtFtpParseHostAndPort(apcszArgs[0], &pClient->DataConn.Addr, &pClient->DataConn.uPort); … … 863 930 if (RT_SUCCESS(rc)) 864 931 { 865 rc = RTStrCopy(pClient->DataConn.szFile, sizeof(pClient->DataConn.szFile), pcszPath);932 rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_FILE_STATUS_OKAY); 866 933 if (RT_SUCCESS(rc)) 867 934 { 868 rc = rtFtpServerDataConnCreate(pClient, &pClient->DataConn);935 rc = RTStrCopy(pClient->DataConn.szFile, sizeof(pClient->DataConn.szFile), pcszPath); 869 936 if (RT_SUCCESS(rc)) 870 937 { 871 rc = rtFtpServer SendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);938 rc = rtFtpServerDataConnCreate(pClient, &pClient->DataConn); 872 939 } 873 940 } … … 950 1017 } 951 1018 1019 static 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 952 1040 static int rtFtpServerHandleSYST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apcszArgs) 953 1041 { … … 971 1059 int rc = VINF_SUCCESS; 972 1060 973 if (!RTStrICmp(pcszType, "A")) /* ASCII (can be 7 or 8 bits). */1061 if (!RTStrICmp(pcszType, "A")) 974 1062 { 975 1063 pClient->State.enmDataType = RTFTPSERVER_DATA_TYPE_ASCII; … … 980 1068 } 981 1069 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); 983 1074 984 1075 return rc; … … 993 1084 AssertPtrReturn(pcszUser, VERR_INVALID_PARAMETER); 994 1085 995 if (pClient->State.pszUser) 996 { 997 RTStrFree(pClient->State.pszUser); 998 pClient->State.pszUser = NULL; 999 } 1086 rtFtpServerClientStateReset(&pClient->State); 1000 1087 1001 1088 int rc = rtFtpServerLookupUser(pClient, pcszUser); … … 1069 1156 1070 1157 /** 1071 * Main loop for processing client commands.1158 * Main function for processing client commands for the control connection. 1072 1159 * 1073 1160 * @returns VBox status code. 1074 1161 * @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 */ 1165 static 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. 1075 1255 */ 1076 1256 static int rtFtpServerProcessCommands(PRTFTPSERVERCLIENT pClient) … … 1078 1258 int rc; 1079 1259 1260 size_t cbRead; 1261 char szCmd[RTFTPSERVER_MAX_CMD_LEN + 1]; 1262 1080 1263 for (;;) 1081 1264 { 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? */ 1085 1266 if (RT_SUCCESS(rc)) 1086 1267 { 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); 1104 1269 if ( RT_SUCCESS(rc) 1105 && c Args) /* At least the actual command (without args) must be present. */1270 && cbRead) 1106 1271 { 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); 1158 1274 } 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)) 1160 1288 { 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); 1162 1296 if (RT_SUCCESS(rc)) 1163 1297 rc = rc2; 1164 1298 } 1165 1299 } 1166 else1167 {1168 int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_CMD_NOT_RECOGNIZED);1169 if (RT_SUCCESS(rc))1170 rc = rc2;1171 }1172 1300 } 1173 1301 … … 1177 1305 rc = rc2; 1178 1306 1307 LogFlowFuncLeaveRC(rc); 1179 1308 return rc; 1180 1309 } … … 1187 1316 static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState) 1188 1317 { 1318 LogFlowFuncEnter(); 1319 1189 1320 RTStrFree(pState->pszUser); 1190 1321 pState->pszUser = NULL; 1191 1322 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; 1193 1327 } 1194 1328 … … 1230 1364 1231 1365 RTR3DECL(int) RTFtpServerCreate(PRTFTPSERVER phFTPServer, const char *pcszAddress, uint16_t uPort, 1232 PRTFTPSERVERCALLBACKS pCallbacks )1366 PRTFTPSERVERCALLBACKS pCallbacks, void *pvUser, size_t cbUser) 1233 1367 { 1234 1368 AssertPtrReturn(phFTPServer, VERR_INVALID_POINTER); … … 1236 1370 AssertReturn (uPort, VERR_INVALID_PARAMETER); 1237 1371 AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER); 1372 /* pvUser is optional. */ 1238 1373 1239 1374 int rc; … … 1244 1379 pThis->u32Magic = RTFTPSERVER_MAGIC; 1245 1380 pThis->Callbacks = *pCallbacks; 1381 pThis->pvUser = pvUser; 1382 pThis->cbUser = cbUser; 1246 1383 1247 1384 rc = RTTcpServerCreate(pcszAddress, uPort, RTTHREADTYPE_DEFAULT, "ftpsrv", -
trunk/src/VBox/Runtime/tools/RTFTPServer.cpp
r82727 r82732 186 186 } 187 187 188 static DECLCALLBACK(int) onUserDisonnect(PRTFTPCALLBACKDATA pData )188 static DECLCALLBACK(int) onUserDisonnect(PRTFTPCALLBACKDATA pData, const char *pcszUser) 189 189 { 190 190 RT_NOREF(pData); 191 191 192 RTPrintf("User disconnected\n");192 RTPrintf("User '%s' disconnected\n", pcszUser); 193 193 194 194 return VINF_SUCCESS; … … 222 222 Assert(pData->cbUser == sizeof(FTPSERVERDATA)); 223 223 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; 225 231 } 226 232 … … 406 412 RT_ZERO(Callbacks); 407 413 408 Callbacks.pvUser = &g_FTPServerData;409 Callbacks.cbUser = sizeof(g_FTPServerData);410 411 414 Callbacks.pfnOnUserConnect = onUserConnect; 412 415 Callbacks.pfnOnUserAuthenticate = onUserAuthenticate; … … 423 426 424 427 RTFTPSERVER hFTPServer; 425 rc = RTFtpServerCreate(&hFTPServer, szAddress, uPort, &Callbacks); 428 rc = RTFtpServerCreate(&hFTPServer, szAddress, uPort, &Callbacks, 429 &g_FTPServerData, sizeof(g_FTPServerData)); 426 430 if (RT_SUCCESS(rc)) 427 431 {
Note:
See TracChangeset
for help on using the changeset viewer.