Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ftp_server_misc.c Source File

ftp_server_misc.c

Go to the documentation of this file.
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