aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGokhan Sengun <gokhansengun@gmail.com>2011-12-19 14:35:20 +0100
committerDaniel Stenberg <daniel@haxx.se>2011-12-20 20:30:02 +0100
commitc834213ad52c52431e9ca597862dc81839cabe84 (patch)
tree678dfd82d3129b8aef8a0222defc55244003203b
parent5527417afae062d7a2b12ee80730a0ab22b86eab (diff)
FTP: perform active connections non-blocking
1- Two new error codes are introduced. CURLE_FTP_ACCEPT_FAILED to be set whenever ACCEPTing fails because of FTP server connected. CURLE_FTP_ACCEPT_TIMEOUT to be set whenever ACCEPTing timeouts. Neither of these errors are considered fatal and control connection remains OK because it could just be a firewall blocking server to connect to the client. 2- One new setopt option was introduced. CURLOPT_ACCEPTTIMEOUT_MS It sets the maximum amount of time FTP client is going to wait for a server to connect. Internal default accept timeout is 60 seconds.
-rw-r--r--docs/libcurl/symbols-in-versions3
-rw-r--r--include/curl/curl.h12
-rw-r--r--lib/connect.c28
-rw-r--r--lib/connect.h5
-rw-r--r--lib/ftp.c364
-rw-r--r--lib/ftp.h3
-rw-r--r--lib/multi.c7
-rw-r--r--lib/progress.c4
-rw-r--r--lib/progress.h1
-rw-r--r--lib/strerror.c8
-rw-r--r--lib/url.c9
-rw-r--r--lib/urldata.h4
-rw-r--r--tests/data/DISABLED4
-rw-r--r--tests/data/test12069
-rw-r--r--tests/data/test12079
-rw-r--r--tests/data/test12089
-rw-r--r--tests/data/test5912
-rw-r--r--tests/data/test5922
-rw-r--r--tests/data/test5932
-rw-r--r--tests/libtest/lib591.c3
20 files changed, 361 insertions, 127 deletions
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index ec902fd4e..73d50a2b2 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -45,6 +45,8 @@ CURLE_COULDNT_RESOLVE_PROXY 7.1
CURLE_FAILED_INIT 7.1
CURLE_FILESIZE_EXCEEDED 7.10.8
CURLE_FILE_COULDNT_READ_FILE 7.1
+CURLE_FTP_ACCEPT_FAILED 7.24.0
+CURLE_FTP_ACCEPT_TIMEOUT 7.24.0
CURLE_FTP_ACCESS_DENIED 7.1
CURLE_FTP_BAD_DOWNLOAD_RESUME 7.1 7.1
CURLE_FTP_BAD_FILE_LIST 7.21.0
@@ -286,6 +288,7 @@ CURLOPTTYPE_FUNCTIONPOINT 7.1
CURLOPTTYPE_LONG 7.1
CURLOPTTYPE_OBJECTPOINT 7.1
CURLOPTTYPE_OFF_T 7.11.0
+CURLOPT_ACCEPTTIMEOUT_MS 7.24.0
CURLOPT_ACCEPT_ENCODING 7.21.6
CURLOPT_ADDRESS_SCOPE 7.19.0
CURLOPT_APPEND 7.17.0
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 1e908a3ec..f53d6d446 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -411,9 +411,12 @@ typedef enum {
CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server
due to lack of access - when login fails
this is not returned. */
- CURLE_OBSOLETE10, /* 10 - NOT USED */
+ CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for
+ 7.15.4, reused in Dec 2011 for 7.24.0]*/
CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */
- CURLE_OBSOLETE12, /* 12 - NOT USED */
+ CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server
+ [was obsoleted in August 2007 for 7.17.0,
+ reused in Dec 2011 for 7.24.0]*/
CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */
CURLE_FTP_WEIRD_227_FORMAT, /* 14 */
CURLE_FTP_CANT_GET_HOST, /* 15 */
@@ -511,7 +514,6 @@ typedef enum {
CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */
CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */
CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */
-
CURL_LAST /* never use! */
} CURLcode;
@@ -1489,6 +1491,10 @@ typedef enum {
/* Set the name servers to use for DNS resolution */
CINIT(DNS_SERVERS, OBJECTPOINT, 211),
+ /* Time-out accept operations (currently for FTP only) after this amount
+ of miliseconds. */
+ CINIT(ACCEPTTIMEOUT_MS, LONG, 212),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
diff --git a/lib/connect.c b/lib/connect.c
index bcd538406..cc835808b 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -99,6 +99,34 @@ singleipconnect(struct connectdata *conn,
bool *connected);
/*
+ * Curl_timeleft_accept() returns the amount of milliseconds left allowed for
+ * waiting server to connect. If the value is negative, the timeout time has
+ * already elapsed.
+ *
+ * The start time is stored in progress.t_acceptdata - as set with
+ * Curl_pgrsTime(..., TIMER_STARTACCEPT);
+ *
+ */
+long Curl_timeleft_accept(struct SessionHandle *data)
+{
+ long timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
+ struct timeval now;
+
+ if(data->set.accepttimeout > 0)
+ timeout_ms = data->set.accepttimeout;
+
+ now = Curl_tvnow();
+
+ /* subtract elapsed time */
+ timeout_ms -= Curl_tvdiff(now, data->progress.t_acceptdata);
+ if(!timeout_ms)
+ /* avoid returning 0 as that means no timeout! */
+ return -1;
+
+ return timeout_ms;
+}
+
+/*
* Curl_timeleft() returns the amount of milliseconds left allowed for the
* transfer/connection. If the value is negative, the timeout time has already
* elapsed.
diff --git a/lib/connect.h b/lib/connect.h
index f84361f2e..4e905bdf5 100644
--- a/lib/connect.h
+++ b/lib/connect.h
@@ -43,7 +43,12 @@ long Curl_timeleft(struct SessionHandle *data,
struct timeval *nowp,
bool duringconnect);
+/* function that returns how much time there's left to wait for incoming
+ server connect */
+long Curl_timeleft_accept(struct SessionHandle *data);
+
#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
+#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
/*
* Used to extract socket and connectdata struct for the most recent
diff --git a/lib/ftp.c b/lib/ftp.c
index b64ef6265..3271907ec 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -108,6 +108,8 @@
#endif
/* Local API functions */
+static void state(struct connectdata *conn,
+ ftpstate newstate);
static CURLcode ftp_sendquote(struct connectdata *conn,
struct curl_slist *quote);
static CURLcode ftp_quit(struct connectdata *conn);
@@ -150,6 +152,11 @@ static void wc_data_dtor(void *ptr);
static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
curl_off_t filesize);
+static CURLcode ftp_readresp(curl_socket_t sockfd,
+ struct pingpong *pp,
+ int *ftpcode,
+ size_t *size);
+
/* easy-to-use macro: */
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
return result
@@ -310,19 +317,16 @@ static bool isBadFtpString(const char *string)
/***********************************************************************
*
- * AllowServerConnect()
+ * AcceptServerConnect()
*
- * When we've issue the PORT command, we have told the server to connect
- * to us. This function will sit and wait here until the server has
- * connected.
+ * After connection request is received from the server this function is
+ * called to accept the connection and close the listening socket
*
*/
-static CURLcode AllowServerConnect(struct connectdata *conn)
+static CURLcode AcceptServerConnect(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
curl_socket_t sock = conn->sock[SECONDARYSOCKET];
- long timeout_ms;
- long interval_ms;
curl_socket_t s = CURL_SOCKET_BAD;
#ifdef ENABLE_IPV6
struct Curl_sockaddr_storage add;
@@ -331,48 +335,220 @@ static CURLcode AllowServerConnect(struct connectdata *conn)
#endif
curl_socklen_t size = (curl_socklen_t) sizeof(add);
- for(;;) {
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
+ size = sizeof(add);
+
+ s=accept(sock, (struct sockaddr *) &add, &size);
+ }
+ Curl_closesocket(conn, sock); /* close the first socket */
+
+ if(CURL_SOCKET_BAD == s) {
+ failf(data, "Error accept()ing server connect");
+ return CURLE_FTP_PORT_FAILED;
+ }
+ infof(data, "Connection accepted from server\n");
+
+ conn->sock[SECONDARYSOCKET] = s;
+ curlx_nonblock(s, TRUE); /* enable non-blocking */
+ conn->sock_accepted[SECONDARYSOCKET] = TRUE;
+ return CURLE_OK;
+
+}
+
+/***********************************************************************
+ *
+ * ReceivedServerConnect()
+ *
+ * After allowing server to connect to us from data port, this function
+ * checks both data connection for connection establishment and ctrl
+ * connection for a negative response regarding a failure in connecting
+ *
+ */
+static CURLcode ReceivedServerConnect(struct connectdata* conn, bool* received)
+{
+ struct SessionHandle *data = conn->data;
+ curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
+ curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+ int result;
+ long timeout_ms;
+ ssize_t nread;
+ int ftpcode;
+
+ *received = FALSE;
+
+ timeout_ms = Curl_timeleft_accept(data);
+ infof(data, "Checking for server connect\n");
+ 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;
+ }
+
+ /* First check whether there is a cached response from server */
+ if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
+ /* Data connection could not be established, let's return */
+ infof(data, "There is negative response in cache while serv connect");
+ Curl_GetFTPResponse(&nread, conn, &ftpcode);
+ return CURLE_FTP_ACCEPT_FAILED;
+ }
+
+ result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
+
+ /* see if the connection request is already here */
+ switch (result) {
+ case -1: /* error */
+ /* let's die here */
+ failf(data, "Error while waiting for server connect");
+ return CURLE_FTP_ACCEPT_FAILED;
+ case 0: /* Server connect is not received yet */
+ break; /* loop */
+ default:
+
+ if(result & CURL_CSELECT_IN2) {
+ infof(data, "Ready to accept data connection from server\n");
+ *received = TRUE;
+ }
+ else if(result & CURL_CSELECT_IN) {
+ infof(data, "Ctrl conn has data while waiting for data conn\n");
+ Curl_GetFTPResponse(&nread, conn, &ftpcode);
+
+ if(ftpcode/100 > 3)
+ return CURLE_FTP_ACCEPT_FAILED;
+
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+
+ break;
+ } /* switch() */
+
+ return CURLE_OK;
+}
+
+
+/***********************************************************************
+ *
+ * InitiateTransfer()
+ *
+ * After connection from server is accepted this function is called to
+ * setup transfer parameters and initiate the data transfer.
+ *
+ */
+static CURLcode InitiateTransfer(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+ struct FTP *ftp = data->state.proto.ftp;
+ CURLcode result = CURLE_OK;
+
+ if(conn->ssl[SECONDARYSOCKET].use) {
+ /* 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\n");
+ result = Curl_ssl_connect(conn, SECONDARYSOCKET);
+ if(result)
+ return result;
+ }
+
+ if(conn->proto.ftpc.state_saved == FTP_STOR) {
+ *(ftp->bytecountp)=0;
+
+ /* When we know we're uploading a specified file, we can get the file
+ size prior to the actual upload. */
+
+ Curl_pgrsSetUploadSize(data, data->set.infilesize);
+
+ /* set the SO_SNDBUF for the secondary socket for those who need it */
+ Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
+
+ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
+ SECONDARYSOCKET, ftp->bytecountp);
+ }
+ else {
+ /* FTP download: */
+ Curl_setup_transfer(conn, SECONDARYSOCKET,
+ conn->proto.ftpc.retr_size_saved, FALSE,
+ ftp->bytecountp, -1, NULL); /* no upload here */
+ }
+
+ conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
+ state(conn, FTP_STOP);
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * AllowServerConnect()
+ *
+ * When we've issue the PORT command, we have told the server to connect
+ * to us. This function
+ * - will sit and wait here until the server has connected for easy interface
+ * - will check whether data connection is established if so it is accepted
+ * for multi interface
+ *
+ */
+static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
+{
+ struct SessionHandle *data = conn->data;
+ long timeout_ms;
+ long interval_ms;
+ CURLcode ret = CURLE_OK;
+
+ *connected = FALSE;
+ infof(data, "Preparing for accepting server on data port\n");
+
+ /* Save the time we start accepting server connect */
+ Curl_pgrsTime(data, TIMER_STARTACCEPT);
+ for(;;) {
+ timeout_ms = Curl_timeleft_accept(data);
if(timeout_ms < 0) {
/* if a timeout was already reached, bail out */
- failf(data, "Timeout while waiting for server connect");
- return CURLE_OPERATION_TIMEDOUT;
+ failf(data, "Accept timeout occurred while waiting server connect");
+ return CURLE_FTP_ACCEPT_TIMEOUT;
}
- interval_ms = 1000; /* use 1 second timeout intervals */
- if(timeout_ms < interval_ms)
- interval_ms = timeout_ms;
+ /* see if the connection request is already here */
+ ret = ReceivedServerConnect(conn, connected);
+ if(ret)
+ return ret;
- switch (Curl_socket_ready(sock, CURL_SOCKET_BAD, interval_ms)) {
- case -1: /* error */
- /* let's die here */
- failf(data, "Error while waiting for server connect");
- return CURLE_FTP_PORT_FAILED;
- case 0: /* timeout */
- break; /* loop */
- default:
- /* we have received data here */
- if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
- size = sizeof(add);
+ if(*connected) {
+ ret = AcceptServerConnect(conn);
+ if(ret)
+ return ret;
- s=accept(sock, (struct sockaddr *) &add, &size);
- }
- Curl_closesocket(conn, sock); /* close the first socket */
+ ret = InitiateTransfer(conn);
+ if(ret)
+ return ret;
- if(CURL_SOCKET_BAD == s) {
- failf(data, "Error accept()ing server connect");
- return CURLE_FTP_PORT_FAILED;
+ break; /* connection is accepted, break the loop */
+ }
+ else {
+ if(data->state.used_interface == Curl_if_easy) {
+ interval_ms = 1000;
+ if(timeout_ms < interval_ms)
+ interval_ms = timeout_ms;
+
+ /* sleep for 1 second and then continue */
+ Curl_socket_ready(CURL_SOCKET_BAD, CURL_SOCKET_BAD, interval_ms);
}
- infof(data, "Connection accepted from server\n");
+ else {
+ /* Add timeout to multi handle and break out of the loop */
+ if(ret == CURLE_OK && *connected == FALSE) {
+ if(data->set.accepttimeout > 0)
+ Curl_expire(data, data->set.accepttimeout);
+ else
+ Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
+ }
- conn->sock[SECONDARYSOCKET] = s;
- curlx_nonblock(s, TRUE); /* enable non-blocking */
- conn->sock_accepted[SECONDARYSOCKET] = TRUE;
- return CURLE_OK;
- } /* switch() */
+ break; /* connection was not accepted immediately */
+ }
+ }
}
- /* never reaches this point */
+
+ return ret;
}
/* macro to check for a three-digit ftp status code at the start of the
@@ -668,6 +844,10 @@ static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
}
socks[0] = conn->sock[SECONDARYSOCKET];
+ if(conn->bits.wait_data_conn) {
+ socks[1] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0) | GETSOCK_READSOCK(1);
+ }
return GETSOCK_READSOCK(0);
}
@@ -2153,11 +2333,10 @@ static CURLcode ftp_state_rest_resp(struct connectdata *conn,
}
static CURLcode ftp_state_stor_resp(struct connectdata *conn,
- int ftpcode)
+ int ftpcode, ftpstate instate)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
- struct FTP *ftp = data->state.proto.ftp;
if(ftpcode>=400) {
failf(data, "Failed FTP upload: %0d", ftpcode);
@@ -2165,41 +2344,26 @@ static CURLcode ftp_state_stor_resp(struct connectdata *conn,
return CURLE_UPLOAD_FAILED;
}
+ conn->proto.ftpc.state_saved = instate;
+
+ /* PORT means we are now awaiting the server to connect to us. */
if(data->set.ftp_use_port) {
- /* BLOCKING */
- /* PORT means we are now awaiting the server to connect to us. */
- result = AllowServerConnect(conn);
- if(result)
- return result;
- }
+ bool connected;
- if(conn->ssl[SECONDARYSOCKET].use) {
- /* 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\n");
- /* BLOCKING */
- result = Curl_ssl_connect(conn, SECONDARYSOCKET);
+ result = AllowServerConnect(conn, &connected);
if(result)
return result;
- }
-
- *(ftp->bytecountp)=0;
-
- /* When we know we're uploading a specified file, we can get the file
- size prior to the actual upload. */
- Curl_pgrsSetUploadSize(data, data->set.infilesize);
-
- /* set the SO_SNDBUF for the secondary socket for those who need it */
- Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
-
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
- SECONDARYSOCKET, ftp->bytecountp);
- state(conn, FTP_STOP);
-
- conn->proto.ftpc.pp.pending_resp = TRUE; /* expect a server response */
+ if(!connected) {
+ infof(data, "Data conn was not available immediately\n");
+ state(conn, FTP_STOP);
+ conn->bits.wait_data_conn = TRUE;
+ }
- return result;
+ return CURLE_OK;
+ }
+ else
+ return InitiateTransfer(conn);
}
/* for LIST and RETR responses */
@@ -2280,22 +2444,6 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn,
else if(ftp->downloadsize > -1)
size = ftp->downloadsize;
- if(data->set.ftp_use_port) {
- /* BLOCKING */
- result = AllowServerConnect(conn);
- if(result)
- return result;
- }
-
- if(conn->ssl[SECONDARYSOCKET].use) {
- /* 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\n");
- result = Curl_ssl_connect(conn, SECONDARYSOCKET);
- if(result)
- return result;
- }
-
if(size > data->req.maxdownload && data->req.maxdownload > 0)
size = data->req.size = data->req.maxdownload;
else if((instate != FTP_LIST) && (data->set.prefer_ascii))
@@ -2307,11 +2455,24 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn,
infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
/* FTP download: */
- Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
- ftp->bytecountp, -1, NULL); /* no upload here */
+ conn->proto.ftpc.state_saved = instate;
+ conn->proto.ftpc.retr_size_saved = size;
+
+ if(data->set.ftp_use_port) {
+ bool connected;
+
+ result = AllowServerConnect(conn, &connected);
+ if(result)
+ return result;
- conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
- state(conn, FTP_STOP);
+ if(!connected) {
+ infof(data, "Data conn was not available immediately\n");
+ state(conn, FTP_STOP);
+ conn->bits.wait_data_conn = TRUE;
+ }
+ }
+ else
+ return InitiateTransfer(conn);
}
else {
if((instate == FTP_LIST) && (ftpcode == 450)) {
@@ -2459,7 +2620,6 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
if(pp->sendleft)
return Curl_pp_flushsend(pp);
- /* we read a piece of response */
result = ftp_readresp(sock, pp, &ftpcode, &nread);
if(result)
return result;
@@ -2865,7 +3025,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
break;
case FTP_STOR:
- result = ftp_state_stor_resp(conn, ftpcode);
+ result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
break;
case FTP_QUIT:
@@ -3081,6 +3241,8 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
case CURLE_BAD_DOWNLOAD_RESUME:
case CURLE_FTP_WEIRD_PASV_REPLY:
case CURLE_FTP_PORT_FAILED:
+ case CURLE_FTP_ACCEPT_FAILED:
+ case CURLE_FTP_ACCEPT_TIMEOUT:
case CURLE_FTP_COULDNT_SET_TYPE:
case CURLE_FTP_COULDNT_RETR_FILE:
case CURLE_UPLOAD_FAILED:
@@ -3474,7 +3636,24 @@ static CURLcode ftp_nextconnect(struct connectdata *conn)
/* a transfer is about to take place, or if not a file name was given
so we'll do a SIZE on it later and then we need the right TYPE first */
- if(data->set.upload) {
+ if(conn->bits.wait_data_conn == TRUE) {
+ bool serv_conned;
+
+ result = ReceivedServerConnect(conn, &serv_conned);
+ if(result)
+ return result; /* Failed to accept data connection */
+
+ if(serv_conned) {
+ /* It looks data connection is established */
+ result = AcceptServerConnect(conn);
+ conn->bits.wait_data_conn = FALSE;
+ if(result == CURLE_OK)
+ result = InitiateTransfer(conn);
+ }
+
+ return result;
+ }
+ else if(data->set.upload) {
result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
if(result)
return result;
@@ -3513,7 +3692,6 @@ static CURLcode ftp_nextconnect(struct connectdata *conn)
too! */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
- /* end of transfer */
DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
return result;
diff --git a/lib/ftp.h b/lib/ftp.h
index b5b3adadf..4c1296fef 100644
--- a/lib/ftp.h
+++ b/lib/ftp.h
@@ -146,6 +146,9 @@ struct ftp_conn {
int count2; /* general purpose counter for the state machine */
int count3; /* general purpose counter for the state machine */
ftpstate state; /* always use ftp.c:state() to change state! */
+ ftpstate state_saved; /* transfer type saved to be reloaded after
+ data connection is established */
+ curl_off_t retr_size_saved; /* Size of retrieved file saved */
char * server_os; /* The target server operating system. */
curl_off_t known_filesize; /* file size is different from -1, if wildcard
LIST parsing was done and wc_statemach set
diff --git a/lib/multi.c b/lib/multi.c
index e5073432e..e408ab184 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -1388,6 +1388,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
break;
case CURLM_STATE_DO_DONE:
+
+ if(easy->easy_conn->bits.wait_data_conn == TRUE) {
+ multistate(easy, CURLM_STATE_DO_MORE);
+ result = CURLM_OK;
+ break;
+ }
+
/* Move ourselves from the send to recv pipeline */
moveHandleFromSendToRecvPipeline(data, easy->easy_conn);
/* Check if we can move pending requests to send pipe */
diff --git a/lib/progress.c b/lib/progress.c
index 6abe72e56..1eeb78052 100644
--- a/lib/progress.c
+++ b/lib/progress.c
@@ -169,6 +169,10 @@ void Curl_pgrsTime(struct SessionHandle *data, timerid timer)
data->progress.t_startsingle = now;
break;
+ case TIMER_STARTACCEPT:
+ data->progress.t_acceptdata = Curl_tvnow();
+ break;
+
case TIMER_NAMELOOKUP:
data->progress.t_nslookup =
Curl_tvdiff_secs(now, data->progress.t_startsingle);
diff --git a/lib/progress.h b/lib/progress.h
index 95944f0ba..f5cc5403a 100644
--- a/lib/progress.h
+++ b/lib/progress.h
@@ -34,6 +34,7 @@ typedef enum {
TIMER_STARTTRANSFER,
TIMER_POSTRANSFER,
TIMER_STARTSINGLE,
+ TIMER_STARTACCEPT,
TIMER_REDIRECT,
TIMER_LAST /* must be last */
} timerid;
diff --git a/lib/strerror.c b/lib/strerror.c
index fcb617cf2..4aa125735 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -81,6 +81,12 @@ curl_easy_strerror(CURLcode error)
case CURLE_REMOTE_ACCESS_DENIED:
return "Access denied to remote resource";
+ case CURLE_FTP_ACCEPT_FAILED:
+ return "FTP: The server failed to connect to data port";
+
+ case CURLE_FTP_ACCEPT_TIMEOUT:
+ return "FTP: Accepting server connect has timed out";
+
case CURLE_FTP_PRET_FAILED:
return "FTP: The server did not accept the PRET command.";
@@ -284,8 +290,6 @@ curl_easy_strerror(CURLcode error)
return "Chunk callback failed";
/* error codes not used by current libcurl */
- case CURLE_OBSOLETE10:
- case CURLE_OBSOLETE12:
case CURLE_OBSOLETE16:
case CURLE_OBSOLETE20:
case CURLE_OBSOLETE24:
diff --git a/lib/url.c b/lib/url.c
index a4aadb28d..b952e920a 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1677,6 +1677,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
data->set.connecttimeout = va_arg(param, long);
break;
+ case CURLOPT_ACCEPTTIMEOUT_MS:
+ /*
+ * The maximum time you allow curl to wait for server connect
+ */
+ data->set.accepttimeout = va_arg(param, long);
+ break;
+
case CURLOPT_USERPWD:
/*
* user:password to use in the operation
@@ -5457,7 +5464,7 @@ CURLcode Curl_do_more(struct connectdata *conn)
if(conn->handler->do_more)
result = conn->handler->do_more(conn);
- if(result == CURLE_OK)
+ if(result == CURLE_OK && conn->bits.wait_data_conn == FALSE)
/* do_complete must be called after the protocol-specific DO function */
do_complete(conn);
diff --git a/lib/urldata.h b/lib/urldata.h
index 53df18cab..f7c35e367 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -412,6 +412,8 @@ struct ConnectBits {
bool do_more; /* this is set TRUE if the ->curl_do_more() function is
supposed to be called, after ->curl_do() */
+ bool wait_data_conn; /* this is set TRUE if data connection is waited */
+
bool tcpconnect[2]; /* the TCP layer (or similar) is connected, this is set
the first time on the first connect function call */
bool protoconnstart;/* the protocol layer has STARTED its operation after
@@ -1038,6 +1040,7 @@ struct Progress {
struct timeval start;
struct timeval t_startsingle;
+ struct timeval t_acceptdata;
#define CURR_TIME (5+1) /* 6 entries for 5 seconds */
curl_off_t speeder[ CURR_TIME ];
@@ -1407,6 +1410,7 @@ struct UserDefined {
void *ioctl_client; /* pointer to pass to the ioctl callback */
long timeout; /* in milliseconds, 0 means no timeout */
long connecttimeout; /* in milliseconds, 0 means no timeout */
+ long accepttimeout; /* in milliseconds, 0 means no timeout */
long server_response_timeout; /* in milliseconds, 0 means no timeout */
long tftp_blksize ; /* in bytes, 0 means use default */
curl_off_t infilesize; /* size of file to upload, -1 means unknown */
diff --git a/tests/data/DISABLED b/tests/data/DISABLED
index 88336f1c0..5a0f2bf95 100644
--- a/tests/data/DISABLED
+++ b/tests/data/DISABLED
@@ -2,8 +2,6 @@
# test cases are run by runtests.pl. Just add the plain test case numbers, one
# per line.
# Lines starting with '#' letters are treated as comments.
-591
-592
-593
594
+1209
1211
diff --git a/tests/data/test1206 b/tests/data/test1206
index ba578a1e2..3f853d1d9 100644
--- a/tests/data/test1206
+++ b/tests/data/test1206
@@ -36,12 +36,6 @@ FTP PORT and 425 on download
<strippart>
s/^EPRT \|1\|(.*)/EPRT \|1\|/
</strippart>
-
-# The protocol part does not include QUIT simply because the error is
-# CURLE_OPERATION_TIMEDOUT (28) which is a generic timeout error without
-# specificly saying for which connection it concerns, and for timeouts libcurl
-# marks the control channel as "invalid". As this test case times out for the
-# data connection it could still use the control channel.
<protocol>
USER anonymous
PASS ftp@example.com
@@ -50,9 +44,10 @@ EPRT |1|
TYPE I
SIZE 1206
RETR 1206
+QUIT
</protocol>
<errorcode>
-28
+10
</errorcode>
</verify>
</testcase>
diff --git a/tests/data/test1207 b/tests/data/test1207
index 6ca713196..283e46d5b 100644
--- a/tests/data/test1207
+++ b/tests/data/test1207
@@ -36,12 +36,6 @@ FTP PORT and 421 on download
<strippart>
s/^EPRT \|1\|(.*)/EPRT \|1\|/
</strippart>
-
-# The protocol part does not include QUIT simply because the error is
-# CURLE_OPERATION_TIMEDOUT (28) which is a generic timeout error without
-# specificly saying for which connection it concerns, and for timeouts libcurl
-# marks the control channel as "invalid". As this test case times out for the
-# data connection it could still use the control channel.
<protocol>
USER anonymous
PASS ftp@example.com
@@ -50,9 +44,10 @@ EPRT |1|
TYPE I
SIZE 1207
RETR 1207
+QUIT
</protocol>
<errorcode>
-28
+10
</errorcode>
</verify>
</testcase>
diff --git a/tests/data/test1208 b/tests/data/test1208
index 725d18afa..a0d428b43 100644
--- a/tests/data/test1208
+++ b/tests/data/test1208
@@ -36,12 +36,6 @@ FTP PORT download, no data conn and no transient negative reply
<strippart>
s/^EPRT \|1\|(.*)/EPRT \|1\|/
</strippart>
-
-# The protocol part does not include QUIT simply because the error is
-# CURLE_OPERATION_TIMEDOUT (28) which is a generic timeout error without
-# specificly saying for which connection it concerns, and for timeouts libcurl
-# marks the control channel as "invalid". As this test case times out for the
-# data connection it could still use the control channel.
<protocol>
USER anonymous
PASS ftp@example.com
@@ -50,9 +44,10 @@ EPRT |1|
TYPE I
SIZE 1208
RETR 1208
+QUIT
</protocol>
<errorcode>
-28
+12
</errorcode>
</verify>
</testcase>
diff --git a/tests/data/test591 b/tests/data/test591
index 0d4bac7dd..e04ae5bcc 100644
--- a/tests/data/test591
+++ b/tests/data/test591
@@ -64,7 +64,7 @@ STOR 591
QUIT
</protocol>
<errorcode>
-28
+10
</errorcode>
<upload>
</upload>
diff --git a/tests/data/test592 b/tests/data/test592
index 4af04e31c..487290dd4 100644
--- a/tests/data/test592
+++ b/tests/data/test592
@@ -64,7 +64,7 @@ STOR 592
QUIT
</protocol>
<errorcode>
-28
+10
</errorcode>
<upload>
</upload>
diff --git a/tests/data/test593 b/tests/data/test593
index 811bf93c6..c3b1f91e5 100644
--- a/tests/data/test593
+++ b/tests/data/test593
@@ -64,7 +64,7 @@ STOR 593
QUIT
</protocol>
<errorcode>
-28
+12
</errorcode>
<upload>
</upload>
diff --git a/tests/libtest/lib591.c b/tests/libtest/lib591.c
index 101f2db26..8a55e2cd1 100644
--- a/tests/libtest/lib591.c
+++ b/tests/libtest/lib591.c
@@ -77,7 +77,8 @@ int test(char *URL)
easy_setopt(easy, CURLOPT_FTPPORT, "-");
/* server connection timeout */
- easy_setopt(easy, CURLOPT_CONNECTTIMEOUT, strtol(libtest_arg2, NULL, 10));
+ easy_setopt(easy, CURLOPT_ACCEPTTIMEOUT_MS,
+ strtol(libtest_arg2, NULL, 10)*1000);
multi_init(multi);