Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/networking/ftpgetput.c

Parent Directory Parent Directory | Revision Log Revision Log


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