Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ftp_server_misc.c
00001 /** 00002 * @file ftp_server_misc.c 00003 * @brief FTP server (miscellaneous functions) 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL FTP_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include "ftp/ftp_server.h" 00034 #include "ftp/ftp_server_events.h" 00035 #include "ftp/ftp_server_commands.h" 00036 #include "ftp/ftp_server_misc.h" 00037 #include "str.h" 00038 #include "path.h" 00039 #include "error.h" 00040 #include "debug.h" 00041 00042 //Check TCP/IP stack configuration 00043 #if (FTP_SERVER_SUPPORT == ENABLED) 00044 00045 00046 /** 00047 * @brief Get a passive port number 00048 * @param[in] context Pointer to the FTP server context 00049 * @return Passive port number 00050 **/ 00051 00052 uint16_t ftpServerGetPassivePort(FtpServerContext *context) 00053 { 00054 uint_t port; 00055 00056 //Retrieve current passive port number 00057 port = context->passivePort; 00058 00059 //Invalid port number? 00060 if(port < context->settings.passivePortMin || 00061 port > context->settings.passivePortMax) 00062 { 00063 //Generate a random port number 00064 port = context->settings.passivePortMin + netGetRand() % 00065 (context->settings.passivePortMax - context->settings.passivePortMin + 1); 00066 } 00067 00068 //Next passive port to use 00069 if(port < context->settings.passivePortMax) 00070 { 00071 //Increment port number 00072 context->passivePort = port + 1; 00073 } 00074 else 00075 { 00076 //Wrap around if necessary 00077 context->passivePort = context->settings.passivePortMin; 00078 } 00079 00080 //Return the passive port number 00081 return port; 00082 } 00083 00084 00085 /** 00086 * @brief Close client connection properly 00087 * @param[in] context Pointer to the FTP server context 00088 * @param[in] connection Pointer to the client connection to be closed 00089 **/ 00090 00091 void ftpServerCloseConnection(FtpServerContext *context, 00092 FtpClientConnection *connection) 00093 { 00094 uint_t i; 00095 00096 //Make sure the connection is active 00097 if(connection != NULL) 00098 { 00099 //Loop through client connection table 00100 for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++) 00101 { 00102 //Search the table for the specified connection 00103 if(context->connection[i] == connection) 00104 { 00105 //Close data connection 00106 ftpServerCloseDataConnection(connection); 00107 //Close control connection 00108 ftpServerCloseControlConnection(connection); 00109 00110 //Release previously allocated resources 00111 if(connection->file != NULL) 00112 fsCloseFile(connection->file); 00113 if(connection->dir != NULL) 00114 fsCloseDir(connection->dir); 00115 00116 //Free memory 00117 memPoolFree(connection->buffer); 00118 memPoolFree(connection); 00119 00120 //Mark the entry as free 00121 context->connection[i] = NULL; 00122 //We are done 00123 break; 00124 } 00125 } 00126 } 00127 } 00128 00129 00130 /** 00131 * @brief Accept control connection 00132 * @param[in] context Pointer to the FTP server context 00133 * @return Pointer to the connection 00134 **/ 00135 00136 FtpClientConnection *ftpServerAcceptControlConnection(FtpServerContext *context) 00137 { 00138 error_t error; 00139 uint_t i; 00140 Socket *socket; 00141 IpAddr clientIpAddr; 00142 uint16_t clientPort; 00143 FtpClientConnection *connection; 00144 00145 //Accept incoming connection 00146 socket = socketAccept(context->socket, &clientIpAddr, &clientPort); 00147 //Failure detected? 00148 if(socket == NULL) 00149 return NULL; 00150 00151 //Force the socket to operate in non-blocking mode 00152 error = socketSetTimeout(socket, 0); 00153 //Any error to report? 00154 if(error) 00155 { 00156 //Close socket 00157 socketClose(socket); 00158 //Exit immediately 00159 return NULL; 00160 } 00161 00162 //Loop through client connection table 00163 for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++) 00164 { 00165 //Check whether the entry is currently in used or not 00166 if(context->connection[i] == NULL) 00167 { 00168 //Allocate resources for the new connection 00169 connection = memPoolAlloc(sizeof(FtpClientConnection)); 00170 //Failed to allocate memory? 00171 if(connection == NULL) 00172 { 00173 //Debug message 00174 TRACE_ERROR("FTP server: Failed to allocate memory!\r\n"); 00175 //Exit immediately 00176 break; 00177 } 00178 00179 //Clear the structure 00180 memset(connection, 0, sizeof(FtpClientConnection)); 00181 00182 //Allocate a memory buffer for I/O operations 00183 connection->buffer = memPoolAlloc(FTP_SERVER_BUFFER_SIZE); 00184 //Failed to allocate memory 00185 if(connection->buffer == NULL) 00186 { 00187 //Clean up side effects 00188 memPoolFree(connection); 00189 //Debug message 00190 TRACE_ERROR("FTP server: Failed to allocate memory!\r\n"); 00191 //Exit immediately 00192 break; 00193 } 00194 00195 //Debug message 00196 TRACE_INFO("TCP server: Control connection established with client %s port %" PRIu16 "...\r\n", 00197 ipAddrToString(&clientIpAddr, NULL), clientPort); 00198 00199 //Underlying network interface 00200 connection->interface = socket->interface; 00201 //Save socket handle 00202 connection->controlSocket = socket; 00203 //Set home directory 00204 strcpy(connection->homeDir, context->settings.rootDir); 00205 //Set current directory 00206 strcpy(connection->currentDir, context->settings.rootDir); 00207 00208 //Format greeting message 00209 strcpy(connection->response, "220 Service ready for new user\r\n"); 00210 //Debug message 00211 TRACE_DEBUG("FTP server: %s", connection->response); 00212 00213 //Number of bytes in the response buffer 00214 connection->responseLength = strlen(connection->response); 00215 connection->responsePos = 0; 00216 00217 //The client connection is ready for use 00218 context->connection[i] = connection; 00219 //Successful processing 00220 return connection; 00221 } 00222 } 00223 00224 //Debug message 00225 TRACE_INFO("TCP server: Connection refused with client %s port %" PRIu16 "...\r\n", 00226 ipAddrToString(&clientIpAddr, NULL), clientPort); 00227 00228 //Close socket 00229 socketClose(socket); 00230 //The FTP server cannot accept the incoming connection request 00231 return NULL; 00232 } 00233 00234 00235 /** 00236 * @brief Close control connection 00237 * @param[in] connection Pointer to the client connection 00238 **/ 00239 00240 void ftpServerCloseControlConnection(FtpClientConnection *connection) 00241 { 00242 IpAddr clientIpAddr; 00243 uint16_t clientPort; 00244 00245 //Any running control connection? 00246 if(connection->controlSocket != NULL) 00247 { 00248 //Retrieve the address of the peer to which a socket is connected 00249 socketGetRemoteAddr(connection->controlSocket, &clientIpAddr, &clientPort); 00250 00251 //Debug message 00252 TRACE_INFO("FTP server: Closing control connection with client %s port %" PRIu16 "...\r\n", 00253 ipAddrToString(&clientIpAddr, NULL), clientPort); 00254 00255 //Close control connection 00256 socketClose(connection->controlSocket); 00257 connection->controlSocket = NULL; 00258 00259 //Back to idle state 00260 connection->controlState = FTP_CONTROL_STATE_IDLE; 00261 } 00262 } 00263 00264 00265 /** 00266 * @brief Open data connection 00267 * @param[in] context Pointer to the FTP server context 00268 * @param[in] connection Pointer to the client connection 00269 * @return Error code 00270 **/ 00271 00272 error_t ftpServerOpenDataConnection(FtpServerContext *context, 00273 FtpClientConnection *connection) 00274 { 00275 error_t error; 00276 00277 //Release previously allocated resources 00278 ftpServerCloseDataConnection(connection); 00279 00280 //No port specified? 00281 if(!connection->remotePort) 00282 return ERROR_FAILURE; 00283 00284 //Debug message 00285 TRACE_INFO("FTP server: Opening data connection with client %s port %" PRIu16 "...\r\n", 00286 ipAddrToString(&connection->remoteIpAddr, NULL), connection->remotePort); 00287 00288 //Open data socket 00289 connection->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); 00290 //Failed to open socket? 00291 if(!connection->dataSocket) 00292 return ERROR_OPEN_FAILED; 00293 00294 //Start of exception handling block 00295 do 00296 { 00297 //Force the socket to operate in non-blocking mode 00298 error = socketSetTimeout(connection->dataSocket, 0); 00299 //Any error to report? 00300 if(error) 00301 break; 00302 00303 //Change the size of the TX buffer 00304 error = socketSetTxBufferSize(connection->dataSocket, 00305 FTP_SERVER_DATA_SOCKET_BUFFER_SIZE); 00306 //Any error to report? 00307 if(error) 00308 break; 00309 00310 //Change the size of the RX buffer 00311 error = socketSetRxBufferSize(connection->dataSocket, 00312 FTP_SERVER_DATA_SOCKET_BUFFER_SIZE); 00313 //Any error to report? 00314 if(error) 00315 break; 00316 00317 //Associate the socket with the relevant interface 00318 error = socketBindToInterface(connection->dataSocket, connection->interface); 00319 //Unable to bind the socket to the desired interface? 00320 if(error) 00321 break; 00322 00323 //The server initiates the data connection from port 20 00324 error = socketBind(connection->dataSocket, &IP_ADDR_ANY, 00325 context->settings.dataPort); 00326 //Any error to report? 00327 if(error) 00328 break; 00329 00330 //Establish data connection 00331 error = socketConnect(connection->dataSocket, 00332 &connection->remoteIpAddr, connection->remotePort); 00333 //Any error to report? 00334 if(error != NO_ERROR && error != ERROR_TIMEOUT) 00335 break; 00336 00337 //Connection is being established 00338 error = NO_ERROR; 00339 00340 //End of exception handling block 00341 } while(0); 00342 00343 //Any error to report? 00344 if(error) 00345 { 00346 //Clean up side effects 00347 ftpServerCloseDataConnection(connection); 00348 //Exit immediately 00349 return error; 00350 } 00351 00352 //Successful processing 00353 return NO_ERROR; 00354 } 00355 00356 00357 /** 00358 * @brief Accept data connection 00359 * @param[in] connection Pointer to the client connection 00360 **/ 00361 00362 void ftpServerAcceptDataConnection(FtpClientConnection *connection) 00363 { 00364 error_t error; 00365 Socket *socket; 00366 IpAddr clientIpAddr; 00367 uint16_t clientPort; 00368 00369 //Accept incoming connection 00370 socket = socketAccept(connection->dataSocket, &clientIpAddr, &clientPort); 00371 //Failure detected? 00372 if(socket == NULL) 00373 return; 00374 00375 //Debug message 00376 TRACE_INFO("FTP server: Data connection established with client %s port %" PRIu16 "...\r\n", 00377 ipAddrToString(&clientIpAddr, NULL), clientPort); 00378 00379 //Close the listening socket 00380 socketClose(connection->dataSocket); 00381 //Save socket handle 00382 connection->dataSocket = socket; 00383 00384 //Force the socket to operate in non-blocking mode 00385 error = socketSetTimeout(connection->dataSocket, 0); 00386 //Any error to report? 00387 if(error) 00388 { 00389 //Clean up side effects 00390 socketClose(connection->dataSocket); 00391 //Exit immediately 00392 return; 00393 } 00394 00395 //Check current state 00396 if(connection->controlState == FTP_CONTROL_STATE_LIST || 00397 connection->controlState == FTP_CONTROL_STATE_RETR) 00398 { 00399 //Prepare to send data 00400 connection->dataState = FTP_DATA_STATE_SEND; 00401 } 00402 else if(connection->controlState == FTP_CONTROL_STATE_STOR || 00403 connection->controlState == FTP_CONTROL_STATE_APPE) 00404 { 00405 //Prepare to receive data 00406 connection->dataState = FTP_DATA_STATE_RECEIVE; 00407 } 00408 else 00409 { 00410 //Data transfer direction is unknown... 00411 connection->dataState = FTP_DATA_STATE_IDLE; 00412 } 00413 } 00414 00415 00416 /** 00417 * @brief Close data connection 00418 * @param[in] connection Pointer to the client connection 00419 **/ 00420 00421 void ftpServerCloseDataConnection(FtpClientConnection *connection) 00422 { 00423 IpAddr clientIpAddr; 00424 uint16_t clientPort; 00425 00426 //Any running data connection? 00427 if(connection->dataSocket != NULL) 00428 { 00429 //Retrieve the address of the peer to which a socket is connected 00430 socketGetRemoteAddr(connection->dataSocket, &clientIpAddr, &clientPort); 00431 00432 //Check whether the data connection is established 00433 if(clientPort != 0) 00434 { 00435 //Debug message 00436 TRACE_INFO("FTP server: Closing data connection with client %s port %" PRIu16 "...\r\n", 00437 ipAddrToString(&clientIpAddr, NULL), clientPort); 00438 } 00439 00440 //Close data connection 00441 socketClose(connection->dataSocket); 00442 connection->dataSocket = NULL; 00443 00444 //Re initialize data connection 00445 connection->passiveMode = FALSE; 00446 connection->remotePort = 0; 00447 00448 //Back to default state 00449 connection->dataState = FTP_DATA_STATE_CLOSED; 00450 } 00451 } 00452 00453 00454 /** 00455 * @brief Retrieve the full pathname 00456 * @param[in] connection Pointer to the client connection 00457 * @param[in] inputPath Relative or absolute path 00458 * @param[out] outputPath Resulting full path 00459 * @param[in] maxLen Maximum acceptable path length 00460 * @return Error code 00461 **/ 00462 00463 error_t ftpServerGetPath(FtpClientConnection *connection, 00464 const char_t *inputPath, char_t *outputPath, size_t maxLen) 00465 { 00466 size_t n; 00467 00468 //Relative or absolute path? 00469 if(pathIsRelative(inputPath)) 00470 { 00471 //Sanity check 00472 if(strlen(connection->currentDir) > maxLen) 00473 return ERROR_FAILURE; 00474 00475 //Copy current directory 00476 strcpy(outputPath, connection->currentDir); 00477 //Append the specified path 00478 pathCombine(outputPath, inputPath, maxLen); 00479 } 00480 else 00481 { 00482 //Sanity check 00483 if(strlen(connection->homeDir) > maxLen) 00484 return ERROR_FAILURE; 00485 00486 //Copy home directory 00487 strcpy(outputPath, connection->homeDir); 00488 //Append the specified path 00489 pathCombine(outputPath, inputPath, maxLen); 00490 } 00491 00492 //Clean the resulting path 00493 pathCanonicalize(outputPath); 00494 pathRemoveSlash(outputPath); 00495 00496 //Calculate the length of the home directory 00497 n = strlen(connection->homeDir); 00498 00499 //Make sure the pathname is valid 00500 if(strncmp(outputPath, connection->homeDir, n)) 00501 return ERROR_INVALID_PATH; 00502 00503 //Successful processing 00504 return NO_ERROR; 00505 } 00506 00507 00508 /** 00509 * @brief Get permissions for the specified file or directory 00510 * @param[in] context Pointer to the FTP server context 00511 * @param[in] connection Pointer to the client connection 00512 * @param[in] path Canonical path of the file 00513 * @return Access rights for the specified file 00514 **/ 00515 00516 uint_t ftpServerGetFilePermissions(FtpServerContext *context, 00517 FtpClientConnection *connection, const char_t *path) 00518 { 00519 size_t n; 00520 uint_t perm; 00521 00522 //Calculate the length of the home directory 00523 n = strlen(connection->homeDir); 00524 00525 //Make sure the pathname is valid 00526 if(!strncmp(path, connection->homeDir, n)) 00527 { 00528 //Strip root directory from the pathname 00529 path = ftpServerStripRootDir(context, path); 00530 00531 //Invoke user-defined callback, if any 00532 if(context->settings.getFilePermCallback != NULL) 00533 { 00534 //Retrieve access rights for the specified file 00535 perm = context->settings.getFilePermCallback(connection, connection->user, path); 00536 } 00537 else 00538 { 00539 //Use default access rights 00540 perm = FTP_FILE_PERM_LIST | FTP_FILE_PERM_READ | FTP_FILE_PERM_WRITE; 00541 } 00542 } 00543 else 00544 { 00545 //The specified pathname is not valid 00546 perm = 0; 00547 } 00548 00549 //Return access rights 00550 return perm; 00551 } 00552 00553 00554 /** 00555 * @brief Strip root dir from specified pathname 00556 * @param[in] context Pointer to the FTP server context 00557 * @param[in] path input pathname 00558 * @return Resulting pathname with root dir stripped 00559 **/ 00560 00561 const char_t *ftpServerStripRootDir(FtpServerContext *context, const char_t *path) 00562 { 00563 //Default directory 00564 static const char_t defaultDir[] = "/"; 00565 00566 //Local variables 00567 size_t m; 00568 size_t n; 00569 00570 //Retrieve the length of the root directory 00571 n = strlen(context->settings.rootDir); 00572 //Retrieve the length of the specified pathname 00573 m = strlen(path); 00574 00575 //Strip the root dir from the specified pathname 00576 if(n <= 1) 00577 return path; 00578 else if(n < m) 00579 return path + n; 00580 else 00581 return defaultDir; 00582 } 00583 00584 00585 /** 00586 * @brief Strip home directory from specified pathname 00587 * @param[in] connection Pointer to the client connection 00588 * @param[in] path input pathname 00589 * @return Resulting pathname with home directory stripped 00590 **/ 00591 00592 const char_t *ftpServerStripHomeDir(FtpClientConnection *connection, const char_t *path) 00593 { 00594 //Default directory 00595 static const char_t defaultDir[] = "/"; 00596 00597 //Local variables 00598 size_t m; 00599 size_t n; 00600 00601 //Retrieve the length of the home directory 00602 n = strlen(connection->homeDir); 00603 //Retrieve the length of the specified pathname 00604 m = strlen(path); 00605 00606 //Strip the home directory from the specified pathname 00607 if(n <= 1) 00608 return path; 00609 else if(n < m) 00610 return path + n; 00611 else 00612 return defaultDir; 00613 } 00614 00615 #endif 00616
Generated on Tue Jul 12 2022 17:10:13 by
1.7.2