int vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess, unsigned short remote_port, int use_port_sockaddr) { static struct vsf_sysutil_sockaddr* p_sockaddr; const struct vsf_sysutil_sockaddr* p_connect_to; int retval; int i; int s = vsf_sysutil_get_ipsock(p_sess->p_local_addr); int port = 0; if (vsf_sysutil_is_port_reserved(remote_port)) { die("Illegal port request"); } if (tunable_connect_from_port_20) { port = tunable_ftp_data_port; } vsf_sysutil_activate_reuseaddr(s); /* A report of failure here on Solaris, presumably buggy address reuse * support? We'll retry. */ for (i = 0; i < 2; ++i) { double sleep_for; vsf_sysutil_sockaddr_clone(&p_sockaddr, p_sess->p_local_addr); vsf_sysutil_sockaddr_set_port(p_sockaddr, port); retval = vsf_sysutil_bind(s, p_sockaddr); if (retval == 0) { break; } if (vsf_sysutil_get_error() != kVSFSysUtilErrADDRINUSE || i == 1) { die("vsf_sysutil_bind"); } sleep_for = vsf_sysutil_get_random_byte(); sleep_for /= 256.0; sleep_for += 1.0; vsf_sysutil_sleep(sleep_for); } if (use_port_sockaddr) { p_connect_to = p_sess->p_port_sockaddr; } else { vsf_sysutil_sockaddr_set_port(p_sess->p_remote_addr, remote_port); p_connect_to = p_sess->p_remote_addr; } retval = vsf_sysutil_connect_timeout(s, p_connect_to, tunable_connect_timeout); if (vsf_sysutil_retval_is_error(retval)) { vsf_sysutil_close(s); s = -1; } return s; }
static void handle_per_user_config(const struct mystr* p_user_str) { struct mystr filename_str = INIT_MYSTR; struct vsf_sysutil_statbuf* p_statbuf = 0; struct str_locate_result loc_result; int retval; if (!tunable_user_config_dir) { return; } /* Security paranoia - ignore if user has a / in it. */ loc_result = str_locate_char(p_user_str, '/'); if (loc_result.found) { return; } str_alloc_text(&filename_str, tunable_user_config_dir); str_append_char(&filename_str, '/'); str_append_str(&filename_str, p_user_str); retval = str_stat(&filename_str, &p_statbuf); if (!vsf_sysutil_retval_is_error(retval)) { /* Security - file ownership check now in vsf_parseconf_load_file() */ vsf_parseconf_load_file(str_getbuf(&filename_str), 1); } else if (vsf_sysutil_get_error() != kVSFSysUtilErrNOENT) { die("error opening per-user config file"); } str_free(&filename_str); vsf_sysutil_free(p_statbuf); }
static int do_checkcap(void) { /* EFAULT (EINVAL if page 0 mapped) vs. ENOSYS */ int retval = capset(0, 0); if (!vsf_sysutil_retval_is_error(retval) || vsf_sysutil_get_error() != kVSFSysUtilErrNOSYS) { return 1; } return 0; }
static void handle_pasv(struct vsf_session* p_sess) { static struct mystr s_pasv_res_str; static struct vsf_sysutil_sockaddr* s_p_sockaddr; struct vsf_sysutil_ipv4port listen_port; struct vsf_sysutil_ipv4addr listen_ipaddr; int bind_retries = 10; pasv_cleanup(p_sess); port_cleanup(p_sess); p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock(); while (--bind_retries) { int retval; unsigned short the_port; double scaled_port; /* IPPORT_RESERVED */ unsigned short min_port = 1024; unsigned short max_port = 65535; if (tunable_pasv_min_port > min_port && tunable_pasv_min_port < max_port) { min_port = tunable_pasv_min_port; } if (tunable_pasv_max_port > min_port && tunable_pasv_max_port < max_port) { max_port = tunable_pasv_max_port; } the_port = vsf_sysutil_get_random_byte(); the_port <<= 8; the_port |= vsf_sysutil_get_random_byte(); scaled_port = (double) min_port; scaled_port += ((double) the_port / (double) 65535) * ((double) max_port - min_port); the_port = (unsigned short) scaled_port; vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr); vsf_sysutil_sockaddr_set_port(s_p_sockaddr, vsf_sysutil_ipv4port_from_int(the_port)); /* Bind to same address we got the incoming connect on */ vsf_sysutil_sockaddr_set_ipaddr(s_p_sockaddr, vsf_sysutil_sockaddr_get_ipaddr(p_sess->p_local_addr)); retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr); if (!vsf_sysutil_retval_is_error(retval)) { break; } if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE) { continue; } die("vsf_sysutil_bind"); } if (!bind_retries) { die("vsf_sysutil_bind"); } vsf_sysutil_listen(p_sess->pasv_listen_fd, 1); /* Get the address of the bound socket, for the port */ vsf_sysutil_getsockname(p_sess->pasv_listen_fd, &s_p_sockaddr); if (tunable_pasv_address != 0) { /* Report passive address as specified in configuration */ if (vsf_sysutil_inet_aton(tunable_pasv_address, &listen_ipaddr) == 0) { die("invalid pasv_address"); } } else { /* Use address of bound socket for passive address */ listen_ipaddr = vsf_sysutil_sockaddr_get_ipaddr(s_p_sockaddr); } listen_port = vsf_sysutil_sockaddr_get_port(s_p_sockaddr); str_alloc_text(&s_pasv_res_str, "Entering Passive Mode ("); str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[0]); str_append_text(&s_pasv_res_str, ","); str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[1]); str_append_text(&s_pasv_res_str, ","); str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[2]); str_append_text(&s_pasv_res_str, ","); str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[3]); str_append_text(&s_pasv_res_str, ","); str_append_ulong(&s_pasv_res_str, listen_port.data[0]); str_append_text(&s_pasv_res_str, ","); str_append_ulong(&s_pasv_res_str, listen_port.data[1]); str_append_text(&s_pasv_res_str, ")"); vsf_cmdio_write_str(p_sess, FTP_PASVOK, &s_pasv_res_str); }
static int do_sendfile(const int out_fd, const int in_fd, unsigned int num_send, filesize_t start_pos) { /* Probably should one day be shared with instance in ftpdataio.c */ static char* p_recvbuf; unsigned int total_written = 0; int retval; enum EVSFSysUtilError error; (void) start_pos; (void) error; #if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \ defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \ defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE) || \ defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) || \ defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE) if (tunable_use_sendfile) { static int s_sendfile_checked; static int s_runtime_sendfile_works; if (!s_sendfile_checked || s_runtime_sendfile_works) { do { #ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE retval = sendfile(out_fd, in_fd, NULL, num_send); #elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) { /* XXX - start_pos will truncate on 32-bit machines - can we * say "start from current pos"? */ off_t written = 0; retval = sendfile(in_fd, out_fd, start_pos, num_send, NULL, &written, 0); /* Translate to Linux-like retval */ if (written > 0) { retval = (int) written; } } #elif defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE) { size_t written = 0; struct sendfilevec the_vec; vsf_sysutil_memclr(&the_vec, sizeof(the_vec)); the_vec.sfv_fd = in_fd; the_vec.sfv_off = start_pos; the_vec.sfv_len = num_send; retval = sendfilev(out_fd, &the_vec, 1, &written); /* Translate to Linux-like retval */ if (written > 0) { retval = (int) written; } } #elif defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) { struct sf_parms sf_iobuf; vsf_sysutil_memclr(&sf_iobuf, sizeof(sf_iobuf)); sf_iobuf.header_data = NULL; sf_iobuf.header_length = 0; sf_iobuf.trailer_data = NULL; sf_iobuf.trailer_length = 0; sf_iobuf.file_descriptor = in_fd; sf_iobuf.file_offset = start_pos; sf_iobuf.file_bytes = num_send; retval = send_file((int*)&out_fd, &sf_iobuf, 0); if (retval >= 0) { retval = sf_iobuf.bytes_sent; } } #else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */ { retval = sendfile(out_fd, in_fd, start_pos, num_send, NULL, 0); } #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */ error = vsf_sysutil_get_error(); vsf_sysutil_check_pending_actions(kVSFSysUtilIO, retval, out_fd); } while (vsf_sysutil_retval_is_error(retval) && error == kVSFSysUtilErrINTR); if (!s_sendfile_checked) { s_sendfile_checked = 1; if (!vsf_sysutil_retval_is_error(retval) || error != kVSFSysUtilErrNOSYS) { s_runtime_sendfile_works = 1; } } if (!vsf_sysutil_retval_is_error(retval)) { return retval; } if (s_runtime_sendfile_works && error != kVSFSysUtilErrINVAL && error != kVSFSysUtilErrOPNOTSUPP) { return retval; } /* Fall thru to normal implementation. We won't check again. NOTE - * also falls through if sendfile() is OK but it returns EINVAL. For * Linux this means the file was not page cache backed. Original * complaint was trying to serve files from an NTFS filesystem! */ } } #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE || VSF_SYSDEP_HAVE_FREEBSD_SENDFILE */ if (p_recvbuf == 0) { vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE); } while (1) { unsigned int num_read; unsigned int num_written; unsigned int num_read_this_time = VSFTP_DATA_BUFSIZE; if (num_read_this_time > num_send) { num_read_this_time = num_send; } retval = vsf_sysutil_read(in_fd, p_recvbuf, num_read_this_time); if (retval < 0) { return retval; } else if (retval == 0) { return -1; } num_read = (unsigned int) retval; retval = vsf_sysutil_write_loop(out_fd, p_recvbuf, num_read); if (retval < 0) { return retval; } num_written = (unsigned int) retval; total_written += num_written; if (num_written != num_read) { return num_written; } if (num_written > num_send) { bug("num_written bigger than num_send in do_sendfile"); } num_send -= num_written; if (num_send == 0) { /* Bingo! */ return total_written; } } }
static void handle_pasv(struct vsf_session* p_sess, int is_epsv) { static struct mystr s_pasv_res_str; static struct vsf_sysutil_sockaddr* s_p_sockaddr; int bind_retries = 10; unsigned short the_port = 0; int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr); if (is_epsv && !str_isempty(&p_sess->ftp_arg_str)) { int argval; str_upper(&p_sess->ftp_arg_str); if (str_equal_text(&p_sess->ftp_arg_str, "ALL")) { p_sess->epsv_all = 1; vsf_cmdio_write(p_sess, FTP_EPSVALLOK, "EPSV ALL ok."); return; } argval = vsf_sysutil_atoi(str_getbuf(&p_sess->ftp_arg_str)); if (!is_ipv6 || argval != 2) { vsf_cmdio_write(p_sess, FTP_EPSVBAD, "Bad network protocol."); return; } } pasv_cleanup(p_sess); port_cleanup(p_sess); if (is_epsv && is_ipv6) { p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock(); } else { p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock(); } vsf_sysutil_activate_reuseaddr(p_sess->pasv_listen_fd); while (--bind_retries) { int retval; double scaled_port; /* IPPORT_RESERVED */ unsigned short min_port = 1024; unsigned short max_port = 65535; if (tunable_pasv_min_port > min_port && tunable_pasv_min_port <= max_port) { min_port = tunable_pasv_min_port; } if (tunable_pasv_max_port >= min_port && tunable_pasv_max_port < max_port) { max_port = tunable_pasv_max_port; } the_port = vsf_sysutil_get_random_byte(); the_port <<= 8; the_port |= vsf_sysutil_get_random_byte(); scaled_port = (double) min_port; scaled_port += ((double) the_port / (double) 65536) * ((double) max_port - min_port + 1); the_port = (unsigned short) scaled_port; vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr); vsf_sysutil_sockaddr_set_port(s_p_sockaddr, the_port); retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr); if (!vsf_sysutil_retval_is_error(retval)) { break; } if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE) { continue; } die("vsf_sysutil_bind"); } if (!bind_retries) { die("vsf_sysutil_bind"); } vsf_sysutil_listen(p_sess->pasv_listen_fd, 1); if (is_epsv) { str_alloc_text(&s_pasv_res_str, "Entering Extended Passive Mode (|||"); str_append_ulong(&s_pasv_res_str, (unsigned long) the_port); str_append_text(&s_pasv_res_str, "|)"); vsf_cmdio_write_str(p_sess, FTP_EPSVOK, &s_pasv_res_str); return; } if (tunable_pasv_address != 0) { /* Report passive address as specified in configuration */ if (vsf_sysutil_inet_aton(tunable_pasv_address, s_p_sockaddr) == 0) { die("invalid pasv_address"); } } str_alloc_text(&s_pasv_res_str, "Entering Passive Mode ("); if (!is_ipv6) { str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr)); } else { const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr); if (p_v4addr) { str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr)); } } str_replace_char(&s_pasv_res_str, '.', ','); str_append_text(&s_pasv_res_str, ","); str_append_ulong(&s_pasv_res_str, the_port >> 8); str_append_text(&s_pasv_res_str, ","); str_append_ulong(&s_pasv_res_str, the_port & 255); str_append_text(&s_pasv_res_str, ")"); vsf_cmdio_write_str(p_sess, FTP_PASVOK, &s_pasv_res_str); }
unsigned short vsf_privop_pasv_listen(struct vsf_session* p_sess) { static struct vsf_sysutil_sockaddr* s_p_sockaddr; int bind_retries = 10; unsigned short the_port = 0; /* IPPORT_RESERVED */ unsigned short min_port = 1024; unsigned short max_port = 65535; int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr); if (is_ipv6) { p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock(); } else { p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock(); } vsf_sysutil_activate_reuseaddr(p_sess->pasv_listen_fd); if (tunable_pasv_min_port > min_port && tunable_pasv_min_port <= max_port) { min_port = tunable_pasv_min_port; } if (tunable_pasv_max_port >= min_port && tunable_pasv_max_port < max_port) { max_port = tunable_pasv_max_port; } while (--bind_retries) { int retval; double scaled_port; the_port = vsf_sysutil_get_random_byte(); the_port <<= 8; the_port |= vsf_sysutil_get_random_byte(); scaled_port = (double) min_port; scaled_port += ((double) the_port / (double) 65536) * ((double) max_port - min_port + 1); the_port = (unsigned short) scaled_port; vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr); vsf_sysutil_sockaddr_set_port(s_p_sockaddr, the_port); retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr); if (!vsf_sysutil_retval_is_error(retval)) { retval = vsf_sysutil_listen(p_sess->pasv_listen_fd, 1); if (!vsf_sysutil_retval_is_error(retval)) { break; } } /* SELinux systems can give you an inopportune EACCES, it seems. */ if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE || vsf_sysutil_get_error() == kVSFSysUtilErrACCES) { continue; } die("vsf_sysutil_bind / listen"); } if (!bind_retries) { die("vsf_sysutil_bind"); } return the_port; }