Blame SOURCES/0010-curl-7.29.0-7cc00d9a.patch

3a27f0
From 3f411052825386a95d039435eb139a63859c3c73 Mon Sep 17 00:00:00 2001
3a27f0
From: Daniel Stenberg <daniel@haxx.se>
3a27f0
Date: Mon, 5 Aug 2013 23:49:53 +0200
3a27f0
Subject: [PATCH] FTP: when EPSV gets a 229 but fails to connect, retry with PASV
3a27f0
3a27f0
This is a regression as this logic used to work. It isn't clear when it
3a27f0
broke, but I'm assuming in 7.28.0 when we went all-multi internally.
3a27f0
3a27f0
This likely never worked with the multi interface. As the failed
3a27f0
connection is detected once the multi state has reached DO_MORE, the
3a27f0
Curl_do_more() function was now expanded somewhat so that the
3a27f0
ftp_do_more() function can request to go "back" to the previous state
3a27f0
when it makes another attempt - using PASV.
3a27f0
3a27f0
Added test case 1233 to verify this fix. It has the little issue that it
3a27f0
assumes no service is listening/accepting connections on port 1...
3a27f0
3a27f0
Reported-by: byte_bucket in the #curl IRC channel
3a27f0
3a27f0
[upstream commit 7cc00d9a832c42a330888aa5c11a2abad1bd5ac0]
3a27f0
3a27f0
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
3a27f0
---
3a27f0
 lib/ftp.c              |   64 ++++++++++++++++++++++++++++-------------------
3a27f0
 lib/multi.c            |   11 ++++++--
3a27f0
 lib/url.c              |   10 ++++---
3a27f0
 lib/url.h              |    4 +-
3a27f0
 lib/urldata.h          |    2 +-
3a27f0
 tests/data/Makefile.am |    2 +-
3a27f0
 tests/data/test1233    |   46 ++++++++++++++++++++++++++++++++++
3a27f0
 7 files changed, 102 insertions(+), 37 deletions(-)
3a27f0
 create mode 100644 tests/data/test1233
3a27f0
3a27f0
diff --git a/lib/ftp.c b/lib/ftp.c
3a27f0
index 469b887..4501116 100644
3a27f0
--- a/lib/ftp.c
3a27f0
+++ b/lib/ftp.c
3a27f0
@@ -136,7 +136,7 @@ static CURLcode ftp_done(struct connectdata *conn,
3a27f0
                          CURLcode, bool premature);
3a27f0
 static CURLcode ftp_connect(struct connectdata *conn, bool *done);
3a27f0
 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
3a27f0
-static CURLcode ftp_do_more(struct connectdata *conn, bool *completed);
3a27f0
+static CURLcode ftp_do_more(struct connectdata *conn, int *completed);
3a27f0
 static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
3a27f0
 static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
3a27f0
                        int numsocks);
3a27f0
@@ -1794,15 +1794,15 @@ static CURLcode ftp_state_quote(struct connectdata *conn,
3a27f0
 static CURLcode ftp_epsv_disable(struct connectdata *conn)
3a27f0
 {
3a27f0
   CURLcode result = CURLE_OK;
3a27f0
-  infof(conn->data, "got positive EPSV response, but can't connect. "
3a27f0
-        "Disabling EPSV\n");
3a27f0
+  infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n");
3a27f0
   /* disable it for next transfer */
3a27f0
   conn->bits.ftp_use_epsv = FALSE;
3a27f0
   conn->data->state.errorbuf = FALSE; /* allow error message to get
3a27f0
                                          rewritten */
3a27f0
   PPSENDF(&conn->proto.ftpc.pp, "PASV", NULL);
3a27f0
   conn->proto.ftpc.count1++;
3a27f0
-  /* remain in the FTP_PASV state */
3a27f0
+  /* remain in/go to the FTP_PASV state */
3a27f0
+  state(conn, FTP_PASV);
3a27f0
   return result;
3a27f0
 }
3a27f0
 
3a27f0
@@ -1931,15 +1931,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
3a27f0
   }
