2 * Part of Very Secure FTPd
7 * Code to handle FTP data connections. This includes both PORT (server
8 * connects) and PASV (client connects) modes of data transfer. This
9 * includes sends and receives, files and directories.
12 #include "ftpdataio.h"
25 #include "sysdeputil.h"
27 #include "oneprocess.h"
28 #include "twoprocess.h"
31 #include "readwrite.h"
34 static void init_data_sock_params(struct vsf_session
* p_sess
, int sock_fd
);
35 static filesize_t
calc_num_send(int file_fd
, filesize_t init_offset
);
36 static struct vsf_transfer_ret
do_file_send_sendfile(
37 struct vsf_session
* p_sess
, int net_fd
, int file_fd
,
38 filesize_t curr_file_offset
, filesize_t bytes_to_send
);
39 static struct vsf_transfer_ret
do_file_send_rwloop(
40 struct vsf_session
* p_sess
, int file_fd
, int is_ascii
);
41 static struct vsf_transfer_ret
do_file_recv(
42 struct vsf_session
* p_sess
, int file_fd
, int is_ascii
);
43 static void handle_sigalrm(void* p_private
);
44 static void start_data_alarm(struct vsf_session
* p_sess
);
45 static void handle_io(int retval
, int fd
, void* p_private
);
46 static int transfer_dir_internal(
47 struct vsf_session
* p_sess
, int is_control
, struct vsf_sysutil_dir
* p_dir
,
48 const struct mystr
* p_base_dir_str
, const struct mystr
* p_option_str
,
49 const struct mystr
* p_filter_str
, int is_verbose
);
50 static int write_dir_list(struct vsf_session
* p_sess
,
51 struct mystr_list
* p_dir_list
,
52 enum EVSFRWTarget target
);
53 static unsigned int get_chunk_size();
56 vsf_ftpdataio_dispose_transfer_fd(struct vsf_session
* p_sess
)
60 if (p_sess
->data_fd
== -1)
62 bug("no data descriptor in vsf_ftpdataio_dispose_transfer_fd");
64 /* Reset the data connection alarm so it runs anew with the blocking close */
65 start_data_alarm(p_sess
);
66 vsf_sysutil_uninstall_io_handler();
67 if (p_sess
->data_use_ssl
&& p_sess
->ssl_slave_active
)
70 priv_sock_send_cmd(p_sess
->ssl_consumer_fd
, PRIV_SOCK_DO_SSL_CLOSE
);
71 result
= priv_sock_get_result(p_sess
->ssl_consumer_fd
);
72 if (result
!= PRIV_SOCK_RESULT_OK
)
77 else if (p_sess
->p_data_ssl
)
79 dispose_ret
= ssl_data_close(p_sess
);
81 /* This close() blocks because we set SO_LINGER */
82 retval
= vsf_sysutil_close_failok(p_sess
->data_fd
);
83 if (vsf_sysutil_retval_is_error(retval
))
85 /* Do it again without blocking. */
86 vsf_sysutil_deactivate_linger_failok(p_sess
->data_fd
);
87 (void) vsf_sysutil_close_failok(p_sess
->data_fd
);
89 if (tunable_data_connection_timeout
> 0)
91 vsf_sysutil_clear_alarm();
98 vsf_ftpdataio_get_pasv_fd(struct vsf_session
* p_sess
)
101 if (tunable_one_process_model
)
103 remote_fd
= vsf_one_process_get_pasv_fd(p_sess
);
107 remote_fd
= vsf_two_process_get_pasv_fd(p_sess
);
109 /* Yes, yes, hardcoded bad I know. */
112 vsf_cmdio_write(p_sess
, FTP_BADSENDCONN
,
113 "Failed to establish connection.");
116 else if (remote_fd
== -2)
118 vsf_cmdio_write(p_sess
, FTP_BADSENDCONN
, "Security: Bad IP connecting.");
119 vsf_sysutil_close(remote_fd
);
122 init_data_sock_params(p_sess
, remote_fd
);
127 vsf_ftpdataio_get_port_fd(struct vsf_session
* p_sess
)
130 if (tunable_one_process_model
|| tunable_port_promiscuous
)
132 remote_fd
= vsf_one_process_get_priv_data_sock(p_sess
);
136 remote_fd
= vsf_two_process_get_priv_data_sock(p_sess
);
138 if (vsf_sysutil_retval_is_error(remote_fd
))
140 vsf_cmdio_write(p_sess
, FTP_BADSENDCONN
,
141 "Failed to establish connection.");
144 init_data_sock_params(p_sess
, remote_fd
);
149 vsf_ftpdataio_post_mark_connect(struct vsf_session
* p_sess
)
152 if (!p_sess
->data_use_ssl
)
156 if (!p_sess
->ssl_slave_active
)
158 ret
= ssl_accept(p_sess
, p_sess
->data_fd
);
163 priv_sock_send_cmd(p_sess
->ssl_consumer_fd
, PRIV_SOCK_DO_SSL_HANDSHAKE
);
164 priv_sock_send_fd(p_sess
->ssl_consumer_fd
, p_sess
->data_fd
);
165 sock_ret
= priv_sock_get_result(p_sess
->ssl_consumer_fd
);
166 if (sock_ret
== PRIV_SOCK_RESULT_OK
)
173 static struct mystr s_err_msg
;
174 str_alloc_text(&s_err_msg
, "SSL connection failed");
175 if (tunable_require_ssl_reuse
)
177 str_append_text(&s_err_msg
, "; session reuse required");
179 &s_err_msg
, ": see require_ssl_reuse option in vsftpd.conf man page");
181 vsf_cmdio_write_str(p_sess
, FTP_DATATLSBAD
, &s_err_msg
);
187 handle_sigalrm(void* p_private
)
189 struct vsf_session
* p_sess
= (struct vsf_session
*) p_private
;
190 if (!p_sess
->data_progress
)
192 vsf_cmdio_write_exit(p_sess
, FTP_DATA_TIMEOUT
,
193 "Data timeout. Reconnect. Sorry.");
195 p_sess
->data_progress
= 0;
196 start_data_alarm(p_sess
);
200 start_data_alarm(struct vsf_session
* p_sess
)
202 if (tunable_data_connection_timeout
> 0)
204 vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM
,
208 vsf_sysutil_set_alarm(tunable_data_connection_timeout
);
210 else if (tunable_idle_session_timeout
> 0)
212 vsf_sysutil_clear_alarm();
217 init_data_sock_params(struct vsf_session
* p_sess
, int sock_fd
)
219 if (p_sess
->data_fd
!= -1)
221 bug("data descriptor still present in init_data_sock_params");
223 p_sess
->data_fd
= sock_fd
;
224 p_sess
->data_progress
= 0;
225 vsf_sysutil_activate_keepalive(sock_fd
);
226 /* And in the vague hope it might help... */
227 vsf_sysutil_set_iptos_throughput(sock_fd
);
228 /* Set up lingering, so that we wait for all data to transfer, and report
229 * more accurate transfer rates.
231 vsf_sysutil_activate_linger(sock_fd
);
232 /* Start the timeout monitor */
233 vsf_sysutil_install_io_handler(handle_io
, p_sess
);
234 start_data_alarm(p_sess
);
238 handle_io(int retval
, int fd
, void* p_private
)
242 unsigned int bw_rate
;
246 struct vsf_session
* p_sess
= (struct vsf_session
*) p_private
;
247 if (p_sess
->data_fd
!= fd
|| vsf_sysutil_retval_is_error(retval
) ||
252 /* Note that the session hasn't stalled, i.e. don't time it out */
253 p_sess
->data_progress
= 1;
254 /* Apply bandwidth quotas via a little pause, if necessary */
255 if (p_sess
->bw_rate_max
== 0)
259 /* Calculate bandwidth rate */
260 vsf_sysutil_update_cached_time();
261 curr_sec
= vsf_sysutil_get_cached_time_sec();
262 curr_usec
= vsf_sysutil_get_cached_time_usec();
263 elapsed
= (double) (curr_sec
- p_sess
->bw_send_start_sec
);
264 elapsed
+= (double) (curr_usec
- p_sess
->bw_send_start_usec
) /
266 if (elapsed
<= (double) 0)
268 elapsed
= (double) 0.01;
270 bw_rate
= (unsigned int) ((double) retval
/ elapsed
);
271 if (bw_rate
<= p_sess
->bw_rate_max
)
273 p_sess
->bw_send_start_sec
= curr_sec
;
274 p_sess
->bw_send_start_usec
= curr_usec
;
277 /* Tut! Rate exceeded, calculate a pause to bring things back into line */
278 rate_ratio
= (double) bw_rate
/ (double) p_sess
->bw_rate_max
;
279 pause_time
= (rate_ratio
- (double) 1) * elapsed
;
280 vsf_sysutil_sleep(pause_time
);
281 vsf_sysutil_update_cached_time();
282 p_sess
->bw_send_start_sec
= vsf_sysutil_get_cached_time_sec();
283 p_sess
->bw_send_start_usec
= vsf_sysutil_get_cached_time_usec();
287 vsf_ftpdataio_transfer_dir(struct vsf_session
* p_sess
, int is_control
,
288 struct vsf_sysutil_dir
* p_dir
,
289 const struct mystr
* p_base_dir_str
,
290 const struct mystr
* p_option_str
,
291 const struct mystr
* p_filter_str
,
294 return transfer_dir_internal(p_sess
, is_control
, p_dir
, p_base_dir_str
,
295 p_option_str
, p_filter_str
, is_verbose
);
299 transfer_dir_internal(struct vsf_session
* p_sess
, int is_control
,
300 struct vsf_sysutil_dir
* p_dir
,
301 const struct mystr
* p_base_dir_str
,
302 const struct mystr
* p_option_str
,
303 const struct mystr
* p_filter_str
,
306 struct mystr_list dir_list
= INIT_STRLIST
;
307 struct mystr_list subdir_list
= INIT_STRLIST
;
308 struct mystr dir_prefix_str
= INIT_MYSTR
;
309 struct mystr_list
* p_subdir_list
= 0;
310 struct str_locate_result loc_result
= str_locate_char(p_option_str
, 'R');
312 enum EVSFRWTarget target
= kVSFRWData
;
315 target
= kVSFRWControl
;
317 if (loc_result
.found
&& tunable_ls_recurse_enable
)
319 p_subdir_list
= &subdir_list
;
321 vsf_ls_populate_dir_list(&dir_list
, p_subdir_list
, p_dir
, p_base_dir_str
,
322 p_option_str
, p_filter_str
, is_verbose
);
326 str_copy(&dir_prefix_str
, p_base_dir_str
);
327 str_append_text(&dir_prefix_str
, ":\r\n");
328 retval
= ftp_write_str(p_sess
, &dir_prefix_str
, target
);
336 failed
= write_dir_list(p_sess
, &dir_list
, target
);
338 /* Recurse into the subdirectories if required... */
341 struct mystr sub_str
= INIT_MYSTR
;
342 unsigned int num_subdirs
= str_list_get_length(&subdir_list
);
343 unsigned int subdir_index
;
344 for (subdir_index
= 0; subdir_index
< num_subdirs
; subdir_index
++)
347 struct vsf_sysutil_dir
* p_subdir
;
348 const struct mystr
* p_subdir_str
=
349 str_list_get_pstr(&subdir_list
, subdir_index
);
350 if (str_equal_text(p_subdir_str
, ".") ||
351 str_equal_text(p_subdir_str
, ".."))
355 str_copy(&sub_str
, p_base_dir_str
);
356 str_append_char(&sub_str
, '/');
357 str_append_str(&sub_str
, p_subdir_str
);
358 p_subdir
= str_opendir(&sub_str
);
361 /* Unreadable, gone missing, etc. - no matter */
364 str_alloc_text(&dir_prefix_str
, "\r\n");
365 retval
= ftp_write_str(p_sess
, &dir_prefix_str
, target
);
371 retval
= transfer_dir_internal(p_sess
, is_control
, p_subdir
, &sub_str
,
372 p_option_str
, p_filter_str
, is_verbose
);
373 vsf_sysutil_closedir(p_subdir
);
382 str_list_free(&dir_list
);
383 str_list_free(&subdir_list
);
384 str_free(&dir_prefix_str
);
395 /* XXX - really, this should be refactored into a "buffered writer" object */
397 write_dir_list(struct vsf_session
* p_sess
, struct mystr_list
* p_dir_list
,
398 enum EVSFRWTarget target
)
400 /* This function writes out a list of strings to the client, over the
401 * data socket. We now coalesce the strings into fewer write() syscalls,
402 * which saved 33% CPU time writing a large directory.
405 unsigned int dir_index_max
= str_list_get_length(p_dir_list
);
406 unsigned int dir_index
;
407 struct mystr buf_str
= INIT_MYSTR
;
408 str_reserve(&buf_str
, VSFTP_DIR_BUFSIZE
);
409 for (dir_index
= 0; dir_index
< dir_index_max
; dir_index
++)
411 str_append_str(&buf_str
, str_list_get_pstr(p_dir_list
, dir_index
));
412 if (dir_index
== dir_index_max
- 1 ||
413 str_getlen(&buf_str
) +
414 str_getlen(str_list_get_pstr(p_dir_list
, dir_index
+ 1)) >
417 /* Writeout needed - we're either at the end, or we filled the buffer */
418 int writeret
= ftp_write_str(p_sess
, &buf_str
, target
);
431 struct vsf_transfer_ret
432 vsf_ftpdataio_transfer_file(struct vsf_session
* p_sess
, int remote_fd
,
433 int file_fd
, int is_recv
, int is_ascii
)
437 if (is_ascii
|| p_sess
->data_use_ssl
)
439 return do_file_send_rwloop(p_sess
, file_fd
, is_ascii
);
443 filesize_t curr_offset
= vsf_sysutil_get_file_offset(file_fd
);
444 filesize_t num_send
= calc_num_send(file_fd
, curr_offset
);
445 return do_file_send_sendfile(
446 p_sess
, remote_fd
, file_fd
, curr_offset
, num_send
);
451 return do_file_recv(p_sess
, file_fd
, is_ascii
);
455 static struct vsf_transfer_ret
456 do_file_send_rwloop(struct vsf_session
* p_sess
, int file_fd
, int is_ascii
)
458 static char* p_readbuf
;
459 static char* p_asciibuf
;
460 struct vsf_transfer_ret ret_struct
= { 0, 0 };
461 unsigned int chunk_size
= get_chunk_size();
462 char* p_writefrom_buf
;
466 vsf_secbuf_alloc(&p_readbuf
, VSFTP_DATA_BUFSIZE
);
472 /* NOTE!! * 2 factor because we can double the data by doing our ASCII
475 vsf_secbuf_alloc(&p_asciibuf
, VSFTP_DATA_BUFSIZE
* 2);
477 p_writefrom_buf
= p_asciibuf
;
481 p_writefrom_buf
= p_readbuf
;
485 unsigned int num_to_write
;
486 int retval
= vsf_sysutil_read(file_fd
, p_readbuf
, chunk_size
);
487 if (vsf_sysutil_retval_is_error(retval
))
489 ret_struct
.retval
= -1;
492 else if (retval
== 0)
499 struct bin_to_ascii_ret ret
=
500 vsf_ascii_bin_to_ascii(p_readbuf
,
502 (unsigned int) retval
,
504 num_to_write
= ret
.stored
;
505 prev_cr
= ret
.last_was_cr
;
509 num_to_write
= (unsigned int) retval
;
511 retval
= ftp_write_data(p_sess
, p_writefrom_buf
, num_to_write
);
512 if (!vsf_sysutil_retval_is_error(retval
))
514 ret_struct
.transferred
+= (unsigned int) retval
;
516 if (vsf_sysutil_retval_is_error(retval
) ||
517 (unsigned int) retval
!= num_to_write
)
519 ret_struct
.retval
= -2;
525 static struct vsf_transfer_ret
526 do_file_send_sendfile(struct vsf_session
* p_sess
, int net_fd
, int file_fd
,
527 filesize_t curr_file_offset
, filesize_t bytes_to_send
)
530 unsigned int chunk_size
= 0;
531 struct vsf_transfer_ret ret_struct
= { 0, 0 };
532 filesize_t init_file_offset
= curr_file_offset
;
533 filesize_t bytes_sent
;
534 if (p_sess
->bw_rate_max
)
536 chunk_size
= get_chunk_size();
538 /* Just because I can ;-) */
539 retval
= vsf_sysutil_sendfile(net_fd
, file_fd
, &curr_file_offset
,
540 bytes_to_send
, chunk_size
);
541 bytes_sent
= curr_file_offset
- init_file_offset
;
542 ret_struct
.transferred
= bytes_sent
;
543 if (vsf_sysutil_retval_is_error(retval
))
545 ret_struct
.retval
= -2;
548 else if (bytes_sent
!= bytes_to_send
)
550 ret_struct
.retval
= -2;
557 calc_num_send(int file_fd
, filesize_t init_offset
)
559 static struct vsf_sysutil_statbuf
* s_p_statbuf
;
560 filesize_t bytes_to_send
;
561 /* Work out how many bytes to send based on file size minus current offset */
562 vsf_sysutil_fstat(file_fd
, &s_p_statbuf
);
563 bytes_to_send
= vsf_sysutil_statbuf_get_size(s_p_statbuf
);
564 if (init_offset
< 0 || bytes_to_send
< 0)
566 die("calc_num_send: negative file offset or send count");
568 /* Don't underflow if some bonehead sets a REST greater than the file size */
569 if (init_offset
> bytes_to_send
)
575 bytes_to_send
-= init_offset
;
577 return bytes_to_send
;
580 static struct vsf_transfer_ret
581 do_file_recv(struct vsf_session
* p_sess
, int file_fd
, int is_ascii
)
583 static char* p_recvbuf
;
584 unsigned int num_to_write
;
585 struct vsf_transfer_ret ret_struct
= { 0, 0 };
586 unsigned int chunk_size
= get_chunk_size();
590 /* Now that we do ASCII conversion properly, the plus one is to cater for
591 * the fact we may need to stick a '\r' at the front of the buffer if the
592 * last buffer fragment eneded in a '\r' and the current buffer fragment
593 * does not start with a '\n'.
595 vsf_secbuf_alloc(&p_recvbuf
, VSFTP_DATA_BUFSIZE
+ 1);
599 const char* p_writebuf
= p_recvbuf
+ 1;
600 int retval
= ftp_read_data(p_sess
, p_recvbuf
+ 1, chunk_size
);
601 if (vsf_sysutil_retval_is_error(retval
))
603 ret_struct
.retval
= -2;
606 else if (retval
== 0 && !prev_cr
)
608 /* Transfer done, nifty */
611 num_to_write
= (unsigned int) retval
;
612 ret_struct
.transferred
+= num_to_write
;
615 /* Handle ASCII conversion if we have to. Note that using the same
616 * buffer for source and destination is safe, because the ASCII ->
617 * binary transform only ever results in a smaller file.
619 struct ascii_to_bin_ret ret
=
620 vsf_ascii_ascii_to_bin(p_recvbuf
, num_to_write
, prev_cr
);
621 num_to_write
= ret
.stored
;
622 prev_cr
= ret
.last_was_cr
;
623 p_writebuf
= ret
.p_buf
;
625 retval
= vsf_sysutil_write_loop(file_fd
, p_writebuf
, num_to_write
);
626 if (vsf_sysutil_retval_is_error(retval
) ||
627 (unsigned int) retval
!= num_to_write
)
629 ret_struct
.retval
= -1;
638 unsigned int ret
= VSFTP_DATA_BUFSIZE
;
639 if (tunable_trans_chunk_size
< VSFTP_DATA_BUFSIZE
&&
640 tunable_trans_chunk_size
> 0)
642 ret
= tunable_trans_chunk_size
;