Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b

Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/tls/openssl/tls_tcp.c b/src/tls/openssl/tls_tcp.c
new file mode 100644
index 0000000..ef3601e
--- /dev/null
+++ b/src/tls/openssl/tls_tcp.c
@@ -0,0 +1,437 @@
+/**
+ * @file openssl/tls_tcp.c TLS/TCP backend using OpenSSL
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_main.h>
+#include <re_sa.h>
+#include <re_net.h>
+#include <re_srtp.h>
+#include <re_tcp.h>
+#include <re_tls.h>
+#include "tls.h"
+
+
+#define DEBUG_MODULE "tls"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/* NOTE: shadow struct defined in tls_*.c */
+struct tls_conn {
+	SSL *ssl;
+#ifdef TLS_BIO_OPAQUE
+	BIO_METHOD *biomet;
+#endif
+	BIO *sbio_out;
+	BIO *sbio_in;
+	struct tcp_helper *th;
+	struct tcp_conn *tcp;
+	bool active;
+	bool up;
+};
+
+
+static void destructor(void *arg)
+{
+	struct tls_conn *tc = arg;
+
+	if (tc->ssl) {
+		int r = SSL_shutdown(tc->ssl);
+		if (r <= 0)
+			ERR_clear_error();
+
+		SSL_free(tc->ssl);
+	}
+
+#ifdef TLS_BIO_OPAQUE
+	if (tc->biomet)
+		BIO_meth_free(tc->biomet);
+#endif
+
+	mem_deref(tc->th);
+	mem_deref(tc->tcp);
+}
+
+
+static int bio_create(BIO *b)
+{
+#ifdef TLS_BIO_OPAQUE
+	BIO_set_init(b, 1);
+	BIO_set_data(b, NULL);
+	BIO_set_flags(b, 0);
+#else
+	b->init  = 1;
+	b->num   = 0;
+	b->ptr   = NULL;
+	b->flags = 0;
+#endif
+
+	return 1;
+}
+
+
+static int bio_destroy(BIO *b)
+{
+	if (!b)
+		return 0;
+
+#ifdef TLS_BIO_OPAQUE
+	BIO_set_init(b, 0);
+	BIO_set_data(b, NULL);
+	BIO_set_flags(b, 0);
+#else
+	b->ptr   = NULL;
+	b->init  = 0;
+	b->flags = 0;
+#endif
+
+	return 1;
+}
+
+
+static int bio_write(BIO *b, const char *buf, int len)
+{
+#ifdef TLS_BIO_OPAQUE
+	struct tls_conn *tc = BIO_get_data(b);
+#else
+	struct tls_conn *tc = b->ptr;
+#endif
+	struct mbuf mb;
+	int err;
+
+	mb.buf = (void *)buf;
+	mb.pos = 0;
+	mb.end = mb.size = len;
+
+	err = tcp_send_helper(tc->tcp, &mb, tc->th);
+	if (err)
+		return -1;
+
+	return len;
+}
+
+
+static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+	(void)b;
+	(void)num;
+	(void)ptr;
+
+	if (cmd == BIO_CTRL_FLUSH) {
+		/* The OpenSSL library needs this */
+		return 1;
+	}
+
+	return 0;
+}
+
+
+#ifdef TLS_BIO_OPAQUE
+
+static BIO_METHOD *bio_method_tcp(void)
+{
+	BIO_METHOD *method;
+
+	method = BIO_meth_new(BIO_TYPE_SOURCE_SINK, "tcp_send");
+	if (!method) {
+		DEBUG_WARNING("alloc: BIO_meth_new() failed\n");
+		ERR_clear_error();
+		return NULL;
+	}
+
+	BIO_meth_set_write(method, bio_write);
+	BIO_meth_set_ctrl(method, bio_ctrl);
+	BIO_meth_set_create(method, bio_create);
+	BIO_meth_set_destroy(method, bio_destroy);
+
+	return method;
+}
+
+#else
+
+static struct bio_method_st bio_tcp_send = {
+	BIO_TYPE_SOURCE_SINK,
+	"tcp_send",
+	bio_write,
+	0,
+	0,
+	0,
+	bio_ctrl,
+	bio_create,
+	bio_destroy,
+	0
+};
+
+#endif
+
+
+static int tls_connect(struct tls_conn *tc)
+{
+	int err = 0, r;
+
+	ERR_clear_error();
+
+	r = SSL_connect(tc->ssl);
+	if (r <= 0) {
+		const int ssl_err = SSL_get_error(tc->ssl, r);
+
+		ERR_clear_error();
+
+		switch (ssl_err) {
+
+		case SSL_ERROR_WANT_READ:
+			break;
+
+		default:
+			DEBUG_WARNING("connect: error (r=%d, ssl_err=%d)\n",
+				      r, ssl_err);
+			err = EPROTO;
+			break;
+		}
+	}
+
+	return err;
+}
+
+
+static int tls_accept(struct tls_conn *tc)
+{
+	int err = 0, r;
+
+	ERR_clear_error();
+
+	r = SSL_accept(tc->ssl);
+	if (r <= 0) {
+		const int ssl_err = SSL_get_error(tc->ssl, r);
+
+		ERR_clear_error();
+
+		switch (ssl_err) {
+
+		case SSL_ERROR_WANT_READ:
+			break;
+
+		default:
+			DEBUG_WARNING("accept error: (r=%d, ssl_err=%d)\n",
+				      r, ssl_err);
+			err = EPROTO;
+			break;
+		}
+	}
+
+	return err;
+}
+
+
+static bool estab_handler(int *err, bool active, void *arg)
+{
+	struct tls_conn *tc = arg;
+
+	DEBUG_INFO("tcp established (active=%u)\n", active);
+
+	if (!active)
+		return true;
+
+	tc->active = true;
+	*err = tls_connect(tc);
+
+	return true;
+}
+
+
+static bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg)
+{
+	struct tls_conn *tc = arg;
+	int r;
+
+	/* feed SSL data to the BIO */
+	r = BIO_write(tc->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb));
+	if (r <= 0) {
+		DEBUG_WARNING("recv: BIO_write %d\n", r);
+		ERR_clear_error();
+		*err = ENOMEM;
+		return true;
+	}
+
+	if (SSL_state(tc->ssl) != SSL_ST_OK) {
+
+		if (tc->up) {
+			*err = EPROTO;
+			return true;
+		}
+
+		if (tc->active) {
+			*err = tls_connect(tc);
+		}
+		else {
+			*err = tls_accept(tc);
+		}
+
+		DEBUG_INFO("state=0x%04x\n", SSL_state(tc->ssl));
+
+		/* TLS connection is established */
+		if (SSL_state(tc->ssl) != SSL_ST_OK)
+			return true;
+
+		*estab = true;
+		tc->up = true;
+	}
+
+	mbuf_set_pos(mb, 0);
+
+	for (;;) {
+		int n;
+
+		if (mbuf_get_space(mb) < 4096) {
+			*err = mbuf_resize(mb, mb->size + 8192);
+			if (*err)
+				return true;
+		}
+
+		ERR_clear_error();
+
+		n = SSL_read(tc->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb));
+		if (n <= 0) {
+			const int ssl_err = SSL_get_error(tc->ssl, n);
+
+			ERR_clear_error();
+
+			switch (ssl_err) {
+
+			case SSL_ERROR_WANT_READ:
+				break;
+
+			case SSL_ERROR_ZERO_RETURN:
+				*err = ECONNRESET;
+				return true;
+
+			default:
+				*err = EPROTO;
+				return true;
+			}
+
+			break;
+		}
+
+		mb->pos += n;
+	}
+
+	mbuf_set_end(mb, mb->pos);
+	mbuf_set_pos(mb, 0);
+
+	return false;
+}
+
+
+static bool send_handler(int *err, struct mbuf *mb, void *arg)
+{
+	struct tls_conn *tc = arg;
+	int r;
+
+	ERR_clear_error();
+
+	r = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb));
+	if (r <= 0) {
+		DEBUG_WARNING("SSL_write: %d\n", SSL_get_error(tc->ssl, r));
+		ERR_clear_error();
+		*err = EPROTO;
+	}
+
+	return true;
+}
+
+
+/**
+ * Start TLS on a TCP-connection
+ *
+ * @param ptc   Pointer to allocated TLS connectioon
+ * @param tls   TLS Context
+ * @param tcp   TCP Connection
+ * @param layer Protocol stack layer
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp,
+		  int layer)
+{
+	struct tls_conn *tc;
+	int err;
+
+	if (!ptc || !tls || !tcp)
+		return EINVAL;
+
+	tc = mem_zalloc(sizeof(*tc), destructor);
+	if (!tc)
+		return ENOMEM;
+
+	err = tcp_register_helper(&tc->th, tcp, layer, estab_handler,
+				  send_handler, recv_handler, tc);
+	if (err)
+		goto out;
+
+	tc->tcp = mem_ref(tcp);
+
+#ifdef TLS_BIO_OPAQUE
+	tc->biomet = bio_method_tcp();
+	if (!tc->biomet) {
+		err = ENOMEM;
+		goto out;
+	}
+#endif
+
+	err = ENOMEM;
+
+	/* Connect the SSL socket */
+	tc->ssl = SSL_new(tls->ctx);
+	if (!tc->ssl) {
+		DEBUG_WARNING("alloc: SSL_new() failed (ctx=%p)\n", tls->ctx);
+		ERR_clear_error();
+		goto out;
+	}
+
+	tc->sbio_in = BIO_new(BIO_s_mem());
+	if (!tc->sbio_in) {
+		DEBUG_WARNING("alloc: BIO_new() failed\n");
+		ERR_clear_error();
+		goto out;
+	}
+
+
+#ifdef TLS_BIO_OPAQUE
+	tc->sbio_out = BIO_new(tc->biomet);
+#else
+	tc->sbio_out = BIO_new(&bio_tcp_send);
+#endif
+	if (!tc->sbio_out) {
+		DEBUG_WARNING("alloc: BIO_new_socket() failed\n");
+		ERR_clear_error();
+		BIO_free(tc->sbio_in);
+		goto out;
+	}
+
+#ifdef TLS_BIO_OPAQUE
+	BIO_set_data(tc->sbio_out, tc);
+#else
+	tc->sbio_out->ptr = tc;
+#endif
+
+	SSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out);
+
+	err = 0;
+
+ out:
+	if (err)
+		mem_deref(tc);
+	else
+		*ptc = tc;
+
+	return err;
+}