📄 ftpd.c
字号:
}
}
else
if (conn->auth == 1)
{
conn->auth = (i > 0) ? 3 : 0;
}
if (conn->auth == 0)
{
ft_reply(conn, 530, "Login incorrect.");
}
else
{
ft_reply(conn, 230, "User logged in.");
}
}
/* This code is shared by ftcmd_pasv and ftcmd_port.
*/
static transfer_t* ft_setupNewTransfer(connection_t *conn)
{
unsigned long one = 1;
int s;
if ((conn->transfer != NULL) &&
(conn->transfer->state >= TSTATE_WAITFORPORT))
{
ft_reply(conn, 500, "Sorry, only one transfer allowed per connection.");
}
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s < 0)
{
ft_reply(conn, 500, "Socket error.");
return NULL;
}
ft_destroyTransfer(conn->transfer);
conn->transfer = ft_newTransfer(s, conn);
if (conn->transfer == NULL)
{
ft_reply(conn, 500, "Out of memory error.");
closesocket(s);
return NULL;
}
/* set nonblocking transfer */
ioctlsocket(s, FIONBIO, (void*) &one);
return conn->transfer;
}
/* Handle the PASV command.
*/
static void ftcmd_pasv(connection_t *conn, char *param)
{
transfer_t *tran;
struct sockaddr_in addr;
unsigned long ip, port;
int len;
tran = ft_setupNewTransfer(conn);
if (tran == NULL)
return;
ft_addSocket(tran->sock);
/* setup socket */
sysMemSet(&addr, 0, sizeof(addr));
len = sizeof(addr);
if (getsockname(conn->sock, (struct sockaddr*)&addr, &len))
{
ft_reply(conn, 500, "Socket error.");
return;
}
addr.sin_port = 0;
if (bind(tran->sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0)
{
ft_reply(conn, 500, "Socket error.");
return;
}
len = sizeof(addr);
if (getsockname(tran->sock, (struct sockaddr *)&addr, &len) < 0)
{
ft_reply(conn, 500, "Socket error.");
return;
}
if (listen(tran->sock, 1) < 0)
{
ft_reply(conn, 500, "Socket error.");
return;
}
tran->state = TSTATE_GOTPASV;
ip = (unsigned long) htonl(addr.sin_addr.s_addr);
port = (unsigned long) htons(addr.sin_port);
ft_reply(conn, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
(ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF,
(port >> 8) & 0xFF, port & 0xFF);
}
/* Handle the PORT command.
*/
static void ftcmd_port(connection_t *conn, char *param)
{
unsigned long one = 1;
transfer_t *tran;
struct sockaddr_in sin;
short unsigned int a0, a1, a2, a3, p0, p1;
int len;
if (sscanf(param, "%3hu,%3hu,%3hu,%3hu,%3hu,%3hu",
&a0, &a1, &a2, &a3, &p0, &p1) < 6)
{
ft_reply(conn, 501, "Parse error.");
return;
}
tran = ft_setupNewTransfer(conn);
if (tran == NULL)
return;
/* bind socket to our FTP data port, port 20. */
len = sizeof(sin);
sysMemSet(&sin, 0, sizeof(sin));
if (getsockname(conn->sock, (struct sockaddr*)&sin, &len) < 0)
{
ft_reply(conn, 500, "Socket error.");
return;
}
setsockopt(tran->sock, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one));
sin.sin_port = FTP_PORT - 1;
bind(tran->sock, (struct sockaddr*)&sin, sizeof(sin));
tran->sin.sin_family = AF_INET;
tran->sin.sin_addr.s_addr = htonl(
((a0 & 0xFF) << 24) + ((a1 & 0xFF) << 16) +
((a2 & 0xFF) << 8) + ((a3 & 0xFF)) );
tran->sin.sin_port = htons((unsigned short)
(((p0 & 0xFF) << 8) + (p1 & 0xFF)) );
tran->state = TSTATE_GOTPORT;
ft_reply(conn, 200, "PORT command OK.");
}
/* Handle PWD command (print current path).
* Paths are not supported by us, so we are always in the root dir.
*/
static void ftcmd_pwd(connection_t *conn, char *param)
{
(void) param;
#if FS_SUBDIRECTORIES
ft_reply(conn, 257, "\"/%s\" is current working directory.", conn->curpath);
#else
ft_reply(conn, 257, "\"/\" is current working directory.");
#endif
}
/* Handles the QUIT command
* (shut down the control and data connection)
*/
static void ftcmd_quit(connection_t *conn, char *param)
{
(void) param;
if (conn->transfer != NULL)
{
ft_abortTransfer(conn);
}
ft_reply(conn, 221, "Have a nice day!");
ft_destroyConnection(conn);
}
/* Handle the REIN command
* (do nearly a full reset of the connection)
*/
static void ftcmd_rein(connection_t *conn, char *param)
{
if (conn->transfer != NULL)
{
/* Note: This is not compliant to RFC959 */
ft_abortTransfer(conn);
}
conn->buflen = 0;
conn->auth = 0;
conn->resumePos = 0;
#if FS_SUBDIRECTORIES
conn->curpath[0] = 0;
#endif
conn->lastTrans = COUNTERVAR;
ft_reply(conn, 220, "FTPD ready.");
}
/* Handle a REST command.
*/
static void ftcmd_rest(connection_t *conn, char *param)
{
if (sscanf(param, "%u", &conn->resumePos) != 1)
{
ft_reply(conn, 501, "Parse error.");
}
else
{
ft_reply(conn, 350, "Setting resume at %u bytes.", conn->resumePos);
}
}
/* Handle the RETR command.
* This function simply opens the source file,
* the download is done by the global state machine.
*/
static void ftcmd_retr(connection_t *conn, char *param)
{
transfer_t *tran = conn->transfer;
char *c;
if ((tran == NULL) ||
((tran->state != TSTATE_GOTPASV) &&
(tran->state != TSTATE_GOTPORT)))
{
ft_reply(conn, 425, "No data connection.");
return;
}
tran->filehandle = -1;
ft_getFilename(conn, param, tran->filename);
if (conn->auth < 4)
{
c = tran->filename;
while ((*c == '/') || (*c == '\\')) c++;
if (sysStrnicmp(c, "public/", 7) != 0)
{
ft_reply(conn, 503, "Please login with USER and PASS.");
return;
}
}
if (*tran->filename != 0)
{
tran->filehandle = fsys_open(tran->filename, FSO_RDONLY);
}
if (tran->filehandle < 0)
{
ft_reply(tran->owner, 550, "File unavailable.");
ft_destroyTransfer(tran);
}
else
{
tran->upload = 0;
tran->dirlisting = 0;
ft_prepTransfer(tran);
}
}
/* Handle the RMD command.
*/
static void ftcmd_rmd(connection_t *conn, char *param)
{
#if FS_SUBDIRECTORIES
if (fsys_buildPathName(fnamebuf_g, conn->curpath, param) >= 0)
{
if (fsys_rmdir(fnamebuf_g) == 0)
{
ft_reply(conn, 250, "Directory successfully removed.");
return;
}
}
ft_reply(conn, 550, "Failed to remove directory.");
#else /* FS_SUBDIRECTORIES */
(void) param;
ft_reply(conn, 502, "Command not implemented.");
#endif /* FS_SUBDIRECTORIES */
}
/* Handle the RNFR command.
* (get the filename of the file to rename)
*/
static void ftcmd_rnfr(connection_t *conn, char *param)
{
struct fsys_stat st;
ft_getFilename(conn, param, conn->renfrom);
if ((*conn->renfrom == 0) || (fsys_stat(conn->renfrom, &st) < 0))
{
conn->renfrom[0] = 0;
ft_reply(conn, 550, "File unavailable.");
}
else
{
ft_reply(conn, 350, "File exists, send RNTO.");
}
}
/* Handle the RNTO command.
* (get filename to rename to and do the work)
*/
static void ftcmd_rnto(connection_t *conn, char *param)
{
if (conn->renfrom[0] == 0)
{
ft_reply(conn, 503, "Need to send RNFR first.");
return;
}
ft_getFilename(conn, param, tempbuf_g);
if (*tempbuf_g == 0)
{
ft_reply(conn, 550, "New file name is illegal.");
}
else
if (fsys_rename(conn->renfrom, tempbuf_g) < 0)
{
ft_reply(conn, 550, "Failed to rename file.");
}
else
{
ft_reply(conn, 250, "File renamed.");
}
conn->renfrom[0] = 0;
}
/* Handle the SIZE command. This is a FTP extension.
*/
static void ftcmd_size(connection_t *conn, char *param)
{
struct fsys_stat st;
ft_getFilename(conn, param, tempbuf_g);
if ((*tempbuf_g == 0) || (fsys_stat(tempbuf_g, &st) < 0))
{
ft_reply(conn, 550, "File unavailable.");
}
else
{
ft_reply(conn, 213, "%lu", (unsigned long)(st.st_size));
}
}
/* Handle the STAT command.
* This is a bit too complex for us, so we
* reply with a 'disabled' message, because
* we won't confess the command is not implemented...
*/
static void ftcmd_stat(connection_t *conn, char *param)
{
(void) param;
ft_reply(conn, 502, "STAT command disabled.");
}
/* Handle the STOR command.
* Note that the function ft_execStore() does the rest.
*/
static void ftcmd_stor(connection_t *conn, char *param)
{
(void) param;
ft_execStore(conn, param, 0);
}
/* Handle the STRU command.
* (we support only the file mode)
*/
static void ftcmd_stru(connection_t *conn, char *param)
{
(void) param;
if ((*param == 'F') || (*param == 'f'))
{
ft_reply(conn, 200, "Structure is FILE.");
}
else
{
ft_reply(conn, 504, "Unknown structure.");
}
}
/* Handle the SYST command.
* (we do not return a system identification)
*/
static void ftcmd_syst(connection_t *conn, char *param)
{
(void) param;
ft_reply(conn, 502, "Command disabled for security reason.");
}
/* Handle the TYPE command.
*/
static void ftcmd_type(connection_t *conn, char *param)
{
(void) param;
ft_reply(conn, 200, "TYPE ignored (always I)");
}
/* Handle the USER command, get user name.
*/
static void ftcmd_user(connection_t *conn, char *param)
{
sysStrncpy(conn->username, param, MAX_NAMESIZE);
conn->username[MAX_NAMESIZE] = 0;
if (sysStrcmp(conn->username, "anonymous") == 0)
{
ft_reply(conn, 331, "Login OK, send password (your e-mail).");
conn->auth = 1;
}
else
{
ft_reply(conn, 331, "Password required for %s.", conn->username);
conn->auth = 2;
}
}
/*---------------------------------------------------------------------------
* COMMAND LINE PARSER + TOOLS
*-------------------------------------------------------------------------*/
/* Parse and execute a FTP command.
*/
static void ft_execCommand(connection_t *conn)
{
const CMDINFO_t *cmd;
sint_t p, w, e;
u16_t i, len;
/* remove all illegal characters
from the beginning of the buffer */
for (i = 0;
((i < conn->buflen) &&
!((conn->rxbuffer[i] >= 'a') && (conn->rxbuffer[i] <= 'z')) &&
!((conn->rxbuffer[i] >= 'A') && (conn->rxbuffer[i] <= 'Z')));
i++);
if (i > 0)
{
ft_removeRxbufBytes(conn, i);
}
/* test if parameters are following and
find end of command (marked by LF or CR/LF) */
for (w = 0, p = 0, e = 0, i = 0;
i < conn->buflen; i++)
{
if (conn->rxbuffer[i] != ' ')
{
p = w;
}
else
{
w = 1;
}
if ((conn->rxbuffer[i] == '\r') || (conn->rxbuffer[i] == '\n'))
{
e = 1;
break;
}
}
if (e == 0)
{
/* no valid command in the buffer */
return;
}
for (cmd = cmdtable_g; cmd->cmdfunc != NULL; cmd++)
{
len = (u16_t) sysStrlen(cmd->name);
if ((i < len) ||
(sysStrnicmp(conn->rxbuffer, cmd->name, len) != 0) ||
((cmd->arg != 0) && (p == 0)))
{
continue;
}
if (conn->auth < cmd->auth)
{
ft_removeRxbufBytes(conn, i);
ft_reply(conn, 503, "Please login with USER and PASS.");
return;
}
while ((len < conn->buflen) && (conn->rxbuffer[len] == ' ')) len++;
ft_removeRxbufBytes(conn, len);
i -= len;
sysStrncpy(parambuf_g, conn->rxbuffer, i);
parambuf_g[i] = 0;
ft_removeRxbufBytes(conn, i);
(cmd->cmdfunc)(conn, parambuf_g);
return;
}
ft_reply(conn, 500, "Syntax error, command unrecognized.");
ft_removeRxbufBytes(conn, i);
}
/* abort an active transfer and delete the broken file.
*/
static void ft_abortTransfer(connection_t *conn)
{
transfer_t *tran = conn->transfer;
ft_reply(conn, 426, "File transfer aborted.");
if (tran->filehandle != -1)
{
fsys_close(tran->filehandle);
tran->filehandle = -1;
}
if (tran->upload)
{
fsys_remove(tran->filename);
}
ft_destroyTransfer(conn->transfer);
}
/* Execute the STOR or APPE command.
*/
static void ft_execStore(connection_t *conn, char *param, sint_t appflag)
{
transfer_t *tran = conn->transfer;
if ((tran == NULL) ||
((tran->state != TSTATE_GOTPASV) && (tran->state != TSTATE_GOTPORT)))
{
ft_reply(conn, 425, "No data connection.");
return;
}
ft_getFilename(conn, param, tran->filename);
if (*tran->filename == 0)
{
ft_reply(conn, 553, "File name not allowed.");
return;
}
tran->filehandle = fsys_open(tran->filename,
FSO_WRONLY | FSO_CREAT |
(((appflag != 0) ||
(conn->resumePos > 0)) ? 0 : FSO_TRUNC));
if (tran->filehandle < 0)
{
ft_reply(tran->owner, 550, "Failed to create file.");
}
else
{
tran->upload = 1;
tran->append = appflag;
ft_prepTransfer(tran);
}
}
/* Initialize a file transfer (up- or download).
* Retrieves the current file size, connects the socket, etc.
*/
static void ft_prepTransfer(transfer_t *tran)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -