2014-11-23 23:39:00 +01:00
|
|
|
#include "ftp.h"
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <ctype.h>
|
2015-08-19 20:25:34 +02:00
|
|
|
#include <dirent.h>
|
2014-11-23 23:39:00 +01:00
|
|
|
#include <errno.h>
|
2014-11-25 09:30:55 +01:00
|
|
|
#include <fcntl.h>
|
2014-11-23 23:39:00 +01:00
|
|
|
#include <malloc.h>
|
2016-01-15 19:13:28 +01:00
|
|
|
#include <netdb.h>
|
2014-11-23 23:39:00 +01:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <stdarg.h>
|
2015-08-19 20:25:34 +02:00
|
|
|
#include <stdio.h>
|
2014-11-23 23:39:00 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/socket.h>
|
2015-08-19 20:25:34 +02:00
|
|
|
#include <sys/stat.h>
|
2014-11-23 23:39:00 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
#ifdef _3DS
|
|
|
|
#include <3ds.h>
|
2015-08-19 20:25:34 +02:00
|
|
|
#define lstat stat
|
2016-01-15 23:45:10 +01:00
|
|
|
#else
|
2016-01-16 00:47:18 +01:00
|
|
|
#include <stdbool.h>
|
2016-01-15 23:45:10 +01:00
|
|
|
#define BIT(x) (1<<(x))
|
2014-11-23 23:39:00 +01:00
|
|
|
#endif
|
|
|
|
#include "console.h"
|
|
|
|
|
|
|
|
#define POLL_UNKNOWN (~(POLLIN|POLLOUT))
|
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
#define XFER_BUFFERSIZE 32768
|
|
|
|
#define SOCK_BUFFERSIZE 32768
|
|
|
|
#define FILE_BUFFERSIZE 65536
|
2014-11-25 09:30:55 +01:00
|
|
|
#define CMD_BUFFERSIZE 1024
|
2016-01-12 23:24:01 +01:00
|
|
|
#define SOCU_ALIGN 0x1000
|
|
|
|
#define SOCU_BUFFERSIZE 0x100000
|
2014-11-25 09:30:55 +01:00
|
|
|
#define LISTEN_PORT 5000
|
2014-11-23 23:39:00 +01:00
|
|
|
#ifdef _3DS
|
2014-11-25 09:30:55 +01:00
|
|
|
#define DATA_PORT (LISTEN_PORT+1)
|
2014-11-23 23:39:00 +01:00
|
|
|
#else
|
2014-11-25 09:30:55 +01:00
|
|
|
#define DATA_PORT 0 /* ephemeral port */
|
2014-11-23 23:39:00 +01:00
|
|
|
#endif
|
|
|
|
|
2014-11-25 08:22:09 +01:00
|
|
|
typedef struct ftp_session_t ftp_session_t;
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
#define FTP_DECLARE(x) static int x(ftp_session_t *session, const char *args)
|
|
|
|
FTP_DECLARE(ALLO);
|
|
|
|
FTP_DECLARE(APPE);
|
|
|
|
FTP_DECLARE(CDUP);
|
|
|
|
FTP_DECLARE(CWD);
|
|
|
|
FTP_DECLARE(DELE);
|
|
|
|
FTP_DECLARE(FEAT);
|
|
|
|
FTP_DECLARE(LIST);
|
|
|
|
FTP_DECLARE(MKD);
|
|
|
|
FTP_DECLARE(MODE);
|
|
|
|
FTP_DECLARE(NLST);
|
|
|
|
FTP_DECLARE(NOOP);
|
2015-08-19 20:25:34 +02:00
|
|
|
FTP_DECLARE(OPTS);
|
2014-11-23 23:39:00 +01:00
|
|
|
FTP_DECLARE(PASS);
|
|
|
|
FTP_DECLARE(PASV);
|
|
|
|
FTP_DECLARE(PORT);
|
|
|
|
FTP_DECLARE(PWD);
|
|
|
|
FTP_DECLARE(QUIT);
|
|
|
|
FTP_DECLARE(REST);
|
|
|
|
FTP_DECLARE(RETR);
|
|
|
|
FTP_DECLARE(RMD);
|
|
|
|
FTP_DECLARE(RNFR);
|
|
|
|
FTP_DECLARE(RNTO);
|
|
|
|
FTP_DECLARE(STOR);
|
|
|
|
FTP_DECLARE(STOU);
|
|
|
|
FTP_DECLARE(STRU);
|
|
|
|
FTP_DECLARE(SYST);
|
|
|
|
FTP_DECLARE(TYPE);
|
|
|
|
FTP_DECLARE(USER);
|
|
|
|
|
|
|
|
/*! session state */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
COMMAND_STATE, /*!< waiting for a command */
|
|
|
|
DATA_CONNECT_STATE, /*!< waiting for connection after PASV command */
|
|
|
|
DATA_TRANSFER_STATE, /*!< data transfer in progress */
|
|
|
|
} session_state_t;
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
/*! ftp_session_set_state flags */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
CLOSE_PASV = BIT(0), /*!< Close the pasv_fd */
|
|
|
|
CLOSE_DATA = BIT(1), /*!< Close the data_fd */
|
|
|
|
} set_state_flags_t;
|
|
|
|
|
|
|
|
/*! ftp_session_t flags */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
SESSION_BINARY = BIT(0), /*!< data transfers in binary mode */
|
|
|
|
SESSION_PASV = BIT(1), /*!< have pasv_addr ready for data transfer command */
|
|
|
|
SESSION_PORT = BIT(2), /*!< have peer_addr ready for data transfer command */
|
|
|
|
SESSION_RECV = BIT(3), /*!< data transfer in source mode */
|
|
|
|
SESSION_SEND = BIT(4), /*!< data transfer in sink mode */
|
|
|
|
SESSION_RENAME = BIT(5), /*!< last command was RNFR and buffer contains path */
|
2016-01-16 00:47:18 +01:00
|
|
|
SESSION_NLST = BIT(6), /*!< list command is NLST */
|
2016-01-15 23:45:10 +01:00
|
|
|
} session_flags_t;
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
/*! ftp session */
|
2014-11-25 08:22:09 +01:00
|
|
|
struct ftp_session_t
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
|
|
|
char cwd[4096]; /*!< current working directory */
|
|
|
|
struct sockaddr_in peer_addr; /*!< peer address for data connection */
|
|
|
|
struct sockaddr_in pasv_addr; /*!< listen address for PASV connection */
|
|
|
|
int cmd_fd; /*!< socket for command connection */
|
|
|
|
int pasv_fd; /*!< listen socket for PASV */
|
|
|
|
int data_fd; /*!< socket for data transfer */
|
2016-01-15 23:45:10 +01:00
|
|
|
session_flags_t flags; /*!< session flags */
|
2014-11-23 23:39:00 +01:00
|
|
|
session_state_t state; /*!< session state */
|
|
|
|
ftp_session_t *next; /*!< link to next session */
|
|
|
|
ftp_session_t *prev; /*!< link to prev session */
|
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
int (*transfer)(ftp_session_t*); /*! data transfer callback */
|
|
|
|
char buffer[XFER_BUFFERSIZE]; /*! persistent data between callbacks */
|
|
|
|
char tmp_buffer[XFER_BUFFERSIZE]; /*! persistent data between callbacks */
|
|
|
|
char file_buffer[FILE_BUFFERSIZE]; /*! stdio file buffer */
|
|
|
|
size_t bufferpos; /*! persistent buffer position between callbacks */
|
|
|
|
size_t buffersize; /*! persistent buffer size between callbacks */
|
|
|
|
uint64_t filepos; /*! persistent file position between callbacks */
|
|
|
|
uint64_t filesize; /*! persistent file size between callbacks */
|
2016-01-15 23:45:10 +01:00
|
|
|
FILE *fp; /*! persistent open file pointer between callbacks */
|
|
|
|
DIR *dp; /*! persistent open directory pointer between callbacks */
|
2014-11-23 23:39:00 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/*! ftp command descriptor */
|
|
|
|
typedef struct ftp_command
|
|
|
|
{
|
|
|
|
const char *name; /*!< command name */
|
|
|
|
int (*handler)(ftp_session_t*, const char*); /*!< command callback */
|
|
|
|
} ftp_command_t;
|
|
|
|
|
|
|
|
static ftp_command_t ftp_commands[] =
|
|
|
|
{
|
|
|
|
#define FTP_COMMAND(x) { #x, x, }
|
|
|
|
#define FTP_ALIAS(x,y) { #x, y, }
|
|
|
|
FTP_COMMAND(ALLO),
|
|
|
|
FTP_COMMAND(APPE),
|
|
|
|
FTP_COMMAND(CDUP),
|
|
|
|
FTP_COMMAND(CWD),
|
|
|
|
FTP_COMMAND(DELE),
|
|
|
|
FTP_COMMAND(FEAT),
|
|
|
|
FTP_COMMAND(LIST),
|
|
|
|
FTP_COMMAND(MKD),
|
|
|
|
FTP_COMMAND(MODE),
|
|
|
|
FTP_COMMAND(NLST),
|
|
|
|
FTP_COMMAND(NOOP),
|
2015-08-19 20:25:34 +02:00
|
|
|
FTP_COMMAND(OPTS),
|
2014-11-23 23:39:00 +01:00
|
|
|
FTP_COMMAND(PASS),
|
|
|
|
FTP_COMMAND(PASV),
|
|
|
|
FTP_COMMAND(PORT),
|
|
|
|
FTP_COMMAND(PWD),
|
|
|
|
FTP_COMMAND(QUIT),
|
|
|
|
FTP_COMMAND(REST),
|
|
|
|
FTP_COMMAND(RETR),
|
|
|
|
FTP_COMMAND(RMD),
|
|
|
|
FTP_COMMAND(RNFR),
|
|
|
|
FTP_COMMAND(RNTO),
|
|
|
|
FTP_COMMAND(STOR),
|
|
|
|
FTP_COMMAND(STOU),
|
|
|
|
FTP_COMMAND(STRU),
|
|
|
|
FTP_COMMAND(SYST),
|
|
|
|
FTP_COMMAND(TYPE),
|
|
|
|
FTP_COMMAND(USER),
|
|
|
|
FTP_ALIAS(XCUP, CDUP),
|
|
|
|
FTP_ALIAS(XMKD, MKD),
|
|
|
|
FTP_ALIAS(XPWD, PWD),
|
|
|
|
FTP_ALIAS(XRMD, RMD),
|
|
|
|
};
|
|
|
|
/*! number of ftp commands */
|
|
|
|
static const size_t num_ftp_commands = sizeof(ftp_commands)/sizeof(ftp_commands[0]);
|
|
|
|
|
|
|
|
/*! compare ftp command descriptors
|
|
|
|
*
|
|
|
|
* @param[in] p1 left side of comparison (ftp_command_t*)
|
|
|
|
* @param[in] p2 right side of comparison (ftp_command_t*)
|
|
|
|
*
|
|
|
|
* @returns <0 if p1 < p2
|
|
|
|
* @returns 0 if p1 == p2
|
|
|
|
* @returns >0 if p1 > p2
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ftp_command_cmp(const void *p1,
|
|
|
|
const void *p2)
|
|
|
|
{
|
|
|
|
ftp_command_t *c1 = (ftp_command_t*)p1;
|
|
|
|
ftp_command_t *c2 = (ftp_command_t*)p2;
|
|
|
|
|
|
|
|
/* ordered by command name */
|
|
|
|
return strcasecmp(c1->name, c2->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _3DS
|
|
|
|
/*! SOC service buffer */
|
2016-01-12 23:24:01 +01:00
|
|
|
static u32 *SOCU_buffer = NULL;
|
2014-11-23 23:39:00 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*! server listen address */
|
|
|
|
static struct sockaddr_in serv_addr;
|
|
|
|
/*! listen file descriptor */
|
|
|
|
static int listenfd = -1;
|
2016-01-12 23:24:01 +01:00
|
|
|
#ifdef _3DS
|
2014-11-25 08:22:09 +01:00
|
|
|
/*! current data port */
|
2015-08-19 20:25:34 +02:00
|
|
|
static in_port_t data_port = DATA_PORT;
|
2016-01-12 23:24:01 +01:00
|
|
|
#endif
|
2014-11-23 23:39:00 +01:00
|
|
|
/*! list of ftp sessions */
|
2015-08-19 20:25:34 +02:00
|
|
|
static ftp_session_t *sessions = NULL;
|
2016-01-12 23:24:01 +01:00
|
|
|
/*! socket buffersize */
|
|
|
|
static int sock_buffersize = SOCK_BUFFERSIZE;
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-25 08:22:09 +01:00
|
|
|
/*! Allocate a new data port
|
|
|
|
*
|
|
|
|
* @returns next data port
|
|
|
|
*/
|
|
|
|
static in_port_t
|
|
|
|
next_data_port(void)
|
|
|
|
{
|
|
|
|
#ifdef _3DS
|
|
|
|
if(++data_port >= 10000)
|
|
|
|
data_port = DATA_PORT;
|
|
|
|
return data_port;
|
|
|
|
#else
|
|
|
|
return 0; /* ephemeral port */
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-11-25 09:30:55 +01:00
|
|
|
/*! set a socket to non-blocking
|
|
|
|
*
|
|
|
|
* @param[in] fd socket
|
|
|
|
*
|
|
|
|
* @returns error
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ftp_set_socket_nonblocking(int fd)
|
|
|
|
{
|
|
|
|
int rc, flags;
|
|
|
|
|
|
|
|
flags = fcntl(fd, F_GETFL, 0);
|
|
|
|
if(flags == -1)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "fcntl: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "fcntl: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
/*! set socket options
|
|
|
|
*
|
|
|
|
* @param[in] fd socket
|
2016-01-15 23:45:10 +01:00
|
|
|
*
|
|
|
|
* @returns failure
|
2016-01-12 23:24:01 +01:00
|
|
|
*/
|
2016-01-15 23:45:10 +01:00
|
|
|
static int
|
2016-01-12 23:24:01 +01:00
|
|
|
ftp_set_socket_options(int fd)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
|
|
|
|
&sock_buffersize, sizeof(sock_buffersize));
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
console_print(RED "setsockopt: %d %s\n" RESET, errno, strerror(errno));
|
2016-01-15 23:45:10 +01:00
|
|
|
return -1;
|
2016-01-12 23:24:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
|
|
|
|
&sock_buffersize, sizeof(sock_buffersize));
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
console_print(RED "setsockopt: %d %s\n" RESET, errno, strerror(errno));
|
2016-01-15 23:45:10 +01:00
|
|
|
return -1;
|
2016-01-12 23:24:01 +01:00
|
|
|
}
|
2016-01-15 23:45:10 +01:00
|
|
|
|
|
|
|
return 0;
|
2016-01-12 23:24:01 +01:00
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
/*! close a socket
|
|
|
|
*
|
|
|
|
* @param[in] fd socket to close
|
|
|
|
* @param[in] connected whether this socket is connected
|
|
|
|
*/
|
|
|
|
static void
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(int fd,
|
|
|
|
bool connected)
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
socklen_t addrlen = sizeof(addr);
|
|
|
|
|
|
|
|
if(connected)
|
|
|
|
{
|
|
|
|
/* get peer address and print */
|
|
|
|
rc = getpeername(fd, (struct sockaddr*)&addr, &addrlen);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "getpeername: %d %s\n" RESET, errno, strerror(errno));
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "closing connection to fd=%d\n" RESET, fd);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
else
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "closing connection to %s:%u\n" RESET,
|
2014-11-23 23:39:00 +01:00
|
|
|
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
|
|
|
|
|
|
|
/* shutdown connection */
|
|
|
|
rc = shutdown(fd, SHUT_RDWR);
|
|
|
|
if(rc != 0)
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "shutdown: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* close socket */
|
2015-08-19 20:25:34 +02:00
|
|
|
rc = close(fd);
|
2014-11-23 23:39:00 +01:00
|
|
|
if(rc != 0)
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "close: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! close command socket on ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ftp_session_close_cmd(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
/* close command socket */
|
2016-01-15 23:45:10 +01:00
|
|
|
if(session->cmd_fd >= 0)
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(session->cmd_fd, true);
|
2014-11-23 23:39:00 +01:00
|
|
|
session->cmd_fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! close listen socket on ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ftp_session_close_pasv(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
/* close pasv socket */
|
2016-01-15 23:45:10 +01:00
|
|
|
if(session->pasv_fd >= 0)
|
|
|
|
{
|
|
|
|
console_print(YELLOW "stop listening on %s:%u\n" RESET,
|
|
|
|
inet_ntoa(session->pasv_addr.sin_addr),
|
|
|
|
ntohs(session->pasv_addr.sin_port));
|
|
|
|
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(session->pasv_fd, false);
|
2016-01-15 23:45:10 +01:00
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
session->pasv_fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! close data socket on ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session */
|
|
|
|
static void
|
|
|
|
ftp_session_close_data(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
/* close data connection */
|
2016-01-15 23:45:10 +01:00
|
|
|
if(session->data_fd >= 0)
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(session->data_fd, true);
|
2014-11-23 23:39:00 +01:00
|
|
|
session->data_fd = -1;
|
|
|
|
|
|
|
|
/* clear send/recv flags */
|
|
|
|
session->flags &= ~(SESSION_RECV|SESSION_SEND);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! close open file for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ftp_session_close_file(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
if(session->fp != NULL)
|
|
|
|
{
|
|
|
|
rc = fclose(session->fp);
|
|
|
|
if(rc != 0)
|
|
|
|
console_print(RED "fclose: %d %s\n" RESET, errno, strerror(errno));
|
|
|
|
}
|
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
session->fp = NULL;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! open file for reading for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*
|
|
|
|
* @returns -1 for error
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ftp_session_open_file_read(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
/* open file in read mode */
|
2016-01-12 23:24:01 +01:00
|
|
|
session->fp = fopen(session->buffer, "rb");
|
|
|
|
if(session->fp == NULL)
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
2016-01-12 23:24:01 +01:00
|
|
|
console_print(RED "fopen '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
/* it's okay if this fails */
|
|
|
|
errno = 0;
|
|
|
|
rc = setvbuf(session->fp, session->file_buffer, _IOFBF, FILE_BUFFERSIZE);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
console_print(RED "setvbuf: %d %s\n" RESET, errno, strerror(errno));
|
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
/* get the file size */
|
2016-01-12 23:24:01 +01:00
|
|
|
rc = fstat(fileno(session->fp), &st);
|
2014-11-23 23:39:00 +01:00
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "fstat '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
session->filesize = st.st_size;
|
|
|
|
|
|
|
|
/* reset file position */
|
|
|
|
/* TODO: support REST command */
|
|
|
|
session->filepos = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! read from an open file for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*
|
|
|
|
* @returns bytes read
|
|
|
|
*/
|
|
|
|
static ssize_t
|
|
|
|
ftp_session_read_file(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
|
|
|
|
/* read file at current position */
|
2016-01-12 23:24:01 +01:00
|
|
|
rc = fread(session->buffer, 1, sizeof(session->buffer), session->fp);
|
2014-11-23 23:39:00 +01:00
|
|
|
if(rc < 0)
|
|
|
|
{
|
2016-01-12 23:24:01 +01:00
|
|
|
console_print(RED "fread: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* adjust file position */
|
|
|
|
session->filepos += rc;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! open file for writing for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
2016-01-16 00:28:59 +01:00
|
|
|
* @param[in] append whether to append
|
2014-11-23 23:39:00 +01:00
|
|
|
*
|
|
|
|
* @returns -1 for error
|
|
|
|
*
|
|
|
|
* @note truncates file
|
|
|
|
*/
|
|
|
|
static int
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_session_open_file_write(ftp_session_t *session,
|
|
|
|
bool append)
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
2016-01-16 00:28:59 +01:00
|
|
|
int rc;
|
|
|
|
const char *mode = "wb";
|
|
|
|
|
|
|
|
if(append)
|
|
|
|
mode = "ab";
|
2016-01-12 23:24:01 +01:00
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
/* open file in write and create mode with truncation */
|
2016-01-16 00:28:59 +01:00
|
|
|
session->fp = fopen(session->buffer, mode);
|
2016-01-12 23:24:01 +01:00
|
|
|
if(session->fp == NULL)
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
2016-01-12 23:24:01 +01:00
|
|
|
console_print(RED "fopen '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
/* it's okay if this fails */
|
|
|
|
errno = 0;
|
|
|
|
rc = setvbuf(session->fp, session->file_buffer, _IOFBF, FILE_BUFFERSIZE);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
console_print(RED "setvbuf: %d %s\n" RESET, errno, strerror(errno));
|
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
/* reset file position */
|
|
|
|
/* TODO: support REST command */
|
|
|
|
session->filepos = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! write to an open file for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*
|
|
|
|
* @returns bytes written
|
|
|
|
*/
|
|
|
|
static ssize_t
|
|
|
|
ftp_session_write_file(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
|
|
|
|
/* write to file at current position */
|
2016-01-12 23:24:01 +01:00
|
|
|
rc = fwrite(session->buffer + session->bufferpos,
|
|
|
|
1, session->buffersize - session->bufferpos,
|
|
|
|
session->fp);
|
2014-11-23 23:39:00 +01:00
|
|
|
if(rc < 0)
|
|
|
|
{
|
2016-01-12 23:24:01 +01:00
|
|
|
console_print(RED "fwrite: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if(rc == 0)
|
2016-01-12 23:24:01 +01:00
|
|
|
console_print(RED "fwrite: wrote 0 bytes\n" RESET);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
/* adjust file position */
|
|
|
|
session->filepos += rc;
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! close current working directory for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ftp_session_close_cwd(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* close open directory pointer */
|
2016-01-15 23:45:10 +01:00
|
|
|
if(session->dp != NULL)
|
|
|
|
{
|
|
|
|
rc = closedir(session->dp);
|
|
|
|
if(rc != 0)
|
|
|
|
console_print(RED "closedir: %d %s\n" RESET, errno, strerror(errno));
|
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
session->dp = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! open current working directory for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*
|
|
|
|
* @return -1 for failure
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ftp_session_open_cwd(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
/* open current working directory */
|
|
|
|
session->dp = opendir(session->cwd);
|
|
|
|
if(session->dp == NULL)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "opendir '%s': %d %s\n" RESET, session->cwd, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! set state for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
* @param[in] state state to set
|
2016-01-15 23:45:10 +01:00
|
|
|
* @param[in] flags flags
|
2014-11-23 23:39:00 +01:00
|
|
|
*/
|
|
|
|
static void
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(ftp_session_t *session,
|
|
|
|
session_state_t state,
|
|
|
|
set_state_flags_t flags)
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
|
|
|
session->state = state;
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
/* close pasv and data sockets */
|
|
|
|
if(flags & CLOSE_PASV)
|
|
|
|
ftp_session_close_pasv(session);
|
|
|
|
if(flags & CLOSE_DATA)
|
|
|
|
ftp_session_close_data(session);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
if(state == COMMAND_STATE)
|
|
|
|
{
|
|
|
|
/* close file/cwd */
|
|
|
|
ftp_session_close_file(session);
|
|
|
|
ftp_session_close_cwd(session);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-25 09:30:55 +01:00
|
|
|
static void
|
|
|
|
ftp_session_transfer(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
rc = session->transfer(session);
|
|
|
|
} while(rc == 0);
|
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
__attribute__((format(printf,3,4)))
|
|
|
|
/*! send ftp response to ftp session's peer
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
* @param[in] code response code
|
|
|
|
* @param[in] fmt format string
|
|
|
|
* @param[in] ... format arguments
|
|
|
|
*
|
|
|
|
* returns bytes send to peer
|
|
|
|
*/
|
|
|
|
static ssize_t
|
|
|
|
ftp_send_response(ftp_session_t *session,
|
|
|
|
int code,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
2014-11-25 09:30:55 +01:00
|
|
|
static char buffer[CMD_BUFFERSIZE];
|
2014-11-23 23:39:00 +01:00
|
|
|
ssize_t rc, to_send;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
/* print response code and message to buffer */
|
|
|
|
va_start(ap, fmt);
|
2015-08-19 20:25:34 +02:00
|
|
|
if(code != 211)
|
|
|
|
rc = sprintf(buffer, "%d ", code);
|
|
|
|
else
|
|
|
|
rc = sprintf(buffer, "%d- ", code);
|
2014-11-23 23:39:00 +01:00
|
|
|
rc += vsnprintf(buffer+rc, sizeof(buffer)-rc, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if(rc >= sizeof(buffer))
|
|
|
|
{
|
|
|
|
/* couldn't fit message; just send code */
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "%s: buffersize too small\n" RESET, __func__);
|
2014-11-23 23:39:00 +01:00
|
|
|
rc = sprintf(buffer, "%d\r\n", code);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* send response */
|
|
|
|
to_send = rc;
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(GREEN "%s" RESET, buffer);
|
2014-11-23 23:39:00 +01:00
|
|
|
rc = send(session->cmd_fd, buffer, to_send, 0);
|
|
|
|
if(rc < 0)
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "send: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
else if(rc != to_send)
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "only sent %u/%u bytes\n" RESET,
|
2014-11-23 23:39:00 +01:00
|
|
|
(unsigned int)rc, (unsigned int)to_send);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! destroy ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*/
|
|
|
|
static ftp_session_t*
|
|
|
|
ftp_session_destroy(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
ftp_session_t *next = session->next;
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
/* close all sockets/files */
|
|
|
|
ftp_session_close_cmd(session);
|
|
|
|
ftp_session_close_pasv(session);
|
|
|
|
ftp_session_close_data(session);
|
|
|
|
ftp_session_close_file(session);
|
|
|
|
ftp_session_close_cwd(session);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
/* unlink from sessions list */
|
|
|
|
if(session->next)
|
|
|
|
session->next->prev = session->prev;
|
|
|
|
if(session == sessions)
|
|
|
|
sessions = session->next;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
session->prev->next = session->next;
|
|
|
|
if(session == sessions->prev)
|
|
|
|
sessions->prev = session->prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* deallocate */
|
|
|
|
free(session);
|
|
|
|
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! allocate new ftp session
|
|
|
|
*
|
|
|
|
* @param[in] listen_fd socket to accept connection from
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ftp_session_new(int listen_fd)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
int new_fd;
|
|
|
|
ftp_session_t *session;
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
socklen_t addrlen = sizeof(addr);
|
2016-01-15 19:13:28 +01:00
|
|
|
char host[NI_MAXHOST+1];
|
|
|
|
char serv[NI_MAXSERV+1];
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
/* accept connection */
|
|
|
|
new_fd = accept(listen_fd, (struct sockaddr*)&addr, &addrlen);
|
|
|
|
if(new_fd < 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "accept: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-15 19:13:28 +01:00
|
|
|
memset(host, 0, sizeof(host));
|
|
|
|
memset(serv, 0, sizeof(serv));
|
|
|
|
|
|
|
|
rc = getnameinfo((struct sockaddr*)&addr, addrlen,
|
|
|
|
host, 15, serv, 2, 0);
|
|
|
|
if(rc != 0)
|
|
|
|
console_print(CYAN "accepted connection from %s:%u\n" RESET,
|
|
|
|
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
|
|
|
else
|
|
|
|
console_print(CYAN "accepted connection from %s:%s\n" RESET,
|
|
|
|
host, serv);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
/* allocate a new session */
|
|
|
|
session = (ftp_session_t*)malloc(sizeof(ftp_session_t));
|
|
|
|
if(session == NULL)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "failed to allocate session\n" RESET);
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(new_fd, true);
|
2014-11-23 23:39:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize session */
|
|
|
|
memset(session->cwd, 0, sizeof(session->cwd));
|
|
|
|
strcpy(session->cwd, "/");
|
|
|
|
session->peer_addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
session->cmd_fd = new_fd;
|
|
|
|
session->pasv_fd = -1;
|
|
|
|
session->data_fd = -1;
|
|
|
|
session->flags = 0;
|
|
|
|
session->state = COMMAND_STATE;
|
|
|
|
session->next = NULL;
|
|
|
|
session->prev = NULL;
|
|
|
|
session->transfer = NULL;
|
|
|
|
|
|
|
|
/* link to the sessions list */
|
|
|
|
if(sessions == NULL)
|
|
|
|
{
|
|
|
|
sessions = session;
|
|
|
|
session->prev = session;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sessions->prev->next = session;
|
|
|
|
session->prev = sessions->prev;
|
|
|
|
sessions->prev = session;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy socket address to pasv address */
|
|
|
|
addrlen = sizeof(session->pasv_addr);
|
|
|
|
rc = getsockname(new_fd, (struct sockaddr*)&session->pasv_addr, &addrlen);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "getsockname: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_send_response(session, 451, "Failed to get connection info\r\n");
|
|
|
|
ftp_session_destroy(session);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
session->cmd_fd = new_fd;
|
|
|
|
|
|
|
|
/* send initiator response */
|
2016-01-15 19:13:56 +01:00
|
|
|
rc = ftp_send_response(session, 220, "Hello!\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
if(rc <= 0)
|
|
|
|
ftp_session_destroy(session);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! accept PASV connection for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*
|
|
|
|
* @returns -1 for failure
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ftp_session_accept(ftp_session_t *session)
|
|
|
|
{
|
2014-11-25 09:30:55 +01:00
|
|
|
int rc, new_fd;
|
2014-11-23 23:39:00 +01:00
|
|
|
struct sockaddr_in addr;
|
|
|
|
socklen_t addrlen = sizeof(addr);
|
|
|
|
|
|
|
|
if(session->flags & SESSION_PASV)
|
|
|
|
{
|
|
|
|
/* clear PASV flag */
|
|
|
|
session->flags &= ~SESSION_PASV;
|
|
|
|
|
|
|
|
/* tell the peer that we're ready to accept the connection */
|
|
|
|
ftp_send_response(session, 150, "Ready\r\n");
|
|
|
|
|
|
|
|
/* accept connection from peer */
|
|
|
|
new_fd = accept(session->pasv_fd, (struct sockaddr*)&addr, &addrlen);
|
|
|
|
if(new_fd < 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "accept: %d %s\n" RESET, errno, strerror(errno));
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-25 09:30:55 +01:00
|
|
|
ftp_send_response(session, 425, "Failed to establish connection\r\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = ftp_set_socket_nonblocking(new_fd);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(new_fd, true);
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_send_response(session, 425, "Failed to establish connection\r\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "accepted connection from %s:%u\n" RESET,
|
2014-11-23 23:39:00 +01:00
|
|
|
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, DATA_TRANSFER_STATE, CLOSE_PASV);
|
2014-11-23 23:39:00 +01:00
|
|
|
session->data_fd = new_fd;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* peer didn't send PASV command */
|
|
|
|
ftp_send_response(session, 503, "Bad sequence of commands\r\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! connect to peer for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*
|
|
|
|
* @returns -1 for failure
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ftp_session_connect(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2014-11-25 09:30:55 +01:00
|
|
|
/* clear PORT flag */
|
|
|
|
session->flags &= ~SESSION_PORT;
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-25 09:30:55 +01:00
|
|
|
/* create a new socket */
|
|
|
|
session->data_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if(session->data_fd < 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "socket: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
2014-11-25 09:30:55 +01:00
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
/* set socket options */
|
2016-01-15 23:45:10 +01:00
|
|
|
rc = ftp_set_socket_options(session->data_fd);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(session->data_fd, false);
|
2016-01-15 23:45:10 +01:00
|
|
|
session->data_fd = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
2016-01-12 23:24:01 +01:00
|
|
|
|
2014-11-25 09:30:55 +01:00
|
|
|
/* connect to peer */
|
|
|
|
rc = connect(session->data_fd, (struct sockaddr*)&session->peer_addr,
|
|
|
|
sizeof(session->peer_addr));
|
|
|
|
if(rc != 0)
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "connect: %d %s\n" RESET, errno, strerror(errno));
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(session->data_fd, false);
|
2014-11-25 09:30:55 +01:00
|
|
|
session->data_fd = -1;
|
2014-11-23 23:39:00 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-11-25 09:30:55 +01:00
|
|
|
|
|
|
|
rc = ftp_set_socket_nonblocking(session->data_fd);
|
|
|
|
if(rc != 0)
|
|
|
|
return -1;
|
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "connected to %s:%u\n" RESET,
|
2014-11-25 09:30:55 +01:00
|
|
|
inet_ntoa(session->peer_addr.sin_addr),
|
|
|
|
ntohs(session->peer_addr.sin_port));
|
|
|
|
|
|
|
|
return 0;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! read command for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ftp_session_read_command(ftp_session_t *session)
|
|
|
|
{
|
2014-11-25 09:30:55 +01:00
|
|
|
static char buffer[CMD_BUFFERSIZE];
|
2014-11-23 23:39:00 +01:00
|
|
|
ssize_t rc;
|
|
|
|
char *args;
|
|
|
|
ftp_command_t key, *command;
|
|
|
|
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
|
|
|
|
/* retrieve command */
|
|
|
|
rc = recv(session->cmd_fd, buffer, sizeof(buffer), 0);
|
|
|
|
if(rc < 0)
|
|
|
|
{
|
|
|
|
/* error retrieving command */
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "recv: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_session_close_cmd(session);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(rc == 0)
|
|
|
|
{
|
|
|
|
/* peer closed connection */
|
|
|
|
ftp_session_close_cmd(session);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* split into command and arguments */
|
|
|
|
/* TODO: support partial transfers */
|
|
|
|
buffer[sizeof(buffer)-1] = 0;
|
|
|
|
|
|
|
|
args = buffer;
|
|
|
|
while(*args && *args != '\r' && *args != '\n')
|
|
|
|
++args;
|
|
|
|
*args = 0;
|
2016-01-16 00:47:18 +01:00
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
args = buffer;
|
|
|
|
while(*args && !isspace((int)*args))
|
|
|
|
++args;
|
|
|
|
if(*args)
|
|
|
|
*args++ = 0;
|
|
|
|
|
|
|
|
/* look up the command */
|
|
|
|
key.name = buffer;
|
|
|
|
command = bsearch(&key, ftp_commands,
|
|
|
|
num_ftp_commands, sizeof(ftp_command_t),
|
|
|
|
ftp_command_cmp);
|
|
|
|
|
|
|
|
/* execute the command */
|
|
|
|
if(command == NULL)
|
2014-11-24 06:24:57 +01:00
|
|
|
{
|
|
|
|
ftp_send_response(session, 502, "invalid command -> %s %s\r\n",
|
|
|
|
key.name, args);
|
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
else
|
2014-11-24 07:16:21 +01:00
|
|
|
{
|
|
|
|
/* clear RENAME flag for all commands except RNTO */
|
|
|
|
if(strcasecmp(command->name, "RNTO") != 0)
|
|
|
|
session->flags &= ~SESSION_RENAME;
|
2014-11-23 23:39:00 +01:00
|
|
|
command->handler(session, args);
|
2014-11-24 07:16:21 +01:00
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! poll sockets for ftp session
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
*
|
|
|
|
* @returns next session
|
|
|
|
*/
|
|
|
|
static ftp_session_t*
|
|
|
|
ftp_session_poll(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct pollfd pollinfo;
|
|
|
|
|
|
|
|
switch(session->state)
|
|
|
|
{
|
|
|
|
case COMMAND_STATE:
|
|
|
|
/* we are waiting to read a command */
|
|
|
|
pollinfo.fd = session->cmd_fd;
|
|
|
|
pollinfo.events = POLLIN;
|
|
|
|
pollinfo.revents = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DATA_CONNECT_STATE:
|
|
|
|
/* we are waiting for a PASV connection */
|
|
|
|
pollinfo.fd = session->pasv_fd;
|
|
|
|
pollinfo.events = POLLIN;
|
|
|
|
pollinfo.revents = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DATA_TRANSFER_STATE:
|
|
|
|
/* we need to transfer data */
|
|
|
|
pollinfo.fd = session->data_fd;
|
|
|
|
if(session->flags & SESSION_RECV)
|
|
|
|
pollinfo.events = POLLIN;
|
|
|
|
else
|
|
|
|
pollinfo.events = POLLOUT;
|
|
|
|
pollinfo.revents = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* poll the selected socket */
|
|
|
|
rc = poll(&pollinfo, 1, 0);
|
|
|
|
if(rc < 0)
|
2016-01-15 23:45:10 +01:00
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "poll: %d %s\n" RESET, errno, strerror(errno));
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_close_cmd(session);
|
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
else if(rc > 0)
|
|
|
|
{
|
|
|
|
if(pollinfo.revents != 0)
|
|
|
|
{
|
|
|
|
/* handle event */
|
|
|
|
switch(session->state)
|
|
|
|
{
|
|
|
|
case COMMAND_STATE:
|
|
|
|
if(pollinfo.revents & POLL_UNKNOWN)
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "cmd_fd: revents=0x%08X\n" RESET, pollinfo.revents);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
/* we need to read a new command */
|
|
|
|
if(pollinfo.revents & (POLLERR|POLLHUP))
|
|
|
|
ftp_session_close_cmd(session);
|
|
|
|
else if(pollinfo.revents & POLLIN)
|
|
|
|
ftp_session_read_command(session);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DATA_CONNECT_STATE:
|
|
|
|
if(pollinfo.revents & POLL_UNKNOWN)
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "pasv_fd: revents=0x%08X\n" RESET, pollinfo.revents);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
/* we need to accept the PASV connection */
|
|
|
|
if(pollinfo.revents & (POLLERR|POLLHUP))
|
|
|
|
{
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_send_response(session, 426, "Data connection failed\r\n");
|
|
|
|
}
|
|
|
|
else if(pollinfo.revents & POLLIN)
|
|
|
|
{
|
|
|
|
if(ftp_session_accept(session) != 0)
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DATA_TRANSFER_STATE:
|
|
|
|
if(pollinfo.revents & POLL_UNKNOWN)
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "data_fd: revents=0x%08X\n" RESET, pollinfo.revents);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
/* we need to transfer data */
|
|
|
|
if(pollinfo.revents & (POLLERR|POLLHUP))
|
|
|
|
{
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_send_response(session, 426, "Data connection failed\r\n");
|
|
|
|
}
|
|
|
|
else if(pollinfo.revents & (POLLIN|POLLOUT))
|
2014-11-25 09:30:55 +01:00
|
|
|
ftp_session_transfer(session);
|
2014-11-23 23:39:00 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* still connected to peer; return next session */
|
|
|
|
if(session->cmd_fd >= 0)
|
|
|
|
return session->next;
|
|
|
|
|
|
|
|
/* disconnected from peer; destroy it and return next session */
|
|
|
|
return ftp_session_destroy(session);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! initialize ftp subsystem */
|
|
|
|
int
|
|
|
|
ftp_init(void)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
#ifdef _3DS
|
|
|
|
Result ret;
|
|
|
|
|
2015-08-19 20:25:34 +02:00
|
|
|
#if ENABLE_LOGGING
|
2015-01-08 06:30:22 +01:00
|
|
|
/* open log file */
|
|
|
|
FILE *fp = freopen("/ftbrony.log", "wb", stderr);
|
|
|
|
if(fp == NULL)
|
|
|
|
{
|
|
|
|
console_print(RED "freopen: 0x%08X\n" RESET, errno);
|
|
|
|
goto stderr_fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* truncate log file */
|
|
|
|
if(ftruncate(fileno(fp), 0) != 0)
|
|
|
|
{
|
|
|
|
console_print(RED "ftruncate: 0x%08X\n" RESET, errno);
|
|
|
|
goto ftruncate_fail;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
/* allocate buffer for SOC service */
|
2016-01-12 23:24:01 +01:00
|
|
|
SOCU_buffer = (u32*)memalign(SOCU_ALIGN, SOCU_BUFFERSIZE);
|
|
|
|
if(SOCU_buffer == NULL)
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "memalign: failed to allocate\n" RESET);
|
|
|
|
goto memalign_fail;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize SOC service */
|
2016-01-12 23:24:01 +01:00
|
|
|
ret = socInit(SOCU_buffer, SOCU_BUFFERSIZE);
|
2014-11-23 23:39:00 +01:00
|
|
|
if(ret != 0)
|
|
|
|
{
|
2016-01-12 23:24:01 +01:00
|
|
|
console_print(RED "socInit: 0x%08X\n" RESET, (unsigned int)ret);
|
2015-01-08 06:26:01 +01:00
|
|
|
goto soc_fail;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* allocate socket to listen for clients */
|
|
|
|
listenfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if(listenfd < 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "socket: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_exit();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get address to listen on */
|
|
|
|
serv_addr.sin_family = AF_INET;
|
|
|
|
#ifdef _3DS
|
|
|
|
serv_addr.sin_addr.s_addr = gethostid();
|
|
|
|
serv_addr.sin_port = htons(LISTEN_PORT);
|
|
|
|
#else
|
|
|
|
serv_addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
serv_addr.sin_port = htons(LISTEN_PORT);
|
2016-01-12 23:24:01 +01:00
|
|
|
#endif
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
/* reuse address */
|
|
|
|
{
|
|
|
|
int yes = 1;
|
|
|
|
rc = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "setsockopt: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_exit();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bind socket to listen address */
|
|
|
|
rc = bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "bind: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_exit();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* listen on socket */
|
|
|
|
rc = listen(listenfd, 5);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "listen: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_exit();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* print server address */
|
|
|
|
#ifdef _3DS
|
2015-01-08 06:26:01 +01:00
|
|
|
console_set_status("\n" GREEN STATUS_STRING " "
|
|
|
|
YELLOW "IP:" CYAN "%s "
|
|
|
|
YELLOW "Port:" CYAN "%u"
|
|
|
|
RESET,
|
2014-11-23 23:39:00 +01:00
|
|
|
inet_ntoa(serv_addr.sin_addr),
|
|
|
|
ntohs(serv_addr.sin_port));
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
char hostname[128];
|
|
|
|
socklen_t addrlen = sizeof(serv_addr);
|
|
|
|
rc = getsockname(listenfd, (struct sockaddr*)&serv_addr, &addrlen);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "getsockname: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_exit();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = gethostname(hostname, sizeof(hostname));
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "gethostname: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_exit();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_set_status(GREEN STATUS_STRING " "
|
|
|
|
YELLOW "IP:" CYAN "%s "
|
|
|
|
YELLOW "Port:" CYAN "%u"
|
|
|
|
RESET,
|
2014-11-23 23:39:00 +01:00
|
|
|
hostname,
|
|
|
|
ntohs(serv_addr.sin_port));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
2015-01-08 06:26:01 +01:00
|
|
|
|
|
|
|
#ifdef _3DS
|
|
|
|
soc_fail:
|
2016-01-12 23:24:01 +01:00
|
|
|
free(SOCU_buffer);
|
2015-01-08 06:26:01 +01:00
|
|
|
|
|
|
|
memalign_fail:
|
2015-01-08 06:30:22 +01:00
|
|
|
#ifdef ENABLE_LOGGING
|
|
|
|
ftruncate_fail:
|
|
|
|
if(fclose(stderr) != 0)
|
|
|
|
console_print(RED "fclose: 0x%08X\n" RESET, errno);
|
|
|
|
|
|
|
|
stderr_fail:
|
2015-08-19 20:25:34 +02:00
|
|
|
#endif
|
2015-01-08 06:26:01 +01:00
|
|
|
return -1;
|
2015-12-30 03:20:26 +01:00
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
#endif
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! deinitialize ftp subsystem */
|
|
|
|
void
|
|
|
|
ftp_exit(void)
|
|
|
|
{
|
|
|
|
#ifdef _3DS
|
|
|
|
Result ret;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* clean up all sessions */
|
|
|
|
while(sessions != NULL)
|
|
|
|
ftp_session_destroy(sessions);
|
|
|
|
|
|
|
|
/* stop listening for new clients */
|
|
|
|
if(listenfd >= 0)
|
2016-01-16 00:28:59 +01:00
|
|
|
ftp_closesocket(listenfd, false);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
#ifdef _3DS
|
|
|
|
/* deinitialize SOC service */
|
2015-12-30 03:20:26 +01:00
|
|
|
ret = socExit();
|
2014-11-23 23:39:00 +01:00
|
|
|
if(ret != 0)
|
2016-01-12 23:24:01 +01:00
|
|
|
console_print(RED "socExit: 0x%08X\n" RESET, (unsigned int)ret);
|
|
|
|
free(SOCU_buffer);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2015-01-08 06:30:22 +01:00
|
|
|
#ifdef ENABLE_LOGGING
|
|
|
|
/* close log file */
|
|
|
|
if(fclose(stderr) != 0)
|
|
|
|
console_print(RED "fclose: 0x%08X\n" RESET, errno);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ftp_loop(void)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct pollfd pollinfo;
|
|
|
|
ftp_session_t *session;
|
|
|
|
|
|
|
|
pollinfo.fd = listenfd;
|
|
|
|
pollinfo.events = POLLIN;
|
|
|
|
pollinfo.revents = 0;
|
|
|
|
|
|
|
|
rc = poll(&pollinfo, 1, 0);
|
|
|
|
if(rc < 0)
|
2015-08-19 20:25:34 +02:00
|
|
|
{
|
|
|
|
console_print(RED "poll: %d %s\n" RESET, errno, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
else if(rc > 0)
|
|
|
|
{
|
|
|
|
if(pollinfo.revents & POLLIN)
|
|
|
|
{
|
|
|
|
ftp_session_new(listenfd);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "listenfd: revents=0x%08X\n" RESET, pollinfo.revents);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
session = sessions;
|
|
|
|
while(session != NULL)
|
|
|
|
session = ftp_session_poll(session);
|
|
|
|
|
|
|
|
#ifdef _3DS
|
|
|
|
hidScanInput();
|
|
|
|
if(hidKeysDown() & KEY_B)
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cd_up(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
char *slash = NULL, *p;
|
|
|
|
|
|
|
|
for(p = session->cwd; *p; ++p)
|
|
|
|
{
|
|
|
|
if(*p == '/')
|
|
|
|
slash = p;
|
|
|
|
}
|
|
|
|
*slash = 0;
|
|
|
|
if(strlen(session->cwd) == 0)
|
|
|
|
strcat(session->cwd, "/");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
validate_path(const char *args)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
/* make sure no path components are '..' */
|
|
|
|
p = args;
|
|
|
|
while((p = strstr(p, "/..")) != NULL)
|
|
|
|
{
|
|
|
|
if(p[3] == 0 || p[3] == '/')
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure there are no '//' */
|
|
|
|
if(strstr(args, "//") != NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-25 08:42:25 +01:00
|
|
|
static int
|
2014-11-23 23:39:00 +01:00
|
|
|
build_path(ftp_session_t *session,
|
|
|
|
const char *args)
|
|
|
|
{
|
2014-11-25 08:42:25 +01:00
|
|
|
int rc;
|
|
|
|
char *p;
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
memset(session->buffer, 0, sizeof(session->buffer));
|
|
|
|
|
2014-11-25 08:42:25 +01:00
|
|
|
if(validate_path(args) != 0)
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
if(args[0] == '/')
|
|
|
|
{
|
2014-11-25 08:42:25 +01:00
|
|
|
if(strlen(args) > sizeof(session->buffer)-1)
|
|
|
|
{
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
return -1;
|
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
strncpy(session->buffer, args, sizeof(session->buffer));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-11-25 08:24:24 +01:00
|
|
|
if(strcmp(session->cwd, "/") == 0)
|
2014-11-25 08:42:25 +01:00
|
|
|
rc = snprintf(session->buffer, sizeof(session->buffer), "/%s",
|
|
|
|
args);
|
2014-11-25 08:24:24 +01:00
|
|
|
else
|
2014-11-25 08:42:25 +01:00
|
|
|
rc = snprintf(session->buffer, sizeof(session->buffer), "%s/%s",
|
|
|
|
session->cwd, args);
|
|
|
|
|
|
|
|
if(rc >= sizeof(session->buffer))
|
|
|
|
{
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
return -1;
|
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
2014-11-25 08:42:25 +01:00
|
|
|
|
|
|
|
p = session->buffer + strlen(session->buffer);
|
|
|
|
while(p > session->buffer && *--p == '/')
|
|
|
|
*p = 0;
|
|
|
|
|
|
|
|
if(strlen(session->buffer) == 0)
|
|
|
|
strcpy(session->buffer, "/");
|
|
|
|
|
|
|
|
return 0;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
2014-11-25 09:30:55 +01:00
|
|
|
static int
|
2014-11-23 23:39:00 +01:00
|
|
|
list_transfer(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
|
|
|
|
if(session->bufferpos == session->buffersize)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
struct dirent *dent = readdir(session->dp);
|
|
|
|
if(dent == NULL)
|
|
|
|
{
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_send_response(session, 226, "OK\r\n");
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
|
2014-11-25 09:30:55 +01:00
|
|
|
return 0;
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2015-08-19 20:25:34 +02:00
|
|
|
if(strcmp(session->cwd, "/") == 0)
|
|
|
|
snprintf(session->buffer, sizeof(session->buffer),
|
|
|
|
"/%s", dent->d_name);
|
|
|
|
else
|
|
|
|
snprintf(session->buffer, sizeof(session->buffer),
|
|
|
|
"%s/%s", session->cwd, dent->d_name);
|
2016-01-16 00:47:18 +01:00
|
|
|
if(session->flags & SESSION_NLST)
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
2016-01-16 00:47:18 +01:00
|
|
|
session->buffersize =
|
|
|
|
sprintf(session->buffer, "%s\r\n", dent->d_name);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
2016-01-16 00:47:18 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = lstat(session->buffer, &st);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
console_print(RED "stat '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno));
|
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
|
|
|
ftp_send_response(session, 550, "unavailable\r\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-16 00:47:18 +01:00
|
|
|
session->buffersize =
|
|
|
|
sprintf(session->buffer,
|
|
|
|
"%crwxrwxrwx 1 3DS 3DS %llu Jan 1 1970 %s\r\n",
|
|
|
|
S_ISDIR(st.st_mode) ? 'd' :
|
|
|
|
S_ISLNK(st.st_mode) ? 'l' : '-',
|
|
|
|
(unsigned long long)st.st_size,
|
|
|
|
dent->d_name);
|
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
session->bufferpos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = send(session->data_fd, session->buffer + session->bufferpos,
|
|
|
|
session->buffersize - session->bufferpos, 0);
|
|
|
|
if(rc <= 0)
|
|
|
|
{
|
|
|
|
if(rc < 0)
|
2014-11-25 09:30:55 +01:00
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
if(errno == EWOULDBLOCK)
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "send: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-25 09:30:55 +01:00
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
else
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "send: %d %s\n" RESET, ECONNRESET, strerror(ECONNRESET));
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_send_response(session, 426, "Connection broken during transfer\r\n");
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
session->bufferpos += rc;
|
2014-11-25 09:30:55 +01:00
|
|
|
return 0;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
2014-11-25 09:30:55 +01:00
|
|
|
static int
|
2014-11-23 23:39:00 +01:00
|
|
|
retrieve_transfer(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
|
|
|
|
if(session->bufferpos == session->buffersize)
|
|
|
|
{
|
|
|
|
rc = ftp_session_read_file(session);
|
|
|
|
if(rc <= 0)
|
|
|
|
{
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
if(rc < 0)
|
|
|
|
ftp_send_response(session, 451, "Failed to read file\r\n");
|
|
|
|
else
|
|
|
|
ftp_send_response(session, 226, "OK\r\n");
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
session->bufferpos = 0;
|
|
|
|
session->buffersize = rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = send(session->data_fd, session->buffer + session->bufferpos,
|
|
|
|
session->buffersize - session->bufferpos, 0);
|
|
|
|
if(rc <= 0)
|
|
|
|
{
|
|
|
|
if(rc < 0)
|
2014-11-25 09:30:55 +01:00
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
if(errno == EWOULDBLOCK)
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "send: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-25 09:30:55 +01:00
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
else
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "send: %d %s\n" RESET, ECONNRESET, strerror(ECONNRESET));
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_send_response(session, 426, "Connection broken during transfer\r\n");
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
session->bufferpos += rc;
|
2014-11-25 09:30:55 +01:00
|
|
|
return 0;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
2014-11-25 09:30:55 +01:00
|
|
|
static int
|
2014-11-23 23:39:00 +01:00
|
|
|
store_transfer(ftp_session_t *session)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
|
|
|
|
if(session->bufferpos == session->buffersize)
|
|
|
|
{
|
|
|
|
rc = recv(session->data_fd, session->buffer, sizeof(session->buffer), 0);
|
|
|
|
if(rc <= 0)
|
|
|
|
{
|
|
|
|
if(rc < 0)
|
2014-11-25 09:30:55 +01:00
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
if(errno == EWOULDBLOCK)
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "recv: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-25 09:30:55 +01:00
|
|
|
}
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
if(rc == 0)
|
|
|
|
ftp_send_response(session, 226, "OK\r\n");
|
|
|
|
else
|
|
|
|
ftp_send_response(session, 426, "Connection broken during transfer\r\n");
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
session->bufferpos = 0;
|
|
|
|
session->buffersize = rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = ftp_session_write_file(session);
|
|
|
|
if(rc <= 0)
|
|
|
|
{
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_send_response(session, 451, "Failed to write file\r\n");
|
2014-11-25 09:30:55 +01:00
|
|
|
return -1;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
session->bufferpos += rc;
|
2014-11-25 09:30:55 +01:00
|
|
|
return 0;
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
2016-01-16 00:28:59 +01:00
|
|
|
/*! ftp_xfer_file mode */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
XFER_FILE_RETR, /*!< Retrieve a file */
|
|
|
|
XFER_FILE_STOR, /*!< Store a file */
|
|
|
|
XFER_FILE_APPE, /*!< Append a file */
|
|
|
|
} xfer_file_mode_t;
|
|
|
|
|
|
|
|
/*! Transfer a file
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
* @param[in] args ftp arguments
|
|
|
|
* @param[in] mode transfer mode
|
|
|
|
*
|
|
|
|
* @returns failure
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ftp_xfer_file(ftp_session_t *session,
|
|
|
|
const char *args,
|
|
|
|
xfer_file_mode_t mode)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if(build_path(session, args) != 0)
|
|
|
|
{
|
|
|
|
rc = errno;
|
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
|
|
|
return ftp_send_response(session, 553, "%s\r\n", strerror(rc));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(mode == XFER_FILE_RETR)
|
|
|
|
rc = ftp_session_open_file_read(session);
|
|
|
|
else
|
|
|
|
rc = ftp_session_open_file_write(session, mode == XFER_FILE_APPE);
|
|
|
|
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
|
|
|
return ftp_send_response(session, 450, "failed to open file\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(session->flags & SESSION_PORT)
|
|
|
|
{
|
|
|
|
ftp_session_set_state(session, DATA_TRANSFER_STATE, CLOSE_PASV);
|
|
|
|
rc = ftp_session_connect(session);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
|
|
|
return ftp_send_response(session, 425, "can't open data connection\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
session->flags &= ~(SESSION_RECV|SESSION_SEND);
|
|
|
|
|
|
|
|
if(mode == XFER_FILE_RETR)
|
|
|
|
{
|
|
|
|
session->flags |= SESSION_SEND;
|
|
|
|
session->transfer = retrieve_transfer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
session->flags |= SESSION_RECV;
|
|
|
|
session->transfer = store_transfer;
|
|
|
|
}
|
|
|
|
|
|
|
|
session->bufferpos = 0;
|
|
|
|
session->buffersize = 0;
|
|
|
|
|
|
|
|
return ftp_send_response(session, 150, "Ready\r\n");
|
|
|
|
}
|
|
|
|
else if(session->flags & SESSION_PASV)
|
|
|
|
{
|
|
|
|
session->flags &= ~(SESSION_RECV|SESSION_SEND);
|
|
|
|
|
|
|
|
if(mode == XFER_FILE_RETR)
|
|
|
|
{
|
|
|
|
session->flags |= SESSION_SEND;
|
|
|
|
session->transfer = retrieve_transfer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
session->flags |= SESSION_RECV;
|
|
|
|
session->transfer = store_transfer;
|
|
|
|
}
|
|
|
|
|
|
|
|
session->bufferpos = 0;
|
|
|
|
session->buffersize = 0;
|
|
|
|
|
|
|
|
ftp_session_set_state(session, DATA_CONNECT_STATE, CLOSE_DATA);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
|
|
|
return ftp_send_response(session, 503, "Bad sequence of commands\r\n");
|
|
|
|
}
|
|
|
|
|
2016-01-16 00:47:18 +01:00
|
|
|
/*! ftp_xfer_dir mode */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
XFER_DIR_LIST, /*!< Long list */
|
|
|
|
XFER_DIR_NLST, /*!< Short list */
|
|
|
|
} xfer_dir_mode_t;
|
|
|
|
|
|
|
|
/*! Transfer a directory
|
|
|
|
*
|
|
|
|
* @param[in] session ftp session
|
|
|
|
* @param[in] args ftp arguments
|
|
|
|
* @param[in] mode transfer mode
|
|
|
|
*
|
|
|
|
* @returns failure
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ftp_xfer_dir(ftp_session_t *session,
|
|
|
|
const char *args,
|
|
|
|
xfer_dir_mode_t mode)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
|
|
|
|
|
|
|
if(ftp_session_open_cwd(session) != 0)
|
|
|
|
{
|
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
|
|
|
return ftp_send_response(session, 550, "unavailable\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(session->flags & SESSION_PORT)
|
|
|
|
{
|
|
|
|
ftp_session_set_state(session, DATA_TRANSFER_STATE, CLOSE_PASV);
|
|
|
|
rc = ftp_session_connect(session);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
|
|
|
return ftp_send_response(session, 425, "can't open data connection\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
session->flags &= ~(SESSION_RECV|SESSION_SEND);
|
|
|
|
session->flags |= SESSION_SEND;
|
|
|
|
|
|
|
|
if(mode == XFER_DIR_LIST)
|
|
|
|
session->flags &= ~SESSION_NLST;
|
|
|
|
else
|
|
|
|
session->flags |= SESSION_NLST;
|
|
|
|
|
|
|
|
session->transfer = list_transfer;
|
|
|
|
session->bufferpos = 0;
|
|
|
|
session->buffersize = 0;
|
|
|
|
|
|
|
|
return ftp_send_response(session, 150, "Ready\r\n");
|
|
|
|
}
|
|
|
|
else if(session->flags & SESSION_PASV)
|
|
|
|
{
|
|
|
|
session->flags &= ~(SESSION_RECV|SESSION_SEND);
|
|
|
|
session->flags |= SESSION_SEND;
|
|
|
|
|
|
|
|
if(mode == XFER_DIR_LIST)
|
|
|
|
session->flags &= ~SESSION_NLST;
|
|
|
|
else
|
|
|
|
session->flags |= SESSION_NLST;
|
|
|
|
|
|
|
|
session->transfer = list_transfer;
|
|
|
|
session->bufferpos = 0;
|
|
|
|
session->buffersize = 0;
|
|
|
|
|
|
|
|
ftp_session_set_state(session, DATA_CONNECT_STATE, CLOSE_DATA);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
|
|
|
return ftp_send_response(session, 503, "Bad sequence of commands\r\n");
|
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
|
* *
|
|
|
|
* F T P C O M M A N D S *
|
|
|
|
* *
|
|
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
|
|
|
|
FTP_DECLARE(ALLO)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
return ftp_send_response(session, 202, "superfluous command\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(APPE)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-16 00:28:59 +01:00
|
|
|
return ftp_xfer_file(session, args, XFER_FILE_APPE);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(CDUP)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
cd_up(session);
|
|
|
|
|
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(CWD)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
if(strcmp(args, "..") == 0)
|
|
|
|
{
|
|
|
|
cd_up(session);
|
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
}
|
|
|
|
|
2014-11-25 08:42:25 +01:00
|
|
|
if(build_path(session, args) != 0)
|
|
|
|
return ftp_send_response(session, 553, "%s\r\n", strerror(errno));
|
2016-01-16 00:47:18 +01:00
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = stat(session->buffer, &st);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "stat '%s': %d %s\n" RESET, session->buffer, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return ftp_send_response(session, 550, "unavailable\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!S_ISDIR(st.st_mode))
|
|
|
|
return ftp_send_response(session, 553, "not a directory\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(session->cwd, session->buffer, sizeof(session->cwd));
|
2016-01-16 00:47:18 +01:00
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(DELE)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
int rc;
|
2014-11-24 06:54:12 +01:00
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-25 08:42:25 +01:00
|
|
|
if(build_path(session, args) != 0)
|
|
|
|
return ftp_send_response(session, 553, "%s\r\n", strerror(errno));
|
2014-11-24 06:54:12 +01:00
|
|
|
|
|
|
|
rc = unlink(session->buffer);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "unlink: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-24 06:54:12 +01:00
|
|
|
return ftp_send_response(session, 550, "failed to delete file\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ftp_send_response(session, 250, "OK\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(FEAT)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2015-08-19 20:25:34 +02:00
|
|
|
return ftp_send_response(session, 211, "\r\n UTF8\r\n211 End\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(LIST)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-16 00:47:18 +01:00
|
|
|
return ftp_xfer_dir(session, args, XFER_DIR_LIST);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(MKD)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
int rc;
|
2014-11-24 06:48:43 +01:00
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-25 08:42:25 +01:00
|
|
|
if(build_path(session, args) != 0)
|
|
|
|
return ftp_send_response(session, 553, "%s\r\n", strerror(errno));
|
2014-11-24 06:48:43 +01:00
|
|
|
|
|
|
|
rc = mkdir(session->buffer, 0755);
|
2016-01-15 23:45:10 +01:00
|
|
|
if(rc != 0 && errno != EEXIST)
|
2014-11-24 06:48:43 +01:00
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "mkdir: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-24 06:48:43 +01:00
|
|
|
return ftp_send_response(session, 550, "failed to create directory\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ftp_send_response(session, 250, "OK\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(MODE)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
if(strcasecmp(args, "S") == 0)
|
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
|
|
|
|
return ftp_send_response(session, 504, "unavailable\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(NLST)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-16 00:47:18 +01:00
|
|
|
return ftp_xfer_dir(session, args, XFER_DIR_NLST);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(NOOP)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
}
|
|
|
|
|
2015-08-19 20:25:34 +02:00
|
|
|
FTP_DECLARE(OPTS)
|
|
|
|
{
|
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2015-08-19 20:25:34 +02:00
|
|
|
|
|
|
|
if(strcasecmp(args, "UTF8") == 0
|
|
|
|
|| strcasecmp(args, "UTF8 ON") == 0
|
|
|
|
|| strcasecmp(args, "UTF8 NLST") == 0)
|
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
|
|
|
|
return ftp_send_response(session, 504, "invalid argument\r\n");
|
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
FTP_DECLARE(PASS)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-24 06:28:54 +01:00
|
|
|
return ftp_send_response(session, 230, "OK\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(PASV)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char buffer[INET_ADDRSTRLEN + 10];
|
|
|
|
char *p;
|
|
|
|
in_port_t port;
|
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
session->flags &= ~(SESSION_PASV|SESSION_PORT);
|
|
|
|
|
|
|
|
session->pasv_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if(session->pasv_fd < 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "socket: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
return ftp_send_response(session, 451, "\r\n");
|
|
|
|
}
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
rc = ftp_set_socket_options(session->pasv_fd);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
|
|
|
ftp_session_close_pasv(session);
|
|
|
|
return ftp_send_response(session, 451, "\r\n");
|
|
|
|
}
|
2016-01-12 23:24:01 +01:00
|
|
|
|
2014-11-25 08:22:09 +01:00
|
|
|
session->pasv_addr.sin_port = htons(next_data_port());
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
#ifdef _3DS
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "binding to %s:%u\n" RESET,
|
2014-11-23 23:39:00 +01:00
|
|
|
inet_ntoa(session->pasv_addr.sin_addr),
|
|
|
|
ntohs(session->pasv_addr.sin_port));
|
|
|
|
#endif
|
|
|
|
rc = bind(session->pasv_fd, (struct sockaddr*)&session->pasv_addr,
|
|
|
|
sizeof(session->pasv_addr));
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "bind: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_session_close_pasv(session);
|
|
|
|
return ftp_send_response(session, 451, "\r\n");
|
|
|
|
}
|
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
rc = listen(session->pasv_fd, 1);
|
2014-11-23 23:39:00 +01:00
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "listen: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_session_close_pasv(session);
|
|
|
|
return ftp_send_response(session, 451, "\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef _3DS
|
|
|
|
{
|
|
|
|
socklen_t addrlen = sizeof(session->pasv_addr);
|
|
|
|
rc = getsockname(session->pasv_fd, (struct sockaddr*)&session->pasv_addr,
|
|
|
|
&addrlen);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
console_print(RED "getsockname: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
ftp_session_close_pasv(session);
|
|
|
|
return ftp_send_response(session, 451, "\r\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(YELLOW "listening on %s:%u\n" RESET,
|
2014-11-23 23:39:00 +01:00
|
|
|
inet_ntoa(session->pasv_addr.sin_addr),
|
|
|
|
ntohs(session->pasv_addr.sin_port));
|
|
|
|
|
|
|
|
session->flags |= SESSION_PASV;
|
|
|
|
|
|
|
|
port = ntohs(session->pasv_addr.sin_port);
|
|
|
|
strcpy(buffer, inet_ntoa(session->pasv_addr.sin_addr));
|
|
|
|
sprintf(buffer+strlen(buffer), ",%u,%u",
|
|
|
|
port >> 8, port & 0xFF);
|
|
|
|
for(p = buffer; *p; ++p)
|
|
|
|
{
|
|
|
|
if(*p == '.')
|
|
|
|
*p = ',';
|
|
|
|
}
|
|
|
|
|
|
|
|
return ftp_send_response(session, 227, "%s\r\n", buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(PORT)
|
|
|
|
{
|
|
|
|
char *addrstr, *p, *portstr;
|
|
|
|
int commas = 0, rc;
|
|
|
|
short port = 0;
|
|
|
|
unsigned long val;
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, CLOSE_PASV | CLOSE_DATA);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
session->flags &= ~(SESSION_PASV|SESSION_PORT);
|
|
|
|
|
|
|
|
addrstr = strdup(args);
|
|
|
|
if(addrstr == NULL)
|
|
|
|
return ftp_send_response(session, 425, "%s\r\n", strerror(ENOMEM));
|
|
|
|
|
|
|
|
for(p = addrstr; *p; ++p)
|
|
|
|
{
|
|
|
|
if(*p == ',')
|
|
|
|
{
|
|
|
|
if(commas != 3)
|
|
|
|
*p = '.';
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*p = 0;
|
|
|
|
portstr = p+1;
|
|
|
|
}
|
|
|
|
++commas;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(commas != 5)
|
|
|
|
{
|
|
|
|
free(addrstr);
|
|
|
|
return ftp_send_response(session, 501, "%s\r\n", strerror(EINVAL));
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = inet_aton(addrstr, &addr.sin_addr);
|
|
|
|
if(rc == 0)
|
|
|
|
{
|
|
|
|
free(addrstr);
|
|
|
|
return ftp_send_response(session, 501, "%s\r\n", strerror(EINVAL));
|
|
|
|
}
|
|
|
|
|
|
|
|
val = 0;
|
|
|
|
port = 0;
|
|
|
|
for(p = portstr; *p; ++p)
|
|
|
|
{
|
|
|
|
if(!isdigit((int)*p))
|
|
|
|
{
|
|
|
|
if(p == portstr || *p != '.' || val > 0xFF)
|
|
|
|
{
|
|
|
|
free(addrstr);
|
|
|
|
return ftp_send_response(session, 501, "%s\r\n", strerror(EINVAL));
|
|
|
|
}
|
|
|
|
port <<= 8;
|
|
|
|
port += val;
|
|
|
|
val = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
val *= 10;
|
|
|
|
val += *p - '0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(val > 0xFF || port > 0xFF)
|
|
|
|
{
|
|
|
|
free(addrstr);
|
|
|
|
return ftp_send_response(session, 501, "%s\r\n", strerror(EINVAL));
|
|
|
|
}
|
|
|
|
port <<= 8;
|
|
|
|
port += val;
|
|
|
|
|
|
|
|
addr.sin_family = AF_INET;
|
|
|
|
addr.sin_port = htons(port);
|
|
|
|
|
|
|
|
free(addrstr);
|
|
|
|
|
|
|
|
memcpy(&session->peer_addr, &addr, sizeof(addr));
|
|
|
|
|
|
|
|
session->flags |= SESSION_PORT;
|
|
|
|
|
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(PWD)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-24 04:15:59 +01:00
|
|
|
return ftp_send_response(session, 257, "\"%s\"\r\n", session->cwd);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(QUIT)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
ftp_send_response(session, 221, "disconnecting\r\n");
|
|
|
|
ftp_session_close_cmd(session);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(REST)
|
|
|
|
{
|
|
|
|
/* TODO */
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
return ftp_send_response(session, 502, "unavailable\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(RETR)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-16 00:28:59 +01:00
|
|
|
return ftp_xfer_file(session, args, XFER_FILE_RETR);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(RMD)
|
|
|
|
{
|
2015-08-19 20:25:34 +02:00
|
|
|
int rc;
|
2014-11-24 07:01:42 +01:00
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-25 08:42:25 +01:00
|
|
|
if(build_path(session, args) != 0)
|
|
|
|
return ftp_send_response(session, 553, "%s\r\n", strerror(errno));
|
2014-11-24 07:01:42 +01:00
|
|
|
|
|
|
|
rc = rmdir(session->buffer);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "rmdir: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-24 07:01:42 +01:00
|
|
|
return ftp_send_response(session, 550, "failed to delete directory\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ftp_send_response(session, 250, "OK\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(RNFR)
|
|
|
|
{
|
2014-11-24 07:16:21 +01:00
|
|
|
int rc;
|
|
|
|
struct stat st;
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-25 08:42:25 +01:00
|
|
|
if(build_path(session, args) != 0)
|
|
|
|
return ftp_send_response(session, 553, "%s\r\n", strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-24 07:16:21 +01:00
|
|
|
rc = lstat(session->buffer, &st);
|
|
|
|
if(rc != 0)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "lstat: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-24 07:16:21 +01:00
|
|
|
return ftp_send_response(session, 450, "no such file or directory\r\n");
|
|
|
|
}
|
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
session->flags |= SESSION_RENAME;
|
|
|
|
|
2014-11-24 07:16:21 +01:00
|
|
|
return ftp_send_response(session, 350, "OK\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(RNTO)
|
|
|
|
{
|
2016-01-12 23:24:01 +01:00
|
|
|
int rc;
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
if(!(session->flags & SESSION_RENAME))
|
|
|
|
return ftp_send_response(session, 503, "Bad sequence of commands\r\n");
|
|
|
|
|
|
|
|
session->flags &= ~SESSION_RENAME;
|
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
memcpy(session->tmp_buffer, session->buffer, XFER_BUFFERSIZE);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-25 08:42:25 +01:00
|
|
|
if(build_path(session, args) != 0)
|
|
|
|
return ftp_send_response(session, 554, "%s\r\n", strerror(errno));
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-12 23:24:01 +01:00
|
|
|
rc = rename(session->tmp_buffer, session->buffer);
|
2014-11-25 08:22:09 +01:00
|
|
|
if(rc != 0)
|
2014-11-24 07:16:21 +01:00
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(RED "rename: %d %s\n" RESET, errno, strerror(errno));
|
2014-11-24 07:16:21 +01:00
|
|
|
return ftp_send_response(session, 550, "failed to rename file/directory\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ftp_send_response(session, 250, "OK\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(STOR)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-16 00:28:59 +01:00
|
|
|
return ftp_xfer_file(session, args, XFER_FILE_STOR);
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|
|
|
|
|
2016-01-16 00:28:59 +01:00
|
|
|
|
2014-11-23 23:39:00 +01:00
|
|
|
FTP_DECLARE(STOU)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
return ftp_send_response(session, 502, "unavailable\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(STRU)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
if(strcasecmp(args, "F") == 0)
|
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
|
|
|
|
return ftp_send_response(session, 504, "unavailable\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(SYST)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
return ftp_send_response(session, 215, "UNIX Type: L8\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(TYPE)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
|
|
|
return ftp_send_response(session, 200, "OK\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
FTP_DECLARE(USER)
|
|
|
|
{
|
2015-01-08 06:26:01 +01:00
|
|
|
console_print(CYAN "%s %s\n" RESET, __func__, args ? args : "");
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2016-01-15 23:45:10 +01:00
|
|
|
ftp_session_set_state(session, COMMAND_STATE, 0);
|
2014-11-23 23:39:00 +01:00
|
|
|
|
2014-11-24 06:28:54 +01:00
|
|
|
return ftp_send_response(session, 230, "OK\r\n");
|
2014-11-23 23:39:00 +01:00
|
|
|
}
|