vsftpd 3.0.2
[tomato/tomato-dir865l.git] / release / src / router / vsftpd / ftpdataio.c
blob43d777c88a3b8d7478d148d09bc23191bef75fd8
1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Chris Evans
5 * ftpdataio.c
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"
13 #include "session.h"
14 #include "ftpcmdio.h"
15 #include "ftpcodes.h"
16 #include "utility.h"
17 #include "tunables.h"
18 #include "defs.h"
19 #include "str.h"
20 #include "strlist.h"
21 #include "sysutil.h"
22 #include "logging.h"
23 #include "secbuf.h"
24 #include "sysstr.h"
25 #include "sysdeputil.h"
26 #include "ascii.h"
27 #include "oneprocess.h"
28 #include "twoprocess.h"
29 #include "ls.h"
30 #include "ssl.h"
31 #include "readwrite.h"
32 #include "privsock.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();
55 int
56 vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
58 int dispose_ret = 1;
59 int retval;
60 if (p_sess->data_fd == -1)
62 bug("no data descriptor in vsf_ftpdataio_dispose_transfer_fd");
64 vsf_sysutil_uninstall_io_handler();
65 if (p_sess->data_use_ssl && p_sess->ssl_slave_active)
67 char result;
68 start_data_alarm(p_sess);
69 priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_CLOSE);
70 result = priv_sock_get_result(p_sess->ssl_consumer_fd);
71 if (result != PRIV_SOCK_RESULT_OK)
73 dispose_ret = 0;
76 else if (p_sess->p_data_ssl)
78 start_data_alarm(p_sess);
79 dispose_ret = ssl_data_close(p_sess);
81 if (!p_sess->abor_received && !p_sess->data_timeout && dispose_ret == 1)
83 /* If we didn't get a failure, linger on the close() in order to get more
84 * accurate transfer times.
86 start_data_alarm(p_sess);
87 vsf_sysutil_activate_linger(p_sess->data_fd);
89 /* This close() blocks because we set SO_LINGER */
90 retval = vsf_sysutil_close_failok(p_sess->data_fd);
91 if (vsf_sysutil_retval_is_error(retval))
93 /* Do it again without blocking. */
94 vsf_sysutil_deactivate_linger_failok(p_sess->data_fd);
95 (void) vsf_sysutil_close_failok(p_sess->data_fd);
97 p_sess->data_fd = -1;
98 if (tunable_data_connection_timeout > 0)
100 vsf_sysutil_clear_alarm();
102 if (p_sess->abor_received || p_sess->data_timeout)
104 dispose_ret = 0;
106 return dispose_ret;
110 vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess)
112 int remote_fd;
113 if (tunable_one_process_model)
115 remote_fd = vsf_one_process_get_pasv_fd(p_sess);
117 else
119 remote_fd = vsf_two_process_get_pasv_fd(p_sess);
121 /* Yes, yes, hardcoded bad I know. */
122 if (remote_fd == -1)
124 vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
125 "Failed to establish connection.");
126 return remote_fd;
128 else if (remote_fd == -2)
130 vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting.");
131 return -1;
133 init_data_sock_params(p_sess, remote_fd);
134 return remote_fd;
138 vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess)
140 int remote_fd;
141 if (tunable_one_process_model || tunable_port_promiscuous)
143 remote_fd = vsf_one_process_get_priv_data_sock(p_sess);
145 else
147 remote_fd = vsf_two_process_get_priv_data_sock(p_sess);
149 if (vsf_sysutil_retval_is_error(remote_fd))
151 vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
152 "Failed to establish connection.");
153 return -1;
155 init_data_sock_params(p_sess, remote_fd);
156 return remote_fd;
160 vsf_ftpdataio_post_mark_connect(struct vsf_session* p_sess)
162 int ret = 0;
163 if (!p_sess->data_use_ssl)
165 return 1;
167 if (!p_sess->ssl_slave_active)
169 ret = ssl_accept(p_sess, p_sess->data_fd);
171 else
173 int sock_ret;
174 priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_HANDSHAKE);
175 priv_sock_send_fd(p_sess->ssl_consumer_fd, p_sess->data_fd);
176 sock_ret = priv_sock_get_result(p_sess->ssl_consumer_fd);
177 if (sock_ret == PRIV_SOCK_RESULT_OK)
179 ret = 1;
182 if (ret != 1)
184 static struct mystr s_err_msg;
185 str_alloc_text(&s_err_msg, "SSL connection failed");
186 if (tunable_require_ssl_reuse)
188 str_append_text(&s_err_msg, "; session reuse required");
189 str_append_text(
190 &s_err_msg, ": see require_ssl_reuse option in vsftpd.conf man page");
192 vsf_cmdio_write_str(p_sess, FTP_DATATLSBAD, &s_err_msg);
194 return ret;
197 static void
198 handle_sigalrm(void* p_private)
200 struct vsf_session* p_sess = (struct vsf_session*) p_private;
201 if (!p_sess->data_progress)
203 p_sess->data_timeout = 1;
204 vsf_sysutil_shutdown_failok(p_sess->data_fd);
205 vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD);
206 vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
208 else
210 p_sess->data_progress = 0;
211 start_data_alarm(p_sess);
215 void
216 start_data_alarm(struct vsf_session* p_sess)
218 if (tunable_data_connection_timeout > 0)
220 vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM,
221 handle_sigalrm,
222 p_sess,
224 vsf_sysutil_set_alarm(tunable_data_connection_timeout);
226 else if (tunable_idle_session_timeout > 0)
228 vsf_sysutil_clear_alarm();
232 static void
233 init_data_sock_params(struct vsf_session* p_sess, int sock_fd)
235 if (p_sess->data_fd != -1)
237 bug("data descriptor still present in init_data_sock_params");
239 p_sess->data_fd = sock_fd;
240 p_sess->data_progress = 0;
241 vsf_sysutil_activate_keepalive(sock_fd);
242 /* And in the vague hope it might help... */
243 vsf_sysutil_set_iptos_throughput(sock_fd);
244 /* Start the timeout monitor */
245 vsf_sysutil_install_io_handler(handle_io, p_sess);
246 start_data_alarm(p_sess);
249 static void
250 handle_io(int retval, int fd, void* p_private)
252 long curr_sec;
253 long curr_usec;
254 unsigned int bw_rate;
255 double elapsed;
256 double pause_time;
257 double rate_ratio;
258 struct vsf_session* p_sess = (struct vsf_session*) p_private;
259 if (p_sess->data_fd != fd || vsf_sysutil_retval_is_error(retval) ||
260 retval == 0)
262 return;
264 /* Note that the session hasn't stalled, i.e. don't time it out */
265 p_sess->data_progress = 1;
266 /* Apply bandwidth quotas via a little pause, if necessary */
267 if (p_sess->bw_rate_max == 0)
269 return;
271 /* Calculate bandwidth rate */
272 curr_sec = vsf_sysutil_get_time_sec();
273 curr_usec = vsf_sysutil_get_time_usec();
274 elapsed = (double) (curr_sec - p_sess->bw_send_start_sec);
275 elapsed += (double) (curr_usec - p_sess->bw_send_start_usec) /
276 (double) 1000000;
277 if (elapsed <= (double) 0)
279 elapsed = (double) 0.01;
281 bw_rate = (unsigned int) ((double) retval / elapsed);
282 if (bw_rate <= p_sess->bw_rate_max)
284 p_sess->bw_send_start_sec = curr_sec;
285 p_sess->bw_send_start_usec = curr_usec;
286 return;
288 /* Tut! Rate exceeded, calculate a pause to bring things back into line */
289 rate_ratio = (double) bw_rate / (double) p_sess->bw_rate_max;
290 pause_time = (rate_ratio - (double) 1) * elapsed;
291 vsf_sysutil_sleep(pause_time);
292 p_sess->bw_send_start_sec = vsf_sysutil_get_time_sec();
293 p_sess->bw_send_start_usec = vsf_sysutil_get_time_usec();
297 vsf_ftpdataio_transfer_dir(struct vsf_session* p_sess, int is_control,
298 struct vsf_sysutil_dir* p_dir,
299 const struct mystr* p_base_dir_str,
300 const struct mystr* p_option_str,
301 const struct mystr* p_filter_str,
302 int is_verbose)
304 return transfer_dir_internal(p_sess, is_control, p_dir, p_base_dir_str,
305 p_option_str, p_filter_str, is_verbose);
308 static int
309 transfer_dir_internal(struct vsf_session* p_sess, int is_control,
310 struct vsf_sysutil_dir* p_dir,
311 const struct mystr* p_base_dir_str,
312 const struct mystr* p_option_str,
313 const struct mystr* p_filter_str,
314 int is_verbose)
316 struct mystr_list dir_list = INIT_STRLIST;
317 struct mystr_list subdir_list = INIT_STRLIST;
318 struct mystr dir_prefix_str = INIT_MYSTR;
319 struct mystr_list* p_subdir_list = 0;
320 struct str_locate_result loc_result = str_locate_char(p_option_str, 'R');
321 int failed = 0;
322 enum EVSFRWTarget target = kVSFRWData;
323 if (is_control)
325 target = kVSFRWControl;
327 if (loc_result.found && tunable_ls_recurse_enable)
329 p_subdir_list = &subdir_list;
331 vsf_ls_populate_dir_list(&dir_list, p_subdir_list, p_dir, p_base_dir_str,
332 p_option_str, p_filter_str, is_verbose);
333 if (p_subdir_list)
335 int retval;
336 str_copy(&dir_prefix_str, p_base_dir_str);
337 str_append_text(&dir_prefix_str, ":\r\n");
338 retval = ftp_write_str(p_sess, &dir_prefix_str, target);
339 if (retval != 0)
341 failed = 1;
344 if (!failed)
346 failed = write_dir_list(p_sess, &dir_list, target);
348 /* Recurse into the subdirectories if required... */
349 if (!failed)
351 struct mystr sub_str = INIT_MYSTR;
352 unsigned int num_subdirs = str_list_get_length(&subdir_list);
353 unsigned int subdir_index;
354 for (subdir_index = 0; subdir_index < num_subdirs; subdir_index++)
356 int retval;
357 struct vsf_sysutil_dir* p_subdir;
358 const struct mystr* p_subdir_str =
359 str_list_get_pstr(&subdir_list, subdir_index);
360 if (str_equal_text(p_subdir_str, ".") ||
361 str_equal_text(p_subdir_str, ".."))
363 continue;
365 str_copy(&sub_str, p_base_dir_str);
366 str_append_char(&sub_str, '/');
367 str_append_str(&sub_str, p_subdir_str);
368 p_subdir = str_opendir(&sub_str);
369 if (p_subdir == 0)
371 /* Unreadable, gone missing, etc. - no matter */
372 continue;
374 str_alloc_text(&dir_prefix_str, "\r\n");
375 retval = ftp_write_str(p_sess, &dir_prefix_str, target);
376 if (retval != 0)
378 failed = 1;
379 vsf_sysutil_closedir(p_subdir);
380 break;
382 retval = transfer_dir_internal(p_sess, is_control, p_subdir, &sub_str,
383 p_option_str, p_filter_str, is_verbose);
384 vsf_sysutil_closedir(p_subdir);
385 if (retval != 0)
387 failed = 1;
388 break;
391 str_free(&sub_str);
393 str_list_free(&dir_list);
394 str_list_free(&subdir_list);
395 str_free(&dir_prefix_str);
396 if (!failed)
398 return 0;
400 else
402 return -1;
406 /* XXX - really, this should be refactored into a "buffered writer" object */
407 static int
408 write_dir_list(struct vsf_session* p_sess, struct mystr_list* p_dir_list,
409 enum EVSFRWTarget target)
411 /* This function writes out a list of strings to the client, over the
412 * data socket. We now coalesce the strings into fewer write() syscalls,
413 * which saved 33% CPU time writing a large directory.
415 int retval = 0;
416 unsigned int dir_index_max = str_list_get_length(p_dir_list);
417 unsigned int dir_index;
418 struct mystr buf_str = INIT_MYSTR;
419 str_reserve(&buf_str, VSFTP_DIR_BUFSIZE);
420 for (dir_index = 0; dir_index < dir_index_max; dir_index++)
422 str_append_str(&buf_str, str_list_get_pstr(p_dir_list, dir_index));
423 if (dir_index == dir_index_max - 1 ||
424 str_getlen(&buf_str) +
425 str_getlen(str_list_get_pstr(p_dir_list, dir_index + 1)) >
426 VSFTP_DIR_BUFSIZE)
428 /* Writeout needed - we're either at the end, or we filled the buffer */
429 int writeret = ftp_write_str(p_sess, &buf_str, target);
430 if (writeret != 0)
432 retval = 1;
433 break;
435 str_empty(&buf_str);
438 str_free(&buf_str);
439 return retval;
442 struct vsf_transfer_ret
443 vsf_ftpdataio_transfer_file(struct vsf_session* p_sess, int remote_fd,
444 int file_fd, int is_recv, int is_ascii)
446 if (!is_recv)
448 if (is_ascii || p_sess->data_use_ssl)
450 return do_file_send_rwloop(p_sess, file_fd, is_ascii);
452 else
454 filesize_t curr_offset = vsf_sysutil_get_file_offset(file_fd);
455 filesize_t num_send = calc_num_send(file_fd, curr_offset);
456 return do_file_send_sendfile(
457 p_sess, remote_fd, file_fd, curr_offset, num_send);
460 else
462 return do_file_recv(p_sess, file_fd, is_ascii);
466 static struct vsf_transfer_ret
467 do_file_send_rwloop(struct vsf_session* p_sess, int file_fd, int is_ascii)
469 static char* p_readbuf;
470 static char* p_asciibuf;
471 struct vsf_transfer_ret ret_struct = { 0, 0 };
472 unsigned int chunk_size = get_chunk_size();
473 char* p_writefrom_buf;
474 int prev_cr = 0;
475 if (p_readbuf == 0)
477 vsf_secbuf_alloc(&p_readbuf, VSFTP_DATA_BUFSIZE);
479 if (is_ascii)
481 if (p_asciibuf == 0)
483 /* NOTE!! * 2 factor because we can double the data by doing our ASCII
484 * linefeed mangling
486 vsf_secbuf_alloc(&p_asciibuf, VSFTP_DATA_BUFSIZE * 2);
488 p_writefrom_buf = p_asciibuf;
490 else
492 p_writefrom_buf = p_readbuf;
494 while (1)
496 unsigned int num_to_write;
497 int retval = vsf_sysutil_read(file_fd, p_readbuf, chunk_size);
498 if (vsf_sysutil_retval_is_error(retval))
500 ret_struct.retval = -1;
501 return ret_struct;
503 else if (retval == 0)
505 /* Success - cool */
506 return ret_struct;
508 if (is_ascii)
510 struct bin_to_ascii_ret ret =
511 vsf_ascii_bin_to_ascii(p_readbuf,
512 p_asciibuf,
513 (unsigned int) retval,
514 prev_cr);
515 num_to_write = ret.stored;
516 prev_cr = ret.last_was_cr;
518 else
520 num_to_write = (unsigned int) retval;
522 retval = ftp_write_data(p_sess, p_writefrom_buf, num_to_write);
523 if (!vsf_sysutil_retval_is_error(retval))
525 ret_struct.transferred += (unsigned int) retval;
527 if (vsf_sysutil_retval_is_error(retval) ||
528 (unsigned int) retval != num_to_write)
530 ret_struct.retval = -2;
531 return ret_struct;
536 static struct vsf_transfer_ret
537 do_file_send_sendfile(struct vsf_session* p_sess, int net_fd, int file_fd,
538 filesize_t curr_file_offset, filesize_t bytes_to_send)
540 int retval;
541 unsigned int chunk_size = 0;
542 struct vsf_transfer_ret ret_struct = { 0, 0 };
543 filesize_t init_file_offset = curr_file_offset;
544 filesize_t bytes_sent;
545 if (p_sess->bw_rate_max)
547 chunk_size = get_chunk_size();
549 /* Just because I can ;-) */
550 retval = vsf_sysutil_sendfile(net_fd, file_fd, &curr_file_offset,
551 bytes_to_send, chunk_size);
552 bytes_sent = curr_file_offset - init_file_offset;
553 ret_struct.transferred = bytes_sent;
554 if (vsf_sysutil_retval_is_error(retval))
556 ret_struct.retval = -2;
557 return ret_struct;
559 else if (bytes_sent != bytes_to_send)
561 ret_struct.retval = -2;
562 return ret_struct;
564 return ret_struct;
567 static filesize_t
568 calc_num_send(int file_fd, filesize_t init_offset)
570 static struct vsf_sysutil_statbuf* s_p_statbuf;
571 filesize_t bytes_to_send;
572 /* Work out how many bytes to send based on file size minus current offset */
573 vsf_sysutil_fstat(file_fd, &s_p_statbuf);
574 bytes_to_send = vsf_sysutil_statbuf_get_size(s_p_statbuf);
575 if (init_offset < 0 || bytes_to_send < 0)
577 die("calc_num_send: negative file offset or send count");
579 /* Don't underflow if some bonehead sets a REST greater than the file size */
580 if (init_offset > bytes_to_send)
582 bytes_to_send = 0;
584 else
586 bytes_to_send -= init_offset;
588 return bytes_to_send;
591 static struct vsf_transfer_ret
592 do_file_recv(struct vsf_session* p_sess, int file_fd, int is_ascii)
594 static char* p_recvbuf;
595 unsigned int num_to_write;
596 struct vsf_transfer_ret ret_struct = { 0, 0 };
597 unsigned int chunk_size = get_chunk_size();
598 int prev_cr = 0;
599 if (p_recvbuf == 0)
601 /* Now that we do ASCII conversion properly, the plus one is to cater for
602 * the fact we may need to stick a '\r' at the front of the buffer if the
603 * last buffer fragment eneded in a '\r' and the current buffer fragment
604 * does not start with a '\n'.
606 vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE + 1);
608 while (1)
610 const char* p_writebuf = p_recvbuf + 1;
611 int retval = ftp_read_data(p_sess, p_recvbuf + 1, chunk_size);
612 if (vsf_sysutil_retval_is_error(retval))
614 ret_struct.retval = -2;
615 return ret_struct;
617 else if (retval == 0 && !prev_cr)
619 /* Transfer done, nifty */
620 return ret_struct;
622 num_to_write = (unsigned int) retval;
623 ret_struct.transferred += num_to_write;
624 if (is_ascii)
626 /* Handle ASCII conversion if we have to. Note that using the same
627 * buffer for source and destination is safe, because the ASCII ->
628 * binary transform only ever results in a smaller file.
630 struct ascii_to_bin_ret ret =
631 vsf_ascii_ascii_to_bin(p_recvbuf, num_to_write, prev_cr);
632 num_to_write = ret.stored;
633 prev_cr = ret.last_was_cr;
634 p_writebuf = ret.p_buf;
636 retval = vsf_sysutil_write_loop(file_fd, p_writebuf, num_to_write);
637 if (vsf_sysutil_retval_is_error(retval) ||
638 (unsigned int) retval != num_to_write)
640 ret_struct.retval = -1;
641 return ret_struct;
646 static unsigned int
647 get_chunk_size()
649 unsigned int ret = VSFTP_DATA_BUFSIZE;
650 if (tunable_trans_chunk_size < VSFTP_DATA_BUFSIZE &&
651 tunable_trans_chunk_size > 0)
653 ret = tunable_trans_chunk_size;
654 if (ret < 4096)
656 ret = 4096;
659 return ret;