source: MondoRescue/branches/3.3/mindi-busybox/networking/ftpgetput.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 8.7 KB
RevLine 
[821]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>
[2725]8 * Copyright (C) 2002 Glenn McGrath
[821]9 *
10 * Based on wget.c by Chip Rosenthal Covad Communications
11 * <chip@laserlink.net>
12 *
[2725]13 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[821]14 */
15
[3232]16//usage:#define ftpget_trivial_usage
17//usage: "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE"
18//usage:#define ftpget_full_usage "\n\n"
19//usage: "Download a file via FTP\n"
20//usage: IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
21//usage: "\n -c,--continue Continue previous transfer"
22//usage: "\n -v,--verbose Verbose"
23//usage: "\n -u,--username USER Username"
24//usage: "\n -p,--password PASS Password"
25//usage: "\n -P,--port NUM Port"
26//usage: )
27//usage: IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
28//usage: "\n -c Continue previous transfer"
29//usage: "\n -v Verbose"
30//usage: "\n -u USER Username"
31//usage: "\n -p PASS Password"
32//usage: "\n -P NUM Port"
33//usage: )
34//usage:
35//usage:#define ftpput_trivial_usage
36//usage: "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE"
37//usage:#define ftpput_full_usage "\n\n"
38//usage: "Upload a file to a FTP server\n"
39//usage: IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
40//usage: "\n -v,--verbose Verbose"
41//usage: "\n -u,--username USER Username"
42//usage: "\n -p,--password PASS Password"
43//usage: "\n -P,--port NUM Port"
44//usage: )
45//usage: IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
46//usage: "\n -v Verbose"
47//usage: "\n -u USER Username"
48//usage: "\n -p PASS Password"
49//usage: "\n -P NUM Port number"
50//usage: )
51
[1765]52#include "libbb.h"
[3621]53#include "common_bufsiz.h"
[821]54
[2725]55struct globals {
[1765]56 const char *user;
57 const char *password;
58 struct len_and_sockaddr *lsa;
[2725]59 FILE *control_stream;
60 int verbose_flag;
61 int do_continue;
[3232]62 char buf[4]; /* actually [BUFSZ] */
[2725]63} FIX_ALIASING;
[3621]64#define G (*(struct globals*)bb_common_bufsiz1)
[2725]65enum { 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 )
[3621]73#define INIT_G() do { \
74 setup_common_bufsiz(); \
75 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
76} while (0)
[821]77
78
[2725]79static void ftp_die(const char *msg) NORETURN;
80static void ftp_die(const char *msg)
[1765]81{
[2725]82 char *cp = buf; /* buf holds peer's response */
83
[1765]84 /* Guard against garbage from remote server */
[2725]85 while (*cp >= ' ' && *cp < '\x7f')
86 cp++;
87 *cp = '\0';
88 bb_error_msg_and_die("unexpected server response%s%s: %s",
89 (msg ? " to " : ""), (msg ? msg : ""), buf);
[1765]90}
91
[2725]92static int ftpcmd(const char *s1, const char *s2)
[821]93{
[1765]94 unsigned n;
[2725]95
[821]96 if (verbose_flag) {
[1765]97 bb_error_msg("cmd %s %s", s1, s2);
[821]98 }
99
100 if (s1) {
[2725]101 fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3),
102 s1, s2);
103 fflush(control_stream);
[821]104 }
[2725]105
[821]106 do {
[3232]107 strcpy(buf, "EOF"); /* for ftp_die */
[2725]108 if (fgets(buf, BUFSZ - 2, control_stream) == NULL) {
109 ftp_die(NULL);
[821]110 }
[1765]111 } while (!isdigit(buf[0]) || buf[3] != ' ');
[821]112
[1765]113 buf[3] = '\0';
114 n = xatou(buf);
115 buf[3] = ' ';
116 return n;
[821]117}
118
[2725]119static void ftp_login(void)
[821]120{
[2725]121 /* Connect to the command socket */
122 control_stream = fdopen(xconnect_stream(lsa), "r+");
123 if (control_stream == NULL) {
124 /* fdopen failed - extremely unlikely */
125 bb_perror_nomsg_and_die();
126 }
127
128 if (ftpcmd(NULL, NULL) != 220) {
129 ftp_die(NULL);
130 }
131
132 /* Login to the server */
133 switch (ftpcmd("USER", user)) {
134 case 230:
135 break;
136 case 331:
137 if (ftpcmd("PASS", password) != 230) {
138 ftp_die("PASS");
139 }
140 break;
141 default:
142 ftp_die("USER");
143 }
144
145 ftpcmd("TYPE I", NULL);
146}
147
148static int xconnect_ftpdata(void)
149{
[821]150 char *buf_ptr;
[2725]151 unsigned port_num;
[821]152
[2725]153/*
154TODO: PASV command will not work for IPv6. RFC2428 describes
155IPv6-capable "extended PASV" - EPSV.
156
157"EPSV [protocol]" asks server to bind to and listen on a data port
158in specified protocol. Protocol is 1 for IPv4, 2 for IPv6.
159If not specified, defaults to "same as used for control connection".
160If server understood you, it should answer "229 <some text>(|||port|)"
161where "|" are literal pipe chars and "port" is ASCII decimal port#.
162
163There is also an IPv6-capable replacement for PORT (EPRT),
164but we don't need that.
165
166NB: PASV may still work for some servers even over IPv6.
167For example, vsftp happily answers
168"227 Entering Passive Mode (0,0,0,0,n,n)" and proceeds as usual.
169
170TODO2: need to stop ignoring IP address in PASV response.
171*/
172
173 if (ftpcmd("PASV", NULL) != 227) {
174 ftp_die("PASV");
175 }
176
[1765]177 /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage]
178 * Server's IP is N1.N2.N3.N4 (we ignore it)
179 * Server's port for data connection is P1*256+P2 */
180 buf_ptr = strrchr(buf, ')');
181 if (buf_ptr) *buf_ptr = '\0';
182
[821]183 buf_ptr = strrchr(buf, ',');
184 *buf_ptr = '\0';
[1765]185 port_num = xatoul_range(buf_ptr + 1, 0, 255);
[821]186
187 buf_ptr = strrchr(buf, ',');
188 *buf_ptr = '\0';
[1765]189 port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256;
[821]190
[3232]191 set_nport(&lsa->u.sa, htons(port_num));
[2725]192 return xconnect_stream(lsa);
[821]193}
194
[2725]195static int pump_data_and_QUIT(int from, int to)
[821]196{
[2725]197 /* copy the file */
198 if (bb_copyfd_eof(from, to) == -1) {
199 /* error msg is already printed by bb_copyfd_eof */
200 return EXIT_FAILURE;
[821]201 }
202
[2725]203 /* close data connection */
204 close(from); /* don't know which one is that, so we close both */
205 close(to);
[821]206
[2725]207 /* does server confirm that transfer is finished? */
208 if (ftpcmd(NULL, NULL) != 226) {
209 ftp_die(NULL);
[821]210 }
[2725]211 ftpcmd("QUIT", NULL);
[821]212
[2725]213 return EXIT_SUCCESS;
[821]214}
215
216#if !ENABLE_FTPGET
[2725]217int ftp_receive(const char *local_path, char *server_path);
[821]218#else
[1765]219static
[2725]220int ftp_receive(const char *local_path, char *server_path)
[821]221{
222 int fd_data;
223 int fd_local = -1;
224 off_t beg_range = 0;
225
[2725]226 /* connect to the data socket */
227 fd_data = xconnect_ftpdata();
[821]228
[2725]229 if (ftpcmd("SIZE", server_path) != 213) {
[821]230 do_continue = 0;
231 }
232
[1765]233 if (LONE_DASH(local_path)) {
[821]234 fd_local = STDOUT_FILENO;
235 do_continue = 0;
236 }
237
238 if (do_continue) {
239 struct stat sbuf;
[2725]240 /* lstat would be wrong here! */
241 if (stat(local_path, &sbuf) < 0) {
242 bb_perror_msg_and_die("stat");
[821]243 }
244 if (sbuf.st_size > 0) {
245 beg_range = sbuf.st_size;
246 } else {
247 do_continue = 0;
248 }
249 }
250
251 if (do_continue) {
[2725]252 sprintf(buf, "REST %"OFF_FMT"u", beg_range);
253 if (ftpcmd(buf, NULL) != 350) {
[821]254 do_continue = 0;
255 }
256 }
257
[2725]258 if (ftpcmd("RETR", server_path) > 150) {
259 ftp_die("RETR");
[821]260 }
261
[2725]262 /* create local file _after_ we know that remote file exists */
[821]263 if (fd_local == -1) {
[2725]264 fd_local = xopen(local_path,
265 do_continue ? (O_APPEND | O_WRONLY)
266 : (O_CREAT | O_TRUNC | O_WRONLY)
267 );
[821]268 }
269
[2725]270 return pump_data_and_QUIT(fd_data, fd_local);
[821]271}
272#endif
273
274#if !ENABLE_FTPPUT
[2725]275int ftp_send(const char *server_path, char *local_path);
[821]276#else
[1765]277static
[2725]278int ftp_send(const char *server_path, char *local_path)
[821]279{
280 int fd_data;
281 int fd_local;
282 int response;
283
[2725]284 /* connect to the data socket */
285 fd_data = xconnect_ftpdata();
[821]286
287 /* get the local file */
[1765]288 fd_local = STDIN_FILENO;
[2725]289 if (NOT_LONE_DASH(local_path))
[1765]290 fd_local = xopen(local_path, O_RDONLY);
[821]291
[2725]292 response = ftpcmd("STOR", server_path);
[821]293 switch (response) {
294 case 125:
295 case 150:
296 break;
297 default:
[2725]298 ftp_die("STOR");
[821]299 }
300
[2725]301 return pump_data_and_QUIT(fd_local, fd_data);
[821]302}
303#endif
304
305#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
[1765]306static const char ftpgetput_longopts[] ALIGN1 =
307 "continue\0" Required_argument "c"
308 "verbose\0" No_argument "v"
309 "username\0" Required_argument "u"
310 "password\0" Required_argument "p"
311 "port\0" Required_argument "P"
312 ;
[821]313#endif
314
[2725]315int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
316int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
[821]317{
[1765]318 const char *port = "ftp";
[821]319 /* socket to ftp server */
320
[1765]321#if ENABLE_FTPPUT && !ENABLE_FTPGET
322# define ftp_action ftp_send
323#elif ENABLE_FTPGET && !ENABLE_FTPPUT
324# define ftp_action ftp_receive
325#else
[2725]326 int (*ftp_action)(const char *, char *) = ftp_send;
327
[821]328 /* Check to see if the command is ftpget or ftput */
[1765]329 if (applet_name[3] == 'g') {
[821]330 ftp_action = ftp_receive;
331 }
[1765]332#endif
[821]333
[2725]334 INIT_G();
[821]335 /* Set default values */
[2725]336 user = "anonymous";
337 password = "busybox@";
[821]338
339 /*
340 * Decipher the command line
341 */
[1765]342#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
343 applet_long_options = ftpgetput_longopts;
344#endif
[2725]345 opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */
[3232]346 getopt32(argv, "cvu:p:P:", &user, &password, &port,
[2725]347 &verbose_flag, &do_continue);
[1765]348 argv += optind;
[821]349
350 /* We want to do exactly _one_ DNS lookup, since some
351 * sites (i.e. ftp.us.debian.org) use round-robin DNS
352 * and we want to connect to only one IP... */
[2725]353 lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));
[821]354 if (verbose_flag) {
[1765]355 printf("Connecting to %s (%s)\n", argv[0],
[2725]356 xmalloc_sockaddr2dotted(&lsa->u.sa));
[821]357 }
358
[2725]359 ftp_login();
360 return ftp_action(argv[1], argv[2] ? argv[2] : argv[1]);
[821]361}
Note: See TracBrowser for help on using the repository browser.