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#include "libbb.h"
58#include "common_bufsiz.h"
59
60struct globals {
61 const char *user;
62 const char *password;
63 struct len_and_sockaddr *lsa;
64 FILE *control_stream;
65 int verbose_flag;
66 int do_continue;
67 char buf[4];
68} FIX_ALIASING;
69#define G (*(struct globals*)bb_common_bufsiz1)
70enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) };
71#define user (G.user )
72#define password (G.password )
73#define lsa (G.lsa )
74#define control_stream (G.control_stream)
75#define verbose_flag (G.verbose_flag )
76#define do_continue (G.do_continue )
77#define buf (G.buf )
78#define INIT_G() do { \
79 setup_common_bufsiz(); \
80 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
81} while (0)
82
83
84static void ftp_die(const char *msg) NORETURN;
85static void ftp_die(const char *msg)
86{
87 char *cp = buf;
88
89
90 while (*cp >= ' ' && *cp < '\x7f')
91 cp++;
92 *cp = '\0';
93 bb_error_msg_and_die("unexpected server response%s%s: %s",
94 (msg ? " to " : ""), (msg ? msg : ""), buf);
95}
96
97static int ftpcmd(const char *s1, const char *s2)
98{
99 unsigned n;
100
101 if (verbose_flag) {
102 bb_error_msg("cmd %s %s", s1, s2);
103 }
104
105 if (s1) {
106 fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3),
107 s1, s2);
108 fflush(control_stream);
109 }
110
111 do {
112 strcpy(buf, "EOF");
113 if (fgets(buf, BUFSZ - 2, control_stream) == NULL) {
114 ftp_die(NULL);
115 }
116 } while (!isdigit(buf[0]) || buf[3] != ' ');
117
118 buf[3] = '\0';
119 n = xatou(buf);
120 buf[3] = ' ';
121 return n;
122}
123
124static void ftp_login(void)
125{
126
127 control_stream = fdopen(xconnect_stream(lsa), "r+");
128 if (control_stream == NULL) {
129
130 bb_perror_nomsg_and_die();
131 }
132
133 if (ftpcmd(NULL, NULL) != 220) {
134 ftp_die(NULL);
135 }
136
137
138 switch (ftpcmd("USER", user)) {
139 case 230:
140 break;
141 case 331:
142 if (ftpcmd("PASS", password) != 230) {
143 ftp_die("PASS");
144 }
145 break;
146 default:
147 ftp_die("USER");
148 }
149
150 ftpcmd("TYPE I", NULL);
151}
152
153static int xconnect_ftpdata(void)
154{
155 int port_num;
156
157 if (ENABLE_FEATURE_IPV6 && ftpcmd("EPSV", NULL) == 229) {
158
159 } else if (ftpcmd("PASV", NULL) != 227) {
160 ftp_die("PASV");
161 }
162 port_num = parse_pasv_epsv(buf);
163 if (port_num < 0)
164 ftp_die("PASV");
165
166 set_nport(&lsa->u.sa, htons(port_num));
167 return xconnect_stream(lsa);
168}
169
170static int pump_data_and_QUIT(int from, int to)
171{
172
173 if (bb_copyfd_eof(from, to) == -1) {
174
175 return EXIT_FAILURE;
176 }
177
178
179 close(from);
180 close(to);
181
182
183 if (ftpcmd(NULL, NULL) != 226) {
184 ftp_die(NULL);
185 }
186 ftpcmd("QUIT", NULL);
187
188 return EXIT_SUCCESS;
189}
190
191#if !ENABLE_FTPGET
192int ftp_receive(const char *local_path, char *server_path);
193#else
194static
195int ftp_receive(const char *local_path, char *server_path)
196{
197 int fd_data;
198 int fd_local = -1;
199 off_t beg_range = 0;
200
201
202 fd_data = xconnect_ftpdata();
203
204 if (ftpcmd("SIZE", server_path) != 213) {
205 do_continue = 0;
206 }
207
208 if (LONE_DASH(local_path)) {
209 fd_local = STDOUT_FILENO;
210 do_continue = 0;
211 }
212
213 if (do_continue) {
214 struct stat sbuf;
215
216 if (stat(local_path, &sbuf) < 0) {
217 bb_simple_perror_msg_and_die("stat");
218 }
219 if (sbuf.st_size > 0) {
220 beg_range = sbuf.st_size;
221 } else {
222 do_continue = 0;
223 }
224 }
225
226 if (do_continue) {
227 sprintf(buf, "REST %"OFF_FMT"u", beg_range);
228 if (ftpcmd(buf, NULL) != 350) {
229 do_continue = 0;
230 }
231 }
232
233 if (ftpcmd("RETR", server_path) > 150) {
234 ftp_die("RETR");
235 }
236
237
238 if (fd_local == -1) {
239 fd_local = xopen(local_path,
240 do_continue ? (O_APPEND | O_WRONLY)
241 : (O_CREAT | O_TRUNC | O_WRONLY)
242 );
243 }
244
245 return pump_data_and_QUIT(fd_data, fd_local);
246}
247#endif
248
249#if !ENABLE_FTPPUT
250int ftp_send(const char *server_path, char *local_path);
251#else
252static
253int ftp_send(const char *server_path, char *local_path)
254{
255 int fd_data;
256 int fd_local;
257 int response;
258
259
260 fd_data = xconnect_ftpdata();
261
262
263 fd_local = STDIN_FILENO;
264 if (NOT_LONE_DASH(local_path))
265 fd_local = xopen(local_path, O_RDONLY);
266
267 response = ftpcmd("STOR", server_path);
268 switch (response) {
269 case 125:
270 case 150:
271 break;
272 default:
273 ftp_die("STOR");
274 }
275
276 return pump_data_and_QUIT(fd_local, fd_data);
277}
278#endif
279
280#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
281static const char ftpgetput_longopts[] ALIGN1 =
282 "continue\0" Required_argument "c"
283 "verbose\0" No_argument "v"
284 "username\0" Required_argument "u"
285 "password\0" Required_argument "p"
286 "port\0" Required_argument "P"
287 ;
288#endif
289
290int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
291int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
292{
293 const char *port = NULL;
294
295#if ENABLE_FTPPUT && !ENABLE_FTPGET
296# define ftp_action ftp_send
297#elif ENABLE_FTPGET && !ENABLE_FTPPUT
298# define ftp_action ftp_receive
299#else
300 int (*ftp_action)(const char *, char *) = ftp_send;
301
302
303 if (applet_name[3] == 'g') {
304 ftp_action = ftp_receive;
305 }
306#endif
307
308 INIT_G();
309
310 user = "anonymous";
311 password = "busybox";
312
313
314
315
316
317#define OPTSTRING "^cvu:p:P:" "\0" "-2:?3:vv:cc"
318#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
319 getopt32long(argv, OPTSTRING, ftpgetput_longopts,
320#else
321 getopt32(argv, OPTSTRING,
322#endif
323 &user, &password, &port, &verbose_flag, &do_continue
324 );
325 argv += optind;
326
327
328
329
330 lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));
331 if (verbose_flag) {
332 printf("Connecting to %s (%s)\n", argv[0],
333 xmalloc_sockaddr2dotted(&lsa->u.sa));
334 }
335
336 ftp_login();
337 return ftp_action(argv[1], argv[2] ? argv[2] : argv[1]);
338}
339