1 Star 0 Fork 6

ivanallen / iceftpd

forked from 冰雪不语 / iceftpd 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
ftpproto.c 25.30 KB
一键复制 编辑 原始数据 按行查看 历史
冰雪不语 提交于 2015-08-31 13:07 . ~
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
#include "ftpproto.h"
#include "sysutil.h"
#include "str.h"
#include "ftpcodes.h"
#include "tunable.h"
#include "privsock.h"
void ftp_lreply(session_t *sess, int status, const char *text);
void handle_alarm_timeout(int sig);
void handle_sigalrm(int sig);
void handle_sigurg(int sig);
void start_cmdio_alarm(void);
void start_data_alarm(void);
void check_abor(session_t *sess);
int list_common(session_t *sess, int detail);
void limit_rate(session_t *sess, int bytes_transfered, int is_upload);
void upload_common(session_t *sess, int is_append);
int get_port_fd(session_t *sess);
int get_pasv_fd(session_t *sess);
int get_transfer_fd(session_t *sess);
int port_active(session_t *sess);
int pasv_active(session_t *sess);
static void do_user(session_t *sess);
static void do_pass(session_t *sess);
static void do_cwd(session_t *sess);
static void do_cdup(session_t *sess);
static void do_quit(session_t *sess);
static void do_port(session_t *sess);
static void do_pasv(session_t *sess);
static void do_type(session_t *sess);
//static void do_stru(session_t *sess);
//static void do_mode(session_t *sess);
static void do_retr(session_t *sess);
static void do_stor(session_t *sess);
static void do_appe(session_t *sess);
static void do_list(session_t *sess);
static void do_nlst(session_t *sess);
static void do_rest(session_t *sess);
static void do_abor(session_t *sess);
static void do_pwd(session_t *sess);
static void do_mkd(session_t *sess);
static void do_rmd(session_t *sess);
static void do_dele(session_t *sess);
static void do_rnfr(session_t *sess);
static void do_rnto(session_t *sess);
static void do_site(session_t *sess);
static void do_syst(session_t *sess);
static void do_feat(session_t *sess);
static void do_size(session_t *sess);
static void do_stat(session_t *sess);
static void do_noop(session_t *sess);
static void do_help(session_t *sess);
static void do_site_chmod(session_t *sess, char *chmod_arg);
static void do_site_umask(session_t *sess, char *umask_arg);
typedef struct ftpcmd {
const char *cmd;
void (*cmd_handler)(session_t *sess);
} ftpcmd_t;
static ftpcmd_t ctrl_cmds[] = {
/* 访问控制命令 */
{"USER", do_user },
{"PASS", do_pass },
{"CWD", do_cwd },
{"XCWD", do_cwd },
{"CDUP", do_cdup },
{"XCUP", do_cdup },
{"QUIT", do_quit },
{"ACCT", NULL },
{"SMNT", NULL },
{"REIN", NULL },
/* 传输参数命令 */
{"PORT", do_port },
{"PASV", do_pasv },
{"TYPE", do_type },
{"STRU", /*do_stru*/NULL },
{"MODE", /*do_mode*/NULL },
/* 服务命令 */
{"RETR", do_retr },
{"STOR", do_stor },
{"APPE", do_appe },
{"LIST", do_list },
{"NLST", do_nlst },
{"REST", do_rest },
{"ABOR", do_abor },
{"\377\364\377\362ABOR", do_abor},
{"PWD", do_pwd },
{"XPWD", do_pwd },
{"MKD", do_mkd },
{"XMKD", do_mkd },
{"RMD", do_rmd },
{"XRMD", do_rmd },
{"DELE", do_dele },
{"RNFR", do_rnfr },
{"RNTO", do_rnto },
{"SITE", do_site },
{"SYST", do_syst },
{"FEAT", do_feat },
{"SIZE", do_size },
{"STAT", do_stat },
{"NOOP", do_noop },
{"HELP", do_help },
{"STOU", NULL },
{"ALLO", NULL }
};
session_t *p_sess;
void handle_alarm_timeout(int sig)
{
shutdown(p_sess->ctrl_fd, SHUT_RD);
ftp_reply(p_sess, FTP_IDLE_TIMEOUT, "Timeout.");
shutdown(p_sess->ctrl_fd, SHUT_WR);
exit(EXIT_FAILURE);
}
void handle_sigalrm(int sig)
{
if (!p_sess->data_process) {
ftp_reply(p_sess, FTP_DATA_TIMEOUT, "Data timeout. Reconnect. Sorry.");
exit(EXIT_FAILURE);
}
// 否则,当前处于数据传输的状态收到了超时信号
p_sess->data_process = 0;
start_data_alarm();
}
void handle_sigurg(int sig)
{
if (p_sess->data_fd == -1) {
return;
}
char cmdline[MAX_COMMAND_LINE] = {0};
int ret = readline(p_sess->ctrl_fd, cmdline, MAX_COMMAND_LINE);
if (ret <= 0) {
ERR_EXIT("readline");
}
str_trim_crlf(cmdline);
if (strcmp(cmdline, "ABOR") == 0
|| strcmp(cmdline, "\377\364\377\362ABOR") == 0) {
p_sess->abor_received = 1;
shutdown(p_sess->data_fd, SHUT_RDWR);
} else {
ftp_reply(p_sess, FTP_BADCMD, "Unknown command.");
}
}
void check_abor(session_t *sess)
{
if (sess->abor_received) {
sess->abor_received = 0;
ftp_reply(p_sess, FTP_ABOROK, "ABOR successful.");
}
}
void start_cmdio_alarm(void)
{
if (tunable_idle_session_timeout > 0) {
// 安装信号
signal(SIGALRM, handle_alarm_timeout);
// 启动闹钟
alarm(tunable_idle_session_timeout);
}
}
void start_data_alarm(void)
{
if (tunable_data_connection_timeout > 0) {
// 安装信号
signal(SIGALRM, handle_sigalrm);
// 启动闹钟
alarm(tunable_data_connection_timeout);
}
else if (tunable_idle_session_timeout > 0) {
// 关闭先前安装的闹钟
alarm(0);
}
}
void handle_child(session_t *sess)
{
ftp_reply(sess, FTP_GREET, "(iceftpd 0.1)");
int ret;
while (1) {
memset(sess->cmdline, 0, sizeof(sess->cmdline));
memset(sess->cmd, 0, sizeof(sess->cmd));
memset(sess->arg, 0, sizeof(sess->arg));
start_cmdio_alarm();
ret = readline(sess->ctrl_fd, sess->cmdline, MAX_COMMAND_LINE);
if (ret == -1)
ERR_EXIT("readline");
else if (ret == 0)
exit(EXIT_SUCCESS);
// 去除\r\n
str_trim_crlf(sess->cmdline);
printf("cmdline=[%s]\n", sess->cmdline);
// 解析FTP命令与参数
str_split(sess->cmdline, sess->cmd, sess->arg, ' ');
printf("cmd=[%s] arg=[%s]\n", sess->cmd, sess->arg);
// 将命令转换为大写
str_upper(sess->cmd);
int i;
int size = sizeof(ctrl_cmds) / sizeof(ctrl_cmds[0]);
for (i=0; i<size; i++) {
if (strcmp(ctrl_cmds[i].cmd, sess->cmd) == 0) {
if (ctrl_cmds[i].cmd_handler != NULL) {
ctrl_cmds[i].cmd_handler(sess);
} else {
ftp_reply(sess, FTP_COMMANDNOTIMPL, "Unimplement command.");
}
break;
}
}
if (i == size) {
ftp_reply(sess, FTP_BADCMD, "Unknown command.");
}
}
}
void ftp_reply(session_t *sess, int status, const char *text)
{
char buf[1024] = {0};
sprintf(buf, "%d %s\r\n", status, text);
writen(sess->ctrl_fd, buf, strlen(buf));
}
void ftp_lreply(session_t *sess, int status, const char *text)
{
char buf[1024] = {0};
sprintf(buf, "%d-%s\r\n", status, text);
writen(sess->ctrl_fd, buf, strlen(buf));
}
int list_common(session_t *sess, int detail)
{
DIR *dir = opendir(".");
if (dir == NULL) {
return 0;
}
struct dirent *dt;
struct stat sbuf;
while ((dt = readdir(dir)) != NULL) {
if (lstat(dt->d_name, &sbuf) < 0) {
continue;
}
if (dt->d_name[0] == '.') {
continue;
}
char buf[1024] = {0};
if (detail) {
const char *perms = statbuf_get_perms(&sbuf);
int off = 0;
off += sprintf(buf, "%s ", perms);
off += sprintf(buf + off, " %3d %-8d %-8d ", sbuf.st_nlink, sbuf.st_uid, sbuf.st_gid);
off += sprintf(buf + off, "%8lu ", (unsigned long)sbuf.st_size);
const char *datebuf = statbuf_get_date(&sbuf);
off += sprintf(buf + off, "%s ", datebuf);
if (S_ISLNK(sbuf.st_mode)) {
char tmp[1024] = {0};
readlink(dt->d_name, tmp, sizeof(tmp));
off += sprintf(buf + off, "%s -> %s\r\n", dt->d_name, tmp);
} else {
off += sprintf(buf + off, "%s\r\n", dt->d_name);
}
}
else {
sprintf(buf, "%s\r\n", dt->d_name);
}
//printf("%s", buf);
writen(sess->data_fd, buf, strlen(buf));
}
closedir(dir);
return 1;
}
void limit_rate(session_t *sess, int bytes_transfered, int is_upload)
{
sess->data_process = 1;
// 睡眠时间 = (当前传输速度 / 最大传输速度 – 1) * 当前传输时间;
long curr_sec = get_time_sec();
long curr_usec = get_time_usec();
double elapsed;
elapsed = (double)(curr_sec - sess->bw_transfer_start_sec);
elapsed += (double)(curr_usec - sess->bw_transfer_start_usec) / (double)1000000;
if (elapsed <= (double)0) {
elapsed = (double)0.01;
}
// 计算当前传输速度
unsigned int bw_rate = (unsigned int)((double)bytes_transfered / elapsed);
double rate_ratio;
if (is_upload) {
if (bw_rate <= sess->bw_upload_rate_max) {
// 不需要限速
sess->bw_transfer_start_sec = curr_sec;
sess->bw_transfer_start_usec = curr_usec;
return;
}
rate_ratio = bw_rate / sess->bw_upload_rate_max;
} else {
if (bw_rate <= sess->bw_download_rate_max) {
// 不需要限速
sess->bw_transfer_start_sec = curr_sec;
sess->bw_transfer_start_usec = curr_usec;
return;
}
rate_ratio = bw_rate / sess->bw_download_rate_max;
}
// 睡眠时间 = (当前传输速度 / 最大传输速度 – 1) * 当前传输时间;
double pause_time;
pause_time = (rate_ratio - (double)1) * elapsed;
nano_sleep(pause_time);
sess->bw_transfer_start_sec = get_time_sec();
sess->bw_transfer_start_usec = get_time_usec();
}
void upload_common(session_t *sess, int is_append)
{
// 创建数据连接
if (get_transfer_fd(sess) == 0) {
return;
}
long long offset = sess->restart_pos;
sess->restart_pos = 0;
// 打开文件
int fd = open(sess->arg, O_CREAT | O_WRONLY, 0666);
if (fd == -1) {
ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
return;
}
int ret;
// 加写锁
ret = lock_file_write(fd);
if (ret == -1) {
ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
return;
}
// STOR
// REST+STOR
// APPE
if (!is_append && offset == 0) { // STOR
ftruncate(fd, 0);
if (lseek(fd, 0, SEEK_SET) < 0) {
ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
return;
}
}
else if (!is_append && offset != 0) { // REST+STOR
if (lseek(fd, offset, SEEK_SET) < 0) {
ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
return;
}
}
else if (is_append) { // APPE
if (lseek(fd, 0, SEEK_END) < 0) {
ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
return;
}
}
struct stat sbuf;
ret = fstat(fd, &sbuf);
if (!S_ISREG(sbuf.st_mode)) {
ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
return;
}
// 150
char text[1024] = {0};
if (sess->is_ascii) {
sprintf(text, "Opening ASCII mode data connection for %s (%lld bytes).",
sess->arg, (long long)sbuf.st_size);
} else {
sprintf(text, "Opening BINARY mode data connection for %s (%lld bytes).",
sess->arg, (long long)sbuf.st_size);
}
ftp_reply(sess, FTP_DATACONN, text);
int flag = 0;
// 上传文件
char buf[1024];
sess->bw_transfer_start_sec = get_time_sec();
sess->bw_transfer_start_usec = get_time_usec();
while (1) {
ret = read(sess->data_fd, buf, sizeof(buf));
if (ret == -1) {
if (errno == EINTR) {
continue;
} else {
flag = 2;
break;
}
}
else if (ret == 0) {
flag = 0;
break;
}
limit_rate(sess, ret, 1);
if (sess->abor_received) {
flag = 2;
break;
}
if (writen(fd, buf, ret) != ret) {
flag = 1;
break;
}
}
// 关闭数据套接字
close(sess->data_fd);
sess->data_fd = -1;
close(fd);
if (flag == 0 && !sess->abor_received) {
// 226
ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
}
else if (flag == 1) {
// 451
ftp_reply(sess, FTP_BADSENDFILE, "Failure writting to local file.");
}
else if (flag == 2) {
// 426
ftp_reply(sess, FTP_BADSENDNET, "Failure reading from network stream.");
}
check_abor(sess);
// 重新开启控制连接通道闹钟
start_cmdio_alarm();
}
int port_active(session_t *sess)
{
if (sess->port_addr) {
if (pasv_active(sess)) {
fprintf(stderr, "both port an pasv are active");
exit(EXIT_FAILURE);
}
return 1;
}
return 0;
}
int pasv_active(session_t *sess)
{
priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_PASV_ACTIVE);
int active = priv_sock_get_int(sess->child_fd);
if (active) {
if (port_active(sess)) {
fprintf(stderr, "both port an pasv are active");
exit(EXIT_FAILURE);
}
return 1;
}
return 0;
}
int get_port_fd(session_t *sess)
{
/*
向nobody发送PRIV_SOCK_GET_DATA_SOCK命令 1
向nobody发送一个整数port 4
向nobody发送一个字符串ip 不定长
*/
priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_GET_DATA_SOCK);
unsigned short port = ntohs(sess->port_addr->sin_port);
char *ip = inet_ntoa(sess->port_addr->sin_addr);
priv_sock_send_int(sess->child_fd, (int)port);
priv_sock_send_buf(sess->child_fd, ip, strlen(ip));
char res = priv_sock_get_result(sess->child_fd);
if (res == PRIV_SOCK_RESULT_BAD) {
return 0;
}
else if (res == PRIV_SOCK_RESULT_OK) {
sess->data_fd = priv_sock_recv_fd(sess->child_fd);
}
return 1;
}
int get_pasv_fd(session_t *sess)
{
priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_PASV_ACCEPT);
char res = priv_sock_get_result(sess->child_fd);
if (res == PRIV_SOCK_RESULT_BAD) {
return 0;
}
else if (res == PRIV_SOCK_RESULT_OK) {
sess->data_fd = priv_sock_recv_fd(sess->child_fd);
}
return 1;
}
int get_transfer_fd(session_t *sess)
{
// 检测是否收到PORT或者PASV命令
if (!port_active(sess) && !pasv_active(sess)) {
ftp_reply(sess, FTP_BADSENDCONN, "Use PORT or PASV first.");
return 0;
}
int ret = 1;
// 如果是主动模式
if (port_active(sess)) {
if (get_port_fd(sess) == 0) {
ret = 0;
}
}
if (pasv_active(sess)) {
if (get_pasv_fd(sess) == 0) {
ret = 0;
}
}
if (sess->port_addr) {
free(sess->port_addr);
sess->port_addr = NULL;
}
if (ret) {
// 重新安装SIGALRM信号,并启动闹钟
start_data_alarm();
}
return ret;
}
static void do_user(session_t *sess)
{
//USER jjl
struct passwd *pw = getpwnam(sess->arg);
if (pw == NULL) {
// 用户不存在
ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
return;
}
sess->uid = pw->pw_uid;
ftp_reply(sess, FTP_GIVEPWORD, "Please specify the password.");
}
static void do_pass(session_t *sess)
{
// PASS 123456
struct passwd *pw = getpwuid(sess->uid);
if (pw == NULL) {
// 用户不存在
ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
return;
}
printf("name=[%s]\n", pw->pw_name);
struct spwd *sp = getspnam(pw->pw_name);
if (sp == NULL) {
ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
return;
}
// 将明文进行加密
char *encrypted_pass = crypt(sess->arg, sp->sp_pwdp);
// 验证密码
if (strcmp(encrypted_pass, sp->sp_pwdp) != 0) {
ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
return;
}
signal(SIGURG, handle_sigurg);
activate_sigurg(sess->ctrl_fd);
umask(tunable_local_umask);
setegid(pw->pw_gid);
seteuid(pw->pw_uid);
chdir(pw->pw_dir);
ftp_reply(sess, FTP_LOGINOK, "Login successful.");
}
static void do_cwd(session_t *sess)
{
if (chdir(sess->arg) < 0) {
ftp_reply(sess, FTP_FILEFAIL, "Failed to change directory.");
return;
}
ftp_reply(sess, FTP_CWDOK, "Directory successfully changed.");
}
static void do_cdup(session_t *sess)
{
if (chdir("..") < 0) {
ftp_reply(sess, FTP_FILEFAIL, "Failed to change directory.");
return;
}
ftp_reply(sess, FTP_CWDOK, "Directory successfully changed.");
}
static void do_quit(session_t *sess)
{
ftp_reply(sess, FTP_GOODBYE, "Goodbye.");
exit(EXIT_SUCCESS);
}
static void do_port(session_t *sess)
{
unsigned int v[6];
sscanf(sess->arg, "%u,%u,%u,%u,%u,%u", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
sess->port_addr = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
memset(sess->port_addr, 0, sizeof(struct sockaddr_in));
sess->port_addr->sin_family = AF_INET;
unsigned char *p = (unsigned char *)&sess->port_addr->sin_port;
p[0] = v[0];
p[1] = v[1];
p = (unsigned char *)&sess->port_addr->sin_addr;
p[0] = v[2];
p[1] = v[3];
p[2] = v[4];
p[3] = v[5];
ftp_reply(sess, FTP_PORTOK, "PORT command successful. Consider using PASV.");
}
static void do_pasv(session_t *sess)
{
//Entering Passive Mode (192,168,244,100,101,46).
char ip[16] = {0};
getlocalip(ip);
priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_PASV_LISTEN);
unsigned short port = (int)priv_sock_get_int(sess->child_fd);
unsigned int v[4];
sscanf(ip, "%u.%u.%u.%u", &v[0], &v[1], &v[2], &v[3]);
char text[1024] = {0};
sprintf(text, "Entering Passive Mode (%u,%u,%u,%u,%u,%u).",
v[0], v[1], v[2], v[3], port>>8, port&0xFF);
ftp_reply(sess, FTP_PASVOK, text);
}
static void do_type(session_t *sess)
{
if (strcmp(sess->arg, "A") == 0) {
sess->is_ascii = 1;
ftp_reply(sess, FTP_TYPEOK, "Switching to ASCII mode.");
}
else if (strcmp(sess->arg, "I") == 0) {
sess->is_ascii = 0;
ftp_reply(sess, FTP_TYPEOK, "Switching to Binary mode.");
} else {
ftp_reply(sess, FTP_BADCMD, "Unrecognised TYPE command.");
}
}
/*
static void do_stru(session_t *sess)
{
}
static void do_mode(session_t *sess)
{
}
*/
static void do_retr(session_t *sess)
{
/* 下载文件 */
/* 断点续载 */
// 创建数据连接
if (get_transfer_fd(sess) == 0) {
return;
}
long long offset = sess->restart_pos;
sess->restart_pos = 0;
// 打开文件
int fd = open(sess->arg, O_RDONLY);
if (fd == -1) {
ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
return;
}
int ret;
// 加读锁
ret = lock_file_read(fd);
if (ret == -1) {
ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
return;
}
// 判断是否是普通文件
struct stat sbuf;
ret = fstat(fd, &sbuf);
if (!S_ISREG(sbuf.st_mode)) {
ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
return;
}
if (offset != 0) {
ret = lseek(fd, offset, SEEK_SET);
if (ret == -1) {
ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
return;
}
}
//150 Opening BINARY mode data connection
// 150
char text[1024] = {0};
if (sess->is_ascii) {
sprintf(text, "Opening ASCII mode data connection for %s (%lld bytes).",
sess->arg, (long long)sbuf.st_size);
}
else {
sprintf(text, "Opening BINARY mode data connection for %s (%lld bytes).",
sess->arg, (long long)sbuf.st_size);
}
ftp_reply(sess, FTP_DATACONN, text);
int flag = 0;
// 下载文件
// ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
long long bytes_to_send = sbuf.st_size;
if (offset > bytes_to_send) {
bytes_to_send = 0;
}
else {
bytes_to_send -= offset;
}
sess->bw_transfer_start_sec = get_time_sec();
sess->bw_transfer_start_usec = get_time_usec();
while (bytes_to_send) {
int num_this_time = bytes_to_send > 4096 ? 4096 : bytes_to_send;
ret = sendfile(sess->data_fd, fd, NULL, num_this_time);
if (ret == -1) {
flag = 2;
break;
}
limit_rate(sess, ret, 0);
if (sess->abor_received) {
flag = 2;
break;
}
bytes_to_send -= ret;
}
if (bytes_to_send == 0) {
flag = 0;
}
// 关闭数据套接字
close(sess->data_fd);
sess->data_fd = -1;
close(fd);
if (flag == 0 && !sess->abor_received) {
// 226
ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
}
else if (flag == 1) {
// 451
ftp_reply(sess, FTP_BADSENDFILE, "Failure reading from local file.");
}
else if (flag == 2) {
// 426
ftp_reply(sess, FTP_BADSENDNET, "Failure writting to network stream.");
}
check_abor(sess);
// 重新开启控制连接通道闹钟
start_cmdio_alarm();
}
static void do_stor(session_t *sess)
{
upload_common(sess, 0);
}
static void do_appe(session_t *sess)
{
upload_common(sess, 1);
}
static void do_list(session_t *sess)
{
// 创建数据连接
if (get_transfer_fd(sess) == 0) {
printf("ssssssss");
return;
}
// 150
ftp_reply(sess, FTP_DATACONN, "Here comes the directory listing.");
// 传输列表
list_common(sess, 1);
// 关闭数据套接字
close(sess->data_fd);
sess->data_fd = -1;
// 226
ftp_reply(sess, FTP_TRANSFEROK, "Directory send OK.");
}
static void do_nlst(session_t *sess)
{
// 创建数据连接
if (get_transfer_fd(sess) == 0) {
return;
}
// 150
ftp_reply(sess, FTP_DATACONN, "Here comes the directory listing.");
// 传输列表
list_common(sess, 0);
// 关闭数据套接字
close(sess->data_fd);
sess->data_fd = -1;
// 226
ftp_reply(sess, FTP_TRANSFEROK, "Directory send OK.");
}
static void do_rest(session_t *sess)
{
sess->restart_pos = str_to_longlong(sess->arg);
char text[1024] = {0};
sprintf(text, "Restart position accepted (%lld).", sess->restart_pos);
ftp_reply(sess, FTP_RESTOK, text);
}
static void do_abor(session_t *sess)
{
ftp_reply(sess, FTP_ABOR_NOCONN, "No transfer to ABOR");
}
static void do_pwd(session_t *sess)
{
char text[1024] = {0};
char dir[1024+1] = {0};
getcwd(dir, 1024);
sprintf(text, "\"%s\"", dir);
ftp_reply(sess, FTP_PWDOK, text);
}
static void do_mkd(session_t *sess)
{
// 0777 & umask
if (mkdir(sess->arg, 0777) < 0) {
ftp_reply(sess, FTP_FILEFAIL, "Create directory operation failed.");
return;
}
char text[4096] = {0};
if (sess->arg[0] == '/') {
sprintf(text, "%s created", sess->arg);
} else {
char dir[4096+1] = {0};
getcwd(dir, 4096);
if (dir[strlen(dir)-1] == '/') {
sprintf(text, "%s%s created", dir, sess->arg);
} else {
sprintf(text, "%s/%s created", dir, sess->arg);
}
}
ftp_reply(sess, FTP_MKDIROK, text);
}
static void do_rmd(session_t *sess)
{
if (rmdir(sess->arg) < 0) {
ftp_reply(sess, FTP_FILEFAIL, "Remove directory operation failed.");
}
ftp_reply(sess, FTP_RMDIROK, "Remove directory operation successful.");
}
static void do_dele(session_t *sess)
{
if (unlink(sess->arg) < 0) {
ftp_reply(sess, FTP_FILEFAIL, "Delete operation failed.");
return;
}
ftp_reply(sess, FTP_DELEOK, "Delete operation successful.");
}
static void do_rnfr(session_t *sess)
{
sess->rnfr_name = (char *)malloc(strlen(sess->arg) + 1);
memset(sess->rnfr_name, 0, strlen(sess->arg) + 1);
strcpy(sess->rnfr_name, sess->arg);
ftp_reply(sess, FTP_RNFROK, "Ready for RNTO.");
}
static void do_rnto(session_t *sess)
{
if (sess->rnfr_name == NULL) {
ftp_reply(sess, FTP_NEEDRNFR, "RNFR required first.");
return;
}
rename(sess->rnfr_name, sess->arg);
ftp_reply(sess, FTP_RENAMEOK, "Rename successful.");
free(sess->rnfr_name);
sess->rnfr_name = NULL;
}
static void do_site(session_t *sess)
{
// SITE CHMOD <perm> <file>
// SITE UMASK [umask]
// SITE HELP
char cmd[100] = {0};
char arg[100] = {0};
str_split(sess->arg , cmd, arg, ' ');
if (strcmp(cmd, "CHMOD") == 0) {
do_site_chmod(sess, arg);
}
else if (strcmp(cmd, "UMASK") == 0) {
do_site_umask(sess, arg);
}
else if (strcmp(cmd, "HELP") == 0) {
ftp_reply(sess, FTP_SITEHELP, "CHMOD UMASK HELP");
} else {
ftp_reply(sess, FTP_BADCMD, "Unknown SITE command.");
}
}
static void do_syst(session_t *sess)
{
ftp_reply(sess, FTP_SYSTOK, "UNIX Type: L8");
}
static void do_feat(session_t *sess)
{
ftp_lreply(sess, FTP_FEAT, "Features:");
writen(sess->ctrl_fd, " EPRT\r\n", strlen(" EPRT\r\n"));
writen(sess->ctrl_fd, " EPSV\r\n", strlen(" EPSV\r\n"));
writen(sess->ctrl_fd, " MDTM\r\n", strlen(" MDTM\r\n"));
writen(sess->ctrl_fd, " PASV\r\n", strlen(" PASV\r\n"));
writen(sess->ctrl_fd, " REST STREAM\r\n", strlen(" REST STREAM\r\n"));
writen(sess->ctrl_fd, " SIZE\r\n", strlen(" SIZE\r\n"));
writen(sess->ctrl_fd, " TVFS\r\n", strlen(" TVFS\r\n"));
writen(sess->ctrl_fd, " UTF8\r\n", strlen(" UTF8\r\n"));
ftp_reply(sess, FTP_FEAT, "End");
}
static void do_size(session_t *sess)
{
//550 Could not get file size.
struct stat buf;
if (stat(sess->arg, &buf) < 0) {
ftp_reply(sess, FTP_FILEFAIL, "SIZE operation failed.");
return;
}
if (!S_ISREG(buf.st_mode)) {
ftp_reply(sess, FTP_FILEFAIL, "Could not get file size.");
return;
}
char text[1024] = {0};
sprintf(text, "%lld", (long long)buf.st_size);
ftp_reply(sess, FTP_SIZEOK, text);
}
static void do_stat(session_t *sess)
{
ftp_lreply(sess, FTP_STATOK, "FTP server status:");
if (sess->bw_upload_rate_max == 0) {
char text[1024];
sprintf(text,
" No session upload bandwidth limit\r\n");
writen(sess->ctrl_fd, text, strlen(text));
}
else if (sess->bw_upload_rate_max > 0) {
char text[1024];
sprintf(text,
" Session upload bandwidth limit in byte/s is %u\r\n",
sess->bw_upload_rate_max);
writen(sess->ctrl_fd, text, strlen(text));
}
if (sess->bw_download_rate_max == 0) {
char text[1024];
sprintf(text,
" No session download bandwidth limit\r\n");
writen(sess->ctrl_fd, text, strlen(text));
}
else if (sess->bw_download_rate_max > 0) {
char text[1024];
sprintf(text,
" Session download bandwidth limit in byte/s is %u\r\n",
sess->bw_download_rate_max);
writen(sess->ctrl_fd, text, strlen(text));
}
char text[1024] = {0};
sprintf(text,
" At session startup, client count was %u\r\n",
sess->num_clients);
writen(sess->ctrl_fd, text, strlen(text));
ftp_reply(sess, FTP_STATOK, "End of status");
}
static void do_noop(session_t *sess)
{
ftp_reply(sess, FTP_NOOPOK, "NOOP ok.");
}
static void do_help(session_t *sess)
{
ftp_lreply(sess, FTP_HELP, "The following commands are recognized.");
writen(sess->ctrl_fd,
" ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n",
strlen(" ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n"));
writen(sess->ctrl_fd,
" MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n",
strlen(" MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n"));
writen(sess->ctrl_fd,
" RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n",
strlen(" RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n"));
writen(sess->ctrl_fd,
" XPWD XRMD\r\n",
strlen(" XPWD XRMD\r\n"));
ftp_reply(sess, FTP_HELP, "Help OK.");
}
static void do_site_chmod(session_t *sess, char *chmod_arg)
{
// SITE CHMOD <perm> <file>
if (strlen(chmod_arg) == 0) {
ftp_reply(sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
return;
}
char perm[100] = {0};
char file[100] = {0};
str_split(chmod_arg , perm, file, ' ');
if (strlen(file) == 0) {
ftp_reply(sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
return;
}
unsigned int mode = str_octal_to_uint(perm);
if (chmod(file, mode) < 0) {
ftp_reply(sess, FTP_CHMODOK, "SITE CHMOD command failed.");
} else {
ftp_reply(sess, FTP_CHMODOK, "SITE CHMOD command ok.");
}
}
static void do_site_umask(session_t *sess, char *umask_arg)
{
// SITE UMASK [umask]
if (strlen(umask_arg) == 0) {
char text[1024] = {0};
sprintf(text, "Your current UMASK is 0%o", tunable_local_umask);
ftp_reply(sess, FTP_UMASKOK, text);
} else {
unsigned int um = str_octal_to_uint(umask_arg);
umask(um);
char text[1024] = {0};
sprintf(text, "UMASK set to 0%o", um);
ftp_reply(sess, FTP_UMASKOK, text);
}
}
1
https://gitee.com/ivan_allen/iceftpd.git
git@gitee.com:ivan_allen/iceftpd.git
ivan_allen
iceftpd
iceftpd
master

搜索帮助