Jelajahi Sumber

tls: async support (major tls core rewrite)

The new tls hook interface (exported by the tcp code) is now used.
All the IO operations are made through a custom SSL BIO, that
reads and writes in some memory buffers (see tls_bio.h), while
externally emulating a socket.  This BIO provides in fact some
functions that will be called by the openssl code on IO.
In the case of a read event, the data is first read in memory
(using tcp_read_data()), then assigned to the BIO and after that
one of SSL_read(), SSL_connect/accept (if the connection is not
yet established) or SSL_write (if the last write wanted to read
some data due to re-keying) are called.  They will all read from
the custom BIO.  A SSL_read() might not use all the data (in some
very unlikely situations), so in this case the encrypted data is
queued and the tcp code is signaled (via some flags) that it
should repeat the read call when more space is available.
Writes are split in 2: 1st write on a new connection in async mode
and other writes (they need to be handled slightly differently).
The encrypted data will end up in a memory buffer (via SSL_write()
and the custom BIO). From there it will be either sent directly if
possible or queued (tcp_async).  In the case when the SSL_write()
needs to read some data (e.g. re-keying), the whole clear text
data will be queued and the SSL_write() will be retried on the
first read event.

There is no separate async mode for tls, if tcp is in async mode,
then tls will be too and vice versa.
Runtime tunning (queue sizes a.s.o) and statistics are missing for
now.

(C) and license changes:
 - moved tls_dump_verification_failure into a separate file
   (tls_dump_vf.c), out of tls_server.c.
 - the remaining tcp_server.[ch] code is either re-written (most of it,
   the entire read & write part) or comes from old iptel code =>
   changed the (c) to iptelorg only and the license to BSD-like
   (only for tcp_server.[ch]).
Andrei Pelinescu-Onciul 15 tahun lalu
induk
melakukan
93c495f34e

+ 144 - 0
modules/tls/tls_dump_vf.c

@@ -0,0 +1,144 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 enum.at
+ *
+ * This file is part of SIP-router, a free SIP server.
+ *
+ * SIP-router is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * SIP-router is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/** log the verification failure reason.
+ * @file tls_dump_vf.c
+ * @ingroup: tls
+ * Module: @ref tls
+ */
+/*
+ * History:
+ * --------
+ *  2010-05-20  split from tls_server.c
+*/
+
+#include "tls_dump_vf.h"
+
+#include <openssl/ssl.h>
+#include "../../dprint.h"
+#include "tls_mod.h"
+
+/** log the verification failure reason.
+ */
+void tls_dump_verification_failure(long verification_result)
+{
+	switch(verification_result) {
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+		LOG(tls_log, "verification failure: unable to get issuer certificate\n");
+		break;
+	case X509_V_ERR_UNABLE_TO_GET_CRL:
+		LOG(tls_log, "verification failure: unable to get certificate CRL\n");
+		break;
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+		LOG(tls_log, "verification failure: unable to decrypt certificate's signature\n");
+		break;
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+		LOG(tls_log, "verification failure: unable to decrypt CRL's signature\n");
+		break;
+	case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+		LOG(tls_log, "verification failure: unable to decode issuer public key\n");
+		break;
+	case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+		LOG(tls_log, "verification failure: certificate signature failure\n");
+		break;
+	case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+		LOG(tls_log, "verification failure: CRL signature failure\n");
+		break;
+	case X509_V_ERR_CERT_NOT_YET_VALID:
+		LOG(tls_log, "verification failure: certificate is not yet valid\n");
+		break;
+	case X509_V_ERR_CERT_HAS_EXPIRED:
+		LOG(tls_log, "verification failure: certificate has expired\n");
+		break;
+	case X509_V_ERR_CRL_NOT_YET_VALID:
+		LOG(tls_log, "verification failure: CRL is not yet valid\n");
+		break;
+	case X509_V_ERR_CRL_HAS_EXPIRED:
+		LOG(tls_log, "verification failure: CRL has expired\n");
+		break;
+	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+		LOG(tls_log, "verification failure: format error in certificate's notBefore field\n");
+		break;
+	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+		LOG(tls_log, "verification failure: format error in certificate's notAfter field\n");
+		break;
+	case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+		LOG(tls_log, "verification failure: format error in CRL's lastUpdate field\n");
+		break;
+	case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+		LOG(tls_log, "verification failure: format error in CRL's nextUpdate field\n");
+		break;
+	case X509_V_ERR_OUT_OF_MEM:
+		LOG(tls_log, "verification failure: out of memory\n");
+		break;
+	case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+		LOG(tls_log, "verification failure: self signed certificate\n");
+		break;
+	case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+		LOG(tls_log, "verification failure: self signed certificate in certificate chain\n");
+		break;
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+		LOG(tls_log, "verification failure: unable to get local issuer certificate\n");
+		break;
+	case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+		LOG(tls_log, "verification failure: unable to verify the first certificate\n");
+		break;
+	case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+		LOG(tls_log, "verification failure: certificate chain too long\n");
+		break;
+	case X509_V_ERR_CERT_REVOKED:
+		LOG(tls_log, "verification failure: certificate revoked\n");
+		break;
+	case X509_V_ERR_INVALID_CA:
+		LOG(tls_log, "verification failure: invalid CA certificate\n");
+		break;
+	case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+		LOG(tls_log, "verification failure: path length constraint exceeded\n");
+		break;
+	case X509_V_ERR_INVALID_PURPOSE:
+		LOG(tls_log, "verification failure: unsupported certificate purpose\n");
+		break;
+	case X509_V_ERR_CERT_UNTRUSTED:
+		LOG(tls_log, "verification failure: certificate not trusted\n");
+		break;
+	case X509_V_ERR_CERT_REJECTED:
+		LOG(tls_log, "verification failure: certificate rejected\n");
+		break;
+	case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+		LOG(tls_log, "verification failure: subject issuer mismatch\n");
+		break;
+	case X509_V_ERR_AKID_SKID_MISMATCH:
+		LOG(tls_log, "verification failure: authority and subject key identifier mismatch\n");
+		break;
+	case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+		LOG(tls_log, "verification failure: authority and issuer serial number mismatch\n");
+		break;
+	case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+		LOG(tls_log, "verification failure: key usage does not include certificate signing\n");
+		break;
+	case X509_V_ERR_APPLICATION_VERIFICATION:
+		LOG(tls_log, "verification failure: application verification failure\n");
+		break;
+	}
+}
+
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 41 - 0
modules/tls/tls_dump_vf.h

@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 enum.at
+ *
+ * This file is part of SIP-router, a free SIP server.
+ *
+ * SIP-router is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * SIP-router is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/** log the verification failure reason.
+ * @file tls_dump_vf.h
+ * @ingroup: tls
+ * Module: @ref tls
+ */
+/*
+ * History:
+ * --------
+ *  2010-05-20  split from tls_server.c
+*/
+
+#ifndef __tls_dump_vf_h
+#define __tls_dump_vf_h
+
+
+void tls_dump_verification_failure(long verification_result);
+
+#endif /*__tls_dump_vf_h*/
+
+/* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 2 - 0
modules/tls/tls_init.c

@@ -69,6 +69,7 @@
 #include "tls_mod.h"
 #include "tls_init.h"
 #include "tls_locking.h"
+#include "tls_ct_wrq.h"
 
 #if OPENSSL_VERSION_NUMBER < 0x00907000L
 #    warning ""
@@ -645,4 +646,5 @@ void destroy_tls_h(void)
 	/* TODO: free all the ctx'es */
 	tls_destroy_cfg();
 	tls_destroy_locks();
+	tls_ct_wq_destroy();
 }

+ 21 - 13
modules/tls/tls_mod.c

