- Linus Nielsen Feltzing introduced the --ftp-ssl-ccc command line option to
authorDaniel Stenberg <daniel@haxx.se>
Fri, 5 Jan 2007 23:11:14 +0000 (23:11 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 5 Jan 2007 23:11:14 +0000 (23:11 +0000)
  curl that uses the new CURLOPT_FTP_SSL_CCC option in libcurl. If enabled, it
  will make libcurl shutdown SSL/TLS after the authentication is done on a
  FTP-SSL operation.

16 files changed:
CHANGES
RELEASE-NOTES
docs/curl.1
docs/libcurl/curl_easy_setopt.3
include/curl/curl.h
lib/ftp.c
lib/gtls.c
lib/gtls.h
lib/sslgen.c
lib/sslgen.h
lib/ssluse.c
lib/ssluse.h
lib/strerror.c
lib/url.c
lib/urldata.h
src/main.c

diff --git a/CHANGES b/CHANGES
index 811122d..9aafe8d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,12 @@
 
                                   Changelog
 
+Daniel (5 January 2007)
+- Linus Nielsen Feltzing introduced the --ftp-ssl-ccc command line option to
+  curl that uses the new CURLOPT_FTP_SSL_CCC option in libcurl. If enabled, it
+  will make libcurl shutdown SSL/TLS after the authentication is done on a
+  FTP-SSL operation.
+
 Daniel (4 January 2007)
 - David McCreedy made changes to allow base64 encoding/decoding to work on
   non-ASCII platforms.
index fe108df..a9317ea 100644 (file)
@@ -5,7 +5,7 @@ Curl and libcurl 7.16.1
  Available command line options:           112
  Available curl_easy_setopt() options:     133
  Number of public functions in libcurl:    54
- Amount of public web site mirrors:        39
+ Amount of public web site mirrors:        38
  Number of known libcurl bindings:         35
  Number of contributors:                   524
 
@@ -13,6 +13,7 @@ This release includes the following changes:
  
  o Support for SCP and SFTP were added
  o CURLOPT_CLOSEPOLICY is now deprecated
+ o --ftp-ssl-ccc and CURLOPT_FTP_SSL_CCC were added
 
 This release includes the following bugfixes:
 
@@ -67,6 +68,6 @@ advice from friends like these:
  Matt Witherspoon, Alexey Simak, Martin Skinner, Sh Diao, Jared Lundell,
  Stefan Krause, Sebastien Willemijns, Alexey Simak, Brendan Jurd,
  Robson Braga Araujo, David McCreedy, Robert Foreman, Nathanael Nerode,
- Victor Snezhko
+ Victor Snezhko, Linus Nielsen Feltzing
 
         Thanks! (and sorry if I forgot to mention someone)
index c874747..4830179 100644 (file)
@@ -5,7 +5,7 @@
 .\" *                            | (__| |_| |  _ <| |___
 .\" *                             \___|\___/|_| \_\_____|
 .\" *
-.\" * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2007, 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
@@ -433,6 +433,14 @@ Terminates the connection if the server doesn't support SSL/TLS.
 (Added in 7.15.5)
 
 If this option is used twice, the second will again disable this.
+.IP "--ftp-ssl-ccc"
+(FTP) Use CCC (Clear Command Channel)
+Shuts down the SSL/TLS layer after authenticating. The rest of the
+control channel communication will be unencrypted. This allows
+NAT routers to follow the FTP transaction.
+(Added in 7.16.1)
+
+If this option is used twice, the second will again disable this.
 .IP "-F/--form <name=content>"
 (HTTP) This lets curl emulate a filled in form in which a user has pressed the
 submit button. This causes curl to POST data using the Content-Type
index 4893c0e..55f4353 100644 (file)
@@ -5,7 +5,7 @@
 .\" *                            | (__| |_| |  _ <| |___
 .\" *                             \___|\___/|_| \_\_____|
 .\" *
-.\" * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2007, 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
@@ -925,6 +925,12 @@ Try "AUTH SSL" first, and only if that fails try "AUTH TLS"
 .IP CURLFTPAUTH_TLS
 Try "AUTH TLS" first, and only if that fails try "AUTH SSL"
 .RE
+.IP CURLOPT_FTP_SSL_CCC
+Pass a long that is set to 0 to disable and 1 to enable. If enabled, this
+option makes libcurl use CCC (Clear Command Channel). It shuts down the
+SSL/TLS layer after authenticating. The rest of the control channel
+communication will be unencrypted. This allows NAT routers to follow the FTP
+transaction.  (Added in 7.16.1)
 .IP CURLOPT_FTP_ACCOUNT
 Pass a pointer to a zero-terminated string (or NULL to disable). When an FTP
 server asks for "account data" after user name and password has been provided,
index ea5b462..8bbdd1b 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -397,6 +397,8 @@ typedef enum {
                                     generic so the error message will be of
                                     interest when this has happened */
 
+  CURLE_FTP_SSL_CCC_FAILED,      /* 80 - Failed to clear the FTP command
+                                    channel */
   CURL_LAST /* never use! */
 } CURLcode;
 
@@ -1049,6 +1051,9 @@ typedef enum {
   CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152),
   CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153),
 
