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/sip/keepalive_udp.c b/src/sip/keepalive_udp.c
new file mode 100644
index 0000000..39efa89
--- /dev/null
+++ b/src/sip/keepalive_udp.c
@@ -0,0 +1,188 @@
+/**
+ * @file keepalive_udp.c  SIP UDP Keepalive
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+#include <string.h>
+#include <re_types.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_hash.h>
+#include <re_fmt.h>
+#include <re_uri.h>
+#include <re_sys.h>
+#include <re_tmr.h>
+#include <re_udp.h>
+#include <re_stun.h>
+#include <re_msg.h>
+#include <re_sip.h>
+#include "sip.h"
+
+
+enum {
+	UDP_KEEPALIVE_INTVAL = 29,
+};
+
+
+struct sip_udpconn {
+	struct le he;
+	struct list kal;
+	struct tmr tmr_ka;
+	struct sa maddr;
+	struct sa paddr;
+	struct udp_sock *us;
+	struct stun_ctrans *ct;
+	struct stun *stun;
+	uint32_t ka_interval;
+};
+
+
+static void udpconn_keepalive_handler(void *arg);
+
+
+static void destructor(void *arg)
+{
+	struct sip_udpconn *uc = arg;
+
+	list_flush(&uc->kal);
+	hash_unlink(&uc->he);
+	tmr_cancel(&uc->tmr_ka);
+	mem_deref(uc->ct);
+	mem_deref(uc->us);
+	mem_deref(uc->stun);
+}
+
+
+static void udpconn_close(struct sip_udpconn *uc, int err)
+{
+	sip_keepalive_signal(&uc->kal, err);
+	hash_unlink(&uc->he);
+	tmr_cancel(&uc->tmr_ka);
+	uc->ct = mem_deref(uc->ct);
+	uc->us = mem_deref(uc->us);
+	uc->stun = mem_deref(uc->stun);
+}
+
+
+static void stun_response_handler(int err, uint16_t scode, const char *reason,
+				  const struct stun_msg *msg, void *arg)
+{
+	struct sip_udpconn *uc = arg;
+	struct stun_attr *attr;
+	(void)reason;
+
+	if (err || scode) {
+		err = err ? err : EPROTO;
+		goto out;
+	}
+
+	attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
+	if (!attr) {
+		attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);
+		if (!attr) {
+			err = EPROTO;
+			goto out;
+		}
+	}
+
+	if (!sa_isset(&uc->maddr, SA_ALL)) {
+		uc->maddr = attr->v.sa;
+	}
+	else if (!sa_cmp(&uc->maddr, &attr->v.sa, SA_ALL)) {
+		err = ENOTCONN;
+		goto out;
+	}
+
+ out:
+	if (err) {
+		udpconn_close(uc, err);
+		mem_deref(uc);
+	}
+	else {
+		tmr_start(&uc->tmr_ka, sip_keepalive_wait(uc->ka_interval),
+			  udpconn_keepalive_handler, uc);
+	}
+}
+
+
+static void udpconn_keepalive_handler(void *arg)
+{
+	struct sip_udpconn *uc = arg;
+	int err;
+
+	if (!uc->kal.head) {
+		/* no need for us anymore */
+		udpconn_close(uc, 0);
+		mem_deref(uc);
+		return;
+	}
+
+	err = stun_request(&uc->ct, uc->stun, IPPROTO_UDP, uc->us,
+			   &uc->paddr, 0, STUN_METHOD_BINDING, NULL, 0,
+			   false, stun_response_handler, uc, 1,
+			   STUN_ATTR_SOFTWARE, stun_software);
+	if (err) {
+		udpconn_close(uc, err);
+		mem_deref(uc);
+	}
+}
+
+
+static struct sip_udpconn *udpconn_find(struct sip *sip, struct udp_sock *us,
+					const struct sa *paddr)
+{
+	struct le *le;
+
+	le = list_head(hash_list(sip->ht_udpconn, sa_hash(paddr, SA_ALL)));
+
+	for (; le; le = le->next) {
+
+		struct sip_udpconn *uc = le->data;
+
+		if (!sa_cmp(&uc->paddr, paddr, SA_ALL))
+			continue;
+
+		if (uc->us != us)
+			continue;
+
+		return uc;
+	}
+
+	return NULL;
+}
+
+
+int  sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip,
+		       struct udp_sock *us, const struct sa *paddr,
+		       uint32_t interval)
+{
+	struct sip_udpconn *uc;
+
+	if (!ka || !sip || !us || !paddr)
+		return EINVAL;
+
+	uc = udpconn_find(sip, us, paddr);
+	if (!uc) {
+		uc = mem_zalloc(sizeof(*uc), destructor);
+		if (!uc)
+			return ENOMEM;
+
+		hash_append(sip->ht_udpconn, sa_hash(paddr, SA_ALL),
+			    &uc->he, uc);
+
+		uc->paddr = *paddr;
+		uc->stun  = mem_ref(sip->stun);
+		uc->us    = mem_ref(us);
+		uc->ka_interval = interval ? interval : UDP_KEEPALIVE_INTVAL;
+
+		/* learn mapped address immediately */
+		tmr_start(&uc->tmr_ka, 0, udpconn_keepalive_handler, uc);
+	}
+
+	list_append(&uc->kal, &ka->le, ka);
+
+	return 0;
+}