@@ -278,12 +278,12 @@ struct module_exports exports = {
 
 
 static struct tls_hooks tls_h = {
-	tls_h_read,
-	tls_h_blocking_write,
+	tls_read_f,
+	tls_do_send_f,
+	tls_1st_send_f,
 	tls_h_tcpconn_init,
 	tls_h_tcpconn_clean,
 	tls_h_close,
-	tls_h_fix_read_conn,
 	tls_h_init_si,
 	init_tls_h,
 	destroy_tls_h
@@ -346,11 +346,13 @@ static int mod_init(void)
 		return 0;
 	}
 
+/*
 	if (cfg_get(tcp, tcp_cfg, async) && !tls_force_run){
 		ERR("tls does not support tcp in async mode, please use"
 				" tcp_async=no in the config file\n");
 		return -1;
 	}
+*/
 	     /* Convert tls_method parameter to integer */
 	method = tls_parse_method(&tls_method);
 	if (method < 0) {
@@ -368,7 +370,7 @@ static int mod_init(void)
 	tls_cfg = (tls_cfg_t**)shm_malloc(sizeof(tls_cfg_t*));
 	if (!tls_cfg) {
 		ERR("Not enough shared memory left\n");
-		return -1;
+		goto error;
 	}
 	*tls_cfg = NULL;
 
@@ -380,23 +382,27 @@ static int mod_init(void)
 	tls_cfg_lock = lock_alloc();
 	if (tls_cfg_lock == 0) {
 		ERR("Unable to create TLS configuration lock\n");
-		return -1;
+		goto error;
 	}
 	if (lock_init(tls_cfg_lock) == 0) {
 		lock_dealloc(tls_cfg_lock);
 		ERR("Unable to initialize TLS configuration lock\n");
-		return -1;
+		goto error;
+	}
+	if (tls_ct_wq_init() < 0) {
+		ERR("Unable to initialize TLS buffering\n");
+		goto error;
 	}
-
 	if (tls_cfg_file.s) {
 		*tls_cfg = tls_load_config(&tls_cfg_file);
-		if (!(*tls_cfg)) return -1;
+		if (!(*tls_cfg)) goto error;
 	} else {
 		*tls_cfg = tls_new_cfg();
-		if (!(*tls_cfg)) return -1;
+		if (!(*tls_cfg)) goto error;
 	}
 
-	if (tls_check_sockets(*tls_cfg) < 0) return -1;
+	if (tls_check_sockets(*tls_cfg) < 0)
+		goto error;
 
 	/* fix the timeouts from s to ticks */
 	if (tls_con_lifetime<0){
@@ -413,10 +419,10 @@ static int mod_init(void)
 			tls_con_lifetime=S_TO_TICKS(tls_con_lifetime);
 		}
 	}
-	
-
-
 	return 0;
+error:
+	destroy_tls_h();
+	return -1;
 }
 
 
@@ -441,6 +447,8 @@ static int mod_child(int rank)
 
 static void destroy(void)
 {
+	/* tls is destroyed via the registered destroy_tls_h callback
+	   => nothing to do here */
 }
 
 

+ 580 - 525
modules/tls/tls_server.c

@@ -4,24 +4,21 @@
  * TLS module - main server part
  * 
  * Copyright (C) 2001-2003 FhG FOKUS
- * Copyright (C) 2004,2005 Free Software Foundation, Inc.
- * Copyright (C) 2005,2006 iptelorg GmbH
+ * Copyright (C) 2005-2010 iptelorg GmbH
  *
  * This file is part of SIP-router, a free SIP server.
  *
- * SIP-router is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * SIP-router is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 /*
  * History:
@@ -31,11 +28,10 @@
  *  2009-09-21  tls connection state is now kept in c->extra_data (no
  *               longer shared with tcp state) (andrei)
  */
-/*!
- * \file
- * \brief SIP-router TLS support :: Main server part
- * \ingroup tls
- * Module: \ref tls
+/** main tls part (implements the tls hooks that are called from the tcp code).
+ * @file tls_server.c
+ * @ingroup tls
+ * Module: @ref tls
  */
 
 
@@ -49,12 +45,16 @@
 #include "../../timer.h"
 #include "../../globals.h"
 #include "../../pt.h"
+#include "../../tcp_int_send.h"
+#include "../../tcp_read.h"
 
 #include "tls_init.h"
 #include "tls_domain.h"
 #include "tls_util.h"
 #include "tls_mod.h"
 #include "tls_server.h"
+#include "tls_bio.h"
+#include "tls_dump_vf.h"
 
 /* low memory treshold for openssl bug #1491 workaround */
 #define LOW_MEM_NEW_CONNECTION_TEST() \
@@ -62,10 +62,15 @@
 #define LOW_MEM_CONNECTED_TEST() \
 	((openssl_mem_threshold2) && (shm_available()<openssl_mem_threshold2))
 
-/* 
- * finish the ssl init (creates the SSL and set extra_data to it)
- * separated from tls_tcpconn_init to allow delayed ssl context
+#define TLS_RD_MBUF_SZ	65536
+#define TLS_WR_MBUF_SZ	65536
+
+/** finish the ssl init.
+ * Creates the SSL and set extra_data to it.
+ * Separated from tls_tcpconn_init to allow delayed ssl context
  * init. (from the "child" process and not from the main one 
+ * WARNING: the connection should be already locked.
+ * @return 0 on success, -1 on errror.
  */
 static int tls_complete_init(struct tcp_connection* c)
 {
@@ -142,37 +147,59 @@ static int tls_complete_init(struct tcp_connection* c)
 }
 
 
-/*
- * Update ssl structure with new fd 
+
+/** completes tls init if needed and checks if tls can be used.
+ *  It will check for low memory.
+ *  WARNING: must be called with c->write_lock held.
+ *  @return 0 on success, < 0 on error (complete init failed or out of memory).
  */
