aboutsummaryrefslogtreecommitdiff
path: root/networking/ftpd.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-03-09 02:23:45 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-03-09 02:23:45 +0000
commitffb4bb3034b44bb61d8916f3e197e80c76062f1d (patch)
treed2f5a6a60e4f6c825dc8400f060c7015ee880d68 /networking/ftpd.c
parent73c571a5fff95d0f50f4fc509c35fedca73122bc (diff)
downloadbusybox-w32-ffb4bb3034b44bb61d8916f3e197e80c76062f1d.tar.gz
busybox-w32-ffb4bb3034b44bb61d8916f3e197e80c76062f1d.tar.bz2
busybox-w32-ffb4bb3034b44bb61d8916f3e197e80c76062f1d.zip
ftpd: further code shrink
function old new delta port_pasv_cleanup - 50 +50 replace_char - 30 +30 ftp_write_str_common 102 104 +2 handle_dir_common 209 204 -5 ftpd_main 1990 1970 -20 port_cleanup 23 - -23 pasv_cleanup 28 - -28 handle_upload_common 320 273 -47 ------------------------------------------------------------------------------ (add/remove: 2/2 grow/shrink: 1/3 up/down: 82/-123) Total: -41 bytes
Diffstat (limited to 'networking/ftpd.c')
-rw-r--r--networking/ftpd.c377
1 files changed, 185 insertions, 192 deletions
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 6289edf0c..404bc98c3 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -9,6 +9,8 @@
9 * 9 *
10 * Options: 10 * Options:
11 * -w - enable FTP write commands 11 * -w - enable FTP write commands
12 *
13 * TODO: implement "421 Timeout" thingy (alarm(60) while waiting for a cmd).
12 */ 14 */
13 15
14#include "libbb.h" 16#include "libbb.h"
@@ -98,7 +100,7 @@ struct globals {
98 int data_fd; 100 int data_fd;
99 off_t restart_pos; 101 off_t restart_pos;
100 char *ftp_cmp; 102 char *ftp_cmp;
101 const char *ftp_arg; 103 char *ftp_arg;
102#if ENABLE_FEATURE_FTP_WRITE 104#if ENABLE_FEATURE_FTP_WRITE
103 char *rnfr_filename; 105 char *rnfr_filename;
104#endif 106#endif
@@ -156,22 +158,25 @@ replace_char(char *str, char from, char to)
156} 158}
157 159
158static void 160static void
159ftp_write_str_common(unsigned int status, const char *str, char sep) 161ftp_write_str_common(unsigned status, const char *str, char sep)
160{ 162{
161 char *escaped_str, *response; 163 char *escaped_str, *response;
162 size_t len; 164 int len;
163 165
164 escaped_str = replace_text(str, '\377', "\377\377"); 166 /* FTP allegedly uses telnet protocol for command link.
167 * In telnet, 0xff is an escape char, and needs to be escaped: */
168 escaped_str = replace_text(str, '\xff', "\xff\xff");
165 169
166 response = xasprintf("%u%c%s\r\n", status, sep, escaped_str); 170 response = xasprintf("%u%c%s\r", status, sep, escaped_str);
167 free(escaped_str); 171 free(escaped_str);
168 172
173 /* ?! does FTP send embedded LFs as NULs? wow */
169 len = strlen(response); 174 len = strlen(response);
170 replace_char(response, '\n', '\0'); 175 replace_char(response, '\n', '\0');
171 176
172 /* Change trailing '\0' back to '\n' */ 177 response[len++] = '\n'; /* tack on trailing '\n' */
173 response[len - 1] = '\n';
174 xwrite(STDIN_FILENO, response, len); 178 xwrite(STDIN_FILENO, response, len);
179 free(response);
175} 180}
176 181
177static void 182static void
@@ -198,35 +203,68 @@ cmdio_write_raw(const char *p_text)
198 xwrite_str(STDIN_FILENO, p_text); 203 xwrite_str(STDIN_FILENO, p_text);
199} 204}
200 205
201static uint32_t 206static void
202cmdio_get_cmd_and_arg(void) 207handle_pwd(void)
203{ 208{
204 size_t len; 209 char *cwd, *promoted_cwd, *response;
205 uint32_t cmdval;
206 char *cmd;
207 210
208 free(G.ftp_cmp); 211 cwd = xrealloc_getcwd_or_warn(NULL);
209 len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */ 212 if (cwd == NULL)
210 G.ftp_cmp = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len); 213 cwd = xstrdup("");
211 if (!cmd)
212 exit(0);
213 214
214 len = strlen(cmd) - 1; 215 /* We have to promote each " to "" */
215 while (len >= 0 && cmd[len] == '\r') { 216 promoted_cwd = replace_text(cwd, '\"', "\"\"");
216 cmd[len] = '\0'; 217 free(cwd);
217 len--; 218 response = xasprintf("\"%s\"", promoted_cwd);
219 free(promoted_cwd);
220 cmdio_write(FTP_PWDOK, response);
221 free(response);
222}
223
224static void
225handle_cwd(void)
226{
227 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
228 cmdio_write(FTP_FILEFAIL, "Can't change directory");
229 return;
218 } 230 }
231 cmdio_write_ok(FTP_CWDOK);
232}
219 233
220 G.ftp_arg = strchr(cmd, ' '); 234static void
221 if (G.ftp_arg != NULL) { 235handle_cdup(void)
222 *(char *)G.ftp_arg = '\0'; 236{
223 G.ftp_arg++; 237 G.ftp_arg = (char*)"..";
238 handle_cwd();
239}
240
241static void
242handle_type(void)
243{
244 if (G.ftp_arg
245 && ( ((G.ftp_arg[0] | 0x20) == 'i' && G.ftp_arg[1] == '\0')
246 || !strcasecmp(G.ftp_arg, "L8")
247 || !strcasecmp(G.ftp_arg, "L 8")
248 )
249 ) {
250 cmdio_write_ok(FTP_TYPEOK);
251 } else {
252 cmdio_write(FTP_BADCMD, "Unrecognised TYPE command");
224 } 253 }
225 cmdval = 0; 254}
226 while (*cmd)
227 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
228 255
229 return cmdval; 256static void
257handle_help(void)
258{
259 cmdio_write_hyphen(FTP_HELP, "Recognized commands:");
260 cmdio_write_raw(" ALLO CDUP CWD HELP LIST\r\n"
261 " MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n"
262 " REST RETR STAT STRU SYST TYPE USER\r\n"
263#if ENABLE_FEATURE_FTP_WRITE
264 " APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n"
265#endif
266 );
267 cmdio_write(FTP_HELP, "Help OK");
230} 268}
231 269
232static void 270static void
@@ -265,7 +303,7 @@ ftpdataio_get_port_fd(void)
265{ 303{
266 int remote_fd; 304 int remote_fd;
267 305
268 /* Do we want die or print error to client? */ 306 /* Do we want to die or print error to client? */
269 remote_fd = xconnect_stream(G.port_addr); 307 remote_fd = xconnect_stream(G.port_addr);
270 308
271 init_data_sock_params(remote_fd); 309 init_data_sock_params(remote_fd);
@@ -289,16 +327,16 @@ ftpdataio_dispose_transfer_fd(void)
289 G.data_fd = -1; 327 G.data_fd = -1;
290} 328}
291 329
292static int 330static inline int
293port_active(void) 331port_active(void)
294{ 332{
295 return (G.port_addr != NULL) ? 1: 0; 333 return (G.port_addr != NULL);
296} 334}
297 335
298static int 336static inline int
299pasv_active(void) 337pasv_active(void)
300{ 338{
301 return (G.pasv_listen_fd != -1) ? 1 : 0; 339 return (G.pasv_listen_fd > STDOUT_FILENO);
302} 340}
303 341
304static int 342static int
@@ -318,41 +356,6 @@ get_remote_transfer_fd(const char *p_status_msg)
318 return remote_fd; 356 return remote_fd;
319} 357}
320 358
321static void
322handle_pwd(void)
323{
324 char *cwd, *promoted_cwd, *response;
325
326 cwd = xrealloc_getcwd_or_warn(NULL);
327 if (cwd == NULL)
328 cwd = xstrdup("");
329
330 /* We have to promote each " to "" */
331 promoted_cwd = replace_text(cwd, '\"', "\"\"");
332 free(cwd);
333 response = xasprintf("\"%s\"", promoted_cwd);
334 free(promoted_cwd);
335 cmdio_write(FTP_PWDOK, response);
336 free(response);
337}
338
339static void
340handle_cwd(void)
341{
342 if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
343 cmdio_write(FTP_FILEFAIL, "Can't change directory");
344 return;
345 }
346 cmdio_write_ok(FTP_CWDOK);
347}
348
349static void
350handle_cdup(void)
351{
352 G.ftp_arg = "..";
353 handle_cwd();
354}
355
356static int 359static int
357data_transfer_checks_ok(void) 360data_transfer_checks_ok(void)
358{ 361{
@@ -365,15 +368,10 @@ data_transfer_checks_ok(void)
365} 368}
366 369
367static void 370static void
368port_cleanup(void) 371port_pasv_cleanup(void)
369{ 372{
370 free(G.port_addr); 373 free(G.port_addr);
371 G.port_addr = NULL; 374 G.port_addr = NULL;
372}
373
374static void
375pasv_cleanup(void)
376{
377 if (G.pasv_listen_fd > STDOUT_FILENO) 375 if (G.pasv_listen_fd > STDOUT_FILENO)
378 close(G.pasv_listen_fd); 376 close(G.pasv_listen_fd);
379 G.pasv_listen_fd = -1; 377 G.pasv_listen_fd = -1;
@@ -385,10 +383,10 @@ handle_pasv(void)
385 int bind_retries = 10; 383 int bind_retries = 10;
386 unsigned short port; 384 unsigned short port;
387 enum { min_port = 1024, max_port = 65535 }; 385 enum { min_port = 1024, max_port = 65535 };
388 char *addr, *wire_addr, *response; 386 char *addr, *response;
387
388 port_pasv_cleanup();
389 389
390 pasv_cleanup();
391 port_cleanup();
392 G.pasv_listen_fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0); 390 G.pasv_listen_fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
393 setsockopt_reuseaddr(G.pasv_listen_fd); 391 setsockopt_reuseaddr(G.pasv_listen_fd);
394 392
@@ -413,14 +411,13 @@ handle_pasv(void)
413 bb_error_msg_and_die("can't create pasv socket"); 411 bb_error_msg_and_die("can't create pasv socket");
414 412
415 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa); 413 addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
416 wire_addr = replace_text(addr, '.', ","); 414 replace_char(addr, '.', ',');
417 free(addr);
418 415
419 response = xasprintf("Entering Passive Mode (%s,%u,%u)", 416 response = xasprintf("Entering Passive Mode (%s,%u,%u)",
420 wire_addr, (int)(port >> 8), (int)(port & 255)); 417 addr, (int)(port >> 8), (int)(port & 255));
418 free(addr);
421 419
422 cmdio_write(FTP_PASVOK, response); 420 cmdio_write(FTP_PASVOK, response);
423 free(wire_addr);
424 free(response); 421 free(response);
425} 422}
426 423
@@ -428,37 +425,35 @@ static void
428handle_port(void) 425handle_port(void)
429{ 426{
430 unsigned short port; 427 unsigned short port;
431 char *raw = NULL, *port_part; 428 char *raw, *port_part;
432 len_and_sockaddr *lsa = NULL; 429 len_and_sockaddr *lsa = NULL;
433 430
434 pasv_cleanup(); 431 port_pasv_cleanup();
435 port_cleanup();
436 432
437 if (G.ftp_arg == NULL) 433 raw = G.ftp_arg;
438 goto bail;
439 434
440 raw = replace_text(G.ftp_arg, ',', "."); 435 /* buglets:
436 * xatou16 will accept wrong input,
437 * xatou16 will exit instead of generating error to peer
438 */
441 439
442 port_part = strrchr(raw, '.'); 440 port_part = strrchr(raw, ',');
443 if (port_part == NULL) 441 if (port_part == NULL)
444 goto bail; 442 goto bail;
445
446 port = xatou16(&port_part[1]); 443 port = xatou16(&port_part[1]);
447 *port_part = '\0'; 444 *port_part = '\0';
448 445
449 port_part = strrchr(raw, '.'); 446 port_part = strrchr(raw, ',');
450 if (port_part == NULL) 447 if (port_part == NULL)
451 goto bail; 448 goto bail;
452
453 port |= xatou16(&port_part[1]) << 8; 449 port |= xatou16(&port_part[1]) << 8;
454 *port_part = '\0'; 450 *port_part = '\0';
455 451
452 replace_char(raw, ',', '.');
456 lsa = xdotted2sockaddr(raw, port); 453 lsa = xdotted2sockaddr(raw, port);
457 454
458bail:
459 free(raw);
460
461 if (lsa == NULL) { 455 if (lsa == NULL) {
456 bail:
462 cmdio_write(FTP_BADCMD, "Illegal PORT command"); 457 cmdio_write(FTP_BADCMD, "Illegal PORT command");
463 return; 458 return;
464 } 459 }
@@ -528,10 +523,10 @@ handle_retr(void)
528 else 523 else
529 cmdio_write_ok(FTP_TRANSFEROK); 524 cmdio_write_ok(FTP_TRANSFEROK);
530 525
531port_pasv_cleanup_out: 526 port_pasv_cleanup_out:
532 port_cleanup(); 527 port_pasv_cleanup();
533 pasv_cleanup(); 528
534file_close_out: 529 file_close_out:
535 close(opened_file); 530 close(opened_file);
536} 531}
537 532
@@ -691,12 +686,11 @@ handle_dir_common(int full_details, int stat_cmd)
691 } else 686 } else
692 write_dirstats(fd, ".", full_details); 687 write_dirstats(fd, ".", full_details);
693 688
694bail: 689 bail:
695 /* Well, if we can't open directory/file it doesn't matter */ 690 /* Well, if we can't open directory/file it doesn't matter */
696 if (!stat_cmd) { 691 if (!stat_cmd) {
697 ftpdataio_dispose_transfer_fd(); 692 ftpdataio_dispose_transfer_fd();
698 pasv_cleanup(); 693 port_pasv_cleanup();
699 port_cleanup();
700 cmdio_write_ok(FTP_TRANSFEROK); 694 cmdio_write_ok(FTP_TRANSFEROK);
701 } else 695 } else
702 cmdio_write_ok(FTP_STATFILE_OK); 696 cmdio_write_ok(FTP_STATFILE_OK);
@@ -728,98 +722,8 @@ handle_stat(void)
728 cmdio_write_ok(FTP_STATOK); 722 cmdio_write_ok(FTP_STATOK);
729} 723}
730 724
731static void
732handle_type(void)
733{
734 if (G.ftp_arg
735 && ( ((G.ftp_arg[0] | 0x20) == 'i' && G.ftp_arg[1] == '\0')
736 || !strcasecmp(G.ftp_arg, "L8")
737 || !strcasecmp(G.ftp_arg, "L 8")
738 )
739 ) {
740 cmdio_write_ok(FTP_TYPEOK);
741 } else {
742 cmdio_write(FTP_BADCMD, "Unrecognised TYPE command");
743 }
744}
745
746static void
747handle_help(void)
748{
749 cmdio_write_hyphen(FTP_HELP, "Recognized commands:");
750 cmdio_write_raw(" ALLO CDUP CWD HELP LIST\r\n"
751 " MODE NLST NOOP PASS PASV PORT PWD QUIT\r\n"
752 " REST RETR STAT STRU SYST TYPE USER\r\n"
753#if ENABLE_FEATURE_FTP_WRITE
754 " APPE DELE MKD RMD RNFR RNTO STOR STOU\r\n"
755#endif
756 );
757 cmdio_write(FTP_HELP, "Help OK");
758}
759
760#if ENABLE_FEATURE_FTP_WRITE 725#if ENABLE_FEATURE_FTP_WRITE
761static void 726static void
762handle_upload_common(int is_append, int is_unique)
763{
764 char *tempname = NULL;
765 int trans_ret;
766 int new_file_fd;
767 int remote_fd;
768 off_t offset = G.restart_pos;
769
770 G.restart_pos = 0;
771 if (!G.ftp_arg || !data_transfer_checks_ok())
772 return;
773
774 if (is_unique) {
775 tempname = xstrdup("FILE: uniq.XXXXXX");
776 /*
777 * XXX Use mkostemp here? vsftpd opens file with O_CREAT, O_WRONLY,
778 * O_APPEND and O_EXCL flags...
779 */
780 new_file_fd = mkstemp(tempname + 6);
781 } else {
782 /* XXX Do we need check if ftp_arg != NULL? */
783 if (!is_append && offset == 0)
784 new_file_fd = open(G.ftp_arg, O_CREAT | O_WRONLY | O_APPEND | O_NONBLOCK | O_TRUNC, 0666);
785 else
786 new_file_fd = open(G.ftp_arg, O_CREAT | O_WRONLY | O_APPEND | O_NONBLOCK, 0666);
787 }
788
789 if (new_file_fd < 0) {
790 cmdio_write(FTP_UPLOADFAIL, "Can't create file");
791 return;
792 }
793
794 if (!is_append && offset != 0) {
795 /* warning, allows seek past end of file! Check for seek > size? */
796 xlseek(new_file_fd, offset, SEEK_SET);
797 }
798
799 if (tempname) {
800 remote_fd = get_remote_transfer_fd(tempname);
801 free(tempname);
802 } else
803 remote_fd = get_remote_transfer_fd("Ok to send data");
804
805 if (remote_fd < 0)
806 goto bail;
807
808 trans_ret = bb_copyfd_eof(remote_fd, new_file_fd);
809 ftpdataio_dispose_transfer_fd();
810
811 if (trans_ret < 0)
812 cmdio_write(FTP_BADSENDFILE, "Failure writing to local file");
813 else
814 cmdio_write_ok(FTP_TRANSFEROK);
815
816bail:
817 port_cleanup();
818 pasv_cleanup();
819 close(new_file_fd);
820}
821
822static void
823handle_mkd(void) 727handle_mkd(void)
824{ 728{
825 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) { 729 if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
@@ -892,6 +796,62 @@ handle_rnto(void)
892} 796}
893 797
894static void 798static void
799handle_upload_common(int is_append, int is_unique)
800{
801 char *tempname = NULL;
802 int trans_ret;
803 int local_file_fd;
804 int remote_fd;
805 off_t offset;
806
807 offset = G.restart_pos;
808 G.restart_pos = 0;
809
810 if (!data_transfer_checks_ok())
811 return;
812
813 local_file_fd = -1;
814 if (is_unique) {
815 tempname = xstrdup("FILE: uniq.XXXXXX");
816 local_file_fd = mkstemp(tempname + 6);
817 } else if (G.ftp_arg) {
818 int flags = O_WRONLY | O_CREAT | O_TRUNC;
819 if (is_append)
820 flags = O_WRONLY | O_CREAT | O_APPEND;
821 if (offset)
822 flags = O_WRONLY | O_CREAT;
823 local_file_fd = open(G.ftp_arg, flags, 0666);
824 }
825 if (local_file_fd < 0) {
826 cmdio_write(FTP_UPLOADFAIL, "Can't create file");
827 return;
828 }
829
830 /* TODO: paranoia: fstat it, refuse to do anything if it's not a regular file */
831
832 if (offset)
833 xlseek(local_file_fd, offset, SEEK_SET);
834
835 remote_fd = get_remote_transfer_fd(tempname ? tempname : "Ok to send data");
836 free(tempname);
837
838 if (remote_fd < 0)
839 goto bail;
840
841 trans_ret = bb_copyfd_eof(remote_fd, local_file_fd);
842 ftpdataio_dispose_transfer_fd();
843
844 if (trans_ret < 0)
845 cmdio_write(FTP_BADSENDFILE, "Failure writing to local file");
846 else
847 cmdio_write_ok(FTP_TRANSFEROK);
848
849 bail:
850 port_pasv_cleanup();
851 close(local_file_fd);
852}
853
854static void
895handle_stor(void) 855handle_stor(void)
896{ 856{
897 handle_upload_common(0, 0); 857 handle_upload_common(0, 0);
@@ -900,16 +860,49 @@ handle_stor(void)
900static void 860static void
901handle_appe(void) 861handle_appe(void)
902{ 862{
863 G.restart_pos = 0;
903 handle_upload_common(1, 0); 864 handle_upload_common(1, 0);
904} 865}
905 866
906static void 867static void
907handle_stou(void) 868handle_stou(void)
908{ 869{
870 G.restart_pos = 0;
909 handle_upload_common(0, 1); 871 handle_upload_common(0, 1);
910} 872}
911#endif /* ENABLE_FEATURE_FTP_WRITE */ 873#endif /* ENABLE_FEATURE_FTP_WRITE */
912 874
875static uint32_t
876cmdio_get_cmd_and_arg(void)
877{
878 size_t len;
879 uint32_t cmdval;
880 char *cmd;
881
882 free(G.ftp_cmp);
883 len = 8 * 1024; /* Paranoia. Peer may send 1 gigabyte long cmd... */
884 G.ftp_cmp = cmd = xmalloc_reads(STDIN_FILENO, NULL, &len);
885 if (!cmd)
886 exit(0);
887
888 len = strlen(cmd) - 1;
889 while (len >= 0 && cmd[len] == '\r') {
890 cmd[len] = '\0';
891 len--;
892 }
893
894 G.ftp_arg = strchr(cmd, ' ');
895 if (G.ftp_arg != NULL) {
896 *G.ftp_arg = '\0';
897 G.ftp_arg++;
898 }
899 cmdval = 0;
900 while (*cmd)
901 cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
902
903 return cmdval;
904}
905
913int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 906int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
914int ftpd_main(int argc UNUSED_PARAM, char **argv) 907int ftpd_main(int argc UNUSED_PARAM, char **argv)
915{ 908{