mirror of https://github.com/toelke/lwip-ftpd.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1390 lines
33 KiB
1390 lines
33 KiB
/*
|
|
* Copyright (c) 2002 Florian Schulze.
|
|
* 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. Neither the name of the authors nor the names of the contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``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 AUTHORS OR CONTRIBUTORS 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.
|
|
*
|
|
* ftpd.c - This file is part of the FTP daemon for lwIP
|
|
*
|
|
*/
|
|
|
|
#include "lwip/debug.h"
|
|
|
|
#include "lwip/stats.h"
|
|
|
|
#include "ftpd.h"
|
|
|
|
#include "lwip/tcp.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "vfs.h"
|
|
|
|
#define EINVAL 1
|
|
#define ENOMEM 2
|
|
#define ENODEV 3
|
|
|
|
#define msg110 "110 MARK %s = %s."
|
|
/*
|
|
110 Restart marker reply.
|
|
In this case, the text is exact and not left to the
|
|
particular implementation; it must read:
|
|
MARK yyyy = mmmm
|
|
Where yyyy is User-process data stream marker, and mmmm
|
|
server's equivalent marker (note the spaces between markers
|
|
and "=").
|
|
*/
|
|
#define msg120 "120 Service ready in nnn minutes."
|
|
#define msg125 "125 Data connection already open; transfer starting."
|
|
#define msg150 "150 File status okay; about to open data connection."
|
|
#define msg150recv "150 Opening BINARY mode data connection for %s (%i bytes)."
|
|
#define msg150stor "150 Opening BINARY mode data connection for %s."
|
|
#define msg200 "200 Command okay."
|
|
#define msg202 "202 Command not implemented, superfluous at this site."
|
|
#define msg211 "211 System status, or system help reply."
|
|
#define msg212 "212 Directory status."
|
|
#define msg213 "213 File status."
|
|
#define msg214 "214 %s."
|
|
/*
|
|
214 Help message.
|
|
On how to use the server or the meaning of a particular
|
|
non-standard command. This reply is useful only to the
|
|
human user.
|
|
*/
|
|
#define msg214SYST "214 %s system type."
|
|
/*
|
|
215 NAME system type.
|
|
Where NAME is an official system name from the list in the
|
|
Assigned Numbers document.
|
|
*/
|
|
#define msg220 "220 lwIP FTP Server ready."
|
|
/*
|
|
220 Service ready for new user.
|
|
*/
|
|
#define msg221 "221 Goodbye."
|
|
/*
|
|
221 Service closing control connection.
|
|
Logged out if appropriate.
|
|
*/
|
|
#define msg225 "225 Data connection open; no transfer in progress."
|
|
#define msg226 "226 Closing data connection."
|
|
/*
|
|
Requested file action successful (for example, file
|
|
transfer or file abort).
|
|
*/
|
|
#define msg227 "227 Entering Passive Mode (%i,%i,%i,%i,%i,%i)."
|
|
/*
|
|
227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
|
|
*/
|
|
#define msg230 "230 User logged in, proceed."
|
|
#define msg250 "250 Requested file action okay, completed."
|
|
#define msg257PWD "257 \"%s\" is current directory."
|
|
#define msg257 "257 \"%s\" created."
|
|
/*
|
|
257 "PATHNAME" created.
|
|
*/
|
|
#define msg331 "331 User name okay, need password."
|
|
#define msg332 "332 Need account for login."
|
|
#define msg350 "350 Requested file action pending further information."
|
|
#define msg421 "421 Service not available, closing control connection."
|
|
/*
|
|
This may be a reply to any command if the service knows it
|
|
must shut down.
|
|
*/
|
|
#define msg425 "425 Can't open data connection."
|
|
#define msg426 "426 Connection closed; transfer aborted."
|
|
#define msg450 "450 Requested file action not taken."
|
|
/*
|
|
File unavailable (e.g., file busy).
|
|
*/
|
|
#define msg451 "451 Requested action aborted: local error in processing."
|
|
#define msg452 "452 Requested action not taken."
|
|
/*
|
|
Insufficient storage space in system.
|
|
*/
|
|
#define msg500 "500 Syntax error, command unrecognized."
|
|
/*
|
|
This may include errors such as command line too long.
|
|
*/
|
|
#define msg501 "501 Syntax error in parameters or arguments."
|
|
#define msg502 "502 Command not implemented."
|
|
#define msg503 "503 Bad sequence of commands."
|
|
#define msg504 "504 Command not implemented for that parameter."
|
|
#define msg530 "530 Not logged in."
|
|
#define msg532 "532 Need account for storing files."
|
|
#define msg550 "550 Requested action not taken."
|
|
/*
|
|
File unavailable (e.g., file not found, no access).
|
|
*/
|
|
#define msg551 "551 Requested action aborted: page type unknown."
|
|
#define msg552 "552 Requested file action aborted."
|
|
/*
|
|
Exceeded storage allocation (for current directory or
|
|
dataset).
|
|
*/
|
|
#define msg553 "553 Requested action not taken."
|
|
/*
|
|
File name not allowed.
|
|
*/
|
|
|
|
enum ftpd_state_e {
|
|
FTPD_USER,
|
|
FTPD_PASS,
|
|
FTPD_IDLE,
|
|
FTPD_NLST,
|
|
FTPD_LIST,
|
|
FTPD_RETR,
|
|
FTPD_RNFR,
|
|
FTPD_STOR,
|
|
FTPD_QUIT
|
|
};
|
|
|
|
static const char *month_table[12] = {
|
|
"Jan",
|
|
"Feb",
|
|
"Mar",
|
|
"Apr",
|
|
"May",
|
|
"Jun",
|
|
"Jul",
|
|
"Aug",
|
|
"Sep",
|
|
"Oct",
|
|
"Nov",
|
|
"Dec"
|
|
};
|
|
|
|
/*
|
|
------------------------------------------------------------
|
|
SFIFO 1.3
|
|
------------------------------------------------------------
|
|
* Simple portable lock-free FIFO
|
|
* (c) 2000-2002, David Olofson
|
|
*
|
|
* Platform support:
|
|
* gcc / Linux / x86: Works
|
|
* gcc / Linux / x86 kernel: Works
|
|
* gcc / FreeBSD / x86: Works
|
|
* gcc / NetBSD / x86: Works
|
|
* gcc / Mac OS X / PPC: Works
|
|
* gcc / Win32 / x86: Works
|
|
* Borland C++ / DOS / x86RM: Works
|
|
* Borland C++ / Win32 / x86PM16: Untested
|
|
* ? / Various Un*ces / ?: Untested
|
|
* ? / Mac OS / PPC: Untested
|
|
* gcc / BeOS / x86: Untested
|
|
* gcc / BeOS / PPC: Untested
|
|
* ? / ? / Alpha: Untested
|
|
*
|
|
* 1.2: Max buffer size halved, to avoid problems with
|
|
* the sign bit...
|
|
*
|
|
* 1.3: Critical buffer allocation bug fixed! For certain
|
|
* requested buffer sizes, older version would
|
|
* allocate a buffer of insufficient size, which
|
|
* would result in memory thrashing. (Amazing that
|
|
* I've manage to use this to the extent I have
|
|
* without running into this... *heh*)
|
|
*/
|
|
|
|
/*
|
|
* Porting note:
|
|
* Reads and writes of a variable of this type in memory
|
|
* must be *atomic*! 'int' is *not* atomic on all platforms.
|
|
* A safe type should be used, and sfifo should limit the
|
|
* maximum buffer size accordingly.
|
|
*/
|
|
typedef int sfifo_atomic_t;
|
|
#ifdef __TURBOC__
|
|
# define SFIFO_MAX_BUFFER_SIZE 0x7fff
|
|
#else /* Kludge: Assume 32 bit platform */
|
|
# define SFIFO_MAX_BUFFER_SIZE 0x7fffffff
|
|
#endif
|
|
|
|
typedef struct sfifo_t
|
|
{
|
|
char *buffer;
|
|
int size; /* Number of bytes */
|
|
sfifo_atomic_t readpos; /* Read position */
|
|
sfifo_atomic_t writepos; /* Write position */
|
|
} sfifo_t;
|
|
|
|
#define SFIFO_SIZEMASK(x) ((x)->size - 1)
|
|
|
|
#define sfifo_used(x) (((x)->writepos - (x)->readpos) & SFIFO_SIZEMASK(x))
|
|
#define sfifo_space(x) ((x)->size - 1 - sfifo_used(x))
|
|
|
|
/*
|
|
* Alloc buffer, init FIFO etc...
|
|
*/
|
|
static int sfifo_init(sfifo_t *f, int size)
|
|
{
|
|
memset(f, 0, sizeof(sfifo_t));
|
|
|
|
if(size > SFIFO_MAX_BUFFER_SIZE)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* Set sufficient power-of-2 size.
|
|
*
|
|
* No, there's no bug. If you need
|
|
* room for N bytes, the buffer must
|
|
* be at least N+1 bytes. (The fifo
|
|
* can't tell 'empty' from 'full'
|
|
* without unsafe index manipulations
|
|
* otherwise.)
|
|
*/
|
|
f->size = 1;
|
|
for(; f->size <= size; f->size <<= 1)
|
|
;
|
|
|
|
/* Get buffer */
|
|
if( 0 == (f->buffer = (void *)malloc(f->size)) )
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Dealloc buffer etc...
|
|
*/
|
|
static void sfifo_close(sfifo_t *f)
|
|
{
|
|
if(f->buffer)
|
|
free(f->buffer);
|
|
}
|
|
|
|
/*
|
|
* Write bytes to a FIFO
|
|
* Return number of bytes written, or an error code
|
|
*/
|
|
static int sfifo_write(sfifo_t *f, const void *_buf, int len)
|
|
{
|
|
int total;
|
|
int i;
|
|
const char *buf = (const char *)_buf;
|
|
|
|
if(!f->buffer)
|
|
return -ENODEV; /* No buffer! */
|
|
|
|
/* total = len = min(space, len) */
|
|
total = sfifo_space(f);
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("sfifo_space() = %d\n",total));
|
|
if(len > total)
|
|
len = total;
|
|
else
|
|
total = len;
|
|
|
|
i = f->writepos;
|
|
if(i + len > f->size)
|
|
{
|
|
memcpy(f->buffer + i, buf, f->size - i);
|
|
buf += f->size - i;
|
|
len -= f->size - i;
|
|
i = 0;
|
|
}
|
|
memcpy(f->buffer + i, buf, len);
|
|
f->writepos = i + len;
|
|
|
|
return total;
|
|
}
|
|
|
|
struct ftpd_datastate {
|
|
int connected;
|
|
vfs_dir_t *vfs_dir;
|
|
vfs_dirent_t *vfs_dirent;
|
|
vfs_file_t *vfs_file;
|
|
sfifo_t fifo;
|
|
struct tcp_pcb *msgpcb;
|
|
struct ftpd_msgstate *msgfs;
|
|
};
|
|
|
|
struct ftpd_msgstate {
|
|
enum ftpd_state_e state;
|
|
sfifo_t fifo;
|
|
vfs_t *vfs;
|
|
struct ip4_addr dataip;
|
|
u16_t dataport;
|
|
struct tcp_pcb *datalistenpcb;
|
|
struct tcp_pcb *datapcb;
|
|
struct ftpd_datastate *datafs;
|
|
int passive;
|
|
char *renamefrom;
|
|
};
|
|
|
|
static void send_msg(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, char *msg, ...);
|
|
|
|
static void ftpd_dataerr(void *arg, err_t err)
|
|
{
|
|
struct ftpd_datastate *fsd = arg;
|
|
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_dataerr: %s (%i)\n", lwip_strerr(err), err));
|
|
if (fsd == NULL)
|
|
return;
|
|
fsd->msgfs->datafs = NULL;
|
|
fsd->msgfs->state = FTPD_IDLE;
|
|
free(fsd);
|
|
}
|
|
|
|
static void ftpd_dataclose(struct tcp_pcb *pcb, struct ftpd_datastate *fsd)
|
|
{
|
|
tcp_arg(pcb, NULL);
|
|
tcp_sent(pcb, NULL);
|
|
tcp_recv(pcb, NULL);
|
|
|
|
if (fsd->msgfs->datalistenpcb) {
|
|
tcp_arg(fsd->msgfs->datalistenpcb, NULL);
|
|
tcp_accept(fsd->msgfs->datalistenpcb, NULL);
|
|
tcp_close(fsd->msgfs->datalistenpcb);
|
|
fsd->msgfs->datalistenpcb = NULL;
|
|
}
|
|
|
|
fsd->msgfs->datafs = NULL;
|
|
sfifo_close(&fsd->fifo);
|
|
free(fsd);
|
|
tcp_arg(pcb, NULL);
|
|
tcp_close(pcb);
|
|
}
|
|
|
|
static void close_with_message(struct ftpd_datastate *fsd, struct tcp_pcb *pcb, char* msg) {
|
|
struct ftpd_msgstate *fsm;
|
|
struct tcp_pcb *msgpcb;
|
|
|
|
fsm = fsd->msgfs;
|
|
msgpcb = fsd->msgpcb;
|
|
ftpd_dataclose(pcb, fsd);
|
|
fsm->datapcb = NULL;
|
|
fsm->datafs = NULL;
|
|
fsm->state = FTPD_IDLE;
|
|
send_msg(msgpcb, fsm, msg);
|
|
}
|
|
|
|
|
|
static void send_data(struct tcp_pcb *pcb, struct ftpd_datastate *fsd)
|
|
{
|
|
err_t err;
|
|
u16_t len;
|
|
|
|
if (sfifo_used(&fsd->fifo) > 0) {
|
|
int i;
|
|
|
|
/* We cannot send more data than space available in the send
|
|
buffer. */
|
|
if (tcp_sndbuf(pcb) < sfifo_used(&fsd->fifo)) {
|
|
len = tcp_sndbuf(pcb);
|
|
} else {
|
|
len = (u16_t) sfifo_used(&fsd->fifo);
|
|
}
|
|
|
|
i = fsd->fifo.readpos;
|
|
if ((i + len) > fsd->fifo.size) {
|
|
err = tcp_write(pcb, fsd->fifo.buffer + i, (u16_t)(fsd->fifo.size - i), 1);
|
|
if (err != ERR_OK) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("send_data: error writing!\n"));
|
|
return;
|
|
}
|
|
len -= fsd->fifo.size - i;
|
|
fsd->fifo.readpos = 0;
|
|
i = 0;
|
|
}
|
|
|
|
err = tcp_write(pcb, fsd->fifo.buffer + i, len, 1);
|
|
if (err != ERR_OK) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("send_data: error writing!\n"));
|
|
return;
|
|
}
|
|
fsd->fifo.readpos += len;
|
|
}
|
|
}
|
|
|
|
static void send_file(struct ftpd_datastate *fsd, struct tcp_pcb *pcb)
|
|
{
|
|
if (!fsd->connected)
|
|
return;
|
|
|
|
if (fsd->vfs_file) {
|
|
char buffer[2048];
|
|
int len;
|
|
|
|
len = sfifo_space(&fsd->fifo);
|
|
if (len == 0) {
|
|
send_data(pcb, fsd);
|
|
return;
|
|
}
|
|
if (len > 2048)
|
|
len = 2048;
|
|
len = vfs_read(buffer, 1, len, fsd->vfs_file);
|
|
if (len == 0) {
|
|
if (vfs_eof(fsd->vfs_file) == 0)
|
|
return;
|
|
vfs_close(fsd->vfs_file);
|
|
fsd->vfs_file = NULL;
|
|
return;
|
|
}
|
|
sfifo_write(&fsd->fifo, buffer, len);
|
|
send_data(pcb, fsd);
|
|
} else {
|
|
if (sfifo_used(&fsd->fifo) > 0) {
|
|
send_data(pcb, fsd);
|
|
return;
|
|
}
|
|
close_with_message(fsd, pcb, msg226);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void send_next_directory(struct ftpd_datastate *fsd, struct tcp_pcb *pcb, int shortlist)
|
|
{
|
|
char buffer[1024];
|
|
int len;
|
|
|
|
while (1) {
|
|
if (fsd->vfs_dirent == NULL)
|
|
fsd->vfs_dirent = vfs_readdir(fsd->vfs_dir);
|
|
|
|
if (fsd->vfs_dirent) {
|
|
if (shortlist) {
|
|
len = sprintf(buffer, "%s\r\n", fsd->vfs_dirent->name);
|
|
if (sfifo_space(&fsd->fifo) < len) {
|
|
send_data(pcb, fsd);
|
|
return;
|
|
}
|
|
sfifo_write(&fsd->fifo, buffer, len);
|
|
fsd->vfs_dirent = NULL;
|
|
} else {
|
|
vfs_stat_t st;
|
|
time_t current_time;
|
|
int current_year;
|
|
struct tm *s_time;
|
|
|
|
time(¤t_time);
|
|
s_time = gmtime(¤t_time);
|
|
current_year = s_time->tm_year;
|
|
|
|
vfs_stat(fsd->msgfs->vfs, fsd->vfs_dirent->name, &st);
|
|
s_time = gmtime(&st.st_mtime);
|
|
if (s_time->tm_year == current_year)
|
|
len = sprintf(buffer, "-rw-rw-rw- 1 user ftp %11ld %s %02i %02i:%02i %s\r\n", st.st_size, month_table[s_time->tm_mon], s_time->tm_mday, s_time->tm_hour, s_time->tm_min, fsd->vfs_dirent->name);
|
|
else
|
|
len = sprintf(buffer, "-rw-rw-rw- 1 user ftp %11ld %s %02i %5i %s\r\n", st.st_size, month_table[s_time->tm_mon], s_time->tm_mday, s_time->tm_year + 1900, fsd->vfs_dirent->name);
|
|
if (VFS_ISDIR(st.st_mode))
|
|
buffer[0] = 'd';
|
|
if (sfifo_space(&fsd->fifo) < len) {
|
|
send_data(pcb, fsd);
|
|
return;
|
|
}
|
|
sfifo_write(&fsd->fifo, buffer, len);
|
|
fsd->vfs_dirent = NULL;
|
|
}
|
|
} else {
|
|
if (sfifo_used(&fsd->fifo) > 0) {
|
|
send_data(pcb, fsd);
|
|
return;
|
|
}
|
|
|
|
vfs_closedir(fsd->vfs_dir);
|
|
fsd->vfs_dir = NULL;
|
|
close_with_message(fsd, pcb, msg226);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static err_t ftpd_datasent(void *arg, struct tcp_pcb *pcb, u16_t len)
|
|
{
|
|
struct ftpd_datastate *fsd = arg;
|
|
(void) len; /* suppress unused warning */
|
|
|
|
switch (fsd->msgfs->state) {
|
|
case FTPD_LIST:
|
|
send_next_directory(fsd, pcb, 0);
|
|
break;
|
|
case FTPD_NLST:
|
|
send_next_directory(fsd, pcb, 1);
|
|
break;
|
|
case FTPD_RETR:
|
|
send_file(fsd, pcb);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
static err_t ftpd_datarecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
|
|
{
|
|
struct ftpd_datastate *fsd = arg;
|
|
|
|
if (err == ERR_OK && p != NULL) {
|
|
struct pbuf *q;
|
|
u16_t tot_len = 0;
|
|
|
|
for (q = p; q != NULL; q = q->next) {
|
|
int len;
|
|
|
|
len = vfs_write(q->payload, 1, q->len, fsd->vfs_file);
|
|
tot_len += len;
|
|
if (len != q->len) {
|
|
vfs_close(fsd->vfs_file);
|
|
fsd->vfs_file = NULL;
|
|
close_with_message(fsd, pcb, msg553);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Inform TCP that we have taken the data. */
|
|
tcp_recved(pcb, tot_len);
|
|
|
|
pbuf_free(p);
|
|
}
|
|
if (err == ERR_OK && p == NULL) {
|
|
vfs_close(fsd->vfs_file);
|
|
fsd->vfs_file = NULL;
|
|
close_with_message(fsd, pcb, msg226);
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
static err_t ftpd_dataconnected(void *arg, struct tcp_pcb *pcb, err_t err)
|
|
{
|
|
struct ftpd_datastate *fsd = arg;
|
|
(void) err; /* suppress unused warning */
|
|
|
|
fsd->msgfs->datapcb = pcb;
|
|
fsd->connected = 1;
|
|
|
|
/* Tell TCP that we wish to be informed of incoming data by a call
|
|
to the http_recv() function. */
|
|
tcp_recv(pcb, ftpd_datarecv);
|
|
|
|
/* Tell TCP that we wish be to informed of data that has been
|
|
successfully sent by a call to the ftpd_sent() function. */
|
|
tcp_sent(pcb, ftpd_datasent);
|
|
|
|
tcp_err(pcb, ftpd_dataerr);
|
|
|
|
switch (fsd->msgfs->state) {
|
|
case FTPD_LIST:
|
|
send_next_directory(fsd, pcb, 0);
|
|
break;
|
|
case FTPD_NLST:
|
|
send_next_directory(fsd, pcb, 1);
|
|
break;
|
|
case FTPD_RETR:
|
|
send_file(fsd, pcb);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
static err_t ftpd_dataaccept(void *arg, struct tcp_pcb *pcb, err_t err)
|
|
{
|
|
struct ftpd_datastate *fsd = arg;
|
|
|
|
(void) err; /* suppress unused warning */
|
|
|
|
fsd->msgfs->datapcb = pcb;
|
|
fsd->connected = 1;
|
|
|
|
/* Tell TCP that we wish to be informed of incoming data by a call
|
|
to the http_recv() function. */
|
|
tcp_recv(pcb, ftpd_datarecv);
|
|
|
|
/* Tell TCP that we wish be to informed of data that has been
|
|
successfully sent by a call to the ftpd_sent() function. */
|
|
tcp_sent(pcb, ftpd_datasent);
|
|
|
|
tcp_err(pcb, ftpd_dataerr);
|
|
|
|
switch (fsd->msgfs->state) {
|
|
case FTPD_LIST:
|
|
send_next_directory(fsd, pcb, 0);
|
|
break;
|
|
case FTPD_NLST:
|
|
send_next_directory(fsd, pcb, 1);
|
|
break;
|
|
case FTPD_RETR:
|
|
send_file(fsd, pcb);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
static int open_dataconnection(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
if (fsm->passive)
|
|
return 0;
|
|
|
|
/* Allocate memory for the structure that holds the state of the
|
|
connection. */
|
|
fsm->datafs = malloc(sizeof(struct ftpd_datastate));
|
|
|
|
if (fsm->datafs == NULL) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("open_dataconnection: Out of memory\n"));
|
|
send_msg(pcb, fsm, msg451);
|
|
return 1;
|
|
}
|
|
memset(fsm->datafs, 0, sizeof(struct ftpd_datastate));
|
|
fsm->datafs->msgfs = fsm;
|
|
fsm->datafs->msgpcb = pcb;
|
|
|
|
if (sfifo_init(&fsm->datafs->fifo, 2000) != 0) {
|
|
free(fsm->datafs);
|
|
send_msg(pcb, fsm, msg451);
|
|
return 1;
|
|
}
|
|
|
|
fsm->datapcb = tcp_new();
|
|
|
|
if (fsm->datapcb == NULL) {
|
|
sfifo_close(&fsm->datafs->fifo);
|
|
free(fsm->datafs);
|
|
send_msg(pcb, fsm, msg451);
|
|
return 1;
|
|
}
|
|
|
|
/* Tell TCP that this is the structure we wish to be passed for our
|
|
callbacks. */
|
|
tcp_arg(fsm->datapcb, fsm->datafs);
|
|
ip_addr_t dataip;
|
|
IP_SET_TYPE_VAL(dataip, IPADDR_TYPE_V4);
|
|
ip4_addr_copy(*ip_2_ip4(&dataip), fsm->dataip);
|
|
tcp_connect(fsm->datapcb, &dataip, fsm->dataport, ftpd_dataconnected);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void cmd_user(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
(void) arg; /* suppress unused warning */
|
|
|
|
send_msg(pcb, fsm, msg331);
|
|
fsm->state = FTPD_PASS;
|
|
/*
|
|
send_msg(pcb, fs, msgLoginFailed);
|
|
fs->state = FTPD_QUIT;
|
|
*/
|
|
}
|
|
|
|
static void cmd_pass(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
(void) arg; /* suppress unused warning */
|
|
send_msg(pcb, fsm, msg230);
|
|
fsm->state = FTPD_IDLE;
|
|
/*
|
|
send_msg(pcb, fs, msgLoginFailed);
|
|
fs->state = FTPD_QUIT;
|
|
*/
|
|
}
|
|
|
|
static void cmd_port(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
int nr;
|
|
unsigned pHi, pLo;
|
|
unsigned ip[4];
|
|
|
|
nr = sscanf(arg, "%u,%u,%u,%u,%u,%u", &(ip[0]), &(ip[1]), &(ip[2]), &(ip[3]), &pHi, &pLo);
|
|
if (nr != 6) {
|
|
send_msg(pcb, fsm, msg501);
|
|
} else {
|
|
IP4_ADDR(&fsm->dataip, (u8_t) ip[0], (u8_t) ip[1], (u8_t) ip[2], (u8_t) ip[3]);
|
|
fsm->dataport = ((u16_t) pHi << 8) | (u16_t) pLo;
|
|
send_msg(pcb, fsm, msg200);
|
|
}
|
|
}
|
|
|
|
static void cmd_quit(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
(void) arg; /* suppress unused warning */
|
|
send_msg(pcb, fsm, msg221);
|
|
fsm->state = FTPD_QUIT;
|
|
}
|
|
|
|
static void cmd_cwd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
if (!vfs_chdir(fsm->vfs, arg)) {
|
|
send_msg(pcb, fsm, msg250);
|
|
} else {
|
|
send_msg(pcb, fsm, msg550);
|
|
}
|
|
}
|
|
|
|
static void cmd_cdup(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
(void) arg; /* suppress unused warning */
|
|
if (!vfs_chdir(fsm->vfs, "..")) {
|
|
send_msg(pcb, fsm, msg250);
|
|
} else {
|
|
send_msg(pcb, fsm, msg550);
|
|
}
|
|
}
|
|
|
|
static void cmd_pwd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
char *path;
|
|
(void) arg; /* suppress unused warning */
|
|
|
|
if ((path = vfs_getcwd(fsm->vfs, NULL, 0))) {
|
|
send_msg(pcb, fsm, msg257PWD, path);
|
|
free(path);
|
|
}
|
|
}
|
|
|
|
static void cmd_list_common(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, int shortlist)
|
|
{
|
|
vfs_dir_t *vfs_dir;
|
|
char *cwd;
|
|
(void) arg; /* suppress unused warning */
|
|
|
|
cwd = vfs_getcwd(fsm->vfs, NULL, 0);
|
|
if ((!cwd)) {
|
|
send_msg(pcb, fsm, msg451);
|
|
return;
|
|
}
|
|
vfs_dir = vfs_opendir(fsm->vfs, cwd);
|
|
free(cwd);
|
|
if (!vfs_dir) {
|
|
send_msg(pcb, fsm, msg451);
|
|
return;
|
|
}
|
|
|
|
if (open_dataconnection(pcb, fsm) != 0) {
|
|
vfs_closedir(vfs_dir);
|
|
return;
|
|
}
|
|
|
|
fsm->datafs->vfs_dir = vfs_dir;
|
|
fsm->datafs->vfs_dirent = NULL;
|
|
if (shortlist != 0)
|
|
fsm->state = FTPD_NLST;
|
|
else
|
|
fsm->state = FTPD_LIST;
|
|
|
|
send_msg(pcb, fsm, msg150);
|
|
}
|
|
|
|
static void cmd_nlst(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
cmd_list_common(arg, pcb, fsm, 1);
|
|
}
|
|
|
|
static void cmd_list(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
cmd_list_common(arg, pcb, fsm, 0);
|
|
}
|
|
|
|
static void cmd_retr(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
vfs_file_t *vfs_file;
|
|
vfs_stat_t st;
|
|
|
|
vfs_stat(fsm->vfs, arg, &st);
|
|
if (!VFS_ISREG(st.st_mode)) {
|
|
send_msg(pcb, fsm, msg550);
|
|
return;
|
|
}
|
|
vfs_file = vfs_open(fsm->vfs, arg, "rb");
|
|
if (!vfs_file) {
|
|
send_msg(pcb, fsm, msg550);
|
|
return;
|
|
}
|
|
|
|
send_msg(pcb, fsm, msg150recv, arg, st.st_size);
|
|
|
|
if (open_dataconnection(pcb, fsm) != 0) {
|
|
vfs_close(vfs_file);
|
|
return;
|
|
}
|
|
|
|
fsm->datafs->vfs_file = vfs_file;
|
|
fsm->state = FTPD_RETR;
|
|
}
|
|
|
|
static void cmd_stor(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
vfs_file_t *vfs_file;
|
|
|
|
vfs_file = vfs_open(fsm->vfs, arg, "wb");
|
|
if (!vfs_file) {
|
|
send_msg(pcb, fsm, msg550);
|
|
return;
|
|
}
|
|
|
|
send_msg(pcb, fsm, msg150stor, arg);
|
|
|
|
if (open_dataconnection(pcb, fsm) != 0) {
|
|
vfs_close(vfs_file);
|
|
return;
|
|
}
|
|
|
|
fsm->datafs->vfs_file = vfs_file;
|
|
fsm->state = FTPD_STOR;
|
|
}
|
|
|
|
static void cmd_noop(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
(void) arg; /* suppress unused warning */
|
|
send_msg(pcb, fsm, msg200);
|
|
}
|
|
|
|
static void cmd_syst(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
(void) arg; /* suppress unused warning */
|
|
send_msg(pcb, fsm, msg214SYST, "UNIX");
|
|
}
|
|
|
|
static void cmd_pasv(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
static u16_t port = 4096;
|
|
static u16_t start_port = 4096;
|
|
struct tcp_pcb *temppcb;
|
|
(void) arg; /* suppress unused warning */
|
|
|
|
/* Allocate memory for the structure that holds the state of the
|
|
connection. */
|
|
fsm->datafs = malloc(sizeof(struct ftpd_datastate));
|
|
|
|
if (fsm->datafs == NULL) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("cmd_pasv: Out of memory\n"));
|
|
send_msg(pcb, fsm, msg451);
|
|
return;
|
|
}
|
|
memset(fsm->datafs, 0, sizeof(struct ftpd_datastate));
|
|
|
|
if (sfifo_init(&fsm->datafs->fifo, 2000) != 0) {
|
|
free(fsm->datafs);
|
|
send_msg(pcb, fsm, msg451);
|
|
return;
|
|
}
|
|
|
|
fsm->datalistenpcb = tcp_new();
|
|
|
|
if (fsm->datalistenpcb == NULL) {
|
|
free(fsm->datafs);
|
|
sfifo_close(&fsm->datafs->fifo);
|
|
send_msg(pcb, fsm, msg451);
|
|
return;
|
|
}
|
|
|
|
start_port = port;
|
|
|
|
while (1) {
|
|
err_t err;
|
|
|
|
if(++port > 0x7fff)
|
|
port = 4096;
|
|
|
|
fsm->dataport = port;
|
|
err = tcp_bind(fsm->datalistenpcb, (ip_addr_t*)&pcb->local_ip, fsm->dataport);
|
|
if (err == ERR_OK)
|
|
break;
|
|
if (start_port == port)
|
|
err = ERR_CLSD;
|
|
if (err == ERR_USE) {
|
|
continue;
|
|
} else {
|
|
ftpd_dataclose(fsm->datalistenpcb, fsm->datafs);
|
|
fsm->datalistenpcb = NULL;
|
|
fsm->datafs = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
temppcb = tcp_listen(fsm->datalistenpcb);
|
|
if (!temppcb) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("cmd_pasv: tcp_listen failed\n"));
|
|
ftpd_dataclose(fsm->datalistenpcb, fsm->datafs);
|
|
fsm->datalistenpcb = NULL;
|
|
fsm->datafs = NULL;
|
|
return;
|
|
}
|
|
fsm->datalistenpcb = temppcb;
|
|
|
|
fsm->passive = 1;
|
|
fsm->datafs->connected = 0;
|
|
fsm->datafs->msgfs = fsm;
|
|
fsm->datafs->msgpcb = pcb;
|
|
|
|
/* Tell TCP that this is the structure we wish to be passed for our
|
|
callbacks. */
|
|
tcp_arg(fsm->datalistenpcb, fsm->datafs);
|
|
tcp_accept(fsm->datalistenpcb, ftpd_dataaccept);
|
|
send_msg(pcb, fsm, msg227, ip4_addr1(ip_2_ip4(&pcb->local_ip)), ip4_addr2(ip_2_ip4(&pcb->local_ip)), ip4_addr3(ip_2_ip4(&pcb->local_ip)), ip4_addr4(ip_2_ip4(&pcb->local_ip)), (fsm->dataport >> 8) & 0xff, (fsm->dataport) & 0xff);
|
|
}
|
|
|
|
static void cmd_abrt(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
(void) arg; /* suppress unused warning */
|
|
if (fsm->datafs != NULL) {
|
|
tcp_arg(fsm->datapcb, NULL);
|
|
tcp_sent(fsm->datapcb, NULL);
|
|
tcp_recv(fsm->datapcb, NULL);
|
|
tcp_abort(pcb);
|
|
sfifo_close(&fsm->datafs->fifo);
|
|
free(fsm->datafs);
|
|
fsm->datafs = NULL;
|
|
}
|
|
fsm->state = FTPD_IDLE;
|
|
}
|
|
|
|
static void cmd_type(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("Got TYPE -%s-\n", arg));
|
|
|
|
if(strcmp(arg, "I") != 0) {
|
|
send_msg(pcb, fsm, msg502);
|
|
return;
|
|
}
|
|
|
|
send_msg(pcb, fsm, msg200);
|
|
}
|
|
|
|
static void cmd_mode(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("Got MODE -%s-\n", arg));
|
|
send_msg(pcb, fsm, msg502);
|
|
}
|
|
|
|
static void cmd_rnfr(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
if (arg == NULL) {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (*arg == '\0') {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (fsm->renamefrom)
|
|
free(fsm->renamefrom);
|
|
fsm->renamefrom = malloc(strlen(arg) + 1);
|
|
if (fsm->renamefrom == NULL) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("cmd_rnfr: Out of memory\n"));
|
|
send_msg(pcb, fsm, msg451);
|
|
return;
|
|
}
|
|
strcpy(fsm->renamefrom, arg);
|
|
fsm->state = FTPD_RNFR;
|
|
send_msg(pcb, fsm, msg350);
|
|
}
|
|
|
|
static void cmd_rnto(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
if (fsm->state != FTPD_RNFR) {
|
|
send_msg(pcb, fsm, msg503);
|
|
return;
|
|
}
|
|
fsm->state = FTPD_IDLE;
|
|
if (arg == NULL) {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (*arg == '\0') {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (vfs_rename(fsm->vfs, fsm->renamefrom, arg)) {
|
|
send_msg(pcb, fsm, msg450);
|
|
} else {
|
|
send_msg(pcb, fsm, msg250);
|
|
}
|
|
}
|
|
|
|
static void cmd_mkd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
if (arg == NULL) {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (*arg == '\0') {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (vfs_mkdir(fsm->vfs, arg, VFS_IRWXU | VFS_IRWXG | VFS_IRWXO) != 0) {
|
|
send_msg(pcb, fsm, msg550);
|
|
} else {
|
|
send_msg(pcb, fsm, msg257, arg);
|
|
}
|
|
}
|
|
|
|
static void cmd_rmd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
vfs_stat_t st;
|
|
|
|
if (arg == NULL) {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (*arg == '\0') {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (vfs_stat(fsm->vfs, arg, &st) != 0) {
|
|
send_msg(pcb, fsm, msg550);
|
|
return;
|
|
}
|
|
if (!VFS_ISDIR(st.st_mode)) {
|
|
send_msg(pcb, fsm, msg550);
|
|
return;
|
|
}
|
|
if (vfs_rmdir(fsm->vfs, arg) != 0) {
|
|
send_msg(pcb, fsm, msg550);
|
|
} else {
|
|
send_msg(pcb, fsm, msg250);
|
|
}
|
|
}
|
|
|
|
static void cmd_dele(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
vfs_stat_t st;
|
|
|
|
if (arg == NULL) {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (*arg == '\0') {
|
|
send_msg(pcb, fsm, msg501);
|
|
return;
|
|
}
|
|
if (vfs_stat(fsm->vfs, arg, &st) != 0) {
|
|
send_msg(pcb, fsm, msg550);
|
|
return;
|
|
}
|
|
if (!VFS_ISREG(st.st_mode)) {
|
|
send_msg(pcb, fsm, msg550);
|
|
return;
|
|
}
|
|
if (vfs_remove(fsm->vfs, arg) != 0) {
|
|
send_msg(pcb, fsm, msg550);
|
|
} else {
|
|
send_msg(pcb, fsm, msg250);
|
|
}
|
|
}
|
|
|
|
struct ftpd_command {
|
|
char *cmd;
|
|
void (*func) (const char *arg, struct tcp_pcb * pcb, struct ftpd_msgstate * fsm);
|
|
};
|
|
|
|
static struct ftpd_command ftpd_commands[] = {
|
|
{"USER", cmd_user},
|
|
{"PASS", cmd_pass},
|
|
{"PORT", cmd_port},
|
|
{"QUIT", cmd_quit},
|
|
{"CWD", cmd_cwd},
|
|
{"CDUP", cmd_cdup},
|
|
{"PWD", cmd_pwd},
|
|
{"XPWD", cmd_pwd},
|
|
{"NLST", cmd_nlst},
|
|
{"LIST", cmd_list},
|
|
{"RETR", cmd_retr},
|
|
{"STOR", cmd_stor},
|
|
{"NOOP", cmd_noop},
|
|
{"SYST", cmd_syst},
|
|
{"ABOR", cmd_abrt},
|
|
{"TYPE", cmd_type},
|
|
{"MODE", cmd_mode},
|
|
{"RNFR", cmd_rnfr},
|
|
{"RNTO", cmd_rnto},
|
|
{"MKD", cmd_mkd},
|
|
{"XMKD", cmd_mkd},
|
|
{"RMD", cmd_rmd},
|
|
{"XRMD", cmd_rmd},
|
|
{"DELE", cmd_dele},
|
|
{"PASV", cmd_pasv},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static void send_msgdata(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
err_t err;
|
|
u16_t len;
|
|
|
|
if (sfifo_used(&fsm->fifo) > 0) {
|
|
int i;
|
|
|
|
/* We cannot send more data than space available in the send
|
|
buffer. */
|
|
if (tcp_sndbuf(pcb) < sfifo_used(&fsm->fifo)) {
|
|
len = tcp_sndbuf(pcb);
|
|
} else {
|
|
len = (u16_t) sfifo_used(&fsm->fifo);
|
|
}
|
|
|
|
i = fsm->fifo.readpos;
|
|
if ((i + len) > fsm->fifo.size) {
|
|
err = tcp_write(pcb, fsm->fifo.buffer + i, (u16_t)(fsm->fifo.size - i), 1);
|
|
if (err != ERR_OK) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("send_msgdata: error writing!\n"));
|
|
return;
|
|
}
|
|
len -= fsm->fifo.size - i;
|
|
fsm->fifo.readpos = 0;
|
|
i = 0;
|
|
}
|
|
|
|
err = tcp_write(pcb, fsm->fifo.buffer + i, len, 1);
|
|
if (err != ERR_OK) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("send_msgdata: error writing!\n"));
|
|
return;
|
|
}
|
|
fsm->fifo.readpos += len;
|
|
}
|
|
}
|
|
|
|
static void send_msg(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, char *msg, ...)
|
|
{
|
|
va_list arg;
|
|
char buffer[1024];
|
|
int len;
|
|
|
|
va_start(arg, msg);
|
|
vsprintf(buffer, msg, arg);
|
|
va_end(arg);
|
|
strcat(buffer, "\r\n");
|
|
len = strlen(buffer);
|
|
if (sfifo_space(&fsm->fifo) < len)
|
|
return;
|
|
sfifo_write(&fsm->fifo, buffer, len);
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("response: %s", buffer));
|
|
send_msgdata(pcb, fsm);
|
|
}
|
|
|
|
static void ftpd_msgerr(void *arg, err_t err)
|
|
{
|
|
struct ftpd_msgstate *fsm = arg;
|
|
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_msgerr: %s (%i)\n", lwip_strerr(err), err));
|
|
if (fsm == NULL)
|
|
return;
|
|
if (fsm->datafs)
|
|
ftpd_dataclose(fsm->datapcb, fsm->datafs);
|
|
sfifo_close(&fsm->fifo);
|
|
vfs_close(fsm->vfs);
|
|
fsm->vfs = NULL;
|
|
if (fsm->renamefrom)
|
|
free(fsm->renamefrom);
|
|
fsm->renamefrom = NULL;
|
|
free(fsm);
|
|
}
|
|
|
|
static void ftpd_msgclose(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm)
|
|
{
|
|
tcp_arg(pcb, NULL);
|
|
tcp_sent(pcb, NULL);
|
|
tcp_recv(pcb, NULL);
|
|
if (fsm->datafs)
|
|
ftpd_dataclose(fsm->datapcb, fsm->datafs);
|
|
sfifo_close(&fsm->fifo);
|
|
vfs_close(fsm->vfs);
|
|
fsm->vfs = NULL;
|
|
if (fsm->renamefrom)
|
|
free(fsm->renamefrom);
|
|
fsm->renamefrom = NULL;
|
|
free(fsm);
|
|
tcp_arg(pcb, NULL);
|
|
tcp_close(pcb);
|
|
}
|
|
|
|
static err_t ftpd_msgsent(void *arg, struct tcp_pcb *pcb, u16_t len)
|
|
{
|
|
struct ftpd_msgstate *fsm = arg;
|
|
(void) len; /* suppress unused warning */
|
|
|
|
if ((sfifo_used(&fsm->fifo) == 0) && (fsm->state == FTPD_QUIT)) {
|
|
ftpd_msgclose(pcb, fsm);
|
|
return ERR_OK;
|
|
}
|
|
|
|
if (pcb->state <= ESTABLISHED) send_msgdata(pcb, fsm);
|
|
return ERR_OK;
|
|
}
|
|
|
|
static err_t ftpd_msgrecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
|
|
{
|
|
char *text;
|
|
struct ftpd_msgstate *fsm = arg;
|
|
|
|
if (err == ERR_OK && p != NULL) {
|
|
|
|
/* Inform TCP that we have taken the data. */
|
|
tcp_recved(pcb, p->tot_len);
|
|
|
|
text = malloc(p->tot_len + 1);
|
|
if (text) {
|
|
char cmd[5];
|
|
struct pbuf *q;
|
|
char *pt = text;
|
|
struct ftpd_command *ftpd_cmd;
|
|
|
|
for (q = p; q != NULL; q = q->next) {
|
|
bcopy(q->payload, pt, q->len);
|
|
pt += q->len;
|
|
}
|
|
*pt = '\0';
|
|
|
|
pt = &text[strlen(text) - 1];
|
|
while (((*pt == '\r') || (*pt == '\n')) && pt >= text)
|
|
*pt-- = '\0';
|
|
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("query: %s\n", text));
|
|
|
|
strncpy(cmd, text, 4);
|
|
for (pt = cmd; isalpha(*pt) && pt < &cmd[4]; pt++)
|
|
*pt = toupper(*pt);
|
|
*pt = '\0';
|
|
|
|
for (ftpd_cmd = ftpd_commands; ftpd_cmd->cmd != NULL; ftpd_cmd++) {
|
|
if (!strcmp(ftpd_cmd->cmd, cmd))
|
|
break;
|
|
}
|
|
|
|
if (strlen(text) < (strlen(cmd) + 1))
|
|
pt = "";
|
|
else
|
|
pt = &text[strlen(cmd) + 1];
|
|
|
|
if (ftpd_cmd->func)
|
|
ftpd_cmd->func(pt, pcb, fsm);
|
|
else
|
|
send_msg(pcb, fsm, msg502);
|
|
|
|
free(text);
|
|
}
|
|
pbuf_free(p);
|
|
} else if (err == ERR_OK && p == NULL) {
|
|
ftpd_msgclose(pcb, fsm);
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
static err_t ftpd_msgpoll(void *arg, struct tcp_pcb *pcb)
|
|
{
|
|
struct ftpd_msgstate *fsm = arg;
|
|
(void) pcb; /* suppress unused warning */
|
|
|
|
if (fsm == NULL)
|
|
return ERR_OK;
|
|
|
|
if (fsm->datafs) {
|
|
if (fsm->datafs->connected) {
|
|
switch (fsm->state) {
|
|
case FTPD_LIST:
|
|
send_next_directory(fsm->datafs, fsm->datapcb, 0);
|
|
break;
|
|
case FTPD_NLST:
|
|
send_next_directory(fsm->datafs, fsm->datapcb, 1);
|
|
break;
|
|
case FTPD_RETR:
|
|
send_file(fsm->datafs, fsm->datapcb);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
static err_t ftpd_msgaccept(void *arg, struct tcp_pcb *pcb, err_t err)
|
|
{
|
|
LWIP_PLATFORM_DIAG(("ftpd_msgaccept called"));
|
|
struct ftpd_msgstate *fsm;
|
|
(void) err; /* suppress unused warning */
|
|
(void) arg;
|
|
|
|
/* Allocate memory for the structure that holds the state of the
|
|
connection. */
|
|
fsm = malloc(sizeof(struct ftpd_msgstate));
|
|
|
|
if (fsm == NULL) {
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_msgaccept: Out of memory\n"));
|
|
return ERR_MEM;
|
|
}
|
|
memset(fsm, 0, sizeof(struct ftpd_msgstate));
|
|
|
|
/* Initialize the structure. */
|
|
if (sfifo_init(&fsm->fifo, 2000) != 0) {
|
|
free(fsm);
|
|
return ERR_MEM;
|
|
}
|
|
fsm->state = FTPD_IDLE;
|
|
fsm->vfs = vfs_openfs();
|
|
if (fsm->vfs == NULL) {
|
|
sfifo_close(&fsm->fifo);
|
|
free(fsm);
|
|
return ERR_CLSD;
|
|
}
|
|
|
|
/* Tell TCP that this is the structure we wish to be passed for our
|
|
callbacks. */
|
|
tcp_arg(pcb, fsm);
|
|
|
|
/* Tell TCP that we wish to be informed of incoming data by a call
|
|
to the http_recv() function. */
|
|
tcp_recv(pcb, ftpd_msgrecv);
|
|
|
|
/* Tell TCP that we wish be to informed of data that has been
|
|
successfully sent by a call to the ftpd_sent() function. */
|
|
tcp_sent(pcb, ftpd_msgsent);
|
|
|
|
tcp_err(pcb, ftpd_msgerr);
|
|
|
|
tcp_poll(pcb, ftpd_msgpoll, 1);
|
|
|
|
send_msg(pcb, fsm, msg220);
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
void ftpd_init(void)
|
|
{
|
|
struct tcp_pcb *pcb;
|
|
|
|
vfs_load_plugin(vfs_default_fs);
|
|
|
|
pcb = tcp_new();
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_init: pcb: %lx\n", (unsigned long) pcb));
|
|
int r = tcp_bind(pcb, IP_ADDR_ANY, 21);
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_init: tcp_bind: %d\n", r));
|
|
pcb = tcp_listen(pcb);
|
|
LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_init: listen-pcb: %lx\n", (unsigned long) pcb));
|
|
tcp_accept(pcb, ftpd_msgaccept);
|
|
}
|
|
|