aboutsummaryrefslogtreecommitdiff
path: root/networking/ftpd.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-03-15 15:54:58 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-03-15 15:54:58 +0000
commitfc58ba1298519712bf42e44ab71a916fb0dfba05 (patch)
tree0058e41c2df77f93efe1e5007961d1bea4195297 /networking/ftpd.c
parent823f10b8f0f3ad23231ab378d68b6cc884b570ef (diff)
downloadbusybox-w32-fc58ba1298519712bf42e44ab71a916fb0dfba05.tar.gz
busybox-w32-fc58ba1298519712bf42e44ab71a916fb0dfba05.tar.bz2
busybox-w32-fc58ba1298519712bf42e44ab71a916fb0dfba05.zip
ftpd: fix the bug where >2GB file ops report errors;
make a few simplifications; add TODOs. function old new delta port_or_pasv_was_seen - 37 +37 get_remote_transfer_fd 104 109 +5 handle_upload_common 265 260 -5 handle_dir_common 228 223 -5 popen_ls 211 203 -8 ftpd_main 1825 1815 -10 data_transfer_checks_ok 37 - -37 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 1/4 up/down: 42/-65) Total: -23 bytes
Diffstat (limited to 'networking/ftpd.c')
-rw-r--r--networking/ftpd.c184
1 files changed, 119 insertions, 65 deletions
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 3faa3ed7d..37b340e78 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -268,6 +268,29 @@ handle_stat(void)
268 STR(FTP_STATOK)" Ok\r\n"); 268 STR(FTP_STATOK)" Ok\r\n");
269} 269}
270 270
271/* TODO: implement FEAT. Example:
272# nc -vvv ftp.kernel.org 21
273ftp.kernel.org (130.239.17.4:21) open
274220 Welcome to ftp.kernel.org.
275FEAT
276211-Features:
277 EPRT
278 EPSV
279 MDTM
280 PASV
281 REST STREAM
282 SIZE
283 TVFS
284 UTF8
285211 End
286HELP
287214-The following commands are recognized.
288 ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
289 MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
290 RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
291 XPWD XRMD
292214 Help OK.
293*/
271static void 294static void
272handle_help(void) 295handle_help(void)
273{ 296{
@@ -283,6 +306,29 @@ handle_help(void)
283 306
284/* Download commands */ 307/* Download commands */
285 308
309static inline int
310port_active(void)
311{
312 return (G.port_addr != NULL);
313}
314
315static inline int
316pasv_active(void)
317{
318 return (G.pasv_listen_fd > STDOUT_FILENO);
319}
320
321static void
322port_pasv_cleanup(void)
323{
324 free(G.port_addr);
325 G.port_addr = NULL;
326 if (G.pasv_listen_fd > STDOUT_FILENO)
327 close(G.pasv_listen_fd);
328 G.pasv_listen_fd = -1;
329}
330
331/* On error, emits error code to the peer */
286static int 332static int
287ftpdataio_get_pasv_fd(void) 333ftpdataio_get_pasv_fd(void)
288{ 334{
@@ -299,28 +345,26 @@ ftpdataio_get_pasv_fd(void)
299 return remote_fd; 345 return remote_fd;
300} 346}
301 347
302static inline int 348/* Clears port/pasv data.
303port_active(void) 349 * This means we dont waste resources, for example, keeping
304{ 350 * PASV listening socket open when it is no longer needed.
305 return (G.port_addr != NULL); 351 * On error, emits error code to the peer (or exits).
306} 352 * On success, emits p_status_msg to the peer.
307 353 */
308static inline int
309pasv_active(void)
310{
311 return (G.pasv_listen_fd > STDOUT_FILENO);
312}
313
314static int 354static int
315get_remote_transfer_fd(const char *p_status_msg) 355get_remote_transfer_fd(const char *p_status_msg)
316{ 356{
317 int remote_fd; 357 int remote_fd;
318 358
319 if (pasv_active()) 359 if (pasv_active())
360 /* On error, emits error code to the peer */
320 remote_fd = ftpdataio_get_pasv_fd(); 361 remote_fd = ftpdataio_get_pasv_fd();
321 else 362 else
363 /* Exits on error */
322 remote_fd = xconnect_stream(G.port_addr); 364 remote_fd = xconnect_stream(G.port_addr);
323 365
366 port_pasv_cleanup();
367
324 if (remote_fd < 0) 368 if (remote_fd < 0)
325 return remote_fd; 369 return remote_fd;
326 370
@@ -328,8 +372,9 @@ get_remote_transfer_fd(const char *p_status_msg)
328 return remote_fd; 372 return remote_fd;
329} 373}
330 374
375/* If there were neither PASV nor PORT, emits error code to the peer */
331static int 376static int
332data_transfer_checks_ok(void) 377port_or_pasv_was_seen(void)
333{ 378{
334 if (!pasv_active() && !port_active()) { 379 if (!pasv_active() && !port_active()) {
335 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT or PASV first\r\n"); 380 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT or PASV first\r\n");
@@ -339,16 +384,7 @@ data_transfer_checks_ok(void)
339 return 1; 384 return 1;
340} 385}
341 386
342static void 387/* Exits on error */
343port_pasv_cleanup(void)
344{
345 free(G.port_addr);
346 G.port_addr = NULL;
347 if (G.pasv_listen_fd > STDOUT_FILENO)
348 close(G.pasv_listen_fd);
349 G.pasv_listen_fd = -1;
350}
351
352static unsigned 388static unsigned
353bind_for_passive_mode(void) 389bind_for_passive_mode(void)
354{ 390{
@@ -370,6 +406,7 @@ bind_for_passive_mode(void)
370 return port; 406 return port;
371} 407}
372 408
409/* Exits on error */
373static void 410static void
374handle_pasv(void) 411handle_pasv(void)
375{ 412{
@@ -391,6 +428,7 @@ handle_pasv(void)
391 free(response); 428 free(response);
392} 429}
393 430
431/* Exits on error */
394static void 432static void
395handle_epsv(void) 433handle_epsv(void)
396{ 434{
@@ -408,16 +446,16 @@ handle_port(void)
408{ 446{
409 unsigned short port; 447 unsigned short port;
410 char *raw, *port_part; 448 char *raw, *port_part;
411 len_and_sockaddr *lsa = NULL; 449 len_and_sockaddr *lsa;
412 450
413 port_pasv_cleanup(); 451 port_pasv_cleanup();
414 452
415 raw = G.ftp_arg; 453 raw = G.ftp_arg;
416 454
417 /* buglets: 455/* Buglets:
418 * xatou16 will accept wrong input, 456 * xatou16 will accept wrong input,
419 * xatou16 will exit instead of generating error to peer 457 * xatou16 will exit instead of generating error to peer
420 */ 458 */
421 459
422 port_part = strrchr(raw, ','); 460 port_part = strrchr(raw, ',');
423 if (port_part == NULL) 461 if (port_part == NULL)
@@ -440,6 +478,11 @@ handle_port(void)
440 return; 478 return;
441 } 479 }
442 480
481/* Should we verify that lsa matches getpeername(STDIN)?
482 * Otherwise peer can make us open data connections
483 * to other hosts (security problem!)
484 */
485
443 G.port_addr = lsa; 486 G.port_addr = lsa;
444 cmdio_write_ok(FTP_PORTOK); 487 cmdio_write_ok(FTP_PORTOK);
445} 488}
@@ -456,38 +499,38 @@ static void
456handle_retr(void) 499handle_retr(void)
457{ 500{
458 struct stat statbuf; 501 struct stat statbuf;
459 int trans_ret; 502 off_t bytes_transferred;
460 int remote_fd; 503 int remote_fd;
461 int opened_file; 504 int local_file_fd;
462 off_t offset = G.restart_pos; 505 off_t offset = G.restart_pos;
463 char *response; 506 char *response;
464 507
465 G.restart_pos = 0; 508 G.restart_pos = 0;
466 509
467 if (!data_transfer_checks_ok()) 510 if (!port_or_pasv_was_seen())
468 return; /* data_transfer_checks_ok emitted error response */ 511 return; /* port_or_pasv_was_seen emitted error response */
469 512
470 /* O_NONBLOCK is useful if file happens to be a device node */ 513 /* O_NONBLOCK is useful if file happens to be a device node */
471 opened_file = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1; 514 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
472 if (opened_file < 0) { 515 if (local_file_fd < 0) {
473 cmdio_write_error(FTP_FILEFAIL); 516 cmdio_write_error(FTP_FILEFAIL);
474 return; 517 return;
475 } 518 }
476 519
477 if (fstat(opened_file, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) { 520 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
478 /* Note - pretend open failed */ 521 /* Note - pretend open failed */
479 cmdio_write_error(FTP_FILEFAIL); 522 cmdio_write_error(FTP_FILEFAIL);
480 goto file_close_out; 523 goto file_close_out;
481 } 524 }
482 525
483 /* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems 526 /* Now deactive O_NONBLOCK, otherwise we have a problem
484 * such as XFS DMAPI. 527 * on DMAPI filesystems such as XFS DMAPI.
485 */ 528 */
486 ndelay_off(opened_file); 529 ndelay_off(local_file_fd);
487 530
488 /* Set the download offset (from REST) if any */ 531 /* Set the download offset (from REST) if any */
489 if (offset != 0) 532 if (offset != 0)
490 xlseek(opened_file, offset, SEEK_SET); 533 xlseek(local_file_fd, offset, SEEK_SET);
491 534
492 response = xasprintf( 535 response = xasprintf(
493 " Opening BINARY mode data connection for %s (%"OFF_FMT"u bytes)", 536 " Opening BINARY mode data connection for %s (%"OFF_FMT"u bytes)",
@@ -495,20 +538,22 @@ handle_retr(void)
495 remote_fd = get_remote_transfer_fd(response); 538 remote_fd = get_remote_transfer_fd(response);
496 free(response); 539 free(response);
497 if (remote_fd < 0) 540 if (remote_fd < 0)
498 goto port_pasv_cleanup_out; 541 goto file_close_out;
542
543/* TODO: if we'll implement timeout, this will need more clever handling.
544 * Perhaps alarm(N) + checking that current position on local_file_fd
545 * is advancing. As of now, peer may stall us indefinitely.
546 */
499 547
500 trans_ret = bb_copyfd_eof(opened_file, remote_fd); 548 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
501 close(remote_fd); 549 close(remote_fd);
502 if (trans_ret < 0) 550 if (bytes_transferred < 0)
503 cmdio_write_error(FTP_BADSENDFILE); 551 cmdio_write_error(FTP_BADSENDFILE);
504 else 552 else
505 cmdio_write_ok(FTP_TRANSFEROK); 553 cmdio_write_ok(FTP_TRANSFEROK);
506 554
507 port_pasv_cleanup_out:
508 port_pasv_cleanup();
509
510 file_close_out: 555 file_close_out:
511 close(opened_file); 556 close(local_file_fd);
512} 557}
513 558
514/* List commands */ 559/* List commands */
@@ -522,12 +567,10 @@ popen_ls(const char *opt)
522 pid_t pid; 567 pid_t pid;
523 568
524 cwd = xrealloc_getcwd_or_warn(NULL); 569 cwd = xrealloc_getcwd_or_warn(NULL);
525
526 xpiped_pair(outfd); 570 xpiped_pair(outfd);
527 571
528 fflush(NULL); 572 /*fflush(NULL); - so far we dont use stdio on output */
529 pid = vfork(); 573 pid = vfork();
530
531 switch (pid) { 574 switch (pid) {
532 case -1: /* failure */ 575 case -1: /* failure */
533 bb_perror_msg_and_die("vfork"); 576 bb_perror_msg_and_die("vfork");
@@ -547,12 +590,18 @@ popen_ls(const char *opt)
547 } 590 }
548 _exit(127); 591 _exit(127);
549 } 592 }
593
550 /* parent */ 594 /* parent */
551 close(outfd.wr); 595 close(outfd.wr);
552 free(cwd); 596 free(cwd);
553 return outfd.rd; 597 return outfd.rd;
554} 598}
555 599
600enum {
601 USE_CTRL_CONN = 1,
602 LONG_LISTING = 2,
603};
604
556static void 605static void
557handle_dir_common(int opts) 606handle_dir_common(int opts)
558{ 607{
@@ -560,15 +609,15 @@ handle_dir_common(int opts)
560 char *line; 609 char *line;
561 int ls_fd; 610 int ls_fd;
562 611
563 if (!(opts & 1) && !data_transfer_checks_ok()) 612 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
564 return; /* data_transfer_checks_ok emitted error response */ 613 return; /* port_or_pasv_was_seen emitted error response */
565 614
566 ls_fd = popen_ls((opts & 2) ? "-l" : "-1"); 615 ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
567 ls_fp = fdopen(ls_fd, "r"); 616 ls_fp = fdopen(ls_fd, "r");
568 if (!ls_fp) /* never happens. paranoia */ 617 if (!ls_fp) /* never happens. paranoia */
569 bb_perror_msg_and_die("fdopen"); 618 bb_perror_msg_and_die("fdopen");
570 619
571 if (opts & 1) { 620 if (opts & USE_CTRL_CONN) {
572 /* STAT <filename> */ 621 /* STAT <filename> */
573 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n"); 622 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
574 while (1) { 623 while (1) {
@@ -587,13 +636,16 @@ handle_dir_common(int opts)
587 line = xmalloc_fgetline(ls_fp); 636 line = xmalloc_fgetline(ls_fp);
588 if (!line) 637 if (!line)
589 break; 638 break;
639 /* I've seen clients complaining when they
640 * are fed with ls output with bare '\n'.
641 * Pity... that would be much simpler.
642 */
590 xwrite_str(remote_fd, line); 643 xwrite_str(remote_fd, line);
591 xwrite(remote_fd, "\r\n", 2); 644 xwrite(remote_fd, "\r\n", 2);
592 free(line); 645 free(line);
593 } 646 }
594 } 647 }
595 close(remote_fd); 648 close(remote_fd);
596 port_pasv_cleanup();
597 cmdio_write_ok(FTP_TRANSFEROK); 649 cmdio_write_ok(FTP_TRANSFEROK);
598 } 650 }
599 fclose(ls_fp); /* closes ls_fd too */ 651 fclose(ls_fp); /* closes ls_fd too */
@@ -601,7 +653,7 @@ handle_dir_common(int opts)
601static void 653static void
602handle_list(void) 654handle_list(void)
603{ 655{
604 handle_dir_common(2); 656 handle_dir_common(LONG_LISTING);
605} 657}
606static void 658static void
607handle_nlst(void) 659handle_nlst(void)
@@ -611,7 +663,7 @@ handle_nlst(void)
611static void 663static void
612handle_stat_file(void) 664handle_stat_file(void)
613{ 665{
614 handle_dir_common(3); 666 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
615} 667}
616 668
617static void 669static void
@@ -698,7 +750,7 @@ static void
698handle_upload_common(int is_append, int is_unique) 750handle_upload_common(int is_append, int is_unique)
699{ 751{
700 char *tempname = NULL; 752 char *tempname = NULL;
701 int trans_ret; 753 off_t bytes_transferred;
702 int local_file_fd; 754 int local_file_fd;
703 int remote_fd; 755 int remote_fd;
704 off_t offset; 756 off_t offset;
@@ -706,8 +758,8 @@ handle_upload_common(int is_append, int is_unique)
706 offset = G.restart_pos; 758 offset = G.restart_pos;
707 G.restart_pos = 0; 759 G.restart_pos = 0;
708 760
709 if (!data_transfer_checks_ok()) 761 if (!port_or_pasv_was_seen())
710 return; 762 return; /* port_or_pasv_was_seen emitted error response */
711 763
712 local_file_fd = -1; 764 local_file_fd = -1;
713 if (is_unique) { 765 if (is_unique) {
@@ -726,7 +778,7 @@ handle_upload_common(int is_append, int is_unique)
726 return; 778 return;
727 } 779 }
728 780
729 /* TODO: paranoia: fstat it, refuse to do anything if it's not a regular file */ 781/* TODO: paranoia: fstat it, refuse to do anything if it's not a regular file */
730 782
731 if (offset) 783 if (offset)
732 xlseek(local_file_fd, offset, SEEK_SET); 784 xlseek(local_file_fd, offset, SEEK_SET);
@@ -737,16 +789,18 @@ handle_upload_common(int is_append, int is_unique)
737 if (remote_fd < 0) 789 if (remote_fd < 0)
738 goto bail; 790 goto bail;
739 791
740 trans_ret = bb_copyfd_eof(remote_fd, local_file_fd); 792/* TODO: if we'll implement timeout, this will need more clever handling.
741 close(remote_fd); 793 * Perhaps alarm(N) + checking that current position on local_file_fd
794 * is advancing. As of now, peer may stall us indefinitely.
795 */
742 796
743 if (trans_ret < 0) 797 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
798 close(remote_fd);
799 if (bytes_transferred < 0)
744 cmdio_write_error(FTP_BADSENDFILE); 800 cmdio_write_error(FTP_BADSENDFILE);
745 else 801 else
746 cmdio_write_ok(FTP_TRANSFEROK); 802 cmdio_write_ok(FTP_TRANSFEROK);
747
748 bail: 803 bail:
749 port_pasv_cleanup();
750 close(local_file_fd); 804 close(local_file_fd);
751} 805}
752 806