smtp: use the upload buffer size for scratch buffer malloc
[platform/upstream/curl.git] / lib / pingpong.c
index fcc9dd5..ad370ee 100644 (file)
@@ -5,11 +5,11 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2017, 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
  *   'pingpong' is for generic back-and-forth support functions used by FTP,
  *   IMAP, POP3, SMTP and whatever more that likes them.
  *
- * $Id$
  ***************************************************************************/
 
-#include "setup.h"
+#include "curl_setup.h"
 
 #include "urldata.h"
 #include "sendf.h"
 #include "speedcheck.h"
 #include "pingpong.h"
 #include "multiif.h"
+#include "non-ascii.h"
+#include "vtls/vtls.h"
 
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
 #include "curl_memory.h"
-/* The last #include file should be: */
 #include "memdebug.h"
 
 #ifdef USE_PINGPONG
 
 /* Returns timeout in ms. 0 or negative number means the timeout has already
    triggered */
-long Curl_pp_state_timeout(struct pingpong *pp)
+time_t Curl_pp_state_timeout(struct pingpong *pp)
 {
   struct connectdata *conn = pp->conn;
-  struct SessionHandle *data=conn->data;
-  long timeout_ms=360000; /* in milliseconds */
-
-  if(data->set.server_response_timeout )
-    /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
-       remaining time.  Also, use pp->response because SERVER_RESPONSE_TIMEOUT
-       is supposed to govern the response for any given server response, not
-       for the time from connect to the given server response. */
-    timeout_ms = data->set.server_response_timeout - /* timeout time */
-      Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
-  else if(data->set.timeout)
+  struct Curl_easy *data = conn->data;
+  time_t timeout_ms; /* in milliseconds */
+  time_t timeout2_ms; /* in milliseconds */
+  long response_time = (data->set.server_response_timeout)?
+    data->set.server_response_timeout: pp->response_time;
+
+  /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
+     remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
+     supposed to govern the response for any given server response, not for
+     the time from connect to the given server response. */
+
+  /* Without a requested timeout, we only wait 'response_time' seconds for the
+     full response to arrive before we bail out */
+  timeout_ms = response_time -
+    Curl_timediff(Curl_now(), pp->response); /* spent time */
+
+  if(data->set.timeout) {
     /* if timeout is requested, find out how much remaining time we have */
-    timeout_ms = data->set.timeout - /* timeout time */
-      Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
-  else
-    /* Without a requested timeout, we only wait 'response_time' seconds for
-       the full response to arrive before we bail out */
-    timeout_ms = pp->response_time -
-      Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
+    timeout2_ms = data->set.timeout - /* timeout time */
+      Curl_timediff(Curl_now(), conn->now); /* spent time */
+
+    /* pick the lowest number */
+    timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
+  }
 
   return timeout_ms;
 }
 
-
 /*
- * Curl_pp_multi_statemach()
- *
- * called repeatedly until done when the multi interface is used.
+ * Curl_pp_statemach()
  */
-CURLcode Curl_pp_multi_statemach(struct pingpong *pp)
+CURLcode Curl_pp_statemach(struct pingpong *pp, bool block)
 {
   struct connectdata *conn = pp->conn;
   curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int rc;
-  struct SessionHandle *data=conn->data;
+  time_t interval_ms;
+  time_t timeout_ms = Curl_pp_state_timeout(pp);
+  struct Curl_easy *data = conn->data;
   CURLcode result = CURLE_OK;
-  long timeout_ms = Curl_pp_state_timeout(pp);
 
   if(timeout_ms <= 0) {
     failf(data, "server response timeout");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
-                         pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
-                         0);
-
-  if(rc == -1) {
-    failf(data, "select/poll error");
-    return CURLE_OUT_OF_MEMORY;
-  }
-  else if(rc != 0)
-    result = pp->statemach_act(conn);
-
-  /* if rc == 0, then select() timed out */
-
-  return result;
-}
-
-/*
- * Curl_pp_easy_statemach()
- *
- * called repeatedly until done when the easy interface is used.
- */
-CURLcode Curl_pp_easy_statemach(struct pingpong *pp)
-{
-  struct connectdata *conn = pp->conn;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  int rc;
-  long interval_ms;
-  long timeout_ms = Curl_pp_state_timeout(pp);
-  struct SessionHandle *data=conn->data;
-  CURLcode result;
-
-  if(timeout_ms <=0 ) {
-    failf(data, "server response timeout");
     return CURLE_OPERATION_TIMEDOUT; /* already too little time */
   }
 
-  interval_ms = 1000;  /* use 1 second timeout intervals */
-  if(timeout_ms < interval_ms)
-    interval_ms = timeout_ms;
-
-  rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
-                         pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
-                         (int)interval_ms);
-
-  if(Curl_pgrsUpdate(conn))
-    result = CURLE_ABORTED_BY_CALLBACK;
+  if(block) {
+    interval_ms = 1000;  /* use 1 second timeout intervals */
+    if(timeout_ms < interval_ms)
+      interval_ms = timeout_ms;
+  }
   else