3a27f0
   else if(ftpc->count1 == 0) {
3a27f0
     /* EPSV failed, move on to PASV */
3a27f0
-
3a27f0
-    /* disable it for next transfer */
3a27f0
-    conn->bits.ftp_use_epsv = FALSE;
3a27f0
-    infof(data, "disabling EPSV usage\n");
3a27f0
-
3a27f0
-    PPSENDF(&ftpc->pp, "PASV", NULL);
3a27f0
-    ftpc->count1++;
3a27f0
-    /* remain in the FTP_PASV state */
3a27f0
-    return result;
3a27f0
+    return ftp_epsv_disable(conn);
3a27f0
   }
3a27f0
   else {
3a27f0
     failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
3a27f0
@@ -2018,14 +2010,17 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
3a27f0
   case CURLPROXY_SOCKS5_HOSTNAME:
3a27f0
     result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
3a27f0
                          SECONDARYSOCKET, conn);
3a27f0
+    connected = TRUE;
3a27f0
     break;
3a27f0
   case CURLPROXY_SOCKS4:
3a27f0
     result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
3a27f0
                          SECONDARYSOCKET, conn, FALSE);
3a27f0
+    connected = TRUE;
3a27f0
     break;
3a27f0
   case CURLPROXY_SOCKS4A:
3a27f0
     result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
3a27f0
                          SECONDARYSOCKET, conn, TRUE);
3a27f0
+    connected = TRUE;
3a27f0
     break;
3a27f0
   case CURLPROXY_HTTP:
3a27f0
   case CURLPROXY_HTTP_1_0:
3a27f0
@@ -2077,8 +2072,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
3a27f0
     }
3a27f0
   }
3a27f0
 
3a27f0
-  conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
3a27f0
-
3a27f0
+  conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
3a27f0
   conn->bits.do_more = TRUE;
3a27f0
   state(conn, FTP_STOP); /* this phase is completed */
3a27f0
 
3a27f0
@@ -3664,20 +3658,23 @@ static CURLcode ftp_range(struct connectdata *conn)
3a27f0
  *
3a27f0
  * This function shall be called when the second FTP (data) connection is
3a27f0
  * connected.
3a27f0
+ *
3a27f0
+ * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
3a27f0
+ * (which basically is only for when PASV is being sent to retry a failed
3a27f0
+ * EPSV).
3a27f0
  */
3a27f0
 
3a27f0
-static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
3a27f0
+static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
3a27f0
 {
3a27f0
   struct SessionHandle *data=conn->data;
3a27f0
   struct ftp_conn *ftpc = &conn->proto.ftpc;
3a27f0
   CURLcode result = CURLE_OK;
3a27f0
   bool connected = FALSE;
3a27f0
+  bool complete = FALSE;
3a27f0
 
3a27f0
   /* the ftp struct is inited in ftp_connect() */
3a27f0
   struct FTP *ftp = data->state.proto.ftp;
3a27f0
 
3a27f0
-  *complete = FALSE;
3a27f0
-
3a27f0
   /* if the second connection isn't done yet, wait for it */
3a27f0
   if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
3a27f0
     if(conn->tunnel_state[SECONDARYSOCKET] == TUNNEL_CONNECT) {
3a27f0
@@ -3694,14 +3691,22 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
3a27f0
     if(connected) {
3a27f0
       DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
3a27f0
     }
3a27f0
-    else
3a27f0
+    else {
3a27f0
+      if(result && (ftpc->count1 == 0)) {
3a27f0
+        *completep = -1; /* go back to DOING please */
3a27f0
+        /* this is a EPSV connect failing, try PASV instead */
3a27f0
+        return ftp_epsv_disable(conn);
3a27f0
+      }
3a27f0
       return result;
3a27f0
+    }
3a27f0
   }
3a27f0
 
3a27f0
   if(ftpc->state) {
3a27f0
     /* already in a state so skip the intial commands.
3a27f0
        They are only done to kickstart the do_more state */
3a27f0
-    result = ftp_multi_statemach(conn, complete);
3a27f0
+    result = ftp_multi_statemach(conn, &complete);
3a27f0
+
3a27f0
+    *completep = (int)complete;
3a27f0
 
3a27f0
     /* if we got an error or if we don't wait for a data connection return
3a27f0
        immediately */
3a27f0
@@ -3712,7 +3717,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
3a27f0
       /* if we reach the end of the FTP state machine here, *complete will be
3a27f0
          TRUE but so is ftpc->wait_data_conn, which says we need to wait for
3a27f0
          the data connection and therefore we're not actually complete */
3a27f0
-      *complete = FALSE;
3a27f0
+      *completep = 0;
3a27f0
   }
3a27f0
 
3a27f0
   if(ftp->transfer <= FTPTRANSFER_INFO) {
3a27f0
@@ -3735,6 +3740,9 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
3a27f0
 
3a27f0
         if(result)
3a27f0
           return result;
3a27f0
+
3a27f0
+        *completep = 1; /* this state is now complete when the server has
3a27f0
+                           connected back to us */
3a27f0
       }
3a27f0
     }
