Squashed 'third_party/rawrtc/rawrtc/' content from commit aa3ae4b24
Change-Id: I38a655a4259b62f591334e90a1315bd4e7e4d8ec
git-subtree-dir: third_party/rawrtc/rawrtc
git-subtree-split: aa3ae4b247275cc6e69c30613b3a4ba7fdc82d1b
diff --git a/src/ice_candidate/attributes.c b/src/ice_candidate/attributes.c
new file mode 100644
index 0000000..822c770
--- /dev/null
+++ b/src/ice_candidate/attributes.c
@@ -0,0 +1,296 @@
+#include "candidate.h"
+#include <rawrtc/ice_candidate.h>
+#include <rawrtcc/code.h>
+#include <rawrtcc/utils.h>
+#include <re.h>
+#include <rew.h>
+
+/*
+ * Get the ICE candidate's foundation.
+ * `*foundationp` will be set to a copy of the foundation that must be
+ * unreferenced.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_foundation(
+ char** const foundationp, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ // Check arguments
+ if (!candidate || !foundationp) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set copied foundation
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ return rawrtc_strdup(foundationp, candidate->candidate.raw_candidate->foundation);
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ return rawrtc_strdup(
+ foundationp, candidate->candidate.local_candidate->attr.foundation);
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ return rawrtc_strdup(
+ foundationp, candidate->candidate.remote_candidate->attr.foundation);
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+}
+
+/*
+ * Get the ICE candidate's priority.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_priority(
+ uint32_t* const priorityp, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ // Check arguments
+ if (!candidate || !priorityp) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set priority
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ *priorityp = candidate->candidate.raw_candidate->priority;
+ return RAWRTC_CODE_SUCCESS;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ *priorityp = candidate->candidate.local_candidate->attr.prio;
+ return RAWRTC_CODE_SUCCESS;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ *priorityp = candidate->candidate.remote_candidate->attr.prio;
+ return RAWRTC_CODE_SUCCESS;
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+}
+
+/*
+ * Get the ICE candidate's IP address.
+ * `*ipp` will be set to a copy of the IP address that must be
+ * unreferenced.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_ip(
+ char** const ipp, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ // Check arguments
+ if (!candidate || !ipp) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set copied IP address
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ return rawrtc_strdup(ipp, candidate->candidate.raw_candidate->ip);
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ return rawrtc_sdprintf(ipp, "%j", &candidate->candidate.local_candidate->attr.addr);
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ return rawrtc_sdprintf(ipp, "%j", &candidate->candidate.remote_candidate->attr.addr);
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+}
+
+/*
+ * Get the ICE candidate's protocol.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_protocol(
+ enum rawrtc_ice_protocol* const protocolp, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ int ipproto;
+
+ // Check arguments
+ if (!candidate || !protocolp) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set protocol
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ *protocolp = candidate->candidate.raw_candidate->protocol;
+ return RAWRTC_CODE_SUCCESS;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ ipproto = candidate->candidate.local_candidate->attr.proto;
+ break;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ ipproto = candidate->candidate.remote_candidate->attr.proto;
+ break;
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+ return rawrtc_ipproto_to_ice_protocol(protocolp, ipproto);
+}
+
+/*
+ * Get the ICE candidate's port.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_port(
+ uint16_t* const portp, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ // Check arguments
+ if (!candidate || !portp) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set port
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ *portp = candidate->candidate.raw_candidate->port;
+ return RAWRTC_CODE_SUCCESS;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ *portp = sa_port(&candidate->candidate.local_candidate->attr.addr);
+ return RAWRTC_CODE_SUCCESS;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ *portp = sa_port(&candidate->candidate.remote_candidate->attr.addr);
+ return RAWRTC_CODE_SUCCESS;
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+}
+
+/*
+ * Get the ICE candidate's type.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_type(
+ enum rawrtc_ice_candidate_type* typep, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ // Check arguments
+ if (!candidate || !typep) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set type
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ *typep = candidate->candidate.raw_candidate->type;
+ return RAWRTC_CODE_SUCCESS;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ return rawrtc_ice_cand_type_to_ice_candidate_type(
+ typep, candidate->candidate.local_candidate->attr.type);
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ return rawrtc_ice_cand_type_to_ice_candidate_type(
+ typep, candidate->candidate.remote_candidate->attr.type);
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+}
+
+/*
+ * Get the ICE candidate's TCP type.
+ * Return `RAWRTC_CODE_NO_VALUE` in case the protocol is not TCP.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_tcp_type(
+ enum rawrtc_ice_tcp_candidate_type* typep, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ struct ice_cand_attr* re_candidate;
+
+ // Check arguments
+ if (!candidate || !typep) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set type/get re candidate
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ *typep = candidate->candidate.raw_candidate->tcp_type;
+ return RAWRTC_CODE_SUCCESS;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ re_candidate = &candidate->candidate.local_candidate->attr;
+ break;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ re_candidate = &candidate->candidate.remote_candidate->attr;
+ break;
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+
+ // Set type from re candidate if TCP
+ if (re_candidate->proto == IPPROTO_TCP) {
+ return rawrtc_ice_tcptype_to_ice_tcp_candidate_type(typep, re_candidate->tcptype);
+ } else {
+ return RAWRTC_CODE_NO_VALUE;
+ }
+}
+
+/*
+ * Get the ICE candidate's related IP address.
+ * `*related_address` will be set to a copy of the related address that
+ * must be unreferenced.
+ *
+ * Return `RAWRTC_CODE_NO_VALUE` in case no related address exists.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_related_address(
+ char** const related_addressp, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ struct ice_cand_attr* re_candidate = NULL;
+
+ // Check arguments
+ if (!candidate || !related_addressp) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set copied related IP address/get re candidate
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ if (candidate->candidate.raw_candidate->related_address) {
+ return rawrtc_strdup(
+ related_addressp, candidate->candidate.raw_candidate->related_address);
+ }
+ break;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ re_candidate = &candidate->candidate.local_candidate->attr;
+ break;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ re_candidate = &candidate->candidate.remote_candidate->attr;
+ break;
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+
+ // Set copied related IP address from re candidate
+ if (re_candidate && sa_isset(&re_candidate->rel_addr, SA_ADDR)) {
+ return rawrtc_sdprintf(
+ related_addressp, "%j", &candidate->candidate.local_candidate->attr.rel_addr);
+ } else {
+ return RAWRTC_CODE_NO_VALUE;
+ }
+}
+
+/*
+ * Get the ICE candidate's related IP address' port.
+ * `*related_portp` will be set to a copy of the related address'
+ * port.
+ *
+ * Return `RAWRTC_CODE_NO_VALUE` in case no related port exists.
+ */
+enum rawrtc_code rawrtc_ice_candidate_get_related_port(
+ uint16_t* const related_portp, // de-referenced
+ struct rawrtc_ice_candidate* const candidate) {
+ struct ice_cand_attr* re_candidate = NULL;
+
+ // Check arguments
+ if (!candidate || !related_portp) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set port
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ if (candidate->candidate.raw_candidate->related_address) {
+ *related_portp = candidate->candidate.raw_candidate->related_port;
+ return RAWRTC_CODE_SUCCESS;
+ }
+ break;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ re_candidate = &candidate->candidate.local_candidate->attr;
+ break;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ re_candidate = &candidate->candidate.remote_candidate->attr;
+ break;
+ default:
+ return RAWRTC_CODE_INVALID_STATE;
+ }
+
+ // Set copied related IP address' port from re candidate
+ if (re_candidate && sa_isset(&re_candidate->rel_addr, SA_PORT)) {
+ *related_portp = sa_port(&candidate->candidate.local_candidate->attr.rel_addr);
+ return RAWRTC_CODE_SUCCESS;
+ } else {
+ return RAWRTC_CODE_NO_VALUE;
+ }
+}
diff --git a/src/ice_candidate/candidate.c b/src/ice_candidate/candidate.c
new file mode 100644
index 0000000..b5db1b0
--- /dev/null
+++ b/src/ice_candidate/candidate.c
@@ -0,0 +1,274 @@
+#include "candidate.h"
+#include <rawrtc/config.h>
+#include <rawrtc/ice_candidate.h>
+#include <rawrtcc/code.h>
+#include <rawrtcc/utils.h>
+#include <re.h>
+#include <rew.h>
+
+#define DEBUG_MODULE "ice-candidate"
+//#define RAWRTC_DEBUG_MODULE_LEVEL 7 // Note: Uncomment this to debug this module only
+#include <rawrtcc/debug.h>
+
+/*
+ * Calculate the ICE candidate priority.
+ *
+ * We prefer:
+ *
+ * 1. UDP over TCP, then
+ * 2. IPv6 over IPv4, then
+ * 3. Older candidates over newer candidates.
+ *
+ * TODO: We should follow ICE Dual-Stack Recommendations.
+ * See: https://tools.ietf.org/html/rfc8421#section-4
+ */
+uint32_t rawrtc_ice_candidate_calculate_priority(
+ uint32_t const n_candidates,
+ enum ice_cand_type const candidate_type,
+ int const protocol,
+ int const address_family,
+ enum ice_tcptype const tcp_type) {
+ uint16_t const age =
+ n_candidates > (1 << 13) ? (uint16_t) 0 : (uint16_t)((1 << 13) - n_candidates);
+ uint16_t const is_udp = protocol == IPPROTO_UDP ? (uint16_t) 1 : (uint16_t) 0;
+ uint16_t const is_ipv6 = address_family == AF_INET6 ? (uint16_t) 1 : (uint16_t) 0;
+ (void) tcp_type;
+ // TODO: Set correct component ID
+ return ice_cand_calc_prio(candidate_type, age | is_ipv6 << 14 | is_udp << 15, 1);
+}
+
+/*
+ * Destructor for an existing ICE candidate.
+ */
+static void rawrtc_ice_candidate_raw_destroy(void* arg) {
+ struct rawrtc_ice_candidate_raw* const candidate = arg;
+
+ // Un-reference
+ mem_deref(candidate->related_address);
+ mem_deref(candidate->ip);
+ mem_deref(candidate->foundation);
+}
+
+/*
+ * Create a raw ICE candidate (pending candidate).
+ */
+static enum rawrtc_code rawrtc_ice_candidate_raw_create(
+ struct rawrtc_ice_candidate_raw** const candidatep, // de-referenced
+ struct pl* const foundation, // copied
+ uint32_t const priority,
+ struct pl* const ip, // copied
+ enum rawrtc_ice_protocol const protocol,
+ uint16_t const port,
+ enum rawrtc_ice_candidate_type const type,
+ enum rawrtc_ice_tcp_candidate_type const tcp_type,
+ struct pl* const related_address, // copied, nullable
+ uint16_t const related_port) {
+ struct rawrtc_ice_candidate_raw* candidate;
+ enum rawrtc_code error;
+
+ // Allocate
+ candidate = mem_zalloc(sizeof(*candidate), rawrtc_ice_candidate_raw_destroy);
+ if (!candidate) {
+ return RAWRTC_CODE_NO_MEMORY;
+ }
+
+ // Set fields/copy
+ error = rawrtc_error_to_code(pl_strdup(&candidate->foundation, foundation));
+ if (error) {
+ goto out;
+ }
+ candidate->priority = priority;
+ error = rawrtc_error_to_code(pl_strdup(&candidate->ip, ip));
+ if (error) {
+ goto out;
+ }
+ candidate->protocol = protocol;
+ candidate->port = port;
+ candidate->type = type;
+ candidate->tcp_type = tcp_type;
+ if (pl_isset(related_address)) {
+ error = rawrtc_error_to_code(pl_strdup(&candidate->related_address, related_address));
+ if (error) {
+ goto out;
+ }
+ }
+ candidate->related_port = related_port;
+
+out:
+ if (error) {
+ mem_deref(candidate);
+ } else {
+ // Set pointer
+ *candidatep = candidate;
+ DEBUG_PRINTF("Created candidate (raw): %r\n", ip);
+ }
+ return error;
+}
+
+/*
+ * Destructor for an existing ICE candidate.
+ */
+static void rawrtc_ice_candidate_destroy(void* arg) {
+ struct rawrtc_ice_candidate* const candidate = arg;
+
+ // Un-reference
+ switch (candidate->storage_type) {
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RAW:
+ mem_deref(candidate->candidate.raw_candidate);
+ break;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_LCAND:
+ mem_deref(candidate->candidate.local_candidate);
+ break;
+ case RAWRTC_ICE_CANDIDATE_STORAGE_RCAND:
+ mem_deref(candidate->candidate.remote_candidate);
+ break;
+ }
+}
+
+/*
+ * Create an ICE candidate (pl variant).
+ */
+enum rawrtc_code rawrtc_ice_candidate_create_internal(
+ struct rawrtc_ice_candidate** const candidatep, // de-referenced
+ struct pl* const foundation, // copied
+ uint32_t const priority,
+ struct pl* const ip, // copied
+ enum rawrtc_ice_protocol const protocol,
+ uint16_t const port,
+ enum rawrtc_ice_candidate_type const type,
+ enum rawrtc_ice_tcp_candidate_type const tcp_type,
+ struct pl* const related_address, // copied, nullable
+ uint16_t const related_port) {
+ struct rawrtc_ice_candidate* candidate;
+ enum rawrtc_code error;
+
+ // Check arguments
+ if (!candidatep || !pl_isset(foundation) || !pl_isset(ip)) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Allocate
+ candidate = mem_zalloc(sizeof(*candidate), rawrtc_ice_candidate_destroy);
+ if (!candidate) {
+ return RAWRTC_CODE_NO_MEMORY;
+ }
+
+ // Set storage type
+ candidate->storage_type = RAWRTC_ICE_CANDIDATE_STORAGE_RAW;
+
+ // Create raw candidate
+ error = rawrtc_ice_candidate_raw_create(
+ &candidate->candidate.raw_candidate, foundation, priority, ip, protocol, port, type,
+ tcp_type, related_address, related_port);
+ if (error) {
+ goto out;
+ }
+
+out:
+ if (error) {
+ mem_deref(candidate);
+ } else {
+ // Set pointer
+ *candidatep = candidate;
+ }
+ return error;
+}
+
+/*
+ * Create an ICE candidate.
+ * `*candidatep` must be unreferenced.
+ */
+enum rawrtc_code rawrtc_ice_candidate_create(
+ struct rawrtc_ice_candidate** const candidatep, // de-referenced
+ char* const foundation, // copied
+ uint32_t const priority,
+ char* const ip, // copied
+ enum rawrtc_ice_protocol const protocol,
+ uint16_t const port,
+ enum rawrtc_ice_candidate_type const type,
+ enum rawrtc_ice_tcp_candidate_type const tcp_type,
+ char* const related_address, // copied, nullable
+ uint16_t const related_port) {
+ struct pl foundation_pl;
+ struct pl ip_pl;
+ struct pl related_address_pl = PL_INIT;
+
+ // Check arguments
+ if (!foundation || !ip) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Convert str to pl
+ pl_set_str(&foundation_pl, foundation);
+ pl_set_str(&ip_pl, ip);
+ if (related_address) {
+ pl_set_str(&related_address_pl, related_address);
+ }
+
+ // Create ICE candidate
+ return rawrtc_ice_candidate_create_internal(
+ candidatep, &foundation_pl, priority, &ip_pl, protocol, port, type, tcp_type,
+ &related_address_pl, related_port);
+}
+
+/*
+ * Create an ICE candidate instance from an existing local candidate.
+ */
+enum rawrtc_code rawrtc_ice_candidate_create_from_local_candidate(
+ struct rawrtc_ice_candidate** const candidatep, // de-referenced
+ struct ice_lcand* const local_candidate // referenced
+) {
+ struct rawrtc_ice_candidate* candidate;
+
+ // Check arguments
+ if (!candidatep || !local_candidate) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Allocate
+ candidate = mem_zalloc(sizeof(*candidate), rawrtc_ice_candidate_destroy);
+ if (!candidate) {
+ return RAWRTC_CODE_NO_MEMORY;
+ }
+
+ // Set storage type and reference local candidate
+ candidate->storage_type = RAWRTC_ICE_CANDIDATE_STORAGE_LCAND;
+ candidate->candidate.local_candidate = mem_ref(local_candidate);
+
+ // Set pointer
+ *candidatep = candidate;
+ DEBUG_PRINTF(
+ "Created candidate (lcand): %J\n", &candidate->candidate.local_candidate->attr.addr);
+ return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Create an ICE candidate instance from an existing remote candidate.
+ */
+enum rawrtc_code rawrtc_ice_candidate_create_from_remote_candidate(
+ struct rawrtc_ice_candidate** const candidatep, // de-referenced
+ struct ice_rcand* const remote_candidate // referenced
+) {
+ struct rawrtc_ice_candidate* candidate;
+
+ // Check arguments
+ if (!candidatep || !remote_candidate) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Allocate
+ candidate = mem_zalloc(sizeof(*candidate), rawrtc_ice_candidate_destroy);
+ if (!candidate) {
+ return RAWRTC_CODE_NO_MEMORY;
+ }
+
+ // Set storage type and reference remote candidate
+ candidate->storage_type = RAWRTC_ICE_CANDIDATE_STORAGE_RCAND;
+ candidate->candidate.remote_candidate = mem_ref(remote_candidate);
+
+ // Set pointer
+ *candidatep = candidate;
+ DEBUG_PRINTF(
+ "Created candidate (rcand): %j\n", &candidate->candidate.remote_candidate->attr.addr);
+ return RAWRTC_CODE_SUCCESS;
+}
diff --git a/src/ice_candidate/candidate.h b/src/ice_candidate/candidate.h
new file mode 100644
index 0000000..3dec216
--- /dev/null
+++ b/src/ice_candidate/candidate.h
@@ -0,0 +1,86 @@
+#pragma once
+#include "../ice_candidate/candidate.h"
+#include <rawrtc/ice_candidate.h>
+#include <rawrtcc/code.h>
+#include <re.h>
+#include <rew.h>
+
+/*
+ * ICE candidate storage type (internal).
+ */
+enum rawrtc_ice_candidate_storage {
+ RAWRTC_ICE_CANDIDATE_STORAGE_RAW,
+ RAWRTC_ICE_CANDIDATE_STORAGE_LCAND,
+ RAWRTC_ICE_CANDIDATE_STORAGE_RCAND,
+};
+
+/*
+ * Raw ICE candidate (pending candidate).
+ */
+struct rawrtc_ice_candidate_raw {
+ char* foundation; // copied
+ uint32_t priority;
+ char* ip; // copied
+ enum rawrtc_ice_protocol protocol;
+ uint16_t port;
+ enum rawrtc_ice_candidate_type type;
+ enum rawrtc_ice_tcp_candidate_type tcp_type;
+ char* related_address; // copied, nullable
+ uint16_t related_port;
+};
+
+struct rawrtc_ice_candidate {
+ enum rawrtc_ice_candidate_storage storage_type;
+ union {
+ struct rawrtc_ice_candidate_raw* raw_candidate;
+ struct ice_lcand* local_candidate;
+ struct ice_rcand* remote_candidate;
+ } candidate;
+};
+
+// Note: Cannot be public until it uses fixed size types in signature (stdint)
+uint32_t rawrtc_ice_candidate_calculate_priority(
+ uint32_t const n_candidates,
+ enum ice_cand_type const candidate_type,
+ int const protocol,
+ int const address_family,
+ enum ice_tcptype const tcp_type);
+
+enum rawrtc_code rawrtc_ice_candidate_create_internal(
+ struct rawrtc_ice_candidate** const candidatep, // de-referenced
+ struct pl* const foundation, // copied
+ uint32_t const priority,
+ struct pl* const ip, // copied
+ enum rawrtc_ice_protocol const protocol,
+ uint16_t const port,
+ enum rawrtc_ice_candidate_type const type,
+ enum rawrtc_ice_tcp_candidate_type const tcp_type,
+ struct pl* const related_address, // copied, nullable
+ uint16_t const related_port);
+
+enum rawrtc_code rawrtc_ice_candidate_create_from_local_candidate(
+ struct rawrtc_ice_candidate** const candidatep, // de-referenced
+ struct ice_lcand* const local_candidate // referenced
+);
+
+enum rawrtc_code rawrtc_ice_candidate_create_from_remote_candidate(
+ struct rawrtc_ice_candidate** const candidatep, // de-referenced
+ struct ice_rcand* const remote_candidate // referenced
+);
+
+int rawrtc_ice_candidate_debug(
+ struct re_printf* const pf, struct rawrtc_ice_candidate* const candidate);
+
+enum ice_cand_type rawrtc_ice_candidate_type_to_ice_cand_type(
+ enum rawrtc_ice_candidate_type const type);
+
+enum rawrtc_code rawrtc_ice_cand_type_to_ice_candidate_type(
+ enum rawrtc_ice_candidate_type* const typep, // de-referenced
+ const enum ice_cand_type re_type);
+
+enum ice_tcptype rawrtc_ice_tcp_candidate_type_to_ice_tcptype(
+ const enum rawrtc_ice_tcp_candidate_type type);
+
+enum rawrtc_code rawrtc_ice_tcptype_to_ice_tcp_candidate_type(
+ enum rawrtc_ice_tcp_candidate_type* const typep, // de-referenced
+ const enum ice_tcptype re_type);
diff --git a/src/ice_candidate/helper.c b/src/ice_candidate/helper.c
new file mode 100644
index 0000000..ff74c71
--- /dev/null
+++ b/src/ice_candidate/helper.c
@@ -0,0 +1,209 @@
+#include "helper.h"
+#include "../ice_gatherer/gatherer.h"
+#include "../ice_server/server.h"
+#include <rawrtc/main.h>
+#include <rawrtcc/code.h>
+#include <rawrtcc/utils.h>
+#include <re.h>
+#include <rew.h>
+
+/*
+ * Destructor for an existing candidate helper.
+ */
+static void rawrtc_candidate_helper_destroy(void* arg) {
+ struct rawrtc_candidate_helper* const local_candidate = arg;
+
+ // Un-reference
+ list_flush(&local_candidate->stun_sessions);
+ mem_deref(local_candidate->udp_helper);
+ mem_deref(local_candidate->candidate);
+ mem_deref(local_candidate->gatherer);
+}
+
+/*
+ * Create a candidate helper.
+ */
+enum rawrtc_code rawrtc_candidate_helper_create(
+ struct rawrtc_candidate_helper** const candidate_helperp, // de-referenced
+ struct rawrtc_ice_gatherer* gatherer,
+ struct ice_lcand* const candidate,
+ udp_helper_recv_h* const receive_handler,
+ void* const arg) {
+ struct rawrtc_candidate_helper* candidate_helper;
+ enum rawrtc_code error;
+
+ // Check arguments
+ if (!candidate_helperp || !gatherer || !candidate || !receive_handler) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Create candidate helper
+ candidate_helper = mem_zalloc(sizeof(*candidate_helper), rawrtc_candidate_helper_destroy);
+ if (!candidate_helper) {
+ return RAWRTC_CODE_NO_MEMORY;
+ }
+
+ // Set fields
+ candidate_helper->gatherer = mem_ref(gatherer);
+ candidate_helper->candidate = mem_ref(candidate);
+ candidate_helper->srflx_pending_count = 0;
+ candidate_helper->relay_pending_count = 0;
+
+ // Set receive handler
+ error = rawrtc_candidate_helper_set_receive_handler(candidate_helper, receive_handler, arg);
+ if (error) {
+ goto out;
+ }
+
+out:
+ if (error) {
+ mem_deref(candidate_helper);
+ } else {
+ // Set pointer
+ *candidate_helperp = candidate_helper;
+ }
+ return error;
+}
+
+/*
+ * Set a candidate helper's receive handler.
+ */
+enum rawrtc_code rawrtc_candidate_helper_set_receive_handler(
+ struct rawrtc_candidate_helper* const candidate_helper,
+ udp_helper_recv_h* const receive_handler,
+ void* const arg) {
+ enum rawrtc_code error;
+ struct udp_helper* udp_helper;
+ struct udp_sock* udp_socket;
+
+ // Check arguments
+ if (!candidate_helper || !receive_handler) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Get local candidate's UDP socket
+ udp_socket = trice_lcand_sock(candidate_helper->gatherer->ice, candidate_helper->candidate);
+ if (!udp_socket) {
+ return RAWRTC_CODE_NO_SOCKET;
+ }
+
+ // Create UDP helper
+ error = rawrtc_error_to_code(udp_register_helper(
+ &udp_helper, udp_socket, RAWRTC_LAYER_DTLS_SRTP_STUN, NULL, receive_handler, arg));
+ if (error) {
+ return error;
+ }
+
+ // Unset current helper (if any) and set new helper
+ mem_deref(candidate_helper->udp_helper);
+ candidate_helper->udp_helper = udp_helper;
+
+ // TODO: What about TCP helpers?
+
+ // Done
+ return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Find a specific candidate helper by re candidate.
+ */
+enum rawrtc_code rawrtc_candidate_helper_find(
+ struct rawrtc_candidate_helper** const candidate_helperp,
+ struct list* const candidate_helpers,
+ struct ice_lcand* re_candidate) {
+ struct le* le;
+
+ // Check arguments
+ if (!candidate_helperp || !candidate_helpers || !re_candidate) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Lookup candidate helper
+ for (le = list_head(candidate_helpers); le != NULL; le = le->next) {
+ struct rawrtc_candidate_helper* const candidate_helper = le->data;
+ if (candidate_helper->candidate->us == re_candidate->us) {
+ // Found
+ *candidate_helperp = candidate_helper;
+ return RAWRTC_CODE_SUCCESS;
+ }
+ }
+
+ // Not found
+ return RAWRTC_CODE_NO_VALUE;
+}
+
+static void rawrtc_candidate_helper_stun_session_destroy(void* arg) {
+ struct rawrtc_candidate_helper_stun_session* const session = arg;
+
+ // Remove from list
+ list_unlink(&session->le);
+
+ // Un-reference
+ mem_deref(session->url);
+ mem_deref(session->stun_keepalive);
+ mem_deref(session->candidate_helper);
+}
+
+/*
+ * Create a STUN session.
+ */
+enum rawrtc_code rawrtc_candidate_helper_stun_session_create(
+ struct rawrtc_candidate_helper_stun_session** const sessionp, // de-referenced
+ struct rawrtc_ice_server_url* const url) {
+ struct rawrtc_candidate_helper_stun_session* session;
+
+ // Check arguments
+ if (!sessionp || !url) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Allocate
+ session = mem_zalloc(sizeof(*session), rawrtc_candidate_helper_stun_session_destroy);
+ if (!session) {
+ return RAWRTC_CODE_NO_MEMORY;
+ }
+
+ // Set fields/reference
+ session->url = mem_ref(url);
+ session->pending = true;
+
+ // Set pointer & done
+ *sessionp = session;
+ return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Add a STUN session to a candidate helper.
+ */
+enum rawrtc_code rawrtc_candidate_helper_stun_session_add(
+ struct rawrtc_candidate_helper_stun_session* const session,
+ struct rawrtc_candidate_helper* const candidate_helper,
+ struct stun_keepalive* const stun_keepalive) {
+ // Check arguments
+ if (!session || !candidate_helper || !stun_keepalive) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Set fields/reference
+ session->candidate_helper = mem_ref(candidate_helper);
+ session->stun_keepalive = mem_ref(stun_keepalive);
+
+ // Append to STUN sessions
+ list_append(&candidate_helper->stun_sessions, &session->le, session);
+
+ // Done
+ return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Remove STUN sessions list handler (for candidate helper lists).
+ */
+bool rawrtc_candidate_helper_remove_stun_sessions_handler(struct le* le, void* arg) {
+ struct rawrtc_candidate_helper* const candidate_helper = le->data;
+ (void) arg;
+
+ // Flush STUN sessions
+ list_flush(&candidate_helper->stun_sessions);
+
+ return false; // continue traversing
+}
diff --git a/src/ice_candidate/helper.h b/src/ice_candidate/helper.h
new file mode 100644
index 0000000..9446574
--- /dev/null
+++ b/src/ice_candidate/helper.h
@@ -0,0 +1,58 @@
+#pragma once
+#include "../ice_server/server.h"
+#include <rawrtc/ice_gatherer.h>
+#include <rawrtcc/code.h>
+#include <re.h>
+#include <rew.h>
+
+/*
+ * Local candidate helper.
+ */
+struct rawrtc_candidate_helper {
+ struct le le;
+ struct rawrtc_ice_gatherer* gatherer;
+ struct ice_lcand* candidate;
+ struct udp_helper* udp_helper;
+ uint_fast8_t srflx_pending_count;
+ struct list stun_sessions;
+ uint_fast8_t relay_pending_count;
+};
+
+/*
+ * STUN keep-alive session.
+ */
+struct rawrtc_candidate_helper_stun_session {
+ struct le le;
+ struct rawrtc_candidate_helper* candidate_helper;
+ struct stun_keepalive* stun_keepalive;
+ struct rawrtc_ice_server_url* url;
+ bool pending;
+};
+
+enum rawrtc_code rawrtc_candidate_helper_create(
+ struct rawrtc_candidate_helper** const candidate_helperp, // de-referenced
+ struct rawrtc_ice_gatherer* gatherer,
+ struct ice_lcand* const candidate,
+ udp_helper_recv_h* const receive_handler,
+ void* const arg);
+
+enum rawrtc_code rawrtc_candidate_helper_set_receive_handler(
+ struct rawrtc_candidate_helper* const candidate_helper,
+ udp_helper_recv_h* const receive_handler,
+ void* const arg);
+
+enum rawrtc_code rawrtc_candidate_helper_find(
+ struct rawrtc_candidate_helper** const candidate_helperp,
+ struct list* const candidate_helpers,
+ struct ice_lcand* re_candidate);
+
+enum rawrtc_code rawrtc_candidate_helper_stun_session_create(
+ struct rawrtc_candidate_helper_stun_session** const sessionp, // de-referenced
+ struct rawrtc_ice_server_url* const url);
+
+enum rawrtc_code rawrtc_candidate_helper_stun_session_add(
+ struct rawrtc_candidate_helper_stun_session* const session,
+ struct rawrtc_candidate_helper* const candidate_helper,
+ struct stun_keepalive* const stun_keepalive);
+
+bool rawrtc_candidate_helper_remove_stun_sessions_handler(struct le* le, void* arg);
diff --git a/src/ice_candidate/meson.build b/src/ice_candidate/meson.build
new file mode 100644
index 0000000..abd4455
--- /dev/null
+++ b/src/ice_candidate/meson.build
@@ -0,0 +1,6 @@
+sources += files([
+ 'attributes.c',
+ 'candidate.c',
+ 'helper.c',
+ 'utils.c',
+])
diff --git a/src/ice_candidate/utils.c b/src/ice_candidate/utils.c
new file mode 100644
index 0000000..a6bee78
--- /dev/null
+++ b/src/ice_candidate/utils.c
@@ -0,0 +1,476 @@
+#include "candidate.h"
+#include <rawrtc/ice_candidate.h>
+#include <rawrtcc/code.h>
+#include <re.h>
+#include <netinet/in.h> // IPPROTO_UDP, IPPROTO_TCP
+
+/*
+ * Translate an ICE candidate type to the corresponding re type.
+ */
+enum ice_cand_type rawrtc_ice_candidate_type_to_ice_cand_type(
+ enum rawrtc_ice_candidate_type const type) {
+ // No conversion needed
+ return (enum ice_cand_type) type;
+}
+
+/*
+ * Translate a re ICE candidate type to the corresponding rawrtc type.
+ */
+enum rawrtc_code rawrtc_ice_cand_type_to_ice_candidate_type(
+ enum rawrtc_ice_candidate_type* const typep, // de-referenced
+ enum ice_cand_type const re_type) {
+ // Check arguments
+ if (!typep) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Convert ice_cand_type
+ switch (re_type) {
+ case ICE_CAND_TYPE_HOST:
+ *typep = RAWRTC_ICE_CANDIDATE_TYPE_HOST;
+ return RAWRTC_CODE_SUCCESS;
+ case ICE_CAND_TYPE_SRFLX:
+ *typep = RAWRTC_ICE_CANDIDATE_TYPE_SRFLX;
+ return RAWRTC_CODE_SUCCESS;
+ case ICE_CAND_TYPE_PRFLX:
+ *typep = RAWRTC_ICE_CANDIDATE_TYPE_PRFLX;
+ return RAWRTC_CODE_SUCCESS;
+ case ICE_CAND_TYPE_RELAY:
+ *typep = RAWRTC_ICE_CANDIDATE_TYPE_RELAY;
+ return RAWRTC_CODE_SUCCESS;
+ default:
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+}
+
+/*
+ * Translate an ICE TCP candidate type to the corresponding re type.
+ */
+enum ice_tcptype rawrtc_ice_tcp_candidate_type_to_ice_tcptype(
+ enum rawrtc_ice_tcp_candidate_type const type) {
+ // No conversion needed
+ return (enum ice_tcptype) type;
+}
+
+/*
+ * Translate a re ICE TCP candidate type to the corresponding rawrtc type.
+ */
+enum rawrtc_code rawrtc_ice_tcptype_to_ice_tcp_candidate_type(
+ enum rawrtc_ice_tcp_candidate_type* const typep, // de-referenced
+ enum ice_tcptype const re_type) {
+ // Check arguments
+ if (!typep) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Convert ice_cand_type
+ switch (re_type) {
+ case ICE_TCP_ACTIVE:
+ *typep = RAWRTC_ICE_TCP_CANDIDATE_TYPE_ACTIVE;
+ return RAWRTC_CODE_SUCCESS;
+ case ICE_TCP_PASSIVE:
+ *typep = RAWRTC_ICE_TCP_CANDIDATE_TYPE_PASSIVE;
+ return RAWRTC_CODE_SUCCESS;
+ case ICE_TCP_SO:
+ *typep = RAWRTC_ICE_TCP_CANDIDATE_TYPE_SO;
+ return RAWRTC_CODE_SUCCESS;
+ default:
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+}
+
+/*
+ * Translate a protocol to the corresponding IPPROTO_*.
+ */
+int rawrtc_ice_protocol_to_ipproto(enum rawrtc_ice_protocol const protocol) {
+ // No conversion needed
+ return (int) protocol;
+}
+
+/*
+ * Translate a IPPROTO_* to the corresponding protocol.
+ */
+enum rawrtc_code rawrtc_ipproto_to_ice_protocol(
+ enum rawrtc_ice_protocol* const protocolp, // de-referenced
+ int const ipproto) {
+ // Check arguments
+ if (!protocolp) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Convert IPPROTO_*
+ switch (ipproto) {
+ case IPPROTO_UDP:
+ *protocolp = RAWRTC_ICE_PROTOCOL_UDP;
+ return RAWRTC_CODE_SUCCESS;
+ case IPPROTO_TCP:
+ *protocolp = RAWRTC_ICE_PROTOCOL_TCP;
+ return RAWRTC_CODE_SUCCESS;
+ default:
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+}
+
+static enum rawrtc_ice_protocol const map_enum_ice_protocol[] = {
+ RAWRTC_ICE_PROTOCOL_UDP,
+ RAWRTC_ICE_PROTOCOL_TCP,
+};
+
+static char const* const map_str_ice_protocol[] = {
+ "udp",
+ "tcp",
+};
+
+static size_t const map_ice_protocol_length = ARRAY_SIZE(map_enum_ice_protocol);
+
+/*
+ * Translate an ICE protocol to str.
+ */
+char const* rawrtc_ice_protocol_to_str(enum rawrtc_ice_protocol const protocol) {
+ size_t i;
+
+ for (i = 0; i < map_ice_protocol_length; ++i) {
+ if (map_enum_ice_protocol[i] == protocol) {
+ return map_str_ice_protocol[i];
+ }
+ }
+
+ return "???";
+}
+
+/*
+ * Translate a pl to an ICE protocol (case-insensitive).
+ */
+enum rawrtc_code rawrtc_pl_to_ice_protocol(
+ enum rawrtc_ice_protocol* const protocolp, // de-referenced
+ struct pl const* const pl) {
+ size_t i;
+
+ // Check arguments
+ if (!protocolp || !pl_isset(pl)) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ for (i = 0; i < map_ice_protocol_length; ++i) {
+ if (pl_strcasecmp(pl, map_str_ice_protocol[i]) == 0) {
+ *protocolp = map_enum_ice_protocol[i];
+ return RAWRTC_CODE_SUCCESS;
+ }
+ }
+
+ return RAWRTC_CODE_NO_VALUE;
+}
+
+/*
+ * Translate a str to an ICE protocol (case-insensitive).
+ */
+enum rawrtc_code rawrtc_str_to_ice_protocol(
+ enum rawrtc_ice_protocol* const protocolp, // de-referenced
+ char const* const str) {
+ struct pl pl;
+
+ // Check arguments
+ if (!str) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Convert str to pl
+ pl_set_str(&pl, str);
+ return rawrtc_pl_to_ice_protocol(protocolp, &pl);
+}
+
+static enum rawrtc_ice_candidate_type const map_enum_ice_candidate_type[] = {
+ RAWRTC_ICE_CANDIDATE_TYPE_HOST,
+ RAWRTC_ICE_CANDIDATE_TYPE_SRFLX,
+ RAWRTC_ICE_CANDIDATE_TYPE_PRFLX,
+ RAWRTC_ICE_CANDIDATE_TYPE_RELAY,
+};
+
+static char const* const map_str_ice_candidate_type[] = {
+ "host",
+ "srflx",
+ "prflx",
+ "relay",
+};
+
+static size_t const map_ice_candidate_type_length = ARRAY_SIZE(map_enum_ice_candidate_type);
+
+/*
+ * Translate an ICE candidate type to str.
+ */
+char const* rawrtc_ice_candidate_type_to_str(enum rawrtc_ice_candidate_type const type) {
+ size_t i;
+
+ for (i = 0; i < map_ice_candidate_type_length; ++i) {
+ if (map_enum_ice_candidate_type[i] == type) {
+ return map_str_ice_candidate_type[i];
+ }
+ }
+
+ return "???";
+}
+
+/*
+ * Translate a pl to an ICE candidate type (case-insensitive).
+ */
+enum rawrtc_code rawrtc_pl_to_ice_candidate_type(
+ enum rawrtc_ice_candidate_type* const typep, // de-referenced
+ struct pl const* const pl) {
+ size_t i;
+
+ // Check arguments
+ if (!typep || !pl_isset(pl)) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ for (i = 0; i < map_ice_candidate_type_length; ++i) {
+ if (pl_strcasecmp(pl, map_str_ice_candidate_type[i]) == 0) {
+ *typep = map_enum_ice_candidate_type[i];
+ return RAWRTC_CODE_SUCCESS;
+ }
+ }
+
+ return RAWRTC_CODE_NO_VALUE;
+}
+
+/*
+ * Translate a str to an ICE candidate type (case-insensitive).
+ */
+enum rawrtc_code rawrtc_str_to_ice_candidate_type(
+ enum rawrtc_ice_candidate_type* const typep, // de-referenced
+ char const* const str) {
+ struct pl pl;
+
+ // Check arguments
+ if (!str) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Convert str to pl
+ pl_set_str(&pl, str);
+ return rawrtc_pl_to_ice_candidate_type(typep, &pl);
+}
+
+static enum rawrtc_ice_tcp_candidate_type const map_enum_ice_tcp_candidate_type[] = {
+ RAWRTC_ICE_TCP_CANDIDATE_TYPE_ACTIVE,
+ RAWRTC_ICE_TCP_CANDIDATE_TYPE_PASSIVE,
+ RAWRTC_ICE_TCP_CANDIDATE_TYPE_SO,
+};
+
+static char const* const map_str_ice_tcp_candidate_type[] = {
+ "active",
+ "passive",
+ "so",
+};
+
+static size_t const map_ice_tcp_candidate_type_length = ARRAY_SIZE(map_enum_ice_tcp_candidate_type);
+
+/*
+ * Translate an ICE TCP candidate type to str.
+ */
+char const* rawrtc_ice_tcp_candidate_type_to_str(enum rawrtc_ice_tcp_candidate_type const type) {
+ size_t i;
+
+ for (i = 0; i < map_ice_tcp_candidate_type_length; ++i) {
+ if (map_enum_ice_tcp_candidate_type[i] == type) {
+ return map_str_ice_tcp_candidate_type[i];
+ }
+ }
+
+ return "???";
+}
+
+/*
+ * Translate a str to an ICE TCP candidate type (case-insensitive).
+ */
+enum rawrtc_code rawrtc_pl_to_ice_tcp_candidate_type(
+ enum rawrtc_ice_tcp_candidate_type* const typep, // de-referenced
+ struct pl const* const pl) {
+ size_t i;
+
+ // Check arguments
+ if (!typep || !pl_isset(pl)) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ for (i = 0; i < map_ice_tcp_candidate_type_length; ++i) {
+ if (pl_strcasecmp(pl, map_str_ice_tcp_candidate_type[i]) == 0) {
+ *typep = map_enum_ice_tcp_candidate_type[i];
+ return RAWRTC_CODE_SUCCESS;
+ }
+ }
+
+ return RAWRTC_CODE_NO_VALUE;
+}
+
+/*
+ * Translate a str to an ICE TCP candidate type (case-insensitive).
+ */
+enum rawrtc_code rawrtc_str_to_ice_tcp_candidate_type(
+ enum rawrtc_ice_tcp_candidate_type* const typep, // de-referenced
+ char const* const str) {
+ struct pl pl;
+
+ // Check arguments
+ if (!str) {
+ return RAWRTC_CODE_INVALID_ARGUMENT;
+ }
+
+ // Convert str to pl
+ pl_set_str(&pl, str);
+ return rawrtc_pl_to_ice_tcp_candidate_type(typep, &pl);
+}
+
+static char const* const map_str_ice_candidate_storage[] = {
+ "raw",
+ "lcand",
+ "rcand",
+};
+
+static enum rawrtc_ice_candidate_storage const map_enum_ice_candidate_storage[] = {
+ RAWRTC_ICE_CANDIDATE_STORAGE_RAW,
+ RAWRTC_ICE_CANDIDATE_STORAGE_LCAND,
+ RAWRTC_ICE_CANDIDATE_STORAGE_RCAND,
+};
+
+static size_t const map_ice_candidate_storage_length = ARRAY_SIZE(map_enum_ice_candidate_storage);
+
+/*
+ * Translate an ICE candidate storage type to str.
+ */
+static char const* ice_candidate_storage_to_str(enum rawrtc_ice_candidate_storage const type) {
+ size_t i;
+
+ for (i = 0; i < map_ice_candidate_storage_length; ++i) {
+ if (map_enum_ice_candidate_storage[i] == type) {
+ return map_str_ice_candidate_storage[i];
+ }
+ }
+
+ return "???";
+}
+
+/*
+ * Print debug information for an ICE candidate.
+ */
+int rawrtc_ice_candidate_debug(
+ struct re_printf* const pf, struct rawrtc_ice_candidate* const candidate) {
+ int err = 0;
+ enum rawrtc_code error;
+ char* foundation = NULL;
+ uint32_t priority;
+ char* ip = NULL;
+ enum rawrtc_ice_protocol protocol;
+ uint16_t port;
+ enum rawrtc_ice_candidate_type type;
+ enum rawrtc_ice_tcp_candidate_type tcp_type;
+ char* related_address = NULL;
+ uint16_t related_port;
+
+ // Check arguments
+ if (!candidate) {
+ return 0;
+ }
+
+ err |= re_hprintf(pf, " ICE Candidate <%p>:\n", candidate);
+
+ // Storage type
+ err |= re_hprintf(
+ pf, " storage_type=%s\n", ice_candidate_storage_to_str(candidate->storage_type));
+
+ // Foundation
+ error = rawrtc_ice_candidate_get_foundation(&foundation, candidate);
+ if (error) {
+ goto out;
+ }
+ err |= re_hprintf(pf, " foundation=\"%s\"\n", foundation);
+
+ // Priority
+ error = rawrtc_ice_candidate_get_priority(&priority, candidate);
+ if (error) {
+ goto out;
+ }
+ err |= re_hprintf(pf, " priority=%" PRIu32 "\n", priority);
+
+ // IP
+ error = rawrtc_ice_candidate_get_ip(&ip, candidate);
+ if (error) {
+ goto out;
+ }
+ err |= re_hprintf(pf, " ip=%s\n", ip);
+
+ // Protocol
+ error = rawrtc_ice_candidate_get_protocol(&protocol, candidate);
+ if (error) {
+ goto out;
+ }
+ err |= re_hprintf(pf, " protocol=%s\n", rawrtc_ice_protocol_to_str(protocol));
+
+ // Port
+ error = rawrtc_ice_candidate_get_port(&port, candidate);
+ if (error) {
+ goto out;
+ }
+ err |= re_hprintf(pf, " port=%" PRIu16 "\n", port);
+
+ // Type
+ error = rawrtc_ice_candidate_get_type(&type, candidate);
+ if (error) {
+ goto out;
+ }
+ err |= re_hprintf(pf, " type=%s\n", rawrtc_ice_candidate_type_to_str(type));
+
+ // TCP type (if any)
+ err |= re_hprintf(pf, " tcp_type=");
+ error = rawrtc_ice_candidate_get_tcp_type(&tcp_type, candidate);
+ switch (error) {
+ case RAWRTC_CODE_SUCCESS:
+ err |= re_hprintf(pf, "%s\n", rawrtc_ice_tcp_candidate_type_to_str(tcp_type));
+ break;
+ case RAWRTC_CODE_NO_VALUE:
+ err |= re_hprintf(pf, "n/a\n");
+ break;
+ default:
+ goto out;
+ }
+
+ // Related address (if any)
+ err |= re_hprintf(pf, " related_address=");
+ error = rawrtc_ice_candidate_get_related_address(&related_address, candidate);
+ switch (error) {
+ case RAWRTC_CODE_SUCCESS:
+ err |= re_hprintf(pf, "%s\n", related_address);
+ break;
+ case RAWRTC_CODE_NO_VALUE:
+ err |= re_hprintf(pf, "n/a\n");
+ break;
+ default:
+ goto out;
+ }
+
+ // Related port (if any)
+ err |= re_hprintf(pf, " related_port=");
+ error = rawrtc_ice_candidate_get_related_port(&related_port, candidate);
+ switch (error) {
+ case RAWRTC_CODE_SUCCESS:
+ err |= re_hprintf(pf, "%" PRIu16 "\n", related_port);
+ break;
+ case RAWRTC_CODE_NO_VALUE:
+ err |= re_hprintf(pf, "n/a\n");
+ break;
+ default:
+ goto out;
+ }
+
+out:
+ // Un-reference
+ mem_deref(related_address);
+ mem_deref(ip);
+ mem_deref(foundation);
+
+ // Translate error & done
+ if (!err && error) {
+ err = EINVAL;
+ }
+ return err;
+}