-    result = Curl_speedcheck(data, Curl_tvnow());
+    interval_ms = 0; /* immediate */
+
+  if(Curl_ssl_data_pending(conn, FIRSTSOCKET))
+    rc = 1;
+  else if(Curl_pp_moredata(pp))
+    /* We are receiving and there is data in the cache so just read it */
+    rc = 1;
+  else if(!pp->sendleft && Curl_ssl_data_pending(conn, FIRSTSOCKET))
+    /* We are receiving and there is data ready in the SSL library */
+    rc = 1;
+  else
+    rc = Curl_socket_check(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
+                           CURL_SOCKET_BAD,
+                           pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
+                           interval_ms);
+
+  if(block) {
+    /* if we didn't wait, we don't have to spend time on this now */
+    if(Curl_pgrsUpdate(conn))
+      result = CURLE_ABORTED_BY_CALLBACK;
+    else
+      result = Curl_speedcheck(data, Curl_now());
+
+    if(result)
+      return result;
+  }
 
-  if(result)
-    ;
-  else if(rc == -1) {
+  if(rc == -1) {
     failf(data, "select/poll error");
     result = CURLE_OUT_OF_MEMORY;
   }
@@ -159,22 +143,19 @@ void Curl_pp_init(struct pingpong *pp)
   pp->nread_resp = 0;
   pp->linestart_resp = conn->data->state.buffer;
   pp->pending_resp = TRUE;
-  pp->response = Curl_tvnow(); /* start response time-out now! */
+  pp->response = Curl_now(); /* start response time-out now! */
 }
 
 
 
 /***********************************************************************
  *
- * Curl_pp_sendfv()
+ * Curl_pp_vsendf()
  *
- * Send the formated string as a command to a pingpong server. Note that
+ * Send the formatted string as a command to a pingpong server. Note that
  * the string should not have any CRLF appended, as this function will
  * append the necessary things itself.
  *
- * NOTE: we build the command in a fixed-length buffer, which sets length
- * restrictions on the command!
- *
  * made to never block
  */
 CURLcode Curl_pp_vsendf(struct pingpong *pp,
@@ -182,70 +163,82 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp,
                         va_list args)
 {
   ssize_t bytes_written;
-/* may still not be big enough for some krb5 tokens */
-#define SBUF_SIZE 1024
-  char s[SBUF_SIZE];
   size_t write_len;
-  char *sptr=s;
-  CURLcode res = CURLE_OK;
+  char *fmt_crlf;
+  char *s;
+  CURLcode result;
   struct connectdata *conn = pp->conn;
-  struct SessionHandle *data = conn->data;
+  struct Curl_easy *data;
 
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  enum protection_level data_sec = conn->data_prot;
+#ifdef HAVE_GSSAPI
+  enum protection_level data_sec;
 #endif
 
-  vsnprintf(s, SBUF_SIZE-3, fmt, args);
+  DEBUGASSERT(pp->sendleft == 0);
+  DEBUGASSERT(pp->sendsize == 0);
+  DEBUGASSERT(pp->sendthis == NULL);
+
+  if(!conn)
+    /* can't send without a connection! */
+    return CURLE_SEND_ERROR;
 
-  strcat(s, "\r\n"); /* append a trailing CRLF */
+  data = conn->data;
+
+  fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
+  if(!fmt_crlf)
+    return CURLE_OUT_OF_MEMORY;
+
+  s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
+  free(fmt_crlf);
+  if(!s)
+    return CURLE_OUT_OF_MEMORY;
 
-  bytes_written=0;
+  bytes_written = 0;
   write_len = strlen(s);
 
   Curl_pp_init(pp);
 
-#ifdef CURL_DOES_CONVERSIONS
-  res = Curl_convert_to_network(data, s, write_len);
+  result = Curl_convert_to_network(data, s, write_len);
   /* Curl_convert_to_network calls failf if unsuccessful */
-  if(res != CURLE_OK) {
-    return res;
+  if(result) {
+    free(s);
+    return result;
   }
-#endif /* CURL_DOES_CONVERSIONS */
 
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-  conn->data_prot = prot_cmd;
+#ifdef HAVE_GSSAPI
+  conn->data_prot = PROT_CMD;
 #endif
-  res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
-                   &bytes_written);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+  result = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
+                     &bytes_written);
+#ifdef HAVE_GSSAPI
+  data_sec = conn->data_prot;
+  DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
   conn->data_prot = data_sec;
 #endif
 
