summaryrefslogtreecommitdiff
path: root/modules/natpmp/natpmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/natpmp/natpmp.c')
-rw-r--r--modules/natpmp/natpmp.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/modules/natpmp/natpmp.c b/modules/natpmp/natpmp.c
new file mode 100644
index 0000000..04b45f0
--- /dev/null
+++ b/modules/natpmp/natpmp.c
@@ -0,0 +1,313 @@
+/**
+ * @file natpmp.c NAT-PMP Module for Media NAT-traversal
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re.h>
+#include <baresip.h>
+#include "libnatpmp.h"
+
+
+/**
+ * @defgroup natpmp natpmp
+ *
+ * NAT Port Mapping Protocol (NAT-PMP)
+ *
+ * https://tools.ietf.org/html/rfc6886
+ */
+
+enum {
+ LIFETIME = 300 /* seconds */
+};
+
+struct mnat_sess {
+ struct list medial;
+ mnat_estab_h *estabh;
+ void *arg;
+};
+
+struct mnat_media {
+ struct le le;
+ struct mnat_sess *sess;
+ struct sdp_media *sdpm;
+ struct natpmp_req *natpmp;
+ struct tmr tmr;
+ uint16_t int_port;
+ uint32_t lifetime;
+ bool granted;
+};
+
+
+static struct mnat *mnat;
+static struct sa natpmp_srv, natpmp_extaddr;
+static struct natpmp_req *natpmp_ext;
+
+
+static void natpmp_resp_handler(int err, const struct natpmp_resp *resp,
+ void *arg);
+
+
+static void session_destructor(void *arg)
+{
+ struct mnat_sess *sess = arg;
+
+ list_flush(&sess->medial);
+}
+
+
+static void media_destructor(void *arg)
+{
+ struct mnat_media *m = arg;
+
+ /* Destroy the mapping */
+ if (m->granted) {
+ (void)natpmp_mapping_request(NULL, &natpmp_srv,
+ m->int_port, 0, 0, NULL, NULL);
+ }
+
+ list_unlink(&m->le);
+ tmr_cancel(&m->tmr);
+ mem_deref(m->sdpm);
+ mem_deref(m->natpmp);
+}
+
+
+static void is_complete(struct mnat_sess *sess)
+{
+ struct le *le;
+
+ for (le = sess->medial.head; le; le = le->next) {
+
+ struct mnat_media *m = le->data;
+
+ if (!m->granted)
+ return;
+ }
+
+ if (sess->estabh) {
+ sess->estabh(0, 0, "done", sess->arg);
+
+ sess->estabh = NULL;
+ }
+}
+
+
+static void refresh_timeout(void *arg)
+{
+ struct mnat_media *m = arg;
+
+ m->natpmp = mem_deref(m->natpmp);
+ (void)natpmp_mapping_request(&m->natpmp, &natpmp_srv,
+ m->int_port, 0, m->lifetime,
+ natpmp_resp_handler, m);
+}
+
+
+static void natpmp_resp_handler(int err, const struct natpmp_resp *resp,
+ void *arg)
+{
+ struct mnat_media *m = arg;
+ struct sa map_addr;
+
+ if (err) {
+ warning("natpmp: response error: %m\n", err);
+ return;
+ }
+
+ if (resp->op != NATPMP_OP_MAPPING_UDP)
+ return;
+ if (resp->result != NATPMP_SUCCESS) {
+ warning("natpmp: request failed with result code: %d\n",
+ resp->result);
+ return;
+ }
+
+ if (resp->u.map.int_port != m->int_port) {
+ info("natpmp: ignoring response for internal_port=%u\n",
+ resp->u.map.int_port);
+ return;
+ }
+
+ info("natpmp: mapping granted:"
+ " internal_port=%u, external_port=%u, lifetime=%u\n",
+ resp->u.map.int_port, resp->u.map.ext_port,
+ resp->u.map.lifetime);
+
+ map_addr = natpmp_extaddr;
+ sa_set_port(&map_addr, resp->u.map.ext_port);
+ m->lifetime = resp->u.map.lifetime;
+
+ /* Update SDP media with external IP-address mapping */
+ sdp_media_set_laddr(m->sdpm, &map_addr);
+
+ m->granted = true;
+
+ tmr_start(&m->tmr, m->lifetime * 1000 * 3/4, refresh_timeout, m);
+
+ is_complete(m->sess);
+}
+
+
+static int session_alloc(struct mnat_sess **sessp, struct dnsc *dnsc,
+ int af, const char *srv, uint16_t port,
+ const char *user, const char *pass,
+ struct sdp_session *ss, bool offerer,
+ mnat_estab_h *estabh, void *arg)
+{
+ struct mnat_sess *sess;
+ int err = 0;
+ (void)af;
+ (void)port;
+ (void)user;
+ (void)pass;
+ (void)ss;
+ (void)offerer;
+
+ if (!sessp || !dnsc || !srv || !ss || !estabh)
+ return EINVAL;
+
+ sess = mem_zalloc(sizeof(*sess), session_destructor);
+ if (!sess)
+ return ENOMEM;
+
+ sess->estabh = estabh;
+ sess->arg = arg;
+
+ if (err)
+ mem_deref(sess);
+ else
+ *sessp = sess;
+
+ return err;
+}
+
+
+static int media_alloc(struct mnat_media **mp, struct mnat_sess *sess,
+ int proto, void *sock1, void *sock2,
+ struct sdp_media *sdpm)
+{
+ struct mnat_media *m;
+ struct sa laddr;
+ int err = 0;
+ (void)sock2;
+
+ if (!mp || !sess || !sdpm || proto != IPPROTO_UDP)
+ return EINVAL;
+
+ m = mem_zalloc(sizeof(*m), media_destructor);
+ if (!m)
+ return ENOMEM;
+
+ list_append(&sess->medial, &m->le, m);
+ m->sess = sess;
+ m->sdpm = mem_ref(sdpm);
+ m->lifetime = LIFETIME;
+
+ err = udp_local_get(sock1, &laddr);
+ if (err)
+ goto out;
+
+ m->int_port = sa_port(&laddr);
+
+ info("natpmp: local UDP port is %u\n", m->int_port);
+
+ err = natpmp_mapping_request(&m->natpmp, &natpmp_srv,
+ m->int_port, 0, m->lifetime,
+ natpmp_resp_handler, m);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(m);
+ else {
+ *mp = m;
+ }
+
+ return err;
+}
+
+
+static void extaddr_handler(int err, const struct natpmp_resp *resp, void *arg)
+{
+ (void)arg;
+
+ if (err) {
+ warning("natpmp: external address ERROR: %m\n", err);
+ return;
+ }
+
+ if (resp->result != NATPMP_SUCCESS) {
+ warning("natpmp: external address failed"
+ " with result code: %d\n", resp->result);
+ return;
+ }
+
+ if (resp->op != NATPMP_OP_EXTERNAL)
+ return;
+
+ sa_set_in(&natpmp_extaddr, resp->u.ext_addr, 0);
+
+ info("natpmp: discovered External address: %j\n", &natpmp_extaddr);
+}
+
+
+static bool net_rt_handler(const char *ifname, const struct sa *dst,
+ int dstlen, const struct sa *gw, void *arg)
+{
+ (void)dstlen;
+ (void)arg;
+
+ if (sa_af(dst) != AF_INET)
+ return false;
+
+ if (sa_in(dst) == 0) {
+ natpmp_srv = *gw;
+ sa_set_port(&natpmp_srv, NATPMP_PORT);
+ info("natpmp: found default gateway %j on interface '%s'\n",
+ gw, ifname);
+ return true;
+ }
+
+ return false;
+}
+
+
+static int module_init(void)
+{
+ int err;
+
+ sa_init(&natpmp_srv, AF_INET);
+ sa_set_port(&natpmp_srv, NATPMP_PORT);
+
+ net_rt_list(net_rt_handler, NULL);
+
+ conf_get_sa(conf_cur(), "natpmp_server", &natpmp_srv);
+
+ info("natpmp: using NAT-PMP server at %J\n", &natpmp_srv);
+
+ err = natpmp_external_request(&natpmp_ext, &natpmp_srv,
+ extaddr_handler, NULL);
+ if (err)
+ return err;
+
+ return mnat_register(&mnat, "natpmp", NULL,
+ session_alloc, media_alloc, NULL);
+}
+
+
+static int module_close(void)
+{
+ mnat = mem_deref(mnat);
+ natpmp_ext = mem_deref(natpmp_ext);
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(natpmp) = {
+ "natpmp",
+ "mnat",
+ module_init,
+ module_close,
+};