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/ice/chklist.c b/src/ice/chklist.c
new file mode 100644
index 0000000..24e4f34
--- /dev/null
+++ b/src/ice/chklist.c
@@ -0,0 +1,341 @@
+/**
+ * @file chklist.c  ICE Checklist
+ *
+ * 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_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_stun.h>
+#include <re_ice.h>
+#include "ice.h"
+
+
+#define DEBUG_MODULE "chklist"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/**
+ * Forming Candidate Pairs
+ */
+static int candpairs_form(struct icem *icem)
+{
+	struct le *le;
+	int err = 0;
+
+	if (list_isempty(&icem->lcandl))
+		return ENOENT;
+
+	if (list_isempty(&icem->rcandl)) {
+		DEBUG_WARNING("%s: no remote candidates\n", icem->name);
+		return ENOENT;
+	}
+
+	for (le = icem->lcandl.head; le; le = le->next) {
+
+		struct ice_cand *lcand = le->data;
+		struct le *rle;
+
+		for (rle = icem->rcandl.head; rle; rle = rle->next) {
+
+			struct ice_cand *rcand = rle->data;
+
+			if (lcand->compid != rcand->compid)
+				continue;
+
+			if (sa_af(&lcand->addr) != sa_af(&rcand->addr))
+				continue;
+
+			err = icem_candpair_alloc(NULL, icem, lcand, rcand);
+			if (err)
+				return err;
+		}
+	}
+
+	return err;
+}
+
+
+/* Replace server reflexive candidates by its base */
+static const struct sa *cand_srflx_addr(const struct ice_cand *c)
+{
+	return (ICE_CAND_TYPE_SRFLX == c->type) ? &c->base->addr : &c->addr;
+}
+
+
+/* return: NULL to keep, pointer to remove object */
+static void *unique_handler(struct le *le1, struct le *le2)
+{
+	struct ice_candpair *cp1 = le1->data, *cp2 = le2->data;
+
+	if (cp1->comp->id != cp2->comp->id)
+		return NULL;
+
+	if (!sa_cmp(cand_srflx_addr(cp1->lcand),
+		    cand_srflx_addr(cp2->lcand), SA_ALL) ||
+	    !sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL))
+		return NULL;
+
+	return cp1->pprio < cp2->pprio ? cp1 : cp2;
+}
+
+
+/**
+ * Pruning the Pairs
+ */
+static void candpair_prune(struct icem *icem)
+{
+	/* The agent MUST prune the list.
+	   This is done by removing a pair if its local and remote
+	   candidates are identical to the local and remote candidates
+	   of a pair higher up on the priority list.
+
+	   NOTE: This logic assumes the list is sorted by priority
+	*/
+
+	uint32_t n = ice_list_unique(&icem->checkl, unique_handler);
+	if (n > 0) {
+		DEBUG_NOTICE("%s: pruned candidate pairs: %u\n",
+			     icem->name, n);
+	}
+}
+
+
+/**
+ * Computing States
+ *
+ * @param icem    ICE Media object
+ */
+void ice_candpair_set_states(struct icem *icem)
+{
+	struct le *le, *le2;
+
+	/*
+	For all pairs with the same foundation, it sets the state of
+	the pair with the lowest component ID to Waiting.  If there is
+	more than one such pair, the one with the highest priority is
+	used.
+	*/
+
+	for (le = icem->checkl.head; le; le = le->next) {
+
+		struct ice_candpair *cp = le->data;
+
+		for (le2 = icem->checkl.head; le2; le2 = le2->next) {
+
+			struct ice_candpair *cp2 = le2->data;
+
+			if (!icem_candpair_cmp_fnd(cp, cp2))
+				continue;
+
+			if (cp2->lcand->compid < cp->lcand->compid &&
+			    cp2->pprio > cp->pprio)
+				cp = cp2;
+		}
+
+		icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);
+	}
+}
+
+
+/**
+ * Forming the Check Lists
+ *
+ *   To form the check list for a media stream,
+ *   the agent forms candidate pairs, computes a candidate pair priority,
+ *   orders the pairs by priority, prunes them, and sets their states.
+ *   These steps are described in this section.
+ *
+ * @param icem ICE Media object
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int icem_checklist_form(struct icem *icem)
+{
+	int err;
+
+	if (!icem)
+		return EINVAL;
+
+	if (ICE_MODE_LITE == icem->lmode) {
+		DEBUG_WARNING("%s: Checklist: only valid for full-mode\n",
+			      icem->name);
+		return EINVAL;
+	}
+
+	if (!list_isempty(&icem->checkl))
+		return EALREADY;
+
+	/* 1. form candidate pairs */
+	err = candpairs_form(icem);
+	if (err)
+		return err;
+
+	/* 2. compute a candidate pair priority */
+	/* 3. order the pairs by priority */
+	icem_candpair_prio_order(&icem->checkl);
+
+	/* 4. prune the pairs */
+	candpair_prune(icem);
+
+	return err;
+}
+
+
+/* If all of the pairs in the check list are now either in the Failed or
+   Succeeded state:
+ */
+static bool iscompleted(const struct icem *icem)
+{
+	struct le *le;
+
+	for (le = icem->checkl.head; le; le = le->next) {
+
+		const struct ice_candpair *cp = le->data;
+
+		if (!icem_candpair_iscompleted(cp))
+			return false;
+	}
+
+	return true;
+}
+
+
+/* 8.  Concluding ICE Processing */
+static void concluding_ice(struct icem_comp *comp)
+{
+	struct ice_candpair *cp;
+
+	if (!comp || comp->concluded)
+		return;
+
+	/* pick the best candidate pair, highest priority */
+	cp = icem_candpair_find_st(&comp->icem->validl, comp->id,
+				   ICE_CANDPAIR_SUCCEEDED);
+	if (!cp) {
+		DEBUG_WARNING("{%s.%u} conclude: no valid candpair found"
+			      " (validlist=%u)\n",
+			      comp->icem->name, comp->id,
+			      list_count(&comp->icem->validl));
+		return;
+	}
+
+	icem_comp_set_selected(comp, cp);
+
+	if (comp->icem->conf.nom == ICE_NOMINATION_REGULAR) {
+
+		/* send STUN request with USE_CAND flag via triggered qeueue */
+		(void)icem_conncheck_send(cp, true, true);
+		icem_conncheck_schedule_check(comp->icem);
+	}
+
+	comp->concluded = true;
+}
+
+
+/**
+ * Check List and Timer State Updates
+ *
+ * @param icem    ICE Media object
+ */
+void icem_checklist_update(struct icem *icem)
+{
+	struct le *le;
+	bool compl;
+	int err = 0;
+
+	compl = iscompleted(icem);
+	if (!compl)
+		return;
+
+	/*
+	 * If there is not a pair in the valid list for each component of the
+	 * media stream, the state of the check list is set to Failed.
+	 */
+	for (le = icem->compl.head; le; le = le->next) {
+
+		struct icem_comp *comp = le->data;
+
+		if (!icem_candpair_find_compid(&icem->validl, comp->id)) {
+			DEBUG_WARNING("{%s.%u} no valid candidate pair"
+				      " (validlist=%u)\n",
+				      icem->name, comp->id,
+				      list_count(&icem->validl));
+			err = ENOENT;
+			break;
+		}
+
+		concluding_ice(comp);
+
+		if (!comp->cp_sel)
+			continue;
+
+		icem_comp_keepalive(comp, true);
+	}
+
+	icem->state = err ? ICE_CHECKLIST_FAILED : ICE_CHECKLIST_COMPLETED;
+
+	if (icem->chkh) {
+		icem->chkh(err, icem->lrole == ICE_ROLE_CONTROLLING,
+			   icem->arg);
+	}
+}
+
+
+/**
+ * Get the Local address of the Selected Candidate pair, if available
+ *
+ * @param icem   ICE Media object
+ * @param compid Component ID
+ *
+ * @return Local address if available, otherwise NULL
+ */
+const struct sa *icem_selected_laddr(const struct icem *icem, unsigned compid)
+{
+	const struct ice_cand *cand = icem_selected_lcand(icem, compid);
+	return icem_lcand_addr(cand);
+}
+
+
+/**
+ * Get the Local candidate of the Selected Candidate pair, if available
+ *
+ * @param icem   ICE Media object
+ * @param compid Component ID
+ *
+ * @return Local candidate if available, otherwise NULL
+ */
+const struct ice_cand *icem_selected_lcand(const struct icem *icem,
+		unsigned compid)
+{
+	const struct icem_comp *comp = icem_comp_find(icem, compid);
+	if (!comp || !comp->cp_sel)
+		return NULL;
+
+	return comp->cp_sel->lcand;
+}
+
+
+/**
+ * Get the Remote candidate of the Selected Candidate pair, if available
+ *
+ * @param icem   ICE Media object
+ * @param compid Component ID
+ *
+ * @return Remote candidate if available, otherwise NULL
+ */
+const struct ice_cand *icem_selected_rcand(const struct icem *icem,
+		unsigned compid)
+{
+	const struct icem_comp *comp = icem_comp_find(icem, compid);
+	if (!comp || !comp->cp_sel)
+		return NULL;
+
+	return comp->cp_sel->rcand;
+}