-static int tls_update_fd(struct tcp_connection *c, int fd)
+static int tls_fix_connection(struct tcp_connection* c)
 {
-	SSL *ssl;
-	BIO *rbio;
-	BIO *wbio;
-	
-	if (!c->extra_data && tls_complete_init(c) < 0) {
-		ERR("Delayed init failed\n");
-		return -1;
-	}else if (LOW_MEM_CONNECTED_TEST()){
+	if (unlikely(!c->extra_data)) {
+		if (unlikely(tls_complete_init(c) < 0)) {
+			ERR("Delayed init failed\n");
+			return -1;
+		}
+	}else if (unlikely(LOW_MEM_CONNECTED_TEST())){
 		ERR("tls: ssl bug #1491 workaround: not enough memory for safe"
 				" operation: %lu\n", shm_available());
 		return -1;
 	}
-	ssl = ((struct tls_extra_data*)c->extra_data)->ssl;
+	return 0;
+}
+
+
+
+/** sets an mbuf pair for the bio used by the tls connection.
+ * WARNING: must be called with c->write_lock held.
+ * @return 0 on success, -1 on error.
+ */
+static int tls_set_mbufs(struct tcp_connection *c,
+							struct tls_mbuf* rd,
+							struct tls_mbuf* wr)
+{
+	SSL *ssl;
+	BIO *rwbio;
+	
+	/* if (unlikely(tls_fix_connection(c) < 0))
+		return -1;
+	*/
 	
-	if (((rbio=SSL_get_rbio(ssl))==0) || ((wbio=SSL_get_wbio(ssl))==0)){
-		/* no BIO connected */
-		if (SSL_set_fd(ssl, fd) != 1) {
-			TLS_ERR("tls_update_fd:");
+	ssl = ((struct tls_extra_data*)c->extra_data)->ssl;
+	if (unlikely(((rwbio=SSL_get_rbio(ssl))==0) ||
+					((rwbio=SSL_get_wbio(ssl))==0))) {
+		rwbio = tls_BIO_new_mbuf(rd, wr);
+		if (unlikely(rwbio == 0)) {
+			ERR("new mbuf BIO creation failure\n");
 			return -1;
 		}
+		/* use the same bio for both read & write */
+		SSL_set_bio(ssl, rwbio, rwbio);
 		return 0;
 	}
-	if ((BIO_set_fd(rbio, fd, BIO_NOCLOSE)!=1) ||
-		(BIO_set_fd(wbio, fd, BIO_NOCLOSE)!=1)) {
+	if (unlikely(tls_BIO_mbuf_set(rwbio, rd, wr)<=0)) {
 		/* it should be always 1 */
-		TLS_ERR("tls_update_fd:");
+		ERR("failed to set mbufs");
 		return -1;
 	}
 	return 0;
@@ -199,115 +226,20 @@ static void tls_dump_cert_info(char* s, X509* cert)
 }
 
 
-static void tls_dump_verification_failure(long verification_result)
-{
-	switch(verification_result) {
-	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
-		LOG(tls_log, "verification failure: unable to get issuer certificate\n");
-		break;
-	case X509_V_ERR_UNABLE_TO_GET_CRL:
-		LOG(tls_log, "verification failure: unable to get certificate CRL\n");
-		break;
-	case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
-		LOG(tls_log, "verification failure: unable to decrypt certificate's signature\n");
-		break;
-	case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
-		LOG(tls_log, "verification failure: unable to decrypt CRL's signature\n");
-		break;
-	case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
-		LOG(tls_log, "verification failure: unable to decode issuer public key\n");
-		break;
-	case X509_V_ERR_CERT_SIGNATURE_FAILURE:
-		LOG(tls_log, "verification failure: certificate signature failure\n");
-		break;
-	case X509_V_ERR_CRL_SIGNATURE_FAILURE:
-		LOG(tls_log, "verification failure: CRL signature failure\n");
-		break;
-	case X509_V_ERR_CERT_NOT_YET_VALID:
-		LOG(tls_log, "verification failure: certificate is not yet valid\n");
-		break;
-	case X509_V_ERR_CERT_HAS_EXPIRED:
-		LOG(tls_log, "verification failure: certificate has expired\n");
-		break;
-	case X509_V_ERR_CRL_NOT_YET_VALID:
-		LOG(tls_log, "verification failure: CRL is not yet valid\n");
-		break;
-	case X509_V_ERR_CRL_HAS_EXPIRED:
-		LOG(tls_log, "verification failure: CRL has expired\n");
-		break;
-	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
-		LOG(tls_log, "verification failure: format error in certificate's notBefore field\n");
-		break;
-	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
-		LOG(tls_log, "verification failure: format error in certificate's notAfter field\n");
-		break;
-	case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
-		LOG(tls_log, "verification failure: format error in CRL's lastUpdate field\n");
-		break;
-	case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
-		LOG(tls_log, "verification failure: format error in CRL's nextUpdate field\n");
-		break;
-	case X509_V_ERR_OUT_OF_MEM:
-		LOG(tls_log, "verification failure: out of memory\n");
-		break;
-	case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-		LOG(tls_log, "verification failure: self signed certificate\n");
-		break;
-	case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-		LOG(tls_log, "verification failure: self signed certificate in certificate chain\n");
-		break;
-	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-		LOG(tls_log, "verification failure: unable to get local issuer certificate\n");
-		break;
-	case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
-		LOG(tls_log, "verification failure: unable to verify the first certificate\n");
-		break;
-	case X509_V_ERR_CERT_CHAIN_TOO_LONG:
-		LOG(tls_log, "verification failure: certificate chain too long\n");
-		break;
-	case X509_V_ERR_CERT_REVOKED:
-		LOG(tls_log, "verification failure: certificate revoked\n");
-		break;
-	case X509_V_ERR_INVALID_CA:
-		LOG(tls_log, "verification failure: invalid CA certificate\n");
-		break;
-	case X509_V_ERR_PATH_LENGTH_EXCEEDED:
-		LOG(tls_log, "verification failure: path length constraint exceeded\n");
-		break;
-	case X509_V_ERR_INVALID_PURPOSE:
-		LOG(tls_log, "verification failure: unsupported certificate purpose\n");
-		break;
-	case X509_V_ERR_CERT_UNTRUSTED:
-		LOG(tls_log, "verification failure: certificate not trusted\n");
-		break;
-	case X509_V_ERR_CERT_REJECTED:
-		LOG(tls_log, "verification failure: certificate rejected\n");
-		break;
-	case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
-		LOG(tls_log, "verification failure: subject issuer mismatch\n");
-		break;
-	case X509_V_ERR_AKID_SKID_MISMATCH:
-		LOG(tls_log, "verification failure: authority and subject key identifier mismatch\n");
-		break;
-	case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
-		LOG(tls_log, "verification failure: authority and issuer serial number mismatch\n");
-		break;
-	case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
-		LOG(tls_log, "verification failure: key usage does not include certificate signing\n");
-		break;
-	case X509_V_ERR_APPLICATION_VERIFICATION:
-		LOG(tls_log, "verification failure: application verification failure\n");
-		break;
-	}
-}
-
 
-/*
- * Wrapper around SSL_accept, returns -1 on error, 0 on success 
+/** wrapper around SSL_accept, usin SSL return convention.
+ * It will also log critical errors and certificate debugging info.
+ * @param c - tcp connection with tls (extra_data must be a filled
+ *            tcp_extra_data structure). The state must be S_TLS_ACCEPTING.
+ * @param error  set to the error reason (SSL_ERROR_*).
+ * @return >=1 on success, 0 and <0 on error. 0 means the underlying SSL
+ *           connection was closed/shutdown. < 0 is also returned for
+ *           WANT_READ or WANT_WRITE 
+ *
  */
 static int tls_accept(struct tcp_connection *c, int* error)
 {
-	int ret, err, ssl_err;
+	int ret, ssl_err;
 	SSL *ssl;
 	X509* cert;
 	struct tls_extra_data* tls_c;
@@ -321,13 +253,13 @@ static int tls_accept(struct tcp_connection *c, int* error)
 	tls_c=(struct tls_extra_data*)c->extra_data;
 	ssl=tls_c->ssl;
 	
-	if (tls_c->state != S_TLS_ACCEPTING) {
+	if (unlikely(tls_c->state != S_TLS_ACCEPTING)) {
 		BUG("Invalid connection state %d (bug in TLS code)\n", tls_c->state);
 		/* Not critical */
-		return 0;
+		goto err;
 	}
 	ret = SSL_accept(ssl);
-	if (ret == 1) {
+	if (unlikely(ret == 1)) {
 		DBG("TLS accept successful\n");
 		tls_c->state = S_TLS_ESTABLISHED;
 		LOG(tls_log, "tls_accept: new connection from %s:%d using %s %s %d\n",
@@ -350,10 +282,9 @@ static int tls_accept(struct tcp_connection *c, int* error)
 		} else {
 			LOG(tls_log, "tls_accept: client did not present a certificate\n");
 		}
-	} else {
-		err = SSL_get_error(ssl, ret);
-		if (error) *error = err;
-		switch (err) {
+	} else { /* ret == 0 or < 0 */
+		*error = SSL_get_error(ssl, ret);
+		switch (*error) {
 		case SSL_ERROR_ZERO_RETURN:
 			DBG("TLS handshake failed cleanly\n");
 			goto err;
@@ -383,7 +314,8 @@ static int tls_accept(struct tcp_connection *c, int* error)
 			TLS_ERR_RET(ssl_err, "TLS accept:");
 			if (!ssl_err) {
 				if (ret == 0) {
-					WARN("Unexpected EOF occurred while performing TLS accept\n");
+					WARN("Unexpected EOF occurred while performing"
+							" TLS accept\n");
 				} else {
 					ERR("IO error: (%d) %s\n", errno, strerror(errno));
 				}
@@ -395,19 +327,26 @@ static int tls_accept(struct tcp_connection *c, int* error)
 			goto err;
 		}
 	}
-	return 0;
+	return ret;
 err:
 	return -1;
 }
 
 
-/*
- * wrapper around SSL_connect, returns 0 on success, -1 on error 
+/** wrapper around SSL_connect, usin SSL return convention.
+ * It will also log critical errors and certificate debugging info.
+ * @param c - tcp connection with tls (extra_data must be a filled
+ *            tcp_extra_data structure). The state must be S_TLS_CONNECTING.
+ * @param error  set to the error reason (SSL_ERROR_*).
+ * @return >=1 on success, 0 and <0 on error. 0 means the underlying SSL
+ *           connection was closed/shutdown. < 0 is also returned for
+ *           WANT_READ or WANT_WRITE 
+ *
  */
 static int tls_connect(struct tcp_connection *c, int* error)
 {
 	SSL *ssl;
-	int ret, err, ssl_err;
+	int ret, ssl_err;
 	X509* cert;
 	struct tls_extra_data* tls_c;
 
@@ -420,14 +359,15 @@ static int tls_connect(struct tcp_connection *c, int* error)
 	tls_c=(struct tls_extra_data*)c->extra_data;
 	ssl=tls_c->ssl;
 	
-	if (tls_c->state != S_TLS_CONNECTING) {
+	*error = SSL_ERROR_NONE;
+	if (unlikely(tls_c->state != S_TLS_CONNECTING)) {
 		BUG("Invalid connection state %d (bug in TLS code)\n", tls_c->state);
 		/* Not critical */
-		return 0;
+		goto err;
 	}
 	ret = SSL_connect(ssl);
-	if (ret == 1) {
-		DBG("TLS connect successuful\n");
+	if (unlikely(ret == 1)) {
+		DBG("TLS connect successful\n");
 		tls_c->state = S_TLS_ESTABLISHED;
 		LOG(tls_log, "tls_connect: new connection to %s:%d using %s %s %d\n", 
 		    ip_addr2a(&c->rcv.src_ip), c->rcv.src_port,
@@ -447,13 +387,13 @@ static int tls_connect(struct tcp_connection *c, int* error)
 			}
 			X509_free(cert);
 		} else {
-			     /* this should not happen, servers always present a cert */
-			LOG(tls_log, "tls_connect: server did not present a certificate\n");
+			/* this should not happen, servers always present a cert */
+			LOG(tls_log, "tls_connect: server did not "
+							"present a certificate\n");
 		}
-	} else {
-		err = SSL_get_error(ssl, ret);
-		if (error) *error = err;
-		switch (err) {
+	} else { /* 0 or < 0 */
+		*error = SSL_get_error(ssl, ret);
+		switch (*error) {
 		case SSL_ERROR_ZERO_RETURN:
 			DBG("TLS handshake failed cleanly\n");
 			goto err;
@@ -495,7 +435,7 @@ static int tls_connect(struct tcp_connection *c, int* error)
 			goto err;
 		}
 	}
-	return 0;
+	return ret;
 err:
 	return -1;
 }
@@ -578,86 +518,12 @@ static int tls_shutdown(struct tcp_connection *c)
 }
 
 
-/* "normal", return number of bytes written,  -1 on error/EOF & sets error
- * & c->state on EOF; 0 on want READ/WRITE
- * 
- * expects a set fd */
-static int tls_write(struct tcp_connection *c, const void *buf, size_t len, int* error)
-{
-	int ret, err, ssl_err;
-	SSL *ssl;
-	ssl = ((struct tls_extra_data*)c->extra_data)->ssl;
-
-	err = 0;
-	if (LOW_MEM_CONNECTED_TEST()){
-		ERR("tls: ssl bug #1491 workaround: not enough memory for safe"
-				" operation: %lu\n", shm_available());
-		ret=-1;
-		goto err;
-	}
-	ret = SSL_write(ssl, buf, len);
-	if (ret <= 0) {
-		err = SSL_get_error(ssl, ret);
-		switch (err) {
-		case SSL_ERROR_ZERO_RETURN:
-			DBG("TLS connection has been closed\n");
-			c->state = S_CONN_EOF;
-			ret = -1;
-			break;
-			
-		case SSL_ERROR_WANT_READ:
-			DBG("Need to get more data to finish TLS write\n");
-			ret = 0;
-			break;
-
-		case SSL_ERROR_WANT_WRITE:
-			DBG("Need to send more data to finish TLS write\n");
-			ret = 0;
-			break;
-			
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L /*0.9.7*/
-		case SSL_ERROR_WANT_CONNECT:
-		case SSL_ERROR_WANT_ACCEPT:
-			DBG("TLS not connected\n");
-			ret = -1;
-			break;
-#endif
-		case SSL_ERROR_WANT_X509_LOOKUP:
-			DBG("Application callback asked to be called again\n");
-			ret = 0;
-			break;
-			
-		case SSL_ERROR_SYSCALL:
-			TLS_ERR_RET(ssl_err, "tls_write:");
-			if (!ssl_err) {
-				if (ret == 0) {
-					WARN("Unexpected EOF occurred while performing TLS shutdown\n");
-					c->state = S_CONN_EOF;
-					ret = -1;
-				} else {
-					ERR("IO error: (%d) %s\n", errno, strerror(errno));
-				}
-			}
-			break;
-			
-		default:
-			TLS_ERR("SSL error:");
-			break;
-		}
-	}
-
-err:
-	if (error) *error = err;
-	return ret;
-}
-
 
 /*
- * Called when new tcp connection is accepted or connected, create ssl
- * data structures here, there is no need to acquire any lock, because the 
- * connection is being created by a new process and on other process has
- * access to it yet, this is called before adding the tcp_connection
- * structure into the hash 
+ * Called when new tcp connection is accepted or connected. It creates the ssl
+ * data structures. There is no need to acquire any lock, because when the
+ * connection is being created no other process has access to it yet
+ * (this is called before adding the tcp_connection structure into the hash) 
  */
 int tls_h_tcpconn_init(struct tcp_connection *c, int sock)
 {
@@ -686,6 +552,12 @@ void tls_h_tcpconn_clean(struct tcp_connection *c)
 		extra = (struct tls_extra_data*)c->extra_data;
 		SSL_free(extra->ssl);
 		extra->cfg->ref_count--;
+		if (extra->ct_wq)
+			tls_ct_wq_free(&extra->ct_wq);
+		if (extra->enc_rd_buf) {
+			shm_free(extra->enc_rd_buf);
+			extra->enc_rd_buf = 0;
+		}
 		shm_free(c->extra_data);
 		c->extra_data = 0;
 	}
@@ -697,333 +569,516 @@ void tls_h_tcpconn_clean(struct tcp_connection *c)
  */
 void tls_h_close(struct tcp_connection *c, int fd)
 {
-	     /*
-	      * runs within global tcp lock 
-	      */
-	DBG("Closing SSL connection\n");
-	if (c->extra_data) {
-		if (tls_update_fd(c, fd)==0)
-			tls_shutdown(c); /* shudown only on succesfull set fd */
+	unsigned char rd_buf[TLS_RD_MBUF_SZ];
+	unsigned char wr_buf[TLS_WR_MBUF_SZ];
+	struct tls_mbuf rd, wr;
+	
+	/*
+	 * runs either within global tcp lock or after the connection has 
+	 * been "detached" and is unreachable from any other process.
+	 * Unfortunately when called via
+	 * tcpconn_put_destroy()+tcpconn_close_main_fd() the connection might
+	 * still be in a writer, so in this case locking is needed.
+	 */
+	DBG("Closing SSL connection %p\n", c->extra_data);
+	if (likely(c->extra_data)) {
+		lock_get(&c->write_lock);
+			if (unlikely(c->extra_data == 0)) {
+				/* changed in the meanwhile */
+				lock_release(&c->write_lock);
+				return;
+			}
+			tls_mbuf_init(&rd, rd_buf, sizeof(rd_buf));
+			tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf));
+			if (tls_set_mbufs(c, &rd, &wr)==0) {
+				tls_shutdown(c); /* shudown only on succesfull set fd */
+				/* write as much as possible and update wr.
+				 * Since this is a close, we don't want to queue the write
+				 * (if it can't write immediately, just fail silently)
+				 */
+				if (wr.used)
+					_tcpconn_write_nb(fd, c, (char*)wr.buf, wr.used);
+				/* we don't bother reading anything (we don't want to wait
+				on close) */
+			}
+		lock_release(&c->write_lock);
 	}
 }
 
 
 
-/*
- * This is shamelessly stolen tsend_stream from tsend.c 
- */
-/*
- * fixme: probably does not work correctly 
+/* generic tcpconn_{do,1st}_send() function pointer type */
+typedef int (*tcp_low_level_send_t)(int fd, struct tcp_connection *c,
+									char* buf, unsigned len,
+									snd_flags_t send_flags,
+									long* resp, int locked);
+
+
+
+/** tls generic send function.
+ * It is used by tls_do_send_f and tls_1st_send_f (which are wrappers
+ * arround it).
+ * WARNING: it must be called with c->write_lock held!
+ * @param c - tcp connection
+ * @param fd - valid file descriptor for the tcp connection
+ * @param buf - data
+ * @param len - data size
+ * @param send_flags
+ * @param resp - filled with a cmd. for tcp_main (@see tcpconn_do_send() for
+ *               more details)
+ * 
+ * @return >=0 on success, < 0 on error && * resp == CON_ERROR.
  */
-int tls_h_blocking_write(struct tcp_connection *c, int fd, const char *buf,
-			  unsigned int len)
+static int tls_generic_send(int fd, struct tcp_connection *c,
+						const char *buf, unsigned int len,
+						snd_flags_t send_flags, long* resp,
+						tcp_low_level_send_t tcp_do_send_f)
 {
-	int err, n, ticks, tout;
-	fd_set sel_set;
-	struct timeval timeout;
+	int n, offs;
+	SSL* ssl;
 	struct tls_extra_data* tls_c;
+	unsigned char wr_buf[TLS_WR_MBUF_SZ];
+	struct tls_mbuf rd, wr;
+	int ssl_error;
 	
+	*resp = CONN_NOP;
 	n = 0;
-	if (tls_update_fd(c, fd) < 0) goto error;
-	tls_c=(struct tls_extra_data*)c->extra_data;
-again:
-	err = 0;
-	     /* first try  a "fast" write -- avoid the extra select call,
-	      * we might get lucky and not need it */
-	if (tls_c->state == S_TLS_CONNECTING) {
-		if (tls_connect(c, &err) < 0) goto error;
-		tout = tls_handshake_timeout;
-	} else if (tls_c->state == S_TLS_ACCEPTING) {
-		if (tls_accept(c, &err) < 0) goto error;
-		tout = tls_handshake_timeout;
+	offs = 0;
+	ssl_error = SSL_ERROR_NONE;
+	lock_get(&c->write_lock);
+	if (unlikely(tls_fix_connection(c) < 0))
+		goto error;
+	tls_c = (struct tls_extra_data*)c->extra_data;
+	ssl = tls_c->ssl;
+	/* clear text already queued (WANTS_READ) queue directly*/
+	if (unlikely(tls_write_wants_read(tls_c))) {
+		if (unlikely(tls_ct_wq_add(&tls_c->ct_wq, buf+offs, len -offs) < 0))
+				goto error_wq_full;
+		goto end;
+	}
+	tls_mbuf_init(&rd, 0, 0); /* no read */
+redo_wr:
+	tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf));
+	if (tls_set_mbufs(c, &rd, &wr) < 0)
+		goto error;
+	if (unlikely(tls_c->state == S_TLS_CONNECTING)) {
+		n = tls_connect(c, &ssl_error);
+		if (unlikely(n>=1)) {
+			n = SSL_write(ssl, buf + offs, len - offs);
+			if (unlikely(n <= 0))
+				ssl_error = SSL_get_error(ssl, n);
+		}
+	} else if (unlikely(tls_c->state == S_TLS_ACCEPTING)) {
+		n = tls_accept(c, &ssl_error);
+		if (unlikely(n>=1)) {
+			n = SSL_write(ssl, buf + offs, len - offs);
+			if (unlikely(n <= 0))
+				ssl_error = SSL_get_error(ssl, n);
+		}
 	} else {
-		n = tls_write(c, buf, len, &err);
-		if (n < 0) {
-			DBG("tls_write error %d (ssl %d)\n", n, err);
-			goto error;
-		} else if (n < len) {
-			     /* not all the contents was written => try again w/ the rest
-			      * (possible when SSL_MODE_ENABLE_PARTIAL_WRITE is set)
-			      */
-			DBG("%ld bytes still need to be written\n", 
-				(long)(len - n));
-			buf += n; 
-			len -= n;
-		} else {
-			     /* succesfull write */
-			DBG("write finished, %d bytes written\n", n);
-			goto end;
+		n = SSL_write(ssl, buf + offs, len - offs);
+		if (unlikely(n <= 0))
+			ssl_error = SSL_get_error(ssl, n);
+	}
+	if (wr.used ) {
+		/* something was written */
+		if (unlikely( n < (len -offs)  && n >= 0)) {
+			/* if partial tls write, don't force close the tcp connection */
+			tcpconn_set_send_flags(c, send_flags); /* set the original flags */
+			send_flags.f &= ~SND_F_CON_CLOSE;
+		}
+		if (unlikely(tcp_do_send_f(fd, c, (char*)wr.buf, wr.used,
+											send_flags, resp, 1) < 0)){
+			tls_set_mbufs(c, 0, 0);
+			goto error_send;
 		}
-		tout = tls_send_timeout;
 	}
-
-	while(1) {
-		FD_ZERO(&sel_set);
-		FD_SET(fd, &sel_set);
-		timeout.tv_sec = tout;
-		timeout.tv_usec = 0;
-		ticks = get_ticks();
-
-		     /* blocking part, wait until we can write again on the fd */
-		switch(err){
-			case 0:
-			case SSL_ERROR_WANT_WRITE:
-				n = select(fd + 1, 0, &sel_set ,0 , &timeout);
+	/* check for possible ssl errors */
+	if (unlikely(n <= 0)){
+		switch(ssl_error) {
+			case SSL_ERROR_NONE:
 				break;
-
+			case SSL_ERROR_ZERO_RETURN:
+				/* SSL EOF */
+				goto ssl_eof;
 			case SSL_ERROR_WANT_READ:
-				n = select(fd + 1, &sel_set, 0 ,0 , &timeout);
-				break;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L /*0.9.7*/
-			case SSL_ERROR_WANT_ACCEPT:
-#endif
-			case SSL_ERROR_WANT_CONNECT:
-				DBG("re-trying accept/connect\n");
-				goto again;
-
+				/* queue write buffer */
+				if (unlikely(tls_ct_wq_add(&tls_c->ct_wq, buf+offs, len -offs)
+								< 0))
+					goto error_wq_full;
+				tls_c->flags |= F_TLS_CON_WR_WANTS_RD;
+				break; /* or goto end */
+			case SSL_ERROR_WANT_WRITE:
+				/*  error, no record fits in the buffer */
+				BUG("write buffer too small (%d/%d bytes)\n",
+						wr.used, wr.size);
+				goto bug;
+			case SSL_ERROR_SYSCALL:
 			default:
-				BUG("Unhandled SSL error %d\n", err);
-				goto error;
-		}
-		if (n < 0) {
-			if (errno == EINTR) continue;/* just a signal */
-			ERR("Select failed:"
-			    " (%d) %s\n", errno, strerror(errno));
-			goto error;
-		}
-		if (n == 0) {
-			     /* timeout, make sure the interval really expired */
-			if ((get_ticks() - ticks) >= tout) {
-				ERR("Peer not "
-				    " responding after %d s=> timeout (state %d) \n",
-				    tout, c->state);
-				goto error;
-			}
-		}
-		if (FD_ISSET(fd, &sel_set)) {
-			     /* we can write again */
-			DBG("Ready to read/write again\n");
-			goto again;
+				BUG("unexpected SSL error %d\n", ssl_error);
+				goto bug;
 		}
+	} else if (unlikely(n < (len - offs))) {
+		/* partial ssl write => retry with the rest */
+		offs += n;
+		goto redo_wr;
 	}
-	
- error:
+	tls_set_mbufs(c, 0, 0);
+end:
+	lock_release(&c->write_lock);
+	return len;
+error:
+error_send:
+error_wq_full:
+bug:
+	tls_set_mbufs(c, 0, 0);
+	lock_release(&c->write_lock);
+	*resp = CONN_ERROR;
+	return -1;
+ssl_eof:
+	c->state = S_CONN_EOF;
+	lock_release(&c->write_lock);
+	DBG("TLS connection has been closed\n");
+	*resp = CONN_EOF;
 	return -1;
- end:
-	return n;
 }
 
 
 
-#if 0  /* not used for now */
-/* nonblocking version */
-int tls_h_nonblocking_write(struct tcp_connection *c, int fd, const char *buf,
-			  unsigned int len)
+/** tls do_send callback.
+ * It is called for all sends (by the tcp send code), except the first send
+ * on an async connection (@see tls_1st_send).
+ * WARNING: it must be called with c->write_lock held!
+ * @param c - tcp connection
+ * @param fd - valid file descriptor for the tcp connection
+ * @param buf - data
+ * @param len - data size
+ * @param send_flags
+ * @param resp - filled with a cmd. for tcp_main (@see tcpconn_do_send() for
+ *               more details)
+ * 
+ * @return >=0 on success, < 0 on error && * resp == CON_ERROR.
+ */
+int tls_do_send_f(int fd, struct tcp_connection *c,
+						const char *buf, unsigned int len,
+						snd_flags_t send_flags, long* resp)
 {
-	int err, n;
-	struct tls_extra_data* tls_c;
-	
-	n = 0;
-	if (tls_update_fd(c, fd) < 0) goto error;
-	tls_c=(struct tls_extra_data*)c->extra_data;
-again:
-	err = 0;
-	if (tls_c->state == S_TLS_CONNECTING) {
-		if (tls_connect(c, &err) < 0) goto error;
-	} else if (tls_c->state == S_TLS_ACCEPTING) {
-		if (tls_accept(c, &err) < 0) goto error;
-	}
-	if (tls_c->state!=S_TLS_CONNECTING && tls_c->state!=S_TLS_ACCEPTING){
-		n = tls_write(c, buf, len, &err);
-		if (n < 0) {
-			DBG("tls_write error %d (ssl %d)\n", n, err);
-			goto error;
-		} else if (n==len){
-			goto end;
-		}else{
-			DBG("%ld bytes still need to be written\n", 
-				(long)(len - n));
-		}
-	}else
-		n=0; /* no bytes written */
+	return tls_generic_send(fd, c, buf, len, send_flags, resp,
+							tcpconn_do_send);
+}
 
-		switch(err){
-			/* TODO: set some flag: WANT_READ, WANT_WRITE */
-			case 0:
-			case SSL_ERROR_WANT_WRITE:
-				break;
-			case SSL_ERROR_WANT_READ:
-				break;
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L /*0.9.7*/
-			case SSL_ERROR_WANT_ACCEPT:
-#endif
-			case SSL_ERROR_WANT_CONNECT:
-				DBG("re-trying accept/connect\n");
-				break;
-			default:
-				BUG("Unhandled SSL error %d\n", err);
-				goto error;
-		}
-	
-error:
-	return -1;
-end:
-	return n;
+
+
+/** tls 1st_send callback.
+ * It is called for the first send on an async tcp connection
+ * (should be non-blocking).
+ * WARNING: it must be called with c->write_lock held!
+ * @param c - tcp connection
+ * @param fd - valid file descriptor for the tcp connection
+ * @param buf - data
+ * @param len - data size
+ * @param send_flags
+ * @param resp - filled with a cmd. for tcp_main (@see tcpconn_1st_send() for
+ *               more details)
+ * 
+ * @return >=0 on success, < 0 on error && * resp == CON_ERROR.
+ */
+int tls_1st_send_f(int fd, struct tcp_connection *c,
+						const char *buf, unsigned int len,
+						snd_flags_t send_flags, long* resp)
+{
+	return tls_generic_send(fd, c, buf, len, send_flags, resp,
+							tcpconn_1st_send);
 }
-#endif
 
 
-/*
- * called only when a connection is in S_TLS_ESTABLISHED, we do not have to
- * care about accepting or connecting here. Each modification of ssl data
- * structures has to be protected, another process might ask for the same
- * connection and attempt write to it which would result in updating the
- * ssl structures 
+
+/** tls read.
+ * Each modification of ssl data structures has to be protected, another process * might ask for the same connection and attempt write to it which would
+ * result in updating the ssl structures 
+ * WARNING: must be called whic c->write_lock _unlocked_.
+ * @param c - tcp connection pointer. The following flags might be set:
+ * @param flags - value/result:
+ *                     input: RD_CONN_FORCE_EOF  - force EOF after the first
+ *                            successful read (bytes_read >=0 )
+ *                     output: RD_CONN_SHORT_READ if the read exhausted
+ *                              all the bytes in the socket read buffer.
+ *                             RD_CONN_EOF if EOF detected (0 bytes read)
+ *                              or forced via RD_CONN_FORCE_EOF.
+ *                             RD_CONN_RETRY_READ  if this function should
+ *                              be called again (e.g. has some data
+ *                              buffered internally that didn't fit in
+ *                              tcp_req).
+ *                     Note: RD_CONN_SHORT_READ & RD_CONN_EOF must be cleared
+ *                           before calling this function.
+ * used to signal a seen or "forced" EOF on the
+ *     connection (when it is known that no more data will come after the
+ *     current socket buffer is emptied )=> return/signal EOF on the first
+ *     short read (=> don't use it on POLLPRI, as OOB data will cause short
+ *      reads even if there are still remaining bytes in the socket buffer)
+ * return number of bytes read, 0 on EOF or -1 on error,
+ * on EOF it also sets c->state to S_CONN_EOF.
+ * sets also r->error.
+ * @return bytes decrypted on success, -1 on error (it also sets some
+ *         tcp connection flags)
  */
-int tls_h_read(struct tcp_connection * c)
+int tls_read_f(struct tcp_connection* c, int* flags)
 {
 	struct tcp_req* r;
-	int bytes_free, bytes_read, err, ssl_err;
+	int bytes_free, bytes_read, read_size, ssl_error, ssl_read;
 	SSL* ssl;
-
+	unsigned char rd_buf[TLS_RD_MBUF_SZ];
+	unsigned char wr_buf[TLS_WR_MBUF_SZ];
+	struct tls_mbuf rd, wr;
+	struct tls_extra_data* tls_c;
+	struct tls_rd_buf* enc_rd_buf;
+	int n, flush_flags;
+	
+	ssl_read = 0;
 	r = &c->req;
+	enc_rd_buf = 0;
+	*flags &= ~RD_CONN_REPEAT_READ;
+	if (unlikely(c->extra_data == 0)) {
+		/* not yet fully init => lock & intialize */
+		lock_get(&c->write_lock);
+			if (tls_fix_connection(c) < 0) {
+				lock_release(&c->write_lock);
+				return -1;
+			}
+		lock_release(&c->write_lock);
+	} else if (unlikely(LOW_MEM_CONNECTED_TEST())){
+		ERR("tls: ssl bug #1491 workaround: not enough memory for safe"
+			" operation: %lu\n", shm_available());
+		return -1;
+	}
+	/* here it's safe to use c->extra_data in read-only mode.
+	   If it's != 0 is changed only on destroy. It's not possible to have
+	   parallel reads.*/
+	tls_c = c->extra_data;
+redo_read:
 	bytes_free = c->req.b_size - (int)(r->pos - r->buf);
-	
-	if (bytes_free == 0) {
+	if (unlikely(bytes_free == 0)) {
 		ERR("Buffer overrun, dropping\n");
 		r->error = TCP_REQ_OVERRUN;
 		return -1;
 	}
-	if (LOW_MEM_CONNECTED_TEST()){
-		ERR("tls: ssl bug #1491 workaround: not enough memory for safe"
-				" operation: %lu\n", shm_available());
-		return -1;
+	/* if data queued from a previous read(), use it (don't perform
+	 * a real read()).
+	*/
+	if (unlikely(tls_c->enc_rd_buf)) {
+		/* use queued data */
+		/* safe to use without locks, because only read changes it and 
+		   there can't be parallel reads on the same connection */
+		enc_rd_buf = tls_c->enc_rd_buf;
+		tls_c->enc_rd_buf = 0;
+		tls_mbuf_init(&rd, enc_rd_buf->buf + enc_rd_buf->pos,
+						enc_rd_buf->size - enc_rd_buf->pos);
+		rd.used = enc_rd_buf->size - enc_rd_buf->pos;
+	} else {
+		/* if we were using using queued data before, free & reset the
+			the queued read data before performing the real read() */
+		if (unlikely(enc_rd_buf)) {
+			shm_free(enc_rd_buf);
+			enc_rd_buf = 0;
+		}
+		/* real read() */
+		tls_mbuf_init(&rd, rd_buf, sizeof(rd_buf));
+		/* read() only if no SSL_PENDING (bytes available for immediate
+		   read inside the SSL context */
+		if (likely(!(tls_c->flags & F_TLS_CON_SSL_PENDING))) {
+			/* don't read more then the free bytes in the tcp req buffer */
+			read_size = MIN_unsigned(rd.size, bytes_free);
+			bytes_read = tcp_read_data(c->fd, c, (char*)rd.buf, read_size,
+										flags);
+			if (unlikely(bytes_read <= 0)) {
+				if (likely(bytes_read == 0))
+					goto end;
+				else
+					goto error;
+			}
+			rd.used = bytes_read;
+		}
 	}
-	     /* we have to avoid to run in the same time 
-	      * with a tls_write because of the 
-	      * update_fd stuff  (we don't want a write
-	      * stealing the fd under us or vice versa)
-	      * => lock on con->write_lock (ugly hack) */
+	
+	tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf));
+	ssl_error = SSL_ERROR_NONE;
+	
+	/* we have to avoid to run in the same time 
+	 * with a tls_write because of the
+	 * update bio stuff  (we don't want a write
+	 * stealing the wbio or rbio under us or vice versa)
+	 * => lock on con->write_lock (ugly hack) */
 	lock_get(&c->write_lock);
-	if (tls_update_fd(c, c->fd) != 0) {
-		     /* error */
-		lock_release(&c->write_lock);
-		return -1;
-	}
-	ssl = ((struct tls_extra_data*)c->extra_data)->ssl;
-	bytes_read = SSL_read(ssl, r->pos, bytes_free);
+		tls_set_mbufs(c, &rd, &wr);
+		ssl = tls_c->ssl;
+		n = 0;
+		if (unlikely(tls_write_wants_read(tls_c) &&
+						!(*flags & RD_CONN_EOF))) {
+			DBG("tls write on read (WRITE_WANTS_READ)\n");
+			n = tls_ct_wq_flush(ssl, &tls_c->ct_wq, &flush_flags,
+								&ssl_error);
+			if (unlikely(n < 0 )) {
+				tls_set_mbufs(c, 0, 0);
+				lock_release(&c->write_lock);
+				ERR("write flush error (%d)\n", n);
+				goto error;
+			}
+			if (likely(flush_flags & F_BUFQ_EMPTY))
+				tls_c->flags &= ~F_TLS_CON_WR_WANTS_RD;
+		}
+		if (likely((rd.pos != rd.used ||
+						(tls_c->flags & F_TLS_CON_SSL_PENDING)) &&
+					ssl_error == SSL_ERROR_NONE)) {
+			/* reset the SSL_PENDING flag */
+			tls_c->flags &= ~F_TLS_CON_SSL_PENDING;
+			if (unlikely(tls_c->state == S_TLS_CONNECTING)) {
+				n = tls_connect(c, &ssl_error);
+				if (unlikely(n>=1)) {
+					n = SSL_read(ssl, r->pos, bytes_free);
+				} else
+					goto ssl_read_skipped;
+			} else if (unlikely(tls_c->state == S_TLS_ACCEPTING)) {
+				n = tls_accept(c, &ssl_error);
+				if (unlikely(n>=1)) {
+					n = SSL_read(ssl, r->pos, bytes_free);
+				} else
+					goto ssl_read_skipped;
+			} else {
+				/* if bytes in then decrypt read buffer into tcpconn req.
+				   buffer */
+				n = SSL_read(ssl, r->pos, bytes_free);
+			}
+			if (unlikely(n <= 0)) {
+				ssl_error = SSL_get_error(ssl, n);
+				/*  errors handled below, outside the lock */
+			} else {
+				ssl_error = SSL_ERROR_NONE;
+				r->pos += n;
+				ssl_read += n;
+				if (unlikely(SSL_pending(ssl)>0)) {
+					tls_c->flags |= F_TLS_CON_SSL_PENDING;
+					*flags |= RD_CONN_REPEAT_READ;
+				}
+			}
+ssl_read_skipped:
+			;
+		}
+		if (unlikely(wr.used != 0 && ssl_error != SSL_ERROR_ZERO_RETURN)) {
+			/* something was written and it's not ssl EOF*/
+			if (unlikely(tcpconn_send_unsafe(c->fd, c, (char*)wr.buf,
+											wr.used, c->send_flags) < 0)) {
+				tls_set_mbufs(c, 0, 0);
+				lock_release(&c->write_lock);
+				goto error_send;
+			}
+		}
+		/* quickly catch bugs: segfault if accessed and not set */
+		tls_set_mbufs(c, 0, 0);
 	lock_release(&c->write_lock);
-	
-	if (bytes_read <= 0) {
-		err = SSL_get_error(ssl, bytes_read);
-		switch(err){
+	switch(ssl_error) {
+		case SSL_ERROR_NONE:
+			break;
 		case SSL_ERROR_ZERO_RETURN:
-			     /* tls connection has been closed */
-			DBG("tls_read: eof\n");
-			c->state = S_CONN_EOF;
-			return 0;
-
+			/* SSL EOF */
+			DBG("tls_read: SSL EOF on %p, FD %d\n", c, c->fd);
+			goto ssl_eof;
 		case SSL_ERROR_WANT_READ:
-			DBG("tls_read: Need to read more data\n");
-			return 0;
-			
+			/* reset the SSL_PENDING flag (in case we end here due to
+			   a failed write buffer flush) */
+			tls_c->flags &= ~F_TLS_CON_SSL_PENDING;
+			/* needs to read more data */
+			if (unlikely(rd.pos != rd.used)) {
+				/* data still in the read buffer */
+				BUG("SSL_ERROR_WANT_READ but data still in"
+						" the rbio (%d bytes)\n", rd.used - rd.pos);
+				goto bug;
+			}
+			if (unlikely((*flags & (RD_CONN_EOF | RD_CONN_SHORT_READ)) == 0))
+				/* there might still be data to read and there is space
+				   to decrypt it in tcp_req (no byte has been written into
+				    tcp_req in this case) */
+				goto redo_read;
+			goto end; /* no more data to read */
 		case SSL_ERROR_WANT_WRITE:
-			     /* retry later */
-			DBG("tls_read: Need to write more data\n");
-			return 0;
-
+			/* write buffer too small, nothing written */
+			BUG("write buffer too small (%d/%d bytes)\n",
+					wr.used, wr.size);
+			goto bug;
 		case SSL_ERROR_SYSCALL:
-			TLS_ERR_RET(ssl_err, "tls_read:");
-			if (!ssl_err) {
-				if (bytes_read == 0) {
-					LOG(tls_log, "WARNING: tls_read: improper EOF on tls"
-					    " (harmless)\n");
-					c->state = S_CONN_EOF;
-					return 0;
-				} else {
-					ERR("Error reading: syscall"
-					    " (%d) %s\n", errno, strerror(errno));
-				}
-			} 
-			     /* error return */
-			r->error = TCP_READ_ERROR;
-			return -1;
 		default:
-			TLS_ERR("tls_read:");
-			r->error = TCP_READ_ERROR;
-			return -1;
-		}
+			BUG("unexpected SSL error %d\n", ssl_error);
+			goto bug;
 	}
-
-	r->pos += bytes_read;
-	return bytes_read;
-}
-
-
-/*
- * called before tls_read, the this function should attempt tls_accept or
- * tls_connect depending on the state of the connection.
- * If this function does not return 1, then the tcp layer would not
- * call tcp_read 
- * @return  1 success, 0 try again (don't attempt tls_read()), -1 error
- */
-int tls_h_fix_read_conn(struct tcp_connection *c)
-{
-	int ret;
-	struct tls_extra_data* tls_c;
-	
-	ret = -1;
-	tls_c = 0;
-	if (unlikely(c->extra_data==0)){
-		lock_get(&c->write_lock);
-			if (unlikely(tls_update_fd(c, c->fd) < 0)){
-				ret = -1;
-			} else {
-				tls_c=(struct tls_extra_data*)c->extra_data;
-				switch(tls_c->state){
-					case S_TLS_ACCEPTING:
-						ret=tls_accept(c, 0);
-						break;
-					case S_TLS_CONNECTING:
-						ret=tls_connect(c, 0);
-						break;
-					default:
-						/* fall through */
-						ret=1;
-						break;
+	if (unlikely(rd.pos != rd.used)) {
+		/* encrypted data still in the read buffer (SSL_read() did not
+		   consume all of it) */
+		/* if (n< bytes_free) then not a full record read yet to get all
+		        the requested bytes (unlikely, since openssl should buffer
+		        it internally in this case).
+		   else more data, but no space to store it => queue read data? */
+		if (unlikely(n < 0))
+			/* here n should always be >= 0 */
+			BUG("unexpected value (n = %d)\n", n);
+		else if (unlikely(n < bytes_free))
+			BUG("read buffer not exhausted (rbio still has %d bytes,"
+					"last SSL_read %d / %d)\n",
+					rd.used - rd.pos, n, bytes_free);
+		else if (n == bytes_free) {
+			/*  queue read data if not fully consumed by SSL_read()
+			 * (very unlikely situation)
+			 */
+			if (likely(!enc_rd_buf)) {
+				enc_rd_buf = shm_malloc(sizeof(*enc_rd_buf) -
+										sizeof(enc_rd_buf->buf) +
+										rd.used - rd.pos);
+				if (unlikely(enc_rd_buf == 0)) {
+					ERR("memory allocation error (%d bytes requested)\n",
+						sizeof(*enc_rd_buf) + sizeof(enc_rd_buf->buf) +
+										rd.used - rd.pos);
+					goto error;
 				}
+				enc_rd_buf->pos = 0;
+				enc_rd_buf->size = rd.used - rd.pos;
+				memcpy(enc_rd_buf->buf, rd.buf + rd.pos,
+										enc_rd_buf->size);
+			} else if ((enc_rd_buf->buf + enc_rd_buf->pos) == rd.buf)
+				enc_rd_buf->pos += rd.pos;
+			else {
+				BUG("enc_rd_buf->buf = %p, pos = %d, rd_buf.buf = %p\n",
+						enc_rd_buf->buf, enc_rd_buf->pos, rd.buf);
+				goto bug;
 			}
-		lock_release(&c->write_lock);
-	} else {
-		tls_c=(struct tls_extra_data*)c->extra_data;
-		switch (tls_c->state) {
-			case S_TLS_ACCEPTING:
-				lock_get(&c->write_lock);
-					tls_c=(struct tls_extra_data*)c->extra_data;
-					/* It might have changed meanwhile */
-					if (likely(tls_c->state == S_TLS_ACCEPTING)) {
-						ret = tls_update_fd(c, c->fd);
-						if (ret == 0) ret = tls_accept(c, 0);
-						else ret = -1;
-					}
-				lock_release(&c->write_lock);
-			break;
-			case S_TLS_CONNECTING:
-				lock_get(&c->write_lock);
-					tls_c=(struct tls_extra_data*)c->extra_data;
-					/* It might have changed meanwhile */
-					if (likely(tls_c->state == S_TLS_CONNECTING)) {
-						ret = tls_update_fd(c, c->fd);
-						if (ret == 0) ret = tls_connect(c, 0);
-						else ret = -1;
-					}
-				lock_release(&c->write_lock);
-			break;
-		default: /* fall through */
-			ret=1;
-			break;
+			if (unlikely(tls_c->enc_rd_buf))
+				BUG("tls_c->enc_rd_buf!=0 (%p)\n", tls_c->enc_rd_buf);
+			/* there can't be 2 reads in parallel, so no locking is needed
+			   here */
+			tls_c->enc_rd_buf = enc_rd_buf;
+			enc_rd_buf = 0;
+			*flags |= RD_CONN_REPEAT_READ;
 		}
+	} else if (n < bytes_free && n > 0 &&
+				((*flags & (RD_CONN_EOF|RD_CONN_SHORT_READ)) == 0)) {
+		/* still space in the tcp unenc. req. buffer, no SSL_read error,
+		   not a short read and not an EOF (possible more data in
+		   the socket buffer) => redo read*/
+		goto redo_read;
 	}
-	return (ret>=0)?(tls_c->state==S_TLS_ESTABLISHED):ret;
+	
+end:
+	if (enc_rd_buf)
+		shm_free(enc_rd_buf);
+	return ssl_read;
+ssl_eof:
+	if (enc_rd_buf)
+		shm_free(enc_rd_buf);
+	c->state = S_CONN_EOF;
+	*flags |= RD_CONN_EOF;
+	return ssl_read;
+error_send:
+error:
+bug:
+	if (enc_rd_buf)
+		shm_free(enc_rd_buf);
+	r->error=TCP_READ_ERROR;
+	return -1;
 }

+ 43 - 33
modules/tls/tls_server.h

@@ -2,37 +2,28 @@
  * $Id$
  *
  * TLS module - main server part
- *
+ * 
  * Copyright (C) 2001-2003 FhG FOKUS
- * Copyright (C) 2004,2005 Free Software Foundation, Inc.
- * Copyright (C) 2005,2006 iptelorg GmbH
- *
- * This file is part of sip-router, a free SIP server.
- *
- * sip-router is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
+ * Copyright (C) 2005-2010 iptelorg GmbH
  *
- * For a license to use the sip-router software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- *    [email protected]
+ * This file is part of SIP-router, a free SIP server.
  *
- * sip-router is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-/*!
- * \file
- * \brief SIP-router TLS support :: Main server part
- * \ingroup tls
- * Module: \ref tls
+/** main tls part (implements the tls hooks that are called from the tcp code).
+ * @file tls_server.h
+ * @ingroup tls
+ * Module: @ref tls
  */
 
 
@@ -42,6 +33,7 @@
 #include <stdio.h>
 #include "../../tcp_conn.h"
 #include "tls_domain.h"
+#include "tls_ct_wrq.h"
 
 enum tls_conn_states {
 						S_TLS_NONE = 0,
@@ -50,16 +42,29 @@ enum tls_conn_states {
 						S_TLS_ESTABLISHED
 					};
 
+struct tls_rd_buf {
+	unsigned int pos; /* current position */
+	unsigned int size; /* total size (buf) */
+	unsigned char buf[1];
+};
+
+/* tls conn flags */
+#define F_TLS_CON_WR_WANTS_RD 1 /* write wants read */
+#define F_TLS_CON_SSL_PENDING 2 /* bytes buffered inside the SSL context */
+
 struct tls_extra_data {
 	tls_cfg_t* cfg; /* Configuration used for this connection */
 	SSL* ssl;       /* SSL context used for the connection */
+	tls_ct_q* ct_wq;
+	struct tls_rd_buf* enc_rd_buf;
+	unsigned int flags;
 	enum  tls_conn_states state;
 };
 
-/*
- * dump ssl error stack 
- */
-void tls_print_errstack(void);
+
+/* return true if write wants read */
+#define tls_write_wants_read(tls_ed) (tls_ed->flags & F_TLS_CON_WR_WANTS_RD)
+
 
 /*
  * Called when new tcp connection is accepted 
@@ -76,10 +81,15 @@ void tls_h_tcpconn_clean(struct tcp_connection *c);
  */
 void tls_h_close(struct tcp_connection *c, int fd);
 
-int tls_h_blocking_write(struct tcp_connection *c, int fd,
-			  const char *buf, unsigned int len);
+int tls_do_send_f(int fd, struct tcp_connection *c,
+						const char *buf, unsigned int len,
+						snd_flags_t send_flags, long* resp);
+
+int tls_1st_send_f(int fd, struct tcp_connection *c,
+						const char *buf, unsigned int len,
+						snd_flags_t send_flags, long* resp);
 
-int tls_h_read(struct tcp_connection *c);
+int tls_read_f(struct tcp_connection *c, int* flags);
 
 int tls_h_fix_read_conn(struct tcp_connection *c);