Annotation of /tags/mkinitrd-6_2_1/busybox/networking/ftpgetput.c
Parent Directory
|
Revision Log
Revision 532 -
(hide annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 10 months ago) by niro
Original Path: trunk/mkinitrd-magellan/busybox/networking/ftpgetput.c
File MIME type: text/plain
File size: 8412 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 10 months ago) by niro
Original Path: trunk/mkinitrd-magellan/busybox/networking/ftpgetput.c
File MIME type: text/plain
File size: 8412 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | niro | 532 | /* 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 <bug1@iinet.net.au> | ||
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 tarball for details. | ||
14 | */ | ||
15 | |||
16 | #include "busybox.h" | ||
17 | #include <getopt.h> | ||
18 | |||
19 | typedef struct ftp_host_info_s { | ||
20 | char *user; | ||
21 | char *password; | ||
22 | struct len_and_sockaddr *lsa; | ||
23 | } ftp_host_info_t; | ||
24 | |||
25 | static smallint verbose_flag; | ||
26 | static smallint do_continue; | ||
27 | |||
28 | static void ftp_die(const char *msg, const char *remote) ATTRIBUTE_NORETURN; | ||
29 | static void ftp_die(const char *msg, const char *remote) | ||
30 | { | ||
31 | /* Guard against garbage from remote server */ | ||
32 | const char *cp = remote; | ||
33 | while (*cp >= ' ' && *cp < '\x7f') cp++; | ||
34 | bb_error_msg_and_die("unexpected server response%s%s: %.*s", | ||
35 | msg ? " to " : "", msg ? msg : "", | ||
36 | (int)(cp - remote), remote); | ||
37 | } | ||
38 | |||
39 | |||
40 | static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf) | ||
41 | { | ||
42 | unsigned n; | ||
43 | if (verbose_flag) { | ||
44 | bb_error_msg("cmd %s %s", s1, s2); | ||
45 | } | ||
46 | |||
47 | if (s1) { | ||
48 | if (s2) { | ||
49 | fprintf(stream, "%s %s\r\n", s1, s2); | ||
50 | } else { | ||
51 | fprintf(stream, "%s\r\n", s1); | ||
52 | } | ||
53 | } | ||
54 | do { | ||
55 | char *buf_ptr; | ||
56 | |||
57 | if (fgets(buf, 510, stream) == NULL) { | ||
58 | bb_perror_msg_and_die("fgets"); | ||
59 | } | ||
60 | buf_ptr = strstr(buf, "\r\n"); | ||
61 | if (buf_ptr) { | ||
62 | *buf_ptr = '\0'; | ||
63 | } | ||
64 | } while (!isdigit(buf[0]) || buf[3] != ' '); | ||
65 | |||
66 | buf[3] = '\0'; | ||
67 | n = xatou(buf); | ||
68 | buf[3] = ' '; | ||
69 | return n; | ||
70 | } | ||
71 | |||
72 | static int xconnect_ftpdata(ftp_host_info_t *server, char *buf) | ||
73 | { | ||
74 | char *buf_ptr; | ||
75 | unsigned short port_num; | ||
76 | |||
77 | /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage] | ||
78 | * Server's IP is N1.N2.N3.N4 (we ignore it) | ||
79 | * Server's port for data connection is P1*256+P2 */ | ||
80 | buf_ptr = strrchr(buf, ')'); | ||
81 | if (buf_ptr) *buf_ptr = '\0'; | ||
82 | |||
83 | buf_ptr = strrchr(buf, ','); | ||
84 | *buf_ptr = '\0'; | ||
85 | port_num = xatoul_range(buf_ptr + 1, 0, 255); | ||
86 | |||
87 | buf_ptr = strrchr(buf, ','); | ||
88 | *buf_ptr = '\0'; | ||
89 | port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256; | ||
90 | |||
91 | set_nport(server->lsa, htons(port_num)); | ||
92 | return xconnect_stream(server->lsa); | ||
93 | } | ||
94 | |||
95 | static FILE *ftp_login(ftp_host_info_t *server) | ||
96 | { | ||
97 | FILE *control_stream; | ||
98 | char buf[512]; | ||
99 | |||
100 | /* Connect to the command socket */ | ||
101 | control_stream = fdopen(xconnect_stream(server->lsa), "r+"); | ||
102 | if (control_stream == NULL) { | ||
103 | /* fdopen failed - extremely unlikely */ | ||
104 | bb_perror_nomsg_and_die(); | ||
105 | } | ||
106 | |||
107 | if (ftpcmd(NULL, NULL, control_stream, buf) != 220) { | ||
108 | ftp_die(NULL, buf); | ||
109 | } | ||
110 | |||
111 | /* Login to the server */ | ||
112 | switch (ftpcmd("USER", server->user, control_stream, buf)) { | ||
113 | case 230: | ||
114 | break; | ||
115 | case 331: | ||
116 | if (ftpcmd("PASS", server->password, control_stream, buf) != 230) { | ||
117 | ftp_die("PASS", buf); | ||
118 | } | ||
119 | break; | ||
120 | default: | ||
121 | ftp_die("USER", buf); | ||
122 | } | ||
123 | |||
124 | ftpcmd("TYPE I", NULL, control_stream, buf); | ||
125 | |||
126 | return control_stream; | ||
127 | } | ||
128 | |||
129 | #if !ENABLE_FTPGET | ||
130 | int ftp_receive(ftp_host_info_t *server, FILE *control_stream, | ||
131 | const char *local_path, char *server_path); | ||
132 | #else | ||
133 | static | ||
134 | int ftp_receive(ftp_host_info_t *server, FILE *control_stream, | ||
135 | const char *local_path, char *server_path) | ||
136 | { | ||
137 | char buf[512]; | ||
138 | /* I think 'filesize' usage here is bogus. Let's see... */ | ||
139 | //off_t filesize = -1; | ||
140 | #define filesize ((off_t)-1) | ||
141 | int fd_data; | ||
142 | int fd_local = -1; | ||
143 | off_t beg_range = 0; | ||
144 | |||
145 | /* Connect to the data socket */ | ||
146 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { | ||
147 | ftp_die("PASV", buf); | ||
148 | } | ||
149 | fd_data = xconnect_ftpdata(server, buf); | ||
150 | |||
151 | if (ftpcmd("SIZE", server_path, control_stream, buf) == 213) { | ||
152 | //filesize = BB_STRTOOFF(buf + 4, NULL, 10); | ||
153 | //if (errno || filesize < 0) | ||
154 | // ftp_die("SIZE", buf); | ||
155 | } else { | ||
156 | do_continue = 0; | ||
157 | } | ||
158 | |||
159 | if (LONE_DASH(local_path)) { | ||
160 | fd_local = STDOUT_FILENO; | ||
161 | do_continue = 0; | ||
162 | } | ||
163 | |||
164 | if (do_continue) { | ||
165 | struct stat sbuf; | ||
166 | if (lstat(local_path, &sbuf) < 0) { | ||
167 | bb_perror_msg_and_die("lstat"); | ||
168 | } | ||
169 | if (sbuf.st_size > 0) { | ||
170 | beg_range = sbuf.st_size; | ||
171 | } else { | ||
172 | do_continue = 0; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | if (do_continue) { | ||
177 | sprintf(buf, "REST %"OFF_FMT"d", beg_range); | ||
178 | if (ftpcmd(buf, NULL, control_stream, buf) != 350) { | ||
179 | do_continue = 0; | ||
180 | } else { | ||
181 | //if (filesize != -1) | ||
182 | // filesize -= beg_range; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | if (ftpcmd("RETR", server_path, control_stream, buf) > 150) { | ||
187 | ftp_die("RETR", buf); | ||
188 | } | ||
189 | |||
190 | /* only make a local file if we know that one exists on the remote server */ | ||
191 | if (fd_local == -1) { | ||
192 | if (do_continue) { | ||
193 | fd_local = xopen(local_path, O_APPEND | O_WRONLY); | ||
194 | } else { | ||
195 | fd_local = xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* Copy the file */ | ||
200 | if (filesize != -1) { | ||
201 | if (bb_copyfd_size(fd_data, fd_local, filesize) == -1) | ||
202 | return EXIT_FAILURE; | ||
203 | } else { | ||
204 | if (bb_copyfd_eof(fd_data, fd_local) == -1) | ||
205 | return EXIT_FAILURE; | ||
206 | } | ||
207 | |||
208 | /* close it all down */ | ||
209 | close(fd_data); | ||
210 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { | ||
211 | ftp_die(NULL, buf); | ||
212 | } | ||
213 | ftpcmd("QUIT", NULL, control_stream, buf); | ||
214 | |||
215 | return EXIT_SUCCESS; | ||
216 | } | ||
217 | #endif | ||
218 | |||
219 | #if !ENABLE_FTPPUT | ||
220 | int ftp_send(ftp_host_info_t *server, FILE *control_stream, | ||
221 | const char *server_path, char *local_path); | ||
222 | #else | ||
223 | static | ||
224 | int ftp_send(ftp_host_info_t *server, FILE *control_stream, | ||
225 | const char *server_path, char *local_path) | ||
226 | { | ||
227 | struct stat sbuf; | ||
228 | char buf[512]; | ||
229 | int fd_data; | ||
230 | int fd_local; | ||
231 | int response; | ||
232 | |||
233 | /* Connect to the data socket */ | ||
234 | if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { | ||
235 | ftp_die("PASV", buf); | ||
236 | } | ||
237 | fd_data = xconnect_ftpdata(server, buf); | ||
238 | |||
239 | /* get the local file */ | ||
240 | fd_local = STDIN_FILENO; | ||
241 | if (NOT_LONE_DASH(local_path)) { | ||
242 | fd_local = xopen(local_path, O_RDONLY); | ||
243 | fstat(fd_local, &sbuf); | ||
244 | |||
245 | sprintf(buf, "ALLO %"OFF_FMT"u", sbuf.st_size); | ||
246 | response = ftpcmd(buf, NULL, control_stream, buf); | ||
247 | switch (response) { | ||
248 | case 200: | ||
249 | case 202: | ||
250 | break; | ||
251 | default: | ||
252 | close(fd_local); | ||
253 | ftp_die("ALLO", buf); | ||
254 | break; | ||
255 | } | ||
256 | } | ||
257 | response = ftpcmd("STOR", server_path, control_stream, buf); | ||
258 | switch (response) { | ||
259 | case 125: | ||
260 | case 150: | ||
261 | break; | ||
262 | default: | ||
263 | close(fd_local); | ||
264 | ftp_die("STOR", buf); | ||
265 | } | ||
266 | |||
267 | /* transfer the file */ | ||
268 | if (bb_copyfd_eof(fd_local, fd_data) == -1) { | ||
269 | exit(EXIT_FAILURE); | ||
270 | } | ||
271 | |||
272 | /* close it all down */ | ||
273 | close(fd_data); | ||
274 | if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { | ||
275 | ftp_die("close", buf); | ||
276 | } | ||
277 | ftpcmd("QUIT", NULL, control_stream, buf); | ||
278 | |||
279 | return EXIT_SUCCESS; | ||
280 | } | ||
281 | #endif | ||
282 | |||
283 | #define FTPGETPUT_OPT_CONTINUE 1 | ||
284 | #define FTPGETPUT_OPT_VERBOSE 2 | ||
285 | #define FTPGETPUT_OPT_USER 4 | ||
286 | #define FTPGETPUT_OPT_PASSWORD 8 | ||
287 | #define FTPGETPUT_OPT_PORT 16 | ||
288 | |||
289 | #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS | ||
290 | static const struct option ftpgetput_long_options[] = { | ||
291 | { "continue", 1, NULL, 'c' }, | ||
292 | { "verbose", 0, NULL, 'v' }, | ||
293 | { "username", 1, NULL, 'u' }, | ||
294 | { "password", 1, NULL, 'p' }, | ||
295 | { "port", 1, NULL, 'P' }, | ||
296 | { 0, 0, 0, 0 } | ||
297 | }; | ||
298 | #endif | ||
299 | |||
300 | int ftpgetput_main(int argc, char **argv) | ||
301 | { | ||
302 | /* content-length of the file */ | ||
303 | unsigned opt; | ||
304 | const char *port = "ftp"; | ||
305 | /* socket to ftp server */ | ||
306 | FILE *control_stream; | ||
307 | /* continue previous transfer (-c) */ | ||
308 | ftp_host_info_t *server; | ||
309 | |||
310 | #if ENABLE_FTPPUT && !ENABLE_FTPGET | ||
311 | # define ftp_action ftp_send | ||
312 | #elif ENABLE_FTPGET && !ENABLE_FTPPUT | ||
313 | # define ftp_action ftp_receive | ||
314 | #else | ||
315 | int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = ftp_send; | ||
316 | /* Check to see if the command is ftpget or ftput */ | ||
317 | if (applet_name[3] == 'g') { | ||
318 | ftp_action = ftp_receive; | ||
319 | } | ||
320 | #endif | ||
321 | |||
322 | /* Set default values */ | ||
323 | server = xmalloc(sizeof(*server)); | ||
324 | server->user = "anonymous"; | ||
325 | server->password = "busybox@"; | ||
326 | |||
327 | /* | ||
328 | * Decipher the command line | ||
329 | */ | ||
330 | #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS | ||
331 | applet_long_options = ftpgetput_long_options; | ||
332 | #endif | ||
333 | opt_complementary = "=3"; /* must have 3 params */ | ||
334 | opt = getopt32(argc, argv, "cvu:p:P:", &server->user, &server->password, &port); | ||
335 | argv += optind; | ||
336 | |||
337 | /* Process the non-option command line arguments */ | ||
338 | if (opt & FTPGETPUT_OPT_CONTINUE) { | ||
339 | do_continue = 1; | ||
340 | } | ||
341 | if (opt & FTPGETPUT_OPT_VERBOSE) { | ||
342 | verbose_flag = 1; | ||
343 | } | ||
344 | |||
345 | /* We want to do exactly _one_ DNS lookup, since some | ||
346 | * sites (i.e. ftp.us.debian.org) use round-robin DNS | ||
347 | * and we want to connect to only one IP... */ | ||
348 | server->lsa = host2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21)); | ||
349 | if (verbose_flag) { | ||
350 | printf("Connecting to %s [%s]\n", argv[0], | ||
351 | xmalloc_sockaddr2dotted(&server->lsa->sa, server->lsa->len)); | ||
352 | } | ||
353 | |||
354 | /* Connect/Setup/Configure the FTP session */ | ||
355 | control_stream = ftp_login(server); | ||
356 | |||
357 | return ftp_action(server, control_stream, argv[1], argv[2]); | ||
358 | } |