1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84#include "libbb.h"
85#include "common_bufsiz.h"
86#include <syslog.h>
87#include <netinet/tcp.h>
88
89#define FTP_DATACONN 150
90#define FTP_NOOPOK 200
91#define FTP_TYPEOK 200
92#define FTP_PORTOK 200
93#define FTP_STRUOK 200
94#define FTP_MODEOK 200
95#define FTP_ALLOOK 202
96#define FTP_STATOK 211
97#define FTP_STATFILE_OK 213
98#define FTP_HELP 214
99#define FTP_SYSTOK 215
100#define FTP_GREET 220
101#define FTP_GOODBYE 221
102#define FTP_TRANSFEROK 226
103#define FTP_PASVOK 227
104
105#define FTP_EPSVOK 229
106#define FTP_LOGINOK 230
107#define FTP_CWDOK 250
108#define FTP_RMDIROK 250
109#define FTP_DELEOK 250
110#define FTP_RENAMEOK 250
111#define FTP_PWDOK 257
112#define FTP_MKDIROK 257
113#define FTP_GIVEPWORD 331
114#define FTP_RESTOK 350
115#define FTP_RNFROK 350
116#define FTP_TIMEOUT 421
117#define FTP_BADSENDCONN 425
118#define FTP_BADSENDNET 426
119#define FTP_BADSENDFILE 451
120#define FTP_BADCMD 500
121#define FTP_COMMANDNOTIMPL 502
122#define FTP_NEEDUSER 503
123#define FTP_NEEDRNFR 503
124#define FTP_BADSTRU 504
125#define FTP_BADMODE 504
126#define FTP_LOGINERR 530
127#define FTP_FILEFAIL 550
128#define FTP_NOPERM 550
129#define FTP_UPLOADFAIL 553
130
131#define STR1(s) #s
132#define STR(s) STR1(s)
133
134
135enum {
136
137 SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
138 SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
139 SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
140
141 SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
142};
143#define STRNUM32(s) (uint32_t)(0 \
144 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
145 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
146 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
147)
148#define STRNUM32sp(s) (uint32_t)(0 \
149 | (' ' << SHIFTsp) \
150 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
151 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
152 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
153)
154
155#define MSG_OK "Operation successful\r\n"
156#define MSG_ERR "Error\r\n"
157
158struct globals {
159 int pasv_listen_fd;
160#if !BB_MMU
161 int root_fd;
162#endif
163 int local_file_fd;
164 unsigned end_time;
165 unsigned timeout;
166 unsigned verbose;
167 off_t local_file_pos;
168 off_t restart_pos;
169 len_and_sockaddr *local_addr;
170 len_and_sockaddr *port_addr;
171 char *ftp_cmd;
172 char *ftp_arg;
173#if ENABLE_FEATURE_FTPD_WRITE
174 char *rnfr_filename;
175#endif
176
177 char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
178 char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
179} FIX_ALIASING;
180#define G (*ptr_to_globals)
181
182
183#define INIT_G() do { \
184 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
185 \
186 \
187 \
188 \
189 \
190} while (0)
191
192
193static char *
194escape_text(const char *prepend, const char *str, unsigned escapee)
195{
196 unsigned retlen, remainlen, chunklen;
197 char *ret, *found;
198 char append;
199
200 append = (char)escapee;
201 escapee >>= 8;
202
203 remainlen = strlen(str);
204 retlen = strlen(prepend);
205 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
206 strcpy(ret, prepend);
207
208 for (;;) {
209 found = strchrnul(str, escapee);
210 chunklen = found - str + 1;
211
212
213 memcpy(ret + retlen, str, chunklen);
214 retlen += chunklen;
215
216 if (*found == '\0') {
217
218 ret[retlen - 1] = append;
219 ret[retlen] = '\0';
220 break;
221 }
222 ret[retlen++] = escapee;
223 str = found + 1;
224 }
225 return ret;
226}
227
228
229static unsigned
230replace_char(char *str, char from, char to)
231{
232 char *p = str;
233 while (*p) {
234 if (*p == from)
235 *p = to;
236 p++;
237 }
238 return p - str;
239}
240
241static void
242verbose_log(const char *str)
243{
244 bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
245}
246
247
248static void
249cmdio_write(uint32_t status_str, const char *str)
250{
251 char *response;
252 int len;
253
254
255
256 response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
257
258
259 len = replace_char(response, '\n', '\0');
260
261 response[len++] = '\n';
262 xwrite(STDOUT_FILENO, response, len);
263 if (G.verbose > 1)
264 verbose_log(response);
265 free(response);
266}
267
268static void
269cmdio_write_ok(unsigned status)
270{
271 *(bb__aliased_uint32_t *) G.msg_ok = status;
272 xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
273 if (G.verbose > 1)
274 verbose_log(G.msg_ok);
275}
276#define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
277
278
279static void
280cmdio_write_error(unsigned status)
281{
282 *(bb__aliased_uint32_t *) G.msg_err = status;
283 xwrite(STDOUT_FILENO, G.msg_err, sizeof("NNN " MSG_ERR) - 1);
284 if (G.verbose > 0)
285 verbose_log(G.msg_err);
286}
287#define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
288
289static void
290cmdio_write_raw(const char *p_text)
291{
292 xwrite_str(STDOUT_FILENO, p_text);
293 if (G.verbose > 1)
294 verbose_log(p_text);
295}
296
297static void
298timeout_handler(int sig UNUSED_PARAM)
299{
300 off_t pos;
301 int sv_errno = errno;
302
303 if ((int)(monotonic_sec() - G.end_time) >= 0)
304 goto timed_out;
305
306 if (!G.local_file_fd)
307 goto timed_out;
308
309 pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
310 if (pos == G.local_file_pos)
311 goto timed_out;
312 G.local_file_pos = pos;
313
314 alarm(G.timeout);
315 errno = sv_errno;
316 return;
317
318 timed_out:
319 cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
320
321 exit(1);
322}
323
324
325
326static void
327handle_pwd(void)
328{
329 char *cwd, *response;
330
331 cwd = xrealloc_getcwd_or_warn(NULL);
332 if (cwd == NULL)
333 cwd = xstrdup("");
334
335
336 response = escape_text(" \"", cwd, ('"' << 8) + '"');
337 free(cwd);
338 cmdio_write(STRNUM32(FTP_PWDOK), response);
339 free(response);
340}
341
342static void
343handle_cwd(void)
344{
345 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
346 WRITE_ERR(FTP_FILEFAIL);
347 return;
348 }
349 WRITE_OK(FTP_CWDOK);
350}
351
352static void
353handle_cdup(void)
354{
355 G.ftp_arg = (char*)"..";
356 handle_cwd();
357}
358
359static void
360handle_stat(void)
361{
362 cmdio_write_raw(STR(FTP_STATOK)"-Server status:\r\n"
363 " TYPE: BINARY\r\n"
364 STR(FTP_STATOK)" Ok\r\n");
365}
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390static void
391handle_feat(unsigned status)
392{
393 cmdio_write(status, "-Features:");
394 cmdio_write_raw(" EPSV\r\n"
395 " PASV\r\n"
396 " REST STREAM\r\n"
397 " MDTM\r\n"
398 " SIZE\r\n");
399 cmdio_write(status, " Ok");
400}
401
402
403
404static inline int
405port_active(void)
406{
407 return (G.port_addr != NULL);
408}
409
410static inline int
411pasv_active(void)
412{
413 return (G.pasv_listen_fd > STDOUT_FILENO);
414}
415
416static void
417port_pasv_cleanup(void)
418{
419 free(G.port_addr);
420 G.port_addr = NULL;
421 if (G.pasv_listen_fd > STDOUT_FILENO)
422 close(G.pasv_listen_fd);
423 G.pasv_listen_fd = -1;
424}
425
426
427static int
428ftpdataio_get_pasv_fd(void)
429{
430 int remote_fd;
431
432 remote_fd = accept(G.pasv_listen_fd, NULL, 0);
433
434 if (remote_fd < 0) {
435 WRITE_ERR(FTP_BADSENDCONN);
436 return remote_fd;
437 }
438
439 setsockopt_keepalive(remote_fd);
440 return remote_fd;
441}
442
443
444
445
446
447
448
449static int
450get_remote_transfer_fd(const char *p_status_msg)
451{
452 int remote_fd;
453
454 if (pasv_active())
455
456 remote_fd = ftpdataio_get_pasv_fd();
457 else
458
459 remote_fd = xconnect_stream(G.port_addr);
460
461 port_pasv_cleanup();
462
463 if (remote_fd < 0)
464 return remote_fd;
465
466 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
467 return remote_fd;
468}
469
470
471static int
472port_or_pasv_was_seen(void)
473{
474 if (!pasv_active() && !port_active()) {
475 cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT/PASV first\r\n");
476 return 0;
477 }
478
479 return 1;
480}
481
482
483static unsigned
484bind_for_passive_mode(void)
485{
486 int fd;
487 unsigned port;
488
489 port_pasv_cleanup();
490
491 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
492 setsockopt_reuseaddr(fd);
493
494 set_nport(&G.local_addr->u.sa, 0);
495 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
496 xlisten(fd, 1);
497 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
498
499 port = get_nport(&G.local_addr->u.sa);
500 port = ntohs(port);
501 return port;
502}
503
504
505static void
506handle_pasv(void)
507{
508 unsigned port;
509 char *addr, *response;
510
511 port = bind_for_passive_mode();
512
513 if (G.local_addr->u.sa.sa_family == AF_INET)
514 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
515 else
516 addr = xstrdup("0.0.0.0");
517 replace_char(addr, '.', ',');
518
519 response = xasprintf(STR(FTP_PASVOK)" PASV ok (%s,%u,%u)\r\n",
520 addr, (int)(port >> 8), (int)(port & 255));
521 free(addr);
522 cmdio_write_raw(response);
523 free(response);
524}
525
526
527static void
528handle_epsv(void)
529{
530 unsigned port;
531 char *response;
532
533 port = bind_for_passive_mode();
534 response = xasprintf(STR(FTP_EPSVOK)" EPSV ok (|||%u|)\r\n", port);
535 cmdio_write_raw(response);
536 free(response);
537}
538
539static void
540handle_port(void)
541{
542 unsigned port, port_hi;
543 char *raw, *comma;
544#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
545 socklen_t peer_ipv4_len;
546 struct sockaddr_in peer_ipv4;
547 struct in_addr port_ipv4_sin_addr;
548#endif
549
550 port_pasv_cleanup();
551
552 raw = G.ftp_arg;
553
554
555 if (!raw
556#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
557 || G.local_addr->u.sa.sa_family != AF_INET
558#endif
559 ) {
560 bail:
561 WRITE_ERR(FTP_BADCMD);
562 return;
563 }
564
565 comma = strrchr(raw, ',');
566 if (comma == NULL)
567 goto bail;
568 *comma = '\0';
569 port = bb_strtou(&comma[1], NULL, 10);
570 if (errno || port > 0xff)
571 goto bail;
572
573 comma = strrchr(raw, ',');
574 if (comma == NULL)
575 goto bail;
576 *comma = '\0';
577 port_hi = bb_strtou(&comma[1], NULL, 10);
578 if (errno || port_hi > 0xff)
579 goto bail;
580 port |= port_hi << 8;
581
582#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
583 replace_char(raw, ',', '.');
584
585
586
587
588
589
590
591
592 if (!inet_aton(raw, &port_ipv4_sin_addr))
593 goto bail;
594 peer_ipv4_len = sizeof(peer_ipv4);
595 if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
596 goto bail;
597 if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
598 goto bail;
599
600 G.port_addr = xdotted2sockaddr(raw, port);
601#else
602 G.port_addr = get_peer_lsa(STDIN_FILENO);
603 set_nport(&G.port_addr->u.sa, htons(port));
604#endif
605 WRITE_OK(FTP_PORTOK);
606}
607
608static void
609handle_rest(void)
610{
611
612 G.restart_pos = G.ftp_arg ? XATOOFF(G.ftp_arg) : 0;
613 WRITE_OK(FTP_RESTOK);
614}
615
616static void
617handle_retr(void)
618{
619 struct stat statbuf;
620 off_t bytes_transferred;
621 int remote_fd;
622 int local_file_fd;
623 off_t offset = G.restart_pos;
624 char *response;
625
626 G.restart_pos = 0;
627
628 if (!port_or_pasv_was_seen())
629 return;
630
631
632 local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
633 if (local_file_fd < 0) {
634 WRITE_ERR(FTP_FILEFAIL);
635 return;
636 }
637
638 if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
639
640 WRITE_ERR(FTP_FILEFAIL);
641 goto file_close_out;
642 }
643 G.local_file_fd = local_file_fd;
644
645
646
647
648 ndelay_off(local_file_fd);
649
650
651 if (offset != 0)
652 xlseek(local_file_fd, offset, SEEK_SET);
653
654 response = xasprintf(
655 " Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
656 G.ftp_arg, statbuf.st_size);
657 remote_fd = get_remote_transfer_fd(response);
658 free(response);
659 if (remote_fd < 0)
660 goto file_close_out;
661
662 bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
663 close(remote_fd);
664 if (bytes_transferred < 0)
665 WRITE_ERR(FTP_BADSENDFILE);
666 else
667 WRITE_OK(FTP_TRANSFEROK);
668
669 file_close_out:
670 close(local_file_fd);
671 G.local_file_fd = 0;
672}
673
674
675
676static int
677popen_ls(const char *opt)
678{
679 const char *argv[5];
680 struct fd_pair outfd;
681 pid_t pid;
682
683 argv[0] = "ftpd";
684 argv[1] = opt;
685 argv[2] = "--";
686 argv[3] = G.ftp_arg;
687 argv[4] = NULL;
688
689
690
691
692 if (ENABLE_FEATURE_FTPD_ACCEPT_BROKEN_LIST
693 && G.ftp_arg && G.ftp_arg[0] == '-'
694 ) {
695 const char *tmp = strchr(G.ftp_arg, ' ');
696 if (tmp)
697 tmp++;
698 argv[3] = tmp;
699 }
700
701 xpiped_pair(outfd);
702
703
704 pid = BB_MMU ? xfork() : xvfork();
705 if (pid == 0) {
706#if !BB_MMU
707 int cur_fd;
708#endif
709
710
711 close(outfd.rd);
712 xmove_fd(outfd.wr, STDOUT_FILENO);
713
714
715
716
717 close(STDIN_FILENO);
718 dup(STDOUT_FILENO);
719#if BB_MMU
720
721 exit(ls_main( 0, (char**) argv));
722#else
723 cur_fd = xopen(".", O_RDONLY | O_DIRECTORY);
724
725
726
727
728
729 if (G.root_fd >= 0) {
730 if (fchdir(G.root_fd) != 0 || chroot(".") != 0)
731 _exit(127);
732
733 }
734
735 xmove_fd(cur_fd, 3);
736 execv(bb_busybox_exec_path, (char**) argv);
737 _exit(127);
738#endif
739 }
740
741
742 close(outfd.wr);
743 return outfd.rd;
744}
745
746enum {
747 USE_CTRL_CONN = 1,
748 LONG_LISTING = 2,
749};
750
751static void
752handle_dir_common(int opts)
753{
754 FILE *ls_fp;
755 char *line;
756 int ls_fd;
757
758 if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
759 return;
760
761 ls_fd = popen_ls((opts & LONG_LISTING) ? "-lA" : "-1A");
762 ls_fp = xfdopen_for_read(ls_fd);
763
764
765 if (opts & USE_CTRL_CONN) {
766
767 cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
768 while (1) {
769 line = xmalloc_fgetline(ls_fp);
770 if (!line)
771 break;
772
773
774
775 cmdio_write(0, line);
776 free(line);
777 }
778 WRITE_OK(FTP_STATFILE_OK);
779 } else {
780
781 int remote_fd = get_remote_transfer_fd(" Directory listing");
782 if (remote_fd >= 0) {
783 while (1) {
784 unsigned len;
785
786 line = xmalloc_fgets(ls_fp);
787 if (!line)
788 break;
789
790
791
792
793 len = strlen(line);
794 if (len != 0)
795 line[len - 1] = '\r';
796 line[len] = '\n';
797 xwrite(remote_fd, line, len + 1);
798 free(line);
799 }
800 }
801 close(remote_fd);
802 WRITE_OK(FTP_TRANSFEROK);
803 }
804 fclose(ls_fp);
805}
806static void
807handle_list(void)
808{
809 handle_dir_common(LONG_LISTING);
810}
811static void
812handle_nlst(void)
813{
814
815
816
817
818
819
820
821 handle_dir_common(0);
822}
823static void
824handle_stat_file(void)
825{
826 handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
827}
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858static void
859handle_size_or_mdtm(int need_size)
860{
861 struct stat statbuf;
862 struct tm broken_out;
863 char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
864 | sizeof("NNN YYYYMMDDhhmmss\r\n")
865 ];
866
867 if (!G.ftp_arg
868 || stat(G.ftp_arg, &statbuf) != 0
869 || !S_ISREG(statbuf.st_mode)
870 ) {
871 WRITE_ERR(FTP_FILEFAIL);
872 return;
873 }
874 if (need_size) {
875 sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
876 } else {
877 gmtime_r(&statbuf.st_mtime, &broken_out);
878 sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n",
879 broken_out.tm_year + 1900,
880 broken_out.tm_mon + 1,
881 broken_out.tm_mday,
882 broken_out.tm_hour,
883 broken_out.tm_min,
884 broken_out.tm_sec);
885 }
886 cmdio_write_raw(buf);
887}
888
889
890
891#if ENABLE_FEATURE_FTPD_WRITE
892static void
893handle_mkd(void)
894{
895 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
896 WRITE_ERR(FTP_FILEFAIL);
897 return;
898 }
899 WRITE_OK(FTP_MKDIROK);
900}
901
902static void
903handle_rmd(void)
904{
905 if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
906 WRITE_ERR(FTP_FILEFAIL);
907 return;
908 }
909 WRITE_OK(FTP_RMDIROK);
910}
911
912static void
913handle_dele(void)
914{
915 if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
916 WRITE_ERR(FTP_FILEFAIL);
917 return;
918 }
919 WRITE_OK(FTP_DELEOK);
920}
921
922static void
923handle_rnfr(void)
924{
925 free(G.rnfr_filename);
926 G.rnfr_filename = xstrdup(G.ftp_arg);
927 WRITE_OK(FTP_RNFROK);
928}
929
930static void
931handle_rnto(void)
932{
933 int retval;
934
935
936 if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
937 cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
938 return;
939 }
940
941 retval = rename(G.rnfr_filename, G.ftp_arg);
942 free(G.rnfr_filename);
943 G.rnfr_filename = NULL;
944
945 if (retval) {
946 WRITE_ERR(FTP_FILEFAIL);
947 return;
948 }
949 WRITE_OK(FTP_RENAMEOK);
950}
951
952static void
953handle_upload_common(int is_append, int is_unique)
954{
955 struct stat statbuf;
956 char *tempname;
957 off_t bytes_transferred;
958 off_t offset;
959 int local_file_fd;
960 int remote_fd;
961
962 offset = G.restart_pos;
963 G.restart_pos = 0;
964
965 if (!port_or_pasv_was_seen())
966 return;
967
968 tempname = NULL;
969 local_file_fd = -1;
970 if (is_unique) {
971 tempname = xstrdup(" FILE: uniq.XXXXXX");
972 local_file_fd = mkstemp(tempname + 7);
973 } else if (G.ftp_arg) {
974 int flags = O_WRONLY | O_CREAT | O_TRUNC;
975 if (is_append)
976 flags = O_WRONLY | O_CREAT | O_APPEND;
977 if (offset)
978 flags = O_WRONLY | O_CREAT;
979 local_file_fd = open(G.ftp_arg, flags, 0666);
980 }
981
982 if (local_file_fd < 0
983 || fstat(local_file_fd, &statbuf) != 0
984 || !S_ISREG(statbuf.st_mode)
985 ) {
986 free(tempname);
987 WRITE_ERR(FTP_UPLOADFAIL);
988 if (local_file_fd >= 0)
989 goto close_local_and_bail;
990 return;
991 }
992 G.local_file_fd = local_file_fd;
993
994 if (offset)
995 xlseek(local_file_fd, offset, SEEK_SET);
996
997 remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
998 free(tempname);
999
1000 if (remote_fd < 0)
1001 goto close_local_and_bail;
1002
1003 bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
1004 close(remote_fd);
1005 if (bytes_transferred < 0)
1006 WRITE_ERR(FTP_BADSENDFILE);
1007 else
1008 WRITE_OK(FTP_TRANSFEROK);
1009
1010 close_local_and_bail:
1011 close(local_file_fd);
1012 G.local_file_fd = 0;
1013}
1014
1015static void
1016handle_stor(void)
1017{
1018 handle_upload_common(0, 0);
1019}
1020
1021static void
1022handle_appe(void)
1023{
1024 G.restart_pos = 0;
1025 handle_upload_common(1, 0);
1026}
1027
1028static void
1029handle_stou(void)
1030{
1031 G.restart_pos = 0;
1032 handle_upload_common(0, 1);
1033}
1034#endif
1035
1036static uint32_t
1037cmdio_get_cmd_and_arg(void)
1038{
1039 int len;
1040 uint32_t cmdval;
1041 char *cmd;
1042
1043 alarm(G.timeout);
1044
1045 free(G.ftp_cmd);
1046 {
1047
1048
1049
1050 size_t len_on_stk = 8 * 1024;
1051 G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len_on_stk);
1052 if (!cmd)
1053 exit(0);
1054 len = len_on_stk;
1055 }
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075 {
1076 int dst, src;
1077
1078
1079 if (len != 0 && cmd[len - 1] == '\n') {
1080 len--;
1081 if (len != 0 && cmd[len - 1] == '\r')
1082 len--;
1083 cmd[len] = '\0';
1084 }
1085 src = strchrnul(cmd, 0xff) - cmd;
1086
1087 if (src < len) {
1088 dst = src;
1089 do {
1090 if ((unsigned char)(cmd[src]) == 255) {
1091 src++;
1092
1093 if ((unsigned char)(cmd[src]) != 255) {
1094
1095 src++;
1096 continue;
1097 }
1098
1099 }
1100
1101 cmd[dst++] = cmd[src] ? cmd[src] : '\n';
1102 src++;
1103 } while (src < len);
1104 cmd[dst] = '\0';
1105 }
1106 }
1107
1108 if (G.verbose > 1)
1109 verbose_log(cmd);
1110
1111 G.ftp_arg = strchr(cmd, ' ');
1112 if (G.ftp_arg != NULL)
1113 *G.ftp_arg++ = '\0';
1114
1115
1116 cmdval = 0;
1117 while (*cmd)
1118 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
1119
1120 return cmdval;
1121}
1122
1123#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
1124#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
1125enum {
1126 const_ALLO = mk_const4('A', 'L', 'L', 'O'),
1127 const_APPE = mk_const4('A', 'P', 'P', 'E'),
1128 const_CDUP = mk_const4('C', 'D', 'U', 'P'),
1129 const_CWD = mk_const3('C', 'W', 'D'),
1130 const_DELE = mk_const4('D', 'E', 'L', 'E'),
1131 const_EPSV = mk_const4('E', 'P', 'S', 'V'),
1132 const_FEAT = mk_const4('F', 'E', 'A', 'T'),
1133 const_HELP = mk_const4('H', 'E', 'L', 'P'),
1134 const_LIST = mk_const4('L', 'I', 'S', 'T'),
1135 const_MDTM = mk_const4('M', 'D', 'T', 'M'),
1136 const_MKD = mk_const3('M', 'K', 'D'),
1137 const_MODE = mk_const4('M', 'O', 'D', 'E'),
1138 const_NLST = mk_const4('N', 'L', 'S', 'T'),
1139 const_NOOP = mk_const4('N', 'O', 'O', 'P'),
1140 const_PASS = mk_const4('P', 'A', 'S', 'S'),
1141 const_PASV = mk_const4('P', 'A', 'S', 'V'),
1142 const_PORT = mk_const4('P', 'O', 'R', 'T'),
1143 const_PWD = mk_const3('P', 'W', 'D'),
1144
1145 const_XPWD = mk_const4('X', 'P', 'W', 'D'),
1146 const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
1147 const_REST = mk_const4('R', 'E', 'S', 'T'),
1148 const_RETR = mk_const4('R', 'E', 'T', 'R'),
1149 const_RMD = mk_const3('R', 'M', 'D'),
1150 const_RNFR = mk_const4('R', 'N', 'F', 'R'),
1151 const_RNTO = mk_const4('R', 'N', 'T', 'O'),
1152 const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
1153 const_STAT = mk_const4('S', 'T', 'A', 'T'),
1154 const_STOR = mk_const4('S', 'T', 'O', 'R'),
1155 const_STOU = mk_const4('S', 'T', 'O', 'U'),
1156 const_STRU = mk_const4('S', 'T', 'R', 'U'),
1157 const_SYST = mk_const4('S', 'Y', 'S', 'T'),
1158 const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
1159 const_USER = mk_const4('U', 'S', 'E', 'R'),
1160
1161#if !BB_MMU
1162 OPT_l = (1 << 0),
1163 OPT_1 = (1 << 1),
1164#endif
1165 BIT_A = (!BB_MMU) * 2,
1166 OPT_A = (1 << (BIT_A + 0)),
1167 OPT_v = (1 << (BIT_A + 1)),
1168 OPT_S = (1 << (BIT_A + 2)),
1169 OPT_w = (1 << (BIT_A + 3)) * ENABLE_FEATURE_FTPD_WRITE,
1170};
1171
1172int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1173int ftpd_main(int argc UNUSED_PARAM, char **argv)
1174{
1175#if ENABLE_FEATURE_FTPD_AUTHENTICATION
1176 struct passwd *pw = NULL;
1177 char *anon_opt = NULL;
1178#endif
1179 unsigned abs_timeout;
1180 unsigned verbose_S;
1181 smallint opts;
1182
1183 INIT_G();
1184
1185 abs_timeout = 1 * 60 * 60;
1186 verbose_S = 0;
1187 G.timeout = 2 * 60;
1188#if BB_MMU
1189 opts = getopt32(argv, "^" "AvS" IF_FEATURE_FTPD_WRITE("w")
1190 "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1191 "\0" "vv:SS",
1192 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1193 &G.verbose, &verbose_S
1194 );
1195#else
1196 opts = getopt32(argv, "^" "l1AvS" IF_FEATURE_FTPD_WRITE("w")
1197 "t:+T:+" IF_FEATURE_FTPD_AUTHENTICATION("a:")
1198 "\0" "vv:SS",
1199 &G.timeout, &abs_timeout, IF_FEATURE_FTPD_AUTHENTICATION(&anon_opt,)
1200 &G.verbose, &verbose_S
1201 );
1202 if (opts & (OPT_l|OPT_1)) {
1203
1204 if (fchdir(3) != 0)
1205 _exit(127);
1206
1207
1208 return ls_main( 0, argv);
1209 }
1210#endif
1211 if (G.verbose < verbose_S)
1212 G.verbose = verbose_S;
1213 if (abs_timeout | G.timeout) {
1214 if (abs_timeout == 0)
1215 abs_timeout = INT_MAX;
1216 G.end_time = monotonic_sec() + abs_timeout;
1217 if (G.timeout > abs_timeout)
1218 G.timeout = abs_timeout;
1219 }
1220 strcpy(G.msg_ok + 4, MSG_OK );
1221 strcpy(G.msg_err + 4, MSG_ERR);
1222
1223 G.local_addr = get_sock_lsa(STDIN_FILENO);
1224 if (!G.local_addr) {
1225
1226
1227
1228 bb_show_usage();
1229
1230
1231
1232 }
1233
1234 if (!(opts & OPT_v))
1235 logmode = LOGMODE_NONE;
1236 if (opts & OPT_S) {
1237
1238 openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
1239 logmode |= LOGMODE_SYSLOG;
1240 }
1241 if (logmode)
1242 applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
1243
1244
1245
1246
1247 bb_signals(0
1248
1249 + (1 << SIGPIPE)
1250
1251 + (1 << SIGCHLD)
1252 , SIG_IGN);
1253
1254
1255 setsockopt_1(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY);
1256 setsockopt_keepalive(STDIN_FILENO);
1257
1258
1259 setsockopt_1(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE);
1260
1261 WRITE_OK(FTP_GREET);
1262 signal(SIGALRM, timeout_handler);
1263
1264#if ENABLE_FEATURE_FTPD_AUTHENTICATION
1265 if (!(opts & OPT_A)) {
1266 while (1) {
1267 uint32_t cmdval = cmdio_get_cmd_and_arg();
1268 if (cmdval == const_USER) {
1269 if (anon_opt && strcmp(G.ftp_arg, "anonymous") == 0) {
1270 pw = getpwnam(anon_opt);
1271 if (pw)
1272 break;
1273 }
1274 pw = getpwnam(G.ftp_arg);
1275 cmdio_write_raw(STR(FTP_GIVEPWORD)" Specify password\r\n");
1276 } else if (cmdval == const_PASS) {
1277 if (check_password(pw, G.ftp_arg) > 0) {
1278 break;
1279 }
1280 cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n");
1281 pw = NULL;
1282 } else if (cmdval == const_QUIT) {
1283 WRITE_OK(FTP_GOODBYE);
1284 return 0;
1285 } else {
1286 cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER+PASS\r\n");
1287 }
1288 }
1289 WRITE_OK(FTP_LOGINOK);
1290 }
1291#endif
1292
1293
1294#if !BB_MMU
1295 G.root_fd = -1;
1296#endif
1297 argv += optind;
1298 if (argv[0]) {
1299 const char *basedir = argv[0];
1300#if !BB_MMU
1301 G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
1302 close_on_exec_on(G.root_fd);
1303#endif
1304 if (chroot(basedir) == 0)
1305 basedir = "/";
1306#if !BB_MMU
1307 else {
1308 close(G.root_fd);
1309 G.root_fd = -1;
1310 }
1311#endif
1312
1313
1314
1315
1316
1317
1318 xchdir(basedir);
1319 }
1320
1321#if ENABLE_FEATURE_FTPD_AUTHENTICATION
1322 if (pw)
1323 change_identity(pw);
1324
1325#endif
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365 while (1) {
1366 uint32_t cmdval = cmdio_get_cmd_and_arg();
1367
1368 if (cmdval == const_QUIT) {
1369 WRITE_OK(FTP_GOODBYE);
1370 return 0;
1371 }
1372 else if (cmdval == const_USER)
1373
1374
1375
1376
1377
1378 WRITE_OK(FTP_LOGINOK);
1379 else if (cmdval == const_PASS)
1380 WRITE_OK(FTP_LOGINOK);
1381 else if (cmdval == const_NOOP)
1382 WRITE_OK(FTP_NOOPOK);
1383 else if (cmdval == const_TYPE)
1384 WRITE_OK(FTP_TYPEOK);
1385 else if (cmdval == const_STRU)
1386 WRITE_OK(FTP_STRUOK);
1387 else if (cmdval == const_MODE)
1388 WRITE_OK(FTP_MODEOK);
1389 else if (cmdval == const_ALLO)
1390 WRITE_OK(FTP_ALLOOK);
1391 else if (cmdval == const_SYST)
1392 cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
1393 else if (cmdval == const_PWD || cmdval == const_XPWD)
1394 handle_pwd();
1395 else if (cmdval == const_CWD)
1396 handle_cwd();
1397 else if (cmdval == const_CDUP)
1398 handle_cdup();
1399
1400
1401 else if (cmdval == const_HELP || cmdval == const_FEAT)
1402 handle_feat(cmdval == const_HELP
1403 ? STRNUM32(FTP_HELP)
1404 : STRNUM32(FTP_STATOK)
1405 );
1406 else if (cmdval == const_LIST)
1407 handle_list();
1408 else if (cmdval == const_NLST)
1409 handle_nlst();
1410
1411
1412 else if (cmdval == const_SIZE || cmdval == const_MDTM)
1413 handle_size_or_mdtm(cmdval == const_SIZE);
1414 else if (cmdval == const_STAT) {
1415 if (G.ftp_arg == NULL)
1416 handle_stat();
1417 else
1418 handle_stat_file();
1419 }
1420 else if (cmdval == const_PASV)
1421 handle_pasv();
1422 else if (cmdval == const_EPSV)
1423 handle_epsv();
1424 else if (cmdval == const_RETR)
1425 handle_retr();
1426 else if (cmdval == const_PORT)
1427 handle_port();
1428 else if (cmdval == const_REST)
1429 handle_rest();
1430#if ENABLE_FEATURE_FTPD_WRITE
1431 else if (opts & OPT_w) {
1432 if (cmdval == const_STOR)
1433 handle_stor();
1434 else if (cmdval == const_MKD)
1435 handle_mkd();
1436 else if (cmdval == const_RMD)
1437 handle_rmd();
1438 else if (cmdval == const_DELE)
1439 handle_dele();
1440 else if (cmdval == const_RNFR)
1441 handle_rnfr();
1442 else if (cmdval == const_RNTO)
1443 handle_rnto();
1444 else if (cmdval == const_APPE)
1445 handle_appe();
1446 else if (cmdval == const_STOU)
1447 handle_stou();
1448 else
1449 goto bad_cmd;
1450 }
1451#endif
1452#if 0
1453 else if (cmdval == const_STOR
1454 || cmdval == const_MKD
1455 || cmdval == const_RMD
1456 || cmdval == const_DELE
1457 || cmdval == const_RNFR
1458 || cmdval == const_RNTO
1459 || cmdval == const_APPE
1460 || cmdval == const_STOU
1461 ) {
1462 cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
1463 }
1464#endif
1465 else {
1466
1467
1468
1469
1470#if ENABLE_FEATURE_FTPD_WRITE
1471 bad_cmd:
1472#endif
1473 cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
1474 }
1475 }
1476}
1477