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
...@@ -61,6 +61,8 @@ ICE ...@@ -61,6 +61,8 @@ ICE
STUN STUN
http://tools.ietf.org/html/rfc3489 (old) http://tools.ietf.org/html/rfc3489 (old)
http://tools.ietf.org/html/rfc5389 http://tools.ietf.org/html/rfc5389
STUN Consent Freshness RFC
https://tools.ietf.org/html/rfc7675
TURN TURN
http://tools.ietf.org/html/rfc5766 http://tools.ietf.org/html/rfc5766
RTP RTP
......
...@@ -106,6 +106,10 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a, ...@@ -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_TA_DEFAULT 20 /* timer Ta, msecs (impl. defined) */
#define NICE_AGENT_TIMER_TR_DEFAULT 25000 /* timer Tr, 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 */ #define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see RFC 8445 6.1.2.5 */
...@@ -184,6 +188,8 @@ struct _NiceAgent ...@@ -184,6 +188,8 @@ struct _NiceAgent
guint conncheck_ongoing_idle_delay; /* ongoing delay before timer stop */ guint conncheck_ongoing_idle_delay; /* ongoing delay before timer stop */
gboolean controlling_mode; /* controlling mode used by the gboolean controlling_mode; /* controlling mode used by the
conncheck */ conncheck */
gboolean consent_freshness; /* rfc 7675 consent freshness with
connchecks */
/* XXX: add pointer to internal data struct for ABI-safe extensions */ /* XXX: add pointer to internal data struct for ABI-safe extensions */
}; };
......
...@@ -123,6 +123,7 @@ enum ...@@ -123,6 +123,7 @@ enum
PROP_ICE_TRICKLE, PROP_ICE_TRICKLE,
PROP_SUPPORT_RENOMINATION, PROP_SUPPORT_RENOMINATION,
PROP_IDLE_TIMEOUT, PROP_IDLE_TIMEOUT,
PROP_CONSENT_FRESHNESS,
}; };
...@@ -773,6 +774,8 @@ nice_agent_class_init (NiceAgentClass *klass) ...@@ -773,6 +774,8 @@ nice_agent_class_init (NiceAgentClass *klass)
* This is always enabled if the compatibility mode is * This is always enabled if the compatibility mode is
* %NICE_COMPATIBILITY_GOOGLE. * %NICE_COMPATIBILITY_GOOGLE.
* *
* This is always enabled if the 'consent-freshness' property is %TRUE
*
* Since: 0.1.8 * Since: 0.1.8
*/ */
g_object_class_install_property (gobject_class, PROP_KEEPALIVE_CONNCHECK, g_object_class_install_property (gobject_class, PROP_KEEPALIVE_CONNCHECK,
...@@ -890,6 +893,28 @@ nice_agent_class_init (NiceAgentClass *klass) ...@@ -890,6 +893,28 @@ nice_agent_class_init (NiceAgentClass *klass)
FALSE, FALSE,
G_PARAM_READWRITE)); 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 */ /* install signals */
/** /**
...@@ -1314,6 +1339,7 @@ nice_agent_new_full (GMainContext *ctx, ...@@ -1314,6 +1339,7 @@ nice_agent_new_full (GMainContext *ctx,
"full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE, "full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE,
"ice-trickle", (flags & NICE_AGENT_OPTION_ICE_TRICKLE) ? TRUE : FALSE, "ice-trickle", (flags & NICE_AGENT_OPTION_ICE_TRICKLE) ? TRUE : FALSE,
"support-renomination", (flags & NICE_AGENT_OPTION_SUPPORT_RENOMINATION) ? TRUE : FALSE, "support-renomination", (flags & NICE_AGENT_OPTION_SUPPORT_RENOMINATION) ? TRUE : FALSE,
"consent-freshness", (flags & NICE_AGENT_OPTION_CONSENT_FRESHNESS) ? TRUE : FALSE,
NULL); NULL);
return agent; return agent;
...@@ -1438,7 +1464,7 @@ nice_agent_get_property ( ...@@ -1438,7 +1464,7 @@ nice_agent_get_property (
break; break;
case PROP_KEEPALIVE_CONNCHECK: 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); g_value_set_boolean (value, TRUE);
else else
g_value_set_boolean (value, agent->keepalive_conncheck); g_value_set_boolean (value, agent->keepalive_conncheck);
...@@ -1464,6 +1490,10 @@ nice_agent_get_property ( ...@@ -1464,6 +1490,10 @@ nice_agent_get_property (
g_value_set_boolean (value, agent->use_ice_trickle); g_value_set_boolean (value, agent->use_ice_trickle);
break; break;
case PROP_CONSENT_FRESHNESS:
g_value_set_boolean (value, agent->consent_freshness);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
} }
...@@ -1502,9 +1532,14 @@ nice_agent_init_stun_agent (NiceAgent *agent, StunAgent *stun_agent) ...@@ -1502,9 +1532,14 @@ nice_agent_init_stun_agent (NiceAgent *agent, StunAgent *stun_agent)
STUN_AGENT_USAGE_USE_FINGERPRINT | STUN_AGENT_USAGE_USE_FINGERPRINT |
STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES); STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES);
} else { } 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_agent_init (stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
STUN_COMPATIBILITY_RFC5389, STUN_COMPATIBILITY_RFC5389,
STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS | stun_usage | STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS |
STUN_AGENT_USAGE_USE_FINGERPRINT); STUN_AGENT_USAGE_USE_FINGERPRINT);
} }
stun_agent_set_software (stun_agent, agent->software_attribute); stun_agent_set_software (stun_agent, agent->software_attribute);
...@@ -1677,6 +1712,10 @@ nice_agent_set_property ( ...@@ -1677,6 +1712,10 @@ nice_agent_set_property (
agent->use_ice_trickle = g_value_get_boolean (value); agent->use_ice_trickle = g_value_get_boolean (value);
break; break;
case PROP_CONSENT_FRESHNESS:
agent->consent_freshness = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
} }
...@@ -5187,6 +5226,13 @@ nice_agent_send_messages_nonblocking_internal ( ...@@ -5187,6 +5226,13 @@ nice_agent_send_messages_nonblocking_internal (
goto done; 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 /* FIXME: Cancellation isn’t yet supported, but it doesn’t matter because
* we only deal with non-blocking writes. */ * we only deal with non-blocking writes. */
if (component->selected_pair.local != NULL) { if (component->selected_pair.local != NULL) {
...@@ -5965,6 +6011,8 @@ nice_agent_set_selected_pair ( ...@@ -5965,6 +6011,8 @@ nice_agent_set_selected_pair (
NICE_COMPONENT_STATE_READY); NICE_COMPONENT_STATE_READY);
/* step: set the selected pair */ /* 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); nice_component_update_selected_pair (agent, component, &pair);
agent_signal_new_selected_pair (agent, stream_id, component_id, agent_signal_new_selected_pair (agent, stream_id, component_id,
(NiceCandidate *) pair.local, (NiceCandidate *) pair.remote); (NiceCandidate *) pair.local, (NiceCandidate *) pair.remote);
...@@ -7103,3 +7151,28 @@ nice_agent_get_sockets (NiceAgent *agent, guint stream_id, guint component_id) ...@@ -7103,3 +7151,28 @@ nice_agent_get_sockets (NiceAgent *agent, guint stream_id, guint component_id)
return array; 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 ...@@ -407,6 +407,7 @@ typedef enum
* @NICE_AGENT_OPTION_ICE_TRICKLE: Enable ICE trickle mode * @NICE_AGENT_OPTION_ICE_TRICKLE: Enable ICE trickle mode
* @NICE_AGENT_OPTION_SUPPORT_RENOMINATION: Enable renomination triggered by NOMINATION STUN attribute * @NICE_AGENT_OPTION_SUPPORT_RENOMINATION: Enable renomination triggered by NOMINATION STUN attribute
* proposed here: https://tools.ietf.org/html/draft-thatcher-ice-renomination-00 * 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 * 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 * various properties on the agent. Not including them sets the property to
...@@ -420,6 +421,7 @@ typedef enum { ...@@ -420,6 +421,7 @@ typedef enum {
NICE_AGENT_OPTION_LITE_MODE = 1 << 2, NICE_AGENT_OPTION_LITE_MODE = 1 << 2,
NICE_AGENT_OPTION_ICE_TRICKLE = 1 << 3, NICE_AGENT_OPTION_ICE_TRICKLE = 1 << 3,
NICE_AGENT_OPTION_SUPPORT_RENOMINATION = 1 << 4, NICE_AGENT_OPTION_SUPPORT_RENOMINATION = 1 << 4,
NICE_AGENT_OPTION_CONSENT_FRESHNESS = 1 << 5,
} NiceAgentOption; } NiceAgentOption;
/** /**
...@@ -919,6 +921,9 @@ nice_agent_get_remote_candidates ( ...@@ -919,6 +921,9 @@ nice_agent_get_remote_candidates (
* "ICE Restarts"), as well as when reacting (spec section 9.2.1.1. * "ICE Restarts"), as well as when reacting (spec section 9.2.1.1.
* "Detecting ICE Restart") to a restart. * "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 * Returns: %TRUE on success %FALSE on error
**/ **/
gboolean gboolean
...@@ -938,6 +943,9 @@ nice_agent_restart ( ...@@ -938,6 +943,9 @@ nice_agent_restart (
* Unlike nice_agent_restart(), this applies to a single stream. It also * Unlike nice_agent_restart(), this applies to a single stream. It also
* does not generate a new tie breaker. * 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 * Returns: %TRUE on success %FALSE on error
* *
* Since: 0.1.6 * Since: 0.1.6
...@@ -1659,6 +1667,34 @@ nice_agent_peer_candidate_gathering_done ( ...@@ -1659,6 +1667,34 @@ nice_agent_peer_candidate_gathering_done (
NiceAgent *agent, NiceAgent *agent,
guint stream_id); 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: * nice_agent_close_async:
* @agent: The #NiceAgent object * @agent: The #NiceAgent object
......
...@@ -307,10 +307,10 @@ nice_component_clean_turn_servers (NiceAgent *agent, NiceComponent *cmp) ...@@ -307,10 +307,10 @@ nice_component_clean_turn_servers (NiceAgent *agent, NiceComponent *cmp)
static void static void
nice_component_clear_selected_pair (NiceComponent *component) nice_component_clear_selected_pair (NiceComponent *component)
{ {
if (component->selected_pair.keepalive.tick_source != NULL) { if (component->selected_pair.remote_consent.tick_source != NULL) {
g_source_destroy (component->selected_pair.keepalive.tick_source); g_source_destroy (component->selected_pair.remote_consent.tick_source);
g_source_unref (component->selected_pair.keepalive.tick_source); g_source_unref (component->selected_pair.remote_consent.tick_source);
component->selected_pair.keepalive.tick_source = NULL; component->selected_pair.remote_consent.tick_source = NULL;
} }
memset (&component->selected_pair, 0, sizeof(CandidatePair)); memset (&component->selected_pair, 0, sizeof(CandidatePair));
...@@ -458,6 +458,8 @@ nice_component_restart (NiceComponent *cmp) ...@@ -458,6 +458,8 @@ nice_component_restart (NiceComponent *cmp)
/* Reset the priority to 0 to make sure we get a new pair */ /* Reset the priority to 0 to make sure we get a new pair */
cmp->selected_pair.priority = 0; cmp->selected_pair.priority = 0;
cmp->have_local_consent = TRUE;
/* note: component state managed by agent */ /* note: component state managed by agent */
} }
...@@ -499,6 +501,7 @@ nice_component_update_selected_pair (NiceAgent *agent, NiceComponent *component, ...@@ -499,6 +501,7 @@ nice_component_update_selected_pair (NiceAgent *agent, NiceComponent *component,
component->selected_pair.remote = pair->remote; component->selected_pair.remote = pair->remote;
component->selected_pair.priority = pair->priority; component->selected_pair.priority = pair->priority;
component->selected_pair.stun_priority = pair->stun_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, nice_component_add_valid_candidate (agent, component,
(NiceCandidate *) pair->remote); (NiceCandidate *) pair->remote);
...@@ -580,6 +583,7 @@ nice_component_set_selected_remote_candidate (NiceComponent *component, ...@@ -580,6 +583,7 @@ nice_component_set_selected_remote_candidate (NiceComponent *component,
component->selected_pair.local = (NiceCandidateImpl *) local; component->selected_pair.local = (NiceCandidateImpl *) local;
component->selected_pair.remote = (NiceCandidateImpl *) remote; component->selected_pair.remote = (NiceCandidateImpl *) remote;
component->selected_pair.priority = priority; component->selected_pair.priority = priority;
component->selected_pair.remote_consent.have = TRUE;
/* Get into fallback mode where packets from any source is accepted once /* 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. * this has been called. This is the expected behavior of pre-ICE SIP.
...@@ -1107,6 +1111,8 @@ nice_component_init (NiceComponent *component) ...@@ -1107,6 +1111,8 @@ nice_component_init (NiceComponent *component)
g_queue_init (&component->queued_tcp_packets); g_queue_init (&component->queued_tcp_packets);
g_queue_init (&component->incoming_checks); g_queue_init (&component->incoming_checks);
component->have_local_consent = TRUE;
} }
static void static void
......
...@@ -65,17 +65,23 @@ G_BEGIN_DECLS ...@@ -65,17 +65,23 @@ G_BEGIN_DECLS
typedef struct _CandidatePair CandidatePair; typedef struct _CandidatePair CandidatePair;
typedef struct _CandidatePairKeepalive CandidatePairKeepalive; typedef struct _CandidatePairKeepalive CandidatePairKeepalive;
typedef struct _CandidatePairConsentCheck CandidatePairConsentCheck;
typedef struct _IncomingCheck IncomingCheck; typedef struct _IncomingCheck IncomingCheck;
struct _CandidatePairKeepalive struct _CandidatePairKeepalive
{ {
guint64 next_tick; /* next tick timestamp */ guint64 next_tick; /* next tick timestamp */
GSource *tick_source;
guint stream_id; guint stream_id;
guint component_id; guint component_id;
StunTimer timer; 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 struct _CandidatePair
...@@ -85,6 +91,7 @@ struct _CandidatePair ...@@ -85,6 +91,7 @@ struct _CandidatePair
guint64 priority; /* candidate pair priority */ guint64 priority; /* candidate pair priority */
guint32 stun_priority; guint32 stun_priority;
CandidatePairKeepalive keepalive; CandidatePairKeepalive keepalive;
CandidatePairConsentCheck remote_consent;
}; };
struct _IncomingCheck struct _IncomingCheck
...@@ -224,6 +231,8 @@ struct _NiceComponent { ...@@ -224,6 +231,8 @@ struct _NiceComponent {
* ACKs on. The messages are dequeued to the pseudo-TCP socket once a selected * 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. */ * UDP socket is available. This is only used for reliable Components. */
GQueue queued_tcp_packets; GQueue queued_tcp_packets;
gboolean have_local_consent;
}; };
typedef struct { typedef struct {
......
This diff is collapsed.
...@@ -49,6 +49,11 @@ ...@@ -49,6 +49,11 @@
#define NICE_CANDIDATE_PAIR_MAX_FOUNDATION NICE_CANDIDATE_MAX_FOUNDATION*2 #define NICE_CANDIDATE_PAIR_MAX_FOUNDATION NICE_CANDIDATE_MAX_FOUNDATION*2
/* A helper macro to test whether connection checks should continue to be
* performed after a component has successfully connected */
#define NICE_AGENT_DO_KEEPALIVE_CONNCHECKS(obj) \
((obj)->consent_freshness || (obj)->keepalive_conncheck || (obj)->compatibility == NICE_COMPATIBILITY_GOOGLE)
/** /**
* NiceCheckState: * NiceCheckState:
* @NICE_CHECK_WAITING: Waiting to be scheduled. * @NICE_CHECK_WAITING: Waiting to be scheduled.
......
...@@ -121,6 +121,10 @@ ...@@ -121,6 +121,10 @@
<title>Index of new symbols in 0.1.18</title> <title>Index of new symbols in 0.1.18</title>
<xi:include href="xml/api-index-0.1.18.xml"><xi:fallback/></xi:include> <xi:include href="xml/api-index-0.1.18.xml"><xi:fallback/></xi:include>
</index> </index>
<index role="0.1.20">
<title>Index of new symbols in 0.1.20</title>
<xi:include href="xml/api-index-0.1.20.xml"><xi:fallback/></xi:include>
</index>
<xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
</part> </part>
</book> </book>
...@@ -57,6 +57,7 @@ nice_agent_get_selected_socket ...@@ -57,6 +57,7 @@ nice_agent_get_selected_socket
nice_agent_get_sockets nice_agent_get_sockets
nice_agent_get_component_state nice_agent_get_component_state
nice_agent_close_async nice_agent_close_async
nice_agent_consent_lost
nice_component_state_to_string nice_component_state_to_string
<SUBSECTION Standard> <SUBSECTION Standard>
NICE_AGENT NICE_AGENT
......
...@@ -18,6 +18,7 @@ nice_address_to_string ...@@ -18,6 +18,7 @@ nice_address_to_string
nice_agent_add_local_address nice_agent_add_local_address
nice_agent_add_stream nice_agent_add_stream
nice_agent_close_async nice_agent_close_async
nice_agent_consent_lost
nice_agent_recv nice_agent_recv
nice_agent_recv_messages nice_agent_recv_messages
nice_agent_recv_nonblocking nice_agent_recv_nonblocking
......
...@@ -337,6 +337,13 @@ StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg, ...@@ -337,6 +337,13 @@ StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg,
} }
} }
if (agent->usage_flags & STUN_AGENT_USAGE_CONSENT_FRESHNESS &&
stun_message_get_class (msg) == STUN_ERROR) {
stun_message_find_error (msg, &error_code);
if (error_code == STUN_ERROR_FORBIDDEN) {
return STUN_VALIDATION_FORBIDDEN;
}
}
if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) { if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) {
agent->sent_ids[sent_id_idx].valid = FALSE; agent->sent_ids[sent_id_idx].valid = FALSE;
......
...@@ -123,6 +123,9 @@ typedef enum { ...@@ -123,6 +123,9 @@ typedef enum {
* @STUN_VALIDATION_UNKNOWN_ATTRIBUTE: The message is valid but contains one * @STUN_VALIDATION_UNKNOWN_ATTRIBUTE: The message is valid but contains one
* or more unknown comprehension attributes. This is a response, or error, * or more unknown comprehension attributes. This is a response, or error,
* or indication message and no error response should be sent * or indication message and no error response should be sent
* @STUN_VALIDATION_FORBIDDEN: The message response is valid and indicates
* the peer responded with the error code 403 'Forbidden'. No response
* should be sent.
* *
* This enum is used as the return value of stun_agent_validate() and represents * This enum is used as the return value of stun_agent_validate() and represents
* the status result of the validation of a STUN message. * the status result of the validation of a STUN message.
...@@ -137,6 +140,7 @@ typedef enum { ...@@ -137,6 +140,7 @@ typedef enum {
STUN_VALIDATION_UNMATCHED_RESPONSE, STUN_VALIDATION_UNMATCHED_RESPONSE,
STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE, STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE,
STUN_VALIDATION_UNKNOWN_ATTRIBUTE, STUN_VALIDATION_UNKNOWN_ATTRIBUTE,
STUN_VALIDATION_FORBIDDEN,
} StunValidationStatus; } StunValidationStatus;
/** /**
...@@ -168,6 +172,9 @@ typedef enum { ...@@ -168,6 +172,9 @@ typedef enum {
* @STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES: The agent should not assume STUN * @STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES: The agent should not assume STUN
* attributes are aligned on 32-bit boundaries when parsing messages and also * attributes are aligned on 32-bit boundaries when parsing messages and also
* do not add padding when creating messages. * do not add padding when creating messages.
* @STUN_AGENT_USAGE_CONSENT_FRESHNESS: The agent should expect and use
* the %STUN_VALIDATION_FORBIDDEN return value from ERROR-CODE responses and
* abort all transactions accordingly.
* *
* This enum defines a bitflag usages for a #StunAgent and they will define how * This enum defines a bitflag usages for a #StunAgent and they will define how
* the agent should behave, independently of the compatibility mode it uses. * the agent should behave, independently of the compatibility mode it uses.
...@@ -183,6 +190,7 @@ typedef enum { ...@@ -183,6 +190,7 @@ typedef enum {
STUN_AGENT_USAGE_NO_INDICATION_AUTH = (1 << 5), STUN_AGENT_USAGE_NO_INDICATION_AUTH = (1 << 5),
STUN_AGENT_USAGE_FORCE_VALIDATER = (1 << 6), STUN_AGENT_USAGE_FORCE_VALIDATER = (1 << 6),
STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES = (1 << 7), STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES = (1 << 7),
STUN_AGENT_USAGE_CONSENT_FRESHNESS = (1 << 8),
} StunAgentUsageFlags; } StunAgentUsageFlags;
......
...@@ -414,6 +414,8 @@ typedef uint8_t StunTransactionId[STUN_MESSAGE_TRANS_ID_LEN]; ...@@ -414,6 +414,8 @@ typedef uint8_t StunTransactionId[STUN_MESSAGE_TRANS_ID_LEN];
* "Bad Request" error as defined in RFC5389 * "Bad Request" error as defined in RFC5389
* @STUN_ERROR_UNAUTHORIZED: The ERROR-CODE value for the * @STUN_ERROR_UNAUTHORIZED: The ERROR-CODE value for the
* "Unauthorized" error as defined in RFC5389 * "Unauthorized" error as defined in RFC5389
* @STUN_ERROR_FORBIDDEN: The ERROR-CODE value for the
* "Forbidden" error as defined in RFC7675
* @STUN_ERROR_UNKNOWN_ATTRIBUTE: The ERROR-CODE value for the * @STUN_ERROR_UNKNOWN_ATTRIBUTE: The ERROR-CODE value for the
* "Unknown Attribute" error as defined in RFC5389 * "Unknown Attribute" error as defined in RFC5389
* @STUN_ERROR_ALLOCATION_MISMATCH:The ERROR-CODE value for the * @STUN_ERROR_ALLOCATION_MISMATCH:The ERROR-CODE value for the
...@@ -457,6 +459,7 @@ typedef enum ...@@ -457,6 +459,7 @@ typedef enum
STUN_ERROR_TRY_ALTERNATE=300, /* RFC5389 */ STUN_ERROR_TRY_ALTERNATE=300, /* RFC5389 */
STUN_ERROR_BAD_REQUEST=400, /* RFC5389 */ STUN_ERROR_BAD_REQUEST=400, /* RFC5389 */
STUN_ERROR_UNAUTHORIZED=401, /* RFC5389 */ STUN_ERROR_UNAUTHORIZED=401, /* RFC5389 */
STUN_ERROR_FORBIDDEN=403, /* RFC7675 */
STUN_ERROR_UNKNOWN_ATTRIBUTE=420, /* RFC5389 */ STUN_ERROR_UNKNOWN_ATTRIBUTE=420, /* RFC5389 */
STUN_ERROR_ALLOCATION_MISMATCH=437, /* TURN-12 */ STUN_ERROR_ALLOCATION_MISMATCH=437, /* TURN-12 */
STUN_ERROR_STALE_NONCE=438, /* RFC5389 */ STUN_ERROR_STALE_NONCE=438, /* RFC5389 */
......
...@@ -28,7 +28,8 @@ nice_tests = [ ...@@ -28,7 +28,8 @@ nice_tests = [
'test-drop-invalid', 'test-drop-invalid',
'test-nomination', 'test-nomination',
'test-interfaces', 'test-interfaces',
'test-set-port-range' 'test-set-port-range',
'test-consent',
] ]
if cc.has_header('arpa/inet.h') if cc.has_header('arpa/inet.h')
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment