Commit 4a378b88 authored by Matthew Waters's avatar Matthew Waters 🐨 Committed by Olivier Crête

agent: implement support for RFC7675 - Consent Freshness

Specified in https://tools.ietf.org/html/rfc7675

RFC 7675 is a slight modification of the existing keepalive connection
checks that could be enabled manually or were used with the GOOGLE
compatibility mode.

Slight differences from the existing keepalive connection checks
include:
- an additional consent expiry timer instead of relying on all binding
  requests to succeed.
- 403: 'Forbidden' stun error-code which revokes consent with immediate
  effect.
parent 3492be60
Pipeline #243119 passed with stages
in 22 minutes and 22 seconds
......@@ -61,6 +61,8 @@ ICE
STUN
http://tools.ietf.org/html/rfc3489 (old)
http://tools.ietf.org/html/rfc5389
STUN Consent Freshness RFC
https://tools.ietf.org/html/rfc7675
TURN
http://tools.ietf.org/html/rfc5766
RTP
......
......@@ -106,6 +106,10 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a,
#define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs (impl. defined) */
#define NICE_AGENT_TIMER_TR_DEFAULT 25000 /* timer Tr, msecs (impl. defined) */
#define NICE_AGENT_TIMER_CONSENT_DEFAULT 5000 /* msec timer consent freshness connchecks (RFC 7675) */
#define NICE_AGENT_TIMER_CONSENT_TIMEOUT 10000 /* msec timer for consent checks to timeout and assume consent lost (RFC 7675) */
#define NICE_AGENT_TIMER_MIN_CONSENT_INTERVAL 4000 /* msec timer minimum for consent lost requests (RFC 7675) */
#define NICE_AGENT_TIMER_KEEPALIVE_TIMEOUT 50000 /* msec timer for keepalive (without consent checks) to timeout and assume conection lost */
#define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see RFC 8445 6.1.2.5 */
......@@ -184,6 +188,8 @@ struct _NiceAgent
guint conncheck_ongoing_idle_delay; /* ongoing delay before timer stop */
gboolean controlling_mode; /* controlling mode used by the
conncheck */
gboolean consent_freshness; /* rfc 7675 consent freshness with
connchecks */
/* XXX: add pointer to internal data struct for ABI-safe extensions */
};
......
......@@ -123,6 +123,7 @@ enum
PROP_ICE_TRICKLE,
PROP_SUPPORT_RENOMINATION,
PROP_IDLE_TIMEOUT,
PROP_CONSENT_FRESHNESS,
};
......@@ -773,6 +774,8 @@ nice_agent_class_init (NiceAgentClass *klass)
* This is always enabled if the compatibility mode is
* %NICE_COMPATIBILITY_GOOGLE.
*
* This is always enabled if the 'consent-freshness' property is %TRUE
*
* Since: 0.1.8
*/
g_object_class_install_property (gobject_class, PROP_KEEPALIVE_CONNCHECK,
......@@ -890,6 +893,28 @@ nice_agent_class_init (NiceAgentClass *klass)
FALSE,
G_PARAM_READWRITE));
/**
* NiceAgent:consent-freshness
*
* Whether to perform periodic consent freshness checks as specified in
* RFC 7675. When %TRUE, the agent will periodically send binding requests
* to the peer to maintain the consent to send with the peer. On receipt
* of any authenticated error response, a component will immediately move
* to the failed state.
*
* Setting this property to %TRUE implies that 'keepalive-conncheck' should
* be %TRUE as well.
*
* Since: 0.1.20
*/
g_object_class_install_property (gobject_class, PROP_CONSENT_FRESHNESS,
g_param_spec_boolean (
"consent-freshness",
"Consent Freshness",
"Whether to perform the consent freshness checks as specified in RFC 7675",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/* install signals */
/**
......@@ -1314,6 +1339,7 @@ nice_agent_new_full (GMainContext *ctx,
"full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE,
"ice-trickle", (flags & NICE_AGENT_OPTION_ICE_TRICKLE) ? TRUE : FALSE,
"support-renomination", (flags & NICE_AGENT_OPTION_SUPPORT_RENOMINATION) ? TRUE : FALSE,
"consent-freshness", (flags & NICE_AGENT_OPTION_CONSENT_FRESHNESS) ? TRUE : FALSE,
NULL);
return agent;
......@@ -1438,7 +1464,7 @@ nice_agent_get_property (
break;
case PROP_KEEPALIVE_CONNCHECK:
if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE || agent->consent_freshness)
g_value_set_boolean (value, TRUE);
else
g_value_set_boolean (value, agent->keepalive_conncheck);
......@@ -1464,6 +1490,10 @@ nice_agent_get_property (
g_value_set_boolean (value, agent->use_ice_trickle);
break;
case PROP_CONSENT_FRESHNESS:
g_value_set_boolean (value, agent->consent_freshness);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
......@@ -1502,9 +1532,14 @@ nice_agent_init_stun_agent (NiceAgent *agent, StunAgent *stun_agent)
STUN_AGENT_USAGE_USE_FINGERPRINT |
STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES);
} else {
StunAgentUsageFlags stun_usage = 0;
if (agent->consent_freshness)
stun_usage |= STUN_AGENT_USAGE_CONSENT_FRESHNESS;
stun_agent_init (stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
STUN_COMPATIBILITY_RFC5389,
STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
stun_usage | STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
STUN_AGENT_USAGE_USE_FINGERPRINT);
}
stun_agent_set_software (stun_agent, agent->software_attribute);
......@@ -1677,6 +1712,10 @@ nice_agent_set_property (
agent->use_ice_trickle = g_value_get_boolean (value);
break;
case PROP_CONSENT_FRESHNESS:
agent->consent_freshness = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
......@@ -5187,6 +5226,13 @@ nice_agent_send_messages_nonblocking_internal (
goto done;
}
if (component->selected_pair.local != NULL &&
!component->selected_pair.remote_consent.have) {
g_set_error (&child_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
"Consent to send has been revoked by the peer");
goto done;
}
/* FIXME: Cancellation isn’t yet supported, but it doesn’t matter because
* we only deal with non-blocking writes. */
if (component->selected_pair.local != NULL) {
......@@ -5965,6 +6011,8 @@ nice_agent_set_selected_pair (
NICE_COMPONENT_STATE_READY);
/* step: set the selected pair */
/* XXX: assume we have consent to send to this selected remote address */
pair.remote_consent.have = TRUE;
nice_component_update_selected_pair (agent, component, &pair);
agent_signal_new_selected_pair (agent, stream_id, component_id,
(NiceCandidate *) pair.local, (NiceCandidate *) pair.remote);
......@@ -7103,3 +7151,28 @@ nice_agent_get_sockets (NiceAgent *agent, guint stream_id, guint component_id)
return array;
}
NICEAPI_EXPORT gboolean
nice_agent_consent_lost (
NiceAgent *agent,
guint stream_id,
guint component_id)
{
gboolean result = FALSE;
NiceComponent *component;
agent_lock (agent);
if (!agent->consent_freshness) {
g_warning ("Agent %p: Attempt made to signal consent lost for "
"stream/component %u/%u but RFC7675/consent-freshness is not enabled "
"for this agent. Ignoring request", agent, stream_id, component_id);
} else if (agent_find_component (agent, stream_id, component_id, NULL, &component)) {
nice_debug ("Agent %p: local consent lost for stream/component %u/%u", agent,
component->stream_id, component->id);
component->have_local_consent = FALSE;
result = TRUE;
}
agent_unlock_and_emit (agent);
return result;
}
......@@ -407,6 +407,7 @@ typedef enum
* @NICE_AGENT_OPTION_ICE_TRICKLE: Enable ICE trickle mode
* @NICE_AGENT_OPTION_SUPPORT_RENOMINATION: Enable renomination triggered by NOMINATION STUN attribute
* proposed here: https://tools.ietf.org/html/draft-thatcher-ice-renomination-00
* @NICE_AGENT_OPTION_CONSENT_FRESHNESS: Enable RFC 7675 consent freshness support. (Since: 0.1.20)
*
* These are options that can be passed to nice_agent_new_full(). They set
* various properties on the agent. Not including them sets the property to
......@@ -420,6 +421,7 @@ typedef enum {
NICE_AGENT_OPTION_LITE_MODE = 1 << 2,
NICE_AGENT_OPTION_ICE_TRICKLE = 1 << 3,
NICE_AGENT_OPTION_SUPPORT_RENOMINATION = 1 << 4,
NICE_AGENT_OPTION_CONSENT_FRESHNESS = 1 << 5,
} NiceAgentOption;
/**
......@@ -919,6 +921,9 @@ nice_agent_get_remote_candidates (
* "ICE Restarts"), as well as when reacting (spec section 9.2.1.1.
* "Detecting ICE Restart") to a restart.
*
* If consent-freshness has been enabled on @agent, as specified in RFC7675
* then restarting streams will restore the local consent.
*
* Returns: %TRUE on success %FALSE on error
**/
gboolean
......@@ -938,6 +943,9 @@ nice_agent_restart (
* Unlike nice_agent_restart(), this applies to a single stream. It also
* does not generate a new tie breaker.
*
* If consent-freshness has been enabled on @agent, as specified in RFC7675
* then restart @stream_id will restore the local consent for that stream.
*
* Returns: %TRUE on success %FALSE on error
*
* Since: 0.1.6
......@@ -1659,6 +1667,34 @@ nice_agent_peer_candidate_gathering_done (
NiceAgent *agent,
guint stream_id);
/**
* nice_agent_consent_lost:
* @agent: The #NiceAgent Object
* @stream_id: The ID of the stream
* @component_id: The ID of the component
*
* Notifies the agent that consent to receive has been revoked. This will
* cause the component to fail with 403 'Forbidden' all incoming STUN binding
* requests as specified in RFC 7675.
*
* A stream with a component in the consent-lost state can be reused by
* performing an ice restart with nice_agent_restart() or
* nice_agent_restart_stream().
*
* Calling the function only has an effect when @agent has been created with
* @NICE_AGENT_OPTION_CONSENT_FRESHNESS.
*
* Returns: %FALSE if the stream or component could not be found or consent
* freshness is not enabled, %TRUE otherwise
*
* Since: 0.1.20
*/
gboolean
nice_agent_consent_lost (
NiceAgent *agent,
guint stream_id,
guint component_id);
/**
* nice_agent_close_async:
* @agent: The #NiceAgent object
......
......@@ -307,10 +307,10 @@ nice_component_clean_turn_servers (NiceAgent *agent, NiceComponent *cmp)
static void
nice_component_clear_selected_pair (NiceComponent *component)
{
if (component->selected_pair.keepalive.tick_source != NULL) {
g_source_destroy (component->selected_pair.keepalive.tick_source);
g_source_unref (component->selected_pair.keepalive.tick_source);
component->selected_pair.keepalive.tick_source = NULL;
if (component->selected_pair.remote_consent.tick_source != NULL) {
g_source_destroy (component->selected_pair.remote_consent.tick_source);
g_source_unref (component->selected_pair.remote_consent.tick_source);
component->selected_pair.remote_consent.tick_source = NULL;
}
memset (&component->selected_pair, 0, sizeof(CandidatePair));
......@@ -458,6 +458,8 @@ nice_component_restart (NiceComponent *cmp)
/* Reset the priority to 0 to make sure we get a new pair */
cmp->selected_pair.priority = 0;
cmp->have_local_consent = TRUE;
/* note: component state managed by agent */
}
......@@ -499,6 +501,7 @@ nice_component_update_selected_pair (NiceAgent *agent, NiceComponent *component,
component->selected_pair.remote = pair->remote;
component->selected_pair.priority = pair->priority;
component->selected_pair.stun_priority = pair->stun_priority;
component->selected_pair.remote_consent.have = pair->remote_consent.have;
nice_component_add_valid_candidate (agent, component,
(NiceCandidate *) pair->remote);
......@@ -580,6 +583,7 @@ nice_component_set_selected_remote_candidate (NiceComponent *component,
component->selected_pair.local = (NiceCandidateImpl *) local;
component->selected_pair.remote = (NiceCandidateImpl *) remote;
component->selected_pair.priority = priority;
component->selected_pair.remote_consent.have = TRUE;
/* Get into fallback mode where packets from any source is accepted once
* this has been called. This is the expected behavior of pre-ICE SIP.
......@@ -1107,6 +1111,8 @@ nice_component_init (NiceComponent *component)
g_queue_init (&component->queued_tcp_packets);
g_queue_init (&component->incoming_checks);
component->have_local_consent = TRUE;
}
static void
......
......@@ -65,17 +65,23 @@ G_BEGIN_DECLS
typedef struct _CandidatePair CandidatePair;
typedef struct _CandidatePairKeepalive CandidatePairKeepalive;
typedef struct _CandidatePairConsentCheck CandidatePairConsentCheck;
typedef struct _IncomingCheck IncomingCheck;
struct _CandidatePairKeepalive
{
guint64 next_tick; /* next tick timestamp */
GSource *tick_source;
guint stream_id;
guint component_id;
StunTimer timer;
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
StunMessage stun_message;
};
struct _CandidatePairConsentCheck
{
GSource *tick_source;
gboolean have;
guint64 last_received; /* g_get_monotonic_time() of last remote
consent received */
};
struct _CandidatePair
......@@ -85,6 +91,7 @@ struct _CandidatePair
guint64 priority; /* candidate pair priority */
guint32 stun_priority;
CandidatePairKeepalive keepalive;
CandidatePairConsentCheck remote_consent;
};
struct _IncomingCheck
......@@ -224,6 +231,8 @@ struct _NiceComponent {
* ACKs on. The messages are dequeued to the pseudo-TCP socket once a selected
* UDP socket is available. This is only used for reliable Components. */
GQueue queued_tcp_packets;
gboolean have_local_consent;
};
typedef struct {
......
......@@ -1234,66 +1234,43 @@ static gboolean priv_conn_check_tick_agent_locked (NiceAgent *agent,
return TRUE;
}
static gboolean priv_conn_keepalive_retransmissions_tick_agent_locked (
static gboolean priv_conn_remote_consent_tick_agent_locked (
NiceAgent *agent, gpointer pointer)
{
CandidatePair *pair = (CandidatePair *) pointer;
guint64 consent_timeout = 0;
guint64 now;
g_source_destroy (pair->keepalive.tick_source);
g_source_unref (pair->keepalive.tick_source);
pair->keepalive.tick_source = NULL;
switch (stun_timer_refresh (&pair->keepalive.timer)) {
case STUN_USAGE_TIMER_RETURN_TIMEOUT:
{
/* Time out */
StunTransactionId id;
NiceComponent *component;
if (!agent_find_component (agent,
pair->keepalive.stream_id, pair->keepalive.component_id,
NULL, &component)) {
nice_debug ("Could not find stream or component in"
" priv_conn_keepalive_retransmissions_tick");
return FALSE;
}
stun_message_id (&pair->keepalive.stun_message, id);
stun_agent_forget_transaction (&component->stun_agent, id);
pair->keepalive.stun_message.buffer = NULL;
if (agent->media_after_tick) {
nice_debug ("Agent %p : Keepalive conncheck timed out!! "
"but media was received. Suspecting keepalive lost because of "
"network bottleneck", agent);
} else {
nice_debug ("Agent %p : Keepalive conncheck timed out!! "
"peer probably lost connection", agent);
agent_signal_component_state_change (agent,
pair->keepalive.stream_id, pair->keepalive.component_id,
NICE_COMPONENT_STATE_FAILED);
}
break;
}
case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
/* Retransmit */
agent_socket_send (pair->local->sockptr, &pair->remote->c.addr,
stun_message_length (&pair->keepalive.stun_message),
(gchar *)pair->keepalive.stun_buffer);
if (pair->remote_consent.tick_source) {
g_source_destroy (pair->remote_consent.tick_source);
g_source_unref (pair->remote_consent.tick_source);
}
pair->remote_consent.tick_source = NULL;
nice_debug ("Agent %p : Retransmitting keepalive conncheck",
agent);
if (agent->consent_freshness) {
consent_timeout = NICE_AGENT_TIMER_CONSENT_TIMEOUT * 1000;
} else {
consent_timeout = NICE_AGENT_TIMER_KEEPALIVE_TIMEOUT* 1000;
}
G_GNUC_FALLTHROUGH;
case STUN_USAGE_TIMER_RETURN_SUCCESS:
agent_timeout_add_with_context (agent,
&pair->keepalive.tick_source,
"Pair keepalive", stun_timer_remainder (&pair->keepalive.timer),
priv_conn_keepalive_retransmissions_tick_agent_locked, pair);
break;
default:
g_assert_not_reached();
break;
now = g_get_monotonic_time();
if (now - pair->remote_consent.last_received > consent_timeout) {
guint64 time_since = now - pair->remote_consent.last_received;
pair->remote_consent.have = FALSE;
nice_debug ("Agent %p : pair %p consent for stream/component %u/%u timed "
"out! -> FAILED. Last consent received: %" G_GUINT64_FORMAT ".%" G_GUINT64_FORMAT "s ago",
agent, pair, pair->keepalive.stream_id, pair->keepalive.component_id,
time_since / G_USEC_PER_SEC, time_since % G_USEC_PER_SEC);
agent_signal_component_state_change (agent, pair->keepalive.stream_id,
pair->keepalive.component_id, NICE_COMPONENT_STATE_FAILED);
} else {
guint64 delay = (consent_timeout - now - pair->remote_consent.last_received) / 1000;
nice_debug ("Agent %p : pair %p rechecking consent in %" G_GUINT64_FORMAT ".%03" G_GUINT64_FORMAT "s",
agent, pair, delay / 1000, delay % 1000);
agent_timeout_add_with_context (agent,
&pair->remote_consent.tick_source,
"Pair remote consent", delay,
priv_conn_remote_consent_tick_agent_locked, pair);
}
return FALSE;
......@@ -1400,12 +1377,17 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
guint64 next_timer_tick;
now = g_get_monotonic_time ();
min_next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
if (agent->consent_freshness) {
min_next_tick = now + 1000 * NICE_AGENT_TIMER_MIN_CONSENT_INTERVAL;
} else {
min_next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
}
/* case 1: session established and media flowing
* (ref ICE sect 11 "Keepalives" RFC-8445)
* TODO: keepalives should be send only when no packet has been sent
* on that pair in the last Tr seconds, and not unconditionally.
* TODO: without RFC 7675 (consent freshness), keepalives should be sent
* only when no packet has been sent on that pair in the last Tr seconds,
* and not unconditionally.
*/
for (i = agent->streams; i; i = i->next) {
......@@ -1417,7 +1399,7 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
/* Disable keepalive checks on TCP candidates unless explicitly enabled */
if (p->local->c.transport != NICE_CANDIDATE_TRANSPORT_UDP &&
!agent->keepalive_conncheck)
!NICE_AGENT_DO_KEEPALIVE_CONNCHECKS (agent))
continue;
if (p->keepalive.next_tick) {
......@@ -1427,8 +1409,7 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
continue;
}
if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
agent->keepalive_conncheck) {
if (NICE_AGENT_DO_KEEPALIVE_CONNCHECKS (agent)) {
uint8_t uname[NICE_STREAM_MAX_UNAME];
size_t uname_len =
priv_create_username (agent, agent_find_stream (agent, stream->id),
......@@ -1438,29 +1419,24 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
size_t password_len = priv_get_password (agent,
agent_find_stream (agent, stream->id),
(NiceCandidate *) p->remote, &password);
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
StunMessage stun_message;
if (p->keepalive.stun_message.buffer != NULL) {
nice_debug ("Agent %p: Keepalive for s%u:c%u still"
" retransmitting, not restarting", agent, stream->id,
component->id);
continue;
}
if (nice_debug_is_enabled ()) {
gchar tmpbuf[INET6_ADDRSTRLEN];
nice_address_to_string (&p->remote->c.addr, tmpbuf);
nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
"(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
"password='%.*s' (%" G_GSIZE_FORMAT "), priority=%08x.",
agent, tmpbuf, nice_address_get_port (&p->remote->c.addr),
component->id, (int) uname_len, uname, uname_len,
(int) password_len, password, password_len,
p->stun_priority);
}
if (uname_len > 0) {
if (nice_debug_is_enabled ()) {
gchar tmpbuf[INET6_ADDRSTRLEN];
nice_address_to_string (&p->remote->c.addr, tmpbuf);
nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
"(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
"password='%.*s' (%" G_GSIZE_FORMAT "), priority=%08x.",
agent, tmpbuf, nice_address_get_port (&p->remote->c.addr),
component->id, (int) uname_len, uname, uname_len,
(int) password_len, password, password_len,
p->stun_priority);
}
buf_len = stun_usage_ice_conncheck_create (&component->stun_agent,
&p->keepalive.stun_message, p->keepalive.stun_buffer,
sizeof(p->keepalive.stun_buffer),
&stun_message, stun_buffer, sizeof(stun_buffer),
uname, uname_len, password, password_len,
agent->controlling_mode, agent->controlling_mode,
p->stun_priority,
......@@ -1469,27 +1445,31 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
agent_to_ice_compatibility (agent));
nice_debug ("Agent %p: conncheck created %zd - %p",
agent, buf_len, p->keepalive.stun_message.buffer);
agent, buf_len, stun_message.buffer);
if (buf_len > 0) {
stun_timer_start (&p->keepalive.timer,
agent->stun_initial_timeout,
agent->stun_max_retransmissions);
/* random range over 0.8 -> 1.2 as specified in RFC7675 */
double modifier = g_random_double() * 0.4 + 0.8;
guint64 delay = 1000 * MAX((guint64) ((NICE_AGENT_TIMER_CONSENT_DEFAULT) * modifier),
NICE_AGENT_TIMER_MIN_CONSENT_INTERVAL);
p->keepalive.stream_id = stream->id;
p->keepalive.component_id = component->id;
p->keepalive.next_tick = now + delay;
if (p->remote_consent.have) {
if (p->remote_consent.last_received == 0) {
p->remote_consent.last_received = g_get_monotonic_time();
}
priv_conn_remote_consent_tick_agent_locked (agent, p);
}
agent->media_after_tick = FALSE;
/* send the conncheck */
agent_socket_send (p->local->sockptr, &p->remote->c.addr,
buf_len, (gchar *)p->keepalive.stun_buffer);
p->keepalive.stream_id = stream->id;
p->keepalive.component_id = component->id;
p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
agent_timeout_add_with_context (agent,
&p->keepalive.tick_source, "Pair keepalive",
stun_timer_remainder (&p->keepalive.timer),
priv_conn_keepalive_retransmissions_tick_agent_locked, p);
buf_len, (gchar *) stun_buffer);
next_timer_tick = now + agent->timer_ta * 1000;
goto done;
......@@ -1498,18 +1478,20 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
}
}
} else {
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
StunMessage stun_message;
buf_len = stun_usage_bind_keepalive (&component->stun_agent,
&p->keepalive.stun_message, p->keepalive.stun_buffer,
sizeof(p->keepalive.stun_buffer));
&stun_message, stun_buffer, sizeof(stun_buffer));
if (buf_len > 0) {
agent_socket_send (p->local->sockptr, &p->remote->c.addr, buf_len,
(gchar *)p->keepalive.stun_buffer);
(gchar *) stun_buffer);
p->keepalive.next_tick = now + 1000 * NICE_AGENT_TIMER_TR_DEFAULT;
if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
ms_ice2_legacy_conncheck_send (&p->keepalive.stun_message,
ms_ice2_legacy_conncheck_send (&stun_message,
p->local->sockptr, &p->remote->c.addr);
}
......@@ -2082,6 +2064,7 @@ conn_check_update_selected_pair (NiceAgent *agent, NiceComponent *component,
cpair.remote = (NiceCandidateImpl *) pair->remote;
cpair.priority = pair->priority;
cpair.stun_priority = pair->stun_priority;
cpair.remote_consent.have = TRUE;
nice_component_update_selected_pair (agent, component, &cpair);
......@@ -4291,27 +4274,13 @@ static gboolean priv_map_reply_to_relay_remove (NiceAgent *agent,
static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
NiceComponent *component, StunMessage *resp)
{
StunTransactionId conncheck_id;
StunTransactionId response_id;
stun_message_id (resp, response_id);
if (component->selected_pair.keepalive.stun_message.buffer) {
stun_message_id (&component->selected_pair.keepalive.stun_message,
conncheck_id);
if (memcmp (conncheck_id, response_id, sizeof(StunTransactionId)) == 0) {
nice_debug ("Agent %p : Keepalive for selected pair received.",
agent);
if (component->selected_pair.keepalive.tick_source) {
g_source_destroy (component->selected_pair.keepalive.tick_source);
g_source_unref (component->selected_pair.keepalive.tick_source);
component->selected_pair.keepalive.tick_source = NULL;
}
component->selected_pair.keepalive.stun_message.buffer = NULL;
return TRUE;
}
nice_debug ("Agent %p : Keepalive for selected pair %p received.",
agent, &component->selected_pair);
if (agent->consent_freshness) {