3a27f0
     else if(data->set.upload) {
3a27f0
@@ -3742,7 +3750,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
3a27f0
       if(result)
3a27f0
         return result;
3a27f0
 
3a27f0
-      result = ftp_multi_statemach(conn, complete);
3a27f0
+      result = ftp_multi_statemach(conn, &complete);
3a27f0
+      *completep = (int)complete;
3a27f0
     }
3a27f0
     else {
3a27f0
       /* download */
3a27f0
@@ -3770,7 +3779,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
3a27f0
           return result;
3a27f0
       }
3a27f0
 
3a27f0
-      result = ftp_multi_statemach(conn, complete);
3a27f0
+      result = ftp_multi_statemach(conn, &complete);
3a27f0
+      *completep = (int)complete;
3a27f0
     }
3a27f0
     return result;
3a27f0
   }
3a27f0
@@ -3782,7 +3792,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
3a27f0
 
3a27f0
   if(!ftpc->wait_data_conn) {
3a27f0
     /* no waiting for the data connection so this is now complete */
3a27f0
-    *complete = TRUE;
3a27f0
+    *completep = 1;
3a27f0
     DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
3a27f0
   }
3a27f0
 
3a27f0
@@ -3825,7 +3835,9 @@ CURLcode ftp_perform(struct connectdata *conn,
3a27f0
   /* run the state-machine */
3a27f0
   result = ftp_multi_statemach(conn, dophase_done);
3a27f0
 
3a27f0
-  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
3a27f0
+  *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
3a27f0
+
3a27f0
+  infof(conn->data, "ftp_perform ends with SECONDARY: %d\n", *connected);
3a27f0
 
3a27f0
   if(*dophase_done)
3a27f0
     DEBUGF(infof(conn->data, "DO phase is complete1\n"));
3a27f0
@@ -4445,7 +4457,7 @@ static CURLcode ftp_dophase_done(struct connectdata *conn,
3a27f0
   struct ftp_conn *ftpc = &conn->proto.ftpc;
3a27f0
 
3a27f0
   if(connected) {
3a27f0
-    bool completed;
3a27f0
+    int completed;
3a27f0
     CURLcode result = ftp_do_more(conn, &completed);
3a27f0
 
3a27f0
     if(result) {
3a27f0
diff --git a/lib/multi.c b/lib/multi.c
3a27f0
index 706df23..9a8e68e 100644
3a27f0
--- a/lib/multi.c
3a27f0
+++ b/lib/multi.c
3a27f0
@@ -906,6 +906,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
3a27f0
   struct SingleRequest *k;
3a27f0
   struct SessionHandle *data;
3a27f0
   long timeout_ms;
3a27f0
+  int control;
3a27f0
 
3a27f0
   if(!GOOD_EASY_HANDLE(easy->easy_handle))
3a27f0
     return CURLM_BAD_EASY_HANDLE;
3a27f0
@@ -1323,13 +1324,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
3a27f0
       /*
3a27f0
        * When we are connected, DO MORE and then go DO_DONE
3a27f0
        */
3a27f0
-      easy->result = Curl_do_more(easy->easy_conn, &dophase_done);
3a27f0
+      easy->result = Curl_do_more(easy->easy_conn, &control);
3a27f0
 
3a27f0
       /* No need to remove this handle from the send pipeline here since that
3a27f0
          is done in Curl_done() */
3a27f0
       if(CURLE_OK == easy->result) {
3a27f0
-        if(dophase_done) {
3a27f0
-          multistate(easy, CURLM_STATE_DO_DONE);
3a27f0
+        if(control) {
3a27f0
+          /* if positive, advance to DO_DONE
3a27f0
+             if negative, go back to DOING */
3a27f0
+          multistate(easy, control==1?
3a27f0
+                     CURLM_STATE_DO_DONE:
3a27f0
+                     CURLM_STATE_DOING);
3a27f0
           result = CURLM_CALL_MULTI_PERFORM;
3a27f0
         }
3a27f0
         else
3a27f0
diff --git a/lib/url.c b/lib/url.c
3a27f0
index b269027..52f7e27 100644
3a27f0
--- a/lib/url.c
3a27f0
+++ b/lib/url.c
3a27f0
@@ -5394,18 +5394,20 @@ CURLcode Curl_do(struct connectdata **connp, bool *done)
3a27f0
  *
3a27f0
  * TODO: A future libcurl should be able to work away this state.
3a27f0
  *
3a27f0
+ * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to
3a27f0
+ * DOING state there's more work to do!
3a27f0
  */
3a27f0
 
3a27f0
-CURLcode Curl_do_more(struct connectdata *conn, bool *completed)
3a27f0
+CURLcode Curl_do_more(struct connectdata *conn, int *complete)
3a27f0
 {
3a27f0
   CURLcode result=CURLE_OK;
3a27f0
 
3a27f0
-  *completed = FALSE;
3a27f0
+  *complete = 0;
3a27f0
 
3a27f0
   if(conn->handler->do_more)
3a27f0
-    result = conn->handler->do_more(conn, completed);
3a27f0
+    result = conn->handler->do_more(conn, complete);
3a27f0
 
3a27f0
-  if(!result && *completed)
3a27f0
+  if(!result && (*complete == 1))
3a27f0
     /* do_complete must be called after the protocol-specific DO function */
3a27f0
     do_complete(conn);
3a27f0
 
3a27f0
diff --git a/lib/url.h b/lib/url.h
3a27f0
index a026e90..c0d9c38 100644
3a27f0
--- a/lib/url.h
3a27f0
+++ b/lib/url.h
3a27f0
@@ -7,7 +7,7 @@
3a27f0
  *                            | (__| |_| |  _ <| |___
3a27f0
  *                             \___|\___/|_| \_\_____|
3a27f0
  *
3a27f0
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
3a27f0
+ * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
3a27f0
  *
3a27f0
  * This software is licensed as described in the file COPYING, which
3a27f0
  * you should have received as part of this distribution. The terms
3a27f0
@@ -37,7 +37,7 @@ CURLcode Curl_close(struct SessionHandle *data); /* opposite of curl_open() */
3a27f0
 CURLcode Curl_connect(struct SessionHandle *, struct connectdata **,
3a27f0
                       bool *async, bool *protocol_connect);
3a27f0
 CURLcode Curl_do(struct connectdata **, bool *done);
3a27f0
-CURLcode Curl_do_more(struct connectdata *, bool *completed);
3a27f0
+CURLcode Curl_do_more(struct connectdata *, int *completed);
3a27f0
 CURLcode Curl_done(struct connectdata **, CURLcode, bool premature);
3a27f0
 CURLcode Curl_disconnect(struct connectdata *, bool dead_connection);
3a27f0
 CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done);
3a27f0
diff --git a/lib/urldata.h b/lib/urldata.h
3a27f0
index 7a275da..2be467b 100644
3a27f0
--- a/lib/urldata.h
3a27f0
+++ b/lib/urldata.h
3a27f0
@@ -550,7 +550,7 @@ struct Curl_async {
3a27f0
 /* These function pointer types are here only to allow easier typecasting
3a27f0
    within the source when we need to cast between data pointers (such as NULL)
3a27f0
    and function pointers. */
3a27f0
-typedef CURLcode (*Curl_do_more_func)(struct connectdata *, bool *);
3a27f0
+typedef CURLcode (*Curl_do_more_func)(struct connectdata *, int *);
3a27f0
 typedef CURLcode (*Curl_done_func)(struct connectdata *, CURLcode, bool);
3a27f0
 
3a27f0
 
3a27f0
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
3a27f0
index 3e8dae0..3f6a047 100644
3a27f0
--- a/tests/data/Makefile.am
3a27f0
+++ b/tests/data/Makefile.am
3a27f0
@@ -78,7 +78,7 @@ test1118 test1119 test1120 test1121 test1122 test1123 test1124 test1125	\
3a27f0
 test1126 test1127 test1128 test1129 test1130 test1131 test1132 test1133 \
3a27f0
 test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
3a27f0
 test1208 test1209 test1210 test1211 test1216 test1218 \
3a27f0
-test1220 test1221 test1222 test1223 \
3a27f0
+test1220 test1221 test1222 test1223 test1233 \
3a27f0
 test1300 test1301 test1302 test1303 test1304 test1305	\
3a27f0
 test1306 test1307 test1308 test1309 test1310 test1311 test1312 test1313 \
3a27f0
 test1314 test1315 test1316 test1317 test1318 test1319 test1320 test1321 \
3a27f0
diff --git a/tests/data/test1233 b/tests/data/test1233
3a27f0
new file mode 100644
3a27f0
index 0000000..caf0527
3a27f0
--- /dev/null
3a27f0
+++ b/tests/data/test1233
3a27f0
@@ -0,0 +1,46 @@
3a27f0
+<testcase>
3a27f0
+<info>
3a27f0
+<keywords>
3a27f0
+FTP
3a27f0
+</keywords>
3a27f0
+</info>
3a27f0
+
3a27f0
+# Server-side
3a27f0
+<reply>
3a27f0
+<servercmd>
3a27f0
+# Assuming there's nothing listening on port 1
3a27f0
+REPLY EPSV 229 Entering Passiv Mode (|||1|)
3a27f0
+</servercmd>
3a27f0
+<data>
3a27f0
+here are some bytes
3a27f0
+</data>
3a27f0
+</reply>
3a27f0
+
3a27f0
+# Client-side
3a27f0
+<client>
3a27f0
+<server>
3a27f0
+ftp
3a27f0
+</server>
3a27f0
+ <name>
3a27f0
+FTP failing to connect to EPSV port, switching to PASV
3a27f0
+ </name>
3a27f0
+ <command>
3a27f0
+ftp://%HOSTIP:%FTPPORT/1233
3a27f0
+</command>
3a27f0
+</client>
3a27f0
+
3a27f0
+# Verify data after the test has been "shot"
3a27f0
+<verify>
3a27f0
+<protocol>
3a27f0
+USER anonymous
3a27f0
+PASS ftp@example.com
3a27f0
+PWD
3a27f0
+EPSV
3a27f0
+PASV
3a27f0
+TYPE I
3a27f0
+SIZE 1233
3a27f0
+RETR 1233
3a27f0
+QUIT
3a27f0
+</protocol>
3a27f0
+</verify>
3a27f0
+</testcase>
3a27f0
-- 
3a27f0
1.7.1
3a27f0