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