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/natbd/filtering.c b/src/natbd/filtering.c
new file mode 100644
index 0000000..8d0acc5
--- /dev/null
+++ b/src/natbd/filtering.c
@@ -0,0 +1,242 @@
+/**
+ * @file filtering.c  NAT Filtering Behaviour Discovery
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_stun.h>
+#include <re_natbd.h>
+
+
+#define DEBUG_MODULE "natbd_filtering"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/* Determining NAT Filtering Behavior
+
+   This will also require at most three tests.  These tests should be
+   performed using a port that wasn't used for mapping or other tests as
+   packets sent during those tests may affect results.  In test I, the
+   client performs the UDP connectivity test.  The server will return
+   its alternate address and port in OTHER-ADDRESS in the binding
+   response.  If OTHER-ADDRESS is not returned, the server does not
+   support this usage and this test cannot be run.
+
+   In test II, the client sends a binding request to the primary address
+   of the server with the CHANGE-REQUEST attribute set to change-port
+   and change-IP.  This will cause the server to send its response from
+   its alternate IP address and alternate port.  If the client receives
+   a response the current behaviour of the NAT is Endpoint-Independent
+   Filtering.
+
+   If no response is received, test III must be performed to distinguish
+   between Address-Dependent Filtering and Address and Port-Dependent
+   Filtering.  In test III, the client sends a binding request to the
+   original server address with CHANGE-REQUEST set to change-port.  If
+   the client receives a response the current behaviour is Address-
+   Dependent Filtering; if no response is received the current behaviour
+   is Address and Port-Dependent Filtering.
+ */
+
+
+/** Defines a NAT Filtering Behaviour Discovery session */
+struct nat_filtering {
+	struct stun *stun;     /**< STUN instance              */
+	struct sa srv;         /**< Server IP address and port */
+	int test_phase;        /**< State machine              */
+	nat_filtering_h *fh;   /**< Result handler             */
+	void *arg;             /**< Handler argument           */
+};
+
+
+static void stun_response_handler(int err, uint16_t scode, const char *reason,
+				  const struct stun_msg *msg, void *arg)
+{
+	struct stun_change_req change_req;
+	struct nat_filtering *nf = arg;
+	struct stun_attr *attr;
+	(void)reason;
+
+	if (err == ECONNABORTED) {
+		nf->fh(err, NAT_TYPE_UNKNOWN, nf->arg);
+		return;
+	}
+
+	attr = stun_msg_attr(msg, STUN_ATTR_OTHER_ADDR);
+	if (!err && !attr) {
+		DEBUG_WARNING("no OTHER-ADDRESS in response - abort\n");
+		nf->fh(EINVAL, NAT_TYPE_UNKNOWN, nf->arg);
+		return;
+	}
+
+	switch (nf->test_phase) {
+
+	case 1:
+		/* Test I completed */
+
+		if (err || scode) {
+			DEBUG_WARNING("Test I: stun_response_handler: %m\n",
+				      err);
+			nf->fh(err, NAT_TYPE_UNKNOWN, nf->arg);
+			return;
+		}
+
+		/* Start Test II */
+
+		/*
+		  In test II, the client sends a binding request to the
+		  primary address of the server with the CHANGE-REQUEST
+		  attribute set to change-port and change-IP.
+		*/
+
+		++nf->test_phase;
+
+		change_req.ip = true;
+		change_req.port = true;
+
+		err = stun_request(NULL, nf->stun, IPPROTO_UDP, NULL, &nf->srv,
+				   0, STUN_METHOD_BINDING, NULL, 0, false,
+				   stun_response_handler, nf, 2,
+				   STUN_ATTR_SOFTWARE, stun_software,
+				   STUN_ATTR_CHANGE_REQ, &change_req);
+		if (err) {
+			DEBUG_WARNING("stunc_request_send: (%m)\n", err);
+			nf->fh(err, NAT_TYPE_UNKNOWN, nf->arg);
+		}
+		break;
+
+	case 2:
+		/* Test II completed */
+
+		/*
+		  If the client receives a response the current behaviour of
+		  the NAT is Endpoint Independent Filtering.
+		*/
+
+		if (0 == err) {
+			if (!scode) {
+				nf->fh(0, NAT_TYPE_ENDP_INDEP, nf->arg);
+			}
+			else {
+				nf->fh(EINVAL, NAT_TYPE_UNKNOWN, nf->arg);
+			}
+			return;
+		}
+
+		/* Start Test III - the client sends a binding request to the
+		   original server address with CHANGE-REQUEST set to
+		   change-port
+		 */
+		++nf->test_phase;
+
+		change_req.ip = false;
+		change_req.port = true;
+
+		err = stun_request(NULL, nf->stun, IPPROTO_UDP, NULL, &nf->srv,
+				   0, STUN_METHOD_BINDING, NULL, 0, false,
+				   stun_response_handler, nf, 2,
+				   STUN_ATTR_SOFTWARE, stun_software,
+				   STUN_ATTR_CHANGE_REQ, &change_req);
+		if (err) {
+			DEBUG_WARNING("stunc_request_send: (%m)\n", err);
+			nf->fh(err, NAT_TYPE_UNKNOWN, nf->arg);
+		}
+		break;
+
+	case 3:
+		/* Test III completed */
+		DEBUG_INFO("Test III completed\n");
+
+		if (0 == err && !scode) {
+			nf->fh(0, NAT_TYPE_ADDR_DEP, nf->arg);
+		}
+		else {
+			nf->fh(0, NAT_TYPE_ADDR_PORT_DEP, nf->arg);
+		}
+		break;
+
+	default:
+		DEBUG_WARNING("invalid test phase %d\n", nf->test_phase);
+		nf->fh(EINVAL, NAT_TYPE_UNKNOWN, nf->arg);
+		return;
+	}
+}
+
+
+static void filtering_destructor(void *data)
+{
+	struct nat_filtering *nf = data;
+
+	mem_deref(nf->stun);
+}
+
+
+/**
+ * Allocate a NAT Filtering Behaviour Discovery session
+ *
+ * @param nfp      Pointer to allocated NAT filtering object
+ * @param srv      STUN Server IP address and port number
+ * @param conf     STUN configuration (Optional)
+ * @param fh       Filtering result handler
+ * @param arg      Handler argument
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_filtering_alloc(struct nat_filtering **nfp, const struct sa *srv,
+			const struct stun_conf *conf,
+			nat_filtering_h *fh, void *arg)
+{
+	struct nat_filtering *nf;
+	int err;
+
+	if (!nfp || !srv || !fh)
+		return EINVAL;
+
+	nf = mem_zalloc(sizeof(*nf), filtering_destructor);
+	if (!nf)
+		return ENOMEM;
+
+	err = stun_alloc(&nf->stun, conf, NULL, NULL);
+	if (err)
+		goto out;
+
+	sa_cpy(&nf->srv, srv);
+
+	nf->fh  = fh;
+	nf->arg = arg;
+
+ out:
+	if (err)
+		mem_deref(nf);
+	else
+		*nfp = nf;
+
+	return err;
+}
+
+
+/**
+ * Start a NAT Filtering Behaviour Discovery session
+ *
+ * @param nf NAT filtering object
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_filtering_start(struct nat_filtering *nf)
+{
+	if (!nf)
+		return EINVAL;
+
+	nf->test_phase = 1;
+
+	return stun_request(NULL, nf->stun, IPPROTO_UDP, NULL, &nf->srv, 0,
+			    STUN_METHOD_BINDING, NULL, 0, false,
+			    stun_response_handler, nf, 1,
+			    STUN_ATTR_SOFTWARE, stun_software);
+}
diff --git a/src/natbd/genalg.c b/src/natbd/genalg.c
new file mode 100644
index 0000000..50b3827
--- /dev/null
+++ b/src/natbd/genalg.c
@@ -0,0 +1,152 @@
+/**
+ * @file genalg.c  Detecting Generic ALGs
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_mbuf.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_stun.h>
+#include <re_natbd.h>
+
+
+#define DEBUG_MODULE "natbd_genalg"
+#define DEBUG_LEVEL 7
+#include <re_dbg.h>
+
+
+/*
+  Detecting Generic ALGs
+
+   A number of NAT boxes are now being deployed into the market which
+   try to provide "generic" ALG functionality.  These generic ALGs hunt
+   for IP addresses, either in text or binary form within a packet, and
+   rewrite them if they match a binding.  This behavior can be detected
+   because the STUN server returns both the MAPPED-ADDRESS and XOR-
+   MAPPED-ADDRESS in the same response.  If the result in the two does
+   not match, there a NAT with a generic ALG in the path.
+ */
+
+
+/** Defines a NAT Generic ALG detection session */
+struct nat_genalg {
+	struct stun *stun;      /**< STUN Client               */
+	struct sa srv;          /**< Server address and port   */
+	int proto;              /**< IP protocol               */
+	nat_genalg_h *h;        /**< Result handler            */
+	void *arg;              /**< Handler argument          */
+};
+
+
+static void stun_response_handler(int err, uint16_t scode, const char *reason,
+				  const struct stun_msg *msg, void *arg)
+{
+	struct stun_attr *xmap, *map;
+	struct nat_genalg *ng = arg;
+	int status = 0;
+	(void)reason;
+
+	if (err) {
+		ng->h(err, 0, NULL, -1, NULL, ng->arg);
+		return;
+	}
+
+	switch (scode) {
+
+	case 0:
+		map  = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);
+		xmap = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
+		if (!map || !xmap) {
+			ng->h(EINVAL, scode, reason, -1, NULL, ng->arg);
+			break;
+		}
+
+		status = sa_cmp(&map->v.sa, &xmap->v.sa, SA_ALL) ? -1 : 1;
+
+		ng->h(0, scode, reason, status, &xmap->v.sa, ng->arg);
+		break;
+
+	default:
+		ng->h(0, scode, reason, -1, NULL, ng->arg);
+		break;
+	}
+}
+
+
+static void genalg_destructor(void *data)
+{
+	struct nat_genalg *ng = data;
+
+	mem_deref(ng->stun);
+}
+
+
+/**
+ * Allocate a new NAT Generic ALG detection session
+ *
+ * @param ngp       Pointer to allocated NAT Generic ALG object
+ * @param srv       STUN Server IP address and port
+ * @param proto     Transport protocol
+ * @param conf      STUN configuration (Optional)
+ * @param gh        Generic ALG handler
+ * @param arg       Handler argument
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_genalg_alloc(struct nat_genalg **ngp, const struct sa *srv, int proto,
+		     const struct stun_conf *conf,
+		     nat_genalg_h *gh, void *arg)
+{
+	struct nat_genalg *ng;
+	int err;
+
+	if (!ngp || !srv || !proto || !gh)
+		return EINVAL;
+
+	ng = mem_zalloc(sizeof(*ng), genalg_destructor);
+	if (!ng)
+		return ENOMEM;
+
+	err = stun_alloc(&ng->stun, conf, NULL, NULL);
+	if (err)
+		goto out;
+
+	sa_cpy(&ng->srv, srv);
+	ng->proto = proto;
+	ng->h     = gh;
+	ng->arg   = arg;
+
+ out:
+	if (err)
+		mem_deref(ng);
+	else
+		*ngp = ng;
+
+	return err;
+}
+
+
+/**
+ * Start the NAT Generic ALG detection
+ *
+ * @param ng NAT Generic ALG object
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_genalg_start(struct nat_genalg *ng)
+{
+	int err;
+
+	if (!ng)
+		return EINVAL;
+
+	err = stun_request(NULL, ng->stun, ng->proto, NULL, &ng->srv, 0,
+			   STUN_METHOD_BINDING, NULL, 0, false,
+			   stun_response_handler, ng, 1,
+			   STUN_ATTR_SOFTWARE, stun_software);
+
+	return err;
+}
diff --git a/src/natbd/hairpinning.c b/src/natbd/hairpinning.c
new file mode 100644
index 0000000..396d50e
--- /dev/null
+++ b/src/natbd/hairpinning.c
@@ -0,0 +1,363 @@
+/**
+ * @file hairpinning.c  NAT Hairpinning Behaviour discovery
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_mem.h>
+#include <re_sa.h>
+#include <re_udp.h>
+#include <re_tcp.h>
+#include <re_list.h>
+#include <re_stun.h>
+#include <re_natbd.h>
+
+
+#define DEBUG_MODULE "natbd_hairpinning"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/*
+   Diagnosing NAT Hairpinning
+
+   STUN Binding Requests allow a a client to determine whether it is
+   behind a NAT that support hairpinning of datagrams.  To perform this
+   test, the client first sends a Binding Request to its STUN server to
+   determine its mapped address.  The client then sends a STUN Binding
+   Request to this mapped address from a different port.  If the client
+   receives its own request, the NAT hairpins datagrams.  This test
+   applies to UDP, TCP, or TCP/TLS connections.
+
+ */
+
+
+/** Defines NAT Hairpinning Behaviour Discovery */
+struct nat_hairpinning {
+	struct stun *stun;       /**< STUN Client             */
+	int proto;               /**< IP Protocol             */
+	struct sa srv;           /**< Server address and port */
+	struct udp_sock *us;     /**< UDP socket              */
+	struct tcp_conn *tc;     /**< Client TCP Connection   */
+	struct tcp_sock *ts;     /**< Server TCP Socket       */
+	struct tcp_conn *tc2;    /**< Server TCP Connection   */
+	nat_hairpinning_h *hph;  /**< Result handler          */
+	void *arg;               /**< Handler argument        */
+};
+
+
+static void hairpinning_destructor(void *data)
+{
+	struct nat_hairpinning *nh = data;
+
+	mem_deref(nh->us);
+	mem_deref(nh->tc);
+	mem_deref(nh->ts);
+	mem_deref(nh->tc2);
+	mem_deref(nh->stun);
+}
+
+
+static void msg_recv(struct nat_hairpinning *nh, int proto, void *sock,
+		     const struct sa *src, struct mbuf *mb)
+{
+	struct stun_unknown_attr ua;
+	struct stun_msg *msg;
+
+	if (0 != stun_msg_decode(&msg, mb, &ua))
+		return;
+
+	switch (stun_msg_class(msg)) {
+
+	case STUN_CLASS_REQUEST:
+		(void)stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 3,
+				 STUN_ATTR_XOR_MAPPED_ADDR, src,
+				 STUN_ATTR_MAPPED_ADDR, src,
+				 STUN_ATTR_SOFTWARE, stun_software);
+		break;
+
+	case STUN_CLASS_ERROR_RESP:
+	case STUN_CLASS_SUCCESS_RESP:
+		(void)stun_ctrans_recv(nh->stun, msg, &ua);
+		break;
+
+	default:
+		DEBUG_WARNING("unknown class 0x%04x\n", stun_msg_class(msg));
+		break;
+	}
+
+	mem_deref(msg);
+}
+
+
+static void udp_recv_handler(const struct sa *src, struct mbuf *mb,
+			     void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+
+	msg_recv(nh, IPPROTO_UDP, nh->us, src, mb);
+}
+
+
+static void stun_response_handler2(int err, uint16_t scode, const char *reason,
+				   const struct stun_msg *msg, void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+	(void)reason;
+	(void)msg;
+
+	if (err || scode) {
+		nh->hph(0, false, nh->arg);
+		return;
+	}
+
+	/* Hairpinning supported */
+	nh->hph(0, true, nh->arg);
+}
+
+
+static int hairpin_send(struct nat_hairpinning *nh, const struct sa *srv)
+{
+	return stun_request(NULL, nh->stun, nh->proto, NULL,
+			    srv, 0, STUN_METHOD_BINDING, NULL, 0, false,
+			    stun_response_handler2, nh, 1,
+			    STUN_ATTR_SOFTWARE, stun_software);
+}
+
+
+/*
+ * TCP Connections: STUN Client2 to Embedded STUN Server
+ */
+
+
+static void tcp_recv_handler2(struct mbuf *mb, void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+
+	msg_recv(nh, IPPROTO_TCP, nh->tc2, NULL, mb);
+}
+
+
+static void tcp_close_handler2(int err, void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+
+	if (err)
+		nh->hph(err, false, nh->arg);
+}
+
+
+static void stun_response_handler(int err, uint16_t scode, const char *reason,
+				  const struct stun_msg *msg, void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+	const struct stun_attr *attr;
+	(void)reason;
+
+	if (err) {
+		nh->hph(err, false, nh->arg);
+		return;
+	}
+
+	attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
+	if (!attr)
+		attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);
+
+	if (scode || !attr) {
+		nh->hph(EBADMSG, false, nh->arg);
+		return;
+	}
+
+	/* Send hairpinning test message */
+	err = hairpin_send(nh, &attr->v.sa);
+	if (err) {
+		DEBUG_WARNING("hairpin_send: (%m)\n", err);
+	}
+
+	if (err)
+		nh->hph(err, false, nh->arg);
+}
+
+
+static int mapped_send(struct nat_hairpinning *nh)
+{
+	return stun_request(NULL, nh->stun, nh->proto, nh->us ?
+			    (void *)nh->us : (void *)nh->tc,
+			    &nh->srv, 0, STUN_METHOD_BINDING, NULL, 0, false,
+			    stun_response_handler, nh, 1,
+			    STUN_ATTR_SOFTWARE, stun_software);
+}
+
+
+static void tcp_conn_handler(const struct sa *peer, void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+	int err;
+
+	(void)peer;
+
+	err = tcp_accept(&nh->tc2, nh->ts, NULL, tcp_recv_handler2,
+			 tcp_close_handler2, nh);
+	if (err) {
+		DEBUG_WARNING("TCP conn: tcp_accept: %m\n", err);
+	}
+}
+
+
+/*
+ * TCP Connection: STUN Client to STUN Server
+ */
+
+static void tcp_estab_handler(void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+	int err;
+
+	err = mapped_send(nh);
+	if (err) {
+		DEBUG_WARNING("TCP established: mapped_send (%m)\n", err);
+		nh->hph(err, false, nh->arg);
+	}
+}
+
+
+static void tcp_recv_handler(struct mbuf *mb, void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+	int err;
+
+	err = stun_recv(nh->stun, mb);
+	if (err && ENOENT != err) {
+		DEBUG_WARNING("stun recv: %m\n", err);
+	}
+}
+
+
+static void tcp_close_handler(int err, void *arg)
+{
+	struct nat_hairpinning *nh = arg;
+
+	if (err)
+		nh->hph(err, false, nh->arg);
+}
+
+
+/**
+ * Allocate a new NAT Hairpinning discovery session
+ *
+ * @param nhp      Pointer to allocated NAT Hairpinning object
+ * @param proto    Transport protocol
+ * @param srv      STUN Server IP address and port number
+ * @param proto    Transport protocol
+ * @param conf     STUN configuration (Optional)
+ * @param hph      Hairpinning result handler
+ * @param arg      Handler argument
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_hairpinning_alloc(struct nat_hairpinning **nhp,
+			  const struct sa *srv, int proto,
+			  const struct stun_conf *conf,
+			  nat_hairpinning_h *hph, void *arg)
+{
+	struct nat_hairpinning *nh;
+	struct sa local;
+	int err;
+
+	if (!srv || !hph)
+		return EINVAL;
+
+	nh = mem_zalloc(sizeof(*nh), hairpinning_destructor);
+	if (!nh)
+		return ENOMEM;
+
+	err = stun_alloc(&nh->stun, conf, NULL, NULL);
+	if (err)
+		goto out;
+
+	sa_cpy(&nh->srv, srv);
+	nh->proto = proto;
+	nh->hph   = hph;
+	nh->arg   = arg;
+
+	switch (proto) {
+
+	case IPPROTO_UDP:
+		err = udp_listen(&nh->us, NULL, udp_recv_handler, nh);
+		break;
+
+	case IPPROTO_TCP:
+		sa_set_in(&local, 0, 0);
+
+		/*
+		 * Part I - Allocate and bind all sockets
+		 */
+		err = tcp_sock_alloc(&nh->ts, &local, tcp_conn_handler, nh);
+		if (err)
+			break;
+
+		err = tcp_conn_alloc(&nh->tc, &nh->srv,
+				     tcp_estab_handler, tcp_recv_handler,
+				     tcp_close_handler, nh);
+		if (err)
+			break;
+
+		err = tcp_sock_bind(nh->ts, &local);
+		if (err)
+			break;
+
+		err = tcp_sock_local_get(nh->ts, &local);
+		if (err)
+			break;
+
+		err = tcp_conn_bind(nh->tc, &local);
+		if (err)
+			break;
+
+		/*
+		 * Part II - Listen and connect all sockets
+		 */
+		err = tcp_sock_listen(nh->ts, 5);
+		break;
+
+	default:
+		err = EPROTONOSUPPORT;
+		break;
+	}
+
+ out:
+	if (err)
+		mem_deref(nh);
+	else
+		*nhp = nh;
+
+	return err;
+}
+
+
+/**
+ * Start a new NAT Hairpinning discovery session
+ *
+ * @param nh NAT Hairpinning object
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_hairpinning_start(struct nat_hairpinning *nh)
+{
+	if (!nh)
+		return EINVAL;
+
+	switch (nh->proto) {
+
+	case IPPROTO_UDP:
+		return mapped_send(nh);
+
+	case IPPROTO_TCP:
+		return tcp_conn_connect(nh->tc, &nh->srv);
+
+	default:
+		return EPROTONOSUPPORT;
+	}
+}
diff --git a/src/natbd/lifetime.c b/src/natbd/lifetime.c
new file mode 100644
index 0000000..0bbffb9
--- /dev/null
+++ b/src/natbd/lifetime.c
@@ -0,0 +1,332 @@
+/**
+ * @file lifetime.c  NAT Binding Lifetime Discovery
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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_udp.h>
+#include <re_stun.h>
+#include <re_natbd.h>
+
+
+#define DEBUG_MODULE "natbd_lifetime"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/* Binding Lifetime Discovery
+
+   STUN can also be used to probe the lifetimes of the bindings created
+   by the NAT.  For many NAT devices, an absolute refresh interval
+   cannot be determined; bindings might be closed quicker under heavy
+   load or might not behave as the tests suggest.  For this reason
+   applications that require reliable bindings must send keep-alives as
+   frequently as required by all NAT devices that will be encountered.
+ */
+
+
+/** Defines a NAT Binding Lifetime Discovery session */
+struct nat_lifetime {
+	struct stun *stun;                      /**< STUN Client            */
+	struct stun_ctrans *ctx;                /**< STUN Transaction 1     */
+	struct stun_ctrans *cty;                /**< STUN Transaction 2     */
+	struct udp_sock *us_x;                  /**< First UDP socket       */
+	struct udp_sock *us_y;                  /**< Second UDP socket      */
+	struct sa srv;                          /**< Server IP-address/port */
+	struct sa map;                          /**< Mapped IP address/port */
+	struct tmr tmr;                         /**< Refresh timer          */
+	bool probing;                           /**< Probing flag           */
+	struct nat_lifetime_interval interval;  /**< Lifetime intervals     */
+	nat_lifetime_h *lh;                     /**< Result handler         */
+	void *arg;                              /**< Handler argument       */
+};
+
+
+static void timeout(void *arg);
+static void binding_ok(struct nat_lifetime *nl);
+static void binding_expired(struct nat_lifetime *nl);
+
+
+/*
+ * X socket
+ */
+
+
+static void msg_recv(struct stun *stun, const struct sa *src,
+		     struct mbuf *mb)
+{
+	int err;
+	(void)src;
+
+	err = stun_recv(stun, mb);
+	if (err && ENOENT != err) {
+		DEBUG_WARNING("msg_recv: stunc_recv(): (%m)\n", err);
+	}
+}
+
+
+static void udp_recv_handler_x(const struct sa *src, struct mbuf *mb,
+			       void *arg)
+{
+	struct nat_lifetime *nl = arg;
+
+	/* Forward response to socket */
+	msg_recv(nl->stun, src, mb);
+}
+
+
+static void stun_response_handler_x(int err, uint16_t scode,
+				    const char *reason,
+				    const struct stun_msg *msg, void *arg)
+{
+	struct nat_lifetime *nl = arg;
+	struct stun_attr *attr;
+
+	(void)reason;
+
+	if (err) {
+		DEBUG_WARNING("stun_response_handler X: %m\n", err);
+		goto out;
+	}
+
+	switch (scode) {
+
+	case 0:
+		attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
+		if (!attr) {
+			err = EPROTO;
+			break;
+		}
+
+		nl->map = attr->v.xor_mapped_addr;
+
+		DEBUG_INFO("Starting timer of %d seconds...[zzz]...\n",
+			   nl->interval.cur);
+
+		tmr_start(&nl->tmr, nl->interval.cur*1000, timeout, nl);
+		return;
+
+	default:
+		err = EPROTO;
+		break;
+	}
+
+ out:
+	nl->lh(err, &nl->interval, nl->arg);
+	binding_expired(nl);
+}
+
+
+/*
+ * Y socket
+ */
+
+
+static void udp_recv_handler_y(const struct sa *src, struct mbuf *mb,
+			       void *arg)
+{
+	struct nat_lifetime *nl = arg;
+
+	(void)src;
+	(void)mb;
+
+	if (!nl->probing) {
+		DEBUG_WARNING("Y: hmm, not probing?\n");
+	}
+
+	binding_expired(nl);
+}
+
+
+static void stun_response_handler_y(int err, uint16_t scode,
+				    const char *reason,
+				    const struct stun_msg *msg, void *arg)
+{
+	struct nat_lifetime *nl = arg;
+	(void)reason;
+	(void)msg;
+
+	if (err) {
+		binding_expired(nl);
+		return;
+	}
+
+	switch (scode) {
+
+	case 0:
+		binding_ok(nl);
+		break;
+
+	default:
+		nl->lh(EBADMSG, &nl->interval, nl->arg);
+		break;
+	}
+}
+
+
+/*
+ * Common
+ */
+
+
+static int start_test(struct nat_lifetime *nl)
+{
+	nl->probing = false;
+
+	tmr_cancel(&nl->tmr);
+
+	nl->ctx = mem_deref(nl->ctx);
+	return stun_request(&nl->ctx, nl->stun, IPPROTO_UDP, nl->us_x,
+			    &nl->srv, 0, STUN_METHOD_BINDING, NULL, 0, false,
+			    stun_response_handler_x, nl, 1,
+			    STUN_ATTR_SOFTWARE, stun_software);
+}
+
+
+static void timeout(void *arg)
+{
+	struct nat_lifetime *nl = arg;
+	const uint16_t rp = sa_port(&nl->map);
+	int err;
+
+	nl->probing = true;
+
+	nl->cty = mem_deref(nl->cty);
+	err = stun_request(&nl->cty, nl->stun, IPPROTO_UDP, nl->us_y,
+			   &nl->srv, 0, STUN_METHOD_BINDING, NULL, 0, false,
+			   stun_response_handler_y, nl, 2,
+			   STUN_ATTR_RESP_PORT, &rp,
+			   STUN_ATTR_SOFTWARE, stun_software);
+	if (err)
+		goto out;
+
+	return;
+
+ out:
+	DEBUG_WARNING("timeout: (%m)\n", err);
+
+	nl->lh(err, &nl->interval, nl->arg);
+
+	(void)start_test(nl);
+}
+
+
+/* Binding OK - recalculate current interval */
+static void binding_ok(struct nat_lifetime *nl)
+{
+	nl->interval.min = max(1, nl->interval.cur);
+
+	if (nl->interval.max > 0)
+		nl->interval.cur = (nl->interval.min + nl->interval.max) / 2;
+	else
+		nl->interval.cur *= 2;
+
+	nl->lh(0, &nl->interval, nl->arg);
+
+	(void)start_test(nl);
+}
+
+
+/* Request timed out - recalculate current interval */
+static void binding_expired(struct nat_lifetime *nl)
+{
+	nl->interval.max = nl->interval.cur;
+
+	nl->interval.cur = (nl->interval.min + nl->interval.max) / 2;
+
+	nl->lh(0, &nl->interval, nl->arg);
+
+	(void)start_test(nl);
+}
+
+
+static void lifetime_destructor(void *data)
+{
+	struct nat_lifetime *nl = data;
+
+	tmr_cancel(&nl->tmr);
+
+	mem_deref(nl->ctx);
+	mem_deref(nl->cty);
+	mem_deref(nl->us_x);
+	mem_deref(nl->us_y);
+	mem_deref(nl->stun);
+}
+
+
+/**
+ * Allocate a new NAT Lifetime discovery session
+ *
+ * @param nlp       Pointer to allocated NAT Lifetime object
+ * @param srv       STUN Server IP address and port number
+ * @param interval  Initial interval in [seconds]
+ * @param conf      STUN configuration (Optional)
+ * @param lh        Lifetime handler - called for each probe
+ * @param arg       Handler argument
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_lifetime_alloc(struct nat_lifetime **nlp, const struct sa *srv,
+		       uint32_t interval, const struct stun_conf *conf,
+		       nat_lifetime_h *lh, void *arg)
+{
+	struct nat_lifetime *nl;
+	int err;
+
+	if (!nlp || !srv || !interval || !lh)
+		return EINVAL;
+
+	nl = mem_zalloc(sizeof(*nl), lifetime_destructor);
+	if (!nl)
+		return ENOMEM;
+
+	tmr_init(&nl->tmr);
+
+	err = stun_alloc(&nl->stun, conf, NULL, NULL);
+	if (err)
+		goto out;
+
+	err = udp_listen(&nl->us_x, NULL, udp_recv_handler_x, nl);
+	if (err)
+		goto out;
+
+	err = udp_listen(&nl->us_y, NULL, udp_recv_handler_y, nl);
+	if (err)
+		goto out;
+
+	sa_cpy(&nl->srv, srv);
+	nl->interval.min = 1;
+	nl->interval.cur = interval;
+	nl->lh  = lh;
+	nl->arg = arg;
+
+ out:
+	if (err)
+		mem_deref(nl);
+	else
+		*nlp = nl;
+
+	return err;
+}
+
+
+/**
+ * Start a new NAT Lifetime discovery session
+ *
+ * @param nl NAT Lifetime object
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_lifetime_start(struct nat_lifetime *nl)
+{
+	if (!nl)
+		return EINVAL;
+
+	return start_test(nl);
+}
diff --git a/src/natbd/mapping.c b/src/natbd/mapping.c
new file mode 100644
index 0000000..e304560
--- /dev/null
+++ b/src/natbd/mapping.c
@@ -0,0 +1,354 @@
+/**
+ * @file mapping.c  NAT Mapping Behaviour discovery
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_mbuf.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_sa.h>
+#include <re_udp.h>
+#include <re_tcp.h>
+#include <re_list.h>
+#include <re_stun.h>
+#include <re_natbd.h>
+
+
+#define DEBUG_MODULE "natbd_mapping"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/* Determining NAT Mapping Behavior
+
+   This will require at most three tests.  In test I, the client
+   performs the UDP connectivity test.  The server will return its
+   alternate address and port in OTHER-ADDRESS in the binding response.
+   If OTHER-ADDRESS is not returned, the server does not support this
+   usage and this test cannot be run.  The client examines the XOR-
+   MAPPED-ADDRESS attribute.  If this address and port are the same as
+   the local IP address and port of the socket used to send the request,
+   the client knows that it is not NATed and the effective mapping will
+   be Endpoint-Independent.
+
+   In test II, the client sends a Binding Request to the alternate
+   address, but primary port.  If the XOR-MAPPED-ADDRESS in the Binding
+   Response is the same as test I the NAT currently has Endpoint-
+   Independent Mapping.  If not, test III is performed: the client sends
+   a Binding Request to the alternate address and port.  If the XOR-
+   MAPPED-ADDRESS matches test II, the NAT currently has Address-
+   Dependent Mapping; if it doesn't match it currently has Address and
+   Port-Dependent Mapping.
+ */
+
+
+/** Defines a NAT Mapping Behaviour Discovery session */
+struct nat_mapping {
+	struct stun *stun;        /**< STUN Instance             */
+	struct udp_sock *us;      /**< UDP socket                */
+	struct tcp_conn *tc;      /**< TCP connection            */
+	struct sa laddr;          /**< Local IP address and port */
+	struct sa map[3];         /**< XOR Mapped address/ports  */
+	struct sa srv;            /**< STUN server address/port  */
+	nat_mapping_h *mh;        /**< Result handler            */
+	void *arg;                /**< Handler argument          */
+	int proto;                /**< IP Protocol               */
+	uint32_t test_phase;      /**< State machine             */
+	struct tcp_conn *tcv[3];  /**< TCP Connections           */
+};
+
+
+static int mapping_send(struct nat_mapping *nm);
+
+
+static void stun_response_handler(int err, uint16_t scode, const char *reason,
+				  const struct stun_msg *msg, void *arg)
+{
+	struct nat_mapping *nm = arg;
+	struct stun_attr *map, *other;
+
+	if (err) {
+		DEBUG_WARNING("stun_response_handler: (%m)\n", err);
+		nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg);
+		return;
+	}
+
+	switch (scode) {
+
+	case 0:
+		other = stun_msg_attr(msg, STUN_ATTR_OTHER_ADDR);
+		map = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
+		if (!map)
+			map = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR);
+
+		if (!map || !other) {
+			DEBUG_WARNING("missing attributes: %s %s\n",
+				      map   ? "" : "MAPPED-ADDR",
+				      other ? "" : "OTHER-ADDR");
+			nm->mh(EPROTO, NAT_TYPE_UNKNOWN, nm->arg);
+			return;
+		}
+
+		nm->map[nm->test_phase-1] = map->v.sa;
+		break;
+
+	default:
+		DEBUG_WARNING("Binding Error Resp: %u %s\n", scode, reason);
+		nm->mh(EPROTO, NAT_TYPE_UNKNOWN, nm->arg);
+		return;
+	}
+
+	switch (nm->test_phase) {
+
+	case 1:
+		/* Test I completed */
+		if (sa_cmp(&nm->laddr, &nm->map[0], SA_ALL)) {
+			nm->mh(0, NAT_TYPE_ENDP_INDEP, nm->arg);
+			return;
+		}
+
+		/* Start Test II - the client sends a Binding Request to the
+		   alternate *address* */
+		++nm->test_phase;
+
+		sa_set_port(&other->v.other_addr, sa_port(&nm->srv));
+		sa_cpy(&nm->srv, &other->v.other_addr);
+
+		err = mapping_send(nm);
+		if (err) {
+			DEBUG_WARNING("stunc_request_send: (%m)\n", err);
+			nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg);
+		}
+		break;
+
+	case 2:
+		/* Test II completed */
+		if (sa_cmp(&nm->map[0], &nm->map[1], SA_ALL)) {
+			nm->mh(0, NAT_TYPE_ENDP_INDEP, nm->arg);
+			return;
+		}
+
+		/* Start Test III - the client sends a Binding Request
+		   to the alternate address and port */
+		++nm->test_phase;
+
+		sa_set_port(&nm->srv, sa_port(&other->v.other_addr));
+		err = mapping_send(nm);
+		if (err) {
+			DEBUG_WARNING("stunc_request_send: (%m)\n", err);
+			nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg);
+		}
+		break;
+
+	case 3:
+		/* Test III completed */
+		if (sa_cmp(&nm->map[1], &nm->map[2], SA_ALL)) {
+			nm->mh(0, NAT_TYPE_ADDR_DEP, nm->arg);
+		}
+		else {
+			nm->mh(0, NAT_TYPE_ADDR_PORT_DEP, nm->arg);
+		}
+		++nm->test_phase;
+		break;
+
+	default:
+		DEBUG_WARNING("invalid test phase %d\n", nm->test_phase);
+		nm->mh(EINVAL, NAT_TYPE_UNKNOWN, nm->arg);
+		break;
+	}
+}
+
+
+static int mapping_send(struct nat_mapping *nm)
+{
+	switch (nm->proto) {
+
+	case IPPROTO_UDP:
+		return stun_request(NULL, nm->stun, IPPROTO_UDP, nm->us,
+				    &nm->srv, 0, STUN_METHOD_BINDING, NULL, 0,
+				    false, stun_response_handler, nm, 1,
+				    STUN_ATTR_SOFTWARE, stun_software);
+
+	case IPPROTO_TCP:
+		nm->tc = mem_deref(nm->tc);
+		nm->tc = mem_ref(nm->tcv[nm->test_phase-1]);
+		return tcp_conn_connect(nm->tc, &nm->srv);
+
+	default:
+		return EPROTONOSUPPORT;
+	}
+}
+
+
+static void udp_recv_handler(const struct sa *src, struct mbuf *mb,
+			     void *arg)
+{
+	struct nat_mapping *nm = arg;
+	int err;
+	(void)src;
+
+	err = stun_recv(nm->stun, mb);
+	if (err && ENOENT != err) {
+		DEBUG_WARNING("udp_recv_handler: stunc_recv(): (%m)\n", err);
+	}
+}
+
+
+static void mapping_destructor(void *data)
+{
+	struct nat_mapping *nm = data;
+	int i;
+
+	mem_deref(nm->us);
+	mem_deref(nm->tc);
+
+	for (i=0; i<3; i++)
+		mem_deref(nm->tcv[i]);
+
+	mem_deref(nm->stun);
+}
+
+
+static void tcp_estab_handler(void *arg)
+{
+	struct nat_mapping *nm = arg;
+	int err;
+
+	err = stun_request(NULL, nm->stun, IPPROTO_TCP, nm->tc, NULL, 0,
+			   STUN_METHOD_BINDING, NULL, 0, false,
+			   stun_response_handler, nm, 1,
+			   STUN_ATTR_SOFTWARE, stun_software);
+
+	if (err) {
+		DEBUG_WARNING("TCP established: mapping_send (%m)\n", err);
+		nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg);
+	}
+}
+
+
+static void tcp_recv_handler(struct mbuf *mb, void *arg)
+{
+	struct nat_mapping *nm = arg;
+	int err;
+
+	err = stun_recv(nm->stun, mb);
+	if (err && ENOENT != err) {
+		DEBUG_WARNING("stunc recv: %m\n", err);
+	}
+}
+
+
+static void tcp_close_handler(int err, void *arg)
+{
+	struct nat_mapping *nm = arg;
+
+	DEBUG_NOTICE("TCP Connection Closed (%m)\n", err);
+
+	if (err) {
+		nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg);
+	}
+}
+
+
+/**
+ * Allocate a new NAT Mapping Behaviour Discovery session
+ *
+ * @param nmp       Pointer to allocated NAT Mapping object
+ * @param laddr     Local IP address
+ * @param srv       STUN Server IP address and port
+ * @param proto     Transport protocol
+ * @param conf      STUN configuration (Optional)
+ * @param mh        Mapping handler
+ * @param arg       Handler argument
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_mapping_alloc(struct nat_mapping **nmp, const struct sa *laddr,
+		      const struct sa *srv, int proto,
+		      const struct stun_conf *conf,
+		      nat_mapping_h *mh, void *arg)
+{
+	struct nat_mapping *nm;
+	int i, err;
+
+	if (!nmp || !laddr || !srv || !mh)
+		return EINVAL;
+
+	nm = mem_zalloc(sizeof(*nm), mapping_destructor);
+	if (!nm)
+		return ENOMEM;
+
+	err = stun_alloc(&nm->stun, conf, NULL, NULL);
+	if (err)
+		goto out;
+
+	nm->proto = proto;
+	sa_cpy(&nm->laddr, laddr);
+
+	switch (proto) {
+
+	case IPPROTO_UDP:
+		err = udp_listen(&nm->us, &nm->laddr, udp_recv_handler, nm);
+		if (err)
+			goto out;
+		err = udp_local_get(nm->us, &nm->laddr);
+		if (err)
+			goto out;
+		break;
+
+	case IPPROTO_TCP:
+
+		/* Allocate and bind 3 TCP Sockets */
+		for (i=0; i<3; i++) {
+			err = tcp_conn_alloc(&nm->tcv[i], srv,
+					     tcp_estab_handler,
+					     tcp_recv_handler,
+					     tcp_close_handler, nm);
+			if (err)
+				goto out;
+
+			err = tcp_conn_bind(nm->tcv[i], &nm->laddr);
+			if (err)
+				goto out;
+
+			err = tcp_conn_local_get(nm->tcv[i], &nm->laddr);
+			if (err)
+				goto out;
+		}
+		break;
+
+	default:
+		err = EPROTONOSUPPORT;
+		goto out;
+	}
+
+	sa_cpy(&nm->srv, srv);
+	nm->mh  = mh;
+	nm->arg = arg;
+
+	*nmp = nm;
+
+ out:
+	if (err)
+		mem_deref(nm);
+	return err;
+}
+
+
+/**
+ * Start a new NAT Mapping Behaviour Discovery session
+ *
+ * @param nm NAT Mapping object
+ *
+ * @return 0 if success, errorcode if failure
+ */
+int nat_mapping_start(struct nat_mapping *nm)
+{
+	if (!nm)
+		return EINVAL;
+
+	nm->test_phase = 1;
+
+	return mapping_send(nm);
+}
diff --git a/src/natbd/mod.mk b/src/natbd/mod.mk
new file mode 100644
index 0000000..7a267fe
--- /dev/null
+++ b/src/natbd/mod.mk
@@ -0,0 +1,12 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS	+= natbd/filtering.c
+SRCS	+= natbd/genalg.c
+SRCS	+= natbd/hairpinning.c
+SRCS	+= natbd/lifetime.c
+SRCS	+= natbd/mapping.c
+SRCS	+= natbd/natstr.c
diff --git a/src/natbd/natstr.c b/src/natbd/natstr.c
new file mode 100644
index 0000000..96a8fa8
--- /dev/null
+++ b/src/natbd/natstr.c
@@ -0,0 +1,32 @@
+/**
+ * @file natstr.c  NAT Behaviour Discovery strings
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_stun.h>
+#include <re_natbd.h>
+
+
+/**
+ * Get the name of the NAT Mapping/Filtering type
+ *
+ * @param type NAT Mapping/Filtering type
+ *
+ * @return Name of the NAT Mapping/Filtering type
+ */
+const char *nat_type_str(enum nat_type type)
+{
+	switch (type) {
+
+	case NAT_TYPE_UNKNOWN:       return "Unknown";
+	case NAT_TYPE_ENDP_INDEP:    return "Endpoint Independent";
+	case NAT_TYPE_ADDR_DEP:      return "Address Dependent";
+	case NAT_TYPE_ADDR_PORT_DEP: return "Address and Port Dependent";
+	default:                     return "???";
+	}
+}