-  if(CURLE_OK != res)
-    return res;
+  if(result) {
+    free(s);
+    return result;
+  }
 
   if(conn->data->set.verbose)
     Curl_debug(conn->data, CURLINFO_HEADER_OUT,
-               sptr, (size_t)bytes_written, conn);
+               s, (size_t)bytes_written, conn);
 
   if(bytes_written != (ssize_t)write_len) {
-    /* the whole chunk was not sent, store the rest of the data */
-    write_len -= bytes_written;
-    sptr += bytes_written;
-    pp->sendthis = malloc(write_len);
-    if(pp->sendthis) {
-      memcpy(pp->sendthis, sptr, write_len);
-      pp->sendsize = pp->sendleft = write_len;
-    }
-    else {
-      failf(data, "out of memory");
-      res = CURLE_OUT_OF_MEMORY;
-    }
+    /* the whole chunk was not sent, keep it around and adjust sizes */
+    pp->sendthis = s;
+    pp->sendsize = write_len;
+    pp->sendleft = write_len - bytes_written;
+  }
+  else {
+    free(s);
+    pp->sendthis = NULL;
+    pp->sendleft = pp->sendsize = 0;
+    pp->response = Curl_now();
   }
-  else
-    pp->response = Curl_tvnow();
 
-  return res;
+  return CURLE_OK;
 }
 
 
