summaryrefslogtreecommitdiff
path: root/modules/stun/stun.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/stun/stun.c')
-rw-r--r--modules/stun/stun.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/modules/stun/stun.c b/modules/stun/stun.c
new file mode 100644
index 0000000..d7edf94
--- /dev/null
+++ b/modules/stun/stun.c
@@ -0,0 +1,251 @@
+/**
+ * @file stun.c STUN Module for Media NAT-traversal
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re.h>
+#include <baresip.h>
+
+
+/**
+ * @defgroup stun stun
+ *
+ * Session Traversal Utilities for NAT (STUN) for media NAT traversal
+ */
+
+
+enum {LAYER = 0, INTERVAL = 30};
+
+struct mnat_sess {
+ struct list medial;
+ struct sa srv;
+ struct stun_dns *dnsq;
+ mnat_estab_h *estabh;
+ void *arg;
+ int mediac;
+};
+
+
+struct mnat_media {
+ struct le le;
+ struct sa addr1;
+ struct sa addr2;
+ struct mnat_sess *sess;
+ struct sdp_media *sdpm;
+ struct stun_keepalive *ska1;
+ struct stun_keepalive *ska2;
+ void *sock1;
+ void *sock2;
+ int proto;
+};
+
+
+static struct mnat *mnat;
+
+
+static void session_destructor(void *arg)
+{
+ struct mnat_sess *sess = arg;
+
+ list_flush(&sess->medial);
+ mem_deref(sess->dnsq);
+}
+
+
+static void media_destructor(void *arg)
+{
+ struct mnat_media *m = arg;
+
+ list_unlink(&m->le);
+ mem_deref(m->sdpm);
+ mem_deref(m->ska1);
+ mem_deref(m->ska2);
+ mem_deref(m->sock1);
+ mem_deref(m->sock2);
+}
+
+
+static void mapped_handler1(int err, const struct sa *map_addr, void *arg)
+{
+ struct mnat_media *m = arg;
+
+ if (!err) {
+
+ sdp_media_set_laddr(m->sdpm, map_addr);
+
+ m->addr1 = *map_addr;
+
+ if (m->ska2 && !sa_isset(&m->addr2, SA_ALL))
+ return;
+
+ if (--m->sess->mediac)
+ return;
+ }
+
+ m->sess->estabh(err, 0, NULL, m->sess->arg);
+}
+
+
+static void mapped_handler2(int err, const struct sa *map_addr, void *arg)
+{
+ struct mnat_media *m = arg;
+
+ if (!err) {
+
+ sdp_media_set_laddr_rtcp(m->sdpm, map_addr);
+
+ m->addr2 = *map_addr;
+
+ if (m->ska1 && !sa_isset(&m->addr1, SA_ALL))
+ return;
+
+ if (--m->sess->mediac)
+ return;
+ }
+
+ m->sess->estabh(err, 0, NULL, m->sess->arg);
+}
+
+
+static int media_start(struct mnat_sess *sess, struct mnat_media *m)
+{
+ int err = 0;
+
+ if (m->sock1) {
+ err |= stun_keepalive_alloc(&m->ska1, m->proto,
+ m->sock1, LAYER, &sess->srv, NULL,
+ mapped_handler1, m);
+ }
+ if (m->sock2) {
+ err |= stun_keepalive_alloc(&m->ska2, m->proto,
+ m->sock2, LAYER, &sess->srv, NULL,
+ mapped_handler2, m);
+ }
+ if (err)
+ return err;
+
+ stun_keepalive_enable(m->ska1, INTERVAL);
+ stun_keepalive_enable(m->ska2, INTERVAL);
+
+ return 0;
+}
+
+
+static void dns_handler(int err, const struct sa *srv, void *arg)
+{
+ struct mnat_sess *sess = arg;
+ struct le *le;
+
+ if (err)
+ goto out;
+
+ sess->srv = *srv;
+
+ for (le=sess->medial.head; le; le=le->next) {
+
+ struct mnat_media *m = le->data;
+
+ err = media_start(sess, m);
+ if (err)
+ goto out;
+ }
+
+ return;
+
+ out:
+ sess->estabh(err, 0, NULL, sess->arg);
+}
+
+
+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;
+ (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;
+
+ err = stun_server_discover(&sess->dnsq, dnsc,
+ stun_usage_binding, stun_proto_udp,
+ af, srv, port, dns_handler, sess);
+
+ 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;
+ int err = 0;
+
+ if (!mp || !sess || !sdpm)
+ return EINVAL;
+
+ m = mem_zalloc(sizeof(*m), media_destructor);
+ if (!m)
+ return ENOMEM;
+
+ list_append(&sess->medial, &m->le, m);
+ m->sdpm = mem_ref(sdpm);
+ m->sess = sess;
+ m->sock1 = mem_ref(sock1);
+ m->sock2 = mem_ref(sock2);
+ m->proto = proto;
+
+ if (sa_isset(&sess->srv, SA_ALL))
+ err = media_start(sess, m);
+
+ if (err)
+ mem_deref(m);
+ else {
+ *mp = m;
+ ++sess->mediac;
+ }
+
+ return err;
+}
+
+
+static int module_init(void)
+{
+ return mnat_register(&mnat, "stun", NULL, session_alloc, media_alloc,
+ NULL);
+}
+
+
+static int module_close(void)
+{
+ mnat = mem_deref(mnat);
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(stun) = {
+ "stun",
+ "mnat",
+ module_init,
+ module_close,
+};