aboutsummaryrefslogtreecommitdiff
path: root/lib/ftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ftp.c')
-rw-r--r--lib/ftp.c160
1 files changed, 116 insertions, 44 deletions
diff --git a/lib/ftp.c b/lib/ftp.c
index d758301f5..12b41ecc5 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -23,11 +23,6 @@
#include "setup.h"
#ifndef CURL_DISABLE_FTP
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <ctype.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -109,7 +104,7 @@
#endif
#ifdef CURL_DISABLE_VERBOSE_STRINGS
-#define ftp_pasv_verbose(a,b,c,d) do { } while(0)
+#define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
#endif
/* Local API functions */
@@ -139,9 +134,10 @@ static CURLcode ftp_connect(struct connectdata *conn, bool *done);
static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
static CURLcode ftp_nextconnect(struct connectdata *conn);
static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
-static int ftp_getsock(struct connectdata *conn,
- curl_socket_t *socks,
+static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
int numsocks);
+static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
+ int numsocks);
static CURLcode ftp_doing(struct connectdata *conn,
bool *dophase_done);
static CURLcode ftp_setup_connection(struct connectdata * conn);
@@ -176,6 +172,7 @@ const struct Curl_handler Curl_handler_ftp = {
ftp_doing, /* doing */
ftp_getsock, /* proto_getsock */
ftp_getsock, /* doing_getsock */
+ ftp_domore_getsock, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ftp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
@@ -201,6 +198,7 @@ const struct Curl_handler Curl_handler_ftps = {
ftp_doing, /* doing */
ftp_getsock, /* proto_getsock */
ftp_getsock, /* doing_getsock */
+ ftp_domore_getsock, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ftp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
@@ -227,6 +225,7 @@ static const struct Curl_handler Curl_handler_ftp_proxy = {
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_getsock */
ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* readwrite */
@@ -252,6 +251,7 @@ static const struct Curl_handler Curl_handler_ftps_proxy = {
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_getsock */
ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
ZERO_NULL, /* readwrite */
@@ -279,7 +279,7 @@ static void freedirs(struct ftp_conn *ftpc)
{
int i;
if(ftpc->dirs) {
- for(i=0; i < ftpc->dirdepth; i++){
+ for(i=0; i < ftpc->dirdepth; i++) {
if(ftpc->dirs[i]) {
free(ftpc->dirs[i]);
ftpc->dirs[i]=NULL;
@@ -303,8 +303,8 @@ static void freedirs(struct ftp_conn *ftpc)
*/
static bool isBadFtpString(const char *string)
{
- return (bool)((NULL != strchr(string, '\r')) ||
- (NULL != strchr(string, '\n')));
+ return ((NULL != strchr(string, '\r')) ||
+ (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
}
/***********************************************************************
@@ -639,6 +639,37 @@ static int ftp_getsock(struct connectdata *conn,
return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
}
+/* For the FTP "DO_MORE" phase only */
+static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
+ int numsocks)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if(!numsocks)
+ return GETSOCK_BLANK;
+
+ /* When in DO_MORE state, we could be either waiting for us to connect to a
+ remote site, or we could wait for that site to connect to us. Or just
+ handle ordinary commands.
+
+ When waiting for a connect, we will be in FTP_STOP state and then we wait
+ for the secondary socket to become writeable. If we're in another state,
+ we're still handling commands on the control (primary) connection.
+
+ */
+
+ switch(ftpc->state) {
+ case FTP_STOP:
+ break;
+ default:
+ return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
+ }
+
+ socks[0] = conn->sock[SECONDARYSOCKET];
+
+ return GETSOCK_READSOCK(0);
+}
+
/* This is called after the FTP_QUOTE state is passed.
ftp_state_cwd() sends the range of CWD commands to the server to change to
@@ -810,7 +841,8 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
host = addr;
else
host = hbuf; /* use the hbuf for host name */
- }else
+ }
+ else
/* there was only a port(-range) given, default the host */
host = NULL;
} /* data->set.ftpport */
@@ -857,7 +889,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
free(addr);
if(res == NULL) {
- failf(data, "Curl_resolv failed, we can not recover!");
+ failf(data, "failed to resolve the address provided to PORT: %s", host);
return CURLE_FTP_PORT_FAILED;
}
@@ -1004,8 +1036,16 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
sa->sa_family == AF_INET?1:2,
myhost, port);
- if(result)
+ if(result) {
+ failf(data, "Failure sending EPRT command: %s",
+ curl_easy_strerror(result));
+ Curl_closesocket(conn, portsock);
+ /* don't retry using PORT */
+ ftpc->count1 = PORT;
+ /* bail out */
+ state(conn, FTP_STOP);
return result;
+ }
break;
}
else if(PORT == fcmd) {
@@ -1025,8 +1065,14 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
- if(result)
+ if(result) {
+ failf(data, "Failure sending PORT command: %s",
+ curl_easy_strerror(result));
+ Curl_closesocket(conn, portsock);
+ /* bail out */
+ state(conn, FTP_STOP);
return result;
+ }
break;
}
}
@@ -1048,7 +1094,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
The *proper* fix is to make sure that the active connection from the
server is done in a non-blocking way. Currently, it is still BLOCKING.
*/
- conn->bits.tcpconnect = TRUE;
+ conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
state(conn, FTP_PORT);
return result;
@@ -1234,13 +1280,16 @@ static CURLcode ftp_state_post_listtype(struct connectdata *conn)
return CURLE_OUT_OF_MEMORY;
}
- PPSENDF(&conn->proto.ftpc.pp, "%s",cmd);
+ result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
if(lstArg)
free(lstArg);
free(cmd);
+ if(result != CURLE_OK)
+ return result;
+
state(conn, FTP_LIST);
return result;
@@ -1708,7 +1757,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
if(result)
return result;
- conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
+ conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
/*
* When this is used from the multi interface, this might've returned with
@@ -1746,6 +1795,9 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
break;
}
+ if(result)
+ return result;
+
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
/* FIX: this MUST wait for a proper connect first if 'connected' is
* FALSE */
@@ -1768,10 +1820,11 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
data->state.proto.ftp = ftp_save;
- if(CURLE_OK != result)
+ if(result)
return result;
}
+ conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
state(conn, FTP_STOP); /* this phase is completed */
@@ -2528,7 +2581,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
if(ftpcode/100 == 2)
/* We have enabled SSL for the data connection! */
conn->ssl[SECONDARYSOCKET].use =
- (bool)(data->set.ftp_ssl != CURLUSESSL_CONTROL);
+ (data->set.ftp_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
/* FTP servers typically responds with 500 if they decide to reject
our 'P' request */
else if(data->set.ftp_ssl > CURLUSESSL_CONTROL)
@@ -2604,12 +2657,6 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
store++;
ptr++;
}
- if(ftpc->entrypath)
- free(ftpc->entrypath);
- ftpc->entrypath =dir; /* remember this */
- infof(data, "Entry path is '%s'\n", ftpc->entrypath);
- /* also save it where getinfo can access it: */
- data->state.most_recent_ftp_entrypath = ftpc->entrypath;
/* If the path name does not look like an absolute path (i.e.: it
does not start with a '/'), we probably need some server-dependent
@@ -2623,11 +2670,27 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
if the path name looks strange to minimize overhead on other
systems. */
- if(!ftpc->server_os && ftpc->entrypath[0] != '/') {
- PPSENDF(&ftpc->pp, "SYST", NULL);
+ if(!ftpc->server_os && dir[0] != '/') {
+
+ result = Curl_pp_sendf(&ftpc->pp, "SYST", NULL);
+ if(result != CURLE_OK) {
+ free(dir);
+ return result;
+ }
+ Curl_safefree(ftpc->entrypath);
+ ftpc->entrypath = dir; /* remember this */
+ infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ data->state.most_recent_ftp_entrypath = ftpc->entrypath;
state(conn, FTP_SYST);
break;
}
+
+ Curl_safefree(ftpc->entrypath);
+ ftpc->entrypath = dir; /* remember this */
+ infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ data->state.most_recent_ftp_entrypath = ftpc->entrypath;
}
else {
/* couldn't get the path */
@@ -2657,19 +2720,28 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
for(store = os; *ptr && *ptr != ' ';)
*store++ = *ptr++;
*store = '\0'; /* zero terminate */
- ftpc->server_os = os;
/* Check for special servers here. */
- if(strequal(ftpc->server_os, "OS/400")) {
+ if(strequal(os, "OS/400")) {
/* Force OS400 name format 1. */
- PPSENDF(&ftpc->pp, "SITE NAMEFMT 1", NULL);
+ result = Curl_pp_sendf(&ftpc->pp, "SITE NAMEFMT 1", NULL);
+ if(result != CURLE_OK) {
+ free(os);
+ return result;
+ }
+ /* remember target server OS */
+ Curl_safefree(ftpc->server_os);
+ ftpc->server_os = os;
state(conn, FTP_NAMEFMT);
break;
}
- else {
- /* Nothing special for the target server. */
- }
+ else {
+ /* Nothing special for the target server. */
+ /* remember target server OS */
+ Curl_safefree(ftpc->server_os);
+ ftpc->server_os = os;
+ }
}
else {
/* Cannot identify server OS. Continue anyway and cross fingers. */
@@ -2820,7 +2892,7 @@ static CURLcode ftp_multi_statemach(struct connectdata *conn,
/* Check for the state outside of the Curl_socket_ready() return code checks
since at times we are in fact already in this state when this function
gets called. */
- *done = (bool)(ftpc->state == FTP_STOP);
+ *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
return result;
}
@@ -2874,9 +2946,9 @@ static CURLcode ftp_init(struct connectdata *conn)
*/
ftp->user = conn->user;
ftp->passwd = conn->passwd;
- if(TRUE == isBadFtpString(ftp->user))
+ if(isBadFtpString(ftp->user))
return CURLE_URL_MALFORMAT;
- if(TRUE == isBadFtpString(ftp->passwd))
+ if(isBadFtpString(ftp->passwd))
return CURLE_URL_MALFORMAT;
conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
@@ -3478,7 +3550,7 @@ CURLcode ftp_perform(struct connectdata *conn,
result = ftp_easy_statemach(conn);
*dophase_done = TRUE; /* with the easy interface we are done here */
}
- *connected = conn->bits.tcpconnect;
+ *connected = conn->bits.tcpconnect[FIRSTSOCKET];
if(*dophase_done)
DEBUGF(infof(conn->data, "DO phase is complete\n"));
@@ -3631,8 +3703,7 @@ static CURLcode wc_statemach(struct connectdata *conn)
strcat(tmp_path, finfo->filename);
/* switch default "state.pathbuffer" and tmp_path, good to see
ftp_parse_url_path function to understand this trick */
- if(conn->data->state.pathbuffer)
- free(conn->data->state.pathbuffer);
+ Curl_safefree(conn->data->state.pathbuffer);
conn->data->state.pathbuffer = tmp_path;
conn->data->state.path = tmp_path;
@@ -4099,12 +4170,13 @@ static CURLcode ftp_dophase_done(struct connectdata *conn,
/* called from multi.c while DOing */
static CURLcode ftp_doing(struct connectdata *conn,
- bool *dophase_done)
+ bool *dophase_done)
{
- CURLcode result;
- result = ftp_multi_statemach(conn, dophase_done);
+ CURLcode result = ftp_multi_statemach(conn, dophase_done);
- if(*dophase_done) {
+ if(result)
+ DEBUGF(infof(conn->data, "DO phase failed\n"));
+ else if(*dophase_done) {
result = ftp_dophase_done(conn, FALSE /* not connected */);
DEBUGF(infof(conn->data, "DO phase is complete\n"));