+#ifdef WITH_SSL
+
+RD_BOOL send_ssl_chunk(const void *msg, size_t size)
+{
+ SecBuffer bufs[4] = {
+ {g_ssl->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, g_ssl->ssl_buf},
+ {size, SECBUFFER_DATA, g_ssl->ssl_buf+g_ssl->ssl_sizes.cbHeader},
+ {g_ssl->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, g_ssl->ssl_buf+g_ssl->ssl_sizes.cbHeader+size},
+ {0, SECBUFFER_EMPTY, NULL}
+ };
+ SecBufferDesc buf_desc = {SECBUFFER_VERSION, sizeof(bufs)/sizeof(*bufs), bufs};
+ SECURITY_STATUS res;
+ int tcp_res;
+
+ memcpy(bufs[1].pvBuffer, msg, size);
+ res = EncryptMessage(&g_ssl->ssl_ctx, 0, &buf_desc, 0);
+ if (res != SEC_E_OK)
+ {
+ error("EncryptMessage failed: %d\n", res);
+ return False;
+ }
+
+ tcp_res = send(g_sock, g_ssl->ssl_buf, bufs[0].cbBuffer+bufs[1].cbBuffer+bufs[2].cbBuffer, 0);
+ if (tcp_res < 1)
+ {
+ error("send failed: %d (%s)\n", tcp_res, TCP_STRERROR);
+ return False;
+ }
+
+ return True;
+}
+
+DWORD read_ssl_chunk(void *buf, SIZE_T buf_size, BOOL blocking, SIZE_T *ret_size, BOOL *eof)
+{
+ const SIZE_T ssl_buf_size = g_ssl->ssl_sizes.cbHeader+g_ssl->ssl_sizes.cbMaximumMessage+g_ssl->ssl_sizes.cbTrailer;
+ SecBuffer bufs[4];
+ SecBufferDesc buf_desc = {SECBUFFER_VERSION, sizeof(bufs)/sizeof(*bufs), bufs};
+ SSIZE_T size, buf_len = 0;
+ int i;
+ SECURITY_STATUS res;
+
+ //assert(conn->extra_len < ssl_buf_size);
+
+ if (g_ssl->extra_len)
+ {
+ memcpy(g_ssl->ssl_buf, g_ssl->extra_buf, g_ssl->extra_len);
+ buf_len = g_ssl->extra_len;
+ g_ssl->extra_len = 0;
+ xfree(g_ssl->extra_buf);
+ g_ssl->extra_buf = NULL;
+ }
+
+ size = recv(g_sock, g_ssl->ssl_buf+buf_len, ssl_buf_size-buf_len, 0);
+ if (size < 0)
+ {
+ if (!buf_len)
+ {
+ if (size == -1 && TCP_BLOCKS)
+ {
+ return WSAEWOULDBLOCK;
+ }
+ error("recv failed: %d (%s)\n", size, TCP_STRERROR);
+ return -1;//ERROR_INTERNET_CONNECTION_ABORTED;
+ }
+ }
+ else
+ {
+ buf_len += size;
+ }
+
+ if (!buf_len)
+ {
+ *eof = TRUE;
+ *ret_size = 0;
+ return ERROR_SUCCESS;
+ }
+
+ *eof = FALSE;
+
+ do
+ {
+ memset(bufs, 0, sizeof(bufs));
+ bufs[0].BufferType = SECBUFFER_DATA;
+ bufs[0].cbBuffer = buf_len;
+ bufs[0].pvBuffer = g_ssl->ssl_buf;
+
+ res = DecryptMessage(&g_ssl->ssl_ctx, &buf_desc, 0, NULL);
+ switch (res)
+ {
+ case SEC_E_OK:
+ break;
+ case SEC_I_CONTEXT_EXPIRED:
+ *eof = TRUE;
+ return ERROR_SUCCESS;
+ case SEC_E_INCOMPLETE_MESSAGE:
+ //assert(buf_len < ssl_buf_size);
+
+ size = recv(g_sock, g_ssl->ssl_buf+buf_len, ssl_buf_size-buf_len, 0);
+ if (size < 1)
+ {
+ if (size == -1 && TCP_BLOCKS)
+ {
+ /* FIXME: Optimize extra_buf usage. */
+ g_ssl->extra_buf = xmalloc(buf_len);
+ if (!g_ssl->extra_buf)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ g_ssl->extra_len = buf_len;
+ memcpy(g_ssl->extra_buf, g_ssl->ssl_buf, g_ssl->extra_len);
+ return WSAEWOULDBLOCK;
+ }
+
+ error("recv failed: %d (%s)\n", size, TCP_STRERROR);
+ return -1;//ERROR_INTERNET_CONNECTION_ABORTED;
+ }
+
+ buf_len += size;
+ continue;
+ default:
+ error("DecryptMessage failed: %d\n", res);
+ return -1;//ERROR_INTERNET_CONNECTION_ABORTED;
+ }
+ }
+ while (res != SEC_E_OK);
+
+ for (i=0; i < sizeof(bufs)/sizeof(*bufs); i++)
+ {
+ if (bufs[i].BufferType == SECBUFFER_DATA)
+ {
+ size = min(buf_size, bufs[i].cbBuffer);
+ memcpy(buf, bufs[i].pvBuffer, size);
+ if (size < bufs[i].cbBuffer)
+ {
+ //assert(!conn->peek_len);
+ g_ssl->peek_msg_mem = g_ssl->peek_msg = xmalloc(bufs[i].cbBuffer - size);
+ if (!g_ssl->peek_msg)
+ return ERROR_NOT_ENOUGH_MEMORY;
+ g_ssl->peek_len = bufs[i].cbBuffer-size;
+ memcpy(g_ssl->peek_msg, (char*)bufs[i].pvBuffer+size, g_ssl->peek_len);
+ }
+
+ *ret_size = size;
+ }
+ }
+
+ for (i=0; i < sizeof(bufs)/sizeof(*bufs); i++)
+ {
+ if (bufs[i].BufferType == SECBUFFER_EXTRA)
+ {
+ g_ssl->extra_buf = xmalloc(bufs[i].cbBuffer);
+ if (!g_ssl->extra_buf)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ g_ssl->extra_len = bufs[i].cbBuffer;
+ memcpy(g_ssl->extra_buf, bufs[i].pvBuffer, g_ssl->extra_len);
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+#endif /* WITH_SSL */