]> granicus.if.org Git - curl/commitdiff
schannel SSL: Made send method handle unexpected cases better
authorMark Salisbury <mark.salisbury@hp.com>
Tue, 19 Jun 2012 22:14:17 +0000 (00:14 +0200)
committerYang Tse <yangsita@gmail.com>
Tue, 19 Jun 2012 22:16:40 +0000 (00:16 +0200)
Implemented timeout loop in schannel_send while sending data.  This
is as close as I think we can get to write buffering; I put a big
comment in to explain my thinking.

With some committer adjustments

lib/curl_schannel.c

index ba010171e534cbf67f4e8d95553a50505f796861..8b2f5ea7257f94ce6a2fb1b8b7fc860e183e4153 100644 (file)
@@ -40,7 +40,6 @@
 
 /*
  * TODO list for TLS/SSL implementation:
- * - implement write buffering
  * - implement client certificate authentication
  * - implement custom server certificate validation
  * - implement cipher/algorithm option
@@ -681,14 +680,75 @@ schannel_send(struct connectdata *conn, int sockindex,
 
   /* check if the message was encrypted */
   if(sspi_status == SEC_E_OK) {
+    written = 0;
+
     /* send the encrypted message including header, data and trailer */
     len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
-    code = Curl_write_plain(conn, conn->sock[sockindex], data, len, &written);
-    if((code != CURLE_OK) || (len != (size_t)written))
-      *err = CURLE_SEND_ERROR;
-    if(code != CURLE_OK)
-      written = -1;
-    /* TODO: implement write buffering */
+
+    /*
+       It's important to send the full message which includes the header,
+       encrypted payload, and trailer.  Until the client receives all the
+       data a coherent message has not been delivered and the client
+       can't read any of it.
+
+       If we wanted to buffer the unwritten encrypted bytes, we would
+       tell the client that all data it has requested to be sent has been
+       sent. The unwritten encrypted bytes would be the first bytes to
+       send on the next invocation.
+       Here's the catch with this - if we tell the client that all the
+       bytes have been sent, will the client call this method again to
+       send the buffered data?  Looking at who calls this function, it
+       seems the answer is NO.
+    */
+
+    /* send entire message or fail */
+    while(len > (size_t)written) {
+      ssize_t this_write;
+      long timeleft;
+      int what;
+
+      this_write = 0;
+
+      timeleft = Curl_timeleft(conn->data, NULL, TRUE);
+      if(timeleft < 0) {
+        /* we already got the timeout */
+        failf(conn->data, "schannel: timed out sending data "
+              "(bytes sent: %zd)", written);
+        *err = CURLE_OPERATION_TIMEDOUT;
+        written = -1;
+        break;
+      }
+
+      what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
+                               timeleft);
+      if(what < 0) {
+        /* fatal error */
+        failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+        *err = CURLE_SEND_ERROR;
+        written = -1;
+        break;
+      }
+      else if(0 == what) {
+        failf(conn->data, "schannel: timed out sending data "
+              "(bytes sent: %zd)", written);
+        *err = CURLE_OPERATION_TIMEDOUT;
+        written = -1;
+        break;
+      }
+      /* socket is writable */
+
+      code = Curl_write_plain(conn, conn->sock[sockindex], data + written,
+                              len - written, &this_write);
+      if(code == CURLE_AGAIN)
+        continue;
+      else if(code != CURLE_OK) {
+        *err = code;
+        written = -1;
+        break;
+      }
+
+      written += this_write;
+    }
   }
   else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
     *err = CURLE_OUT_OF_MEMORY;
@@ -699,6 +759,11 @@ schannel_send(struct connectdata *conn, int sockindex,
 
   free(data);
 
+  if(len == (size_t)written)
+    /* Encrypted message including header, data and trailer entirely sent.
+       The return value is the number of unencrypted bytes that were sent. */
+    written = outbuf[1].cbBuffer;
+
   return written;
 }