📄 ftpd.c
字号:
/*
* Copyright (c) 2004, Dennis Kuschel.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* @file ftpd.c
* @author Dennis Kuschel
* @brief FTP demon for embedded devices
*
* This software is from http://mycpu.mikrocontroller.net.
* Please send questions and bug reports to dennis_k@freenet.de.
*/
#include "ftpd.h"
#include "filesys.h"
/*---------------------------------------------------------------------------
* DEFINES
*-------------------------------------------------------------------------*/
#define FTP_PORT 21
#define FTP_MAXCON 10
#define FTP_TIMEOUT 600
#define MAX_BLOCKSIZE 4096
#define FTP_INPBUFSIZE 256
#define FTP_FNAMESIZE FS_MAXFNAMELEN
#define MAX_NAMESIZE 16
/*---------------------------------------------------------------------------
* TYPEDEFS
*-------------------------------------------------------------------------*/
/* forward declaration */
typedef struct connection_s connection_t;
typedef struct transfer_s transfer_t;
typedef void (*CMDFUNC_t)(connection_t *conn, char *param);
typedef struct {
char name[8];
u16_t arg;
u16_t auth;
CMDFUNC_t cmdfunc;
} CMDINFO_t;
/* List link header.
* This structure is equal to the
* head of struct connection_s and
* struct transfer_s.
*/
typedef struct lelem_s lelem_t;
struct lelem_s
{
lelem_t *prev;
lelem_t *next;
};
#define INIT_LIST_HEAD(head) \
do { (head)->prev = head; (head)->next = head; } while(0)
#define END_OF_LIST(head, elem) ((void*)(head) == (void*)(elem))
#define NEXT_LIST_ELEM(elem, type) \
(type)(void*)((lelem_t*)(void*)(elem))->next
#define PREV_LIST_ELEM(elem, type) \
(type)(void*)((lelem_t*)(void*)(elem))->prev
#define FIRST_LIST_ELEM(head, type) NEXT_LIST_ELEM(head, type)
#define LAST_LIST_ELEM(head, type) PREV_LIST_ELEM(head, type)
/* This structure describes a connection.
*/
struct connection_s
{
lelem_t list;
transfer_t *transfer;
COUNTERTYPE lastTrans;
int sock;
u16_t auth;
u16_t buflen;
u32_t resumePos;
char username[(MAX_NAMESIZE+1+3)&~3];
char rxbuffer[FTP_INPBUFSIZE];
char renfrom[FTP_FNAMESIZE];
#if FS_SUBDIRECTORIES
char curpath[FTP_FNAMESIZE];
#endif
};
/* This structure describes a file transfer.
*/
struct transfer_s
{
lelem_t list;
connection_t *owner;
u16_t state;
u16_t spare;
u16_t append;
u16_t upload;
u32_t size;
u32_t pos;
u32_t dlbufpos;
u32_t dlbufremain;
COUNTERTYPE starttime;
struct sockaddr_in sin;
int sock;
sint_t wrDisabled;
sint_t filehandle;
sint_t dirhandle;
sint_t dirlisting;
char dlbuf[MAX_BLOCKSIZE];
char filename[FTP_FNAMESIZE];
};
/* transfer states */
#define TSTATE_NONE 0
#define TSTATE_GOTPASV 1
#define TSTATE_WAITONPASV 2
#define TSTATE_GOTPORT 3
#define TSTATE_WAITFORPORT 4
#define TSTATE_TRANSFER 5
/*---------------------------------------------------------------------------
* FUNCTION PROTOTYPES
*-------------------------------------------------------------------------*/
static sint_t ft_newServerSocket(void);
static sint_t ft_fdSanity(void);
static void ft_listAddTail(lelem_t *headelem, lelem_t *elem);
static void ft_listAddHead(lelem_t *headelem, lelem_t *elem);
static void ft_listDel(lelem_t *elem);
static sint_t ft_processCtrlConnections(fd_set *clientset, int fdcount);
static sint_t ft_processDataConnections(fd_set* clientset, int fdcount);
static sint_t ft_handleUpload(transfer_t *tran);
static sint_t ft_handleDownload(transfer_t *tran);
static void ft_acceptClient(void);
static void ft_timeout(void);
static void ft_initTransfer(transfer_t *tran);
static void ft_prepTransfer(transfer_t *tran);
static void ft_execStore(connection_t *conn, char *param, sint_t appflag);
static void ft_getFilename(connection_t *conn, char *param, char *dst);
static void ft_execCommand(connection_t *conn);
static void ft_removeRxbufBytes(connection_t *conn, u16_t count);
static void ft_reply(connection_t *conn, u16_t num, const char *fmt, ...);
static void ft_destroyConnection(connection_t *conn);
static void ft_destroyTransfer(transfer_t *tran);
static sint_t ft_addSendSocket(const int sock);
static sint_t ft_addSocket(const int sock);
static void ft_delSocket(const int sock);
static void ft_abortTransfer(connection_t *conn);
static sint_t ft_getNextDirEntry(transfer_t *tran, char *buf);
static sint_t ft_newConnection(int socket);
static transfer_t* ft_newTransfer(int socket, connection_t *conn);
static transfer_t* ft_setupNewTransfer(connection_t *conn);
static void ft_doListing(connection_t *conn, char *param,sint_t fulllist);
#if FS_SUBDIRECTORIES
static sint_t ft_changeCurrentPath(connection_t *conn, char *path);
#endif
static void ftcmd_abor(connection_t *conn, char *param);
static void ftcmd_acct(connection_t *conn, char *param);
static void ftcmd_allo(connection_t *conn, char *param);
static void ftcmd_appe(connection_t *conn, char *param);
static void ftcmd_cdup(connection_t *conn, char *param);
static void ftcmd_cwd (connection_t *conn, char *param);
static void ftcmd_dele(connection_t *conn, char *param);
static void ftcmd_help(connection_t *conn, char *param);
static void ftcmd_list(connection_t *conn, char *param);
static void ftcmd_mdtm(connection_t *conn, char *param);
static void ftcmd_mkd (connection_t *conn, char *param);
static void ftcmd_mode(connection_t *conn, char *param);
static void ftcmd_nlst(connection_t *conn, char *param);
static void ftcmd_noop(connection_t *conn, char *param);
static void ftcmd_rnfr(connection_t *conn, char *param);
static void ftcmd_rnto(connection_t *conn, char *param);
static void ftcmd_pass(connection_t *conn, char *param);
static void ftcmd_pasv(connection_t *conn, char *param);
static void ftcmd_port(connection_t *conn, char *param);
static void ftcmd_pwd (connection_t *conn, char *param);
static void ftcmd_quit(connection_t *conn, char *param);
static void ftcmd_rein(connection_t *conn, char *param);
static void ftcmd_rest(connection_t *conn, char *param);
static void ftcmd_retr(connection_t *conn, char *param);
static void ftcmd_rmd (connection_t *conn, char *param);
static void ftcmd_size(connection_t *conn, char *param);
static void ftcmd_stat(connection_t *conn, char *param);
static void ftcmd_stor(connection_t *conn, char *param);
static void ftcmd_stru(connection_t *conn, char *param);
static void ftcmd_syst(connection_t *conn, char *param);
static void ftcmd_type(connection_t *conn, char *param);
static void ftcmd_user(connection_t *conn, char *param);
/*---------------------------------------------------------------------------
* GLOBAL VARIABLES
*-------------------------------------------------------------------------*/
static lelem_t connListRoot_g;
static lelem_t transfListRoot_g;
static fd_set globalSocketSet;
static fd_set globalSocketSendSet;
static int serverSocket_g;
static u32_t open_connections_g;
static sint_t blockedTrans_g;
#define UPLOADBUF_SIZE 0x4000
static char upload_buf_g[UPLOADBUF_SIZE];
#define REPLYBUF_SIZE 0x200
static char reply_buf_g[REPLYBUF_SIZE];
static volatile sint_t ftpd_terminate_request_g = 0;
static volatile sint_t ftpd_running_g = 0;
static char parambuf_g[FTP_INPBUFSIZE];
static char tempbuf_g[FTP_INPBUFSIZE];
#if FS_SUBDIRECTORIES
static char fnamebuf_g[FTP_FNAMESIZE*2];
#endif
static char username_g[(MAX_NAMESIZE+1+3)&~3];
static char password_g[(MAX_NAMESIZE+1+3)&~3];
static const char month_g[13][4] = { "???",
"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"
};
static const CMDINFO_t cmdtable_g[] =
{
{ "user", 1, 0, ftcmd_user },
{ "pass", 1, 1, ftcmd_pass },
{ "retr", 1, 3, ftcmd_retr },
{ "acct", 1, 0, ftcmd_acct },
{ "port", 1, 3, ftcmd_port },
{ "pasv", 0, 3, ftcmd_pasv },
{ "pwd" , 0, 3, ftcmd_pwd },
{ "xpwd", 0, 3, ftcmd_pwd },
{ "cwd" , 1, 3, ftcmd_cwd },
{ "xcwd", 1, 3, ftcmd_cwd },
{ "cdup", 0, 3, ftcmd_cdup },
{ "rest", 1, 3, ftcmd_rest },
{ "list", 0, 3, ftcmd_list },
{ "nlst", 0, 3, ftcmd_nlst },
{ "type", 1, 3, ftcmd_type },
{ "mode", 1, 3, ftcmd_mode },
{ "stru", 1, 3, ftcmd_stru },
{ "size", 1, 3, ftcmd_size },
{ "mdtm", 1, 3, ftcmd_mdtm },
{ "abor", 0, 3, ftcmd_abor },
{ "dele", 1, 4, ftcmd_dele },
{ "rnfr", 1, 4, ftcmd_rnfr },
{ "rnto", 1, 4, ftcmd_rnto },
{ "mkd" , 1, 4, ftcmd_mkd },
{ "xmkd", 1, 4, ftcmd_mkd },
{ "rmd" , 1, 4, ftcmd_rmd },
{ "xrmd", 1, 4, ftcmd_rmd },
{ "allo", 1, 2, ftcmd_allo },
{ "stat", 0, 0, ftcmd_stat },
{ "noop", 0, 0, ftcmd_noop },
{ "syst", 0, 0, ftcmd_syst },
{ "help", 0, 0, ftcmd_help },
{ "quit", 0, 0, ftcmd_quit },
{ "rein", 0, 0, ftcmd_rein },
{ "stor", 1, 4, ftcmd_stor },
{ "appe", 1, 4, ftcmd_appe },
{ "" , 0, 0, NULL }
};
/*---------------------------------------------------------------------------
* FUNCTION IMPLEMENTATION
*-------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
* FTP COMMANDS
*-------------------------------------------------------------------------*/
/* Handle the ABOR command.
*/
static void ftcmd_abor(connection_t *conn, char *param)
{
(void) param;
if (conn->transfer != NULL)
{
ft_abortTransfer(conn);
}
ft_reply(conn, 226, "ABOR successful.");
}
/* Handle the ACCT command.
* This command is not handled by us, since we will
* never ask the client for an account identification.
*/
static void ftcmd_acct(connection_t *conn, char *param)
{
(void) param;
ft_reply(conn, 202, "ACCT not supported.");
}
/* Handle the ALLO command.
* We do not need forward allocation, so we behave
* like a NOOP (see RFC959).
*/
static void ftcmd_allo(connection_t *conn, char *param)
{
(void) param;
ft_reply(conn, 200, "ALLO not necessary, OK.");
}
/* Handle the APPE command.
* Note that the function ft_execStore() does the rest.
*/
static void ftcmd_appe(connection_t *conn, char *param)
{
(void) param;
ft_execStore(conn, param, 1);
}
#if FS_SUBDIRECTORIES
/* Change the current working directory.
*/
static sint_t ft_changeCurrentPath(connection_t *conn, char *path)
{
struct fsys_stat st;
sint_t i;
char c;
if ((*path == '/') || (*path == '\\'))
{
i = fsys_buildPathName(fnamebuf_g, path, "");
}
else
{
sysStrcpy(fnamebuf_g, conn->curpath);
sysStrcat(fnamebuf_g, "/");
sysStrcat(fnamebuf_g, path);
i = fsys_buildPathName(fnamebuf_g, fnamebuf_g, "");
}
if (i < 0)
return -1;
if (i > 0)
{
c = fnamebuf_g[i-1];
if (c == '/')
fnamebuf_g[i-1] = 0;
}
if (*fnamebuf_g != 0)
{
if (fsys_stat(fnamebuf_g, &st) < 0)
return -1;
if ((st.st_mode & FSS_IFDIR) == 0)
return -1;
}
sysStrcpy(conn->curpath, fnamebuf_g);
return 0;
}
#endif /* FS_SUBDIRECTORIES */
/* Handle a CDUP command.
*/
static void ftcmd_cdup(connection_t *conn, char *param)
{
(void) param;
#if FS_SUBDIRECTORIES
if (ft_changeCurrentPath(conn, "..") == 0)
{
ft_reply(conn, 250, "CDUP successful.");
}
else
{
ft_reply(conn, 550, "CDUP failed.");
}
#else /* FS_SUBDIRECTORIES */
/* subdirectorys are not supported */
ft_reply(conn, 250, "CDUP successful.");
#endif
}
/* Handle CWD command.
*/
static void ftcmd_cwd(connection_t *conn, char *param)
{
#if FS_SUBDIRECTORIES
if (ft_changeCurrentPath(conn, param) == 0)
{
ft_reply(conn, 250, "CWD successful.");
}
else
{
ft_reply(conn, 550, "Path not found.");
}
#else /* FS_SUBDIRECTORIES */
(void) param;
ft_reply(conn, 250, "CWD successful.");
#endif /* FS_SUBDIRECTORIES */
}
/* Handle the DELE command.
*/
static void ftcmd_dele(connection_t *conn, char *param)
{
sint_t rc = -1;
ft_getFilename(conn, param, tempbuf_g);
if (*tempbuf_g != 0)
{
rc = fsys_remove(tempbuf_g);
}
if (rc < 0)
{
ft_reply(conn, 550, "File unavailable.");
}
else
{
ft_reply(conn, 250, "File deleted.");
}
}
/* Handle the HELP command (not supported by us).
*/
static void ftcmd_help(connection_t *conn, char *param)
{
(void) param;
ft_reply(conn, 214, "Sorry, no help available, "
"please use standard FTP commands.");
}
/* Handle the LIST command.
*/
static void ftcmd_list(connection_t *conn, char *param)
{
ft_doListing(conn, param, 1);
}
/* Handle the MDTM command (not yet supported by us).
*/
static void ftcmd_mdtm(connection_t *conn, char *param)
{
(void) param;
ft_reply(conn, 502, "Command not implemented.");
}
/* Handle the MKD command.
*/
static void ftcmd_mkd(connection_t *conn, char *param)
{
#if FS_SUBDIRECTORIES
if (fsys_buildPathName(fnamebuf_g, conn->curpath, param) >= 0)
{
if (fsys_mkdir(fnamebuf_g) == 0)
{
ft_reply(conn, 257, "\"%s\" created.", fnamebuf_g);
return;
}
}
ft_reply(conn, 550, "Failed to create directory.");
#else /* FS_SUBDIRECTORIES */
(void) param;
ft_reply(conn, 502, "Command not implemented.");
#endif /* FS_SUBDIRECTORIES */
}
/* Handle the MODE command.
* (we support only the stream mode)
*/
static void ftcmd_mode(connection_t *conn, char *param)
{
(void) param;
if ((*param == 'S') || (*param == 's'))
{
ft_reply(conn, 200, "Mode is STREAM.");
}
else
{
ft_reply(conn, 504, "Unknown mode.");
}
}
/* Handle the NLST command.
*/
static void ftcmd_nlst(connection_t *conn, char *param)
{
ft_doListing(conn, param, 0);
}
/* Handle the NOOP command.
*/
static void ftcmd_noop(connection_t *conn, char *param)
{
(void) param;
ft_reply(conn, 200, "NOOP command successful.");
}
/* Handle the PASS command, check for password.
*/
static void ftcmd_pass(connection_t *conn, char *param)
{
char *pass = (char*) param;
sint_t i;
i = (sint_t) sysStrlen(pass);
while ((i > 0) && (pass[i-1] == ' ')) i--;
pass[i] = 0;
if (conn->auth == 2)
{
if ((sysStrcmp(username_g, conn->username) == 0) &&
(sysStrcmp(password_g, pass) == 0))
{
conn->auth = 4;
}
else
{
conn->auth = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -