#include "ftpd.h" #include "avltree.h" #include "bsp_fatfs.h" #include "ff.h" #include "fly_param.h" #include "ftp_server.h" #include "os_cpu.h" #include "sockets.h" #include "ucos_ii.h" #include #include static struct avl_root ftp_root = {.avl_node = NULL}; // 命令匹配的平衡二叉树树根 /* Private variables ------------------------------------------------------------*/ static const char normal_format[] = "-rw-rw-rw- 1 user ftp %11ld %s %02i %5i %s\r\n"; static const char this_year_format[] = "-rw-rw-rw- 1 user ftp %11ld %s %02i %02i:%02i %s\r\n"; static const char *month_list[] = { // 月份从 1 到 12 ,0 填充 NULL NULL, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dez"}; // ftp 命令树构建 #define FTP_REGISTER_COMMAND(CMD) \ do \ { \ static ftp_cmd_t CmdBuf; \ CmdBuf.index = FTP_STR2ID(#CMD); \ CmdBuf.func = cmd_##CMD; \ ftp_insert_command(&CmdBuf); \ } while (0) // ftp 文件列表格式 #define NORMAL_LIST(listbuf, filesize, month, day, year, filename) \ sprintf(listbuf, normal_format, (filesize), month_list[(month)], (day), (year), (filename)) #define THIS_YEAR_LIST(listbuf, filesize, month, day, hour, min, filename) \ sprintf(listbuf, this_year_format, (filesize), month_list[(month)], (day), (hour), (min), (filename)) // ftp 格式一般为 xxxx /dirx/diry/\r\n ,去掉 /\r\n 提取可用路径 #define FTP_PATH_GET(path, pathend) \ do \ { \ while (*path == ' ') \ ++path; \ if (*pathend == '\n') \ *pathend-- = 0; \ if (*pathend == '\r') \ *pathend-- = 0; \ if (*pathend == '/') \ *pathend = 0; \ } while (0) /** * @brief ftp_insert_command * 命令树插入 * @param pCmd 命令控制块 * @return 成功返回 0 */ int ftp_insert_command(ftp_cmd_t *p_cmd) { struct avl_node **tmp = &ftp_root.avl_node; struct avl_node *parent = NULL; while (*tmp) { ftp_cmd_t *this = container_of(*tmp, ftp_cmd_t, cmd_node); parent = *tmp; if (p_cmd->index < this->index) tmp = &((*tmp)->avl_left); else if (p_cmd->index > this->index) tmp = &((*tmp)->avl_right); else return 1; } avl_insert(&ftp_root, &p_cmd->cmd_node, parent, tmp); return 0; } /** * @brief ftp_search_command * 命令树查找,根据 Index 号找到对应的控制块 * @param index 命令号 * @return 成功 Index 号对应的控制块 */ ftp_cmd_t *ftp_search_command(int i_ctrl_cmd) { struct avl_node *node = ftp_root.avl_node; while (node) { ftp_cmd_t *p_cmd = container_of(node, ftp_cmd_t, cmd_node); if (i_ctrl_cmd < p_cmd->index) node = node->avl_left; else if (i_ctrl_cmd > p_cmd->index) node = node->avl_right; else return p_cmd; } return NULL; } // 登录系统用户名 static void cmd_user(ftp_mbox_t *msgbox) { if (strcasecmp(msgbox->arg, "root") == 0) { send(msgbox->cmd_socket, msg331, sizeof(msg331) - 1, 0); } else { send(msgbox->cmd_socket, msg331, sizeof(msg331) - 1, 0); } } // 登录系统密码 static void cmd_pass(ftp_mbox_t *msgbox) { // printf(msgbox->arg); // if (strcasecmp(msgbox->arg, "ASASAS") == 0) // { send(msgbox->cmd_socket, msg230, sizeof(msg230) - 1, 0); // } // else // { // send(msgbox->cmd_socket, msg503, sizeof(msg503) - 1, 0); // } } // 操作系统类型 static void cmd_syst(ftp_mbox_t *msgbox) { static const char reply_msg[] = "215 UNIX Type: L8\r\n"; // 215 系统类型回复 send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0); } // 显示当前工作目录 static void cmd_pwd(ftp_mbox_t *msgbox) { #if 1 char reply_msg[128]; // 257 路径名建立 // sprintf(reply_msg, "257 \"%s/\"\r\n", msgbox->current_dir); sprintf(reply_msg, "257 \"%s/\"\r\n", "LINUX"); #else static const char reply_msg[] = "257 \"/\"\r\n"; #endif send(msgbox->cmd_socket, reply_msg, strlen(reply_msg), 0); } /** * @brief cmd_noop * ftp 命令端口输入 noop * @param arg 命令所跟参数 * @return NULL */ static void cmd_noop(ftp_mbox_t *msgbox) { static const char reply_msg[] = "200 Operation successful\r\n"; send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0); } // 路径名建立 static void cmd_cwd(ftp_mbox_t *msgbox) { static const char reply_msg[] = "250 Operation successful\r\n"; DIR fsdir; char *pcFilePath = msgbox->arg; char *pcPathEnd = msgbox->arg + msgbox->arglen - 1; FTP_PATH_GET(pcFilePath, pcPathEnd); if (FR_OK != f_opendir(&fsdir, pcFilePath)) { goto cmddone; } f_closedir(&fsdir); if (pcPathEnd != pcFilePath) memcpy(msgbox->current_dir, pcFilePath, pcPathEnd - pcFilePath); msgbox->current_dir[pcPathEnd - pcFilePath] = 0; cmddone: send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0); } // static void cmd_feat(ftp_mbox_t *msgbox) // { // static const char reply_msg[] = "211-feature:\r\n"; // static const char msgtprt[] = "EPRT\r\n"; // send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0); // send(msgbox->cmd_socket, msgtprt, sizeof(msgtprt) - 1, 0); // } // 被动模式 static void cmd_pasv(ftp_mbox_t *msgbox) { static char reply_msg[64] = {0}; INT32S stor = strlen(reply_msg); if (0 == stor) // 未初始化信息 { sprintf(reply_msg, "227 pasv ok(%d,%d,%d,%d,%d,%d)\r\n", 192, 168, 1, 40, (FTP_DATA_PORT >> 8), FTP_DATA_PORT & 0x00ff); stor = strlen(reply_msg); } send(msgbox->cmd_socket, reply_msg, stor, 0); } /** * @brief cmd_nlst * ftp 命令端口输入 nlst ,被动模式 * * @param arg 命令所跟参数 * @return NULL */ static void cmd_nlst(ftp_mbox_t *msgbox) { static const char reply_msg[] = "150 Directory listing\r\n"; // 150 打开连接 // 1.在控制端口对 list 命令进行回复 // 2.在数据端口发送 "total 0",这个貌似可以没有 // 3.在数据端口发送文件列表 // 4.关闭数据端口 send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0); msgbox->event = FTP_LIST; // 事件为列表事件 OSMboxPost(ftp_mbox, msgbox); } /** * @brief cmd_list * ftp 命令端口输入 list , 获取当前文件列表 * @param arg 命令所跟参数 * @return NULL */ static void cmd_list(ftp_mbox_t *msgbox) { static const char reply_msg[] = "150 Directory listing\r\n"; // 150 打开连接 // 1.在控制端口对 list 命令进行回复 // 2.在数据端口发送 "total 0",这个貌似可以没有 // 3.在数据端口发送文件列表 // 4.关闭数据端口 send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0); msgbox->event = FTP_LIST; // 事件为列表事件 OSMboxPost(ftp_mbox, msgbox); } /** * @brief cmd_size * ftp 命令端口输入 size , 获取当前文件列表 * @param arg 命令所跟参数 * @return NULL */ static void cmd_size(ftp_mbox_t *msgbox) { char acFtpBuf[128]; uint32_t iFilesize; char *pcFilePath = msgbox->arg; char *pcPathEnd = msgbox->arg + msgbox->arglen - 1; FTP_PATH_GET(pcFilePath, pcPathEnd); if (*pcFilePath != '/') // 相对路径补全为绝对路径 { pcFilePath = acFtpBuf; } if (FR_OK != f_open(&file_object, pcFilePath, FA_READ)) { goto sizedone; } iFilesize = f_size(&file_object); f_close(&file_object); sizedone: send(msgbox->cmd_socket, acFtpBuf, strlen(acFtpBuf), 0); } /** * @brief cmd_retr * ftp 命令端口输入 retr * @param arg 命令所跟参数 * @return NULL */ static void cmd_retr(ftp_mbox_t *msgbox) { static const char reply_msg[] = "108 Operation successful\r\n"; send(msgbox->cmd_socket, reply_msg, sizeof(reply_msg) - 1, 0); msgbox->event = FTP_SEND_FILE_DATA; OSMboxPost(ftp_mbox, msgbox); } /** * @brief cmd_dele * ftp 命令端口输入 dele * @param arg 命令所跟参数 * @return NULL */ static void cmd_dele(ftp_mbox_t *msgbox) { FRESULT res; char databuf[128]; char *pcFilePath = msgbox->arg; char *pcPathEnd = msgbox->arg + msgbox->arglen - 1; FTP_PATH_GET(pcFilePath, pcPathEnd); if (*pcFilePath != '/') // 相对路径 { sprintf(databuf, "%s/%s", msgbox->current_dir, pcFilePath); pcFilePath = databuf; } res = f_unlink(pcFilePath); if (FR_OK != res) goto deleError; send(msgbox->cmd_socket, msg250, sizeof(msg250) - 1, 0); return; deleError: send(msgbox->cmd_socket, msg450, sizeof(msg450) - 1, 0); return; } /** * @brief cmd_stor * ftp 命令端口输入 stor * @param arg 命令所跟参数 * @return NULL */ static void cmd_stor(ftp_mbox_t *msgbox) { static const char reply_msgOK[] = "125 Waiting\r\n"; send(msgbox->cmd_socket, reply_msgOK, sizeof(reply_msgOK) - 1, 0); msgbox->event = FTP_RECV_FILE; OSMboxPost(ftp_mbox, msgbox); } static const char ftp_msg_451[] = "451 errors"; static const char ftp_msg_226[] = "226 transfer complete\r\n"; /* Start node to be scanned (***also used as work area***) */ char *data_port_list_file(ftp_mbox_t *msgbox) { char *ctrl_msg = (char *)ftp_msg_226; char list_buf[128]; DIR dir; FILINFO fno; if (FR_OK != f_opendir(&dir, msgbox->current_dir)) { goto ScanDirDone; } for (;;) { struct FileDate *pStDate; struct FileTime *pStTime; FRESULT res = f_readdir(&dir, &fno); /* Read a directory item */ if (res != FR_OK || fno.fname[0] == 0) /* Break on error or end of dir */ break; if ((fno.fattrib & AM_DIR) && (fno.fattrib != AM_DIR)) // 不显示只读/系统/隐藏文件夹 continue; pStDate = (struct FileDate *)(&fno.fdate); pStTime = (struct FileTime *)(&fno.ftime); if (fno.fdate == 0 || fno.ftime == 0) // 没有日期的文件 NORMAL_LIST(list_buf, fno.fsize, 1, 1, 1980, fno.fname); else if (pStDate->Year + 1980 == 2018) // 同一年的文件 THIS_YEAR_LIST(list_buf, fno.fsize, pStDate->Month, pStDate->Day, pStTime->Hour, pStTime->Min, fno.fname); else NORMAL_LIST(list_buf, fno.fsize, pStDate->Month, pStDate->Day, pStDate->Year + 1980, fno.fname); if (fno.fattrib & AM_DIR) /* It is a directory */ list_buf[0] = 'd'; send(g_ftp_data_socket, list_buf, strlen(list_buf), 0); } f_closedir(&dir); // 把路径关闭 ScanDirDone: return ctrl_msg; } // char *data_port_recv_file(ftp_mbox_t *msgbox) // { // // static __align(4) char recv_buf[TCP_MSS]; // fatfs 写文件的时候,buf要地址对齐,否则容易出错 // char *ctrl_msg = (char *)ftp_msg_451; // FIL RecvFile; /* File object */ // FRESULT res; // char *pcFile = msgbox->arg; // char *pcPathEnd = msgbox->arg + msgbox->arglen - 1; // char databuf[128]; // uint16_t sRxlen; // uint32_t byteswritten; // struct netbuf *data_netbuf; // int ret; // // vFtp_GetLegalPath(pcFile, pcPathEnd); // if (*pcFile != '/') // 相对路径 // { // sprintf(databuf, "%s/%s", msgbox->current_dir, pcFile); // pcFile = databuf; // } // res = f_open(&RecvFile, pcFile, FA_CREATE_ALWAYS | FA_WRITE); // if (res != FR_OK) // { // Errors("cannot open/create \"%s\",error code = %d\r\n", pcFile, res); // goto RecvEnd; // } // ret = recv(g_ftp_data_socket, data_netbuf, sizeof(data_netbuf) - 1, 0); // // while (ERR_OK == recv(g_ftp_data_socket, &data_netbuf)) // 阻塞直到收到数据 // // { // // do // // { // // netbuf_data(data_netbuf, (void **)&pcFile, &sRxlen); // 提取数据指针 // // #if 1 // // memcpy(recv_buf, pcFile, sRxlen); // 把数据拷出来,否则容易出错 // // pcFile = recv_buf; // // #endif // // res = f_write(&RecvFile, (void *)pcFile, sRxlen, &byteswritten); // // printk("."); // // if ((byteswritten == 0) || (res != FR_OK)) // // { // // f_close(&RecvFile); // // Errors("write file error\r\n"); // // goto RecvEnd; // // } // // } while (netbuf_next(data_netbuf) >= 0); // // netbuf_delete(data_netbuf); // // } // ctrl_msg = (char *)ftp_msg_226; // f_close(&RecvFile); // RecvEnd: // return ctrl_msg; // } // void ftp_data_deal(void) // { // INT8U err = 0; // ftp_mbox_t *msgbox; // char *ctrl_msg; // while (g_ftp_data_socket != -1) // { // msgbox = (ftp_mbox_t *)OSMboxPend(ftp_mbox, 50, &err); // printf((char *)msgbox->event); // if ((err == OS_ERR_NONE) && (msgbox->arglen >= 2)) // { // switch (msgbox->event) // 根据不同的操作命令进行操作 // { // case FTP_LIST: // ctrl_msg = data_port_list_file(msgbox); // break; // // case FTP_SEND_FILE_DATA: // // ctrl_msg = data_port_send_file(data_port_conn, msgbox); // // break; // // case FTP_RECV_FILE: // // ctrl_msg = data_port_recv_file(data_port_conn, msgbox); // // break; // default:; // } // send(msgbox->cmd_socket, ctrl_msg, strlen(ctrl_msg), 0); // 控制端口反馈 // lwip_close(g_ftp_data_socket); // 关闭链接 // g_ftp_data_socket = -1; // 清空释放连接的内存 // } // else // { // lwip_close(g_ftp_data_socket); // 关闭链接 // g_ftp_data_socket = -1; // 清空释放连接的内存 // } // } // } void ftp_server_init(void) { ftp_cmd_func cmd_type = cmd_noop; // 生成相关的命令二叉树 FTP_REGISTER_COMMAND(user); FTP_REGISTER_COMMAND(pass); FTP_REGISTER_COMMAND(syst); FTP_REGISTER_COMMAND(pwd); FTP_REGISTER_COMMAND(cwd); // FTP_REGISTER_COMMAND(feat); FTP_REGISTER_COMMAND(pasv); FTP_REGISTER_COMMAND(nlst); FTP_REGISTER_COMMAND(list); FTP_REGISTER_COMMAND(noop); FTP_REGISTER_COMMAND(type); FTP_REGISTER_COMMAND(size); FTP_REGISTER_COMMAND(retr); FTP_REGISTER_COMMAND(dele); FTP_REGISTER_COMMAND(stor); }