+  /* Send CCC (Clear Command Channel) after authentication */
+  CINIT(FTP_SSL_CCC, LONG, 154),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
index 015c5ad..56b6cf2 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -666,6 +666,7 @@ static void state(struct connectdata *conn,
     "ACCT",
     "PBSZ",
     "PROT",
+    "CCC",
     "PWD",
     "QUOTE",
     "RETR_PREQUOTE",
@@ -2545,6 +2546,27 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
         /* we failed and bails out */
         return CURLE_FTP_SSL_FAILED;
 
+      if(data->set.ftp_use_ccc) {
+        /* CCC - Clear Command Channel
+         */
+        NBFTPSENDF(conn, "CCC", NULL);
+        state(conn, FTP_CCC);
+      }
+      else {
+        result = ftp_state_pwd(conn);
+        if(result)
+          return result;
+      }
+      break;
+
+    case FTP_CCC:
+      /* First shut down the SSL layer (note: this call will block) */
+      result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
+
+      if(result)
+        return CURLE_FTP_SSL_CCC_FAILED;
+
+      /* Then continue as normal */
       result = ftp_state_pwd(conn);
       if(result)
         return result;
index bbd8716..250ecad 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -517,6 +517,72 @@ void Curl_gtls_close(struct connectdata *conn)
 }
 
 /*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+int Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
+{
+  int result;
+  int retval = 0;
+  struct SessionHandle *data = conn->data;
+  int done = 0;
+  ssize_t nread;
+  char buf[120];
+
+  /* This has only been tested on the proftpd server, and the mod_tls code
+     sends a close notify alert without waiting for a close notify alert in
+     response. Thus we wait for a close notify alert from the server, but
+     we do not send one. Let's hope other servers do the same... */
+
+  if(conn->ssl[sockindex].session) {
+    while(!done) {
+      int what = Curl_select(conn->sock[sockindex],
+                             CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+      if(what > 0) {
+        /* Something to read, let's do it and hope that it is the close
+           notify alert from the server */
+        result = gnutls_record_recv(conn->ssl[sockindex].session,
+                                    buf, sizeof(buf));
+        switch(result) {
+        case 0:
+          /* This is the expected response. There was no data but only
+             the close notify alert */
+          done = 1;
+          break;
+        case GNUTLS_E_AGAIN:
+        case GNUTLS_E_INTERRUPTED:
+          infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
+          break;
+        default:
+          retval = -1;
+          done = 1;
+          break;
+        }
+      }
+      else if(0 == what) {
+        /* timeout */
+        failf(data, "SSL shutdown timeout");
+        done = 1;
+        break;
+      }
+      else {
+        /* anything that gets here is fatally bad */
+        failf(data, "select on SSL socket, errno: %d", Curl_sockerrno());
+        retval = -1;
+        done = 1;
+      }
+    }
+    gnutls_deinit(conn->ssl[sockindex].session);
+  }
+  gnutls_certificate_free_credentials(conn->ssl[sockindex].cred);
+
+  conn->ssl[sockindex].session = NULL;
+  conn->ssl[sockindex].use = FALSE;
+
+  return retval;
+}
+
+/*
  * If the read would block we return -1 and set 'wouldblock' to TRUE.
  * Otherwise we return the amount of data read. Other errors should return -1
  * and set 'wouldblock' to FALSE.
index 4e7025c..bff3f86 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -41,5 +41,6 @@ ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */
                        bool *wouldblock);
 void Curl_gtls_session_free(void *ptr);
 size_t Curl_gtls_version(char *buffer, size_t size);
+int Curl_gtls_shutdown(struct connectdata *conn, int sockindex);
 
 #endif
index 1d88ba3..e4fb5fb 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -397,6 +397,18 @@ void Curl_ssl_close(struct connectdata *conn)
   }
 }
 
+CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
+{
+  if(conn->ssl[sockindex].use) {
+#ifdef USE_GNUTLS
+    return Curl_gtls_shutdown(conn, sockindex);
+#else
+    return Curl_ossl_shutdown(conn, sockindex);
+#endif
+  }
+  return CURLE_OK;
+}
+
 /* Selects an (Open)SSL crypto engine
  */
 CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine)
index b910ac7..11dea32 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -69,9 +69,13 @@ size_t Curl_ssl_version(char *buffer, size_t size);
 
 int Curl_ssl_check_cxn(struct connectdata *conn);
 
+CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex);
+
 #if !defined(USE_SSL) && !defined(SSLGEN_C)
 /* set up blank macros for none-SSL builds */
 #define Curl_ssl_close_all(x)
 #endif
 
+#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
+
 #endif
index 1c02edc..5d149ba 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -728,6 +728,102 @@ void Curl_ossl_close(struct connectdata *conn)
   }
 }
 
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+int Curl_ossl_shutdown(struct connectdata *conn, int sockindex)
+{
+  int result;
+  int retval = 0;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct SessionHandle *data = conn->data;
+  char buf[120]; /* We will use this for the OpenSSL error buffer, so it has
+                    to be at least 120 bytes long. */
+  unsigned long sslerror;
+  ssize_t nread;
+  int err;
+  int done = 0;
+
+  /* This has only been tested on the proftpd server, and the mod_tls code
+     sends a close notify alert without waiting for a close notify alert in
+     response. Thus we wait for a close notify alert from the server, but
+     we do not send one. Let's hope other servers do the same... */
+
+  if(connssl->handle) {
+    while(!done) {
+      int what = Curl_select(conn->sock[sockindex],
+                             CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT);
+      if(what > 0) {
+        /* Something to read, let's do it and hope that it is the close
+           notify alert from the server */
+        nread = (ssize_t)SSL_read(conn->ssl[sockindex].handle, buf,
+                                  sizeof(buf));
+        err = SSL_get_error(conn->ssl[sockindex].handle, (int)nread);
+
+        switch(err) {
+        case SSL_ERROR_NONE: /* this is not an error */
+        case SSL_ERROR_ZERO_RETURN: /* no more data */
+          /* This is the expected response. There was no data but only
+             the close notify alert */
+          done = 1;
+          break;
+        case SSL_ERROR_WANT_READ:
+          /* there's data pending, re-invoke SSL_read() */
+          infof(data, "SSL_ERROR_WANT_READ\n");
+          break;
+        case SSL_ERROR_WANT_WRITE:
+          /* SSL wants a write. Really odd. Let's bail out. */
+          infof(data, "SSL_ERROR_WANT_WRITE\n");
+          done = 1;
+          break;
+        default:
+          /* openssl/ssl.h says "look at error stack/return value/errno" */
+          sslerror = ERR_get_error();
+          failf(conn->data, "SSL read: %s, errno %d",
+                ERR_error_string(sslerror, buf),
+                Curl_sockerrno() );
+          done = 1;
+          break;
+        }
+      }
+      else if(0 == what) {
+        /* timeout */
+        failf(data, "SSL shutdown timeout");
+        done = 1;
+        break;
+      }
+      else {
+        /* anything that gets here is fatally bad */
+        failf(data, "select on SSL socket, errno: %d", Curl_sockerrno());
+        retval = -1;
+        done = 1;
+      }
+    } /* while()-loop for the select() */
+
+    if(data->set.verbose) {
+      switch(SSL_get_shutdown(connssl->handle)) {
+      case SSL_SENT_SHUTDOWN:
+        infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n");
+        break;
+      case SSL_RECEIVED_SHUTDOWN:
+        infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN\n");
+        break;
+      case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN:
+        infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|"
+              "SSL_RECEIVED__SHUTDOWN\n");
+        break;
+      }
+    }
+
+    connssl->use = FALSE; /* get back to ordinary socket usage */
+
+    SSL_free (connssl->handle);
+    connssl->handle = NULL;
+  }
+  return retval;
+}
+
 void Curl_ossl_session_free(void *ptr)
 {
   /* free the ID */
@@ -1629,7 +1725,7 @@ Curl_ossl_connect_common(struct connectdata *conn,
       while(1) {
         int what = Curl_select(readfd, writefd, nonblocking?0:(int)timeout_ms);
         if(what > 0)
-          /* reabable or writable, go loop in the outer loop */
+          /* readable or writable, go loop in the outer loop */
           break;
         else if(0 == what) {
           if (nonblocking) {
index 05238f0..5bb7090 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -29,8 +29,8 @@
 
 #include "urldata.h"
 CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex);
-CURLcode Curl_ossl_connect_nonblocking(struct connectdata *conn, 
-                                       int sockindex, 
+CURLcode Curl_ossl_connect_nonblocking(struct connectdata *conn,
+                                       int sockindex,
                                        bool *done);
 void Curl_ossl_close(struct connectdata *conn); /* close a SSL connection */
 /* tell OpenSSL to close down all open information regarding connections (and
@@ -66,4 +66,6 @@ size_t Curl_ossl_version(char *buffer, size_t size);
 int Curl_ossl_check_cxn(struct connectdata *cxn);
 int Curl_ossl_seed(struct SessionHandle *data);
 
+int Curl_ossl_shutdown(struct connectdata *conn, int sockindex);
+
 #endif
index 4c2daca..2634dff 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2004 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2004 - 2007, 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
@@ -244,6 +244,9 @@ curl_easy_strerror(CURLcode error)
   case CURLE_FTP_SSL_FAILED:
     return "Requested FTP SSL level failed";
 
+  case CURLE_FTP_SSL_CCC_FAILED:
+    return "Failed to clear the FTP command channel";
+
   case CURLE_SEND_FAIL_REWIND:
     return "Send failed since rewinding of the data stream failed";
 
index c988506..0f3f85a 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -1140,6 +1140,10 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     data->set.ftp_use_epsv = (bool)(0 != va_arg(param, long));
     break;
 
+  case CURLOPT_FTP_SSL_CCC:
+    data->set.ftp_use_ccc = (bool)(0 != va_arg(param, long));
+    break;
+
   case CURLOPT_FTP_SKIP_PASV_IP:
     /*
      * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
index b94761c..e2cc0d1 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -302,7 +302,7 @@ struct HTTP {
  ***************************************************************************/
 typedef enum {
   FTP_STOP,    /* do nothing state, stops the state machine */
-  FTP_WAIT220, /* waiting for the inintial 220 response immediately after
+  FTP_WAIT220, /* waiting for the initial 220 response immediately after
                   a connect */
   FTP_AUTH,
   FTP_USER,
@@ -310,6 +310,7 @@ typedef enum {
   FTP_ACCT,
   FTP_PBSZ,
   FTP_PROT,
+  FTP_CCC,
   FTP_PWD,
   FTP_QUOTE, /* waiting for a response to a command sent in a quote list */
   FTP_RETR_PREQUOTE,
@@ -1273,6 +1274,8 @@ struct UserDefined {
   bool reuse_fresh;      /* do not re-use an existing connection  */
   bool ftp_use_epsv;     /* if EPSV is to be attempted or not */
   bool ftp_use_eprt;     /* if EPRT is to be attempted or not */
+  bool ftp_use_ccc;      /* if CCC is to be attempted or not */
+
   curl_ftpssl ftp_ssl;   /* if AUTH TLS is to be attempted etc */
   curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */
   bool no_signal;        /* do not use any signal/alarm handler */
index 5de0229..2dc94c1 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2007, 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
@@ -349,6 +349,7 @@ struct Configurable {
   bool ftp_ssl;
   bool ftp_ssl_reqd;
   bool ftp_ssl_control;
+  bool ftp_ssl_ccc;
 
   char *socksproxy; /* set to server string */
   int socksver;     /* set to CURLPROXY_SOCKS* define */
@@ -529,6 +530,7 @@ static void help(void)
     "    --ftp-ssl       Try SSL/TLS for ftp transfer (F)",
     "    --ftp-ssl-control Require SSL/TLS for ftp login, clear for transfer (F)",
     "    --ftp-ssl-reqd  Require SSL/TLS for ftp transfer (F)",
+    "    --ftp-ssl-ccc   Send CCC after authenticating (F)",
     " -F/--form <name=content> Specify HTTP multipart POST data (H)",
     "    --form-string <name=string> Specify HTTP multipart POST data (H)",
     " -g/--globoff       Disable URL sequences and ranges using {} and []",
@@ -1355,6 +1357,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
     {"$v", "ftp-ssl-reqd", FALSE},
     {"$w", "no-sessionid", FALSE},
     {"$x", "ftp-ssl-control", FALSE},
+    {"$y", "ftp-ssl-ccc", FALSE},
 
     {"0", "http1.0",     FALSE},
     {"1", "tlsv1",       FALSE},
@@ -1779,6 +1782,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
       case 'x': /* --ftp-ssl-control */
         config->ftp_ssl_control ^= TRUE;
         break;
+      case 'y': /* --ftp-ssl-ccc */
+        config->ftp_ssl_ccc ^= TRUE;
+        break;
       }
       break;
     case '#': /* --progress-bar */
@@ -4002,6 +4008,10 @@ operate(struct Configurable *config, int argc, char *argv[])
         else if(config->ftp_ssl_control)
           curl_easy_setopt(curl, CURLOPT_FTP_SSL, CURLFTPSSL_CONTROL);
 
+        /* new in curl 7.16.1 */
+        if(config->ftp_ssl_ccc)
+          curl_easy_setopt(curl, CURLOPT_FTP_SSL_CCC, TRUE);
+
         /* new in curl 7.11.1, modified in 7.15.2 */
         if(config->socksproxy) {
           curl_easy_setopt(curl, CURLOPT_PROXY, config->socksproxy);