6712 lines
254 KiB
Diff
6712 lines
254 KiB
Diff
commit fb2f1f77a31baa91968fc81c205f980b6913f403
|
||
Author: Youness Alaoui <kakaroto@kakaroto.homelinux.net>
|
||
Date: Tue Nov 28 16:05:18 2017 -0500
|
||
|
||
conncheck: handle alternate-server for turn relays differently
|
||
|
||
If a relay gives us an alternate-server, we need to cancel and reset
|
||
every candidate discovery attempt that uses the same server, to avoid
|
||
ending up with one component on one server and the other component on
|
||
another server (causing relay candidates with mismatched foundations).
|
||
|
||
commit 4172d48852ecd1c86cc7bd4665b23697603d1eed
|
||
Author: Youness Alaoui <kakaroto@kakaroto.homelinux.net>
|
||
Date: Tue Nov 28 15:14:11 2017 -0500
|
||
|
||
discovery: Increase discovery_unsched_items whenever we restart a check
|
||
|
||
The discovery_unsched_items is decremented every time a DiscoveryCandidate
|
||
goes from non-pending to pending. So if we restart a check by setting
|
||
pending to FALSE, we should re-increase the discovery_unsched_items.
|
||
|
||
commit 59fcf95d505c3995f858b826d10cd48321ed383e
|
||
Author: Youness Alaoui <kakaroto@kakaroto.homelinux.net>
|
||
Date: Mon Nov 27 17:07:02 2017 -0500
|
||
|
||
turn: Add support for ALTERNATE_SERVER in OC2007 Compatibility
|
||
|
||
The MS Office TURN servers will always return the MS_ALTERNATE_SERVER in
|
||
allocation responses, and if they are not handled, we end up using the
|
||
main turn server to send allocation requests that then get sent to the
|
||
alternate server which will return the XOR_MAPPED_ADDRESS containing
|
||
the IP address of the turn server that proxied the message instead of
|
||
our own actual external IP.
|
||
|
||
commit 17f30e4465efe9533799b02d6f95feeaf0f2748c
|
||
Author: Miguel París <mparisdiaz@gmail.com>
|
||
Date: Wed Nov 8 16:26:47 2017 +0000
|
||
|
||
conncheck: do not require that all streams have a connection check list
|
||
|
||
One or more streams might not have any connection check list if the
|
||
number of streams differs from the peer agent.
|
||
Differential Revision: https://phabricator.freedesktop.org/D1880
|
||
|
||
commit c63349894b3fe974494453a883dfb5ad05df5a46
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Thu Nov 23 18:31:31 2017 +0100
|
||
|
||
Makefile: really enable debug for tests
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1888
|
||
|
||
commit 02216a6766caccb652387d5ee19686149eedbc93
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Nov 21 15:12:45 2017 +0100
|
||
|
||
agent: prevent external role change while conncheck is running
|
||
|
||
With this patch, we stash the controlling mode property change, and
|
||
apply it safely, when it won't interfere with an ongoing conncheck
|
||
running. According to RFC5245, sect 5.2. "Determining Role", the role
|
||
is determined for a session, and persists unless an ICE is restarted.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1887
|
||
|
||
commit fbdccf0c2787ebdc65fe13ac64bd25c829ea7972
|
||
Author: Philip Withnall <withnall@endlessm.com>
|
||
Date: Thu Aug 3 12:20:32 2017 +0100
|
||
|
||
stun: Fix FD leak in test/utility code
|
||
|
||
https://phabricator.freedesktop.org/T7798
|
||
|
||
Signed-off-by: Philip Withnall <withnall@endlessm.com>
|
||
Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
|
||
Differential Revision: https://phabricator.freedesktop.org/D1819
|
||
|
||
commit 4c4834ab634f735145c8f758a22cbdd9cab79bac
|
||
Author: Philip Withnall <withnall@endlessm.com>
|
||
Date: Tue Sep 12 13:23:53 2017 +0100
|
||
|
||
tests: Fix agent.h header inclusion in test-gstreamer.c
|
||
|
||
Spotted by Lukas Gradl on the mailing list.
|
||
|
||
Signed-off-by: Philip Withnall <withnall@endlessm.com>
|
||
|
||
commit 1a1803a45778000720c93d91060cedeb19124a27
|
||
Author: Philip Withnall <withnall@endlessm.com>
|
||
Date: Tue Sep 12 13:23:31 2017 +0100
|
||
|
||
tests: Fix copyright dates in test-gstreamer.c
|
||
|
||
This code is not 1000 years old.
|
||
|
||
Signed-off-by: Philip Withnall <withnall@endlessm.com>
|
||
|
||
commit 14102d44449d2eb4148588ce54fa897fa13b87ad
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Sun Jul 2 16:02:09 2017 +0200
|
||
|
||
conncheck: change state before updating nominated pairs
|
||
|
||
When a pair is nominated while in state failed, we first move
|
||
back to state connecting, then we update the selected pair, and
|
||
finally we move to state connected.
|
||
|
||
commit 72dd26a3368d3506fe8faca7067a02784fb5f0fd
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Wed Jun 28 12:06:48 2017 +0200
|
||
|
||
conncheck: forgot to put a pair in triggered check list
|
||
|
||
When a new pair is created from an unknown remote candidate, it
|
||
should be enqueue for a triggered check, to allow it to be marked
|
||
as nominated on response arrival in priv_mark_pair_nominated().
|
||
Creating it in waiting state is not sufficient since the update
|
||
in priv_mark_pair_nominated() from previous commits.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1763
|
||
|
||
commit 6fe64fdbc53ab87dffd79972f492665cff14c0a0
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Jun 27 11:01:14 2017 +0200
|
||
|
||
conncheck: update the state of triggered checks pairs
|
||
|
||
With this patch, we fix an ambiguity of some parts of the spec, when
|
||
the document refers to in-progress pairs, that also concern pairs in
|
||
the triggered checks list.
|
||
|
||
The first cast is in section 7.1.2.5, "Updating the Nominated Flag",
|
||
when the in-progress pair will be nominated on response arrival. This is
|
||
handled in function priv_mark_pair_nominated(), when a pair is put to
|
||
the triggered check list in reaction to a matching inbound stun request.
|
||
Such a pair in priv_mark_pair_nominated() will _always_ be in the
|
||
triggered check list, from the previously called function
|
||
priv_schedule_triggered_check().
|
||
|
||
The second case is in section 8.1.2, "Updating State" when an in-progress
|
||
pair stops its retransmission when another pair of higher priority is
|
||
already nominated. This is handled by function priv_prune_pending_checks().
|
||
|
||
Until now, pairs enqueued in the triggered check list move transiently
|
||
to state waiting, according to 7.2.1.4. But this state causes wrong
|
||
decisions in the two previous cases, because such pairs should in fact
|
||
rather be considered "like in-progress", to avoid discarding them
|
||
inadvertantly.
|
||
|
||
This patch update the state of the triggered check list
|
||
pairs to in-progress. It allows to remove exception handling cited
|
||
above: the code is a bit more simple, and allows some refactoring
|
||
in priv_mark_pair_nominated() between RFC and compatibility modes.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1762
|
||
|
||
commit 36f306f4a95f1c2b3e9c584b5a645a78e231c020
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Mon Jun 26 21:41:44 2017 +0200
|
||
|
||
conncheck: support several stun requests per pair
|
||
|
||
This patch should improve the reliabily of the connection check by
|
||
keeping the record of several simultaneous ongoing stun requests per
|
||
pair. A new stun request on an in-progress pair typically is caused by
|
||
in inbound stun request from the peer on this same pair. This is named
|
||
"Triggered Checks" in the spec. When this situation arises, it is fair
|
||
to handle these two stun requests simultaneously, the triggered check,
|
||
and the initial ordinary check, since both can potentially succeed.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1761
|
||
|
||
commit e860948b5fe3a791119957f26045b8f5159baeff
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Mon Jun 26 21:06:36 2017 +0200
|
||
|
||
conncheck: use stun_timer_remainder less frequently
|
||
|
||
We try to use stun_timer_remainder() less frequently, particularily
|
||
in the debug messages, and favour of the next_tick value associated
|
||
to the pair.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1760
|
||
|
||
commit ce33747e6fc9ca59ea22d54e3b5a9a67b69d8141
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Mon Jun 26 20:41:49 2017 +0200
|
||
|
||
conncheck: make debug sentences more accurate
|
||
|
||
We add a helper function to print the pair state in-extenso.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1759
|
||
|
||
commit 25be00271a4c8c684a2d435d29ae0811dbf5e21c
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Mon Jun 26 20:36:35 2017 +0200
|
||
|
||
conncheck: reorder some chunks of code
|
||
|
||
With this patch we simplify the levels of code indentation.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1758
|
||
|
||
commit 5a42089aeb2dbbb52d820cd1b6efdfcfbe9b055e
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Sep 5 14:50:29 2017 -0400
|
||
|
||
agent: Set error if it isn't set
|
||
|
||
commit dbaf8f5ccd76089e340883887c7e08e6c04de80a
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 12 13:22:21 2016 +0200
|
||
|
||
conncheck: improve role conflict debug
|
||
|
||
This patch displays explicitely the controlling or controlled
|
||
role of the agent.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D874
|
||
|
||
commit 9f800d3597767855accccc592c34bc4e945f5bd5
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Wed Jun 21 20:42:57 2017 -0400
|
||
|
||
configure: Remove -Wswitch-enum
|
||
|
||
Creates useless warnings when other libraries change.
|
||
|
||
https://phabricator.freedesktop.org/T7770
|
||
|
||
commit 63d273cea42def3567701ad9feab91f63cf9345f
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Thu Feb 11 22:16:48 2016 -0500
|
||
|
||
component: Use non-GClosure dummy callbacks
|
||
|
||
GClosures are not that cheap to setup
|
||
|
||
commit 2c50d73b82f2ec2422a8e0ea393194486c193c64
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Wed Feb 10 23:20:39 2016 -0500
|
||
|
||
agent: Don't crash if recv cancelled without a GError
|
||
|
||
commit dcb0d647174416a292492f8deca86f83a2ef124c
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Wed Jun 21 17:07:17 2017 -0400
|
||
|
||
Repleace UNRELEASED with 0.1.15
|
||
|
||
commit e3ddaa285e389baf3f26cfb6964919718a8f6a00
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Wed Jun 21 16:55:32 2017 -0400
|
||
|
||
agent: Adjust the nice_agent_new_full() to use flags
|
||
|
||
This makes it easier to read and more extensible.
|
||
|
||
commit c7a5a92b66f9b83baf2aa446966bdfb2cf39ecd1
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Sun Jun 18 10:12:58 2017 +0200
|
||
|
||
agent: remove spurious newlines
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1756
|
||
|
||
commit b4b8d6628c8c5d4f10af0101f846db4938a3f6c4
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Sun May 28 22:20:36 2017 +0200
|
||
|
||
stun: fix gcc7 implicit fallthrough warning
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1754
|
||
|
||
commit 195db6b344fc4f9fadc39419dfeec2fc14b23fac
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Fri Jul 15 23:31:42 2016 +0200
|
||
|
||
agent: add new pairs only for gathering streams
|
||
|
||
At the end of the local candidate gathering process, we only create new
|
||
pairs for streams that are in gathering state.
|
||
|
||
Other stream that may be in ready state for example, due to a
|
||
previously succeeded conncheck process, may have accumulated some
|
||
couples (local,remote) candidates that have not resulted in the creation
|
||
a new pair during this previous conncheck process, and we don't want
|
||
these new pairs to be added now, because it would generate unneeded
|
||
transition changes for a stream unconcerned by this gathering.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1755
|
||
|
||
commit 07366a5bca7e4818b8df29d9c7c220da8f752547
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Jun 21 21:47:42 2016 +0200
|
||
|
||
conncheck: fix the component failed transition
|
||
|
||
This patch fixes the transition of a component from connecting to
|
||
failed, that previously occured due to the propagation of the
|
||
keep_timer_going variable, and to the final call to function
|
||
priv_update_check_list_failed_components(), after the global agent
|
||
timer was stopped.
|
||
|
||
Previously, the code almost never entered to failed state, because the
|
||
timer was going one, as long as the number of nominated pair was not
|
||
enough, and as long as there were valid pairs not yet nominated. Even
|
||
if all pair timers were over.
|
||
|
||
The definition of the Failed state of a conncheck list is somewhat
|
||
contradictory in the spec, depending on weather you read :
|
||
|
||
* sect 5.7.4. "Computing States",
|
||
"Failed: In this state, the ICE checks have not completed successfully
|
||
for this media stream."
|
||
|
||
or
|
||
|
||
* sect 7.1.3.3. "Check List and Timer State Updates",
|
||
"If all of the pairs in the check list are now either in the Failed or
|
||
Succeeded state: If there is not a pair in the valid list for each
|
||
component of the media stream, the state of the check list is set to
|
||
Failed."
|
||
|
||
Our understanding of the ICE spec is that, the proper way to enter failed
|
||
state instead in when all connchecks have no longer in-progress pairs.
|
||
All pairs are either in state succeeded, discovered, or failed. No timer
|
||
is still running, and we have no hope that the conncheck list changes
|
||
again, except if an unexpected STUN packet arrives later. All pairs in
|
||
frozen state is a special case, that is handled separately (sect
|
||
7.1.3.3).
|
||
|
||
A special grace delay is added before declaring a component in state
|
||
Failed. This delay is not part of the RFC, and it is aimed to limit the
|
||
cases when a conncheck list is reactivated just after it's been declared
|
||
failed, causing a user visible transition from connecting to failed, and
|
||
back from failed to connecting again. This is also required by the test
|
||
suite, that counts exactly the number of time each state is entered, and
|
||
doesn't expect these transcient failed states to happen (frequent due to
|
||
the nature of the testsuite, less frequent in real life).
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1111
|
||
|
||
commit 95f8805eb7b77755337e28daf1f134587d42b35f
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Thu Jun 16 17:32:39 2016 +0200
|
||
|
||
conncheck: remove cancelled pair state
|
||
|
||
Pairs with the state NICE_CHECK_CANCELLED are the pairs targeted for
|
||
removal after the nomination of a pair with an higher priority,
|
||
described in Section 8.1.2 "Updating States", item 2 of RFC 5245. They
|
||
include also pairs that overflow the conncheck list size, but this is a
|
||
somewhat more marginal situation. So we are mainly interested in the
|
||
first use case of this state.
|
||
|
||
This state mixes two different situations, that deserve a distinct
|
||
handling : on one side, there are waiting or frozen pairs that must be
|
||
removed, this is an immediate action that doesn't need a dedicated state
|
||
for that. And on the other side, there are in-progress pairs that
|
||
should no longer be retransmitted, because another pair with a higher
|
||
priority has already been nominated.
|
||
|
||
This patch removes the cancelled state, and adds a flag
|
||
retransmit_on_timeout to deal with this last situation. Note that this
|
||
case should not generate a triggered check, as per described in section
|
||
7.2.1.4, when the state of the pair is In-Progress or Failed, since this
|
||
pair of lower priority has no hope to replace the nominated one.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1114
|
||
|
||
commit d516fca1b0e0a6606afec797bdc0690104e779a9
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Jun 14 21:32:26 2016 +0200
|
||
|
||
conncheck: adjust recheck on timeout strategy
|
||
|
||
The pair recheck on timeout can easily cause repetitive rechecks in
|
||
a ping-pong effect, if both peers with the same behaviour try to
|
||
check the same pair almost simultaneously, and if the network rtt
|
||
is greater than the initial timer rto. The reply to the initial
|
||
stun request may arrive after the in-progress conncheck
|
||
cancellation (described in RFC 5245, sect 7.2.1.4). Cancellation
|
||
creates a new stun request, and forgets the initial one.
|
||
The conncheck timer is restarted with the same initial value,
|
||
so the same situation happens again later.
|
||
|
||
We choose to avoid resetting the timer in such situation. After enough
|
||
retransmissions, the timeout delay, that doubles after each timeout,
|
||
becomes longer than the rtt, and the stun reply can be handled.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1115
|
||
|
||
commit f19d209decac432a1597d84c3d5809d2208f7457
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Jun 14 21:20:49 2016 +0200
|
||
|
||
conncheck: do not recheck a just succeeded pair
|
||
|
||
We cancel the potential in-progress transaction cancellation, caused by
|
||
sect 7.2.1.4 "Triggered Checks", when we receive a valid reply before
|
||
transmission timeout, or just after timeout, when the pair is
|
||
temporarily put on the triggered check list on the way to be
|
||
rechecked. This situation is not covered by the RFC 5245.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1119
|
||
|
||
commit 59fe48517c0b7db77b99183d31fdd84b55adb5d4
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Jun 14 21:12:16 2016 +0200
|
||
|
||
conncheck: fix a state transition case
|
||
|
||
When a new stun request hits a valid pair, of a failed component, we may
|
||
have a transition from state failed to connected. In this situation, we
|
||
do a logical progression failed -> connecting -> connected, like we do
|
||
in function priv_update_check_list_state_for_ready()
|
||
|
||
Similarily, when a new stun request hits a failed pair, of a failed
|
||
component, triggering a new conncheck for this pair may also cause the
|
||
component state to move back from failed to connecting state.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1118
|
||
|
||
commit 6a512b6eca9603ce8bf3ed0814fd314684c66ea7
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Jun 14 21:04:49 2016 +0200
|
||
|
||
conncheck: try to change earlier to state ready
|
||
|
||
We check if we can move from state connected to ready just
|
||
after a pair expired its retransmission count. This pair
|
||
will be marked failed, and will no longer be in-progress.
|
||
The number of in-progress dropping down to zero is one
|
||
of the conditions needed to make the transition to ready,
|
||
per component (and not globally as it's the case in other
|
||
locations where this check function is called).
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1117
|
||
|
||
commit 8fa648a15a6700d08165fe97a09f5c068abae1e6
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Mon Apr 11 13:13:51 2016 +0200
|
||
|
||
conncheck: dont cancel a pair for triggered check
|
||
|
||
This patch adds another supplementary "corner" case, not covered by the
|
||
ICE spec, sect 8.1.2, "Updating States". A pair in waiting state and in
|
||
the triggered check list should be considered like an in-progress pair,
|
||
and cancelled only if its priority is lower than the priority of the
|
||
nominated pair. This is required in some aggressive nomination
|
||
situations for both peers to select the same pair, having the highest
|
||
priority.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D933
|
||
|
||
commit 11d4e37a030eb144a355dc26c705ef5aa5a975a7
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Fri Apr 1 17:31:44 2016 +0200
|
||
|
||
conncheck: remove a useless pair recheck
|
||
|
||
This exception to the ICE spec is no longer needed: when a pair is in
|
||
the succeeded state, there is no needed to recheck it again upon
|
||
reception of an incoming stun request on it.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D884
|
||
|
||
commit 25b3eeec70b4e8e3b2154a18cdc8c5604f572012
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 12 12:56:28 2016 +0200
|
||
|
||
conncheck: update the pair state in triggered check list
|
||
|
||
With this patch, we update the state of the pair to waiting when
|
||
it is put in the triggered check queue. We also take care to call
|
||
priv_schedule_triggered_check() before priv_mark_pair_nominated()
|
||
so a pair to be rechecked and put on the triggered check queue
|
||
will have a unique state to be tested in the following call to
|
||
priv_mark_pair_nominated() when evaluating its nomination attributes.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D883
|
||
|
||
commit afd8d41bb34afb3864e838ef79026ae4ef15c0d4
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 12 13:32:49 2016 +0200
|
||
|
||
conncheck: new pairs never have the nominated flag preset
|
||
|
||
This patch disables the possibility to set the nominated flag of a
|
||
candidate pair at creation time. This possibility was used when a new
|
||
pair is created from a new peer reflexive remote candidate, when the
|
||
agent is in controlled mode, and an stun request with USE-CANDIDATE is
|
||
received. In this case, since previous commit "conncheck: fix a
|
||
nomination corner case", we set the nominated flag when the stun
|
||
response of this new pair will arrive, and not before. Consequently,
|
||
this flag is no longer required when the pair is created.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D881
|
||
|
||
commit 3916b8bcbf7e78e1dcb6b77882075c2c22719b4e
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 12 13:30:04 2016 +0200
|
||
|
||
conncheck: fix a nomination corner case
|
||
|
||
This patch add two supplementary cases, not covered by the ICE spec,
|
||
sect 7.2.1.5 "Updating the Nominated Flag" when a controlled agent
|
||
receives a STUN request with the USE-CANDIDATE flag, for a pair that is
|
||
in the waiting state. We consider that this case is similar to the
|
||
in-progress state, and should be handled in the same way. We also accept
|
||
when the pair is in frozen state. This latter case happens in the
|
||
new-dribble test, when an agent replays incoming early connchecks.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D880
|
||
|
||
commit 9103a5f2e184211fc160d1d3070ce4d043c71ff0
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 19 18:16:26 2016 +0200
|
||
|
||
conncheck: use the right pair when retriggering a check
|
||
|
||
This patch completes the previous patch by adding a link back from the
|
||
discovered pair, to the parent pair that generated this check. This link
|
||
is needed by the ICE spec, to comply with section 8.1.1.1, "Regular
|
||
nomination", where the check to be retriggered is the initial check that
|
||
caused the discovery of the valid pair. When the valid pair is a
|
||
peer-reflexive pair, the retriggered check must target the succeeded
|
||
pair, and not the valid discovered pair.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D879
|
||
|
||
commit 72ee528f7fdf82fb1a44958c18a0d4d5055d2d1a
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 12 13:25:16 2016 +0200
|
||
|
||
conncheck: link succeeded and discovered pairs
|
||
|
||
When the agent has the role of the stun server, is in controlled mode,
|
||
and receives a pair with the "use-candidate" attribute set, it must find
|
||
a matching succeded or discovered pair in its conncheck list. This is
|
||
described in ICE spec 7.2.1.5, "Updating the Nominated Flag", item #1.
|
||
When a matching pair is in succeeded state, the agent must nominate the
|
||
valid pair (a discovered pair) constructed from section 7.1.3.2.2,
|
||
that's been created from this succeeded one. To make this lookup, we
|
||
introduce a new "discovered_pair" member of the CandidateCheckPair
|
||
struct, that links the succeeded pair, and its discovered pair
|
||
if any.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D878
|
||
|
||
commit 2fd7808419f459d5f6e97701ca6a350ddee6b7f2
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 19 17:59:27 2016 +0200
|
||
|
||
conncheck: improve triggered check of in-progress pairs
|
||
|
||
This patch update the way triggered checks of in-progress pairs are
|
||
handled, according to ICE spec, section 7.2.1.4. Previously the same
|
||
connection check was retransmitted with an updated timeout. This causes
|
||
problems when a controlling role switch occurs in this time frame.
|
||
This is the reason why a new connection check must be generated
|
||
reflecting the updated role. We introduce a new flag "recheck_on_timeout"
|
||
in the pair indicating that the pair must be rechecked at the next timer
|
||
expiration.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D875
|
||
|
||
commit ead3453d04fc70865d176ab073636f8b9078cbbc
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 12 13:20:38 2016 +0200
|
||
|
||
conncheck: invoke the debug dump in more places
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1123
|
||
|
||
commit 15c0546f624113b8c0546a1f883a48bff7020f1b
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 19 17:06:32 2016 +0200
|
||
|
||
conncheck: improve the selection of the pairs to be checked
|
||
|
||
This patch aims to implement more closely the algorithm described
|
||
in RFC 5245 indicating how pairs are transitionned from state Frozen
|
||
to Waiting. This is described in 7.1.3.2 when a check succeeded, and
|
||
correspond to modifications in function priv_conn_check_unfreeze_related().
|
||
This is also described in 5.7.4 when defining the initial state of the
|
||
pairs in a conncheck, and correspond to modifications in function
|
||
priv_conn_check_unfreeze_next().
|
||
|
||
This patch introduces the notion of active and frozen check list. It
|
||
allows us to define the timer restranmission delay as described in 16.1.
|
||
|
||
Another modification in priv_conn_check_tick_unlocked() is that every
|
||
stream in handled consecutively, and in an independant way. The pacing
|
||
was previously of a single STUN request emitted per callback, it is now
|
||
of a triggered check per callback OR a single STUN per callback AND per
|
||
stream per callback.
|
||
|
||
The description of ordinary checks per stream in 5.8 is detailled in
|
||
function priv_conn_check_tick_stream(), and a remaining of the code
|
||
used to nominate a pair by the controlling agent is put in a dedicated
|
||
function priv_conn_check_tick_stream_nominate()
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D813
|
||
|
||
commit 58d061df8f5425dc1add9c6030a2f891ebda4616
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Mon Mar 7 16:35:09 2016 +0100
|
||
|
||
conncheck: update pair valid property selectively
|
||
|
||
With this patch, we fix a corner case when the succeeded pair is a
|
||
peer-reflexive candidate pair, that already has been discovered
|
||
previously, In this case, the current pair -p- should not be marked
|
||
valid, because the valid flag is already set on the discovered pair.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1124
|
||
|
||
commit 0636f9addc041cf93c4ff4eaa351b1768d48a32e
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 19 13:12:48 2016 +0200
|
||
|
||
conncheck: implement ice regular nomination method
|
||
|
||
This patch implements Regular Nomation as described in RFC5245
|
||
8.1.1.1. The controlling agent lets valid pairs accumulate, and
|
||
decides which pair to recheck with the use-candidate attribute set.
|
||
priv_mark_pair_nominated() follows 7.2.1.5, to update the nominated
|
||
pair when acting as a STUN server, and
|
||
priv_map_reply_to_conn_check_request() implements 7.1.3.2.4 to
|
||
update the nominated pair when acting as a STUN client. A new
|
||
property is also added to the agent to control the nomination
|
||
mode, which can be regular of aggressive, with default value
|
||
set to aggressive.
|
||
|
||
Two new flags are introduced in the CandidateCheckPair structure:
|
||
|
||
- use_candidate_on_next_check indicates the STUN client to add the
|
||
use-candidate attribute when the pair will be checked. At this
|
||
time, the nominated flag has not been set on this pair yet.
|
||
|
||
- mark_nominated_on_response_arrival indicates the STUN server
|
||
to nominate the pair when its succesfull response to a
|
||
previous triggered check will arrive (7.2.1.5, item #2)
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D811
|
||
|
||
commit a602ff57aae6a6afdeab843954c48e6fb5d82d31
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 12 13:02:45 2016 +0200
|
||
|
||
conncheck: fix pair state transition when successful response is received
|
||
|
||
According the ICE RFC 5245, 7.1.3.2.3, the pair that *generated* a
|
||
successful check should go to state succeeded, not only the valid
|
||
pair built in section 7.1.3.2.2.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D810
|
||
|
||
commit 3a58ba6120b188d78c5709e0349c0346bfa21c1a
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Mon Feb 1 11:10:21 2016 +0100
|
||
|
||
conncheck: peer reflexive candidates are not paired
|
||
|
||
This patch makes the code compliant with ICE RFC, 7.2.1.3 "Learning
|
||
Peer Reflexive Candidates" and 7.1.3.2.1 "Discovering Peer Reflexive
|
||
Candidates", where discovered candidates do not cause the creation
|
||
of new pairs to be checked.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D805
|
||
|
||
commit 7a2c1edf502849a868b6f1026e8e2c343dee4ded
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Mon Jun 6 22:24:50 2016 +0200
|
||
|
||
conncheck: update selected pair when nominated flag is set
|
||
|
||
This modifies commit 8f1f615. It is better focused to update the
|
||
selected pair just after its nominated flag has been set. We also keep
|
||
the code homogeneous with other places, where the call to
|
||
priv_update_selected_pair() immediately follows the setting of
|
||
pair->nominated. Moreover in priv_update_check_list_state_for_ready(),
|
||
we would call priv_update_selected_pair() more times that necessary when
|
||
iterating on all nominated pairs.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1125
|
||
|
||
commit 8bb210c5af4bcaf342d7fa4fef6034269e976532
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Thu Jun 9 23:28:43 2016 +0200
|
||
|
||
stun timer: make properties for stun timer tunables
|
||
|
||
Three STUN binding request properties should be customisable. RFC 5245
|
||
describes the retransmission timer of the STUN transaction 'RTO', and
|
||
RFC 5389 describes the number of retransmissions to send until a
|
||
response is received 'Rc'. The third property is the 'RTO' when
|
||
a reliable connection is used.
|
||
|
||
RFC 5389 introduces a supplementary property 'Rm' as a multiplier used
|
||
to compute the final timeout RTO * Rm. However, this property is not
|
||
added in libnice, because this would require breaking the public API for
|
||
STUN. Currently, our STUN implementation hardcodes a division by two for
|
||
this final timeout.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1109
|
||
|
||
commit 80c613699786567fd93db74377138600794a86e0
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Thu Jun 8 16:34:21 2017 -0400
|
||
|
||
agent: Use base_addr to generate rport in SDP
|
||
|
||
Reported by Capricornus (zhushengliang)
|
||
|
||
https://phabricator.freedesktop.org/T7763
|
||
|
||
commit b4abda09c79e4ce372a3771300abf568c85c7ff5
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Thu Apr 21 18:18:59 2016 +0200
|
||
|
||
interfaces: ignore predefined network interfaces
|
||
|
||
Some interfaces, like the one managed by libvirtd to provide a network
|
||
bridge to locally hosted virtual machines, can be completely ignored
|
||
when gathering ICE candidates. The motivation for adding this
|
||
possibility is that, ignoring them doesn't remove capabilities, and
|
||
improves the overall speed of the connection check method, by reducing
|
||
the number of pairs to be tested. This patch adds the possibility to
|
||
define such interfaces in the configuration script.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D948
|
||
|
||
commit d5446a72233eab8501be0b3fb9060c8be3ba034b
|
||
Author: Philip Withnall <withnall@endlessm.com>
|
||
Date: Mon May 1 08:51:40 2017 +0100
|
||
|
||
examples: Stop installing the examples
|
||
|
||
There’s no point in installing them; their benefit is in providing
|
||
example code to developers.
|
||
|
||
Debian doesn’t package them; Fedora packages them in a separate
|
||
subpackage which will have to disappear.
|
||
|
||
Signed-off-by: Philip Withnall <withnall@endlessm.com>
|
||
Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
|
||
Differential Revision: https://phabricator.freedesktop.org/D1737
|
||
|
||
commit 0a2cb0a9b14a5a1a4b01ba68ab2e5a2aa965f342
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Tue Apr 5 21:32:39 2016 +0200
|
||
|
||
agent: do not create a GSource for UDP TURN socket
|
||
|
||
With this patch, we don't create a new GSource for udp-turn socket,
|
||
because it would duplicate the packets already received on the base UDP
|
||
socket, as the underlying GSocket is the same. This is a race condition,
|
||
because an UDP packet arriving on the base socket, may randomly be
|
||
handled by the GSource callback created for the base socket (udp-bsd) of
|
||
the callback created for the udp-turn socket. Moreover this callback
|
||
already knows how to parse UDP datagrams received from a known turn
|
||
server.
|
||
|
||
This patch also prevents a subtle bug, when a STUN request is received
|
||
directly from a peer, is handled by the udp turn socket. If the agent
|
||
already has a valid permission for this remote candidate, established
|
||
for another pair, it will happily send the STUN reply through the turn
|
||
relay. This generates a source address mismatch on the peer agent, when
|
||
it'll receive the STUN response from the turn relay instead of the
|
||
initial address the request has been sent to.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D932
|
||
|
||
commit f6f704c5e8d2193bc67ba2b697c77694e1698c43
|
||
Author: Fabrice Bellet <fabrice@bellet.info>
|
||
Date: Thu Jun 9 22:22:33 2016 +0200
|
||
|
||
stun timer: fix timeout of the last retransmission
|
||
|
||
According to RFC 5389, section 7.2.1, a special timeout is applied to
|
||
the last retransmission (Rm * RTO), with Rm default value of 16, instead
|
||
of (64 * RTO), 2^6 when the number of transmissions Rc is set to 7.
|
||
|
||
As spotted by Olivier Crete, stun_timer_* is a public API, that cannot
|
||
be changed, and the initial delay (RTO) is not preserved in the
|
||
stun_timer_s struct. So we use a hack that implicitely guess Rm from the
|
||
number of transmissions Rc, by generalizing the default value of the
|
||
spec for Rm and Rc to other values of Rc passed in stun_timer_start(
|
||
|
||
According to the spec, with the default value of Rc=7, the last delay
|
||
should be (64 * RTO), and it is instead (16 * RTO). So the last delay
|
||
can be computed by dividing the penultimate delay by two, instead of
|
||
multiplying it by two.
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1108
|
||
|
||
commit b0538d8c51f65019867b56a45cf90a70bef38f01
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 11 18:31:21 2017 -0400
|
||
|
||
agent: Ignore remote candidate of non-accepted types
|
||
|
||
If we disable ice-tcp or ice-udp, ignore the remote
|
||
candidates for those types.
|
||
|
||
commit f49ab88f957a3a250ef2ada0c0fab4fbbd3e5da8
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 11 16:42:55 2017 -0400
|
||
|
||
conncheck: Check the controlling state when the req was sent
|
||
|
||
It was checking when the pair was created, but the role may have
|
||
already changed when the request is sent.
|
||
|
||
commit 8fc22b0034d04cbc222e0637152b1cee2879eef3
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Wed Apr 5 17:43:26 2017 -0400
|
||
|
||
tests_: Add test to verify that only packets from validated addresses pass
|
||
|
||
https://phabricator.freedesktop.org/T104
|
||
|
||
Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
|
||
Differential Revision: https://phabricator.freedesktop.org/D1717
|
||
|
||
commit ffc7fddac42728bac6e4753a17bc52e5e610ae8b
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 21:27:39 2017 -0400
|
||
|
||
agent: Drop packets not from validated addresses
|
||
|
||
This is required by the WebRTC spec.
|
||
|
||
Remove test-mainloop as it doesnt even try to do
|
||
a negotiation.
|
||
|
||
https://phabricator.freedesktop.org/T104
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1716
|
||
|
||
commit 1e9e28dbc98b4f6a7cf4bda0ca73b5abc2735ddc
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 14:41:51 2017 -0400
|
||
|
||
candidate: Add equality check function
|
||
|
||
Add a function that can check if two candidates point to the same place.
|
||
|
||
https://phabricator.freedesktop.org/T104
|
||
|
||
Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
|
||
Differential Revision: https://phabricator.freedesktop.org/D1715
|
||
|
||
commit 10c557f23f8337f1304fff27bd85d2eb713cb249
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Wed Apr 5 17:01:35 2017 -0400
|
||
|
||
test-credentials: Fix leak
|
||
|
||
commit ae6d939e48366b80570d713b83334191b0982e71
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 20:34:05 2017 -0400
|
||
|
||
debug: Use libnice-verbose, not libnice-nice-verbose
|
||
|
||
commit efc6a9be8cb34c899f0454c32e8a1e62b38df474
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 18:42:57 2017 -0400
|
||
|
||
tests: Use automake test-driver for valgrind
|
||
|
||
This fixes the valgrind integration with the new test drivers.
|
||
|
||
commit 4e605885c9dcaeb3ee443ec902c9c9189b19043f
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 16:16:46 2017 -0400
|
||
|
||
agent: Remove impossible case
|
||
|
||
commit e56b910d2d8b70f5677bbd4be579d5b95aff33ad
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 16:16:05 2017 -0400
|
||
|
||
agent: Separate return from NiceSocket and internal enum
|
||
|
||
The same variable was used for return values from NiceSocket and
|
||
for the internal enum, but 0 and -1 have different meanings in both.
|
||
|
||
commit cd255bddc7fa0ddae056b5358a22b380c4eefc42
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 15:24:43 2017 -0400
|
||
|
||
udp-turn: Add some const to internal APIs
|
||
|
||
commit db05e8b0fdc713df93cd6a4c3914e5aee38b2391
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 12:30:27 2017 -0400
|
||
|
||
Make clang-analyzer happy
|
||
|
||
Various little things, none of which should make a functional difference.
|
||
|
||
commit 0672758b9621801c8f0d9e3c920370983b267a68
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 12:29:29 2017 -0400
|
||
|
||
agent: Don't set variable that won't be used
|
||
|
||
It exits the loop immediately, so no point to set the variable.
|
||
And it makes the clang static analyzer happy.
|
||
|
||
commit 0de1657e4d15d4f1911ab1fad84ea23e7013070f
|
||
Author: Olivier Crête <olivier.crete@collabora.com>
|
||
Date: Tue Apr 4 12:25:50 2017 -0400
|
||
|
||
conncheck: Use the right test for empty remote_frag
|
||
|
||
It's now an array, not a pointer, so needs to test to emptyness.
|
||
|
||
It's a bugfix on the previous commit, 59ce41df
|
||
|
||
commit 59ce41dfb837adf4222b25490cde2e394384ad15
|
||
Author: Miguel París Díaz <mparisdiaz@gmail.com>
|
||
Date: Fri Mar 31 20:20:38 2017 -0400
|
||
|
||
conncheck: consider answer received when remote credentials are set
|
||
|
||
Consider that the answer is received when remote credentials
|
||
are set instead of when a remote candidate is set,
|
||
which could not happen or could cause more delay for the
|
||
connection establishment.
|
||
|
||
Ported to git master by Olivier Crête
|
||
|
||
Differential Revision: https://phabricator.freedesktop.org/D1704
|
||
diff --git a/agent/Makefile.am b/agent/Makefile.am
|
||
index b585393..915f312 100644
|
||
--- a/agent/Makefile.am
|
||
+++ b/agent/Makefile.am
|
||
@@ -22,6 +22,12 @@ if WINDOWS
|
||
AM_CFLAGS += -DWINVER=0x0501 # _WIN32_WINNT_WINXP
|
||
endif
|
||
|
||
+BUILT_SOURCES = \
|
||
+ agent-enum-types.h \
|
||
+ agent-enum-types.c
|
||
+
|
||
+CLEANFILES += $(BUILT_SOURCES)
|
||
+
|
||
noinst_LTLIBRARIES = libagent.la
|
||
|
||
libagent_la_SOURCES = \
|
||
@@ -54,6 +60,23 @@ libagent_la_SOURCES = \
|
||
outputstream.c \
|
||
$(BUILT_SOURCES)
|
||
|
||
+agent-enum-types.h: agent.h Makefile
|
||
+ $(AM_V_GEN)$(GLIB_MKENUMS) \
|
||
+ --fhead "#ifndef __AGENT_ENUM_TYPES_H__\n#define __AGENT_ENUM_TYPES_H__ 1\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
|
||
+ --fprod "/* enumerations from \"@filename@\" */\n" \
|
||
+ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define NICE_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
|
||
+ --ftail "G_END_DECLS\n\n#endif /* !AGENT_ENUM_TYPES_H */" \
|
||
+ $(addprefix $(srcdir)/,agent.h) > $@
|
||
+
|
||
+agent-enum-types.c: agent.h Makefile agent-enum-types.h
|
||
+ $(AM_V_GEN)$(GLIB_MKENUMS) \
|
||
+ --fhead "#include <config.h>\n#include <glib-object.h>\n#include \"agent.h\"\n#include \"agent-enum-types.h\"" \
|
||
+ --fprod "\n/* enumerations from \"@filename@\" */" \
|
||
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType type = 0;\n if (!type) {\n static const G@Type@Value values[] = {" \
|
||
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
|
||
+ --vtail " { 0, NULL, NULL }\n };\n type = g_@type@_register_static (\"@EnumName@\", values);\n }\n return type;\n}\n\n" \
|
||
+ $(addprefix $(srcdir)/,agent.h) > $@
|
||
+
|
||
libagent_la_LIBADD = \
|
||
$(top_builddir)/random/libnice-random.la \
|
||
$(top_builddir)/socket/libsocket.la \
|
||
diff --git a/agent/agent-priv.h b/agent/agent-priv.h
|
||
index 4d8c9b8..7269be0 100644
|
||
--- a/agent/agent-priv.h
|
||
+++ b/agent/agent-priv.h
|
||
@@ -106,7 +106,6 @@ 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_TR_MIN 15000 /* timer Tr, msecs (ICE ID-19) */
|
||
#define NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT 100 /* see spec 5.7.3 (ID-19) */
|
||
|
||
|
||
@@ -114,6 +113,27 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a,
|
||
* MTU and estimated typical sizes of ICE STUN packet */
|
||
#define MAX_STUN_DATAGRAM_PAYLOAD 1300
|
||
|
||
+#define NICE_COMPONENT_MAX_VALID_CANDIDATES 50 /* maximum number of validates remote candidates to keep, the number is arbitrary but hopefully large enough */
|
||
+
|
||
+/* A convenient macro to test if the agent is compatible with RFC5245
|
||
+ * or OC2007R2. Specifically these two modes share the support
|
||
+ * of the regular or aggressive nomination mode */
|
||
+#define NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2(obj) \
|
||
+ ((obj)->compatibility == NICE_COMPATIBILITY_RFC5245 || \
|
||
+ (obj)->compatibility == NICE_COMPATIBILITY_OC2007R2)
|
||
+
|
||
+/* A grace period before declaring a component as failed, in msecs. This
|
||
+ * delay is added to reduce the chance to see the agent receiving new
|
||
+ * stun activity just after the conncheck list has been declared failed,
|
||
+ * reactiviting conncheck activity, and causing a (valid) state
|
||
+ * transitions like that: connecting -> failed -> connecting ->
|
||
+ * connected -> ready.
|
||
+ * Such transitions are not buggy per-se, but may break the
|
||
+ * test-suite, that counts precisely the number of time each state
|
||
+ * has been set, and doesnt expect these transcient failed states.
|
||
+ */
|
||
+#define NICE_AGENT_MAX_TIMER_GRACE_PERIOD 1000
|
||
+
|
||
struct _NiceAgent
|
||
{
|
||
GObject parent; /* gobject pointer */
|
||
@@ -126,10 +146,14 @@ struct _NiceAgent
|
||
NiceProxyType proxy_type; /* property: Proxy type */
|
||
gchar *proxy_username; /* property: Proxy username */
|
||
gchar *proxy_password; /* property: Proxy password */
|
||
- gboolean controlling_mode; /* property: controlling-mode */
|
||
+ gboolean saved_controlling_mode;/* property: controlling-mode */
|
||
guint timer_ta; /* property: timer Ta */
|
||
guint max_conn_checks; /* property: max connectivity checks */
|
||
gboolean force_relay; /* property: force relay */
|
||
+ guint stun_max_retransmissions; /* property: stun max retransmissions, Rc */
|
||
+ guint stun_initial_timeout; /* property: stun initial timeout, RTO */
|
||
+ guint stun_reliable_timeout; /* property: stun reliable timeout */
|
||
+ NiceNominationMode nomination_mode; /* property: Nomination mode */
|
||
|
||
GSList *local_addresses; /* list of NiceAddresses for local
|
||
interfaces */
|
||
@@ -164,6 +188,10 @@ struct _NiceAgent
|
||
guint16 rfc4571_expecting_length;
|
||
gboolean use_ice_udp;
|
||
gboolean use_ice_tcp;
|
||
+
|
||
+ guint conncheck_timer_grace_period; /* ongoing delay before timer stop */
|
||
+ gboolean controlling_mode; /* controlling mode used by the
|
||
+ conncheck */
|
||
/* XXX: add pointer to internal data struct for ABI-safe extensions */
|
||
};
|
||
|
||
diff --git a/agent/agent.c b/agent/agent.c
|
||
index 555fd16..0773c53 100644
|
||
--- a/agent/agent.c
|
||
+++ b/agent/agent.c
|
||
@@ -73,6 +73,7 @@
|
||
#include "interfaces.h"
|
||
|
||
#include "pseudotcp.h"
|
||
+#include "agent-enum-types.h"
|
||
|
||
/* Maximum size of a UDP packet’s payload, as the packet’s length field is 16b
|
||
* wide. */
|
||
@@ -113,6 +114,10 @@ enum
|
||
PROP_BYTESTREAM_TCP,
|
||
PROP_KEEPALIVE_CONNCHECK,
|
||
PROP_FORCE_RELAY,
|
||
+ PROP_STUN_MAX_RETRANSMISSIONS,
|
||
+ PROP_STUN_INITIAL_TIMEOUT,
|
||
+ PROP_STUN_RELIABLE_TIMEOUT,
|
||
+ PROP_NOMINATION_MODE,
|
||
};
|
||
|
||
|
||
@@ -400,6 +405,13 @@ nice_agent_class_init (NiceAgentClass *klass)
|
||
1, /* not a construct property, ignored */
|
||
G_PARAM_READWRITE));
|
||
|
||
+ /**
|
||
+ * NiceAgent:controlling-mode:
|
||
+ *
|
||
+ * Whether the agent has the controlling role. This property should
|
||
+ * be modified before gathering candidates, any modification occuring
|
||
+ * later will be hold until ICE is restarted.
|
||
+ */
|
||
g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE,
|
||
g_param_spec_boolean (
|
||
"controlling-mode",
|
||
@@ -436,6 +448,24 @@ nice_agent_class_init (NiceAgentClass *klass)
|
||
0, /* default set in init */
|
||
G_PARAM_READWRITE));
|
||
|
||
+ /**
|
||
+ * NiceAgent:nomination-mode:
|
||
+ *
|
||
+ * The nomination mode used in the ICE specification for describing
|
||
+ * the selection of valid pairs to be used upstream.
|
||
+ * <para> See also: #NiceNominationMode </para>
|
||
+ *
|
||
+ * Since: 0.1.15
|
||
+ */
|
||
+ g_object_class_install_property (gobject_class, PROP_NOMINATION_MODE,
|
||
+ g_param_spec_enum (
|
||
+ "nomination-mode",
|
||
+ "ICE nomination mode",
|
||
+ "Nomination mode used in the ICE specification for describing "
|
||
+ "the selection of valid pairs to be used upstream",
|
||
+ NICE_TYPE_NOMINATION_MODE, NICE_NOMINATION_MODE_AGGRESSIVE,
|
||
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||
+
|
||
/**
|
||
* NiceAgent:proxy-ip:
|
||
*
|
||
@@ -708,6 +738,76 @@ nice_agent_class_init (NiceAgentClass *klass)
|
||
FALSE,
|
||
G_PARAM_READWRITE));
|
||
|
||
+ /**
|
||
+ * NiceAgent:stun-max-retransmissions
|
||
+ *
|
||
+ * The maximum number of retransmissions of the STUN binding requests
|
||
+ * used in the gathering stage, to find our local candidates, and used
|
||
+ * in the connection check stage, to test the validity of each
|
||
+ * constructed pair. This property is described as 'Rc' in the RFC
|
||
+ * 5389, with a default value of 7. The timeout of each STUN request
|
||
+ * is doubled for each retransmission, so the choice of this value has
|
||
+ * a direct impact on the time needed to move from the CONNECTED state
|
||
+ * to the READY state, and on the time needed to complete the GATHERING
|
||
+ * state.
|
||
+ *
|
||
+ * Since: 0.1.15
|
||
+ */
|
||
+
|
||
+ g_object_class_install_property (gobject_class, PROP_STUN_MAX_RETRANSMISSIONS,
|
||
+ g_param_spec_uint (
|
||
+ "stun-max-retransmissions",
|
||
+ "STUN Max Retransmissions",
|
||
+ "Maximum number of STUN binding requests retransmissions "
|
||
+ "described as 'Rc' in the STUN specification.",
|
||
+ 1, 99,
|
||
+ STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS,
|
||
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
||
+
|
||
+ /**
|
||
+ * NiceAgent:stun-initial-timeout
|
||
+ *
|
||
+ * The initial timeout (msecs) of the STUN binding requests
|
||
+ * used in the gathering stage, to find our local candidates.
|
||
+ * This property is described as 'RTO' in the RFC 5389 and RFC 5245.
|
||
+ * This timeout is doubled for each retransmission, until
|
||
+ * #NiceAgent:stun-max-retransmissions have been done,
|
||
+ * with an exception for the last restransmission, where the timeout is
|
||
+ * divided by two instead (RFC 5389 indicates that a customisable
|
||
+ * multiplier 'Rm' to 'RTO' should be used).
|
||
+ *
|
||
+ * Since: 0.1.15
|
||
+ */
|
||
+
|
||
+ g_object_class_install_property (gobject_class, PROP_STUN_INITIAL_TIMEOUT,
|
||
+ g_param_spec_uint (
|
||
+ "stun-initial-timeout",
|
||
+ "STUN Initial Timeout",
|
||
+ "STUN timeout in msecs of the initial binding requests used in the "
|
||
+ "gathering state, described as 'RTO' in the ICE specification.",
|
||
+ 20, 9999,
|
||
+ STUN_TIMER_DEFAULT_TIMEOUT,
|
||
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
||
+
|
||
+ /**
|
||
+ * NiceAgent:stun-reliable-timeout
|
||
+ *
|
||
+ * The initial timeout of the STUN binding requests used
|
||
+ * for a reliable timer.
|
||
+ *
|
||
+ * Since: 0.1.15
|
||
+ */
|
||
+
|
||
+ g_object_class_install_property (gobject_class, PROP_STUN_RELIABLE_TIMEOUT,
|
||
+ g_param_spec_uint (
|
||
+ "stun-reliable-timeout",
|
||
+ "STUN Reliable Timeout",
|
||
+ "STUN timeout in msecs of the initial binding requests used for "
|
||
+ "a reliable timer.",
|
||
+ 20, 99999,
|
||
+ STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT,
|
||
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
||
+
|
||
/* install signals */
|
||
|
||
/**
|
||
@@ -1013,6 +1113,47 @@ static void priv_generate_tie_breaker (NiceAgent *agent)
|
||
nice_rng_generate_bytes (agent->rng, 8, (gchar*)&agent->tie_breaker);
|
||
}
|
||
|
||
+static void
|
||
+priv_update_controlling_mode (NiceAgent *agent, gboolean value)
|
||
+{
|
||
+ gboolean update_controlling_mode;
|
||
+ GSList *i, *j;
|
||
+
|
||
+ agent->saved_controlling_mode = value;
|
||
+ /* It is safe to update the agent controlling mode when all
|
||
+ * components are still in state disconnected. When we leave
|
||
+ * this state, the role must stay under the control of the
|
||
+ * conncheck algorithm exclusively, until the conncheck is
|
||
+ * eventually restarted. See RFC5245, sect 5.2. Determining Role
|
||
+ */
|
||
+ if (agent->controlling_mode != agent->saved_controlling_mode) {
|
||
+ update_controlling_mode = TRUE;
|
||
+ for (i = agent->streams;
|
||
+ i && update_controlling_mode; i = i->next) {
|
||
+ NiceStream *stream = i->data;
|
||
+ for (j = stream->components;
|
||
+ j && update_controlling_mode; j = j->next) {
|
||
+ NiceComponent *component = j->data;
|
||
+ if (component->state > NICE_COMPONENT_STATE_DISCONNECTED)
|
||
+ update_controlling_mode = FALSE;
|
||
+ }
|
||
+ }
|
||
+ if (update_controlling_mode) {
|
||
+ agent->controlling_mode = agent->saved_controlling_mode;
|
||
+ nice_debug ("Agent %p : Property set, changing role to \"%s\".",
|
||
+ agent, agent->controlling_mode ? "controlling" : "controlled");
|
||
+ } else {
|
||
+ nice_debug ("Agent %p : Property set, role switch requested "
|
||
+ "but conncheck already started.", agent);
|
||
+ nice_debug ("Agent %p : Property set, staying with role \"%s\" "
|
||
+ "until restart.", agent,
|
||
+ agent->controlling_mode ? "controlling" : "controlled");
|
||
+ }
|
||
+ } else
|
||
+ nice_debug ("Agent %p : Property set, role is already \"%s\".", agent,
|
||
+ agent->controlling_mode ? "controlling" : "controlled");
|
||
+}
|
||
+
|
||
static void
|
||
nice_agent_init (NiceAgent *agent)
|
||
{
|
||
@@ -1022,7 +1163,9 @@ nice_agent_init (NiceAgent *agent)
|
||
/* set defaults; not construct params, so set here */
|
||
agent->stun_server_port = DEFAULT_STUN_PORT;
|
||
agent->controlling_mode = TRUE;
|
||
+ agent->saved_controlling_mode = TRUE;
|
||
agent->max_conn_checks = NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT;
|
||
+ agent->nomination_mode = NICE_NOMINATION_MODE_AGGRESSIVE;
|
||
|
||
agent->discovery_list = NULL;
|
||
agent->discovery_unsched_items = 0;
|
||
@@ -1071,6 +1214,24 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat)
|
||
}
|
||
|
||
|
||
+NICEAPI_EXPORT NiceAgent *
|
||
+nice_agent_new_full (GMainContext *ctx,
|
||
+ NiceCompatibility compat,
|
||
+ NiceAgentOption flags)
|
||
+{
|
||
+ NiceAgent *agent = g_object_new (NICE_TYPE_AGENT,
|
||
+ "compatibility", compat,
|
||
+ "main-context", ctx,
|
||
+ "reliable", (flags & NICE_AGENT_OPTION_RELIABLE) ? TRUE : FALSE,
|
||
+ "nomination-mode", (flags & NICE_AGENT_OPTION_REGULAR_NOMINATION) ?
|
||
+ NICE_NOMINATION_MODE_REGULAR : NICE_NOMINATION_MODE_AGGRESSIVE,
|
||
+ "full-mode", (flags & NICE_AGENT_OPTION_LITE_MODE) ? FALSE : TRUE,
|
||
+ NULL);
|
||
+
|
||
+ return agent;
|
||
+}
|
||
+
|
||
+
|
||
static void
|
||
nice_agent_get_property (
|
||
GObject *object,
|
||
@@ -1101,7 +1262,7 @@ nice_agent_get_property (
|
||
break;
|
||
|
||
case PROP_CONTROLLING_MODE:
|
||
- g_value_set_boolean (value, agent->controlling_mode);
|
||
+ g_value_set_boolean (value, agent->saved_controlling_mode);
|
||
break;
|
||
|
||
case PROP_FULL_MODE:
|
||
@@ -1117,6 +1278,10 @@ nice_agent_get_property (
|
||
/* XXX: should we prune the list of already existing checks? */
|
||
break;
|
||
|
||
+ case PROP_NOMINATION_MODE:
|
||
+ g_value_set_enum (value, agent->nomination_mode);
|
||
+ break;
|
||
+
|
||
case PROP_PROXY_IP:
|
||
g_value_set_string (value, agent->proxy_ip);
|
||
break;
|
||
@@ -1187,6 +1352,18 @@ nice_agent_get_property (
|
||
g_value_set_boolean (value, agent->force_relay);
|
||
break;
|
||
|
||
+ case PROP_STUN_MAX_RETRANSMISSIONS:
|
||
+ g_value_set_uint (value, agent->stun_max_retransmissions);
|
||
+ break;
|
||
+
|
||
+ case PROP_STUN_INITIAL_TIMEOUT:
|
||
+ g_value_set_uint (value, agent->stun_initial_timeout);
|
||
+ break;
|
||
+
|
||
+ case PROP_STUN_RELIABLE_TIMEOUT:
|
||
+ g_value_set_uint (value, agent->stun_reliable_timeout);
|
||
+ break;
|
||
+
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||
}
|
||
@@ -1294,7 +1471,7 @@ nice_agent_set_property (
|
||
break;
|
||
|
||
case PROP_CONTROLLING_MODE:
|
||
- agent->controlling_mode = g_value_get_boolean (value);
|
||
+ priv_update_controlling_mode (agent, g_value_get_boolean (value));
|
||
break;
|
||
|
||
case PROP_FULL_MODE:
|
||
@@ -1309,6 +1486,10 @@ nice_agent_set_property (
|
||
agent->max_conn_checks = g_value_get_uint (value);
|
||
break;
|
||
|
||
+ case PROP_NOMINATION_MODE:
|
||
+ agent->nomination_mode = g_value_get_enum (value);
|
||
+ break;
|
||
+
|
||
case PROP_PROXY_IP:
|
||
g_free (agent->proxy_ip);
|
||
agent->proxy_ip = g_value_dup_string (value);
|
||
@@ -1374,6 +1555,18 @@ nice_agent_set_property (
|
||
agent->force_relay = g_value_get_boolean (value);
|
||
break;
|
||
|
||
+ case PROP_STUN_MAX_RETRANSMISSIONS:
|
||
+ agent->stun_max_retransmissions = g_value_get_uint (value);
|
||
+ break;
|
||
+
|
||
+ case PROP_STUN_INITIAL_TIMEOUT:
|
||
+ agent->stun_initial_timeout = g_value_get_uint (value);
|
||
+ break;
|
||
+
|
||
+ case PROP_STUN_RELIABLE_TIMEOUT:
|
||
+ agent->stun_reliable_timeout = g_value_get_uint (value);
|
||
+ break;
|
||
+
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||
}
|
||
@@ -1552,7 +1745,6 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self,
|
||
|
||
if (len == 0) {
|
||
/* Reached EOS. */
|
||
- len = 0;
|
||
goto done;
|
||
} else if (len < 0 &&
|
||
pseudo_tcp_socket_get_error (self) == EWOULDBLOCK) {
|
||
@@ -1890,6 +2082,17 @@ void agent_gathering_done (NiceAgent *agent)
|
||
|
||
for (i = agent->streams; i; i = i->next) {
|
||
NiceStream *stream = i->data;
|
||
+
|
||
+ /* We ignore streams not in gathering state, typically already in
|
||
+ * ready state. Such streams may have couples (local,remote)
|
||
+ * candidates that have not resulted in the creation a new pair
|
||
+ * during a previous conncheck session, and we don't want these new
|
||
+ * pairs to be added now, because it would generate unneeded
|
||
+ * transition changes for a stream unconcerned by this gathering.
|
||
+ */
|
||
+ if (!stream->gathering)
|
||
+ continue;
|
||
+
|
||
for (j = stream->components; j; j = j->next) {
|
||
NiceComponent *component = j->data;
|
||
|
||
@@ -3113,6 +3316,13 @@ static gboolean priv_add_remote_candidate (
|
||
NiceComponent *component;
|
||
NiceCandidate *candidate;
|
||
|
||
+ if (transport == NICE_CANDIDATE_TRANSPORT_UDP &&
|
||
+ !agent->use_ice_udp)
|
||
+ return FALSE;
|
||
+ if (transport != NICE_CANDIDATE_TRANSPORT_UDP &&
|
||
+ !agent->use_ice_tcp)
|
||
+ return FALSE;
|
||
+
|
||
if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
|
||
return FALSE;
|
||
|
||
@@ -3195,6 +3405,19 @@ static gboolean priv_add_remote_candidate (
|
||
username, password, priority);
|
||
}
|
||
|
||
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
|
||
+ /* note: If there are TCP candidates for a media stream,
|
||
+ * a controlling agent MUST use the regular selection algorithm,
|
||
+ * RFC 6544, sect 8, "Concluding ICE Processing"
|
||
+ */
|
||
+ if (agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE &&
|
||
+ transport != NICE_CANDIDATE_TRANSPORT_UDP) {
|
||
+ nice_debug ("Agent %p : we have TCP candidates, switching back "
|
||
+ "to regular nomination mode", agent);
|
||
+ agent->nomination_mode = NICE_NOMINATION_MODE_REGULAR;
|
||
+ }
|
||
+ }
|
||
+
|
||
if (base_addr)
|
||
candidate->base_addr = *base_addr;
|
||
|
||
@@ -3240,6 +3463,8 @@ nice_agent_set_remote_credentials (
|
||
g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG);
|
||
g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD);
|
||
|
||
+ conn_check_remote_credentials_set(agent, stream);
|
||
+
|
||
ret = TRUE;
|
||
goto done;
|
||
}
|
||
@@ -3342,8 +3567,6 @@ _set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream,
|
||
}
|
||
}
|
||
|
||
- conn_check_remote_candidates_set(agent, stream, component);
|
||
-
|
||
if (added > 0) {
|
||
conn_check_schedule_next (agent);
|
||
}
|
||
@@ -3423,7 +3646,8 @@ agent_recv_message_unlocked (
|
||
{
|
||
NiceAddress from;
|
||
GList *item;
|
||
- gint retval;
|
||
+ RecvStatus retval;
|
||
+ gint sockret;
|
||
gboolean is_turn = FALSE;
|
||
|
||
/* We need an address for packet parsing, below. */
|
||
@@ -3484,8 +3708,8 @@ agent_recv_message_unlocked (
|
||
local_bufs[i + 1].buffer = message->buffers[i].buffer;
|
||
local_bufs[i + 1].size = message->buffers[i].size;
|
||
}
|
||
- retval = nice_socket_recv_messages (nicesock, &local_message, 1);
|
||
- if (retval == 1) {
|
||
+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1);
|
||
+ if (sockret == 1) {
|
||
message->length = ntohs (rfc4571_frame);
|
||
}
|
||
} else {
|
||
@@ -3500,7 +3724,7 @@ agent_recv_message_unlocked (
|
||
_priv_set_socket_tos (agent, new_socket, stream->tos);
|
||
nice_component_attach_socket (component, new_socket);
|
||
}
|
||
- retval = 0;
|
||
+ sockret = 0;
|
||
} else {
|
||
/* In the case of a real ICE-TCP connection, we can use the socket as a
|
||
* bytestream and do the read here with caching of data being read
|
||
@@ -3509,9 +3733,9 @@ agent_recv_message_unlocked (
|
||
|
||
/* TODO: Support bytestream reads */
|
||
message->length = 0;
|
||
- retval = 0;
|
||
+ sockret = 0;
|
||
if (available <= 0) {
|
||
- retval = available;
|
||
+ sockret = available;
|
||
|
||
/* If we don't call check_connect_result on an outbound connection,
|
||
* then is_connected will always return FALSE. That's why we check
|
||
@@ -3524,7 +3748,7 @@ agent_recv_message_unlocked (
|
||
* not connected, it means that it failed to connect, so we must
|
||
* return an error to make the socket fail/closed
|
||
*/
|
||
- retval = -1;
|
||
+ sockret = -1;
|
||
} else {
|
||
gint flags = G_SOCKET_MSG_PEEK;
|
||
|
||
@@ -3537,7 +3761,7 @@ agent_recv_message_unlocked (
|
||
*/
|
||
if (g_socket_receive_message (nicesock->fileno, NULL,
|
||
NULL, 0, NULL, NULL, &flags, NULL, NULL) == 0)
|
||
- retval = -1;
|
||
+ sockret = -1;
|
||
}
|
||
} else if (agent->rfc4571_expecting_length == 0) {
|
||
if ((gsize) available >= sizeof(guint16)) {
|
||
@@ -3545,8 +3769,8 @@ agent_recv_message_unlocked (
|
||
GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)};
|
||
NiceInputMessage local_message = { &local_buf, 1, message->from, 0};
|
||
|
||
- retval = nice_socket_recv_messages (nicesock, &local_message, 1);
|
||
- if (retval == 1) {
|
||
+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1);
|
||
+ if (sockret == 1) {
|
||
agent->rfc4571_expecting_length = ntohs (rfc4571_frame);
|
||
available = g_socket_get_available_bytes (nicesock->fileno);
|
||
}
|
||
@@ -3590,8 +3814,8 @@ agent_recv_message_unlocked (
|
||
off += local_bufs[i].size;
|
||
}
|
||
}
|
||
- retval = nice_socket_recv_messages (nicesock, &local_message, 1);
|
||
- if (retval == 1) {
|
||
+ sockret = nice_socket_recv_messages (nicesock, &local_message, 1);
|
||
+ if (sockret == 1) {
|
||
message->length = local_message.length;
|
||
agent->rfc4571_expecting_length -= local_message.length;
|
||
}
|
||
@@ -3599,23 +3823,26 @@ agent_recv_message_unlocked (
|
||
}
|
||
}
|
||
} else {
|
||
- retval = nice_socket_recv_messages (nicesock, message, 1);
|
||
+ sockret = nice_socket_recv_messages (nicesock, message, 1);
|
||
}
|
||
|
||
- if (retval == 0) {
|
||
+ if (sockret == 0) {
|
||
retval = RECV_WOULD_BLOCK; /* EWOULDBLOCK */
|
||
nice_debug_verbose ("%s: Agent %p: no message available on read attempt",
|
||
G_STRFUNC, agent);
|
||
goto done;
|
||
- } else if (retval < 0) {
|
||
+ } else if (sockret < 0) {
|
||
nice_debug ("Agent %p: %s returned %d, errno (%d) : %s",
|
||
- agent, G_STRFUNC, retval, errno, g_strerror (errno));
|
||
+ agent, G_STRFUNC, sockret, errno, g_strerror (errno));
|
||
|
||
retval = RECV_ERROR;
|
||
goto done;
|
||
+ } else {
|
||
+ retval = sockret;
|
||
}
|
||
|
||
- if (retval == RECV_OOB || message->length == 0) {
|
||
+ g_assert (retval != RECV_OOB);
|
||
+ if (message->length == 0) {
|
||
retval = RECV_OOB;
|
||
nice_debug_verbose ("%s: Agent %p: message handled out-of-band", G_STRFUNC,
|
||
agent);
|
||
@@ -3679,8 +3906,6 @@ agent_recv_message_unlocked (
|
||
if (retval == RECV_OOB)
|
||
goto done;
|
||
|
||
- agent->media_after_tick = TRUE;
|
||
-
|
||
/* If the message’s stated length is equal to its actual length, it’s probably
|
||
* a STUN message; otherwise it’s probably data. */
|
||
if (stun_message_validate_buffer_length_fast (
|
||
@@ -3712,6 +3937,7 @@ agent_recv_message_unlocked (
|
||
nice_debug ("%s: Valid STUN packet received.", G_STRFUNC);
|
||
retval = RECV_OOB;
|
||
g_free (big_buf);
|
||
+ agent->media_after_tick = TRUE;
|
||
goto done;
|
||
}
|
||
}
|
||
@@ -3722,6 +3948,23 @@ agent_recv_message_unlocked (
|
||
g_free (big_buf);
|
||
}
|
||
|
||
+ if (!nice_component_verify_remote_candidate (component,
|
||
+ message->from, nicesock)) {
|
||
+ if (nice_debug_is_verbose ()) {
|
||
+ gchar str[INET6_ADDRSTRLEN];
|
||
+
|
||
+ nice_address_to_string (message->from, str);
|
||
+ nice_debug_verbose ("Agent %p : %d:%d DROPPING packet from unknown source"
|
||
+ " %s:%d sock-type: %d", agent, stream->id, component->id, str,
|
||
+ nice_address_get_port (message->from), nicesock->type);
|
||
+ }
|
||
+
|
||
+ retval = RECV_OOB;
|
||
+ goto done;
|
||
+ }
|
||
+
|
||
+ agent->media_after_tick = TRUE;
|
||
+
|
||
/* Unhandled STUN; try handling TCP data, then pass to the client. */
|
||
if (message->length > 0 && agent->reliable) {
|
||
if (!nice_socket_is_reliable (nicesock) &&
|
||
@@ -4085,7 +4328,10 @@ static gboolean
|
||
nice_agent_recv_cancelled_cb (GCancellable *cancellable, gpointer user_data)
|
||
{
|
||
GError **error = user_data;
|
||
- return !g_cancellable_set_error_if_cancelled (cancellable, error);
|
||
+
|
||
+ if (error && !*error)
|
||
+ g_cancellable_set_error_if_cancelled (cancellable, error);
|
||
+ return G_SOURCE_REMOVE;
|
||
}
|
||
|
||
static gint
|
||
@@ -4245,7 +4491,6 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent,
|
||
"Component removed during call.");
|
||
|
||
component = NULL;
|
||
- error_reported = TRUE;
|
||
|
||
goto recv_error;
|
||
}
|
||
@@ -4734,6 +4979,11 @@ nice_agent_restart (
|
||
/* step: regenerate tie-breaker value */
|
||
priv_generate_tie_breaker (agent);
|
||
|
||
+ /* step: reset controlling mode from the property value */
|
||
+ agent->controlling_mode = agent->saved_controlling_mode;
|
||
+ nice_debug ("Agent %p : ICE restart, reset role to \"%s\".",
|
||
+ agent, agent->controlling_mode ? "controlling" : "controlled");
|
||
+
|
||
for (i = agent->streams; i; i = i->next) {
|
||
NiceStream *stream = i->data;
|
||
|
||
@@ -5665,7 +5915,7 @@ _generate_candidate_sdp (NiceAgent *agent,
|
||
g_string_append_printf (sdp, " typ %s", _cand_type_to_sdp (candidate->type));
|
||
if (nice_address_is_valid (&candidate->base_addr) &&
|
||
!nice_address_equal (&candidate->addr, &candidate->base_addr)) {
|
||
- port = nice_address_get_port (&candidate->addr);
|
||
+ port = nice_address_get_port (&candidate->base_addr);
|
||
nice_address_to_string (&candidate->base_addr, ip4);
|
||
g_string_append_printf (sdp, " raddr %s rport %d", ip4,
|
||
port == 0 ? 9 : port);
|
||
diff --git a/agent/agent.h b/agent/agent.h
|
||
index 47c4d5a..520c4c5 100644
|
||
--- a/agent/agent.h
|
||
+++ b/agent/agent.h
|
||
@@ -377,6 +377,45 @@ typedef enum
|
||
NICE_PROXY_TYPE_LAST = NICE_PROXY_TYPE_HTTP,
|
||
} NiceProxyType;
|
||
|
||
+/**
|
||
+ * NiceNominationMode:
|
||
+ * @NICE_NOMINATION_MODE_AGGRESSIVE: Aggressive nomination mode
|
||
+ * @NICE_NOMINATION_MODE_REGULAR: Regular nomination mode
|
||
+ *
|
||
+ * An enum to specity the kind of nomination mode to use by
|
||
+ * the agent, as described in RFC 5245. Two modes exists,
|
||
+ * regular and aggressive. They differ by the way the controlling
|
||
+ * agent chooses to put the USE-CANDIDATE attribute in its STUN
|
||
+ * messages. The aggressive mode is supposed to nominate a pair
|
||
+ * faster, than the regular mode, potentially causing the nominated
|
||
+ * pair to change until the connection check completes.
|
||
+ *
|
||
+ * Since: 0.1.15
|
||
+ */
|
||
+typedef enum
|
||
+{
|
||
+ NICE_NOMINATION_MODE_REGULAR = 0,
|
||
+ NICE_NOMINATION_MODE_AGGRESSIVE,
|
||
+} NiceNominationMode;
|
||
+
|
||
+/**
|
||
+ * NiceAgentOption:
|
||
+ * @NICE_AGENT_OPTION_REGULAR_NOMINATION: Enables regular nomination, default
|
||
+ * is aggrssive mode (see #NiceNominationMode).
|
||
+ * @NICE_AGENT_OPTION_RELIABLE: Enables reliable mode, possibly using PseudoTCP, * see nice_agent_new_reliable().
|
||
+ * @NICE_AGENT_OPTION_LITE_MODE: Enable lite mode
|
||
+ *
|
||
+ * 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
|
||
+ * the other value.
|
||
+ *
|
||
+ * Since: 0.1.15
|
||
+ */
|
||
+typedef enum {
|
||
+ NICE_AGENT_OPTION_REGULAR_NOMINATION = 1 << 0,
|
||
+ NICE_AGENT_OPTION_RELIABLE = 1 << 1,
|
||
+ NICE_AGENT_OPTION_LITE_MODE = 1 << 2,
|
||
+} NiceAgentOption;
|
||
|
||
/**
|
||
* NiceAgentRecvFunc:
|
||
@@ -428,6 +467,26 @@ nice_agent_new (GMainContext *ctx, NiceCompatibility compat);
|
||
NiceAgent *
|
||
nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat);
|
||
|
||
+/**
|
||
+ * nice_agent_new_full:
|
||
+ * @ctx: The Glib Mainloop Context to use for timers
|
||
+ * @compat: The compatibility mode of the agent
|
||
+ * @flags: Flags to set the properties
|
||
+ *
|
||
+ * Create a new #NiceAgent with parameters that must be be defined at
|
||
+ * construction time.
|
||
+ * The returned object must be freed with g_object_unref()
|
||
+ * <para> See also: #NiceNominationMode and #NiceAgentOption</para>
|
||
+ *
|
||
+ * Since: 0.1.15
|
||
+ *
|
||
+ * Returns: The new agent GObject
|
||
+ */
|
||
+NiceAgent *
|
||
+nice_agent_new_full (GMainContext *ctx,
|
||
+ NiceCompatibility compat,
|
||
+ NiceAgentOption flags);
|
||
+
|
||
/**
|
||
* nice_agent_add_local_address:
|
||
* @agent: The #NiceAgent Object
|
||
@@ -447,7 +506,6 @@ nice_agent_new_reliable (GMainContext *ctx, NiceCompatibility compat);
|
||
gboolean
|
||
nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr);
|
||
|
||
-
|
||
/**
|
||
* nice_agent_add_stream:
|
||
* @agent: The #NiceAgent Object
|
||
diff --git a/agent/candidate.c b/agent/candidate.c
|
||
index 27966ef..85f8f65 100644
|
||
--- a/agent/candidate.c
|
||
+++ b/agent/candidate.c
|
||
@@ -360,3 +360,14 @@ nice_candidate_copy (const NiceCandidate *candidate)
|
||
|
||
return copy;
|
||
}
|
||
+
|
||
+NICEAPI_EXPORT gboolean
|
||
+nice_candidate_equal_target (const NiceCandidate *candidate1,
|
||
+ const NiceCandidate *candidate2)
|
||
+{
|
||
+ g_return_val_if_fail (candidate1 != NULL, FALSE);
|
||
+ g_return_val_if_fail (candidate2 != NULL, FALSE);
|
||
+
|
||
+ return (candidate1->transport == candidate2->transport &&
|
||
+ nice_address_equal (&candidate1->addr, &candidate2->addr));
|
||
+}
|
||
diff --git a/agent/candidate.h b/agent/candidate.h
|
||
index fadfce3..e556c16 100644
|
||
--- a/agent/candidate.h
|
||
+++ b/agent/candidate.h
|
||
@@ -230,7 +230,23 @@ nice_candidate_free (NiceCandidate *candidate);
|
||
NiceCandidate *
|
||
nice_candidate_copy (const NiceCandidate *candidate);
|
||
|
||
-GType nice_candidate_get_type (void);
|
||
+/**
|
||
+ * nice_candidate_equal_target:
|
||
+ * @candidate1: A candidate
|
||
+ * @candidate2: A candidate
|
||
+ *
|
||
+ * Verifies that the candidates point to the same place, meaning they have
|
||
+ * the same transport and the same address. It ignores all other aspects.
|
||
+ *
|
||
+ * Returns: %TRUE if the candidates point to the same place
|
||
+ *
|
||
+ * Since: 0.1.15
|
||
+ */
|
||
+gboolean
|
||
+nice_candidate_equal_target (const NiceCandidate *candidate1,
|
||
+ const NiceCandidate *candidate2);
|
||
+
|
||
+ GType nice_candidate_get_type (void);
|
||
|
||
/**
|
||
* NICE_TYPE_CANDIDATE:
|
||
diff --git a/agent/component.c b/agent/component.c
|
||
index 32f7463..6eee90e 100644
|
||
--- a/agent/component.c
|
||
+++ b/agent/component.c
|
||
@@ -105,6 +105,13 @@ socket_source_attach (SocketSource *socket_source, GMainContext *context)
|
||
if (socket_source->socket->fileno == NULL)
|
||
return;
|
||
|
||
+ /* Do not create a GSource for UDP turn socket, because it
|
||
+ * would duplicate the packets already received on the base
|
||
+ * UDP socket.
|
||
+ */
|
||
+ if (socket_source->socket->type == NICE_SOCKET_TYPE_UDP_TURN)
|
||
+ return;
|
||
+
|
||
/* Create a source. */
|
||
source = g_socket_create_source (socket_source->socket->fileno,
|
||
G_IO_IN, NULL);
|
||
@@ -380,7 +387,7 @@ nice_component_restart (NiceComponent *cmp)
|
||
for (i = cmp->remote_candidates; i; i = i->next) {
|
||
NiceCandidate *candidate = i->data;
|
||
|
||
- /* note: do not remove the local candidate that is
|
||
+ /* note: do not remove the remote candidate that is
|
||
* currently part of the 'selected pair', see ICE
|
||
* 9.1.1.1. "ICE Restarts" (ID-19) */
|
||
if (candidate == cmp->selected_pair.remote) {
|
||
@@ -435,6 +442,8 @@ nice_component_update_selected_pair (NiceComponent *component, const CandidatePa
|
||
component->selected_pair.remote = pair->remote;
|
||
component->selected_pair.priority = pair->priority;
|
||
component->selected_pair.prflx_priority = pair->prflx_priority;
|
||
+
|
||
+ nice_component_add_valid_candidate (component, pair->remote);
|
||
}
|
||
|
||
/*
|
||
@@ -514,6 +523,11 @@ nice_component_set_selected_remote_candidate (NiceComponent *component,
|
||
component->selected_pair.remote = remote;
|
||
component->selected_pair.priority = priority;
|
||
|
||
+ /* 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.
|
||
+ */
|
||
+ component->fallback_mode = TRUE;
|
||
+
|
||
return local;
|
||
}
|
||
|
||
@@ -991,6 +1005,18 @@ nice_component_class_init (NiceComponentClass *klass)
|
||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||
}
|
||
|
||
+static gboolean
|
||
+dummy_callback (gpointer data)
|
||
+{
|
||
+ return G_SOURCE_CONTINUE;
|
||
+}
|
||
+
|
||
+static void
|
||
+source_set_dummy_callback (GSource *source)
|
||
+{
|
||
+ g_source_set_callback (source, dummy_callback, NULL, NULL);
|
||
+}
|
||
+
|
||
static void
|
||
nice_component_init (NiceComponent *component)
|
||
{
|
||
@@ -1013,7 +1039,7 @@ nice_component_init (NiceComponent *component)
|
||
component->stop_cancellable = g_cancellable_new ();
|
||
component->stop_cancellable_source =
|
||
g_cancellable_source_new (component->stop_cancellable);
|
||
- g_source_set_dummy_callback (component->stop_cancellable_source);
|
||
+ source_set_dummy_callback (component->stop_cancellable_source);
|
||
g_source_attach (component->stop_cancellable_source, component->own_ctx);
|
||
component->ctx = g_main_context_ref (component->own_ctx);
|
||
|
||
@@ -1107,6 +1133,9 @@ nice_component_finalize (GObject *obj)
|
||
g_warn_if_fail (cmp->remote_candidates == NULL);
|
||
g_warn_if_fail (cmp->incoming_checks == NULL);
|
||
|
||
+ g_list_free_full (cmp->valid_candidates,
|
||
+ (GDestroyNotify) nice_candidate_free);
|
||
+
|
||
g_clear_object (&cmp->tcp);
|
||
g_clear_object (&cmp->stop_cancellable);
|
||
g_clear_object (&cmp->iostream);
|
||
@@ -1225,7 +1254,7 @@ component_source_prepare (GSource *source, gint *timeout_)
|
||
child_socket_source->source =
|
||
g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN,
|
||
NULL);
|
||
- g_source_set_dummy_callback (child_socket_source->source);
|
||
+ source_set_dummy_callback (child_socket_source->source);
|
||
g_source_add_child_source (source, child_socket_source->source);
|
||
g_source_unref (child_socket_source->source);
|
||
component_source->socket_sources =
|
||
@@ -1370,7 +1399,7 @@ nice_component_input_source_new (NiceAgent *agent, guint stream_id,
|
||
GSource *cancellable_source;
|
||
|
||
cancellable_source = g_cancellable_source_new (cancellable);
|
||
- g_source_set_dummy_callback (cancellable_source);
|
||
+ source_set_dummy_callback (cancellable_source);
|
||
g_source_add_child_source ((GSource *) component_source,
|
||
cancellable_source);
|
||
g_source_unref (cancellable_source);
|
||
@@ -1421,3 +1450,83 @@ turn_server_unref (TurnServer *turn)
|
||
g_slice_free (TurnServer, turn);
|
||
}
|
||
}
|
||
+
|
||
+void
|
||
+nice_component_add_valid_candidate (NiceComponent *component,
|
||
+ const NiceCandidate *candidate)
|
||
+{
|
||
+ guint count = 0;
|
||
+ GList *item, *last = NULL;
|
||
+
|
||
+ for (item = component->valid_candidates; item; item = item->next) {
|
||
+ NiceCandidate *cand = item->data;
|
||
+
|
||
+ last = item;
|
||
+ count++;
|
||
+ if (nice_candidate_equal_target (cand, candidate))
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* New candidate */
|
||
+
|
||
+ if (nice_debug_is_enabled ()) {
|
||
+ char str[INET6_ADDRSTRLEN];
|
||
+ nice_address_to_string (&candidate->addr, str);
|
||
+ nice_debug ("Agent %p : %d:%d Adding valid source"
|
||
+ " candidate: %s:%d trans: %d", component->agent,
|
||
+ candidate->stream_id, candidate->component_id, str,
|
||
+ nice_address_get_port (&candidate->addr), candidate->transport);
|
||
+ }
|
||
+
|
||
+ component->valid_candidates = g_list_prepend (
|
||
+ component->valid_candidates, nice_candidate_copy (candidate));
|
||
+
|
||
+ /* Delete the last one to make sure we don't have a list that is too long,
|
||
+ * the candidates are not freed on ICE restart as this would be more complex,
|
||
+ * we just keep the list not too long.
|
||
+ */
|
||
+ if (count > NICE_COMPONENT_MAX_VALID_CANDIDATES) {
|
||
+ NiceCandidate *cand = last->data;
|
||
+
|
||
+ component->valid_candidates = g_list_delete_link (
|
||
+ component->valid_candidates, last);
|
||
+ nice_candidate_free (cand);
|
||
+ }
|
||
+}
|
||
+
|
||
+gboolean
|
||
+nice_component_verify_remote_candidate (NiceComponent *component,
|
||
+ const NiceAddress *address, NiceSocket *nicesock)
|
||
+{
|
||
+ GList *item;
|
||
+
|
||
+ if (component->fallback_mode)
|
||
+ return TRUE;
|
||
+
|
||
+ for (item = component->valid_candidates; item; item = item->next) {
|
||
+ NiceCandidate *cand = item->data;
|
||
+
|
||
+ if (((nicesock->type == NICE_SOCKET_TYPE_TCP_BSD &&
|
||
+ (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE ||
|
||
+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE ||
|
||
+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_SO)) ||
|
||
+ cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) &&
|
||
+ nice_address_equal (address, &cand->addr)) {
|
||
+ /* fast return if it's already the first */
|
||
+ if (item == component->valid_candidates)
|
||
+ return TRUE;
|
||
+
|
||
+ /* Put the current candidate at the top so that in the normal use-case,
|
||
+ * this function becomes O(1).
|
||
+ */
|
||
+ component->valid_candidates = g_list_remove_link (
|
||
+ component->valid_candidates, item);
|
||
+ component->valid_candidates = g_list_concat (item,
|
||
+ component->valid_candidates);
|
||
+
|
||
+ return TRUE;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return FALSE;
|
||
+}
|
||
diff --git a/agent/component.h b/agent/component.h
|
||
index 6712794..a8a1222 100644
|
||
--- a/agent/component.h
|
||
+++ b/agent/component.h
|
||
@@ -159,12 +159,14 @@ struct _NiceComponent {
|
||
NiceComponentState state;
|
||
GSList *local_candidates; /* list of NiceCandidate objs */
|
||
GSList *remote_candidates; /* list of NiceCandidate objs */
|
||
+ GList *valid_candidates; /* list of owned remote NiceCandidates that are part of valid pairs */
|
||
GSList *socket_sources; /* list of SocketSource objs; must only grow monotonically */
|
||
guint socket_sources_age; /* incremented when socket_sources changes */
|
||
GSList *incoming_checks; /* list of IncomingCheck objs */
|
||
GList *turn_servers; /* List of TurnServer objs */
|
||
CandidatePair selected_pair; /* independent from checklists,
|
||
see ICE 11.1. "Sending Media" (ID-19) */
|
||
+ gboolean fallback_mode; /* in this case, accepts packets from all, ignore candidate validation */
|
||
NiceCandidate *restart_candidate; /* for storing active remote candidate during a restart */
|
||
NiceCandidate *turn_candidate; /* for storing active turn candidate if turn servers have been cleared */
|
||
/* I/O handling. The main context must always be non-NULL, and is used for all
|
||
@@ -301,6 +303,14 @@ turn_server_ref (TurnServer *turn);
|
||
void
|
||
turn_server_unref (TurnServer *turn);
|
||
|
||
+void
|
||
+nice_component_add_valid_candidate (NiceComponent *component,
|
||
+ const NiceCandidate *candidate);
|
||
+
|
||
+gboolean
|
||
+nice_component_verify_remote_candidate (NiceComponent *component,
|
||
+ const NiceAddress *address, NiceSocket *nicesock);
|
||
+
|
||
|
||
G_END_DECLS
|
||
|
||
diff --git a/agent/conncheck.c b/agent/conncheck.c
|
||
index dda2f2f..c8a4edf 100644
|
||
--- a/agent/conncheck.c
|
||
+++ b/agent/conncheck.c
|
||
@@ -64,8 +64,8 @@
|
||
|
||
static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream);
|
||
static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component);
|
||
-static guint priv_prune_pending_checks (NiceStream *stream, guint component_id);
|
||
-static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate);
|
||
+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id);
|
||
+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand);
|
||
static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand);
|
||
static size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
|
||
guint component_id, NiceCandidate *remote, NiceCandidate *local,
|
||
@@ -76,6 +76,8 @@ static void conn_check_free_item (gpointer data);
|
||
static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
|
||
NiceAgent *agent, guint stream_id, NiceComponent *component,
|
||
NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state);
|
||
+static gboolean priv_update_selected_pair (NiceAgent *agent,
|
||
+ NiceComponent *component, CandidateCheckPair *pair);
|
||
|
||
static int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
|
||
{
|
||
@@ -84,6 +86,17 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
|
||
now->tv_sec >= timer->tv_sec;
|
||
}
|
||
|
||
+static unsigned int priv_timer_remainder (GTimeVal *timer, GTimeVal *now)
|
||
+{
|
||
+ unsigned int delay;
|
||
+ if (now->tv_sec > timer->tv_sec ||
|
||
+ (now->tv_sec == timer->tv_sec && now->tv_usec > timer->tv_usec))
|
||
+ return 0;
|
||
+ delay = (timer->tv_sec - now->tv_sec) * 1000;
|
||
+ delay += ((signed long)(timer->tv_usec - now->tv_usec)) / 1000;
|
||
+ return delay;
|
||
+}
|
||
+
|
||
static gchar
|
||
priv_state_to_gchar (NiceCheckState state)
|
||
{
|
||
@@ -98,8 +111,6 @@ priv_state_to_gchar (NiceCheckState state)
|
||
return 'F';
|
||
case NICE_CHECK_FROZEN:
|
||
return 'Z';
|
||
- case NICE_CHECK_CANCELLED:
|
||
- return 'C';
|
||
case NICE_CHECK_DISCOVERED:
|
||
return 'D';
|
||
default:
|
||
@@ -107,6 +118,54 @@ priv_state_to_gchar (NiceCheckState state)
|
||
}
|
||
}
|
||
|
||
+static const gchar *
|
||
+priv_state_to_string (NiceCheckState state)
|
||
+{
|
||
+ switch (state) {
|
||
+ case NICE_CHECK_WAITING:
|
||
+ return "waiting";
|
||
+ case NICE_CHECK_IN_PROGRESS:
|
||
+ return "in progress";
|
||
+ case NICE_CHECK_SUCCEEDED:
|
||
+ return "succeeded";
|
||
+ case NICE_CHECK_FAILED:
|
||
+ return "failed";
|
||
+ case NICE_CHECK_FROZEN:
|
||
+ return "frozen";
|
||
+ case NICE_CHECK_DISCOVERED:
|
||
+ return "discovered";
|
||
+ default:
|
||
+ g_assert_not_reached ();
|
||
+ }
|
||
+}
|
||
+
|
||
+static const gchar *
|
||
+priv_ice_return_to_string (StunUsageIceReturn ice_return)
|
||
+{
|
||
+ switch (ice_return) {
|
||
+ case STUN_USAGE_ICE_RETURN_SUCCESS:
|
||
+ return "success";
|
||
+ case STUN_USAGE_ICE_RETURN_ERROR:
|
||
+ return "error";
|
||
+ case STUN_USAGE_ICE_RETURN_INVALID:
|
||
+ return "invalid";
|
||
+ case STUN_USAGE_ICE_RETURN_ROLE_CONFLICT:
|
||
+ return "role conflict";
|
||
+ case STUN_USAGE_ICE_RETURN_INVALID_REQUEST:
|
||
+ return "invalid request";
|
||
+ case STUN_USAGE_ICE_RETURN_INVALID_METHOD:
|
||
+ return "invalid method";
|
||
+ case STUN_USAGE_ICE_RETURN_MEMORY_ERROR:
|
||
+ return "memory error";
|
||
+ case STUN_USAGE_ICE_RETURN_INVALID_ADDRESS:
|
||
+ return "invalid address";
|
||
+ case STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS:
|
||
+ return "no mapped address";
|
||
+ default:
|
||
+ g_assert_not_reached ();
|
||
+ }
|
||
+}
|
||
+
|
||
static const gchar *
|
||
priv_candidate_type_to_string (NiceCandidateType type)
|
||
{
|
||
@@ -130,12 +189,15 @@ priv_candidate_type_to_string (NiceCandidateType type)
|
||
static void
|
||
priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail)
|
||
{
|
||
- GSList *i, *k;
|
||
- guint j;
|
||
+ GSList *i, *k, *l;
|
||
+ guint j, m;
|
||
+ GTimeVal now;
|
||
|
||
if (!nice_debug_is_verbose ())
|
||
return;
|
||
|
||
+ g_get_current_time (&now);
|
||
+
|
||
#define PRIORITY_LEN 32
|
||
|
||
nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)",
|
||
@@ -148,26 +210,34 @@ priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *
|
||
if (pair->component_id == j) {
|
||
gchar local_addr[INET6_ADDRSTRLEN];
|
||
gchar remote_addr[INET6_ADDRSTRLEN];
|
||
- StunTimer *timer = &pair->timer;
|
||
|
||
nice_address_to_string (&pair->local->addr, local_addr);
|
||
nice_address_to_string (&pair->remote->addr, remote_addr);
|
||
|
||
nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
|
||
- "f=%s t=%s:%s timer=%d/%d %d/%dms "
|
||
- "[%s]:%u > [%s]:%u state=%c%s%s%s",
|
||
+ "f=%s t=%s:%s [%s]:%u > [%s]:%u state=%c%s%s%s",
|
||
agent, pair->stream_id, pair->component_id, pair,
|
||
pair->foundation,
|
||
priv_candidate_type_to_string (pair->local->type),
|
||
priv_candidate_type_to_string (pair->remote->type),
|
||
- timer->retransmissions, timer->max_retransmissions,
|
||
- timer->delay - stun_timer_remainder (timer), timer->delay,
|
||
local_addr, nice_address_get_port (&pair->local->addr),
|
||
remote_addr, nice_address_get_port (&pair->remote->addr),
|
||
priv_state_to_gchar (pair->state),
|
||
pair->valid ? "V" : "",
|
||
pair->nominated ? "N" : "",
|
||
g_slist_find (agent->triggered_check_queue, pair) ? "T" : "");
|
||
+
|
||
+ for (l = pair->stun_transactions, m = 0; l; l = l->next, m++) {
|
||
+ StunTransaction *stun = l->data;
|
||
+ nice_debug ("Agent %p : *** sc=%d/%d : pair %p : "
|
||
+ "stun#=%d timer=%d/%d %d/%dms buf=%p %s",
|
||
+ agent, pair->stream_id, pair->component_id, pair, m,
|
||
+ stun->timer.retransmissions, stun->timer.max_retransmissions,
|
||
+ stun->timer.delay - priv_timer_remainder (&stun->next_tick, &now),
|
||
+ stun->timer.delay,
|
||
+ stun->message.buffer,
|
||
+ (m == 0 && pair->retransmit) ? "(R)" : "");
|
||
+ }
|
||
}
|
||
}
|
||
}
|
||
@@ -181,6 +251,8 @@ priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pa
|
||
{
|
||
g_assert (pair);
|
||
|
||
+ pair->state = NICE_CHECK_IN_PROGRESS;
|
||
+ nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair);
|
||
if (agent->triggered_check_queue == NULL ||
|
||
g_slist_find (agent->triggered_check_queue, pair) == NULL)
|
||
agent->triggered_check_queue = g_slist_append (agent->triggered_check_queue, pair);
|
||
@@ -209,6 +281,89 @@ priv_get_pair_from_triggered_check_queue (NiceAgent *agent)
|
||
return pair;
|
||
}
|
||
|
||
+/*
|
||
+ * Check if the conncheck list if Active according to
|
||
+ * ICE spec, 5.7.4 (Computing States)
|
||
+ *
|
||
+ * note: the ICE spec in unclear about that, but the check list should
|
||
+ * be considered active when there is at least a pair in Waiting state
|
||
+ * OR a pair in In-Progress state.
|
||
+ */
|
||
+static gboolean
|
||
+priv_is_checklist_active (NiceStream *stream)
|
||
+{
|
||
+ GSList *i;
|
||
+
|
||
+ for (i = stream->conncheck_list; i ; i = i->next) {
|
||
+ CandidateCheckPair *p = i->data;
|
||
+ if (p->state == NICE_CHECK_WAITING || p->state == NICE_CHECK_IN_PROGRESS)
|
||
+ return TRUE;
|
||
+ }
|
||
+ return FALSE;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Check if the conncheck list if Frozen according to
|
||
+ * ICE spec, 5.7.4 (Computing States)
|
||
+ */
|
||
+static gboolean
|
||
+priv_is_checklist_frozen (NiceStream *stream)
|
||
+{
|
||
+ GSList *i;
|
||
+
|
||
+ if (stream->conncheck_list == NULL)
|
||
+ return FALSE;
|
||
+
|
||
+ for (i = stream->conncheck_list; i ; i = i->next) {
|
||
+ CandidateCheckPair *p = i->data;
|
||
+ if (p->state != NICE_CHECK_FROZEN)
|
||
+ return FALSE;
|
||
+ }
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Check if all components of the stream have
|
||
+ * a valid pair (used for ICE spec, 7.1.3.2.3, point 2.)
|
||
+ */
|
||
+static gboolean
|
||
+priv_all_components_have_valid_pair (NiceStream *stream)
|
||
+{
|
||
+ guint i;
|
||
+ GSList *j;
|
||
+
|
||
+ for (i = 1; i <= stream->n_components; i++) {
|
||
+ for (j = stream->conncheck_list; j ; j = j->next) {
|
||
+ CandidateCheckPair *p = j->data;
|
||
+ if (p->component_id == i && p->valid)
|
||
+ break;
|
||
+ }
|
||
+ if (j == NULL)
|
||
+ return FALSE;
|
||
+ }
|
||
+ return TRUE;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Check if the foundation in parameter matches the foundation
|
||
+ * of a valid pair in the conncheck list [of stream] (used for ICE spec,
|
||
+ * 7.1.3.2.3, point 2.)
|
||
+ */
|
||
+static gboolean
|
||
+priv_foundation_matches_a_valid_pair (const gchar *foundation, NiceStream *stream)
|
||
+{
|
||
+ GSList *i;
|
||
+
|
||
+ for (i = stream->conncheck_list; i ; i = i->next) {
|
||
+ CandidateCheckPair *p = i->data;
|
||
+ if (p->valid &&
|
||
+ strncmp (p->foundation, foundation,
|
||
+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0)
|
||
+ return TRUE;
|
||
+ }
|
||
+ return FALSE;
|
||
+}
|
||
+
|
||
/*
|
||
* Finds the next connectivity check in WAITING state.
|
||
*/
|
||
@@ -218,7 +373,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
|
||
|
||
/* note: list is sorted in priority order to first waiting check has
|
||
* the highest priority */
|
||
-
|
||
for (i = conn_check_list; i ; i = i->next) {
|
||
CandidateCheckPair *p = i->data;
|
||
if (p->state == NICE_CHECK_WAITING)
|
||
@@ -228,6 +382,57 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
|
||
return NULL;
|
||
}
|
||
|
||
+/*
|
||
+ * Finds the next connectivity check in FROZEN state.
|
||
+ */
|
||
+static CandidateCheckPair *
|
||
+priv_conn_check_find_next_frozen (GSList *conn_check_list)
|
||
+{
|
||
+ GSList *i;
|
||
+
|
||
+ /* note: list is sorted in priority order to first frozen check has
|
||
+ * the highest priority */
|
||
+ for (i = conn_check_list; i ; i = i->next) {
|
||
+ CandidateCheckPair *p = i->data;
|
||
+ if (p->state == NICE_CHECK_FROZEN)
|
||
+ return p;
|
||
+ }
|
||
+
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Returns the number of active check lists of the agent
|
||
+ */
|
||
+static guint
|
||
+priv_number_of_active_check_lists (NiceAgent *agent)
|
||
+{
|
||
+ guint n = 0;
|
||
+ GSList *i;
|
||
+
|
||
+ for (i = agent->streams; i ; i = i->next)
|
||
+ if (priv_is_checklist_active (i->data))
|
||
+ n++;
|
||
+ return n;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Returns the first stream of the agent having a Frozen
|
||
+ * connection check list
|
||
+ */
|
||
+static NiceStream *
|
||
+priv_find_first_frozen_check_list (NiceAgent *agent)
|
||
+{
|
||
+ GSList *i;
|
||
+
|
||
+ for (i = agent->streams; i ; i = i->next) {
|
||
+ NiceStream *stream = i->data;
|
||
+ if (priv_is_checklist_frozen (stream))
|
||
+ return stream;
|
||
+ }
|
||
+ return NULL;
|
||
+}
|
||
+
|
||
/*
|
||
* Initiates a new connectivity check for a ICE candidate pair.
|
||
*
|
||
@@ -235,8 +440,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
|
||
*/
|
||
static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
|
||
{
|
||
- g_get_current_time (&pair->next_tick);
|
||
- g_time_val_add (&pair->next_tick, agent->timer_ta * 1000);
|
||
pair->state = NICE_CHECK_IN_PROGRESS;
|
||
nice_debug ("Agent %p : pair %p state IN_PROGRESS", agent, pair);
|
||
conn_check_send (agent, pair);
|
||
@@ -246,58 +449,55 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *
|
||
/*
|
||
* Unfreezes the next connectivity check in the list. Follows the
|
||
* algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
|
||
- * (ID-19), with some exceptions (see comments in code).
|
||
+ * (RFC5245)
|
||
*
|
||
* See also sect 7.1.2.2.3 (Updating Pair States), and
|
||
* priv_conn_check_unfreeze_related().
|
||
*
|
||
* @return TRUE on success, and FALSE if no frozen candidates were found.
|
||
*/
|
||
-static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
|
||
+static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, NiceStream *stream)
|
||
{
|
||
- CandidateCheckPair *pair = NULL;
|
||
GSList *i, *j;
|
||
-
|
||
- /* XXX: the unfreezing is implemented a bit differently than in the
|
||
- * current ICE spec, but should still be interoperate:
|
||
- * - checks are not grouped by foundation
|
||
- * - one frozen check is unfrozen (lowest component-id, highest
|
||
- * priority)
|
||
- */
|
||
+ GSList *found_list = NULL;
|
||
+ gboolean result = FALSE;
|
||
|
||
priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
|
||
|
||
- for (i = agent->streams; i; i = i->next) {
|
||
- NiceStream *stream = i->data;
|
||
- guint64 max_frozen_priority = 0;
|
||
+ for (i = stream->conncheck_list; i ; i = i->next) {
|
||
+ CandidateCheckPair *p1 = i->data;
|
||
+ CandidateCheckPair *pair = NULL;
|
||
+ guint lowest_component_id = stream->n_components + 1;
|
||
+ guint64 highest_priority = 0;
|
||
|
||
+ if (g_slist_find_custom (found_list, p1->foundation, (GCompareFunc)strcmp))
|
||
+ continue;
|
||
+ found_list = g_slist_prepend (found_list, p1->foundation);
|
||
|
||
for (j = stream->conncheck_list; j ; j = j->next) {
|
||
- CandidateCheckPair *p = j->data;
|
||
-
|
||
- /* XXX: the prio check could be removed as the pairs are sorted
|
||
- * already */
|
||
-
|
||
- if (p->state == NICE_CHECK_FROZEN) {
|
||
- if (p->priority > max_frozen_priority) {
|
||
- max_frozen_priority = p->priority;
|
||
- pair = p;
|
||
- }
|
||
+ CandidateCheckPair *p2 = i->data;
|
||
+ if (strncmp (p2->foundation, p1->foundation,
|
||
+ NICE_CANDIDATE_PAIR_MAX_FOUNDATION) == 0) {
|
||
+ if (p2->component_id < lowest_component_id ||
|
||
+ (p2->component_id == lowest_component_id &&
|
||
+ p2->priority > highest_priority)) {
|
||
+ pair = p2;
|
||
+ lowest_component_id = p2->component_id;
|
||
+ highest_priority = p2->priority;
|
||
+ }
|
||
}
|
||
}
|
||
|
||
- if (pair)
|
||
- break;
|
||
- }
|
||
-
|
||
- if (pair) {
|
||
- nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.", agent, pair, pair->stream_id, pair->component_id, pair->foundation);
|
||
- pair->state = NICE_CHECK_WAITING;
|
||
- nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
|
||
- return TRUE;
|
||
+ if (pair) {
|
||
+ nice_debug ("Agent %p : Pair %p with s/c-id %u/%u (%s) unfrozen.",
|
||
+ agent, pair, pair->stream_id, pair->component_id, pair->foundation);
|
||
+ pair->state = NICE_CHECK_WAITING;
|
||
+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
|
||
+ result = TRUE;
|
||
+ }
|
||
}
|
||
-
|
||
- return FALSE;
|
||
+ g_slist_free (found_list);
|
||
+ return result;
|
||
}
|
||
|
||
/*
|
||
@@ -314,7 +514,6 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
|
||
static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check)
|
||
{
|
||
GSList *i, *j;
|
||
- guint unfrozen = 0;
|
||
|
||
g_assert (ok_check);
|
||
g_assert (ok_check->state == NICE_CHECK_SUCCEEDED);
|
||
@@ -334,60 +533,147 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stre
|
||
nice_debug ("Agent %p : Unfreezing check %p (after successful check %p).", agent, p, ok_check);
|
||
p->state = NICE_CHECK_WAITING;
|
||
nice_debug ("Agent %p : pair %p state WAITING", agent, p);
|
||
- ++unfrozen;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* step: perform the step (2) of 'Updating Pair States' */
|
||
stream = agent_find_stream (agent, ok_check->stream_id);
|
||
- if (nice_stream_all_components_ready (stream)) {
|
||
- /* step: unfreeze checks from other streams */
|
||
+ if (priv_all_components_have_valid_pair (stream)) {
|
||
for (i = agent->streams; i ; i = i->next) {
|
||
+ /* the agent examines the check list for each other
|
||
+ * media stream in turn
|
||
+ */
|
||
NiceStream *s = i->data;
|
||
- for (j = stream->conncheck_list; j ; j = j->next) {
|
||
- CandidateCheckPair *p = j->data;
|
||
-
|
||
- if (p->stream_id == s->id &&
|
||
- p->stream_id != ok_check->stream_id) {
|
||
- if (p->state == NICE_CHECK_FROZEN &&
|
||
- strcmp (p->foundation, ok_check->foundation) == 0) {
|
||
+ if (s->id == ok_check->stream_id)
|
||
+ continue;
|
||
+ if (priv_is_checklist_active (s)) {
|
||
+ /* checklist is Active
|
||
+ */
|
||
+ for (j = s->conncheck_list; j ; j = j->next) {
|
||
+ CandidateCheckPair *p = j->data;
|
||
+ if (p->state == NICE_CHECK_FROZEN &&
|
||
+ priv_foundation_matches_a_valid_pair (p->foundation, stream)) {
|
||
nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check);
|
||
p->state = NICE_CHECK_WAITING;
|
||
nice_debug ("Agent %p : pair %p state WAITING", agent, p);
|
||
- ++unfrozen;
|
||
-
|
||
- }
|
||
- }
|
||
+ }
|
||
+ }
|
||
+ } else if (priv_is_checklist_frozen (s)) {
|
||
+ /* checklist is Frozen
|
||
+ */
|
||
+ gboolean match_found = FALSE;
|
||
+ /* check if there is one pair in the check list whose
|
||
+ * foundation matches a pair in the valid list under
|
||
+ * consideration
|
||
+ */
|
||
+ for (j = s->conncheck_list; j ; j = j->next) {
|
||
+ CandidateCheckPair *p = j->data;
|
||
+ if (priv_foundation_matches_a_valid_pair (p->foundation, stream)) {
|
||
+ match_found = TRUE;
|
||
+ nice_debug ("Agent %p : Unfreezing check %p from stream %u (after successful check %p).", agent, p, s->id, ok_check);
|
||
+ p->state = NICE_CHECK_WAITING;
|
||
+ nice_debug ("Agent %p : pair %p state WAITING", agent, p);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (!match_found) {
|
||
+ /* set the pair with the lowest component ID
|
||
+ * and highest priority to Waiting
|
||
+ */
|
||
+ priv_conn_check_unfreeze_next (agent, s);
|
||
+ }
|
||
}
|
||
- /* note: only unfreeze check from one stream at a time */
|
||
- if (unfrozen)
|
||
- break;
|
||
}
|
||
}
|
||
+}
|
||
|
||
- if (unfrozen == 0)
|
||
- priv_conn_check_unfreeze_next (agent);
|
||
+/*
|
||
+ * Create a new STUN transaction and add it to the list
|
||
+ * of ongoing stun transactions of a pair.
|
||
+ *
|
||
+ * @pair the pair the new stun transaction should be added to.
|
||
+ * @return the created stun transaction.
|
||
+ */
|
||
+static StunTransaction *
|
||
+priv_add_stun_transaction (CandidateCheckPair *pair)
|
||
+{
|
||
+ StunTransaction *stun = g_slice_new0 (StunTransaction);
|
||
+ pair->stun_transactions = g_slist_prepend (pair->stun_transactions, stun);
|
||
+ pair->retransmit = TRUE;
|
||
+ return stun;
|
||
}
|
||
|
||
+/*
|
||
+ * Forget a STUN transaction.
|
||
+ *
|
||
+ * @data the stun transaction to be forgotten.
|
||
+ * @user_data the component contained the concerned stun agent.
|
||
+ */
|
||
static void
|
||
-candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p)
|
||
+priv_forget_stun_transaction (gpointer data, gpointer user_data)
|
||
{
|
||
+ StunTransaction *stun = data;
|
||
+ NiceComponent *component = user_data;
|
||
StunTransactionId id;
|
||
+
|
||
+ if (stun->message.buffer != NULL) {
|
||
+ stun_message_id (&stun->message, id);
|
||
+ stun_agent_forget_transaction (&component->stun_agent, id);
|
||
+ }
|
||
+}
|
||
+
|
||
+static void
|
||
+priv_free_stun_transaction (gpointer data)
|
||
+{
|
||
+ g_slice_free (StunTransaction, data);
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Remove a STUN transaction from a pair, and forget it
|
||
+ * from the related component stun agent.
|
||
+ *
|
||
+ * @pair the pair the stun transaction should be removed from.
|
||
+ * @stun the stun transaction to be removed.
|
||
+ * @component the component containing the stun agent used to
|
||
+ * forget the stun transaction.
|
||
+ */
|
||
+static void
|
||
+priv_remove_stun_transaction (CandidateCheckPair *pair,
|
||
+ StunTransaction *stun, NiceComponent *component)
|
||
+{
|
||
+ priv_forget_stun_transaction (stun, component);
|
||
+ pair->stun_transactions = g_slist_remove (pair->stun_transactions, stun);
|
||
+ priv_free_stun_transaction (stun);
|
||
+}
|
||
+
|
||
+/*
|
||
+ * Remove all STUN transactions from a pair, and forget them
|
||
+ * from the related component stun agent.
|
||
+ *
|
||
+ * @pair the pair the stun list should be cleared.
|
||
+ * @component the component containing the stun agent used to
|
||
+ * forget the stun transactions.
|
||
+ */
|
||
+static void
|
||
+priv_free_all_stun_transactions (CandidateCheckPair *pair,
|
||
+ NiceComponent *component)
|
||
+{
|
||
+ if (component)
|
||
+ g_slist_foreach (pair->stun_transactions, priv_forget_stun_transaction, component);
|
||
+ g_slist_free_full (pair->stun_transactions, priv_free_stun_transaction);
|
||
+ pair->stun_transactions = NULL;
|
||
+}
|
||
+
|
||
+static void
|
||
+candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p)
|
||
+{
|
||
NiceComponent *component;
|
||
|
||
component = nice_stream_find_component_by_id (stream, p->component_id);
|
||
-
|
||
p->state = NICE_CHECK_FAILED;
|
||
nice_debug ("Agent %p : pair %p state FAILED", agent, p);
|
||
-
|
||
- if (p->stun_message.buffer != NULL) {
|
||
- stun_message_id (&p->stun_message, id);
|
||
- stun_agent_forget_transaction (&component->stun_agent, id);
|
||
- }
|
||
-
|
||
- p->stun_message.buffer = NULL;
|
||
- p->stun_message.buffer_len = 0;
|
||
+ priv_free_all_stun_transactions (p, component);
|
||
}
|
||
|
||
/*
|
||
@@ -398,78 +684,183 @@ candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckP
|
||
*
|
||
* @return will return FALSE when no more pending timers.
|
||
*/
|
||
-static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted)
|
||
+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent)
|
||
{
|
||
gboolean keep_timer_going = FALSE;
|
||
- guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0,
|
||
- s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0;
|
||
- guint frozen = 0, waiting = 0;
|
||
- GSList *i, *k;
|
||
+ GSList *i, *j;
|
||
+ CandidateCheckPair *pair;
|
||
+ unsigned int timeout;
|
||
+ GTimeVal now;
|
||
|
||
+ g_get_current_time (&now);
|
||
+
|
||
+ /* step: process ongoing STUN transactions */
|
||
for (i = stream->conncheck_list; i ; i = i->next) {
|
||
CandidateCheckPair *p = i->data;
|
||
+ gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN];
|
||
+ NiceComponent *component;
|
||
+ StunTransaction *stun;
|
||
|
||
- if (p->state == NICE_CHECK_IN_PROGRESS) {
|
||
- if (p->stun_message.buffer == NULL) {
|
||
- nice_debug ("Agent %p : STUN connectivity check was cancelled, marking as done.", agent);
|
||
- p->state = NICE_CHECK_FAILED;
|
||
- nice_debug ("Agent %p : pair %p state FAILED", agent, p);
|
||
- } else if (priv_timer_expired (&p->next_tick, now)) {
|
||
- switch (stun_timer_refresh (&p->timer)) {
|
||
- case STUN_USAGE_TIMER_RETURN_TIMEOUT:
|
||
- {
|
||
- /* case: error, abort processing */
|
||
- gchar tmpbuf1[INET6_ADDRSTRLEN], tmpbuf2[INET6_ADDRSTRLEN];
|
||
- nice_address_to_string (&p->local->addr, tmpbuf1);
|
||
- nice_address_to_string (&p->remote->addr, tmpbuf2);
|
||
- nice_debug ("Agent %p : Retransmissions failed, giving up on connectivity check %p", agent, p);
|
||
- nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent,
|
||
- tmpbuf1, nice_address_get_port (&p->local->addr),
|
||
- tmpbuf2, nice_address_get_port (&p->remote->addr));
|
||
- candidate_check_pair_fail (stream, agent, p);
|
||
- priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
- ", retransmission failed");
|
||
-
|
||
- break;
|
||
- }
|
||
- case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
|
||
- {
|
||
- /* case: not ready, so schedule a new timeout */
|
||
- unsigned int timeout = stun_timer_remainder (&p->timer);
|
||
- nice_debug ("Agent %p :STUN transaction retransmitted on pair %p "
|
||
- "(timeout %dms, delay=%dms, retrans=%d).",
|
||
- agent, p, timeout, p->timer.delay, p->timer.retransmissions);
|
||
+ if (p->stun_transactions == NULL)
|
||
+ continue;
|
||
|
||
- agent_socket_send (p->sockptr, &p->remote->addr,
|
||
- stun_message_length (&p->stun_message),
|
||
- (gchar *)p->stun_buffer);
|
||
+ if (!agent_find_component (agent, p->stream_id, p->component_id,
|
||
+ NULL, &component))
|
||
+ continue;
|
||
|
||
+ /* The first stun transaction of the list may eventually be
|
||
+ * retransmitted, other stun transactions just have their
|
||
+ * timer updated.
|
||
+ */
|
||
|
||
- /* note: convert from milli to microseconds for g_time_val_add() */
|
||
- p->next_tick = *now;
|
||
- g_time_val_add (&p->next_tick, timeout * 1000);
|
||
+ j = p->stun_transactions->next;
|
||
|
||
- *stun_transmitted = TRUE;
|
||
- return TRUE;
|
||
- }
|
||
- case STUN_USAGE_TIMER_RETURN_SUCCESS:
|
||
- {
|
||
- unsigned int timeout = stun_timer_remainder (&p->timer);
|
||
+ /* process all stun transactions except the first one */
|
||
+ while (j) {
|
||
+ StunTransaction *s = j->data;
|
||
+ GSList *next = j->next;
|
||
|
||
- /* note: convert from milli to microseconds for g_time_val_add() */
|
||
- p->next_tick = *now;
|
||
- g_time_val_add (&p->next_tick, timeout * 1000);
|
||
-
|
||
- keep_timer_going = TRUE;
|
||
- break;
|
||
- }
|
||
+ if (priv_timer_expired (&s->next_tick, &now))
|
||
+ switch (stun_timer_refresh (&s->timer)) {
|
||
+ case STUN_USAGE_TIMER_RETURN_TIMEOUT:
|
||
+ priv_remove_stun_transaction (p, s, component);
|
||
+ break;
|
||
+ case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
|
||
+ timeout = stun_timer_remainder (&s->timer);
|
||
+ s->next_tick = now;
|
||
+ g_time_val_add (&s->next_tick, timeout * 1000);
|
||
+ break;
|
||
default:
|
||
- /* Nothing to do. */
|
||
break;
|
||
- }
|
||
}
|
||
+ j = next;
|
||
}
|
||
|
||
+ if (p->state != NICE_CHECK_IN_PROGRESS)
|
||
+ continue;
|
||
+
|
||
+ /* process the first stun transaction of the list */
|
||
+ stun = p->stun_transactions->data;
|
||
+ if (!priv_timer_expired (&stun->next_tick, &now))
|
||
+ continue;
|
||
+
|
||
+ switch (stun_timer_refresh (&stun->timer)) {
|
||
+ case STUN_USAGE_TIMER_RETURN_TIMEOUT:
|
||
+timer_return_timeout:
|
||
+ /* case: error, abort processing */
|
||
+ nice_address_to_string (&p->local->addr, tmpbuf1);
|
||
+ nice_address_to_string (&p->remote->addr, tmpbuf2);
|
||
+ nice_debug ("Agent %p : Retransmissions failed, giving up on "
|
||
+ "connectivity check %p", agent, p);
|
||
+ nice_debug ("Agent %p : Failed pair is [%s]:%u --> [%s]:%u", agent,
|
||
+ tmpbuf1, nice_address_get_port (&p->local->addr),
|
||
+ tmpbuf2, nice_address_get_port (&p->remote->addr));
|
||
+ candidate_check_pair_fail (stream, agent, p);
|
||
+ priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
+ ", retransmission failed");
|
||
+
|
||
+ /* perform a check if a transition state from connected to
|
||
+ * ready can be performed. This may happen here, when the last
|
||
+ * in-progress pair has expired its retransmission count
|
||
+ * in priv_conn_check_tick_stream(), which is a condition to
|
||
+ * make the transition connected to ready.
|
||
+ */
|
||
+ priv_update_check_list_state_for_ready (agent, stream, component);
|
||
+ break;
|
||
+ case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
|
||
+ /* case: retransmission stopped, due to the nomination of
|
||
+ * a pair with a higher priority than this in-progress pair,
|
||
+ * ICE spec, sect 8.1.2 "Updating States", item 2.2
|
||
+ */
|
||
+ if (!p->retransmit)
|
||
+ goto timer_return_timeout;
|
||
+
|
||
+ /* case: not ready, so schedule a new timeout */
|
||
+ timeout = stun_timer_remainder (&stun->timer);
|
||
+
|
||
+ nice_debug ("Agent %p :STUN transaction retransmitted on pair %p "
|
||
+ "(timer=%d/%d %d/%dms).",
|
||
+ agent, p,
|
||
+ stun->timer.retransmissions, stun->timer.max_retransmissions,
|
||
+ stun->timer.delay - timeout, stun->timer.delay);
|
||
+
|
||
+ agent_socket_send (p->sockptr, &p->remote->addr,
|
||
+ stun_message_length (&stun->message),
|
||
+ (gchar *)stun->buffer);
|
||
+
|
||
+ /* note: convert from milli to microseconds for g_time_val_add() */
|
||
+ stun->next_tick = now;
|
||
+ g_time_val_add (&stun->next_tick, timeout * 1000);
|
||
+
|
||
+ return TRUE;
|
||
+ case STUN_USAGE_TIMER_RETURN_SUCCESS:
|
||
+ timeout = stun_timer_remainder (&stun->timer);
|
||
+
|
||
+ /* note: convert from milli to microseconds for g_time_val_add() */
|
||
+ stun->next_tick = now;
|
||
+ g_time_val_add (&stun->next_tick, timeout * 1000);
|
||
+
|
||
+ keep_timer_going = TRUE;
|
||
+ break;
|
||
+ default:
|
||
+ /* Nothing to do. */
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ /* step: perform an ordinary check, ICE spec, 5.8 "Scheduling Checks"
|
||
+ * note: This code is executed when the triggered checks list is
|
||
+ * empty, and when no STUN message has been sent (pacing constraint)
|
||
+ */
|
||
+ pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
|
||
+ if (pair) {
|
||
+ priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
+ ", got a pair in Waiting state");
|
||
+ priv_conn_check_initiate (agent, pair);
|
||
+ return TRUE;
|
||
+ }
|
||
+
|
||
+ /* note: this is unclear in the ICE spec, but a check list in Frozen
|
||
+ * state (where all pairs are in Frozen state) is not supposed to
|
||
+ * change its state by an ordinary check, but only by the success of
|
||
+ * checks in other check lists, in priv_conn_check_unfreeze_related().
|
||
+ * The underlying idea is to concentrate the checks on a single check
|
||
+ * list initially.
|
||
+ */
|
||
+ if (priv_is_checklist_frozen (stream))
|
||
+ return keep_timer_going;
|
||
+
|
||
+ /* step: ordinary check continued, if there's no pair in the waiting
|
||
+ * state, pick a pair in the frozen state
|
||
+ */
|
||
+ pair = priv_conn_check_find_next_frozen (stream->conncheck_list);
|
||
+ if (pair) {
|
||
+ priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
+ ", got a pair in Frozen state");
|
||
+ pair->state = NICE_CHECK_WAITING;
|
||
+ nice_debug ("Agent %p : pair %p state WAITING", agent, pair);
|
||
+ priv_conn_check_initiate (agent, pair);
|
||
+ return TRUE;
|
||
+ }
|
||
+ return keep_timer_going;
|
||
+}
|
||
+
|
||
+static gboolean
|
||
+priv_conn_check_tick_stream_nominate (NiceStream *stream, NiceAgent *agent)
|
||
+{
|
||
+ gboolean keep_timer_going = FALSE;
|
||
+ guint s_inprogress = 0;
|
||
+ guint s_succeeded = 0;
|
||
+ guint s_discovered = 0;
|
||
+ guint s_nominated = 0;
|
||
+ guint s_waiting_for_nomination = 0;
|
||
+ guint s_valid = 0;
|
||
+ guint frozen = 0;
|
||
+ guint waiting = 0;
|
||
+ GSList *i, *k;
|
||
+
|
||
+ for (i = stream->conncheck_list; i ; i = i->next) {
|
||
+ CandidateCheckPair *p = i->data;
|
||
if (p->state == NICE_CHECK_FROZEN)
|
||
++frozen;
|
||
else if (p->state == NICE_CHECK_IN_PROGRESS)
|
||
@@ -495,12 +886,102 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
|
||
if (s_inprogress)
|
||
keep_timer_going = TRUE;
|
||
|
||
- /* note: if some components have established connectivity,
|
||
- * but yet no nominated pair, keep timer going */
|
||
if (s_nominated < stream->n_components &&
|
||
s_waiting_for_nomination) {
|
||
- keep_timer_going = TRUE;
|
||
- if (agent->controlling_mode) {
|
||
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
|
||
+ if (agent->nomination_mode == NICE_NOMINATION_MODE_REGULAR &&
|
||
+ agent->controlling_mode) {
|
||
+#define NICE_MIN_NUMBER_OF_VALID_PAIRS 2
|
||
+ /* ICE 8.1.1.1 Regular nomination
|
||
+ * we choose to nominate the valid pair of a component if
|
||
+ * - there is no pair left frozen, waiting or in-progress, or
|
||
+ * - if there are at least two valid pairs, or
|
||
+ * - if there is at least one valid pair of type HOST-HOST
|
||
+ *
|
||
+ * This is the "stopping criterion" described in 8.1.1.1, and is
|
||
+ * a "local optimization" between accumulating more valid pairs,
|
||
+ * and limiting the time spent waiting for in-progress connections
|
||
+ * checks until they finally fail.
|
||
+ */
|
||
+ GSList *component_item;
|
||
+
|
||
+ for (component_item = stream->components; component_item;
|
||
+ component_item = component_item->next) {
|
||
+ NiceComponent *component = component_item->data;
|
||
+ gboolean already_done = FALSE;
|
||
+ gboolean stopping_criterion = FALSE;
|
||
+ guint p_valid = 0;
|
||
+ guint p_frozen = 0;
|
||
+ guint p_waiting = 0;
|
||
+ guint p_inprogress = 0;
|
||
+ guint p_host_host_valid = 0;
|
||
+
|
||
+ /* verify that the choice of the pair to be nominated
|
||
+ * has not already been done
|
||
+ */
|
||
+ for (k = stream->conncheck_list; k ; k = k->next) {
|
||
+ CandidateCheckPair *p = k->data;
|
||
+ if (p->component_id == component->id) {
|
||
+ if (p->use_candidate_on_next_check)
|
||
+ already_done = TRUE;
|
||
+ if (p->state == NICE_CHECK_FROZEN)
|
||
+ p_frozen++;
|
||
+ else if (p->state == NICE_CHECK_WAITING)
|
||
+ p_waiting++;
|
||
+ else if (p->state == NICE_CHECK_IN_PROGRESS)
|
||
+ p_inprogress++;
|
||
+ if (p->valid)
|
||
+ p_valid++;
|
||
+ if (p->valid &&
|
||
+ p->local->type == NICE_CANDIDATE_TYPE_HOST &&
|
||
+ p->remote->type == NICE_CANDIDATE_TYPE_HOST)
|
||
+ p_host_host_valid++;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (already_done)
|
||
+ continue;
|
||
+
|
||
+ stopping_criterion =
|
||
+ (p_host_host_valid > 0 ||
|
||
+ p_valid >= NICE_MIN_NUMBER_OF_VALID_PAIRS ||
|
||
+ (p_waiting == 0 && p_inprogress == 0 && p_frozen == 0));
|
||
+
|
||
+ if (!stopping_criterion)
|
||
+ continue;
|
||
+
|
||
+ /* when the stopping criterion is satisfied, we choose
|
||
+ * a pair to be nominated in the list of valid pairs,
|
||
+ * and add it to the triggered checks list
|
||
+ */
|
||
+ for (k = stream->conncheck_list; k ; k = k->next) {
|
||
+ CandidateCheckPair *p = k->data;
|
||
+ /* note: highest priority item selected (list always sorted) */
|
||
+ if (p->component_id == component->id &&
|
||
+ !p->nominated &&
|
||
+ !p->use_candidate_on_next_check &&
|
||
+ p->valid) {
|
||
+ /* According a ICE spec, sect 8.1.1.1. "Regular
|
||
+ * Nomination", we enqueue the check that produced this
|
||
+ * valid pair. When this pair has been discovered, we want
|
||
+ * to test its parent pair instead.
|
||
+ */
|
||
+ if (p->succeeded_pair != NULL) {
|
||
+ g_assert (p->state == NICE_CHECK_DISCOVERED);
|
||
+ p = p->succeeded_pair;
|
||
+ }
|
||
+ g_assert (p->state == NICE_CHECK_SUCCEEDED);
|
||
+ nice_debug ("Agent %p : restarting check %p with "
|
||
+ "USE-CANDIDATE attrib (regular nomination)", agent, p);
|
||
+ p->use_candidate_on_next_check = TRUE;
|
||
+ priv_add_pair_to_triggered_check_queue (agent, p);
|
||
+ keep_timer_going = TRUE;
|
||
+ break; /* move to the next component */
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ } else if (agent->controlling_mode) {
|
||
GSList *component_item;
|
||
|
||
for (component_item = stream->components; component_item;
|
||
@@ -515,7 +996,9 @@ static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agen
|
||
p->state == NICE_CHECK_DISCOVERED)) {
|
||
nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p);
|
||
p->nominated = TRUE;
|
||
+ priv_update_selected_pair (agent, component, p);
|
||
priv_add_pair_to_triggered_check_queue (agent, p);
|
||
+ keep_timer_going = TRUE;
|
||
break; /* move to the next component */
|
||
}
|
||
}
|
||
@@ -542,6 +1025,7 @@ conn_check_stop (NiceAgent *agent)
|
||
g_source_destroy (agent->conncheck_timer_source);
|
||
g_source_unref (agent->conncheck_timer_source);
|
||
agent->conncheck_timer_source = NULL;
|
||
+ agent->conncheck_timer_grace_period = 0;
|
||
}
|
||
|
||
|
||
@@ -557,75 +1041,83 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent)
|
||
{
|
||
CandidateCheckPair *pair = NULL;
|
||
gboolean keep_timer_going = FALSE;
|
||
- gboolean res;
|
||
- /* note: we try to only generate a single stun transaction per timer
|
||
- * callback, to respect some pacing of STUN transaction, as per
|
||
- * appendix B.1 of ICE spec.
|
||
- */
|
||
- gboolean stun_transmitted = FALSE;
|
||
GSList *i, *j;
|
||
- GTimeVal now;
|
||
|
||
- /* step: process ongoing STUN transactions */
|
||
- g_get_current_time (&now);
|
||
-
|
||
- for (j = agent->streams; j; j = j->next) {
|
||
- NiceStream *stream = j->data;
|
||
- res = priv_conn_check_tick_stream (stream, agent, &now, &stun_transmitted);
|
||
- if (res)
|
||
- keep_timer_going = res;
|
||
- if (stun_transmitted)
|
||
- return TRUE;
|
||
+ /* configure the initial state of the check lists of the agent
|
||
+ * as described in ICE spec, 5.7.4
|
||
+ *
|
||
+ * if all pairs in all check lists are in frozen state, then
|
||
+ * we are in the initial state (5.7.4, point 1.)
|
||
+ */
|
||
+ if (priv_number_of_active_check_lists (agent) == 0) {
|
||
+ /* set some pairs of the first stream in the waiting state
|
||
+ * ICE spec, 5.7.4, point 2.
|
||
+ *
|
||
+ * note: we adapt the ICE spec here, by selecting the first
|
||
+ * frozen check list, which is not necessarily the check
|
||
+ * list of the first stream (the first stream may be completed)
|
||
+ */
|
||
+ NiceStream *stream = priv_find_first_frozen_check_list (agent);
|
||
+ if (stream)
|
||
+ priv_conn_check_unfreeze_next (agent, stream);
|
||
}
|
||
|
||
- /* step: first initiate a conncheck with a pair from the triggered list */
|
||
+ /* step: perform a test from the triggered checks list,
|
||
+ * ICE spec, 5.8 "Scheduling Checks"
|
||
+ */
|
||
pair = priv_get_pair_from_triggered_check_queue (agent);
|
||
|
||
if (pair) {
|
||
priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
", got a pair from triggered check list");
|
||
- priv_conn_check_initiate (agent, pair);
|
||
+ conn_check_send (agent, pair);
|
||
return TRUE;
|
||
}
|
||
|
||
- /* step: when the triggered list is empty,
|
||
- * find the highest priority waiting check and send it */
|
||
+ /* step: process ongoing STUN transactions and
|
||
+ * perform an ordinary check, ICE spec, 5.8, "Scheduling Checks"
|
||
+ */
|
||
for (i = agent->streams; i ; i = i->next) {
|
||
NiceStream *stream = i->data;
|
||
-
|
||
- pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
|
||
- if (pair)
|
||
- break;
|
||
+ if (priv_conn_check_tick_stream (stream, agent))
|
||
+ keep_timer_going = TRUE;
|
||
+ if (priv_conn_check_tick_stream_nominate (stream, agent))
|
||
+ keep_timer_going = TRUE;
|
||
}
|
||
|
||
- if (pair) {
|
||
- priv_conn_check_initiate (agent, pair);
|
||
- return TRUE;
|
||
+ /* step: if no work left and a conncheck list of a stream is still
|
||
+ * frozen, set the pairs to waiting, according to ICE SPEC, sect
|
||
+ * 7.1.3.3. "Check List and Timer State Updates"
|
||
+ */
|
||
+ if (!keep_timer_going) {
|
||
+ for (i = agent->streams; i ; i = i->next) {
|
||
+ NiceStream *stream = i->data;
|
||
+ if (priv_is_checklist_frozen (stream)) {
|
||
+ nice_debug ("Agent %p : stream %d conncheck list is still "
|
||
+ "frozen, while other lists are completed. Unfreeze it.",
|
||
+ agent, stream->id);
|
||
+ keep_timer_going = priv_conn_check_unfreeze_next (agent, stream);
|
||
+ }
|
||
+ }
|
||
}
|
||
|
||
- /* step: when there's no pair in the Waiting state,
|
||
- * unfreeze a new pair and check it
|
||
+ /* note: we provide a grace period before declaring a component as
|
||
+ * failed. Components marked connected, and then ready follow another
|
||
+ * code path, and are not concerned by this grace period.
|
||
*/
|
||
- res = priv_conn_check_unfreeze_next (agent);
|
||
-
|
||
- for (i = agent->streams; i ; i = i->next) {
|
||
- NiceStream *stream = i->data;
|
||
+ if (!keep_timer_going && agent->conncheck_timer_grace_period == 0)
|
||
+ nice_debug ("Agent %p : waiting %d msecs before checking "
|
||
+ "for failed components.", agent, NICE_AGENT_MAX_TIMER_GRACE_PERIOD);
|
||
|
||
- pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
|
||
- if (pair)
|
||
- break;
|
||
- }
|
||
-
|
||
- if (pair) {
|
||
- priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
- ", got a pair in Waiting state");
|
||
- priv_conn_check_initiate (agent, pair);
|
||
- return TRUE;
|
||
- }
|
||
+ if (keep_timer_going)
|
||
+ agent->conncheck_timer_grace_period = 0;
|
||
+ else
|
||
+ agent->conncheck_timer_grace_period += agent->timer_ta;
|
||
|
||
/* step: stop timer if no work left */
|
||
- if (keep_timer_going != TRUE) {
|
||
- nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
|
||
+ if (!keep_timer_going &&
|
||
+ agent->conncheck_timer_grace_period >= NICE_AGENT_MAX_TIMER_GRACE_PERIOD) {
|
||
+ nice_debug ("Agent %p : checking for failed components now.", agent);
|
||
for (i = agent->streams; i; i = i->next) {
|
||
NiceStream *stream = i->data;
|
||
priv_update_check_list_failed_components (agent, stream);
|
||
@@ -635,6 +1127,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent)
|
||
}
|
||
}
|
||
|
||
+ nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
|
||
priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
", conncheck timer stopped");
|
||
|
||
@@ -645,9 +1138,10 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent)
|
||
|
||
/* XXX: what to signal, is all processing now really done? */
|
||
nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
|
||
+ return FALSE;
|
||
}
|
||
|
||
- return keep_timer_going;
|
||
+ return TRUE;
|
||
}
|
||
|
||
static gboolean priv_conn_check_tick (gpointer pointer)
|
||
@@ -888,8 +1382,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
|
||
agent, buf_len, p->keepalive.stun_message.buffer);
|
||
|
||
if (buf_len > 0) {
|
||
- stun_timer_start (&p->keepalive.timer, STUN_TIMER_DEFAULT_TIMEOUT,
|
||
- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
|
||
+ stun_timer_start (&p->keepalive.timer,
|
||
+ agent->stun_initial_timeout,
|
||
+ agent->stun_max_retransmissions);
|
||
|
||
agent->media_after_tick = FALSE;
|
||
|
||
@@ -1116,8 +1611,9 @@ static void priv_turn_allocate_refresh_tick_unlocked (CandidateRefresh *cand)
|
||
}
|
||
|
||
if (buffer_len > 0) {
|
||
- stun_timer_start (&cand->timer, STUN_TIMER_DEFAULT_TIMEOUT,
|
||
- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
|
||
+ stun_timer_start (&cand->timer,
|
||
+ cand->agent->stun_initial_timeout,
|
||
+ cand->agent->stun_max_retransmissions);
|
||
|
||
/* send the refresh */
|
||
agent_socket_send (cand->nicesock, &cand->server,
|
||
@@ -1213,154 +1709,136 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStrea
|
||
if (nice_address_equal (&icheck->from, &pair->remote->addr) &&
|
||
icheck->local_socket == pair->sockptr) {
|
||
nice_debug ("Agent %p : Updating check %p with stored early-icheck %p, %p/%u/%u (agent/stream/component).", agent, pair, icheck, agent, stream->id, component->id);
|
||
+ priv_schedule_triggered_check (agent, stream, component,
|
||
+ icheck->local_socket, pair->remote);
|
||
if (icheck->use_candidate)
|
||
priv_mark_pair_nominated (agent, stream, component, pair->local, pair->remote);
|
||
- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, pair->remote, icheck->use_candidate);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
-static GSList *prune_cancelled_conn_check (GSList *conncheck_list)
|
||
-{
|
||
- GSList *item = conncheck_list;
|
||
-
|
||
- while (item) {
|
||
- CandidateCheckPair *pair = item->data;
|
||
- GSList *next = item->next;
|
||
-
|
||
- if (pair->state == NICE_CHECK_CANCELLED) {
|
||
- conn_check_free_item (pair);
|
||
- conncheck_list = g_slist_delete_link (conncheck_list, item);
|
||
- }
|
||
-
|
||
- item = next;
|
||
- }
|
||
-
|
||
- return conncheck_list;
|
||
-}
|
||
-
|
||
-
|
||
/*
|
||
* Handle any processing steps for connectivity checks after
|
||
- * remote candidates have been set. This function handles
|
||
+ * remote credentials have been set. This function handles
|
||
* the special case where answerer has sent us connectivity
|
||
- * checks before the answer (containing candidate information),
|
||
+ * checks before the answer (containing credentials information),
|
||
* reaches us. The special case is documented in sect 7.2
|
||
* if ICE spec (ID-19).
|
||
*/
|
||
-void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component)
|
||
+void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream)
|
||
{
|
||
- GSList *j, *k, *l, *m, *n;
|
||
+ GSList *j, *k, *l, *m, *n, *o;
|
||
|
||
for (j = stream->conncheck_list; j ; j = j->next) {
|
||
CandidateCheckPair *pair = j->data;
|
||
- if (pair->component_id == component->id) {
|
||
- gboolean match = FALSE;
|
||
-
|
||
- /* performn delayed processing of spec steps section 7.2.1.4,
|
||
- and section 7.2.1.5 */
|
||
- priv_preprocess_conn_check_pending_data (agent, stream, component, pair);
|
||
-
|
||
- for (k = component->incoming_checks; k; k = k->next) {
|
||
- IncomingCheck *icheck = k->data;
|
||
- /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
|
||
- * be handled separately */
|
||
- for (l = component->remote_candidates; l; l = l->next) {
|
||
- NiceCandidate *cand = l->data;
|
||
- if (nice_address_equal (&icheck->from, &cand->addr)) {
|
||
- match = TRUE;
|
||
- break;
|
||
- }
|
||
- }
|
||
- if (match != TRUE) {
|
||
- /* note: we have gotten an incoming connectivity check from
|
||
- * an address that is not a known remote candidate */
|
||
-
|
||
- NiceCandidate *local_candidate = NULL;
|
||
- NiceCandidate *remote_candidate = NULL;
|
||
-
|
||
- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
|
||
- agent->compatibility == NICE_COMPATIBILITY_MSN ||
|
||
- agent->compatibility == NICE_COMPATIBILITY_OC2007) {
|
||
- /* We need to find which local candidate was used */
|
||
- uint8_t uname[NICE_STREAM_MAX_UNAME];
|
||
- guint uname_len;
|
||
-
|
||
- nice_debug ("Agent %p: We have a peer-reflexive candidate in a "
|
||
- "stored pending check", agent);
|
||
-
|
||
- for (m = component->remote_candidates;
|
||
- m != NULL && remote_candidate == NULL; m = m->next) {
|
||
- for (n = component->local_candidates; n; n = n->next) {
|
||
- NiceCandidate *rcand = m->data;
|
||
- NiceCandidate *lcand = n->data;
|
||
-
|
||
- uname_len = priv_create_username (agent, stream,
|
||
- component->id, rcand, lcand,
|
||
- uname, sizeof (uname), TRUE);
|
||
-
|
||
- stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d",
|
||
- icheck->username_len, uname_len,
|
||
- icheck->username && uname_len == icheck->username_len &&
|
||
- memcmp (uname, icheck->username, icheck->username_len) == 0);
|
||
- stun_debug_bytes (" first username: ",
|
||
- icheck->username,
|
||
- icheck->username? icheck->username_len : 0);
|
||
- stun_debug_bytes (" second username: ", uname, uname_len);
|
||
-
|
||
- if (icheck->username &&
|
||
- uname_len == icheck->username_len &&
|
||
- memcmp (uname, icheck->username, icheck->username_len) == 0) {
|
||
- local_candidate = lcand;
|
||
- remote_candidate = rcand;
|
||
- break;
|
||
- }
|
||
- }
|
||
- }
|
||
- } else {
|
||
- for (l = component->local_candidates; l; l = l->next) {
|
||
- NiceCandidate *cand = l->data;
|
||
- if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) {
|
||
- local_candidate = cand;
|
||
+ NiceComponent *component =
|
||
+ nice_stream_find_component_by_id (stream, pair->component_id);
|
||
+ gboolean match = FALSE;
|
||
+
|
||
+ /* performn delayed processing of spec steps section 7.2.1.4,
|
||
+ and section 7.2.1.5 */
|
||
+ priv_preprocess_conn_check_pending_data (agent, stream, component, pair);
|
||
+
|
||
+ for (k = component->incoming_checks; k; k = k->next) {
|
||
+ IncomingCheck *icheck = k->data;
|
||
+ /* sect 7.2.1.3., "Learning Peer Reflexive Candidates", has to
|
||
+ * be handled separately */
|
||
+ for (l = component->remote_candidates; l; l = l->next) {
|
||
+ NiceCandidate *cand = l->data;
|
||
+ if (nice_address_equal (&icheck->from, &cand->addr)) {
|
||
+ match = TRUE;
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ if (match != TRUE) {
|
||
+ /* note: we have gotten an incoming connectivity check from
|
||
+ * an address that is not a known remote candidate */
|
||
+
|
||
+ NiceCandidate *local_candidate = NULL;
|
||
+ NiceCandidate *remote_candidate = NULL;
|
||
+
|
||
+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
|
||
+ agent->compatibility == NICE_COMPATIBILITY_MSN ||
|
||
+ agent->compatibility == NICE_COMPATIBILITY_OC2007) {
|
||
+ /* We need to find which local candidate was used */
|
||
+ uint8_t uname[NICE_STREAM_MAX_UNAME];
|
||
+ guint uname_len;
|
||
+
|
||
+ nice_debug ("Agent %p: We have a peer-reflexive candidate in a "
|
||
+ "stored pending check", agent);
|
||
+
|
||
+ for (m = component->remote_candidates;
|
||
+ m != NULL && remote_candidate == NULL; m = m->next) {
|
||
+ for (n = component->local_candidates; n; n = n->next) {
|
||
+ NiceCandidate *rcand = m->data;
|
||
+ NiceCandidate *lcand = n->data;
|
||
+
|
||
+ uname_len = priv_create_username (agent, stream,
|
||
+ component->id, rcand, lcand,
|
||
+ uname, sizeof (uname), TRUE);
|
||
+
|
||
+ stun_debug ("pending check, comparing usernames of len %d and %d, equal=%d",
|
||
+ icheck->username_len, uname_len,
|
||
+ icheck->username && uname_len == icheck->username_len &&
|
||
+ memcmp (uname, icheck->username, icheck->username_len) == 0);
|
||
+ stun_debug_bytes (" first username: ",
|
||
+ icheck->username,
|
||
+ icheck->username? icheck->username_len : 0);
|
||
+ stun_debug_bytes (" second username: ", uname, uname_len);
|
||
+
|
||
+ if (icheck->username &&
|
||
+ uname_len == icheck->username_len &&
|
||
+ memcmp (uname, icheck->username, icheck->username_len) == 0) {
|
||
+ local_candidate = lcand;
|
||
+ remote_candidate = rcand;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
-
|
||
- if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
|
||
- local_candidate == NULL) {
|
||
- /* if we couldn't match the username, then the matching remote
|
||
- * candidate hasn't been received yet.. we must wait */
|
||
- nice_debug ("Agent %p : Username check failed. pending check has "
|
||
- "to wait to be processed", agent);
|
||
- } else {
|
||
- NiceCandidate *candidate;
|
||
-
|
||
- nice_debug ("Agent %p : Discovered peer reflexive from early i-check",
|
||
- agent);
|
||
- candidate =
|
||
- discovery_learn_remote_peer_reflexive_candidate (agent,
|
||
- stream,
|
||
- component,
|
||
- icheck->priority,
|
||
- &icheck->from,
|
||
- icheck->local_socket,
|
||
- local_candidate, remote_candidate);
|
||
- if (candidate) {
|
||
- if (local_candidate &&
|
||
- local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
|
||
- priv_conn_check_add_for_candidate_pair_matched (agent,
|
||
- stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED);
|
||
- else
|
||
- conn_check_add_for_candidate (agent, stream->id, component, candidate);
|
||
-
|
||
- if (icheck->use_candidate)
|
||
- priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate);
|
||
- priv_schedule_triggered_check (agent, stream, component, icheck->local_socket, candidate, icheck->use_candidate);
|
||
+ } else {
|
||
+ for (l = component->local_candidates; l; l = l->next) {
|
||
+ NiceCandidate *cand = l->data;
|
||
+ if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) {
|
||
+ local_candidate = cand;
|
||
+ break;
|
||
}
|
||
}
|
||
}
|
||
+
|
||
+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
|
||
+ local_candidate == NULL) {
|
||
+ /* if we couldn't match the username, then the matching remote
|
||
+ * candidate hasn't been received yet.. we must wait */
|
||
+ nice_debug ("Agent %p : Username check failed. pending check has "
|
||
+ "to wait to be processed", agent);
|
||
+ } else {
|
||
+ NiceCandidate *candidate;
|
||
+
|
||
+ nice_debug ("Agent %p : Discovered peer reflexive from early i-check",
|
||
+ agent);
|
||
+ candidate =
|
||
+ discovery_learn_remote_peer_reflexive_candidate (agent,
|
||
+ stream,
|
||
+ component,
|
||
+ icheck->priority,
|
||
+ &icheck->from,
|
||
+ icheck->local_socket,
|
||
+ local_candidate, remote_candidate);
|
||
+ if (candidate) {
|
||
+ if (local_candidate &&
|
||
+ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
|
||
+ priv_conn_check_add_for_candidate_pair_matched (agent,
|
||
+ stream->id, component, local_candidate, candidate, NICE_CHECK_DISCOVERED);
|
||
+ else
|
||
+ conn_check_add_for_candidate (agent, stream->id, component, candidate);
|
||
+
|
||
+ priv_schedule_triggered_check (agent, stream, component,
|
||
+ icheck->local_socket, candidate);
|
||
+ if (icheck->use_candidate)
|
||
+ priv_mark_pair_nominated (agent, stream, component, local_candidate, candidate);
|
||
+ }
|
||
+ }
|
||
}
|
||
}
|
||
}
|
||
@@ -1368,12 +1846,12 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice
|
||
/* Once we process the pending checks, we should free them to avoid
|
||
* reprocessing them again if a dribble-mode set_remote_candidates
|
||
* is called */
|
||
- g_slist_free_full (component->incoming_checks,
|
||
- (GDestroyNotify) incoming_check_free);
|
||
- component->incoming_checks = NULL;
|
||
-
|
||
- stream->conncheck_list =
|
||
- prune_cancelled_conn_check (stream->conncheck_list);
|
||
+ for (o = stream->components; o; o = o->next) {
|
||
+ NiceComponent *component = o->data;
|
||
+ g_slist_free_full (component->incoming_checks,
|
||
+ (GDestroyNotify) incoming_check_free);
|
||
+ component->incoming_checks = NULL;
|
||
+ }
|
||
}
|
||
|
||
/*
|
||
@@ -1381,7 +1859,7 @@ void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, Nice
|
||
* in ICE spec section 5.7.3 (ID-19). See also
|
||
* conn_check_add_for_candidate().
|
||
*/
|
||
-static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit)
|
||
+static GSList *priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper_limit)
|
||
{
|
||
guint valid = 0;
|
||
guint cancelled = 0;
|
||
@@ -1389,22 +1867,22 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper
|
||
|
||
while (item) {
|
||
CandidateCheckPair *pair = item->data;
|
||
+ GSList *next = item->next;
|
||
|
||
- if (pair->state != NICE_CHECK_CANCELLED) {
|
||
- valid++;
|
||
- if (valid > upper_limit) {
|
||
- pair->state = NICE_CHECK_CANCELLED;
|
||
+ valid++;
|
||
+ if (valid > upper_limit) {
|
||
+ conn_check_free_item (pair);
|
||
+ conncheck_list = g_slist_delete_link (conncheck_list, item);
|
||
cancelled++;
|
||
- }
|
||
}
|
||
-
|
||
- item = item->next;
|
||
+ item = next;
|
||
}
|
||
|
||
if (cancelled > 0)
|
||
nice_debug ("Agent : Pruned %d candidates. Conncheck list has %d elements"
|
||
" left. Maximum connchecks allowed : %d", cancelled, valid,
|
||
upper_limit);
|
||
+ return conncheck_list;
|
||
}
|
||
|
||
/*
|
||
@@ -1444,15 +1922,18 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *comp
|
||
* Updates the check list state.
|
||
*
|
||
* Implements parts of the algorithm described in
|
||
- * ICE sect 8.1.2. "Updating States" (ID-19): if for any
|
||
+ * ICE sect 8.1.2. "Updating States" (RFC 5245): if for any
|
||
* component, all checks have been completed and have
|
||
- * failed, mark that component's state to NICE_CHECK_FAILED.
|
||
+ * failed to produce a nominated pair, mark that component's
|
||
+ * state to NICE_CHECK_FAILED.
|
||
*
|
||
* Sends a component state changesignal via 'agent'.
|
||
*/
|
||
static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream)
|
||
{
|
||
GSList *i;
|
||
+ gboolean completed;
|
||
+ guint nominated;
|
||
/* note: emitting a signal might cause the client
|
||
* to remove the stream, thus the component count
|
||
* must be fetched before entering the loop*/
|
||
@@ -1476,6 +1957,8 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre
|
||
if (!agent_find_component (agent, stream->id, c+1, NULL, &comp))
|
||
continue;
|
||
|
||
+ nominated = 0;
|
||
+ completed = TRUE;
|
||
for (i = stream->conncheck_list; i; i = i->next) {
|
||
CandidateCheckPair *p = i->data;
|
||
|
||
@@ -1483,16 +1966,22 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStre
|
||
g_assert (p->stream_id == stream->id);
|
||
|
||
if (p->component_id == (c + 1)) {
|
||
- if (p->state != NICE_CHECK_FAILED)
|
||
- break;
|
||
+ if (p->nominated)
|
||
+ ++nominated;
|
||
+ if (p->state != NICE_CHECK_FAILED &&
|
||
+ p->state != NICE_CHECK_SUCCEEDED &&
|
||
+ p->state != NICE_CHECK_DISCOVERED)
|
||
+ completed = FALSE;
|
||
}
|
||
}
|
||
|
||
- /* note: all checks have failed
|
||
+ /* note: all pairs are either failed or succeeded, and the component
|
||
+ * has not produced a nominated pair.
|
||
* Set the component to FAILED only if it actually had remote candidates
|
||
* that failed.. */
|
||
- if (i == NULL && comp != NULL && comp->remote_candidates != NULL)
|
||
- agent_signal_component_state_change (agent,
|
||
+ if (completed && nominated == 0 &&
|
||
+ comp != NULL && comp->remote_candidates != NULL)
|
||
+ agent_signal_component_state_change (agent,
|
||
stream->id,
|
||
(c + 1), /* component-id */
|
||
NICE_COMPONENT_STATE_FAILED);
|
||
@@ -1525,7 +2014,6 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream
|
||
++valid;
|
||
if (p->nominated == TRUE) {
|
||
++nominated;
|
||
- priv_update_selected_pair (agent, component, p);
|
||
}
|
||
}
|
||
}
|
||
@@ -1535,7 +2023,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream
|
||
/* Only go to READY if no checks are left in progress. If there are
|
||
* any that are kept, then this function will be called again when the
|
||
* conncheck tick timer finishes them all */
|
||
- if (priv_prune_pending_checks (stream, component->id) == 0) {
|
||
+ if (priv_prune_pending_checks (agent, stream, component->id) == 0) {
|
||
/* Continue through the states to give client code a nice
|
||
* logical progression. See http://phabricator.freedesktop.org/D218 for
|
||
* discussion. */
|
||
@@ -1564,24 +2052,59 @@ static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, Nice
|
||
|
||
g_assert (component);
|
||
|
||
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
|
||
+ agent->controlling_mode)
|
||
+ return;
|
||
+
|
||
/* step: search for at least one nominated pair */
|
||
for (i = stream->conncheck_list; i; i = i->next) {
|
||
CandidateCheckPair *pair = i->data;
|
||
if (pair->local == localcand && pair->remote == remotecand) {
|
||
- nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation);
|
||
- pair->nominated = TRUE;
|
||
+ /* ICE, 7.2.1.5. Updating the Nominated Flag */
|
||
+ /* note: TCP candidates typically produce peer reflexive
|
||
+ * candidate, generating a "discovered" pair that can be
|
||
+ * nominated.
|
||
+ */
|
||
+ if (pair->state == NICE_CHECK_SUCCEEDED &&
|
||
+ pair->discovered_pair != NULL) {
|
||
+ pair = pair->discovered_pair;
|
||
+ g_assert (pair->state == NICE_CHECK_DISCOVERED);
|
||
+ }
|
||
+
|
||
+ /* If the state of this pair is In-Progress, [...] the resulting
|
||
+ * valid pair has its nominated flag set when the response
|
||
+ * arrives.
|
||
+ */
|
||
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent) &&
|
||
+ pair->state == NICE_CHECK_IN_PROGRESS) {
|
||
+ pair->mark_nominated_on_response_arrival = TRUE;
|
||
+ nice_debug ("Agent %p : pair %p (%s) is in-progress, "
|
||
+ "will be nominated on response receipt.",
|
||
+ agent, pair, pair->foundation);
|
||
+ }
|
||
+
|
||
+ if (pair->valid ||
|
||
+ !NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
|
||
+ nice_debug ("Agent %p : marking pair %p (%s) as nominated",
|
||
+ agent, pair, pair->foundation);
|
||
+ pair->nominated = TRUE;
|
||
+ }
|
||
+
|
||
if (pair->valid) {
|
||
- priv_update_selected_pair (agent, component, pair);
|
||
/* Do not step down to CONNECTED if we're already at state READY*/
|
||
- if (component->state != NICE_COMPONENT_STATE_READY) {
|
||
+ if (component->state == NICE_COMPONENT_STATE_FAILED)
|
||
+ agent_signal_component_state_change (agent,
|
||
+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTING);
|
||
+ priv_update_selected_pair (agent, component, pair);
|
||
+ if (component->state == NICE_COMPONENT_STATE_CONNECTING)
|
||
/* step: notify the client of a new component state (must be done
|
||
* before the possible check list state update step */
|
||
agent_signal_component_state_change (agent,
|
||
stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
|
||
- }
|
||
-
|
||
}
|
||
- priv_update_check_list_state_for_ready (agent, stream, component);
|
||
+
|
||
+ if (pair->nominated)
|
||
+ priv_update_check_list_state_for_ready (agent, stream, component);
|
||
}
|
||
}
|
||
}
|
||
@@ -1624,7 +2147,7 @@ ensure_unique_priority (NiceComponent *component, guint32 priority)
|
||
*/
|
||
static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
|
||
guint stream_id, NiceComponent *component, NiceCandidate *local,
|
||
- NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
|
||
+ NiceCandidate *remote, NiceCheckState initial_state)
|
||
{
|
||
NiceStream *stream;
|
||
CandidateCheckPair *pair;
|
||
@@ -1662,8 +2185,6 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
|
||
tmpbuf1, nice_address_get_port (&pair->local->addr),
|
||
tmpbuf2, nice_address_get_port (&pair->remote->addr));
|
||
}
|
||
- pair->nominated = use_candidate;
|
||
- pair->controlling = agent->controlling_mode;
|
||
pair->prflx_priority = ensure_unique_priority (component,
|
||
peer_reflexive_candidate_priority (agent, local));
|
||
|
||
@@ -1675,7 +2196,8 @@ static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
|
||
/* implement the hard upper limit for number of
|
||
checks (see sect 5.7.3 ICE ID-19): */
|
||
if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
|
||
- priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks);
|
||
+ stream->conncheck_list = priv_limit_conn_check_list_size (
|
||
+ stream->conncheck_list, agent->max_conn_checks);
|
||
}
|
||
|
||
return pair;
|
||
@@ -1709,7 +2231,7 @@ static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
|
||
agent, local->foundation, remote->foundation,
|
||
stream_id, component->id);
|
||
pair = priv_add_new_check_pair (agent, stream_id, component, local, remote,
|
||
- initial_state, FALSE);
|
||
+ initial_state);
|
||
if (component->state == NICE_COMPONENT_STATE_CONNECTED ||
|
||
component->state == NICE_COMPONENT_STATE_READY) {
|
||
agent_signal_component_state_change (agent,
|
||
@@ -1781,6 +2303,15 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceCompone
|
||
|
||
g_assert (remote != NULL);
|
||
|
||
+ /* note: according to 7.2.1.3, "Learning Peer Reflexive Candidates",
|
||
+ * the agent does not pair this candidate with any local candidates.
|
||
+ */
|
||
+ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
|
||
+ remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
|
||
+ {
|
||
+ return added;
|
||
+ }
|
||
+
|
||
for (i = component->local_candidates; i ; i = i->next) {
|
||
NiceCandidate *local = i->data;
|
||
|
||
@@ -1815,6 +2346,18 @@ int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceC
|
||
|
||
g_assert (local != NULL);
|
||
|
||
+ /*
|
||
+ * note: according to 7.1.3.2.1 "Discovering Peer Reflexive
|
||
+ * Candidates", the peer reflexive candidate is not paired
|
||
+ * with other remote candidates
|
||
+ */
|
||
+
|
||
+ if (agent->compatibility == NICE_COMPATIBILITY_RFC5245 &&
|
||
+ local->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
|
||
+ {
|
||
+ return added;
|
||
+ }
|
||
+
|
||
for (i = component->remote_candidates; i ; i = i->next) {
|
||
|
||
NiceCandidate *remote = i->data;
|
||
@@ -1838,8 +2381,7 @@ static void conn_check_free_item (gpointer data)
|
||
|
||
if (pair->agent)
|
||
priv_remove_pair_from_triggered_check_queue (pair->agent, pair);
|
||
- pair->stun_message.buffer = NULL;
|
||
- pair->stun_message.buffer_len = 0;
|
||
+ priv_free_all_stun_transactions (pair, NULL);
|
||
g_slice_free (CandidateCheckPair, pair);
|
||
}
|
||
|
||
@@ -2057,30 +2599,28 @@ size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
|
||
|
||
/* Implement the computation specific in RFC 5245 section 16 */
|
||
|
||
-static unsigned int priv_compute_conncheck_timer (NiceAgent *agent)
|
||
+static unsigned int priv_compute_conncheck_timer (NiceAgent *agent, NiceStream *stream)
|
||
{
|
||
- GSList *item1, *item2;
|
||
+ GSList *i;
|
||
guint waiting_and_in_progress = 0;
|
||
+ guint n = 0;
|
||
unsigned int rto = 0;
|
||
|
||
-
|
||
- for (item1 = agent->streams; item1; item1 = item1->next) {
|
||
- NiceStream *stream = item1->data;;
|
||
- for (item2 = stream->conncheck_list; item2; item2 = item2->next) {
|
||
- CandidateCheckPair *pair = item2->data;
|
||
-
|
||
- if (pair->state == NICE_CHECK_IN_PROGRESS ||
|
||
- pair->state == NICE_CHECK_WAITING)
|
||
- waiting_and_in_progress++;
|
||
- }
|
||
+ for (i = stream->conncheck_list; i ; i = i->next) {
|
||
+ CandidateCheckPair *p = i->data;
|
||
+ if (p->state == NICE_CHECK_IN_PROGRESS ||
|
||
+ p->state == NICE_CHECK_WAITING)
|
||
+ waiting_and_in_progress++;
|
||
}
|
||
|
||
- rto = agent->timer_ta * waiting_and_in_progress;
|
||
+ n = priv_number_of_active_check_lists (agent);
|
||
+ rto = agent->timer_ta * n * waiting_and_in_progress;
|
||
|
||
/* We assume non-reliable streams are RTP, so we use 100 as the max */
|
||
- nice_debug ("Agent %p : timer set to %dms (waiting+in_progress=%d)",
|
||
+ nice_debug ("Agent %p : timer set to %dms, "
|
||
+ "waiting+in_progress=%d, nb_active=%d",
|
||
agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100),
|
||
- waiting_and_in_progress);
|
||
+ waiting_and_in_progress, n);
|
||
if (agent->reliable)
|
||
return MAX (rto, 500);
|
||
else
|
||
@@ -2114,6 +2654,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
|
||
bool cand_use = controlling;
|
||
size_t buffer_len;
|
||
unsigned int timeout;
|
||
+ StunTransaction *stun;
|
||
|
||
if (!agent_find_component (agent, pair->stream_id, pair->component_id,
|
||
&stream, &component))
|
||
@@ -2135,99 +2676,119 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
|
||
nice_address_to_string (&pair->local->addr, tmpbuf1);
|
||
nice_address_to_string (&pair->remote->addr, tmpbuf2);
|
||
nice_debug ("Agent %p : STUN-CC REQ [%s]:%u --> [%s]:%u, socket=%u, "
|
||
- "pair=%s (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), "
|
||
- "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", agent,
|
||
+ "pair=%p (c-id:%u), tie=%llu, username='%.*s' (%" G_GSIZE_FORMAT "), "
|
||
+ "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, %s.", agent,
|
||
tmpbuf1, nice_address_get_port (&pair->local->addr),
|
||
tmpbuf2, nice_address_get_port (&pair->remote->addr),
|
||
pair->sockptr->fileno ? g_socket_get_fd(pair->sockptr->fileno) : -1,
|
||
- pair->foundation, pair->component_id,
|
||
+ pair, pair->component_id,
|
||
(unsigned long long)agent->tie_breaker,
|
||
(int) uname_len, uname, uname_len,
|
||
(int) password_len, password, password_len,
|
||
- pair->prflx_priority, controlling);
|
||
+ pair->prflx_priority,
|
||
+ controlling ? "controlling" : "controlled");
|
||
}
|
||
|
||
- if (cand_use)
|
||
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
|
||
+ switch (agent->nomination_mode) {
|
||
+ case NICE_NOMINATION_MODE_REGULAR:
|
||
+ /* We are doing regular nomination, so we set the use-candidate
|
||
+ * attrib, when the controlling agent decided which valid pair to
|
||
+ * resend with this flag in priv_conn_check_tick_stream()
|
||
+ */
|
||
+ cand_use = pair->use_candidate_on_next_check;
|
||
+ nice_debug ("Agent %p : %s: set cand_use=%d "
|
||
+ "(regular nomination).", agent, G_STRFUNC, cand_use);
|
||
+ break;
|
||
+ case NICE_NOMINATION_MODE_AGGRESSIVE:
|
||
+ /* We are doing aggressive nomination, we set the use-candidate
|
||
+ * attrib in every check we send, when we are the controlling
|
||
+ * agent, RFC 5245, 8.1.1.2
|
||
+ */
|
||
+ cand_use = controlling;
|
||
+ nice_debug ("Agent %p : %s: set cand_use=%d "
|
||
+ "(aggressive nomination).", agent, G_STRFUNC, cand_use);
|
||
+ break;
|
||
+ default:
|
||
+ /* Nothing to do. */
|
||
+ break;
|
||
+ }
|
||
+ } else if (cand_use)
|
||
pair->nominated = controlling;
|
||
|
||
- if (uname_len > 0) {
|
||
- buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent,
|
||
- &pair->stun_message, pair->stun_buffer, sizeof(pair->stun_buffer),
|
||
- uname, uname_len, password, password_len,
|
||
- cand_use, controlling, pair->prflx_priority,
|
||
- agent->tie_breaker,
|
||
- pair->local->foundation,
|
||
- agent_to_ice_compatibility (agent));
|
||
+ if (uname_len == 0) {
|
||
+ nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent);
|
||
+ return -1;
|
||
+ }
|
||
|
||
- nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len,
|
||
- pair->stun_message.buffer);
|
||
+ stun = priv_add_stun_transaction (pair);
|
||
|
||
- if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
|
||
- agent->compatibility == NICE_COMPATIBILITY_OC2007) {
|
||
- g_free (password);
|
||
- }
|
||
+ buffer_len = stun_usage_ice_conncheck_create (&component->stun_agent,
|
||
+ &stun->message, stun->buffer, sizeof(stun->buffer),
|
||
+ uname, uname_len, password, password_len,
|
||
+ cand_use, controlling, pair->prflx_priority,
|
||
+ agent->tie_breaker,
|
||
+ pair->local->foundation,
|
||
+ agent_to_ice_compatibility (agent));
|
||
|
||
- if (buffer_len > 0) {
|
||
- if (nice_socket_is_reliable(pair->sockptr)) {
|
||
- stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT);
|
||
- } else {
|
||
- stun_timer_start (&pair->timer,
|
||
- priv_compute_conncheck_timer (agent),
|
||
- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
|
||
- }
|
||
+ nice_debug ("Agent %p: conncheck created %zd - %p", agent, buffer_len,
|
||
+ stun->message.buffer);
|
||
|
||
- /* TCP-ACTIVE candidate must create a new socket before sending
|
||
- * by connecting to the peer. The new socket is stored in the candidate
|
||
- * check pair, until we discover a new local peer reflexive */
|
||
- if (pair->sockptr->fileno == NULL &&
|
||
- pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN &&
|
||
- pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
|
||
- NiceStream *stream2 = NULL;
|
||
- NiceComponent *component2 = NULL;
|
||
- NiceSocket *new_socket;
|
||
-
|
||
- if (agent_find_component (agent, pair->stream_id, pair->component_id,
|
||
- &stream2, &component2)) {
|
||
- new_socket = nice_tcp_active_socket_connect (pair->sockptr,
|
||
- &pair->remote->addr);
|
||
- if (new_socket) {
|
||
- pair->sockptr = new_socket;
|
||
- _priv_set_socket_tos (agent, pair->sockptr, stream2->tos);
|
||
-
|
||
- if (agent->reliable) {
|
||
- nice_socket_set_writable_callback (pair->sockptr,
|
||
- _tcp_sock_is_writable, component2);
|
||
- }
|
||
+ if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
|
||
+ agent->compatibility == NICE_COMPATIBILITY_OC2007) {
|
||
+ g_free (password);
|
||
+ }
|
||
|
||
- nice_component_attach_socket (component2, new_socket);
|
||
- }
|
||
- }
|
||
- }
|
||
- /* send the conncheck */
|
||
- agent_socket_send (pair->sockptr, &pair->remote->addr,
|
||
- buffer_len, (gchar *)pair->stun_buffer);
|
||
+ if (buffer_len == 0) {
|
||
+ nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent);
|
||
+ priv_remove_stun_transaction (pair, stun, component);
|
||
+ return -1;
|
||
+ }
|
||
|
||
- if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
|
||
- ms_ice2_legacy_conncheck_send (&pair->stun_message, pair->sockptr,
|
||
- &pair->remote->addr);
|
||
- }
|
||
+ if (nice_socket_is_reliable(pair->sockptr)) {
|
||
+ timeout = agent->stun_reliable_timeout;
|
||
+ stun_timer_start_reliable(&stun->timer, timeout);
|
||
+ } else {
|
||
+ timeout = priv_compute_conncheck_timer (agent, stream);
|
||
+ stun_timer_start (&stun->timer, timeout, agent->stun_max_retransmissions);
|
||
+ }
|
||
|
||
- timeout = stun_timer_remainder (&pair->timer);
|
||
- /* note: convert from milli to microseconds for g_time_val_add() */
|
||
- g_get_current_time (&pair->next_tick);
|
||
- g_time_val_add (&pair->next_tick, timeout * 1000);
|
||
- } else {
|
||
- nice_debug ("Agent %p: buffer is empty, cancelling conncheck", agent);
|
||
- pair->stun_message.buffer = NULL;
|
||
- pair->stun_message.buffer_len = 0;
|
||
- return -1;
|
||
+ g_get_current_time (&stun->next_tick);
|
||
+ g_time_val_add (&stun->next_tick, timeout * 1000);
|
||
+
|
||
+ /* TCP-ACTIVE candidate must create a new socket before sending
|
||
+ * by connecting to the peer. The new socket is stored in the candidate
|
||
+ * check pair, until we discover a new local peer reflexive */
|
||
+ if (pair->sockptr->fileno == NULL &&
|
||
+ pair->sockptr->type != NICE_SOCKET_TYPE_UDP_TURN &&
|
||
+ pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
|
||
+ NiceStream *stream2 = NULL;
|
||
+ NiceComponent *component2 = NULL;
|
||
+ NiceSocket *new_socket;
|
||
+
|
||
+ if (agent_find_component (agent, pair->stream_id, pair->component_id,
|
||
+ &stream2, &component2)) {
|
||
+ new_socket = nice_tcp_active_socket_connect (pair->sockptr,
|
||
+ &pair->remote->addr);
|
||
+ if (new_socket) {
|
||
+ pair->sockptr = new_socket;
|
||
+ _priv_set_socket_tos (agent, pair->sockptr, stream2->tos);
|
||
+
|
||
+ if (agent->reliable)
|
||
+ nice_socket_set_writable_callback (pair->sockptr,
|
||
+ _tcp_sock_is_writable, component2);
|
||
+
|
||
+ nice_component_attach_socket (component2, new_socket);
|
||
+ }
|
||
}
|
||
- } else {
|
||
- nice_debug ("Agent %p: no credentials found, cancelling conncheck", agent);
|
||
- pair->stun_message.buffer = NULL;
|
||
- pair->stun_message.buffer_len = 0;
|
||
- return -1;
|
||
}
|
||
+ /* send the conncheck */
|
||
+ agent_socket_send (pair->sockptr, &pair->remote->addr,
|
||
+ buffer_len, (gchar *)stun->buffer);
|
||
+
|
||
+ if (agent->compatibility == NICE_COMPATIBILITY_OC2007R2)
|
||
+ ms_ice2_legacy_conncheck_send (&stun->message, pair->sockptr,
|
||
+ &pair->remote->addr);
|
||
|
||
return 0;
|
||
}
|
||
@@ -2238,14 +2799,14 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
|
||
*
|
||
* @see priv_update_check_list_state_failed_components()
|
||
*/
|
||
-static guint priv_prune_pending_checks (NiceStream *stream, guint component_id)
|
||
+static guint priv_prune_pending_checks (NiceAgent *agent, NiceStream *stream, guint component_id)
|
||
{
|
||
GSList *i;
|
||
guint64 highest_nominated_priority = 0;
|
||
guint in_progress = 0;
|
||
|
||
- nice_debug ("Agent XXX: Finding highest priority for component %d",
|
||
- component_id);
|
||
+ nice_debug ("Agent %p: Finding highest priority for component %d",
|
||
+ agent, component_id);
|
||
|
||
for (i = stream->conncheck_list; i; i = i->next) {
|
||
CandidateCheckPair *p = i->data;
|
||
@@ -2257,37 +2818,40 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id)
|
||
}
|
||
}
|
||
|
||
- nice_debug ("Agent XXX: Pruning pending checks. Highest nominated priority "
|
||
- "is %" G_GUINT64_FORMAT, highest_nominated_priority);
|
||
+ nice_debug ("Agent %p: Pruning pending checks. Highest nominated priority "
|
||
+ "is %" G_GUINT64_FORMAT, agent, highest_nominated_priority);
|
||
|
||
/* step: cancel all FROZEN and WAITING pairs for the component */
|
||
- for (i = stream->conncheck_list; i; i = i->next) {
|
||
+ i = stream->conncheck_list;
|
||
+ while (i) {
|
||
CandidateCheckPair *p = i->data;
|
||
+ GSList *next = i->next;
|
||
+
|
||
if (p->component_id == component_id) {
|
||
- if (p->state == NICE_CHECK_FROZEN ||
|
||
- p->state == NICE_CHECK_WAITING) {
|
||
- p->state = NICE_CHECK_CANCELLED;
|
||
- nice_debug ("Agent XXX : pair %p state CANCELED", p);
|
||
+ if (p->state == NICE_CHECK_FROZEN || p->state == NICE_CHECK_WAITING) {
|
||
+ nice_debug ("Agent %p : pair %p removed.", agent, p);
|
||
+ conn_check_free_item (p);
|
||
+ stream->conncheck_list = g_slist_delete_link(stream->conncheck_list, i);
|
||
}
|
||
|
||
/* note: a SHOULD level req. in ICE 8.1.2. "Updating States" (ID-19) */
|
||
- if (p->state == NICE_CHECK_IN_PROGRESS) {
|
||
- if (highest_nominated_priority != 0 &&
|
||
- p->priority < highest_nominated_priority) {
|
||
- p->stun_message.buffer = NULL;
|
||
- p->stun_message.buffer_len = 0;
|
||
- p->state = NICE_CHECK_CANCELLED;
|
||
- nice_debug ("Agent XXX : pair %p state CANCELED", p);
|
||
+ else if (p->state == NICE_CHECK_IN_PROGRESS) {
|
||
+ if (p->priority < highest_nominated_priority) {
|
||
+ p->retransmit = FALSE;
|
||
+ nice_debug ("Agent %p : pair %p will not be retransmitted.",
|
||
+ agent, p);
|
||
} else {
|
||
/* We must keep the higher priority pairs running because if a udp
|
||
* packet was lost, we might end up using a bad candidate */
|
||
- nice_debug ("Agent XXX : pair %p kept IN_PROGRESS because priority %"
|
||
+ nice_debug ("Agent %p : pair %p kept IN_PROGRESS because priority %"
|
||
G_GUINT64_FORMAT " is higher than currently nominated pair %"
|
||
- G_GUINT64_FORMAT, p, p->priority, highest_nominated_priority);
|
||
+ G_GUINT64_FORMAT, agent,
|
||
+ p, p->priority, highest_nominated_priority);
|
||
in_progress++;
|
||
}
|
||
}
|
||
}
|
||
+ i = next;
|
||
}
|
||
|
||
return in_progress;
|
||
@@ -2301,17 +2865,17 @@ static guint priv_prune_pending_checks (NiceStream *stream, guint component_id)
|
||
* @param component the check is related to
|
||
* @param local_socket socket from which the inbound check was received
|
||
* @param remote_cand remote candidate from which the inbound check was sent
|
||
- * @param use_candidate whether the original check had USE-CANDIDATE attribute set
|
||
*/
|
||
-static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate)
|
||
+static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *local_socket, NiceCandidate *remote_cand)
|
||
{
|
||
GSList *i;
|
||
NiceCandidate *local = NULL;
|
||
+ CandidateCheckPair *p;
|
||
|
||
g_assert (remote_cand != NULL);
|
||
|
||
for (i = stream->conncheck_list; i ; i = i->next) {
|
||
- CandidateCheckPair *p = i->data;
|
||
+ p = i->data;
|
||
if (p->component_id == component->id &&
|
||
p->remote == remote_cand &&
|
||
((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
|
||
@@ -2322,61 +2886,67 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str
|
||
* tcp-active we don't want to retrigger a check on a pair that
|
||
* was FAILED when a peer-reflexive pair was created */
|
||
|
||
- nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p);
|
||
+ if (p->succeeded_pair != NULL) {
|
||
+ g_assert (p->state == NICE_CHECK_DISCOVERED);
|
||
+ p = p->succeeded_pair;
|
||
+ }
|
||
+
|
||
+ nice_debug ("Agent %p : Found a matching pair %p (%s) (%s) ...",
|
||
+ agent, p, p->foundation, priv_state_to_string (p->state));
|
||
|
||
- if (p->state == NICE_CHECK_WAITING ||
|
||
- p->state == NICE_CHECK_FROZEN)
|
||
- priv_add_pair_to_triggered_check_queue (agent, p);
|
||
- else if (p->state == NICE_CHECK_IN_PROGRESS) {
|
||
- /* XXX: according to ICE 7.2.1.4 "Triggered Checks" (ID-19),
|
||
- * we should cancel the existing one, instead we reset our timer, so
|
||
- * we'll resend the exiting transactions faster if needed...? :P
|
||
- */
|
||
- nice_debug ("Agent %p : check already in progress, "
|
||
- "restarting the timer again?: %s ..", agent,
|
||
- p->timer_restarted ? "no" : "yes");
|
||
- if (!nice_socket_is_reliable (p->sockptr) && !p->timer_restarted) {
|
||
- stun_timer_start (&p->timer,
|
||
- priv_compute_conncheck_timer (agent),
|
||
- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
|
||
- p->timer_restarted = TRUE;
|
||
- }
|
||
- }
|
||
- else if (p->state == NICE_CHECK_SUCCEEDED ||
|
||
- p->state == NICE_CHECK_DISCOVERED) {
|
||
- nice_debug ("Agent %p : Skipping triggered check, already completed..", agent);
|
||
- /* note: this is a bit unsure corner-case -- let's do the
|
||
- same state update as for processing responses to our own checks */
|
||
- /* note: this update is required by the dribble test, to
|
||
- * ensure the transition ready -> connected -> ready, because
|
||
- * an incoming stun request generates a discovered peer reflexive,
|
||
- * that causes the ready -> connected transition.
|
||
- */
|
||
- priv_update_check_list_state_for_ready (agent, stream, component);
|
||
-
|
||
- /* note: this new check is required by the new-dribble test,
|
||
- * when early icheck on the peer controlled agent causes an
|
||
- * incoming stun request to an already succeeded (and
|
||
- * nominated) pair on the controlling agent. If the
|
||
- * controlling agent doesn't retrigger a check with
|
||
- * USE-CANDIDATE=1, the peer agent has no way to nominate it.
|
||
- *
|
||
- * This behavior differs from ICE spec 7.2.1.4
|
||
- */
|
||
- if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
|
||
- agent->compatibility == NICE_COMPATIBILITY_WLM2009 ||
|
||
- agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
|
||
- agent->controlling_mode) {
|
||
+ switch (p->state) {
|
||
+ case NICE_CHECK_WAITING:
|
||
+ case NICE_CHECK_FROZEN:
|
||
+ nice_debug ("Agent %p : pair %p added for a triggered check.",
|
||
+ agent, p);
|
||
priv_add_pair_to_triggered_check_queue (agent, p);
|
||
- conn_check_schedule_next(agent);
|
||
- }
|
||
- } else if (p->state == NICE_CHECK_FAILED) {
|
||
- /* 7.2.1.4 Triggered Checks
|
||
- * If the state of the pair is Failed, it is changed to Waiting
|
||
- and the agent MUST create a new connectivity check for that
|
||
- pair (representing a new STUN Binding request transaction), by
|
||
- enqueueing the pair in the triggered check queue. */
|
||
- priv_add_pair_to_triggered_check_queue (agent, p);
|
||
+ break;
|
||
+ case NICE_CHECK_IN_PROGRESS:
|
||
+ /* note: according to ICE SPEC sect 7.2.1.4 "Triggered Checks"
|
||
+ * we cancel the in-progress transaction, and after the
|
||
+ * retransmission timeout, we create a new connectivity check
|
||
+ * for that pair. The controlling role of this new check may
|
||
+ * be different from the role of this cancelled check.
|
||
+ *
|
||
+ * note: the flag retransmit unset means that
|
||
+ * another pair, with a higher priority is already nominated,
|
||
+ * so there's no reason to recheck this pair, since it can in
|
||
+ * no way replace the nominated one.
|
||
+ */
|
||
+ if (!nice_socket_is_reliable (p->sockptr) && p->retransmit) {
|
||
+ nice_debug ("Agent %p : pair %p added for a triggered check.",
|
||
+ agent, p);
|
||
+ priv_add_pair_to_triggered_check_queue (agent, p);
|
||
+ }
|
||
+ break;
|
||
+ case NICE_CHECK_FAILED:
|
||
+ if (p->retransmit) {
|
||
+ nice_debug ("Agent %p : pair %p added for a triggered check.",
|
||
+ agent, p);
|
||
+ priv_add_pair_to_triggered_check_queue (agent, p);
|
||
+ /* If the component for this pair is in failed state, move it
|
||
+ * back to connecting, and reinitiate the timers
|
||
+ */
|
||
+ if (component->state == NICE_COMPONENT_STATE_FAILED) {
|
||
+ agent_signal_component_state_change (agent, stream->id,
|
||
+ component->id, NICE_COMPONENT_STATE_CONNECTING);
|
||
+ conn_check_schedule_next (agent);
|
||
+ }
|
||
+ }
|
||
+ break;
|
||
+ case NICE_CHECK_SUCCEEDED:
|
||
+ nice_debug ("Agent %p : nothing to do for pair %p.", agent, p);
|
||
+ /* note: this is a bit unsure corner-case -- let's do the
|
||
+ same state update as for processing responses to our own checks */
|
||
+ /* note: this update is required by the dribble test, to
|
||
+ * ensure the transition ready -> connected -> ready, because
|
||
+ * an incoming stun request generates a discovered peer reflexive,
|
||
+ * that causes the ready -> connected transition.
|
||
+ */
|
||
+ priv_update_check_list_state_for_ready (agent, stream, component);
|
||
+ break;
|
||
+ default:
|
||
+ break;
|
||
}
|
||
|
||
/* note: the spec says the we SHOULD retransmit in-progress
|
||
@@ -2394,7 +2964,9 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, NiceStream *str
|
||
|
||
if (i) {
|
||
nice_debug ("Agent %p : Adding a triggered check to conn.check list (local=%p).", agent, local);
|
||
- priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate);
|
||
+ p = priv_add_new_check_pair (agent, stream->id, component,
|
||
+ local, remote_cand, NICE_CHECK_WAITING);
|
||
+ priv_add_pair_to_triggered_check_queue (agent, p);
|
||
return TRUE;
|
||
}
|
||
else {
|
||
@@ -2447,9 +3019,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream,
|
||
}
|
||
|
||
if (rcand) {
|
||
- /* note: upon successful check, make the reserve check immediately */
|
||
- priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate);
|
||
-
|
||
+ priv_schedule_triggered_check (agent, stream, component, sockptr, rcand);
|
||
if (use_candidate)
|
||
priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
|
||
}
|
||
@@ -2509,6 +3079,8 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint
|
||
pair->remote = parent_pair->remote;
|
||
pair->sockptr = local_cand->sockptr;
|
||
pair->state = NICE_CHECK_DISCOVERED;
|
||
+ parent_pair->discovered_pair = pair;
|
||
+ pair->succeeded_pair = parent_pair;
|
||
nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair);
|
||
{
|
||
gchar tmpbuf1[INET6_ADDRSTRLEN];
|
||
@@ -2528,7 +3100,6 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint
|
||
pair->priority = nice_candidate_pair_priority (pair->remote->priority,
|
||
pair->local->priority);
|
||
pair->nominated = FALSE;
|
||
- pair->controlling = agent->controlling_mode;
|
||
pair->prflx_priority = ensure_unique_priority (component,
|
||
peer_reflexive_candidate_priority (agent, local_cand));
|
||
nice_debug ("Agent %p : added a new peer-discovered pair with foundation of '%s'.", agent, pair->foundation);
|
||
@@ -2567,14 +3138,16 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
|
||
{
|
||
/* role conflict, change mode; wait for a new conn. check */
|
||
if (control != agent->controlling_mode) {
|
||
- nice_debug ("Agent %p : Role conflict, changing agent role to %d.", agent, control);
|
||
+ nice_debug ("Agent %p : Role conflict, changing agent role to \"%s\".",
|
||
+ agent, control ? "controlling" : "controlled");
|
||
agent->controlling_mode = control;
|
||
/* the pair priorities depend on the roles, so recalculation
|
||
* is needed */
|
||
priv_recalculate_pair_priorities (agent);
|
||
}
|
||
else
|
||
- nice_debug ("Agent %p : Role conflict, agent role already changed to %d.", agent, control);
|
||
+ nice_debug ("Agent %p : Role conflict, staying with role \"%s\".",
|
||
+ agent, control ? "controlling" : "controlled");
|
||
}
|
||
|
||
/*
|
||
@@ -2621,13 +3194,25 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *
|
||
}
|
||
|
||
if (new_pair) {
|
||
+ /* note: when new_pair is distinct from p, it means new_pair is a
|
||
+ * previously discovered peer-reflexive candidate pair, so we don't
|
||
+ * set the valid flag on p in this case, because the valid flag is
|
||
+ * already set on the discovered pair.
|
||
+ */
|
||
+ if (new_pair == p)
|
||
+ p->valid = TRUE;
|
||
p->state = NICE_CHECK_SUCCEEDED;
|
||
+ priv_remove_pair_from_triggered_check_queue (agent, p);
|
||
+ priv_free_all_stun_transactions (p, component);
|
||
nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p);
|
||
- priv_conn_check_unfreeze_related (agent, stream, p);
|
||
+ nice_component_add_valid_candidate (component, remote_candidate);
|
||
}
|
||
else {
|
||
if (!local_cand) {
|
||
- if (!agent->force_relay)
|
||
+ if (!agent->force_relay) {
|
||
+ /* step: find a new local candidate, see RFC 5245 7.1.3.2.1.
|
||
+ * "Discovering Peer Reflexive Candidates"
|
||
+ */
|
||
local_cand = discovery_add_peer_reflexive_candidate (agent,
|
||
stream->id,
|
||
component->id,
|
||
@@ -2635,8 +3220,9 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *
|
||
sockptr,
|
||
local_candidate,
|
||
remote_candidate);
|
||
- p->state = NICE_CHECK_FAILED;
|
||
- nice_debug ("Agent %p : pair %p state FAILED", agent, p);
|
||
+ nice_debug ("Agent %p : added a new peer-reflexive local candidate %p",
|
||
+ agent, local_cand);
|
||
+ }
|
||
}
|
||
|
||
/* step: add a new discovered pair (see RFC 5245 7.1.3.2.2
|
||
@@ -2644,13 +3230,23 @@ static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *
|
||
if (local_cand)
|
||
new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component,
|
||
local_cand, p);
|
||
- nice_debug ("Agent %p : conncheck %p FAILED, %p DISCOVERED.", agent, p, new_pair);
|
||
+ /* note: this is same as "adding to VALID LIST" in the spec
|
||
+ text */
|
||
+ if (new_pair)
|
||
+ new_pair->valid = TRUE;
|
||
+ /* step: The agent sets the state of the pair that *generated* the check to
|
||
+ * Succeeded, RFC 5245, 7.1.3.2.3, "Updating Pair States"
|
||
+ */
|
||
+ p->state = NICE_CHECK_SUCCEEDED;
|
||
+ priv_remove_pair_from_triggered_check_queue (agent, p);
|
||
+ priv_free_all_stun_transactions (p, component);
|
||
+ nice_debug ("Agent %p : conncheck %p SUCCEEDED, %p DISCOVERED.",
|
||
+ agent, p, new_pair);
|
||
}
|
||
|
||
- /* note: this is same as "adding to VALID LIST" in the spec
|
||
- text */
|
||
- if (new_pair)
|
||
- new_pair->valid = TRUE;
|
||
+ if (new_pair && new_pair->valid)
|
||
+ nice_component_add_valid_candidate (component, remote_candidate);
|
||
+
|
||
|
||
return new_pair;
|
||
}
|
||
@@ -2669,134 +3265,199 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStre
|
||
struct sockaddr addr;
|
||
} sockaddr;
|
||
socklen_t socklen = sizeof (sockaddr);
|
||
- GSList *i;
|
||
+ GSList *i, *j;
|
||
+ guint k;
|
||
StunUsageIceReturn res;
|
||
- gboolean trans_found = FALSE;
|
||
StunTransactionId discovery_id;
|
||
StunTransactionId response_id;
|
||
stun_message_id (resp, response_id);
|
||
|
||
- for (i = stream->conncheck_list; i && trans_found != TRUE; i = i->next) {
|
||
+ for (i = stream->conncheck_list; i; i = i->next) {
|
||
CandidateCheckPair *p = i->data;
|
||
|
||
- if (p->stun_message.buffer) {
|
||
- stun_message_id (&p->stun_message, discovery_id);
|
||
-
|
||
- if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)) == 0) {
|
||
- res = stun_usage_ice_conncheck_process (resp,
|
||
- &sockaddr.storage, &socklen,
|
||
- agent_to_ice_compatibility (agent));
|
||
- nice_debug ("Agent %p : stun_bind_process/conncheck for %p res %d "
|
||
- "(controlling=%d).", agent, p, (int)res, agent->controlling_mode);
|
||
-
|
||
- if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
|
||
- res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
|
||
- /* case: found a matching connectivity check request */
|
||
-
|
||
- CandidateCheckPair *ok_pair = NULL;
|
||
-
|
||
- nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p);
|
||
- p->stun_message.buffer = NULL;
|
||
- p->stun_message.buffer_len = 0;
|
||
-
|
||
- /* step: verify that response came from the same IP address we
|
||
- * sent the original request to (see 7.1.2.1. "Failure
|
||
- * Cases") */
|
||
- if (nice_address_equal (from, &p->remote->addr) != TRUE) {
|
||
-
|
||
- p->state = NICE_CHECK_FAILED;
|
||
- if (nice_debug_is_enabled ()) {
|
||
- gchar tmpbuf[INET6_ADDRSTRLEN];
|
||
- gchar tmpbuf2[INET6_ADDRSTRLEN];
|
||
- nice_debug ("Agent %p : conncheck %p FAILED"
|
||
- " (mismatch of source address).", agent, p);
|
||
- nice_address_to_string (&p->remote->addr, tmpbuf);
|
||
- nice_address_to_string (from, tmpbuf2);
|
||
- nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
|
||
- tmpbuf, nice_address_get_port (&p->remote->addr),
|
||
- tmpbuf2, nice_address_get_port (from));
|
||
- }
|
||
- trans_found = TRUE;
|
||
- break;
|
||
- }
|
||
-
|
||
- /* note: CONNECTED but not yet READY, see docs */
|
||
-
|
||
- /* step: handle the possible case of a peer-reflexive
|
||
- * candidate where the mapped-address in response does
|
||
- * not match any local candidate, see 7.1.2.2.1
|
||
- * "Discovering Peer Reflexive Candidates" ICE ID-19) */
|
||
-
|
||
- if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
|
||
- /* note: this is same as "adding to VALID LIST" in the spec
|
||
- text */
|
||
- p->state = NICE_CHECK_SUCCEEDED;
|
||
- p->valid = TRUE;
|
||
- g_assert_not_reached ();
|
||
- nice_debug ("Agent %p : Mapped address not found."
|
||
- " conncheck %p SUCCEEDED.", agent, p);
|
||
- priv_conn_check_unfreeze_related (agent, stream, p);
|
||
- } else {
|
||
- ok_pair = priv_process_response_check_for_reflexive (agent,
|
||
- stream, component, p, sockptr, &sockaddr.addr,
|
||
- local_candidate, remote_candidate);
|
||
- }
|
||
-
|
||
-
|
||
- if (!ok_pair)
|
||
- ok_pair = p;
|
||
-
|
||
- /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
|
||
- Nominated Flag" (ID-19) */
|
||
- if (ok_pair->nominated == TRUE) {
|
||
- priv_update_selected_pair (agent, component, ok_pair);
|
||
- priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
- ", got a nominated pair");
|
||
+ for (j = p->stun_transactions, k = 0; j; j = j->next, k++) {
|
||
+ StunTransaction *stun = j->data;
|
||
+
|
||
+ stun_message_id (&stun->message, discovery_id);
|
||
+
|
||
+ if (memcmp (discovery_id, response_id, sizeof(StunTransactionId)))
|
||
+ continue;
|
||
+
|
||
+ res = stun_usage_ice_conncheck_process (resp,
|
||
+ &sockaddr.storage, &socklen,
|
||
+ agent_to_ice_compatibility (agent));
|
||
+ nice_debug ("Agent %p : stun_bind_process/conncheck for %p: "
|
||
+ "%s,res=%s,stun#=%d.",
|
||
+ agent, p,
|
||
+ agent->controlling_mode ? "controlling" : "controlled",
|
||
+ priv_ice_return_to_string (res), k);
|
||
+
|
||
+ if (res == STUN_USAGE_ICE_RETURN_SUCCESS ||
|
||
+ res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
|
||
+ /* case: found a matching connectivity check request */
|
||
+
|
||
+ CandidateCheckPair *ok_pair = NULL;
|
||
+
|
||
+ nice_debug ("Agent %p : conncheck %p MATCHED.", agent, p);
|
||
+ priv_remove_stun_transaction (p, stun, component);
|
||
+
|
||
+ /* step: verify that response came from the same IP address we
|
||
+ * sent the original request to (see 7.1.2.1. "Failure
|
||
+ * Cases") */
|
||
+ if (nice_address_equal (from, &p->remote->addr) == FALSE) {
|
||
+ candidate_check_pair_fail (stream, agent, p);
|
||
+ if (nice_debug_is_enabled ()) {
|
||
+ gchar tmpbuf[INET6_ADDRSTRLEN];
|
||
+ gchar tmpbuf2[INET6_ADDRSTRLEN];
|
||
+ nice_debug ("Agent %p : conncheck %p FAILED"
|
||
+ " (mismatch of source address).", agent, p);
|
||
+ nice_address_to_string (&p->remote->addr, tmpbuf);
|
||
+ nice_address_to_string (from, tmpbuf2);
|
||
+ nice_debug ("Agent %p : '%s:%u' != '%s:%u'", agent,
|
||
+ tmpbuf, nice_address_get_port (&p->remote->addr),
|
||
+ tmpbuf2, nice_address_get_port (from));
|
||
+ }
|
||
+ return TRUE;
|
||
+ }
|
||
|
||
- /* Do not step down to CONNECTED if we're already at state READY*/
|
||
- if (component->state != NICE_COMPONENT_STATE_READY) {
|
||
- /* step: notify the client of a new component state (must be done
|
||
- * before the possible check list state update step */
|
||
- agent_signal_component_state_change (agent,
|
||
- stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
|
||
- }
|
||
- }
|
||
+ /* note: CONNECTED but not yet READY, see docs */
|
||
+
|
||
+ /* step: handle the possible case of a peer-reflexive
|
||
+ * candidate where the mapped-address in response does
|
||
+ * not match any local candidate, see 7.1.2.2.1
|
||
+ * "Discovering Peer Reflexive Candidates" ICE ID-19) */
|
||
+
|
||
+ if (res == STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS) {
|
||
+ p->state = NICE_CHECK_SUCCEEDED;
|
||
+ p->valid = TRUE;
|
||
+ nice_debug ("Agent %p : Mapped address not found."
|
||
+ " conncheck %p SUCCEEDED.", agent, p);
|
||
+ nice_component_add_valid_candidate (component, p->remote);
|
||
+ } else
|
||
+ ok_pair = priv_process_response_check_for_reflexive (agent,
|
||
+ stream, component, p, sockptr, &sockaddr.addr,
|
||
+ local_candidate, remote_candidate);
|
||
+
|
||
+ /* note: The success of this check might also
|
||
+ * cause the state of other checks to change as well, ICE
|
||
+ * spec 7.1.3.2.3
|
||
+ */
|
||
+ priv_conn_check_unfreeze_related (agent, stream, p);
|
||
+
|
||
+ /* Note: this assignment helps to reduce the numbers of cases
|
||
+ * to be tested. If ok_pair and p refer to distinct pairs, it
|
||
+ * means that ok_pair is a discovered peer reflexive one,
|
||
+ * caused by the check made on pair p. In that case, the
|
||
+ * flags to be tested are on p, but the nominated flag will be
|
||
+ * set on ok_pair. When there's no discovered pair, p and
|
||
+ * ok_pair refer to the same pair.
|
||
+ * To summarize : p is a SUCCEEDED pair, ok_pair is a
|
||
+ * DISCOVERED, VALID, and eventually NOMINATED pair.
|
||
+ */
|
||
+ if (!ok_pair)
|
||
+ ok_pair = p;
|
||
+
|
||
+ /* step: updating nominated flag (ICE 7.1.2.2.4 "Updating the
|
||
+ Nominated Flag" (ID-19) */
|
||
+ if (NICE_AGENT_IS_COMPATIBLE_WITH_RFC5245_OR_OC2007R2 (agent)) {
|
||
+ nice_debug ("Agent %p : Updating nominated flag (%s): "
|
||
+ "ok_pair=%p (%d/%d) p=%p (%d/%d) (ucnc/mnora)",
|
||
+ agent, p->local->transport == NICE_CANDIDATE_TRANSPORT_UDP ?
|
||
+ "UDP" : "TCP",
|
||
+ ok_pair, ok_pair->use_candidate_on_next_check,
|
||
+ ok_pair->mark_nominated_on_response_arrival,
|
||
+ p, p->use_candidate_on_next_check,
|
||
+ p->mark_nominated_on_response_arrival);
|
||
+
|
||
+ if (agent->controlling_mode) {
|
||
+ switch (agent->nomination_mode) {
|
||
+ case NICE_NOMINATION_MODE_REGULAR:
|
||
+ if (p->use_candidate_on_next_check) {
|
||
+ nice_debug ("Agent %p : marking pair %p (%s) as nominated "
|
||
+ "(regular nomination, controlling, "
|
||
+ "use_cand_on_next_check=1).",
|
||
+ agent, ok_pair, ok_pair->foundation);
|
||
+ ok_pair->nominated = TRUE;
|
||
+ }
|
||
+ break;
|
||
+ case NICE_NOMINATION_MODE_AGGRESSIVE:
|
||
+ if (!p->nominated) {
|
||
+ nice_debug ("Agent %p : marking pair %p (%s) as nominated "
|
||
+ "(aggressive nomination, controlling).",
|
||
+ agent, ok_pair, ok_pair->foundation);
|
||
+ ok_pair->nominated = TRUE;
|
||
+ }
|
||
+ break;
|
||
+ default:
|
||
+ /* Nothing to do */
|
||
+ break;
|
||
+ }
|
||
+ } else {
|
||
+ if (p->mark_nominated_on_response_arrival) {
|
||
+ nice_debug ("Agent %p : marking pair %p (%s) as nominated "
|
||
+ "(%s nomination, controlled, mark_on_response=1).",
|
||
+ agent, ok_pair, ok_pair->foundation,
|
||
+ agent->nomination_mode == NICE_NOMINATION_MODE_AGGRESSIVE ?
|
||
+ "aggressive" : "regular");
|
||
+ ok_pair->nominated = TRUE;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
|
||
- /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
|
||
- states" and 8.1.2 "Updating States", ID-19) */
|
||
- priv_update_check_list_state_for_ready (agent, stream, component);
|
||
+ if (ok_pair->nominated == TRUE) {
|
||
+ priv_update_selected_pair (agent, component, ok_pair);
|
||
+ priv_print_conn_check_lists (agent, G_STRFUNC,
|
||
+ ", got a nominated pair");
|
||
+
|
||
+ /* Do not step down to CONNECTED if we're already at state READY*/
|
||
+ if (component->state != NICE_COMPONENT_STATE_READY)
|
||
+ /* step: notify the client of a new component state (must be done
|
||
+ * before the possible check list state update step */
|
||
+ agent_signal_component_state_change (agent,
|
||
+ stream->id, component->id, NICE_COMPONENT_STATE_CONNECTED);
|
||
+ }
|
||
|
||
- trans_found = TRUE;
|
||
- } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
|
||
- /* case: role conflict error, need to restart with new role */
|
||
- nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p);
|
||
- /* note: our role might already have changed due to an
|
||
- * incoming request, but if not, change role now;
|
||
- * follows ICE 7.1.2.1 "Failure Cases" (ID-19) */
|
||
- priv_check_for_role_conflict (agent, !p->controlling);
|
||
-
|
||
- p->stun_message.buffer = NULL;
|
||
- p->stun_message.buffer_len = 0;
|
||
- p->state = NICE_CHECK_WAITING;
|
||
- priv_add_pair_to_triggered_check_queue (agent, p);
|
||
- nice_debug ("Agent %p : pair %p state WAITING", agent, p);
|
||
- trans_found = TRUE;
|
||
- } else {
|
||
- /* case: STUN error, the check STUN context was freed */
|
||
- nice_debug ("Agent %p : conncheck %p FAILED.", agent, p);
|
||
- p->stun_message.buffer = NULL;
|
||
- p->stun_message.buffer_len = 0;
|
||
- trans_found = TRUE;
|
||
- }
|
||
+ /* step: update pair states (ICE 7.1.2.2.3 "Updating pair
|
||
+ states" and 8.1.2 "Updating States", ID-19) */
|
||
+ priv_update_check_list_state_for_ready (agent, stream, component);
|
||
+ } else if (res == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT) {
|
||
+ guint64 tie;
|
||
+ gboolean controlled_mode;
|
||
+
|
||
+ /* case: role conflict error, need to restart with new role */
|
||
+ nice_debug ("Agent %p : conncheck %p ROLE CONFLICT, restarting", agent, p);
|
||
+
|
||
+ /* note: this res value indicates that the role of the peer
|
||
+ * agent has not changed after the tie-breaker comparison, so
|
||
+ * this is our role that must change. see ICE sect. 7.1.3.1
|
||
+ * "Failure Cases". Our role might already have changed due to
|
||
+ * an earlier incoming request, but if not, change role now.
|
||
+ *
|
||
+ * Sect. 7.1.3.1 is not clear on this point, but we choose to
|
||
+ * put the candidate pair in the triggered check list even
|
||
+ * when the agent did not switch its role. The reason for this
|
||
+ * interpretation is that the reception of the stun reply, even
|
||
+ * an error reply, is a good sign that this pair will be
|
||
+ * valid, if we retry the check after the role of both peers
|
||
+ * has been fixed.
|
||
+ */
|
||
+ controlled_mode = (stun_message_find64 (&stun->message,
|
||
+ STUN_ATTRIBUTE_ICE_CONTROLLED, &tie) ==
|
||
+ STUN_MESSAGE_RETURN_SUCCESS);
|
||
+
|
||
+ priv_check_for_role_conflict (agent, controlled_mode);
|
||
+ priv_remove_stun_transaction (p, stun, component);
|
||
+ priv_add_pair_to_triggered_check_queue (agent, p);
|
||
+ } else {
|
||
+ /* case: STUN error, the check STUN context was freed */
|
||
+ nice_debug ("Agent %p : conncheck %p FAILED.", agent, p);
|
||
+ candidate_check_pair_fail (stream, agent, p);
|
||
}
|
||
+ return TRUE;
|
||
}
|
||
}
|
||
|
||
-
|
||
- stream->conncheck_list =
|
||
- prune_cancelled_conn_check (stream->conncheck_list);
|
||
-
|
||
- return trans_found;
|
||
+ return FALSE;
|
||
}
|
||
|
||
/*
|
||
@@ -2846,6 +3507,7 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa
|
||
d->server = niceaddr;
|
||
|
||
d->pending = FALSE;
|
||
+ agent->discovery_unsched_items++;
|
||
} else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
|
||
/* case: successful binding discovery, create a new local candidate */
|
||
|
||
@@ -2930,6 +3592,41 @@ priv_add_new_turn_refresh (CandidateDiscovery *cdisco, NiceCandidate *relay_cand
|
||
return cand;
|
||
}
|
||
|
||
+static void priv_handle_turn_alternate_server (NiceAgent *agent,
|
||
+ CandidateDiscovery *disco, NiceAddress server, NiceAddress alternate)
|
||
+{
|
||
+ /* We need to cancel and reset all candidate discovery turn for the same
|
||
+ stream and type if there is an alternate server. Otherwise, we might end up
|
||
+ with two relay components on different servers, creating candidates with
|
||
+ unique foundations that only contain one component.
|
||
+ */
|
||
+ GSList *i;
|
||
+
|
||
+ for (i = agent->discovery_list; i; i = i->next) {
|
||
+ CandidateDiscovery *d = i->data;
|
||
+
|
||
+ if (!d->done &&
|
||
+ d->type == disco->type &&
|
||
+ d->stream == disco->stream &&
|
||
+ d->turn->type == disco->turn->type &&
|
||
+ nice_address_equal (&d->server, &server)) {
|
||
+ gchar ip[INET6_ADDRSTRLEN];
|
||
+ // Cancel the pending request to avoid a race condition with another
|
||
+ // component responding with another altenrate-server
|
||
+ d->stun_message.buffer = NULL;
|
||
+ d->stun_message.buffer_len = 0;
|
||
+
|
||
+ nice_address_to_string (&server, ip);
|
||
+ nice_debug ("Agent %p : Cancelling and setting alternate server %s for "
|
||
+ "CandidateDiscovery %p", agent, ip, d);
|
||
+ d->server = alternate;
|
||
+ d->turn->server = alternate;
|
||
+ d->pending = FALSE;
|
||
+ agent->discovery_unsched_items++;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
/*
|
||
* Tries to match STUN reply in 'buf' to an existing STUN discovery
|
||
* transaction. If found, a reply is sent.
|
||
@@ -2982,11 +3679,11 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
|
||
agent, d, (int)res);
|
||
|
||
if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
|
||
- /* handle alternate server */
|
||
- nice_address_set_from_sockaddr (&d->server, &alternate.addr);
|
||
- nice_address_set_from_sockaddr (&d->turn->server, &alternate.addr);
|
||
+ NiceAddress addr;
|
||
|
||
- d->pending = FALSE;
|
||
+ /* handle alternate server */
|
||
+ nice_address_set_from_sockaddr (&addr, &alternate.addr);
|
||
+ priv_handle_turn_alternate_server (agent, d, d->server, addr);
|
||
} else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
|
||
res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
|
||
/* case: successful allocate, create a new local candidate */
|
||
@@ -3103,6 +3800,17 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
|
||
recv_realm = (uint8_t *) stun_message_find (resp,
|
||
STUN_ATTRIBUTE_REALM, &recv_realm_len);
|
||
|
||
+ if ((agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
|
||
+ agent->compatibility == NICE_COMPATIBILITY_OC2007R2) &&
|
||
+ alternatelen != sizeof(alternate)) {
|
||
+ NiceAddress addr;
|
||
+
|
||
+ nice_address_set_from_sockaddr (&addr, &alternate.addr);
|
||
+
|
||
+ if (!nice_address_equal (&addr, &d->server)) {
|
||
+ priv_handle_turn_alternate_server (agent, d, d->server, addr);
|
||
+ }
|
||
+ }
|
||
/* check for unauthorized error response */
|
||
if ((agent->compatibility == NICE_COMPATIBILITY_RFC5245 ||
|
||
agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
|
||
@@ -3123,6 +3831,7 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
|
||
d->stun_resp_msg.buffer = d->stun_resp_buffer;
|
||
d->stun_resp_msg.buffer_len = sizeof(d->stun_resp_buffer);
|
||
d->pending = FALSE;
|
||
+ agent->discovery_unsched_items++;
|
||
} else {
|
||
/* case: a real unauthorized error */
|
||
d->stun_message.buffer = NULL;
|
||
@@ -3619,8 +4328,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
|
||
stun_usage_ice_conncheck_use_candidate (&req);
|
||
uint32_t priority = stun_usage_ice_conncheck_priority (&req);
|
||
|
||
- if (agent->controlling_mode ||
|
||
- agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
|
||
+ if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
|
||
agent->compatibility == NICE_COMPATIBILITY_MSN ||
|
||
agent->compatibility == NICE_COMPATIBILITY_OC2007)
|
||
use_candidate = TRUE;
|
||
@@ -3628,21 +4336,21 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
|
||
if (stream->initial_binding_request_received != TRUE)
|
||
agent_signal_initial_binding_request_received (agent, stream);
|
||
|
||
- if (component->remote_candidates && remote_candidate == NULL) {
|
||
+ if (remote_candidate == NULL) {
|
||
nice_debug ("Agent %p : No matching remote candidate for incoming check ->"
|
||
"peer-reflexive candidate.", agent);
|
||
remote_candidate = discovery_learn_remote_peer_reflexive_candidate (
|
||
agent, stream, component, priority, from, nicesock,
|
||
local_candidate,
|
||
remote_candidate2 ? remote_candidate2 : remote_candidate);
|
||
- if(remote_candidate) {
|
||
+ if(remote_candidate && stream->remote_ufrag[0]) {
|
||
if (local_candidate &&
|
||
local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
|
||
CandidateCheckPair *pair;
|
||
|
||
pair = priv_conn_check_add_for_candidate_pair_matched (agent,
|
||
stream->id, component, local_candidate, remote_candidate,
|
||
- NICE_CHECK_DISCOVERED);
|
||
+ NICE_CHECK_SUCCEEDED);
|
||
if (pair) {
|
||
pair->valid = TRUE;
|
||
}
|
||
@@ -3651,13 +4359,15 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
|
||
}
|
||
}
|
||
|
||
+ nice_component_add_valid_candidate (component, remote_candidate);
|
||
+
|
||
priv_reply_to_conn_check (agent, stream, component, local_candidate,
|
||
remote_candidate, from, nicesock, rbuf_len, &msg, use_candidate);
|
||
|
||
- if (component->remote_candidates == NULL) {
|
||
+ if (stream->remote_ufrag[0] == 0) {
|
||
/* case: We've got a valid binding request to a local candidate
|
||
- * but we do not yet know remote credentials nor
|
||
- * candidates. As per sect 7.2 of ICE (ID-19), we send a reply
|
||
+ * but we do not yet know remote credentials.
|
||
+ * As per sect 7.2 of ICE (ID-19), we send a reply
|
||
* immediately but postpone all other processing until
|
||
* we get information about the remote candidates */
|
||
|
||
diff --git a/agent/conncheck.h b/agent/conncheck.h
|
||
index 431c606..e16dc67 100644
|
||
--- a/agent/conncheck.h
|
||
+++ b/agent/conncheck.h
|
||
@@ -56,7 +56,6 @@
|
||
* @NICE_CHECK_SUCCEEDED: Connection successfully checked.
|
||
* @NICE_CHECK_FAILED: No connectivity; retransmissions ceased.
|
||
* @NICE_CHECK_FROZEN: Waiting to be scheduled to %NICE_CHECK_WAITING.
|
||
- * @NICE_CHECK_CANCELLED: Check cancelled.
|
||
* @NICE_CHECK_DISCOVERED: A valid candidate pair not on the check list.
|
||
*
|
||
* States for checking a candidate pair.
|
||
@@ -68,11 +67,19 @@ typedef enum
|
||
NICE_CHECK_SUCCEEDED,
|
||
NICE_CHECK_FAILED,
|
||
NICE_CHECK_FROZEN,
|
||
- NICE_CHECK_CANCELLED,
|
||
NICE_CHECK_DISCOVERED,
|
||
} NiceCheckState;
|
||
|
||
typedef struct _CandidateCheckPair CandidateCheckPair;
|
||
+typedef struct _StunTransaction StunTransaction;
|
||
+
|
||
+struct _StunTransaction
|
||
+{
|
||
+ GTimeVal next_tick; /* next tick timestamp */
|
||
+ StunTimer timer;
|
||
+ uint8_t buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
|
||
+ StunMessage message;
|
||
+};
|
||
|
||
struct _CandidateCheckPair
|
||
{
|
||
@@ -85,15 +92,15 @@ struct _CandidateCheckPair
|
||
gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION];
|
||
NiceCheckState state;
|
||
gboolean nominated;
|
||
- gboolean controlling;
|
||
- gboolean timer_restarted;
|
||
gboolean valid;
|
||
+ gboolean use_candidate_on_next_check;
|
||
+ gboolean mark_nominated_on_response_arrival;
|
||
+ gboolean retransmit; /* if the first stun request must be retransmitted */
|
||
+ CandidateCheckPair *discovered_pair;
|
||
+ CandidateCheckPair *succeeded_pair;
|
||
guint64 priority;
|
||
guint32 prflx_priority;
|
||
- GTimeVal next_tick; /* next tick timestamp */
|
||
- StunTimer timer;
|
||
- uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE_IPV6];
|
||
- StunMessage stun_message;
|
||
+ GSList *stun_transactions; /* a list of ongoing stun requests */
|
||
};
|
||
|
||
int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote);
|
||
@@ -105,7 +112,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair);
|
||
void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream);
|
||
gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len);
|
||
gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b);
|
||
-void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component);
|
||
+void conn_check_remote_credentials_set(NiceAgent *agent, NiceStream *stream);
|
||
NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport);
|
||
void
|
||
conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component,
|
||
diff --git a/agent/debug.c b/agent/debug.c
|
||
index e1a298c..84fee94 100644
|
||
--- a/agent/debug.c
|
||
+++ b/agent/debug.c
|
||
@@ -102,7 +102,7 @@ void nice_debug_init (void)
|
||
flags |= g_parse_debug_string (gflags_string, gkeys, 4);
|
||
if (gflags_string && strstr (gflags_string, "libnice-pseudotcp-verbose"))
|
||
flags |= NICE_DEBUG_PSEUDOTCP_VERBOSE;
|
||
- if (gflags_string && strstr (gflags_string, "libnice-nice-verbose")) {
|
||
+ if (gflags_string && strstr (gflags_string, "libnice-verbose")) {
|
||
flags |= NICE_DEBUG_NICE_VERBOSE;
|
||
}
|
||
|
||
diff --git a/agent/discovery.c b/agent/discovery.c
|
||
index 7a890a0..4cc99c2 100644
|
||
--- a/agent/discovery.c
|
||
+++ b/agent/discovery.c
|
||
@@ -1072,11 +1072,11 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer)
|
||
|
||
if (buffer_len > 0) {
|
||
if (nice_socket_is_reliable (cand->nicesock)) {
|
||
- stun_timer_start_reliable (&cand->timer,
|
||
- STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT);
|
||
+ stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout);
|
||
} else {
|
||
- stun_timer_start (&cand->timer, 200,
|
||
- STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
|
||
+ stun_timer_start (&cand->timer,
|
||
+ agent->stun_initial_timeout,
|
||
+ agent->stun_max_retransmissions);
|
||
}
|
||
|
||
/* send the conncheck */
|
||
diff --git a/agent/interfaces.c b/agent/interfaces.c
|
||
index 0fa2fd7..a81888e 100644
|
||
--- a/agent/interfaces.c
|
||
+++ b/agent/interfaces.c
|
||
@@ -276,6 +276,12 @@ nice_interfaces_get_local_ips (gboolean include_loopback)
|
||
nice_debug ("Ignoring loopback interface");
|
||
g_free (addr_string);
|
||
}
|
||
+#ifdef IGNORED_IFACE_PREFIX
|
||
+ } else if (g_str_has_prefix (ifa->ifa_name, IGNORED_IFACE_PREFIX)) {
|
||
+ nice_debug ("Ignoring interface %s as it matches prefix %s",
|
||
+ ifa->ifa_name, IGNORED_IFACE_PREFIX);
|
||
+ g_free (addr_string);
|
||
+#endif
|
||
} else {
|
||
if (nice_interfaces_is_private_ip (ifa->ifa_addr))
|
||
ips = add_ip_to_list (ips, addr_string, TRUE);
|
||
diff --git a/agent/stream.c b/agent/stream.c
|
||
index 8121e12..533ff15 100644
|
||
--- a/agent/stream.c
|
||
+++ b/agent/stream.c
|
||
@@ -103,27 +103,6 @@ nice_stream_find_component_by_id (NiceStream *stream, guint id)
|
||
return NULL;
|
||
}
|
||
|
||
-/*
|
||
- * Returns true if all components of the stream are either
|
||
- * 'CONNECTED' or 'READY' (connected plus nominated).
|
||
- */
|
||
-gboolean
|
||
-nice_stream_all_components_ready (NiceStream *stream)
|
||
-{
|
||
- GSList *i;
|
||
-
|
||
- for (i = stream->components; i; i = i->next) {
|
||
- NiceComponent *component = i->data;
|
||
- if (component &&
|
||
- !(component->state == NICE_COMPONENT_STATE_CONNECTED ||
|
||
- component->state == NICE_COMPONENT_STATE_READY))
|
||
- return FALSE;
|
||
- }
|
||
-
|
||
- return TRUE;
|
||
-}
|
||
-
|
||
-
|
||
/*
|
||
* Initialized the local crendentials for the stream.
|
||
*/
|
||
diff --git a/agent/stream.h b/agent/stream.h
|
||
index f9188cb..954ba66 100644
|
||
--- a/agent/stream.h
|
||
+++ b/agent/stream.h
|
||
@@ -103,9 +103,6 @@ nice_stream_new (guint n_components, NiceAgent *agent);
|
||
void
|
||
nice_stream_close (NiceStream *stream);
|
||
|
||
-gboolean
|
||
-nice_stream_all_components_ready (NiceStream *stream);
|
||
-
|
||
NiceComponent *
|
||
nice_stream_find_component_by_id (NiceStream *stream, guint id);
|
||
|
||
diff --git a/common.mk b/common.mk
|
||
index e2ca3f4..b16380d 100644
|
||
--- a/common.mk
|
||
+++ b/common.mk
|
||
@@ -4,6 +4,8 @@ pkgincludedir = $(includedir)/nice
|
||
|
||
|
||
check-valgrind:
|
||
- $(MAKE) TESTS_ENVIRONMENT="sh $$(cd "$(top_srcdir)" && pwd)/scripts/valgrind.sh" check
|
||
+ $(MAKE) TESTS_ENVIRONMENT="USE_VALGRIND=1 " check
|
||
+
|
||
+LOG_DRIVER=$(top_srcdir)/scripts/valgrind-test-driver
|
||
|
||
.PHONY: check-valgrind
|
||
diff --git a/configure.ac b/configure.ac
|
||
index b39bfe3..16988ad 100644
|
||
--- a/configure.ac
|
||
+++ b/configure.ac
|
||
@@ -57,6 +57,7 @@ AC_PROG_CC
|
||
AM_PROG_AR
|
||
LT_PREREQ([2.2.6])
|
||
LT_INIT([dlopen win32-dll disable-static])
|
||
+AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums])
|
||
|
||
# Check Operating System
|
||
AC_MSG_CHECKING([operating system])
|
||
@@ -153,7 +154,6 @@ AS_IF([test "$enable_compile_warnings" = "yes" -o \
|
||
])
|
||
AS_IF([test "$enable_compile_warnings" = "maximum" -o \
|
||
"$enable_compile_warnings" = "error"],[
|
||
- NICE_ADD_FLAG([-Wswitch-enum])
|
||
NICE_ADD_FLAG([-Wswitch-default])
|
||
NICE_ADD_FLAG([-Waggregate-return])
|
||
])
|
||
@@ -354,6 +354,20 @@ AM_CONDITIONAL([ENABLE_GTK_DOC], false)
|
||
# GObject introspection
|
||
GOBJECT_INTROSPECTION_CHECK([1.30.0])
|
||
|
||
+dnl Ignore a specific network interface name prefix from the connection check
|
||
+AC_MSG_CHECKING([whether to ignore a specific network interface name prefix])
|
||
+AC_ARG_WITH([ignored-network-interface-prefix],
|
||
+ [AS_HELP_STRING([--with-ignored-network-interface-prefix=string],
|
||
+ [Ignore network interfaces whose name starts with "string" from the ICE connection
|
||
+ check algorithm. For example, interfaces "virbr" in the case of the virtual bridge
|
||
+ handled by libvirtd, do not help in finding connectivity.])],
|
||
+ [interface_prefix="$withval"])
|
||
+AS_IF([test -n "$interface_prefix"],
|
||
+ [AC_DEFINE_UNQUOTED([IGNORED_IFACE_PREFIX],["$interface_prefix"],
|
||
+ [Ignore this network interface prefix from the connection check])
|
||
+ AC_MSG_RESULT([yes, $interface_prefix])],
|
||
+ [AC_MSG_RESULT([no])])
|
||
+
|
||
AC_CONFIG_MACRO_DIR(m4)
|
||
|
||
AC_OUTPUT
|
||
diff --git a/docs/reference/libnice/libnice-docs.xml b/docs/reference/libnice/libnice-docs.xml
|
||
index 53487bc..391be1a 100644
|
||
--- a/docs/reference/libnice/libnice-docs.xml
|
||
+++ b/docs/reference/libnice/libnice-docs.xml
|
||
@@ -105,6 +105,10 @@
|
||
<title>Index of new symbols in 0.1.14</title>
|
||
<xi:include href="xml/api-index-0.1.14.xml"><xi:fallback/></xi:include>
|
||
</index>
|
||
+ <index role="0.1.15">
|
||
+ <title>Index of new symbols in 0.1.15</title>
|
||
+ <xi:include href="xml/api-index-0.1.15.xml"><xi:fallback/></xi:include>
|
||
+ </index>
|
||
<xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
|
||
</part>
|
||
</book>
|
||
diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt
|
||
index d377257..a481106 100644
|
||
--- a/docs/reference/libnice/libnice-sections.txt
|
||
+++ b/docs/reference/libnice/libnice-sections.txt
|
||
@@ -5,6 +5,7 @@ NiceAgent
|
||
NiceComponentState
|
||
NiceComponentType
|
||
NiceProxyType
|
||
+NiceNominationMode
|
||
NiceCompatibility
|
||
NiceAgentRecvFunc
|
||
NiceInputMessage
|
||
@@ -12,6 +13,7 @@ NiceOutputMessage
|
||
NICE_AGENT_MAX_REMOTE_CANDIDATES
|
||
nice_agent_new
|
||
nice_agent_new_reliable
|
||
+nice_agent_new_full
|
||
nice_agent_add_local_address
|
||
nice_agent_set_port_range
|
||
nice_agent_add_stream
|
||
@@ -76,6 +78,7 @@ NICE_CANDIDATE_MAX_FOUNDATION
|
||
nice_candidate_new
|
||
nice_candidate_free
|
||
nice_candidate_copy
|
||
+nice_candidate_equal_target
|
||
<SUBSECTION Standard>
|
||
NICE_TYPE_CANDIDATE
|
||
nice_candidate_get_type
|
||
diff --git a/examples/Makefile.am b/examples/Makefile.am
|
||
index 1e7decf..9c80854 100644
|
||
--- a/examples/Makefile.am
|
||
+++ b/examples/Makefile.am
|
||
@@ -18,7 +18,7 @@ AM_CFLAGS = \
|
||
$(GLIB_CFLAGS) \
|
||
$(GUPNP_CFLAGS)
|
||
|
||
-bin_PROGRAMS = simple-example threaded-example sdp-example
|
||
+noinst_PROGRAMS = simple-example threaded-example sdp-example
|
||
|
||
simple_example_SOURCES = simple-example.c
|
||
simple_example_LDADD = $(top_builddir)/agent/libagent.la \
|
||
diff --git a/nice/libnice.sym b/nice/libnice.sym
|
||
index b04bb95..1e522ad 100644
|
||
--- a/nice/libnice.sym
|
||
+++ b/nice/libnice.sym
|
||
@@ -58,6 +58,7 @@ nice_agent_set_software
|
||
nice_agent_set_stream_name
|
||
nice_agent_set_stream_tos
|
||
nice_candidate_copy
|
||
+nice_candidate_equal_target
|
||
nice_candidate_free
|
||
nice_candidate_new
|
||
nice_component_state_to_string
|
||
diff --git a/scripts/valgrind-test-driver b/scripts/valgrind-test-driver
|
||
new file mode 100755
|
||
index 0000000..5b660ee
|
||
--- /dev/null
|
||
+++ b/scripts/valgrind-test-driver
|
||
@@ -0,0 +1,162 @@
|
||
+#! /bin/sh
|
||
+# test-driver - basic testsuite driver script.
|
||
+
|
||
+scriptversion=2017-04-04.22; # UTC
|
||
+
|
||
+# Copyright (C) 2011-2014 Free Software Foundation, Inc.
|
||
+#
|
||
+# This program is free software; you can redistribute it and/or modify
|
||
+# it under the terms of the GNU General Public License as published by
|
||
+# the Free Software Foundation; either version 2, or (at your option)
|
||
+# any later version.
|
||
+#
|
||
+# This program is distributed in the hope that it will be useful,
|
||
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
+# GNU General Public License for more details.
|
||
+#
|
||
+# You should have received a copy of the GNU General Public License
|
||
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
+
|
||
+# As a special exception to the GNU General Public License, if you
|
||
+# distribute this file as part of a program that contains a
|
||
+# configuration script generated by Autoconf, you may include it under
|
||
+# the same distribution terms that you use for the rest of that program.
|
||
+
|
||
+# This file is maintained in Automake, please report
|
||
+# bugs to <bug-automake@gnu.org> or send patches to
|
||
+# <automake-patches@gnu.org>.
|
||
+
|
||
+# Make unconditional expansion of undefined variables an error. This
|
||
+# helps a lot in preventing typo-related bugs.
|
||
+set -u
|
||
+
|
||
+usage_error ()
|
||
+{
|
||
+ echo "$0: $*" >&2
|
||
+ print_usage >&2
|
||
+ exit 2
|
||
+}
|
||
+
|
||
+print_usage ()
|
||
+{
|
||
+ cat <<END
|
||
+Usage:
|
||
+ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||
+ [--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||
+ [--enable-hard-errors={yes|no}] [--]
|
||
+ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
|
||
+The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||
+END
|
||
+}
|
||
+
|
||
+test_name= # Used for reporting.
|
||
+log_file= # Where to save the output of the test script.
|
||
+trs_file= # Where to save the metadata of the test run.
|
||
+expect_failure=no
|
||
+color_tests=no
|
||
+enable_hard_errors=yes
|
||
+while test $# -gt 0; do
|
||
+ case $1 in
|
||
+ --help) print_usage; exit $?;;
|
||
+ --version) echo "test-driver $scriptversion"; exit $?;;
|
||
+ --test-name) test_name=$2; shift;;
|
||
+ --log-file) log_file=$2; shift;;
|
||
+ --trs-file) trs_file=$2; shift;;
|
||
+ --color-tests) color_tests=$2; shift;;
|
||
+ --expect-failure) expect_failure=$2; shift;;
|
||
+ --enable-hard-errors) enable_hard_errors=$2; shift;;
|
||
+ --) shift; break;;
|
||
+ -*) usage_error "invalid option: '$1'";;
|
||
+ *) break;;
|
||
+ esac
|
||
+ shift
|
||
+done
|
||
+
|
||
+missing_opts=
|
||
+test x"$test_name" = x && missing_opts="$missing_opts --test-name"
|
||
+test x"$log_file" = x && missing_opts="$missing_opts --log-file"
|
||
+test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
|
||
+if test x"$missing_opts" != x; then
|
||
+ usage_error "the following mandatory options are missing:$missing_opts"
|
||
+fi
|
||
+
|
||
+if test $# -eq 0; then
|
||
+ usage_error "missing argument"
|
||
+fi
|
||
+
|
||
+if test $color_tests = yes; then
|
||
+ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||
+ red='[0;31m' # Red.
|
||
+ grn='[0;32m' # Green.
|
||
+ lgn='[1;32m' # Light green.
|
||
+ blu='[1;34m' # Blue.
|
||
+ mgn='[0;35m' # Magenta.
|
||
+ std='[m' # No color.
|
||
+else
|
||
+ red= grn= lgn= blu= mgn= std=
|
||
+fi
|
||
+
|
||
+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||
+trap "st=129; $do_exit" 1
|
||
+trap "st=130; $do_exit" 2
|
||
+trap "st=141; $do_exit" 13
|
||
+trap "st=143; $do_exit" 15
|
||
+
|
||
+# Test script is run here.
|
||
+top_srcdir="`dirname $0`/.."
|
||
+tests_dir="${top_srcdir}/tests"
|
||
+
|
||
+USE_VALGRIND="`printenv USE_VALGRIND`"
|
||
+
|
||
+if test "x${USE_VALGRIND}" = "x1"; then
|
||
+ ${top_srcdir}/libtool --mode=execute valgrind \
|
||
+ --leak-check=full \
|
||
+ --show-reachable=no \
|
||
+ --error-exitcode=1 \
|
||
+ --suppressions=$tests_dir/libnice.supp \
|
||
+ --num-callers=30 "$@" >$log_file 2>&1
|
||
+else
|
||
+ "$@" >$log_file 2>&1
|
||
+fi
|
||
+estatus=$?
|
||
+
|
||
+if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||
+ tweaked_estatus=1
|
||
+else
|
||
+ tweaked_estatus=$estatus
|
||
+fi
|
||
+
|
||
+case $tweaked_estatus:$expect_failure in
|
||
+ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||
+ 0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||
+ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||
+ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||
+ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||
+ *:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||
+esac
|
||
+
|
||
+# Report the test outcome and exit status in the logs, so that one can
|
||
+# know whether the test passed or failed simply by looking at the '.log'
|
||
+# file, without the need of also peaking into the corresponding '.trs'
|
||
+# file (automake bug#11814).
|
||
+echo "$res $test_name (exit status: $estatus)" >>$log_file
|
||
+
|
||
+# Report outcome to console.
|
||
+echo "${col}${res}${std}: $test_name"
|
||
+
|
||
+# Register the test result, and other relevant metadata.
|
||
+echo ":test-result: $res" > $trs_file
|
||
+echo ":global-test-result: $res" >> $trs_file
|
||
+echo ":recheck: $recheck" >> $trs_file
|
||
+echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||
+
|
||
+# Local Variables:
|
||
+# mode: shell-script
|
||
+# sh-indentation: 2
|
||
+# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||
+# time-stamp-start: "scriptversion="
|
||
+# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||
+# time-stamp-time-zone: "UTC"
|
||
+# time-stamp-end: "; # UTC"
|
||
+# End:
|
||
diff --git a/scripts/valgrind.sh b/scripts/valgrind.sh
|
||
deleted file mode 100755
|
||
index 2864b6f..0000000
|
||
--- a/scripts/valgrind.sh
|
||
+++ /dev/null
|
||
@@ -1,28 +0,0 @@
|
||
-#!/bin/sh
|
||
-
|
||
-export G_SLICE=always-malloc
|
||
-export G_DEBUG=gc-friendly
|
||
-
|
||
-tests_dir="`dirname $0`/../tests"
|
||
-
|
||
-report=`libtool --mode=execute valgrind \
|
||
- --leak-check=full \
|
||
- --show-reachable=no \
|
||
- --error-exitcode=1 \
|
||
- --suppressions=$tests_dir/libnice.supp \
|
||
- --num-callers=30 \
|
||
- $1 2>&1`
|
||
-
|
||
-#if echo "$report" | grep -q ==; then
|
||
-if test $? != 0; then
|
||
- echo "$report"
|
||
- exit 1
|
||
-fi
|
||
-
|
||
-if echo "$report" | grep -q "definitely lost"; then
|
||
- if ! echo "$report" | grep -q "definitely lost: 0 bytes"; then
|
||
- echo "$report"
|
||
- exit 1
|
||
- fi
|
||
-fi
|
||
-
|
||
diff --git a/socket/udp-turn.c b/socket/udp-turn.c
|
||
index cc3409b..190a9ea 100644
|
||
--- a/socket/udp-turn.c
|
||
+++ b/socket/udp-turn.c
|
||
@@ -174,8 +174,8 @@ priv_send_data_queue_destroy (gpointer user_data)
|
||
|
||
NiceSocket *
|
||
nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr,
|
||
- NiceSocket *base_socket, NiceAddress *server_addr,
|
||
- gchar *username, gchar *password,
|
||
+ NiceSocket *base_socket, const NiceAddress *server_addr,
|
||
+ const gchar *username, const gchar *password,
|
||
NiceTurnSocketCompatibility compatibility)
|
||
{
|
||
UdpTurnPriv *priv;
|
||
@@ -406,9 +406,8 @@ socket_recv_messages (NiceSocket *sock,
|
||
|
||
/* Split up the monolithic buffer again into the caller-provided buffers. */
|
||
if (parsed_buffer_length > 0 && allocated_buffer) {
|
||
- parsed_buffer_length =
|
||
- memcpy_buffer_to_input_message (message, buffer,
|
||
- parsed_buffer_length);
|
||
+ memcpy_buffer_to_input_message (message, buffer,
|
||
+ parsed_buffer_length);
|
||
}
|
||
|
||
if (allocated_buffer)
|
||
@@ -1185,7 +1184,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc
|
||
gsize
|
||
nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
|
||
NiceAddress *from, gsize len, guint8 *buf,
|
||
- NiceAddress *recv_from, guint8 *_recv_buf, gsize recv_len)
|
||
+ const NiceAddress *recv_from, const guint8 *_recv_buf, gsize recv_len)
|
||
{
|
||
|
||
UdpTurnPriv *priv = (UdpTurnPriv *) sock->priv;
|
||
@@ -1195,8 +1194,8 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
|
||
ChannelBinding *binding = NULL;
|
||
|
||
union {
|
||
- guint8 *u8;
|
||
- guint16 *u16;
|
||
+ const guint8 *u8;
|
||
+ const guint16 *u16;
|
||
} recv_buf;
|
||
|
||
/* In the case of a reliable UDP-TURN-OVER-TCP (which means MS-TURN)
|
||
diff --git a/socket/udp-turn.h b/socket/udp-turn.h
|
||
index b1eeeb4..df10a1c 100644
|
||
--- a/socket/udp-turn.h
|
||
+++ b/socket/udp-turn.h
|
||
@@ -59,15 +59,16 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc
|
||
gsize
|
||
nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
|
||
NiceAddress *from, gsize len, guint8 *buf,
|
||
- NiceAddress *recv_from, guint8 *recv_buf, gsize recv_len);
|
||
+ const NiceAddress *recv_from, const guint8 *recv_buf, gsize recv_len);
|
||
|
||
gboolean
|
||
nice_udp_turn_socket_set_peer (NiceSocket *sock, NiceAddress *peer);
|
||
|
||
NiceSocket *
|
||
nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr,
|
||
- NiceSocket *base_socket, NiceAddress *server_addr,
|
||
- gchar *username, gchar *password, NiceTurnSocketCompatibility compatibility);
|
||
+ NiceSocket *base_socket, const NiceAddress *server_addr,
|
||
+ const gchar *username, const gchar *password,
|
||
+ NiceTurnSocketCompatibility compatibility);
|
||
|
||
void
|
||
nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg);
|
||
diff --git a/stun/stunmessage.c b/stun/stunmessage.c
|
||
index e8184c4..4cc3392 100644
|
||
--- a/stun/stunmessage.c
|
||
+++ b/stun/stunmessage.c
|
||
@@ -120,6 +120,7 @@ stun_message_find (const StunMessage *msg, StunAttribute type,
|
||
/* Only fingerprint may come after M-I */
|
||
if (type == STUN_ATTRIBUTE_FINGERPRINT)
|
||
break;
|
||
+ return NULL;
|
||
|
||
case STUN_ATTRIBUTE_FINGERPRINT:
|
||
/* Nothing may come after FPR */
|
||
diff --git a/stun/usages/bind.c b/stun/usages/bind.c
|
||
index 8dd7afc..ee600a0 100644
|
||
--- a/stun/usages/bind.c
|
||
+++ b/stun/usages/bind.c
|
||
@@ -479,7 +479,7 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv,
|
||
size_t len;
|
||
StunUsageTransReturn ret;
|
||
int val;
|
||
- struct sockaddr_storage alternate_server;
|
||
+ struct sockaddr_storage alternate_server = { AF_UNSPEC } ;
|
||
socklen_t alternate_server_len = sizeof (alternate_server);
|
||
StunUsageBindReturn bind_ret;
|
||
|
||
@@ -491,13 +491,15 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv,
|
||
ret = stun_trans_create (&trans, SOCK_DGRAM, 0, srv, srvlen);
|
||
if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) {
|
||
stun_debug ("STUN transaction failed: couldn't create transport.");
|
||
- return STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ goto done;
|
||
}
|
||
|
||
val = stun_trans_send (&trans, req_buf, len);
|
||
if (val < -1) {
|
||
stun_debug ("STUN transaction failed: couldn't send request.");
|
||
- return STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ goto done;
|
||
}
|
||
|
||
stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT,
|
||
@@ -514,14 +516,16 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv,
|
||
switch (stun_timer_refresh (&timer)) {
|
||
case STUN_USAGE_TIMER_RETURN_TIMEOUT:
|
||
stun_debug ("STUN transaction failed: time out.");
|
||
- return STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error!
|
||
+ bind_ret = STUN_USAGE_BIND_RETURN_TIMEOUT; // fatal error!
|
||
+ goto done;
|
||
case STUN_USAGE_TIMER_RETURN_RETRANSMIT:
|
||
stun_debug ("STUN transaction retransmitted (timeout %dms).",
|
||
stun_timer_remainder (&timer));
|
||
val = stun_trans_send (&trans, req_buf, len);
|
||
if (val < -1) {
|
||
stun_debug ("STUN transaction failed: couldn't resend request.");
|
||
- return STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ goto done;
|
||
}
|
||
continue;
|
||
case STUN_USAGE_TIMER_RETURN_SUCCESS:
|
||
@@ -538,7 +542,10 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv,
|
||
|
||
valid = stun_agent_validate (&agent, &msg, buf, val, NULL, NULL);
|
||
if (valid == STUN_VALIDATION_UNKNOWN_ATTRIBUTE)
|
||
- return STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ {
|
||
+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ goto done;
|
||
+ }
|
||
|
||
if (valid != STUN_VALIDATION_SUCCESS) {
|
||
ret = STUN_USAGE_TRANS_RETURN_RETRY;
|
||
@@ -548,16 +555,22 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv,
|
||
if (bind_ret == STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER) {
|
||
stun_trans_deinit (&trans);
|
||
|
||
+ assert (alternate_server.ss_family != AF_UNSPEC);
|
||
+
|
||
ret = stun_trans_create (&trans, SOCK_DGRAM, 0,
|
||
(struct sockaddr *) &alternate_server, alternate_server_len);
|
||
|
||
if (ret != STUN_USAGE_TRANS_RETURN_SUCCESS) {
|
||
- return STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ goto done;
|
||
}
|
||
|
||
val = stun_trans_send (&trans, req_buf, len);
|
||
if (val < -1)
|
||
- return STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ {
|
||
+ bind_ret = STUN_USAGE_BIND_RETURN_ERROR;
|
||
+ goto done;
|
||
+ }
|
||
|
||
stun_timer_start (&timer, STUN_TIMER_DEFAULT_TIMEOUT,
|
||
STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
|
||
@@ -571,5 +584,9 @@ StunUsageBindReturn stun_usage_bind_run (const struct sockaddr *srv,
|
||
}
|
||
while (ret == STUN_USAGE_TRANS_RETURN_RETRY);
|
||
|
||
+done:
|
||
+ if (trans.fd != -1)
|
||
+ stun_trans_deinit (&trans);
|
||
+
|
||
return bind_ret;
|
||
}
|
||
diff --git a/stun/usages/timer.c b/stun/usages/timer.c
|
||
index 2862ab8..5370cba 100644
|
||
--- a/stun/usages/timer.c
|
||
+++ b/stun/usages/timer.c
|
||
@@ -145,7 +145,11 @@ StunUsageTimerReturn stun_timer_refresh (StunTimer *timer)
|
||
if (timer->retransmissions >= timer->max_retransmissions)
|
||
return STUN_USAGE_TIMER_RETURN_TIMEOUT;
|
||
|
||
- add_delay (&timer->deadline, timer->delay *= 2);
|
||
+ if (timer->retransmissions == timer->max_retransmissions - 1)
|
||
+ timer->delay = timer->delay / 2;
|
||
+ else
|
||
+ timer->delay = timer->delay * 2;
|
||
+ add_delay (&timer->deadline, timer->delay);
|
||
timer->retransmissions++;
|
||
return STUN_USAGE_TIMER_RETURN_RETRANSMIT;
|
||
}
|
||
diff --git a/stun/usages/timer.h b/stun/usages/timer.h
|
||
index e74353b..097e75b 100644
|
||
--- a/stun/usages/timer.h
|
||
+++ b/stun/usages/timer.h
|
||
@@ -132,7 +132,11 @@ struct stun_timer_s {
|
||
* The default intial timeout to use for the timer
|
||
* RFC recommendds 500, but it's ridiculous, 50ms is known to work in most
|
||
* cases as it is also what is used by SIP style VoIP when sending A-Law and
|
||
- * mu-Law audio, so 200ms should be hyper safe.
|
||
+ * mu-Law audio, so 200ms should be hyper safe. With an initial timeout
|
||
+ * of 200ms, a default of 7 transmissions, the last timeout will be
|
||
+ * 16 * 200ms, and we expect to receive a response from the stun server
|
||
+ * before (1 + 2 + 4 + 8 + 16 + 32 + 16) * 200ms = 15200 ms after the initial
|
||
+ * stun request has been sent.
|
||
*/
|
||
#define STUN_TIMER_DEFAULT_TIMEOUT 200
|
||
|
||
diff --git a/stun/usages/turn.c b/stun/usages/turn.c
|
||
index 3b94959..ec12642 100644
|
||
--- a/stun/usages/turn.c
|
||
+++ b/stun/usages/turn.c
|
||
@@ -300,6 +300,17 @@ StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg,
|
||
stun_debug (" STUN error message received (code: %d)", code);
|
||
|
||
/* ALTERNATE-SERVER mechanism */
|
||
+ if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_OC2007 &&
|
||
+ alternate_server && alternate_server_len &&
|
||
+ stun_message_find_addr (msg, STUN_ATTRIBUTE_MS_ALTERNATE_SERVER,
|
||
+ alternate_server,
|
||
+ alternate_server_len) == STUN_MESSAGE_RETURN_SUCCESS) {
|
||
+ stun_debug ("Found alternate server");
|
||
+ /* The ALTERNATE_SERVER will always be returned by the MS turn server.
|
||
+ * We need to check if the ALTERNATE_SERVER is the same as the current
|
||
+ * server to decide whether we need to switch servers or not.
|
||
+ */
|
||
+ }
|
||
if ((code / 100) == 3) {
|
||
if (alternate_server && alternate_server_len) {
|
||
if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER,
|
||
diff --git a/stun/usages/turn.h b/stun/usages/turn.h
|
||
index 7a2d4e6..83fa00a 100644
|
||
--- a/stun/usages/turn.h
|
||
+++ b/stun/usages/turn.h
|
||
@@ -256,6 +256,10 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg,
|
||
* Allocate request, in case the currently used TURN server is requesting the use
|
||
* of an alternate server. This argument will only be filled if the return value
|
||
* of the function is #STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER
|
||
+ * In the case of @STUN_USAGE_TURN_COMPATIBILITY_OC2007 compatibility, the
|
||
+ * @alternate_server could be filled at any time, and should only be considered
|
||
+ * if the request was sent to a different server than the address returned
|
||
+ * in the @alternate_server field
|
||
* @alternate_server_len: The length of @alternate_server
|
||
* @bandwidth: A pointer to fill with the bandwidth the TURN server allocated us
|
||
* @lifetime: A pointer to fill with the lifetime of the allocation
|
||
diff --git a/tests/Makefile.am b/tests/Makefile.am
|
||
index 7bfe075..30d6f8e 100644
|
||
--- a/tests/Makefile.am
|
||
+++ b/tests/Makefile.am
|
||
@@ -24,7 +24,7 @@ AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\"
|
||
|
||
AM_TESTS_ENVIRONMENT = \
|
||
G_MESSAGES_DEBUG=all \
|
||
- NICE_DEBUG=all;
|
||
+ NICE_DEBUG=all
|
||
|
||
COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS)
|
||
|
||
@@ -45,8 +45,8 @@ check_PROGRAMS = \
|
||
test-send-recv \
|
||
test-socket-is-based-on \
|
||
test-priority \
|
||
- test-mainloop \
|
||
test-fullmode \
|
||
+ test-different-number-streams \
|
||
test-restart \
|
||
test-fallback \
|
||
test-thread \
|
||
@@ -55,7 +55,9 @@ check_PROGRAMS = \
|
||
test-tcp \
|
||
test-icetcp \
|
||
test-credentials \
|
||
- test-turn
|
||
+ test-turn \
|
||
+ test-drop-invalid \
|
||
+ test-nomination
|
||
|
||
dist_check_SCRIPTS = \
|
||
check-test-fullmode-with-stun.sh \
|
||
@@ -113,6 +115,8 @@ test_mainloop_LDADD = $(COMMON_LDADD)
|
||
|
||
test_fullmode_LDADD = $(COMMON_LDADD)
|
||
|
||
+test_different_number_streams_LDADD = $(COMMON_LDADD)
|
||
+
|
||
test_restart_LDADD = $(COMMON_LDADD)
|
||
|
||
test_fallback_LDADD = $(COMMON_LDADD)
|
||
@@ -129,6 +133,10 @@ test_credentials_LDADD = $(COMMON_LDADD)
|
||
|
||
test_turn_LDADD = $(COMMON_LDADD)
|
||
|
||
+test_drop_invalid_LDADD = $(COMMON_LDADD)
|
||
+
|
||
+test_nomination_LDADD = $(COMMON_LDADD)
|
||
+
|
||
test_gstreamer_CFLAGS = $(AM_CFLAGS) $(GST_CHECK_CFLAGS)
|
||
test_gstreamer_LDADD = -lnice -L$(top_builddir)/nice/.libs $(GLIB_LIBS) $(GUPNP_LIBS) $(GST_CHECK_LIBS) $(GST_LIBS)
|
||
|
||
diff --git a/tests/test-credentials.c b/tests/test-credentials.c
|
||
index 1de4e49..f1ea89d 100644
|
||
--- a/tests/test-credentials.c
|
||
+++ b/tests/test-credentials.c
|
||
@@ -184,6 +184,8 @@ int main (void)
|
||
nice_agent_get_local_credentials (lagent, 1, &ufrag, &password);
|
||
g_assert (g_strcmp0("unicorns", ufrag) == 0);
|
||
g_assert (g_strcmp0("awesome", password) == 0);
|
||
+ g_free (ufrag);
|
||
+ g_free (password);
|
||
|
||
nice_agent_gather_candidates (lagent, 1);
|
||
nice_agent_gather_candidates (ragent, 1);
|
||
diff --git a/tests/test-different-number-streams.c b/tests/test-different-number-streams.c
|
||
new file mode 100644
|
||
index 0000000..7fd4763
|
||
--- /dev/null
|
||
+++ b/tests/test-different-number-streams.c
|
||
@@ -0,0 +1,208 @@
|
||
+#ifdef HAVE_CONFIG_H
|
||
+# include <config.h>
|
||
+#endif
|
||
+
|
||
+#include "agent.h"
|
||
+
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+
|
||
+#define ADD_2_STREAMS TRUE
|
||
+#define USE_SECOND_STREAM TRUE
|
||
+
|
||
+static GMainLoop *global_mainloop = NULL;
|
||
+
|
||
+static guint global_components_ready = 0;
|
||
+static guint global_components_ready_exit = 0;
|
||
+
|
||
+static gboolean timer_cb (gpointer pointer)
|
||
+{
|
||
+ g_debug ("test-different-number-streams:%s: %p", G_STRFUNC, pointer);
|
||
+
|
||
+ /* signal status via a global variable */
|
||
+
|
||
+ /* note: should not be reached, abort */
|
||
+ g_error ("ERROR: test has got stuck, aborting...");
|
||
+
|
||
+ return FALSE;
|
||
+}
|
||
+
|
||
+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data)
|
||
+{
|
||
+ g_debug ("%p: gathering done (stream_id: %u)", agent, stream_id);
|
||
+}
|
||
+
|
||
+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
|
||
+{
|
||
+ g_debug ("%p: component state changed (stream_id: %u, component_id: %u, state: %s)",
|
||
+ agent, stream_id, component_id, nice_component_state_to_string (state));
|
||
+
|
||
+ if (state == NICE_COMPONENT_STATE_READY) {
|
||
+ global_components_ready++;
|
||
+ }
|
||
+
|
||
+ /* signal status via a global variable */
|
||
+ if (global_components_ready == global_components_ready_exit) {
|
||
+ g_debug ("Components ready/failed achieved. Stopping mailoop");
|
||
+ g_main_loop_quit (global_mainloop);
|
||
+ }
|
||
+}
|
||
+
|
||
+static void set_candidates (NiceAgent *from, guint from_stream,
|
||
+ NiceAgent *to, guint to_stream, guint component)
|
||
+{
|
||
+ GSList *cands = NULL, *i;
|
||
+
|
||
+ cands = nice_agent_get_local_candidates (from, from_stream, component);
|
||
+ nice_agent_set_remote_candidates (to, to_stream, component, cands);
|
||
+
|
||
+ for (i = cands; i; i = i->next)
|
||
+ nice_candidate_free ((NiceCandidate *) i->data);
|
||
+ g_slist_free (cands);
|
||
+}
|
||
+
|
||
+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data)
|
||
+{
|
||
+ g_debug ("%p: recv (stream_id: %u, component_id: %u)", agent, stream_id, component_id);
|
||
+}
|
||
+
|
||
+int main (void)
|
||
+{
|
||
+ NiceAgent *lagent, *ragent;
|
||
+ guint timer_id;
|
||
+ guint ls_id, rs_id_1, rs_id_2;
|
||
+ gchar *lufrag = NULL, *lpassword = NULL;
|
||
+ gchar *rufrag1 = NULL, *rpassword1 = NULL, *rufrag2 = NULL, *rpassword2 = NULL;
|
||
+
|
||
+#ifdef G_OS_WIN32
|
||
+ WSADATA w;
|
||
+
|
||
+ WSAStartup(0x0202, &w);
|
||
+#endif
|
||
+
|
||
+ global_mainloop = g_main_loop_new (NULL, FALSE);
|
||
+
|
||
+ /* step: create the agents L and R */
|
||
+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop),
|
||
+ NICE_COMPATIBILITY_GOOGLE);
|
||
+ g_debug ("lagent: %p", lagent);
|
||
+ nice_agent_set_software (lagent, "test-different-number-streams, Left Agent");
|
||
+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL);
|
||
+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
|
||
+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL);
|
||
+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done",
|
||
+ G_CALLBACK (cb_candidate_gathering_done), NULL);
|
||
+ g_signal_connect (G_OBJECT (lagent), "component-state-changed",
|
||
+ G_CALLBACK (cb_component_state_changed), NULL);
|
||
+
|
||
+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop),
|
||
+ NICE_COMPATIBILITY_GOOGLE);
|
||
+ g_debug ("ragent: %p", ragent);
|
||
+ nice_agent_set_software (ragent, "test-different-number-streams, Right Agent");
|
||
+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL);
|
||
+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
|
||
+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL);
|
||
+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done",
|
||
+ G_CALLBACK (cb_candidate_gathering_done), NULL);
|
||
+ g_signal_connect (G_OBJECT (ragent), "component-state-changed",
|
||
+ G_CALLBACK (cb_component_state_changed), NULL);
|
||
+
|
||
+ /* step: add a timer to catch state changes triggered by signals */
|
||
+ timer_id = g_timeout_add (30000, timer_cb, NULL);
|
||
+
|
||
+ ls_id = nice_agent_add_stream (lagent, 2);
|
||
+ g_assert (ls_id > 0);
|
||
+ nice_agent_get_local_credentials(lagent, ls_id, &lufrag, &lpassword);
|
||
+
|
||
+ /* step: attach to mainloop (needed to register the fds) */
|
||
+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
|
||
+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
|
||
+
|
||
+ global_components_ready_exit = 4;
|
||
+
|
||
+ if (ADD_2_STREAMS) {
|
||
+ rs_id_1 = nice_agent_add_stream (ragent, 2);
|
||
+ g_assert (rs_id_1 > 0);
|
||
+ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1);
|
||
+
|
||
+ rs_id_2 = nice_agent_add_stream (ragent, 2);
|
||
+ g_assert (rs_id_2 > 0);
|
||
+ nice_agent_get_local_credentials(ragent, rs_id_2, &rufrag2, &rpassword2);
|
||
+
|
||
+ nice_agent_set_remote_credentials (ragent, rs_id_2, lufrag, lpassword);
|
||
+ nice_agent_set_remote_credentials (lagent, ls_id, rufrag2, rpassword2);
|
||
+
|
||
+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE);
|
||
+ g_assert (nice_agent_gather_candidates (ragent, rs_id_2) == TRUE);
|
||
+ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE);
|
||
+
|
||
+ if (USE_SECOND_STREAM) {
|
||
+ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (ragent, rs_id_2, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP);
|
||
+ } else {
|
||
+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP);
|
||
+ }
|
||
+
|
||
+ /* step: attach to mainloop (needed to register the fds) */
|
||
+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
|
||
+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
|
||
+ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
|
||
+ nice_agent_attach_recv (ragent, rs_id_2, NICE_COMPONENT_TYPE_RTCP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
|
||
+ } else {
|
||
+ rs_id_1 = nice_agent_add_stream (ragent, 2);
|
||
+ g_assert (rs_id_1 > 0);
|
||
+ nice_agent_get_local_credentials(ragent, rs_id_1, &rufrag1, &rpassword1);
|
||
+
|
||
+ nice_agent_set_remote_credentials (ragent, rs_id_1, lufrag, lpassword);
|
||
+ nice_agent_set_remote_credentials (lagent, ls_id, rufrag1, rpassword1);
|
||
+
|
||
+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE);
|
||
+ g_assert (nice_agent_gather_candidates (ragent, rs_id_1) == TRUE);
|
||
+
|
||
+ /* step: attach to mainloop (needed to register the fds) */
|
||
+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
|
||
+ nice_agent_attach_recv (ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
|
||
+
|
||
+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (ragent, rs_id_1, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id_1, NICE_COMPONENT_TYPE_RTCP);
|
||
+ }
|
||
+
|
||
+ /* step: run the mainloop until connectivity checks succeed
|
||
+ * (see timer_cb() above) */
|
||
+ g_main_loop_run (global_mainloop);
|
||
+
|
||
+ g_free (lufrag);
|
||
+ g_free (lpassword);
|
||
+ g_free (rufrag1);
|
||
+ g_free (rpassword1);
|
||
+ g_free (rufrag2);
|
||
+ g_free (rpassword2);
|
||
+ g_object_unref (lagent);
|
||
+ g_object_unref (ragent);
|
||
+
|
||
+ g_main_loop_unref (global_mainloop);
|
||
+ global_mainloop = NULL;
|
||
+
|
||
+ g_source_remove (timer_id);
|
||
+
|
||
+#ifdef G_OS_WIN32
|
||
+ WSACleanup();
|
||
+#endif
|
||
+
|
||
+ return 0;
|
||
+}
|
||
diff --git a/tests/test-drop-invalid.c b/tests/test-drop-invalid.c
|
||
new file mode 100644
|
||
index 0000000..97e3586
|
||
--- /dev/null
|
||
+++ b/tests/test-drop-invalid.c
|
||
@@ -0,0 +1,517 @@
|
||
+/*
|
||
+ * This file is part of the Nice GLib ICE library.
|
||
+ *
|
||
+ * Unit test for ICE full-mode related features.
|
||
+ *
|
||
+ * (C) 2007 Nokia Corporation. All rights reserved.
|
||
+ * Contact: Kai Vehmanen
|
||
+ * (C) 2017 Collabora Ltd
|
||
+ * Contact: Olivier Crete <olivier.crete@collabora.com>
|
||
+ *
|
||
+ * The contents of this file are subject to the Mozilla Public License Version
|
||
+ * 1.1 (the "License"); you may not use this file except in compliance with
|
||
+ * the License. You may obtain a copy of the License at
|
||
+ * http://www.mozilla.org/MPL/
|
||
+ *
|
||
+ * Software distributed under the License is distributed on an "AS IS" basis,
|
||
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
+ * for the specific language governing rights and limitations under the
|
||
+ * License.
|
||
+ *
|
||
+ * The Original Code is the Nice GLib ICE library.
|
||
+ *
|
||
+ * The Initial Developers of the Original Code are Collabora Ltd and Nokia
|
||
+ * Corporation. All Rights Reserved.
|
||
+ *
|
||
+ * Contributors:
|
||
+ * Kai Vehmanen, Nokia
|
||
+ *
|
||
+ * Alternatively, the contents of this file may be used under the terms of the
|
||
+ * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
|
||
+ * case the provisions of LGPL are applicable instead of those above. If you
|
||
+ * wish to allow use of your version of this file only under the terms of the
|
||
+ * LGPL and not to allow others to use your version of this file under the
|
||
+ * MPL, indicate your decision by deleting the provisions above and replace
|
||
+ * them with the notice and other provisions required by the LGPL. If you do
|
||
+ * not delete the provisions above, a recipient may use your version of this
|
||
+ * file under either the MPL or the LGPL.
|
||
+ */
|
||
+#ifdef HAVE_CONFIG_H
|
||
+# include <config.h>
|
||
+#endif
|
||
+
|
||
+#include "agent.h"
|
||
+
|
||
+#include "socket/socket.h"
|
||
+
|
||
+#include <stdlib.h>
|
||
+#include <string.h>
|
||
+
|
||
+
|
||
+
|
||
+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
|
||
+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
|
||
+static guint global_components_ready = 0;
|
||
+static guint global_components_ready_exit = 0;
|
||
+static guint global_components_failed = 0;
|
||
+static guint global_components_failed_exit = 0;
|
||
+static GMainLoop *global_mainloop = NULL;
|
||
+static gboolean global_lagent_gathering_done = FALSE;
|
||
+static gboolean global_ragent_gathering_done = FALSE;
|
||
+static gboolean global_lagent_ibr_received = FALSE;
|
||
+static gboolean global_ragent_ibr_received = FALSE;
|
||
+static int global_lagent_cands = 0;
|
||
+static int global_ragent_cands = 0;
|
||
+static gint global_ragent_read = 0;
|
||
+static guint global_exit_when_ibr_received = 0;
|
||
+
|
||
+static void priv_print_global_status (void)
|
||
+{
|
||
+ g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done);
|
||
+ g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]);
|
||
+ g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]);
|
||
+ g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands);
|
||
+}
|
||
+
|
||
+static gboolean timer_cb (gpointer pointer)
|
||
+{
|
||
+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, pointer);
|
||
+
|
||
+ /* signal status via a global variable */
|
||
+
|
||
+ /* note: should not be reached, abort */
|
||
+ g_error ("ERROR: test has got stuck, aborting...");
|
||
+
|
||
+ return FALSE;
|
||
+}
|
||
+
|
||
+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data)
|
||
+{
|
||
+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, user_data);
|
||
+
|
||
+ /* XXX: dear compiler, these are for you: */
|
||
+ (void)agent; (void)stream_id; (void)component_id; (void)buf;
|
||
+
|
||
+ /* Core of the test
|
||
+ * Assert on any unreleated packet received. This would include anything
|
||
+ * send before the negotiation is over.
|
||
+ */
|
||
+ g_assert (len == 16);
|
||
+ g_assert (strncmp ("1234567812345678", buf, 16) == 0);
|
||
+
|
||
+ if (component_id == 2)
|
||
+ return;
|
||
+
|
||
+ if (GPOINTER_TO_UINT (user_data) == 2) {
|
||
+ g_debug ("right agent received %d bytes, stopping mainloop", len);
|
||
+ global_ragent_read = len;
|
||
+ g_main_loop_quit (global_mainloop);
|
||
+ }
|
||
+}
|
||
+
|
||
+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data)
|
||
+{
|
||
+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
|
||
+
|
||
+ if (GPOINTER_TO_UINT (data) == 1)
|
||
+ global_lagent_gathering_done = TRUE;
|
||
+ else if (GPOINTER_TO_UINT (data) == 2)
|
||
+ global_ragent_gathering_done = TRUE;
|
||
+
|
||
+ if (global_lagent_gathering_done &&
|
||
+ global_ragent_gathering_done)
|
||
+ g_main_loop_quit (global_mainloop);
|
||
+
|
||
+ /* XXX: dear compiler, these are for you: */
|
||
+ (void)agent;
|
||
+}
|
||
+
|
||
+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
|
||
+{
|
||
+ gboolean ready_to_connected = FALSE;
|
||
+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
|
||
+
|
||
+ if (GPOINTER_TO_UINT (data) == 1) {
|
||
+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY &&
|
||
+ state == NICE_COMPONENT_STATE_CONNECTED)
|
||
+ ready_to_connected = TRUE;
|
||
+ global_lagent_state[component_id - 1] = state;
|
||
+ } else if (GPOINTER_TO_UINT (data) == 2) {
|
||
+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY &&
|
||
+ state == NICE_COMPONENT_STATE_CONNECTED)
|
||
+ ready_to_connected = TRUE;
|
||
+ global_ragent_state[component_id - 1] = state;
|
||
+ }
|
||
+
|
||
+ if (state == NICE_COMPONENT_STATE_READY)
|
||
+ global_components_ready++;
|
||
+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected)
|
||
+ global_components_ready--;
|
||
+ if (state == NICE_COMPONENT_STATE_FAILED)
|
||
+ global_components_failed++;
|
||
+
|
||
+ g_debug ("test-drop-invalid: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit);
|
||
+ g_debug ("test-drop-invalid: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit);
|
||
+
|
||
+ /* signal status via a global variable */
|
||
+ if (global_components_ready == global_components_ready_exit &&
|
||
+ global_components_failed == global_components_failed_exit) {
|
||
+ g_debug ("Components ready/failed achieved. Stopping mailoop");
|
||
+ g_main_loop_quit (global_mainloop);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ /* XXX: dear compiler, these are for you: */
|
||
+ (void)agent; (void)stream_id; (void)data; (void)component_id;
|
||
+}
|
||
+
|
||
+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id,
|
||
+ gchar *lfoundation, gchar* rfoundation, gpointer data)
|
||
+{
|
||
+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
|
||
+
|
||
+ if (GPOINTER_TO_UINT (data) == 1)
|
||
+ ++global_lagent_cands;
|
||
+ else if (GPOINTER_TO_UINT (data) == 2)
|
||
+ ++global_ragent_cands;
|
||
+
|
||
+ /* XXX: dear compiler, these are for you: */
|
||
+ (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation;
|
||
+}
|
||
+
|
||
+static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id,
|
||
+ gchar *foundation, gpointer data)
|
||
+{
|
||
+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
|
||
+
|
||
+ /* XXX: dear compiler, these are for you: */
|
||
+ (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation;
|
||
+}
|
||
+
|
||
+static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data)
|
||
+{
|
||
+ g_debug ("test-drop-invalid:%s: %p", G_STRFUNC, data);
|
||
+
|
||
+ if (GPOINTER_TO_UINT (data) == 1)
|
||
+ global_lagent_ibr_received = TRUE;
|
||
+ else if (GPOINTER_TO_UINT (data) == 2)
|
||
+ global_ragent_ibr_received = TRUE;
|
||
+
|
||
+ if (global_exit_when_ibr_received) {
|
||
+ g_debug ("Received initial binding request. Stopping mailoop");
|
||
+ g_main_loop_quit (global_mainloop);
|
||
+ }
|
||
+
|
||
+ /* XXX: dear compiler, these are for you: */
|
||
+ (void)agent; (void)stream_id; (void)data;
|
||
+}
|
||
+
|
||
+static void set_candidates (NiceAgent *from, guint from_stream,
|
||
+ NiceAgent *to, guint to_stream, guint component)
|
||
+{
|
||
+ GSList *cands = NULL;
|
||
+ GSList *peer_cands = NULL;
|
||
+ GSList *item1, *item2;
|
||
+
|
||
+ cands = nice_agent_get_local_candidates (from, from_stream, component);
|
||
+ peer_cands = nice_agent_get_local_candidates (to, to_stream, component);
|
||
+
|
||
+ /*
|
||
+ * Core of the test:
|
||
+ *
|
||
+ * Send packets that shoudl be dropped.
|
||
+ */
|
||
+
|
||
+ for (item1 = cands; item1; item1 = item1->next) {
|
||
+ NiceCandidate *cand = item1->data;
|
||
+ NiceSocket *nicesock = cand->sockptr;
|
||
+
|
||
+ g_assert (nicesock);
|
||
+
|
||
+ for (item2 = peer_cands; item2; item2 = item2->next) {
|
||
+ NiceCandidate *target_cand = item2->data;
|
||
+
|
||
+ nice_socket_send (nicesock, &target_cand->addr, 12, "123456789AB");
|
||
+ }
|
||
+
|
||
+ }
|
||
+
|
||
+ nice_agent_set_remote_candidates (to, to_stream, component, cands);
|
||
+
|
||
+ g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free);
|
||
+ g_slist_free_full (peer_cands, (GDestroyNotify) nice_candidate_free);
|
||
+}
|
||
+
|
||
+static void set_credentials (NiceAgent *lagent, guint lstream,
|
||
+ NiceAgent *ragent, guint rstream)
|
||
+{
|
||
+ gchar *ufrag = NULL, *password = NULL;
|
||
+
|
||
+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password);
|
||
+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password);
|
||
+ g_free (ufrag);
|
||
+ g_free (password);
|
||
+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password);
|
||
+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password);
|
||
+ g_free (ufrag);
|
||
+ g_free (password);
|
||
+}
|
||
+
|
||
+static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed)
|
||
+{
|
||
+ guint ls_id, rs_id;
|
||
+ gint ret;
|
||
+
|
||
+ /* XXX: dear compiler, this is for you */
|
||
+ (void)baseaddr;
|
||
+
|
||
+ /* step: initialize variables modified by the callbacks */
|
||
+ global_components_ready = 0;
|
||
+ global_components_ready_exit = ready;
|
||
+ global_components_failed = 0;
|
||
+ global_components_failed_exit = failed;
|
||
+ global_lagent_gathering_done = FALSE;
|
||
+ global_ragent_gathering_done = FALSE;
|
||
+ global_lagent_ibr_received =
|
||
+ global_ragent_ibr_received = FALSE;
|
||
+ global_lagent_cands =
|
||
+ global_ragent_cands = 0;
|
||
+
|
||
+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
|
||
+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
|
||
+
|
||
+ /* step: add one stream, with RTP+RTCP components, to each agent */
|
||
+ ls_id = nice_agent_add_stream (lagent, 2);
|
||
+
|
||
+ rs_id = nice_agent_add_stream (ragent, 2);
|
||
+ g_assert (ls_id > 0);
|
||
+ g_assert (rs_id > 0);
|
||
+
|
||
+ /* Gather candidates and test nice_agent_set_port_range */
|
||
+ nice_agent_set_port_range (lagent, ls_id, 1, 10000, 10000);
|
||
+ nice_agent_set_port_range (lagent, ls_id, 2, 10001, 10001);
|
||
+ nice_agent_set_port_range (ragent, rs_id, 1, 12345, 12345);
|
||
+ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10001);
|
||
+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE);
|
||
+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == FALSE);
|
||
+ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 1) == NULL);
|
||
+ g_assert (nice_agent_get_local_candidates (ragent, rs_id, 2) == NULL);
|
||
+ nice_agent_set_port_range (ragent, rs_id, 2, 10000, 10002);
|
||
+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE);
|
||
+
|
||
+ {
|
||
+ GSList *cands = NULL, *i;
|
||
+ NiceCandidate *cand = NULL;
|
||
+
|
||
+ cands = nice_agent_get_local_candidates (lagent, ls_id, 1);
|
||
+ g_assert (g_slist_length (cands) == 1);
|
||
+ cand = cands->data;
|
||
+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST);
|
||
+ g_assert (nice_address_get_port (&cand->addr) == 10000);
|
||
+ for (i = cands; i; i = i->next)
|
||
+ nice_candidate_free ((NiceCandidate *) i->data);
|
||
+ g_slist_free (cands);
|
||
+
|
||
+ cands = nice_agent_get_local_candidates (lagent, ls_id, 2);
|
||
+ g_assert (g_slist_length (cands) == 1);
|
||
+ cand = cands->data;
|
||
+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST);
|
||
+ g_assert (nice_address_get_port (&cand->addr) == 10001);
|
||
+ for (i = cands; i; i = i->next)
|
||
+ nice_candidate_free ((NiceCandidate *) i->data);
|
||
+ g_slist_free (cands);
|
||
+
|
||
+ cands = nice_agent_get_local_candidates (ragent, rs_id, 1);
|
||
+ g_assert (g_slist_length (cands) == 1);
|
||
+ cand = cands->data;
|
||
+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST);
|
||
+ g_assert (nice_address_get_port (&cand->addr) == 12345);
|
||
+ for (i = cands; i; i = i->next)
|
||
+ nice_candidate_free ((NiceCandidate *) i->data);
|
||
+ g_slist_free (cands);
|
||
+
|
||
+ cands = nice_agent_get_local_candidates (ragent, rs_id, 2);
|
||
+ g_assert (g_slist_length (cands) == 1);
|
||
+ cand = cands->data;
|
||
+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST);
|
||
+ g_assert (nice_address_get_port (&cand->addr) == 10002);
|
||
+ for (i = cands; i; i = i->next)
|
||
+ nice_candidate_free ((NiceCandidate *) i->data);
|
||
+ g_slist_free (cands);
|
||
+
|
||
+ }
|
||
+
|
||
+ /* step: attach to mainloop (needed to register the fds) */
|
||
+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv,
|
||
+ GUINT_TO_POINTER (1));
|
||
+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv,
|
||
+ GUINT_TO_POINTER (1));
|
||
+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv,
|
||
+ GUINT_TO_POINTER (2));
|
||
+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP,
|
||
+ g_main_loop_get_context (global_mainloop), cb_nice_recv,
|
||
+ GUINT_TO_POINTER (2));
|
||
+
|
||
+ /* step: run mainloop until local candidates are ready
|
||
+ * (see timer_cb() above) */
|
||
+ if (global_lagent_gathering_done != TRUE ||
|
||
+ global_ragent_gathering_done != TRUE) {
|
||
+ g_debug ("test-drop-invalid: Added streams, running mainloop until 'candidate-gathering-done'...");
|
||
+ g_main_loop_run (global_mainloop);
|
||
+ g_assert (global_lagent_gathering_done == TRUE);
|
||
+ g_assert (global_ragent_gathering_done == TRUE);
|
||
+ }
|
||
+
|
||
+ set_credentials (lagent, ls_id, ragent, rs_id);
|
||
+
|
||
+ /* step: pass the remote candidates to agents */
|
||
+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP);
|
||
+
|
||
+ g_debug ("test-drop-invalid: Set properties, next running mainloop until connectivity checks succeed...");
|
||
+
|
||
+ /* step: run the mainloop until connectivity checks succeed
|
||
+ * (see timer_cb() above) */
|
||
+ g_main_loop_run (global_mainloop);
|
||
+
|
||
+ /* note: verify that STUN binding requests were sent */
|
||
+ g_assert (global_lagent_ibr_received == TRUE);
|
||
+ g_assert (global_ragent_ibr_received == TRUE);
|
||
+
|
||
+ /* note: Send a packet from another address */
|
||
+ /* These should also be ignored */
|
||
+ {
|
||
+ NiceCandidate *local_cand = NULL;
|
||
+ NiceCandidate *remote_cand = NULL;
|
||
+ NiceSocket *tmpsock;
|
||
+
|
||
+ g_assert (nice_agent_get_selected_pair (lagent, ls_id, 1, &local_cand,
|
||
+ &remote_cand));
|
||
+ g_assert (local_cand);
|
||
+ g_assert (remote_cand);
|
||
+
|
||
+ tmpsock = nice_udp_bsd_socket_new (NULL);
|
||
+ nice_socket_send (tmpsock, &remote_cand->addr, 4, "ABCD");
|
||
+ nice_socket_send (tmpsock, &local_cand->addr, 5, "ABCDE");
|
||
+ nice_socket_free (tmpsock);
|
||
+ }
|
||
+
|
||
+ /* note: test payload send and receive */
|
||
+ global_ragent_read = 0;
|
||
+ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678");
|
||
+ g_assert (ret != -1);
|
||
+ g_debug ("Sent %d bytes", ret);
|
||
+ g_assert (ret == 16);
|
||
+ while (global_ragent_read != 16)
|
||
+ g_main_context_iteration (NULL, TRUE);
|
||
+ g_assert (global_ragent_read == 16);
|
||
+
|
||
+ g_debug ("test-drop-invalid: Ran mainloop, removing streams...");
|
||
+
|
||
+ /* step: clean up resources and exit */
|
||
+
|
||
+ nice_agent_remove_stream (lagent, ls_id);
|
||
+ nice_agent_remove_stream (ragent, rs_id);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+int main (void)
|
||
+{
|
||
+ NiceAgent *lagent, *ragent; /* agent's L and R */
|
||
+ NiceAddress baseaddr;
|
||
+ int result;
|
||
+ guint timer_id;
|
||
+
|
||
+#ifdef G_OS_WIN32
|
||
+ WSADATA w;
|
||
+
|
||
+ WSAStartup(0x0202, &w);
|
||
+#endif
|
||
+
|
||
+ global_mainloop = g_main_loop_new (NULL, FALSE);
|
||
+
|
||
+ /* step: create the agents L and R */
|
||
+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop),
|
||
+ NICE_COMPATIBILITY_RFC5245);
|
||
+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop),
|
||
+ NICE_COMPATIBILITY_RFC5245);
|
||
+
|
||
+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL);
|
||
+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL);
|
||
+
|
||
+
|
||
+ nice_agent_set_software (lagent, "test-drop-invalid, Left Agent");
|
||
+ nice_agent_set_software (ragent, "test-drop-invalid, Right Agent");
|
||
+
|
||
+ /* step: add a timer to catch state changes triggered by signals */
|
||
+ timer_id = g_timeout_add (30000, timer_cb, NULL);
|
||
+
|
||
+ /* step: specify which local interface to use */
|
||
+ if (!nice_address_set_from_string (&baseaddr, "127.0.0.1"))
|
||
+ g_assert_not_reached ();
|
||
+ nice_agent_add_local_address (lagent, &baseaddr);
|
||
+ nice_agent_add_local_address (ragent, &baseaddr);
|
||
+
|
||
+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done",
|
||
+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1));
|
||
+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done",
|
||
+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2));
|
||
+ g_signal_connect (G_OBJECT (lagent), "component-state-changed",
|
||
+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1));
|
||
+ g_signal_connect (G_OBJECT (ragent), "component-state-changed",
|
||
+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2));
|
||
+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair",
|
||
+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1));
|
||
+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair",
|
||
+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2));
|
||
+ g_signal_connect (G_OBJECT (lagent), "new-candidate",
|
||
+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1));
|
||
+ g_signal_connect (G_OBJECT (ragent), "new-candidate",
|
||
+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2));
|
||
+ g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received",
|
||
+ G_CALLBACK (cb_initial_binding_request_received),
|
||
+ GUINT_TO_POINTER (1));
|
||
+ g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received",
|
||
+ G_CALLBACK (cb_initial_binding_request_received),
|
||
+ GUINT_TO_POINTER (2));
|
||
+
|
||
+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL);
|
||
+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL);
|
||
+
|
||
+
|
||
+ /* step: run test the first time */
|
||
+ g_debug ("test-drop-invalid: TEST STARTS / running test for the 1st time");
|
||
+ result = run_full_test (lagent, ragent, &baseaddr, 4 ,0);
|
||
+ priv_print_global_status ();
|
||
+ g_assert (result == 0);
|
||
+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY);
|
||
+ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY);
|
||
+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY);
|
||
+ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY);
|
||
+ /* When using TURN, we get peer reflexive candidates for the host cands
|
||
+ that we removed so we can get another new_selected_pair signal later
|
||
+ depending on timing/racing, we could double (or not) the amount we expected
|
||
+ */
|
||
+
|
||
+ /* note: verify that correct number of local candidates were reported */
|
||
+ g_assert (global_lagent_cands == 2);
|
||
+ g_assert (global_ragent_cands == 2);
|
||
+
|
||
+ g_object_unref (lagent);
|
||
+ g_object_unref (ragent);
|
||
+
|
||
+ g_main_loop_unref (global_mainloop);
|
||
+ global_mainloop = NULL;
|
||
+
|
||
+ g_source_remove (timer_id);
|
||
+#ifdef G_OS_WIN32
|
||
+ WSACleanup();
|
||
+#endif
|
||
+ return result;
|
||
+}
|
||
diff --git a/tests/test-gstreamer.c b/tests/test-gstreamer.c
|
||
index 74d7133..f060efc 100644
|
||
--- a/tests/test-gstreamer.c
|
||
+++ b/tests/test-gstreamer.c
|
||
@@ -1,7 +1,7 @@
|
||
/*
|
||
* This file is part of the Nice GLib ICE library.
|
||
*
|
||
- * (C) 1015 Kurento.
|
||
+ * (C) 2015 Kurento.
|
||
* Contact: Jose Antonio Santos Cadenas
|
||
*
|
||
* The contents of this file are subject to the Mozilla Public License Version
|
||
@@ -34,7 +34,7 @@
|
||
*/
|
||
|
||
#include <gst/check/gstcheck.h>
|
||
-#include <nice/agent.h>
|
||
+#include "agent.h"
|
||
|
||
#define RTP_HEADER_SIZE 12
|
||
#define RTP_PAYLOAD_SIZE 1024
|
||
diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c
|
||
deleted file mode 100644
|
||
index 7c52daa..0000000
|
||
--- a/tests/test-mainloop.c
|
||
+++ /dev/null
|
||
@@ -1,108 +0,0 @@
|
||
-/*
|
||
- * This file is part of the Nice GLib ICE library.
|
||
- *
|
||
- * (C) 2006, 2007 Collabora Ltd.
|
||
- * Contact: Dafydd Harries
|
||
- * (C) 2006, 2007 Nokia Corporation. All rights reserved.
|
||
- * Contact: Kai Vehmanen
|
||
- *
|
||
- * The contents of this file are subject to the Mozilla Public License Version
|
||
- * 1.1 (the "License"); you may not use this file except in compliance with
|
||
- * the License. You may obtain a copy of the License at
|
||
- * http://www.mozilla.org/MPL/
|
||
- *
|
||
- * Software distributed under the License is distributed on an "AS IS" basis,
|
||
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
- * for the specific language governing rights and limitations under the
|
||
- * License.
|
||
- *
|
||
- * The Original Code is the Nice GLib ICE library.
|
||
- *
|
||
- * The Initial Developers of the Original Code are Collabora Ltd and Nokia
|
||
- * Corporation. All Rights Reserved.
|
||
- *
|
||
- * Contributors:
|
||
- * Dafydd Harries, Collabora Ltd.
|
||
- * Kai Vehmanen, Nokia
|
||
- *
|
||
- * Alternatively, the contents of this file may be used under the terms of the
|
||
- * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
|
||
- * case the provisions of LGPL are applicable instead of those above. If you
|
||
- * wish to allow use of your version of this file only under the terms of the
|
||
- * LGPL and not to allow others to use your version of this file under the
|
||
- * MPL, indicate your decision by deleting the provisions above and replace
|
||
- * them with the notice and other provisions required by the LGPL. If you do
|
||
- * not delete the provisions above, a recipient may use your version of this
|
||
- * file under either the MPL or the LGPL.
|
||
- */
|
||
-#ifdef HAVE_CONFIG_H
|
||
-# include <config.h>
|
||
-#endif
|
||
-
|
||
-#include <string.h>
|
||
-
|
||
-#include <nice/nice.h>
|
||
-#include "socket/socket.h"
|
||
-
|
||
-static GMainLoop *loop = NULL;
|
||
-
|
||
-static void
|
||
-recv_cb (
|
||
- NiceAgent *agent,
|
||
- guint stream_id,
|
||
- guint component_id,
|
||
- guint len,
|
||
- gchar *buf,
|
||
- gpointer data)
|
||
-{
|
||
- g_assert (agent != NULL);
|
||
- g_assert (stream_id == 1);
|
||
- g_assert (component_id == 1);
|
||
- g_assert (len == 6);
|
||
- g_assert (0 == strncmp (buf, "\x80hello", len));
|
||
- g_assert (42 == GPOINTER_TO_UINT (data));
|
||
- g_main_loop_quit (loop);
|
||
-}
|
||
-
|
||
-int
|
||
-main (void)
|
||
-{
|
||
- NiceAgent *agent;
|
||
- NiceAddress addr;
|
||
- guint stream;
|
||
-
|
||
- nice_address_init (&addr);
|
||
-
|
||
- loop = g_main_loop_new (NULL, FALSE);
|
||
-
|
||
- agent = nice_agent_new (g_main_loop_get_context (loop), NICE_COMPATIBILITY_RFC5245);
|
||
- nice_address_set_ipv4 (&addr, 0x7f000001);
|
||
- nice_agent_add_local_address (agent, &addr);
|
||
- stream = nice_agent_add_stream (agent, 1);
|
||
- nice_agent_gather_candidates (agent, stream);
|
||
-
|
||
- // attach to default main context
|
||
- nice_agent_attach_recv (agent, stream, NICE_COMPONENT_TYPE_RTP,
|
||
- g_main_loop_get_context (loop), recv_cb, GUINT_TO_POINTER (42));
|
||
-
|
||
- {
|
||
- NiceCandidate *candidate;
|
||
- GSList *candidates, *i;
|
||
-
|
||
- candidates = nice_agent_get_local_candidates (agent, 1, 1);
|
||
- candidate = candidates->data;
|
||
-
|
||
- nice_socket_send (candidate->sockptr, &(candidate->addr), 6, "\x80hello");
|
||
- for (i = candidates; i; i = i->next)
|
||
- nice_candidate_free ((NiceCandidate *) i->data);
|
||
- g_slist_free (candidates);
|
||
- }
|
||
-
|
||
- g_main_loop_run (loop);
|
||
-
|
||
- nice_agent_remove_stream (agent, stream);
|
||
- g_object_unref (agent);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
diff --git a/tests/test-nomination.c b/tests/test-nomination.c
|
||
new file mode 100644
|
||
index 0000000..bf21557
|
||
--- /dev/null
|
||
+++ b/tests/test-nomination.c
|
||
@@ -0,0 +1,263 @@
|
||
+#include <stdlib.h>
|
||
+#include <stdio.h>
|
||
+#include <string.h>
|
||
+
|
||
+#include <gio/gio.h>
|
||
+#include <agent.h>
|
||
+
|
||
+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
|
||
+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
|
||
+static guint global_components_ready = 0;
|
||
+static gboolean global_lagent_gathering_done = FALSE;
|
||
+static gboolean global_ragent_gathering_done = FALSE;
|
||
+static int global_lagent_cands = 0;
|
||
+static int global_ragent_cands = 0;
|
||
+
|
||
+static gboolean timer_cb (gpointer pointer)
|
||
+{
|
||
+ g_debug ("test-nomination:%s: %p", G_STRFUNC, pointer);
|
||
+
|
||
+ /* signal status via a global variable */
|
||
+
|
||
+ /* note: should not be reached, abort */
|
||
+ g_error ("ERROR: test has got stuck, aborting...");
|
||
+
|
||
+ return FALSE;
|
||
+}
|
||
+
|
||
+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data)
|
||
+{
|
||
+ g_debug ("test-nomination:%s: %p", G_STRFUNC, user_data);
|
||
+
|
||
+ /* XXX: dear compiler, these are for you: */
|
||
+ (void)agent; (void)stream_id; (void)component_id; (void)buf;
|
||
+
|
||
+ /*
|
||
+ * Lets ignore stun packets that got through
|
||
+ */
|
||
+ if (len < 8)
|
||
+ return;
|
||
+ if (strncmp ("12345678", buf, 8))
|
||
+ return;
|
||
+
|
||
+ if (component_id != 1)
|
||
+ return;
|
||
+}
|
||
+
|
||
+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data)
|
||
+{
|
||
+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data);
|
||
+
|
||
+ if (GPOINTER_TO_UINT (data) == 1)
|
||
+ global_lagent_gathering_done = TRUE;
|
||
+ else if (GPOINTER_TO_UINT (data) == 2)
|
||
+ global_ragent_gathering_done = TRUE;
|
||
+}
|
||
+
|
||
+
|
||
+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
|
||
+{
|
||
+ gboolean ready_to_connected = FALSE;
|
||
+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data);
|
||
+
|
||
+ if (GPOINTER_TO_UINT (data) == 1) {
|
||
+ if (global_lagent_state[component_id - 1] == NICE_COMPONENT_STATE_READY &&
|
||
+ state == NICE_COMPONENT_STATE_CONNECTED)
|
||
+ ready_to_connected = TRUE;
|
||
+ global_lagent_state[component_id - 1] = state;
|
||
+ } else if (GPOINTER_TO_UINT (data) == 2) {
|
||
+ if (global_ragent_state[component_id - 1] == NICE_COMPONENT_STATE_READY &&
|
||
+ state == NICE_COMPONENT_STATE_CONNECTED)
|
||
+ ready_to_connected = TRUE;
|
||
+ global_ragent_state[component_id - 1] = state;
|
||
+ }
|
||
+
|
||
+ if (state == NICE_COMPONENT_STATE_READY)
|
||
+ global_components_ready++;
|
||
+ else if (state == NICE_COMPONENT_STATE_CONNECTED && ready_to_connected)
|
||
+ global_components_ready--;
|
||
+ g_assert (state != NICE_COMPONENT_STATE_FAILED);
|
||
+
|
||
+ g_debug ("test-nomination: checks READY %u.", global_components_ready);
|
||
+}
|
||
+
|
||
+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id,
|
||
+ guint component_id, gchar *lfoundation, gchar* rfoundation, gpointer data)
|
||
+{
|
||
+ g_debug ("test-nomination:%s: %p", G_STRFUNC, data);
|
||
+
|
||
+ if (GPOINTER_TO_UINT (data) == 1)
|
||
+ ++global_lagent_cands;
|
||
+ else if (GPOINTER_TO_UINT (data) == 2)
|
||
+ ++global_ragent_cands;
|
||
+}
|
||
+
|
||
+static void set_candidates (NiceAgent *from, guint from_stream,
|
||
+ NiceAgent *to, guint to_stream, guint component)
|
||
+{
|
||
+ GSList *cands = NULL, *i;
|
||
+
|
||
+ cands = nice_agent_get_local_candidates (from, from_stream, component);
|
||
+ nice_agent_set_remote_candidates (to, to_stream, component, cands);
|
||
+
|
||
+ for (i = cands; i; i = i->next)
|
||
+ nice_candidate_free ((NiceCandidate *) i->data);
|
||
+ g_slist_free (cands);
|
||
+}
|
||
+
|
||
+static void set_credentials (NiceAgent *lagent, guint lstream,
|
||
+ NiceAgent *ragent, guint rstream)
|
||
+{
|
||
+ gchar *ufrag = NULL, *password = NULL;
|
||
+
|
||
+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password);
|
||
+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password);
|
||
+ g_free (ufrag);
|
||
+ g_free (password);
|
||
+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password);
|
||
+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password);
|
||
+ g_free (ufrag);
|
||
+ g_free (password);
|
||
+}
|
||
+
|
||
+static void
|
||
+run_test(NiceNominationMode l_nomination_mode,
|
||
+ NiceNominationMode r_nomination_mode)
|
||
+{
|
||
+ NiceAgent *lagent, *ragent; /* agent's L and R */
|
||
+ const gchar *localhost;
|
||
+ NiceAddress localaddr;
|
||
+ guint ls_id, rs_id;
|
||
+ gulong timer_id;
|
||
+
|
||
+ localhost = "127.0.0.1";
|
||
+
|
||
+ /* step: initialize variables modified by the callbacks */
|
||
+ global_components_ready = 0;
|
||
+ global_lagent_gathering_done = FALSE;
|
||
+ global_ragent_gathering_done = FALSE;
|
||
+ global_lagent_cands = global_ragent_cands = 0;
|
||
+
|
||
+ lagent = nice_agent_new_full (NULL,
|
||
+ NICE_COMPATIBILITY_RFC5245,
|
||
+ l_nomination_mode == NICE_NOMINATION_MODE_REGULAR ?
|
||
+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0);
|
||
+
|
||
+ ragent = nice_agent_new_full (NULL,
|
||
+ NICE_COMPATIBILITY_RFC5245,
|
||
+ r_nomination_mode == NICE_NOMINATION_MODE_REGULAR ?
|
||
+ NICE_AGENT_OPTION_REGULAR_NOMINATION : 0);
|
||
+
|
||
+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL);
|
||
+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL);
|
||
+
|
||
+ g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL);
|
||
+ g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL);
|
||
+ nice_agent_set_software (lagent, "Test-nomination, Left Agent");
|
||
+ nice_agent_set_software (ragent, "Test-nomination, Right Agent");
|
||
+
|
||
+ timer_id = g_timeout_add (30000, timer_cb, NULL);
|
||
+
|
||
+ if (!nice_address_set_from_string (&localaddr, localhost))
|
||
+ g_assert_not_reached ();
|
||
+ nice_agent_add_local_address (lagent, &localaddr);
|
||
+ nice_agent_add_local_address (ragent, &localaddr);
|
||
+
|
||
+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done",
|
||
+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1));
|
||
+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done",
|
||
+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2));
|
||
+ g_signal_connect (G_OBJECT (lagent), "component-state-changed",
|
||
+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1));
|
||
+ g_signal_connect (G_OBJECT (ragent), "component-state-changed",
|
||
+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2));
|
||
+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair",
|
||
+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1));
|
||
+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair",
|
||
+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2));
|
||
+
|
||
+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
|
||
+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
|
||
+
|
||
+ ls_id = nice_agent_add_stream (lagent, 1);
|
||
+ rs_id = nice_agent_add_stream (ragent, 1);
|
||
+ g_assert (ls_id > 0);
|
||
+ g_assert (rs_id > 0);
|
||
+
|
||
+ /* Gather candidates and test nice_agent_set_port_range */
|
||
+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE);
|
||
+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE);
|
||
+
|
||
+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP,
|
||
+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (1));
|
||
+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP,
|
||
+ g_main_context_default (), cb_nice_recv, GUINT_TO_POINTER (2));
|
||
+
|
||
+ g_debug ("test-nomination: Added streams, running context until 'candidate-gathering-done'...");
|
||
+ while (!global_lagent_gathering_done)
|
||
+ g_main_context_iteration (NULL, TRUE);
|
||
+ g_assert (global_lagent_gathering_done == TRUE);
|
||
+ while (!global_ragent_gathering_done)
|
||
+ g_main_context_iteration (NULL, TRUE);
|
||
+ g_assert (global_ragent_gathering_done == TRUE);
|
||
+
|
||
+ set_credentials (lagent, ls_id, ragent, rs_id);
|
||
+
|
||
+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
|
||
+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
|
||
+
|
||
+ while (global_lagent_state[0] != NICE_COMPONENT_STATE_READY ||
|
||
+ global_ragent_state[0] != NICE_COMPONENT_STATE_READY)
|
||
+ g_main_context_iteration (NULL, TRUE);
|
||
+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY);
|
||
+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY);
|
||
+
|
||
+ nice_agent_remove_stream (lagent, ls_id);
|
||
+ nice_agent_remove_stream (ragent, rs_id);
|
||
+
|
||
+ g_source_remove (timer_id);
|
||
+
|
||
+ g_clear_object(&lagent);
|
||
+ g_clear_object(&ragent);
|
||
+}
|
||
+
|
||
+static void
|
||
+regular (void)
|
||
+{
|
||
+ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_REGULAR);
|
||
+}
|
||
+
|
||
+static void
|
||
+aggressive (void)
|
||
+{
|
||
+ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_AGGRESSIVE);
|
||
+}
|
||
+
|
||
+static void
|
||
+mixed_ra (void)
|
||
+{
|
||
+ run_test(NICE_NOMINATION_MODE_REGULAR, NICE_NOMINATION_MODE_AGGRESSIVE);
|
||
+}
|
||
+
|
||
+static void
|
||
+mixed_ar (void)
|
||
+{
|
||
+ run_test(NICE_NOMINATION_MODE_AGGRESSIVE, NICE_NOMINATION_MODE_REGULAR);
|
||
+}
|
||
+
|
||
+int
|
||
+main (int argc, char **argv)
|
||
+{
|
||
+ int ret;
|
||
+
|
||
+ g_test_init (&argc, &argv, NULL);
|
||
+
|
||
+ g_test_add_func ("/nice/nomination/regular", regular);
|
||
+ g_test_add_func ("/nice/nomination/aggressive", aggressive);
|
||
+ g_test_add_func ("/nice/nomination/mixed_ra", mixed_ra);
|
||
+ g_test_add_func ("/nice/nomination/mixed_ar", mixed_ar);
|
||
+
|
||
+ ret = g_test_run ();
|
||
+
|
||
+ return ret;
|
||
+}
|
||
diff --git a/tests/test-restart.c b/tests/test-restart.c
|
||
index c2cbe9a..afc51b6 100644
|
||
--- a/tests/test-restart.c
|
||
+++ b/tests/test-restart.c
|
||
@@ -301,6 +301,11 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *
|
||
nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP, cands);
|
||
cdes.addr = laddr_rtcp;
|
||
nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP, cands);
|
||
+ /* This role switch request will be effective after restart. We test
|
||
+ * here that the role cannot be externally modified after conncheck
|
||
+ * has started. */
|
||
+ g_object_set (G_OBJECT (ragent), "controlling-mode", TRUE, NULL);
|
||
+ g_assert (ragent->controlling_mode == FALSE);
|
||
|
||
g_debug ("test-restart: Set properties, next running mainloop until connectivity checks succeed...");
|
||
|
||
@@ -329,10 +334,18 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *
|
||
global_ragent_read = 0;
|
||
g_assert (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678") == 16);
|
||
|
||
+ /* Both agent have a distinct role at the end of the conncheck */
|
||
+ g_assert (lagent->controlling_mode == TRUE);
|
||
+ g_assert (ragent->controlling_mode == FALSE);
|
||
/* step: restart agents, exchange updated credentials */
|
||
tie_breaker = ragent->tie_breaker;
|
||
nice_agent_restart (ragent);
|
||
g_assert (tie_breaker != ragent->tie_breaker);
|
||
+ /* This role switch of ragent should be done now, and both agents
|
||
+ * have now the same role, which should generate a role conflict
|
||
+ * resolution situation */
|
||
+ g_assert (lagent->controlling_mode == TRUE);
|
||
+ g_assert (ragent->controlling_mode == TRUE);
|
||
nice_agent_restart (lagent);
|
||
{
|
||
gchar *ufrag = NULL, *password = NULL;
|
||
@@ -375,6 +388,8 @@ static int run_restart_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *
|
||
/* note: verify binding requests were resent after restart */
|
||
g_assert (global_lagent_ibr_received == TRUE);
|
||
g_assert (global_ragent_ibr_received == TRUE);
|
||
+ /* note: verify that a role switch occured for one of the agents */
|
||
+ g_assert (ragent->controlling_mode != lagent->controlling_mode);
|
||
|
||
g_debug ("test-restart: Ran mainloop, removing streams...");
|
||
|