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  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @section Description
28  *
29  * File Transfer Protocol (FTP) is a standard network protocol used to
30  * transfer files from one host to another host over a TCP-based network.
31  * Refer to the following RFCs for complete details:
32  * - RFC 959: File Transfer Protocol (FTP)
33  * - RFC 3659: Extensions to FTP
34  * - RFC 2428: FTP Extensions for IPv6 and NATs
35  *
36  * @author Oryx Embedded SARL (www.oryx-embedded.com)
37  * @version 2.4.4
38  **/
39 
40 //Switch to the appropriate trace level
41 #define TRACE_LEVEL FTP_TRACE_LEVEL
42 
43 //Dependencies
44 #include "ftp/ftp_server.h"
45 #include "ftp/ftp_server_control.h"
46 #include "ftp/ftp_server_data.h"
47 #include "ftp/ftp_server_misc.h"
48 #include "path.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  //Default task parameters
63  settings->task = OS_TASK_DEFAULT_PARAMS;
65  settings->task.priority = FTP_SERVER_PRIORITY;
66 
67  //The FTP server is not bound to any interface
68  settings->interface = NULL;
69 
70  //FTP command port number
71  settings->port = FTP_PORT;
72  //FTP data port number
73  settings->dataPort = FTP_DATA_PORT;
74 
75  //Passive port range
78 
79  //Public IPv4 address to be used in PASV replies
81 
82  //Default security mode (no security)
83  settings->mode = FTP_SERVER_MODE_PLAINTEXT;
84 
85  //Client connections
86  settings->maxConnections = 0;
87  settings->connections = NULL;
88 
89  //Set root directory
90  osStrcpy(settings->rootDir, "/");
91 
92  //Connection callback function
93  settings->connectCallback = NULL;
94  //Disconnection callback function
95  settings->disconnectCallback = NULL;
96 
97 #if (FTP_SERVER_TLS_SUPPORT == ENABLED)
98  //TLS initialization callback function
99  settings->tlsInitCallback = NULL;
100 #endif
101 
102  //User verification callback function
103  settings->checkUserCallback = NULL;
104  //Password verification callback function
105  settings->checkPasswordCallback = NULL;
106  //Callback used to retrieve file permissions
107  settings->getFilePermCallback = NULL;
108  //Unknown command callback function
109  settings->unknownCommandCallback = NULL;
110 }
111 
112 
113 /**
114  * @brief FTP server initialization
115  * @param[in] context Pointer to the FTP server context
116  * @param[in] settings FTP server specific settings
117  * @return Error code
118  **/
119 
121  const FtpServerSettings *settings)
122 {
123  error_t error;
124  uint_t i;
125 
126  //Debug message
127  TRACE_INFO("Initializing FTP server...\r\n");
128 
129  //Ensure the parameters are valid
130  if(context == NULL || settings == NULL)
132 
133  //Sanity check
134  if(settings->passivePortMax <= settings->passivePortMin)
135  {
137  }
138 
139  //Invalid number of client connections?
140  if(settings->maxConnections < 1 ||
142  {
144  }
145 
146  //Invalid pointer?
147  if(settings->connections == NULL)
149 
150  //Clear the FTP server context
151  osMemset(context, 0, sizeof(FtpServerContext));
152 
153  //Initialize task parameters
154  context->taskParams = settings->task;
155  context->taskId = OS_INVALID_TASK_ID;
156 
157  //Save user settings
158  context->settings = *settings;
159  //Client connections
160  context->connections = settings->connections;
161 
162  //Clean the root directory path
163  pathCanonicalize(context->settings.rootDir);
164  pathRemoveSlash(context->settings.rootDir);
165 
166  //Loop through client connections
167  for(i = 0; i < context->settings.maxConnections; i++)
168  {
169  //Initialize the structure representing the client connection
170  osMemset(&context->connections[i], 0, sizeof(FtpClientConnection));
171  }
172 
173  //Initialize status code
174  error = NO_ERROR;
175 
176  //Create an event object to poll the state of sockets
177  if(!osCreateEvent(&context->event))
178  {
179  //Failed to create event
180  error = ERROR_OUT_OF_RESOURCES;
181  }
182 
183 #if (FTP_SERVER_TLS_SUPPORT == ENABLED && TLS_TICKET_SUPPORT == ENABLED)
184  //Check status code
185  if(!error)
186  {
187  //Initialize ticket encryption context
188  error = tlsInitTicketContext(&context->tlsTicketContext);
189  }
190 #endif
191 
192  //Any error to report?
193  if(error)
194  {
195  //Clean up side effects
196  ftpServerDeinit(context);
197  }
198 
199  //Return status code
200  return error;
201 }
202 
203 
204 /**
205  * @brief Start FTP server
206  * @param[in] context Pointer to the FTP server context
207  * @return Error code
208  **/
209 
211 {
212  error_t error;
213 
214  //Make sure the FTP server context is valid
215  if(context == NULL)
217 
218  //Debug message
219  TRACE_INFO("Starting FTP server...\r\n");
220 
221  //Make sure the FTP server is not already running
222  if(context->running)
223  return ERROR_ALREADY_RUNNING;
224 
225  //Start of exception handling block
226  do
227  {
228  //Open a TCP socket
230  //Failed to open socket?
231  if(context->socket == NULL)
232  {
233  //Report an error
234  error = ERROR_OPEN_FAILED;
235  break;
236  }
237 
238  //Force the socket to operate in non-blocking mode
239  error = socketSetTimeout(context->socket, 0);
240  //Any error to report?
241  if(error)
242  break;
243 
244  //Adjust the size of the TX buffer
245  error = socketSetTxBufferSize(context->socket,
247  //Any error to report?
248  if(error)
249  break;
250 
251  //Adjust the size of the RX buffer
252  error = socketSetRxBufferSize(context->socket,
254  //Any error to report?
255  if(error)
256  break;
257 
258  //Associate the socket with the relevant interface
259  error = socketBindToInterface(context->socket,
260  context->settings.interface);
261  //Any error to report?
262  if(error)
263  break;
264 
265  //The FTP server listens for connection requests on port 21
266  error = socketBind(context->socket, &IP_ADDR_ANY,
267  context->settings.port);
268  //Any error to report?
269  if(error)
270  break;
271 
272  //Place socket in listening state
273  error = socketListen(context->socket, FTP_SERVER_BACKLOG);
274  //Any failure to report?
275  if(error)
276  break;
277 
278  //Start the FTP server
279  context->stop = FALSE;
280  context->running = TRUE;
281 
282  //Create a task
283  context->taskId = osCreateTask("FTP Server", (OsTaskCode) ftpServerTask,
284  context, &context->taskParams);
285 
286  //Failed to create task?
287  if(context->taskId == OS_INVALID_TASK_ID)
288  {
289  //Report an error
290  error = ERROR_OUT_OF_RESOURCES;
291  break;
292  }
293 
294  //End of exception handling block
295  } while(0);
296 
297  //Any error to report?
298  if(error)
299  {
300  //Clean up side effects
301  context->running = FALSE;
302 
303  //Close listening socket
304  socketClose(context->socket);
305  context->socket = NULL;
306  }
307 
308  //Return status code
309  return error;
310 }
311 
312 
313 /**
314  * @brief Stop FTP server
315  * @param[in] context Pointer to the FTP server context
316  * @return Error code
317  **/
318 
320 {
321  uint_t i;
322 
323  //Make sure the FTP server context is valid
324  if(context == NULL)
326 
327  //Debug message
328  TRACE_INFO("Stopping FTP server...\r\n");
329 
330  //Check whether the FTP server is running
331  if(context->running)
332  {
333 #if (NET_RTOS_SUPPORT == ENABLED)
334  //Stop the FTP server
335  context->stop = TRUE;
336  //Send a signal to the task to abort any blocking operation
337  osSetEvent(&context->event);
338 
339  //Wait for the task to terminate
340  while(context->running)
341  {
342  osDelayTask(1);
343  }
344 #endif
345 
346  //Loop through the connection table
347  for(i = 0; i < context->settings.maxConnections; i++)
348  {
349  //Close client connection
350  ftpServerCloseConnection(&context->connections[i]);
351  }
352 
353  //Close listening socket
354  socketClose(context->socket);
355  context->socket = NULL;
356  }
357 
358  //Successful processing
359  return NO_ERROR;
360 }
361 
362 
363 /**
364  * @brief Set home directory
365  * @param[in] connection Pointer to the client connection
366  * @param[in] homeDir NULL-terminated string specifying the home directory
367  * @return Error code
368  **/
369 
371  const char_t *homeDir)
372 {
373  //Check parameters
374  if(connection == NULL || homeDir == NULL)
376 
377  //Set home directory
378  pathCombine(connection->homeDir, homeDir, FTP_SERVER_MAX_HOME_DIR_LEN);
379 
380  //Clean the resulting path
381  pathCanonicalize(connection->homeDir);
382  pathRemoveSlash(connection->homeDir);
383 
384  //Set current directory
385  osStrcpy(connection->currentDir, connection->homeDir);
386 
387  //Successful processing
388  return NO_ERROR;
389 }
390 
391 
392 /**
393  * @brief FTP server task
394  * @param[in] context Pointer to the FTP server context
395  **/
396 
398 {
399  error_t error;
400  uint_t i;
401  systime_t time;
402  systime_t timeout;
403  FtpClientConnection *connection;
404 
405 #if (NET_RTOS_SUPPORT == ENABLED)
406  //Task prologue
407  osEnterTask();
408 
409  //Process events
410  while(1)
411  {
412 #endif
413  //Set polling timeout
414  timeout = FTP_SERVER_TICK_INTERVAL;
415 
416  //Clear event descriptor set
417  osMemset(context->eventDesc, 0, sizeof(context->eventDesc));
418 
419  //Specify the events the application is interested in
420  for(i = 0; i < context->settings.maxConnections; i++)
421  {
422  //Point to the structure describing the current connection
423  connection = &context->connections[i];
424 
425  //Check whether the control connection is active
426  if(connection->controlChannel.socket != NULL)
427  {
428  //Register the events related to the control connection
430  &context->eventDesc[2 * i]);
431 
432  //Check whether the socket is ready for I/O operation
433  if(context->eventDesc[2 * i].eventFlags != 0)
434  {
435  //No need to poll the underlying socket for incoming traffic
436  timeout = 0;
437  }
438  }
439 
440  //Check whether the data connection is active
441  if(connection->dataChannel.socket != NULL)
442  {
443  //Register the events related to the data connection
445  &context->eventDesc[2 * i + 1]);
446 
447  //Check whether the socket is ready for I/O operation
448  if(context->eventDesc[2 * i + 1].eventFlags != 0)
449  {
450  //No need to poll the underlying socket for incoming traffic
451  timeout = 0;
452  }
453  }
454  }
455 
456  //Accept connection request events
457  context->eventDesc[2 * i].socket = context->socket;
458  context->eventDesc[2 * i].eventMask = SOCKET_EVENT_RX_READY;
459 
460  //Wait for one of the set of sockets to become ready to perform I/O
461  error = socketPoll(context->eventDesc,
462  2 * context->settings.maxConnections + 1, &context->event, timeout);
463 
464  //Get current time
465  time = osGetSystemTime();
466 
467  //Check status code
468  if(error == NO_ERROR || error == ERROR_TIMEOUT ||
469  error == ERROR_WAIT_CANCELED)
470  {
471  //Stop request?
472  if(context->stop)
473  {
474  //Stop FTP server operation
475  context->running = FALSE;
476  //Task epilogue
477  osExitTask();
478  //Kill ourselves
480  }
481 
482  //Event-driven processing
483  for(i = 0; i < context->settings.maxConnections; i++)
484  {
485  //Point to the structure describing the current connection
486  connection = &context->connections[i];
487 
488  //Check whether the control connection is active
489  if(connection->controlChannel.socket != NULL)
490  {
491  //Check whether the control socket is to ready to perform I/O
492  if(context->eventDesc[2 * i].eventFlags)
493  {
494  //Update time stamp
495  connection->timestamp = time;
496 
497  //Control connection event handler
499  context->eventDesc[2 * i].eventFlags);
500  }
501  }
502 
503  //Check whether the data connection is active
504  if(connection->dataChannel.socket != NULL)
505  {
506  //Check whether the data socket is ready to perform I/O
507  if(context->eventDesc[2 * i + 1].eventFlags)
508  {
509  //Update time stamp
510  connection->timestamp = time;
511 
512  //Data connection event handler
514  context->eventDesc[2 * i + 1].eventFlags);
515  }
516  }
517  }
518 
519  //Check the state of the listening socket
520  if(context->eventDesc[2 * i].eventFlags & SOCKET_EVENT_RX_READY)
521  {
522  //Accept connection request
524  }
525  }
526 
527  //Handle periodic operations
528  ftpServerTick(context);
529 
530 #if (NET_RTOS_SUPPORT == ENABLED)
531  }
532 #endif
533 }
534 
535 
536 /**
537  * @brief Release FTP server context
538  * @param[in] context Pointer to the FTP server context
539  **/
540 
542 {
543  //Make sure the FTP server context is valid
544  if(context != NULL)
545  {
546  //Free previously allocated resources
547  osDeleteEvent(&context->event);
548 
549 #if (FTP_SERVER_TLS_SUPPORT == ENABLED && TLS_TICKET_SUPPORT == ENABLED)
550  //Release ticket encryption context
551  tlsFreeTicketContext(&context->tlsTicketContext);
552 #endif
553 
554  //Clear FTP server context
555  osMemset(context, 0, sizeof(FtpServerContext));
556  }
557 }
558 
559 #endif
#define FtpServerContext
Definition: ftp_server.h:208
OsTaskId osCreateTask(const char_t *name, OsTaskCode taskCode, void *arg, const OsTaskParameters *params)
Create a task.
Path manipulation helper functions.
error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort)
Associate a local address with a socket.
Definition: socket.c:1316
error_t tlsInitTicketContext(TlsTicketContext *ticketContext)
Initialize ticket encryption context.
Definition: tls_ticket.c:49
error_t ftpServerInit(FtpServerContext *context, const FtpServerSettings *settings)
FTP server initialization.
Definition: ftp_server.c:120
uint16_t passivePortMin
Passive port range (lower value)
Definition: ftp_server.h:356
#define osExitTask()
FtpServerConnectCallback connectCallback
Connection callback function.
Definition: ftp_server.h:363
#define FTP_SERVER_MIN_TCP_BUFFER_SIZE
Definition: ftp_server.h:137
FTP data connection.
#define FTP_SERVER_PRIORITY
Definition: ftp_server.h:62
#define TRUE
Definition: os_port.h:50
#define OS_INVALID_TASK_ID
void socketClose(Socket *socket)
Close an existing socket.
Definition: socket.c:2062
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
error_t ftpServerSetHomeDir(FtpClientConnection *connection, const char_t *homeDir)
Set home directory.
Definition: ftp_server.c:370
NetInterface * interface
Underlying network interface.
Definition: ftp_server.h:353
uint16_t dataPort
FTP data port number.
Definition: ftp_server.h:355
@ SOCKET_TYPE_STREAM
Definition: socket.h:92
#define OS_SELF_TASK_ID
uint_t mode
Security modes.
Definition: ftp_server.h:359
Helper functions for FTP server.
void ftpServerTick(FtpServerContext *context)
Handle periodic operations.
error_t socketSetTxBufferSize(Socket *socket, size_t size)
Specify the size of the TCP send buffer.
Definition: socket.c:1201
uint16_t passivePortMax
Passive port range (upper value)
Definition: ftp_server.h:357
FTP server settings.
Definition: ftp_server.h:351
void ftpServerTask(FtpServerContext *context)
FTP server task.
Definition: ftp_server.c:397
@ ERROR_OPEN_FAILED
Definition: error.h:75
const IpAddr IP_ADDR_ANY
Definition: ip.c:53
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:158
OsTaskParameters task
Task parameters.
Definition: ftp_server.h:352
void osDeleteTask(OsTaskId taskId)
Delete a task.
#define FALSE
Definition: os_port.h:46
error_t socketSetRxBufferSize(Socket *socket, size_t size)
Specify the size of the TCP receive buffer.
Definition: socket.c:1237
FtpServerCheckPasswordCallback checkPasswordCallback
Password verification callback function.
Definition: ftp_server.h:369
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
FtpServerGetFilePermCallback getFilePermCallback
Callback used to retrieve file permissions.
Definition: ftp_server.h:370
error_t
Error codes.
Definition: error.h:43
void(* OsTaskCode)(void *arg)
Task routine.
Ipv4Addr publicIpv4Addr
Public IPv4 address to be used in PASV replies.
Definition: ftp_server.h:358
#define FTP_SERVER_TICK_INTERVAL
Definition: ftp_server.h:81
#define FTP_SERVER_BACKLOG
Definition: ftp_server.h:88
void ftpServerRegisterDataChannelEvents(FtpClientConnection *connection, SocketEventDesc *eventDesc)
Register data connection events.
void osDeleteEvent(OsEvent *event)
Delete an event object.
void ftpServerAcceptControlChannel(FtpServerContext *context)
Accept control connection.
void ftpServerCloseConnection(FtpClientConnection *connection)
Close client connection properly.
const OsTaskParameters OS_TASK_DEFAULT_PARAMS
void ftpServerGetDefaultSettings(FtpServerSettings *settings)
Initialize settings with default values.
Definition: ftp_server.c:60
#define TRACE_INFO(...)
Definition: debug.h:95
uint16_t port
FTP command port number.
Definition: ftp_server.h:354
#define FTP_SERVER_MAX_HOME_DIR_LEN
Definition: ftp_server.h:116
Socket * socketOpen(uint_t type, uint_t protocol)
Create a socket (UDP or TCP)
Definition: socket.c:125
error_t ftpServerStop(FtpServerContext *context)
Stop FTP server.
Definition: ftp_server.c:319
#define osEnterTask()
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:2149
#define socketBindToInterface
Definition: net_legacy.h:193
FtpServerTlsInitCallback tlsInitCallback
TLS initialization callback function.
Definition: ftp_server.h:366
#define FTP_SERVER_PASSIVE_PORT_MIN
Definition: ftp_server.h:172
error_t ftpServerStart(FtpServerContext *context)
Start FTP server.
Definition: ftp_server.c:210
uint32_t systime_t
System time.
@ ERROR_TIMEOUT
Definition: error.h:95
char char_t
Definition: compiler_port.h:48
#define FTP_SERVER_STACK_SIZE
Definition: ftp_server.h:55
uint32_t time
#define FTP_PORT
Definition: ftp_server.h:197
@ SOCKET_EVENT_RX_READY
Definition: socket.h:179
char_t rootDir[FTP_SERVER_MAX_ROOT_DIR_LEN+1]
Root directory.
Definition: ftp_server.h:362
@ ERROR_WAIT_CANCELED
Definition: error.h:73
bool_t osCreateEvent(OsEvent *event)
Create an event object.
FtpClientConnection * connections
Client connections.
Definition: ftp_server.h:361
void ftpServerRegisterControlChannelEvents(FtpClientConnection *connection, SocketEventDesc *eventDesc)
Register control connection events.
FTP server (File Transfer Protocol)
FtpServerDisconnectCallback disconnectCallback
Disconnection callback function.
Definition: ftp_server.h:364
@ FTP_SERVER_MODE_PLAINTEXT
Definition: ftp_server.h:255
#define FtpClientConnection
Definition: ftp_server.h:212
#define FTP_SERVER_PASSIVE_PORT_MAX
Definition: ftp_server.h:179
void osDelayTask(systime_t delay)
Delay routine.
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
void ftpServerDeinit(FtpServerContext *context)
Release FTP server context.
Definition: ftp_server.c:541
FtpServerCheckUserCallback checkUserCallback
User verification callback function.
Definition: ftp_server.h:368
void pathRemoveSlash(char_t *path)
Remove the trailing slash from a given path.
Definition: path.c:360
FtpServerUnknownCommandCallback unknownCommandCallback
Unknown command callback function.
Definition: ftp_server.h:371
void tlsFreeTicketContext(TlsTicketContext *ticketContext)
Properly dispose ticket encryption context.
Definition: tls_ticket.c:448
#define FTP_SERVER_MAX_CONNECTIONS
Definition: ftp_server.h:67
uint_t maxConnections
Maximum number of client connections.
Definition: ftp_server.h:360
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:135
#define FTP_DATA_PORT
Definition: ftp_server.h:199
#define osStrcpy(s1, s2)
Definition: os_port.h:207
@ SOCKET_IP_PROTO_TCP
Definition: socket.h:107
error_t socketSetTimeout(Socket *socket, systime_t timeout)
Set timeout value for blocking operations.
Definition: socket.c:148
void ftpServerProcessDataChannelEvents(FtpClientConnection *connection, uint_t eventFlags)
Data connection event handler.
FTP control connection.
@ ERROR_ALREADY_RUNNING
Definition: error.h:293
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define IPV4_UNSPECIFIED_ADDR
Definition: ipv4.h:117
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:394
systime_t osGetSystemTime(void)
Retrieve system time.
void ftpServerProcessControlChannelEvents(FtpClientConnection *connection, uint_t eventFlags)
Control connection event handler.
error_t socketListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: socket.c:1413