@@ -253,27 +246,24 @@ CURLcode Curl_pp_vsendf(struct pingpong *pp,
  *
  * Curl_pp_sendf()
  *
- * Send the formated string as a command to a pingpong server. Note that
+ * Send the formatted string as a command to a pingpong server. Note that
  * the string should not have any CRLF appended, as this function will
  * append the necessary things itself.
  *
- * NOTE: we build the command in a fixed-length buffer, which sets length
- * restrictions on the command!
- *
  * made to never block
  */
 CURLcode Curl_pp_sendf(struct pingpong *pp,
                        const char *fmt, ...)
 {
-  CURLcode res;
+  CURLcode result;
   va_list ap;
   va_start(ap, fmt);
 
-  res = Curl_pp_vsendf(pp, fmt, ap);
+  result = Curl_pp_vsendf(pp, fmt, ap);
 
   va_end(ap);
 
-  return res;
+  return result;
 }
 
 /*
@@ -287,71 +277,68 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
                           size_t *size) /* size of the response */
 {
   ssize_t perline; /* count bytes per line */
-  bool keepon=TRUE;
+  bool keepon = TRUE;
   ssize_t gotbytes;
   char *ptr;
   struct connectdata *conn = pp->conn;
-  struct SessionHandle *data = conn->data;
+  struct Curl_easy *data = conn->data;
   char * const buf = data->state.buffer;
   CURLcode result = CURLE_OK;
 
   *code = 0; /* 0 for errors or not done */
   *size = 0;
 
-  ptr=buf + pp->nread_resp;
+  ptr = buf + pp->nread_resp;
 
   /* number of bytes in the current line, so far */
   perline = (ssize_t)(ptr-pp->linestart_resp);
 
-  keepon=TRUE;
-
-  while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
+  while((pp->nread_resp < (size_t)data->set.buffer_size) &&
+        (keepon && !result)) {
 
     if(pp->cache) {
       /* we had data in the "cache", copy that instead of doing an actual
        * read
        *
-       * ftp->cache_size is cast to int here.  This should be safe,
-       * because it would have been populated with something of size
-       * int to begin with, even though its datatype may be larger
-       * than an int.
+       * pp->cache_size is cast to ssize_t here.  This should be safe, because
+       * it would have been populated with something of size int to begin
+       * with, even though its datatype may be larger than an int.
        */
-      DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
+      if((ptr + pp->cache_size) > (buf + data->set.buffer_size + 1)) {
+        failf(data, "cached response data too big to handle");
+        return CURLE_RECV_ERROR;
+      }
       memcpy(ptr, pp->cache, pp->cache_size);
-      gotbytes = pp->cache_size;
+      gotbytes = (ssize_t)pp->cache_size;
       free(pp->cache);    /* free the cache */
       pp->cache = NULL;   /* clear the pointer */
       pp->cache_size = 0; /* zero the size just in case */
     }
     else {
-      int res;
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+#ifdef HAVE_GSSAPI
       enum protection_level prot = conn->data_prot;
-
-      conn->data_prot = 0;
+      conn->data_prot = PROT_CLEAR;
 #endif
-      DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
-      res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
-                      &gotbytes);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+      DEBUGASSERT((ptr + data->set.buffer_size - pp->nread_resp) <=
+                  (buf + data->set.buffer_size + 1));
+      result = Curl_read(conn, sockfd, ptr,
+                         data->set.buffer_size - pp->nread_resp,
+                         &gotbytes);
+#ifdef HAVE_GSSAPI
+      DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
       conn->data_prot = prot;
 #endif
-      if(res < 0)
-        /* EWOULDBLOCK */
+      if(result == CURLE_AGAIN)
         return CURLE_OK; /* return */
 
-#ifdef CURL_DOES_CONVERSIONS
-      if((res == CURLE_OK) && (gotbytes > 0)) {
+      if(!result && (gotbytes > 0))
         /* convert from the network encoding */
-        res = Curl_convert_from_network(data, ptr, gotbytes);
-        /* Curl_convert_from_network calls failf if unsuccessful */
-      }
-#endif /* CURL_DOES_CONVERSIONS */
+        result = Curl_convert_from_network(data, ptr, gotbytes);
+      /* Curl_convert_from_network calls failf if unsuccessful */
 
-      if(CURLE_OK != res) {
-        result = (CURLcode)res; /* Set outer result variable to this error. */
+      if(result)
+        /* Set outer result variable to this error. */
         keepon = FALSE;
-      }
     }
 
     if(!keepon)
@@ -359,7 +346,7 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
     else if(gotbytes <= 0) {
       keepon = FALSE;
       result = CURLE_RECV_ERROR;
-      failf(data, "FTP response reading failed");
+      failf(data, "response reading failed");
     }
     else {
       /* we got a whole chunk of data, which can be anything from one
@@ -374,17 +361,17 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
       pp->nread_resp += gotbytes;
       for(i = 0; i < gotbytes; ptr++, i++) {
         perline++;
-        if(*ptr=='\n') {
-          /* a newline is CRLF in ftp-talk, so the CR is ignored as
+        if(*ptr == '\n') {
+          /* a newline is CRLF in pp-talk, so the CR is ignored as
              the line isn't really terminated until the LF comes */
 
           /* output debug output if that is requested */
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+#ifdef HAVE_GSSAPI
           if(!conn->sec_complete)
 #endif
             if(data->set.verbose)
-            Curl_debug(data, CURLINFO_HEADER_IN,
-                       pp->linestart_resp, (size_t)perline, conn);
+              Curl_debug(data, CURLINFO_HEADER_IN,
+                         pp->linestart_resp, (size_t)perline, conn);
 
           /*
            * We pass all response-lines to the callback function registered
@@ -396,25 +383,22 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
           if(result)
             return result;
 
-          if(pp->endofresp(pp, code)) {
+          if(pp->endofresp(conn, pp->linestart_resp, perline, code)) {
             /* This is the end of the last line, copy the last line to the
-               start of the buffer and zero terminate, for old times sake (and
-               krb4)! */
-            char *meow;
-            int n;
-            for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
-              buf[n] = *meow;
-            *meow=0; /* zero terminate */
-            keepon=FALSE;
-            pp->linestart_resp = ptr+1; /* advance pointer */
+               start of the buffer and zero terminate, for old times sake */
+            size_t n = ptr - pp->linestart_resp;
+            memmove(buf, pp->linestart_resp, n);
+            buf[n] = 0; /* zero terminate */
+            keepon = FALSE;
+            pp->linestart_resp = ptr + 1; /* advance pointer */
             i++; /* skip this before getting out */
 
             *size = pp->nread_resp; /* size of the response */
             pp->nread_resp = 0; /* restart */
             break;
           }
-          perline=0; /* line starts over here */
-          pp->linestart_resp = ptr+1;
+          perline = 0; /* line starts over here */
+          pp->linestart_resp = ptr + 1;
         }
       }
 
