Squashed 'third_party/rawrtc/rew/' content from commit 24c91fd83
Change-Id: Ica2fcc790472ecd5b195d20da982c4e84139cbdd
git-subtree-dir: third_party/rawrtc/rew
git-subtree-split: 24c91fd839b40b11f727c902fa46d20874da33fb
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..23f48de
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+
+build*
+librew.pc
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4f4a140
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,19 @@
+language: c
+
+os:
+ - linux
+ - osx
+
+before_install:
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -qq ; fi
+ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
+
+install:
+ - git clone https://github.com/creytiv/re.git
+ - cd re && make && sudo make install && cd ..
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo ldconfig ; fi
+ - wget "https://github.com/alfredh/pytools/raw/master/ccheck.py"
+
+script:
+ - make EXTRA_CFLAGS=-Werror
+ - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then python2 ccheck.py ; fi
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..63880e9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,147 @@
+#
+# Makefile
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+# Master version number
+VER_MAJOR := 0
+VER_MINOR := 5
+VER_PATCH := 0
+
+PROJECT := rew
+VERSION := 0.5.0
+OPT_SPEED := 1
+
+LIBRE_MK := $(shell [ -f ../re/mk/re.mk ] && \
+ echo "../re/mk/re.mk")
+ifeq ($(LIBRE_MK),)
+LIBRE_MK := $(shell [ -f /usr/share/re/re.mk ] && \
+ echo "/usr/share/re/re.mk")
+endif
+ifeq ($(LIBRE_MK),)
+LIBRE_MK := $(shell [ -f /usr/local/share/re/re.mk ] && \
+ echo "/usr/local/share/re/re.mk")
+endif
+
+include $(LIBRE_MK)
+
+# List of modules
+MODULES += shim
+MODULES += trice
+MODULES += pcp
+
+LIBS += -lm
+
+INSTALL := install
+ifndef PREFIX
+ifeq ($(DESTDIR),)
+PREFIX := /usr/local
+else
+PREFIX := /usr
+endif
+endif
+ifeq ($(LIBDIR),)
+LIBDIR := $(PREFIX)/lib
+endif
+INCDIR := $(PREFIX)/include/rew
+CFLAGS += -I$(LIBRE_INC) -Iinclude
+
+
+MODMKS := $(patsubst %,src/%/mod.mk,$(MODULES))
+SHARED := librew$(LIB_SUFFIX)
+STATIC := librew.a
+
+
+include $(MODMKS)
+
+
+OBJS ?= $(patsubst %.c,$(BUILD)/%.o,$(filter %.c,$(SRCS)))
+OBJS += $(patsubst %.S,$(BUILD)/%.o,$(filter %.S,$(SRCS)))
+
+
+all: $(SHARED) $(STATIC)
+
+
+-include $(OBJS:.o=.d)
+
+
+$(SHARED): $(OBJS)
+ @echo " LD $@"
+ @$(LD) $(LFLAGS) $(SH_LFLAGS) $^ -L$(LIBRE_SO) -lre $(LIBS) -o $@
+
+
+$(STATIC): $(OBJS)
+ @echo " AR $@"
+ @$(AR) $(AFLAGS) $@ $^
+ifneq ($(RANLIB),)
+ @$(RANLIB) $@
+endif
+
+librew.pc:
+ @echo 'prefix='$(PREFIX) > librew.pc
+ @echo 'exec_prefix=$${prefix}' >> librew.pc
+ @echo 'libdir=$${prefix}/lib' >> librew.pc
+ @echo 'includedir=$${prefix}/include/rew' >> librew.pc
+ @echo '' >> librew.pc
+ @echo 'Name: librew' >> librew.pc
+ @echo 'Description: ' >> librew.pc
+ @echo 'Version: '$(VERSION) >> librew.pc
+ @echo 'URL: https://github.com/alfredh/rew' >> librew.pc
+ @echo 'Libs: -L$${libdir} -lrew' >> librew.pc
+ @echo 'Libs.private: -L$${libdir} -lrew ${LIBS}' >> librew.pc
+ @echo 'Cflags: -I$${includedir}' >> librew.pc
+
+$(BUILD)/%.o: src/%.c $(BUILD) Makefile $(MK) $(MODMKS)
+ @echo " CC $@"
+ @$(CC) $(CFLAGS) -c $< -o $@ $(DFLAGS)
+
+
+$(BUILD)/%.o: src/%.S $(BUILD) Makefile $(MK) $(MODMKS)
+ @echo " AS $@"
+ @$(CC) $(CFLAGS) -c $< -o $@ $(DFLAGS)
+
+
+$(BUILD): Makefile $(MK) $(MODMKS)
+ @mkdir -p $(patsubst %,$(BUILD)/%,$(sort $(dir $(SRCS))))
+ @touch $@
+
+
+.PHONY: clean
+clean:
+ @rm -rf $(SHARED) $(STATIC) librew.pc test.d test.o test $(BUILD)
+
+
+install: $(SHARED) $(STATIC) librew.pc
+ @mkdir -p $(DESTDIR)$(LIBDIR) $(DESTDIR)$(LIBDIR)/pkgconfig \
+ $(DESTDIR)$(INCDIR)
+ $(INSTALL) -m 0644 $(shell find include -name "*.h") \
+ $(DESTDIR)$(INCDIR)
+ $(INSTALL) -m 0755 $(SHARED) $(DESTDIR)$(LIBDIR)
+ $(INSTALL) -m 0755 $(STATIC) $(DESTDIR)$(LIBDIR)
+ $(INSTALL) -m 0644 librew.pc $(DESTDIR)$(LIBDIR)/pkgconfig
+
+install-static: $(STATIC) librew.pc
+ @mkdir -p $(DESTDIR)$(LIBDIR) $(DESTDIR)$(LIBDIR)/pkgconfig \
+ $(DESTDIR)$(INCDIR)
+ $(INSTALL) -m 0644 $(shell find include -name "*.h") \
+ $(DESTDIR)$(INCDIR)
+ $(INSTALL) -m 0755 $(STATIC) $(DESTDIR)$(LIBDIR)
+ $(INSTALL) -m 0644 librew.pc $(DESTDIR)$(LIBDIR)/pkgconfig
+
+.PHONY: uninstall
+uninstall:
+ @rm -rf $(DESTDIR)$(INCDIR)
+ @rm -f $(DESTDIR)$(LIBDIR)/$(SHARED)
+ @rm -f $(DESTDIR)$(LIBDIR)/$(STATIC)
+ @rm -f $(DESTDIR)$(LIBDIR)/pkgconfig/librew.pc
+
+-include test.d
+
+test.o: test.c
+ @echo " CC $@"
+ @$(CC) $(CFLAGS) -c $< -o $@ $(DFLAGS)
+
+test$(BIN_SUFFIX): test.o $(SHARED) $(STATIC)
+ @echo " LD $@"
+ @$(LD) $(LFLAGS) $< -L. -lrew -lre $(LIBS) -o $@
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..888be89
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+# librew
+
+[](https://travis-ci.org/alfredh/rew)
+
+A staging library to libre with experimental code
+
+
+# Building
+
+You need to install libre first (http://www.creytiv.com/re.html)
+Then:
+
+```
+$ make
+$ sudo make install
+```
+
+
+## Modules:
+
+The following modules are implemented:
+
+### trice -- Trickle ICE
+
+- https://tools.ietf.org/html/draft-ietf-ice-trickle-02
+- https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00
+
+### pcp -- Port Control Protocol
+
+- https://tools.ietf.org/html/rfc6887
+- https://tools.ietf.org/html/rfc7220
+
+
+# Testing
+
+The retest program has a branch for testing librew:
+
+https://github.com/creytiv/retest/tree/rew
diff --git a/docs/COPYING b/docs/COPYING
new file mode 100644
index 0000000..6c68ed7
--- /dev/null
+++ b/docs/COPYING
@@ -0,0 +1,32 @@
+Copyright (c) 2010 - 2015, Alfred E. Heggestad
+Copyright (c) 2010 - 2015, Richard Aas
+Copyright (c) 2010 - 2015, Creytiv.com
+All rights reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the Creytiv.com nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/include/re_pcp.h b/include/re_pcp.h
new file mode 100644
index 0000000..a98d5d4
--- /dev/null
+++ b/include/re_pcp.h
@@ -0,0 +1,200 @@
+/**
+ * @file re_pcp.h PCP - Port Control Protocol (RFC 6887)
+ *
+ * Copyright (C) 2010 - 2014 Creytiv.com
+ */
+
+
+/*
+ * The following specifications are implemented:
+ *
+ * RFC 6887
+ * draft-ietf-pcp-description-option-02
+ * draft-cheshire-pcp-unsupp-family
+ *
+ */
+
+
+/** PCP Version numbers */
+enum {
+ PCP_VERSION = 2,
+};
+
+/* PCP port numbers */
+enum {
+ PCP_PORT_CLI = 5350, /* for ANNOUNCE notifications */
+ PCP_PORT_SRV = 5351,
+};
+
+/** PCP Protocol sizes */
+enum {
+ PCP_HDR_SZ = 24,
+ PCP_NONCE_SZ = 12,
+ PCP_MAP_SZ = 36,
+ PCP_PEER_SZ = 56,
+
+ PCP_MIN_PACKET = 24,
+ PCP_MAX_PACKET = 1100
+};
+
+enum pcp_opcode {
+ PCP_ANNOUNCE = 0,
+ PCP_MAP = 1,
+ PCP_PEER = 2,
+};
+
+enum pcp_result {
+ PCP_SUCCESS = 0,
+ PCP_UNSUPP_VERSION = 1,
+ PCP_NOT_AUTHORIZED = 2,
+ PCP_MALFORMED_REQUEST = 3,
+ PCP_UNSUPP_OPCODE = 4,
+ PCP_UNSUPP_OPTION = 5,
+ PCP_MALFORMED_OPTION = 6,
+ PCP_NETWORK_FAILURE = 7,
+ PCP_NO_RESOURCES = 8,
+ PCP_UNSUPP_PROTOCOL = 9,
+ PCP_USER_EX_QUOTA = 10,
+ PCP_CANNOT_PROVIDE_EXTERNAL = 11,
+ PCP_ADDRESS_MISMATCH = 12,
+ PCP_EXCESSIVE_REMOTE_PEERS = 13,
+ PCP_UNSUPP_FAMILY = 14 /*draft-cheshire-pcp-unsupp-family*/
+};
+
+enum pcp_option_code {
+ PCP_OPTION_THIRD_PARTY = 1,
+ PCP_OPTION_PREFER_FAILURE = 2,
+ PCP_OPTION_FILTER = 3,
+ PCP_OPTION_DESCRIPTION = 128, /* RFC 7220 */
+};
+
+/* forward declarations */
+struct udp_sock;
+
+/** Defines a PCP option */
+struct pcp_option {
+ struct le le;
+ enum pcp_option_code code;
+ union {
+ struct sa third_party; /* Internal IP-address */
+ struct pcp_option_filter {
+ uint8_t prefix_length;
+ struct sa remote_peer;
+ } filter;
+ char *description;
+ } u;
+};
+
+/**
+ * Defines a complete and decoded PCP request/response.
+ *
+ * A PCP message consist of a header, and optional payload and options:
+ *
+ * [ Header ]
+ * ( Opcode Payload )
+ * ( PCP Options )
+ *
+ */
+struct pcp_msg {
+
+ /** PCP Common Header */
+ struct pcp_hdr {
+ uint8_t version; /**< PCP Protocol version 2 */
+ unsigned resp:1; /**< R-bit; 0=Request, 1=Response */
+ uint8_t opcode; /**< A 7-bit opcode */
+ uint32_t lifetime; /**< Lifetime in [seconds] */
+
+ /* request: */
+ struct sa cli_addr; /**< Client's IP Address (SA_ADDR) */
+
+ /* response: */
+ enum pcp_result result; /**< Result code for this response */
+ uint32_t epoch; /**< Server's Epoch Time [seconds] */
+ } hdr;
+
+ /** PCP Opcode-specific payload */
+ union pcp_payload {
+ struct pcp_map {
+ uint8_t nonce[PCP_NONCE_SZ]; /**< Mapping Nonce */
+ uint8_t proto; /**< IANA protocol */
+ uint16_t int_port; /**< Internal Port */
+ struct sa ext_addr; /**< External Address */
+ } map;
+ struct pcp_peer {
+ struct pcp_map map; /**< Common with MAP */
+ struct sa remote_addr; /**< Remote address */
+ } peer;
+ } pld;
+
+ /** List of PCP Options (struct pcp_option) */
+ struct list optionl;
+};
+
+/** PCP request configuration */
+struct pcp_conf {
+ uint32_t irt; /**< Initial retransmission time [seconds] */
+ uint32_t mrc; /**< Maximum retransmission count */
+ uint32_t mrt; /**< Maximum retransmission time [seconds] */
+ uint32_t mrd; /**< Maximum retransmission duration [seconds] */
+};
+
+
+/* request */
+
+struct pcp_request;
+
+typedef void (pcp_resp_h)(int err, struct pcp_msg *msg, void *arg);
+
+int pcp_request(struct pcp_request **reqp, const struct pcp_conf *conf,
+ const struct sa *pcp_server, enum pcp_opcode opcode,
+ uint32_t lifetime, const void *payload,
+ pcp_resp_h *resph, void *arg, uint32_t optionc, ...);
+void pcp_force_refresh(struct pcp_request *req);
+
+
+/* reply */
+
+int pcp_reply(struct udp_sock *us, const struct sa *dst, struct mbuf *req,
+ enum pcp_opcode opcode, enum pcp_result result,
+ uint32_t lifetime, uint32_t epoch_time, const void *payload);
+
+
+/* msg */
+
+typedef bool (pcp_option_h)(const struct pcp_option *opt, void *arg);
+
+int pcp_msg_decode(struct pcp_msg **msgp, struct mbuf *mb);
+int pcp_msg_printhdr(struct re_printf *pf, const struct pcp_msg *msg);
+int pcp_msg_print(struct re_printf *pf, const struct pcp_msg *msg);
+struct pcp_option *pcp_msg_option(const struct pcp_msg *msg,
+ enum pcp_option_code code);
+struct pcp_option *pcp_msg_option_apply(const struct pcp_msg *msg,
+ pcp_option_h *h, void *arg);
+const void *pcp_msg_payload(const struct pcp_msg *msg);
+
+
+/* option */
+
+int pcp_option_encode(struct mbuf *mb, enum pcp_option_code code,
+ const void *v);
+int pcp_option_decode(struct pcp_option **optp, struct mbuf *mb);
+int pcp_option_print(struct re_printf *pf, const struct pcp_option *opt);
+
+
+/* encode */
+
+int pcp_msg_req_vencode(struct mbuf *mb, enum pcp_opcode opcode,
+ uint32_t lifetime, const struct sa *cli_addr,
+ const void *payload, uint32_t optionc, va_list ap);
+int pcp_msg_req_encode(struct mbuf *mb, enum pcp_opcode opcode,
+ uint32_t lifetime, const struct sa *cli_addr,
+ const void *payload, uint32_t optionc, ...);
+
+
+/* pcp */
+
+int pcp_ipaddr_encode(struct mbuf *mb, const struct sa *sa);
+int pcp_ipaddr_decode(struct mbuf *mb, struct sa *sa);
+const char *pcp_result_name(enum pcp_result result);
+const char *pcp_opcode_name(enum pcp_opcode opcode);
+const char *pcp_proto_name(int proto);
diff --git a/include/re_shim.h b/include/re_shim.h
new file mode 100644
index 0000000..f5966a6
--- /dev/null
+++ b/include/re_shim.h
@@ -0,0 +1,15 @@
+
+
+/* RFC 4571 */
+
+
+enum { SHIM_HDR_SIZE = 2 };
+
+struct shim;
+
+typedef bool (shim_frame_h)(struct mbuf *mb, void *arg);
+
+
+int shim_insert(struct shim **shimp, struct tcp_conn *tc, int layer,
+ shim_frame_h *frameh, void *arg);
+int shim_debug(struct re_printf *pf, const struct shim *shim);
diff --git a/include/re_trice.h b/include/re_trice.h
new file mode 100644
index 0000000..9eba6c7
--- /dev/null
+++ b/include/re_trice.h
@@ -0,0 +1,167 @@
+/**
+ * @file re_ice.h Interface to Interactive Connectivity Establishment (ICE)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+/** ICE Configuration */
+struct trice_conf {
+ enum ice_nomination nom; /**< Nomination algorithm */
+ bool debug; /**< Enable ICE debugging */
+ bool trace; /**< Enable tracing of Connectivity
+ checks */
+ bool ansi; /**< Enable ANSI colors for debug
+ output */
+ bool enable_prflx; /**< Enable Peer-Reflexive candidates */
+ bool optimize_loopback_pairing;/**< Reduce candidate pairs when
+ using loopback addresses */
+};
+
+struct trice;
+struct ice_lcand;
+struct ice_candpair;
+struct stun_conf;
+
+
+typedef bool (ice_cand_recv_h)(struct ice_lcand *lcand,
+ int proto, void *sock, const struct sa *src,
+ struct mbuf *mb, void *arg);
+
+
+/** Local candidate */
+struct ice_lcand {
+ struct ice_cand_attr attr; /**< Base class (inheritance) */
+ struct le le; /**< List element */
+
+ /* Base-address only set for SRFLX, PRFLX, RELAY */
+ struct sa base_addr; /* IP-address of "base" candidate (optional) */
+
+ struct udp_sock *us;
+ struct udp_helper *uh;
+ struct tcp_sock *ts; /* TCP for simultaneous-open or passive. */
+ char ifname[32]; /**< Network interface, for diagnostics */
+ int layer;
+ ice_cand_recv_h *recvh;
+ void *arg;
+
+ // todo: remove
+ struct trice *icem; /* parent */
+
+ struct {
+ size_t n_tx;
+ size_t n_rx;
+ } stats;
+};
+
+/** Remote candidate */
+struct ice_rcand {
+ struct ice_cand_attr attr; /**< Base class (inheritance) */
+ struct le le; /**< List element */
+};
+
+
+/** Defines a candidate pair */
+struct ice_candpair {
+ struct le le; /**< List element */
+ struct ice_lcand *lcand; /**< Local candidate */
+ struct ice_rcand *rcand; /**< Remote candidate */
+ enum ice_candpair_state state;/**< Candidate pair state */
+ uint64_t pprio; /**< Pair priority */
+ //bool def; /**< Default flag */
+ bool valid; /**< Valid flag */
+ bool nominated; /**< Nominated flag */
+ bool estab;
+ bool trigged;
+ int err; /**< Saved error code, if failed */
+ uint16_t scode; /**< Saved STUN code, if failed */
+
+ struct tcp_conn *tc;
+
+ struct ice_tcpconn *conn; /* the TCP-connection used */
+};
+
+
+typedef void (trice_estab_h)(struct ice_candpair *pair,
+ const struct stun_msg *msg, void *arg);
+
+
+typedef void (trice_failed_h)(int err, uint16_t scode,
+ struct ice_candpair *pair, void *arg);
+
+
+int trice_alloc(struct trice **icemp, const struct trice_conf *conf,
+ enum ice_role role, const char *lufrag, const char *lpwd);
+int trice_set_remote_ufrag(struct trice *icem, const char *rufrag);
+int trice_set_remote_pwd(struct trice *icem, const char *rpwd);
+int trice_set_software(struct trice *icem, const char *sw);
+int trice_set_role(struct trice *trice, enum ice_role role);
+enum ice_role trice_local_role(const struct trice *icem);
+int trice_debug(struct re_printf *pf, const struct trice *icem);
+struct trice_conf *trice_conf(struct trice *icem);
+
+
+/* Candidates (common) */
+int trice_cand_print(struct re_printf *pf, const struct ice_cand_attr *cand);
+enum ice_tcptype ice_tcptype_reverse(enum ice_tcptype type);
+const char *ice_tcptype_name(enum ice_tcptype tcptype);
+enum ice_cand_type ice_cand_type_base(enum ice_cand_type type);
+
+
+/* Local candidates */
+int trice_lcand_add(struct ice_lcand **lcandp, struct trice *icem,
+ unsigned compid, int proto, uint32_t prio,
+ const struct sa *addr, const struct sa *base_addr,
+ enum ice_cand_type type, const struct sa *rel_addr,
+ enum ice_tcptype tcptype,
+ void *sock, int layer);
+struct list *trice_lcandl(const struct trice *icem);
+struct ice_lcand *trice_lcand_find(struct trice *icem,
+ enum ice_cand_type type,
+ unsigned compid, int proto,
+ const struct sa *addr);
+struct ice_lcand *trice_lcand_find2(const struct trice *icem,
+ enum ice_cand_type type, int af);
+void *trice_lcand_sock(struct trice *icem, const struct ice_lcand *lcand);
+void trice_lcand_recv_packet(struct ice_lcand *lcand,
+ const struct sa *src, struct mbuf *mb);
+
+
+/* Remote candidate */
+struct list *trice_rcandl(const struct trice *icem);
+int trice_rcand_add(struct ice_rcand **rcandp, struct trice *icem,
+ unsigned compid, const char *foundation, int proto,
+ uint32_t prio, const struct sa *addr,
+ enum ice_cand_type type, enum ice_tcptype tcptype);
+struct ice_rcand *trice_rcand_find(struct trice *icem, unsigned compid,
+ int proto, const struct sa *addr);
+
+
+/* ICE Candidate pairs */
+struct list *trice_checkl(const struct trice *icem);
+struct list *trice_validl(const struct trice *icem);
+struct ice_candpair *trice_candpair_find_state(const struct list *lst,
+ enum ice_candpair_state state);
+int trice_candpair_debug(struct re_printf *pf, const struct ice_candpair *cp);
+int trice_candpairs_debug(struct re_printf *pf, bool ansi_output,
+ const struct list *list);
+
+
+/* ICE checklist */
+void trice_checklist_set_waiting(struct trice *icem);
+int trice_checklist_start(struct trice *icem, struct stun *stun,
+ uint32_t interval,
+ trice_estab_h *estabh, trice_failed_h *failh,
+ void *arg);
+void trice_checklist_stop(struct trice *icem);
+bool trice_checklist_isrunning(const struct trice *icem);
+bool trice_checklist_iscompleted(const struct trice *icem);
+
+
+/* ICE Conncheck */
+int trice_conncheck_send(struct trice *icem, struct ice_candpair *pair,
+ bool use_cand);
+
+/* Port range */
+int trice_set_port_range(struct trice *trice,
+ uint16_t min_port, uint16_t max_port);
diff --git a/include/rew.h b/include/rew.h
new file mode 100644
index 0000000..afeeb68
--- /dev/null
+++ b/include/rew.h
@@ -0,0 +1,24 @@
+/**
+ * @file rew.h Wrapper for librew headers
+ *
+ * Copyright (C) 2010 - 2015 Creytiv.com
+ */
+
+#ifndef REW_H__
+#define REW_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include "re_pcp.h"
+#include "re_shim.h"
+#include "re_trice.h"
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/pcp/README b/src/pcp/README
new file mode 100644
index 0000000..620a614
--- /dev/null
+++ b/src/pcp/README
@@ -0,0 +1,13 @@
+PCP README:
+----------
+
+Port Control Protocol (PCP) as of RFC 6887
+
+
+
+
+other PCP implementations:
+
+ https://github.com/libpcp/pcp
+
+
diff --git a/src/pcp/mod.mk b/src/pcp/mod.mk
new file mode 100644
index 0000000..2a1dc95
--- /dev/null
+++ b/src/pcp/mod.mk
@@ -0,0 +1,12 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 - 2016 Creytiv.com
+#
+
+SRCS += pcp/msg.c
+SRCS += pcp/option.c
+SRCS += pcp/payload.c
+SRCS += pcp/pcp.c
+SRCS += pcp/reply.c
+SRCS += pcp/request.c
diff --git a/src/pcp/msg.c b/src/pcp/msg.c
new file mode 100644
index 0000000..327f254
--- /dev/null
+++ b/src/pcp/msg.c
@@ -0,0 +1,378 @@
+/**
+ * @file pcp/msg.c PCP messages
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_pcp.h>
+#include "pcp.h"
+
+
+static int pcp_map_decode(struct pcp_map *map, struct mbuf *mb)
+{
+ uint16_t port;
+ int err;
+
+ if (!map || !mb)
+ return EINVAL;
+
+ if (mbuf_get_left(mb) < PCP_MAP_SZ)
+ return EBADMSG;
+
+ (void)mbuf_read_mem(mb, map->nonce, sizeof(map->nonce));
+ map->proto = mbuf_read_u8(mb); mbuf_advance(mb, 3);
+ map->int_port = ntohs(mbuf_read_u16(mb));
+
+ port = ntohs(mbuf_read_u16(mb));
+ err = pcp_ipaddr_decode(mb, &map->ext_addr);
+ sa_set_port(&map->ext_addr, port);
+
+ return err;
+}
+
+
+static int pcp_peer_decode(struct pcp_peer *peer, struct mbuf *mb)
+{
+ uint16_t port;
+ int err = 0;
+
+ if (!peer || !mb)
+ return EINVAL;
+
+ if (mbuf_get_left(mb) < PCP_PEER_SZ)
+ return EBADMSG;
+
+ /* note: the MAP and PEER opcodes are quite similar */
+ err = pcp_map_decode(&peer->map, mb);
+ if (err)
+ return err;
+
+ port = ntohs(mbuf_read_u16(mb)); mbuf_advance(mb, 2);
+ err |= pcp_ipaddr_decode(mb, &peer->remote_addr);
+ sa_set_port(&peer->remote_addr, port);
+
+ return err;
+}
+
+
+static void destructor(void *arg)
+{
+ struct pcp_msg *msg = arg;
+
+ list_flush(&msg->optionl);
+}
+
+
+static int pcp_header_encode_request(struct mbuf *mb, enum pcp_opcode opcode,
+ uint32_t req_lifetime, const struct sa *int_addr)
+{
+ int err = 0;
+
+ if (!mb || !int_addr)
+ return EINVAL;
+
+ err |= mbuf_write_u8(mb, PCP_VERSION);
+ err |= mbuf_write_u8(mb, opcode);
+ err |= mbuf_write_u16(mb, 0x0000);
+ err |= mbuf_write_u32(mb, htonl(req_lifetime));
+ err |= pcp_ipaddr_encode(mb, int_addr);
+
+ return err;
+}
+
+
+static int pcp_header_decode(struct pcp_hdr *hdr, struct mbuf *mb)
+{
+ uint8_t b;
+
+ if (!hdr || !mb)
+ return EINVAL;
+
+ if (mbuf_get_left(mb) < PCP_HDR_SZ)
+ return EBADMSG;
+
+ hdr->version = mbuf_read_u8(mb);
+
+ if (hdr->version != PCP_VERSION) {
+ (void)re_fprintf(stderr, "pcp: unknown version %u\n",
+ hdr->version);
+ return EPROTO;
+ }
+
+ b = mbuf_read_u8(mb);
+ hdr->resp = b>>7;
+ hdr->opcode = b & 0x7f;
+
+ (void)mbuf_read_u8(mb);
+ b = mbuf_read_u8(mb);
+
+ if (hdr->resp)
+ hdr->result = b;
+
+ hdr->lifetime = ntohl(mbuf_read_u32(mb));
+
+ if (hdr->resp) {
+ hdr->epoch = ntohl(mbuf_read_u32(mb));
+ mbuf_advance(mb, 12);
+ }
+ else { /* Request */
+ (void)pcp_ipaddr_decode(mb, &hdr->cli_addr);
+ }
+
+ return 0;
+}
+
+
+int pcp_msg_req_vencode(struct mbuf *mb, enum pcp_opcode opcode,
+ uint32_t lifetime, const struct sa *cli_addr,
+ const void *payload, uint32_t optionc, va_list ap)
+{
+ uint32_t i;
+ int err;
+
+ if (!mb || !cli_addr)
+ return EINVAL;
+
+ err = pcp_header_encode_request(mb, opcode, lifetime, cli_addr);
+ if (err)
+ return err;
+
+ if (payload) {
+ err = pcp_payload_encode(mb, opcode, payload);
+ if (err)
+ return err;
+ }
+
+ /* encode options */
+ for (i=0; i<optionc; i++) {
+
+ enum pcp_option_code code = va_arg(ap, int);
+ const void *v = va_arg(ap, const void *);
+
+ if (!v)
+ continue;
+
+ err |= pcp_option_encode(mb, code, v);
+ }
+
+ return err;
+}
+
+
+int pcp_msg_req_encode(struct mbuf *mb, enum pcp_opcode opcode,
+ uint32_t lifetime, const struct sa *cli_addr,
+ const void *payload, uint32_t optionc, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, optionc);
+ err = pcp_msg_req_vencode(mb, opcode, lifetime, cli_addr,
+ payload, optionc, ap);
+ va_end(ap);
+
+ return err;
+}
+
+
+int pcp_msg_decode(struct pcp_msg **msgp, struct mbuf *mb)
+{
+ struct pcp_msg *msg;
+ size_t len, pos;
+ int err;
+
+ if (!msgp || !mb)
+ return EINVAL;
+
+ len = mbuf_get_left(mb);
+ if (len < PCP_MIN_PACKET || len > PCP_MAX_PACKET || len&3)
+ return EBADMSG;
+
+ msg = mem_zalloc(sizeof(*msg), destructor);
+ if (!msg)
+ return ENOMEM;
+
+ pos = mb->pos;
+ err = pcp_header_decode(&msg->hdr, mb);
+ if (err)
+ goto out;
+
+ switch (msg->hdr.opcode) {
+
+ case PCP_MAP:
+ err = pcp_map_decode(&msg->pld.map, mb);
+ break;
+
+ case PCP_PEER:
+ err = pcp_peer_decode(&msg->pld.peer, mb);
+ break;
+
+ default:
+ break;
+ }
+ if (err)
+ goto out;
+
+ /* Decode PCP Options */
+ while (mbuf_get_left(mb) >= 4) {
+
+ struct pcp_option *opt;
+
+ err = pcp_option_decode(&opt, mb);
+ if (err)
+ goto out;
+
+ list_append(&msg->optionl, &opt->le, opt);
+ }
+
+ out:
+ if (err) {
+ mb->pos = pos;
+ mem_deref(msg);
+ }
+ else
+ *msgp = msg;
+
+ return err;
+}
+
+
+struct pcp_option *pcp_msg_option(const struct pcp_msg *msg,
+ enum pcp_option_code code)
+{
+ struct le *le = msg ? list_head(&msg->optionl) : NULL;
+
+ while (le) {
+ struct pcp_option *opt = le->data;
+
+ le = le->next;
+
+ if (opt->code == code)
+ return opt;
+ }
+
+ return NULL;
+}
+
+
+struct pcp_option *pcp_msg_option_apply(const struct pcp_msg *msg,
+ pcp_option_h *h, void *arg)
+{
+ struct le *le = msg ? list_head(&msg->optionl) : NULL;
+
+ while (le) {
+ struct pcp_option *opt = le->data;
+
+ le = le->next;
+
+ if (h && h(opt, arg))
+ return opt;
+ }
+
+ return NULL;
+}
+
+
+static bool option_print(const struct pcp_option *opt, void *arg)
+{
+ return 0 != pcp_option_print(arg, opt);
+}
+
+
+int pcp_msg_printhdr(struct re_printf *pf, const struct pcp_msg *msg)
+{
+ int err;
+
+ if (!msg)
+ return 0;
+
+ err = re_hprintf(pf, "%s %s %usec",
+ msg->hdr.resp ? "Response" : "Request",
+ pcp_opcode_name(msg->hdr.opcode),
+ msg->hdr.lifetime);
+
+ if (msg->hdr.resp) {
+ err |= re_hprintf(pf, " result=%s, epoch_time=%u sec",
+ pcp_result_name(msg->hdr.result),
+ msg->hdr.epoch);
+ }
+ else {
+ err |= re_hprintf(pf, " client_addr=%j", &msg->hdr.cli_addr);
+ }
+
+ return err;
+}
+
+
+static int pcp_map_print(struct re_printf *pf, const struct pcp_map *map)
+{
+ if (!map)
+ return 0;
+
+ return re_hprintf(pf,
+ " nonce = %w\n protocol = %s\n"
+ " int_port = %u\n ext_addr = %J\n",
+ map->nonce, sizeof(map->nonce),
+ pcp_proto_name(map->proto),
+ map->int_port,
+ &map->ext_addr);
+}
+
+
+int pcp_msg_print(struct re_printf *pf, const struct pcp_msg *msg)
+{
+ int err;
+
+ if (!msg)
+ return 0;
+
+ err = pcp_msg_printhdr(pf, msg);
+ err |= re_hprintf(pf, "\n");
+
+ switch (msg->hdr.opcode) {
+
+ case PCP_MAP:
+ err |= pcp_map_print(pf, &msg->pld.map);
+ break;
+
+ case PCP_PEER:
+ err |= pcp_map_print(pf, &msg->pld.peer.map);
+ err |= re_hprintf(pf, " remote_peer = %J\n",
+ &msg->pld.peer.remote_addr);
+ break;
+ }
+
+ if (err)
+ return err;
+
+ if (pcp_msg_option_apply(msg, option_print, pf))
+ return ENOMEM;
+
+ return 0;
+}
+
+
+/**
+ * Get the payload from a PCP message
+ *
+ * @param msg PCP message
+ *
+ * @return either "struct pcp_map" or "struct pcp_peer"
+ */
+const void *pcp_msg_payload(const struct pcp_msg *msg)
+{
+ if (!msg)
+ return NULL;
+
+ switch (msg->hdr.opcode) {
+
+ case PCP_MAP: return &msg->pld.map;
+ case PCP_PEER: return &msg->pld.peer;
+ default: return NULL;
+ }
+}
diff --git a/src/pcp/option.c b/src/pcp/option.c
new file mode 100644
index 0000000..5ca9e0b
--- /dev/null
+++ b/src/pcp/option.c
@@ -0,0 +1,228 @@
+/**
+ * @file option.c PCP options
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_pcp.h>
+#include "pcp.h"
+
+
+static void destructor(void *arg)
+{
+ struct pcp_option *opt = arg;
+
+ list_unlink(&opt->le);
+
+ switch (opt->code) {
+
+ case PCP_OPTION_DESCRIPTION:
+ mem_deref(opt->u.description);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+int pcp_option_encode(struct mbuf *mb, enum pcp_option_code code,
+ const void *v)
+{
+ const struct sa *sa = v;
+ const struct pcp_option_filter *filt = v;
+ size_t start, len;
+ int err = 0;
+
+ if (!mb)
+ return EINVAL;
+
+ mb->pos += 4;
+ start = mb->pos;
+
+ switch (code) {
+
+ case PCP_OPTION_THIRD_PARTY:
+ if (!sa)
+ return EINVAL;
+ err |= pcp_ipaddr_encode(mb, sa);
+ break;
+
+ case PCP_OPTION_PREFER_FAILURE:
+ /* no payload */
+ break;
+
+ case PCP_OPTION_FILTER:
+ if (!filt)
+ return EINVAL;
+ err |= mbuf_write_u8(mb, 0x00);
+ err |= mbuf_write_u8(mb, filt->prefix_length);
+ err |= mbuf_write_u16(mb, htons(sa_port(&filt->remote_peer)));
+ err |= pcp_ipaddr_encode(mb, &filt->remote_peer);
+ break;
+
+ case PCP_OPTION_DESCRIPTION:
+ if (!v)
+ return EINVAL;
+ err |= mbuf_write_str(mb, v);
+ break;
+
+ default:
+ (void)re_fprintf(stderr,
+ "pcp: unsupported option %d\n", code);
+ return EINVAL;
+ }
+
+ /* header */
+ len = mb->pos - start;
+
+ mb->pos = start - 4;
+ err |= mbuf_write_u8(mb, code);
+ err |= mbuf_write_u8(mb, 0x00);
+ err |= mbuf_write_u16(mb, htons(len));
+ mb->pos += len;
+
+ /* padding */
+ while ((mb->pos - start) & 0x03)
+ err |= mbuf_write_u8(mb, 0x00);
+
+ return err;
+}
+
+
+int pcp_option_decode(struct pcp_option **optp, struct mbuf *mb)
+{
+ struct pcp_option *opt;
+ size_t start, len;
+ uint16_t port;
+ int err = 0;
+
+ if (!optp || !mb)
+ return EINVAL;
+
+ if (mbuf_get_left(mb) < 4)
+ return EBADMSG;
+
+ opt = mem_zalloc(sizeof(*opt), destructor);
+ if (!opt)
+ return ENOMEM;
+
+ opt->code = mbuf_read_u8(mb);
+ (void)mbuf_read_u8(mb);
+ len = ntohs(mbuf_read_u16(mb));
+
+ if (mbuf_get_left(mb) < len)
+ goto badmsg;
+
+ start = mb->pos;
+
+ switch (opt->code) {
+
+ case PCP_OPTION_THIRD_PARTY:
+ if (len < 16)
+ goto badmsg;
+ err = pcp_ipaddr_decode(mb, &opt->u.third_party);
+ break;
+
+ case PCP_OPTION_PREFER_FAILURE:
+ /* no payload */
+ break;
+
+ case PCP_OPTION_FILTER:
+ if (len < 20)
+ goto badmsg;
+ (void)mbuf_read_u8(mb);
+ opt->u.filter.prefix_length = mbuf_read_u8(mb);
+ port = ntohs(mbuf_read_u16(mb));
+ err = pcp_ipaddr_decode(mb, &opt->u.filter.remote_peer);
+ sa_set_port(&opt->u.filter.remote_peer, port);
+ break;
+
+ case PCP_OPTION_DESCRIPTION:
+ err = mbuf_strdup(mb, &opt->u.description, len);
+ break;
+
+ default:
+ mb->pos += len;
+
+ (void)re_printf("pcp: ignore option code %d (len=%zu)\n",
+ opt->code, len);
+ break;
+ }
+
+ if (err)
+ goto error;
+
+ /* padding */
+ while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
+ ++mb->pos;
+
+ *optp = opt;
+
+ return 0;
+
+ badmsg:
+ err = EBADMSG;
+ error:
+ mem_deref(opt);
+
+ return err;
+}
+
+
+static const char *pcp_option_name(enum pcp_option_code code)
+{
+ switch (code) {
+
+ case PCP_OPTION_THIRD_PARTY: return "THIRD_PARTY";
+ case PCP_OPTION_PREFER_FAILURE: return "PREFER_FAILURE";
+ case PCP_OPTION_FILTER: return "FILTER";
+ case PCP_OPTION_DESCRIPTION: return "DESCRIPTION";
+ default: return "?";
+ }
+}
+
+
+int pcp_option_print(struct re_printf *pf, const struct pcp_option *opt)
+{
+ int err;
+
+ if (!opt)
+ return 0;
+
+ err = re_hprintf(pf, " %-25s", pcp_option_name(opt->code));
+
+ switch (opt->code) {
+
+ case PCP_OPTION_THIRD_PARTY:
+ err |= re_hprintf(pf, "address=%j",
+ &opt->u.third_party);
+ break;
+
+ case PCP_OPTION_PREFER_FAILURE:
+ break;
+
+ case PCP_OPTION_FILTER:
+ err |= re_hprintf(pf, "prefix_length=%u, remote_peer=%J",
+ opt->u.filter.prefix_length,
+ &opt->u.filter.remote_peer);
+ break;
+
+ case PCP_OPTION_DESCRIPTION:
+ err |= re_hprintf(pf, "'%s'", opt->u.description);
+ break;
+
+ default:
+ err |= re_hprintf(pf, "???");
+ break;
+ }
+
+ err |= re_hprintf(pf, "\n");
+
+ return err;
+}
diff --git a/src/pcp/payload.c b/src/pcp/payload.c
new file mode 100644
index 0000000..c9d6033
--- /dev/null
+++ b/src/pcp/payload.c
@@ -0,0 +1,112 @@
+/**
+ * @file payload.c PCP payload encoding and decoding
+ *
+ * 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_pcp.h>
+#include "pcp.h"
+
+
+static int pcp_write_port(struct mbuf *mb, const struct sa *sa)
+{
+ uint16_t port_be;
+
+ if (!mb || !sa)
+ return EINVAL;
+
+ switch (sa->u.sa.sa_family) {
+
+ case AF_INET:
+ port_be = sa->u.in.sin_port;
+ break;
+
+#ifdef HAVE_INET6
+ case AF_INET6:
+ port_be = sa->u.in6.sin6_port;
+ break;
+#endif
+
+ default:
+ return EAFNOSUPPORT;
+ }
+
+ return mbuf_write_u16(mb, port_be);
+}
+
+
+static int pcp_map_encode(struct mbuf *mb, const struct pcp_map *map)
+{
+ int err = 0;
+
+ if (!mb || !map)
+ return EINVAL;
+
+ err |= mbuf_write_mem(mb, map->nonce, sizeof(map->nonce));
+ err |= mbuf_write_u8(mb, map->proto);
+ err |= mbuf_fill(mb, 0x00, 3);
+ err |= mbuf_write_u16(mb, htons(map->int_port));
+ err |= pcp_write_port(mb, &map->ext_addr);
+ err |= pcp_ipaddr_encode(mb, &map->ext_addr);
+
+ return err;
+}
+
+
+static int pcp_peer_encode(struct mbuf *mb, const struct pcp_peer *peer)
+{
+ int err;
+
+ if (!mb || !peer)
+ return EINVAL;
+
+ /* Protocol MUST NOT be zero.
+ * Internal port MUST NOT be zero.
+ */
+ if (!peer->map.proto || !peer->map.int_port)
+ return EPROTO;
+
+ /* note: the MAP and PEER opcodes are quite similar */
+ err = pcp_map_encode(mb, &peer->map);
+ if (err)
+ return err;
+
+ err = pcp_write_port(mb, &peer->remote_addr);
+ err |= mbuf_write_u16(mb, 0x0000);
+ err |= pcp_ipaddr_encode(mb, &peer->remote_addr);
+
+ return err;
+}
+
+
+int pcp_payload_encode(struct mbuf *mb, enum pcp_opcode opcode,
+ const union pcp_payload *pld)
+{
+ int err;
+
+ if (!mb || !pld)
+ return EINVAL;
+
+ switch (opcode) {
+
+ case PCP_MAP:
+ err = pcp_map_encode(mb, &pld->map);
+ break;
+
+ case PCP_PEER:
+ err = pcp_peer_encode(mb, &pld->peer);
+ break;
+
+ default:
+ re_fprintf(stderr, "pcp: dont know how to encode payload"
+ " for opcode %d\n", opcode);
+ err = EPROTO;
+ break;
+ }
+
+ return err;
+}
diff --git a/src/pcp/pcp.c b/src/pcp/pcp.c
new file mode 100644
index 0000000..28961fb
--- /dev/null
+++ b/src/pcp/pcp.c
@@ -0,0 +1,122 @@
+/**
+ * @file pcp/pcp.c PCP protocol details
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_list.h>
+#include <re_pcp.h>
+#include "pcp.h"
+
+
+static const uint8_t pattern[12] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
+
+
+int pcp_ipaddr_encode(struct mbuf *mb, const struct sa *sa)
+{
+ int err = 0;
+
+ if (!mb || !sa)
+ return EINVAL;
+
+ switch (sa_af(sa)) {
+
+ case AF_INET:
+ err |= mbuf_write_mem(mb, pattern, sizeof(pattern));
+ err |= mbuf_write_mem(mb, (void *)&sa->u.in.sin_addr.s_addr,
+ 4);
+ break;
+
+#ifdef HAVE_INET6
+ case AF_INET6:
+ err |= mbuf_write_mem(mb, sa->u.in6.sin6_addr.s6_addr, 16);
+ break;
+#endif
+
+ default:
+ err = EAFNOSUPPORT;
+ break;
+ }
+
+ return err;
+}
+
+
+int pcp_ipaddr_decode(struct mbuf *mb, struct sa *sa)
+{
+ uint8_t *p;
+
+ if (!mb || !sa)
+ return EINVAL;
+
+ if (mbuf_get_left(mb) < 16)
+ return EBADMSG;
+
+ p = mbuf_buf(mb);
+
+ if (0 == memcmp(p, pattern, sizeof(pattern))) {
+
+ sa_init(sa, AF_INET);
+ memcpy(&sa->u.in.sin_addr, p + 12, 4);
+ }
+#ifdef HAVE_INET6
+ else {
+ sa_init(sa, AF_INET6);
+ memcpy(sa->u.in6.sin6_addr.s6_addr, p, 16);
+ }
+#endif
+
+ mb->pos += 16;
+
+ return 0;
+}
+
+
+const char *pcp_result_name(enum pcp_result result)
+{
+ switch (result) {
+
+ case PCP_SUCCESS: return "SUCCESS";
+ case PCP_UNSUPP_VERSION: return "UNSUPP_VERSION";
+ case PCP_NOT_AUTHORIZED: return "NOT_AUTHORIZED";
+ case PCP_MALFORMED_REQUEST: return "MALFORMED_REQUEST";
+ case PCP_UNSUPP_OPCODE: return "UNSUPP_OPCODE";
+ case PCP_UNSUPP_OPTION: return "UNSUPP_OPTION";
+ case PCP_MALFORMED_OPTION: return "MALFORMED_OPTION";
+ case PCP_NETWORK_FAILURE: return "NETWORK_FAILURE";
+ case PCP_NO_RESOURCES: return "NO_RESOURCES";
+ case PCP_UNSUPP_PROTOCOL: return "UNSUPP_PROTOCOL";
+ case PCP_USER_EX_QUOTA: return "USER_EX_QUOTA";
+ case PCP_CANNOT_PROVIDE_EXTERNAL: return "CANNOT_PROVIDE_EXTERNAL";
+ case PCP_ADDRESS_MISMATCH: return "ADDRESS_MISMATCH";
+ case PCP_EXCESSIVE_REMOTE_PEERS: return "EXCESSIVE_REMOTE_PEERS";
+ case PCP_UNSUPP_FAMILY: return "UNSUPP_FAMILY";
+ default: return "?";
+ }
+}
+
+
+const char *pcp_opcode_name(enum pcp_opcode opcode)
+{
+ switch (opcode) {
+
+ case PCP_ANNOUNCE: return "ANNOUNCE";
+ case PCP_MAP: return "MAP";
+ case PCP_PEER: return "PEER";
+ default: return "?";
+ }
+}
+
+
+const char *pcp_proto_name(int proto)
+{
+ switch (proto) {
+
+ case IPPROTO_UDP: return "UDP";
+ case IPPROTO_TCP: return "TCP";
+ default: return "?";
+ }
+}
diff --git a/src/pcp/pcp.h b/src/pcp/pcp.h
new file mode 100644
index 0000000..5c85ecc
--- /dev/null
+++ b/src/pcp/pcp.h
@@ -0,0 +1,9 @@
+/**
+ * @file pcp/pcp.h PCP protocol -- Internal interface
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+
+
+int pcp_payload_encode(struct mbuf *mb, enum pcp_opcode opcode,
+ const union pcp_payload *pld);
diff --git a/src/pcp/reply.c b/src/pcp/reply.c
new file mode 100644
index 0000000..264dcbb
--- /dev/null
+++ b/src/pcp/reply.c
@@ -0,0 +1,93 @@
+/**
+ * @file pcp/reply.c PCP reply
+ *
+ * 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_sa.h>
+#include <re_udp.h>
+#include <re_pcp.h>
+#include "pcp.h"
+
+
+static int pcp_header_encode_response(struct mbuf *mb, enum pcp_opcode opcode,
+ enum pcp_result result,
+ uint32_t lifetime, uint32_t epoch_time)
+{
+ int err = 0;
+
+ if (!mb)
+ return EINVAL;
+
+ err |= mbuf_write_u8(mb, PCP_VERSION);
+ err |= mbuf_write_u8(mb, 1<<7 | opcode);
+ err |= mbuf_write_u8(mb, 0x00);
+ err |= mbuf_write_u8(mb, result);
+ err |= mbuf_write_u32(mb, htonl(lifetime));
+ err |= mbuf_write_u32(mb, htonl(epoch_time));
+ err |= mbuf_fill(mb, 0x00, 12);
+
+ return err;
+}
+
+
+/**
+ * Send a PCP response message
+ *
+ * @param us UDP Socket
+ * @param dst Destination network address
+ * @param req Buffer containing original PCP request (optional)
+ * @param opcode PCP opcode
+ * @param result PCP result for the response
+ * @param lifetime Lifetime in [seconds]
+ * @param epoch_time Server Epoch-time
+ * @param payload PCP payload, e.g. struct pcp_map (optional)
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int pcp_reply(struct udp_sock *us, const struct sa *dst, struct mbuf *req,
+ enum pcp_opcode opcode, enum pcp_result result,
+ uint32_t lifetime, uint32_t epoch_time, const void *payload)
+{
+ struct mbuf *mb;
+ size_t start;
+ int err;
+
+ if (!us || !dst)
+ return EINVAL;
+
+ if (req) {
+ /* the complete Request must be included in the Response */
+ mb = mem_ref(req);
+ }
+ else {
+ mb = mbuf_alloc(128);
+ if (!mb)
+ return ENOMEM;
+ }
+
+ start = mb->pos;
+
+ /* encode the response packet */
+ err = pcp_header_encode_response(mb, opcode, result,
+ lifetime, epoch_time);
+ if (err)
+ goto out;
+
+ if (payload) {
+ err = pcp_payload_encode(mb, opcode, payload);
+ if (err)
+ goto out;
+ }
+
+ mb->pos = start;
+ err = udp_send(us, dst, mb);
+
+ out:
+ mem_deref(mb);
+ return err;
+}
diff --git a/src/pcp/request.c b/src/pcp/request.c
new file mode 100644
index 0000000..60d58b4
--- /dev/null
+++ b/src/pcp/request.c
@@ -0,0 +1,346 @@
+/**
+ * @file pcp/request.c PCP request
+ *
+ * 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_list.h>
+#include <re_sys.h>
+#include <re_sa.h>
+#include <re_tmr.h>
+#include <re_udp.h>
+#include <re_pcp.h>
+#include "pcp.h"
+
+
+/*
+ * Defines a PCP client request
+ *
+ * the application must keep a reference to this object and the
+ * object must be deleted by the application. the response handler
+ * might be called multiple times.
+ */
+struct pcp_request {
+ struct pcp_conf conf;
+ struct sa srv;
+ struct udp_sock *us;
+ struct mbuf *mb;
+ struct tmr tmr;
+ struct tmr tmr_dur;
+ struct tmr tmr_refresh;
+ enum pcp_opcode opcode;
+ union pcp_payload payload;
+ uint32_t lifetime;
+ bool granted;
+ unsigned txc;
+ double RT;
+ pcp_resp_h *resph;
+ void *arg;
+};
+
+
+/*
+ * RT: Retransmission timeout
+ * IRT: Initial retransmission time, SHOULD be 3 seconds
+ * MRC: Maximum retransmission count, SHOULD be 0 (no maximum)
+ * MRT: Maximum retransmission time, SHOULD be 1024 seconds
+ * MRD: Maximum retransmission duration, SHOULD be 0 (no maximum)
+ * RAND: Randomization factor
+ */
+static const struct pcp_conf default_conf = {
+ 3,
+ 0,
+ 1024,
+ 0
+};
+
+
+static int start_sending(struct pcp_request *req);
+
+
+/* random number between -0.1 and +0.1 */
+static inline double RAND(void)
+{
+ return (1.0 * rand_u16() / 32768 - 1.0) / 10.0;
+}
+
+static double RT_init(const struct pcp_conf *conf)
+{
+ return (1.0 + RAND()) * conf->irt;
+}
+
+static double RT_next(const struct pcp_conf *conf, double RTprev)
+{
+ return (1.0 + RAND()) * min (2 * RTprev, conf->mrt);
+}
+
+
+static void destructor(void *arg)
+{
+ struct pcp_request *req = arg;
+
+ /* Destroy the mapping if it was granted */
+ if (req->granted && req->lifetime && req->mb) {
+
+ /* set the lifetime to zero */
+ req->mb->pos = 4;
+ mbuf_write_u32(req->mb, 0);
+
+ req->mb->pos = 0;
+ (void)udp_send(req->us, &req->srv, req->mb);
+ }
+
+ tmr_cancel(&req->tmr);
+ tmr_cancel(&req->tmr_dur);
+ tmr_cancel(&req->tmr_refresh);
+ mem_deref(req->us);
+ mem_deref(req->mb);
+}
+
+
+static void completed(struct pcp_request *req, int err, struct pcp_msg *msg)
+{
+ pcp_resp_h *resph = req->resph;
+ void *arg = req->arg;
+
+ tmr_cancel(&req->tmr);
+ tmr_cancel(&req->tmr_dur);
+
+ /* if the request failed, we only called the
+ response handler once and never again */
+ if (err || msg->hdr.result != PCP_SUCCESS ) {
+ req->resph = NULL;
+ }
+
+ if (resph)
+ resph(err, msg, arg);
+}
+
+
+static void refresh_timeout(void *arg)
+{
+ struct pcp_request *req = arg;
+
+ /* todo: update request with new EXT-ADDR from server */
+ (void)start_sending(req);
+}
+
+
+static void timeout(void *arg)
+{
+ struct pcp_request *req = arg;
+ int err;
+
+ req->txc++;
+
+ if (req->conf.mrc > 0 && req->txc > req->conf.mrc) {
+ completed(req, ETIMEDOUT, NULL);
+ return;
+ }
+
+ req->mb->pos = 0;
+ err = udp_send(req->us, &req->srv, req->mb);
+ if (err) {
+ completed(req, err, NULL);
+ return;
+ }
+
+ req->RT = RT_next(&req->conf, req->RT);
+ tmr_start(&req->tmr, req->RT * 1000, timeout, req);
+}
+
+
+static void timeout_duration(void *arg)
+{
+ struct pcp_request *req = arg;
+
+ completed(req, ETIMEDOUT, NULL);
+}
+
+
+static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct pcp_request *req = arg;
+ struct pcp_msg *msg;
+ int err;
+
+ if (!sa_cmp(src, &req->srv, SA_ALL))
+ return;
+
+ err = pcp_msg_decode(&msg, mb);
+ if (err)
+ return;
+
+ if (!msg->hdr.resp) {
+ (void)re_fprintf(stderr, "pcp: ignoring PCP request\n");
+ goto out;
+ }
+
+ if (msg->hdr.opcode != req->opcode)
+ goto out;
+
+ /* compare opcode-specific data */
+
+ switch (msg->hdr.opcode) {
+
+ case PCP_MAP:
+ case PCP_PEER:
+ if (0 != memcmp(msg->pld.map.nonce, req->payload.map.nonce,
+ PCP_NONCE_SZ)) {
+ (void)re_fprintf(stderr, "ignoring unknown nonce\n");
+ goto out;
+ }
+ req->payload.map.ext_addr = msg->pld.map.ext_addr;
+ break;
+
+ default:
+ break;
+ }
+
+ req->lifetime = msg->hdr.lifetime;
+ req->granted = (msg->hdr.result == PCP_SUCCESS);
+
+ /* todo:
+ *
+ * Once a PCP client has successfully received a response from a PCP
+ * server on that interface, it resets RT to a value randomly selected
+ * in the range 1/2 to 5/8 of the mapping lifetime, as described in
+ * Section 11.2.1, "Renewing a Mapping", and sends subsequent PCP
+ * requests for that mapping to that same server.
+ */
+ if (req->granted && req->lifetime) {
+
+ uint32_t v = req->lifetime * 3/4;
+
+ tmr_start(&req->tmr_refresh, v * 1000, refresh_timeout, req);
+ }
+
+ completed(req, 0, msg);
+
+ out:
+ mem_deref(msg);
+}
+
+
+static int start_sending(struct pcp_request *req)
+{
+ int err;
+
+ req->txc = 1;
+
+ req->mb->pos = 0;
+ err = udp_send(req->us, &req->srv, req->mb);
+ if (err)
+ return err;
+
+ req->RT = RT_init(&req->conf);
+ tmr_start(&req->tmr, req->RT * 1000, timeout, req);
+
+ if (req->conf.mrd) {
+ tmr_start(&req->tmr_dur, req->conf.mrd * 1000,
+ timeout_duration, req);
+ }
+
+ return err;
+}
+
+
+static int pcp_vrequest(struct pcp_request **reqp, const struct pcp_conf *conf,
+ const struct sa *srv, enum pcp_opcode opcode,
+ uint32_t lifetime, const void *payload,
+ pcp_resp_h *resph, void *arg,
+ uint32_t optionc, va_list ap)
+{
+ const union pcp_payload *up = payload;
+ struct pcp_request *req;
+ struct sa laddr;
+ int err;
+
+ if (!reqp || !srv)
+ return EINVAL;
+
+ sa_init(&laddr, sa_af(srv));
+
+ req = mem_zalloc(sizeof(*req), destructor);
+ if (!req)
+ return ENOMEM;
+
+ req->conf = conf ? *conf : default_conf;
+ req->opcode = opcode;
+ req->srv = *srv;
+ req->resph = resph;
+ req->arg = arg;
+
+ req->lifetime = lifetime;
+
+ if (up)
+ req->payload = *up;
+
+ err = udp_listen(&req->us, &laddr, udp_recv, req);
+ if (err)
+ goto out;
+
+ /*
+ * see RFC 6887 section 16.4
+ */
+ err = udp_connect(req->us, srv);
+ if (err)
+ goto out;
+ err = udp_local_get(req->us, &laddr);
+ if (err)
+ goto out;
+
+ req->mb = mbuf_alloc(128);
+ if (!req->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ err = pcp_msg_req_vencode(req->mb, opcode, lifetime,
+ &laddr, up, optionc, ap);
+ if (err)
+ goto out;
+
+ err = start_sending(req);
+
+ out:
+ if (err)
+ mem_deref(req);
+ else
+ *reqp = req;
+
+ return err;
+}
+
+
+int pcp_request(struct pcp_request **reqp, const struct pcp_conf *conf,
+ const struct sa *srv, enum pcp_opcode opcode,
+ uint32_t lifetime, const void *payload,
+ pcp_resp_h *resph, void *arg, uint32_t optionc, ...)
+{
+ va_list ap;
+ int err;
+
+ va_start(ap, optionc);
+ err = pcp_vrequest(reqp, conf, srv, opcode, lifetime, payload,
+ resph, arg, optionc, ap);
+ va_end(ap);
+
+ return err;
+}
+
+
+void pcp_force_refresh(struct pcp_request *req)
+{
+ if (!req)
+ return;
+
+ tmr_cancel(&req->tmr);
+ tmr_cancel(&req->tmr_dur);
+
+ tmr_start(&req->tmr_refresh, rand_u16() % 2000, refresh_timeout, req);
+}
diff --git a/src/shim/mod.mk b/src/shim/mod.mk
new file mode 100644
index 0000000..6593d57
--- /dev/null
+++ b/src/shim/mod.mk
@@ -0,0 +1,7 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS += shim/shim.c
diff --git a/src/shim/shim.c b/src/shim/shim.c
new file mode 100644
index 0000000..07e3b9c
--- /dev/null
+++ b/src/shim/shim.c
@@ -0,0 +1,200 @@
+
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_tcp.h>
+#include <re_net.h>
+#include <re_shim.h>
+
+
+#define DEBUG_MODULE "shim"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+struct shim {
+ struct tcp_conn *tc;
+ struct tcp_helper *th;
+ struct mbuf *mb;
+ shim_frame_h *frameh;
+ void *arg;
+
+ uint64_t n_tx;
+ uint64_t n_rx;
+};
+
+
+/* responsible for adding the SHIM header
+ - assumes that the sent MBUF contains a complete packet
+ */
+static bool shim_send_handler(int *err, struct mbuf *mb, void *arg)
+{
+ struct shim *shim = arg;
+ size_t len;
+ (void)shim;
+
+ if (mb->pos < SHIM_HDR_SIZE) {
+ DEBUG_WARNING("send: not enough space for SHIM header\n");
+ *err = ENOMEM;
+ return true;
+ }
+
+ len = mbuf_get_left(mb);
+
+ mb->pos -= SHIM_HDR_SIZE;
+ *err = mbuf_write_u16(mb, htons(len));
+ mb->pos -= SHIM_HDR_SIZE;
+
+ ++shim->n_tx;
+
+ return false;
+}
+
+
+static bool shim_recv_handler(int *errp, struct mbuf *mbx, bool *estab,
+ void *arg)
+{
+ struct shim *shim = arg;
+ int err = 0;
+ (void)estab;
+
+ /* handle re-assembly */
+ if (!shim->mb) {
+ shim->mb = mbuf_alloc(1024);
+ if (!shim->mb) {
+ *errp = ENOMEM;
+ return true;
+ }
+ }
+
+ if (shim->mb) {
+ size_t pos;
+
+ pos = shim->mb->pos;
+
+ shim->mb->pos = shim->mb->end;
+
+ err = mbuf_write_mem(shim->mb, mbuf_buf(mbx),
+ mbuf_get_left(mbx));
+ if (err)
+ goto out;
+
+ shim->mb->pos = pos;
+ }
+
+ /* extract all SHIM-frames in the TCP-stream */
+ for (;;) {
+
+ size_t start, len, pos, end;
+ bool hdld;
+
+ start = shim->mb->pos;
+
+ if (mbuf_get_left(shim->mb) < (SHIM_HDR_SIZE))
+ break;
+
+ len = ntohs(mbuf_read_u16(shim->mb));
+
+ if (mbuf_get_left(shim->mb) < len)
+ goto rewind;
+
+ pos = shim->mb->pos;
+ end = shim->mb->end;
+
+ shim->mb->end = pos + len;
+
+ ++shim->n_rx;
+
+ hdld = shim->frameh(shim->mb, shim->arg);
+ if (!hdld) {
+ /* XXX: handle multiple frames per segment */
+
+ shim->mb->pos = pos;
+ shim->mb->end = pos + len;
+
+ mbx->pos = mbx->end = 2;
+ err = mbuf_write_mem(mbx, mbuf_buf(shim->mb), len);
+ if (err)
+ goto out;
+ mbx->pos = 2;
+
+ shim->mb->pos = pos + len;
+ shim->mb->end = end;
+
+ return false; /* continue recv-handlers */
+ }
+
+ shim->mb->pos = pos + len;
+ shim->mb->end = end;
+
+ if (shim->mb->pos >= shim->mb->end) {
+ shim->mb = mem_deref(shim->mb);
+ break;
+ }
+
+ continue;
+
+ rewind:
+ shim->mb->pos = start;
+ break;
+ }
+
+ out:
+ if (err)
+ *errp = err;
+
+ return true; /* always handled */
+}
+
+
+static void destructor(void *arg)
+{
+ struct shim *shim = arg;
+
+ mem_deref(shim->th);
+ mem_deref(shim->tc);
+ mem_deref(shim->mb);
+}
+
+
+int shim_insert(struct shim **shimp, struct tcp_conn *tc, int layer,
+ shim_frame_h *frameh, void *arg)
+{
+ struct shim *shim;
+ int err;
+
+ if (!shimp || !tc || !frameh)
+ return EINVAL;
+
+ shim = mem_zalloc(sizeof(*shim), destructor);
+ if (!shim)
+ return ENOMEM;
+
+ shim->tc = mem_ref(tc);
+ err = tcp_register_helper(&shim->th, tc, layer, NULL,
+ shim_send_handler,
+ shim_recv_handler, shim);
+ if (err)
+ goto out;
+
+ shim->frameh = frameh;
+ shim->arg = arg;
+
+ out:
+ if (err)
+ mem_deref(shim);
+ else
+ *shimp = shim;
+
+ return err;
+}
+
+
+int shim_debug(struct re_printf *pf, const struct shim *shim)
+{
+ if (!shim)
+ return 0;
+
+ return re_hprintf(pf, "tx=%llu, rx=%llu", shim->n_tx, shim->n_rx);
+}
diff --git a/src/trice/README.md b/src/trice/README.md
new file mode 100644
index 0000000..8c2fcee
--- /dev/null
+++ b/src/trice/README.md
@@ -0,0 +1,109 @@
+ICE-NOTES:
+---------
+
+
+
+----------------------------------
+Application layer:
+
+- Can handle any modes (Full, Ice, Trickle)
+- Can implement any nomination-type (regular, aggressive)
+- Can encode/decode SDP attributes
+- Must gather all candidates (including STUN/TURN servers)
+- Application can choose the Default Local Candidate
+- Application can choose the Default Remote Candidate
+- Can measure RTT end-to-end
+- Can apply a STUN consent timer on top of ICE
+- the application should have the freedom to choose any selected candidate-pair
+- can install Keep-Alive timer (Binding Indication) for any pairs
+- can install a consent timer for any pair
+
+
+----------------------------------
+ICE layer:
+
+- All modes (Full-ICE and Trickle-ICE) are implemented
+- ICE-lite is NOT supported
+- agnostic to modes (Full, Trickle)
+- agnostic to nomination-type (regular, aggressive)
+- SDP encoding and decoding of ICE-relavant attributes
+- can handle between 1 and 2 components per media-stream
+- gathering: No candidate gathering, must be done in App
+- agnostic to transport protocol (should handle both UDP and TCP)
+- rel-addr (related address) is supported, but it is not used in the logic
+- ICE-stack does not choose the Default Local Candidate
+- ICE-stack does not choose the Default Remote Candidate
+- no TURN client
+- Each local candidate can have its own listen address/port. Yes
+- Support for UDP-transport
+- Support for TCP-transport
+- Support for IPv4 and IPv6
+- modular design with building blocks.
+- check all components before calling estabh ? no.
+- no support for "default" candidate/candidate-pair
+- component object: NO
+- selected pair: NO
+- must be able to support custom UDP/TCP-transport via helpers
+
+
+----------------------------------
+Interop:
+
+OK - Firefox 31.0
+OK - Firefox 35.0
+OK - Firefox 36.0
+OK - Chrome 41
+OK - Chrome 58
+
+
+TODO:
+
+done - remove ICE-lite mode
+done - remove rel-addr from ICE-code
+done - interop testing with Chrome
+done - interop testing with Firefox - seems to be working with 31.0
+done - Firefox: test with trickling candidates over Websock
+done - add support for TCP-candidates (test with Chrome)
+done - make a new test-client (reicec) and test on a public server
+done - do we need to support LITE at all? No.
+done - split EOC flag into local_eoc and remote_eoc
+done - send trigged request from stunsrv
+done - new module "icesdp" for SDP encoding/decoding
+done - add a new module "shim" (RFC 4571)
+ - ICE module should be Conncheck-only, no data-transport
+ - test_ice_tcp: S-O not working on Linux
+done - check when adding PRFLX that EOC-flag is set/unset (not needed)
+done - move use_cand flag to checklist_start/send_conncheck ?
+ - verify that APP can grab udp/tcp-sock
+ - consider moving pacing-logic to application?
+
+
+
+
+Architecture Diagram:
+--------------------
+
+
+
+```
+
+ .-------. .-------.
+ | App | | App |
+ '-------' '-------'
+ | | \
+ | .--------. | |
+ +--------+ STUN +--------+ |
+ | '--------' | | "ICE-layer"
+ | .-------. |
+ | | SHIM | |
+ | '-------' |
+ | | /
+ .-------. .-------.
+ | UDP | | TCP |
+ '-------' '-------'
+ | |
+ ! !
+
+
+
+```
diff --git a/src/trice/cand.c b/src/trice/cand.c
new file mode 100644
index 0000000..df4f0e6
--- /dev/null
+++ b/src/trice/cand.c
@@ -0,0 +1,88 @@
+/**
+ * @file cand.c Common ICE Candidates
+ *
+ * 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_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_net.h>
+#include <re_sys.h>
+#include <re_stun.h>
+#include <re_udp.h>
+#include <re_tcp.h>
+#include <re_ice.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+const char *ice_tcptype_name(enum ice_tcptype tcptype)
+{
+ switch (tcptype) {
+
+ case ICE_TCP_ACTIVE: return "active";
+ case ICE_TCP_PASSIVE: return "passive";
+ case ICE_TCP_SO: return "so";
+ default: return "???";
+ }
+}
+
+
+/*
+ Local Remote
+ Candidate Candidate
+ ---------------------------
+ tcp-so tcp-so
+ tcp-active tcp-passive
+ tcp-passive tcp-active
+
+ */
+enum ice_tcptype ice_tcptype_reverse(enum ice_tcptype type)
+{
+ switch (type) {
+
+ case ICE_TCP_SO: return ICE_TCP_SO;
+ case ICE_TCP_ACTIVE: return ICE_TCP_PASSIVE;
+ case ICE_TCP_PASSIVE: return ICE_TCP_ACTIVE;
+ default: return (enum ice_tcptype)-1;
+ }
+}
+
+
+enum ice_cand_type ice_cand_type_base(enum ice_cand_type type)
+{
+ switch (type) {
+
+ case ICE_CAND_TYPE_HOST: return ICE_CAND_TYPE_HOST;
+ case ICE_CAND_TYPE_SRFLX: return ICE_CAND_TYPE_HOST;
+ case ICE_CAND_TYPE_PRFLX: return ICE_CAND_TYPE_HOST;
+ case ICE_CAND_TYPE_RELAY: return ICE_CAND_TYPE_RELAY;
+ default: return type;
+ }
+}
+
+
+int trice_cand_print(struct re_printf *pf, const struct ice_cand_attr *cand)
+{
+ int err = 0;
+
+ if (!cand)
+ return 0;
+
+ err |= re_hprintf(pf, "%s|%s", ice_cand_type2name(cand->type),
+ net_proto2name(cand->proto));
+
+ if (cand->proto == IPPROTO_TCP) {
+
+ err |= re_hprintf(pf, ".%s", ice_tcptype_name(cand->tcptype));
+ }
+
+ err |= re_hprintf(pf, "|%J", &cand->addr);
+
+ return err;
+}
diff --git a/src/trice/candpair.c b/src/trice/candpair.c
new file mode 100644
index 0000000..deb23c4
--- /dev/null
+++ b/src/trice/candpair.c
@@ -0,0 +1,563 @@
+/**
+ * @file candpair.c ICE Candidate Pairs
+ *
+ * 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_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_udp.h>
+#include <re_stun.h>
+#include <re_ice.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "candpair"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/*
+ * generic routines to operate on "struct ice_candpair"
+ * (for both checkl and validl)
+ */
+
+
+/*
+ * g = controlling agent
+ * d = controlled agent
+
+ pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
+
+ */
+static uint64_t ice_calc_pair_prio(uint32_t g, uint32_t d)
+{
+ const uint64_t m = min(g, d);
+ const uint64_t x = max(g, d);
+
+ return (m<<32) + 2*x + (g>d?1:0);
+}
+
+
+static void candpair_destructor(void *arg)
+{
+ struct ice_candpair *cp = arg;
+
+ list_unlink(&cp->le);
+ mem_deref(cp->lcand);
+ mem_deref(cp->rcand);
+ mem_deref(cp->tc);
+
+ mem_deref(cp->conn);
+}
+
+
+static bool sort_handler(struct le *le1, struct le *le2, void *arg)
+{
+ const struct ice_candpair *cp1 = le1->data, *cp2 = le2->data;
+ (void)arg;
+
+ return cp1->pprio >= cp2->pprio;
+}
+
+
+static void candpair_set_pprio(struct ice_candpair *cp, bool controlling)
+{
+ uint32_t g, d;
+
+ if (controlling) {
+ g = cp->lcand->attr.prio;
+ d = cp->rcand->attr.prio;
+ }
+ else {
+ g = cp->rcand->attr.prio;
+ d = cp->lcand->attr.prio;
+ }
+
+ cp->pprio = ice_calc_pair_prio(g, d);
+}
+
+
+/**
+ * Add candidate pair to list, sorted by pair priority (highest is first)
+ */
+static void list_add_sorted(struct list *list, struct ice_candpair *cp)
+{
+ struct le *le;
+
+ /* find our slot */
+ for (le = list_tail(list); le; le = le->prev) {
+ struct ice_candpair *cp0 = le->data;
+
+ if (cp->pprio < cp0->pprio) {
+ list_insert_after(list, le, &cp->le, cp);
+ return;
+ }
+ }
+
+ list_prepend(list, &cp->le, cp);
+}
+
+
+int trice_candpair_alloc(struct ice_candpair **cpp, struct trice *icem,
+ struct ice_lcand *lcand, struct ice_rcand *rcand)
+{
+ struct ice_candpair *cp;
+
+ if (!icem || !lcand || !rcand)
+ return EINVAL;
+
+ if (icem->lrole == ICE_ROLE_UNKNOWN) {
+ DEBUG_WARNING("trice_candpair_alloc: invalid local role!\n");
+ return EINVAL;
+ }
+
+ cp = mem_zalloc(sizeof(*cp), candpair_destructor);
+ if (!cp)
+ return ENOMEM;
+
+ cp->lcand = mem_ref(lcand);
+ cp->rcand = mem_ref(rcand);
+ cp->state = ICE_CANDPAIR_FROZEN;
+
+ candpair_set_pprio(cp, icem->lrole == ICE_ROLE_CONTROLLING);
+
+ list_add_sorted(&icem->checkl, cp);
+
+ if (cpp)
+ *cpp = cp;
+
+ return 0;
+}
+
+
+/** Computing Pair Priority and Ordering Pairs */
+void trice_candpair_prio_order(struct list *lst, bool controlling)
+{
+ struct le *le;
+
+ for (le = list_head(lst); le; le = le->next) {
+ struct ice_candpair *cp = le->data;
+
+ candpair_set_pprio(cp, controlling);
+ }
+
+ list_sort(lst, sort_handler, NULL);
+}
+
+
+void trice_candpair_make_valid(struct trice *icem, struct ice_candpair *pair)
+{
+ if (!icem || !pair)
+ return;
+
+ if (pair->state == ICE_CANDPAIR_FAILED) {
+ DEBUG_WARNING("make_valid: pair already FAILED [%H]\n",
+ trice_candpair_debug, pair);
+ }
+
+ pair->err = 0;
+ pair->scode = 0;
+ pair->valid = true;
+
+ trice_candpair_set_state(pair, ICE_CANDPAIR_SUCCEEDED);
+
+ list_unlink(&pair->le);
+ list_add_sorted(&icem->validl, pair);
+}
+
+
+void trice_candpair_failed(struct ice_candpair *cp, int err, uint16_t scode)
+{
+ if (!cp)
+ return;
+
+ if (cp->state == ICE_CANDPAIR_SUCCEEDED) {
+ DEBUG_WARNING("set_failed(%m): pair already SUCCEEDED [%H]\n",
+ err, trice_candpair_debug, cp);
+ }
+
+ cp->err = err;
+ cp->scode = scode;
+ cp->valid = false;
+
+ cp->conn = mem_deref(cp->conn);
+
+ trice_candpair_set_state(cp, ICE_CANDPAIR_FAILED);
+}
+
+
+void trice_candpair_set_state(struct ice_candpair *pair,
+ enum ice_candpair_state state)
+{
+ if (!pair)
+ return;
+ if (pair->state == state)
+ return;
+
+ if (trice_candpair_iscompleted(pair)) {
+ DEBUG_WARNING("set_state(%s): pair is already completed"
+ " [%H]\n",
+ trice_candpair_state2name(state),
+ trice_candpair_debug, pair);
+ }
+
+#if 0
+ trice_printf(pair->lcand->icem,
+ "%H new state \"%s\"\n",
+ trice_candpair_debug, pair,
+ trice_candpair_state2name(state));
+#endif
+
+ pair->state = state;
+}
+
+
+bool trice_candpair_iscompleted(const struct ice_candpair *cp)
+{
+ if (!cp)
+ return false;
+
+ return cp->state == ICE_CANDPAIR_FAILED ||
+ cp->state == ICE_CANDPAIR_SUCCEEDED;
+}
+
+
+/**
+ * Find the highest-priority candidate-pair in a given list, with
+ * optional match parameters
+ *
+ * @param lst List of candidate pairs
+ * @param lcand Local candidate (optional)
+ * @param rcand Remote candidate (optional)
+ *
+ * @return Matching candidate pair if found, otherwise NULL
+ *
+ * note: assume list is sorted by priority
+ */
+struct ice_candpair *trice_candpair_find(const struct list *lst,
+ const struct ice_lcand *lcand,
+ const struct ice_rcand *rcand)
+{
+ struct le *le;
+
+ for (le = list_head(lst); le; le = le->next) {
+
+ struct ice_candpair *cp = le->data;
+
+ if (!cp->lcand || !cp->rcand) {
+ DEBUG_WARNING("corrupt candpair %p\n", cp);
+ continue;
+ }
+
+ if (lcand && cp->lcand != lcand)
+ continue;
+
+ if (rcand && cp->rcand != rcand)
+ continue;
+
+ return cp;
+ }
+
+ return NULL;
+}
+
+
+/* find the first pair with a given state */
+struct ice_candpair *trice_candpair_find_state(const struct list *lst,
+ enum ice_candpair_state state)
+{
+ struct le *le;
+
+ for (le = list_head(lst); le; le = le->next) {
+
+ struct ice_candpair *cp = le->data;
+
+ if (cp->state != state)
+ continue;
+
+ return cp;
+ }
+
+ return NULL;
+}
+
+
+bool trice_candpair_cmp_fnd(const struct ice_candpair *cp1,
+ const struct ice_candpair *cp2)
+{
+ if (!cp1 || !cp2)
+ return false;
+
+ return 0 == strcmp(cp1->lcand->attr.foundation,
+ cp2->lcand->attr.foundation) &&
+ 0 == strcmp(cp1->rcand->attr.foundation,
+ cp2->rcand->attr.foundation);
+}
+
+
+/* RFC 6544 -- 6.2. Forming the Check Lists
+
+ Local Remote
+ Candidate Candidate
+ ---------------------------
+ tcp-so tcp-so
+ tcp-active tcp-passive
+ tcp-passive tcp-active
+
+ */
+static bool tcptype_match(enum ice_tcptype loc, enum ice_tcptype rem)
+{
+ if (loc == ICE_TCP_SO && rem == ICE_TCP_SO) return true;
+ if (loc == ICE_TCP_ACTIVE && rem == ICE_TCP_PASSIVE) return true;
+ if (loc == ICE_TCP_PASSIVE && rem == ICE_TCP_ACTIVE) return true;
+
+ return false;
+}
+
+
+/* Replace server reflexive candidates by its base */
+static const struct sa *cand_srflx_addr(const struct ice_lcand *cand)
+{
+ if (ICE_CAND_TYPE_SRFLX == cand->attr.type)
+ return &cand->base_addr;
+ else
+ return &cand->attr.addr;
+}
+
+
+static struct ice_candpair *find_same_base_list(const struct list *lst,
+ const struct ice_lcand *lcand,
+ const struct ice_rcand *rcand)
+{
+ struct le *le;
+
+ for (le = list_head(lst); le; le = le->next) {
+
+ struct ice_candpair *cp = le->data;
+
+ if (cp->lcand->attr.compid == lcand->attr.compid
+ &&
+ cp->lcand->attr.proto == lcand->attr.proto
+ &&
+ sa_cmp(cand_srflx_addr(cp->lcand),
+ cand_srflx_addr(lcand), SA_ALL)
+ &&
+ sa_cmp(&cp->rcand->attr.addr,
+ &rcand->attr.addr, SA_ALL)) {
+
+ return cp;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* look in both check-list and valid-list */
+static struct ice_candpair *find_same_base(struct trice *icem,
+ const struct ice_lcand *lcand,
+ const struct ice_rcand *rcand)
+{
+ struct ice_candpair *cp;
+
+ cp = find_same_base_list(&icem->checkl, lcand, rcand);
+ if (cp)
+ return cp;
+
+ cp = find_same_base_list(&icem->validl, lcand, rcand);
+ if (cp)
+ return cp;
+
+ return NULL;
+}
+
+
+/* Pair a local candidate with a remote candidate */
+static int create_pair(struct trice *icem, struct ice_lcand *lcand,
+ struct ice_rcand *rcand)
+{
+ struct ice_candpair *cpx;
+
+ if (lcand->attr.compid != rcand->attr.compid ||
+ lcand->attr.proto != rcand->attr.proto ||
+ sa_af(&lcand->attr.addr) != sa_af(&rcand->attr.addr)) {
+ return 0;
+ }
+
+ /*
+ * IPv6 link-local: only pair with IPv6 link-local addresses
+ * see: RFC5245bis, section 6.1.2.2
+ */
+ if (sa_af(&lcand->attr.addr) == AF_INET6 &&
+ sa_is_linklocal(&lcand->attr.addr) !=
+ sa_is_linklocal(&rcand->attr.addr)) {
+ return 0;
+ }
+
+ /* loopback pairing optimization: only pair with loopback addresses */
+ if (icem->conf.optimize_loopback_pairing &&
+ sa_is_loopback(&lcand->attr.addr) !=
+ sa_is_loopback(&rcand->attr.addr)) {
+ return 0;
+ }
+
+ cpx = find_same_base(icem, lcand, rcand);
+ if (cpx) {
+ trice_printf(icem,
+ "with: pair with same"
+ " base already exist"
+ " (%H)\n",
+ trice_candpair_debug, cpx);
+
+ return 0;
+ }
+
+ if (lcand->attr.proto == IPPROTO_TCP) {
+ if (!tcptype_match(lcand->attr.tcptype,
+ rcand->attr.tcptype))
+ return 0;
+ }
+
+ /* add sorted */
+ return trice_candpair_alloc(NULL, icem, lcand, rcand);
+}
+
+
+/* Pair a candidate with all other candidates of the opposite kind */
+int trice_candpair_with_local(struct trice *icem, struct ice_lcand *lcand)
+{
+ struct list *lst = &icem->rcandl;
+ struct le *le;
+ int err = 0;
+
+ if (icem->lrole == ICE_ROLE_UNKNOWN) {
+ DEBUG_WARNING("trice_candpair_with_local: invalid local role!"
+ "\n");
+ return EINVAL;
+ }
+
+ for (le = list_head(lst); le; le = le->next) {
+
+ struct ice_rcand *rcand = le->data;
+
+ err = create_pair(icem, lcand, rcand);
+ if (err)
+ goto out;
+ }
+
+ out:
+ return err;
+}
+
+
+/* Pair a candidate with all other candidates of the opposite kind */
+int trice_candpair_with_remote(struct trice *icem, struct ice_rcand *rcand)
+{
+ struct list *lst = &icem->lcandl;
+ struct le *le;
+ int err = 0;
+
+ if (icem->lrole == ICE_ROLE_UNKNOWN) {
+ DEBUG_WARNING("trice_candpair_with_remote: invalid local role!"
+ "\n");
+ return EINVAL;
+ }
+
+ for (le = list_head(lst); le; le = le->next) {
+
+ struct ice_lcand *lcand = le->data;
+
+ err = create_pair(icem, lcand, rcand);
+ if (err)
+ goto out;
+ }
+
+ out:
+ return err;
+}
+
+
+int trice_candpair_debug(struct re_printf *pf, const struct ice_candpair *cp)
+{
+ int err;
+
+ if (!cp)
+ return 0;
+
+ err = re_hprintf(pf, "{comp=%u} %10s {%c%c%c%c} %28H <---> %28H",
+ cp->lcand->attr.compid,
+ trice_candpair_state2name(cp->state),
+ cp->valid ? 'V' : ' ',
+ cp->nominated ? 'N' : ' ',
+ cp->estab ? 'E' : ' ',
+ cp->trigged ? 'T' : ' ',
+ trice_cand_print, cp->lcand,
+ trice_cand_print, cp->rcand);
+
+ if (cp->err)
+ err |= re_hprintf(pf, " (%m)", cp->err);
+
+ if (cp->scode)
+ err |= re_hprintf(pf, " [%u]", cp->scode);
+
+ return err;
+}
+
+
+int trice_candpairs_debug(struct re_printf *pf, bool ansi_output,
+ const struct list *list)
+{
+ struct le *le;
+ int err;
+
+ if (!list)
+ return 0;
+
+ err = re_hprintf(pf, " (%u)\n", list_count(list));
+
+ for (le = list->head; le && !err; le = le->next) {
+
+ const struct ice_candpair *cp = le->data;
+ bool ansi = false;
+
+ if (ansi_output) {
+ if (cp->state == ICE_CANDPAIR_SUCCEEDED) {
+ err |= re_hprintf(pf, "\x1b[32m");
+ ansi = true;
+ }
+ else if (cp->err || cp->scode) {
+ err |= re_hprintf(pf, "\x1b[31m");
+ ansi = true;
+ }
+ }
+
+ err |= re_hprintf(pf, " %H\n",
+ trice_candpair_debug, cp);
+
+ if (ansi)
+ err |= re_hprintf(pf, "\x1b[;m");
+ }
+
+ return err;
+}
+
+
+const char *trice_candpair_state2name(enum ice_candpair_state st)
+{
+ switch (st) {
+
+ case ICE_CANDPAIR_FROZEN: return "Frozen";
+ case ICE_CANDPAIR_WAITING: return "Waiting";
+ case ICE_CANDPAIR_INPROGRESS: return "InProgress";
+ case ICE_CANDPAIR_SUCCEEDED: return "Succeeded";
+ case ICE_CANDPAIR_FAILED: return "Failed";
+ default: return "???";
+ }
+}
diff --git a/src/trice/chklist.c b/src/trice/chklist.c
new file mode 100644
index 0000000..f4e7409
--- /dev/null
+++ b/src/trice/chklist.c
@@ -0,0 +1,334 @@
+/**
+ * @file chklist.c ICE Checklist
+ *
+ * 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_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_stun.h>
+#include <re_ice.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "checklist"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+static void destructor(void *arg)
+{
+ struct ice_checklist *ic = arg;
+
+ tmr_cancel(&ic->tmr_pace);
+ list_flush(&ic->conncheckl); /* flush before stun deref */
+ mem_deref(ic->stun);
+}
+
+
+static void pace_timeout(void *arg)
+{
+ struct ice_checklist *ic = arg;
+ struct trice *icem = (struct trice *)ic->icem;
+
+ tmr_start(&ic->tmr_pace, ic->interval,
+ pace_timeout, ic);
+
+ trice_conncheck_schedule_check(icem);
+
+ trice_checklist_update(icem);
+}
+
+
+int trice_checklist_start(struct trice *icem, struct stun *stun,
+ uint32_t interval,
+ trice_estab_h *estabh, trice_failed_h *failh,
+ void *arg)
+{
+ struct ice_checklist *ic;
+ int err = 0;
+
+ if (!icem)
+ return EINVAL;
+
+ if (icem->lrole == ICE_ROLE_UNKNOWN) {
+ DEBUG_WARNING("trice_checklist_start: missing local role!\n");
+ return EINVAL;
+ }
+
+ if (icem->checklist) {
+ ic = icem->checklist;
+
+ if (!tmr_isrunning(&ic->tmr_pace)) {
+ tmr_start(&ic->tmr_pace, 1, pace_timeout, ic);
+ }
+ return 0;
+ }
+
+ /* The password is equal to the password provided by the peer */
+ if (!str_isset(icem->rpwd)) {
+ DEBUG_WARNING("start: remote password not set\n");
+ return EINVAL;
+ }
+
+ ic = mem_zalloc(sizeof(*ic), destructor);
+ if (!ic)
+ return ENOMEM;
+
+ if (stun) {
+ ic->stun = mem_ref(stun);
+ }
+ else {
+ err = stun_alloc(&ic->stun, NULL, NULL, NULL);
+ if (err)
+ goto out;
+
+ /* Update STUN Transport */
+ stun_conf(ic->stun)->rto = 100;
+ stun_conf(ic->stun)->rc = 4;
+
+ }
+
+ tmr_init(&ic->tmr_pace);
+
+ ic->interval = interval;
+ ic->icem = icem;
+ ic->estabh = estabh;
+ ic->failh = failh;
+ ic->arg = arg;
+
+ ic->is_running = true;
+ tmr_start(&ic->tmr_pace, 0, pace_timeout, ic);
+
+ icem->checklist = ic;
+
+ out:
+ if (err)
+ mem_deref(ic);
+
+ return err;
+}
+
+
+void trice_checklist_stop(struct trice *icem)
+{
+ struct ice_checklist *ic;
+
+ if (!icem || !icem->checklist)
+ return;
+
+ ic = icem->checklist;
+
+ ic->is_running = false;
+ tmr_cancel(&ic->tmr_pace);
+}
+
+
+/* If all of the pairs in the check list are now either in the Failed or
+ Succeeded state:
+ */
+bool trice_checklist_iscompleted(const struct trice *icem)
+{
+ struct le *le;
+
+ if (!icem)
+ return false;
+
+ for (le = icem->checkl.head; le; le = le->next) {
+
+ const struct ice_candpair *cp = le->data;
+
+ if (!trice_candpair_iscompleted(cp))
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Scheduling Checks
+ */
+void trice_conncheck_schedule_check(struct trice *icem)
+{
+ struct ice_candpair *pair;
+ bool use_cand;
+ int err = 0;
+
+ if (!icem)
+ return;
+
+ switch (icem->conf.nom) {
+
+ case ICE_NOMINATION_REGULAR:
+ use_cand = false;
+ break;
+
+ case ICE_NOMINATION_AGGRESSIVE:
+ use_cand = true;
+ break;
+
+ default:
+ DEBUG_WARNING("schedule_check: invalid nomination %d\n",
+ icem->conf.nom);
+ return;
+ }
+
+ /* Find the highest priority pair in that check list that is in the
+ Waiting state. */
+ pair = trice_candpair_find_state(&icem->checkl, ICE_CANDPAIR_WAITING);
+ if (pair) {
+ err = trice_conncheck_send(icem, pair,
+ use_cand);
+ if (err)
+ trice_candpair_failed(pair, err, 0);
+ return;
+ }
+
+ /* If there is no such pair: */
+
+ /* Find the highest priority pair in that check list that is in
+ the Frozen state. */
+ pair = trice_candpair_find_state(&icem->checkl, ICE_CANDPAIR_FROZEN);
+ if (pair) { /* If there is such a pair: */
+
+ /* Unfreeze the pair.
+ Perform a check for that pair, causing its state to
+ transition to In-Progress. */
+ err = trice_conncheck_send(icem, pair,
+ use_cand);
+ if (err)
+ trice_candpair_failed(pair, err, 0);
+ return;
+ }
+
+ /* If there is no such pair: */
+
+ /* Terminate the timer for that check list. */
+}
+
+
+/**
+ * Computing States
+ */
+void trice_checklist_set_waiting(struct trice *icem)
+{
+ struct le *le, *le2;
+
+ if (!icem)
+ return;
+
+ if (icem->lrole == ICE_ROLE_UNKNOWN) {
+ DEBUG_WARNING("trice_checklist_set_waiting: invalid local"
+ "role!\n");
+ return;
+ }
+
+ /*
+ For all pairs with the same foundation, it sets the state of
+ the pair with the lowest component ID to Waiting. If there is
+ more than one such pair, the one with the highest priority is
+ used.
+ */
+
+ for (le = icem->checkl.head; le; le = le->next) {
+
+ struct ice_candpair *cp = le->data;
+
+ for (le2 = icem->checkl.head; le2; le2 = le2->next) {
+
+ struct ice_candpair *cp2 = le2->data;
+
+ if (!trice_candpair_cmp_fnd(cp, cp2))
+ continue;
+
+ if (cp2->lcand->attr.compid < cp->lcand->attr.compid &&
+ cp2->pprio > cp->pprio)
+ cp = cp2;
+ }
+
+ if (cp->state == ICE_CANDPAIR_FROZEN)
+ trice_candpair_set_state(cp, ICE_CANDPAIR_WAITING);
+ }
+}
+
+
+int trice_checklist_update(struct trice *icem)
+{
+ struct ice_checklist *ic;
+
+ if (!icem)
+ return EINVAL;
+
+ ic = icem->checklist;
+ if (!ic)
+ return ENOSYS;
+
+ if (trice_checklist_iscompleted(icem)) {
+ tmr_cancel(&ic->tmr_pace);
+
+ trice_printf(icem, "ICE checklist is complete"
+ " (checkl=%u, valid=%u)\n",
+ list_count(&icem->checkl),
+ list_count(&icem->validl));
+ }
+
+ return 0;
+}
+
+
+void trice_checklist_refresh(struct trice *icem)
+{
+ struct ice_checklist *ic;
+
+ if (!icem || !icem->checklist)
+ return;
+
+ ic = icem->checklist;
+
+ tmr_start(&ic->tmr_pace, ic->interval, pace_timeout, ic);
+}
+
+
+bool trice_checklist_isrunning(const struct trice *icem)
+{
+ struct ice_checklist *ic;
+
+ if (!icem || !icem->checklist)
+ return false;
+
+ ic = icem->checklist;
+
+ return ic->is_running;
+}
+
+
+int trice_checklist_debug(struct re_printf *pf, const struct ice_checklist *ic)
+{
+ struct le *le;
+ int err = 0;
+
+ if (!ic)
+ return 0;
+
+ err |= re_hprintf(pf, " Checklist: %s, interval=%ums\n",
+ tmr_isrunning(&ic->tmr_pace) ? "Running" : "Not-Running",
+ ic->interval);
+ err |= re_hprintf(pf, " Pending connchecks: %u\n",
+ list_count(&ic->conncheckl));
+ for (le = ic->conncheckl.head; le; le = le->next) {
+ struct ice_conncheck *cc = le->data;
+
+ err |= re_hprintf(pf, " ...%H\n", trice_conncheck_debug, cc);
+ }
+
+ err |= stun_debug(pf, ic->stun);
+
+ return err;
+}
diff --git a/src/trice/connchk.c b/src/trice/connchk.c
new file mode 100644
index 0000000..f25ca16
--- /dev/null
+++ b/src/trice/connchk.c
@@ -0,0 +1,532 @@
+/**
+ * @file connchk.c ICE Connectivity Checks
+ *
+ * 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_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_net.h>
+#include <re_stun.h>
+#include <re_ice.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "conncheck"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+enum {PRESZ_RELAY = 36};
+
+
+static void conncheck_destructor(void *arg)
+{
+ struct ice_conncheck *cc = arg;
+
+ cc->term = true;
+ list_unlink(&cc->le);
+ mem_deref(cc->ct_conn);
+}
+
+
+static void pair_established(struct trice *icem, struct ice_candpair *pair,
+ const struct stun_msg *msg)
+{
+ struct ice_tcpconn *conn;
+
+ if (!icem || !pair)
+ return;
+
+ if (pair->lcand->attr.proto == IPPROTO_TCP) {
+
+ conn = pair->conn;
+
+ if (!conn) {
+ /* todo: Hack to grab TCPCONN */
+ conn = trice_conn_find(&icem->connl,
+ pair->lcand->attr.compid,
+ &pair->lcand->attr.addr,
+ &pair->rcand->attr.addr);
+ }
+
+ if (conn) {
+ pair->tc = mem_deref(pair->tc);
+ pair->tc = mem_ref(conn->tc);
+ }
+ else {
+ DEBUG_WARNING("pair_established: TCP-connection "
+ " from %H to %H not found!\n",
+ trice_cand_print, pair->lcand,
+ trice_cand_print, pair->rcand);
+ }
+
+ }
+
+ if (!pair->estab) {
+ pair->estab = true;
+
+ if (icem->checklist->estabh) {
+ icem->checklist->estabh(pair, msg,
+ icem->checklist->arg);
+ }
+ }
+}
+
+
+/*
+ * NOTE for TCP-candidates:
+ *
+ * Note also that STUN responses received on an active TCP candidate
+ * will typically produce a peer reflexive candidate.
+ *
+ *
+ * NOTE for Trickle ICE and Peer Reflexive Candidates:
+ *
+ * With Trickle ICE, it is possible that
+ * server reflexive candidates be discovered as peer reflexive in cases
+ * where incoming connectivity checks are received from these candidates
+ * before the trickle updates that carry them.
+ *
+ */
+static void handle_success(struct trice *icem, struct ice_candpair *pair,
+ const struct sa *mapped_addr,
+ const struct stun_msg *msg,
+ struct ice_conncheck *cc)
+{
+ unsigned compid = pair->lcand->attr.compid;
+ int err;
+
+ if (!icem || !pair) {
+ DEBUG_WARNING("handle_success: invalid params\n");
+ }
+
+ if (icem->conf.enable_prflx &&
+ !trice_lcand_find(icem, -1, compid,
+ pair->lcand->attr.proto, mapped_addr)) {
+
+ struct ice_lcand *lcand;
+ struct ice_candpair *pair_prflx;
+ uint32_t prio;
+
+ prio = ice_cand_calc_prio(ICE_CAND_TYPE_PRFLX, 0, compid);
+
+ err = trice_add_lcandidate(&lcand, icem, &icem->lcandl, compid,
+ "FND", pair->lcand->attr.proto,
+ prio, mapped_addr,
+ &pair->lcand->attr.addr,
+ ICE_CAND_TYPE_PRFLX,
+ &pair->lcand->attr.addr,
+ pair->lcand->attr.tcptype);
+ if (err) {
+ DEBUG_WARNING("failed to add PRFLX: %m\n", err);
+ return;
+ }
+
+ if (str_isset(pair->lcand->ifname)) {
+ str_ncpy(lcand->ifname, pair->lcand->ifname,
+ sizeof(lcand->ifname));
+ }
+
+ trice_printf(icem, "added PRFLX local candidate (%H)"
+ " from base (%H)\n",
+ trice_cand_print, lcand,
+ trice_cand_print, pair->lcand);
+
+ /* newly created Candidate-PAir */
+ err = trice_candpair_alloc(&pair_prflx, icem,
+ lcand, pair->rcand);
+ if (err) {
+ DEBUG_WARNING("prflx alloc: %m\n", err);
+ return;
+ }
+
+ lcand->us = mem_ref(pair->lcand->us);
+ pair_prflx->conn = mem_ref(pair->conn);
+
+ /* mark the original HOST-one as failed */
+ trice_candpair_failed(pair, 0, 0);
+
+ trice_candpair_make_valid(icem, pair_prflx);
+
+ pair_established(icem, pair_prflx, msg);
+ return;
+ }
+
+ pair->state = ICE_CANDPAIR_FROZEN;
+ trice_candpair_make_valid(icem, pair);
+
+ /* Updating the Nominated Flag */
+ if (ICE_ROLE_CONTROLLING == icem->lrole) {
+
+ if (cc->use_cand)
+ pair->nominated = true;
+ }
+
+ pair_established(icem, pair, msg);
+}
+
+
+static int print_err(struct re_printf *pf, const int *err)
+{
+ if (err && *err)
+ return re_hprintf(pf, " (%m)", *err);
+
+ return 0;
+}
+
+
+static void stunc_resp_handler(int err, uint16_t scode, const char *reason,
+ const struct stun_msg *msg, void *arg)
+{
+ struct ice_conncheck *cc = arg;
+ struct ice_candpair *pair = cc->pair;
+ struct trice *icem = cc->icem;
+ struct stun_attr *attr;
+ bool success = (err == 0) && (scode == 0);
+ (void)reason;
+
+ if (!icem) {
+ DEBUG_WARNING("stun response: no icem\n");
+ }
+
+ if (cc->term)
+ return;
+
+ trice_tracef(icem, success ? 32 : 31,
+ "[%u] Rx %H <--- %H '%u %s'%H\n",
+ pair->lcand->attr.compid,
+ trice_cand_print, pair->lcand,
+ trice_cand_print, pair->rcand,
+ scode, reason, print_err, &err);
+
+ if (err) {
+ DEBUG_NOTICE("stun response: [%H --> %H] %m\n",
+ trice_cand_print, pair->lcand,
+ trice_cand_print, pair->rcand,
+ err);
+
+ trice_candpair_failed(pair, err, scode);
+ goto out;
+ }
+
+ switch (scode) {
+
+ case 0: /* Success case */
+ attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR);
+ if (!attr) {
+ DEBUG_WARNING("no XOR-MAPPED-ADDR in response\n");
+ trice_candpair_failed(pair, EPROTO, 0);
+ break;
+ }
+
+ handle_success(icem, pair, &attr->v.sa, msg, cc);
+ break;
+
+ case 487: /* Role Conflict */
+ trice_switch_local_role(icem);
+ (void)trice_conncheck_send(icem, pair, cc->use_cand);
+ break;
+
+ default:
+ trice_candpair_failed(pair, err, scode);
+ break;
+ }
+
+ out:
+ if (err || scode) {
+ icem->checklist->failh(err, scode, pair, icem->checklist->arg);
+ }
+
+ mem_deref(cc);
+ return;
+}
+
+
+int trice_conncheck_stun_request(struct ice_checklist *ic,
+ struct ice_conncheck *cc,
+ struct ice_candpair *cp, void *sock,
+ bool cc_use_cand)
+{
+ struct ice_lcand *lcand = cp->lcand;
+ struct trice *icem = ic->icem;
+ char username_buf[256];
+ uint32_t prio_prflx;
+ uint16_t ctrl_attr;
+ bool use_cand = false;
+ size_t presz = 0;
+ int err = 0;
+
+ if (!cp)
+ return EINVAL;
+
+ if (!ic)
+ return ENOSYS;
+
+ if (!sock) {
+ DEBUG_NOTICE("conncheck: no SOCK\n");
+ return EINVAL;
+ }
+
+ /* The password is equal to the password provided by the peer */
+ if (!str_isset(icem->rpwd)) {
+ DEBUG_WARNING("conncheck: remote password missing for"
+ " raddr=%J\n", &cp->rcand->attr.addr);
+ err = EINVAL;
+ goto out;
+ }
+
+ if (lcand->attr.proto == IPPROTO_UDP &&
+ lcand->attr.type == ICE_CAND_TYPE_RELAY)
+ presz = PRESZ_RELAY;
+ else if (lcand->attr.proto == IPPROTO_TCP)
+ presz = 2;
+
+ if (re_snprintf(username_buf, sizeof(username_buf),
+ "%s:%s", icem->rufrag, icem->lufrag) < 0) {
+ DEBUG_WARNING("conncheck: username buffer too small\n");
+ err = ENOMEM;
+ goto out;
+ }
+
+ /* PRIORITY and USE-CANDIDATE */
+ prio_prflx = ice_cand_calc_prio(ICE_CAND_TYPE_PRFLX, 0,
+ lcand->attr.compid);
+
+ switch (icem->lrole) {
+
+ case ICE_ROLE_CONTROLLING:
+ ctrl_attr = STUN_ATTR_CONTROLLING;
+ use_cand = cc_use_cand;
+ break;
+
+ case ICE_ROLE_CONTROLLED:
+ ctrl_attr = STUN_ATTR_CONTROLLED;
+ break;
+
+ default:
+ DEBUG_WARNING("conncheck: invalid local role\n");
+ return EINVAL;
+ }
+
+ trice_tracef(icem, 36,
+ "[%u] Tx [presz=%zu] %H ---> %H (%s) %s\n",
+ lcand->attr.compid,
+ presz,
+ trice_cand_print, cp->lcand, trice_cand_print, cp->rcand,
+ trice_candpair_state2name(cp->state),
+ use_cand ? "[USE]" : "");
+
+ /* A connectivity check MUST utilize the STUN short term credential
+ mechanism. */
+
+ err = stun_request(&cc->ct_conn, ic->stun, lcand->attr.proto,
+ sock, &cp->rcand->attr.addr, presz,
+ STUN_METHOD_BINDING,
+ (uint8_t *)icem->rpwd, str_len(icem->rpwd),
+ true, stunc_resp_handler, cc,
+ 4,
+ STUN_ATTR_USERNAME, username_buf,
+ STUN_ATTR_PRIORITY, &prio_prflx,
+ ctrl_attr, &icem->tiebrk,
+ STUN_ATTR_USE_CAND,
+ use_cand ? &use_cand : 0);
+ if (err) {
+ DEBUG_NOTICE("stun_request from %H to %H failed (%m)\n",
+ trice_cand_print, lcand,
+ trice_cand_print, cp->rcand,
+ err);
+ goto out;
+ }
+
+ out:
+ if (err) {
+ trice_candpair_failed(cp, err, 0);
+ }
+
+ return err;
+}
+
+
+static bool tcpconn_frame_handler(struct trice *icem,
+ struct tcp_conn *tc, struct sa *src,
+ struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+
+ return trice_stun_process(icem, lcand,
+ IPPROTO_TCP, tc, src, mb);
+}
+
+
+int trice_conncheck_send(struct trice *icem, struct ice_candpair *pair,
+ bool use_cand)
+{
+ struct ice_checklist *ic;
+ struct ice_lcand *lcand;
+ struct ice_tcpconn *conn;
+ struct ice_conncheck *cc = NULL;
+ void *sock;
+ int err = 0;
+
+ if (!icem || !pair)
+ return EINVAL;
+
+ lcand = pair->lcand;
+ ic = icem->checklist;
+ if (!ic) {
+ DEBUG_WARNING("conncheck_send: no checklist\n");
+ return EINVAL;
+ }
+
+ cc = mem_zalloc(sizeof(*cc), conncheck_destructor);
+ if (!cc)
+ return ENOMEM;
+
+ cc->icem = icem;
+ cc->pair = pair;
+ cc->use_cand = use_cand;
+
+ if (pair->state < ICE_CANDPAIR_INPROGRESS)
+ trice_candpair_set_state(pair, ICE_CANDPAIR_INPROGRESS);
+
+ switch (pair->lcand->attr.proto) {
+
+ case IPPROTO_UDP:
+ sock = trice_lcand_sock(icem, lcand);
+
+ err = trice_conncheck_stun_request(ic, cc, pair,
+ sock, use_cand);
+ if (err)
+ goto out;
+ break;
+
+ case IPPROTO_TCP:
+ conn = trice_conn_find(&icem->connl, lcand->attr.compid,
+ &pair->lcand->attr.addr,
+ &pair->rcand->attr.addr);
+ if (conn) {
+ trice_printf(icem, "TCP-connection"
+ " already exist [%H]\n",
+ trice_conn_debug, conn);
+
+ pair->conn = mem_ref(conn); /* todo: */
+
+ err = trice_conncheck_stun_request(ic, cc, pair,
+ conn->tc, use_cand);
+ if (err)
+ goto out;
+ break;
+ }
+
+ switch (pair->lcand->attr.tcptype) {
+
+ case ICE_TCP_ACTIVE:
+ case ICE_TCP_SO:
+ err = trice_conn_alloc(&icem->connl, icem,
+ lcand->attr.compid, true,
+ &lcand->attr.addr,
+ &pair->rcand->attr.addr,
+ lcand->ts, lcand->layer,
+ tcpconn_frame_handler, lcand);
+ if (err) {
+ DEBUG_NOTICE("trice_conn_alloc to"
+ " %J failed (%m)\n",
+ &pair->rcand->attr.addr, err);
+ goto out;
+ }
+ break;
+
+ case ICE_TCP_PASSIVE:
+ /* do nothing now. */
+
+ /* we must wait for the other side to create a
+ TCP-connection to us. when this TCP-connection
+ is established, we can then send our
+ Connectivity-check */
+
+ trice_candpair_set_state(pair,
+ ICE_CANDPAIR_INPROGRESS);
+ break;
+ }
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+
+ list_append(&ic->conncheckl, &cc->le, cc);
+
+ out:
+ if (err) {
+ mem_deref(cc);
+ trice_candpair_failed(pair, err, 0);
+ }
+
+ return err;
+}
+
+
+int trice_conncheck_trigged(struct trice *icem, struct ice_candpair *pair,
+ void *sock, bool use_cand)
+{
+ struct ice_checklist *ic;
+ struct ice_conncheck *cc = NULL;
+ int err = 0;
+
+ if (!icem || !pair)
+ return EINVAL;
+
+ ic = icem->checklist;
+ if (!ic) {
+ DEBUG_WARNING("conncheck_send: no checklist\n");
+ return EINVAL;
+ }
+
+ cc = mem_zalloc(sizeof(*cc), conncheck_destructor);
+ if (!cc)
+ return ENOMEM;
+
+ cc->icem = icem;
+ cc->pair = pair;
+ cc->use_cand = use_cand;
+
+ if (pair->state < ICE_CANDPAIR_INPROGRESS)
+ trice_candpair_set_state(pair, ICE_CANDPAIR_INPROGRESS);
+
+ err = trice_conncheck_stun_request(icem->checklist, cc,
+ pair, sock, use_cand);
+ if (err)
+ goto out;
+
+ list_append(&ic->conncheckl, &cc->le, cc);
+
+ out:
+ if (err) {
+ mem_deref(cc);
+ trice_candpair_failed(pair, err, 0);
+ }
+
+ return err;
+}
+
+
+int trice_conncheck_debug(struct re_printf *pf, const struct ice_conncheck *cc)
+{
+ if (!cc)
+ return 0;
+
+ return re_hprintf(pf, "proto=%s stun=%p use_cand=%d"
+ " state=%s"
+ ,
+ net_proto2name(cc->pair->lcand->attr.proto),
+ cc->ct_conn, cc->use_cand,
+ trice_candpair_state2name(cc->pair->state));
+}
diff --git a/src/trice/lcand.c b/src/trice/lcand.c
new file mode 100644
index 0000000..a5ab080
--- /dev/null
+++ b/src/trice/lcand.c
@@ -0,0 +1,625 @@
+/**
+ * @file lcand.c Local ICE Candidates
+ *
+ * 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_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_net.h>
+#include <re_sys.h>
+#include <re_stun.h>
+#include <re_udp.h>
+#include <re_tcp.h>
+#include <re_ice.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "icelcand"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+static bool tcpconn_frame_handler(struct trice *icem,
+ struct tcp_conn *tc, struct sa *src,
+ struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+ (void)icem;
+
+ return lcand->recvh(lcand, IPPROTO_TCP, tc,
+ src, mb, lcand->arg);
+}
+
+
+static void tcp_conn_handler(const struct sa *peer, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+ int err;
+
+#if 0
+ trice_printf(lcand->icem,
+ "[local=%H] incoming TCP-connect from %J\n",
+ trice_cand_print, lcand, peer);
+#endif
+
+ err = trice_conn_alloc(&lcand->icem->connl, lcand->icem,
+ lcand->attr.compid, false,
+ &lcand->attr.addr, peer, lcand->ts, lcand->layer,
+ tcpconn_frame_handler, lcand);
+ if (err) {
+ DEBUG_WARNING("ice_conn_alloc error (%m)\n", err);
+ }
+}
+
+
+static void lcand_destructor(void *arg)
+{
+ struct ice_lcand *cand = arg;
+
+ list_unlink(&cand->le);
+
+ mem_deref(cand->ts);
+ mem_deref(cand->uh);
+ mem_deref(cand->us);
+}
+
+
+/** Foundation is a hash of IP address and candidate type */
+static int compute_foundation(struct ice_lcand *cand,
+ const struct sa *addr, enum ice_cand_type type)
+{
+ uint32_t v;
+
+ v = sa_hash(addr, SA_ADDR);
+ v ^= type;
+
+ if (re_snprintf(cand->attr.foundation, sizeof(cand->attr.foundation),
+ "%08x", v) < 0)
+ return ENOMEM;
+
+ return 0;
+}
+
+
+static bool trice_lcand_recv_handler(struct ice_lcand *lcand,
+ int proto, void *sock, const struct sa *src,
+ struct mbuf *mb, void *arg)
+{
+ struct trice *icem = arg;
+
+ return trice_stun_process(icem, lcand, proto, sock, src, mb);
+}
+
+
+int trice_add_lcandidate(struct ice_lcand **candp,
+ struct trice *icem, struct list *lst,
+ unsigned compid, char *foundation, int proto,
+ uint32_t prio, const struct sa *addr,
+ const struct sa *base_addr,
+ enum ice_cand_type type,
+ const struct sa *rel_addr,
+ enum ice_tcptype tcptype)
+{
+ struct ice_lcand *cand;
+ int err = 0;
+
+ if (!lst || !compid || !proto || !addr)
+ return EINVAL;
+
+ cand = mem_zalloc(sizeof(*cand), lcand_destructor);
+ if (!cand)
+ return ENOMEM;
+
+ cand->attr.compid = compid;
+ if (foundation)
+ str_ncpy(cand->attr.foundation, foundation,
+ sizeof(cand->attr.foundation));
+ else
+ err = compute_foundation(cand, addr, type);
+ cand->attr.proto = proto;
+ cand->attr.prio = prio;
+ cand->attr.addr = *addr;
+ cand->attr.type = type;
+ cand->attr.tcptype = tcptype;
+ if (rel_addr)
+ cand->attr.rel_addr = *rel_addr;
+ if (err)
+ goto out;
+
+ cand->icem = icem;
+
+ cand->recvh = trice_lcand_recv_handler;
+ cand->arg = icem;
+
+ if (base_addr)
+ cand->base_addr = *base_addr;
+
+ list_append(lst, &cand->le, cand);
+
+ out:
+ if (err)
+ mem_deref(cand);
+ else if (candp)
+ *candp = cand;
+
+ return err;
+}
+
+
+/* this one is only for Send statistics on Local Candidate */
+static bool udp_helper_send_handler(int *err, struct sa *dst,
+ struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+ (void)err;
+ (void)dst;
+ (void)mb;
+
+ lcand->stats.n_tx += 1;
+
+ return false; /* NOT handled */
+}
+
+
+/*
+ * lcand: on which Local Candidate to receive the packet
+ *
+ * return TRUE if handled
+ */
+static bool udp_helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+
+ lcand->stats.n_rx += 1;
+
+ return lcand->recvh(lcand, IPPROTO_UDP, lcand->us,
+ src, mb, lcand->arg);
+}
+
+
+/* The incoming data should not get here */
+static void dummy_udp_recv(const struct sa *src, struct mbuf *mb, void *arg)
+{
+ struct ice_lcand *lcand = arg;
+
+ DEBUG_NOTICE("@@@@ NO-ONE cared about this UDP packet? @@@@@"
+ " (%zu bytes from %J to %s.%J)\n",
+ mbuf_get_left(mb), src,
+ ice_cand_type2name(lcand->attr.type),
+ &lcand->attr.addr);
+}
+
+static int udp_listen_range(struct udp_sock **usp, const struct sa *ip,
+ uint16_t min_port, uint16_t max_port,
+ udp_recv_h *rh, void *arg)
+{
+ struct sa laddr;
+ int tries = 64;
+ int err = 0;
+
+ sa_cpy(&laddr, ip);
+
+ /* try hard */
+ while (tries--) {
+ struct udp_sock *us;
+ uint16_t port;
+
+ port = (min_port + (rand_u16() % (max_port - min_port)));
+
+ sa_set_port(&laddr, port);
+ err = udp_listen(&us, &laddr, rh, arg);
+ if (err)
+ continue;
+
+ /* OK */
+ if (usp)
+ *usp = us;
+ break;
+ }
+
+ return err;
+}
+
+
+/*
+ * you can call this at any time
+ *
+ * @param addr HOST: SA_ADDR portion is used
+ * non-HOST: SA_ADDR + SA_PORT portion is used
+ *
+ * @param base_addr Optional
+ * @param rel_addr Optional
+ *
+ * @param layer mandatory for HOST and RELAY candidates
+ */
+int trice_lcand_add(struct ice_lcand **lcandp, struct trice *icem,
+ unsigned compid, int proto,
+ uint32_t prio, const struct sa *addr,
+ const struct sa *base_addr,
+ enum ice_cand_type type, const struct sa *rel_addr,
+ enum ice_tcptype tcptype,
+ void *sock, int layer)
+{
+ struct ice_lcand *lcand;
+ int err = 0;
+
+ if (!icem || !compid || !proto || !addr)
+ return EINVAL;
+
+ if (!sa_isset(addr, SA_ADDR)) {
+ DEBUG_WARNING("lcand_add: SA_ADDR is not set\n");
+ return EINVAL;
+ }
+ if (type != ICE_CAND_TYPE_HOST) {
+ if (!sa_isset(addr, SA_PORT)) {
+ DEBUG_WARNING("lcand_add: %s: SA_PORT"
+ " must be set (%J)\n",
+ ice_cand_type2name(type), addr);
+ return EINVAL;
+ }
+ }
+
+ /* lookup candidate, replace if PRIO is higher */
+
+ /* TODO: dont look up TCP-ACTIVE types for now (port is zero) */
+ if (proto == IPPROTO_UDP) {
+
+ lcand = trice_lcand_find(icem, -1, compid, proto, addr);
+ if (lcand) {
+ trice_printf(icem,
+ "add_local[%s.%J] --"
+ " candidate already exists"
+ " (%H)\n",
+ ice_cand_type2name(type), addr,
+ trice_cand_print, lcand);
+
+ if (prio > lcand->attr.prio)
+ lcand = mem_deref(lcand);
+ else {
+ goto out;
+ }
+ }
+ }
+
+ err = trice_add_lcandidate(&lcand, icem, &icem->lcandl, compid, NULL,
+ proto, prio, addr, base_addr,
+ type, rel_addr, tcptype);
+ if (err)
+ return err;
+
+ if (type == ICE_CAND_TYPE_HOST) {
+
+ switch (proto) {
+
+ case IPPROTO_UDP:
+ if (sock) {
+ struct sa laddr;
+
+ lcand->us = mem_ref(sock);
+
+ err = udp_local_get(lcand->us,
+ &laddr);
+ if (err)
+ goto out;
+
+ lcand->attr.addr = *addr;
+ sa_set_port(&lcand->attr.addr,
+ sa_port(&laddr));
+ }
+ else {
+ if (icem->ports.min && icem->ports.max) {
+ err = udp_listen_range(
+ &lcand->us,
+ addr,
+ icem->ports.min,
+ icem->ports.max,
+ dummy_udp_recv,
+ lcand);
+ }
+ else {
+ err = udp_listen(&lcand->us, addr,
+ dummy_udp_recv,
+ lcand);
+ }
+ if (err)
+ goto out;
+
+ err = udp_local_get(lcand->us,
+ &lcand->attr.addr);
+ if (err)
+ goto out;
+ }
+
+ err = udp_register_helper(&lcand->uh, lcand->us,
+ layer,
+ udp_helper_send_handler,
+ udp_helper_recv_handler,
+ lcand);
+ if (err)
+ goto out;
+ break;
+
+ case IPPROTO_TCP:
+
+ /* TCP-transport has 3 variants:
+ active, passive, so */
+
+ if (lcand->attr.tcptype == ICE_TCP_ACTIVE) {
+
+ /* the port MUST be set to 9 (i.e., Discard) */
+ /*sa_set_port(&lcand->attr.addr, 9); */
+ }
+ else if (lcand->attr.tcptype == ICE_TCP_PASSIVE ||
+ lcand->attr.tcptype == ICE_TCP_SO) {
+
+ err = tcp_listen(&lcand->ts, addr,
+ tcp_conn_handler, lcand);
+ if (err)
+ goto out;
+ err = tcp_local_get(lcand->ts,
+ &lcand->attr.addr);
+ if (err)
+ goto out;
+ }
+ else {
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+ }
+ else if (type == ICE_CAND_TYPE_RELAY) {
+
+ switch (proto) {
+
+ case IPPROTO_UDP:
+
+ if (sock) {
+ lcand->us = mem_ref(sock);
+ }
+ else {
+ err = udp_listen(&lcand->us, NULL,
+ dummy_udp_recv, lcand);
+ if (err)
+ goto out;
+ }
+
+ err = udp_register_helper(&lcand->uh, lcand->us,
+ layer,
+ udp_helper_send_handler,
+ udp_helper_recv_handler,
+ lcand);
+ if (err)
+ goto out;
+
+ break;
+
+ default:
+ err = EPROTONOSUPPORT;
+ goto out;
+ }
+ }
+ else if (type == ICE_CAND_TYPE_SRFLX ||
+ type == ICE_CAND_TYPE_PRFLX) {
+
+ /* Special case for SRFLX UDP candidates, if he has
+ * its own UDP-socket that can be used.
+ */
+ if (proto == IPPROTO_UDP && sock) {
+
+ lcand->us = mem_ref(sock);
+
+ err = udp_register_helper(&lcand->uh, lcand->us,
+ layer,
+ udp_helper_send_handler,
+ udp_helper_recv_handler,
+ lcand);
+ if (err)
+ goto out;
+ }
+ }
+
+ lcand->layer = layer;
+
+ if (icem->lrole != ICE_ROLE_UNKNOWN) {
+ /* pair this local-candidate with all existing
+ * remote-candidates */
+ err = trice_candpair_with_local(icem, lcand);
+ if (err)
+ goto out;
+
+ /* new pair -- refresh the checklist timer */
+ trice_checklist_refresh(icem);
+ }
+
+ out:
+ if (err)
+ mem_deref(lcand);
+ else if (lcandp)
+ *lcandp = lcand;
+
+ return err;
+}
+
+
+struct ice_lcand *trice_lcand_find(struct trice *icem,
+ enum ice_cand_type type,
+ unsigned compid, int proto,
+ const struct sa *addr)
+{
+ struct list *lst;
+ struct le *le;
+
+ if (!icem)
+ return NULL;
+
+ if (!proto) {
+ DEBUG_WARNING("find_candidate: invalid args\n");
+ return NULL;
+ }
+
+ lst = &icem->lcandl;
+
+ for (le = list_head(lst); le; le = le->next) {
+
+ struct ice_cand_attr *cand = le->data;
+
+ if (type != (enum ice_cand_type)-1 && type != cand->type)
+ continue;
+
+ if (compid && cand->compid != compid)
+ continue;
+
+ if (cand->proto != proto)
+ continue;
+
+ if (addr && !sa_cmp(&cand->addr, addr, SA_ALL))
+ continue;
+
+ return (void *)cand;
+ }
+
+ return NULL;
+}
+
+
+struct ice_lcand *trice_lcand_find2(const struct trice *icem,
+ enum ice_cand_type type, int af)
+{
+ struct le *le;
+
+ if (!icem)
+ return NULL;
+
+ for (le = list_head(&icem->lcandl); le; le = le->next) {
+
+ struct ice_cand_attr *cand = le->data;
+
+ if (cand->type != type)
+ continue;
+
+ if (af != sa_af(&cand->addr))
+ continue;
+
+ return (void *)cand;
+ }
+
+ return NULL;
+}
+
+
+int trice_lcands_debug(struct re_printf *pf, const struct list *lst)
+{
+ struct le *le;
+ int err;
+
+ err = re_hprintf(pf, " (%u)\n", list_count(lst));
+
+ for (le = list_head(lst); le && !err; le = le->next) {
+
+ const struct ice_lcand *cand = le->data;
+
+ err |= re_hprintf(pf, " {%u} [tx=%3zu, rx=%3zu] "
+ "fnd=%-8s prio=%08x ",
+ cand->attr.compid,
+ cand->stats.n_tx,
+ cand->stats.n_rx,
+ cand->attr.foundation,
+ cand->attr.prio);
+
+ if (str_isset(cand->ifname))
+ err |= re_hprintf(pf, "%s:", cand->ifname);
+
+ err |= re_hprintf(pf, "%24H", trice_cand_print, cand);
+
+ if (sa_isset(&cand->base_addr, SA_ADDR)) {
+ err |= re_hprintf(pf, " (base-addr = %J)",
+ &cand->base_addr);
+ }
+
+ if (sa_isset(&cand->attr.rel_addr, SA_ADDR)) {
+ err |= re_hprintf(pf, " (rel-addr = %J)",
+ &cand->attr.rel_addr);
+ }
+
+ err |= re_hprintf(pf, "\n");
+ }
+
+ return err;
+}
+
+
+void *trice_lcand_sock(struct trice *icem, const struct ice_lcand *lcand)
+{
+ struct ice_lcand *base = NULL;
+
+ if (!icem || !lcand)
+ return NULL;
+
+ if (sa_isset(&lcand->base_addr, SA_ALL)) {
+
+ enum ice_cand_type base_type;
+
+ base_type = ice_cand_type_base(lcand->attr.type);
+
+ base = trice_lcand_find(icem,
+ base_type,
+ lcand->attr.compid,
+ lcand->attr.proto,
+ &lcand->base_addr);
+ }
+
+ /* note: original lcand has presedence, fallback to base-candidate */
+ switch (lcand->attr.type) {
+
+ case ICE_CAND_TYPE_HOST:
+ return lcand->us;
+
+ case ICE_CAND_TYPE_SRFLX:
+ case ICE_CAND_TYPE_PRFLX:
+ if (lcand->us)
+ return lcand->us;
+ else if (base && base->us)
+ return base->us;
+ else {
+ DEBUG_NOTICE("lcand_sock: no SOCK or BASE for "
+ " type '%s'\n",
+ ice_cand_type2name(lcand->attr.type));
+ return NULL;
+ }
+ break;
+
+ case ICE_CAND_TYPE_RELAY:
+ return lcand->us;
+
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+void trice_lcand_recv_packet(struct ice_lcand *lcand,
+ const struct sa *src, struct mbuf *mb)
+{
+ struct sa addr;
+
+ if (!lcand || !src || !mb)
+ return;
+
+ addr = *src;
+
+ udp_helper_recv_handler(&addr, mb, lcand);
+}
diff --git a/src/trice/mod.mk b/src/trice/mod.mk
new file mode 100644
index 0000000..c830b2d
--- /dev/null
+++ b/src/trice/mod.mk
@@ -0,0 +1,15 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS += trice/cand.c
+SRCS += trice/candpair.c
+SRCS += trice/chklist.c
+SRCS += trice/connchk.c
+SRCS += trice/lcand.c
+SRCS += trice/rcand.c
+SRCS += trice/stunsrv.c
+SRCS += trice/tcpconn.c
+SRCS += trice/trice.c
diff --git a/src/trice/rcand.c b/src/trice/rcand.c
new file mode 100644
index 0000000..9516699
--- /dev/null
+++ b/src/trice/rcand.c
@@ -0,0 +1,205 @@
+/**
+ * @file rcand.c Remote ICE Candidates
+ *
+ * Copyright (C) 2010 - 2015 Creytiv.com
+ */
+#include <string.h>
+#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_net.h>
+#include <re_stun.h>
+#include <re_ice.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "rcand"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+static void rcand_destructor(void *data)
+{
+ struct ice_rcand *cand = data;
+
+ list_unlink(&cand->le);
+}
+
+
+static int trice_add_rcandidate(struct ice_rcand **candp,
+ struct list *lst,
+ unsigned compid, const char *foundation,
+ int proto,
+ uint32_t prio, const struct sa *addr,
+ enum ice_cand_type type,
+ enum ice_tcptype tcptype)
+{
+ struct ice_rcand *cand;
+ int err = 0;
+
+ if (!lst || !compid || !foundation || !proto || !addr)
+ return EINVAL;
+
+ cand = mem_zalloc(sizeof(*cand), rcand_destructor);
+ if (!cand)
+ return ENOMEM;
+
+ cand->attr.compid = compid;
+ str_ncpy(cand->attr.foundation, foundation,
+ sizeof(cand->attr.foundation));
+ cand->attr.proto = proto;
+ cand->attr.prio = prio;
+ cand->attr.addr = *addr;
+ cand->attr.type = type;
+ cand->attr.tcptype = tcptype;
+
+ if (err)
+ goto out;
+
+ list_append(lst, &cand->le, cand);
+
+ out:
+ if (err)
+ mem_deref(cand);
+ else if (candp)
+ *candp = cand;
+
+ return err;
+}
+
+
+/* you can call this at any time */
+int trice_rcand_add(struct ice_rcand **rcandp, struct trice *icem,
+ unsigned compid, const char *foundation,
+ int proto, uint32_t prio,
+ const struct sa *addr, enum ice_cand_type type,
+ enum ice_tcptype tcptype)
+{
+ struct ice_rcand *rcand;
+ int sa_flags = SA_ADDR;
+ int err = 0;
+
+ if (!icem || !foundation)
+ return EINVAL;
+
+ if (proto == IPPROTO_UDP)
+ sa_flags |= SA_PORT;
+
+ if (proto == IPPROTO_TCP &&
+ (tcptype == ICE_TCP_PASSIVE || tcptype == ICE_TCP_SO))
+ sa_flags |= SA_PORT;
+
+ if (!sa_isset(addr, sa_flags)) {
+ DEBUG_WARNING("add_remote_candidate: invalid address"
+ " (%J) for %s.%s\n",
+ addr, net_proto2name(proto),
+ ice_tcptype_name(tcptype));
+ return EINVAL;
+ }
+
+ /* avoid duplicates */
+ rcand = trice_rcand_find(icem, compid, proto, addr);
+ if (rcand) {
+
+ if (rcand->attr.type == ICE_CAND_TYPE_PRFLX &&
+ prio > rcand->attr.prio) {
+
+ rcand->attr.type = type;
+ rcand->attr.prio = prio;
+ }
+
+ goto out;
+ }
+
+ err = trice_add_rcandidate(&rcand, &icem->rcandl,
+ compid, foundation,
+ proto, prio, addr, type, tcptype);
+ if (err)
+ goto out;
+
+ if (icem->lrole != ICE_ROLE_UNKNOWN) {
+ /* pair this remote-candidate with all existing
+ * local-candidates */
+ err = trice_candpair_with_remote(icem, rcand);
+ if (err)
+ goto out;
+
+ /* new pair -- refresh the checklist timer */
+ trice_checklist_refresh(icem);
+ }
+
+ out:
+ if (err)
+ mem_deref(rcand);
+ else if (rcandp)
+ *rcandp = rcand;
+
+ return err;
+}
+
+
+struct ice_rcand *trice_rcand_find(struct trice *icem,
+ unsigned compid, int proto,
+ const struct sa *addr)
+{
+ struct list *lst;
+ struct le *le;
+
+ if (!icem)
+ return NULL;
+
+ if (!proto) {
+ DEBUG_WARNING("find_candidate: invalid args\n");
+ return NULL;
+ }
+
+ lst = &icem->rcandl;
+
+ for (le = list_head(lst); le; le = le->next) {
+
+ struct ice_cand_attr *cand = le->data;
+
+ if (compid && cand->compid != compid)
+ continue;
+
+ if (cand->proto != proto)
+ continue;
+
+ if (addr && !sa_cmp(&cand->addr, addr, SA_ALL))
+ continue;
+
+ return (void *)cand;
+ }
+
+ return NULL;
+}
+
+
+int trice_rcands_debug(struct re_printf *pf, const struct list *lst)
+{
+ struct le *le;
+ int err;
+
+ err = re_hprintf(pf, " (%u)\n", list_count(lst));
+
+ for (le = list_head(lst); le && !err; le = le->next) {
+
+ const struct ice_rcand *rcand = le->data;
+
+ err |= re_hprintf(pf, " {%u} "
+ "fnd=%-8s prio=%08x %24H",
+ rcand->attr.compid,
+ rcand->attr.foundation,
+ rcand->attr.prio,
+ trice_cand_print, rcand);
+
+ err |= re_hprintf(pf, "\n");
+ }
+
+ return err;
+}
diff --git a/src/trice/stunsrv.c b/src/trice/stunsrv.c
new file mode 100644
index 0000000..b32785c
--- /dev/null
+++ b/src/trice/stunsrv.c
@@ -0,0 +1,280 @@
+/**
+ * @file stunsrv.c Basic STUN Server for Connectivity checks
+ *
+ * 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_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_stun.h>
+#include <re_ice.h>
+#include <re_udp.h>
+#include <re_tcp.h>
+#include <re_sys.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "stunsrv"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+static const char *sw = "ice stunsrv v" VERSION " (" ARCH "/" OS ")";
+
+
+/*
+ * NOTE about TCP-candidates:
+ *
+ * Note that STUN requests received on a passive TCP candidate
+ * will typically produce a remote peer reflexive candidate.
+ */
+static int handle_stun_full(struct trice *icem, struct ice_lcand *lcand,
+ void *sock, const struct sa *src,
+ uint32_t prio, bool use_cand)
+{
+ struct ice_candpair *pair = NULL;
+ struct ice_rcand *rcand;
+ enum ice_tcptype tcptype_rev;
+ int err = 0;
+
+ trice_tracef(icem, 36,
+ "[%u] STUNSRV: Rx Binding Request [%H <--- %J] %s\n",
+ lcand->attr.compid,
+ trice_cand_print, lcand,
+ src,
+ use_cand ? "[USE]" : "");
+
+ tcptype_rev = ice_tcptype_reverse(lcand->attr.tcptype);
+
+ rcand = trice_rcand_find(icem, lcand->attr.compid,
+ lcand->attr.proto, src);
+ if (!rcand) {
+
+ if (icem->conf.enable_prflx) {
+
+ err = trice_rcand_add(&rcand, icem,
+ lcand->attr.compid,
+ "444", lcand->attr.proto, prio,
+ src, ICE_CAND_TYPE_PRFLX,
+ tcptype_rev);
+ if (err)
+ return err;
+
+ trice_printf(icem, "{%u} added PRFLX "
+ "remote candidate (%H)\n",
+ lcand->attr.compid,
+ trice_cand_print, rcand);
+ }
+ }
+
+ /* already valid, skip */
+ pair = trice_candpair_find(&icem->validl, lcand, rcand);
+ if (pair)
+ goto out;
+
+ /* note: the candidate-pair can exist in either list */
+ pair = trice_candpair_find(&icem->checkl, lcand, rcand);
+ if (!pair) {
+ if (icem->conf.enable_prflx) {
+ DEBUG_WARNING("{%u} candidate pair not found:"
+ " source=%J\n",
+ lcand->attr.compid, src);
+ }
+ goto out;
+ }
+
+ /* 7.2.1.5. Updating the Nominated Flag */
+ if (use_cand) {
+ if (icem->lrole == ICE_ROLE_CONTROLLED) {
+
+ pair->nominated = true;
+ }
+ }
+
+ out:
+ /*
+ send a triggered request
+ */
+ if (pair && use_cand) {
+
+ if (icem->checklist && !pair->trigged) {
+
+ err = trice_conncheck_trigged(icem, pair,
+ sock, use_cand);
+ if (err) {
+ DEBUG_WARNING("ice_checklist_stun_request"
+ " failed (%m)\n",
+ err);
+ }
+ pair->trigged = true;
+ }
+ }
+
+ return 0;
+}
+
+
+static int stunsrv_ereply(struct trice *icem, struct ice_lcand *lcand,
+ void *sock, const struct sa *src,
+ size_t presz, const struct stun_msg *req,
+ uint16_t scode, const char *reason)
+{
+ DEBUG_WARNING("[%H] replying error to %J (%u %s)\n",
+ trice_cand_print, lcand,
+ src,
+ scode, reason);
+
+ trice_tracef(icem, 31,
+ "[%u] STUNSRV: Tx error [%J <--- %H] (%u %s)\n",
+ lcand->attr.compid,
+ src,
+ trice_cand_print, lcand,
+ scode, reason);
+
+ return stun_ereply(lcand->attr.proto, sock, src, presz, req,
+ scode, reason,
+ (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 1,
+ STUN_ATTR_SOFTWARE, icem->sw ? icem->sw : sw);
+}
+
+
+int trice_stund_recv(struct trice *icem, struct ice_lcand *lcand,
+ void *sock, const struct sa *src,
+ struct stun_msg *req, size_t presz)
+{
+ struct stun_attr *attr;
+ struct pl lu, ru;
+ int err;
+
+ /* RFC 5389: Fingerprint errors are silently discarded */
+ err = stun_msg_chk_fingerprint(req);
+ if (err)
+ return err;
+
+ err = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd));
+ if (err) {
+ DEBUG_WARNING("message-integrity failed (src=%J)\n", src);
+ if (err == EBADMSG)
+ goto unauth;
+ else
+ goto badmsg;
+ }
+
+ attr = stun_msg_attr(req, STUN_ATTR_USERNAME);
+ if (!attr)
+ goto badmsg;
+
+ err = re_regex(attr->v.username, strlen(attr->v.username),
+ "[^:]+:[^]+", &lu, &ru);
+ if (err) {
+ DEBUG_WARNING("could not parse USERNAME attribute (%s)\n",
+ attr->v.username);
+ goto unauth;
+ }
+ if (pl_strcmp(&lu, icem->lufrag)) {
+ DEBUG_WARNING("local ufrag err (expected %s, actual %r)\n",
+ icem->lufrag, &lu);
+ goto unauth;
+ }
+ if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag)) {
+ DEBUG_WARNING("remote ufrag err (expected %s, actual %r)\n",
+ icem->rufrag, &ru);
+ goto unauth;
+ }
+
+ if (icem->lrole == ICE_ROLE_UNKNOWN) {
+ err = trice_reqbuf_append(icem, lcand, sock, src, req, presz);
+ if (err) {
+ DEBUG_WARNING("unable to buffer STUN request: %m\n",
+ err);
+ }
+ }
+
+ return trice_stund_recv_role_set(icem, lcand, sock, src, req, presz);
+
+ badmsg:
+ return stunsrv_ereply(icem, lcand, sock, src, presz, req,
+ 400, "Bad Request");
+
+ unauth:
+ return stunsrv_ereply(icem, lcand, sock, src, presz, req,
+ 401, "Unauthorized");
+}
+
+
+int trice_stund_recv_role_set(struct trice *icem, struct ice_lcand *lcand,
+ void *sock, const struct sa *src,
+ struct stun_msg *req, size_t presz)
+{
+ struct stun_attr *attr;
+ enum ice_role remote_role = ICE_ROLE_UNKNOWN;
+ uint64_t tiebrk = 0;
+ uint32_t prio_prflx;
+ int err;
+ bool use_cand = false;
+
+ attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED);
+ if (attr) {
+ remote_role = ICE_ROLE_CONTROLLED;
+ tiebrk = attr->v.uint64;
+ }
+
+ attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING);
+ if (attr) {
+ remote_role = ICE_ROLE_CONTROLLING;
+ tiebrk = attr->v.uint64;
+ }
+
+ if (remote_role == ICE_ROLE_UNKNOWN)
+ goto badmsg;
+
+ if (remote_role == icem->lrole) {
+ DEBUG_NOTICE("role conflict detected (both %s)\n",
+ ice_role2name(remote_role));
+
+ if (icem->tiebrk >= tiebrk)
+ trice_switch_local_role(icem);
+ else
+ goto conflict;
+ }
+
+ attr = stun_msg_attr(req, STUN_ATTR_PRIORITY);
+ if (attr)
+ prio_prflx = attr->v.uint32;
+ else
+ goto badmsg;
+
+ attr = stun_msg_attr(req, STUN_ATTR_USE_CAND);
+ if (attr)
+ use_cand = true;
+
+ err = handle_stun_full(icem, lcand, sock, src, prio_prflx, use_cand);
+
+ if (err)
+ goto badmsg;
+
+ trice_tracef(icem, 32,
+ "[%u] STUNSRV: Tx success respons [%H ---> %J]\n",
+ lcand->attr.compid,
+ trice_cand_print, lcand, src);
+
+ return stun_reply(lcand->attr.proto, sock, src, presz, req,
+ (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2,
+ STUN_ATTR_XOR_MAPPED_ADDR, src,
+ STUN_ATTR_SOFTWARE, icem->sw ? icem->sw : sw);
+
+
+ badmsg:
+ return stunsrv_ereply(icem, lcand, sock, src, presz, req,
+ 400, "Bad Request");
+
+ conflict:
+ return stunsrv_ereply(icem, lcand, sock, src, presz, req,
+ 487, "Role Conflict");
+}
diff --git a/src/trice/tcpconn.c b/src/trice/tcpconn.c
new file mode 100644
index 0000000..5159ee7
--- /dev/null
+++ b/src/trice/tcpconn.c
@@ -0,0 +1,286 @@
+/**
+ * @file tcpconn.c ICE handling of TCP-connections
+ *
+ * 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_list.h>
+#include <re_tmr.h>
+#include <re_sa.h>
+#include <re_tcp.h>
+#include <re_udp.h>
+#include <re_stun.h>
+#include <re_ice.h>
+#include <re_shim.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "tcpconn"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/* `mb' contains a complete frame */
+static bool shim_frame_handler(struct mbuf *mb, void *arg)
+{
+ struct ice_tcpconn *conn = arg;
+
+ return conn->frameh(conn->icem, conn->tc, &conn->paddr, mb, conn->arg);
+}
+
+
+static void tcp_estab_handler(void *arg)
+{
+ struct ice_tcpconn *conn = arg;
+ struct trice *icem = conn->icem;
+ struct le *le;
+ int err;
+
+ conn->estab = true;
+
+ trice_printf(icem, "TCP established (local=%J <---> peer=%J)\n",
+ &conn->laddr, &conn->paddr);
+
+ err = shim_insert(&conn->shim, conn->tc, conn->layer,
+ shim_frame_handler, conn);
+ if (err)
+ goto out;
+
+ if (!icem->checklist)
+ goto out;
+
+ /* check all pending CONNCHECKs for TCP */
+ le = icem->checklist->conncheckl.head;
+ while (le) {
+ struct ice_conncheck *cc = le->data;
+ struct ice_candpair *pair = cc->pair;
+ le = le->next;
+
+ if (pair->state == ICE_CANDPAIR_INPROGRESS &&
+ pair->lcand->attr.compid == conn->compid &&
+ pair->lcand->attr.proto == IPPROTO_TCP &&
+ sa_cmp(&pair->lcand->attr.addr, &conn->laddr, SA_ADDR) &&
+ sa_cmp(&pair->rcand->attr.addr, &conn->paddr, SA_ALL)) {
+
+ trice_printf(icem,
+ " estab: sending pending check"
+ " from %j to %J\n",
+ &pair->lcand->attr.addr,
+ &pair->rcand->attr.addr);
+
+ /* todo: */
+ pair->conn = mem_ref(conn);
+
+ err = trice_conncheck_stun_request(icem->checklist, cc,
+ pair, conn->tc,
+ cc->use_cand);
+ if (err) {
+ DEBUG_WARNING("stun_request error (%m)\n",
+ err);
+ }
+ }
+ }
+
+ out:
+ if (err) {
+ DEBUG_WARNING("estab: errors (%m)\n", err);
+ }
+}
+
+
+/* todo: re-connect if estab and active (with a timer) */
+static void tcp_close_handler(int err, void *arg)
+{
+ struct ice_tcpconn *conn = arg;
+ struct trice *icem = conn->icem;
+ struct le *le;
+
+ trice_printf(conn->icem, "TCP-connection [%J -> %J] closed (%m)\n",
+ &conn->laddr, &conn->paddr, err);
+
+ err = err ? err : ECONNRESET;
+
+ /* note: helper must be closed before tc */
+ conn->shim = mem_deref(conn->shim);
+ conn->tc = mem_deref(conn->tc);
+
+ /* todo: iterate through conncheckl and cancel all checks
+ * that are using this conn
+ */
+
+ le = conn->icem->checkl.head;
+ while (le) {
+ struct ice_candpair *pair = le->data;
+
+ le = le->next;
+
+ if (pair->lcand->attr.compid == conn->compid &&
+ pair->lcand->attr.proto == IPPROTO_TCP &&
+ sa_cmp(&pair->rcand->attr.addr, &conn->paddr, SA_ALL)) {
+
+ trice_candpair_failed(pair, err, 0);
+
+ if (icem->checklist) {
+ icem->checklist->failh(err, 0,
+ pair,
+ icem->checklist->arg);
+ }
+ }
+ }
+
+ mem_deref(conn);
+}
+
+
+static void conn_destructor(void *arg)
+{
+ struct ice_tcpconn *conn = arg;
+
+ list_unlink(&conn->le);
+ mem_deref(conn->shim);
+ mem_deref(conn->tc);
+}
+
+
+/* ts: only for accept */
+int trice_conn_alloc(struct list *connl, struct trice *icem, unsigned compid,
+ bool active, const struct sa *laddr, const struct sa *peer,
+ struct tcp_sock *ts, int layer,
+ tcpconn_frame_h *frameh, void *arg)
+{
+ struct ice_tcpconn *conn;
+ int err = 0;
+
+ if (!connl || !icem || !laddr || !peer || !frameh)
+ return EINVAL;
+
+ conn = mem_zalloc(sizeof(*conn), conn_destructor);
+ if (!conn)
+ return ENOMEM;
+
+ conn->icem = icem;
+ conn->active = active;
+ conn->paddr = *peer;
+ conn->compid = compid;
+ conn->layer = layer;
+ conn->frameh = frameh;
+ conn->arg = arg;
+
+ if (active) {
+
+ trice_printf(conn->icem, "<%p> TCP connecting"
+ " [laddr=%J paddr=%J] ..\n",
+ icem, laddr, peer);
+
+ /* This connection is opened from the local candidate of the
+ pair to the remote candidate of the pair.
+ */
+ err = tcp_conn_alloc(&conn->tc, peer, tcp_estab_handler,
+ NULL, tcp_close_handler,
+ conn);
+ if (err) {
+ DEBUG_WARNING("tcp_conn_alloc [peer=%J] (%m)\n",
+ peer, err);
+ goto out;
+ }
+
+ err = tcp_conn_bind(conn->tc, laddr);
+ if (err) {
+ DEBUG_WARNING("tcp_conn_bind [laddr=%J paddr=%J]"
+ " (%m)\n",
+ laddr, peer, err);
+ goto out;
+ }
+
+ err = tcp_conn_connect(conn->tc, peer);
+ if (err) {
+ /* NOTE: this happens sometimes on OSX when
+ * setting up two S-O connections
+ */
+ if (err == EADDRINUSE) {
+ re_printf("EADDRINUSE\n");
+ err = 0;
+ }
+ else {
+ DEBUG_NOTICE("tcp_conn_connect [peer=%J]"
+ " (%d/%m)\n",
+ peer, err, err);
+ goto out;
+ }
+ }
+ }
+ else {
+ err = tcp_accept(&conn->tc, ts, tcp_estab_handler,
+ NULL, tcp_close_handler, conn);
+ if (err) {
+ tcp_reject(ts);
+ goto out;
+ }
+ }
+
+ err = tcp_conn_local_get(conn->tc, &conn->laddr);
+ if (err)
+ goto out;
+
+ list_append(connl, &conn->le, conn);
+
+ out:
+ if (err)
+ mem_deref(conn);
+
+ return err;
+}
+
+
+/* NOTE: laddr matching is SA_ADDR only */
+struct ice_tcpconn *trice_conn_find(struct list *connl, unsigned compid,
+ const struct sa *laddr,
+ const struct sa *peer)
+{
+ struct le *le;
+
+ for (le = list_head(connl); le; le = le->next) {
+
+ struct ice_tcpconn *conn = le->data;
+
+ if (compid != conn->compid)
+ continue;
+
+ /* NOTE: only for established */
+ if (!conn->estab)
+ continue;
+
+ if (sa_cmp(laddr, &conn->laddr, SA_ADDR) &&
+ sa_cmp(peer, &conn->paddr, SA_ALL))
+ return conn;
+ }
+
+ return NULL;
+}
+
+
+int trice_conn_debug(struct re_printf *pf, const struct ice_tcpconn *conn)
+{
+ int err;
+
+ if (!conn)
+ return 0;
+
+ err = re_hprintf(pf, "... {%u} [%s|%5s] %J - %J "
+ " (usage = %u) ",
+ conn->compid,
+ conn->active ? "Active" : "Passive",
+ conn->estab ? "ESTAB" : " ",
+ &conn->laddr, &conn->paddr,
+ mem_nrefs(conn)-1);
+
+ if (conn->shim)
+ err |= shim_debug(pf, conn->shim);
+
+ return err;
+}
diff --git a/src/trice/trice.c b/src/trice/trice.c
new file mode 100644
index 0000000..780226d
--- /dev/null
+++ b/src/trice/trice.c
@@ -0,0 +1,551 @@
+/**
+ * @file icem.c ICE Media stream
+ *
+ * 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_stun.h>
+#include <re_ice.h>
+#include <re_sys.h>
+#include <re_trice.h>
+#include "trice.h"
+
+
+#define DEBUG_MODULE "icem"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+static const struct trice_conf conf_default = {
+ ICE_NOMINATION_REGULAR,
+ false,
+ false,
+ false,
+ true,
+ false
+};
+
+
+static void trice_destructor(void *data)
+{
+ struct trice *icem = data;
+
+ mem_deref(icem->checklist);
+
+ list_flush(&icem->validl);
+ list_flush(&icem->checkl);
+ list_flush(&icem->lcandl);
+ list_flush(&icem->rcandl);
+ list_flush(&icem->reqbufl);
+
+ list_flush(&icem->connl);
+
+ mem_deref(icem->rufrag);
+ mem_deref(icem->rpwd);
+ mem_deref(icem->lufrag);
+ mem_deref(icem->lpwd);
+ mem_deref(icem->sw);
+}
+
+
+/**
+ * Allocate a new ICE Media object
+ *
+ * @param icemp Pointer to allocated ICE Media object
+ * @param conf ICE configuration
+ * @param role Local role
+ * @param lufrag Local username fragment
+ * @param lpwd Local password
+ * @param estabh Candidate pair established handler
+ * @param closeh Close / error handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+
+int trice_alloc(struct trice **icemp, const struct trice_conf *conf,
+ enum ice_role role,
+ const char *lufrag, const char *lpwd)
+{
+ struct trice *icem;
+ int err = 0;
+
+ if (!icemp || !lufrag || !lpwd)
+ return EINVAL;
+
+ if (str_len(lufrag) < 4 || str_len(lpwd) < 22) {
+ DEBUG_WARNING("alloc: lufrag/lpwd is too short\n");
+ return EINVAL;
+ }
+
+ icem = mem_zalloc(sizeof(*icem), trice_destructor);
+ if (!icem)
+ return ENOMEM;
+
+ icem->conf = conf ? *conf : conf_default;
+ list_init(&icem->reqbufl);
+ list_init(&icem->lcandl);
+ list_init(&icem->rcandl);
+ list_init(&icem->checkl);
+ list_init(&icem->validl);
+
+ icem->lrole = role;
+ icem->tiebrk = rand_u64();
+
+ err |= str_dup(&icem->lufrag, lufrag);
+ err |= str_dup(&icem->lpwd, lpwd);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(icem);
+ else
+ *icemp = icem;
+
+ return err;
+}
+
+
+int trice_set_remote_ufrag(struct trice *icem, const char *rufrag)
+{
+ if (!icem || !rufrag)
+ return EINVAL;
+
+ icem->rufrag = mem_deref(icem->rufrag);
+ return str_dup(&icem->rufrag, rufrag);
+}
+
+
+int trice_set_remote_pwd(struct trice *icem, const char *rpwd)
+{
+ if (!icem || !rpwd)
+ return EINVAL;
+
+ icem->rpwd = mem_deref(icem->rpwd);
+
+ return str_dup(&icem->rpwd, rpwd);
+}
+
+
+int trice_set_software(struct trice *icem, const char *sw)
+{
+ if (!icem)
+ return EINVAL;
+
+ icem->sw = mem_deref(icem->sw);
+
+ if (sw)
+ return str_dup(&icem->sw, sw);
+
+ return 0;
+}
+
+
+struct trice_conf *trice_conf(struct trice *icem)
+{
+ return icem ? &icem->conf : NULL;
+}
+
+
+/* note: call this ONCE AFTER role has been set */
+static void trice_create_candpairs(struct trice *icem)
+{
+ struct list *lst;
+ struct le *le;
+ bool refresh_checklist = false;
+ int err;
+
+ lst = &icem->lcandl;
+ for (le = list_head(lst); le; le = le->next) {
+ struct ice_lcand *lcand = le->data;
+
+ /* pair this local-candidate with all existing
+ * remote-candidates */
+ err = trice_candpair_with_local(icem, lcand);
+ if (err) {
+ DEBUG_WARNING("trice_candpair_with_local: %m\n", err);
+ }
+ else {
+ refresh_checklist = true;
+ }
+ }
+
+ lst = &icem->rcandl;
+ for (le = list_head(lst); le; le = le->next) {
+ struct ice_rcand *rcand = le->data;
+
+ /* pair this remote-candidate with all existing
+ * local-candidates */
+ err = trice_candpair_with_remote(icem, rcand);
+ if (err) {
+ DEBUG_WARNING("trice_candpair_with_remote: %m\n", err);
+ }
+ else {
+ refresh_checklist = true;
+ }
+ }
+
+ /* new pair -- refresh the checklist timer */
+ if (refresh_checklist)
+ trice_checklist_refresh(icem);
+}
+
+
+/* note: call this AFTER role has been set AND candidate pairs
+ * have been created */
+static void trice_reqbuf_process(struct trice *icem)
+{
+ struct le *le;
+
+ le = list_head(&icem->reqbufl);
+ while (le) {
+ struct trice_reqbuf *reqbuf = le->data;
+ le = le->next;
+
+ DEBUG_PRINTF("trice_reqbuf_process: Processing buffered "
+ "request\n");
+
+ (void)trice_stund_recv_role_set(icem, reqbuf->lcand,
+ reqbuf->sock, &reqbuf->src, reqbuf->req,
+ reqbuf->presz);
+
+ mem_deref(reqbuf);
+ }
+}
+
+
+/**
+ * Set the local role to either CONTROLLING or CONTROLLED.
+ * Note: The role can be set multiple times.
+ *
+ * @param icem ICE Media object
+ * @param role New local role
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int trice_set_role(struct trice *trice, enum ice_role role)
+{
+ bool refresh;
+
+ if (!trice)
+ return EINVAL;
+
+ /* Cannot change the role to unknown */
+ if (role == ICE_ROLE_UNKNOWN)
+ return EINVAL;
+
+ if (trice->lrole == role)
+ return 0;
+
+ /* Cannot switch role manually once it has been set */
+ if (trice->lrole == ICE_ROLE_UNKNOWN)
+ refresh = false;
+ else
+ refresh = true;
+
+ trice->lrole = role;
+
+ /* Create candidate pairs and process pending requests */
+ if (refresh) {
+ trice_candpair_prio_order(&trice->checkl,
+ role == ICE_ROLE_CONTROLLING);
+ }
+ else {
+ trice_create_candpairs(trice);
+ }
+
+ trice_reqbuf_process(trice);
+
+ return 0;
+}
+
+
+/**
+ * Get the local role
+ *
+ * @param icem ICE Media object
+ *
+ * @return Local role
+ */
+enum ice_role trice_local_role(const struct trice *icem)
+{
+ if (!icem)
+ return ICE_ROLE_UNKNOWN;
+
+ return icem->lrole;
+}
+
+
+/**
+ * Print debug information for the ICE Media
+ *
+ * @param pf Print function for debug output
+ * @param icem ICE Media object
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int trice_debug(struct re_printf *pf, const struct trice *icem)
+{
+ struct le *le;
+ int err = 0;
+
+ if (!icem)
+ return 0;
+
+ err |= re_hprintf(pf, "----- ICE Media <%p> -----\n", icem);
+
+ err |= re_hprintf(pf, " local_role=%s\n",
+ ice_role2name(icem->lrole));
+ err |= re_hprintf(pf, " local_ufrag=\"%s\" local_pwd=\"%s\"\n",
+ icem->lufrag, icem->lpwd);
+
+ err |= re_hprintf(pf, " Local Candidates: %H",
+ trice_lcands_debug, &icem->lcandl);
+ err |= re_hprintf(pf, " Remote Candidates: %H",
+ trice_rcands_debug, &icem->rcandl);
+ err |= re_hprintf(pf, " Check list: ");
+ err |= trice_candpairs_debug(pf, icem->conf.ansi, &icem->checkl);
+
+ err |= re_hprintf(pf, " Valid list: ");
+ err |= trice_candpairs_debug(pf, icem->conf.ansi, &icem->validl);
+
+ err |= re_hprintf(pf, " Buffered STUN Requests: (%u)\n",
+ list_count(&icem->reqbufl));
+
+ if (icem->checklist)
+ err |= trice_checklist_debug(pf, icem->checklist);
+
+ err |= re_hprintf(pf, " TCP Connections: (%u)\n",
+ list_count(&icem->connl));
+
+ for (le = list_head(&icem->connl); le; le = le->next) {
+ struct ice_tcpconn *conn = le->data;
+
+ err |= re_hprintf(pf, " %H\n",
+ trice_conn_debug, conn);
+ }
+
+ return err;
+}
+
+
+/**
+ * Get the list of Local Candidates (struct cand)
+ *
+ * @param icem ICE Media object
+ *
+ * @return List of Local Candidates
+ */
+struct list *trice_lcandl(const struct trice *icem)
+{
+ return icem ? (struct list *)&icem->lcandl : NULL;
+}
+
+
+/**
+ * Get the list of Remote Candidates (struct cand)
+ *
+ * @param icem ICE Media object
+ *
+ * @return List of Remote Candidates
+ */
+struct list *trice_rcandl(const struct trice *icem)
+{
+ return icem ? (struct list *)&icem->rcandl : NULL;
+}
+
+
+/**
+ * Get the checklist of Candidate Pairs
+ *
+ * @param icem ICE Media object
+ *
+ * @return Checklist (struct ice_candpair)
+ */
+struct list *trice_checkl(const struct trice *icem)
+{
+ return icem ? (struct list *)&icem->checkl : NULL;
+}
+
+
+/**
+ * Get the list of valid Candidate Pairs
+ *
+ * @param icem ICE Media object
+ *
+ * @return Validlist (struct ice_candpair)
+ */
+struct list *trice_validl(const struct trice *icem)
+{
+ return icem ? (struct list *)&icem->validl : NULL;
+}
+
+
+void trice_printf(struct trice *icem, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!icem || !icem->conf.debug)
+ return;
+
+ va_start(ap, fmt);
+ (void)re_printf("%v", fmt, &ap);
+ va_end(ap);
+}
+
+
+void trice_tracef(struct trice *icem, int color, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!icem || !icem->conf.trace)
+ return;
+
+ if (icem->conf.ansi && color) {
+ re_printf("\x1b[%dm", color);
+ }
+
+ va_start(ap, fmt);
+ (void)re_printf("%v", fmt, &ap);
+ va_end(ap);
+
+ if (icem->conf.ansi && color) {
+ re_printf("\x1b[;m");
+ }
+}
+
+
+void trice_switch_local_role(struct trice *ice)
+{
+ enum ice_role new_role;
+
+ if (!ice)
+ return;
+
+ switch (ice->lrole) {
+
+ case ICE_ROLE_CONTROLLING:
+ new_role = ICE_ROLE_CONTROLLED;
+ break;
+
+ case ICE_ROLE_CONTROLLED:
+ new_role = ICE_ROLE_CONTROLLING;
+ break;
+
+ default:
+ DEBUG_WARNING("trice_switch_local_role: local role unknown\n");
+ return;
+ }
+
+ DEBUG_NOTICE("Switch local role from %s to %s\n",
+ ice_role2name(ice->lrole), ice_role2name(new_role));
+
+ ice->lrole = new_role;
+
+ /* recompute pair priorities for all media streams */
+ trice_candpair_prio_order(&ice->checkl,
+ ice->lrole == ICE_ROLE_CONTROLLING);
+}
+
+
+/* sock = [ struct udp_sock | struct tcp_conn ] */
+bool trice_stun_process(struct trice *icem, struct ice_lcand *lcand,
+ int proto, void *sock, const struct sa *src,
+ struct mbuf *mb)
+{
+ struct stun_msg *msg = NULL;
+ struct stun_unknown_attr ua;
+ size_t start = mb->pos;
+ (void)proto;
+
+ if (stun_msg_decode(&msg, mb, &ua)) {
+ return false; /* continue recv-processing */
+ }
+
+ if (STUN_METHOD_BINDING == stun_msg_method(msg)) {
+
+ switch (stun_msg_class(msg)) {
+
+ case STUN_CLASS_REQUEST:
+ (void)trice_stund_recv(icem, lcand, sock,
+ src, msg, start);
+ break;
+
+ default:
+ if (icem->checklist) {
+ (void)stun_ctrans_recv(icem->checklist->stun,
+ msg, &ua);
+ }
+ else {
+ DEBUG_NOTICE("STUN resp from %J dropped"
+ " (no checklist)\n",
+ src);
+ }
+ break;
+ }
+ }
+
+ mem_deref(msg);
+
+ return true;
+}
+
+
+static void trice_reqbuf_destructor(void *data)
+{
+ struct trice_reqbuf *reqbuf = data;
+
+ list_unlink(&reqbuf->le);
+
+ mem_deref(reqbuf->req);
+ mem_deref(reqbuf->sock);
+ mem_deref(reqbuf->lcand);
+}
+
+
+int trice_reqbuf_append(struct trice *icem, struct ice_lcand *lcand,
+ void *sock, const struct sa *src,
+ struct stun_msg *req, size_t presz)
+{
+ struct trice_reqbuf *reqbuf;
+
+ if (!icem || !src ||!req)
+ return EINVAL;
+
+ reqbuf = mem_zalloc(sizeof(*reqbuf), trice_reqbuf_destructor);
+ if (!reqbuf)
+ return ENOMEM;
+
+ DEBUG_PRINTF("trice_reqbuf_append: Buffering request\n");
+ reqbuf->lcand = mem_ref(lcand);
+ reqbuf->sock = mem_ref(sock);
+ reqbuf->src = *src;
+ reqbuf->req = mem_ref(req);
+ reqbuf->presz = presz;
+
+ list_append(&icem->reqbufl, &reqbuf->le, reqbuf);
+
+ return 0;
+}
+
+int trice_set_port_range(struct trice *trice,
+ uint16_t min_port, uint16_t max_port)
+{
+ if (!trice)
+ return EINVAL;
+
+ if (max_port < min_port)
+ return ERANGE;
+
+ trice->ports.min = min_port;
+ trice->ports.max = max_port;
+
+ return 0;
+}
diff --git a/src/trice/trice.h b/src/trice/trice.h
new file mode 100644
index 0000000..eafcb2c
--- /dev/null
+++ b/src/trice/trice.h
@@ -0,0 +1,208 @@
+/**
+ * @file ice.h Internal Interface to ICE
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+struct ice_tcpconn;
+struct ice_conncheck;
+
+
+/**
+ * Active Checklist. Only used by Full-ICE and Trickle-ICE
+ */
+struct ice_checklist {
+ struct trice *icem; /* parent */
+
+ struct tmr tmr_pace; /**< Timer for pacing STUN requests */
+ uint32_t interval; /**< Interval in [ms] */
+ struct stun *stun; /**< STUN Transport */
+ struct list conncheckl;
+ bool is_running; /**< Checklist is running */
+
+ /* callback handlers */
+ trice_estab_h *estabh;
+ trice_failed_h *failh;
+ void *arg;
+};
+
+
+/**
+ * Defines an ICE media-stream
+ *
+ * NOTE: We try to follow the Resource Acquisition Is Initialization (RAII)
+ * programming idiom, which means:
+ *
+ * - at any time is the number of local/remote candidates correct
+ * - at any time is the checklist up to date (matching local/remote candidates)
+ *
+ */
+struct trice {
+ struct trice_conf conf;
+ enum ice_role lrole; /**< Local role */
+ uint64_t tiebrk; /**< Tie-break value for roleconflict */
+
+ /* stun/authentication */
+ char *lufrag; /**< Local Username fragment */
+ char *lpwd; /**< Local Password */
+ char *rufrag; /**< Remote Username fragment */
+ char *rpwd; /**< Remote Password */
+
+ struct list lcandl; /**< local candidates (add order) */
+ struct list rcandl; /**< remote candidates (add order) */
+ struct list checkl; /**< Check List of cand pairs (sorted) */
+ struct list validl; /**< Valid List of cand pairs (sorted) */
+ struct list reqbufl; /**< buffered incoming requests */
+
+ struct ice_checklist *checklist;
+
+ struct list connl; /**< TCP-connections for all components */
+
+ char *sw;
+
+ /* Port range */
+ struct {
+ uint16_t min;
+ uint16_t max;
+ } ports;
+};
+
+
+/**
+ * Holds an unhandled STUN request message that will be handled once
+ * the role has been determined.
+ */
+struct trice_reqbuf {
+ struct le le; /**< list element */
+ struct ice_lcand *lcand; /**< corresponding local candidate */
+ void *sock; /**< request's socket */
+ struct sa src; /**< source address */
+ struct stun_msg *req; /**< buffered STUN request */
+ size_t presz; /**< number of bytes in preamble */
+};
+
+
+/* return TRUE if handled */
+typedef bool (tcpconn_frame_h)(struct trice *icem,
+ struct tcp_conn *tc, struct sa *src,
+ struct mbuf *mb, void *arg);
+
+/**
+ * Defines a TCP-connection from local-adress to remote-address
+ *
+ * - one TCP-connection can be shared by multiple candidate pairs
+ *
+ * - one TCP-connection is always created by the Local Candidate
+ */
+struct ice_tcpconn {
+ struct trice *icem; /* parent */
+ struct le le;
+ struct tcp_conn *tc;
+ struct shim *shim;
+ struct sa laddr;
+ struct sa paddr;
+ unsigned compid;
+ int layer;
+ bool active;
+ bool estab;
+
+ tcpconn_frame_h *frameh;
+ void *arg;
+};
+
+struct ice_conncheck {
+ struct le le;
+ struct ice_candpair *pair; /* pointer */
+ struct stun_ctrans *ct_conn;
+ struct trice *icem; /* owner */
+ bool use_cand;
+ bool term;
+};
+
+
+/* cand */
+int trice_add_lcandidate(struct ice_lcand **candp,
+ struct trice *icem, struct list *lst,
+ unsigned compid, char *foundation, int proto,
+ uint32_t prio, const struct sa *addr,
+ const struct sa *base_addr,
+ enum ice_cand_type type,
+ const struct sa *rel_addr,
+ enum ice_tcptype tcptype);
+int trice_lcands_debug(struct re_printf *pf, const struct list *lst);
+int trice_rcands_debug(struct re_printf *pf, const struct list *lst);
+
+
+/* candpair */
+int trice_candpair_alloc(struct ice_candpair **cpp, struct trice *icem,
+ struct ice_lcand *lcand, struct ice_rcand *rcand);
+void trice_candpair_prio_order(struct list *lst, bool controlling);
+void trice_candpair_make_valid(struct trice *icem, struct ice_candpair *pair);
+void trice_candpair_failed(struct ice_candpair *cp, int err, uint16_t scode);
+void trice_candpair_set_state(struct ice_candpair *cp,
+ enum ice_candpair_state state);
+bool trice_candpair_iscompleted(const struct ice_candpair *cp);
+bool trice_candpair_cmp_fnd(const struct ice_candpair *cp1,
+ const struct ice_candpair *cp2);
+struct ice_candpair *trice_candpair_find(const struct list *lst,
+ const struct ice_lcand *lcand,
+ const struct ice_rcand *rcand);
+int trice_candpair_with_local(struct trice *icem, struct ice_lcand *lcand);
+int trice_candpair_with_remote(struct trice *icem, struct ice_rcand *rcand);
+const char *trice_candpair_state2name(enum ice_candpair_state st);
+
+
+/* STUN server */
+int trice_stund_recv(struct trice *icem, struct ice_lcand *lcand,
+ void *sock, const struct sa *src,
+ struct stun_msg *req, size_t presz);
+int trice_stund_recv_role_set(struct trice *icem, struct ice_lcand *lcand,
+ void *sock, const struct sa *src,
+ struct stun_msg *req, size_t presz);
+
+
+/* ICE media */
+void trice_switch_local_role(struct trice *ice);
+void trice_printf(struct trice *icem, const char *fmt, ...);
+void trice_tracef(struct trice *icem, int color, const char *fmt, ...);
+
+
+/* ICE checklist */
+int trice_checklist_debug(struct re_printf *pf,
+ const struct ice_checklist *ic);
+void trice_conncheck_schedule_check(struct trice *icem);
+int trice_checklist_update(struct trice *icem);
+void trice_checklist_refresh(struct trice *icem);
+
+
+/* ICE conncheck */
+int trice_conncheck_stun_request(struct ice_checklist *ic,
+ struct ice_conncheck *cc,
+ struct ice_candpair *cp, void *sock,
+ bool cc_use_cand);
+int trice_conncheck_trigged(struct trice *icem, struct ice_candpair *pair,
+ void *sock, bool use_cand);
+int trice_conncheck_debug(struct re_printf *pf,
+ const struct ice_conncheck *cc);
+
+
+/* TCP connections */
+
+
+int trice_conn_alloc(struct list *connl, struct trice *icem, unsigned compid,
+ bool active, const struct sa *laddr, const struct sa *peer,
+ struct tcp_sock *ts, int layer,
+ tcpconn_frame_h *frameh, void *arg);
+struct ice_tcpconn *trice_conn_find(struct list *connl, unsigned compid,
+ const struct sa *laddr,
+ const struct sa *peer);
+int trice_conn_debug(struct re_printf *pf, const struct ice_tcpconn *conn);
+
+
+bool trice_stun_process(struct trice *icem, struct ice_lcand *lcand,
+ int proto, void *sock, const struct sa *src,
+ struct mbuf *mb);
+int trice_reqbuf_append(struct trice *icem, struct ice_lcand *lcand,
+ void *sock, const struct sa *src,
+ struct stun_msg *req, size_t presz);