summaryrefslogtreecommitdiffstats
path: root/cpukit/ftpd
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2001-01-12 13:51:56 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2001-01-12 13:51:56 +0000
commit3f777d0edfa0ea8413d2c83eacbbda135970cb3c (patch)
tree6ea6b3158c2ccd62d7f599e6720a9b3eb8f2dbdf /cpukit/ftpd
parentd19415873fb6b6197d77916758593c9897a0cb69 (diff)
downloadrtems-3f777d0edfa0ea8413d2c83eacbbda135970cb3c.tar.bz2
2001-01-12 Sergei Organov <osv@javad.ru>
* rtems_servers/ftpd.c, rtems_servers/ftpd.h: Major enhancements as listed below: - use pool of pre-created threads to handle sessions instead of creating/deleting threads on the fly - LIST output is now similar to what "/bin/ls -al" would output, thus FTP clients such Netscape are happy with it. - LIST NAME now works (both for files and directories) - added support for NLST, CDUP, and MDTM FTP commands to make more FTP clients happy - keep track of CWD for every session separately - ability to specify root directory name for FTPD in configuration table. FTPD will then create illusion for FTP clients that this is actually root directory. - ignore options sent in commands, thus LIST -al FILE works and doesn't try to list "-al" directory. - buffers are allocated on stack instead of heap where possible to eliminate malloc/free calls (avoid possible heap fragmentation troubles). - drop using of task notepad to pass parameters - use function arguments instead - use snprintf() instead of sprintf() as the latter is unsafe - use of PF_INET in socket() instead of AF_INET Here are ftp clients I've tried new FTPD with (all of them running on Debian GNU/Linux 2.2): Lftp 2.1.10 NcFTP 2.4.3 Netscape 4.75 ftp mc 4.5.49
Diffstat (limited to 'cpukit/ftpd')
-rw-r--r--cpukit/ftpd/ftpd.c2090
-rw-r--r--cpukit/ftpd/ftpd.h2
2 files changed, 1360 insertions, 732 deletions
diff --git a/cpukit/ftpd/ftpd.c b/cpukit/ftpd/ftpd.c
index aef88c82c7..28e803b628 100644
--- a/cpukit/ftpd/ftpd.c
+++ b/cpukit/ftpd/ftpd.c
@@ -1,12 +1,42 @@
/* FIXME: 1. Parse command is a hack. We can do better.
- * 2. chdir is a hack. We can do better.
- * 3. PWD doesn't work.
- * 4. Some sort of access control?
+ * 2. Some sort of access control?
+ * 3. OSV: Timeouts on sockets.
+ * 4. OSV: hooks support seems to be a joke, as it requires storing of
+ * entire input file in memory. Seem to be better to change it to
+ * something more reasonable, like having
+ * 'hook_write(void const *buf, int count)' routine that will be
+ * called multiple times while file is being received.
+ * 5. OSV: Remove hack with "/dev/null"?
+ * 6. OSV: data_addr field of SessionInfo structure is not initialized.
+ * This will lead to undefined behavior if FTP client doesn't send
+ * PORT command. Seems we need to implement usage of default port
+ * (ftp port - 1). I didn't find any FTP client that doesn't send
+ * PORT command though.
+ * 7. OSV: while server claims TYPE ASCII command succeeds (to make
+ * happy some clients), ASCII mode isn't actually implemented.
+ * 8. Passive Mode?
*
- * FTP Server Daemon
+ * FTP Server Daemon
*
* Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
*
+ * Changed by: Sergei Organov <osv@javad.ru> (OSV)
+ * Changes:
+ * - use pool of pre-created threads to handle sessions
+ * - LIST output now similar to what "/bin/ls -al" would output, thus
+ * FTP clients could parse it.
+ * - LIST NAME now works (both for files and directories)
+ * - keep track of CWD for every session separately
+ * - ability to specify root directory name in configuration table
+ * - options sent in commands are ignored, thus LIST -al FILE works
+ * - added support for NLST, CDUP and MDTM commands
+ * - buffers are allocated on stack instead of heap where possible
+ * - drop using of task notepad to pass parameters - use function
+ * arguments instead
+ * - various bug-fixes, e.g., use of PF_INET in socket() instead of
+ * AF_INET, use snprintf() instead of sprintf() everywhere for safety,
+ * etc.
+ *
* $Id$
*/
@@ -24,11 +54,12 @@
* *
* Organization: *
* *
- * The FTP daemon is started upon boot. It runs all the time *
- * and waits for connections on the known FTP port (21). When *
- * a connection is made, it starts a 'session' task. That *
+ * The FTP daemon is started upon boot along with a (configurable) *
+ * number of tasks to handle sessions. It runs all the time and *
+ * waits for connections on the known FTP port (21). When *
+ * a connection is made, it wakeups a 'session' task. That *
* session then interacts with the remote host. When the session *
- * is complete, the session task deletes itself. The daemon still *
+ * is complete, the session task goes to sleep. The daemon still *
* runs, however. *
* *
* *
@@ -37,7 +68,7 @@
* RETR xxx - Sends a file from the client. *
* STOR xxx - Receives a file from the client. xxx = filename. *
* LIST xxx - Sends a file list to the client. *
- * (LIST xxx isn't working yet...) *
+ * NLST xxx - Sends a file list to the client. *
* USER - Does nothing. *
* PASS - Does nothing. *
* SYST - Replies with the system type (`RTEMS'). *
@@ -46,29 +77,18 @@
* RMD xxx - Remove directory xxx. *
* PWD - Print working directory. *
* CWD xxx - Change working directory. *
+ * CDUP - Change to upper directory. *
* SITE CHMOD xxx yyy - Change permissions on file yyy to xxx. *
* PORT a,b,c,d,x,y - Setup for a data port to IP address a.b.c.d *
* and port (x*256 + y). *
+ * MDTM xxx - Send date/time to the client. xxx = filename. *
* *
* *
* The public routines contained in this file are: *
* *
- * rtems_initialize_ftpd_start - Starts the server daemon, then *
- * returns to its caller. *
- * *
- * *
- * The private routines contained in this file are: *
+ * rtems_initialize_ftpd - Initializes and starts the server daemon, *
+ * then returns to its caller. *
* *
- * rtems_ftpd_send_reply - Sends a reply code and text through the *
- * control port. *
- * rtems_ftpd_command_retrieve - Performs to "RETR" command. *
- * rtems_ftpd_command_store - Performs the "STOR" command. *
- * rtems_ftpd_command_list - Performs the "LIST" command. *
- * rtems_ftpd_command_port - Opens a data port (the "PORT" command). *
- * rtems_ftpd_parse_command - Parses an incoming command. *
- * rtmes_ftpd_session - Begins a service session. *
- * rtems_ftpd_daemon - Listens on the FTP port for service *
- * requests. *
*------------------------------------------------------------------------*
* Jake Janovetz *
* University of Illinois *
@@ -76,31 +96,10 @@
* Urbana IL 61801 *
**************************************************************************
* Change History: *
- * 12/01/97 - Creation (JWJ) *
+ * 12/01/97 - Creation (JWJ) *
+ * 2001-01-08 - Changes by OSV *
*************************************************************************/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-#include <rtems.h>
-#include <rtems/rtems_bsdnet.h>
-#include <rtems/error.h>
-#include <syslog.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/ftp.h>
-#include <netinet/in.h>
-
-#include "ftpd.h"
-
-
-extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
-
/**************************************************************************
* Meanings of first and second digits of reply codes:
*
@@ -114,7 +113,7 @@ extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
* 4yz Transient negative completion reply. The requested action did
* not take place, but the error condition is temporary so the
* command can be reissued later.
- * 5yz Permanent negative completion reply. The command was not
+ * 5yz Permanent negative completion reply. The command was not
* accepted and should not be retried.
*-------------------------------------------------------------------------
* x0z Syntax errors.
@@ -127,32 +126,515 @@ extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
* x5z Filesystem status.
*************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
-/**************************************************************************
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/error.h>
+#include <syslog.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/ftp.h>
+#include <netinet/in.h>
+
+#include "ftpd.h"
+
+
+#ifdef __GNUC__
+/* change to #if 1 to disable syslog entirely */
+#if 0
+#undef syslog
+#define syslog(a, b, ...) while(0){}
+#endif
+#endif
+
+#define FTPD_SERVER_MESSAGE "RTEMS FTP server (Version 1.1-JWJ) ready."
+
+#define FTPD_SYSTYPE "RTEMS"
+
+/* Seem to be unused */
+#if 0
+#define FTPD_WELCOME_MESSAGE \
+ "Welcome to the RTEMS FTP server.\n" \
+ "\n" \
+ "Login accepted.\n"
+#endif
+
+/* Various buffer sizes */
+enum
+{
+ FTPD_BUFSIZE = 256, /* Size for temporary buffers */
+ FTPD_DATASIZE = 1024, /* Size for file transfer buffers */
+ FTPD_STACKSIZE = 8 * 1024, /* Tasks stack size */
+};
+
+/* Event to be used by session tasks for waiting */
+enum
+{
+ FTPD_RTEMS_EVENT = RTEMS_EVENT_1
+};
+
+/* Configuration table */
+extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
+
+/* this is not prototyped in strict ansi mode */
+FILE *fdopen (int fildes, const char *mode);
+
+/*
* SessionInfo structure.
*
- * The following structure is allocated for each session. The pointer
- * to this structure is contained in the tasks notepad entry.
- *************************************************************************/
+ * The following structure is allocated for each session.
+ */
typedef struct
{
- struct sockaddr_in data_addr; /* Data address for PORT commands */
- FILE *ctrl_fp; /* File pointer for control connection */
- char cwd[255]; /* Current working directory */
- /* Login -- future use -- */
- int xfer_mode; /* Transfer mode (ASCII/binary) */
+ struct sockaddr_in data_addr; /* Data address for PORT commands */
+ FILE *ctrl_fp; /* File pointer for control connection */
+ int socket; /* Socket descriptor for ctrl connection */
+ char cwd[FTPD_BUFSIZE]; /* Current working directory */
+ /* Login -- future use -- */
+ int xfer_mode; /* Transfer mode (ASCII/binary) */
+ rtems_id tid; /* Task id */
} FTPD_SessionInfo_t;
-#define FTPD_SERVER_MESSAGE "RTEMS FTP server (Version 1.0-JWJ) ready."
-#define FTPD_WELCOME_MESSAGE \
- "Welcome to the RTEMS FTP server.\n" \
- "\n" \
- "Login accepted.\n"
+/*
+ * TaskPool structure.
+ */
+typedef struct
+{
+ FTPD_SessionInfo_t *info;
+ FTPD_SessionInfo_t **queue;
+ int count;
+ int head;
+ int tail;
+ rtems_id mutex;
+ rtems_id sem;
+} FTPD_TaskPool_t;
+
+/*
+ * Task pool instance.
+ */
+static FTPD_TaskPool_t task_pool;
+
+/*
+ * Root node for FTPD without trailing slash. Even '/' node is denoted as
+ * empty string here.
+ */
+static char root[FTPD_BUFSIZE];
+
+
+/*PAGE
+ *
+ * serr
+ *
+ * Return errror string corresponding to current 'errno'.
+ *
+ */
+static char const*
+serr(void)
+{
+ return strerror(errno);
+}
+
+
+/*
+ * Utility routines to manage root directory and session local
+ * current working directory.
+ */
+
+
+/*PAGE
+ *
+ * squeeze_path
+ *
+ * Squeezes path according to OS rules, i.e., eliminates /./, /../, and //
+ * from the path. Does nothing if the path is relative, i.e. doesn't begin
+ * with '/'. The trailing slash is always removed, even when alone, i.e. "/"
+ * will be "" after squeeze.
+ *
+ * Input parameters:
+ * path - the path to be squeezed
+ *
+ * Output parameters:
+ * path - squeezed path
+ *
+ */
+static void
+squeeze_path(char* path)
+{
+ if(path[0] == '/')
+ {
+ char* e = path + 1;
+ int rest = strlen(e);
+ while(rest >= 0)
+ {
+ int len;
+ char* s = e;
+ e = strchr(s, '/');
+ if(e)
+ ++e;
+ else
+ e = s + rest + 1;
+ len = e - s;
+ rest -= len;
+ if(len == 1 || (len == 2 && s[0] == '.'))
+ {
+ if(rest >= 0)
+ memmove(s, e, rest + 1);
+ else
+ *s++ = '\0';
+ e = s;
+ }
+ else if(len == 3 && s[0] == '.' && s[1] == '.')
+ {
+ char* ps = s;
+ if(ps - 1 > path) {
+ do
+ --ps;
+ while(ps[-1] != '/');
+ }
+ if(rest >= 0)
+ memmove(ps, e, rest + 1);
+ else
+ *ps++ = '\0';
+ e = ps;
+ }
+ }
+ if(e[-2] == '/')
+ e[-2] = '\0';
+ }
+}
+
+
+/*PAGE
+ *
+ * make_path
+ *
+ * Makes full path given file name, current working directory and root
+ * directory (file scope variable 'root').
+ *
+ * Input parameters:
+ * cwd - current working directory
+ * name - file name
+ * root (file scope variable) - FTPD root directory
+ *
+ * Output parameters:
+ * buf - full path
+ * returns pointer to non-root part of the 'buf', i.e. to first character
+ * different from '/' after root part.
+ *
+ */
+static char const*
+make_path(char* buf, char const* cwd, char const* name)
+{
+ char* res = NULL;
+
+ int rlen = strlen(root);
+ int clen = strlen(cwd);
+ int nlen = strlen(name);
+ int len = rlen + nlen;
+
+ if (name[0] != '/')
+ {
+ ++len;
+ if (clen > 0)
+ len += clen + 1;
+ }
+
+ if (FTPD_BUFSIZE > len)
+ {
+ char* b = buf;
+ memcpy(b, root, rlen); b += rlen;
+ if (name[0] != '/')
+ {
+ *b++ = '/';
+ if (clen > 0)
+ {
+ memcpy(b, cwd, clen); b += clen;
+ *b++ = '/';
+ }
+ }
+ memcpy(b, name, nlen); b += nlen;
+ *b = '\0';
+
+ res = buf + rlen;
+ while(rlen-- > 0 && res[-1] == '/')
+ --res;
+ squeeze_path(res);
+ }
+
+ return res;
+}
+
+/*
+ * Task pool management routines
+ */
+
+
+/*PAGE
+ *
+ * task_pool_done
+ *
+ * Cleanup task pool.
+ *
+ * Input parameters:
+ * count - number of entries in task pool to cleanup
+ *
+ * Output parameters:
+ * NONE
+ *
+ */
+static void
+task_pool_done(int count)
+{
+ int i;
+ for(i = 0; i < count; ++i)
+ rtems_task_delete(task_pool.info[i].tid);
+ if(task_pool.info)
+ free(task_pool.info);
+ if(task_pool.queue)
+ free(task_pool.queue);
+ if(task_pool.mutex != (rtems_id)-1)
+ rtems_semaphore_delete(task_pool.mutex);
+ if(task_pool.sem != (rtems_id)-1)
+ rtems_semaphore_delete(task_pool.sem);
+ task_pool.info = 0;
+ task_pool.queue = 0;
+ task_pool.count = 0;
+ task_pool.sem = -1;
+ task_pool.mutex = -1;
+}
+
+/* Forward declare */
+static void session(rtems_task_argument arg);
+
+/*PAGE
+ *
+ * task_pool_init
+ *
+ * Initialize task pool.
+ *
+ * Input parameters:
+ * count - number of entries in task pool to create
+ * priority - priority tasks are started with
+ *
+ * Output parameters:
+ * returns 1 on success, 0 on failure.
+ *
+ */
+static int
+task_pool_init(int count, rtems_task_priority priority)
+{
+ int i;
+ rtems_status_code sc;
+ char id = 'a';
+
+ task_pool.count = 0;
+ task_pool.head = task_pool.tail = 0;
+ task_pool.mutex = (rtems_id)-1;
+ task_pool.sem = (rtems_id)-1;
+
+ sc = rtems_semaphore_create(
+ rtems_build_name('F', 'T', 'P', 'M'),
+ 1,
+ RTEMS_DEFAULT_ATTRIBUTES
+ | RTEMS_BINARY_SEMAPHORE
+ | RTEMS_INHERIT_PRIORITY
+ | RTEMS_PRIORITY,
+ RTEMS_NO_PRIORITY,
+ &task_pool.mutex);
+
+ if(sc == RTEMS_SUCCESSFUL)
+ sc = rtems_semaphore_create(
+ rtems_build_name('F', 'T', 'P', 'S'),
+ count,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ RTEMS_NO_PRIORITY,
+ &task_pool.sem);
+
+ if(sc != RTEMS_SUCCESSFUL) {
+ task_pool_done(0);
+ syslog(LOG_ERR, "ftpd: Can not create semaphores");
+ return 0;
+ }
+
+ task_pool.info = (FTPD_SessionInfo_t*)
+ malloc(sizeof(FTPD_SessionInfo_t) * count);
+ task_pool.queue = (FTPD_SessionInfo_t**)
+ malloc(sizeof(FTPD_SessionInfo_t*) * count);
+ if (NULL == task_pool.info || NULL == task_pool.queue)
+ {
+ task_pool_done(0);
+ syslog(LOG_ERR, "ftpd: Not enough memory");
+ return 0;
+ }
+
+ for(i = 0; i < count; ++i)
+ {
+ FTPD_SessionInfo_t *info = &task_pool.info[i];
+ sc = rtems_task_create(rtems_build_name('F', 'T', 'P', id),
+ priority, FTPD_STACKSIZE,
+ RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
+ RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
+ RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
+ &info->tid);
+ if (sc == RTEMS_SUCCESSFUL)
+ {
+ sc = rtems_task_start(
+ info->tid, session, (rtems_task_argument)info);
+ if (sc != RTEMS_SUCCESSFUL)
+ task_pool_done(i);
+ }
+ else
+ task_pool_done(i + 1);
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ syslog(LOG_ERR, "ftpd: Could not create/start FTPD session: %s",
+ rtems_status_text(sc));
+ return 0;
+ }
+ task_pool.queue[i] = task_pool.info + i;
+ info->ctrl_fp = NULL;
+ info->socket = -1;
+ if (++id > 'z')
+ id = 'a';
+ }
+ task_pool.count = count;
+ return 1;
+}
+
+/*PAGE
+ *
+ * task_pool_obtain
+ *
+ * Obtain free task from task pool.
+ *
+ * Input parameters:
+ * NONE
+ *
+ * Output parameters:
+ * returns pointer to the corresponding SessionInfo structure on success,
+ * NULL if there are no free tasks in the pool.
+ *
+ */
+static FTPD_SessionInfo_t*
+task_pool_obtain()
+{
+ FTPD_SessionInfo_t* info = 0;
+ rtems_status_code sc;
+ sc = rtems_semaphore_obtain(task_pool.sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT);
+ if (sc == RTEMS_SUCCESSFUL)
+ {
+ rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ info = task_pool.queue[task_pool.head];
+ if(++task_pool.head >= task_pool.count)
+ task_pool.head = 0;
+ info->socket = -1;
+ info->ctrl_fp = NULL;
+ rtems_semaphore_release(task_pool.mutex);
+ }
+ return info;
+}
+
+/*PAGE
+ *
+ * task_pool_release
+ *
+ * Return task obtained by 'obtain()' back to the task pool.
+ *
+ * Input parameters:
+ * info - pointer to corresponding SessionInfo structure.
+ *
+ * Output parameters:
+ * NONE
+ *
+ */
+static void
+task_pool_release(FTPD_SessionInfo_t* info)
+{
+ rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ task_pool.queue[task_pool.tail] = info;
+ if(++task_pool.tail >= task_pool.count)
+ task_pool.tail = 0;
+ rtems_semaphore_release(task_pool.mutex);
+ rtems_semaphore_release(task_pool.sem);
+}
+
+/*
+ * End of task pool routines
+ */
+
+/*PAGE
+ *
+ * close_socket
+ *
+ * Close socket.
+ *
+ * Input parameters:
+ * s - socket descriptor to be closed.
+ *
+ * Output parameters:
+ * returns 1 on success, 0 on failure
+ *
+ */
+static int
+close_socket(int s)
+{
+ if (0 <= s)
+ {
+ if (0 != close(s))
+ {
+ shutdown(s, 2);
+ if (0 != close(s))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*PAGE
+ *
+ * close_stream
+ *
+ * Close data stream of session.
+ *
+ * Input parameters:
+ * info - corresponding SessionInfo structure
+ *
+ * Output parameters:
+ * NONE
+ *
+ */
+static void
+close_stream(FTPD_SessionInfo_t* info)
+{
+ if (NULL != info->ctrl_fp)
+ {
+ if (0 != fclose(info->ctrl_fp))
+ {
+ syslog(LOG_ERR, "ftpd: Could not close control stream: %s", serr());
+ }
+ else
+ info->socket = -1;
+ }
+
+ if (!close_socket(info->socket))
+ syslog(LOG_ERR, "ftpd: Could not close control socket: %s", serr());
+
+ info->ctrl_fp = NULL;
+ info->socket = -1;
+}
/**************************************************************************
- * Function: rtems_ftpd_send_reply *
+ * Function: send_reply *
**************************************************************************
* Description: *
* *
@@ -169,49 +651,87 @@ typedef struct
* *
* none *
* *
- **************************************************************************
- * Change History: *
- * 12/01/97 - Creation (JWJ) *
*************************************************************************/
static void
-rtems_ftpd_send_reply(int code, char *text)
+send_reply(FTPD_SessionInfo_t *info, int code, char *text)
{
- rtems_status_code sc;
- FTPD_SessionInfo_t *info = NULL;
- char str[80];
-
+ char const* s;
+
+
+ /***********************************************************************
+ * code must be a 3-digit number.
+ **********************************************************************/
+ if ((code < 100) || (code > 999))
+ {
+ syslog(LOG_ERR, "ftpd: Code not 3-digits.");
+ return;
+ }
+
+ /***********************************************************************
+ * If a text reply exists, add it to the reply data.
+ **********************************************************************/
+ s = (info->xfer_mode == TYPE_A) ? "\r" : "";
+ if (text != NULL)
+ fprintf(info->ctrl_fp, "%d %.70s%s\n", code, text, s);
+ else
+ fprintf(info->ctrl_fp, "%d%s\n", code, s);
+ fflush(info->ctrl_fp);
+}
- sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
- (rtems_unsigned32 *)&info);
- /***********************************************************************
- * code must be a 3-digit number.
- **********************************************************************/
- if ((code < 100) || (code > 999))
- {
- syslog(LOG_ERR, "ftpd: Code not 3-digits.");
- return;
- }
-
- /***********************************************************************
- * If a text reply exists, add it to the reply data.
- **********************************************************************/
- if (text != NULL)
- {
- sprintf(str, "%d %.70s\r\n", code, text);
- fprintf(info->ctrl_fp, "%d %.70s\r\n", code, text);
- }
- else
- {
- sprintf(str, "%d\r\n", code);
- fprintf(info->ctrl_fp, "%d\r\n", code);
- }
- fflush(info->ctrl_fp);
+/*PAGE
+ *
+ * send_mode_reply
+ *
+ * Sends BINARY/ASCII reply string depending on current transfer mode.
+ *
+ * Input parameters:
+ * info - corresponding SessionInfo structure
+ *
+ * Output parameters:
+ * NONE
+ *
+ */
+static void
+send_mode_reply(FTPD_SessionInfo_t *info)
+{
+ if(info->xfer_mode == TYPE_I)
+ send_reply(info, 150, "Opening BINARY mode data connection.");
+ else
+ send_reply(info, 150, "Opening ASCII mode data connection.");
}
+/*PAGE
+ *
+ * data_socket
+ *
+ * Create data socket for session.
+ *
+ * Input parameters:
+ * info - corresponding SessionInfo structure
+ *
+ * Output parameters:
+ * returns socket descriptor, or -1 if failure
+ *
+ */
+static int
+data_socket(FTPD_SessionInfo_t *info)
+{
+ int s = socket(PF_INET, SOCK_STREAM, 0);
+ if(0 > s)
+ send_reply(info, 420, "Server error - could not create socket.");
+ else if(0 > connect(s, (struct sockaddr *)&info->data_addr,
+ sizeof(struct sockaddr)))
+ {
+ send_reply(info, 420, "Server error - could not connect socket.");
+ close_socket(s);
+ s = -1;
+ }
+ return s;
+}
/**************************************************************************
- * Function: rtems_ftpd_command_retrieve *
+ * Function: command_retrieve *
**************************************************************************
* Description: *
* *
@@ -229,84 +749,67 @@ rtems_ftpd_send_reply(int code, char *text)
* int - 0 for reply sent. *
* 1 for no reply sent. *
* *
- **************************************************************************
- * Change History: *
- * 04/29/98 - Creation (JWJ) *
*************************************************************************/
static int
-rtems_ftpd_command_retrieve(char *filename)
+command_retrieve(FTPD_SessionInfo_t *info, char *filename)
{
- int s;
- int n;
- int fd;
- unsigned char *bufr;
- rtems_status_code sc;
- FTPD_SessionInfo_t *info = NULL;
+ int s = -1;
+ int n;
+ int fd = -1;
+ char buf[FTPD_DATASIZE];
+ int res = 0;
+
+ char const* r = make_path(buf, info->cwd, filename);
+
+ if (NULL == r || 0 > (fd = open(buf, O_RDONLY)))
+ {
+ send_reply(info, 450, "Error opening file.");
+ return(res);
+ }
+
+ send_mode_reply(info);
+
+ /***********************************************************************
+ * Connect to the data connection (PORT made in an earlier PORT call).
+ **********************************************************************/
+ s = data_socket(info);
+ if (0 <= s)
+ {
+ /***********************************************************************
+ * Send the data over the ether.
+ **********************************************************************/
+ while ((n = read(fd, buf, FTPD_DATASIZE)) > 0)
+ {
+ send(s, buf, n, 0);
+ }
+
+ if (0 == n)
+ {
+ if (0 == close(fd))
+ {
+ fd = -1;
+ res = 1;
+ }
+ }
+ }
+ if (0 == res)
+ send_reply(info, 450, "Retrieve failed.");
+ else
+ send_reply(info, 210, "File sent successfully.");
- sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
- (rtems_unsigned32 *)&info);
+ if (-1 != fd)
+ close(fd);
- if ((fd = open(filename, O_RDONLY)) == -1)
- {
- rtems_ftpd_send_reply(450, "Error opening file.");
- return(0);
- }
+ if (!close_socket(s))
+ syslog(LOG_ERR, "ftpd: Error closing data socket");
- bufr = (unsigned char *)malloc(BUFSIZ);
- if (bufr == NULL)
- {
- rtems_ftpd_send_reply(440, "Server error - malloc fail.");
- close(fd);
- return(0);
- }
-
- /***********************************************************************
- * Connect to the data connection (PORT made in an earlier PORT call).
- **********************************************************************/
- rtems_ftpd_send_reply(150, "BINARY data connection.");
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (connect(s, (struct sockaddr *)&info->data_addr,
- sizeof(struct sockaddr)) < 0)
- {
- rtems_ftpd_send_reply(420, "Server error - could not connect socket.");
- free(bufr);
- close(fd);
- close(s);
- return(1);
- }
-
- /***********************************************************************
- * Send the data over the ether.
- **********************************************************************/
- while ((n = read(fd, bufr, BUFSIZ)) > 0)
- {
- send(s, bufr, n, 0);
- bufr[n-1] = '\0';
- }
-
- if (n == 0)
- {
- rtems_ftpd_send_reply(210, "File sent successfully.");
- }
- else
- {
- rtems_ftpd_send_reply(450, "Retrieve failed.");
- }
-
- if (close(s) != 0)
- {
- syslog(LOG_ERR, "ftpd: Error closing data socket");
- }
-
- free(bufr);
- close(fd);
- return(0);
+ return(res);
}
/**************************************************************************
- * Function: rtems_ftpd_command_store *
+ * Function: command_store *
**************************************************************************
* Description: *
* *
@@ -324,159 +827,251 @@ rtems_ftpd_command_retrieve(char *filename)
* int - 0 for success. *
* 1 for failure. *
* *
- **************************************************************************
- * Change History: *
- * 12/01/97 - Creation (JWJ) *
*************************************************************************/
static int
-rtems_ftpd_command_store(char *filename)
+command_store(FTPD_SessionInfo_t *info, char *filename)
{
- char *bufr;
- int s;
- int n;
- unsigned long size = 0;
- rtems_status_code sc;
- FTPD_SessionInfo_t *info = NULL;
- struct rtems_ftpd_hook *usehook = NULL;
-
-
- sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
- (rtems_unsigned32 *)&info);
-
- bufr = (char *)malloc(BUFSIZ * sizeof(char));
- if (bufr == NULL)
- {
- rtems_ftpd_send_reply(440, "Server error - malloc fail.");
- return(1);
- }
-
- rtems_ftpd_send_reply(150, "BINARY data connection.");
-
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (connect(s, (struct sockaddr *)&info->data_addr,
- sizeof(struct sockaddr)) < 0)
- {
- free(bufr);
- close(s);
- return(1);
- }
-
-
- /***********************************************************************
- * File: "/dev/null" just throws the data away.
- * Otherwise, search our list of hooks to see if we need to do something
- * special.
- **********************************************************************/
- if (!strncmp("/dev/null", filename, 9))
- {
- while ((n = read(s, bufr, BUFSIZ)) > 0);
- }
- else if (rtems_ftpd_configuration.hooks != NULL)
- {
- struct rtems_ftpd_hook *hook;
- int i;
-
- i = 0;
- hook = &rtems_ftpd_configuration.hooks[i++];
- while (hook->filename != NULL)
- {
- if (!strcmp(hook->filename, filename))
- {
- usehook = hook;
- break;
- }
- hook = &rtems_ftpd_configuration.hooks[i++];
- }
- }
-
- if (usehook != NULL)
- {
- char *bigBufr;
-
- /***********************************************************************
- * Allocate space for our "file".
- **********************************************************************/
- bigBufr = (char *)malloc(
- rtems_ftpd_configuration.max_hook_filesize * sizeof(char));
- if (bigBufr == NULL)
+ int s;
+ int n;
+ unsigned long size = 0;
+ struct rtems_ftpd_hook *usehook = NULL;
+ char buf[FTPD_DATASIZE];
+
+ int null = !strcmp("/dev/null", filename);
+
+ if(!null)
+ {
+ if (NULL == make_path(buf, info->cwd, filename))
+ {
+ send_reply(info, 450, "Error creating file.");
+ return(0);
+ }
+ }
+
+ send_mode_reply(info);
+
+ s = data_socket(info);
+ if(0 > s)
+ {
+ return(1);
+ }
+
+
+ /***********************************************************************
+ * File: "/dev/null" just throws the data away.
+ * Otherwise, search our list of hooks to see if we need to do something
+ * special.
+ * OSV: FIXME: this is hack. Using /dev/null filesystem entry would be
+ * better. However, it's not clear how to handle root directory
+ * other than '/' then.
+ **********************************************************************/
+ if (null)
+ {
+ while ((n = read(s, buf, FTPD_DATASIZE)) > 0);
+ }
+ else if (rtems_ftpd_configuration.hooks != NULL)
+ {
+ struct rtems_ftpd_hook *hook;
+ int i;
+
+ i = 0;
+ hook = &rtems_ftpd_configuration.hooks[i++];
+ while (hook->filename != NULL)
+ {
+ if (!strcmp(hook->filename, buf))
{
- rtems_ftpd_send_reply(440, "Server error - malloc fail.");
- free(bufr);
- return(1);
+ usehook = hook;
+ break;
}
+ hook = &rtems_ftpd_configuration.hooks[i++];
+ }
+ }
+
+ if (usehook != NULL)
+ {
+ /*
+ * OSV: FIXME: Small buffer could be used and hook routine
+ * called multiple times instead. Alternatively, the support could be
+ * removed entirely in favor of configuring RTEMS pseudo-device with
+ * given name.
+ */
+
+ char *bigBufr;
+ size_t filesize = rtems_ftpd_configuration.max_hook_filesize + 1;
+
+ /***********************************************************************
+ * Allocate space for our "file".
+ **********************************************************************/
+ bigBufr = (char *)malloc(filesize);
+ if (bigBufr == NULL)
+ {
+ send_reply(info, 440, "Server error - malloc fail.");
+ return(1);
+ }
+
+ /***********************************************************************
+ * Retrieve the file into our buffer space.
+ **********************************************************************/
+ size = 0;
+ while ((n = read(s, bigBufr + size, filesize - size)) > 0)
+ {
+ size += n;
+ }
+ if (size >= filesize)
+ {
+ send_reply(info, 440, "Server error - Buffer size exceeded.");
+ free(bigBufr);
+ close_socket(s);
+ return(1);
+ }
+ close_socket(s);
+
+ /***********************************************************************
+ * Call our hook.
+ **********************************************************************/
+ if ((usehook->hook_function)(bigBufr, size) == 0)
+ {
+ send_reply(info, 210, "File transferred successfully.");
+ }
+ else
+ {
+ send_reply(info, 440, "File transfer failed.");
+ }
+ free(bigBufr);
+ }
+ else
+ {
+ int fd =
+ creat(buf, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+
+ if (0 > fd)
+ {
+ send_reply(info, 450, "Error creating file.");
+ close_socket(s);
+ return(0);
+ }
- /***********************************************************************
- * Retrieve the file into our buffer space.
- **********************************************************************/
- size = 0;
- while ((n = read(s, bufr, BUFSIZ)) > 0)
+ while ((n = read(s, buf, FTPD_DATASIZE)) > 0)
+ {
+ if (0 > write(fd, buf, n))
{
- if (size + n >
- rtems_ftpd_configuration.max_hook_filesize * sizeof(char))
- {
- rtems_ftpd_send_reply(440, "Server error - Buffer size exceeded.");
- free(bufr);
- free(bigBufr);
- close(s);
- return(1);
- }
- memcpy(&bigBufr[size], bufr, n);
- size += n;
+ send_reply(info, 450, "Error during write.");
+ close(fd);
+ close_socket(s);
+ return(1);
}
- close(s);
+ }
+ if (0 > close(fd))
+ {
+ send_reply(info, 450, "Error during write.");
+ close_socket(s);
+ return(1);
+ }
+ close_socket(s);
+ send_reply(info, 226, "Transfer complete.");
+ }
+
+ return(0);
+}
- /***********************************************************************
- * Call our hook.
- **********************************************************************/
- if ((usehook->hook_function)(bigBufr, size) == 0)
+
+/*PAGE
+ *
+ * send_dirline
+ *
+ * Sends one line of LIST command reply corresponding to single file.
+ *
+ * Input parameters:
+ * s - socket descriptor to send data to
+ * wide - if 0, send only file name. If not 0, send 'stat' info as well in
+ * "ls -l" format.
+ * curTime - current time
+ * path - path to be prepended to what is given by 'add'
+ * add - path to be appended to what is given by 'path', the resulting path
+ * is then passed to 'stat()' routine
+ * name - file name to be reported in output
+ * buf - buffer for temporary data
+ *
+ * Output parameters:
+ * NONE
+ *
+ */
+static void
+send_dirline(int s, int wide, time_t curTime, char const* path,
+ char const* add, char const* fname, char* buf)
+{
+ if(wide)
+ {
+ struct stat stat_buf;
+
+ int plen = strlen(path);
+ int alen = strlen(add);
+ if(plen == 0)
+ {
+ buf[plen++] = '/';
+ buf[plen] = '\0';
+ }
+ else
+ {
+ strcpy(buf, path);
+ if(alen > 0 && buf[plen - 1] != '/')
{
- rtems_ftpd_send_reply(210, "File transferred successfully.");
+ buf[plen++] = '/';
+ if(plen >= FTPD_BUFSIZE)
+ return;
+ buf[plen] = '\0';
}
+ }
+ if(plen + alen >= FTPD_BUFSIZE)
+ return;
+ strcpy(buf + plen, add);
+
+ if (stat(buf, &stat_buf) == 0)
+ {
+ int len;
+ struct tm bt;
+ time_t tf = stat_buf.st_mtime;
+ enum { SIZE = 80 };
+ enum { SIX_MONTHS = 365*24*60*60/2 };
+ char timeBuf[SIZE];
+ gmtime_r(&tf, &bt);
+ if(curTime > tf + SIX_MONTHS || tf > curTime + SIX_MONTHS)
+ strftime (timeBuf, SIZE, "%b %d %Y", &bt);
else
- {
- rtems_ftpd_send_reply(440, "File transfer failed.");
- }
- free(bigBufr);
- }
- else
- {
- int fd;
- size_t written;
-
- fd = creat(filename, S_IRUSR | S_IWUSR |
- S_IRGRP | S_IWGRP |
- S_IROTH | S_IWOTH);
- if (fd == -1)
- {
- rtems_ftpd_send_reply(450, "Could not open file.");
- close(s);
- free(bufr);
- return(1);
- }
- while ((n = read(s, bufr, BUFSIZ)) > 0)
- {
- written = write(fd, bufr, n);
- if (written == -1)
- {
- rtems_ftpd_send_reply(450, "Error during write.");
- close(fd);
- close(s);
- free(bufr);
- return(1);
- }
- }
- close(fd);
- close(s);
- rtems_ftpd_send_reply(226, "Transfer complete.");
- }
-
- free(bufr);
- return(0);
+ strftime (timeBuf, SIZE, "%b %d %H:%M", &bt);
+
+ len = snprintf(buf, FTPD_BUFSIZE,
+ "%c%c%c%c%c%c%c%c%c%c 1 %5d %5d %11u %s %s\r\n",
+ (S_ISLNK(stat_buf.st_mode)?('l'):
+ (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
+ (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
+ (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
+ (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
+ (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
+ (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
+ (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
+ (stat_buf.st_mode & S_IROTH)?('r'):('-'),
+ (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
+ (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
+ (int)stat_buf.st_uid,
+ (int)stat_buf.st_gid,
+ (int)stat_buf.st_size,
+ timeBuf,
+ fname
+ );
+
+ send(s, buf, len, 0);
+ }
+ }
+ else
+ {
+ int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname);
+ send(s, buf, len, 0);
+ }
}
-
/**************************************************************************
- * Function: rtems_ftpd_command_list *
+ * Function: command_list *
**************************************************************************
* Description: *
* *
@@ -492,101 +1087,146 @@ rtems_ftpd_command_store(char *filename)
* *
* none *
* *
- **************************************************************************
- * Change History: *
- * 12/01/97 - Creation (JWJ) *
*************************************************************************/
static void
-rtems_ftpd_command_list(char *fname)
+command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
{
- int s;
- rtems_status_code sc;
- FTPD_SessionInfo_t *info = NULL;
- DIR *dirp;
- struct dirent *dp;
- char dirline[255];
- struct stat stat_buf;
-
-
- sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
- (rtems_unsigned32 *)&info);
-
- rtems_ftpd_send_reply(150, "ASCII data connection for LIST.");
-
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (connect(s, (struct sockaddr *)&info->data_addr,
- sizeof(struct sockaddr)) < 0)
- {
- syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
- return;
- }
-
- if ((dirp = opendir(fname)) == NULL)
- {
- sprintf(dirline, "%s: No such file or directory.%s\n",
- fname, (info->xfer_mode==TYPE_A)?("\r"):(""));
- send(s, dirline, strlen(dirline), 0);
- close(s);
- rtems_ftpd_send_reply(226, "Transfer complete.");
- return;
- }
- while ((dp = readdir(dirp)) != NULL)
- {
- if (stat(dp->d_name, &stat_buf) == 0)
- {
- sprintf(dirline, "%c%c%c%c%c%c%c%c%c%c %5d %5d %11d %s%s\n",
- (S_ISLNK(stat_buf.st_mode)?('l'):
- (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
- (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
- (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
- (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
- (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
- (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
- (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
- (stat_buf.st_mode & S_IROTH)?('r'):('-'),
- (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
- (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
- (int)stat_buf.st_uid,
- (int)stat_buf.st_gid,
- (int)stat_buf.st_size,
- dp->d_name,
- (info->xfer_mode==TYPE_A)?("\r"):(""));
- send(s, dirline, strlen(dirline), 0);
- }
- }
- closedir(dirp);
-
- close(s);
- rtems_ftpd_send_reply(226, "Transfer complete.");
+ int s;
+ DIR *dirp = 0;
+ struct dirent *dp = 0;
+ struct stat stat_buf;
+ char buf[FTPD_BUFSIZE];
+ char path[FTPD_BUFSIZE];
+ time_t curTime;
+ char const* res;
+ char const* cwd = info->cwd;
+
+ send_reply(info, 150, "Opening ASCII mode data connection for LIST.");
+
+ s = data_socket(info);
+ if(0 > s)
+ {
+ syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
+ return;
+ }
+
+ if(fname[0] == '\0')
+ fname = ".";
+
+ res = make_path(path, cwd, fname);
+
+ if (NULL == res || 0 > stat(path, &stat_buf))
+ {
+ snprintf(buf, FTPD_BUFSIZE,
+ "%s: No such file or directory.\r\n", fname);
+ send(s, buf, strlen(buf), 0);
+ }
+ else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(path))))
+ {
+ snprintf(buf, FTPD_BUFSIZE,
+ "%s: Can not open directory.\r\n", fname);
+ send(s, buf, strlen(buf), 0);
+ }
+ else
+ {
+ time(&curTime);
+ if(!dirp)
+ send_dirline(s, wide, curTime, path, "", fname, buf);
+ else {
+ /* FIXME: need "." and ".." only when '-a' option is given */
+ send_dirline(s, wide, curTime, path, "", ".", buf);
+ if(!strcmp(path, root))
+ send_dirline(s, wide, curTime, path, "", "..", buf);
+ else
+ send_dirline(s, wide, curTime, path, "..", "..", buf);
+ while ((dp = readdir(dirp)) != NULL)
+ send_dirline(s, wide, curTime, path, dp->d_name, dp->d_name, buf);
+ }
+ }
+
+ if(dirp)
+ closedir(dirp);
+ close_socket(s);
+ send_reply(info, 226, "Transfer complete.");
}
-/*
- * Cheesy way to change directories
+/*PAGE
+ *
+ * rtems_ftpd_cwd
+ *
+ * Change current working directory. We use 'chdir' here only to validate the
+ * new directory. We keep track of current working directory ourselves because
+ * current working directory in RTEMS isn't thread local, but we need it to be
+ * session local.
+ *
+ * Input parameters:
+ * info - corresponding SessionInfo structure
+ * dir - directory name passed in CWD command
+ *
+ * Output parameters:
+ * info->cwd is set to new CWD value.
+ *
*/
static void
-rtems_ftpd_CWD(char *dir)
+rtems_ftpd_cwd(FTPD_SessionInfo_t *info, char *dir)
{
- rtems_status_code sc;
- FTPD_SessionInfo_t *info = NULL;
+ char buf[FTPD_BUFSIZE];
+ char const* cwd = make_path(buf, info->cwd, dir);
+ if(cwd && chdir(buf) == 0)
+ {
+ send_reply(info, 250, "CWD command successful.");
+ strcpy(info->cwd, cwd);
+ }
+ else
+ {
+ send_reply(info, 550, "CWD command failed.");
+ }
+}
- sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
- (rtems_unsigned32 *)&info);
- if (chdir(dir) == 0)
- {
- rtems_ftpd_send_reply(250, "CWD command successful.");
- }
- else
- {
- rtems_ftpd_send_reply(550, "CWD command failed.");
- }
+/*PAGE
+ *
+ * command_mdtm
+ *
+ * Handle FTP MDTM command
+ *
+ * Input parameters:
+ * info - corresponding SessionInfo structure
+ * fname - file name passed in MDTM command
+ *
+ * Output parameters:
+ * info->cwd is set to new CWD value.
+ *
+ */
+static void
+command_mdtm(FTPD_SessionInfo_t *info, char const* fname)
+{
+ struct stat stbuf;
+ char buf[FTPD_BUFSIZE];
+
+ if(*fname == '\0')
+ fname = ".";
+
+ if (stat(fname, &stbuf) < 0)
+ {
+ snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());
+ send_reply(info, 550, buf);
+ }
+ else
+ {
+ struct tm *t = gmtime(&stbuf.st_mtime);
+ snprintf(buf, FTPD_BUFSIZE, "%04d%02d%02d%02d%02d%02d",
+ 1900 + t->tm_year,
+ t->tm_mon+1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ send_reply(info, 213, buf);
+ }
}
-
/**************************************************************************
- * Function: rtems_ftpd_command_port *
+ * Function: command_port *
**************************************************************************
* Description: *
* *
@@ -604,38 +1244,65 @@ rtems_ftpd_CWD(char *dir)
* *
* none *
* *
- **************************************************************************
- * Change History: *
- * 12/01/97 - Creation (JWJ) *
*************************************************************************/
static void
-rtems_ftpd_command_port(char *bufr)
+command_port(FTPD_SessionInfo_t *info, char *bufr)
{
- char *ip;
- char *port;
- int ip0, ip1, ip2, ip3, port0, port1;
- rtems_status_code sc;
- FTPD_SessionInfo_t *info = NULL;
-
-
- sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
- (rtems_unsigned32 *)&info);
-
- sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
- ip = (char *)&(info->data_addr.sin_addr);
- ip[0] = ip0 & 0xff;
- ip[1] = ip1 & 0xff;
- ip[2] = ip2 & 0xff;
- ip[3] = ip3 & 0xff;
- port = (char *)&(info->data_addr.sin_port);
- port[0] = port0 & 0xff;
- port[1] = port1 & 0xff;
- info->data_addr.sin_family = AF_INET;
+ char *ip;
+ char *port;
+ int ip0, ip1, ip2, ip3, port0, port1;
+
+ sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
+ ip = (char *)&(info->data_addr.sin_addr);
+ ip[0] = ip0 & 0xff;
+ ip[1] = ip1 & 0xff;
+ ip[2] = ip2 & 0xff;
+ ip[3] = ip3 & 0xff;
+ port = (char *)&(info->data_addr.sin_port);
+ port[0] = port0 & 0xff;
+ port[1] = port1 & 0xff;
+ info->data_addr.sin_family = AF_INET;
}
+/*PAGE
+ *
+ * skip_options
+ *
+ * Utility routine to skip options (if any) from input command.
+ *
+ * Input parameters:
+ * p - pointer to pointer to command
+ *
+ * Output parameters:
+ * p - is changed to point to first non-option argument
+ *
+ */
+static void
+skip_options(char **p)
+{
+ char* buf = *p;
+ while(1) {
+ while(*buf == ' ')
+ ++buf;
+ if(*buf == '-') {
+ if(*++buf == '-') { /* `--' should terminate options */
+ ++buf;
+ while(*buf == ' ')
+ ++buf;
+ break;
+ }
+ while(*buf != ' ' && *buf != '\0')
+ ++buf;
+ }
+ else
+ break;
+ }
+ *p = buf;
+}
+
/**************************************************************************
- * Function: rtems_ftpd_parse_command *
+ * Function: parse_command *
**************************************************************************
* Description: *
* *
@@ -654,149 +1321,156 @@ rtems_ftpd_command_port(char *bufr)
* *
* none *
* *
- **************************************************************************
- * Change History: *
- * 12/01/97 - Creation (JWJ) *
*************************************************************************/
static void
-rtems_ftpd_parse_command(char *bufr)
+parse_command(FTPD_SessionInfo_t *info, char *bufr)
{
- char fname[255];
- rtems_status_code sc;
- FTPD_SessionInfo_t *info = NULL;
-
-
- sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
- (rtems_unsigned32 *)&info);
-
- if (!strncmp("PORT", bufr, 4))
- {
- rtems_ftpd_send_reply(200, "PORT command successful.");
- rtems_ftpd_command_port(&bufr[5]);
- }
- else if (!strncmp("RETR", bufr, 4))
- {
- sscanf(&bufr[5], "%254s", fname);
- rtems_ftpd_command_retrieve(fname);
- }
- else if (!strncmp("STOR", bufr, 4))
- {
- sscanf(&bufr[5], "%254s", fname);
- rtems_ftpd_command_store(fname);
- }
- else if (!strncmp("LIST", bufr, 4))
- {
- if (bufr[5] == '\n')
- {
- rtems_ftpd_command_list(".");
- }
- else
- {
- sscanf(&bufr[5], "%254s", fname);
- rtems_ftpd_command_list(fname);
- }
- }
- else if (!strncmp("USER", bufr, 4))
- {
- rtems_ftpd_send_reply(230, "User logged in.");
- }
- else if (!strncmp("SYST", bufr, 4))
- {
- rtems_ftpd_send_reply(240, "RTEMS");
- }
- else if (!strncmp("TYPE", bufr, 4))
- {
- if (bufr[5] == 'I')
- {
- info->xfer_mode = TYPE_I;
- rtems_ftpd_send_reply(200, "Type set to I.");
- }
- else if (bufr[5] == 'A')
- {
- info->xfer_mode = TYPE_A;
- rtems_ftpd_send_reply(200, "Type set to A.");
- }
- else
- {
- info->xfer_mode = TYPE_I;
- rtems_ftpd_send_reply(504, "Type not implemented. Set to I.");
- }
- }
- else if (!strncmp("PASS", bufr, 4))
- {
- rtems_ftpd_send_reply(230, "User logged in.");
- }
- else if (!strncmp("DELE", bufr, 4))
- {
- sscanf(&bufr[4], "%254s", fname);
- if (unlink(fname) == 0)
- {
- rtems_ftpd_send_reply(257, "DELE successful.");
- }
- else
- {
- rtems_ftpd_send_reply(550, "DELE failed.");
- }
- }
- else if (!strncmp("SITE CHMOD", bufr, 10))
- {
- int mask;
-
- sscanf(&bufr[11], "%o %254s", &mask, fname);
- if (chmod(fname, (mode_t)mask) == 0)
- {
- rtems_ftpd_send_reply(257, "CHMOD successful.");
- }
- else
- {
- rtems_ftpd_send_reply(550, "CHMOD failed.");
- }
- }
- else if (!strncmp("RMD", bufr, 3))
- {
- sscanf(&bufr[4], "%254s", fname);
- if (rmdir(fname) == 0)
- {
- rtems_ftpd_send_reply(257, "RMD successful.");
- }
- else
- {
- rtems_ftpd_send_reply(550, "RMD failed.");
- }
- }
- else if (!strncmp("MKD", bufr, 3))
- {
- sscanf(&bufr[4], "%254s", fname);
- if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
- {
- rtems_ftpd_send_reply(257, "MKD successful.");
- }
- else
- {
- rtems_ftpd_send_reply(550, "MKD failed.");
- }
- }
- else if (!strncmp("CWD", bufr, 3))
- {
- sscanf(&bufr[4], "%254s", fname);
- rtems_ftpd_CWD(fname);
- }
- else if (!strncmp("PWD", bufr, 3))
- {
- char *cwd = getcwd(0, 0);
- sprintf(bufr, "\"%s\" is the current directory.", cwd);
- rtems_ftpd_send_reply(250, bufr);
- free(cwd);
- }
- else
- {
- rtems_ftpd_send_reply(500, "Unrecognized/unsupported command.");
- }
+ char fname[FTPD_BUFSIZE];
+
+ if (!strncmp("PORT", bufr, 4))
+ {
+ send_reply(info, 200, "PORT command successful.");
+ command_port(info, &bufr[5]);
+ }
+ else if (!strncmp("RETR", bufr, 4))
+ {
+ sscanf(&bufr[5], "%254s", fname);
+ command_retrieve(info, fname);
+ }
+ else if (!strncmp("STOR", bufr, 4))
+ {
+ sscanf(&bufr[5], "%254s", fname);
+ command_store(info, fname);
+ }
+ else if (!strncmp("LIST", bufr, 4))
+ {
+ bufr += 4;
+ skip_options(&bufr);
+ sscanf(bufr, "%254s", fname);
+ command_list(info, fname, 1);
+ }
+ else if (!strncmp("NLST", bufr, 4))
+ {
+ bufr += 4;
+ skip_options(&bufr);
+ sscanf(bufr, "%254s", fname);
+ command_list(info, fname, 0);
+ }
+ else if (!strncmp("MDTM", bufr, 4))
+ {
+ bufr += 4;
+ skip_options(&bufr);
+ sscanf(bufr, "%254s", fname);
+ command_mdtm(info, fname);
+ }
+ else if (!strncmp("USER", bufr, 4))
+ {
+ send_reply(info, 230, "User logged in.");
+ }
+ else if (!strncmp("SYST", bufr, 4))
+ {
+ send_reply(info, 240, FTPD_SYSTYPE);
+ }
+ else if (!strncmp("TYPE", bufr, 4))
+ {
+ if (bufr[5] == 'I')
+ {
+ info->xfer_mode = TYPE_I;
+ send_reply(info, 200, "Type set to I.");
+ }
+ else if (bufr[5] == 'A')
+ {
+ /* FIXME: ASCII mode isn't actually supported yet. */
+ info->xfer_mode = TYPE_A;
+ send_reply(info, 200, "Type set to A.");
+ }
+ else
+ {
+ info->xfer_mode = TYPE_I;
+ send_reply(info, 504, "Type not implemented. Set to I.");
+ }
+ }
+ else if (!strncmp("PASS", bufr, 4))
+ {
+ send_reply(info, 230, "User logged in.");
+ }
+ else if (!strncmp("DELE", bufr, 4))
+ {
+ sscanf(&bufr[4], "%254s", fname);
+ if (unlink(fname) == 0)
+ {
+ send_reply(info, 257, "DELE successful.");
+ }
+ else
+ {
+ send_reply(info, 550, "DELE failed.");
+ }
+ }
+ else if (!strncmp("SITE CHMOD", bufr, 10))
+ {
+ int mask;
+
+ sscanf(&bufr[11], "%o %254s", &mask, fname);
+ if (chmod(fname, (mode_t)mask) == 0)
+ {
+ send_reply(info, 257, "CHMOD successful.");
+ }
+ else
+ {
+ send_reply(info, 550, "CHMOD failed.");
+ }
+ }
+ else if (!strncmp("RMD", bufr, 3))
+ {
+ sscanf(&bufr[4], "%254s", fname);
+ if (rmdir(fname) == 0)
+ {
+ send_reply(info, 257, "RMD successful.");
+ }
+ else
+ {
+ send_reply(info, 550, "RMD failed.");
+ }
+ }
+ else if (!strncmp("MKD", bufr, 3))
+ {
+ sscanf(&bufr[4], "%254s", fname);
+ if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
+ {
+ send_reply(info, 257, "MKD successful.");
+ }
+ else
+ {
+ send_reply(info, 550, "MKD failed.");
+ }
+ }
+ else if (!strncmp("CWD", bufr, 3))
+ {
+ sscanf(&bufr[4], "%254s", fname);
+ rtems_ftpd_cwd(info, fname);
+ }
+ else if (!strncmp("CDUP", bufr, 4))
+ {
+ rtems_ftpd_cwd(info, "..");
+ }
+ else if (!strncmp("PWD", bufr, 3))
+ {
+ char const* cwd = "/";
+ if(info->cwd[0])
+ cwd = info->cwd;
+ snprintf(bufr, FTPD_BUFSIZE,
+ "\"%s\" is the current directory.", cwd);
+ send_reply(info, 250, bufr);
+ }
+ else
+ {
+ send_reply(info, 500, "Unrecognized/unsupported command.");
+ }
}
/**************************************************************************
- * Function: rtems_ftpd_session *
+ * Function: session *
**************************************************************************
* Description: *
* *
@@ -816,68 +1490,51 @@ rtems_ftpd_parse_command(char *bufr)
* *
* none *
* *
- **************************************************************************
- * Change History: *
- * 12/01/97 - Creation (JWJ) *
*************************************************************************/
static void
-rtems_ftpd_session(rtems_task_argument arg)
+session(rtems_task_argument arg)
{
- char cmd[256];
- rtems_status_code sc;
- FTPD_SessionInfo_t *info = NULL;
+ char cmd[FTPD_BUFSIZE];
+ FTPD_SessionInfo_t *info = (FTPD_SessionInfo_t *)arg;
+ rtems_event_set set;
+ while(1) {
+ rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,
+ &set);
- sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
- (rtems_unsigned32 *)&info);
+ send_reply(info, 220, FTPD_SERVER_MESSAGE);
- rtems_ftpd_send_reply(220, FTPD_SERVER_MESSAGE);
+ info->cwd[0] = 0;
+ info->xfer_mode = TYPE_I;
- /***********************************************************************
- * Set initial directory to "/".
- **********************************************************************/
- strcpy(info->cwd, "/");
- info->xfer_mode = TYPE_A;
- while (1)
- {
- if (fgets(cmd, 256, info->ctrl_fp) == NULL)
+ while (1)
+ {
+ if (fgets(cmd, FTPD_BUFSIZE, info->ctrl_fp) == NULL)
{
- syslog(LOG_INFO, "ftpd: Connection aborted.");
- break;
+ syslog(LOG_INFO, "ftpd: Connection aborted.");
+ break;
}
if (!strncmp("QUIT", cmd, 4))
{
- rtems_ftpd_send_reply(221, "Goodbye.");
- break;
+ send_reply(info, 221, "Goodbye.");
+ break;
}
else
{
- rtems_ftpd_parse_command(cmd);
+ parse_command(info, cmd);
}
- }
-
- if (fclose(info->ctrl_fp) != 0)
- {
- syslog(LOG_ERR, "ftpd: Could not close session.");
- }
-
-
- /* Least we can do is put the CWD back to /. */
- chdir("/");
+ }
- /***********************************************************************
- * Free up the allocated SessionInfo struct and exit.
- **********************************************************************/
- free(info);
- sc = rtems_task_delete(RTEMS_SELF);
- syslog(LOG_ERR, "ftpd: Task deletion failed: %s",
- rtems_status_text(sc));
+ /* Close connection and put ourselves back into the task pool. */
+ close_stream(info);
+ task_pool_release(info);
+ }
}
/**************************************************************************
- * Function: rtems_ftpd_daemon *
+ * Function: daemon *
**************************************************************************
* Description: *
* *
@@ -895,117 +1552,69 @@ rtems_ftpd_session(rtems_task_argument arg)
* *
* none *
* *
- **************************************************************************
- * Change History: *
- * 12/01/97 - Creation (JWJ) *
*************************************************************************/
-
-/* this is not prototyped in strict ansi mode */
-
-FILE *fdopen (int fildes, const char *mode);
-
static void
-rtems_ftpd_daemon()
+daemon()
{
- int s;
- int s1;
- int addrLen;
- struct sockaddr_in remoteAddr;
- struct sockaddr_in localAddr;
- char sessionID;
- rtems_task_priority priority;
- rtems_status_code sc;
- rtems_id tid;
- FTPD_SessionInfo_t *info = NULL;
-
-
- sessionID = 'a';
-
- s = socket(AF_INET, SOCK_STREAM, 0);
- if (s < 0)
- {
- perror("Creating socket");
- }
-
- localAddr.sin_family = AF_INET;
- localAddr.sin_port = htons(rtems_ftpd_configuration.port);
- localAddr.sin_addr.s_addr = INADDR_ANY;
- memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
- if (bind(s, (struct sockaddr *)&localAddr,
- sizeof(localAddr)) < 0)
- {
- perror("Binding control socket");
- }
-
- if (listen(s, 2) < 0)
- {
- perror("Listening on control socket");
- }
-
- while (1)
- {
- /********************************************************************
- * Allocate a SessionInfo structure for the session task.
- *******************************************************************/
- info = (FTPD_SessionInfo_t *)malloc(sizeof(FTPD_SessionInfo_t));
- if (info == NULL)
- {
- syslog(LOG_ERR, "ftpd: Could not allocate session info struct.");
- rtems_panic("Malloc fail.");
- }
-
- /********************************************************************
- * Accept on the socket and start the session task.
- *******************************************************************/
- addrLen = sizeof(remoteAddr);
- s1 = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
- if (s1 < 0)
- {
- perror("Accepting control connection");
- }
-
- rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority);
- sc = rtems_task_create(rtems_build_name('F', 'T', 'P', sessionID),
- priority, 8*1024,
- RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
- RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
- RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
- &tid);
- if (sc != RTEMS_SUCCESSFUL)
- {
- syslog(LOG_ERR, "ftpd: Could not create FTPD session: %s",
- rtems_status_text(sc));
- }
-
- if (sessionID == 'z')
+ int s;
+ int addrLen;
+ struct sockaddr_in remoteAddr;
+ struct sockaddr_in localAddr;
+ char sessionID;
+ FTPD_SessionInfo_t *info = NULL;
+
+
+ sessionID = 'a';
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ syslog(LOG_ERR, "ftpd: Error creating socket: %s", serr());
+
+ localAddr.sin_family = AF_INET;
+ localAddr.sin_port = htons(rtems_ftpd_configuration.port);
+ localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
+
+ if (0 > bind(s, (struct sockaddr *)&localAddr, sizeof(localAddr)))
+ syslog(LOG_ERR, "ftpd: Error binding control socket: %s", serr());
+
+ if (0 > listen(s, 1))
+ syslog(LOG_ERR, "ftpd: Error listening on control socket: %s", serr());
+
+ while (1)
+ {
+ int ss;
+ addrLen = sizeof(remoteAddr);
+ ss = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
+ if (0 > ss)
+ {
+ syslog(LOG_ERR, "ftpd: Error accepting control connection: %s", serr());
+ }
+ else
+ {
+ info = task_pool_obtain();
+ if (NULL == info)
{
- sessionID = 'a';
+ close_socket(ss);
}
else
{
- sessionID++;
- }
-
- /********************************************************************
- * Send the socket on to the new session.
- *******************************************************************/
- if ((info->ctrl_fp = fdopen(s1, "r+")) == NULL)
- {
- syslog(LOG_ERR, "ftpd: fdopen() on socket failed.");
- close(s1);
+ info->socket = ss;
+ if ((info->ctrl_fp = fdopen(info->socket, "r+")) == NULL)
+ {
+ syslog(LOG_ERR, "ftpd: fdopen() on socket failed: %s", serr());
+ close_stream(info);
+ task_pool_release(info);
+ }
+ else
+ {
+ /* Wakeup the session task. The task will call task_pool_release
+ after it closes connection. */
+ rtems_event_send(info->tid, FTPD_RTEMS_EVENT);
+ }
}
- else
- {
- sc = rtems_task_set_note(tid, RTEMS_NOTEPAD_0,
- (rtems_unsigned32)info);
- sc = rtems_task_start(tid, rtems_ftpd_session, 0);
- if (sc != RTEMS_SUCCESSFUL)
- {
- syslog(LOG_ERR, "ftpd: Could not start FTPD session: %s",
- rtems_status_text(sc));
- }
- }
- }
+ }
+ }
}
@@ -1021,57 +1630,74 @@ rtems_ftpd_daemon()
* *
* Inputs: *
* *
- * rtems_task_priority priority - Priority to assign to this task. *
- * *
* Output: *
* *
* int - RTEMS_SUCCESSFUL on successful start of the daemon. *
* *
- **************************************************************************
- * Change History: *
- * 12/01/97 - Creation (JWJ) *
*************************************************************************/
int
rtems_initialize_ftpd()
{
- rtems_status_code sc;
- rtems_id tid;
-
-
- if (rtems_ftpd_configuration.port == 0)
- {
- rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;
- }
-
- /***********************************************************************
- * Default FTPD priority.
- **********************************************************************/
- if (rtems_ftpd_configuration.priority == 0)
- {
- rtems_ftpd_configuration.priority = 40;
- }
- sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
- rtems_ftpd_configuration.priority, 8*1024,
- RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
- RTEMS_INTERRUPT_LEVEL(0),
- RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
- &tid);
- if (sc != RTEMS_SUCCESSFUL)
- {
- syslog(LOG_ERR, "ftpd: Could not create FTP daemon: %s",
- rtems_status_text(sc));
- return(RTEMS_UNSATISFIED);
- }
-
- sc = rtems_task_start(tid, rtems_ftpd_daemon, 0);
- if (sc != RTEMS_SUCCESSFUL)
- {
- syslog(LOG_ERR, "ftpd: Could not start FTP daemon: %s",
- rtems_status_text(sc));
- return(RTEMS_UNSATISFIED);
- }
-
- syslog(LOG_INFO, "ftpd: FTP daemon started.");
- return(RTEMS_SUCCESSFUL);
+ rtems_status_code sc;
+ rtems_id tid;
+ rtems_task_priority priority;
+ int count;
+
+ if (rtems_ftpd_configuration.port == 0)
+ {
+ rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;
+ }
+
+ if (rtems_ftpd_configuration.priority == 0)
+ {
+ rtems_ftpd_configuration.priority = 40;
+ }
+ priority = rtems_ftpd_configuration.priority;
+
+ if (rtems_ftpd_configuration.tasks_count <= 0)
+ rtems_ftpd_configuration.tasks_count = 1;
+ count = rtems_ftpd_configuration.tasks_count;
+
+ if (!task_pool_init(count, priority))
+ {
+ syslog(LOG_ERR, "ftpd: Could not initialize task pool.");
+ return RTEMS_UNSATISFIED;
+ }
+
+ sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
+ priority, FTPD_STACKSIZE,
+ RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
+ RTEMS_INTERRUPT_LEVEL(0),
+ RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
+ &tid);
+
+ if (sc == RTEMS_SUCCESSFUL)
+ {
+ sc = rtems_task_start(tid, daemon, 0);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_task_delete(tid);
+ }
+
+ if (sc != RTEMS_SUCCESSFUL)
+ {
+ task_pool_done(count);
+ syslog(LOG_ERR, "ftpd: Could not create/start FTP daemon: %s",
+ rtems_status_text(sc));
+ return RTEMS_UNSATISFIED;
+ }
+
+ root[0] = '\0';
+ if (
+ rtems_ftpd_configuration.root &&
+ strlen(rtems_ftpd_configuration.root) < FTPD_BUFSIZE &&
+ rtems_ftpd_configuration.root[0] == '/')
+ {
+ strcpy(root, rtems_ftpd_configuration.root);
+ squeeze_path(root);
+ rtems_ftpd_configuration.root = root;
+ }
+
+ syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)",
+ count, ((count > 1) ? "s" : ""));
+ return RTEMS_SUCCESSFUL;
}
-
diff --git a/cpukit/ftpd/ftpd.h b/cpukit/ftpd/ftpd.h
index 6daaaf4d51..cfc5efe69b 100644
--- a/cpukit/ftpd/ftpd.h
+++ b/cpukit/ftpd/ftpd.h
@@ -25,6 +25,8 @@ struct rtems_ftpd_configuration
/* for hooks */
int port; /* Well-known port */
struct rtems_ftpd_hook *hooks; /* List of hooks */
+ char const *root; /* Root for FTPD or 0 for / */
+ int tasks_count; /* Max. connections */
};
/*