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/stun/ctrans.c b/src/stun/ctrans.c
new file mode 100644
index 0000000..e164b9d
--- /dev/null
+++ b/src/stun/ctrans.c
@@ -0,0 +1,386 @@
+/**
+ * @file stun/ctrans.c STUN Client transactions
+ *
+ * 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_sa.h>
+#include <re_udp.h>
+#include <re_tcp.h>
+#include <re_srtp.h>
+#include <re_tls.h>
+#include <re_list.h>
+#include <re_tmr.h>
+#include <re_md5.h>
+#include <re_stun.h>
+#include "stun.h"
+
+
+struct stun_ctrans {
+ struct le le;
+ struct tmr tmr;
+ struct sa dst;
+ uint8_t tid[STUN_TID_SIZE];
+ struct stun_ctrans **ctp;
+ uint8_t *key;
+ size_t keylen;
+ void *sock;
+ struct mbuf *mb;
+ size_t pos;
+ struct stun *stun;
+ stun_resp_h *resph;
+ void *arg;
+ int proto;
+ uint32_t txc;
+ uint32_t ival;
+ uint16_t met;
+};
+
+
+static void completed(struct stun_ctrans *ct, int err, uint16_t scode,
+ const char *reason, const struct stun_msg *msg)
+{
+ stun_resp_h *resph = ct->resph;
+ void *arg = ct->arg;
+
+ list_unlink(&ct->le);
+ tmr_cancel(&ct->tmr);
+
+ if (ct->ctp) {
+ *ct->ctp = NULL;
+ ct->ctp = NULL;
+ }
+
+ ct->resph = NULL;
+
+ /* must be destroyed before calling handler */
+ mem_deref(ct);
+
+ if (resph)
+ resph(err, scode, reason, msg, arg);
+}
+
+
+static void destructor(void *arg)
+{
+ struct stun_ctrans *ct = arg;
+
+ list_unlink(&ct->le);
+ tmr_cancel(&ct->tmr);
+ mem_deref(ct->key);
+ mem_deref(ct->sock);
+ mem_deref(ct->mb);
+}
+
+
+static void timeout_handler(void *arg)
+{
+ struct stun_ctrans *ct = arg;
+ const struct stun_conf *cfg = stun_conf(ct->stun);
+ int err = ETIMEDOUT;
+
+ if (ct->txc++ >= cfg->rc)
+ goto error;
+
+ ct->mb->pos = ct->pos;
+
+ err = stun_send(ct->proto, ct->sock, &ct->dst, ct->mb);
+ if (err)
+ goto error;
+
+ ct->ival = (ct->txc >= cfg->rc) ? cfg->rto * cfg->rm : ct->ival * 2;
+
+ tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
+ return;
+
+ error:
+ completed(ct, err, 0, NULL, NULL);
+}
+
+
+static bool match_handler(struct le *le, void *arg)
+{
+ struct stun_ctrans *ct = le->data;
+ struct stun_msg *msg = arg;
+
+ if (ct->met != stun_msg_method(msg))
+ return false;
+
+ if (memcmp(ct->tid, stun_msg_tid(msg), STUN_TID_SIZE))
+ return false;
+
+ return true;
+}
+
+
+static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct stun *stun = arg;
+ (void)src;
+
+ (void)stun_recv(stun, mb);
+}
+
+
+static void tcp_recv_handler(struct mbuf *mb, void *arg)
+{
+ struct stun_ctrans *ct = arg;
+
+ (void)stun_recv(ct->stun, mb);
+}
+
+
+static void tcp_estab_handler(void *arg)
+{
+ struct stun_ctrans *ct = arg;
+ int err;
+
+ err = tcp_send(ct->sock, ct->mb);
+ if (!err)
+ return;
+
+ completed(ct, err, 0, NULL, NULL);
+}
+
+
+static void tcp_close_handler(int err, void *arg)
+{
+ struct stun_ctrans *ct = arg;
+
+ completed(ct, err, 0, NULL, NULL);
+}
+
+
+/**
+ * Handle an incoming STUN message to a Client Transaction
+ *
+ * @param stun STUN instance
+ * @param msg STUN message
+ * @param ua Unknown attributes
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int stun_ctrans_recv(struct stun *stun, const struct stun_msg *msg,
+ const struct stun_unknown_attr *ua)
+{
+ struct stun_errcode ec = {0, "OK"};
+ struct stun_attr *errcode;
+ struct stun_ctrans *ct;
+ int err = 0, herr = 0;
+
+ if (!stun || !msg || !ua)
+ return EINVAL;
+
+ switch (stun_msg_class(msg)) {
+
+ case STUN_CLASS_ERROR_RESP:
+ errcode = stun_msg_attr(msg, STUN_ATTR_ERR_CODE);
+ if (!errcode)
+ herr = EPROTO;
+ else
+ ec = errcode->v.err_code;
+ /*@fallthrough@*/
+
+ case STUN_CLASS_SUCCESS_RESP:
+ ct = list_ledata(list_apply(&stun->ctl, true,
+ match_handler, (void *)msg));
+ if (!ct) {
+ err = ENOENT;
+ break;
+ }
+
+ switch (ec.code) {
+
+ case 401:
+ case 438:
+ break;
+
+ default:
+ if (!ct->key)
+ break;
+
+ err = stun_msg_chk_mi(msg, ct->key, ct->keylen);
+ break;
+ }
+
+ if (err)
+ break;
+
+ if (!herr && ua->typec > 0)
+ herr = EPROTO;
+
+ completed(ct, herr, ec.code, ec.reason, msg);
+ break;
+
+ default:
+ break;
+ }
+
+ return err;
+}
+
+
+int stun_ctrans_request(struct stun_ctrans **ctp, struct stun *stun, int proto,
+ void *sock, const struct sa *dst, struct mbuf *mb,
+ const uint8_t tid[], uint16_t met, const uint8_t *key,
+ size_t keylen, stun_resp_h *resph, void *arg)
+{
+ struct stun_ctrans *ct;
+ int err = 0;
+
+ if (!stun || !mb)
+ return EINVAL;
+
+ ct = mem_zalloc(sizeof(*ct), destructor);
+ if (!ct)
+ return ENOMEM;
+
+ list_append(&stun->ctl, &ct->le, ct);
+ memcpy(ct->tid, tid, STUN_TID_SIZE);
+ ct->proto = proto;
+ ct->sock = mem_ref(sock);
+ ct->mb = mem_ref(mb);
+ ct->pos = mb->pos;
+ ct->stun = stun;
+ ct->met = met;
+
+ if (key) {
+ ct->key = mem_alloc(keylen, NULL);
+ if (!ct->key) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ memcpy(ct->key, key, keylen);
+ ct->keylen = keylen;
+ }
+
+ switch (proto) {
+
+ case IPPROTO_UDP:
+ if (!dst) {
+ err = EINVAL;
+ break;
+ }
+
+ ct->dst = *dst;
+ ct->ival = stun_conf(stun)->rto;
+ tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
+
+ if (!sock) {
+ err = udp_listen((struct udp_sock **)&ct->sock, NULL,
+ udp_recv_handler, stun);
+ if (err)
+ break;
+ }
+
+ ct->txc = 1;
+ err = udp_send(ct->sock, dst, mb);
+ break;
+
+ case IPPROTO_TCP:
+ ct->txc = stun_conf(stun)->rc;
+ tmr_start(&ct->tmr, stun_conf(stun)->ti, timeout_handler, ct);
+ if (sock) {
+ err = tcp_send(sock, mb);
+ break;
+ }
+
+ err = tcp_connect((struct tcp_conn **)&ct->sock, dst,
+ tcp_estab_handler, tcp_recv_handler,
+ tcp_close_handler, ct);
+ break;
+
+#ifdef USE_DTLS
+ case STUN_TRANSP_DTLS:
+ if (!sock) {
+ err = EINVAL;
+ break;
+ }
+
+ ct->ival = stun_conf(stun)->rto;
+ tmr_start(&ct->tmr, ct->ival, timeout_handler, ct);
+
+ ct->txc = 1;
+ err = dtls_send(ct->sock, mb);
+ break;
+#endif
+
+ default:
+ err = EPROTONOSUPPORT;
+ break;
+ }
+
+ out:
+ if (!err) {
+ if (ctp) {
+ ct->ctp = ctp;
+ *ctp = ct;
+ }
+
+ ct->resph = resph;
+ ct->arg = arg;
+ }
+ else
+ mem_deref(ct);
+
+ return err;
+}
+
+
+static bool close_handler(struct le *le, void *arg)
+{
+ struct stun_ctrans *ct = le->data;
+ (void)arg;
+
+ completed(ct, ECONNABORTED, 0, NULL, NULL);
+
+ return false;
+}
+
+
+void stun_ctrans_close(struct stun *stun)
+{
+ if (!stun)
+ return;
+
+ (void)list_apply(&stun->ctl, true, close_handler, NULL);
+}
+
+
+static bool debug_handler(struct le *le, void *arg)
+{
+ struct stun_ctrans *ct = le->data;
+ struct re_printf *pf = arg;
+ int err = 0;
+
+ err |= re_hprintf(pf, " method=%s", stun_method_name(ct->met));
+ err |= re_hprintf(pf, " tid=%w", ct->tid, sizeof(ct->tid));
+ err |= re_hprintf(pf, " rto=%ums", stun_conf(ct->stun)->rto);
+ err |= re_hprintf(pf, " tmr=%llu", tmr_get_expire(&ct->tmr));
+ err |= re_hprintf(pf, " n=%u", ct->txc);
+ err |= re_hprintf(pf, " interval=%u", ct->ival);
+ err |= re_hprintf(pf, "\n");
+
+ return 0 != err;
+}
+
+
+int stun_ctrans_debug(struct re_printf *pf, const struct stun *stun)
+{
+ int err;
+
+ if (!stun)
+ return 0;
+
+ err = re_hprintf(pf, "STUN client transactions: (%u)\n",
+ list_count(&stun->ctl));
+
+ (void)list_apply(&stun->ctl, true, debug_handler, pf);
+
+ return err;
+}