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/stunsrv.c b/src/ice/stunsrv.c
new file mode 100644
index 0000000..5f73f1d
--- /dev/null
+++ b/src/ice/stunsrv.c
@@ -0,0 +1,324 @@
+/**
+ * @file stunsrv.c Basic STUN Server for Connectivity checks
+ *
+ * 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 <re_sys.h>
+#include "ice.h"
+
+
+#define DEBUG_MODULE "stunsrv"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+static const char *sw = "ice stunsrv v" VERSION " (" ARCH "/" OS ")";
+
+
+static void triggered_check(struct icem *icem, struct ice_cand *lcand,
+ struct ice_cand *rcand)
+{
+ struct ice_candpair *cp = NULL;
+ int err;
+
+ if (lcand && rcand)
+ cp = icem_candpair_find(&icem->checkl, lcand, rcand);
+
+ if (cp) {
+
+ switch (cp->state) {
+
+#if 0
+ /* TODO: I am not sure why we should cancel the
+ * pending Connectivity check here. this
+ * can lead to a deadlock situation where
+ * both agents are stuck on sending
+ * triggered checks on the same candidate pair
+ */
+ case ICE_CANDPAIR_INPROGRESS:
+ icem_candpair_cancel(cp);
+ /*@fallthrough@*/
+#endif
+
+ case ICE_CANDPAIR_FAILED:
+ icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);
+ /*@fallthrough@*/
+
+ case ICE_CANDPAIR_FROZEN:
+ case ICE_CANDPAIR_WAITING:
+ err = icem_conncheck_send(cp, false, true);
+ if (err) {
+ DEBUG_WARNING("triggered check failed\n");
+ }
+ break;
+
+ case ICE_CANDPAIR_SUCCEEDED:
+ default:
+ break;
+ }
+ }
+ else {
+
+#if 0
+ err = icem_candpair_alloc(&cp, icem, lcand, rcand);
+ if (err) {
+ DEBUG_WARNING("failed to allocate candpair:"
+ " lcand=%p rcand=%p (%m)\n",
+ lcand, rcand, err);
+ return;
+ }
+
+ icem_candpair_prio_order(&icem->checkl);
+
+ icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING);
+
+ (void)icem_conncheck_send(cp, false, true);
+#endif
+
+ }
+}
+
+
+/*
+ * 7.2.1. Additional Procedures for Full Implementations
+ */
+static int handle_stun_full(struct icem *icem,
+ struct icem_comp *comp, const struct sa *src,
+ uint32_t prio, bool use_cand, bool tunnel)
+{
+ struct ice_cand *lcand = NULL, *rcand;
+ struct ice_candpair *cp = NULL;
+ int err;
+
+ rcand = icem_cand_find(&icem->rcandl, comp->id, src);
+ if (!rcand) {
+ err = icem_rcand_add_prflx(&rcand, icem, comp->id, prio, src);
+ if (err)
+ return err;
+ }
+
+ cp = icem_candpair_find_rcand(icem, rcand);
+ if (cp)
+ lcand = cp->lcand;
+ else
+ lcand = icem_lcand_find_checklist(icem, comp->id);
+
+ if (!lcand) {
+ DEBUG_WARNING("{%s.%u} local candidate not found"
+ " (checklist=%u) (src=%J)\n",
+ icem->name, comp->id,
+ list_count(&icem->checkl), src);
+ return 0;
+ }
+
+ triggered_check(icem, lcand, rcand);
+
+ if (!cp) {
+ cp = icem_candpair_find_rcand(icem, rcand);
+ if (!cp) {
+ DEBUG_WARNING("{%s.%u} candidate pair not found:"
+ " source=%J\n",
+ icem->name, comp->id, src);
+ return 0;
+ }
+ }
+
+#if ICE_TRACE
+ icecomp_printf(comp, "Rx Binding Request from %J via %s"
+ " (candpair=%s) %s\n",
+ src, tunnel ? "Tunnel" : "Socket",
+ cp ? ice_candpair_state2name(cp->state) : "n/a",
+ use_cand ? "[USE]" : "");
+#else
+ (void)tunnel;
+#endif
+
+ /* 7.2.1.5. Updating the Nominated Flag */
+ if (use_cand) {
+ if (icem->lrole == ICE_ROLE_CONTROLLED &&
+ cp->state == ICE_CANDPAIR_SUCCEEDED) {
+
+ if (!cp->nominated) {
+ icecomp_printf(comp, "setting NOMINATED"
+ " flag on candpair [%H]\n",
+ icem_candpair_debug, cp);
+ }
+
+ cp->nominated = true;
+ }
+
+ /* Cancel conncheck. Choose Selected Pair */
+ icem_candpair_make_valid(cp);
+
+ if (icem->conf.nom == ICE_NOMINATION_REGULAR) {
+ icem_candpair_cancel(cp);
+ icem_comp_set_selected(comp, cp);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * 7.2.2. Additional Procedures for Lite Implementations
+ */
+static int handle_stun_lite(struct icem *icem,
+ struct icem_comp *comp, const struct sa *src,
+ bool use_cand)
+{
+ struct ice_cand *lcand, *rcand;
+ struct ice_candpair *cp;
+ int err;
+
+ if (!use_cand)
+ return 0;
+
+ rcand = icem_cand_find(&icem->rcandl, comp->id, src);
+ if (!rcand) {
+ DEBUG_WARNING("lite: could not find remote candidate\n");
+ return 0;
+ }
+
+ /* find the local host candidate with the same component */
+ lcand = icem_cand_find(&icem->lcandl, comp->id, NULL);
+ if (!lcand) {
+ DEBUG_WARNING("lite: could not find local candidate\n");
+ return 0;
+ }
+
+ /* search validlist for existing candpair's */
+ if (icem_candpair_find(&icem->validl, lcand, rcand))
+ return 0;
+
+ err = icem_candpair_alloc(&cp, icem, lcand, rcand);
+ if (err) {
+ DEBUG_WARNING("lite: failed to created candidate pair\n");
+ return err;
+ }
+
+ icem_candpair_make_valid(cp);
+ cp->nominated = true;
+
+ return 0;
+}
+
+
+static int stunsrv_ereply(struct icem_comp *comp, const struct sa *src,
+ size_t presz, const struct stun_msg *req,
+ uint16_t scode, const char *reason)
+{
+ struct icem *icem = comp->icem;
+
+ return stun_ereply(icem->proto, comp->sock, src, presz, req,
+ scode, reason,
+ (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 1,
+ STUN_ATTR_SOFTWARE, sw);
+}
+
+
+int icem_stund_recv(struct icem_comp *comp, const struct sa *src,
+ struct stun_msg *req, size_t presz)
+{
+ struct icem *icem = comp->icem;
+ struct stun_attr *attr;
+ struct pl lu, ru;
+ enum ice_role rrole = ICE_ROLE_UNKNOWN;
+ uint64_t tiebrk = 0;
+ uint32_t prio_prflx;
+ bool use_cand = false;
+ int err;
+
+ /* RFC 5389: Fingerprint errors are silently discarded */
+ err = stun_msg_chk_fingerprint(req);
+ if (err)
+ return err;
+
+ err = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd));
+ if (err) {
+ if (err == EBADMSG)
+ goto unauth;
+ else
+ goto badmsg;
+ }
+
+ attr = stun_msg_attr(req, STUN_ATTR_USERNAME);
+ if (!attr)
+ goto badmsg;
+
+ err = re_regex(attr->v.username, strlen(attr->v.username),
+ "[^:]+:[^]+", &lu, &ru);
+ if (err) {
+ DEBUG_WARNING("could not parse USERNAME attribute (%s)\n",
+ attr->v.username);
+ goto unauth;
+ }
+ if (pl_strcmp(&lu, icem->lufrag))
+ goto unauth;
+ if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag))
+ goto unauth;
+
+ attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED);
+ if (attr) {
+ rrole = ICE_ROLE_CONTROLLED;
+ tiebrk = attr->v.uint64;
+ }
+
+ attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING);
+ if (attr) {
+ rrole = ICE_ROLE_CONTROLLING;
+ tiebrk = attr->v.uint64;
+ }
+
+ if (rrole == icem->lrole) {
+ if (icem->tiebrk >= tiebrk)
+ ice_switch_local_role(icem);
+ else
+ goto conflict;
+ }
+
+ attr = stun_msg_attr(req, STUN_ATTR_PRIORITY);
+ if (attr)
+ prio_prflx = attr->v.uint32;
+ else
+ goto badmsg;
+
+ attr = stun_msg_attr(req, STUN_ATTR_USE_CAND);
+ if (attr)
+ use_cand = true;
+
+ if (icem->lmode == ICE_MODE_FULL) {
+ err = handle_stun_full(icem, comp, src, prio_prflx,
+ use_cand, presz > 0);
+ }
+ else {
+ err = handle_stun_lite(icem, comp, src, use_cand);
+ }
+
+ if (err)
+ goto badmsg;
+
+ return stun_reply(icem->proto, comp->sock, src, presz, req,
+ (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2,
+ STUN_ATTR_XOR_MAPPED_ADDR, src,
+ STUN_ATTR_SOFTWARE, sw);
+
+ badmsg:
+ return stunsrv_ereply(comp, src, presz, req, 400, "Bad Request");
+
+ unauth:
+ return stunsrv_ereply(comp, src, presz, req, 401, "Unauthorized");
+
+ conflict:
+ return stunsrv_ereply(comp, src, presz, req, 487, "Role Conflict");
+}