Commit 66aa9bf5 authored by Nick Zitzmann's avatar Nick Zitzmann
Browse files

darwinssl: Fix send glitchiness with data > 32 or so KB

An ambiguity in the SSLWrite() documentation lead to a bad inference in the
code where we assumed SSLWrite() returned the amount of bytes written to
the socket, when that is not actually true; it returns the amount of data
that is buffered for writing to the socket if it returns errSSLWouldBlock.
Now darwinssl_send() returns CURLE_AGAIN if data is buffered but not written.

Reference URL: http://curl.haxx.se/mail/lib-2013-02/0145.html
parent bd93062e
Loading
Loading
Loading
Loading
+51 −14
Original line number Diff line number Diff line
@@ -739,6 +739,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
    return CURLE_OUT_OF_MEMORY;
  }
#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */
  connssl->ssl_write_buffered_length = 0UL; /* reset buffered write length */

  /* check to see if we've been told to use an explicit SSL/TLS version */
#if defined(__MAC_10_8) || defined(__IPHONE_5_0)
@@ -1424,22 +1425,58 @@ static ssize_t darwinssl_send(struct connectdata *conn,
  /*struct SessionHandle *data = conn->data;*/
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  size_t processed = 0UL;
  OSStatus err = SSLWrite(connssl->ssl_ctx, mem, len, &processed);
  OSStatus err;

  /* The SSLWrite() function works a little differently than expected. The
     fourth argument (processed) is currently documented in Apple's
     documentation as: "On return, the length, in bytes, of the data actually
     written."

     Now, one could interpret that as "written to the socket," but actually,
     it returns the amount of data that was written to a buffer internal to
     the SSLContextRef instead. So it's possible for SSLWrite() to return
     errSSLWouldBlock and a number of bytes "written" because those bytes were
     encrypted and written to a buffer, not to the socket.

     So if this happens, then we need to keep calling SSLWrite() over and
     over again with no new data until it quits returning errSSLWouldBlock. */

  /* Do we have buffered data to write from the last time we were called? */
  if(connssl->ssl_write_buffered_length) {
    /* Write the buffered data: */
    err = SSLWrite(connssl->ssl_ctx, NULL, 0UL, &processed);
    switch (err) {
      case noErr:
        /* processed is always going to be 0 because we didn't write to
           the buffer, so return how much was written to the socket */
        processed = connssl->ssl_write_buffered_length;
        connssl->ssl_write_buffered_length = 0UL;
        break;
      case errSSLWouldBlock: /* argh, try again */
        *curlcode = CURLE_AGAIN;
        return -1L;
      default:
        failf(conn->data, "SSLWrite() returned error %d", err);
        *curlcode = CURLE_SEND_ERROR;
        return -1L;
    }
  }
  else {
    /* We've got new data to write: */
    err = SSLWrite(connssl->ssl_ctx, mem, len, &processed);
    if(err != noErr) {
      switch (err) {
      case errSSLWouldBlock:  /* return how much we sent (if anything) */
        if(processed)
          return (ssize_t)processed;
        case errSSLWouldBlock:
          /* Data was buffered but not sent, we have to tell the caller
             to try sending again, and remember how much was buffered */
          connssl->ssl_write_buffered_length = len;
          *curlcode = CURLE_AGAIN;
        return -1;
        break;

          return -1L;
        default:
        failf(conn->data, "SSLWrite() return error %d", err);
          failf(conn->data, "SSLWrite() returned error %d", err);
          *curlcode = CURLE_SEND_ERROR;
        return -1;
        break;
          return -1L;
      }
    }
  }
  return (ssize_t)processed;
@@ -1462,18 +1499,18 @@ static ssize_t darwinssl_recv(struct connectdata *conn,
        if(processed)
          return (ssize_t)processed;
        *curlcode = CURLE_AGAIN;
        return -1;
        return -1L;
        break;

      case errSSLClosedGraceful: /* they're done; fail gracefully */
        *curlcode = CURLE_OK;
        return -1;
        return -1L;
        break;

      default:
        failf(conn->data, "SSLRead() return error %d", err);
        *curlcode = CURLE_RECV_ERROR;
        return -1;
        return -1L;
        break;
    }
  }
+1 −0
Original line number Diff line number Diff line
@@ -326,6 +326,7 @@ struct ssl_connect_data {
  curl_socket_t ssl_sockfd;
  ssl_connect_state connecting_state;
  bool ssl_direction; /* true if writing, false if reading */
  size_t ssl_write_buffered_length;
#endif /* USE_DARWINSSL */
};