#include "ftp_server.h" #include #include // ---------- /* 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); } }