* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
#include "warnless.h"
#include "http_proxy.h"
#include "non-ascii.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
+#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
bool connected);
/* easy-to-use macro: */
-#define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z))) \
- return result
+#define PPSENDF(x,y,z) result = Curl_pp_sendf(x,y,z); \
+ if(result) \
+ return result
/*
#endif
#endif
+static void close_secondarysocket(struct connectdata *conn)
+{
+ if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
+ Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
+ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+ }
+ conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
+ conn->tunnel_state[SECONDARYSOCKET] = TUNNEL_INIT;
+}
/*
* NOTE: back in the old days, we added code in the FTP code that made NOBODY
int i;
if(ftpc->dirs) {
for(i=0; i < ftpc->dirdepth; i++) {
- if(ftpc->dirs[i]) {
- free(ftpc->dirs[i]);
- ftpc->dirs[i]=NULL;
- }
+ free(ftpc->dirs[i]);
+ ftpc->dirs[i]=NULL;
}
free(ftpc->dirs);
ftpc->dirs = NULL;
ftpc->dirdepth = 0;
}
- if(ftpc->file) {
- free(ftpc->file);
- ftpc->file = NULL;
- }
+ Curl_safefree(ftpc->file);
+
+ /* no longer of any use */
+ Curl_safefree(ftpc->newhost);
}
/* Returns non-zero if the given string contains CR (\r) or LF (\n),
return CURLE_FTP_PORT_FAILED;
}
infof(data, "Connection accepted from server\n");
+ /* when this happens within the DO state it is important that we mark us as
+ not needing DO_MORE anymore */
+ conn->bits.do_more = FALSE;
conn->sock[SECONDARYSOCKET] = s;
(void)curlx_nonblock(s, TRUE); /* enable non-blocking */
CURLSOCKTYPE_ACCEPT);
if(error) {
- Curl_closesocket(conn, s); /* close the socket and bail out */
- conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+ close_secondarysocket(conn);
return CURLE_ABORTED_BY_CALLBACK;
}
}
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
- Curl_safefree(addr);
+ free(addr);
return CURLE_FTP_PORT_FAILED;
}
switch(sa->sa_family) {
if(res == NULL) {
failf(data, "failed to resolve the address provided to PORT: %s", host);
- Curl_safefree(addr);
+ free(addr);
return CURLE_FTP_PORT_FAILED;
}
- Curl_safefree(addr);
+ free(addr);
host = NULL;
/* step 2, create a socket for the requested address */
/* store which command was sent */
ftpc->count1 = fcmd;
+ close_secondarysocket(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 */
- if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
- Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = portsock;
/* this tcpconnect assignment below is a hackish work-around to make the
The other ftp_filemethods will CWD into dir/dir/ first and
then just do LIST (in that case: nothing to do here)
*/
- char *cmd,*lstArg,*slashPos;
+ char *cmd, *lstArg, *slashPos;
lstArg = NULL;
if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
data->state.path &&
data->state.path[0] &&
- strchr(data->state.path,'/')) {
+ strchr(data->state.path, '/')) {
lstArg = strdup(data->state.path);
if(!lstArg)
if(lstArg[strlen(lstArg) - 1] != '/') {
/* chop off the file part if format is dir/dir/file */
- slashPos = strrchr(lstArg,'/');
+ slashPos = strrchr(lstArg, '/');
if(slashPos)
*(slashPos+1) = '\0';
}
lstArg? lstArg: "" );
if(!cmd) {
- if(lstArg)
- free(lstArg);
+ free(lstArg);
return CURLE_OUT_OF_MEMORY;
}
result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
- if(lstArg)
- free(lstArg);
-
+ free(lstArg);
free(cmd);
if(result)
BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread =
- conn->fread_func(data->state.buffer, 1, readthisamountnow,
- conn->fread_in);
+ data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+ data->state.in);
passed += actuallyread;
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
result = ftp_state_retr(conn, ftpc->known_filesize);
}
else {
- PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
- state(conn, FTP_RETR_SIZE);
+ if(data->set.ignorecl) {
+ /* This code is to support download of growing files. It prevents
+ the state machine from requesting the file size from the
+ server. With an unknown file size the download continues until
+ the server terminates it, otherwise the client stops if the
+ received byte count exceeds the reported file size. Set option
+ CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this behavior.*/
+ PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
+ state(conn, FTP_RETR);
+ }
+ else {
+ PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
+ state(conn, FTP_RETR_SIZE);
+ }
}
}
break;
static CURLcode ftp_epsv_disable(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
+
+ if(conn->bits.ipv6) {
+ /* We can't disable EPSV when doing IPv6, so this is instead a fail */
+ failf(conn->data, "Failed EPSV attempt, exiting\n");
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+
infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n");
/* disable it for next transfer */
conn->bits.ftp_use_epsv = FALSE;
memset(&http_proxy, 0, sizeof(http_proxy));
data->req.protop = &http_proxy;
- result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
+ result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport, TRUE);
data->req.protop = ftp_save;
return result;
}
+static char *control_address(struct connectdata *conn)
+{
+ /* Returns the control connection IP address.
+ If a proxy tunnel is used, returns the original host name instead, because
+ the effective control connection address is the proxy address,
+ not the ftp host. */
+ if(conn->bits.tunnel_proxy ||
+ conn->proxytype == CURLPROXY_SOCKS5 ||
+ conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
+ conn->proxytype == CURLPROXY_SOCKS4 ||
+ conn->proxytype == CURLPROXY_SOCKS4A)
+ return conn->host.name;
+
+ return conn->ip_addr_str;
+}
+
static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
int ftpcode)
{
unsigned short connectport; /* the local port connect() should use! */
char *str=&data->state.buffer[4]; /* start on the first letter */
+ /* if we come here again, make sure the former name is cleared */
+ Curl_safefree(ftpc->newhost);
+
if((ftpc->count1 == 0) &&
(ftpcode == 229)) {
/* positive EPSV response */
unsigned int num;
char separator[4];
ptr++;
- if(5 == sscanf(ptr, "%c%c%c%u%c",
- &separator[0],
- &separator[1],
- &separator[2],
- &num,
- &separator[3])) {
+ if(5 == sscanf(ptr, "%c%c%c%u%c",
+ &separator[0],
+ &separator[1],
+ &separator[2],
+ &num,
+ &separator[3])) {
const char sep1 = separator[0];
int i;
}
if(ptr) {
ftpc->newport = (unsigned short)(num & 0xffff);
-
- if(conn->bits.tunnel_proxy ||
- conn->proxytype == CURLPROXY_SOCKS5 ||
- conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
- conn->proxytype == CURLPROXY_SOCKS4 ||
- conn->proxytype == CURLPROXY_SOCKS4A)
- /* proxy tunnel -> use other host info because ip_addr_str is the
- proxy address not the ftp host */
- snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s",
- conn->host.name);
- else
- /* use the same IP we are already connected to */
- snprintf(ftpc->newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
+ ftpc->newhost = strdup(control_address(conn));
+ if(!ftpc->newhost)
+ return CURLE_OUT_OF_MEMORY;
}
}
else
*/
while(*str) {
if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
- &ip[0], &ip[1], &ip[2], &ip[3],
- &port[0], &port[1]))
+ &ip[0], &ip[1], &ip[2], &ip[3],
+ &port[0], &port[1]))
break;
str++;
}
/* we got OK from server */
if(data->set.ftp_skip_ip) {
- /* told to ignore the remotely given IP but instead use the one we used
+ /* told to ignore the remotely given IP but instead use the host we used
for the control connection */
- infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
+ infof(data, "Skip %d.%d.%d.%d for data connection, re-use %s instead\n",
ip[0], ip[1], ip[2], ip[3],
- conn->ip_addr_str);
- if(conn->bits.tunnel_proxy ||
- conn->proxytype == CURLPROXY_SOCKS5 ||
- conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
- conn->proxytype == CURLPROXY_SOCKS4 ||
- conn->proxytype == CURLPROXY_SOCKS4A)
- /* proxy tunnel -> use other host info because ip_addr_str is the
- proxy address not the ftp host */
- snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s", conn->host.name);
- else
- snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s",
- conn->ip_addr_str);
+ conn->host.name);
+ ftpc->newhost = strdup(control_address(conn));
}
else
- snprintf(ftpc->newhost, sizeof(ftpc->newhost),
- "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+ ftpc->newhost = aprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+
+ if(!ftpc->newhost)
+ return CURLE_OUT_OF_MEMORY;
+
ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
}
else if(ftpc->count1 == 0) {
conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
result = Curl_connecthost(conn, addr);
- Curl_resolv_unlock(data, addr); /* we're done using this address */
-
if(result) {
+ Curl_resolv_unlock(data, addr); /* we're done using this address */
if(ftpc->count1 == 0 && ftpcode == 229)
return ftp_epsv_disable(conn);
if(data->set.verbose)
/* this just dumps information about this second connection */
- ftp_pasv_verbose(conn, conn->ip_addr, ftpc->newhost, connectport);
+ ftp_pasv_verbose(conn, addr->addr, ftpc->newhost, connectport);
+ Curl_resolv_unlock(data, addr); /* we're done using this address */
conn->bits.do_more = TRUE;
state(conn, FTP_STOP); /* this phase is completed */
ftpport fcmd = (ftpport)ftpc->count1;
CURLcode result = CURLE_OK;
- if(ftpcode != 200) {
+ /* The FTP spec tells a positive response should have code 200.
+ Be more permissive here to tolerate deviant servers. */
+ if(ftpcode / 100 != 2) {
/* the command failed */
if(EPRT == fcmd) {
}
/* now store a copy of the directory we are in */
- if(ftpc->prevpath)
- free(ftpc->prevpath);
+ free(ftpc->prevpath);
if(data->set.wildcardmatch) {
if(data->set.chunk_end && ftpc->file) {
/* shut down the socket to inform the server we're done */
#ifdef _WIN32_WCE
- shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
+ shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */
#endif
if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
/* Note that we keep "use" set to TRUE since that (next) connection is
still requested to use SSL */
}
- if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
- Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
- conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
- conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
- }
+ close_secondarysocket(conn);
}
if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
if(conn->tunnel_state[SECONDARYSOCKET] == TUNNEL_CONNECT) {
/* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
aren't used so we blank their arguments. TODO: make this nicer */
- result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0);
+ result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0, FALSE);
return result;
}
return result;
result = ftp_multi_statemach(conn, &complete);
- *completep = (int)complete;
+ if(ftpc->wait_data_conn)
+ /* if we reach the end of the FTP state machine here, *complete will be
+ TRUE but so is ftpc->wait_data_conn, which says we need to wait for
+ the data connection and therefore we're not actually complete */
+ *completep = 0;
+ else
+ *completep = (int)complete;
}
else {
/* download */
struct ftp_wc_tmpdata *tmp = ptr;
if(tmp)
Curl_ftp_parselist_data_free(&tmp->parser);
- Curl_safefree(tmp);
+ free(tmp);
}
static CURLcode init_wc_data(struct connectdata *conn)
ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
if(!ftp_tmp->parser) {
Curl_safefree(wildcard->pattern);
- Curl_safefree(ftp_tmp);
+ free(ftp_tmp);
return CURLE_OUT_OF_MEMORY;
}
result = Curl_convert_to_network(conn->data, s, write_len);
/* Curl_convert_to_network calls failf if unsuccessful */
if(result)
- return(result);
+ return result;
for(;;) {
#ifdef HAVE_GSSAPI
}
freedirs(ftpc);
- if(ftpc->prevpath) {
- free(ftpc->prevpath);
- ftpc->prevpath = NULL;
- }
- if(ftpc->server_os) {
- free(ftpc->server_os);
- ftpc->server_os = NULL;
- }
+ free(ftpc->prevpath);
+ ftpc->prevpath = NULL;
+ free(ftpc->server_os);
+ ftpc->server_os = NULL;
Curl_pp_disconnect(pp);
the first condition in the if() right here, is there just in case
someone decides to set path to NULL one day
*/
- if(data->state.path &&
- data->state.path[0] &&
- (data->state.path[strlen(data->state.path) - 1] != '/') )
- filename = data->state.path; /* this is a full file path */
- /*
+ if(path_to_use[0] &&
+ (path_to_use[strlen(path_to_use) - 1] != '/') )
+ filename = path_to_use; /* this is a full file path */
+ /*
+ else {
ftpc->file is not used anywhere other than for operations on a file.
In other words, never for directory operations.
So we can safely leave filename as NULL here and use it as a
argument in dir/file decisions.
- */
+ }
+ */
break;
case FTPFILE_SINGLECWD:
CURLcode result = ftp_do_more(conn, &completed);
if(result) {
- if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
- /* close the second socket if it was created already */
- Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
- conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
- }
+ close_secondarysocket(conn);
return result;
}
}