ftpgetput.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * ftpget
  4. *
  5. * Mini implementation of FTP to retrieve a remote file.
  6. *
  7. * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com>
  8. * Copyright (C) 2002 Glenn McGrath
  9. *
  10. * Based on wget.c by Chip Rosenthal Covad Communications
  11. * <chip@laserlink.net>
  12. *
  13. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  14. */
  15. //config:config FTPGET
  16. //config: bool "ftpget (7.8 kb)"
  17. //config: default y
  18. //config: help
  19. //config: Retrieve a remote file via FTP.
  20. //config:
  21. //config:config FTPPUT
  22. //config: bool "ftpput (7.5 kb)"
  23. //config: default y
  24. //config: help
  25. //config: Store a remote file via FTP.
  26. //config:
  27. //config:config FEATURE_FTPGETPUT_LONG_OPTIONS
  28. //config: bool "Enable long options in ftpget/ftpput"
  29. //config: default y
  30. //config: depends on LONG_OPTS && (FTPGET || FTPPUT)
  31. //applet:IF_FTPGET(APPLET_ODDNAME(ftpget, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpget))
  32. //applet:IF_FTPPUT(APPLET_ODDNAME(ftpput, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpput))
  33. //kbuild:lib-$(CONFIG_FTPGET) += ftpgetput.o
  34. //kbuild:lib-$(CONFIG_FTPPUT) += ftpgetput.o
  35. //usage:#define ftpget_trivial_usage
  36. //usage: "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE"
  37. //usage:#define ftpget_full_usage "\n\n"
  38. //usage: "Download a file via FTP\n"
  39. //usage: "\n -c Continue previous transfer"
  40. //usage: "\n -v Verbose"
  41. //usage: "\n -u USER Username"
  42. //usage: "\n -p PASS Password"
  43. //usage: "\n -P PORT"
  44. //usage:
  45. //usage:#define ftpput_trivial_usage
  46. //usage: "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE"
  47. //usage:#define ftpput_full_usage "\n\n"
  48. //usage: "Upload a file to a FTP server\n"
  49. //usage: "\n -v Verbose"
  50. //usage: "\n -u USER Username"
  51. //usage: "\n -p PASS Password"
  52. //usage: "\n -P PORT"
  53. #include "libbb.h"
  54. #include "common_bufsiz.h"
  55. struct globals {
  56. const char *user;
  57. const char *password;
  58. struct len_and_sockaddr *lsa;
  59. FILE *control_stream;
  60. int verbose_flag;
  61. int do_continue;
  62. char buf[4]; /* actually [BUFSZ] */
  63. } FIX_ALIASING;
  64. #define G (*(struct globals*)bb_common_bufsiz1)
  65. enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) };
  66. #define user (G.user )
  67. #define password (G.password )
  68. #define lsa (G.lsa )
  69. #define control_stream (G.control_stream)
  70. #define verbose_flag (G.verbose_flag )
  71. #define do_continue (G.do_continue )
  72. #define buf (G.buf )
  73. #define INIT_G() do { \
  74. setup_common_bufsiz(); \
  75. BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
  76. } while (0)
  77. static void ftp_die(const char *msg) NORETURN;
  78. static void ftp_die(const char *msg)
  79. {
  80. char *cp = buf; /* buf holds peer's response */
  81. /* Guard against garbage from remote server */
  82. while (*cp >= ' ' && *cp < '\x7f')
  83. cp++;
  84. *cp = '\0';
  85. bb_error_msg_and_die("unexpected server response%s%s: %s",
  86. (msg ? " to " : ""), (msg ? msg : ""), buf);
  87. }
  88. static int ftpcmd(const char *s1, const char *s2)
  89. {
  90. unsigned n;
  91. if (verbose_flag) {
  92. bb_error_msg("cmd %s %s", s1, s2);
  93. }
  94. if (s1) {
  95. fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3),
  96. s1, s2);
  97. fflush(control_stream);
  98. }
  99. do {
  100. strcpy(buf, "EOF"); /* for ftp_die */
  101. if (fgets(buf, BUFSZ - 2, control_stream) == NULL) {
  102. ftp_die(NULL);
  103. }
  104. } while (!isdigit(buf[0]) || buf[3] != ' ');
  105. buf[3] = '\0';
  106. n = xatou(buf);
  107. buf[3] = ' ';
  108. return n;
  109. }
  110. static void ftp_login(void)
  111. {
  112. /* Connect to the command socket */
  113. control_stream = fdopen(xconnect_stream(lsa), "r+");
  114. if (control_stream == NULL) {
  115. /* fdopen failed - extremely unlikely */
  116. bb_perror_nomsg_and_die();
  117. }
  118. if (ftpcmd(NULL, NULL) != 220) {
  119. ftp_die(NULL);
  120. }
  121. /* Login to the server */
  122. switch (ftpcmd("USER", user)) {
  123. case 230:
  124. break;
  125. case 331:
  126. if (ftpcmd("PASS", password) != 230) {
  127. ftp_die("PASS");
  128. }
  129. break;
  130. default:
  131. ftp_die("USER");
  132. }
  133. ftpcmd("TYPE I", NULL);
  134. }
  135. static int xconnect_ftpdata(void)
  136. {
  137. int port_num;
  138. if (ENABLE_FEATURE_IPV6 && ftpcmd("EPSV", NULL) == 229) {
  139. /* good */
  140. } else if (ftpcmd("PASV", NULL) != 227) {
  141. ftp_die("PASV");
  142. }
  143. port_num = parse_pasv_epsv(buf);
  144. if (port_num < 0)
  145. ftp_die("PASV");
  146. set_nport(&lsa->u.sa, htons(port_num));
  147. return xconnect_stream(lsa);
  148. }
  149. static int pump_data_and_QUIT(int from, int to)
  150. {
  151. /* copy the file */
  152. if (bb_copyfd_eof(from, to) == -1) {
  153. /* error msg is already printed by bb_copyfd_eof */
  154. return EXIT_FAILURE;
  155. }
  156. /* close data connection */
  157. close(from); /* don't know which one is that, so we close both */
  158. close(to);
  159. /* does server confirm that transfer is finished? */
  160. if (ftpcmd(NULL, NULL) != 226) {
  161. ftp_die(NULL);
  162. }
  163. ftpcmd("QUIT", NULL);
  164. return EXIT_SUCCESS;
  165. }
  166. #if !ENABLE_FTPGET
  167. int ftp_receive(const char *local_path, char *server_path);
  168. #else
  169. static
  170. int ftp_receive(const char *local_path, char *server_path)
  171. {
  172. int fd_data;
  173. int fd_local = -1;
  174. off_t beg_range = 0;
  175. /* connect to the data socket */
  176. fd_data = xconnect_ftpdata();
  177. if (ftpcmd("SIZE", server_path) != 213) {
  178. do_continue = 0;
  179. }
  180. if (LONE_DASH(local_path)) {
  181. fd_local = STDOUT_FILENO;
  182. do_continue = 0;
  183. }
  184. if (do_continue) {
  185. struct stat sbuf;
  186. /* lstat would be wrong here! */
  187. if (stat(local_path, &sbuf) < 0) {
  188. bb_simple_perror_msg_and_die("stat");
  189. }
  190. if (sbuf.st_size > 0) {
  191. beg_range = sbuf.st_size;
  192. } else {
  193. do_continue = 0;
  194. }
  195. }
  196. if (do_continue) {
  197. sprintf(buf, "REST %"OFF_FMT"u", beg_range);
  198. if (ftpcmd(buf, NULL) != 350) {
  199. do_continue = 0;
  200. }
  201. }
  202. if (ftpcmd("RETR", server_path) > 150) {
  203. ftp_die("RETR");
  204. }
  205. /* create local file _after_ we know that remote file exists */
  206. if (fd_local == -1) {
  207. fd_local = xopen(local_path,
  208. do_continue ? (O_APPEND | O_WRONLY)
  209. : (O_CREAT | O_TRUNC | O_WRONLY)
  210. );
  211. }
  212. return pump_data_and_QUIT(fd_data, fd_local);
  213. }
  214. #endif
  215. #if !ENABLE_FTPPUT
  216. int ftp_send(const char *server_path, char *local_path);
  217. #else
  218. static
  219. int ftp_send(const char *server_path, char *local_path)
  220. {
  221. int fd_data;
  222. int fd_local;
  223. int response;
  224. /* connect to the data socket */
  225. fd_data = xconnect_ftpdata();
  226. /* get the local file */
  227. fd_local = STDIN_FILENO;
  228. if (NOT_LONE_DASH(local_path))
  229. fd_local = xopen(local_path, O_RDONLY);
  230. response = ftpcmd("STOR", server_path);
  231. switch (response) {
  232. case 125:
  233. case 150:
  234. break;
  235. default:
  236. ftp_die("STOR");
  237. }
  238. return pump_data_and_QUIT(fd_local, fd_data);
  239. }
  240. #endif
  241. #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
  242. static const char ftpgetput_longopts[] ALIGN1 =
  243. "continue\0" Required_argument "c"
  244. "verbose\0" No_argument "v"
  245. "username\0" Required_argument "u"
  246. "password\0" Required_argument "p"
  247. "port\0" Required_argument "P"
  248. ;
  249. #endif
  250. int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  251. int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
  252. {
  253. const char *port = "ftp";
  254. /* socket to ftp server */
  255. #if ENABLE_FTPPUT && !ENABLE_FTPGET
  256. # define ftp_action ftp_send
  257. #elif ENABLE_FTPGET && !ENABLE_FTPPUT
  258. # define ftp_action ftp_receive
  259. #else
  260. int (*ftp_action)(const char *, char *) = ftp_send;
  261. /* Check to see if the command is ftpget or ftput */
  262. if (applet_name[3] == 'g') {
  263. ftp_action = ftp_receive;
  264. }
  265. #endif
  266. INIT_G();
  267. /* Set default values */
  268. user = "anonymous";
  269. password = "busybox";
  270. /*
  271. * Decipher the command line
  272. */
  273. /* must have 2 to 3 params; -v and -c count */
  274. #define OPTSTRING "^cvu:p:P:" "\0" "-2:?3:vv:cc"
  275. #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
  276. getopt32long(argv, OPTSTRING, ftpgetput_longopts,
  277. #else
  278. getopt32(argv, OPTSTRING,
  279. #endif
  280. &user, &password, &port, &verbose_flag, &do_continue
  281. );
  282. argv += optind;
  283. /* We want to do exactly _one_ DNS lookup, since some
  284. * sites (i.e. ftp.us.debian.org) use round-robin DNS
  285. * and we want to connect to only one IP... */
  286. lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));
  287. if (verbose_flag) {
  288. printf("Connecting to %s (%s)\n", argv[0],
  289. xmalloc_sockaddr2dotted(&lsa->u.sa));
  290. }
  291. ftp_login();
  292. return ftp_action(argv[1], argv[2] ? argv[2] : argv[1]);
  293. }