diff options
Diffstat (limited to 'Utilities/cmcurl/lib/ftp.c')
-rw-r--r-- | Utilities/cmcurl/lib/ftp.c | 250 |
1 files changed, 107 insertions, 143 deletions
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index d02ab99..874725b 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -43,11 +43,6 @@ #include <inet.h> #endif -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - #include <curl/curl.h> #include "urldata.h" #include "sendf.h" @@ -65,6 +60,7 @@ #include "strtoofft.h" #include "strcase.h" #include "vtls/vtls.h" +#include "cfilters.h" #include "connect.h" #include "strerror.h" #include "inet_ntop.h" @@ -74,7 +70,6 @@ #include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "multiif.h" #include "url.h" -#include "strcase.h" #include "speedcheck.h" #include "warnless.h" #include "http_proxy.h" @@ -219,14 +214,8 @@ const struct Curl_handler Curl_handler_ftps = { static void close_secondarysocket(struct Curl_easy *data, struct connectdata *conn) { - if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) { - Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]); - conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; - } - conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; -#ifndef CURL_DISABLE_PROXY - conn->bits.proxy_ssl_connected[SECONDARYSOCKET] = FALSE; -#endif + Curl_conn_close(data, SECONDARYSOCKET); + Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET); } /* @@ -278,13 +267,13 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data) struct sockaddr_in add; #endif curl_socklen_t size = (curl_socklen_t) sizeof(add); + CURLcode result; if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) { size = sizeof(add); s = accept(sock, (struct sockaddr *) &add, &size); } - Curl_closesocket(data, conn, sock); /* close the first socket */ if(CURL_SOCKET_BAD == s) { failf(data, "Error accept()ing server connect"); @@ -295,9 +284,11 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data) not needing DO_MORE anymore */ conn->bits.do_more = FALSE; - conn->sock[SECONDARYSOCKET] = s; (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ - conn->bits.sock_accepted = TRUE; + /* Replace any filter on SECONDARY with one listeing on this socket */ + result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET, &s); + if(result) + return result; if(data->set.fsockopt) { int error = 0; @@ -441,15 +432,12 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; + bool connected; - if(conn->bits.ftp_use_data_ssl) { - /* since we only have a plaintext TCP connection here, we must now - * do the TLS stuff */ - infof(data, "Doing the SSL/TLS handshake on the data stream"); - result = Curl_ssl_connect(data, conn, SECONDARYSOCKET); - if(result) - return result; - } + DEBUGF(infof(data, "ftp InitiateTransfer()")); + result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected); + if(result || !connected) + return result; if(conn->proto.ftpc.state_saved == FTP_STOR) { /* When we know we're uploading a specified file, we can get the file @@ -497,22 +485,23 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) if(timeout_ms < 0) { /* if a timeout was already reached, bail out */ failf(data, "Accept timeout occurred while waiting server connect"); - return CURLE_FTP_ACCEPT_TIMEOUT; + result = CURLE_FTP_ACCEPT_TIMEOUT; + goto out; } /* see if the connection request is already here */ result = ReceivedServerConnect(data, connected); if(result) - return result; + goto out; if(*connected) { result = AcceptServerConnect(data); if(result) - return result; + goto out; result = InitiateTransfer(data); if(result) - return result; + goto out; } else { /* Add timeout to multi handle and break out of the loop */ @@ -521,6 +510,8 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) EXPIRE_FTP_ACCEPT); } +out: + DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result)); return result; } @@ -672,7 +663,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, * wait for more data anyway. */ } - else if(!Curl_conn_data_pending(conn, FIRSTSOCKET)) { + else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) { switch(SOCKET_READABLE(sockfd, interval_ms)) { case -1: /* select() error, stop reading */ failf(data, "FTP response aborted due to select/poll error: %d", @@ -821,8 +812,10 @@ static int ftp_domore_getsock(struct Curl_easy *data, * handle ordinary commands. */ - if(SOCKS_STATE(conn->cnnct.state)) - return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET); + DEBUGF(infof(data, "ftp_domore_getsock()")); + if(conn->cfilter[SECONDARYSOCKET] + && !Curl_conn_is_connected(conn, SECONDARYSOCKET)) + return Curl_conn_get_select_socks(data, SECONDARYSOCKET, socks); if(FTP_STOP == ftpc->state) { int bits = GETSOCK_READSOCK(0); @@ -917,7 +910,7 @@ typedef enum { static CURLcode ftp_state_use_port(struct Curl_easy *data, ftpport fcmd) /* start with this */ { - CURLcode result = CURLE_OK; + CURLcode result = CURLE_FTP_PORT_FAILED; struct connectdata *conn = data->conn; struct ftp_conn *ftpc = &conn->proto.ftpc; curl_socket_t portsock = CURL_SOCKET_BAD; @@ -966,8 +959,10 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, char *port_sep = NULL; addr = calloc(addrlen + 1, 1); - if(!addr) - return CURLE_OUT_OF_MEMORY; + if(!addr) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } #ifdef ENABLE_IPV6 if(*string_ftpport == '[') { @@ -1027,7 +1022,6 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(port_min > port_max) port_min = port_max = 0; - if(*addr != '\0') { /* attempt to get the address of the given interface name */ switch(Curl_if2ip(conn->ip_addr->ai_family, @@ -1041,7 +1035,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, host = addr; break; case IF2IP_AF_NOT_SUPPORTED: - return CURLE_FTP_PORT_FAILED; + goto out; case IF2IP_FOUND: host = hbuf; /* use the hbuf for host name */ } @@ -1059,8 +1053,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { failf(data, "getsockname() failed: %s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - free(addr); - return CURLE_FTP_PORT_FAILED; + goto out; } switch(sa->sa_family) { #ifdef ENABLE_IPV6 @@ -1072,8 +1065,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); break; } - if(!r) - return CURLE_FTP_PORT_FAILED; + if(!r) { + goto out; + } host = hbuf; /* use this host name */ possibly_non_local = FALSE; /* we know it is local now */ } @@ -1093,11 +1087,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(!res) { failf(data, "failed to resolve the address provided to PORT: %s", host); - free(addr); - return CURLE_FTP_PORT_FAILED; + goto out; } - free(addr); host = NULL; /* step 2, create a socket for the requested address */ @@ -1105,8 +1097,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, portsock = CURL_SOCKET_BAD; error = 0; for(ai = res; ai; ai = ai->ai_next) { - result = Curl_socket(data, ai, NULL, &portsock); - if(result) { + if(Curl_socket(data, ai, NULL, &portsock)) { error = SOCKERRNO; continue; } @@ -1115,8 +1106,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(!ai) { failf(data, "socket failure: %s", Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_FTP_PORT_FAILED; + goto out; } + DEBUGF(infof(data, "ftp_state_use_port(), opened socket")); /* step 3, bind to a suitable local address */ @@ -1145,8 +1137,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { failf(data, "getsockname() failed: %s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; + goto out; } port = port_min; possibly_non_local = FALSE; /* don't try this again */ @@ -1155,8 +1146,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(error != EADDRINUSE && error != EACCES) { failf(data, "bind(port=%hu) failed: %s", port, Curl_strerror(error, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; + goto out; } } else @@ -1165,31 +1155,30 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, port++; } - /* maybe all ports were in use already*/ + /* maybe all ports were in use already */ if(port > port_max) { failf(data, "bind() failed, we ran out of ports"); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; + goto out; } /* get the name again after the bind() so that we can extract the port number it uses now */ sslen = sizeof(ss); - if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) { + if(getsockname(portsock, sa, &sslen)) { failf(data, "getsockname() failed: %s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; + goto out; } + DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port)); /* step 4, listen on the socket */ if(listen(portsock, 1)) { failf(data, "socket failure: %s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - Curl_closesocket(data, conn, portsock); - return CURLE_FTP_PORT_FAILED; + goto out; } + DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port)); /* step 5, send the proper FTP command */ @@ -1242,12 +1231,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(result) { failf(data, "Failure sending EPRT command: %s", curl_easy_strerror(result)); - Curl_closesocket(data, conn, portsock); - /* don't retry using PORT */ - ftpc->count1 = PORT; - /* bail out */ - state(data, FTP_STOP); - return result; + goto out; } break; } @@ -1273,10 +1257,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(result) { failf(data, "Failure sending PORT command: %s", curl_easy_strerror(result)); - Curl_closesocket(data, conn, portsock); - /* bail out */ - state(data, FTP_STOP); - return result; + goto out; } break; } @@ -1285,23 +1266,21 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* store which command was sent */ ftpc->count1 = fcmd; - close_secondarysocket(data, conn); - - /* we set the secondary socket variable to this for now, it is only so that - the cleanup function will close it in case we fail before the true - secondary stuff is made */ - conn->sock[SECONDARYSOCKET] = portsock; - - /* this tcpconnect assignment below is a hackish work-around to make the - multi interface with active FTP work - as it will not wait for a - (passive) connect in Curl_is_connected(). - - 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[SECONDARYSOCKET] = TRUE; - + /* Replace any filter on SECONDARY with one listeing on this socket */ + result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET, + &portsock); + if(result) + goto out; + portsock = CURL_SOCKET_BAD; /* now held in filter */ state(data, FTP_PORT); + +out: + if(result) { + state(data, FTP_STOP); + } + if(portsock != CURL_SOCKET_BAD) + Curl_closesocket(data, conn, portsock); + free(addr); return result; } @@ -1525,7 +1504,7 @@ static CURLcode ftp_state_type(struct Curl_easy *data) /* If we have selected NOBODY and HEADER, it means that we only want file information. Which in FTP can't be much more than the file size and date. */ - if(data->set.opt_no_body && ftpc->file && + if(data->req.no_body && ftpc->file && ftp_need_type(conn, data->state.prefer_ascii)) { /* The SIZE command is _not_ RFC 959 specified, and therefore many servers may not support it! It is however the only way we have to get a file's @@ -1804,6 +1783,8 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, infof(data, "Failed EPSV attempt. Disabling EPSV"); /* disable it for next transfer */ conn->bits.ftp_use_epsv = FALSE; + Curl_conn_close(data, SECONDARYSOCKET); + Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET); data->state.errorbuf = FALSE; /* allow error message to get rewritten */ result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV"); @@ -1951,7 +1932,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, */ const char * const host_name = conn->bits.socksproxy ? conn->socks_proxy.host.name : conn->http_proxy.host.name; - rc = Curl_resolv(data, host_name, (int)conn->port, FALSE, &addr); + rc = Curl_resolv(data, host_name, conn->port, FALSE, &addr); if(rc == CURLRESOLV_PENDING) /* BLOCKING, ignores the return code but 'addr' will be NULL in case of failure */ @@ -1993,8 +1974,9 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, } } - conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE; - result = Curl_connecthost(data, conn, addr); + result = Curl_conn_setup(data, conn, SECONDARYSOCKET, addr, + conn->bits.ftp_use_data_ssl? + CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); if(result) { Curl_resolv_unlock(data, addr); /* we're done using this address */ @@ -2092,9 +2074,9 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, #ifdef CURL_FTP_HTTPSTYLE_HEAD /* If we asked for a time of the file and we actually got one as well, - we "emulate" a HTTP-style header in our output. */ + we "emulate" an HTTP-style header in our output. */ - if(data->set.opt_no_body && + if(data->req.no_body && ftpc->file && data->set.get_filetime && (data->info.filetime >= 0) ) { @@ -2210,6 +2192,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data, struct connectdata *conn = data->conn; struct ftp_conn *ftpc = &conn->proto.ftpc; + DEBUGF(infof(data, "ftp_state_retr()")); if(data->set.max_filesize && (filesize > data->set.max_filesize)) { failf(data, "Maximum file size exceeded"); return CURLE_FILESIZE_EXCEEDED; @@ -2310,7 +2293,7 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data, else fdigit = start; /* ignores parsing errors, which will make the size remain unknown */ - (void)curlx_strtoofft(fdigit, NULL, 0, &filesize); + (void)curlx_strtoofft(fdigit, NULL, 10, &filesize); } else if(ftpcode == 550) { /* "No such file or directory" */ @@ -2465,6 +2448,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if((instate != FTP_LIST) && !data->state.prefer_ascii && + !data->set.ignorecl && (ftp->downloadsize < 1)) { /* * It seems directory listings either don't show the size or very @@ -2495,7 +2479,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, if(bytes) { ++bytes; /* get the number! */ - (void)curlx_strtoofft(bytes, NULL, 0, &size); + (void)curlx_strtoofft(bytes, NULL, 10, &size); } } } @@ -2756,8 +2740,16 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, */ if((ftpcode == 234) || (ftpcode == 334)) { - /* Curl_ssl_connect is BLOCKING */ - result = Curl_ssl_connect(data, conn, FIRSTSOCKET); + /* this was BLOCKING, keep it so for now */ + bool done; + if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + if(result) { + /* we failed and bail out */ + return CURLE_USE_SSL_FAILED; + } + } + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done); if(!result) { conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ @@ -2823,7 +2815,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, case FTP_CCC: if(ftpcode < 500) { /* First shut down the SSL layer (note: this call will block) */ - result = Curl_ssl_shutdown(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET); if(result) failf(data, "Failed to clear the command channel (CCC)"); @@ -3167,7 +3159,7 @@ static CURLcode ftp_connect(struct Curl_easy *data, if(conn->handler->flags & PROTOPT_SSL) { /* BLOCKING */ - result = Curl_ssl_connect(data, conn, FIRSTSOCKET); + result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); if(result) return result; conn->bits.ftp_use_control_ssl = TRUE; @@ -3310,14 +3302,6 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } } - if(conn->ssl[SECONDARYSOCKET].use) { - /* The secondary socket is using SSL so we must close down that part - first before we close the socket for real */ - Curl_ssl_close(data, conn, SECONDARYSOCKET); - - /* Note that we keep "use" set to TRUE since that (next) connection is - still requested to use SSL */ - } close_secondarysocket(data, conn); } @@ -3571,23 +3555,15 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) * complete */ struct FTP *ftp = NULL; - /* if the second connection isn't done yet, wait for it */ - if(!conn->bits.tcpconnect[SECONDARYSOCKET]) { - if(Curl_connect_ongoing(conn)) { - /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port - aren't used so we blank their arguments. */ - result = Curl_proxyCONNECT(data, SECONDARYSOCKET, NULL, 0); - - return result; - } - - result = Curl_is_connected(data, conn, SECONDARYSOCKET, &connected); - - /* Ready to do more? */ - if(connected) { - DEBUGF(infof(data, "DO-MORE connected phase starts")); - } - else { + /* if the second connection isn't done yet, wait for it to have + * connected to the remote host. When using proxy tunneling, this + * means the tunnel needs to have been establish. However, we + * can not expect the remote host to talk to us in any way yet. + * So, when using ftps: the SSL handshake will not start until we + * tell the remote server that we are there. */ + if(conn->cfilter[SECONDARYSOCKET]) { + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); + if(result || !Curl_conn_is_ip_connected(data, SECONDARYSOCKET)) { if(result && (ftpc->count1 == 0)) { *completep = -1; /* go back to DOING please */ /* this is a EPSV connect failing, try PASV instead */ @@ -3597,19 +3573,6 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) } } -#ifndef CURL_DISABLE_PROXY - result = Curl_proxy_connect(data, SECONDARYSOCKET); - if(result) - return result; - - if(CONNECT_SECONDARYSOCKET_PROXY_SSL()) - return result; - - if(conn->bits.tunnel_proxy && conn->bits.httpproxy && - Curl_connect_ongoing(conn)) - return result; -#endif - /* Curl_proxy_connect might have moved the protocol state */ ftp = data->req.p.ftp; @@ -3739,11 +3702,10 @@ CURLcode ftp_perform(struct Curl_easy *data, { /* this is FTP and no proxy */ CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; DEBUGF(infof(data, "DO phase starts")); - if(data->set.opt_no_body) { + if(data->req.no_body) { /* requested no body means no transfer... */ struct FTP *ftp = data->req.p.ftp; ftp->transfer = PPTRANSFER_INFO; @@ -3759,7 +3721,7 @@ CURLcode ftp_perform(struct Curl_easy *data, /* run the state-machine */ result = ftp_multi_statemach(data, dophase_done); - *connected = conn->bits.tcpconnect[SECONDARYSOCKET]; + *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET); infof(data, "ftp_perform ends with SECONDARY: %d", *connected); @@ -4169,7 +4131,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) /* get path before last slash, except for / */ size_t dirlen = slashPos - rawPath; if(dirlen == 0) - dirlen++; + dirlen = 1; ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); if(!ftpc->dirs) { @@ -4196,13 +4158,14 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) /* current position: begin of next path component */ const char *curPos = rawPath; - int dirAlloc = 0; /* number of entries allocated for the 'dirs' array */ + /* number of entries allocated for the 'dirs' array */ + size_t dirAlloc = 0; const char *str = rawPath; for(; *str != 0; ++str) if (*str == '/') ++dirAlloc; - if(dirAlloc > 0) { + if(dirAlloc) { ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0])); if(!ftpc->dirs) { free(rawPath); @@ -4232,7 +4195,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data) curPos = slashPos + 1; } } - DEBUGASSERT(ftpc->dirdepth <= dirAlloc); + DEBUGASSERT((size_t)ftpc->dirdepth <= dirAlloc); fileName = curPos; /* the rest is the file name (or empty) */ } break; @@ -4375,6 +4338,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, { char *type; struct FTP *ftp; + CURLcode result = CURLE_OK; data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1); if(!ftp) @@ -4416,7 +4380,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, ftp->downloadsize = 0; conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ - return CURLE_OK; + return result; } #endif /* CURL_DISABLE_FTP */ |