Squashed 'third_party/rawrtc/rew/' content from commit 24c91fd83
Change-Id: Ica2fcc790472ecd5b195d20da982c4e84139cbdd
git-subtree-dir: third_party/rawrtc/rew
git-subtree-split: 24c91fd839b40b11f727c902fa46d20874da33fb
diff --git a/src/trice/lcand.c b/src/trice/lcand.c
new file mode 100644
index 0000000..a5ab080
--- /dev/null
+++ b/src/trice/lcand.c
@@ -0,0 +1,625 @@
+/**
+ * @file lcand.c Local ICE Candidates
+ *
+ * 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_net.h>
+#include <re_sys.h>
+#include <re_stun.h>
+#include <re_udp.h>
+#include <re_tcp.h>
+#include <re_ice.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "icelcand"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+static bool tcpconn_frame_handler(struct trice *icem,
+ struct tcp_conn *tc, struct sa *src,
+ struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+ (void)icem;
+
+ return lcand->recvh(lcand, IPPROTO_TCP, tc,
+ src, mb, lcand->arg);
+}
+
+
+static void tcp_conn_handler(const struct sa *peer, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+ int err;
+
+#if 0
+ trice_printf(lcand->icem,
+ "[local=%H] incoming TCP-connect from %J\n",
+ trice_cand_print, lcand, peer);
+#endif
+
+ err = trice_conn_alloc(&lcand->icem->connl, lcand->icem,
+ lcand->attr.compid, false,
+ &lcand->attr.addr, peer, lcand->ts, lcand->layer,
+ tcpconn_frame_handler, lcand);
+ if (err) {
+ DEBUG_WARNING("ice_conn_alloc error (%m)\n", err);
+ }
+}
+
+
+static void lcand_destructor(void *arg)
+{
+ struct ice_lcand *cand = arg;
+
+ list_unlink(&cand->le);
+
+ mem_deref(cand->ts);
+ mem_deref(cand->uh);
+ mem_deref(cand->us);
+}
+
+
+/** Foundation is a hash of IP address and candidate type */
+static int compute_foundation(struct ice_lcand *cand,
+ const struct sa *addr, enum ice_cand_type type)
+{
+ uint32_t v;
+
+ v = sa_hash(addr, SA_ADDR);
+ v ^= type;
+
+ if (re_snprintf(cand->attr.foundation, sizeof(cand->attr.foundation),
+ "%08x", v) < 0)
+ return ENOMEM;
+
+ return 0;
+}
+
+
+static bool trice_lcand_recv_handler(struct ice_lcand *lcand,
+ int proto, void *sock, const struct sa *src,
+ struct mbuf *mb, void *arg)
+{
+ struct trice *icem = arg;
+
+ return trice_stun_process(icem, lcand, proto, sock, src, mb);
+}
+
+
+int trice_add_lcandidate(struct ice_lcand **candp,
+ struct trice *icem, struct list *lst,
+ unsigned compid, char *foundation, int proto,
+ uint32_t prio, const struct sa *addr,
+ const struct sa *base_addr,
+ enum ice_cand_type type,
+ const struct sa *rel_addr,
+ enum ice_tcptype tcptype)
+{
+ struct ice_lcand *cand;
+ int err = 0;
+
+ if (!lst || !compid || !proto || !addr)
+ return EINVAL;
+
+ cand = mem_zalloc(sizeof(*cand), lcand_destructor);
+ if (!cand)
+ return ENOMEM;
+
+ cand->attr.compid = compid;
+ if (foundation)
+ str_ncpy(cand->attr.foundation, foundation,
+ sizeof(cand->attr.foundation));
+ else
+ err = compute_foundation(cand, addr, type);
+ cand->attr.proto = proto;
+ cand->attr.prio = prio;
+ cand->attr.addr = *addr;
+ cand->attr.type = type;
+ cand->attr.tcptype = tcptype;
+ if (rel_addr)
+ cand->attr.rel_addr = *rel_addr;
+ if (err)
+ goto out;
+
+ cand->icem = icem;
+
+ cand->recvh = trice_lcand_recv_handler;
+ cand->arg = icem;
+
+ if (base_addr)
+ cand->base_addr = *base_addr;
+
+ list_append(lst, &cand->le, cand);
+
+ out:
+ if (err)
+ mem_deref(cand);
+ else if (candp)
+ *candp = cand;
+
+ return err;
+}
+
+
+/* this one is only for Send statistics on Local Candidate */
+static bool udp_helper_send_handler(int *err, struct sa *dst,
+ struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+ (void)err;
+ (void)dst;
+ (void)mb;
+
+ lcand->stats.n_tx += 1;
+
+ return false; /* NOT handled */
+}
+
+
+/*
+ * lcand: on which Local Candidate to receive the packet
+ *
+ * return TRUE if handled
+ */
+static bool udp_helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+
+ lcand->stats.n_rx += 1;
+
+ return lcand->recvh(lcand, IPPROTO_UDP, lcand->us,
+ src, mb, lcand->arg);
+}
+
+
+/* The incoming data should not get here */
+static void dummy_udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+
+ DEBUG_NOTICE("@@@@ NO-ONE cared about this UDP packet? @@@@@"
+ " (%zu bytes from %J to %s.%J)\n",
+ mbuf_get_left(mb), src,
+ ice_cand_type2name(lcand->attr.type),
+ &lcand->attr.addr);
+}
+
+static int udp_listen_range(struct udp_sock **usp, const struct sa *ip,
+ uint16_t min_port, uint16_t max_port,
+ udp_recv_h *rh, void *arg)
+{
+ struct sa laddr;
+ int tries = 64;
+ int err = 0;
+
+ sa_cpy(&laddr, ip);
+
+ /* try hard */
+ while (tries--) {
+ struct udp_sock *us;
+ uint16_t port;
+
+ port = (min_port + (rand_u16() % (max_port - min_port)));
+
+ sa_set_port(&laddr, port);
+ err = udp_listen(&us, &laddr, rh, arg);
+ if (err)
+ continue;
+
+ /* OK */
+ if (usp)
+ *usp = us;
+ break;
+ }
+
+ return err;
+}
+
+
+/*
+ * you can call this at any time
+ *
+ * @param addr HOST: SA_ADDR portion is used
+ * non-HOST: SA_ADDR + SA_PORT portion is used
+ *
+ * @param base_addr Optional
+ * @param rel_addr Optional
+ *
+ * @param layer mandatory for HOST and RELAY candidates
+ */
+int trice_lcand_add(struct ice_lcand **lcandp, struct trice *icem,
+ unsigned compid, int proto,
+ uint32_t prio, const struct sa *addr,
+ const struct sa *base_addr,
+ enum ice_cand_type type, const struct sa *rel_addr,
+ enum ice_tcptype tcptype,
+ void *sock, int layer)
+{
+ struct ice_lcand *lcand;
+ int err = 0;
+
+ if (!icem || !compid || !proto || !addr)
+ return EINVAL;
+
+ if (!sa_isset(addr, SA_ADDR)) {
+ DEBUG_WARNING("lcand_add: SA_ADDR is not set\n");
+ return EINVAL;
+ }
+ if (type != ICE_CAND_TYPE_HOST) {
+ if (!sa_isset(addr, SA_PORT)) {
+ DEBUG_WARNING("lcand_add: %s: SA_PORT"
+ " must be set (%J)\n",
+ ice_cand_type2name(type), addr);
+ return EINVAL;
+ }
+ }
+
+ /* lookup candidate, replace if PRIO is higher */
+
+ /* TODO: dont look up TCP-ACTIVE types for now (port is zero) */
+ if (proto == IPPROTO_UDP) {
+
+ lcand = trice_lcand_find(icem, -1, compid, proto, addr);
+ if (lcand) {
+ trice_printf(icem,
+ "add_local[%s.%J] --"
+ " candidate already exists"
+ " (%H)\n",
+ ice_cand_type2name(type), addr,
+ trice_cand_print, lcand);
+
+ if (prio > lcand->attr.prio)
+ lcand = mem_deref(lcand);
+ else {
+ goto out;
+ }
+ }
+ }
+
+ err = trice_add_lcandidate(&lcand, icem, &icem->lcandl, compid, NULL,
+ proto, prio, addr, base_addr,
+ type, rel_addr, tcptype);
+ if (err)
+ return err;
+
+ if (type == ICE_CAND_TYPE_HOST) {
+
+ switch (proto) {
+
+ case IPPROTO_UDP:
+ if (sock) {
+ struct sa laddr;
+
+ lcand->us = mem_ref(sock);
+
+ err = udp_local_get(lcand->us,
+ &laddr);
+ if (err)
+ goto out;
+
+ lcand->attr.addr = *addr;
+ sa_set_port(&lcand->attr.addr,
+ sa_port(&laddr));
+ }
+ else {
+ if (icem->ports.min && icem->ports.max) {
+ err = udp_listen_range(
+ &lcand->us,
+ addr,
+ icem->ports.min,
+ icem->ports.max,
+ dummy_udp_recv,
+ lcand);
+ }
+ else {
+ err = udp_listen(&lcand->us, addr,
+ dummy_udp_recv,
+ lcand);
+ }
+ if (err)
+ goto out;
+
+ err = udp_local_get(lcand->us,
+ &lcand->attr.addr);
+ if (err)
+ goto out;
+ }
+
+ err = udp_register_helper(&lcand->uh, lcand->us,
+ layer,
+ udp_helper_send_handler,
+ udp_helper_recv_handler,
+ lcand);
+ if (err)
+ goto out;
+ break;
+
+ case IPPROTO_TCP:
+
+ /* TCP-transport has 3 variants:
+ active, passive, so */
+
+ if (lcand->attr.tcptype == ICE_TCP_ACTIVE) {
+
+ /* the port MUST be set to 9 (i.e., Discard) */
+ /*sa_set_port(&lcand->attr.addr, 9); */
+ }
+ else if (lcand->attr.tcptype == ICE_TCP_PASSIVE ||
+ lcand->attr.tcptype == ICE_TCP_SO) {
+
+ err = tcp_listen(&lcand->ts, addr,
+ tcp_conn_handler, lcand);
+ if (err)
+ goto out;
+ err = tcp_local_get(lcand->ts,
+ &lcand->attr.addr);
+ if (err)
+ goto out;
+ }
+ else {
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+ }
+ else if (type == ICE_CAND_TYPE_RELAY) {
+
+ switch (proto) {
+
+ case IPPROTO_UDP:
+
+ if (sock) {
+ lcand->us = mem_ref(sock);
+ }
+ else {
+ err = udp_listen(&lcand->us, NULL,
+ dummy_udp_recv, lcand);
+ if (err)
+ goto out;
+ }
+
+ err = udp_register_helper(&lcand->uh, lcand->us,
+ layer,
+ udp_helper_send_handler,
+ udp_helper_recv_handler,
+ lcand);
+ if (err)
+ goto out;
+
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+ }
+ else if (type == ICE_CAND_TYPE_SRFLX ||
+ type == ICE_CAND_TYPE_PRFLX) {
+
+ /* Special case for SRFLX UDP candidates, if he has
+ * its own UDP-socket that can be used.
+ */
+ if (proto == IPPROTO_UDP && sock) {
+
+ lcand->us = mem_ref(sock);
+
+ err = udp_register_helper(&lcand->uh, lcand->us,
+ layer,
+ udp_helper_send_handler,
+ udp_helper_recv_handler,
+ lcand);
+ if (err)
+ goto out;
+ }
+ }
+
+ lcand->layer = layer;
+
+ if (icem->lrole != ICE_ROLE_UNKNOWN) {
+ /* pair this local-candidate with all existing
+ * remote-candidates */
+ err = trice_candpair_with_local(icem, lcand);
+ if (err)
+ goto out;
+
+ /* new pair -- refresh the checklist timer */
+ trice_checklist_refresh(icem);
+ }
+
+ out:
+ if (err)
+ mem_deref(lcand);
+ else if (lcandp)
+ *lcandp = lcand;
+
+ return err;
+}
+
+
+struct ice_lcand *trice_lcand_find(struct trice *icem,
+ enum ice_cand_type type,
+ unsigned compid, int proto,
+ const struct sa *addr)
+{
+ struct list *lst;
+ struct le *le;
+
+ if (!icem)
+ return NULL;
+
+ if (!proto) {
+ DEBUG_WARNING("find_candidate: invalid args\n");
+ return NULL;
+ }
+
+ lst = &icem->lcandl;
+
+ for (le = list_head(lst); le; le = le->next) {
+
+ struct ice_cand_attr *cand = le->data;
+
+ if (type != (enum ice_cand_type)-1 && type != cand->type)
+ continue;
+
+ if (compid && cand->compid != compid)
+ continue;
+
+ if (cand->proto != proto)
+ continue;
+
+ if (addr && !sa_cmp(&cand->addr, addr, SA_ALL))
+ continue;
+
+ return (void *)cand;
+ }
+
+ return NULL;
+}
+
+
+struct ice_lcand *trice_lcand_find2(const struct trice *icem,
+ enum ice_cand_type type, int af)
+{
+ struct le *le;
+
+ if (!icem)
+ return NULL;
+
+ for (le = list_head(&icem->lcandl); le; le = le->next) {
+
+ struct ice_cand_attr *cand = le->data;
+
+ if (cand->type != type)
+ continue;
+
+ if (af != sa_af(&cand->addr))
+ continue;
+
+ return (void *)cand;
+ }
+
+ return NULL;
+}
+
+
+int trice_lcands_debug(struct re_printf *pf, const struct list *lst)
+{
+ struct le *le;
+ int err;
+
+ err = re_hprintf(pf, " (%u)\n", list_count(lst));
+
+ for (le = list_head(lst); le && !err; le = le->next) {
+
+ const struct ice_lcand *cand = le->data;
+
+ err |= re_hprintf(pf, " {%u} [tx=%3zu, rx=%3zu] "
+ "fnd=%-8s prio=%08x ",
+ cand->attr.compid,
+ cand->stats.n_tx,
+ cand->stats.n_rx,
+ cand->attr.foundation,
+ cand->attr.prio);
+
+ if (str_isset(cand->ifname))
+ err |= re_hprintf(pf, "%s:", cand->ifname);
+
+ err |= re_hprintf(pf, "%24H", trice_cand_print, cand);
+
+ if (sa_isset(&cand->base_addr, SA_ADDR)) {
+ err |= re_hprintf(pf, " (base-addr = %J)",
+ &cand->base_addr);
+ }
+
+ if (sa_isset(&cand->attr.rel_addr, SA_ADDR)) {
+ err |= re_hprintf(pf, " (rel-addr = %J)",
+ &cand->attr.rel_addr);
+ }
+
+ err |= re_hprintf(pf, "\n");
+ }
+
+ return err;
+}
+
+
+void *trice_lcand_sock(struct trice *icem, const struct ice_lcand *lcand)
+{
+ struct ice_lcand *base = NULL;
+
+ if (!icem || !lcand)
+ return NULL;
+
+ if (sa_isset(&lcand->base_addr, SA_ALL)) {
+
+ enum ice_cand_type base_type;
+
+ base_type = ice_cand_type_base(lcand->attr.type);
+
+ base = trice_lcand_find(icem,
+ base_type,
+ lcand->attr.compid,
+ lcand->attr.proto,
+ &lcand->base_addr);
+ }
+
+ /* note: original lcand has presedence, fallback to base-candidate */
+ switch (lcand->attr.type) {
+
+ case ICE_CAND_TYPE_HOST:
+ return lcand->us;
+
+ case ICE_CAND_TYPE_SRFLX:
+ case ICE_CAND_TYPE_PRFLX:
+ if (lcand->us)
+ return lcand->us;
+ else if (base && base->us)
+ return base->us;
+ else {
+ DEBUG_NOTICE("lcand_sock: no SOCK or BASE for "
+ " type '%s'\n",
+ ice_cand_type2name(lcand->attr.type));
+ return NULL;
+ }
+ break;
+
+ case ICE_CAND_TYPE_RELAY:
+ return lcand->us;
+
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+void trice_lcand_recv_packet(struct ice_lcand *lcand,
+ const struct sa *src, struct mbuf *mb)
+{
+ struct sa addr;
+
+ if (!lcand || !src || !mb)
+ return;
+
+ addr = *src;
+
+ udp_helper_recv_handler(&addr, mb, lcand);
+}