1 /* ftpget.c - Get a remote file from FTP. 2 * 3 * Copyright 2013 Ranjan Kumar <ranjankumar.bth (at) gmail.com> 4 * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com> 5 * 6 * No Standard. 7 * 8 USE_FTPGET(NEWTOY(ftpget, "<2cvu:p:P#<0=21>65535", TOYFLAG_BIN)) 9 USE_FTPGET(OLDTOY(ftpput, ftpget, TOYFLAG_BIN)) 10 11 config FTPGET 12 bool "ftpget/ftpput" 13 default n 14 help 15 usage: ftpget [-cv] [-u USER -p PASSWORD -P PORT] HOST_NAME [LOCAL_FILENAME] REMOTE_FILENAME 16 usage: ftpput [-v] [-u USER -p PASSWORD -P PORT] HOST_NAME [REMOTE_FILENAME] LOCAL_FILENAME 17 18 ftpget - Get a remote file from FTP. 19 ftpput - Upload a local file on remote machine through FTP. 20 21 -c Continue previous transfer. 22 -v Verbose. 23 -u User name. 24 -p Password. 25 -P Port Number (default 21). 26 */ 27 #define FOR_ftpget 28 #include "toys.h" 29 30 GLOBALS( 31 long port; // char *port; 32 char *password; 33 char *username; 34 35 FILE *sockfp; 36 int c; 37 int isget; 38 char buf[sizeof(struct sockaddr_storage)]; 39 ) 40 41 #define DATACONNECTION_OPENED 125 42 #define FTPFILE_STATUSOKAY 150 43 #define FTP_COMMAND_OKAY 200 44 #define FTPFILE_STATUS 213 45 #define FTPSERVER_READY 220 46 #define CLOSE_DATACONECTION 226 47 #define PASSIVE_MODE 227 48 #define USERLOGGED_SUCCESS 230 49 #define PASSWORD_REQUEST 331 50 #define REQUESTED_PENDINGACTION 350 51 52 53 static void setport(unsigned port_num) 54 { 55 int af = ((struct sockaddr *)TT.buf)->sa_family; 56 57 if (af == AF_INET) ((struct sockaddr_in*)TT.buf)->sin_port = port_num; 58 else if (af == AF_INET6) ((struct sockaddr_in6*)TT.buf)->sin6_port = port_num; 59 } 60 61 static int connect_to_stream() 62 { 63 int sockfd, af = ((struct sockaddr *)TT.buf)->sa_family; 64 65 sockfd = xsocket(af, SOCK_STREAM, 0); 66 if (connect(sockfd, (struct sockaddr*)TT.buf,((af == AF_INET)? 67 sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))) < 0) { 68 close(sockfd); 69 perror_exit("can't connect to remote host"); 70 } 71 return sockfd; 72 } 73 74 //close ftp connection and print the message. 75 static void close_stream(char *msg_str) 76 { 77 char *str = toybuf; //toybuf holds response data. 78 79 //Remove garbage chars (from ' ' space to '\x7f') DEL remote server response. 80 while ((*str >= 0x20) && (*str < 0x7f)) str++; 81 *str = '\0'; 82 if (TT.sockfp) fclose(TT.sockfp); 83 error_exit("%s server response: %s", (msg_str) ? msg_str:"", toybuf); 84 } 85 86 //send command to ftp and get return status. 87 static int get_ftp_response(char *command, char *param) 88 { 89 unsigned cmd_status = 0; 90 char *fmt = "%s %s\r\n"; 91 92 if (command) { 93 if (!param) fmt += 3; 94 fprintf(TT.sockfp, fmt, command, param); 95 fflush(TT.sockfp); 96 if (toys.optflags & FLAG_v) 97 fprintf(stderr, "FTP Request: %s %s\r\n", command, param); 98 } 99 100 do { 101 if (!fgets(toybuf, sizeof(toybuf)-1, TT.sockfp)) close_stream(NULL); 102 } while (!isdigit(toybuf[0]) || toybuf[3] != ' '); 103 104 toybuf[3] = '\0'; 105 cmd_status = atolx_range(toybuf, 0, INT_MAX); 106 toybuf[3] = ' '; 107 return cmd_status; 108 } 109 110 static void send_requests(void) 111 { 112 int cmd_status = 0; 113 114 //FTP connection request. 115 if (get_ftp_response(NULL, NULL) != FTPSERVER_READY) close_stream(NULL); 116 117 //230 User authenticated, password please; 331 Password request. 118 cmd_status = get_ftp_response("USER", TT.username); 119 if (cmd_status == PASSWORD_REQUEST) { //user logged in. Need Password. 120 if (get_ftp_response("PASS", TT.password) != USERLOGGED_SUCCESS) 121 close_stream("PASS"); 122 } else if (cmd_status == USERLOGGED_SUCCESS); //do nothing 123 else close_stream("USER"); 124 //200 Type Binary. Command okay. 125 if (get_ftp_response("TYPE I", NULL) != FTP_COMMAND_OKAY) 126 close_stream("TYPE I"); 127 } 128 129 static void get_sockaddr(char *host) 130 { 131 struct addrinfo hints, *result; 132 char port[6]; 133 int status; 134 135 errno = 0; 136 snprintf(port, 6, "%ld", TT.port); 137 138 memset(&hints, 0 , sizeof(struct addrinfo)); 139 hints.ai_family = AF_UNSPEC; 140 hints.ai_socktype = SOCK_STREAM; 141 142 status = getaddrinfo(host, port, &hints, &result); 143 if (status) error_exit("bad address '%s' : %s", host, gai_strerror(status)); 144 145 memcpy(TT.buf, result->ai_addr, result->ai_addrlen); 146 freeaddrinfo(result); 147 } 148 149 // send commands to ftp fo PASV mode. 150 static void verify_pasv_mode(char *r_filename) 151 { 152 char *pch; 153 unsigned portnum; 154 155 //vsftpd reply like:- "227 Entering Passive Mode (125,19,39,117,43,39)". 156 if (get_ftp_response("PASV", NULL) != PASSIVE_MODE) goto close_stream; 157 158 //Response is "NNN <some text> (N1,N2,N3,N4,P1,P2) garbage. 159 //Server's IP is N1.N2.N3.N4 160 //Server's port for data connection is P1*256+P2. 161 if (!(pch = strrchr(toybuf, ')'))) goto close_stream; 162 *pch = '\0'; 163 if (!(pch = strrchr(toybuf, ','))) goto close_stream; 164 *pch = '\0'; 165 166 portnum = atolx_range(pch + 1, 0, 255); 167 168 if (!(pch = strrchr(toybuf, ','))) goto close_stream; 169 *pch = '\0'; 170 portnum = portnum + (atolx_range(pch + 1, 0, 255) * 256); 171 setport(htons(portnum)); 172 173 if (TT.isget && get_ftp_response("SIZE", r_filename) != FTPFILE_STATUS) 174 TT.c = 0; 175 return; 176 177 close_stream: 178 close_stream("PASV"); 179 } 180 181 /* 182 * verify the local file presence. 183 * if present, get the size of the file. 184 */ 185 static void is_localfile_present(char *l_filename) 186 { 187 struct stat sb; 188 189 if (stat(l_filename, &sb) < 0) perror_exit("stat"); 190 //if local file present, then request for pending file action. 191 if (sb.st_size > 0) { 192 sprintf(toybuf, "REST %lu", (unsigned long) sb.st_size); 193 if (get_ftp_response(toybuf, NULL) != REQUESTED_PENDINGACTION) TT.c = 0; 194 } else TT.c = 0; 195 } 196 197 static void transfer_file(int local_fd, int remote_fd) 198 { 199 int len, rfd = (TT.isget)?remote_fd:local_fd, 200 wfd = (TT.isget)?local_fd:remote_fd; 201 202 if (rfd < 0 || wfd < 0) error_exit("Error in file creation:"); 203 while ((len = xread(rfd, toybuf, sizeof(toybuf)))) xwrite(wfd, toybuf, len); 204 } 205 206 static void get_file(char *l_filename, char *r_filename) 207 { 208 int local_fd = -1, remote_fd; 209 210 verify_pasv_mode(r_filename); 211 remote_fd = connect_to_stream(); //Connect to data socket. 212 213 //if local file name will be '-' then local fd will be stdout. 214 if ((l_filename[0] == '-') && !l_filename[1]) { 215 local_fd = 1; //file descriptor will become stdout. 216 TT.c = 0; 217 } 218 219 //if continue, check for local file existance. 220 if (TT.c) is_localfile_present(l_filename); 221 222 //verify the remote file presence. 223 if (get_ftp_response("RETR", r_filename) > FTPFILE_STATUSOKAY) 224 close_stream("RETR"); 225 226 //if local fd is not stdout, create a file descriptor. 227 if (local_fd == -1) { 228 int flags = O_WRONLY; 229 230 flags |= (TT.c)? O_APPEND : (O_CREAT | O_TRUNC); 231 local_fd = xcreate((char *)l_filename, flags, 0666); 232 } 233 transfer_file(local_fd, remote_fd); 234 xclose(remote_fd); 235 xclose(local_fd); 236 if (get_ftp_response(NULL, NULL) != CLOSE_DATACONECTION) close_stream(NULL); 237 get_ftp_response("QUIT", NULL); 238 toys.exitval = EXIT_SUCCESS; 239 } 240 241 static void put_file(char *r_filename, char *l_filename) 242 { 243 int local_fd = 0, remote_fd; 244 unsigned cmd_status = 0; 245 246 verify_pasv_mode(r_filename); 247 remote_fd = connect_to_stream(); //Connect to data socket. 248 249 //open the local file for transfer. 250 if ((l_filename[0] != '-') || l_filename[1]) 251 local_fd = xcreate((char *)l_filename, O_RDONLY, 0666); 252 253 //verify for the remote file status, Ok or Open: transfer File. 254 cmd_status = get_ftp_response("STOR", r_filename); 255 if ( (cmd_status == DATACONNECTION_OPENED) || 256 (cmd_status == FTPFILE_STATUSOKAY)) { 257 transfer_file(local_fd, remote_fd); 258 if (get_ftp_response(NULL, NULL) != CLOSE_DATACONECTION) close_stream(NULL); 259 get_ftp_response("QUIT", NULL); 260 toys.exitval = EXIT_SUCCESS; 261 } else { 262 toys.exitval = EXIT_FAILURE; 263 close_stream("STOR"); 264 } 265 xclose(remote_fd); 266 xclose(local_fd); 267 } 268 269 void ftpget_main(void) 270 { 271 char **argv = toys.optargs; //host name + file name. 272 273 TT.isget = toys.which->name[3] == 'g'; 274 TT.c = 1; 275 //if user name is not specified. 276 if (!(toys.optflags & FLAG_u) && (toys.optflags & FLAG_p)) 277 error_exit("Missing username:"); 278 //if user name and password is not specified in command line. 279 if (!(toys.optflags & FLAG_u) && !(toys.optflags & FLAG_p)) 280 TT.username = TT.password ="anonymous"; 281 282 //if continue is not in the command line argument. 283 if (TT.isget && !(toys.optflags & FLAG_c)) TT.c = 0; 284 285 if (toys.optflags & FLAG_v) fprintf(stderr, "Connecting to %s\n", argv[0]); 286 get_sockaddr(argv[0]); 287 288 TT.sockfp = xfdopen(connect_to_stream(), "r+"); 289 send_requests(); 290 291 if (TT.isget) get_file(argv[1], argv[2] ? argv[2] : argv[1]); 292 else put_file(argv[1], argv[2] ? argv[2] : argv[1]); 293 } 294