1/* 2 * Part of Very Secure FTPd 3 * Licence: GPL v2 4 * Author: Chris Evans 5 * ftpdataio.c 6 * 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. 10 */ 11 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 33static void init_data_sock_params(struct vsf_session* p_sess, int sock_fd); 34static filesize_t calc_num_send(int file_fd, filesize_t init_offset); 35static struct vsf_transfer_ret do_file_send_sendfile( 36 struct vsf_session* p_sess, int net_fd, int file_fd, 37 filesize_t curr_file_offset, filesize_t bytes_to_send); 38static struct vsf_transfer_ret do_file_send_rwloop( 39 struct vsf_session* p_sess, int file_fd, int is_ascii); 40static struct vsf_transfer_ret do_file_recv( 41 struct vsf_session* p_sess, int file_fd, int is_ascii); 42static void handle_sigalrm(void* p_private); 43static void start_data_alarm(struct vsf_session* p_sess); 44static void handle_io(int retval, int fd, void* p_private); 45static int transfer_dir_internal( 46 struct vsf_session* p_sess, int is_control, struct vsf_sysutil_dir* p_dir, 47 const struct mystr* p_base_dir_str, const struct mystr* p_option_str, 48 const struct mystr* p_filter_str, int is_verbose); 49static int write_dir_list(struct vsf_session* p_sess, 50 struct mystr_list* p_dir_list, 51 enum EVSFRWTarget target); 52static unsigned int get_chunk_size(); 53 54void 55vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess) 56{ 57 int retval; 58 if (p_sess->data_fd == -1) 59 { 60 bug("no data descriptor in vsf_ftpdataio_dispose_transfer_fd"); 61 } 62 /* Reset the data connection alarm so it runs anew with the blocking close */ 63 start_data_alarm(p_sess); 64 vsf_sysutil_uninstall_io_handler(); 65 if (p_sess->p_data_ssl != 0) 66 { 67 ssl_data_close(p_sess); 68 } 69 /* This close() blocks because we set SO_LINGER */ 70 retval = vsf_sysutil_close_failok(p_sess->data_fd); 71 if (vsf_sysutil_retval_is_error(retval)) 72 { 73 /* Do it again without blocking. */ 74 vsf_sysutil_deactivate_linger_failok(p_sess->data_fd); 75 (void) vsf_sysutil_close_failok(p_sess->data_fd); 76 } 77 vsf_sysutil_clear_alarm(); 78 p_sess->data_fd = -1; 79} 80 81int 82vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess) 83{ 84 int remote_fd; 85 struct vsf_sysutil_sockaddr* p_accept_addr = 0; 86 vsf_sysutil_sockaddr_alloc(&p_accept_addr); 87 remote_fd = vsf_sysutil_accept_timeout(p_sess->pasv_listen_fd, p_accept_addr, 88 tunable_accept_timeout); 89 if (vsf_sysutil_retval_is_error(remote_fd)) 90 { 91 vsf_cmdio_write(p_sess, FTP_BADSENDCONN, 92 "Failed to establish connection."); 93 vsf_sysutil_sockaddr_clear(&p_accept_addr); 94 return remote_fd; 95 } 96 /* SECURITY: 97 * Reject the connection if it wasn't from the same IP as the 98 * control connection. 99 */ 100 if (!tunable_pasv_promiscuous) 101 { 102 if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_accept_addr)) 103 { 104 vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting."); 105 vsf_sysutil_close(remote_fd); 106 vsf_sysutil_sockaddr_clear(&p_accept_addr); 107 return -1; 108 } 109 } 110 vsf_sysutil_sockaddr_clear(&p_accept_addr); 111 init_data_sock_params(p_sess, remote_fd); 112 return remote_fd; 113} 114 115int 116vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess) 117{ 118 int retval; 119 int remote_fd; 120 if (tunable_connect_from_port_20) 121 { 122 if (tunable_one_process_model) 123 { 124 remote_fd = vsf_one_process_get_priv_data_sock(p_sess); 125 } 126 else 127 { 128 remote_fd = vsf_two_process_get_priv_data_sock(p_sess); 129 } 130 } 131 else 132 { 133 static struct vsf_sysutil_sockaddr* s_p_addr; 134 remote_fd = vsf_sysutil_get_ipsock(p_sess->p_local_addr); 135 vsf_sysutil_sockaddr_clone(&s_p_addr, p_sess->p_local_addr); 136 vsf_sysutil_sockaddr_set_port(s_p_addr, 0); 137 retval = vsf_sysutil_bind(remote_fd, s_p_addr); 138 if (retval != 0) 139 { 140 die("vsf_sysutil_bind"); 141 } 142 } 143 retval = vsf_sysutil_connect_timeout(remote_fd, p_sess->p_port_sockaddr, 144 tunable_connect_timeout); 145 if (vsf_sysutil_retval_is_error(retval)) 146 { 147 vsf_cmdio_write(p_sess, FTP_BADSENDCONN, 148 "Failed to establish connection."); 149 vsf_sysutil_close(remote_fd); 150 return -1; 151 } 152 init_data_sock_params(p_sess, remote_fd); 153 return remote_fd; 154} 155 156int 157vsf_ftpdataio_post_mark_connect(struct vsf_session* p_sess) 158{ 159 if (p_sess->data_use_ssl) 160 { 161 if (!ssl_accept(p_sess, p_sess->data_fd)) 162 { 163 vsf_cmdio_write( 164 p_sess, FTP_DATATLSBAD, "Secure connection negotiation failed."); 165 return 0; 166 } 167 } 168 return 1; 169} 170 171static void 172handle_sigalrm(void* p_private) 173{ 174 struct vsf_session* p_sess = (struct vsf_session*) p_private; 175 if (!p_sess->data_progress) 176 { 177 vsf_cmdio_write_exit(p_sess, FTP_DATA_TIMEOUT, 178 "Data timeout. Reconnect. Sorry."); 179 } 180 p_sess->data_progress = 0; 181 start_data_alarm(p_sess); 182} 183 184void 185start_data_alarm(struct vsf_session* p_sess) 186{ 187 if (tunable_data_connection_timeout > 0) 188 { 189 vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM, handle_sigalrm, p_sess); 190 vsf_sysutil_set_alarm(tunable_data_connection_timeout); 191 } 192} 193 194static void 195init_data_sock_params(struct vsf_session* p_sess, int sock_fd) 196{ 197 if (p_sess->data_fd != -1) 198 { 199 bug("data descriptor still present in init_data_sock_params"); 200 } 201 p_sess->data_fd = sock_fd; 202 p_sess->data_progress = 0; 203 vsf_sysutil_activate_keepalive(sock_fd); 204 /* And in the vague hope it might help... */ 205 vsf_sysutil_set_iptos_throughput(sock_fd); 206 /* Set up lingering, so that we wait for all data to transfer, and report 207 * more accurate transfer rates. 208 */ 209 vsf_sysutil_activate_linger(sock_fd); 210 /* Start the timeout monitor */ 211 vsf_sysutil_install_io_handler(handle_io, p_sess); 212 start_data_alarm(p_sess); 213} 214 215static void 216handle_io(int retval, int fd, void* p_private) 217{ 218 long curr_sec; 219 long curr_usec; 220 unsigned int bw_rate; 221 double elapsed; 222 double pause_time; 223 double rate_ratio; 224 struct vsf_session* p_sess = (struct vsf_session*) p_private; 225 if (p_sess->data_fd != fd || vsf_sysutil_retval_is_error(retval) || 226 retval == 0) 227 { 228 return; 229 } 230 /* Note that the session hasn't stalled, i.e. don't time it out */ 231 p_sess->data_progress = 1; 232 /* Apply bandwidth quotas via a little pause, if necessary */ 233 if (p_sess->bw_rate_max == 0) 234 { 235 return; 236 } 237 /* Calculate bandwidth rate */ 238 vsf_sysutil_update_cached_time(); 239 curr_sec = vsf_sysutil_get_cached_time_sec(); 240 curr_usec = vsf_sysutil_get_cached_time_usec(); 241 elapsed = (double) (curr_sec - p_sess->bw_send_start_sec); 242 elapsed += (double) (curr_usec - p_sess->bw_send_start_usec) / 243 (double) 1000000; 244 if (elapsed <= (double) 0) 245 { 246 elapsed = (double) 0.01; 247 } 248 bw_rate = (unsigned int) ((double) retval / elapsed); 249 if (bw_rate <= p_sess->bw_rate_max) 250 { 251 p_sess->bw_send_start_sec = curr_sec; 252 p_sess->bw_send_start_usec = curr_usec; 253 return; 254 } 255 /* Tut! Rate exceeded, calculate a pause to bring things back into line */ 256 rate_ratio = (double) bw_rate / (double) p_sess->bw_rate_max; 257 pause_time = (rate_ratio - (double) 1) * elapsed; 258 vsf_sysutil_sleep(pause_time); 259 vsf_sysutil_update_cached_time(); 260 p_sess->bw_send_start_sec = vsf_sysutil_get_cached_time_sec(); 261 p_sess->bw_send_start_usec = vsf_sysutil_get_cached_time_usec(); 262} 263 264int 265vsf_ftpdataio_transfer_dir(struct vsf_session* p_sess, int is_control, 266 struct vsf_sysutil_dir* p_dir, 267 const struct mystr* p_base_dir_str, 268 const struct mystr* p_option_str, 269 const struct mystr* p_filter_str, 270 int is_verbose) 271{ 272 return transfer_dir_internal(p_sess, is_control, p_dir, p_base_dir_str, 273 p_option_str, p_filter_str, is_verbose); 274} 275 276static int 277transfer_dir_internal(struct vsf_session* p_sess, int is_control, 278 struct vsf_sysutil_dir* p_dir, 279 const struct mystr* p_base_dir_str, 280 const struct mystr* p_option_str, 281 const struct mystr* p_filter_str, 282 int is_verbose) 283{ 284 struct mystr_list dir_list = INIT_STRLIST; 285 struct mystr_list subdir_list = INIT_STRLIST; 286 struct mystr dir_prefix_str = INIT_MYSTR; 287 struct mystr_list* p_subdir_list = 0; 288 struct str_locate_result loc_result = str_locate_char(p_option_str, 'R'); 289 int failed = 0; 290 enum EVSFRWTarget target = kVSFRWData; 291 if (is_control) 292 { 293 target = kVSFRWControl; 294 } 295 if (loc_result.found && tunable_ls_recurse_enable) 296 { 297 p_subdir_list = &subdir_list; 298 } 299 300// 2007.05 James { 301 char *session_user = p_sess->user_str.PRIVATE_HANDS_OFF_p_buf; 302 vsf_ls_populate_dir_list(session_user, &dir_list, p_subdir_list, p_dir, p_base_dir_str, 303 p_option_str, p_filter_str, is_verbose); 304// 2007.05 James } 305 306 if (p_subdir_list) 307 { 308 int retval; 309 str_copy(&dir_prefix_str, p_base_dir_str); 310 str_append_text(&dir_prefix_str, ":\r\n"); 311 retval = ftp_write_str(p_sess, &dir_prefix_str, target); 312 if (retval != 0) 313 { 314 failed = 1; 315 } 316 } 317 if (!failed) 318 { 319 failed = write_dir_list(p_sess, &dir_list, target); 320 } 321 /* Recurse into the subdirectories if required... */ 322 if (!failed) 323 { 324 struct mystr sub_str = INIT_MYSTR; 325 unsigned int num_subdirs = str_list_get_length(&subdir_list); 326 unsigned int subdir_index; 327 for (subdir_index = 0; subdir_index < num_subdirs; subdir_index++) 328 { 329 int retval; 330 struct vsf_sysutil_dir* p_subdir; 331 const struct mystr* p_subdir_str = 332 str_list_get_pstr(&subdir_list, subdir_index); 333 if (str_equal_text(p_subdir_str, ".") || 334 str_equal_text(p_subdir_str, "..")) 335 { 336 continue; 337 } 338 str_copy(&sub_str, p_base_dir_str); 339 str_append_char(&sub_str, '/'); 340 str_append_str(&sub_str, p_subdir_str); 341 p_subdir = str_opendir(&sub_str); 342 if (p_subdir == 0) 343 { 344 /* Unreadable, gone missing, etc. - no matter */ 345 continue; 346 } 347 str_alloc_text(&dir_prefix_str, "\r\n"); 348 retval = ftp_write_str(p_sess, &dir_prefix_str, target); 349 if (retval != 0) 350 { 351 failed = 1; 352 break; 353 } 354 retval = transfer_dir_internal(p_sess, is_control, p_subdir, &sub_str, 355 p_option_str, p_filter_str, is_verbose); 356 vsf_sysutil_closedir(p_subdir); 357 if (retval != 0) 358 { 359 failed = 1; 360 break; 361 } 362 } 363 str_free(&sub_str); 364 } 365 str_list_free(&dir_list); 366 str_list_free(&subdir_list); 367 str_free(&dir_prefix_str); 368 if (!failed) 369 { 370 return 0; 371 } 372 else 373 { 374 return -1; 375 } 376} 377 378/* XXX - really, this should be refactored into a "buffered writer" object */ 379static int 380write_dir_list(struct vsf_session* p_sess, struct mystr_list* p_dir_list, 381 enum EVSFRWTarget target) 382{ 383 /* This function writes out a list of strings to the client, over the 384 * data socket. We now coalesce the strings into fewer write() syscalls, 385 * which saved 33% CPU time writing a large directory. 386 */ 387 int retval = 0; 388 unsigned int dir_index_max = str_list_get_length(p_dir_list); 389 unsigned int dir_index; 390 struct mystr buf_str = INIT_MYSTR; 391 str_reserve(&buf_str, VSFTP_DIR_BUFSIZE); 392 for (dir_index = 0; dir_index < dir_index_max; dir_index++) 393 { 394 str_append_str(&buf_str, str_list_get_pstr(p_dir_list, dir_index)); 395 if (dir_index == dir_index_max - 1 || 396 str_getlen(&buf_str) + 397 str_getlen(str_list_get_pstr(p_dir_list, dir_index + 1)) > 398 VSFTP_DIR_BUFSIZE) 399 { 400 /* Writeout needed - we're either at the end, or we filled the buffer */ 401 int writeret = ftp_write_str(p_sess, &buf_str, target); 402 if (writeret != 0) 403 { 404 retval = 1; 405 break; 406 } 407 str_empty(&buf_str); 408 } 409 } 410 str_free(&buf_str); 411 return retval; 412} 413 414struct vsf_transfer_ret 415vsf_ftpdataio_transfer_file(struct vsf_session* p_sess, int remote_fd, 416 int file_fd, int is_recv, int is_ascii) 417{ 418 if (!is_recv) 419 { 420 if (is_ascii || p_sess->data_use_ssl) 421 { 422 return do_file_send_rwloop(p_sess, file_fd, is_ascii); 423 } 424 else 425 { 426 filesize_t curr_offset = vsf_sysutil_get_file_offset(file_fd); 427 filesize_t num_send = calc_num_send(file_fd, curr_offset); 428 return do_file_send_sendfile( 429 p_sess, remote_fd, file_fd, curr_offset, num_send); 430 } 431 } 432 else 433 { 434 return do_file_recv(p_sess, file_fd, is_ascii); 435 } 436} 437 438static struct vsf_transfer_ret 439do_file_send_rwloop(struct vsf_session* p_sess, int file_fd, int is_ascii) 440{ 441 static char* p_readbuf; 442 static char* p_asciibuf; 443 struct vsf_transfer_ret ret_struct = { 0, 0 }; 444 unsigned int chunk_size = get_chunk_size(); 445 char* p_writefrom_buf; 446 if (p_readbuf == 0) 447 { 448 /* NOTE!! * 2 factor because we can double the data by doing our ASCII 449 * linefeed mangling 450 */ 451 vsf_secbuf_alloc(&p_asciibuf, VSFTP_DATA_BUFSIZE * 2); 452 vsf_secbuf_alloc(&p_readbuf, VSFTP_DATA_BUFSIZE); 453 } 454 if (is_ascii) 455 { 456 p_writefrom_buf = p_asciibuf; 457 } 458 else 459 { 460 p_writefrom_buf = p_readbuf; 461 } 462 while (1) 463 { 464 unsigned int num_to_write; 465 int retval = vsf_sysutil_read(file_fd, p_readbuf, chunk_size); 466 if (vsf_sysutil_retval_is_error(retval)) 467 { 468 ret_struct.retval = -1; 469 return ret_struct; 470 } 471 else if (retval == 0) 472 { 473 /* Success - cool */ 474 return ret_struct; 475 } 476 if (is_ascii) 477 { 478 num_to_write = vsf_ascii_bin_to_ascii(p_readbuf, p_asciibuf, 479 (unsigned int) retval); 480 } 481 else 482 { 483 num_to_write = (unsigned int) retval; 484 } 485 retval = ftp_write_data(p_sess, p_writefrom_buf, num_to_write); 486 if (vsf_sysutil_retval_is_error(retval) || 487 (unsigned int) retval != num_to_write) 488 { 489 ret_struct.retval = -2; 490 return ret_struct; 491 } 492 ret_struct.transferred += (unsigned int) retval; 493 } 494} 495 496static struct vsf_transfer_ret 497do_file_send_sendfile(struct vsf_session* p_sess, int net_fd, int file_fd, 498 filesize_t curr_file_offset, filesize_t bytes_to_send) 499{ 500 int retval; 501 unsigned int chunk_size = 0; 502 struct vsf_transfer_ret ret_struct = { 0, 0 }; 503 filesize_t init_file_offset = curr_file_offset; 504 filesize_t bytes_sent; 505 if (p_sess->bw_rate_max) 506 { 507 chunk_size = get_chunk_size(); 508 } 509 /* Just because I can ;-) */ 510 retval = vsf_sysutil_sendfile(net_fd, file_fd, &curr_file_offset, 511 bytes_to_send, chunk_size); 512 bytes_sent = curr_file_offset - init_file_offset; 513 ret_struct.transferred = bytes_sent; 514 if (vsf_sysutil_retval_is_error(retval)) 515 { 516 ret_struct.retval = -2; 517 return ret_struct; 518 } 519 else if (bytes_sent != bytes_to_send) 520 { 521 ret_struct.retval = -2; 522 return ret_struct; 523 } 524 return ret_struct; 525} 526 527static filesize_t 528calc_num_send(int file_fd, filesize_t init_offset) 529{ 530 static struct vsf_sysutil_statbuf* s_p_statbuf; 531 filesize_t bytes_to_send; 532 /* Work out how many bytes to send based on file size minus current offset */ 533 vsf_sysutil_fstat(file_fd, &s_p_statbuf); 534 bytes_to_send = vsf_sysutil_statbuf_get_size(s_p_statbuf); 535 if (init_offset < 0 || bytes_to_send < 0) 536 { 537 die("calc_num_send: negative file offset or send count"); 538 } 539 /* Don't underflow if some bonehead sets a REST greater than the file size */ 540 if (init_offset > bytes_to_send) 541 { 542 bytes_to_send = 0; 543 } 544 else 545 { 546 bytes_to_send -= init_offset; 547 } 548 return bytes_to_send; 549} 550 551static struct vsf_transfer_ret 552do_file_recv(struct vsf_session* p_sess, int file_fd, int is_ascii) 553{ 554 static char* p_recvbuf; 555 unsigned int num_to_write; 556 struct vsf_transfer_ret ret_struct = { 0, 0 }; 557 unsigned int chunk_size = get_chunk_size(); 558 int prev_cr = 0; 559 if (p_recvbuf == 0) 560 { 561 /* Now that we do ASCII conversion properly, the plus one is to cater for 562 * the fact we may need to stick a '\r' at the front of the buffer if the 563 * last buffer fragment eneded in a '\r' and the current buffer fragment 564 * does not start with a '\n'. 565 */ 566 vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE + 1); 567 } 568 while (1) 569 { 570 const char* p_writebuf = p_recvbuf + 1; 571 int retval = ftp_read_data(p_sess, p_recvbuf + 1, chunk_size); 572 if (vsf_sysutil_retval_is_error(retval)) 573 { 574 ret_struct.retval = -2; 575 return ret_struct; 576 } 577 else if (retval == 0 && !prev_cr) 578 { 579 /* Transfer done, nifty */ 580 return ret_struct; 581 } 582 num_to_write = (unsigned int) retval; 583 ret_struct.transferred += num_to_write; 584 if (is_ascii) 585 { 586 /* Handle ASCII conversion if we have to. Note that using the same 587 * buffer for source and destination is safe, because the ASCII -> 588 * binary transform only ever results in a smaller file. 589 */ 590 struct ascii_to_bin_ret ret = 591 vsf_ascii_ascii_to_bin(p_recvbuf, num_to_write, prev_cr); 592 num_to_write = ret.stored; 593 prev_cr = ret.last_was_cr; 594 p_writebuf = ret.p_buf; 595 } 596 retval = vsf_sysutil_write_loop(file_fd, p_writebuf, num_to_write); 597 if (vsf_sysutil_retval_is_error(retval) || 598 (unsigned int) retval != num_to_write) 599 { 600 ret_struct.retval = -1; 601 return ret_struct; 602 } 603 } 604} 605 606static unsigned int 607get_chunk_size() 608{ 609 unsigned int ret = VSFTP_DATA_BUFSIZE; 610 if (tunable_trans_chunk_size < VSFTP_DATA_BUFSIZE && 611 tunable_trans_chunk_size > 0) 612 { 613 ret = tunable_trans_chunk_size; 614 if (ret < 4096) 615 { 616 ret = 4096; 617 } 618 } 619 return ret; 620} 621 622