- FTP basics
This commit is contained in:
parent
612cb471d8
commit
cea600b3a4
213
apps/ftp_server.c
Normal file
213
apps/ftp_server.c
Normal file
@ -0,0 +1,213 @@
|
||||
#include "ftp_server.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <etherlib/prefab/conn_blocks/tcp_connblock.h>
|
||||
|
||||
// ----------
|
||||
|
||||
/* FTP response messages */
|
||||
|
||||
static char ftpGreeting[] = "220 Service ready for new user.\r\n";
|
||||
static char ftpNotImplemented[] = "502 Command not implemented.\r\n";
|
||||
static char ftpLoginOK[] = "230 User logged in, proceed.\r\n";
|
||||
static char ftpRootDirName[] = "257 \"/\" is the current directory\r\n";
|
||||
|
||||
/* Implemented commands */
|
||||
|
||||
static char *ftpImplementedCmdsStr[] = {
|
||||
// this is the smallest standard set of implemented FTP commands
|
||||
"USER",
|
||||
"QUIT",
|
||||
"PORT",
|
||||
"TYPE",
|
||||
"MODE",
|
||||
"STRU",
|
||||
"RETR",
|
||||
"STOR",
|
||||
"NOOP",
|
||||
|
||||
// ------ additional commands
|
||||
"PWD",
|
||||
"\0" // SENTRY!
|
||||
};
|
||||
|
||||
// THIS MUST REMAIN LOCAL!
|
||||
typedef enum { // ordering must be the same as in the string version above
|
||||
USER,
|
||||
QUIT,
|
||||
PORT,
|
||||
TYPE,
|
||||
MODE,
|
||||
STRU,
|
||||
RETR,
|
||||
STOR,
|
||||
NOOP,
|
||||
|
||||
// ------ additional commands
|
||||
PWD
|
||||
} Ftp_ImplementedCmds;
|
||||
|
||||
// -----------
|
||||
|
||||
static Ftps_State s; // TODO: dynamically allocate this later
|
||||
|
||||
// TODO deinit!
|
||||
// TODO ambiguous naming between server and client connections
|
||||
|
||||
int ftps_ctrl_recv(cbd d) {
|
||||
uint32_t event = FTPEVT_CTRL_RECVD;
|
||||
ETHLIB_OS_QUEUE_PUSH(s.eventQ, &event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ftps_accept_cb(cbd d) {
|
||||
s.connCtrl = d;
|
||||
uint32_t event = FTPEVT_NEW_CONNECTION;
|
||||
ETHLIB_OS_QUEUE_PUSH(s.eventQ, &event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ftps_thread(void *param);
|
||||
|
||||
void ftps_init() {
|
||||
osThreadAttr_t attr;
|
||||
memset(&attr, 0, sizeof(osThreadAttr_t));
|
||||
attr.stack_size = 2048;
|
||||
attr.priority = osPriorityNormal;
|
||||
attr.name = "ftp";
|
||||
osThreadNew(ftps_thread, NULL, &attr);
|
||||
}
|
||||
|
||||
bool ftps_is_command_implemented(char *cmd, uint32_t len, uint32_t *cmdCode) {
|
||||
char *iter = ftpImplementedCmdsStr[0];
|
||||
bool ret = false;
|
||||
uint8_t index = 0;
|
||||
while (*iter != '\0') {
|
||||
// compare passed command with the iterator value
|
||||
if (len == 0) {
|
||||
if (!strcmp(iter, cmd)) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!strncmp(iter, cmd, len)) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// select next command
|
||||
iter = ftpImplementedCmdsStr[++index];
|
||||
}
|
||||
|
||||
// if cmdCode pointer is provided, then write back the command code
|
||||
if (ret && (cmdCode != NULL)) {
|
||||
*cmdCode = index;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ftps_rtrim(char * str) {
|
||||
char * iter = str + strlen(str) - 1;
|
||||
while ((*iter <= ' ') && (iter >= str)) {
|
||||
*iter = '\0';
|
||||
iter--;
|
||||
}
|
||||
}
|
||||
|
||||
void ftps_process_event(uint32_t event) {
|
||||
const char *resp = NULL; // response towards the FTP client
|
||||
char *cmdArg = NULL; // command argument
|
||||
int cmdCode = -1; // command code fetched from the string command
|
||||
|
||||
// is something is received, then read it
|
||||
if (event == FTPEVT_CTRL_RECVD) {
|
||||
// read into the linebuffer
|
||||
uint32_t recvLen = tcp_recv(s.connCtrl, (uint8_t *)s.lineBuffer, FTPS_LINEBUF_LEN);
|
||||
s.lineBuffer[recvLen] = '\0';
|
||||
|
||||
// right trim the line
|
||||
ftps_rtrim(s.lineBuffer);
|
||||
|
||||
MSG("%s\n", s.lineBuffer);
|
||||
|
||||
// get first word: get pointer to first space and calculate compare length
|
||||
char *spacePos = strchr(s.lineBuffer, ' ');
|
||||
uint32_t compareLen = (spacePos == NULL) ? 0 : (spacePos - s.lineBuffer);
|
||||
|
||||
// determine if we support this command or not
|
||||
bool cmdImpl = ftps_is_command_implemented(s.lineBuffer, compareLen, (uint32_t *)&cmdCode);
|
||||
|
||||
// if command is not implemented, then send the "not implemented" answer and return from this function
|
||||
if (!cmdImpl) {
|
||||
resp = ftpNotImplemented;
|
||||
goto resp_and_return; // yes, goto, but it's efficient
|
||||
}
|
||||
|
||||
// if command is implemented, then set the argument pointer
|
||||
cmdArg = spacePos + 1; // the argument begins right after the first space
|
||||
}
|
||||
|
||||
// ------
|
||||
|
||||
if (s.connState == FTPS_IDLE) { // IDLE...
|
||||
if (event == FTPEVT_NEW_CONNECTION) { // ...and there's an incoming connection
|
||||
// send greeting to the client
|
||||
resp = ftpGreeting;
|
||||
|
||||
// set state to logging in
|
||||
s.connState = FTPS_LOGGING_IN;
|
||||
}
|
||||
} else if (s.connState == FTPS_LOGGING_IN) {
|
||||
if (cmdCode == USER) { // if user command is issued, the login, no username or password checking
|
||||
// send login ok
|
||||
resp = ftpLoginOK;
|
||||
|
||||
// switch to established state
|
||||
s.connState = FTPS_ESTABLISHED;
|
||||
}
|
||||
} else if (s.connState == FTPS_ESTABLISHED) {
|
||||
switch (cmdCode)
|
||||
{
|
||||
case PWD: // print working directory name
|
||||
resp = ftpRootDirName;
|
||||
break;
|
||||
case TYPE: //
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ------
|
||||
|
||||
resp_and_return:
|
||||
// send response if instructed
|
||||
if (resp != NULL) {
|
||||
tcp_send(s.connCtrl, (const uint8_t *)resp, strlen(resp));
|
||||
}
|
||||
}
|
||||
|
||||
void ftps_thread(void *param) {
|
||||
// initialize event queue
|
||||
s.eventQ = ETHLIB_OS_QUEUE_CREATE(FTPS_EVENT_QUEUE_LENGTH, uint32_t);
|
||||
|
||||
// initialize control connection
|
||||
s.connCtrlS = tcp_new_connblock(get_default_interface(), IPv4_IF_ADDR, FTPS_CTRL_PORT, ftps_ctrl_recv);
|
||||
tcp_listen(s.connCtrlS); // make it listen for incoming connections
|
||||
tcp_set_accept_callback(s.connCtrlS, ftps_accept_cb); // set callback for accepting an incoming connection
|
||||
|
||||
// initialize connection state
|
||||
s.connState = FTPS_IDLE;
|
||||
|
||||
// process loop
|
||||
for (;;) {
|
||||
// wait for some connection event
|
||||
uint32_t event = FTPEVT_NONE;
|
||||
ETHLIB_OS_QUEUE_POP(s.eventQ, &event);
|
||||
|
||||
// execute event process
|
||||
ftps_process_event(event);
|
||||
}
|
||||
}
|
45
apps/ftp_server.h
Normal file
45
apps/ftp_server.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef APPS_FTP_SERVER
|
||||
#define APPS_FTP_SERVER
|
||||
|
||||
#include <etherlib/etherlib.h>
|
||||
#include <etherlib/cbd_table.h>
|
||||
|
||||
#define FTPS_CTRL_PORT (21) // FTP server control connection port
|
||||
|
||||
#define FTPS_EVENT_QUEUE_LENGTH (8) // internal event queue length
|
||||
|
||||
#define FTPS_LINEBUF_LEN (63)
|
||||
|
||||
#define FTPS_ROOT_DIR_NAME "ftp_root"
|
||||
|
||||
/**
|
||||
* FTP connection state machine states.
|
||||
*/
|
||||
typedef enum {
|
||||
FTPS_IDLE, // the server is IDLE
|
||||
FTPS_LOGGING_IN, // the client is logging in
|
||||
FTPS_ESTABLISHED // the connection is established
|
||||
} Ftps_ConnState;
|
||||
|
||||
/**
|
||||
* FTP connection events.
|
||||
*/
|
||||
typedef enum {
|
||||
FTPEVT_NONE, // no action
|
||||
FTPEVT_NEW_CONNECTION, // a new connection was recently initiated
|
||||
FTPEVT_CTRL_RECVD, // a control message has been received
|
||||
} Ftps_ConnEvents;
|
||||
|
||||
typedef struct {
|
||||
cbd connCtrlS; // control connection server
|
||||
cbd connCtrl; // established control connection
|
||||
cbd connDataS; // data connection server
|
||||
cbd connData; // established data connection
|
||||
Ftps_ConnState connState; // connection state
|
||||
ETHLIB_OS_QUEUE_TYPE eventQ; // event queue
|
||||
char lineBuffer[FTPS_LINEBUF_LEN + 1]; // line buffer
|
||||
} Ftps_State;
|
||||
|
||||
void ftps_init();
|
||||
|
||||
#endif /* APPS_FTP_SERVER */
|
Loading…
x
Reference in New Issue
Block a user