@@ -425,25 +409,28 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
            it may actually contain another end of response already! */
         clipamount = gotbytes - i;
         restart = TRUE;
+        DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
+                     "server response left\n",
+                     (int)clipamount));
       }
       else if(keepon) {
 
-        if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
+        if((perline == gotbytes) && (gotbytes > data->set.buffer_size/2)) {
           /* We got an excessive line without newlines and we need to deal
              with it. We keep the first bytes of the line then we throw
              away the rest. */
-          infof(data, "Excessive server response line length received, %zd bytes."
-                " Stripping\n", gotbytes);
+          infof(data, "Excessive server response line length received, "
+                "%zd bytes. Stripping\n", gotbytes);
           restart = TRUE;
 
           /* we keep 40 bytes since all our pingpong protocols are only
              interested in the first piece */
           clipamount = 40;
         }
-        else if(pp->nread_resp > BUFSIZE/2) {
-          /* We got a large chunk of data and there's potentially still trailing
-             data to take care of, so we put any such part in the "cache", clear
-             the buffer to make space and restart. */
+        else if(pp->nread_resp > (size_t)data->set.buffer_size/2) {
+          /* We got a large chunk of data and there's potentially still
+             trailing data to take care of, so we put any such part in the
+             "cache", clear the buffer to make space and restart. */
           clipamount = perline;
           restart = TRUE;
         }
@@ -501,11 +488,9 @@ CURLcode Curl_pp_flushsend(struct pingpong *pp)
   /* we have a piece of a command still left to send */
   struct connectdata *conn = pp->conn;
   ssize_t written;
-  CURLcode result = CURLE_OK;
   curl_socket_t sock = conn->sock[FIRSTSOCKET];
-
-  result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
-                      pp->sendleft, pp->sendleft, &written);
+  CURLcode result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
+                               pp->sendleft, pp->sendleft, &written);
   if(result)
     return result;
 
@@ -515,22 +500,24 @@ CURLcode Curl_pp_flushsend(struct pingpong *pp)
   }
   else {
     free(pp->sendthis);
-    pp->sendthis=NULL;
+    pp->sendthis = NULL;
     pp->sendleft = pp->sendsize = 0;
-    pp->response = Curl_tvnow();
+    pp->response = Curl_now();
   }
   return CURLE_OK;
 }
 
 CURLcode Curl_pp_disconnect(struct pingpong *pp)
 {
-  if(pp->cache) {
-    free(pp->cache);
-    pp->cache = NULL;
-  }
+  free(pp->cache);
+  pp->cache = NULL;
   return CURLE_OK;
 }
 
-
+bool Curl_pp_moredata(struct pingpong *pp)
+{
+  return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
+         TRUE : FALSE;
+}
 
 #endif