ftp_server.c
Go to the documentation of this file.
1 /**
2  * @file ftp_server.c
3  * @brief FTP server (File Transfer Protocol)
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @section Description
26  *
27  * File Transfer Protocol (FTP) is a standard network protocol used to
28  * transfer files from one host to another host over a TCP-based network.
29  * Refer to the following RFCs for complete details:
30  * - RFC 959: File Transfer Protocol (FTP)
31  * - RFC 3659: Extensions to FTP
32  * - RFC 2428: FTP Extensions for IPv6 and NATs
33  *
34  * @author Oryx Embedded SARL (www.oryx-embedded.com)
35  * @version 1.9.0
36  **/
37 
38 //Switch to the appropriate trace level
39 #define TRACE_LEVEL FTP_TRACE_LEVEL
40 
41 //Dependencies
42 #include "ftp/ftp_server.h"
43 #include "ftp/ftp_server_events.h"
45 #include "ftp/ftp_server_misc.h"
46 #include "str.h"
47 #include "path.h"
48 #include "error.h"
49 #include "debug.h"
50 
51 //Check TCP/IP stack configuration
52 #if (FTP_SERVER_SUPPORT == ENABLED)
53 
54 
55 /**
56  * @brief Initialize settings with default values
57  * @param[out] settings Structure that contains FTP server settings
58  **/
59 
61 {
62  //The FTP server is not bound to any interface
63  settings->interface = NULL;
64 
65  //FTP command port number
66  settings->port = FTP_PORT;
67  //FTP data port number
68  settings->dataPort = FTP_DATA_PORT;
69  //Passive port range
72  //Public IPv4 address to be used in PASV replies
74  //Set root directory
75  strcpy(settings->rootDir, "/");
76  //User verification callback function
77  settings->checkUserCallback = NULL;
78  //Password verification callback function
79  settings->checkPasswordCallback = NULL;
80  //Callback used to retrieve file permissions
81  settings->getFilePermCallback = NULL;
82  //Unknown command callback function
83  settings->unknownCommandCallback = NULL;
84 }
85 
86 
87 /**
88  * @brief FTP server initialization
89  * @param[in] context Pointer to the FTP server context
90  * @param[in] settings FTP server specific settings
91  * @return Error code
92  **/
93 
95 {
96  error_t error;
97 
98  //Debug message
99  TRACE_INFO("Initializing FTP server...\r\n");
100 
101  //Ensure the parameters are valid
102  if(context == NULL || settings == NULL)
104 
105  //Check passive port range
106  if(settings->passivePortMax <= settings->passivePortMin)
108 
109  //Clear the FTP server context
110  memset(context, 0, sizeof(FtpServerContext));
111 
112  //Save user settings
113  context->settings = *settings;
114 
115  //Clean the root directory path
117  pathRemoveSlash(context->settings.rootDir);
118 
119  //Create an event object to poll the state of sockets
120  if(!osCreateEvent(&context->event))
121  {
122  //Failed to create event
123  return ERROR_OUT_OF_RESOURCES;
124  }
125 
126  //Start of exception handling block
127  do
128  {
129  //Open a TCP socket
131  //Failed to open socket?
132  if(context->socket == NULL)
133  {
134  //Report an error
135  error = ERROR_OPEN_FAILED;
136  //Exit immediately
137  break;
138  }
139 
140  //Set timeout for blocking functions
141  error = socketSetTimeout(context->socket, INFINITE_DELAY);
142  //Any error to report?
143  if(error)
144  break;
145 
146  //Adjust the size of the TX buffer
147  error = socketSetTxBufferSize(context->socket,
149  //Any error to report?
150  if(error)
151  break;
152 
153  //Adjust the size of the RX buffer
154  error = socketSetRxBufferSize(context->socket,
156  //Any error to report?
157  if(error)
158  break;
159 
160  //Associate the socket with the relevant interface
161  error = socketBindToInterface(context->socket, settings->interface);
162  //Unable to bind the socket to the desired interface?
163  if(error)
164  break;
165 
166  //Bind newly created socket to port 21
167  error = socketBind(context->socket, &IP_ADDR_ANY, settings->port);
168  //Failed to bind socket to port 21?
169  if(error)
170  break;
171 
172  //Place socket in listening state
173  error = socketListen(context->socket, FTP_SERVER_BACKLOG);
174  //Any failure to report?
175  if(error)
176  break;
177 
178  //End of exception handling block
179  } while(0);
180 
181  //Did we encounter an error?
182  if(error)
183  {
184  //Free previously allocated resources
185  osDeleteEvent(&context->event);
186  //Close socket
187  socketClose(context->socket);
188  }
189 
190  //Return status code
191  return error;
192 }
193 
194 
195 /**
196  * @brief Start FTP server
197  * @param[in] context Pointer to the FTP server context
198  * @return Error code
199  **/
200 
202 {
203  OsTask *task;
204 
205  //Debug message
206  TRACE_INFO("Starting FTP server...\r\n");
207 
208  //Make sure the FTP server context is valid
209  if(context == NULL)
211 
212  //Create the FTP server task
213  task = osCreateTask("FTP Server", (OsTaskCode) ftpServerTask,
215 
216  //Unable to create the task?
217  if(task == OS_INVALID_HANDLE)
218  return ERROR_OUT_OF_RESOURCES;
219 
220  //Successful processing
221  return NO_ERROR;
222 }
223 
224 
225 /**
226  * @brief Set home directory
227  * @param[in] connection Pointer to the client connection
228  * @param[in] homeDir NULL-terminated string specifying the home directory
229  * @return Error code
230  **/
231 
233 {
234  //Ensure the parameters are valid
235  if(connection == NULL || homeDir == NULL)
237 
238  //Set home directory
239  pathCombine(connection->homeDir, homeDir, FTP_SERVER_MAX_HOME_DIR_LEN);
240  //Clean the resulting path
241  pathCanonicalize(connection->homeDir);
242  pathRemoveSlash(connection->homeDir);
243 
244  //Set current directory
245  strcpy(connection->currentDir, connection->homeDir);
246 
247  //Successful processing
248  return NO_ERROR;
249 }
250 
251 
252 /**
253  * @brief FTP server task
254  * @param[in] context Pointer to the FTP server context
255  **/
256 
258 {
259  error_t error;
260  uint_t i;
261  systime_t time;
262  FtpClientConnection *connection;
263 
264 #if (NET_RTOS_SUPPORT == ENABLED)
265  //Process events
266  while(1)
267  {
268 #endif
269  //Clear event descriptor set
270  memset(context->eventDesc, 0, sizeof(context->eventDesc));
271 
272  //Specify the events the application is interested in
273  for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++)
274  {
275  //Point to the structure describing the current connection
276  connection = context->connection[i];
277 
278  //Loop through active connections only
279  if(connection != NULL)
280  {
281  //Ensure the control connection is opened
282  if(connection->controlSocket != NULL)
283  {
284  //Check the state of the control connection
285  if(connection->responseLength > 0)
286  {
287  //Wait until there is more room in the send buffer
288  context->eventDesc[2 * i].socket = connection->controlSocket;
289  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_TX_READY;
290  }
291  else if(connection->controlState == FTP_CONTROL_STATE_WAIT_ACK)
292  {
293  //Wait for all the data to be transmitted and acknowledged
294  context->eventDesc[2 * i].socket = connection->controlSocket;
295  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_TX_ACKED;
296  }
297  else if(connection->controlState == FTP_CONTROL_STATE_SHUTDOWN_TX)
298  {
299  //Wait for the FIN to be acknowledged
300  context->eventDesc[2 * i].socket = connection->controlSocket;
301  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_TX_SHUTDOWN;
302  }
303  else if(connection->controlState == FTP_CONTROL_STATE_SHUTDOWN_RX)
304  {
305  //Wait for a FIN to be received
306  context->eventDesc[2 * i].socket = connection->controlSocket;
307  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_RX_SHUTDOWN;
308  }
309  else
310  {
311  //Wait for data to be available for reading
312  context->eventDesc[2 * i].socket = connection->controlSocket;
313  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_RX_READY;
314  }
315  }
316 
317  //Ensure the data connection is opened
318  if(connection->dataSocket != NULL)
319  {
320  //Check the state of the data connection
321  if(connection->dataState == FTP_DATA_STATE_LISTEN ||
322  connection->dataState == FTP_DATA_STATE_RECEIVE)
323  {
324  //Wait for data to be available for reading
325  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
326  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_RX_READY;
327  }
328  else if(connection->dataState == FTP_DATA_STATE_SEND)
329  {
330  //Wait until there is more room in the send buffer
331  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
332  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_TX_READY;
333  }
334 
335  else if(connection->dataState == FTP_DATA_STATE_WAIT_ACK)
336  {
337  //Wait for all the data to be transmitted and acknowledged
338  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
339  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_TX_ACKED;
340  }
341  else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_TX)
342  {
343  //Wait for the FIN to be acknowledged
344  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
345  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_TX_SHUTDOWN;
346  }
347  else if(connection->dataState == FTP_DATA_STATE_SHUTDOWN_RX)
348  {
349  //Wait for a FIN to be received
350  context->eventDesc[2 * i + 1].socket = connection->dataSocket;
351  context->eventDesc[2 * i + 1].eventMask = SOCKET_EVENT_RX_SHUTDOWN;
352  }
353  }
354  }
355  }
356 
357  //Accept connection request events
358  context->eventDesc[2 * i].socket = context->socket;
359  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_RX_READY;
360 
361  //Wait for one of the set of sockets to become ready to perform I/O
362  error = socketPoll(context->eventDesc, 2 * FTP_SERVER_MAX_CONNECTIONS + 1,
364 
365  //Get current time
366  time = osGetSystemTime();
367 
368  //Verify status code
369  if(!error)
370  {
371  //Event-driven processing
372  for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++)
373  {
374  //Point to the structure describing the current connection
375  connection = context->connection[i];
376 
377  //Make sure the control connection is still active
378  if(connection != NULL && connection->controlSocket != NULL)
379  {
380  //Check whether the control socket is to ready to perform I/O
381  if(context->eventDesc[2 * i].eventFlags)
382  {
383  //Update time stamp
384  connection->timestamp = time;
385  //Control connection event handler
386  ftpServerControlEventHandler(context, connection,
387  context->eventDesc[2 * i].eventFlags);
388  }
389  }
390 
391  //The connection may have been closed...
392  connection = context->connection[i];
393 
394  //Make sure the data connection is still active
395  if(connection != NULL && connection->dataSocket != NULL)
396  {
397  //Check whether the data socket is ready to perform I/O
398  if(context->eventDesc[2 * i + 1].eventFlags)
399  {
400  //Update time stamp
401  connection->timestamp = time;
402  //Data connection event handler
403  ftpServerDataEventHandler(context, connection,
404  context->eventDesc[2 * i + 1].eventFlags);
405  }
406  }
407  }
408 
409  //Check the state of the listening socket
410  if(context->eventDesc[2 * i].eventFlags & SOCKET_EVENT_RX_READY)
411  {
412  //Process incoming connection request
413  connection = ftpServerAcceptControlConnection(context);
414  //Initialize time stamp
415  if(connection != NULL)
416  connection->timestamp = time;
417  }
418  }
419 
420  //Manage timeouts
421  for(i = 0; i < FTP_SERVER_MAX_CONNECTIONS; i++)
422  {
423  //Point to the structure describing the current connection
424  connection = context->connection[i];
425 
426  //Any client connected?
427  if(connection != NULL)
428  {
429  //Disconnect inactive client after idle timeout
430  if((time - connection->timestamp) >= FTP_SERVER_TIMEOUT)
431  {
432  //Debug message
433  TRACE_INFO("FTP server: Closing inactive connection...\r\n");
434  //Close connection with the client
435  ftpServerCloseConnection(context, connection);
436  }
437  }
438  }
439 #if (NET_RTOS_SUPPORT == ENABLED)
440  }
441 #endif
442 }
443 
444 #endif
char_t currentDir[FTP_SERVER_MAX_PATH_LEN+1]
Current directory.
Definition: ftp_server.h:252
Helper functions for FTP server.
uint32_t systime_t
Definition: compiler_port.h:44
char char_t
Definition: compiler_port.h:41
Socket * socket
Listening socket.
Definition: ftp_server.h:325
Socket * socket
Handle to a socket to monitor.
Definition: socket.h:305
FtpUnknownCommandCallback unknownCommandCallback
Unknown command callback function.
Definition: ftp_server.h:313
NetInterface * interface
Underlying network interface.
Definition: ftp_server.h:303
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t time
void ftpServerTask(FtpServerContext *context)
FTP server task.
Definition: ftp_server.c:257
void ftpServerCloseConnection(FtpServerContext *context, FtpClientConnection *connection)
Close client connection properly.
Debugging facilities.
#define FTP_SERVER_PASSIVE_PORT_MIN
Definition: ftp_server.h:142
Socket * controlSocket
Control connection socket.
Definition: ftp_server.h:242
error_t ftpServerStart(FtpServerContext *context)
Start FTP server.
Definition: ftp_server.c:201
#define FTP_SERVER_BACKLOG
Definition: ftp_server.h:79
FtpControlConnState controlState
Control connection state.
Definition: ftp_server.h:241
OsTask * osCreateTask(const char_t *name, OsTaskCode taskCode, void *param, size_t stackSize, int_t priority)
Create a new task.
Invalid parameter.
Definition: error.h:45
FtpCheckPasswordCallback checkPasswordCallback
Password verification callback function.
Definition: ftp_server.h:311
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:420
#define FTP_SERVER_PRIORITY
Definition: ftp_server.h:53
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:797
String manipulation helper functions.
const IpAddr IP_ADDR_ANY
Definition: ip.c:42
FtpCheckUserCallback checkUserCallback
User verification callback function.
Definition: ftp_server.h:310
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:330
uint_t eventFlags
Returned events.
Definition: socket.h:307
bool_t osCreateEvent(OsEvent *event)
Create an event object.
FTP server (command processing)
Ipv4Addr publicIpv4Addr
Public IPv4 address to be used in PASV replies.
Definition: ftp_server.h:308
FTP server context.
Definition: ftp_server.h:321
error_t socketPoll(SocketEventDesc *eventDesc, uint_t size, OsEvent *extEvent, systime_t timeout)
Wait for one of a set of sockets to become ready to perform I/O.
Definition: socket.c:857
Error codes description.
#define FTP_SERVER_CTRL_SOCKET_BUFFER_SIZE
Definition: ftp_server.h:128
FtpClientConnection * connection[FTP_SERVER_MAX_CONNECTIONS]
Client connections.
Definition: ftp_server.h:327
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:216
SocketEventDesc eventDesc[2 *FTP_SERVER_MAX_CONNECTIONS+1]
The events the application is interested in.
Definition: ftp_server.h:328
#define FTP_PORT
Definition: ftp_server.h:155
FtpDataConnState dataState
Data connection state.
Definition: ftp_server.h:243
error_t socketSetRxBufferSize(Socket *socket, size_t size)
Specify the size of the receive buffer.
Definition: socket.c:275
void pathRemoveSlash(char_t *path)
Remove the trailing slash from a given path.
Definition: path.c:300
uint_t eventMask
Requested events.
Definition: socket.h:306
size_t responseLength
Number of bytes available in the response buffer.
Definition: ftp_server.h:257
Task object.
FTP server settings.
Definition: ftp_server.h:301
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:110
char_t rootDir[FTP_SERVER_MAX_ROOT_DIR_LEN+1]
Root directory.
Definition: ftp_server.h:309
#define INFINITE_DELAY
Definition: os_port.h:72
FtpClientConnection * ftpServerAcceptControlConnection(FtpServerContext *context)
Accept control connection.
OsEvent event
Event object used to poll the sockets.
Definition: ftp_server.h:324
error_t socketSetTxBufferSize(Socket *socket, size_t size)
Specify the size of the send buffer.
Definition: socket.c:241
#define FTP_SERVER_PASSIVE_PORT_MAX
Definition: ftp_server.h:149
error_t ftpServerSetHomeDir(FtpClientConnection *connection, const char_t *homeDir)
Set home directory.
Definition: ftp_server.c:232
FtpGetFilePermCallback getFilePermCallback
Callback used to retrieve file permissions.
Definition: ftp_server.h:312
#define FTP_SERVER_MAX_CONNECTIONS
Definition: ftp_server.h:58
void(* OsTaskCode)(void *param)
Task routine.
#define FTP_DATA_PORT
Definition: ftp_server.h:157
#define TRACE_INFO(...)
Definition: debug.h:86
Success.
Definition: error.h:42
Path manipulation helper functions.
error_t
Error codes.
Definition: error.h:40
unsigned int uint_t
Definition: compiler_port.h:43
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:92
uint16_t passivePortMax
Passive port range (upper value)
Definition: ftp_server.h:307
FTP server (event handlers)
#define FTP_SERVER_SOCKET_POLLING_TIMEOUT
Definition: ftp_server.h:72
#define FTP_SERVER_TIMEOUT
Definition: ftp_server.h:65
void ftpServerGetDefaultSettings(FtpServerSettings *settings)
Initialize settings with default values.
Definition: ftp_server.c:60
Socket * dataSocket
Data connection socket.
Definition: ftp_server.h:244
void ftpServerDataEventHandler(FtpServerContext *context, FtpClientConnection *connection, uint_t eventFlags)
Data connection event handler.
void osDeleteEvent(OsEvent *event)
Delete an event object.
FtpServerSettings settings
User settings.
Definition: ftp_server.h:323
#define FTP_SERVER_MAX_HOME_DIR_LEN
Definition: ftp_server.h:107
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:95
error_t ftpServerInit(FtpServerContext *context, const FtpServerSettings *settings)
FTP server initialization.
Definition: ftp_server.c:94
void ftpServerControlEventHandler(FtpServerContext *context, FtpClientConnection *connection, uint_t eventFlags)
Control connection event handler.
#define OS_INVALID_HANDLE
Definition: os_port.h:77
uint16_t port
FTP command port number.
Definition: ftp_server.h:304
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:331
#define FTP_SERVER_STACK_SIZE
Definition: ftp_server.h:46
uint16_t dataPort
FTP data port number.
Definition: ftp_server.h:305
char_t homeDir[FTP_SERVER_MAX_HOME_DIR_LEN+1]
Home directory.
Definition: ftp_server.h:251
systime_t timestamp
Time stamp to manage timeout.
Definition: ftp_server.h:240
FTP server (File Transfer Protocol)
uint16_t passivePortMin
Passive port range (lower value)
Definition: ftp_server.h:306
error_t socketBindToInterface(Socket *socket, NetInterface *interface)
Bind a socket to a particular network interface.
Definition: socket.c:309
FTP client connection.
Definition: ftp_server.h:236