diff options
Diffstat (limited to 'lib/ftp.c')
-rw-r--r-- | lib/ftp.c | 160 |
1 files changed, 116 insertions, 44 deletions
@@ -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")); |