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/stun/ctrans.c b/src/stun/ctrans.c
new file mode 100644
index 0000000..e164b9d
--- /dev/null
+++ b/src/stun/ctrans.c
@@ -0,0 +1,386 @@
+/**
+ * @file stun/ctrans.c  STUN Client transactions
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_udp.h>
+#include <re_tcp.h>
+#include <re_srtp.h>
+#include <re_tls.h>
+#include <re_list.h>
+#include <re_tmr.h>
+#include <re_md5.h>
+#include <re_stun.h>
+#include "stun.h"
+
+
+struct stun_ctrans {
+	struct le le;
+	struct tmr tmr;
+	struct sa dst;
+	uint8_t tid[STUN_TID_SIZE];
+	struct stun_ctrans **ctp;
+	uint8_t *key;
+	size_t keylen;
+	void *sock;
+	struct mbuf *mb;
+	size_t pos;
+	struct stun *stun;
+	stun_resp_h *resph;
+	void *arg;
+	int proto;
+	uint32_t txc;
+	uint32_t ival;
+	uint16_t met;
+};
+
+
+static void completed(struct stun_ctrans *ct, int err, uint16_t scode,
+		      const char *reason, const struct stun_msg *msg)
+{
+	stun_resp_h *resph = ct->resph;
+	void *arg = ct->arg;
+
+	list_unlink(&ct->le);
+	tmr_cancel(&ct->tmr);
+
+	if (ct->ctp) {
+		*ct->ctp = NULL;
+		ct->ctp = NULL;
+	}
+
+	ct->resph = NULL;
+
+	/* must be destroyed before calling handler */
+	mem_deref(ct);
+
+	if (resph)
+		resph(err, scode, reason, msg, arg);
+}
+
+
+static void destructor(void *arg)
+{
+	struct stun_ctrans *ct = arg;
+
+	list_unlink(&ct->le);
+	tmr_cancel(&ct->tmr);
+	mem_deref(ct->key);
+	mem_deref(ct->sock);
+	mem_deref(ct->mb);
+}
+
+
+static void timeout_handler(void *arg)
+{
+	struct stun_ctrans *ct = arg;
+	const struct stun_conf *cfg = stun_conf(ct->stun);
+	int err = ETIMEDOUT;
+
+	if (ct->txc++ >= cfg->rc)
+		goto error;
+
+	ct->mb->pos = ct->pos;
+
+	err = stun_send(ct->proto, ct->sock, &ct->dst, ct->mb);
+	if (err)
+		goto error;
+
+	ct->ival = (ct->txc >= cfg->rc) ? cfg->rto * cfg->rm : ct->ival * 2;
+
+	tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
+	return;
+
+ error:
+	completed(ct, err, 0, NULL, NULL);
+}
+
+
+static bool match_handler(struct le *le, void *arg)
+{
+	struct stun_ctrans *ct = le->data;
+	struct stun_msg *msg = arg;
+
+	if (ct->met != stun_msg_method(msg))
+		return false;
+
+	if (memcmp(ct->tid, stun_msg_tid(msg), STUN_TID_SIZE))
+		return false;
+
+	return true;
+}
+
+
+static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)
+{
+	struct stun *stun = arg;
+	(void)src;
+
+	(void)stun_recv(stun, mb);
+}
+
+
+static void tcp_recv_handler(struct mbuf *mb, void *arg)
+{
+	struct stun_ctrans *ct = arg;
+
+	(void)stun_recv(ct->stun, mb);
+}
+
+
+static void tcp_estab_handler(void *arg)
+{
+	struct stun_ctrans *ct = arg;
+	int err;
+
+	err = tcp_send(ct->sock, ct->mb);
+	if (!err)
+		return;
+
+	completed(ct, err, 0, NULL, NULL);
+}
+
+
+static void tcp_close_handler(int err, void *arg)
+{
+	struct stun_ctrans *ct = arg;
+
+	completed(ct, err, 0, NULL, NULL);
+}
+
+
+/**
+ * Handle an incoming STUN message to a Client Transaction
+ *
+ * @param stun STUN instance
+ * @param msg  STUN message
+ * @param ua   Unknown attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int stun_ctrans_recv(struct stun *stun, const struct stun_msg *msg,
+		     const struct stun_unknown_attr *ua)
+{
+	struct stun_errcode ec = {0, "OK"};
+	struct stun_attr *errcode;
+	struct stun_ctrans *ct;
+	int err = 0, herr = 0;
+
+	if (!stun || !msg || !ua)
+		return EINVAL;
+
+	switch (stun_msg_class(msg)) {
+
+	case STUN_CLASS_ERROR_RESP:
+		errcode = stun_msg_attr(msg, STUN_ATTR_ERR_CODE);
+		if (!errcode)
+			herr = EPROTO;
+		else
+			ec = errcode->v.err_code;
+		/*@fallthrough@*/
+
+	case STUN_CLASS_SUCCESS_RESP:
+		ct = list_ledata(list_apply(&stun->ctl, true,
+					    match_handler, (void *)msg));
+		if (!ct) {
+			err = ENOENT;
+			break;
+		}
+
+		switch (ec.code) {
+
+		case 401:
+		case 438:
+			break;
+
+		default:
+			if (!ct->key)
+				break;
+
+			err = stun_msg_chk_mi(msg, ct->key, ct->keylen);
+			break;
+		}
+
+		if (err)
+			break;
+
+		if (!herr && ua->typec > 0)
+			herr = EPROTO;
+
+		completed(ct, herr, ec.code, ec.reason, msg);
+		break;
+
+	default:
+		break;
+	}
+
+	return err;
+}
+
+
+int stun_ctrans_request(struct stun_ctrans **ctp, struct stun *stun, int proto,
+			void *sock, const struct sa *dst, struct mbuf *mb,
+			const uint8_t tid[], uint16_t met, const uint8_t *key,
+			size_t keylen, stun_resp_h *resph, void *arg)
+{
+	struct stun_ctrans *ct;
+	int err = 0;
+
+	if (!stun || !mb)
+		return EINVAL;
+
+	ct = mem_zalloc(sizeof(*ct), destructor);
+	if (!ct)
+		return ENOMEM;
+
+	list_append(&stun->ctl, &ct->le, ct);
+	memcpy(ct->tid, tid, STUN_TID_SIZE);
+	ct->proto = proto;
+	ct->sock  = mem_ref(sock);
+	ct->mb    = mem_ref(mb);
+	ct->pos   = mb->pos;
+	ct->stun  = stun;
+	ct->met   = met;
+
+	if (key) {
+		ct->key = mem_alloc(keylen, NULL);
+		if (!ct->key) {
+			err = ENOMEM;
+			goto out;
+		}
+
+		memcpy(ct->key, key, keylen);
+		ct->keylen = keylen;
+	}
+
+	switch (proto) {
+
+	case IPPROTO_UDP:
+		if (!dst) {
+			err = EINVAL;
+			break;
+		}
+
+		ct->dst = *dst;
+		ct->ival = stun_conf(stun)->rto;
+		tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
+
+		if (!sock) {
+			err = udp_listen((struct udp_sock **)&ct->sock, NULL,
+					 udp_recv_handler, stun);
+			if (err)
+				break;
+		}
+
+		ct->txc = 1;
+		err = udp_send(ct->sock, dst, mb);
+		break;
+
+	case IPPROTO_TCP:
+		ct->txc = stun_conf(stun)->rc;
+		tmr_start(&ct->tmr, stun_conf(stun)->ti, timeout_handler, ct);
+		if (sock) {
+			err = tcp_send(sock, mb);
+			break;
+		}
+
+		err = tcp_connect((struct tcp_conn **)&ct->sock, dst,
+				  tcp_estab_handler, tcp_recv_handler,
+				  tcp_close_handler, ct);
+		break;
+
+#ifdef USE_DTLS
+	case STUN_TRANSP_DTLS:
+		if (!sock) {
+			err = EINVAL;
+			break;
+		}
+
+		ct->ival = stun_conf(stun)->rto;
+		tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
+
+		ct->txc = 1;
+		err = dtls_send(ct->sock, mb);
+		break;
+#endif
+
+	default:
+		err = EPROTONOSUPPORT;
+		break;
+	}
+
+ out:
+	if (!err) {
+		if (ctp) {
+			ct->ctp = ctp;
+			*ctp = ct;
+		}
+
+		ct->resph = resph;
+		ct->arg   = arg;
+	}
+	else
+		mem_deref(ct);
+
+	return err;
+}
+
+
+static bool close_handler(struct le *le, void *arg)
+{
+	struct stun_ctrans *ct = le->data;
+	(void)arg;
+
+	completed(ct, ECONNABORTED, 0, NULL, NULL);
+
+	return false;
+}
+
+
+void stun_ctrans_close(struct stun *stun)
+{
+	if (!stun)
+		return;
+
+	(void)list_apply(&stun->ctl, true, close_handler, NULL);
+}
+
+
+static bool debug_handler(struct le *le, void *arg)
+{
+	struct stun_ctrans *ct = le->data;
+	struct re_printf *pf = arg;
+	int err = 0;
+
+	err |= re_hprintf(pf, " method=%s", stun_method_name(ct->met));
+	err |= re_hprintf(pf, " tid=%w", ct->tid, sizeof(ct->tid));
+	err |= re_hprintf(pf, " rto=%ums", stun_conf(ct->stun)->rto);
+	err |= re_hprintf(pf, " tmr=%llu", tmr_get_expire(&ct->tmr));
+	err |= re_hprintf(pf, " n=%u", ct->txc);
+	err |= re_hprintf(pf, " interval=%u", ct->ival);
+	err |= re_hprintf(pf, "\n");
+
+	return 0 != err;
+}
+
+
+int stun_ctrans_debug(struct re_printf *pf, const struct stun *stun)
+{
+	int err;
+
+	if (!stun)
+		return 0;
+
+	err = re_hprintf(pf, "STUN client transactions: (%u)\n",
+			 list_count(&stun->ctl));
+
+	(void)list_apply(&stun->ctl, true, debug_handler, pf);
+
+	return err;
+}