int get_transfer_fd(session_t* sess) { //check have received port or pasv command before 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) start_data_alarm(); return ret; }
int get_transfer_fd(session_t *pses) { int iret = 0; //可能是主动模式,或者被动模式 //检测是否收到PORT命令或者PASV命令 if((port_active(pses) < 0) && (pasv_active(pses) < 0)) { ftp_reply(pses->ctrl_fd, FTP_BADSENDCONN, "Use PORT or PASV first."); iret = -1; return iret; } //主动模式 if(port_active(pses) == 0) { //向nobody进程发送命令 priv_sock_send_cmd(pses->child_fd, PRIV_SOCK_GET_DATA_SOCK); unsigned short port = ntohs(pses->port_addr->sin_port); char *ip = inet_ntoa(pses->port_addr->sin_addr); //发送port priv_sock_send_int(pses->child_fd, (int)port); //发送ip priv_sock_send_buf(pses->child_fd, ip, strlen(ip)); //接收应答 char res = priv_sock_get_result(pses->child_fd); if(res == PRIV_SOCK_RESULT_BAD) { iret = -1; } else if(res == PRIV_SOCK_RESULT_OK) { //接收文件描述符 pses->data_fd = priv_sock_recv_fd(pses->child_fd); } } else if(pasv_active(pses) == 0)//被动模式 { //发送命令给nobody进程,完成客户端和服务端的连接 priv_sock_send_cmd(pses->child_fd, PRIV_SOCK_PASV_ACCEPT); //接收nobody进程的应答 char res = priv_sock_get_result(pses->child_fd); if(res == PRIV_SOCK_RESULT_BAD) { iret = -1; } else if(res == PRIV_SOCK_RESULT_OK) { pses->data_fd = priv_sock_recv_fd(pses->child_fd); } } //连接成功后,该内存空间就不需要了 if(pses->port_addr != NULL) { free(pses->port_addr); pses->port_addr = NULL; } return iret; }
/** *get_transfer_fd - 创建数据连接,主动用connect,被动用accept *@sess:会话结构体 *成功返回0,失败返回1 */ int ftpproto::get_transfer_fd(session_t* sess) { //检测是否收到PORT或者PASV命令 //激活过是1 if (!port_active(sess) && !pasv_active(sess))//没有被激活过 {//两个都没有被激活过,要给个应答,若直接返回会使客户端阻塞 ftp_reply(sess,FTP_BADSENDCONN,"Use PORT or PASV first."); return 1;//失败 } int ret = 0; if (port_active(sess))//主动模式,服务器创建数据套接字(bind 20端口) { //调用connect连接客户端IP与端口,建立数据连接 if (get_port_fd(sess))//获取主动模式的套接字 {//失败 ret = 1; } } if (pasv_active(sess))//被动模式,使用accept { if (get_pasv_fd(sess))//获取被动模式的套接字 {//失败 ret = 1; } } if (sess->port_addr) {//之前调用过do_port了,数据连接用完就free free(sess->port_addr); sess->port_addr = NULL; } // if (ret) // { // //成功创建数据通道后就开启闹钟信号 // start_data_alarm();//重新安装SIGALRM信号,并启动闹钟 // } return ret;//成功是0,失败是1 }
static int pasv_active(struct vsf_session* p_sess) { int ret = 0; if (p_sess->pasv_listen_fd != -1) { ret = 1; if (port_active(p_sess)) { bug("pasv and port both active"); } } return ret; }
/** *pasv_active - 检查被动模式是否被激活过 *@sess:会话结构体 *激活过返回1,没有激活过返回0 */ int ftpproto::pasv_active(session_t* sess) { //若被动模式开启,pasv_listen_fd应该不等于-1 if (sess->pasv_listen_fd != -1) { //处于激活状态 if (port_active(sess)) { //这种 状况是不允许的 LCWFTPD_LOG(ERROR, "both port and pasv are active"); } 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 and pasv are active"); exit(EXIT_FAILURE); } return 1; } return 0; }
static int get_remote_transfer_fd(struct vsf_session* p_sess) { int remote_fd; if (!pasv_active(p_sess) && !port_active(p_sess)) { bug("neither PORT nor PASV active in get_remote_transfer_fd"); } p_sess->abor_received = 0; if (pasv_active(p_sess)) { remote_fd = vsf_ftpdataio_get_pasv_fd(p_sess); } else { remote_fd = vsf_ftpdataio_get_port_fd(p_sess); } return remote_fd; }
int pasv_active(session_t *pses) { //向nobody进程发送命令,看看是否创建了监听套接字,如果是,被动模式处于激活状态 priv_sock_send_cmd(pses->child_fd, PRIV_SOCK_PASV_ACTIVE); int iret = priv_sock_get_int(pses->child_fd); if(iret != -1) { if(port_active(pses) == 0) //主被动模式不能同时处于激活状态 { handle_error_str("both port and pasv are active."); } return 0; } else { return -1; } }
static void handle_upload_common(struct vsf_session* p_sess, int is_append) { struct vsf_transfer_ret trans_ret; int new_file_fd; int remote_fd; int retval; filesize_t offset = p_sess->restart_pos; p_sess->restart_pos = 0; if (!pasv_active(p_sess) && !port_active(p_sess)) { vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first."); return; } /* NOTE - actual file permissions will be governed by the tunable umask */ /* XXX - do we care about race between create and chown() of anonymous * upload? */ if (p_sess->is_anonymous && !tunable_anon_other_write_enable) { new_file_fd = str_create(&p_sess->ftp_arg_str); } else { /* For non-anonymous, allow open() to overwrite or append existing files */ if (!is_append && offset == 0) { new_file_fd = str_create_overwrite(&p_sess->ftp_arg_str); } else { new_file_fd = str_create_append(&p_sess->ftp_arg_str); } } if (vsf_sysutil_retval_is_error(new_file_fd)) { vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file."); return; } /* Are we required to chown() this file for security? */ if (p_sess->is_anonymous && tunable_chown_uploads) { if (tunable_one_process_model) { vsf_one_process_chown_upload(p_sess, new_file_fd); } else { vsf_two_process_chown_upload(p_sess, new_file_fd); } } if (!is_append && offset != 0) { /* XXX - warning, allows seek past end of file! Check for seek > size? */ vsf_sysutil_lseek_to(new_file_fd, offset); } remote_fd = get_remote_transfer_fd(p_sess); if (vsf_sysutil_retval_is_error(remote_fd)) { goto port_pasv_cleanup_out; } vsf_cmdio_write(p_sess, FTP_DATACONN, "Ok to send data."); vsf_log_start_entry(p_sess, kVSFLogEntryUpload); str_copy(&p_sess->log_str, &p_sess->ftp_arg_str); prepend_path_to_filename(&p_sess->log_str); if (tunable_ascii_upload_enable && p_sess->is_ascii) { trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd, new_file_fd, 1, 1); } else { trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd, new_file_fd, 1, 0); } p_sess->transfer_size = trans_ret.transferred; /* XXX - handle failure, delete file? */ retval = dispose_remote_transfer_fd(p_sess); /* Log _after_ the blocking dispose call, so we get transfer times right */ if (trans_ret.retval == 0 && retval == 0) { vsf_log_do_log(p_sess, 1); } else { vsf_log_do_log(p_sess, 0); } port_pasv_cleanup_out: port_cleanup(p_sess); pasv_cleanup(p_sess); vsf_sysutil_close(new_file_fd); }
static void handle_dir_common(struct vsf_session* p_sess, int full_details) { static struct mystr s_option_str; static struct mystr s_filter_str; static struct mystr s_dir_name_str; static struct vsf_sysutil_statbuf* s_p_dirstat; int remote_fd; int dir_allow_read = 1; struct vsf_sysutil_dir* p_dir = 0; str_empty(&s_option_str); str_empty(&s_filter_str); /* By default open the current directory */ str_alloc_text(&s_dir_name_str, "."); if (!pasv_active(p_sess) && !port_active(p_sess)) { vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first."); return; } /* Do we have an option? Going to be strict here - the option must come * first. e.g. "ls -a .." fine, "ls .. -a" not fine */ if (!str_isempty(&p_sess->ftp_arg_str) && str_get_char_at(&p_sess->ftp_arg_str, 0) == '-') { /* Chop off the '-' */ str_mid_to_end(&p_sess->ftp_arg_str, &s_option_str, 1); /* A space will separate options from filter (if any) */ str_split_char(&s_option_str, &s_filter_str, ' '); } else { /* The argument, if any, is just a filter */ str_copy(&s_filter_str, &p_sess->ftp_arg_str); } if (!str_isempty(&s_filter_str)) { /* First check - is it an outright directory, as in "ls /pub" */ p_dir = str_opendir(&s_filter_str); if (p_dir != 0) { /* Listing a directory! */ str_copy(&s_dir_name_str, &s_filter_str); str_free(&s_filter_str); } else { struct str_locate_result locate_result = str_locate_char(&s_filter_str, '/'); if (locate_result.found) { /* Includes a path! Reverse scan for / in the arg, to get the * base directory and filter (if any) */ str_copy(&s_dir_name_str, &s_filter_str); str_split_char_reverse(&s_dir_name_str, &s_filter_str, '/'); /* If we have e.g. "ls /.message", we just ripped off the leading * slash because it is the only one! */ if (str_isempty(&s_dir_name_str)) { str_alloc_text(&s_dir_name_str, "/"); } } } } if (p_dir == 0) { /* NOTE - failure check done below, it's not forgotten */ p_dir = str_opendir(&s_dir_name_str); } /* Fine, do it */ remote_fd = get_remote_transfer_fd(p_sess); if (vsf_sysutil_retval_is_error(remote_fd)) { goto dir_close_out; } vsf_cmdio_write(p_sess, FTP_DATACONN, "Here comes the directory listing."); if (p_sess->is_anonymous && p_dir && tunable_anon_world_readable_only) { vsf_sysutil_dir_stat(p_dir, &s_p_dirstat); if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat)) { dir_allow_read = 0; } } if (p_dir == 0 || !dir_allow_read) { vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Transfer done (but failed to open directory)."); } else { (void) vsf_ftpdataio_transfer_dir(p_sess, remote_fd, p_dir, &s_dir_name_str, &s_option_str, &s_filter_str, full_details); } (void) dispose_remote_transfer_fd(p_sess); dir_close_out: if (p_dir) { vsf_sysutil_closedir(p_dir); } port_cleanup(p_sess); pasv_cleanup(p_sess); }
static void handle_retr(struct vsf_session* p_sess) { static struct mystr s_mark_str; static struct vsf_sysutil_statbuf* s_p_statbuf; struct vsf_transfer_ret trans_ret; int retval; int remote_fd; int opened_file; int is_ascii = 0; filesize_t offset = p_sess->restart_pos; p_sess->restart_pos = 0; if (!pasv_active(p_sess) && !port_active(p_sess)) { vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first."); return; } if (p_sess->is_ascii && offset != 0) { vsf_cmdio_write(p_sess, FTP_FILEFAIL, "No support for resume of ASCII transfer."); return; } opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly); if (vsf_sysutil_retval_is_error(opened_file)) { vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file."); return; } vsf_sysutil_fstat(opened_file, &s_p_statbuf); /* No games please */ if (!vsf_sysutil_statbuf_is_regfile(s_p_statbuf)) { /* Note - pretend open failed */ vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file."); goto file_close_out; } /* Optionally, we'll be paranoid and only serve publicly readable stuff */ if (p_sess->is_anonymous && tunable_anon_world_readable_only && !vsf_sysutil_statbuf_is_readable_other(s_p_statbuf)) { vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file."); goto file_close_out; } /* Set the download offset (from REST) if any */ if (offset != 0) { vsf_sysutil_lseek_to(opened_file, offset); } remote_fd = get_remote_transfer_fd(p_sess); if (vsf_sysutil_retval_is_error(remote_fd)) { goto port_pasv_cleanup_out; } vsf_log_start_entry(p_sess, kVSFLogEntryDownload); str_copy(&p_sess->log_str, &p_sess->ftp_arg_str); prepend_path_to_filename(&p_sess->log_str); str_alloc_text(&s_mark_str, "Opening "); if (tunable_ascii_download_enable && p_sess->is_ascii) { str_append_text(&s_mark_str, "ASCII"); is_ascii = 1; } else { str_append_text(&s_mark_str, "BINARY"); } str_append_text(&s_mark_str, " mode data connection for "); str_append_str(&s_mark_str, &p_sess->ftp_arg_str); str_append_text(&s_mark_str, " ("); str_append_filesize_t(&s_mark_str, vsf_sysutil_statbuf_get_size(s_p_statbuf)); str_append_text(&s_mark_str, " bytes)."); vsf_cmdio_write_str(p_sess, FTP_DATACONN, &s_mark_str); trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd, opened_file, 0, is_ascii); p_sess->transfer_size = trans_ret.transferred; retval = dispose_remote_transfer_fd(p_sess); /* Log _after_ the blocking dispose call, so we get transfer times right */ if (trans_ret.retval == 0 && retval == 0) { vsf_log_do_log(p_sess, 1); } else { vsf_log_do_log(p_sess, 0); } port_pasv_cleanup_out: port_cleanup(p_sess); pasv_cleanup(p_sess); file_close_out: vsf_sysutil_close(opened_file); }