Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/networking/ftpgetput.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/networking/ftpgetput.c
r1765 r2725 6 6 * 7 7 * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com> 8 * Copyright (C) 2002 Glenn McGrath <bug1@iinet.net.au>8 * Copyright (C) 2002 Glenn McGrath 9 9 * 10 10 * Based on wget.c by Chip Rosenthal Covad Communications 11 11 * <chip@laserlink.net> 12 12 * 13 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.13 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 14 14 */ 15 15 16 #include <getopt.h>17 16 #include "libbb.h" 18 17 19 typedef struct ftp_host_info_s {18 struct globals { 20 19 const char *user; 21 20 const char *password; 22 21 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 { 22 FILE *control_stream; 23 int verbose_flag; 24 int do_continue; 25 char buf[1]; /* actually [BUFSZ] */ 26 } FIX_ALIASING; 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 41 42 static void ftp_die(const char *msg) NORETURN; 43 static void ftp_die(const char *msg) 44 { 45 char *cp = buf; /* buf holds peer's response */ 46 31 47 /* 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) 48 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 } 54 55 static int ftpcmd(const char *s1, const char *s2) 41 56 { 42 57 unsigned n; 58 43 59 if (verbose_flag) { 44 60 bb_error_msg("cmd %s %s", s1, s2); … … 46 62 47 63 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 } 64 fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3), 65 s1, s2); 66 fflush(control_stream); 67 } 68 54 69 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'; 70 strcpy(buf, "EOF"); 71 if (fgets(buf, BUFSZ - 2, control_stream) == NULL) { 72 ftp_die(NULL); 63 73 } 64 74 } while (!isdigit(buf[0]) || buf[3] != ' '); … … 70 80 } 71 81 72 static int xconnect_ftpdata(ftp_host_info_t *server, char *buf) 82 static void ftp_login(void) 83 { 84 /* 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) 73 112 { 74 113 char *buf_ptr; 75 unsigned short port_num; 114 unsigned port_num; 115 116 /* 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 } 76 139 77 140 /* Response is "NNN garbageN1,N2,N3,N4,P1,P2[)garbage] … … 89 152 port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256; 90 153 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; 154 set_nport(lsa, htons(port_num)); 155 return xconnect_stream(lsa); 156 } 157 158 static int pump_data_and_QUIT(int from, int to) 159 { 160 /* 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 } 165 166 /* close data connection */ 167 close(from); /* don't know which one is that, so we close both */ 168 close(to); 169 170 /* does server confirm that transfer is finished? */ 171 if (ftpcmd(NULL, NULL) != 226) { 172 ftp_die(NULL); 173 } 174 ftpcmd("QUIT", NULL); 175 176 return EXIT_SUCCESS; 127 177 } 128 178 129 179 #if !ENABLE_FTPGET 130 int ftp_receive(ftp_host_info_t *server, FILE *control_stream, 131 const char *local_path, char *server_path); 180 int ftp_receive(const char *local_path, char *server_path); 132 181 #else 133 182 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) 183 int ftp_receive(const char *local_path, char *server_path) 184 { 141 185 int fd_data; 142 186 int fd_local = -1; 143 187 off_t beg_range = 0; 144 188 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 { 189 /* connect to the data socket */ 190 fd_data = xconnect_ftpdata(); 191 192 if (ftpcmd("SIZE", server_path) != 213) { 156 193 do_continue = 0; 157 194 } … … 164 201 if (do_continue) { 165 202 struct stat sbuf; 166 if (lstat(local_path, &sbuf) < 0) { 167 bb_perror_msg_and_die("lstat"); 203 /* lstat would be wrong here! */ 204 if (stat(local_path, &sbuf) < 0) { 205 bb_perror_msg_and_die("stat"); 168 206 } 169 207 if (sbuf.st_size > 0) { … … 175 213 176 214 if (do_continue) { 177 sprintf(buf, "REST %"OFF_FMT" d", beg_range);178 if (ftpcmd(buf, NULL , control_stream, buf) != 350) {215 sprintf(buf, "REST %"OFF_FMT"u", beg_range); 216 if (ftpcmd(buf, NULL) != 350) { 179 217 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 */ 218 } 219 } 220 221 if (ftpcmd("RETR", server_path) > 150) { 222 ftp_die("RETR"); 223 } 224 225 /* create local file _after_ we know that remote file exists */ 191 226 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; 227 fd_local = xopen(local_path, 228 do_continue ? (O_APPEND | O_WRONLY) 229 : (O_CREAT | O_TRUNC | O_WRONLY) 230 ); 231 } 232 233 return pump_data_and_QUIT(fd_data, fd_local); 216 234 } 217 235 #endif 218 236 219 237 #if !ENABLE_FTPPUT 220 int ftp_send(ftp_host_info_t *server, FILE *control_stream, 221 const char *server_path, char *local_path); 238 int ftp_send(const char *server_path, char *local_path); 222 239 #else 223 240 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]; 241 int ftp_send(const char *server_path, char *local_path) 242 { 229 243 int fd_data; 230 244 int fd_local; 231 245 int response; 232 246 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); 247 /* connect to the data socket */ 248 fd_data = xconnect_ftpdata(); 238 249 239 250 /* get the local file */ 240 251 fd_local = STDIN_FILENO; 241 if (NOT_LONE_DASH(local_path)) {252 if (NOT_LONE_DASH(local_path)) 242 253 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); 254 255 response = ftpcmd("STOR", server_path); 258 256 switch (response) { 259 257 case 125: … … 261 259 break; 262 260 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 261 ftp_die("STOR"); 262 } 263 264 return pump_data_and_QUIT(fd_local, fd_data); 265 } 266 #endif 288 267 289 268 #if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS … … 297 276 #endif 298 277 299 int ftpgetput_main(int argc, char **argv); 300 int ftpgetput_main(int argc, char **argv) 301 { 302 /* content-length of the file */ 278 int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 279 int ftpgetput_main(int argc UNUSED_PARAM, char **argv) 280 { 303 281 unsigned opt; 304 282 const char *port = "ftp"; 305 283 /* socket to ftp server */ 306 FILE *control_stream;307 /* continue previous transfer (-c) */308 ftp_host_info_t *server;309 284 310 285 #if ENABLE_FTPPUT && !ENABLE_FTPGET … … 313 288 # define ftp_action ftp_receive 314 289 #else 315 int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = ftp_send; 290 int (*ftp_action)(const char *, char *) = ftp_send; 291 316 292 /* Check to see if the command is ftpget or ftput */ 317 293 if (applet_name[3] == 'g') { … … 320 296 #endif 321 297 298 INIT_G(); 322 299 /* Set default values */ 323 server = xmalloc(sizeof(*server)); 324 server->user = "anonymous"; 325 server->password = "busybox@"; 300 user = "anonymous"; 301 password = "busybox@"; 326 302 327 303 /* … … 331 307 applet_long_options = ftpgetput_longopts; 332 308 #endif 333 opt_complementary = "=3"; /* must have 3 params */ 334 opt = getopt32(argv, "cvu:p:P:", &server->user, &server->password, &port); 309 opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */ 310 opt = getopt32(argv, "cvu:p:P:", &user, &password, &port, 311 &verbose_flag, &do_continue); 335 312 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 313 345 314 /* We want to do exactly _one_ DNS lookup, since some 346 315 * sites (i.e. ftp.us.debian.org) use round-robin DNS 347 316 * and we want to connect to only one IP... */ 348 server->lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));317 lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21)); 349 318 if (verbose_flag) { 350 319 printf("Connecting to %s (%s)\n", argv[0], 351 xmalloc_sockaddr2dotted(&server->lsa->sa)); 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 } 320 xmalloc_sockaddr2dotted(&lsa->u.sa)); 321 } 322 323 ftp_login(); 324 return ftp_action(argv[1], argv[2] ? argv[2] : argv[1]); 325 }
Note:
See TracChangeset
for help on using the changeset viewer.