]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
request: on shutdown send, proceed normally on timeout
authorStefan Eissing <stefan@eissing.org>
Tue, 10 Sep 2024 12:08:17 +0000 (14:08 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 20 Sep 2024 21:43:43 +0000 (23:43 +0200)
When ending an FTP upload, we shut down the connection gracefully, since
the server should be notified we had send all bytes. Mostly, this is a
NOP without TLS involved. With TLS, close-notify messages should be
exchanged.

As reported in #14843, not all servers seem to do that. Since it is the
server's responsiblity to check it has received everything, we just log
the timeout and proceed as if everything is fine.

In the receive direction, we still fail the transfer if the server does
not shut down its direction properly.

Fixes #14843
Reported-by: Rasmus Melchior Jacobsen
Closes #14848

lib/cfilters.c
lib/ftp.c
lib/request.c
lib/request.h
lib/transfer.c
lib/transfer.h

index 59683084d5c6a9a749ea840822754a42782df5d2..daa1877ab211f9ebc4ec6dc85d91a60fbc3fd8e7 100644 (file)
@@ -210,7 +210,8 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
   else {
     timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);
     if(timeout_ms < 0) {
-      failf(data, "SSL shutdown timeout");
+      /* info message, since this might be regarded as acceptable */
+      infof(data, "shutdown timeout");
       return CURLE_OPERATION_TIMEDOUT;
     }
   }
index 8a6a00b67762e4460e9c66f3f2b3d4b4dd72c121..07ba3bddd899788921a30d3e691e2da8d62e425e 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -653,12 +653,14 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
     /* set the SO_SNDBUF for the secondary socket for those who need it */
     Curl_sndbuf_init(conn->sock[SECONDARYSOCKET]);
 
-    Curl_xfer_setup2(data, CURL_XFER_SEND, -1, TRUE);
+    /* FTP upload, shutdown DATA, ignore shutdown errors, as we rely
+     * on the server response on the CONTROL connection. */
+    Curl_xfer_setup2(data, CURL_XFER_SEND, -1, TRUE, TRUE);
   }
   else {
-    /* FTP download: */
+    /* FTP download, shutdown, do not ignore errors */
     Curl_xfer_setup2(data, CURL_XFER_RECV,
-                     conn->proto.ftpc.retr_size_saved, TRUE);
+                     conn->proto.ftpc.retr_size_saved, TRUE, FALSE);
   }
 
   conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
index 0fe4c801174c675fa973c17fa1ea8c0b61023e9b..310e4eac00000b9ba54af771a34d15137d1c8654 100644 (file)
@@ -327,6 +327,13 @@ static CURLcode req_flush(struct Curl_easy *data)
     if(data->req.shutdown) {
       bool done;
       result = Curl_xfer_send_shutdown(data, &done);
+      if(result && data->req.shutdown_err_ignore) {
+        infof(data, "Shutdown send direction error: %d. Broken server? "
+              "Proceeding as if everything is ok.", result);
+        result = CURLE_OK;
+        done = TRUE;
+      }
+
       if(result)
         return result;
       if(!done)
index c53c3eb5ae7e7f4787f93e21762f92581fb1e47c..bb722477882da2258b1b82fe413a9ef0f809dd78 100644 (file)
@@ -151,6 +151,7 @@ struct SingleRequest {
                         negotiation. */
   BIT(sendbuf_init); /* sendbuf is initialized */
   BIT(shutdown);     /* request end will shutdown connection */
+  BIT(shutdown_err_ignore); /* errors in shutdown will not fail request */
 #ifdef USE_HYPER
   BIT(bodywritten);
 #endif
index 3ca67d88cc8b6e071808229d9f960bd09e922a15..4c6538e9a5a8134c0c3508eb0c21f2cd873fecf5 100644 (file)
@@ -1069,8 +1069,10 @@ static void xfer_setup(
   bool getheader,           /* TRUE if header parsing is wanted */
   int writesockindex,       /* socket index to write to, it may very well be
                                the same we read from. -1 disables */
-  bool shutdown             /* shutdown connection at transfer end. Only
+  bool shutdown,            /* shutdown connection at transfer end. Only
                              * supported when sending OR receiving. */
+  bool shutdown_err_ignore  /* errors during shutdown do not fail the
+                             * transfer */
   )
 {
   struct SingleRequest *k = &data->req;
@@ -1102,6 +1104,7 @@ static void xfer_setup(
   k->getheader = getheader;
   k->size = size;
   k->shutdown = shutdown;
+  k->shutdown_err_ignore = shutdown_err_ignore;
 
   /* The code sequence below is placed in this function just because all
      necessary input is not always known in do_complete() as this function may
@@ -1126,7 +1129,7 @@ static void xfer_setup(
 
 void Curl_xfer_setup_nop(struct Curl_easy *data)
 {
-  xfer_setup(data, -1, -1, FALSE, -1, FALSE);
+  xfer_setup(data, -1, -1, FALSE, -1, FALSE, FALSE);
 }
 
 void Curl_xfer_setup1(struct Curl_easy *data,
@@ -1137,18 +1140,20 @@ void Curl_xfer_setup1(struct Curl_easy *data,
   int recv_index = (send_recv & CURL_XFER_RECV) ? FIRSTSOCKET : -1;
   int send_index = (send_recv & CURL_XFER_SEND) ? FIRSTSOCKET : -1;
   DEBUGASSERT((recv_index >= 0) || (recv_size == -1));
-  xfer_setup(data, recv_index, recv_size, getheader, send_index, FALSE);
+  xfer_setup(data, recv_index, recv_size, getheader, send_index, FALSE, FALSE);
 }
 
 void Curl_xfer_setup2(struct Curl_easy *data,
                       int send_recv,
                       curl_off_t recv_size,
-                      bool shutdown)
+                      bool shutdown,
+                      bool shutdown_err_ignore)
 {
   int recv_index = (send_recv & CURL_XFER_RECV) ? SECONDARYSOCKET : -1;
   int send_index = (send_recv & CURL_XFER_SEND) ? SECONDARYSOCKET : -1;
   DEBUGASSERT((recv_index >= 0) || (recv_size == -1));
-  xfer_setup(data, recv_index, recv_size, FALSE, send_index, shutdown);
+  xfer_setup(data, recv_index, recv_size, FALSE, send_index,
+             shutdown, shutdown_err_ignore);
 }
 
 CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
index 8d6f98d750680a018ea5cab4e7439426770753d2..87a5a389ff89eaa22fa42765c72f308ac039b747 100644 (file)
@@ -100,12 +100,13 @@ void Curl_xfer_setup1(struct Curl_easy *data,
  * the amount to receive or -1 if unknown. With `shutdown` being
  * set, the transfer is only allowed to either send OR receive
  * and the socket 2 connection will be shutdown at the end of
- * the transfer. An unclean shutdown will fail the transfer.
+ * the transfer. An unclean shutdown will fail the transfer
+ * unless `shutdown_err_ignore` is TRUE.
  */
 void Curl_xfer_setup2(struct Curl_easy *data,
                       int send_recv,
                       curl_off_t recv_size,
-                      bool shutdown);
+                      bool shutdown, bool shutdown_err_ignore);
 
 /**
  * Multi has set transfer to DONE. Last chance to trigger