LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File libnice-0.1.13-20160610.patch of Package libnice (Project home:jblunck:sipe)

commit 30a0c230ae9b70c572060ad3037f68e102e4759a
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Jun 6 18:31:22 2016 -0400

    conncheck: Remove pairs before freeing candidate
    
    Remove the whole pair before the candidate is
    to be freed.
    
    https://phabricator.freedesktop.org/T7460

commit 71f7ed3eda829c3dc6afe9ed013c0ab826a1aa40
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri Feb 19 15:01:03 2016 -0500

    stun timer: Do 7 retransmissions as recommended
    
    Also reduce the normal timeout to make the test bearable.
    
    This is what RFC 5389 section 7.2.1
    
    Differential Revision: https://phabricator.freedesktop.org/D1056
    Maniphest Task: https://phabricator.freedesktop.org/T3339

commit dc1e1b7a1b258fb54ba582d2fe77ccd159c9fe88
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Jun 6 16:21:54 2016 -0400

    timer: Maximum retransmission should include the original one
    
    We really care about the maximum transmissions, the first one counts.

commit fad72879fa4a0896c55ac6fc5f77f6c05e369a2b
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri Jun 3 18:42:59 2016 -0400

    pseudotcp: it's still a GObject

commit f645ea6b11c167b1d7f4c5034f79664bdb8706d6
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Apr 14 13:32:51 2016 +0200

    pseudotcp: Make sure duplicate ack representing losses have no data
    
    If they have data in them, they won't be recognized as duplicate acks by
    the sender.

commit adba0d4a51f4e0deac888ab08f7976cef70a8e99
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Apr 14 09:50:09 2016 +0200

    pseudotcp: Implement NewReno timestamp heuristic
    
    This allows the sender to enter fast retransmit after a timeout because
    it can now detect that three duplicate acks are caused by a packet loss.
    
    As specific in RFC 6582 section 4.2.

commit 1f532aeb6bf5b5b3042c445e677988f3327b1cb5
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Wed Apr 6 10:46:46 2016 +0300

    pseudotcp: Set min RTO to 1 second
    
    This is recommended by RFC 6298

commit b5952012bc5b403550f8dd9945d92747323acfc4
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Wed Apr 6 01:59:36 2016 +0300

    pseudotcp: Implement full NewReno

commit e31932bdf25ce545a88fe6078b9557bc4d9e6365
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue Feb 2 16:59:18 2016 -0500

    pseudotcp: Make debug more useful

commit 8ccb2c1711a2e5cdebf9411764fd92d5a089ffbf
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue Jan 12 20:14:48 2016 -0500

    pseudotcp: Separate default and maximum MTU
    
    Accept packets much beyond the default MTU, but
    set a reasonable default MTU for sending of 1400

commit cb644b2baa681f510a79e158cd50c490dcfa5186
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Dec 24 01:15:59 2015 -0500

    pseudotcp: close local socket on initial transmission error
    
    This is required as no retransmissions will happen

commit 23331ff2add5a60d611eee2093614d1fb8749164
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Sep 17 21:26:36 2015 -0400

    pseudotcp: Export more symbols for PseudoTCP

commit 026c15a838554c30ea96a59b08a2064b61d62736
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Sep 17 15:00:27 2015 -0400

    pseudotcp: Make structs definitions private

commit 11d4bb9783a69363de80ff49638030ba892a93fe
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Tue Jun 23 15:42:33 2015 +0100

    pseudotcp: Correct behaviour of buffer size methods when part-closed
    
    Correct the behaviour of pseudo_tcp_socket_get_available_bytes() and
    pseudo_tcp_get_available_send_space() when the socket is not in
    TCP_ESTABLISHED state. It’s still permissible to send and receive up
    until the local side calls pseudo_tcp_socket_close(), which means we
    may be in state TCP_ESTABLISHED *or TCP_CLOSE_WAIT*.

commit a9a149f529b3165543b52260d40a7855401841da
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Fri Jul 31 14:28:51 2015 +0100

    pseudotcp: Fix EOS checks in high packet loss situations
    
    The state tracking previously assumed that if a FIN packet was sent, the
    other side received it and the preceding packets, and hence it was
    correct to sent an RST if an unexpected packet (such as a delayed
    SYN-ACK) was received.
    
    In cases where there is high packet loss, this won’t work. For example,
    peer A sends a SYN, it is received and peer B replies with a SYN-ACK
    which is also received; then peer A sends its data and a FIN, which are
    both dropped. Since it hasn’t received anything since the original SYN,
    peer B resends its SYN-ACK. If that is received, peer A was incorrectly
    treating it as an erroneous packet, and would then send a RST. In actual
    fact, it should take this as a signal that the data and FIN packets were
    dropped, and should resend them.
    
    TODO: Add unit tests

commit 4dc2b5d9a01e3314d229fb9aa80884d84c45c1f0
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Fri Jul 31 14:19:30 2015 +0100

    pseudotcp: Propagate error codes from transmit() to callers
    
    Otherwise we can’t easily differentiate between different transmission
    failures; for example: underlying socket failures, versus retransmission
    timeouts.

commit 8a6bc000a5d7395bd9c4ff9942be26bd4f7d2e44
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Tue Jun 23 15:40:13 2015 +0100

    pseudotcp: Add more debug info on closing down a pseudo-TCP socket

commit a72a93e51dba5d239e0607380bb4799cf1b0caca
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Jun 24 14:06:05 2015 +0100

    pseudotcp: Fix pseudo_tcp_socket_recv() in state TCP_CLOSE_WAIT
    
    Previously, pseudo_tcp_socket_recv() would start returning 0 (EOS) as
    soon as a FIN segment was received from the peer, even if there was
    unread data already in the receive buffer.
    
    Instead, the unread data should all be accessible before
    pseudo_tcp_socket_recv() starts returning 0.

commit 02699917641922c9f1d337e3102f13a1ea1d83c4
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Jun 24 13:52:16 2015 +0100

    pseudotcp: Fix retransmission of segments before handling a FIN
    
    Previously, if peer A transmitted one or more data segments (1),
    followed by a FIN segment (2) to peer B, and segments 1 were
    dropped, peer B would not request retransmission of them and would
    instead continue with the FIN handshake. This effectively meant
    segments 1 were lost without peer B realising.
    
    Fix this by only handling the FIN segment once its sequence number is
    acknowledged in the receive window.

commit b58e852de6183f2bda4e7d322a35d18edf5cbbed
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Jun 2 19:22:50 2016 -0400

    socket: Assert trying to use free'd socket
    
    Cleanly returnign makes no sense and may hide
    worse problems.

commit baab2c3c7049f984cdca6ed622059c62ce8cebf7
Author: Misha Uliutin <mishau@microsoft.com>
Date:   Mon Apr 25 09:59:48 2016 +0300

    component: Fix set TCP selected remote candidate
    
    https://phabricator.freedesktop.org/T7407

commit 6329509b86f3a6877a39fb59b7a1b535408db0ce
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Jun 2 19:00:17 2016 -0400

    agent: Parse TURN packet on the right socket
    
    https://phabricator.freedesktop.org/T99

commit 2f0daa030a69ebb2dea4c1a6fc47699d0f6828aa
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Jun 2 17:34:27 2016 -0400

    tests: Add TURN test
    
    This test depends on rfc5766-turn-server which must
    be installed for this test to run.

commit 75d332cc4b7d9ee76bdf92b38f9cc3f6dd94b796
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Tue May 31 11:42:44 2016 +0000

    conncheck: mark discovered pairs with TCP passive as valid
    
    Doing so similarly to priv_process_response_check_for_reflexive(),
    which also sets valid flag on discovered peer reflexive pairs.
    
    Fixes a regression in previously working scenario.
    Differential Revision: https://phabricator.freedesktop.org/D1035

commit 1a23476513d487bb09afbc7fb4853169399312d7
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Wed Jun 1 08:52:41 2016 +0000

    test-icetcp: don't be sensitive to the signal order
    
    "new-selected-pair" may be emitted after "component-state-changed"
    to READY, by which time the main loop might have gotten quit in
    cb_component_state_changed(). Consequently, cb_new_selected_pair() could
    miss to register the selected pair, ultimately leading to an assertion
    failure in main().
    
    We should wait for both selected pair and state change events to occur
    before stopping the main loop.
    
    Differential Revision: https://phabricator.freedesktop.org/D1044

commit b559384734deb9ec934f5ff69814f3d90c6a36c1
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue May 31 17:31:18 2016 -0400

    Revert "WIP"
    
    This reverts commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a.

commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue May 31 17:31:12 2016 -0400

    WIP

commit 955323915c43b1a066399e61d0ab091f0b1d112b
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Tue May 31 09:27:03 2016 +0000

    conncheck: fix pruning conn checks with TCP active sockets
    
    TCP active socket makes a NiceSocket for each peer in conn_check_send()
    and this new socket is then stored as CandidateCheckPair's 'sockptr'.
    We thus have to look also at the 'sockptr' value when eliminating
    sockets which have received HUP from connection checks.
    Differential Revision: https://phabricator.freedesktop.org/D1034

commit 2112ebba886d15fd96cc36b9fe3196834acaa892
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue Mar 8 15:37:05 2016 -0500

    agent: Remove socket on read error
    
    If a socket returned an error, remove it.

commit 1949b89f3de6e45616187e86f542d26a003ea7a6
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri Jan 15 22:40:27 2016 -0500

    component: Add API to cleanly remove a base socket

commit 93330f1f97e4f2a9ff09b602765e620cb279574b
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Sat Feb 27 03:35:27 2016 -0500

    agent: Fix udp-turn-over-tcp
    
    The TCP-based turns don't come pre-parsed unlike
    the UDP variants!

commit 7f6ddac880ee07530ae59f4c64a0a17417fb9e24
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Wed Jan 27 18:56:13 2016 -0500

    agent: Add force-relay property to force messages through the relay
    
    This allows implementing WebRTC privacy mode.

commit c69d479edfaeb461ff2bc61cf7257ce0c2d273da
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Wed Feb 10 16:29:57 2016 -0500

    conncheck: Start conncheck on server reply if needed
    
    This only really applies in the force relay mode where there are
    no local candidates.

commit 1513ce23ff4279dad16e177a3fc779cb61074fa1
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Feb 8 16:44:47 2016 -0500

    Replace g_malloc/g_new with g_alloca where possible
    
    This should reduce the overhead a bit.

commit 524c1090cc2813bcb1be6f7f29a894c742a608f7
Author: Fabrice Bellet <fabrice@bellet.info>
Date:   Wed Apr 20 10:17:05 2016 +0000

    conncheck: explain some corner cases
    
    This patch give details why some exceptions to the ICE spec are needed.
    
    Differential Revision: https://phabricator.freedesktop.org/D876

commit b05debeb95c13d162286c0a5c4076eee2ae7cf51
Author: Fabrice Bellet <fabrice@bellet.info>
Date:   Fri May 27 19:15:39 2016 -0400

    conncheck: add a debug dump of the whole stream check list
    
    https://phabricator.freedesktop.org/D814

commit 71e271095032bd50ac2be2b5d60f4beb15801fa0
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri May 27 18:50:59 2016 -0400

    conncheck: fix the replay of early incoming connchecks
    
    This patch fixes a bug in the way the list of incoming checks
    is handled. The code purges this list too early, before all ichecks
    for a given component are processed. It happens because the component
    is computed from each pair of the check list, instead of being passed
    as a fixed parameter of the function.
    
    Differential Revision: https://phabricator.freedesktop.org/D882

commit acdc0b8bb3cd2d48afe82c8dd8396a3c245191d9
Author: Fabrice Bellet <fabrice@bellet.info>
Date:   Wed Apr 20 09:23:14 2016 +0000

    stun: fix ice role conflict handling
    
    This patch fixes the role conflict handling in stun ICE usage,
    according to RFC 5245, by adding including missing cases in the
    test. The role switch not only depends of the comparison of the
    stun ice-controlling/controlled attrib with the agent tie breaker
    value, but it also depends on the current role of the agent.
    
    This patch also changes the value returned by
    stun_usage_ice_conncheck_create_reply() when a role conflict exists
    but doesn't change the role of the agent, causing an error stun
    response. Previously, this case could not be differenciated by the
    caller from a case with no role conflict. Now by examinating the
    return value, and whether the control param changed, the caller
    can check the four possibles situations. The stun test suite is
    updated to match this change.
    
    Differential Revision: https://phabricator.freedesktop.org/D873

commit c90f93838db6a315ab2cbedaa92fed3f277b2103
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri May 27 17:26:06 2016 -0400

    conncheck: Make previous commit compile
    
    https://phabricator.freedesktop.org/T3324

commit d252feb553a423ee81482ff6b87e0033d9c046a6
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Feb 8 18:49:42 2016 -0500

    discovery: Make sure each candidate has a unique priority
    
    This should fix compliance with RFC 5245 Section 4.1.2
    
    https://phabricator.freedesktop.org/T3324

commit b72b9153d91a93a550c4ec40fce5f9a18e7eaac6
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Sun Sep 20 16:53:26 2015 -0400

    agent: Restrict transitions to gathering
    
    Only allow transitions to gathering from disconnected or
    failed states.

commit 059a0e33c973a54c44f7c4fd1e766155f6078f80
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri May 27 14:06:24 2016 -0400

    conncheck: fix TCP active relay handling
    
    TCP active relay candidates use UDP TURN for their underlying socket.
    Since 0a6c779f1f, socket->fileno of UDP TURN sockets is always NULL,
    which caused a wrong code path to be chosen in conn_check_send().
    
    We have to update the if-expression accordingly.
    
    Maniphest Tasks: T7442
    Differential Revision: https://phabricator.freedesktop.org/D1017

commit fc4d3aab5392f855dbda7ad0225bf0ad4e5fafb6
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri May 27 11:29:22 2016 -0400

    agent: ignore gathering failures on auto-generated IPs
    
    Candidate gathering is stopped when discovery_add_local_host_candidate()
    returns HOST_CANDIDATE_CANT_CREATE_SOCKET. This may be too radical
    a measure when other local addresses can still be able to generate
    usable candidates.
    
    The issue was observed by a user who had an IPv6 address with tentative
    flag on one of the interfaces. That single failing address was causing
    the whole gathering process to end with no candidates found.
    
    Still, don't do this if nice_agent_add_local_address() has been called.
    In that case, the user really cares about the addresses and if there's
    any problem, the process should fail.
    
    https://phabricator.freedesktop.org/D1016

commit 1fb6401d9d5dbee8ba28a20f3d787a95f13d1883
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue Mar 1 15:27:46 2016 -0500

    candidate: Give lower priority to TCP relayed candidates

commit 8ee6d1bbed87fda37ade7b5c5d9483f41037b06a
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Feb 29 16:11:18 2016 -0500

    udp-turn: Fix binding timeout leak

commit 8f1f615e92cd56ad4d8487457c2fde2c4aaa51d9
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Wed Feb 24 22:53:08 2016 -0500

    conncheck: Update selected pair if necessary

commit 65f2eda04c1c73cc7ebc3df2032d528eedc236e1
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Feb 22 19:36:58 2016 -0500

    conncheck: Don't reset keepalive timer on next keepalive
    
    If the keepalive is still being re-send, just let the retries do their
    job. If they don't get a reply, then declare the attempt failed.

commit 1ab9d7c104978ea1904aaaad708c1c8c23c77592
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu May 26 16:05:36 2016 -0400

    conncheck: Separate valid and succeded states
    
    RFC 5245 specifies that when a mapped-address differs from the address
    from the request was sent, the mapped-address is used to select the
    valid pair, but the source address of the check is used to select the
    pair that succeeded, so they are not the same.

commit 0a6c779f1f24099db2c1cd34cd339e240682525d
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri Feb 19 20:47:08 2016 -0500

    udp-turn: Don't expose GSocket
    
    UDP turn sockets should never be read frm directly.
    Because they may share the same socket with the non-relay,
    so the incoming data may not be relayed and then the NiceSocket
    API doesn't allow returning the base socket as the source.

commit 5b27b028d8ad89214dc7b1ecd018f56aa0333b9c
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Feb 18 14:20:52 2016 -0500

    conncheck: Make very frequent debug verbose-only

commit b7c2eabfd0bd8c1321d9e8450caa8fa6b6ecb5ab
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Feb 15 19:09:58 2016 -0500

    debug: Enable based on G_MESSAGES_DEBUG

commit acfe2b1f366d5f33314db7e9878225f2a70358ef
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Fri Feb 12 01:01:37 2016 -0500

    agent: Don't emit signal in the middle of recv call

commit 716c5805d5b73e94bc6af3637dfd50266150734e
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Wed Feb 10 19:38:52 2016 -0500

    agent: Update type of peer-reflexive candidate on trickled candidate
    
    If a remote candidate matches an already discovered peer-reflexive candidate,
    then the type can be updated to the real type and the foundation
    can be set correctly.

commit fc0d3744ebc03f8137866170594968ba61e6be30
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue Feb 9 12:52:45 2016 -0500

    agent: Only try to use the address of the same family to connect to TURN
    
    Using a IPv6 local address to connect to a IPv4 relay just creates an
    extra discovery attempt that will not provide something useful.

commit c129b05a469b59b576f4700fe9bfe3adca0a48dc
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Sun Feb 7 19:48:07 2016 -0500

    stun turn usage: Only send the username if short term creds or nonce present
    
    This is recommended by the STUN RFC 5389.

commit 501f9a82e47076cda0deab8cf54758b608e899aa
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Sun Feb 7 19:41:52 2016 -0500

    turn: Cache the nonce & realm to remove useless round trips
    
    Instead of re-discovering the nonce and realm for every request, cache them
    in th socket.

commit 82ea4d71728af95cf0c7bff478f69342a461134b
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue Feb 9 11:18:30 2016 -0500

    conncheck: Stay READY if a new nominated pairs comes in

commit 729bd3bb215f0a9a67293dea6df3f8f234eea0ac
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Feb 8 21:04:24 2016 -0500

    conncheck: Deduplicate conncheck stopping code

commit f122e4174d19c60bc434f3986f5c08f8673344bd
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Mon Feb 8 19:41:28 2016 -0500

    Reset to connecting if reconnected after failed

commit 9c1a41b06ab459ce33f32d25ce258fb21ba49047
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Jan 14 17:59:16 2016 -0500

    agent: Add warning on ignored result

commit a357b17f5f3415320b9ec7122738396ccd998521
Author: Fabrice Bellet <fabrice@bellet.info>
Date:   Mon Apr 4 23:02:52 2016 +0100

    conncheck: display controlling mode of stun requests
    
    This patch makes the debug log more explicit about the agent
    controlling role for each stun request sent. It helps to debug
    role conflict resolution.
    
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
    Differential Revision: https://phabricator.freedesktop.org/D877

commit b986d6e5f2ee0b7b0e09031c1a369bf89153e4c5
Author: Fabrice Bellet <fabrice@bellet.info>
Date:   Mon Apr 4 22:38:07 2016 +0100

    agent: remove newline from debug output
    
    Just a cosmetic fix.
    
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
    Differential Revision: https://phabricator.freedesktop.org/D872

commit e0ed4fb3a236710846d9129438e0077782569633
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Mon Apr 4 21:52:29 2016 +0100

    socket: refactor nice_socket_is_base_of()
    
    • rename to nice_socket_is_based_on() and swap the order of arguments
       accordingly; the implementation doesn't have to use the confusing
       'return other->is_base_of()' pattern anymore
     • fix potential NULL dereferences
    
    The argument order in agent_recv_message_unlocked() was already wrongly
    swapped in 1732c7d6 and thus this commit isn't changing it back because
    that order has become the correct one.
    
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
    Differential Revision: https://phabricator.freedesktop.org/D866

commit 38268e53fde8cd97055d88d2066c0016fe04b31b
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Mon Apr 4 21:46:05 2016 +0100

    socket: fix wrong function called in nice_socket_is_base_of()
    
    We have to call is_base_of "virtual function pointer" of 'other'
    object, not 'sock', since 'other' is the structure whose base
    NiceSocket we need to get from its private data.
    
    For instance calling nice_socket_is_base_of() with 'sock' and 'other'
    being respectively pseudo-SSL and UDP-TURN-over-TCP invoked is_base_of
    variant for pseudo-SSL, casting other->priv into PseudoSSLPriv *, but
    other->priv is actually TurnTcpPriv *. It must be called the other way
    around.
    
    https://phabricator.freedesktop.org/T7335
    https://phabricator.freedesktop.org/T7336
    
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
    Reviewed-by: José Antonio Santos Cadenas <santoscadenas@gmail.com>
    Reviewed-by: Philip Withnall <philip@tecnocode.co.uk>
    Reviewed-by: José Antonio Santos Cadenas <santoscadenas@gmail.com>
    Differential Revision: https://phabricator.freedesktop.org/D785

commit 7037ab4cf384edd9f700bc221a9d980b30d9c64f
Author: Fabrice Bellet <fabrice@bellet.info>
Date:   Mon Apr 4 21:38:59 2016 +0100

    conncheck: implement a "triggered queue" list
    
    The checks should not be sent immediately in priv_conn_check_initiate(),
    but be put into the "triggered queue", see  "7.2.1.4 Triggered Checks".
    This patch implements this triggered checks list, and uses it to enforce a
    pacing of STUN transactions, no more than one per Ta ms, according to
    "B.1. Pacing of STUN Transactions".
    
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
    Reviewed-by: Philip Withnall <philip@tecnocode.co.uk>
    Differential Revision: https://phabricator.freedesktop.org/D802

commit 1732c7d6a7a104438412309373818e493a2504c9
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Sun Mar 6 15:16:18 2016 -0500

    agent: Fix argument order
    
    Fixes crash reported on https://phabricator.freedesktop.org/D786

commit aac283e0fa75b226fe2431403761ebd45e4f5614
Author: Fabrice Bellet <fabrice@bellet.info>
Date:   Sat Mar 5 18:46:48 2016 +0000

    ice: fix the debug of the presence of the controlling/controlled attrib
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D807

commit ed75d55cf279613bb736f7646d3010d816797ddf
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Wed Mar 2 00:01:19 2016 +0000

    agent: fix relay candidate discovery on hosts having several IPs
    
    When a message is received from a TURN server and we manage to find a
    local relay candidate with matching stream and component IDs, we should
    also check whether the message came from the candidate's respective
    socket.
    
    We should do this because there might still be some pending TURN
    candidate discovery with the same server from a different local host IP
    and the message may be a response to our allocate request. If
    nice_udp_turn_socket_parse_recv_message() is passed such request, it can
    make some wrong assumptions and modify it like in the case of reliable
    UDP-TURN-OVER-TCP by removing (supposed) RFC4571 framing, which in turn
    causes the reply to be unrecognized and discarded.
    
    Because of this, any subsequent replies following the first successful
    allocate response from that server couldn't create any additional relay
    candidates.
    
    Maniphest Tasks: https://phabricator.freedesktop.org/T7336
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D786

commit 1493a381d5bf6e15348c2bc17270f45b69cb70d2
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 23:50:11 2016 +0000

    build: Update autogen.sh from GNOME template
    
    https://wiki.gnome.org/Projects/GnomeCommon/Migration#autogen.sh

commit bc620c0de966b47d761bbcb1279dac6221a5a30e
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Tue Mar 1 23:29:10 2016 +0000

    component.c: Fix memory leak
    
    If nicesocket is not added to a component it will be leaked.
    
    This is the case of active tcp sockets
    
    Change-Id: I57fefffef71d35ce9871139ee1064181f6fe125b
    Reviewed-by: José Antonio Santos Cadenas <santoscadenas@gmail.com>
    Differential Revision: https://phabricator.freedesktop.org/D822

commit 38c5e66886cb8138d6be57b8a4721d9b42a358b7
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Tue Mar 1 23:23:14 2016 +0000

    agent: Use provided CandidatePair rather than re-finding a pair
    
    In priv_update_selected_pair(), commit 57393333 changed the code to
    re-find a CandidatePair matching the given lfoundation and rfoundation.
    However, the foundation does not uniquely identify candidate pairs,
    and if we’re aiming to set a specific candidate pair as the selected
    pair, this could result in the wrong pair being selected.
    
    This can happen when handling multiple similar candidate pairs, such as
    when generating peer reflexive candidates from multiple sources.
    
    See https://tools.ietf.org/html/rfc5245#section-2.4.
    
    Originally spotted by Fabrice Bellet in
    https://phabricator.freedesktop.org/T3557.
    
    Reviewed-by: José Antonio Santos Cadenas <santoscadenas@gmail.com>
    Differential Revision: https://phabricator.freedesktop.org/D742

commit 70981d41edf46a09393017f3de34748fcecea046
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 23:05:20 2016 +0000

    simple-example: transmission can begin earlier than in ready state
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D819

commit 47eaf50a99d91c4a666f05c4de24613706847c88
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 23:04:14 2016 +0000

    test-new-dribble: wait until ragent reaches state completed
    
    The test didn't let enough time for ragent to reach the completed state
    after obtaining its remote candidates and switching to connecting state.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D817

commit 4e68244a51698aefdf44dd1ceb17b95275e655bf
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 23:02:52 2016 +0000

    conncheck: reorder the connection list when priorities are updated
    
    The update of pairs priorities due to agent role change requires the
    conncheck list to be reordered to reflect this modification.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D806

commit 0f1e6c64515871298b115b497b019506b8065235
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 23:01:14 2016 +0000

    conncheck: fix keepalive stun agent initialisation
    
    With this patch, we send keepalive binding requests using agent
    compatibility flags, instead of RFC 3489 classic stun. The peer stun
    agent will known how to handle it, and won't be confused by the
    uncompatible RFC 3489 message, causing "no cookie" errors in the debug
    log.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D804

commit 17488a20d8db8ea1f8fa2d7a44090070407d6db8
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 22:58:15 2016 +0000

    conncheck: foundations are shared across streams
    
    This patch fixes a bug where the foundation definition shouldn't take
    into account the stream the pair belongs to. This is important, because
    the ordinary checks algorithm will change pair state from Frozen to
    Waiting, by selecting pairs from other streams sharing the same
    foundation than already succeeded pairs.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D815

commit 3ce45c25af238fb4d9a040abb44597531140db2d
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 22:37:33 2016 +0000

    test-priority: ignore the local preference
    
    The local preference depends on the rank of the IP address in the list
    of all IP addresses available of the box running the test. As this value
    is not fixed we ignore it in the test.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D818

commit c309905ff446bed2dc47811023b98bc586a02d63
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 22:33:51 2016 +0000

    conncheck: nominate only one matching pair
    
    This patch fixes a bug in priv_mark_pair_nominated(), where the local
    candidate was not passed to the function, so removing the possibility to
    find which local candidate the check was sent to.
    
    Reviewed-by: Philip Withnall <philip@tecnocode.co.uk>
    Differential Revision: https://phabricator.freedesktop.org/D808

commit 80973c096de872983bc60cd28a84653931f8d601
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Tue Mar 1 22:28:35 2016 +0000

    conncheck: add more debug information
    
    Add a more debug details, specifically in some places, it is interesting
    to have the src and dst IP addresses of the pairs being checked, and
    also to make the difference between log related to different stream ids.
    
    Reviewed-by: Philip Withnall <philip@tecnocode.co.uk>
    Differential Revision: https://phabricator.freedesktop.org/D803

commit 41ab61def82aa275649afd5f4a3fcb43e75fb360
Author: Mike Ruprecht <cmaiku@gmail.com>
Date:   Mon Jan 18 12:42:46 2016 +0000

    agent: Fix not setting UPnP timeout on second gather_candidates()
    
    If the first call to nice_agent_gather_candidates() partially succeeds
    (setting a UPnP agent and timeout), then fails before starting
    gathering, a second call to nice_agent_gather_candidates() would fail to
    set a new UPnP timeout because the UPnP initialisation block would be
    skipped. That means gathering would never succeed due to timing out on
    UPnP.
    
    Fix that by setting the UPnP timeout whenever a new pending UPnP mapping
    is added.
    
    https://phabricator.freedesktop.org/T3534
    
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>

commit 9638bc132f909177f6ecfb86cdd17af557a35c56
Author: Jose Antonio Santos Cadenas <santoscadenas@gmail.com>
Date:   Thu Dec 3 15:01:35 2015 +0100

    configure.ac: Update glib version
    
    As udp-bsd.ccode is using G_IO_ERROR_CONNECTION_CLOSED glib 2.44
    is required.
    
    Change-Id: I1bb63f2484c513c58eeec312ba0835164604c40c
    Reviewed-by: Philip Withnall <philip@tecnocode.co.uk>
    https://phabricator.freedesktop.org/T3492

commit 3e71f42c8b15790e252d850ba42a7ae7e7cc69a9
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Oct 7 19:03:58 2015 +0100

    pseudotcp: Use labs() rather than abs() for handling long integers
    
    This fixes a compiler warning and prevents a possible truncation.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D345

commit 53283c218a5eb7a29e7019ac320e74f9dbe4b3fc
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Mon Jun 22 11:30:31 2015 +0100

    tests: Enable G_MESSAGES_DEBUG for all unit tests
    
    Now that we’re using automake’s parallel test harness, it automatically
    redirects all the debug log spew away from the console, so we should
    always have it enabled.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D292

commit aef748ec7d0b06067312e5cc761a6375cd0f699c
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Thu Oct 1 17:52:08 2015 +0100

    build: Set repository callsign in .arcconfig
    
    This fixes `arc diff` to select the right repository when submitting
    patches.

commit 008739a5a60e591629e38da9b8b7065dbc2c746f
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 14:57:10 2015 +0100

    agent: Correctly namespace Component and its methods
    
    Remove all references to the old, unnamespaced versions. This should
    cause no functional changes.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D309

commit 529bc193d56522b10a4de83409396b3316863934
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 14:34:34 2015 +0100

    agent: Correctly namespace Stream and its methods
    
    Remove all references to the old, unnamespaced versions. This should
    cause no functional changes.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D308

commit ef2b58f64887546e426dd8cda382f2908f84caca
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 14:11:14 2015 +0100

    agent: Turn Component into a GObject
    
    This makes it reference-counted. This will be useful for allowing
    GDatagramBased and GIOStream objects to hold references to the stream
    and component they are interested in, allowing removal of the global
    NiceAgent lock previously needed to look up the component for every I/O
    operation.
    
    Deprecate all the old methods until it’s properly namespaced.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D307

commit 1f08419382c52c7e796da06c9271a362aa60333d
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 14:10:32 2015 +0100

    agent: Turn Stream into a GObject
    
    This makes it reference-counted. This will be useful for allowing
    GDatagramBased and GIOStream objects to hold references to the stream
    and component they are interested in, allowing removal of the global
    NiceAgent lock previously needed to look up the component for every I/O
    operation.
    
    It also means that nice_stream_close() could eventually become
    asynchronous, which would fix a few race conditions.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D306

commit da70716162b2085ca4db6e7efd446fcda7bd488d
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 17:59:04 2015 +0100

    tests: Update expected priority values in test-priority
    
    This is a follow up to T3324, to update the test case to match the new
    values generated.
    
    Bug: https://phabricator.freedesktop.org/T3324
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D301

commit 66e47aa39f9cd3666e610fab78caa0c7d8f9c410
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 17:54:21 2015 +0100

    tests: Use g_assert_cmpuint() to make test failures easier to diagnose
    
    Now we can actually see the priority numbers which are unequal.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D300

commit 1ae15f66af2af17e991ab028ca16a1200fd5f4e5
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 17:53:14 2015 +0100

    tests: Set candidate addresses in test-priority
    
    This avoids an assertion failure in nice_address_to_string() when the
    addresses are compared for priority.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D299

commit ea3348020da586f20e1a64c9732405e730563616
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 17:46:47 2015 +0100

    agent: Remove redundant GLIB_CHECK_VERSION macros
    
    We depend on GLib 2.36.0, which is a higher version than any of these
    version checks cared about, so they were all trivially true or false.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D298

commit 9f10231fc787e4683e896bf35f086906a5b16c03
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 17:44:44 2015 +0100

    tests: Remove g_thread_init() calls
    
    We depend on GLib 2.36.0; g_thread_init() has been deprecated since
    2.32.0, when thread initialisation was changed to happen automatically.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D297

commit dac3e280d6f3fd792f1d79313debd03c0df282c4
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 30 17:41:36 2015 +0100

    tests: Remove g_type_init() calls
    
    We depend on GLib 2.36.0, which deprecated g_type_init() since GType
    initialisation is now done automatically.
    
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    Differential Revision: https://phabricator.freedesktop.org/D296

commit fae0f9d37c6e34f34c92dee85384c29e565bdbce
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Fri Sep 11 11:57:54 2015 +0100

    conncheck: rename priv_process_response_check_for_peer_reflexive()
    
    Renamed the function to priv_process_response_check_for_reflexive()
    because it now checks also for server reflexive candidates.
    
    Updated the documentation to indicate that the function never returns
    NULL.
    
    Maniphest Tasks: https://phabricator.freedesktop.org/T115
    Differential Revision: https://phabricator.freedesktop.org/D243
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>

commit 02852728ef347f5cb4c9227848b266c72b5fe38b
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Fri Sep 11 11:56:02 2015 +0100

    conncheck: generate candidate pair for valid srflx candidate
    
    In priv_process_response_check_for_peer_reflexive(), mere presence of a candidate in local_candidates doesn't mean there's also some candidate
    pair in conncheck_list using it - for instance that candidate may be server reflexive, for which no check pairs are initially created (see
    conn_check_add_for_candidate_pair()).
    
    If we fail to find corresponding pair upon receiving such candidate's IP in a conncheck response's XOR-MAPPED-ADDRESS attribute, we shall add a
    new one in a similar way we would add a new pair for a just discovered peer reflexive candidate.
    
    Previous priv_process_response_check_for_peer_reflexive() implementation would return NULL, causing a CandidateCheckPair with local candidate of
    type HOST to be wrongly selected even though the local host IP might not be directly accessible by the remote counterpart (e.g. it's an address
    on a private network segment). In practice this was coming through as a duplex connection that libnice was reporting as properly established,
    but only one direction of the communication was actually working.
    
    Maniphest Tasks: https://phabricator.freedesktop.org/T115
    Differential Revision: https://phabricator.freedesktop.org/D242
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>

commit 84eaa12b0b1a76c45ce2d77294e0477c10552cd4
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Fri Sep 11 11:33:51 2015 +0100

    agent: check for base socket in _tcp_sock_is_writable()
    
    The argument passed into the callback is always a base (TCP/UDP) socket,
    which can't be directly compared with local candidate's sockptr (may be
    TURN, http, or other socket wrapping another one). We're in fact
    interested whether sock is a base socket of sockptr.
    
    Maniphest Tasks: T114
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
    Differential Revision: https://phabricator.freedesktop.org/D241

commit 837c8953fe87bdd5d5bccc444e72739100578ef8
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Fri Sep 11 11:29:39 2015 +0100

    socket: add nice_socket_is_base_of()
    
    This will be used in the next commit.
    
    Maniphest Tasks: T114
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
    Differential Revision: https://phabricator.freedesktop.org/D240

commit c6bc33c031493e0db11a1f57055a656f6428c60a
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Thu Jul 10 08:32:04 2014 +0100

    build: Bump GLib dependency to 2.36
    
    This is needed for G_IO_ERROR_BROKEN_PIPE, which is used in the I/O
    stream code.
    
    Reported by Emanuele Bizzarri <emabiz76@gmail.com> on the mailing list.

commit 757f8aecdb4fda86b2bbac828a3acc528d8eb8bc
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Fri Sep 4 08:14:08 2015 +0100

    stun: Disable debug by default
    
    To match debug_enable in agent/debug.c. Debug can still be enabled by
    calling stun_debug_enable() or nice_debug_enable().
    
    Spotted on the mailing list by Tom Chen.

commit 2eaa8b3277f4f39515ff5dc7b512a44fd79e7275
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Mon Jun 29 16:30:12 2015 +0100

    agent: Add assertions to check component state transitions are valid
    
    There is no point in the NiceComponents having a state machine if the
    state transition graph is not documented or enforced. Document and
    enforce it.
    
    http://phabricator.freedesktop.org/T120

commit 3f54b333525e2a4ae35e0be439062900fb8ab7c3
Merge: 1034832 181ad3a
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 2 16:44:45 2015 +0100

    ms-ice: ensure distinct candidate priority for multihomed hosts
    
    Summary:
    Offering multiple host candidates with equal priorities could lead to unpredictable candidate pair selection by our counterparty.
    
    Fixes call disconnection by MS Lync client after 30 seconds while VPN (2nd IP) was active on libnice host.
    
    Maniphest Tasks: T3324
    
    Reviewers: pwithnall
    
    Projects: #libnice
    
    Reviewed By: pwithnall
    
    Subscribers: pwithnall
    
    Differential Revision: https://phabricator.freedesktop.org/D234

commit 181ad3a9bf54b9d6c4e0921ae148bab6d9fd0b3a
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Wed Sep 2 16:43:02 2015 +0100

    candidate: use distinct priority also for non-MS compatibilities
    
    Maniphest Tasks: T3324
    
    Reviewers: pwithnall
    
    Projects: #libnice
    
    Reviewed By: pwithnall
    
    Differential Revision: https://phabricator.freedesktop.org/D239

commit 799b322d6c654b864b5442cdaaa62aaee81d4901
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Wed Sep 2 16:41:49 2015 +0100

    conncheck: give temporary candidate_priority a base_addr
    
    Summary:
    Fixes "(nice_address_to_string): should not be reached" errors when calling nice_candidate_ms_ice_priority() because of invalid NiceAddress.
    
    Maniphest Tasks: T3324
    
    Reviewers: pwithnall
    
    Projects: #libnice
    
    Reviewed By: pwithnall
    
    Subscribers: pwithnall
    
    Differential Revision: https://phabricator.freedesktop.org/D238

commit 85cd01c1396ef244f90e0d9c995fab29ed121f23
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Wed Sep 2 16:41:49 2015 +0100

    ms-ice: ensure distinct candidate priority for multihomed hosts
    
    Summary:
    Offering multiple host candidates with equal priorities could lead to unpredictable candidate pair selection by our counterparty.
    
    Fixes call disconnection by MS Lync client after 30 seconds while VPN (2nd IP) was active on libnice host.
    
    Maniphest Tasks: T3324
    
    Reviewers: pwithnall
    
    Projects: #libnice
    
    Reviewed By: pwithnall
    
    Subscribers: pwithnall
    
    Differential Revision: https://phabricator.freedesktop.org/D234

commit 88ed42619049ac1e3fe3a6e481df8aeb95033ac0
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Wed Sep 2 16:41:48 2015 +0100

    discovery: assign candidate priority after base_addr is set
    
    Summary: So that we can take the base address into account in the calculation.
    
    Maniphest Tasks: T3324
    
    Reviewers: pwithnall
    
    Projects: #libnice
    
    Reviewed By: pwithnall
    
    Subscribers: pwithnall
    
    Differential Revision: https://phabricator.freedesktop.org/D235

commit 10348322a960258043363e7c84e78c4821c90412
Merge: abdab05 ab4ced5
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Sep 2 16:34:01 2015 +0100

    ms-turn: don't wait for a reply to STUN_SEND request
    
    Maniphest Tasks: T126
    
    Reviewers: pwithnall
    
    Projects: #libnice
    
    Reviewed By: pwithnall
    
    Subscribers: pwithnall
    
    Differential Revision: https://phabricator.freedesktop.org/D223

commit ab4ced5a46a7edba7cd49c0495bc41cd8fff7cc2
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Wed Sep 2 16:32:05 2015 +0100

    ms-turn: don't wait for a reply to STUN_SEND request
    
    As per [MS-TURN] Section 2.2.1, TURN message type 0x0104 "Send request
    response" isn't supported and the TURN server MUST NOT send them. Thus,
    libnice should not remember Send requests in agent->sent_ids because
    without replies coming, the number of allowed pending transaction gets
    quickly exhausted, causing our data packets to be dropped until a
    request timeout frees some space in the queue.
    
    This behavior resulted in choppy reception of our audio on a Lync client
    when connected via Lync Edge (TURN) Server.
    
    Maniphest Tasks: T126
    
    Reviewers: pwithnall
    
    Projects: #libnice
    
    Reviewed By: pwithnall
    
    Subscribers: pwithnall
    
    Differential Revision: https://phabricator.freedesktop.org/D223

commit abdab053af41068406caf95e80a64c512fd3db90
Merge: 03d11b4 490b16f
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Sat Aug 29 23:26:00 2015 +0100

    Creating TCP sockets with TCP_NODELAY option set to TRUE
    
    Summary:
    Disable Nagling for underlying TCP sockets used by libnice, because they
    are typically used for streaming applications, or for pseudo-TCP; the
    bandwidth in both cases is harmed by Nagling.
    
    Based on a patch by Vadim Genkin.
    
    Maniphest Tasks: T3317
    
    Reviewers: vadimgenkin, pwithnall
    
    Projects: #libnice
    
    Reviewed By: pwithnall
    
    Subscribers: pwithnall, vadimgenkin
    
    Differential Revision: https://phabricator.freedesktop.org/D230

commit 490b16f400284c5df6508fd095d592efdfbc62ae
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Sat Aug 29 22:39:56 2015 +0100

    Creating TCP sockets with TCP_NODELAY option set to TRUE
    
    Disable Nagling for underlying TCP sockets used by libnice, because they
    are typically used for streaming applications, or for pseudo-TCP; the
    bandwidth in both cases is harmed by Nagling.
    
    Based on a patch by Vadim Genkin.
    
    Maniphest Tasks: T3317
    
    Reviewers: pwithnall
    
    Projects: #libnice
    
    Subscribers: pwithnall, vadimgenkin
    
    Differential Revision: https://phabricator.freedesktop.org/D230

commit 03d11b49b0ac14ff320192562df57898ea9f94b4
Merge: dddca10 1c34734
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Sat Aug 29 20:05:00 2015 +0100

    Fix agent leak in case component socket is reset remotely
    
    Summary: The patch fixes the issue where agent reference count is not properly decremented causing instance leak in cases where component's socket is reset remotely.
    
    Reviewers: #libnice, pwithnall
    
    Projects: #libnice
    
    Reviewed By: #libnice, pwithnall
    
    Subscribers: pwithnall, maximgolunov
    
    Differential Revision: https://phabricator.freedesktop.org/D236

commit 1c34734cd105c6cca0ec8bbb908002c4bfb007b3
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Sat Aug 29 20:04:03 2015 +0100

    Fix agent leak in case component socket is reset remotely
    
    Summary: The patch fixes the issue where agent reference count is not properly decremented causing instance leak in cases where component's socket is reset remotely.
    
    Reviewers: #libnice, pwithnall
    
    Projects: #libnice
    
    Reviewed By: #libnice, pwithnall
    
    Subscribers: pwithnall, maximgolunov
    
    Differential Revision: https://phabricator.freedesktop.org/D236

commit dddca10ceff20e3578fc901b9919c3d20f87b7b6
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Aug 19 09:24:27 2015 +0100

    build: Update .gitignore
    
    Add stun/usages/.dirstamp.

commit 6c9afb4db35168cc699ff0aa2e56b127f3706083
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Aug 19 09:22:49 2015 +0100

    build: Fix multiple definition of CLEANFILES
    
    It’s already defined in common.mk.

commit 041158c4fe36c4af6d56cd4445946d7f5e5be57d
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Aug 19 09:20:01 2015 +0100

    build: Add .arcconfig file
    
    This completes the transition to Phabricator; everyone should be using
    the same project settings now.
    
    https://phabricator.freedesktop.org/tag/libnice/

commit 8f2a14b1d6af73e91c5712898e8a3e97308920a6
Merge: e5e77b6 ad0003b
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Aug 19 09:19:18 2015 +0100

    socket: Handle ECONNRESET as EWOULDBLOCK on Windows
    
    Summary:
    Some versions of Windows can return ECONNRESET for UDP recvmsg() calls
    if they would otherwise block. Hence, handle the two equivalently; this
    should not affect behaviour on Linux, which apparently does not return
    ECONNRESET for UDP recvmsg() calls at all.
    
    https://phabricator.freedesktop.org/T121
    
    Maniphest Tasks: T121
    
    Reviewers: ocrete
    
    Projects: #libnice
    
    Reviewed By: ocrete
    
    Subscribers: stwiname, felixSchl
    
    Differential Revision: https://phabricator.freedesktop.org/D227

commit e5e77b67fb6152dd9fb0af3d7410a428299504d3
Merge: 6835e7b a2e25cf
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Wed Aug 19 09:16:53 2015 +0100

    socket: Close base socket for a TCP passive socket when closing parent
    
    Summary:
    Otherwise the base socket will leak. Spotted by Vadim Genkin.
    
    https://phabricator.freedesktop.org/T125
    
    Maniphest Tasks: T125
    
    Reviewers: ocrete
    
    Projects: #libnice
    
    Reviewed By: ocrete
    
    Subscribers: vadimgenkin
    
    Differential Revision: https://phabricator.freedesktop.org/D228

commit a2e25cf49b24c2012e8e9058ecc7e62f1e027cb3
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Tue Aug 18 14:58:23 2015 +0100

    socket: Close base socket for a TCP passive socket when closing parent
    
    Otherwise the base socket will leak. Spotted by Vadim Genkin.
    
    https://phabricator.freedesktop.org/T125

commit 6835e7b7f4a20078d508444659c636fc98e680fc
Merge: e1f748c 6b3e59c
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Tue Aug 18 14:03:41 2015 +0100

    agent: Remove unused inet_pton() function
    
    Summary:
    As spotted by Felix <felixschlitter@gmail.com>. This is a static
    function which is totally unused in this compilation unit and is causing
    build failures with `-Werror=unused-function`.
    
    Maniphest Tasks: T123
    
    Reviewers: felixSchl, ocrete
    
    Projects: #libnice
    
    Differential Revision: https://phabricator.freedesktop.org/D221

commit 6b3e59c5a670d5117ed293ae612082dcd047ba8a
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Tue Jun 30 14:39:51 2015 +0100

    agent: Remove unused inet_pton() function
    
    As spotted by Felix <felixschlitter@gmail.com>. This is a static
    function which is totally unused in this compilation unit and is causing
    build failures with -Werror=unused-function.
    
    http://phabricator.freedesktop.org/T123

commit ad0003b48414c789a1191fd5f44fec41e694dfa0
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Tue Aug 18 13:33:23 2015 +0100

    socket: Handle ECONNRESET as EWOULDBLOCK on Windows
    
    Some versions of Windows can return ECONNRESET for UDP recvmsg() calls
    if they would otherwise block. Hence, handle the two equivalently; this
    should not affect behaviour on Linux, which apparently does not return
    ECONNRESET for UDP recvmsg() calls at all.
    
    https://phabricator.freedesktop.org/T121

commit e1f748cccacd81cce4db338d0203dc49bc915a30
Author: Jakub Adam <jakub.adam@ktknet.cz>
Date:   Thu Jun 18 09:05:21 2015 +0200

    conncheck: set writable callback to socket from TCP active connect
    
    A new socket created in nice_tcp_active_socket_connect() should have its
    writable callback set, because it's possible for it to become a base
    socket of a peer reflexive candidate, if some is discovered by
    connection checks on that TCP active candidate.
    
    Previously, when such prflx candidate became selected, without write_cb
    on the socket the agent was never notified about it becoming writable
    again after the socket's buffer got filled up. This caused the data flow
    to hang permanently.
    
    Reviewed-by: Philip Withnall <philip.withnall@collabora.co.uk>
    Reviewed-by: Olivier Crête <olivier.crete@collabora.com>
    
    http://phabricator.freedesktop.org/T117

commit cd61af3114a51df53619fb0460ace2b3660fc5da
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Tue Jun 30 14:29:43 2015 +0100

    pseudotcp: Only define errnos on Windows if not already defined
    
    Recent versions of MinGW define at least ECONNABORTED and EAFNOSUPPORT,
    so only define the various socket errnos if they are not defined
    already.
    
    Based on a patch by Alexey Pawlow <alexey.pawlow@gmail.com> and Felix
    <felixschlitter@gmail.com>.
    
    Reviewed-by: Olivier Crete <olivier.crete@collabora.com>
    Reviewed-by: Felix Schlitter <felixschlitter@gmail.com>
    
    http://phabricator.freedesktop.org/T122

commit 3cccc311b1becf4307f5a4004734d8f7b2cf84f6
Author: Felix Schlitter <felixschlitter@gmail.com>
Date:   Tue Jun 30 07:37:31 2015 +1200

    Use G_GSIZE_FORMAT instead of %zu
    
    The C runtime on windows does not implement a printf that understands
    %zu and other C99 format specifiers. Use G_GSIZE_FORMAT instead. This
    is further consistent with the rest of the code base that makes use of
    G_GSIZE_FORMAT throughout.
    
    https://github.com/libnice/libnice/pull/3

commit c4d5ec572ae0ede14d13d9ce5b193cdcb36b07a1
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Sep 18 19:33:10 2014 -0400

    Split "verbose" on-every-packet messages to a verbose log
    
    This way, the regular log will only contain connection-time information.

commit 2d24ef532d1b6ec684f28d52aa0592dfdfb7e067
Author: Olivier Crête <olivier.crete@collabora.com>
Date:   Thu Sep 18 19:33:06 2014 -0400

    stun: Remove annoying non-error on non-STUN packet

commit 81a929ac141aae66b6450e8ce93cb357ed404cda
Author: Timo Gurr <timo.gurr@gmail.com>
Date:   Mon Jun 1 16:10:16 2015 +0200

    configure: Fix configure failure when building without gstreamer support
    
    Error introduced in 20ea22e0a11a9bdfe4d8125b68083249b694338a, resulting in a
    configure/build error when building without gstreamer:
    
    configure: error: conditional "HAVE_GST_CHECK" was never defined.
    Usually this means the macro was only invoked conditionally.
    
    https://bugs.freedesktop.org/show_bug.cgi?id=90801

commit d3a7b315ec708ac9ecb8df53bcc8d108016bd508
Author: Philip Withnall <philip.withnall@collabora.co.uk>
Date:   Fri May 8 10:13:39 2015 +0100

    build: Auto-generate win32 .def file from libnice.sym
    
    We’ve neglected to manually update this file once too often — it’s been
    out of date for important new symbols (for example,
    nice_agent_get_io_stream()) since at least 0.1.11.
    
    Since the format is a simple extension of libnice.sym, we might as well
    automatically generate it at dist time.

commit 6a8c63219c632c27707267b6510dca096c6fd511
Author: Youness Alaoui <kakaroto@kakaroto.homelinux.net>
Date:   Tue May 5 15:07:10 2015 -0400

    Removing no-op assignment

commit 91a7b9324244844baf35d8fcef019a4ea3872d30
Author: Youness Alaoui <kakaroto@kakaroto.homelinux.net>
Date:   Tue May 5 15:00:30 2015 -0400

    Do not compare scope for IPv6 address when scope is 0
    
    This caused issues with thinking local host candidates were peer-reflexive
    candidates because the nice_address_equal would fail since the scope
    would be 6 (or some other value) but locally created NiceAddress from
    a stun response would have the scope set to 0.
    We ignore the scope when comparing ipv6 candidates when scope is 0
    to avoid these kinds of issues.
    Thanks to ikonst_ for finding these issues

commit 93862c1e1940618e06143d4788f54bffd4d1c5da
Author: Youness Alaoui <kakaroto@kakaroto.homelinux.net>
Date:   Tue May 5 14:24:15 2015 -0400

    Do not update a remote candidate's type
    
    When adding a remote candidate, if it's the same ip:port, we should
    also check its type, otherwise it's a new candidate. We can't allow
    a candidate type to be updated. This caused issues to ikonst_ on IRC
    where for some reason a host candidate appeared as both host and prflx
    and the update caused a remote host candidate to be updated to prflx
    causing a crash when the sockptr was being accessed.

commit 7b7d2d986876fc53a23af7b516d78f82f2a546e9
Author: Philip Withnall <philip@tecnocode.co.uk>
Date:   Sun May 3 16:05:30 2015 +0100

    agent: Remove unnecessary NULL check
    
    With the changes in commit 483bdcf8, @name is now guaranteed to be
    non-NULL. Spotted by Coverity.
    
    CID: #109878
diff --git a/.arcconfig b/.arcconfig
new file mode 100644
index 0000000..ba3d1ea
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,6 @@
+{
+	"phabricator.uri" : "https:\/\/phabricator.freedesktop.org\/",
+	"history.immutable" : true,
+	"project.name" : "libnice",
+	"repository.callsign" : "LIBNICE"
+}
diff --git a/Makefile.am b/Makefile.am
index 432a14d..896c751 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,6 +33,7 @@ EXTRA_DIST = \
 	scripts/lcov.sh \
 	scripts/valgrind.sh \
 	win32 \
+	win32/vs9/libnice.def \
 	m4/introspection.m4
 
 MAINTAINERCLEANFILES = ar-lib
@@ -41,6 +42,22 @@ dist_check_SCRIPTS = \
 	scripts/check-symbols.sh \
 	scripts/make-symbol-list.sh
 
+# Generate the win32 DLL symbol export file.
+# The stun_*() symbols at the end have historically been exported on Windows
+# but not Linux, for no particular reason. They can’t be removed without
+# breaking ABI. FIXME: Remove them when we next break ABI.
+win32/vs9/libnice.def: nice/libnice.sym
+	$(AM_V_GEN)(echo "LIBRARY libnice"; \
+	            echo ""; \
+	            echo "EXPORTS"; \
+	            echo ""; \
+	            cat $<; \
+	            echo "stun_debug"; \
+	            echo "stun_debug_bytes"; \
+	            echo "stun_hash_creds") > $@
+
+CLEANFILES += win32/vs9/libnice.def
+
 lcov:
 	find -name '*.gcda' -delete
 	$(MAKE) $(AM_MAKEFLAGS) check
diff --git a/agent/address.c b/agent/address.c
index a8d9c76..3c20220 100644
--- a/agent/address.c
+++ b/agent/address.c
@@ -51,7 +51,6 @@
 #include "address.h"
 
 #ifdef G_OS_WIN32
-#define inet_pton inet_pton_win32
 #define inet_ntop inet_ntop_win32
 
 /* Defined in recent versions of mingw:
@@ -86,36 +85,6 @@ inet_ntop_win32 (int af, const void *src, char *dst, socklen_t cnt)
   return NULL;
 }
 
-static int
-inet_pton_win32(int af, const char *src, void *dst)
-{
-  struct addrinfo hints, *res, *ressave;
-
-  memset(&hints, 0, sizeof(struct addrinfo));
-  hints.ai_family = af;
-
-  if (getaddrinfo(src, NULL, &hints, &res) != 0) {
-    return 0;
-  }
-
-  ressave = res;
-
-  while (res)  {
-    if( res->ai_addr->sa_family == AF_INET) {
-      memcpy(dst, &((struct sockaddr_in *) res->ai_addr)->sin_addr,
-          sizeof(struct in_addr));
-      res = res->ai_next;
-    } else if(res->ai_addr->sa_family == AF_INET6) {
-      memcpy(dst, &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr,
-          sizeof(struct in_addr6));
-      res = res->ai_next;
-    }
-  }
-
-  freeaddrinfo(ressave);
-  return 1;
-}
-
 #endif
 
 
@@ -297,7 +266,8 @@ nice_address_equal (const NiceAddress *a, const NiceAddress *b)
     case AF_INET6:
       return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr)
           && (a->s.ip6.sin6_port == b->s.ip6.sin6_port)
-          && (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id);
+          && (a->s.ip6.sin6_scope_id == 0 || b->s.ip6.sin6_scope_id == 0 ||
+              (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id));
 
     default:
       g_return_val_if_reached (FALSE);
@@ -412,7 +382,8 @@ nice_address_equal_no_port (const NiceAddress *a, const NiceAddress *b)
 
     case AF_INET6:
       return IN6_ARE_ADDR_EQUAL (&a->s.ip6.sin6_addr, &b->s.ip6.sin6_addr)
-          && (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id);
+          && (a->s.ip6.sin6_scope_id == 0 || b->s.ip6.sin6_scope_id == 0 ||
+              (a->s.ip6.sin6_scope_id == b->s.ip6.sin6_scope_id));
 
     default:
       g_return_val_if_reached (FALSE);
diff --git a/agent/agent-priv.h b/agent/agent-priv.h
index c413bc1..d66a9ef 100644
--- a/agent/agent-priv.h
+++ b/agent/agent-priv.h
@@ -130,6 +130,7 @@ struct _NiceAgent
   gboolean 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 */
 
   GSList *local_addresses;        /* list of NiceAddresses for local
 				     interfaces */
@@ -139,6 +140,7 @@ struct _NiceAgent
   guint next_stream_id;           /* id of next created candidate */
   NiceRNG *rng;                   /* random number generator */
   GSList *discovery_list;         /* list of CandidateDiscovery items */
+  GSList *triggered_check_queue;  /* pairs in the triggered check list */
   guint discovery_unsched_items;  /* number of discovery items unscheduled */
   GSource *discovery_timer_source; /* source of discovery timer */
   GSource *conncheck_timer_source; /* source of conncheck timer */
@@ -171,10 +173,10 @@ agent_find_component (
   NiceAgent *agent,
   guint stream_id,
   guint component_id,
-  Stream **stream,
-  Component **component);
+  NiceStream **stream,
+  NiceComponent **component) G_GNUC_WARN_UNUSED_RESULT;
 
-Stream *agent_find_stream (NiceAgent *agent, guint stream_id);
+NiceStream *agent_find_stream (NiceAgent *agent, guint stream_id);
 
 void agent_gathering_done (NiceAgent *agent);
 void agent_signal_gathering_done (NiceAgent *agent);
@@ -202,7 +204,7 @@ void agent_signal_new_candidate (
 
 void agent_signal_new_remote_candidate (NiceAgent *agent, NiceCandidate *candidate);
 
-void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream);
+void agent_signal_initial_binding_request_received (NiceAgent *agent, NiceStream *stream);
 
 guint64 agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandidate *remote);
 
@@ -220,6 +222,8 @@ void nice_agent_init_stun_agent (NiceAgent *agent, StunAgent *stun_agent);
 
 void _priv_set_socket_tos (NiceAgent *agent, NiceSocket *sock, gint tos);
 
+void _tcp_sock_is_writable (NiceSocket *sock, gpointer user_data);
+
 gboolean
 component_io_cb (
   GSocket *gsocket,
@@ -274,10 +278,14 @@ void nice_debug_init (void);
 
 #ifdef NDEBUG
 static inline gboolean nice_debug_is_enabled (void) { return FALSE; }
+static inline gboolean nice_debug_is_verbose (void) { return FALSE; }
 static inline void nice_debug (const char *fmt, ...) { }
+static inline void nice_debug_verbose (const char *fmt, ...) { }
 #else
 gboolean nice_debug_is_enabled (void);
+gboolean nice_debug_is_verbose (void);
 void nice_debug (const char *fmt, ...) G_GNUC_PRINTF (1, 2);
+void nice_debug_verbose (const char *fmt, ...) G_GNUC_PRINTF (1, 2);
 #endif
 
 #endif /*_NICE_AGENT_PRIV_H */
diff --git a/agent/agent.c b/agent/agent.c
index 259fdc9..f6c7a36 100644
--- a/agent/agent.c
+++ b/agent/agent.c
@@ -86,6 +86,7 @@
 static void
 nice_debug_input_message_composition (const NiceInputMessage *messages,
     guint n_messages);
+static const gchar *_cand_type_to_sdp (NiceCandidateType type);
 
 G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT);
 
@@ -110,7 +111,8 @@ enum
   PROP_ICE_UDP,
   PROP_ICE_TCP,
   PROP_BYTESTREAM_TCP,
-  PROP_KEEPALIVE_CONNCHECK
+  PROP_KEEPALIVE_CONNCHECK,
+  PROP_FORCE_RELAY,
 };
 
 
@@ -133,11 +135,7 @@ enum
 
 static guint signals[N_SIGNALS];
 
-#if GLIB_CHECK_VERSION(2,31,8)
 static GMutex agent_mutex;    /* Mutex used for thread-safe lib */
-#else
-static GStaticMutex agent_mutex = G_STATIC_MUTEX_INIT;
-#endif
 
 static void priv_stop_upnp (NiceAgent *agent);
 
@@ -148,7 +146,7 @@ static void pseudo_tcp_socket_closed (PseudoTcpSocket *sock, guint32 err,
     gpointer user_data);
 static PseudoTcpWriteResult pseudo_tcp_socket_write_packet (PseudoTcpSocket *sock,
     const gchar *buffer, guint32 len, gpointer user_data);
-static void adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component);
+static void adjust_tcp_clock (NiceAgent *agent, NiceStream *stream, NiceComponent *component);
 
 static void nice_agent_dispose (GObject *object);
 static void nice_agent_get_property (GObject *object,
@@ -156,7 +154,6 @@ static void nice_agent_get_property (GObject *object,
 static void nice_agent_set_property (GObject *object,
   guint property_id, const GValue *value, GParamSpec *pspec);
 
-#if GLIB_CHECK_VERSION(2,31,8)
 void agent_lock (void)
 {
   g_mutex_lock (&agent_mutex);
@@ -167,19 +164,6 @@ void agent_unlock (void)
   g_mutex_unlock (&agent_mutex);
 }
 
-#else
-void agent_lock(void)
-{
-  g_static_mutex_lock (&agent_mutex);
-}
-
-void agent_unlock(void)
-{
-  g_static_mutex_unlock (&agent_mutex);
-}
-
-#endif
-
 static GType _nice_agent_stream_ids_get_type (void);
 
 G_DEFINE_POINTER_TYPE (_NiceAgentStreamIds, _nice_agent_stream_ids);
@@ -314,13 +298,13 @@ agent_to_turn_socket_compatibility (NiceAgent *agent)
       NICE_TURN_SOCKET_COMPATIBILITY_RFC5766;
 }
 
-Stream *agent_find_stream (NiceAgent *agent, guint stream_id)
+NiceStream *agent_find_stream (NiceAgent *agent, guint stream_id)
 {
   GSList *i;
 
   for (i = agent->streams; i; i = i->next)
     {
-      Stream *s = i->data;
+      NiceStream *s = i->data;
 
       if (s->id == stream_id)
         return s;
@@ -335,18 +319,18 @@ agent_find_component (
   NiceAgent *agent,
   guint stream_id,
   guint component_id,
-  Stream **stream,
-  Component **component)
+  NiceStream **stream,
+  NiceComponent **component)
 {
-  Stream *s;
-  Component *c;
+  NiceStream *s;
+  NiceComponent *c;
 
   s = agent_find_stream (agent, stream_id);
 
   if (s == NULL)
     return FALSE;
 
-  c = stream_find_component_by_id (s, component_id);
+  c = nice_stream_find_component_by_id (s, component_id);
 
   if (c == NULL)
     return FALSE;
@@ -706,6 +690,24 @@ nice_agent_class_init (NiceAgentClass *klass)
 	FALSE,
         G_PARAM_READWRITE));
 
+   /**
+   * NiceAgent:force-relay
+   *
+   * Force all traffic to go through a relay for added privacy, this
+   * allows hiding the local IP address. When this is enabled, so
+   * local candidates are available before relay servers have been set
+   * with nice_agent_set_relay_info().
+   *
+   * Since: UNRELEASED
+   */
+   g_object_class_install_property (gobject_class, PROP_FORCE_RELAY,
+      g_param_spec_boolean (
+        "force-relay",
+        "Force Relay",
+        "Force all traffic to go through a relay for added privacy.",
+	FALSE,
+        G_PARAM_READWRITE));
+
   /* install signals */
 
   /**
@@ -713,9 +715,12 @@ nice_agent_class_init (NiceAgentClass *klass)
    * @agent: The #NiceAgent object
    * @stream_id: The ID of the stream
    * @component_id: The ID of the component
-   * @state: The #NiceComponentState of the component
+   * @state: The new #NiceComponentState of the component
+   *
+   * This signal is fired whenever a component’s state changes. There are many
+   * valid state transitions.
    *
-   * This signal is fired whenever a component's state changes
+   * ![State transition diagram](states.png)
    */
   signals[SIGNAL_COMPONENT_STATE_CHANGED] =
       g_signal_new (
@@ -1178,6 +1183,10 @@ nice_agent_get_property (
         g_value_set_boolean (value, agent->keepalive_conncheck);
       break;
 
+    case PROP_FORCE_RELAY:
+      g_value_set_boolean (value, agent->force_relay);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -1231,11 +1240,11 @@ nice_agent_reset_all_stun_agents (NiceAgent *agent, gboolean only_software)
 
   for (stream_item = agent->streams; stream_item;
        stream_item = stream_item->next) {
-    Stream *stream = stream_item->data;
+    NiceStream *stream = stream_item->data;
 
     for (component_item = stream->components; component_item;
          component_item = component_item->next) {
-      Component *component = component_item->data;
+      NiceComponent *component = component_item->data;
 
       if (only_software)
         stun_agent_set_software (&component->stun_agent,
@@ -1361,6 +1370,10 @@ nice_agent_set_property (
       agent->keepalive_conncheck = g_value_get_boolean (value);
       break;
 
+    case PROP_FORCE_RELAY:
+      agent->force_relay = g_value_get_boolean (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -1371,7 +1384,7 @@ nice_agent_set_property (
 
 
 static void
- agent_signal_socket_writable (NiceAgent *agent, Component *component)
+ agent_signal_socket_writable (NiceAgent *agent, NiceComponent *component)
 {
   g_cancellable_cancel (component->tcp_writable_cancellable);
 
@@ -1380,7 +1393,7 @@ static void
 }
 
 static void
-pseudo_tcp_socket_create (NiceAgent *agent, Stream *stream, Component *component)
+pseudo_tcp_socket_create (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
 {
   PseudoTcpCallbacks tcp_callbacks = {component,
                                       pseudo_tcp_socket_opened,
@@ -1394,8 +1407,8 @@ pseudo_tcp_socket_create (NiceAgent *agent, Stream *stream, Component *component
       agent, component->id);
 }
 
-static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream,
-    Component *component)
+static void priv_pseudo_tcp_error (NiceAgent *agent, NiceStream *stream,
+    NiceComponent *component)
 {
   if (component->tcp_writable_cancellable) {
     g_cancellable_cancel (component->tcp_writable_cancellable);
@@ -1405,7 +1418,7 @@ static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream,
   if (component->tcp) {
     agent_signal_component_state_change (agent, stream->id,
         component->id, NICE_COMPONENT_STATE_FAILED);
-    component_detach_all_sockets (component);
+    nice_component_detach_all_sockets (component);
     pseudo_tcp_socket_close (component->tcp, TRUE);
   }
 
@@ -1419,9 +1432,9 @@ static void priv_pseudo_tcp_error (NiceAgent *agent, Stream *stream,
 static void
 pseudo_tcp_socket_opened (PseudoTcpSocket *sock, gpointer user_data)
 {
-  Component *component = user_data;
+  NiceComponent *component = user_data;
   NiceAgent *agent = component->agent;
-  Stream *stream = component->stream;
+  NiceStream *stream = component->stream;
 
   nice_debug ("Agent %p: s%d:%d pseudo Tcp socket Opened", agent,
       stream->id, component->id);
@@ -1533,7 +1546,7 @@ pseudo_tcp_socket_recv_messages (PseudoTcpSocket *self,
             (gchar *) buffer->buffer + iter->offset,
             buffer->size - iter->offset);
 
-        nice_debug ("%s: Received %" G_GSSIZE_FORMAT " bytes into "
+        nice_debug_verbose ("%s: Received %" G_GSSIZE_FORMAT " bytes into "
             "buffer %p (offset %" G_GSIZE_FORMAT ", length %" G_GSIZE_FORMAT
             ").", G_STRFUNC, len, buffer->buffer, iter->offset, buffer->size);
 
@@ -1580,25 +1593,25 @@ done:
 static void
 pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data)
 {
-  Component *component = user_data;
+  NiceComponent *component = user_data;
   NiceAgent *agent = component->agent;
-  Stream *stream = component->stream;
+  NiceStream *stream = component->stream;
   gboolean has_io_callback;
   guint stream_id = stream->id;
   guint component_id = component->id;
 
   g_object_ref (agent);
 
-  nice_debug ("Agent %p: s%d:%d pseudo Tcp socket readable", agent,
+  nice_debug_verbose ("Agent %p: s%d:%d pseudo Tcp socket readable", agent,
       stream->id, component->id);
 
   component->tcp_readable = TRUE;
 
-  has_io_callback = component_has_io_callback (component);
+  has_io_callback = nice_component_has_io_callback (component);
 
   /* Only dequeue pseudo-TCP data if we can reliably inform the client. The
    * agent lock is held here, so has_io_callback can only change during
-   * component_emit_io_callback(), after which it’s re-queried. This ensures
+   * nice_component_emit_io_callback(), after which it’s re-queried. This ensures
    * no data loss of packets already received and dequeued. */
   if (has_io_callback) {
     do {
@@ -1641,7 +1654,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data)
         break;
       }
 
-      component_emit_io_callback (component, buf, len);
+      nice_component_emit_io_callback (component, buf, len);
 
       if (!agent_find_component (agent, stream_id, component_id,
               &stream, &component)) {
@@ -1653,7 +1666,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data)
         goto out;
       }
 
-      has_io_callback = component_has_io_callback (component);
+      has_io_callback = nice_component_has_io_callback (component);
     } while (has_io_callback);
   } else if (component->recv_messages != NULL) {
     gint n_valid_messages;
@@ -1667,7 +1680,7 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data)
         component->recv_messages, component->n_recv_messages,
         &component->recv_messages_iter, &child_error);
 
-    nice_debug ("%s: Client buffers case: Received %d valid messages:",
+    nice_debug_verbose ("%s: Client buffers case: Received %d valid messages:",
         G_STRFUNC, n_valid_messages);
     nice_debug_input_message_composition (component->recv_messages,
         component->n_recv_messages);
@@ -1700,17 +1713,16 @@ pseudo_tcp_socket_readable (PseudoTcpSocket *sock, gpointer user_data)
 out:
 
   g_object_unref (agent);
-
 }
 
 static void
 pseudo_tcp_socket_writable (PseudoTcpSocket *sock, gpointer user_data)
 {
-  Component *component = user_data;
+  NiceComponent *component = user_data;
   NiceAgent *agent = component->agent;
-  Stream *stream = component->stream;
+  NiceStream *stream = component->stream;
 
-  nice_debug ("Agent %p: s%d:%d pseudo Tcp socket writable", agent,
+  nice_debug_verbose ("Agent %p: s%d:%d pseudo Tcp socket writable", agent,
       stream->id, component->id);
 
   agent_signal_socket_writable (agent, component);
@@ -1720,9 +1732,9 @@ static void
 pseudo_tcp_socket_closed (PseudoTcpSocket *sock, guint32 err,
     gpointer user_data)
 {
-  Component *component = user_data;
+  NiceComponent *component = user_data;
   NiceAgent *agent = component->agent;
-  Stream *stream = component->stream;
+  NiceStream *stream = component->stream;
 
   nice_debug ("Agent %p: s%d:%d pseudo Tcp socket closed. "
       "Calling priv_pseudo_tcp_error().",  agent, stream->id, component->id);
@@ -1734,7 +1746,7 @@ static PseudoTcpWriteResult
 pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket,
     const gchar *buffer, guint32 len, gpointer user_data)
 {
-  Component *component = user_data;
+  NiceComponent *component = user_data;
 
   if (component->selected_pair.local != NULL) {
     NiceSocket *sock;
@@ -1747,7 +1759,7 @@ pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket,
       gchar tmpbuf[INET6_ADDRSTRLEN];
       nice_address_to_string (addr, tmpbuf);
 
-      nice_debug (
+      nice_debug_verbose (
           "Agent %p : s%d:%d: sending %d bytes on socket %p (FD %d) to [%s]:%d",
           component->agent, component->stream->id, component->id, len,
           sock->fileno, g_socket_get_fd (sock->fileno), tmpbuf,
@@ -1775,8 +1787,8 @@ pseudo_tcp_socket_write_packet (PseudoTcpSocket *psocket,
 static gboolean
 notify_pseudo_tcp_socket_clock (gpointer user_data)
 {
-  Component *component = user_data;
-  Stream *stream;
+  NiceComponent *component = user_data;
+  NiceStream *stream;
   NiceAgent *agent;
 
   agent_lock();
@@ -1800,7 +1812,7 @@ notify_pseudo_tcp_socket_clock (gpointer user_data)
 }
 
 static void
-adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component)
+adjust_tcp_clock (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
 {
   if (!pseudo_tcp_socket_is_closed (component->tcp)) {
     guint64 timeout = component->last_clock_timeout;
@@ -1809,13 +1821,7 @@ adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component)
       if (timeout != component->last_clock_timeout) {
         component->last_clock_timeout = timeout;
         if (component->tcp_clock) {
-#if GLIB_CHECK_VERSION (2, 36, 0)
           g_source_set_ready_time (component->tcp_clock, timeout * 1000);
-#else
-          g_source_destroy (component->tcp_clock);
-          g_source_unref (component->tcp_clock);
-          component->tcp_clock = NULL;
-#endif
         }
         if (!component->tcp_clock) {
           long interval = timeout - (guint32) (g_get_monotonic_time () / 1000);
@@ -1837,19 +1843,19 @@ adjust_tcp_clock (NiceAgent *agent, Stream *stream, Component *component)
   }
 }
 
-static void
+void
 _tcp_sock_is_writable (NiceSocket *sock, gpointer user_data)
 {
-  Component *component = user_data;
+  NiceComponent *component = user_data;
   NiceAgent *agent = component->agent;
-  Stream *stream = component->stream;
+  NiceStream *stream = component->stream;
 
   agent_lock ();
 
   /* Don't signal writable if the socket that has become writable is not
    * the selected pair */
   if (component->selected_pair.local == NULL ||
-      component->selected_pair.local->sockptr != sock) {
+      !nice_socket_is_based_on (component->selected_pair.local->sockptr, sock)) {
     agent_unlock ();
     return;
   }
@@ -1883,12 +1889,17 @@ void agent_gathering_done (NiceAgent *agent)
   GSList *i, *j, *k, *l, *m;
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     for (j = stream->components; j; j = j->next) {
-      Component *component = j->data;
+      NiceComponent *component = j->data;
 
       for (k = component->local_candidates; k; k = k->next) {
         NiceCandidate *local_candidate = k->data;
+
+        if (agent->force_relay &&
+            local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED)
+          continue;
+
 	if (nice_debug_is_enabled ()) {
 	  gchar tmpbuf[INET6_ADDRSTRLEN];
 	  nice_address_to_string (&local_candidate->addr, tmpbuf);
@@ -1933,7 +1944,7 @@ void agent_signal_gathering_done (NiceAgent *agent)
   GSList *i;
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     if (stream->gathering) {
       stream->gathering = FALSE;
       agent_queue_signal (agent, signals[SIGNAL_CANDIDATE_GATHERING_DONE],
@@ -1942,7 +1953,9 @@ void agent_signal_gathering_done (NiceAgent *agent)
   }
 }
 
-void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream)
+void
+agent_signal_initial_binding_request_received (NiceAgent *agent,
+    NiceStream *stream)
 {
   if (stream->initial_binding_request_received != TRUE) {
     stream->initial_binding_request_received = TRUE;
@@ -1957,8 +1970,8 @@ void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *st
  *
  * Must be called with the agent lock held. */
 static void
-process_queued_tcp_packets (NiceAgent *agent, Stream *stream,
-    Component *component)
+process_queued_tcp_packets (NiceAgent *agent, NiceStream *stream,
+    NiceComponent *component)
 {
   GOutputVector *vec;
   guint stream_id = stream->id;
@@ -1972,7 +1985,7 @@ process_queued_tcp_packets (NiceAgent *agent, Stream *stream,
     return;
   }
 
-  nice_debug ("%s: Sending outstanding packets for agent %p.", G_STRFUNC,
+  nice_debug_verbose ("%s: Sending outstanding packets for agent %p.", G_STRFUNC,
       agent);
 
   while ((vec = g_queue_peek_head (&component->queued_tcp_packets)) != NULL) {
@@ -2011,8 +2024,8 @@ process_queued_tcp_packets (NiceAgent *agent, Stream *stream,
 void agent_signal_new_selected_pair (NiceAgent *agent, guint stream_id,
     guint component_id, NiceCandidate *lcandidate, NiceCandidate *rcandidate)
 {
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
 
   if (!agent_find_component (agent, stream_id, component_id,
           &stream, &component))
@@ -2122,28 +2135,67 @@ nice_component_state_to_string (NiceComponentState state)
     }
 }
 
-void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state)
+void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState new_state)
 {
-  Component *component;
-  Stream *stream;
+  NiceComponentState old_state;
+  NiceComponent *component;
+  NiceStream *stream;
+
+  g_return_if_fail (new_state < NICE_COMPONENT_STATE_LAST);
 
   if (!agent_find_component (agent, stream_id, component_id,
           &stream, &component))
     return;
 
-  if (component->state != state && state < NICE_COMPONENT_STATE_LAST) {
-    nice_debug ("Agent %p : stream %u component %u STATE-CHANGE %s -> %s.", agent,
-        stream_id, component_id, nice_component_state_to_string (component->state),
-        nice_component_state_to_string (state));
+  /* Validate the state change. */
+  old_state = component->state;
 
-    component->state = state;
+  if (new_state == old_state) {
+    return;
+  }
 
-    if (agent->reliable)
-      process_queued_tcp_packets (agent, stream, component);
+  nice_debug ("Agent %p : stream %u component %u STATE-CHANGE %s -> %s.", agent,
+      stream_id, component_id, nice_component_state_to_string (old_state),
+      nice_component_state_to_string (new_state));
+
+  /* Check whether it’s a valid state transition. */
+#define TRANSITION(OLD, NEW) \
+  (old_state == NICE_COMPONENT_STATE_##OLD && \
+   new_state == NICE_COMPONENT_STATE_##NEW)
+
+  g_assert (/* Can (almost) always transition to FAILED (including
+             * DISCONNECTED → FAILED which happens if one component fails
+             * before another leaves DISCONNECTED): */
+            TRANSITION (DISCONNECTED, FAILED) ||
+            TRANSITION (GATHERING, FAILED) ||
+            TRANSITION (CONNECTING, FAILED) ||
+            TRANSITION (CONNECTED, FAILED) ||
+            TRANSITION (READY, FAILED) ||
+            /* Standard progression towards a ready connection: */
+            TRANSITION (DISCONNECTED, GATHERING) ||
+            TRANSITION (GATHERING, CONNECTING) ||
+            TRANSITION (CONNECTING, CONNECTED) ||
+            TRANSITION (CONNECTED, READY) ||
+            /* priv_conn_check_add_for_candidate_pair_matched(): */
+            TRANSITION (READY, CONNECTED) ||
+            /* If set_remote_candidates() is called with new candidates after
+             * reaching FAILED: */
+            TRANSITION (FAILED, CONNECTING) ||
+            /* if new relay servers are added to a failed connection */
+            TRANSITION (FAILED, GATHERING) ||
+            /* Possible by calling set_remote_candidates() without calling
+             * nice_agent_gather_candidates(): */
+            TRANSITION (DISCONNECTED, CONNECTING));
+
+#undef TRANSITION
+
+  component->state = new_state;
 
-    agent_queue_signal (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED],
-        stream_id, component_id, state);
-  }
+  if (agent->reliable)
+    process_queued_tcp_packets (agent, stream, component);
+
+  agent_queue_signal (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED],
+      stream_id, component_id, new_state);
 }
 
 guint64
@@ -2158,7 +2210,7 @@ agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandi
 static void
 priv_add_new_candidate_discovery_stun (NiceAgent *agent,
     NiceSocket *nicesock, NiceAddress server,
-    Stream *stream, guint component_id)
+    NiceStream *stream, guint component_id)
 {
   CandidateDiscovery *cdisco;
 
@@ -2171,7 +2223,7 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent,
   cdisco->nicesock = nicesock;
   cdisco->server = server;
   cdisco->stream = stream;
-  cdisco->component = stream_find_component_by_id (stream, component_id);
+  cdisco->component = nice_stream_find_component_by_id (stream, component_id);
   cdisco->agent = agent;
   stun_agent_init (&cdisco->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
       STUN_COMPATIBILITY_RFC3489,
@@ -2179,7 +2231,7 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent,
        agent->compatibility == NICE_COMPATIBILITY_OC2007R2) ?
         STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES : 0);
 
-  nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p\n",
+  nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p",
       agent, cdisco);
 
   agent->discovery_list = g_slist_append (agent->discovery_list, cdisco);
@@ -2189,10 +2241,10 @@ priv_add_new_candidate_discovery_stun (NiceAgent *agent,
 static void
 priv_add_new_candidate_discovery_turn (NiceAgent *agent,
     NiceSocket *nicesock, TurnServer *turn,
-    Stream *stream, guint component_id, gboolean turn_tcp)
+    NiceStream *stream, guint component_id, gboolean turn_tcp)
 {
   CandidateDiscovery *cdisco;
-  Component *component = stream_find_component_by_id (stream, component_id);
+  NiceComponent *component = nice_stream_find_component_by_id (stream, component_id);
   NiceAddress local_address;
 
   /* note: no need to check for redundant candidates, as this is
@@ -2214,7 +2266,7 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent,
       new_socket = nice_udp_bsd_socket_new (&addr);
       if (new_socket) {
         _priv_set_socket_tos (agent, new_socket, stream->tos);
-        component_attach_socket (component, new_socket);
+        nice_component_attach_socket (component, new_socket);
         nicesock = new_socket;
       }
     }
@@ -2308,14 +2360,14 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent,
     cdisco->nicesock = nice_udp_turn_over_tcp_socket_new (nicesock,
         agent_to_turn_socket_compatibility (agent));
 
-    component_attach_socket (component, cdisco->nicesock);
+    nice_component_attach_socket (component, cdisco->nicesock);
   }
 
   cdisco->turn = turn_server_ref (turn);
   cdisco->server = turn->server;
 
   cdisco->stream = stream;
-  cdisco->component = stream_find_component_by_id (stream, component_id);
+  cdisco->component = nice_stream_find_component_by_id (stream, component_id);
   cdisco->agent = agent;
 
   if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
@@ -2342,7 +2394,7 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent,
   }
   stun_agent_set_software (&cdisco->stun_agent, agent->software_attribute);
 
-  nice_debug ("Agent %p : Adding new relay-rflx candidate discovery %p\n",
+  nice_debug ("Agent %p : Adding new relay-rflx candidate discovery %p",
       agent, cdisco);
   agent->discovery_list = g_slist_append (agent->discovery_list, cdisco);
   ++agent->discovery_unsched_items;
@@ -2353,7 +2405,7 @@ nice_agent_add_stream (
   NiceAgent *agent,
   guint n_components)
 {
-  Stream *stream;
+  NiceStream *stream;
   guint ret = 0;
   guint i;
 
@@ -2361,7 +2413,7 @@ nice_agent_add_stream (
   g_return_val_if_fail (n_components >= 1, 0);
 
   agent_lock();
-  stream = stream_new (n_components, agent);
+  stream = nice_stream_new (n_components, agent);
 
   agent->streams = g_slist_append (agent->streams, stream);
   stream->id = agent->next_stream_id++;
@@ -2369,7 +2421,7 @@ nice_agent_add_stream (
   if (agent->reliable) {
     nice_debug ("Agent %p : reliable stream", agent);
     for (i = 0; i < n_components; i++) {
-      Component *component = stream_find_component_by_id (stream, i + 1);
+      NiceComponent *component = nice_stream_find_component_by_id (stream, i + 1);
       if (component) {
         pseudo_tcp_socket_create (agent, stream, component);
       } else {
@@ -2378,7 +2430,7 @@ nice_agent_add_stream (
     }
   }
 
-  stream_initialize_credentials (stream, agent->rng);
+  nice_stream_initialize_credentials (stream, agent->rng);
 
   ret = stream->id;
 
@@ -2395,8 +2447,8 @@ nice_agent_set_relay_info(NiceAgent *agent,
     NiceRelayType type)
 {
 
-  Component *component = NULL;
-  Stream *stream = NULL;
+  NiceComponent *component = NULL;
+  NiceStream *stream = NULL;
   gboolean ret = TRUE;
   TurnServer *turn;
 
@@ -2439,7 +2491,9 @@ nice_agent_set_relay_info(NiceAgent *agent,
       NiceCandidate *candidate = i->data;
 
       if  (candidate->type == NICE_CANDIDATE_TYPE_HOST &&
-           candidate->transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
+           candidate->transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
+          nice_address_ip_version (&candidate->addr) ==
+          nice_address_ip_version (&turn->server))
         priv_add_new_candidate_discovery_turn (agent,
             candidate->sockptr, turn, stream, component_id,
             candidate->transport != NICE_CANDIDATE_TRANSPORT_UDP);
@@ -2547,12 +2601,16 @@ static void _upnp_mapped_external_port (GUPnPSimpleIgd *self, gchar *proto,
   nice_address_set_port (&externaddr, external_port);
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     for (j = stream->components; j; j = j->next) {
-      Component *component = j->data;
+      NiceComponent *component = j->data;
       for (k = component->local_candidates; k; k = k->next) {
         NiceCandidate *local_candidate = k->data;
 
+        if (agent->force_relay &&
+            local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED)
+          continue;
+
         if (nice_address_equal (&localaddr, &local_candidate->base_addr)) {
           discovery_add_server_reflexive_candidate (
               agent,
@@ -2613,7 +2671,7 @@ nice_agent_gather_candidates (
 {
   guint cid;
   GSList *i;
-  Stream *stream;
+  NiceStream *stream;
   GSList *local_addresses = NULL;
   gboolean ret = TRUE;
 
@@ -2638,13 +2696,10 @@ nice_agent_gather_candidates (
       agent->full_mode ? "ICE-FULL" : "ICE-LITE");
 
 #ifdef HAVE_GUPNP
-  if (agent->upnp_enabled && agent->upnp == NULL) {
+  if (agent->upnp_enabled && agent->upnp == NULL && !agent->force_relay) {
     agent->upnp = gupnp_simple_igd_thread_new ();
 
     if (agent->upnp) {
-      agent_timeout_add_with_context (agent, &agent->upnp_timer_source,
-          "UPnP timeout", agent->upnp_timeout, priv_upnp_timeout_cb, agent);
-
       g_signal_connect (agent->upnp, "mapped-external-port",
           G_CALLBACK (_upnp_mapped_external_port), agent);
       g_signal_connect (agent->upnp, "error-mapping-port",
@@ -2698,7 +2753,7 @@ nice_agent_gather_candidates (
 #endif
 
     for (cid = 1; cid <= stream->n_components; cid++) {
-      Component *component = stream_find_component_by_id (stream, cid);
+      NiceComponent *component = nice_stream_find_component_by_id (stream, cid);
       enum {
         ADD_HOST_MIN = 0,
         ADD_HOST_UDP = ADD_HOST_MIN,
@@ -2769,6 +2824,10 @@ nice_agent_gather_candidates (
                 " s%d:%d. Invalid interface?", agent, ip, stream->id,
                 component->id);
           }
+          if (agent->local_addresses == NULL) {
+            /* Ignore when an auto-generated address fails. */
+            continue;
+          }
           ret = FALSE;
           goto error;
         }
@@ -2791,11 +2850,14 @@ nice_agent_gather_candidates (
             0, local_ip, nice_address_get_port (base_addr),
             0, PACKAGE_STRING);
         agent->upnp_mapping = g_slist_prepend (agent->upnp_mapping, base_addr);
+
+        agent_timeout_add_with_context (agent, &agent->upnp_timer_source,
+            "UPnP timeout", agent->upnp_timeout, priv_upnp_timeout_cb, agent);
       }
 #endif
 
         /* TODO: Add server-reflexive support for TCP candidates */
-        if (agent->full_mode && agent->stun_server_ip &&
+        if (agent->full_mode && agent->stun_server_ip && !agent->force_relay &&
             transport == NICE_CANDIDATE_TRANSPORT_UDP) {
           NiceAddress stun_server;
           if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
@@ -2834,9 +2896,13 @@ nice_agent_gather_candidates (
   /* Only signal the new candidates after we're sure that the gathering was
    * succesfful. But before sending gathering-done */
   for (cid = 1; cid <= stream->n_components; cid++) {
-    Component *component = stream_find_component_by_id (stream, cid);
+    NiceComponent *component = nice_stream_find_component_by_id (stream, cid);
     for (i = component->local_candidates; i; i = i->next) {
       NiceCandidate *candidate = i->data;
+
+      if (agent->force_relay && candidate->type != NICE_CANDIDATE_TYPE_RELAYED)
+        continue;
+
       agent_signal_new_candidate (agent, candidate);
     }
   }
@@ -2863,9 +2929,9 @@ nice_agent_gather_candidates (
   if (ret == FALSE) {
     priv_stop_upnp (agent);
     for (cid = 1; cid <= stream->n_components; cid++) {
-      Component *component = stream_find_component_by_id (stream, cid);
+      NiceComponent *component = nice_stream_find_component_by_id (stream, cid);
 
-      component_free_socket_sources (component);
+      nice_component_free_socket_sources (component);
 
       for (i = component->local_candidates; i; i = i->next) {
         NiceCandidate *candidate = i->data;
@@ -2938,7 +3004,7 @@ nice_agent_remove_stream (
 
   /* note that streams/candidates can be in use by other threads */
 
-  Stream *stream;
+  NiceStream *stream;
 
   g_return_if_fail (NICE_IS_AGENT (agent));
   g_return_if_fail (stream_id >= 1);
@@ -2958,7 +3024,7 @@ nice_agent_remove_stream (
 
   /* Remove the stream and signal its removal. */
   agent->streams = g_slist_remove (agent->streams, stream);
-  stream_close (stream);
+  nice_stream_close (stream);
 
   if (!agent->streams)
     priv_remove_keepalive_timer (agent);
@@ -2971,7 +3037,7 @@ nice_agent_remove_stream (
   /* Actually free the stream. This should be done with the lock released, as
    * it could end up disposing of a NiceIOStream, which tries to take the
    * agent lock itself. */
-  stream_free (stream);
+  g_object_unref (stream);
 
   return;
 }
@@ -2980,8 +3046,8 @@ NICEAPI_EXPORT void
 nice_agent_set_port_range (NiceAgent *agent, guint stream_id, guint component_id,
     guint min_port, guint max_port)
 {
-  Stream *stream;
-  Component *component;
+  NiceStream *stream;
+  NiceComponent *component;
 
   g_return_if_fail (NICE_IS_AGENT (agent));
   g_return_if_fail (stream_id >= 1);
@@ -3033,15 +3099,28 @@ static gboolean priv_add_remote_candidate (
   const gchar *password,
   const gchar *foundation)
 {
-  Component *component;
+  NiceComponent *component;
   NiceCandidate *candidate;
 
   if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
     return FALSE;
 
   /* step: check whether the candidate already exists */
-  candidate = component_find_remote_candidate(component, addr, transport);
-  if (candidate) {
+  candidate = nice_component_find_remote_candidate (component, addr, transport);
+
+  /* If it was a discovered remote peer reflexive candidate, then it should
+   * be updated according to RFC 5245 section 7.2.1.3 */
+  if (candidate && candidate->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE &&
+      candidate->priority == priority) {
+    nice_debug ("Agent %p : Updating existing peer-rfx remote candidate to %s",
+        agent, _cand_type_to_sdp (type));
+    candidate->type = type;
+    /* If it got there, the next one will also be ran, so the foundation
+     * will be set.
+     */
+  }
+
+  if (candidate && candidate->type == type) {
     if (nice_debug_is_enabled ()) {
       gchar tmpbuf[INET6_ADDRSTRLEN];
       nice_address_to_string (addr, tmpbuf);
@@ -3051,7 +3130,6 @@ static gboolean priv_add_remote_candidate (
           username, password, priority);
     }
     /* case 1: an existing candidate, update the attributes */
-    candidate->type = type;
     if (base_addr)
       candidate->base_addr = *base_addr;
     candidate->priority = priority;
@@ -3136,7 +3214,7 @@ nice_agent_set_remote_credentials (
   guint stream_id,
   const gchar *ufrag, const gchar *pwd)
 {
-  Stream *stream;
+  NiceStream *stream;
   gboolean ret = FALSE;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE);
@@ -3167,7 +3245,7 @@ nice_agent_set_local_credentials (
   const gchar *ufrag,
   const gchar *pwd)
 {
-  Stream *stream;
+  NiceStream *stream;
   gboolean ret = FALSE;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE);
@@ -3198,7 +3276,7 @@ nice_agent_get_local_credentials (
   guint stream_id,
   gchar **ufrag, gchar **pwd)
 {
-  Stream *stream;
+  NiceStream *stream;
   gboolean ret = TRUE;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE);
@@ -3226,8 +3304,8 @@ nice_agent_get_local_credentials (
 }
 
 static int
-_set_remote_candidates_locked (NiceAgent *agent, Stream *stream,
-    Component *component, const GSList *candidates)
+_set_remote_candidates_locked (NiceAgent *agent, NiceStream *stream,
+    NiceComponent *component, const GSList *candidates)
 {
   const GSList *i;
   int added = 0;
@@ -3253,12 +3331,10 @@ _set_remote_candidates_locked (NiceAgent *agent, Stream *stream,
     }
   }
 
-  conn_check_remote_candidates_set(agent);
+  conn_check_remote_candidates_set(agent, stream, component);
 
   if (added > 0) {
-    gboolean res = conn_check_schedule_next (agent);
-    if (res != TRUE)
-      nice_debug ("Agent %p : Warning: unable to schedule any conn checks!", agent);
+    conn_check_schedule_next (agent);
   }
 
   return added;
@@ -3269,8 +3345,8 @@ NICEAPI_EXPORT int
 nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint component_id, const GSList *candidates)
 {
   int added = 0;
-  Stream *stream;
-  Component *component;
+  NiceStream *stream;
+  NiceComponent *component;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), 0);
   g_return_val_if_fail (stream_id >= 1, 0);
@@ -3329,14 +3405,15 @@ typedef enum {
 static RecvStatus
 agent_recv_message_unlocked (
   NiceAgent *agent,
-  Stream *stream,
-  Component *component,
+  NiceStream *stream,
+  NiceComponent *component,
   NiceSocket *nicesock,
   NiceInputMessage *message)
 {
   NiceAddress from;
   GList *item;
   gint retval;
+  gboolean is_turn = FALSE;
 
   /* We need an address for packet parsing, below. */
   if (message->from == NULL) {
@@ -3383,7 +3460,7 @@ agent_recv_message_unlocked (
         n_bufs = message->n_buffers;
       }
 
-      local_bufs = g_malloc_n (n_bufs + 1, sizeof (GInputVector));
+      local_bufs = g_alloca ((n_bufs + 1) * sizeof (GInputVector));
       local_message.buffers = local_bufs;
       local_message.n_buffers = n_bufs + 1;
       local_message.from = message->from;
@@ -3400,7 +3477,6 @@ agent_recv_message_unlocked (
       if (retval == 1) {
         message->length = ntohs (rfc4571_frame);
       }
-      g_free (local_bufs);
     } else {
       if (nicesock->type == NICE_SOCKET_TYPE_TCP_PASSIVE) {
         NiceSocket *new_socket;
@@ -3411,7 +3487,7 @@ agent_recv_message_unlocked (
         new_socket = nice_tcp_passive_socket_accept (nicesock);
         if (new_socket) {
           _priv_set_socket_tos (agent, new_socket, stream->tos);
-          component_attach_socket (component, new_socket);
+          nice_component_attach_socket (component, new_socket);
         }
         retval = 0;
       } else {
@@ -3481,7 +3557,7 @@ agent_recv_message_unlocked (
             n_bufs = message->n_buffers;
           }
 
-          local_bufs = g_malloc_n (n_bufs, sizeof (GInputVector));
+          local_bufs = g_alloca (n_bufs * sizeof (GInputVector));
           local_message.buffers = local_bufs;
           local_message.from = message->from;
           local_message.length = 0;
@@ -3508,7 +3584,6 @@ agent_recv_message_unlocked (
             message->length = local_message.length;
             agent->rfc4571_expecting_length -= local_message.length;
           }
-          g_free (local_bufs);
         }
       }
     }
@@ -3516,7 +3591,7 @@ agent_recv_message_unlocked (
     retval = nice_socket_recv_messages (nicesock, message, 1);
   }
 
-  nice_debug ("%s: Received %d valid messages of length %" G_GSIZE_FORMAT
+  nice_debug_verbose ("%s: Received %d valid messages of length %" G_GSIZE_FORMAT
       " from base socket %p.", G_STRFUNC, retval, message->length, nicesock);
 
   if (retval == 0) {
@@ -3535,30 +3610,45 @@ agent_recv_message_unlocked (
     goto done;
   }
 
-  if (nice_debug_is_enabled ()) {
+  if (nice_debug_is_verbose ()) {
     gchar tmpbuf[INET6_ADDRSTRLEN];
     nice_address_to_string (message->from, tmpbuf);
-    nice_debug ("Agent %p : Packet received on local socket %d from [%s]:%u (%" G_GSSIZE_FORMAT " octets).", agent,
+    nice_debug_verbose ("Agent %p : Packet received on local socket %d from [%s]:%u (%" G_GSSIZE_FORMAT " octets).", agent,
         g_socket_get_fd (nicesock->fileno), tmpbuf,
         nice_address_get_port (message->from), message->length);
   }
 
-  for (item = component->turn_servers; item; item = g_list_next (item)) {
+  if (nicesock->type == NICE_SOCKET_TYPE_UDP_TURN)
+    is_turn = TRUE;
+
+  if (!is_turn && component->turn_candidate &&
+      nice_socket_is_based_on (component->turn_candidate->sockptr, nicesock) &&
+      nice_address_equal (message->from,
+          &component->turn_candidate->turn->server)) {
+    is_turn = TRUE;
+    retval = nice_udp_turn_socket_parse_recv_message (
+        component->turn_candidate->sockptr, &nicesock, message);
+  }
+
+  for (item = component->turn_servers; item && !is_turn;
+       item = g_list_next (item)) {
     TurnServer *turn = item->data;
     GSList *i = NULL;
 
     if (!nice_address_equal (message->from, &turn->server))
       continue;
 
-    nice_debug ("Agent %p : Packet received from TURN server candidate.",
+    nice_debug_verbose ("Agent %p : Packet received from TURN server candidate.",
         agent);
+    is_turn = TRUE;
 
     for (i = component->local_candidates; i; i = i->next) {
       NiceCandidate *cand = i->data;
 
       if (cand->type == NICE_CANDIDATE_TYPE_RELAYED &&
+          cand->turn == turn &&
           cand->stream_id == stream->id &&
-          cand->component_id == component->id) {
+          nice_socket_is_based_on (cand->sockptr, nicesock)) {
         retval = nice_udp_turn_socket_parse_recv_message (cand->sockptr, &nicesock,
             message);
         break;
@@ -3567,6 +3657,12 @@ agent_recv_message_unlocked (
     break;
   }
 
+  if (agent->force_relay && !is_turn) {
+    /* Ignore messages not from TURN if TURN is required */
+    retval = RECV_WOULD_BLOCK;  /* EWOULDBLOCK */
+    goto done;
+  }
+
   if (retval == RECV_OOB)
     goto done;
 
@@ -3638,7 +3734,7 @@ agent_recv_message_unlocked (
 
       /* Received data on a reliable connection. */
 
-      nice_debug ("%s: notifying pseudo-TCP of packet, length %" G_GSIZE_FORMAT,
+      nice_debug_verbose ("%s: notifying pseudo-TCP of packet, length %" G_GSIZE_FORMAT,
           G_STRFUNC, message->length);
       pseudo_tcp_socket_notify_message (component->tcp, message);
 
@@ -3672,14 +3768,14 @@ nice_debug_input_message_composition (const NiceInputMessage *messages,
 {
   guint i;
 
-  if (!nice_debug_is_enabled ())
+  if (!nice_debug_is_verbose ())
     return;
 
   for (i = 0; i < n_messages; i++) {
     const NiceInputMessage *message = &messages[i];
     guint j;
 
-    nice_debug ("Message %p (from: %p, length: %" G_GSIZE_FORMAT ")", message,
+    nice_debug_verbose ("Message %p (from: %p, length: %" G_GSIZE_FORMAT ")", message,
         message->from, message->length);
 
     for (j = 0;
@@ -3688,7 +3784,7 @@ nice_debug_input_message_composition (const NiceInputMessage *messages,
          j++) {
       GInputVector *buffer = &message->buffers[j];
 
-      nice_debug ("\tBuffer %p (length: %" G_GSIZE_FORMAT ")", buffer->buffer,
+      nice_debug_verbose ("\tBuffer %p (length: %" G_GSIZE_FORMAT ")", buffer->buffer,
           buffer->size);
     }
   }
@@ -3724,7 +3820,7 @@ compact_message (const NiceOutputMessage *message, gsize buffer_length)
 guint8 *
 compact_input_message (const NiceInputMessage *message, gsize *buffer_length)
 {
-  nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
+  nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
   nice_debug_input_message_composition (message, 1);
 
   /* This works as long as NiceInputMessage is a subset of eNiceOutputMessage */
@@ -3742,7 +3838,7 @@ memcpy_buffer_to_input_message (NiceInputMessage *message,
 {
   guint i;
 
-  nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
+  nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
 
   message->length = 0;
 
@@ -3912,7 +4008,7 @@ nice_input_message_iter_compare (const NiceInputMessageIter *a,
  *
  * Must be called with the io_mutex held. */
 static gint
-pending_io_messages_recv_messages (Component *component, gboolean reliable,
+pending_io_messages_recv_messages (NiceComponent *component, gboolean reliable,
     NiceInputMessage *messages, guint n_messages, NiceInputMessageIter *iter)
 {
   gsize len;
@@ -3986,8 +4082,8 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent,
   GCancellable *cancellable, GError **error)
 {
   GMainContext *context;
-  Stream *stream;
-  Component *component;
+  NiceStream *stream;
+  NiceComponent *component;
   gint n_valid_messages = -1;
   GSource *cancellable_source = NULL;
   gboolean received_enough = FALSE, error_reported = FALSE;
@@ -4041,7 +4137,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent,
     goto done;
   }
 
-  nice_debug ("%s: %p: (%s):", G_STRFUNC, agent,
+  nice_debug_verbose ("%s: %p: (%s):", G_STRFUNC, agent,
       blocking ? "blocking" : "non-blocking");
   nice_debug_input_message_composition (messages, n_messages);
 
@@ -4050,8 +4146,8 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent,
       component->recv_messages == NULL);
 
   /* Set the component’s receive buffer. */
-  context = component_dup_io_context (component);
-  component_set_io_callback (component, NULL, NULL, messages, n_messages,
+  context = nice_component_dup_io_context (component);
+  nice_component_set_io_callback (component, NULL, NULL, messages, n_messages,
       &child_error);
 
   /* Add the cancellable as a source. */
@@ -4074,7 +4170,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent,
         component->recv_messages, component->n_recv_messages,
         &component->recv_messages_iter);
 
-    nice_debug ("%s: %p: Received %d valid messages from pending I/O buffer.",
+    nice_debug_verbose ("%s: %p: Received %d valid messages from pending I/O buffer.",
         G_STRFUNC, agent,
         nice_input_message_iter_get_n_valid_messages (
             &component->recv_messages_iter));
@@ -4095,7 +4191,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent,
         &component->recv_messages_iter, &child_error);
     adjust_tcp_clock (agent, stream, component);
 
-    nice_debug ("%s: %p: Received %d valid messages from pseudo-TCP read "
+    nice_debug_verbose ("%s: %p: Received %d valid messages from pseudo-TCP read "
         "buffer.", G_STRFUNC, agent,
         nice_input_message_iter_get_n_valid_messages (
             &component->recv_messages_iter));
@@ -4125,7 +4221,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent,
     memcpy (&prev_recv_messages_iter, &component->recv_messages_iter,
         sizeof (NiceInputMessageIter));
 
-    agent_unlock_and_emit (agent);
+    agent_unlock ();
     g_main_context_iteration (context, blocking);
     agent_lock ();
 
@@ -4159,7 +4255,7 @@ nice_agent_recv_messages_blocking_or_nonblocking (NiceAgent *agent,
       nice_input_message_iter_get_n_valid_messages (
           &component->recv_messages_iter);  /* grab before resetting the iter */
 
-  component_set_io_callback (component, NULL, NULL, NULL, 0, NULL);
+  nice_component_set_io_callback (component, NULL, NULL, NULL, 0, NULL);
 
 recv_error:
   /* Tidy up. Below this point, @component may be %NULL. */
@@ -4179,7 +4275,7 @@ recv_error:
     n_valid_messages = -1;
   }
 
-  nice_debug ("%s: %p: n_valid_messages: %d, n_messages: %u", G_STRFUNC, agent,
+  nice_debug_verbose ("%s: %p: n_valid_messages: %d, n_messages: %u", G_STRFUNC, agent,
       n_valid_messages, n_messages);
 
 done:
@@ -4311,8 +4407,8 @@ nice_agent_send_messages_nonblocking_internal (
   gboolean allow_partial,
   GError **error)
 {
-  Stream *stream;
-  Component *component;
+  NiceStream *stream;
+  NiceComponent *component;
   gint n_sent = -1; /* is in bytes if allow_partial is TRUE,
                        otherwise in messages */
   GError *child_error = NULL;
@@ -4335,7 +4431,7 @@ nice_agent_send_messages_nonblocking_internal (
       gchar tmpbuf[INET6_ADDRSTRLEN];
       nice_address_to_string (&component->selected_pair.remote->addr, tmpbuf);
 
-      nice_debug ("Agent %p : s%d:%d: sending %u messages to "
+      nice_debug_verbose ("Agent %p : s%d:%d: sending %u messages to "
           "[%s]:%d", agent, stream_id, component_id, n_messages, tmpbuf,
           nice_address_get_port (&component->selected_pair.remote->addr));
     }
@@ -4485,7 +4581,7 @@ nice_agent_send_messages_nonblocking_internal (
     n_sent = -1;
   }
 
-  nice_debug ("%s: n_sent: %d, n_messages: %u", G_STRFUNC,
+  nice_debug_verbose ("%s: n_sent: %d, n_messages: %u", G_STRFUNC,
       n_sent, n_messages);
 
 done:
@@ -4558,7 +4654,7 @@ nice_agent_get_local_candidates (
   guint stream_id,
   guint component_id)
 {
-  Component *component;
+  NiceComponent *component;
   GSList * ret = NULL;
   GSList * item = NULL;
 
@@ -4572,8 +4668,14 @@ nice_agent_get_local_candidates (
     goto done;
   }
 
-  for (item = component->local_candidates; item; item = item->next)
-    ret = g_slist_append (ret, nice_candidate_copy (item->data));
+  for (item = component->local_candidates; item; item = item->next) {
+    NiceCandidate *cand = item->data;
+
+    if (agent->force_relay && cand->type != NICE_CANDIDATE_TYPE_RELAYED)
+      continue;
+
+    ret = g_slist_append (ret, nice_candidate_copy (cand));
+  }
 
  done:
   agent_unlock_and_emit (agent);
@@ -4587,7 +4689,7 @@ nice_agent_get_remote_candidates (
   guint stream_id,
   guint component_id)
 {
-  Component *component;
+  NiceComponent *component;
   GSList *ret = NULL, *item = NULL;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
@@ -4620,11 +4722,11 @@ nice_agent_restart (
   priv_generate_tie_breaker (agent);
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
 
     /* step: reset local credentials for the stream and
      * clean up the list of remote candidates */
-    stream_restart (agent, stream);
+    nice_stream_restart (stream, agent);
   }
 
   agent_unlock_and_emit (agent);
@@ -4637,7 +4739,7 @@ nice_agent_restart_stream (
     guint stream_id)
 {
   gboolean res = FALSE;
-  Stream *stream;
+  NiceStream *stream;
 
   agent_lock();
 
@@ -4649,7 +4751,7 @@ nice_agent_restart_stream (
 
   /* step: reset local credentials for the stream and
    * clean up the list of remote candidates */
-  stream_restart (agent, stream);
+  nice_stream_restart (stream, agent);
 
   res = TRUE;
  done:
@@ -4688,10 +4790,10 @@ nice_agent_dispose (GObject *object)
 
   for (i = agent->streams; i; i = i->next)
     {
-      Stream *s = i->data;
+      NiceStream *s = i->data;
 
-      stream_close (s);
-      stream_free (s);
+      nice_stream_close (s);
+      g_object_unref (s);
     }
 
   g_slist_free (agent->streams);
@@ -4739,9 +4841,9 @@ gboolean
 component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data)
 {
   SocketSource *socket_source = user_data;
-  Component *component;
+  NiceComponent *component;
   NiceAgent *agent;
-  Stream *stream;
+  NiceStream *stream;
   gboolean has_io_callback;
   gboolean remove_source = FALSE;
 
@@ -4774,20 +4876,22 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data)
           stream->id, component->id, NICE_COMPONENT_STATE_FAILED);
     }
 
-    component_detach_socket (component, socket_source->socket);
+    nice_component_remove_socket (component, socket_source->socket);
     agent_unlock ();
+    g_object_unref (agent);
     return G_SOURCE_REMOVE;
   }
 
-  has_io_callback = component_has_io_callback (component);
+  has_io_callback = nice_component_has_io_callback (component);
 
   /* Choose which receive buffer to use. If we’re reading for
    * nice_agent_attach_recv(), use a local static buffer. If we’re reading for
    * nice_agent_recv_messages(), use the buffer provided by the client.
    *
    * has_io_callback cannot change throughout this function, as we operate
-   * entirely with the agent lock held, and component_set_io_callback() would
-   * need to take the agent lock to change the Component’s io_callback. */
+   * entirely with the agent lock held, and nice_component_set_io_callback()
+   * would need to take the agent lock to change the Component’s
+   * io_callback. */
   g_assert (!has_io_callback || component->recv_messages == NULL);
 
   if (agent->reliable && !nice_socket_is_reliable (socket_source->socket)) {
@@ -4835,7 +4939,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data)
       retval = agent_recv_message_unlocked (agent, stream, component,
           socket_source->socket, &local_message);
 
-      nice_debug ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT
+      nice_debug_verbose ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT
           " bytes", G_STRFUNC, agent, retval, local_message.length);
 
       /* Don’t expect any valid messages to escape pseudo_tcp_socket_readable()
@@ -4852,7 +4956,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data)
         break;
       }
 
-      has_io_callback = component_has_io_callback (component);
+      has_io_callback = nice_component_has_io_callback (component);
     }
   } else if (has_io_callback) {
     while (has_io_callback) {
@@ -4865,7 +4969,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data)
       retval = agent_recv_message_unlocked (agent, stream, component,
           socket_source->socket, &local_message);
 
-      nice_debug ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT
+      nice_debug_verbose ("%s: %p: received %d valid messages with %" G_GSSIZE_FORMAT
            " bytes", G_STRFUNC, agent, retval, local_message.length);
 
       if (retval == RECV_WOULD_BLOCK) {
@@ -4879,13 +4983,13 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data)
       }
 
       if (retval == RECV_SUCCESS && local_message.length > 0)
-        component_emit_io_callback (component, local_buf, local_message.length);
+        nice_component_emit_io_callback (component, local_buf, local_message.length);
 
       if (g_source_is_destroyed (g_main_current_source ())) {
         nice_debug ("Component IO source disappeared during the callback");
         goto out;
       }
-      has_io_callback = component_has_io_callback (component);
+      has_io_callback = nice_component_has_io_callback (component);
     }
   } else if (component->recv_messages != NULL) {
     RecvStatus retval;
@@ -4906,7 +5010,7 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data)
           socket_source->socket,
           &component->recv_messages[component->recv_messages_iter.message]);
 
-      nice_debug ("%s: %p: received %d valid messages", G_STRFUNC, agent,
+      nice_debug_verbose ("%s: %p: received %d valid messages", G_STRFUNC, agent,
           retval);
 
       if (retval == RECV_SUCCESS) {
@@ -4931,6 +5035,10 @@ component_io_cb (GSocket *gsocket, GIOCondition condition, gpointer user_data)
   }
 
 done:
+
+  if (remove_source)
+    nice_component_remove_socket (component, socket_source->socket);
+
   /* If we’re in the middle of a read, don’t emit any signals, or we could cause
    * re-entrancy by (e.g.) emitting component-state-changed and having the
    * client perform a read. */
@@ -4946,6 +5054,7 @@ done:
 
 out:
   g_object_unref (agent);
+
   agent_unlock_and_emit (agent);
   return G_SOURCE_REMOVE;
 }
@@ -4959,8 +5068,8 @@ nice_agent_attach_recv (
   NiceAgentRecvFunc func,
   gpointer data)
 {
-  Component *component = NULL;
-  Stream *stream = NULL;
+  NiceComponent *component = NULL;
+  NiceStream *stream = NULL;
   gboolean ret = FALSE;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE);
@@ -4982,8 +5091,8 @@ nice_agent_attach_recv (
     ctx = g_main_context_default ();
 
   /* Set the component’s I/O context. */
-  component_set_io_context (component, ctx);
-  component_set_io_callback (component, func, data, NULL, 0, NULL);
+  nice_component_set_io_context (component, ctx);
+  nice_component_set_io_callback (component, func, data, NULL, 0, NULL);
   ret = TRUE;
 
   if (func) {
@@ -5011,8 +5120,8 @@ nice_agent_set_selected_pair (
   const gchar *lfoundation,
   const gchar *rfoundation)
 {
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   CandidatePair pair;
   gboolean ret = FALSE;
 
@@ -5029,7 +5138,7 @@ nice_agent_set_selected_pair (
     goto done;
   }
 
-  if (!component_find_pair (component, agent, lfoundation, rfoundation, &pair)){
+  if (!nice_component_find_pair (component, agent, lfoundation, rfoundation, &pair)){
     goto done;
   }
 
@@ -5044,11 +5153,22 @@ nice_agent_set_selected_pair (
     goto done;
   }
 
-  /* step: change component state */
-  agent_signal_component_state_change (agent, stream_id, component_id, NICE_COMPONENT_STATE_READY);
+  /* step: change component state; we could be in STATE_DISCONNECTED; skip
+   * STATE_GATHERING and continue through the states to give client code a nice
+   * logical progression. See http://phabricator.freedesktop.org/D218 for
+   * discussion. */
+  if (component->state < NICE_COMPONENT_STATE_CONNECTING ||
+      component->state == NICE_COMPONENT_STATE_FAILED)
+    agent_signal_component_state_change (agent, stream_id, component_id,
+        NICE_COMPONENT_STATE_CONNECTING);
+  if (component->state < NICE_COMPONENT_STATE_CONNECTED)
+    agent_signal_component_state_change (agent, stream_id, component_id,
+        NICE_COMPONENT_STATE_CONNECTED);
+  agent_signal_component_state_change (agent, stream_id, component_id,
+      NICE_COMPONENT_STATE_READY);
 
   /* step: set the selected pair */
-  component_update_selected_pair (component, &pair);
+  nice_component_update_selected_pair (component, &pair);
   agent_signal_new_selected_pair (agent, stream_id, component_id,
       pair.local, pair.remote);
 
@@ -5063,8 +5183,8 @@ NICEAPI_EXPORT gboolean
 nice_agent_get_selected_pair (NiceAgent *agent, guint stream_id,
     guint component_id, NiceCandidate **local, NiceCandidate **remote)
 {
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   gboolean ret = FALSE;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE);
@@ -5096,8 +5216,8 @@ NICEAPI_EXPORT GSocket *
 nice_agent_get_selected_socket (NiceAgent *agent, guint stream_id,
     guint component_id)
 {
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   NiceSocket *nice_socket;
   GSocket *g_socket = NULL;
 
@@ -5176,8 +5296,8 @@ nice_agent_set_selected_remote_candidate (
   guint component_id,
   NiceCandidate *candidate)
 {
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   NiceCandidate *lcandidate = NULL;
   gboolean ret = FALSE;
   NiceCandidate *local = NULL, *remote = NULL;
@@ -5204,7 +5324,7 @@ nice_agent_set_selected_remote_candidate (
   priority = component->selected_pair.priority;
 
   /* step: set the selected pair */
-  lcandidate = component_set_selected_remote_candidate (agent, component,
+  lcandidate = nice_component_set_selected_remote_candidate (component, agent,
       candidate);
   if (!lcandidate)
     goto done;
@@ -5222,8 +5342,19 @@ nice_agent_set_selected_remote_candidate (
     goto done;
   }
 
-  /* step: change component state */
-  agent_signal_component_state_change (agent, stream_id, component_id, NICE_COMPONENT_STATE_READY);
+  /* step: change component state; we could be in STATE_DISCONNECTED; skip
+   * STATE_GATHERING and continue through the states to give client code a nice
+   * logical progression. See http://phabricator.freedesktop.org/D218 for
+   * discussion. */
+  if (component->state < NICE_COMPONENT_STATE_CONNECTING ||
+      component->state == NICE_COMPONENT_STATE_FAILED)
+    agent_signal_component_state_change (agent, stream_id, component_id,
+        NICE_COMPONENT_STATE_CONNECTING);
+  if (component->state < NICE_COMPONENT_STATE_CONNECTED)
+    agent_signal_component_state_change (agent, stream_id, component_id,
+        NICE_COMPONENT_STATE_CONNECTED);
+  agent_signal_component_state_change (agent, stream_id, component_id,
+      NICE_COMPONENT_STATE_READY);
 
   agent_signal_new_selected_pair (agent, stream_id, component_id,
       lcandidate, candidate);
@@ -5261,7 +5392,7 @@ nice_agent_set_stream_tos (NiceAgent *agent,
   guint stream_id, gint tos)
 {
   GSList *i, *j;
-  Stream *stream;
+  NiceStream *stream;
 
   g_return_if_fail (NICE_IS_AGENT (agent));
   g_return_if_fail (stream_id >= 1);
@@ -5274,7 +5405,7 @@ nice_agent_set_stream_tos (NiceAgent *agent,
 
   stream->tos = tos;
   for (i = stream->components; i; i = i->next) {
-    Component *component = i->data;
+    NiceComponent *component = i->data;
 
     for (j = component->local_candidates; j; j = j->next) {
       NiceCandidate *local_candidate = j->data;
@@ -5308,7 +5439,7 @@ NICEAPI_EXPORT gboolean
 nice_agent_set_stream_name (NiceAgent *agent, guint stream_id,
     const gchar *name)
 {
-  Stream *stream_to_name = NULL;
+  NiceStream *stream_to_name = NULL;
   GSList *i;
   gboolean ret = FALSE;
 
@@ -5329,16 +5460,14 @@ nice_agent_set_stream_name (NiceAgent *agent, guint stream_id,
 
   agent_lock();
 
-  if (name != NULL) {
-    for (i = agent->streams; i; i = i->next) {
-      Stream *stream = i->data;
+  for (i = agent->streams; i; i = i->next) {
+    NiceStream *stream = i->data;
 
-      if (stream->id != stream_id &&
-          g_strcmp0 (stream->name, name) == 0)
-        goto done;
-      else if (stream->id == stream_id)
-        stream_to_name = stream;
-    }
+    if (stream->id != stream_id &&
+        g_strcmp0 (stream->name, name) == 0)
+      goto done;
+    else if (stream->id == stream_id)
+      stream_to_name = stream;
   }
 
   if (stream_to_name == NULL)
@@ -5358,7 +5487,7 @@ nice_agent_set_stream_name (NiceAgent *agent, guint stream_id,
 NICEAPI_EXPORT const gchar *
 nice_agent_get_stream_name (NiceAgent *agent, guint stream_id)
 {
-  Stream *stream;
+  NiceStream *stream;
   gchar *name = NULL;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
@@ -5379,14 +5508,14 @@ nice_agent_get_stream_name (NiceAgent *agent, guint stream_id)
 
 static NiceCandidate *
 _get_default_local_candidate_locked (NiceAgent *agent,
-    Stream *stream,  Component *component)
+    NiceStream *stream,  NiceComponent *component)
 {
   GSList *i;
   NiceCandidate *default_candidate = NULL;
   NiceCandidate *default_rtp_candidate = NULL;
 
   if (component->id != NICE_COMPONENT_TYPE_RTP) {
-    Component *rtp_component;
+    NiceComponent *rtp_component;
 
     if (!agent_find_component (agent, stream->id, NICE_COMPONENT_TYPE_RTP,
             NULL, &rtp_component))
@@ -5402,6 +5531,10 @@ _get_default_local_candidate_locked (NiceAgent *agent,
   for (i = component->local_candidates; i; i = i->next) {
     NiceCandidate *local_candidate = i->data;
 
+    if (agent->force_relay &&
+        local_candidate->type != NICE_CANDIDATE_TYPE_RELAYED)
+      continue;
+
     /* Only check for ipv4 candidates */
     if (nice_address_ip_version (&local_candidate->addr) != 4)
       continue;
@@ -5426,8 +5559,8 @@ NICEAPI_EXPORT NiceCandidate *
 nice_agent_get_default_local_candidate (NiceAgent *agent,
     guint stream_id,  guint component_id)
 {
-  Stream *stream = NULL;
-  Component *component = NULL;
+  NiceStream *stream = NULL;
+  NiceComponent *component = NULL;
   NiceCandidate *default_candidate = NULL;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
@@ -5526,7 +5659,7 @@ _generate_candidate_sdp (NiceAgent *agent,
 }
 
 static void
-_generate_stream_sdp (NiceAgent *agent, Stream *stream,
+_generate_stream_sdp (NiceAgent *agent, NiceStream *stream,
     GString *sdp, gboolean include_non_ice)
 {
   GSList *i, *j;
@@ -5542,7 +5675,7 @@ _generate_stream_sdp (NiceAgent *agent, Stream *stream,
 
     /* Find default candidates */
     for (i = stream->components; i; i = i->next) {
-      Component *component = i->data;
+      NiceComponent *component = i->data;
       NiceCandidate *default_candidate;
 
       if (component->id == NICE_COMPONENT_TYPE_RTP) {
@@ -5571,11 +5704,14 @@ _generate_stream_sdp (NiceAgent *agent, Stream *stream,
   g_string_append_printf (sdp, "a=ice-pwd:%s\n", stream->local_password);
 
   for (i = stream->components; i; i = i->next) {
-    Component *component = i->data;
+    NiceComponent *component = i->data;
 
     for (j = component->local_candidates; j; j = j->next) {
       NiceCandidate *candidate = j->data;
 
+      if (agent->force_relay && candidate->type != NICE_CANDIDATE_TYPE_RELAYED)
+        continue;
+
       _generate_candidate_sdp (agent, candidate, sdp);
       g_string_append (sdp, "\n");
     }
@@ -5593,7 +5729,7 @@ nice_agent_generate_local_sdp (NiceAgent *agent)
   agent_lock();
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
 
     _generate_stream_sdp (agent, stream, sdp, TRUE);
   }
@@ -5609,7 +5745,7 @@ nice_agent_generate_local_stream_sdp (NiceAgent *agent, guint stream_id,
 {
   GString *sdp = NULL;
   gchar *ret = NULL;
-  Stream *stream;
+  NiceStream *stream;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
   g_return_val_if_fail (stream_id >= 1, NULL);
@@ -5652,7 +5788,7 @@ nice_agent_generate_local_candidate_sdp (NiceAgent *agent,
 NICEAPI_EXPORT gint
 nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp)
 {
-  Stream *current_stream = NULL;
+  NiceStream *current_stream = NULL;
   gchar **sdp_lines = NULL;
   GSList *l, *stream_item = NULL;
   gint i;
@@ -5664,7 +5800,7 @@ nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp)
   agent_lock();
 
   for (l = agent->streams; l; l = l->next) {
-    Stream *stream = l->data;
+    NiceStream *stream = l->data;
 
     if (stream->name == NULL) {
       ret = -1;
@@ -5701,7 +5837,7 @@ nice_agent_parse_remote_sdp (NiceAgent *agent, const gchar *sdp)
           NICE_STREAM_MAX_PWD);
     } else if (g_str_has_prefix (sdp_lines[i], "a=candidate:")) {
       NiceCandidate *candidate = NULL;
-      Component *component = NULL;
+      NiceComponent *component = NULL;
       GSList *cands = NULL;
       gint added;
 
@@ -5744,7 +5880,7 @@ NICEAPI_EXPORT GSList *
 nice_agent_parse_remote_stream_sdp (NiceAgent *agent, guint stream_id,
     const gchar *sdp, gchar **ufrag, gchar **pwd)
 {
-  Stream *stream = NULL;
+  NiceStream *stream = NULL;
   gchar **sdp_lines = NULL;
   GSList *candidates = NULL;
   gint i;
@@ -5924,7 +6060,7 @@ nice_agent_get_io_stream (NiceAgent *agent, guint stream_id,
     guint component_id)
 {
   GIOStream *iostream = NULL;
-  Component *component;
+  NiceComponent *component;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), NULL);
   g_return_val_if_fail (stream_id >= 1, NULL);
@@ -5951,7 +6087,7 @@ nice_agent_get_io_stream (NiceAgent *agent, guint stream_id,
 NICEAPI_EXPORT gboolean
 nice_agent_forget_relays (NiceAgent *agent, guint stream_id, guint component_id)
 {
-  Component *component;
+  NiceComponent *component;
   gboolean ret = TRUE;
 
   g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE);
@@ -5965,7 +6101,7 @@ nice_agent_forget_relays (NiceAgent *agent, guint stream_id, guint component_id)
     goto done;
   }
 
-  component_clean_turn_servers (component);
+  nice_component_clean_turn_servers (component);
 
  done:
   agent_unlock_and_emit (agent);
@@ -6013,7 +6149,7 @@ nice_agent_get_component_state (NiceAgent *agent,
     guint stream_id, guint component_id)
 {
   NiceComponentState state = NICE_COMPONENT_STATE_FAILED;
-  Component *component;
+  NiceComponent *component;
 
   agent_lock ();
 
diff --git a/agent/candidate.c b/agent/candidate.c
index a472c4a..68c7714 100644
--- a/agent/candidate.c
+++ b/agent/candidate.c
@@ -52,6 +52,7 @@
 
 #include "agent.h"
 #include "component.h"
+#include "interfaces.h"
 
 G_DEFINE_BOXED_TYPE (NiceCandidate, nice_candidate, nice_candidate_copy,
     nice_candidate_free);
@@ -144,6 +145,43 @@ nice_candidate_ice_local_preference_full (guint direction_preference,
       other_preference);
 }
 
+static guint8
+nice_candidate_ip_local_preference (const NiceCandidate *candidate)
+{
+  guint8 preference = 0;
+  gchar ip_string[INET6_ADDRSTRLEN];
+  GList/*<owned gchar*>*/ *ips = NULL;
+  GList/*<unowned gchar*>*/ *iter;
+
+  /* Ensure otherwise identical host candidates with only different IP addresses
+   * (multihomed host) get assigned different priorities. Position of the IP in
+   * the list obtained from nice_interfaces_get_local_ips() serves here as the
+   * distinguishing value of other_preference. Reflexive and relayed candidates
+   * are likewise differentiated by their base address.
+   *
+   * This is required by RFC 5245 Section 4.1.2.1:
+   *   https://tools.ietf.org/html/rfc5245#section-4.1.2.1
+   */
+  if (candidate->type == NICE_CANDIDATE_TYPE_HOST) {
+    nice_address_to_string (&candidate->addr, ip_string);
+  } else {
+    nice_address_to_string (&candidate->base_addr, ip_string);
+  }
+
+  ips = nice_interfaces_get_local_ips (TRUE);
+
+  for (iter = ips; iter; iter = g_list_next (iter)) {
+    if (g_strcmp0 (ip_string, iter->data) == 0) {
+      break;
+    }
+    ++preference;
+  }
+
+  g_list_free_full (ips, g_free);
+
+  return preference;
+}
+
 static guint16
 nice_candidate_ice_local_preference (const NiceCandidate *candidate)
 {
@@ -178,7 +216,8 @@ nice_candidate_ice_local_preference (const NiceCandidate *candidate)
         break;
     }
 
-  return nice_candidate_ice_local_preference_full (direction_preference, 1);
+  return nice_candidate_ice_local_preference_full (direction_preference,
+      nice_candidate_ip_local_preference (candidate));
 }
 
 static guint32
@@ -214,7 +253,7 @@ nice_candidate_ms_ice_local_preference (const NiceCandidate *candidate)
     }
 
   return nice_candidate_ms_ice_local_preference_full(transport_preference,
-      direction_preference, 0);
+      direction_preference, nice_candidate_ip_local_preference (candidate));
 }
 
 static guint8
@@ -238,7 +277,10 @@ nice_candidate_ice_type_preference (const NiceCandidate *candidate,
         type_preference = NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE;
       break;
     case NICE_CANDIDATE_TYPE_RELAYED:
-      type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED;
+      if (candidate->turn->type == NICE_RELAY_TYPE_TURN_UDP)
+        type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP;
+      else
+        type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED;
       break;
     default:
       type_preference = 0;
diff --git a/agent/candidate.h b/agent/candidate.h
index 5e0eaf3..fadfce3 100644
--- a/agent/candidate.h
+++ b/agent/candidate.h
@@ -64,8 +64,8 @@ G_BEGIN_DECLS
 #define NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE       110
 #define NICE_CANDIDATE_TYPE_PREF_NAT_ASSISTED         105
 #define NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE     100
-#define NICE_CANDIDATE_TYPE_PREF_UDP_TUNNELED          75
-#define NICE_CANDIDATE_TYPE_PREF_RELAYED               10
+#define NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP           30
+#define NICE_CANDIDATE_TYPE_PREF_RELAYED               20
 
 /* Priority preference constants for MS-ICE compatibility */
 #define NICE_CANDIDATE_TRANSPORT_MS_PREF_UDP           15
diff --git a/agent/component.c b/agent/component.c
index 1a1f84a..d38d3e5 100644
--- a/agent/component.c
+++ b/agent/component.c
@@ -59,11 +59,33 @@ static volatile unsigned int n_components_destroyed = 0;
 #include "discovery.h"
 #include "agent-priv.h"
 
+G_DEFINE_TYPE (NiceComponent, nice_component, G_TYPE_OBJECT);
 
+typedef enum {
+  PROP_ID = 1,
+  PROP_AGENT,
+  PROP_STREAM,
+} NiceComponentProperty;
+
+static void
+nice_component_constructed (GObject *obj);
 static void
-component_schedule_io_callback (Component *component);
+nice_component_get_property (GObject *obj,
+    guint property_id, GValue *value, GParamSpec *pspec);
 static void
-component_deschedule_io_callback (Component *component);
+nice_component_set_property (GObject *obj,
+    guint property_id, const GValue *value, GParamSpec *pspec);
+static void
+nice_component_finalize (GObject *obj);
+
+static void
+nice_component_schedule_io_callback (NiceComponent *component);
+static void
+nice_component_deschedule_io_callback (NiceComponent *component);
+static void
+nice_component_detach_socket (NiceComponent *component, NiceSocket *nicesock);
+static void
+nice_component_clear_selected_pair (NiceComponent *component);
 
 
 void
@@ -74,12 +96,15 @@ incoming_check_free (IncomingCheck *icheck)
 }
 
 /* Must *not* take the agent lock, since it’s called from within
- * component_set_io_context(), which holds the Component’s I/O lock. */
+ * nice_component_set_io_context(), which holds the Component’s I/O lock. */
 static void
 socket_source_attach (SocketSource *socket_source, GMainContext *context)
 {
   GSource *source;
 
+  if (socket_source->socket->fileno == NULL)
+    return;
+
   /* Create a source. */
   source = g_socket_create_source (socket_source->socket->fileno,
       G_IO_IN, NULL);
@@ -121,50 +146,57 @@ socket_source_free (SocketSource *source)
   g_slice_free (SocketSource, source);
 }
 
-Component *
-component_new (guint id, NiceAgent *agent, Stream *stream)
+NiceComponent *
+nice_component_new (guint id, NiceAgent *agent, NiceStream *stream)
 {
-  Component *component;
-
-  g_atomic_int_inc (&n_components_created);
-  nice_debug ("Created NiceComponent (%u created, %u destroyed)",
-      n_components_created, n_components_destroyed);
+  return g_object_new (NICE_TYPE_COMPONENT,
+                       "id", id,
+                       "agent", agent,
+                       "stream", stream,
+                       NULL);
+}
 
-  component = g_slice_new0 (Component);
-  component->id = id;
-  component->state = NICE_COMPONENT_STATE_DISCONNECTED;
-  component->restart_candidate = NULL;
-  component->tcp = NULL;
-  component->agent = agent;
-  component->stream = stream;
+void
+nice_component_remove_socket (NiceComponent *cmp, NiceSocket *nsocket)
+{
+  GSList *i;
 
-  nice_agent_init_stun_agent (agent, &component->stun_agent);
+  for (i = cmp->local_candidates; i;) {
+    NiceCandidate *candidate = i->data;
+    GSList *next = i->next;
 
-  g_mutex_init (&component->io_mutex);
-  g_queue_init (&component->pending_io_messages);
-  component->io_callback_id = 0;
+    if (!nice_socket_is_based_on (candidate->sockptr, nsocket)) {
+      i = next;
+      continue;
+    }
 
-  component->own_ctx = g_main_context_new ();
-  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);
-  g_source_attach (component->stop_cancellable_source, component->own_ctx);
-  component->ctx = g_main_context_ref (component->own_ctx);
+    if (candidate == cmp->selected_pair.local) {
+      nice_component_clear_selected_pair (cmp);
+      agent_signal_component_state_change (cmp->agent, cmp->stream->id,
+          cmp->id, NICE_COMPONENT_STATE_FAILED);
+    }
 
-  /* Start off with a fresh main context and all I/O paused. This
-   * will be updated when nice_agent_attach_recv() or nice_agent_recv_messages()
-   * are called. */
-  component_set_io_context (component, NULL);
-  component_set_io_callback (component, NULL, NULL, NULL, 0, NULL);
+    refresh_prune_candidate (cmp->agent, candidate);
+    if (candidate->sockptr != nsocket) {
+      discovery_prune_socket (cmp->agent, candidate->sockptr);
+      conn_check_prune_socket (cmp->agent, cmp->stream, cmp,
+          candidate->sockptr);
+      nice_component_detach_socket (cmp, candidate->sockptr);
+    }
+    agent_remove_local_candidate (cmp->agent, candidate);
+    nice_candidate_free (candidate);
 
-  g_queue_init (&component->queued_tcp_packets);
+    cmp->local_candidates = g_slist_delete_link (cmp->local_candidates, i);
+    i = next;
+  }
 
-  return component;
+  discovery_prune_socket (cmp->agent, nsocket);
+  conn_check_prune_socket (cmp->agent, cmp->stream, cmp, nsocket);
+  nice_component_detach_socket (cmp, nsocket);
 }
 
 void
-component_clean_turn_servers (Component *cmp)
+nice_component_clean_turn_servers (NiceComponent *cmp)
 {
   GSList *i;
 
@@ -195,7 +227,7 @@ component_clean_turn_servers (Component *cmp)
         discovery_prune_socket (cmp->agent, cmp->turn_candidate->sockptr);
         conn_check_prune_socket (cmp->agent, cmp->stream, cmp,
             cmp->turn_candidate->sockptr);
-        component_detach_socket (cmp, cmp->turn_candidate->sockptr);
+        nice_component_detach_socket (cmp, cmp->turn_candidate->sockptr);
 	nice_candidate_free (cmp->turn_candidate);
       }
       /* Bring the priority down to 0, so that it will be replaced
@@ -208,7 +240,7 @@ component_clean_turn_servers (Component *cmp)
       discovery_prune_socket (cmp->agent, candidate->sockptr);
       conn_check_prune_socket (cmp->agent, cmp->stream, cmp,
           candidate->sockptr);
-      component_detach_socket (cmp, candidate->sockptr);
+      nice_component_detach_socket (cmp, candidate->sockptr);
       agent_remove_local_candidate (cmp->agent, candidate);
       nice_candidate_free (candidate);
     }
@@ -218,7 +250,7 @@ component_clean_turn_servers (Component *cmp)
 }
 
 static void
-component_clear_selected_pair (Component *component)
+nice_component_clear_selected_pair (NiceComponent *component)
 {
   if (component->selected_pair.keepalive.tick_source != NULL) {
     g_source_destroy (component->selected_pair.keepalive.tick_source);
@@ -232,7 +264,7 @@ component_clear_selected_pair (Component *component)
 /* Must be called with the agent lock held as it touches internal Component
  * state. */
 void
-component_close (Component *cmp)
+nice_component_close (NiceComponent *cmp)
 {
   IOCallbackData *data;
   GOutputVector *vec;
@@ -240,13 +272,13 @@ component_close (Component *cmp)
   /* Start closing the pseudo-TCP socket first. FIXME: There is a very big and
    * reliably triggerable race here. pseudo_tcp_socket_close() does not block
    * on the socket closing — it only sends the first packet of the FIN
-   * handshake. component_close() will immediately afterwards close the
+   * handshake. nice_component_close() will immediately afterwards close the
    * underlying component sockets, aborting the handshake.
    *
    * On the principle that starting the FIN handshake is better than not
    * starting it, even if it’s later truncated, call pseudo_tcp_socket_close().
-   * A long-term fix is needed in the form of making component_close() (and all
-   * its callers) async, so we can properly block on closure. */
+   * A long-term fix is needed in the form of making nice_component_close() (and
+   * all its callers) async, so we can properly block on closure. */
   if (cmp->tcp) {
     pseudo_tcp_socket_close (cmp->tcp, TRUE);
   }
@@ -269,12 +301,12 @@ component_close (Component *cmp)
   g_slist_free_full (cmp->remote_candidates,
       (GDestroyNotify) nice_candidate_free);
   cmp->remote_candidates = NULL;
-  component_free_socket_sources (cmp);
+  nice_component_free_socket_sources (cmp);
   g_slist_free_full (cmp->incoming_checks,
       (GDestroyNotify) incoming_check_free);
   cmp->incoming_checks = NULL;
 
-  component_clean_turn_servers (cmp);
+  nice_component_clean_turn_servers (cmp);
 
   if (cmp->tcp_clock) {
     g_source_destroy (cmp->tcp_clock);
@@ -289,7 +321,7 @@ component_close (Component *cmp)
   while ((data = g_queue_pop_head (&cmp->pending_io_messages)) != NULL)
     io_callback_data_free (data);
 
-  component_deschedule_io_callback (cmp);
+  nice_component_deschedule_io_callback (cmp);
 
   g_cancellable_cancel (cmp->stop_cancellable);
 
@@ -299,47 +331,13 @@ component_close (Component *cmp)
   }
 }
 
-/* Must be called with the agent lock released as it could dispose of
- * NiceIOStreams. */
-void
-component_free (Component *cmp)
-{
-  /* Component should have been closed already. */
-  g_warn_if_fail (cmp->local_candidates == NULL);
-  g_warn_if_fail (cmp->remote_candidates == NULL);
-  g_warn_if_fail (cmp->incoming_checks == NULL);
-
-  g_clear_object (&cmp->tcp);
-  g_clear_object (&cmp->stop_cancellable);
-  g_clear_object (&cmp->iostream);
-  g_mutex_clear (&cmp->io_mutex);
-
-  if (cmp->stop_cancellable_source != NULL) {
-    g_source_destroy (cmp->stop_cancellable_source);
-    g_source_unref (cmp->stop_cancellable_source);
-  }
-
-  if (cmp->ctx != NULL) {
-    g_main_context_unref (cmp->ctx);
-    cmp->ctx = NULL;
-  }
-
-  g_main_context_unref (cmp->own_ctx);
-
-  g_slice_free (Component, cmp);
-
-  g_atomic_int_inc (&n_components_destroyed);
-  nice_debug ("Destroyed NiceComponent (%u created, %u destroyed)",
-      n_components_created, n_components_destroyed);
-}
-
 /*
  * Finds a candidate pair that has matching foundation ids.
  *
  * @return TRUE if pair found, pointer to pair stored at 'pair'
  */
 gboolean
-component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair)
+nice_component_find_pair (NiceComponent *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair)
 {
   GSList *i;
   CandidatePair result = { 0, };
@@ -375,7 +373,7 @@ component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation,
  * session.
  */
 void
-component_restart (Component *cmp)
+nice_component_restart (NiceComponent *cmp)
 {
   GSList *i;
 
@@ -410,7 +408,8 @@ component_restart (Component *cmp)
  * Changes the selected pair for the component to 'pair'. Does not
  * emit the "selected-pair-changed" signal.
  */ 
-void component_update_selected_pair (Component *component, const CandidatePair *pair)
+void
+nice_component_update_selected_pair (NiceComponent *component, const CandidatePair *pair)
 {
   g_assert (component);
   g_assert (pair);
@@ -425,17 +424,17 @@ void component_update_selected_pair (Component *component, const CandidatePair *
         component->turn_candidate->sockptr);
     conn_check_prune_socket (component->agent, component->stream, component,
         component->turn_candidate->sockptr);
-    component_detach_socket (component, component->turn_candidate->sockptr);
+    nice_component_detach_socket (component, component->turn_candidate->sockptr);
     nice_candidate_free (component->turn_candidate);
     component->turn_candidate = NULL;
   }
 
-  component_clear_selected_pair (component);
+  nice_component_clear_selected_pair (component);
 
   component->selected_pair.local = pair->local;
   component->selected_pair.remote = pair->remote;
   component->selected_pair.priority = pair->priority;
-
+  component->selected_pair.prflx_priority = pair->prflx_priority;
 }
 
 /*
@@ -445,7 +444,7 @@ void component_update_selected_pair (Component *component, const CandidatePair *
  * @return pointer to candidate or NULL if not found
  */
 NiceCandidate *
-component_find_remote_candidate (const Component *component, const NiceAddress *addr, NiceCandidateTransport transport)
+nice_component_find_remote_candidate (NiceComponent *component, const NiceAddress *addr, NiceCandidateTransport transport)
 {
   GSList *i;
 
@@ -469,8 +468,8 @@ component_find_remote_candidate (const Component *component, const NiceAddress *
  */
 
 NiceCandidate *
-component_set_selected_remote_candidate (NiceAgent *agent, Component *component,
-    NiceCandidate *candidate)
+nice_component_set_selected_remote_candidate (NiceComponent *component,
+    NiceAgent *agent, NiceCandidate *candidate)
 {
   NiceCandidate *local = NULL;
   NiceCandidate *remote = NULL;
@@ -483,7 +482,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component,
     NiceCandidate *tmp = item->data;
     guint64 tmp_prio = 0;
 
-    if (tmp->transport != candidate->transport ||
+    if (tmp->transport != conn_check_match_transport(candidate->transport) ||
 	tmp->addr.s.addr.sa_family != candidate->addr.s.addr.sa_family ||
         tmp->type != NICE_CANDIDATE_TYPE_HOST)
       continue;
@@ -499,7 +498,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component,
   if (local == NULL)
     return NULL;
 
-  remote = component_find_remote_candidate (component, &candidate->addr,
+  remote = nice_component_find_remote_candidate (component, &candidate->addr,
       candidate->transport);
 
   if (!remote) {
@@ -509,7 +508,7 @@ component_set_selected_remote_candidate (NiceAgent *agent, Component *component,
     agent_signal_new_remote_candidate (agent, remote);
   }
 
-  component_clear_selected_pair (component);
+  nice_component_clear_selected_pair (component);
 
   component->selected_pair.local = local;
   component->selected_pair.remote = remote;
@@ -530,7 +529,7 @@ _find_socket_source (gconstpointer a, gconstpointer b)
 /* This takes ownership of the socket.
  * It creates and attaches a source to the component’s context. */
 void
-component_attach_socket (Component *component, NiceSocket *nicesock)
+nice_component_attach_socket (NiceComponent *component, NiceSocket *nicesock)
 {
   GSList *l;
   SocketSource *socket_source;
@@ -540,9 +539,6 @@ component_attach_socket (Component *component, NiceSocket *nicesock)
 
   g_assert (component->ctx != NULL);
 
-  if (nicesock->fileno == NULL)
-    return;
-
   /* Find an existing SocketSource in the component which contains @socket, or
    * create a new one.
    *
@@ -559,7 +555,8 @@ component_attach_socket (Component *component, NiceSocket *nicesock)
     socket_source->component = component;
     component->socket_sources =
         g_slist_prepend (component->socket_sources, socket_source);
-    component->socket_sources_age++;
+    if (nicesock->fileno != NULL)
+      component->socket_sources_age++;
   }
 
   /* Create and attach a source */
@@ -571,9 +568,9 @@ component_attach_socket (Component *component, NiceSocket *nicesock)
 /* Reattaches socket handles of @component to the main context.
  *
  * Must *not* take the agent lock, since it’s called from within
- * component_set_io_context(), which holds the Component’s I/O lock. */
+ * nice_component_set_io_context(), which holds the Component’s I/O lock. */
 static void
-component_reattach_all_sockets (Component *component)
+nice_component_reattach_all_sockets (NiceComponent *component)
 {
   GSList *i;
 
@@ -586,8 +583,8 @@ component_reattach_all_sockets (Component *component)
 }
 
 /**
- * component_detach_socket:
- * @component: a #Component
+ * nice_component_detach_socket:
+ * @component: a #NiceComponent
  * @socket: the socket to detach the source for
  *
  * Detach the #GSource for the single specified @socket. It also closes it
@@ -595,8 +592,8 @@ component_reattach_all_sockets (Component *component)
  *
  * If the @socket doesn’t exist in this @component, do nothing.
  */
-void
-component_detach_socket (Component *component, NiceSocket *nicesock)
+static void
+nice_component_detach_socket (NiceComponent *component, NiceSocket *nicesock)
 {
   GSList *l;
   SocketSource *socket_source;
@@ -637,10 +634,10 @@ component_detach_socket (Component *component, NiceSocket *nicesock)
  * sockets themselves untouched.
  *
  * Must *not* take the agent lock, since it’s called from within
- * component_set_io_context(), which holds the Component’s I/O lock.
+ * nice_component_set_io_context(), which holds the Component’s I/O lock.
  */
 void
-component_detach_all_sockets (Component *component)
+nice_component_detach_all_sockets (NiceComponent *component)
 {
   GSList *i;
 
@@ -653,7 +650,7 @@ component_detach_all_sockets (Component *component)
 }
 
 void
-component_free_socket_sources (Component *component)
+nice_component_free_socket_sources (NiceComponent *component)
 {
   nice_debug ("Free socket sources for component %p.", component);
 
@@ -662,11 +659,11 @@ component_free_socket_sources (Component *component)
   component->socket_sources = NULL;
   component->socket_sources_age++;
 
-  component_clear_selected_pair (component);
+  nice_component_clear_selected_pair (component);
 }
 
 GMainContext *
-component_dup_io_context (Component *component)
+nice_component_dup_io_context (NiceComponent *component)
 {
   return g_main_context_ref (component->own_ctx);
 }
@@ -674,7 +671,7 @@ component_dup_io_context (Component *component)
 /* If @context is %NULL, it's own context is used, so component->ctx is always
  * guaranteed to be non-%NULL. */
 void
-component_set_io_context (Component *component, GMainContext *context)
+nice_component_set_io_context (NiceComponent *component, GMainContext *context)
 {
   g_mutex_lock (&component->io_mutex);
 
@@ -684,11 +681,11 @@ component_set_io_context (Component *component, GMainContext *context)
     else
       g_main_context_ref (context);
 
-    component_detach_all_sockets (component);
+    nice_component_detach_all_sockets (component);
     g_main_context_unref (component->ctx);
 
     component->ctx = context;
-    component_reattach_all_sockets (component);
+    nice_component_reattach_all_sockets (component);
   }
 
   g_mutex_unlock (&component->io_mutex);
@@ -705,7 +702,7 @@ component_set_io_context (Component *component, GMainContext *context)
  * emitted for it (which could cause data loss if the I/O callback function was
  * unset in that time). */
 void
-component_set_io_callback (Component *component,
+nice_component_set_io_callback (NiceComponent *component,
     NiceAgentRecvFunc func, gpointer user_data,
     NiceInputMessage *recv_messages, guint n_recv_messages,
     GError **error)
@@ -722,14 +719,14 @@ component_set_io_callback (Component *component,
     component->recv_messages = NULL;
     component->n_recv_messages = 0;
 
-    component_schedule_io_callback (component);
+    nice_component_schedule_io_callback (component);
   } else {
     component->io_callback = NULL;
     component->io_user_data = NULL;
     component->recv_messages = recv_messages;
     component->n_recv_messages = n_recv_messages;
 
-    component_deschedule_io_callback (component);
+    nice_component_deschedule_io_callback (component);
   }
 
   nice_input_message_iter_reset (&component->recv_messages_iter);
@@ -739,7 +736,7 @@ component_set_io_callback (Component *component,
 }
 
 gboolean
-component_has_io_callback (Component *component)
+nice_component_has_io_callback (NiceComponent *component)
 {
   gboolean has_io_callback;
 
@@ -775,7 +772,7 @@ io_callback_data_free (IOCallbackData *data)
 static gboolean
 emit_io_callback_cb (gpointer user_data)
 {
-  Component *component = user_data;
+  NiceComponent *component = user_data;
   IOCallbackData *data;
   NiceAgentRecvFunc io_callback;
   gpointer io_user_data;
@@ -792,7 +789,7 @@ emit_io_callback_cb (gpointer user_data)
   g_mutex_lock (&component->io_mutex);
 
   /* The members of Component are guaranteed not to have changed since this
-   * GSource was attached in component_emit_io_callback(). The Component’s agent
+   * GSource was attached in nice_component_emit_io_callback(). The Component’s agent
    * and stream are immutable after construction, as are the stream and
    * component IDs. The callback and its user data may have changed, but are
    * guaranteed to be non-%NULL at the start as the idle source is removed when
@@ -802,7 +799,7 @@ emit_io_callback_cb (gpointer user_data)
    *
    * If the component is destroyed (which happens if the agent or stream are
    * destroyed) between attaching the GSource and firing it, the GSource is
-   * detached in component_free() and this callback is never invoked. If the
+   * detached during dispose and this callback is never invoked. If the
    * agent is destroyed during an io_callback, its weak pointer will be
    * nullified. Similarly, the Component needs to be re-queried for after every
    * iteration, just in case the client has removed the stream in the
@@ -845,7 +842,7 @@ emit_io_callback_cb (gpointer user_data)
 
 /* This must be called with the agent lock *held*. */
 void
-component_emit_io_callback (Component *component,
+nice_component_emit_io_callback (NiceComponent *component,
     const guint8 *buf, gsize buf_len)
 {
   NiceAgent *agent;
@@ -897,7 +894,7 @@ component_emit_io_callback (Component *component,
 
     nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
 
-    component_schedule_io_callback (component);
+    nice_component_schedule_io_callback (component);
 
     g_mutex_unlock (&component->io_mutex);
   }
@@ -905,7 +902,7 @@ component_emit_io_callback (Component *component,
 
 /* Note: Must be called with the io_mutex held. */
 static void
-component_schedule_io_callback (Component *component)
+nice_component_schedule_io_callback (NiceComponent *component)
 {
   GSource *source;
 
@@ -928,7 +925,7 @@ component_schedule_io_callback (Component *component)
 
 /* Note: Must be called with the io_mutex held. */
 static void
-component_deschedule_io_callback (Component *component)
+nice_component_deschedule_io_callback (NiceComponent *component)
 {
   /* Already descheduled? */
   if (component->io_callback_id == 0)
@@ -938,6 +935,202 @@ component_deschedule_io_callback (Component *component)
   component->io_callback_id = 0;
 }
 
+static void
+nice_component_class_init (NiceComponentClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = nice_component_constructed;
+  object_class->get_property = nice_component_get_property;
+  object_class->set_property = nice_component_set_property;
+  object_class->finalize = nice_component_finalize;
+
+  /**
+   * NiceComponent:id:
+   *
+   * The unique numeric ID of the component.
+   *
+   * Since: UNRELEASED
+   */
+  g_object_class_install_property (object_class, PROP_ID,
+      g_param_spec_uint (
+         "id",
+         "ID",
+         "The unique numeric ID of the component.",
+         1, G_MAXUINT, 1,
+         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  /**
+   * NiceComponent:agent:
+   *
+   * The #NiceAgent this component belongs to.
+   *
+   * Since: UNRELEASED
+   */
+  g_object_class_install_property (object_class, PROP_AGENT,
+      g_param_spec_object (
+         "agent",
+         "Agent",
+         "The NiceAgent this component belongs to.",
+         NICE_TYPE_AGENT,
+         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  /**
+   * NiceComponent:stream:
+   *
+   * The #NiceStream this component belongs to.
+   *
+   * Since: UNRELEASED
+   */
+  g_object_class_install_property (object_class, PROP_STREAM,
+      g_param_spec_object (
+         "stream",
+         "Stream",
+         "The NiceStream this component belongs to.",
+         NICE_TYPE_STREAM,
+         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+nice_component_init (NiceComponent *component)
+{
+  g_atomic_int_inc (&n_components_created);
+  nice_debug ("Created NiceComponent (%u created, %u destroyed)",
+      n_components_created, n_components_destroyed);
+
+  component->id = 0;
+  component->state = NICE_COMPONENT_STATE_DISCONNECTED;
+  component->restart_candidate = NULL;
+  component->tcp = NULL;
+  component->agent = NULL;
+  component->stream = NULL;
+
+  g_mutex_init (&component->io_mutex);
+  g_queue_init (&component->pending_io_messages);
+  component->io_callback_id = 0;
+
+  component->own_ctx = g_main_context_new ();
+  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);
+  g_source_attach (component->stop_cancellable_source, component->own_ctx);
+  component->ctx = g_main_context_ref (component->own_ctx);
+
+  /* Start off with a fresh main context and all I/O paused. This
+   * will be updated when nice_agent_attach_recv() or nice_agent_recv_messages()
+   * are called. */
+  nice_component_set_io_context (component, NULL);
+  nice_component_set_io_callback (component, NULL, NULL, NULL, 0, NULL);
+
+  g_queue_init (&component->queued_tcp_packets);
+}
+
+static void
+nice_component_constructed (GObject *obj)
+{
+  NiceComponent *component;
+
+  component = NICE_COMPONENT (obj);
+
+  g_assert (component->agent != NULL);
+  nice_agent_init_stun_agent (component->agent, &component->stun_agent);
+
+  G_OBJECT_CLASS (nice_component_parent_class)->constructed (obj);
+}
+
+static void
+nice_component_get_property (GObject *obj,
+    guint property_id, GValue *value, GParamSpec *pspec)
+{
+  NiceComponent *component;
+
+  component = NICE_COMPONENT (obj);
+
+  switch ((NiceComponentProperty) property_id)
+    {
+    case PROP_ID:
+      g_value_set_uint (value, component->id);
+      break;
+
+    case PROP_AGENT:
+      g_value_set_object (value, component->agent);
+      break;
+
+    case PROP_STREAM:
+      g_value_set_object (value, component->stream);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+    }
+}
+
+static void
+nice_component_set_property (GObject *obj,
+    guint property_id, const GValue *value, GParamSpec *pspec)
+{
+  NiceComponent *component;
+
+  component = NICE_COMPONENT (obj);
+
+  switch ((NiceComponentProperty) property_id)
+    {
+    case PROP_ID:
+      component->id = g_value_get_uint (value);
+      break;
+
+    case PROP_AGENT:
+      component->agent = g_value_get_object (value);
+      break;
+
+    case PROP_STREAM:
+      component->stream = g_value_get_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+    }
+}
+
+/* Must be called with the agent lock released as it could dispose of
+ * NiceIOStreams. */
+static void
+nice_component_finalize (GObject *obj)
+{
+  NiceComponent *cmp;
+
+  cmp = NICE_COMPONENT (obj);
+
+  /* Component should have been closed already. */
+  g_warn_if_fail (cmp->local_candidates == NULL);
+  g_warn_if_fail (cmp->remote_candidates == NULL);
+  g_warn_if_fail (cmp->incoming_checks == NULL);
+
+  g_clear_object (&cmp->tcp);
+  g_clear_object (&cmp->stop_cancellable);
+  g_clear_object (&cmp->iostream);
+  g_mutex_clear (&cmp->io_mutex);
+
+  if (cmp->stop_cancellable_source != NULL) {
+    g_source_destroy (cmp->stop_cancellable_source);
+    g_source_unref (cmp->stop_cancellable_source);
+  }
+
+  if (cmp->ctx != NULL) {
+    g_main_context_unref (cmp->ctx);
+    cmp->ctx = NULL;
+  }
+
+  g_main_context_unref (cmp->own_ctx);
+
+  g_atomic_int_inc (&n_components_destroyed);
+  nice_debug ("Destroyed NiceComponent (%u created, %u destroyed)",
+      n_components_created, n_components_destroyed);
+
+  G_OBJECT_CLASS (nice_component_parent_class)->finalize (obj);
+}
+
 /**
  * ComponentSource:
  *
@@ -980,7 +1173,7 @@ component_source_prepare (GSource *source, gint *timeout_)
 {
   ComponentSource *component_source = (ComponentSource *) source;
   NiceAgent *agent;
-  Component *component;
+  NiceComponent *component;
   GSList *parentl, *childl;
 
   agent = g_weak_ref_get (&component_source->agent_ref);
@@ -1011,6 +1204,9 @@ component_source_prepare (GSource *source, gint *timeout_)
     SocketSource *parent_socket_source = parentl->data;
     SocketSource *child_socket_source;
 
+    if (parent_socket_source->socket->fileno == NULL)
+      continue;
+
     /* Iterating the list of socket sources every time isn't a big problem
      * because the number of pairs is limited ~100 normally, so there will
      * rarely be more than 10.
@@ -1128,7 +1324,7 @@ static GSourceFuncs component_source_funcs = {
 };
 
 /**
- * component_source_new:
+ * nice_component_source_new:
  * @agent: a #NiceAgent
  * @stream_id: The stream's id
  * @component_id: The component's number
@@ -1150,7 +1346,7 @@ static GSourceFuncs component_source_funcs = {
  * Returns: (transfer full): a new #ComponentSource; unref with g_source_unref()
  */
 GSource *
-component_input_source_new (NiceAgent *agent, guint stream_id,
+nice_component_input_source_new (NiceAgent *agent, guint stream_id,
     guint component_id, GPollableInputStream *pollable_istream,
     GCancellable *cancellable)
 {
diff --git a/agent/component.h b/agent/component.h
index 7ded710..6712794 100644
--- a/agent/component.h
+++ b/agent/component.h
@@ -42,7 +42,7 @@
 
 #include <glib.h>
 
-typedef struct _Component Component;
+typedef struct _NiceComponent NiceComponent;
 
 #include "agent.h"
 #include "agent-priv.h"
@@ -83,6 +83,7 @@ struct _CandidatePair
   NiceCandidate *local;
   NiceCandidate *remote;
   guint64 priority;           /* candidate pair priority */
+  guint32 prflx_priority;
   CandidatePairKeepalive keepalive;
 };
 
@@ -110,7 +111,7 @@ incoming_check_free (IncomingCheck *icheck);
 typedef struct {
   NiceSocket *socket;
   GSource *source;
-  Component *component;
+  NiceComponent *component;
 } SocketSource;
 
 
@@ -137,9 +138,22 @@ io_callback_data_new (const guint8 *buf, gsize buf_len);
 void
 io_callback_data_free (IOCallbackData *data);
 
+#define NICE_TYPE_COMPONENT nice_component_get_type()
+#define NICE_COMPONENT(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), NICE_TYPE_COMPONENT, NiceComponent))
+#define NICE_COMPONENT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), NICE_TYPE_COMPONENT, NiceComponentClass))
+#define NICE_IS_COMPONENT(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NICE_TYPE_COMPONENT))
+#define NICE_IS_COMPONENT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), NICE_TYPE_COMPONENT))
+#define NICE_COMPONENT_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_COMPONENT, NiceComponentClass))
+
+struct _NiceComponent {
+  /*< private >*/
+  GObject parent;
 
-struct _Component
-{
   NiceComponentType type;
   guint id;                    /* component id */
   NiceComponentState state;
@@ -186,8 +200,8 @@ struct _Component
 
   NiceAgent *agent;  /* unowned, immutable: can be accessed without holding the
                       * agent lock */
-  Stream *stream;  /* unowned, immutable: can be accessed without holding the
-                    * agent lock */
+  NiceStream *stream;  /* unowned, immutable: can be accessed without holding
+                        * the agent lock */
 
   StunAgent stun_agent; /* This stun agent is used to validate all stun requests */
 
@@ -212,63 +226,69 @@ struct _Component
   GQueue queued_tcp_packets;
 };
 
-Component *
-component_new (guint component_id, NiceAgent *agent, Stream *stream);
+typedef struct {
+  GObjectClass parent_class;
+} NiceComponentClass;
+
+GType nice_component_get_type (void);
 
-void
-component_close (Component *cmp);
+NiceComponent *
+nice_component_new (guint component_id, NiceAgent *agent, NiceStream *stream);
 
 void
-component_free (Component *cmp);
+nice_component_close (NiceComponent *component);
 
 gboolean
-component_find_pair (Component *cmp, NiceAgent *agent, const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair);
+nice_component_find_pair (NiceComponent *component, NiceAgent *agent,
+    const gchar *lfoundation, const gchar *rfoundation, CandidatePair *pair);
 
 void
-component_restart (Component *cmp);
+nice_component_restart (NiceComponent *component);
 
 void
-component_update_selected_pair (Component *component, const CandidatePair *pair);
+nice_component_update_selected_pair (NiceComponent *component,
+    const CandidatePair *pair);
 
 NiceCandidate *
-component_find_remote_candidate (const Component *component, const NiceAddress *addr, NiceCandidateTransport transport);
+nice_component_find_remote_candidate (NiceComponent *component,
+    const NiceAddress *addr, NiceCandidateTransport transport);
 
 NiceCandidate *
-component_set_selected_remote_candidate (NiceAgent *agent, Component *component,
-    NiceCandidate *candidate);
+nice_component_set_selected_remote_candidate (NiceComponent *component,
+    NiceAgent *agent, NiceCandidate *candidate);
 
 void
-component_attach_socket (Component *component, NiceSocket *nsocket);
+nice_component_attach_socket (NiceComponent *component, NiceSocket *nsocket);
+
 void
-component_detach_socket (Component *component, NiceSocket *nsocket);
+nice_component_remove_socket (NiceComponent *component, NiceSocket *nsocket);
 void
-component_detach_all_sockets (Component *component);
+nice_component_detach_all_sockets (NiceComponent *component);
+
 void
-component_free_socket_sources (Component *component);
+nice_component_free_socket_sources (NiceComponent *component);
 
 GSource *
-component_input_source_new (NiceAgent *agent, guint stream_id,
+nice_component_input_source_new (NiceAgent *agent, guint stream_id,
     guint component_id, GPollableInputStream *pollable_istream,
     GCancellable *cancellable);
 
 GMainContext *
-component_dup_io_context (Component *component);
+nice_component_dup_io_context (NiceComponent *component);
 void
-component_set_io_context (Component *component, GMainContext *context);
+nice_component_set_io_context (NiceComponent *component, GMainContext *context);
 void
-component_set_io_callback (Component *component,
+nice_component_set_io_callback (NiceComponent *component,
     NiceAgentRecvFunc func, gpointer user_data,
     NiceInputMessage *recv_messages, guint n_recv_messages,
     GError **error);
 void
-component_emit_io_callback (Component *component,
+nice_component_emit_io_callback (NiceComponent *component,
     const guint8 *buf, gsize buf_len);
-
 gboolean
-component_has_io_callback (Component *component);
-
+nice_component_has_io_callback (NiceComponent *component);
 void
-component_clean_turn_servers (Component *component);
+nice_component_clean_turn_servers (NiceComponent *component);
 
 
 TurnServer *
diff --git a/agent/conncheck.c b/agent/conncheck.c
index 057fc81..7e03985 100644
--- a/agent/conncheck.c
+++ b/agent/conncheck.c
@@ -61,18 +61,20 @@
 #include "stun/usages/bind.h"
 #include "stun/usages/turn.h"
 
-static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream);
-static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component);
-static guint priv_prune_pending_checks (Stream *stream, guint component_id);
-static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate);
-static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand);
-static size_t priv_create_username (NiceAgent *agent, Stream *stream,
+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 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,
     uint8_t *dest, guint dest_len, gboolean inbound);
-static size_t priv_get_password (NiceAgent *agent, Stream *stream,
+static size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
     NiceCandidate *remote, uint8_t **password);
 static void conn_check_free_item (gpointer data);
-static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state);
+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 int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
 {
@@ -81,6 +83,140 @@ static int priv_timer_expired (GTimeVal *timer, GTimeVal *now)
     now->tv_sec >= timer->tv_sec;
 }
 
+static gchar
+priv_state_to_gchar (NiceCheckState state)
+{
+  switch (state) {
+    case NICE_CHECK_WAITING:
+      return 'W';
+    case NICE_CHECK_IN_PROGRESS:
+      return 'I';
+    case NICE_CHECK_SUCCEEDED:
+      return 'S';
+    case NICE_CHECK_FAILED:
+      return 'F';
+    case NICE_CHECK_FROZEN:
+      return 'Z';
+    case NICE_CHECK_CANCELLED:
+      return 'C';
+    case NICE_CHECK_DISCOVERED:
+      return 'D';
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+static const gchar *
+priv_candidate_type_to_string (NiceCandidateType type)
+{
+  switch (type) {
+    case NICE_CANDIDATE_TYPE_HOST:
+      return "host";
+    case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
+      return "srflx";
+    case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
+      return "prflx";
+    case NICE_CANDIDATE_TYPE_RELAYED:
+      return "relay";
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+/*
+ * Dump the conncheck lists of the agent
+ */
+static void
+priv_print_conn_check_lists (NiceAgent *agent, const gchar *where, const gchar *detail)
+{
+  GSList *i, *k;
+  guint j;
+
+  if (!nice_debug_is_verbose ())
+    return;
+
+#define PRIORITY_LEN 32
+
+  nice_debug ("Agent %p : *** conncheck list DUMP (called from %s%s)",
+      agent, where, detail ? detail : "");
+  for (i = agent->streams; i ; i = i->next) {
+    NiceStream *stream = i->data;
+    for (j = 1; j <= stream->n_components; j++) {
+      for (k = stream->conncheck_list; k ; k = k->next) {
+        CandidateCheckPair *pair = k->data;
+        if (pair->component_id == j) {
+          gchar priority[PRIORITY_LEN];
+          guint p1, p2, p3;
+          gchar local_addr[INET6_ADDRSTRLEN];
+          gchar remote_addr[INET6_ADDRSTRLEN];
+
+          p1 = (pair->priority >> 32);
+          p2 = (pair->priority >> 1) & 0x7fffffff;
+          p3 = (pair->priority & 1);
+
+          g_snprintf (priority, PRIORITY_LEN,
+            "%02x:%04x:%02x:%02x:%04x:%02x:%1x",
+            (p1 >> 24) & 0x7f, (p1 >> 8) & 0xffff, (p1 & 0xff),
+            (p2 >> 24) & 0x7f, (p2 >> 8) & 0xffff, (p2 & 0xff),
+            p3);
+
+          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 p=%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),
+              priority,
+              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" : "");
+        }
+      }
+    }
+  }
+}
+
+/* Add the pair to the triggered checks list, if not already present
+ */
+static void
+priv_add_pair_to_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
+{
+  g_assert (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);
+}
+
+/* Remove the pair from the triggered checks list
+ */
+static void
+priv_remove_pair_from_triggered_check_queue (NiceAgent *agent, CandidateCheckPair *pair)
+{
+  g_assert (pair);
+  agent->triggered_check_queue = g_slist_remove (agent->triggered_check_queue, pair);
+}
+
+/* Get the pair from the triggered checks list
+ */
+static CandidateCheckPair *
+priv_get_pair_from_triggered_check_queue (NiceAgent *agent)
+{
+  CandidateCheckPair *pair = NULL;
+
+  if (agent->triggered_check_queue) {
+    pair = (CandidateCheckPair *)agent->triggered_check_queue->data;
+    priv_remove_pair_from_triggered_check_queue (agent, pair);
+  }
+  return pair;
+}
+
 /*
  * Finds the next connectivity check in WAITING state.
  */
@@ -107,10 +243,6 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
  */
 static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
 {
-  /* XXX: from ID-16 onwards, the checks should not be sent
-   * immediately, but be put into the "triggered queue",
-   * see  "7.2.1.4 Triggered Checks"
-   */
   g_get_current_time (&pair->next_tick);
   g_time_val_add (&pair->next_tick, agent->timer_ta * 1000);
   pair->state = NICE_CHECK_IN_PROGRESS;
@@ -142,7 +274,7 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
    */
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     guint64 max_frozen_priority = 0;
 
 
@@ -185,7 +317,7 @@ static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent)
  *
  * @return TRUE on success, and FALSE if no frozen candidates were found.
  */
-static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream, CandidateCheckPair *ok_check)
+static void priv_conn_check_unfreeze_related (NiceAgent *agent, NiceStream *stream, CandidateCheckPair *ok_check)
 {
   GSList *i, *j;
   guint unfrozen = 0;
@@ -212,10 +344,10 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream,
 
   /* step: perform the step (2) of 'Updating Pair States' */
   stream = agent_find_stream (agent, ok_check->stream_id);
-  if (stream_all_components_ready (stream)) {
+  if (nice_stream_all_components_ready (stream)) {
     /* step: unfreeze checks from other streams */
     for (i = agent->streams; i ; i = i->next) {
-      Stream *s = i->data;
+      NiceStream *s = i->data;
       for (j = stream->conncheck_list; j ; j = j->next) {
 	CandidateCheckPair *p = j->data;
 
@@ -242,12 +374,12 @@ static void priv_conn_check_unfreeze_related (NiceAgent *agent, Stream *stream,
 }
 
 static void
-candidate_check_pair_fail (Stream *stream, NiceAgent *agent, CandidateCheckPair *p)
+candidate_check_pair_fail (NiceStream *stream, NiceAgent *agent, CandidateCheckPair *p)
 {
   StunTransactionId id;
-  Component *component;
+  NiceComponent *component;
 
-  component = stream_find_component_by_id (stream, p->component_id);
+  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);
@@ -269,14 +401,16 @@ candidate_check_pair_fail (Stream *stream, NiceAgent *agent, CandidateCheckPair
  *
  * @return will return FALSE when no more pending timers.
  */
-static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, GTimeVal *now)
+static gboolean priv_conn_check_tick_stream (NiceStream *stream, NiceAgent *agent, GTimeVal *now, gboolean *stun_transmitted)
 {
   gboolean keep_timer_going = FALSE;
   guint s_inprogress = 0, s_succeeded = 0, s_discovered = 0,
-      s_nominated = 0, s_waiting_for_nomination = 0;
+      s_nominated = 0, s_waiting_for_nomination = 0, s_valid = 0;
   guint frozen = 0, waiting = 0;
   GSList *i, *k;
 
+  priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
+
   for (i = stream->conncheck_list; i ; i = i->next) {
     CandidateCheckPair *p = i->data;
 
@@ -290,7 +424,13 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G
           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);
 
               break;
@@ -299,8 +439,9 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G
             {
               /* case: not ready, so schedule a new timeout */
               unsigned int timeout = stun_timer_remainder (&p->timer);
-              nice_debug ("Agent %p :STUN transaction retransmitted (timeout %dms).",
-                  agent, timeout);
+              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);
 
               agent_socket_send (p->sockptr, &p->remote->addr,
                   stun_message_length (&p->stun_message),
@@ -311,8 +452,8 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G
               p->next_tick = *now;
               g_time_val_add (&p->next_tick, timeout * 1000);
 
-              keep_timer_going = TRUE;
-              break;
+              *stun_transmitted = TRUE;
+              return TRUE;
             }
           case STUN_USAGE_TIMER_RETURN_SUCCESS:
             {
@@ -342,6 +483,8 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G
       ++s_succeeded;
     else if (p->state == NICE_CHECK_DISCOVERED)
       ++s_discovered;
+    if (p->valid)
+      ++s_valid;
 
     if ((p->state == NICE_CHECK_SUCCEEDED || p->state == NICE_CHECK_DISCOVERED)
         && p->nominated)
@@ -365,7 +508,7 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G
 
       for (component_item = stream->components; component_item;
            component_item = component_item->next) {
-        Component *component = component_item->data;
+        NiceComponent *component = component_item->data;
 
 	for (k = stream->conncheck_list; k ; k = k->next) {
 	  CandidateCheckPair *p = k->data;
@@ -375,7 +518,7 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G
                p->state == NICE_CHECK_DISCOVERED)) {
 	    nice_debug ("Agent %p : restarting check %p as the nominated pair.", agent, p);
 	    p->nominated = TRUE;
-	    priv_conn_check_initiate (agent, p);
+            priv_add_pair_to_triggered_check_queue (agent, p);
 	    break; /* move to the next component */
 	  }
 	}
@@ -385,17 +528,28 @@ static gboolean priv_conn_check_tick_stream (Stream *stream, NiceAgent *agent, G
     {
     static int tick_counter = 0;
     if (tick_counter++ % 50 == 0 || keep_timer_going != TRUE)
-      nice_debug ("Agent %p : timer tick #%u: %u frozen, %u in-progress, "
+      nice_debug ("Agent %p : stream %u: timer tick #%u: %u frozen, %u in-progress, "
           "%u waiting, %u succeeded, %u discovered, %u nominated, "
-          "%u waiting-for-nom.", agent,
+          "%u waiting-for-nom, %u valid.", agent, stream->id,
           tick_counter, frozen, s_inprogress, waiting, s_succeeded,
-          s_discovered, s_nominated, s_waiting_for_nomination);
+          s_discovered, s_nominated, s_waiting_for_nomination, s_valid);
   }
 
   return keep_timer_going;
 
 }
 
+static void
+conn_check_stop (NiceAgent *agent)
+{
+  if (agent->conncheck_timer_source == NULL)
+    return;
+
+  g_source_destroy (agent->conncheck_timer_source);
+  g_source_unref (agent->conncheck_timer_source);
+  agent->conncheck_timer_source = NULL;
+}
+
 
 /*
  * Timer callback that handles initiating and managing connectivity
@@ -409,15 +563,39 @@ 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);
 
-  /* step: find the highest priority waiting check and send it */
+  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;
+  }
+
+  /* step: first initiate a conncheck with a pair from the triggered list */
+  pair = priv_get_pair_from_triggered_check_queue (agent);
+
+  if (pair) {
+    priv_conn_check_initiate (agent, pair);
+    return TRUE;
+  }
+
+  /* step: when the triggered list is empty,
+   * find the highest priority waiting check and send it */
   for (i = agent->streams; i ; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
 
     pair = priv_conn_check_find_next_waiting (stream->conncheck_list);
     if (pair)
@@ -426,27 +604,37 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent)
 
   if (pair) {
     priv_conn_check_initiate (agent, pair);
-    keep_timer_going = TRUE;
-  } else {
-    keep_timer_going = priv_conn_check_unfreeze_next (agent);
+    return TRUE;
   }
 
-  for (j = agent->streams; j; j = j->next) {
-    Stream *stream = j->data;
-    gboolean res =
-      priv_conn_check_tick_stream (stream, agent, &now);
-    if (res)
-      keep_timer_going = res;
+  /* step: when there's no pair in the Waiting state,
+   * unfreeze a new pair and check it
+   */
+  res = priv_conn_check_unfreeze_next (agent);
+
+  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 (pair) {
+    priv_print_conn_check_lists (agent, G_STRFUNC,
+        ", got a pair in Waiting state");
+    priv_conn_check_initiate (agent, pair);
+    return TRUE;
   }
 
   /* step: stop timer if no work left */
   if (keep_timer_going != TRUE) {
     nice_debug ("Agent %p : %s: stopping conncheck timer", agent, G_STRFUNC);
     for (i = agent->streams; i; i = i->next) {
-      Stream *stream = i->data;
+      NiceStream *stream = i->data;
       priv_update_check_list_failed_components (agent, stream);
       for (j = stream->components; j; j = j->next) {
-        Component *component = j->data;
+        NiceComponent *component = j->data;
         priv_update_check_list_state_for_ready (agent, stream, component);
       }
     }
@@ -454,11 +642,7 @@ static gboolean priv_conn_check_tick_unlocked (NiceAgent *agent)
     /* Stopping the timer so destroy the source.. this will allow
        the timer to be reset if we get a set_remote_candidates after this
        point */
-    if (agent->conncheck_timer_source != NULL) {
-      g_source_destroy (agent->conncheck_timer_source);
-      g_source_unref (agent->conncheck_timer_source);
-      agent->conncheck_timer_source = NULL;
-    }
+    conn_check_stop (agent);
 
     /* XXX: what to signal, is all processing now really done? */
     nice_debug ("Agent %p : changing conncheck state to COMPLETED.", agent);
@@ -512,7 +696,7 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer)
       {
         /* Time out */
         StunTransactionId id;
-        Component *component;
+        NiceComponent *component;
 
         if (!agent_find_component (pair->keepalive.agent,
                 pair->keepalive.stream_id, pair->keepalive.component_id,
@@ -525,13 +709,12 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer)
 
         stun_message_id (&pair->keepalive.stun_message, id);
         stun_agent_forget_transaction (&component->stun_agent, id);
+        pair->keepalive.stun_message.buffer = NULL;
 
         if (pair->keepalive.agent->media_after_tick) {
           nice_debug ("Agent %p : Keepalive conncheck timed out!! "
               "but media was received. Suspecting keepalive lost because of "
               "network bottleneck", pair->keepalive.agent);
-
-          pair->keepalive.stun_message.buffer = NULL;
         } else {
           nice_debug ("Agent %p : Keepalive conncheck timed out!! "
               "peer probably lost connection", pair->keepalive.agent);
@@ -561,7 +744,7 @@ static gboolean priv_conn_keepalive_retransmissions_tick (gpointer pointer)
           priv_conn_keepalive_retransmissions_tick, pair);
       break;
     default:
-      /* Nothing to do. */
+      g_assert_not_reached();
       break;
   }
 
@@ -579,6 +762,7 @@ static guint32 peer_reflexive_candidate_priority (NiceAgent *agent,
 
   candidate_priority->transport = local_candidate->transport;
   candidate_priority->component_id = local_candidate->component_id;
+  candidate_priority->base_addr = local_candidate->addr;
   if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
     priority = nice_candidate_jingle_priority (candidate_priority);
   } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
@@ -617,9 +801,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
    *         (ref ICE sect 10 "Keepalives" ID-19)  */
   for (i = agent->streams; i; i = i->next) {
 
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     for (j = stream->components; j; j = j->next) {
-      Component *component = j->data;
+      NiceComponent *component = j->data;
       if (component->selected_pair.local != NULL) {
 	CandidatePair *p = &component->selected_pair;
 
@@ -629,7 +813,6 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
 
         if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE ||
             agent->keepalive_conncheck) {
-          guint32 priority;
           uint8_t uname[NICE_STREAM_MAX_UNAME];
           size_t uname_len =
               priv_create_username (agent, agent_find_stream (agent, stream->id),
@@ -639,25 +822,31 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
           size_t password_len = priv_get_password (agent,
               agent_find_stream (agent, stream->id), p->remote, &password);
 
-          priority = peer_reflexive_candidate_priority (agent, p->local);
+          if (p->keepalive.stun_message.buffer != NULL) {
+            nice_debug ("Agent %p: Keepalive for s%u:c%u still"
+                " retransmitting, not restarting", agent, stream->id,
+                component->id);
+            continue;
+          }
 
           if (nice_debug_is_enabled ()) {
             gchar tmpbuf[INET6_ADDRSTRLEN];
             nice_address_to_string (&p->remote->addr, tmpbuf);
             nice_debug ("Agent %p : Keepalive STUN-CC REQ to '%s:%u', "
-                "socket=%u (c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
+                "(c-id:%u), username='%.*s' (%" G_GSIZE_FORMAT "), "
                 "password='%.*s' (%" G_GSIZE_FORMAT "), priority=%u.", agent,
                 tmpbuf, nice_address_get_port (&p->remote->addr),
-                g_socket_get_fd(((NiceSocket *)p->local->sockptr)->fileno),
                 component->id, (int) uname_len, uname, uname_len,
-                (int) password_len, password, password_len, priority);
+                (int) password_len, password, password_len,
+                p->prflx_priority);
           }
           if (uname_len > 0) {
             buf_len = stun_usage_ice_conncheck_create (&component->stun_agent,
                 &p->keepalive.stun_message, p->keepalive.stun_buffer,
                 sizeof(p->keepalive.stun_buffer),
                 uname, uname_len, password, password_len,
-                agent->controlling_mode, agent->controlling_mode, priority,
+                agent->controlling_mode, agent->controlling_mode,
+                p->prflx_priority,
                 agent->tie_breaker,
                 NULL,
                 agent_to_ice_compatibility (agent));
@@ -709,9 +898,9 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
   /* case 2: connectivity establishment ongoing
    *         (ref ICE sect 4.1.1.4 "Keeping Candidates Alive" ID-19)  */
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     for (j = stream->components; j; j = j->next) {
-      Component *component = j->data;
+      NiceComponent *component = j->data;
       if (component->state < NICE_COMPONENT_STATE_READY &&
           agent->stun_server_ip) {
         NiceAddress stun_server;
@@ -723,12 +912,7 @@ static gboolean priv_conn_keepalive_tick_unlocked (NiceAgent *agent)
 
           nice_address_set_port (&stun_server, agent->stun_server_port);
 
-          /* FIXME: This will cause the stun response to arrive on the socket
-           * but the stun agent will not be able to parse it due to an invalid
-           * stun message since RFC3489 will not be compatible, and the response
-           * will be forwarded to the application as user data */
-          stun_agent_init (&stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
-              STUN_COMPATIBILITY_RFC3489, 0);
+          nice_agent_init_stun_agent (agent, &stun_agent);
 
           buffer_len = stun_usage_bind_create (&stun_agent,
               &stun_message, stun_buffer, sizeof(stun_buffer));
@@ -937,23 +1121,14 @@ static gboolean priv_turn_allocate_refresh_tick (gpointer pointer)
 
 /*
  * Initiates the next pending connectivity check.
- * 
- * @return TRUE if a pending check was scheduled
  */
-gboolean conn_check_schedule_next (NiceAgent *agent)
+void conn_check_schedule_next (NiceAgent *agent)
 {
-  gboolean res = priv_conn_check_unfreeze_next (agent);
-  nice_debug ("Agent %p : priv_conn_check_unfreeze_next returned %d", agent, res);
-
   if (agent->discovery_unsched_items > 0)
     nice_debug ("Agent %p : WARN: starting conn checks before local candidate gathering is finished.", agent);
 
-  /* step: call once imediately */
-  res = priv_conn_check_tick_unlocked (agent);
-  nice_debug ("Agent %p : priv_conn_check_tick_unlocked returned %d", agent, res);
-
   /* step: schedule timer if not running yet */
-  if (res && agent->conncheck_timer_source == NULL) {
+  if (agent->conncheck_timer_source == NULL) {
     agent_timeout_add_with_context (agent, &agent->conncheck_timer_source,
         "Connectivity check schedule", agent->timer_ta,
         priv_conn_check_tick, agent);
@@ -965,9 +1140,6 @@ gboolean conn_check_schedule_next (NiceAgent *agent)
         "Connectivity keepalive timeout", NICE_AGENT_TIMER_TR_DEFAULT,
         priv_conn_keepalive_tick, agent);
   }
-
-  nice_debug ("Agent %p : conn_check_schedule_next returning %d", agent, res);
-  return res;
 }
 
 /*
@@ -995,7 +1167,7 @@ gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *
  * @param component pointer to component object to which 'pair'has been added
  * @param pair newly added connectivity check
  */
-static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *pair)
+static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *pair)
 {
   GSList *i;
   for (i = component->incoming_checks; i; i = i->next) {
@@ -1004,7 +1176,7 @@ static void priv_preprocess_conn_check_pending_data (NiceAgent *agent, Stream *s
 	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);
       if (icheck->use_candidate)
-	priv_mark_pair_nominated (agent, stream, component, pair->remote);
+	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);
     }
   }
@@ -1039,15 +1211,13 @@ static GSList *prune_cancelled_conn_check (GSList *conncheck_list)
  * reaches us. The special case is documented in sect 7.2 
  * if ICE spec (ID-19).
  */
-void conn_check_remote_candidates_set(NiceAgent *agent)
+void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component)
 {
-  GSList *i, *j, *k, *l, *m, *n;
+  GSList *j, *k, *l, *m, *n;
 
-  for (i = agent->streams; i ; i = i->next) {
-    Stream *stream = i->data;
-    for (j = stream->conncheck_list; j ; j = j->next) {
-      CandidateCheckPair *pair = j->data;
-      Component *component = stream_find_component_by_id (stream, pair->component_id);
+  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,
@@ -1148,24 +1318,24 @@ void conn_check_remote_candidates_set(NiceAgent *agent)
                 conn_check_add_for_candidate (agent, stream->id, component, candidate);
 
               if (icheck->use_candidate)
-                priv_mark_pair_nominated (agent, stream, component, 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);
             }
           }
         }
       }
-
-      /* 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);
   }
+
+  /* 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);
 }
 
 /*
@@ -1204,20 +1374,23 @@ static void priv_limit_conn_check_list_size (GSList *conncheck_list, guint upper
  * and has higher priority than the currently selected pair. See
  * ICE sect 11.1.1. "Procedures for Full Implementations" (ID-19).
  */
-static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair)
+static gboolean priv_update_selected_pair (NiceAgent *agent, NiceComponent *component, CandidateCheckPair *pair)
 {
-  CandidatePair cpair;
+  CandidatePair cpair = { 0, };
 
   g_assert (component);
   g_assert (pair);
-  if (pair->priority > component->selected_pair.priority &&
-      component_find_pair (component, agent, pair->local->foundation,
-          pair->remote->foundation, &cpair)) {
+  if (pair->priority > component->selected_pair.priority) {
     nice_debug ("Agent %p : changing SELECTED PAIR for component %u: %s:%s "
         "(prio:%" G_GUINT64_FORMAT ").", agent, component->id,
         pair->local->foundation, pair->remote->foundation, pair->priority);
 
-    component_update_selected_pair (component, &cpair);
+    cpair.local = pair->local;
+    cpair.remote = pair->remote;
+    cpair.priority = pair->priority;
+    /* cpair.keepalive is not used by nice_component_update_selected_pair() */
+
+    nice_component_update_selected_pair (component, &cpair);
 
     priv_conn_keepalive_tick_unlocked (agent);
 
@@ -1239,7 +1412,7 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, Component *componen
  *
  * Sends a component state changesignal via 'agent'.
  */
-static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *stream)
+static void priv_update_check_list_failed_components (NiceAgent *agent, NiceStream *stream)
 {
   GSList *i;
   /* note: emitting a signal might cause the client 
@@ -1261,7 +1434,7 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *
 
   /* note: iterate the conncheck list for each component separately */
   for (c = 0; c < components; c++) {
-    Component *comp = NULL;
+    NiceComponent *comp = NULL;
     if (!agent_find_component (agent, stream->id, c+1, NULL, &comp))
       continue;
 
@@ -1299,10 +1472,10 @@ static void priv_update_check_list_failed_components (NiceAgent *agent, Stream *
  *
  * Sends a component state changesignal via 'agent'.
  */
-static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *stream, Component *component)
+static void priv_update_check_list_state_for_ready (NiceAgent *agent, NiceStream *stream, NiceComponent *component)
 {
   GSList *i;
-  guint succeeded = 0, nominated = 0;
+  guint valid = 0, nominated = 0;
 
   g_assert (component);
 
@@ -1310,26 +1483,36 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *st
   for (i = stream->conncheck_list; i; i = i->next) {
     CandidateCheckPair *p = i->data;
     if (p->component_id == component->id) {
-      if (p->state == NICE_CHECK_SUCCEEDED ||
-	  p->state == NICE_CHECK_DISCOVERED) {
-	++succeeded;
+      if (p->valid) {
+	++valid;
 	if (p->nominated == TRUE) {
           ++nominated;
+          priv_update_selected_pair (agent, component, p);
 	}
       }
     }
   }
 
-  if (nominated > 0) {
+  if (valid > 0) {
     /* 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) {
+      /* Continue through the states to give client code a nice
+       * logical progression. See http://phabricator.freedesktop.org/D218 for
+       * discussion. */
+      if (component->state < NICE_COMPONENT_STATE_CONNECTING ||
+          component->state == NICE_COMPONENT_STATE_FAILED)
+        agent_signal_component_state_change (agent, stream->id, component->id,
+            NICE_COMPONENT_STATE_CONNECTING);
+      if (component->state < NICE_COMPONENT_STATE_CONNECTED)
+        agent_signal_component_state_change (agent, stream->id, component->id,
+            NICE_COMPONENT_STATE_CONNECTED);
       agent_signal_component_state_change (agent, stream->id,
           component->id, NICE_COMPONENT_STATE_READY);
     }
   }
-  nice_debug ("Agent %p : conn.check list status: %u nominated, %u succeeded, c-id %u.", agent, nominated, succeeded, component->id);
+  nice_debug ("Agent %p : conn.check list status: %u nominated, %u valid, c-id %u.", agent, nominated, valid, component->id);
 }
 
 /*
@@ -1337,7 +1520,7 @@ static void priv_update_check_list_state_for_ready (NiceAgent *agent, Stream *st
  * described by 'component' and 'remotecand' is nominated
  * for use.
  */
-static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *remotecand)
+static void priv_mark_pair_nominated (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *localcand, NiceCandidate *remotecand)
 {
   GSList *i;
 
@@ -1346,27 +1529,66 @@ static void priv_mark_pair_nominated (NiceAgent *agent, Stream *stream, Componen
   /* step: search for at least one nominated pair */
   for (i = stream->conncheck_list; i; i = i->next) {
     CandidateCheckPair *pair = i->data;
-    /* XXX: hmm, how to figure out to which local candidate the 
-     *      check was sent to? let's mark all matching pairs
-     *      as nominated instead */
-    if (pair->remote == remotecand) {
+    if (pair->local == localcand && pair->remote == remotecand) {
       nice_debug ("Agent %p : marking pair %p (%s) as nominated", agent, pair, pair->foundation);
       pair->nominated = TRUE;
-      if (pair->state == NICE_CHECK_SUCCEEDED ||
-	  pair->state == NICE_CHECK_DISCOVERED)
+      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) {
+          /* 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);
     }
   }
 }
 
+guint32
+ensure_unique_priority (NiceComponent *component, guint32 priority)
+{
+  GSList *item;
+
+ again:
+  if (priority == 0)
+    priority--;
+
+  for (item = component->local_candidates; item; item = item->next) {
+    NiceCandidate *cand = item->data;
+
+    if (cand->priority == priority) {
+      priority--;
+      goto again;
+    }
+  }
+
+  for (item = component->stream->conncheck_list; item; item = item->next) {
+    CandidateCheckPair *p = item->data;
+
+    if (p->component_id == component->id &&
+        p->prflx_priority == priority) {
+      priority--;
+      goto again;
+    }
+  }
+
+  return priority;
+}
+
+
 /*
  * Creates a new connectivity check pair and adds it to
  * the agent's list of checks.
  */
-static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
+static CandidateCheckPair *priv_add_new_check_pair (NiceAgent *agent,
+    guint stream_id, NiceComponent *component, NiceCandidate *local,
+    NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
 {
-  Stream *stream;
+  NiceStream *stream;
   CandidateCheckPair *pair;
 
   g_assert (local != NULL);
@@ -1389,8 +1611,19 @@ static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Componen
   pair->priority = agent_candidate_pair_priority (agent, local, remote);
   pair->state = initial_state;
   nice_debug ("Agent %p : creating new pair %p state %d", agent, pair, initial_state);
+  {
+      gchar tmpbuf1[INET6_ADDRSTRLEN];
+      gchar tmpbuf2[INET6_ADDRSTRLEN];
+      nice_address_to_string (&pair->local->addr, tmpbuf1);
+      nice_address_to_string (&pair->remote->addr, tmpbuf2);
+      nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
+          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));
 
   stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
       (GCompareFunc)conn_check_compare);
@@ -1402,6 +1635,8 @@ static void priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Componen
   if (agent->compatibility == NICE_COMPATIBILITY_RFC5245) {
     priv_limit_conn_check_list_size (stream->conncheck_list, agent->max_conn_checks);
   }
+
+  return pair;
 }
 
 NiceCandidateTransport
@@ -1422,13 +1657,16 @@ conn_check_match_transport (NiceCandidateTransport transport)
   }
 }
 
-static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent,
-    guint stream_id, Component *component, NiceCandidate *local,
-    NiceCandidate *remote, NiceCheckState initial_state)
+static CandidateCheckPair *priv_conn_check_add_for_candidate_pair_matched (
+    NiceAgent *agent, guint stream_id, NiceComponent *component,
+     NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state)
 {
-  nice_debug ("Agent %p, Adding check pair between %s and %s", agent,
-      local->foundation, remote->foundation);
-  priv_add_new_check_pair (agent, stream_id, component, local, remote,
+  CandidateCheckPair *pair;
+
+  nice_debug ("Agent %p : Adding check pair between %s and %s for s%d/c%d",
+      agent, local->foundation, remote->foundation,
+      stream_id, component->id);
+  pair = priv_add_new_check_pair (agent, stream_id, component, local, remote,
       initial_state, FALSE);
   if (component->state == NICE_COMPONENT_STATE_CONNECTED ||
       component->state == NICE_COMPONENT_STATE_READY) {
@@ -1442,10 +1680,12 @@ static void priv_conn_check_add_for_candidate_pair_matched (NiceAgent *agent,
         component->id,
         NICE_COMPONENT_STATE_CONNECTING);
   }
+
+  return pair;
 }
 
 gboolean conn_check_add_for_candidate_pair (NiceAgent *agent,
-    guint stream_id, Component *component, NiceCandidate *local,
+    guint stream_id, NiceComponent *component, NiceCandidate *local,
     NiceCandidate *remote)
 {
   gboolean ret = FALSE;
@@ -1491,7 +1731,7 @@ gboolean conn_check_add_for_candidate_pair (NiceAgent *agent,
  *
  * @return number of checks added, negative on fatal errors
  */
-int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote)
+int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote)
 {
   GSList *i;
   int added = 0;
@@ -1500,8 +1740,11 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *
   g_assert (remote != NULL);
 
   for (i = component->local_candidates; i ; i = i->next) {
-
     NiceCandidate *local = i->data;
+
+    if (agent->force_relay && local->type != NICE_CANDIDATE_TYPE_RELAYED)
+        continue;
+
     ret = conn_check_add_for_candidate_pair (agent, stream_id, component, local, remote);
 
     if (ret) {
@@ -1522,7 +1765,7 @@ int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *
  *
  * @return number of checks added, negative on fatal errors
  */
-int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local)
+int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local)
 {
   GSList *i;
   int added = 0;
@@ -1551,22 +1794,13 @@ static void conn_check_free_item (gpointer data)
 {
   CandidateCheckPair *pair = 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;
   g_slice_free (CandidateCheckPair, pair);
 }
 
-static void
-conn_check_stop (NiceAgent *agent)
-{
-  if (agent->conncheck_timer_source == NULL)
-    return;
-
-  g_source_destroy (agent->conncheck_timer_source);
-  g_source_unref (agent->conncheck_timer_source);
-  agent->conncheck_timer_source = NULL;
-}
-
 /*
  * Frees all resources of all connectivity checks.
  */
@@ -1574,7 +1808,7 @@ void conn_check_free (NiceAgent *agent)
 {
   GSList *i;
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
 
     if (stream->conncheck_list) {
       nice_debug ("Agent %p, freeing conncheck_list of stream %p", agent,
@@ -1593,7 +1827,7 @@ void conn_check_free (NiceAgent *agent)
  *
  * @return TRUE on success, FALSE on a fatal error
  */
-void conn_check_prune_stream (NiceAgent *agent, Stream *stream)
+void conn_check_prune_stream (NiceAgent *agent, NiceStream *stream)
 {
   GSList *i;
   gboolean keep_going = FALSE;
@@ -1606,7 +1840,7 @@ void conn_check_prune_stream (NiceAgent *agent, Stream *stream)
   }
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *s = i->data;
+    NiceStream *s = i->data;
     if (s->conncheck_list) {
       keep_going = TRUE;
       break;
@@ -1717,7 +1951,7 @@ size_t priv_gen_username (NiceAgent *agent, guint component_id,
  * NULL) is ever written to the 'dest'.
  */
 static
-size_t priv_create_username (NiceAgent *agent, Stream *stream,
+size_t priv_create_username (NiceAgent *agent, NiceStream *stream,
     guint component_id, NiceCandidate *remote, NiceCandidate *local,
     uint8_t *dest, guint dest_len, gboolean inbound)
 {
@@ -1760,7 +1994,7 @@ size_t priv_create_username (NiceAgent *agent, Stream *stream,
  * check.
  */
 static
-size_t priv_get_password (NiceAgent *agent, Stream *stream,
+size_t priv_get_password (NiceAgent *agent, NiceStream *stream,
     NiceCandidate *remote, uint8_t **password)
 {
   if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE)
@@ -1781,26 +2015,30 @@ size_t priv_get_password (NiceAgent *agent, Stream *stream,
 
 /* Implement the computation specific in RFC 5245 section 16 */
 
-static unsigned int priv_compute_conncheck_timer (NiceAgent *agent,
-    Stream *stream)
+static unsigned int priv_compute_conncheck_timer (NiceAgent *agent)
 {
-  GSList *item;
+  GSList *item1, *item2;
   guint waiting_and_in_progress = 0;
   unsigned int rto = 0;
 
-  for (item = stream->conncheck_list; item; item = item->next) {
-    CandidateCheckPair *pair = item->data;
 
-    if (pair->state == NICE_CHECK_IN_PROGRESS ||
-        pair->state == NICE_CHECK_WAITING)
-      waiting_and_in_progress++;
+  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++;
+    }
   }
 
-  /* FIXME: This should also be multiple by "N", which I believe is the
-   * number of Streams currently in the conncheck state. */
   rto = agent->timer_ta  * 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)",
+    agent, agent->reliable ? MAX (rto, 500) : MAX (rto, 100),
+    waiting_and_in_progress);
   if (agent->reliable)
     return MAX (rto, 500);
   else
@@ -1822,11 +2060,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
    *  - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
    *  - USE-CANDIDATE (if sent by the controlling agent)
    */
-  guint32 priority;
 
   uint8_t uname[NICE_STREAM_MAX_UNAME];
-  Stream *stream;
-  Component *component;
+  NiceStream *stream;
+  NiceComponent *component;
   gsize uname_len;
   uint8_t *password = NULL;
   gsize password_len;
@@ -1844,8 +2081,6 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
       pair->remote, pair->local, uname, sizeof (uname), FALSE);
   password_len = priv_get_password (agent, stream, pair->remote, &password);
 
-  priority = peer_reflexive_candidate_priority (agent, pair->local);
-
   if (password != NULL &&
       (agent->compatibility == NICE_COMPATIBILITY_MSN ||
        agent->compatibility == NICE_COMPATIBILITY_OC2007)) {
@@ -1853,20 +2088,21 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
   }
 
   if (nice_debug_is_enabled ()) {
-    gchar tmpbuf[INET6_ADDRSTRLEN];
-    nice_address_to_string (&pair->remote->addr, tmpbuf);
-    nice_debug ("Agent %p : STUN-CC REQ to '%s:%u', socket=%u, "
+    gchar tmpbuf1[INET6_ADDRSTRLEN];
+    gchar tmpbuf2[INET6_ADDRSTRLEN];
+    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 "), priority=%u.", agent,
-	     tmpbuf,
-             nice_address_get_port (&pair->remote->addr),
+        "password='%.*s' (%" G_GSIZE_FORMAT "), prio=%u, cont=%d.", 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,
 	     (unsigned long long)agent->tie_breaker,
         (int) uname_len, uname, uname_len,
         (int) password_len, password, password_len,
-        priority);
-
+        pair->prflx_priority, controlling);
   }
 
   if (cand_use)
@@ -1876,7 +2112,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
     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, priority,
+        cand_use, controlling, pair->prflx_priority,
         agent->tie_breaker,
         pair->local->foundation,
         agent_to_ice_compatibility (agent));
@@ -1894,7 +2130,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
         stun_timer_start_reliable(&pair->timer, STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT);
       } else {
         stun_timer_start (&pair->timer,
-            priv_compute_conncheck_timer (agent, stream),
+            priv_compute_conncheck_timer (agent),
             STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
       }
 
@@ -1902,9 +2138,10 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
        * 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) {
-        Stream *stream2 = NULL;
-        Component *component2 = NULL;
+        NiceStream *stream2 = NULL;
+        NiceComponent *component2 = NULL;
         NiceSocket *new_socket;
 
         if (agent_find_component (agent, pair->stream_id, pair->component_id,
@@ -1914,7 +2151,13 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
           if (new_socket) {
             pair->sockptr = new_socket;
             _priv_set_socket_tos (agent, pair->sockptr, stream2->tos);
-            component_attach_socket (component2, new_socket);
+
+            if (agent->reliable) {
+              nice_socket_set_writable_callback (pair->sockptr,
+                  _tcp_sock_is_writable, component2);
+            }
+
+            nice_component_attach_socket (component2, new_socket);
           }
         }
       }
@@ -1948,7 +2191,7 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
  *
  * @see priv_update_check_list_state_failed_components()
  */
-static guint priv_prune_pending_checks (Stream *stream, guint component_id)
+static guint priv_prune_pending_checks (NiceStream *stream, guint component_id)
 {
   GSList *i;
   guint64 highest_nominated_priority = 0;
@@ -1959,10 +2202,8 @@ static guint priv_prune_pending_checks (Stream *stream, guint component_id)
 
   for (i = stream->conncheck_list; i; i = i->next) {
     CandidateCheckPair *p = i->data;
-    if (p->component_id == component_id &&
-        (p->state == NICE_CHECK_SUCCEEDED ||
-            p->state == NICE_CHECK_DISCOVERED) &&
-        p->nominated == TRUE){
+    if (p->component_id == component_id && p->valid == TRUE &&
+        p->nominated == TRUE) {
       if (p->priority > highest_nominated_priority) {
         highest_nominated_priority = p->priority;
       }
@@ -2015,7 +2256,7 @@ static guint priv_prune_pending_checks (Stream *stream, guint component_id)
  * @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, Stream *stream, Component *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, gboolean use_candidate)
 {
   GSList *i;
   NiceCandidate *local = NULL;
@@ -2038,7 +2279,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream,
 	
 	if (p->state == NICE_CHECK_WAITING ||
 	    p->state == NICE_CHECK_FROZEN)
-	  priv_conn_check_initiate (agent, p);
+          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
@@ -2049,7 +2290,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream,
             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, stream),
+                priv_compute_conncheck_timer (agent),
                 STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
 	    p->timer_restarted = TRUE;
 	  }
@@ -2059,23 +2300,36 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream,
 	  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: to take care of the controlling-controlling case in
-	   *       aggressive nomination mode, send a new triggered
-	   *       check to nominate the pair */
+          /* 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)
-	    priv_conn_check_initiate (agent, p);
+              agent->controlling_mode) {
+            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_conn_check_initiate (agent, p);
+          priv_add_pair_to_triggered_check_queue (agent, p);
         }
 
 	/* note: the spec says the we SHOULD retransmit in-progress
@@ -2121,7 +2375,7 @@ static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream,
  * 
  * @pre (rcand == NULL || nice_address_equal(rcand->addr, toaddr) == TRUE)
  */
-static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *rcand, const NiceAddress *toaddr, NiceSocket *sockptr, size_t  rbuf_len, uint8_t *rbuf, gboolean use_candidate)
+static void priv_reply_to_conn_check (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceCandidate *lcand, NiceCandidate *rcand, const NiceAddress *toaddr, NiceSocket *sockptr, size_t  rbuf_len, uint8_t *rbuf, gboolean use_candidate)
 {
   g_assert (rcand == NULL || nice_address_equal(&rcand->addr, toaddr) == TRUE);
 
@@ -2144,7 +2398,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Componen
     priv_schedule_triggered_check (agent, stream, component, sockptr, rcand, use_candidate);
 
     if (use_candidate)
-      priv_mark_pair_nominated (agent, stream, component, rcand);
+      priv_mark_pair_nominated (agent, stream, component, lcand, rcand);
   }
 }
 
@@ -2156,7 +2410,7 @@ static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Componen
  *
  * @return non-zero on error, zero on success
  */
-static int priv_store_pending_check (NiceAgent *agent, Component *component,
+static int priv_store_pending_check (NiceAgent *agent, NiceComponent *component,
     const NiceAddress *from, NiceSocket *sockptr, uint8_t *username,
     uint16_t username_len, uint32_t priority, gboolean use_candidate)
 {
@@ -2190,19 +2444,28 @@ static int priv_store_pending_check (NiceAgent *agent, Component *component,
  *
  * @return created pair, or NULL on fatal (memory allocation) errors
  */
-static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *local_cand, CandidateCheckPair *parent_pair)
+static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local_cand, CandidateCheckPair *parent_pair)
 {
   CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
-  Stream *stream = agent_find_stream (agent, stream_id);
+  NiceStream *stream = agent_find_stream (agent, stream_id);
 
   pair->agent = agent;
   pair->stream_id = stream_id;
-  pair->component_id = component_id;;
+  pair->component_id = component->id;;
   pair->local = local_cand;
   pair->remote = parent_pair->remote;
   pair->sockptr = local_cand->sockptr;
   pair->state = NICE_CHECK_DISCOVERED;
-  nice_debug ("Agent %p : pair %p state DISCOVERED", agent, pair);
+  nice_debug ("Agent %p : new pair %p state DISCOVERED", agent, pair);
+  {
+      gchar tmpbuf1[INET6_ADDRSTRLEN];
+      gchar tmpbuf2[INET6_ADDRSTRLEN];
+      nice_address_to_string (&pair->local->addr, tmpbuf1);
+      nice_address_to_string (&pair->remote->addr, tmpbuf2);
+      nice_debug ("Agent %p : new pair %p : [%s]:%u --> [%s]:%u", agent, pair,
+          tmpbuf1, nice_address_get_port (&pair->local->addr),
+          tmpbuf2, nice_address_get_port (&pair->remote->addr));
+  }
   g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s",
       local_cand->foundation, parent_pair->remote->foundation);
   if (agent->controlling_mode == TRUE)
@@ -2213,6 +2476,8 @@ static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint
         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);
 
   stream->conncheck_list = g_slist_insert_sorted (stream->conncheck_list, pair,
@@ -2230,11 +2495,13 @@ static void priv_recalculate_pair_priorities (NiceAgent *agent)
   GSList *i, *j;
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     for (j = stream->conncheck_list; j; j = j->next) {
       CandidateCheckPair *p = j->data;
       p->priority = agent_candidate_pair_priority (agent, p->local, p->remote);
     }
+    stream->conncheck_list = g_slist_sort (stream->conncheck_list,
+        (GCompareFunc)conn_check_compare);
   }
 }
 
@@ -2269,21 +2536,21 @@ static void priv_check_for_role_conflict (NiceAgent *agent, gboolean control)
  * @param socketptr socket used to send the reply
  * @param mapped_sockaddr mapped address in the response
  *
- * @return pointer to a new pair if one was created, otherwise NULL
+ * @return pointer to a candidate pair, found in conncheck list or newly created
  */
-static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAgent *agent, Stream *stream, Component *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate)
+static CandidateCheckPair *priv_process_response_check_for_reflexive(NiceAgent *agent, NiceStream *stream, NiceComponent *component, CandidateCheckPair *p, NiceSocket *sockptr, struct sockaddr *mapped_sockaddr, NiceCandidate *local_candidate, NiceCandidate *remote_candidate)
 {
   CandidateCheckPair *new_pair = NULL;
   NiceAddress mapped;
   GSList *i, *j;
-  gboolean local_cand_matches = FALSE;
+  NiceCandidate *local_cand = NULL;
 
   nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
 
   for (j = component->local_candidates; j; j = j->next) {
     NiceCandidate *cand = j->data;
     if (nice_address_equal (&mapped, &cand->addr)) {
-      local_cand_matches = TRUE;
+      local_cand = cand;
 
       /* We always need to select the peer-reflexive Candidate Pair in the case
        * of a TCP-ACTIVE local candidate, so we find it even if an incoming
@@ -2300,31 +2567,38 @@ static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAg
     }
   }
 
-  if (local_cand_matches == TRUE) {
-    /* note: this is same as "adding to VALID LIST" in the spec
-       text */
+  if (new_pair) {
     p->state = NICE_CHECK_SUCCEEDED;
     nice_debug ("Agent %p : conncheck %p SUCCEEDED.", agent, p);
     priv_conn_check_unfreeze_related (agent, stream, p);
   }
   else {
-    NiceCandidate *cand =
-      discovery_add_peer_reflexive_candidate (agent,
-					      stream->id,
-					      component->id,
-					      &mapped,
-					      sockptr,
-					      local_candidate,
-					      remote_candidate);
-    p->state = NICE_CHECK_FAILED;
-    nice_debug ("Agent %p : pair %p state FAILED", agent, p);
+    if (!local_cand) {
+      if (!agent->force_relay)
+        local_cand = discovery_add_peer_reflexive_candidate (agent,
+                                                             stream->id,
+                                                             component->id,
+                                                            &mapped,
+                                                             sockptr,
+                                                             local_candidate,
+                                                             remote_candidate);
+      p->state = NICE_CHECK_FAILED;
+      nice_debug ("Agent %p : pair %p state FAILED", agent, p);
+    }
 
     /* step: add a new discovered pair (see RFC 5245 7.1.3.2.2
 	       "Constructing a Valid Pair") */
-    new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component->id, cand, p);
+    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;
+
   return new_pair;
 }
 
@@ -2335,7 +2609,7 @@ static CandidateCheckPair *priv_process_response_check_for_peer_reflexive(NiceAg
  *
  * @return TRUE if a matching transaction is found
  */
-static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *sockptr, const NiceAddress *from, NiceCandidate *local_candidate, NiceCandidate *remote_candidate, StunMessage *resp)
+static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, NiceStream *stream, NiceComponent *component, NiceSocket *sockptr, const NiceAddress *from, NiceCandidate *local_candidate, NiceCandidate *remote_candidate, StunMessage *resp)
 {
   union {
     struct sockaddr_storage storage;
@@ -2404,11 +2678,13 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream *
             /* 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_peer_reflexive (agent,
+            ok_pair = priv_process_response_check_for_reflexive (agent,
                 stream, component, p, sockptr, &sockaddr.addr,
                 local_candidate, remote_candidate);
           }
@@ -2431,6 +2707,8 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream *
             }
           }
 
+          priv_print_conn_check_lists (agent, G_STRFUNC, NULL);
+
           /* 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);
@@ -2447,6 +2725,7 @@ static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream *
           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 {
@@ -2516,25 +2795,27 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa
           d->pending = FALSE;
         } else if (res == STUN_USAGE_BIND_RETURN_SUCCESS) {
           /* case: successful binding discovery, create a new local candidate */
-          NiceAddress niceaddr;
-          nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr);
-
-          discovery_add_server_reflexive_candidate (
-              d->agent,
-              d->stream->id,
-              d->component->id,
-              &niceaddr,
-              NICE_CANDIDATE_TRANSPORT_UDP,
-              d->nicesock,
-              FALSE);
-          if (d->agent->use_ice_tcp)
-            discovery_discover_tcp_server_reflexive_candidates (
+
+          if (!agent->force_relay) {
+            NiceAddress niceaddr;
+
+            nice_address_set_from_sockaddr (&niceaddr, &sockaddr.addr);
+            discovery_add_server_reflexive_candidate (
                 d->agent,
                 d->stream->id,
                 d->component->id,
                 &niceaddr,
-                d->nicesock);
-
+                NICE_CANDIDATE_TRANSPORT_UDP,
+                d->nicesock,
+                FALSE);
+            if (d->agent->use_ice_tcp)
+              discovery_discover_tcp_server_reflexive_candidates (
+                  d->agent,
+                  d->stream->id,
+                  d->component->id,
+                  &niceaddr,
+                  d->nicesock);
+          }
           d->stun_message.buffer = NULL;
           d->stun_message.buffer_len = 0;
           d->done = TRUE;
@@ -2667,7 +2948,8 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
              * on a TCP connection, which cannot be used for server-reflexive
              * discovery of candidates.
              */
-            if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP) {
+            if (d->turn->type == NICE_RELAY_TYPE_TURN_UDP &&
+                !agent->force_relay) {
               discovery_add_server_reflexive_candidate (
                   d->agent,
                   d->stream->id,
@@ -2729,6 +3011,9 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
           }
 
           if (relay_cand) {
+	    if (d->stun_resp_msg.buffer)
+	      nice_udp_turn_socket_cache_realm_nonce (relay_cand->sockptr,
+                  &d->stun_resp_msg);
             if (agent->compatibility == NICE_COMPATIBILITY_OC2007 ||
                 agent->compatibility == NICE_COMPATIBILITY_OC2007R2) {
               /* These data are needed on TURN socket when sending requests,
@@ -2744,6 +3029,9 @@ static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *
             } else {
               priv_add_new_turn_refresh (d, relay_cand, lifetime);
             }
+
+            /* In case a new candidate has been added */
+            conn_check_schedule_next (agent);
           }
 
           d->stun_message.buffer = NULL;
@@ -2889,7 +3177,7 @@ static gboolean priv_map_reply_to_relay_refresh (NiceAgent *agent, StunMessage *
 
 
 static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
-    Component *component, StunMessage *resp)
+    NiceComponent *component, StunMessage *resp)
 {
   StunTransactionId conncheck_id;
   StunTransactionId response_id;
@@ -2917,8 +3205,8 @@ static gboolean priv_map_reply_to_keepalive_conncheck (NiceAgent *agent,
 
 typedef struct {
   NiceAgent *agent;
-  Stream *stream;
-  Component *component;
+  NiceStream *stream;
+  NiceComponent *component;
   uint8_t *password;
 } conncheck_validater_data;
 
@@ -2957,7 +3245,7 @@ static bool conncheck_stun_validater (StunAgent *agent,
     if (ufrag == NULL)
       continue;
 
-    stun_debug ("Comparing username/ufrag of len %d and %zu, equal=%d",
+    stun_debug ("Comparing username/ufrag of len %d and %" G_GSIZE_FORMAT ", equal=%d",
         username_len, ufrag_len, username_len >= ufrag_len ?
         memcmp (username, ufrag, ufrag_len) : 0);
     stun_debug_bytes ("  username: ", username, username_len);
@@ -3014,8 +3302,8 @@ static bool conncheck_stun_validater (StunAgent *agent,
  *
  * @return XXX (what FALSE means exactly?)
  */
-gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
-    Component *component, NiceSocket *nicesock, const NiceAddress *from,
+gboolean conn_check_handle_inbound_stun (NiceAgent *agent, NiceStream *stream,
+    NiceComponent *component, NiceSocket *nicesock, const NiceAddress *from,
     gchar *buf, guint len)
 {
   union {
@@ -3080,9 +3368,11 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
       valid == STUN_VALIDATION_UNMATCHED_RESPONSE) {
     for (i = agent->refresh_list; i; i = i->next) {
       CandidateRefresh *r = i->data;
-      nice_debug ("Comparing %p to %p, %p to %p and %p and %p to %p", r->stream,
-          stream, r->component, component, r->nicesock, r->candidate->sockptr,
-          nicesock);
+
+      nice_debug_verbose ("Comparing %p to %p, %p to %p and %p and %p to %p",
+          r->stream, stream, r->component, component, r->nicesock,
+          r->candidate->sockptr, nicesock);
+
       if (r->stream == stream && r->component == component &&
           (r->nicesock == nicesock || r->candidate->sockptr == nicesock)) {
         valid = stun_agent_validate (&r->stun_agent, &req,
@@ -3294,16 +3584,22 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
             remote_candidate2 ? remote_candidate2 : remote_candidate);
         if(remote_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, remote_candidate, NICE_CHECK_DISCOVERED);
-          else
+              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);
+            if (pair) {
+              pair->valid = TRUE;
+            }
+          } else
             conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
         }
       }
 
-      priv_reply_to_conn_check (agent, stream, component, remote_candidate,
-          from, nicesock, rbuf_len, rbuf, use_candidate);
+      priv_reply_to_conn_check (agent, stream, component, local_candidate,
+          remote_candidate, from, nicesock, rbuf_len, rbuf, use_candidate);
 
       if (component->remote_candidates == NULL) {
         /* case: We've got a valid binding request to a local candidate
@@ -3360,7 +3656,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
 /* Remove all pointers to the given @sock from the connection checking process.
  * These are entirely NiceCandidates pointed to from various places. */
 void
-conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component,
+conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component,
     NiceSocket *sock)
 {
   GSList *l;
@@ -3375,14 +3671,20 @@ conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component,
   }
 
   /* Prune from the candidate check pairs. */
-  for (l = stream->conncheck_list; l != NULL; l = l->next) {
+  for (l = stream->conncheck_list; l != NULL;) {
     CandidateCheckPair *p = l->data;
+    GSList *next = l->next;
 
     if ((p->local != NULL && p->local->sockptr == sock) ||
-        (p->remote != NULL && p->remote->sockptr == sock)) {
+        (p->remote != NULL && p->remote->sockptr == sock) ||
+        (p->sockptr == sock)) {
       nice_debug ("Agent %p : Retransmissions failed, giving up on "
           "connectivity check %p", agent, p);
       candidate_check_pair_fail (stream, agent, p);
+      conn_check_free_item (p);
+      stream->conncheck_list = g_slist_delete_link (stream->conncheck_list, l);
     }
+
+    l = next;
   }
 }
diff --git a/agent/conncheck.h b/agent/conncheck.h
index e6c2c62..431c606 100644
--- a/agent/conncheck.h
+++ b/agent/conncheck.h
@@ -87,26 +87,30 @@ struct _CandidateCheckPair
   gboolean nominated;
   gboolean controlling;
   gboolean timer_restarted;
+  gboolean valid;
   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;
 };
 
-int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote);
-int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local);
-gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote);
+int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *remote);
+int conn_check_add_for_local_candidate (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local);
+gboolean conn_check_add_for_candidate_pair (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *local, NiceCandidate *remote);
 void conn_check_free (NiceAgent *agent);
-gboolean conn_check_schedule_next (NiceAgent *agent);
+void conn_check_schedule_next (NiceAgent *agent);
 int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair);
-void conn_check_prune_stream (NiceAgent *agent, Stream *stream);
-gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, NiceSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len);
+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);
+void conn_check_remote_candidates_set(NiceAgent *agent, NiceStream *stream, NiceComponent *component);
 NiceCandidateTransport conn_check_match_transport (NiceCandidateTransport transport);
 void
-conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component,
+conn_check_prune_socket (NiceAgent *agent, NiceStream *stream, NiceComponent *component,
     NiceSocket *sock);
 
+guint32 ensure_unique_priority (NiceComponent *component, guint32 priority);
+
 #endif /*_NICE_CONNCHECK_H */
diff --git a/agent/debug.c b/agent/debug.c
index 6e69f9b..e1a298c 100644
--- a/agent/debug.c
+++ b/agent/debug.c
@@ -48,17 +48,29 @@
 #include "agent-priv.h"
 
 static int debug_enabled = 0;
+static int debug_verbose_enabled = 0;
 
 #define NICE_DEBUG_STUN 1
 #define NICE_DEBUG_NICE 2
 #define NICE_DEBUG_PSEUDOTCP 4
 #define NICE_DEBUG_PSEUDOTCP_VERBOSE 8
+#define NICE_DEBUG_NICE_VERBOSE 16
 
 static const GDebugKey keys[] = {
   { (gchar *)"stun",  NICE_DEBUG_STUN },
   { (gchar *)"nice",  NICE_DEBUG_NICE },
   { (gchar *)"pseudotcp",  NICE_DEBUG_PSEUDOTCP },
   { (gchar *)"pseudotcp-verbose",  NICE_DEBUG_PSEUDOTCP_VERBOSE },
+  { (gchar *)"nice-verbose",  NICE_DEBUG_NICE_VERBOSE },
+  { NULL, 0},
+};
+
+static const GDebugKey gkeys[] = {
+  { (gchar *)"libnice-stun",  NICE_DEBUG_STUN },
+  { (gchar *)"libnice",  NICE_DEBUG_NICE },
+  { (gchar *)"libnice-pseudotcp",  NICE_DEBUG_PSEUDOTCP },
+  { (gchar *)"libnice-pseudotcp-verbose",  NICE_DEBUG_PSEUDOTCP_VERBOSE },
+  { (gchar *)"libnice-verbose",  NICE_DEBUG_NICE_VERBOSE },
   { NULL, 0},
 };
 
@@ -86,18 +98,30 @@ void nice_debug_init (void)
 
     if (flags_string)
       flags = g_parse_debug_string (flags_string, keys,  4);
+    if (gflags_string)
+      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")) {
+      flags |= NICE_DEBUG_NICE_VERBOSE;
+    }
 
     stun_set_debug_handler (stun_handler);
-    nice_debug_enable (TRUE);
+    debug_enabled = !!(flags & NICE_DEBUG_NICE);
+    if (flags & NICE_DEBUG_STUN)
+      stun_debug_enable ();
+    else
+      stun_debug_disable ();
+
+    if (flags & NICE_DEBUG_NICE_VERBOSE)
+      debug_verbose_enabled = TRUE;
 
     /* Set verbose before normal so that if we use 'all', then only
        normal debug is enabled, we'd need to set pseudotcp-verbose without the
        pseudotcp flag in order to actually enable verbose pseudotcp */
     if (flags & NICE_DEBUG_PSEUDOTCP_VERBOSE)
       pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE);
-    else
+    else if (flags & NICE_DEBUG_PSEUDOTCP)
       pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_NORMAL);
   }
 }
@@ -107,6 +131,10 @@ gboolean nice_debug_is_enabled (void)
 {
   return debug_enabled;
 }
+gboolean nice_debug_is_verbose (void)
+{
+  return debug_verbose_enabled;
+}
 #else
 /* Defined in agent-priv.h. */
 #endif
@@ -136,6 +164,15 @@ void nice_debug (const char *fmt, ...)
     va_end (ap);
   }
 }
+void nice_debug_verbose (const char *fmt, ...)
+{
+  va_list ap;
+  if (debug_verbose_enabled) {
+    va_start (ap, fmt);
+    g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, fmt, ap);
+    va_end (ap);
+  }
+}
 #else
 /* Defined in agent-priv.h. */
 #endif
diff --git a/agent/discovery.c b/agent/discovery.c
index f3a702d..7a890a0 100644
--- a/agent/discovery.c
+++ b/agent/discovery.c
@@ -305,7 +305,7 @@ void refresh_cancel (CandidateRefresh *refresh)
  * defined in ICE spec section 4.1.3 "Eliminating Redundant
  * Candidates" (ID-19).
  */
-static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *candidate)
+static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_id, NiceComponent *component, NiceCandidate *candidate)
 {
   GSList *i;
 
@@ -329,7 +329,7 @@ static gboolean priv_add_local_candidate_pruned (NiceAgent *agent, guint stream_
   return TRUE;
 }
 
-static guint priv_highest_remote_foundation (Component *component)
+static guint priv_highest_remote_foundation (NiceComponent *component)
 {
   GSList *i;
   guint highest = 1;
@@ -382,9 +382,9 @@ static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
   GSList *i, *j, *k;
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     for (j = stream->components; j; j = j->next) {
-      Component *component = j->data;
+      NiceComponent *component = j->data;
       for (k = component->local_candidates; k; k = k->next) {
 	NiceCandidate *n = k->data;
 
@@ -393,7 +393,6 @@ static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
 
 	if (candidate->type == n->type &&
             candidate->transport == n->transport &&
-            candidate->stream_id == n->stream_id &&
 	    nice_address_equal_no_port (&candidate->base_addr, &n->base_addr) &&
             (candidate->type != NICE_CANDIDATE_TYPE_RELAYED ||
                 priv_compare_turn_servers (candidate->turn, n->turn)) &&
@@ -427,12 +426,12 @@ static void priv_assign_remote_foundation (NiceAgent *agent, NiceCandidate *cand
 {
   GSList *i, *j, *k;
   guint next_remote_id;
-  Component *component = NULL;
+  NiceComponent *component = NULL;
 
   for (i = agent->streams; i; i = i->next) {
-    Stream *stream = i->data;
+    NiceStream *stream = i->data;
     for (j = stream->components; j; j = j->next) {
-      Component *c = j->data;
+      NiceComponent *c = j->data;
 
       if (c->id == candidate->component_id)
         component = c;
@@ -523,8 +522,8 @@ HostCandidateResult discovery_add_local_host_candidate (
   NiceCandidate **outcandidate)
 {
   NiceCandidate *candidate;
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   NiceSocket *nicesock = NULL;
   HostCandidateResult res = HOST_CANDIDATE_FAILED;
 
@@ -550,6 +549,8 @@ HostCandidateResult discovery_add_local_host_candidate (
         agent->reliable, FALSE);
   }
 
+  candidate->priority = ensure_unique_priority (component,
+      candidate->priority);
   priv_generate_candidate_credentials (agent, candidate);
   priv_assign_foundation (agent, candidate);
 
@@ -580,7 +581,7 @@ HostCandidateResult discovery_add_local_host_candidate (
   }
 
   _priv_set_socket_tos (agent, nicesock, stream->tos);
-  component_attach_socket (component, nicesock);
+  nice_component_attach_socket (component, nicesock);
 
   *outcandidate = candidate;
 
@@ -610,8 +611,8 @@ discovery_add_server_reflexive_candidate (
   gboolean nat_assisted)
 {
   NiceCandidate *candidate;
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   gboolean result = FALSE;
 
   if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
@@ -623,6 +624,10 @@ discovery_add_server_reflexive_candidate (
   candidate->component_id = component_id;
   candidate->addr = *address;
 
+  /* step: link to the base candidate+socket */
+  candidate->sockptr = base_socket;
+  candidate->base_addr = base_socket->addr;
+
   if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
     candidate->priority = nice_candidate_jingle_priority (candidate);
   } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
@@ -636,10 +641,8 @@ discovery_add_server_reflexive_candidate (
         agent->reliable, nat_assisted);
   }
 
-  /* step: link to the base candidate+socket */
-  candidate->sockptr = base_socket;
-  candidate->base_addr = base_socket->addr;
-
+  candidate->priority = ensure_unique_priority (component,
+      candidate->priority);
   priv_generate_candidate_credentials (agent, candidate);
   priv_assign_foundation (agent, candidate);
 
@@ -670,8 +673,8 @@ discovery_discover_tcp_server_reflexive_candidates (
   NiceAddress *address,
   NiceSocket *base_socket)
 {
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   NiceAddress base_addr = base_socket->addr;
   GSList *i;
 
@@ -718,8 +721,8 @@ discovery_add_relay_candidate (
   TurnServer *turn)
 {
   NiceCandidate *candidate;
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   NiceSocket *relay_socket = NULL;
 
   if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
@@ -732,6 +735,17 @@ discovery_add_relay_candidate (
   candidate->addr = *address;
   candidate->turn = turn_server_ref (turn);
 
+  /* step: link to the base candidate+socket */
+  relay_socket = nice_udp_turn_socket_new (agent->main_context, address,
+      base_socket, &turn->server,
+      turn->username, turn->password,
+      agent_to_turn_socket_compatibility (agent));
+  if (!relay_socket)
+    goto errors;
+
+  candidate->sockptr = relay_socket;
+  candidate->base_addr = base_socket->addr;
+
   if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
     candidate->priority = nice_candidate_jingle_priority (candidate);
   } else if (agent->compatibility == NICE_COMPATIBILITY_MSN ||
@@ -745,17 +759,8 @@ discovery_add_relay_candidate (
         agent->reliable, FALSE);
   }
 
-  /* step: link to the base candidate+socket */
-  relay_socket = nice_udp_turn_socket_new (agent->main_context, address,
-      base_socket, &turn->server,
-      turn->username, turn->password,
-      agent_to_turn_socket_compatibility (agent));
-  if (!relay_socket)
-    goto errors;
-
-  candidate->sockptr = relay_socket;
-  candidate->base_addr = base_socket->addr;
-
+  candidate->priority = ensure_unique_priority (component,
+      candidate->priority);
   priv_generate_candidate_credentials (agent, candidate);
 
   /* Google uses the turn username as the candidate username */
@@ -769,7 +774,7 @@ discovery_add_relay_candidate (
   if (!priv_add_local_candidate_pruned (agent, stream_id, component, candidate))
     goto errors;
 
-  component_attach_socket (component, relay_socket);
+  nice_component_attach_socket (component, relay_socket);
   agent_signal_new_candidate (agent, candidate);
 
   return candidate;
@@ -798,8 +803,8 @@ discovery_add_peer_reflexive_candidate (
   NiceCandidate *remote)
 {
   NiceCandidate *candidate;
-  Component *component;
-  Stream *stream;
+  NiceComponent *component;
+  NiceStream *stream;
   gboolean result;
 
   if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
@@ -836,6 +841,8 @@ discovery_add_peer_reflexive_candidate (
         agent->reliable, FALSE);
   }
 
+  candidate->priority = ensure_unique_priority (component,
+      candidate->priority);
   priv_assign_foundation (agent, candidate);
 
   if ((agent->compatibility == NICE_COMPATIBILITY_MSN ||
@@ -891,8 +898,8 @@ discovery_add_peer_reflexive_candidate (
  */
 NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
   NiceAgent *agent,
-  Stream *stream,
-  Component *component,
+  NiceStream *stream,
+  NiceComponent *component,
   guint32 priority,
   const NiceAddress *remote_address,
   NiceSocket *nicesock,
@@ -1023,10 +1030,12 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer)
           (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
               cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
 
-	agent_signal_component_state_change (agent,
-					     cand->stream->id,
-					     cand->component->id,
-					     NICE_COMPONENT_STATE_GATHERING);
+        if (cand->component->state == NICE_COMPONENT_STATE_DISCONNECTED ||
+            cand->component->state == NICE_COMPONENT_STATE_FAILED)
+          agent_signal_component_state_change (agent,
+					       cand->stream->id,
+					       cand->component->id,
+					       NICE_COMPONENT_STATE_GATHERING);
 
         if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
           buffer_len = stun_usage_bind_create (&cand->stun_agent,
diff --git a/agent/discovery.h b/agent/discovery.h
index c22ea6a..67e2186 100644
--- a/agent/discovery.h
+++ b/agent/discovery.h
@@ -53,8 +53,8 @@ typedef struct
   GTimeVal next_tick;       /* next tick timestamp */
   gboolean pending;         /* is discovery in progress? */
   gboolean done;            /* is discovery complete? */
-  Stream *stream;
-  Component *component;
+  NiceStream *stream;
+  NiceComponent *component;
   TurnServer *turn;
   StunAgent stun_agent;
   StunTimer timer;
@@ -70,8 +70,8 @@ typedef struct
   NiceSocket *nicesock;     /* existing socket to use */
   NiceAddress server;       /* STUN/TURN server address */
   NiceCandidate *candidate; /* candidate to refresh */
-  Stream *stream;
-  Component *component;
+  NiceStream *stream;
+  NiceComponent *component;
   StunAgent stun_agent;
   GSource *timer_source;
   GSource *tick_source;
@@ -151,8 +151,8 @@ discovery_add_peer_reflexive_candidate (
 NiceCandidate *
 discovery_learn_remote_peer_reflexive_candidate (
   NiceAgent *agent,
-  Stream *stream,
-  Component *component,
+  NiceStream *stream,
+  NiceComponent *component,
   guint32 priority, 
   const NiceAddress *remote_address,
   NiceSocket *udp_socket,
diff --git a/agent/inputstream.c b/agent/inputstream.c
index b9c5369..58a4a0d 100644
--- a/agent/inputstream.c
+++ b/agent/inputstream.c
@@ -332,8 +332,8 @@ nice_input_stream_close (GInputStream *stream, GCancellable *cancellable,
     GError **error)
 {
   NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv;
-  Component *component = NULL;
-  Stream *_stream = NULL;
+  NiceComponent *component = NULL;
+  NiceStream *_stream = NULL;
   NiceAgent *agent;  /* owned */
 
   /* Has the agent disappeared? */
@@ -361,8 +361,8 @@ static gboolean
 nice_input_stream_is_readable (GPollableInputStream *stream)
 {
   NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv;
-  Component *component = NULL;
-  Stream *_stream = NULL;
+  NiceComponent *component = NULL;
+  NiceStream *_stream = NULL;
   gboolean retval = FALSE;
   GSList *i;
   NiceAgent *agent;  /* owned */
@@ -458,7 +458,7 @@ nice_input_stream_create_source (GPollableInputStream *stream,
   if (agent == NULL)
     goto dummy_source;
 
-  component_source = component_input_source_new (agent, priv->stream_id,
+  component_source = nice_component_input_source_new (agent, priv->stream_id,
       priv->component_id, stream, cancellable);
 
   g_object_unref (agent);
diff --git a/agent/outputstream.c b/agent/outputstream.c
index d479aa5..4c918a7 100644
--- a/agent/outputstream.c
+++ b/agent/outputstream.c
@@ -476,8 +476,8 @@ nice_output_stream_close (GOutputStream *stream, GCancellable *cancellable,
     GError **error)
 {
   NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
-  Component *component = NULL;
-  Stream *_stream = NULL;
+  NiceComponent *component = NULL;
+  NiceStream *_stream = NULL;
   NiceAgent *agent;  /* owned */
 
   /* Has the agent disappeared? */
@@ -505,8 +505,8 @@ static gboolean
 nice_output_stream_is_writable (GPollableOutputStream *stream)
 {
   NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
-  Component *component = NULL;
-  Stream *_stream = NULL;
+  NiceComponent *component = NULL;
+  NiceStream *_stream = NULL;
   gboolean retval = FALSE;
   NiceAgent *agent;  /* owned */
 
@@ -595,8 +595,8 @@ nice_output_stream_create_source (GPollableOutputStream *stream,
 {
   NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv;
   GSource *component_source = NULL;
-  Component *component = NULL;
-  Stream *_stream = NULL;
+  NiceComponent *component = NULL;
+  NiceStream *_stream = NULL;
   NiceAgent *agent;  /* owned */
 
   component_source = g_pollable_source_new (G_OBJECT (stream));
diff --git a/agent/pseudotcp.c b/agent/pseudotcp.c
index eb91e3c..3160c34 100644
--- a/agent/pseudotcp.c
+++ b/agent/pseudotcp.c
@@ -77,8 +77,19 @@
 #include "pseudotcp.h"
 #include "agent-priv.h"
 
-G_DEFINE_TYPE (PseudoTcpSocket, pseudo_tcp_socket, G_TYPE_OBJECT);
+struct _PseudoTcpSocketClass {
+    GObjectClass parent_class;
+};
+
+typedef struct _PseudoTcpSocketPrivate PseudoTcpSocketPrivate;
+
+
+struct _PseudoTcpSocket {
+    GObject parent;
+    PseudoTcpSocketPrivate *priv;
+};
 
+G_DEFINE_TYPE (PseudoTcpSocket, pseudo_tcp_socket, G_TYPE_OBJECT);
 
 //////////////////////////////////////////////////////////////////////
 // Network Constants
@@ -107,7 +118,9 @@ const guint16 PACKET_MAXIMUMS[] = {
   0,      // End of list marker
 };
 
-#define MAX_PACKET 65535
+// FIXME: This is a reasonable MTU, but we should get it from the lower layer
+#define DEF_MTU 1400
+#define MAX_PACKET 65532
 // Note: we removed lowest level because packet overhead was larger!
 #define MIN_PACKET 296
 
@@ -151,8 +164,8 @@ const guint16 PACKET_MAXIMUMS[] = {
 #define PACKET_OVERHEAD (HEADER_SIZE + UDP_HEADER_SIZE + \
       IP_HEADER_SIZE + JINGLE_HEADER_SIZE)
 
-// MIN_RTO = 250 ms (RFC1122, Sec 4.2.3.1 "fractions of a second")
-#define MIN_RTO      250
+// MIN_RTO = 1 second (RFC6298, Sec 2.4)
+#define MIN_RTO     1000
 #define DEF_RTO     1000 /* 1 seconds (RFC 6298 sect 2.1) */
 #define MAX_RTO    60000 /* 60 seconds */
 #define DEFAULT_ACK_DELAY    100 /* 100 milliseconds */
@@ -416,6 +429,7 @@ typedef enum {
   sfImmediateAck,
   sfFin,
   sfRst,
+  sfDuplicateAck,
 } SendFlags;
 
 typedef struct {
@@ -471,6 +485,7 @@ struct _PseudoTcpSocketPrivate {
   guint32 rbuf_len, rcv_nxt, rcv_wnd, lastrecv;
   guint8 rwnd_scale; // Window scale factor
   PseudoTcpFifo rbuf;
+  guint32 rcv_fin;  /* sequence number of the received FIN octet, or 0 */
 
   // Outgoing data
   GQueue slist;
@@ -495,7 +510,9 @@ struct _PseudoTcpSocketPrivate {
   guint32 ssthresh, cwnd;
   guint8 dup_acks;
   guint32 recover;
+  gboolean fast_recovery;
   guint32 t_ack;  /* time a delayed ack was scheduled; 0 if no acks scheduled */
+  guint32 last_acked_ts;
 
   gboolean use_nagling;
   guint32 ack_delay;
@@ -550,7 +567,7 @@ static gboolean parse (PseudoTcpSocket *self,
     const guint8 *_header_buf, gsize header_buf_len,
     const guint8 *data_buf, gsize data_buf_len);
 static gboolean process(PseudoTcpSocket *self, Segment *seg);
-static gboolean transmit(PseudoTcpSocket *self, SSegment *sseg, guint32 now);
+static int transmit(PseudoTcpSocket *self, SSegment *sseg, guint32 now);
 static void attempt_send(PseudoTcpSocket *self, SendFlags sflags);
 static void closedown (PseudoTcpSocket *self, guint32 err,
     ClosedownSource source);
@@ -566,6 +583,7 @@ static void set_state_closed (PseudoTcpSocket *self, guint32 err);
 static const gchar *pseudo_tcp_state_get_name (PseudoTcpState state);
 static gboolean pseudo_tcp_state_has_sent_fin (PseudoTcpState state);
 static gboolean pseudo_tcp_state_has_received_fin (PseudoTcpState state);
+static gboolean pseudo_tcp_state_has_received_fin_ack (PseudoTcpState state);
 
 // The following logging is for detailed (packet-level) pseudotcp analysis only.
 static PseudoTcpDebugLevel debug_level = PSEUDO_TCP_DEBUG_NONE;
@@ -809,12 +827,14 @@ pseudo_tcp_socket_init (PseudoTcpSocket *obj)
   priv->snd_una = priv->rcv_nxt = 0;
   priv->bReadEnable = TRUE;
   priv->bWriteEnable = FALSE;
+  priv->rcv_fin = 0;
+
   priv->t_ack = 0;
 
   priv->msslevel = 0;
   priv->largest = 0;
   priv->mss = MIN_PACKET - PACKET_OVERHEAD;
-  priv->mtu_advise = MAX_PACKET;
+  priv->mtu_advise = DEF_MTU;
 
   priv->rto_base = 0;
 
@@ -825,6 +845,7 @@ pseudo_tcp_socket_init (PseudoTcpSocket *obj)
 
   priv->dup_acks = 0;
   priv->recover = 0;
+  priv->last_acked_ts = 0;
 
   priv->ts_recent = priv->ts_lastack = 0;
 
@@ -959,18 +980,24 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self)
       // retransmit segments
       guint32 nInFlight;
       guint32 rto_limit;
+      int transmit_status;
 
       DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "timeout retransmit (rto: %u) "
           "(rto_base: %u) (now: %u) (dup_acks: %u)",
           priv->rx_rto, priv->rto_base, now, (guint) priv->dup_acks);
 
-      if (!transmit(self, g_queue_peek_head (&priv->slist), now)) {
-        closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL);
+      transmit_status = transmit(self, g_queue_peek_head (&priv->slist), now);
+      if (transmit_status != 0) {
+        DEBUG (PSEUDO_TCP_DEBUG_NORMAL,
+            "Error transmitting segment. Closing down.");
+        closedown (self, transmit_status, CLOSEDOWN_LOCAL);
         return;
       }
 
       nInFlight = priv->snd_nxt - priv->snd_una;
       priv->ssthresh = max(nInFlight / 2, 2 * priv->mss);
+      DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "ssthresh: %u = (nInFlight: %u / 2) + "
+          "2 * mss: %u", priv->ssthresh, nInFlight, priv->mss);
       //LOG(LS_INFO) << "priv->ssthresh: " << priv->ssthresh << "  nInFlight: " << nInFlight << "  priv->mss: " << priv->mss;
       priv->cwnd = priv->mss;
 
@@ -978,6 +1005,13 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self)
       rto_limit = (priv->state < TCP_ESTABLISHED) ? DEF_RTO : MAX_RTO;
       priv->rx_rto = min(rto_limit, priv->rx_rto * 2);
       priv->rto_base = now;
+
+      priv->recover = priv->snd_nxt;
+      if (priv->dup_acks >= 3) {
+        priv->dup_acks = 0;
+        priv->fast_recovery = FALSE;
+        DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery on timeout");
+      }
     }
   }
 
@@ -985,6 +1019,7 @@ pseudo_tcp_socket_notify_clock(PseudoTcpSocket *self)
   if ((priv->snd_wnd == 0)
         && (time_diff(priv->lastsend + priv->rx_rto, now) <= 0)) {
     if (time_diff(now, priv->lastrecv) >= 15000) {
+      DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Receive window closed. Closing down.");
       closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL);
       return;
     }
@@ -1012,9 +1047,11 @@ pseudo_tcp_socket_notify_packet(PseudoTcpSocket *self,
 
   if (len > MAX_PACKET) {
     //LOG_F(WARNING) << "packet too large";
+    self->priv->error = EMSGSIZE;
     return FALSE;
   } else if (len < HEADER_SIZE) {
     //LOG_F(WARNING) << "packet too small";
+    self->priv->error = EINVAL;
     return FALSE;
   }
 
@@ -1149,9 +1186,7 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len)
   gsize available_space;
 
   /* Received a FIN from the peer, so return 0. RFC 793, §3.5, Case 2. */
-  if (priv->support_fin_ack &&
-      (priv->shutdown_reads ||
-       pseudo_tcp_state_has_received_fin (priv->state))) {
+  if (priv->support_fin_ack && priv->shutdown_reads) {
     return 0;
   }
 
@@ -1173,7 +1208,9 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len)
   bytesread = pseudo_tcp_fifo_read (&priv->rbuf, (guint8 *) buffer, len);
 
  // If there's no data in |m_rbuf|.
-  if (bytesread == 0) {
+  if (bytesread == 0 &&
+      !(pseudo_tcp_state_has_received_fin (priv->state) ||
+        pseudo_tcp_state_has_received_fin_ack (priv->state))) {
     priv->bReadEnable = TRUE;
     priv->error = EWOULDBLOCK;
     return -1;
@@ -1407,7 +1444,7 @@ packet(PseudoTcpSocket *self, guint32 seq, TcpFlags flags,
     g_assert (bytes_read == len);
   }
 
-  DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "<-- <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>"
+  DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Sending <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>"
       "<WND=%u><TS=%u><TSR=%u><LEN=%u>",
       priv->conv, (unsigned)flags, seq, seq + len, priv->rcv_nxt, priv->rcv_wnd,
       now % 10000, priv->ts_recent % 10000, len);
@@ -1460,7 +1497,8 @@ parse (PseudoTcpSocket *self, const guint8 *_header_buf, gsize header_buf_len,
   seg.data = (const gchar *) data_buf;
   seg.len = data_buf_len;
 
-  DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "--> <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>"
+  DEBUG (PSEUDO_TCP_DEBUG_VERBOSE,
+      "Received <CONV=%u><FLG=%u><SEQ=%u:%u><ACK=%u>"
       "<WND=%u><TS=%u><TSR=%u><LEN=%u>",
       seg.conv, (unsigned)seg.flags, seg.seq, seg.seq + seg.len, seg.ack,
       seg.wnd, seg.tsval % 10000, seg.tsecr % 10000, seg.len);
@@ -1516,6 +1554,30 @@ pseudo_tcp_state_has_received_fin (PseudoTcpState state)
   }
 }
 
+/* True iff the @state requires that a FIN-ACK has already been received from
+ * the peer. */
+static gboolean
+pseudo_tcp_state_has_received_fin_ack (PseudoTcpState state)
+{
+  switch (state) {
+  case TCP_LISTEN:
+  case TCP_SYN_SENT:
+  case TCP_SYN_RECEIVED:
+  case TCP_ESTABLISHED:
+  case TCP_FIN_WAIT_1:
+  case TCP_FIN_WAIT_2:
+  case TCP_CLOSING:
+  case TCP_CLOSE_WAIT:
+  case TCP_LAST_ACK:
+    return FALSE;
+  case TCP_CLOSED:
+  case TCP_TIME_WAIT:
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
 static gboolean
 process(PseudoTcpSocket *self, Segment *seg)
 {
@@ -1529,6 +1591,7 @@ process(PseudoTcpSocket *self, Segment *seg)
   gsize available_space;
   guint32 kIdealRefillSize;
   gboolean is_valuable_ack, is_duplicate_ack, is_fin_ack = FALSE;
+  gboolean received_fin = FALSE;
 
   /* If this is the wrong conversation, send a reset!?!
      (with the correct conversation?) */
@@ -1545,17 +1608,23 @@ process(PseudoTcpSocket *self, Segment *seg)
   priv->bOutgoing = FALSE;
 
   if (priv->state == TCP_CLOSED ||
-      (pseudo_tcp_state_has_sent_fin (priv->state) && seg->len > 0)) {
-    /* Send an RST segment. See: RFC 1122, §4.2.2.13. */
+      (pseudo_tcp_state_has_received_fin_ack (priv->state) && seg->len > 0)) {
+    /* Send an RST segment. See: RFC 1122, §4.2.2.13; RFC 793, §3.4, point 3,
+     * page 37. We can only send RST if we know the peer knows we’re closed;
+     * otherwise this could be a timeout retransmit from them, due to our
+     * packets from data through to FIN being dropped. */
+    DEBUG (PSEUDO_TCP_DEBUG_NORMAL,
+        "Segment received while closed; sending RST.");
     if ((seg->flags & FLAG_RST) == 0) {
       closedown (self, 0, CLOSEDOWN_LOCAL);
     }
-    DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Segment received while closed; sent RST.");
+
     return FALSE;
   }
 
   // Check if this is a reset segment
   if (seg->flags & FLAG_RST) {
+    DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Received RST segment; closing down.");
     closedown (self, ECONNRESET, CLOSEDOWN_REMOTE);
     return FALSE;
   }
@@ -1607,18 +1676,20 @@ process(PseudoTcpSocket *self, Segment *seg)
           priv->rx_rttvar = rtt / 2;
         } else {
           priv->rx_rttvar = (3 * priv->rx_rttvar +
-              abs((long)(rtt - priv->rx_srtt))) / 4;
+              labs((long)(rtt - priv->rx_srtt))) / 4;
           priv->rx_srtt = (7 * priv->rx_srtt + rtt) / 8;
         }
         priv->rx_rto = bound(MIN_RTO,
             priv->rx_srtt + max(1LU, 4 * priv->rx_rttvar), MAX_RTO);
 
-        DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "rtt: %ld   srtt: %u  rto: %u",
-                rtt, priv->rx_srtt, priv->rx_rto);
+        DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "rtt: %ld srtt: %u rttvar: %u rto: %u",
+            rtt, priv->rx_srtt, priv->rx_rttvar, priv->rx_rto);
       } else {
         DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Invalid RTT: %ld", rtt);
         return FALSE;
       }
+
+      priv->last_acked_ts = seg->tsecr;
     }
 
     priv->snd_wnd = seg->wnd << priv->swnd_scale;
@@ -1663,16 +1734,24 @@ process(PseudoTcpSocket *self, Segment *seg)
       if (LARGER_OR_EQUAL (priv->snd_una, priv->recover)) { // NewReno
         guint32 nInFlight = priv->snd_nxt - priv->snd_una;
         // (Fast Retransmit)
-        priv->cwnd = min(priv->ssthresh, nInFlight + priv->mss);
-        DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery");
+        priv->cwnd = min(priv->ssthresh,
+            max (nInFlight, priv->mss) + priv->mss);
+        DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "exit recovery cwnd=%d ssthresh=%d nInFlight=%d mss: %d", priv->cwnd, priv->ssthresh, nInFlight, priv->mss);
+        priv->fast_recovery = FALSE;
         priv->dup_acks = 0;
       } else {
+        int transmit_status;
+
         DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit");
-        if (!transmit(self, g_queue_peek_head (&priv->slist), now)) {
-          closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL);
+        transmit_status = transmit(self, g_queue_peek_head (&priv->slist), now);
+        if (transmit_status != 0) {
+          DEBUG (PSEUDO_TCP_DEBUG_NORMAL,
+              "Error transmitting recovery retransmit segment. Closing down.");
+          closedown (self, transmit_status, CLOSEDOWN_LOCAL);
           return FALSE;
         }
-        priv->cwnd += priv->mss - min(nAcked, priv->cwnd);
+        priv->cwnd += (nAcked > priv->mss ? priv->mss : 0) -
+            min(nAcked, priv->cwnd);
       }
     } else {
       priv->dup_acks = 0;
@@ -1695,20 +1774,43 @@ process(PseudoTcpSocket *self, Segment *seg)
       guint32 nInFlight;
 
       priv->dup_acks += 1;
+      DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Received dup ack (dups: %u)",
+          priv->dup_acks);
       if (priv->dup_acks == 3) { // (Fast Retransmit)
-        DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "enter recovery");
-        DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit");
-        if (!transmit(self, g_queue_peek_head (&priv->slist), now)) {
-          closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL);
-          return FALSE;
+        int transmit_status;
+
+
+        if (LARGER_OR_EQUAL (priv->snd_una, priv->recover) ||
+            seg->tsecr == priv->last_acked_ts) { /* NewReno */
+          /* Invoke fast retransmit  RFC3782 section 3 step 1A*/
+          DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "enter recovery");
+          DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "recovery retransmit");
+
+          transmit_status = transmit(self, g_queue_peek_head (&priv->slist),
+              now);
+          if (transmit_status != 0) {
+            DEBUG (PSEUDO_TCP_DEBUG_NORMAL,
+                "Error transmitting recovery retransmit segment. Closing down.");
+
+            closedown (self, transmit_status, CLOSEDOWN_LOCAL);
+            return FALSE;
+          }
+          priv->recover = priv->snd_nxt;
+          nInFlight = priv->snd_nxt - priv->snd_una;
+          priv->ssthresh = max(nInFlight / 2, 2 * priv->mss);
+          DEBUG (PSEUDO_TCP_DEBUG_NORMAL,
+              "ssthresh: %u = max((nInFlight: %u / 2), 2 * mss: %u)",
+              priv->ssthresh, nInFlight, priv->mss);
+          priv->cwnd = priv->ssthresh + 3 * priv->mss;
+          priv->fast_recovery = TRUE;
+        } else {
+          DEBUG (PSEUDO_TCP_DEBUG_VERBOSE,
+              "Skipping fast recovery: recover: %u snd_una: %u", priv->recover,
+              priv->snd_una);
         }
-        priv->recover = priv->snd_nxt;
-        nInFlight = priv->snd_nxt - priv->snd_una;
-        priv->ssthresh = max(nInFlight / 2, 2 * priv->mss);
-        //LOG(LS_INFO) << "priv->ssthresh: " << priv->ssthresh << "  nInFlight: " << nInFlight << "  priv->mss: " << priv->mss;
-        priv->cwnd = priv->ssthresh + 3 * priv->mss;
       } else if (priv->dup_acks > 3) {
-        priv->cwnd += priv->mss;
+        if (priv->fast_recovery)
+          priv->cwnd += priv->mss;
       }
     } else {
       priv->dup_acks = 0;
@@ -1720,19 +1822,34 @@ process(PseudoTcpSocket *self, Segment *seg)
     set_state_established (self);
   }
 
-  /* Check for connection closure. */
+  /* Check for connection closure. Only pay attention to FIN segments if they
+   * are in sequence; otherwise we’ve missed a packet earlier in the stream and
+   * need to request retransmission first. */
   if (priv->support_fin_ack) {
+    /* @received_fin is set when, and only when, all segments preceding the FIN
+     * have been acknowledged. This is to handle the case where the FIN arrives
+     * out of order with a preceding data segment. */
+    if (seg->flags & FLAG_FIN && priv->rcv_fin == 0) {
+      priv->rcv_fin = seg->seq;
+      DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Setting rcv_fin = %u", priv->rcv_fin);
+    } else if (seg->flags & FLAG_FIN && seg->seq != priv->rcv_fin) {
+      DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Second FIN segment received; ignored");
+      return FALSE;
+    }
+
     /* For the moment, FIN segments must not contain data. */
     if (seg->flags & FLAG_FIN && seg->len != 0) {
       DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "FIN segment contained data; ignored");
       return FALSE;
     }
 
+    received_fin = (priv->rcv_nxt != 0 && priv->rcv_nxt + seg->len == priv->rcv_fin);
+
     /* Update the state machine, implementing all transitions on ‘rcv FIN’ or
      * ‘rcv ACK of FIN’ from RFC 793, Figure 6; and RFC 1122, §4.2.2.8. */
     switch (priv->state) {
     case TCP_ESTABLISHED:
-      if (seg->flags & FLAG_FIN) {
+      if (received_fin) {
         /* Received a FIN from the network, RFC 793, §3.5, Case 2.
          * The code below will send an ACK for the FIN. */
          set_state (self, TCP_CLOSE_WAIT);
@@ -1751,20 +1868,20 @@ process(PseudoTcpSocket *self, Segment *seg)
       }
       break;
     case TCP_FIN_WAIT_1:
-      if (is_fin_ack && seg->flags & FLAG_FIN) {
+      if (is_fin_ack && received_fin) {
         /* Simultaneous close with an ACK for a FIN previously sent,
          * RFC 793, §3.5, Case 3. */
         set_state (self, TCP_TIME_WAIT);
       } else if (is_fin_ack) {
         /* Handle the ACK of a locally-sent FIN flag. RFC 793, §3.5, Case 1. */
         set_state (self, TCP_FIN_WAIT_2);
-      } else if (seg->flags & FLAG_FIN) {
+      } else if (received_fin) {
         /* Simultaneous close, RFC 793, §3.5, Case 3. */
         set_state (self, TCP_CLOSING);
       }
       break;
     case TCP_FIN_WAIT_2:
-      if (seg->flags & FLAG_FIN) {
+      if (received_fin) {
         /* Local user closed the connection, RFC 793, §3.5, Case 1. */
         set_state (self, TCP_TIME_WAIT);
       }
@@ -1776,7 +1893,7 @@ process(PseudoTcpSocket *self, Segment *seg)
     case TCP_CLOSED:
     case TCP_CLOSE_WAIT:
       /* Shouldn’t ever hit these cases. */
-      if (seg->flags & FLAG_FIN) {
+      if (received_fin) {
         DEBUG (PSEUDO_TCP_DEBUG_NORMAL,
            "Unexpected state %u when FIN received", priv->state);
       } else if (is_fin_ack) {
@@ -1820,19 +1937,20 @@ process(PseudoTcpSocket *self, Segment *seg)
    *    see RFC 793, §3.3. Also see: RFC 793, §3.5.
    */
   if (seg->seq != priv->rcv_nxt) {
-    sflags = sfImmediateAck; // (Fast Recovery)
+    sflags = sfDuplicateAck; // (Fast Recovery)
   } else if (seg->len != 0) {
     if (priv->ack_delay == 0) {
       sflags = sfImmediateAck;
     } else {
       sflags = sfDelayedAck;
     }
-  } else if (seg->flags & FLAG_FIN) {
+  } else if (received_fin) {
+    /* FIN flags have a sequence number. Only acknowledge them after all
+     * preceding octets have been acknowledged. */
     sflags = sfImmediateAck;
-    priv->rcv_nxt += 1;
   }
 
-  if (sflags == sfImmediateAck) {
+  if (sflags == sfDuplicateAck) {
     if (seg->seq > priv->rcv_nxt) {
       DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "too new");
     } else if (SMALLER_OR_EQUAL(seg->seq + seg->len, priv->rcv_nxt)) {
@@ -1869,12 +1987,7 @@ process(PseudoTcpSocket *self, Segment *seg)
 
   bNewData = FALSE;
 
-  if (seg->flags & FLAG_FIN) {
-    /* FIN flags have a sequence number. */
-    if (seg->seq == priv->rcv_nxt) {
-      priv->rcv_nxt++;
-    }
-  } else if (seg->len > 0) {
+  if (seg->len > 0) {
     if (bIgnoreData) {
       if (seg->seq == priv->rcv_nxt) {
         priv->rcv_nxt += seg->len;
@@ -1929,6 +2042,12 @@ process(PseudoTcpSocket *self, Segment *seg)
     }
   }
 
+  if (received_fin) {
+    /* FIN flags have a sequence number. */
+    priv->rcv_nxt++;
+  }
+
+
   attempt_send(self, sflags);
 
   // If we have new data, notify the user
@@ -1952,7 +2071,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now)
 
   if (segment->xmit >= ((priv->state == TCP_ESTABLISHED) ? 15 : 30)) {
     DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "too many retransmits");
-    return FALSE;
+    return ETIMEDOUT;
   }
 
   while (TRUE) {
@@ -1972,7 +2091,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now)
 
     if (wres == WR_FAIL) {
       DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "packet failed");
-      return FALSE;
+      return ECONNABORTED;  /* FIXME: This error code doesn’t quite seem right */
     }
 
     g_assert(wres == WR_TOO_LARGE);
@@ -1980,7 +2099,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now)
     while (TRUE) {
       if (PACKET_MAXIMUMS[priv->msslevel + 1] == 0) {
         DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "MTU too small");
-        return FALSE;
+        return EMSGSIZE;
       }
       /* !?! We need to break up all outstanding and pending packets
          and then retransmit!?! */
@@ -2029,7 +2148,7 @@ transmit(PseudoTcpSocket *self, SSegment *segment, guint32 now)
     priv->rto_base = now;
   }
 
-  return TRUE;
+  return 0;
 }
 
 static void
@@ -2039,6 +2158,8 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags)
   guint32 now = get_current_time (self);
   gboolean bFirst = TRUE;
 
+  DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Attempting send with flags %u.", sflags);
+
   if (time_diff(now, priv->lastsend) > (long) priv->rx_rto) {
     priv->cwnd = priv->mss;
   }
@@ -2053,6 +2174,7 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags)
     gsize snd_buffered;
     GList *iter;
     SSegment *sseg;
+    int transmit_status;
 
     cwnd = priv->cwnd;
     if ((priv->dup_acks == 1) || (priv->dup_acks == 2)) { // Limited Transmit
@@ -2078,12 +2200,19 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags)
 
     if (bFirst) {
       gsize available_space = pseudo_tcp_fifo_get_write_remaining (&priv->sbuf);
+
       bFirst = FALSE;
       DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "[cwnd: %u  nWindow: %u  nInFlight: %u "
           "nAvailable: %u nQueued: %" G_GSIZE_FORMAT " nEmpty: %" G_GSIZE_FORMAT
-          "  ssthresh: %u]",
+          "  nWaiting: %zu ssthresh: %u]",
           priv->cwnd, nWindow, nInFlight, nAvailable, snd_buffered,
-          available_space, priv->ssthresh);
+          available_space, snd_buffered - nInFlight, priv->ssthresh);
+    }
+
+    if (sflags == sfDuplicateAck) {
+      packet(self, priv->snd_nxt, 0, 0, 0, now);
+      sflags = sfNone;
+      continue;
     }
 
     if (nAvailable == 0 && sflags != sfFin && sflags != sfRst) {
@@ -2091,7 +2220,8 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags)
         return;
 
       // If this is an immediate ack, or the second delayed ack
-      if ((sflags == sfImmediateAck) || priv->t_ack) {
+      if ((sflags == sfImmediateAck || sflags == sfDuplicateAck) ||
+          priv->t_ack) {
         packet(self, priv->snd_nxt, 0, 0, 0, now);
       } else {
         priv->t_ack = now;
@@ -2128,9 +2258,12 @@ attempt_send(PseudoTcpSocket *self, SendFlags sflags)
           subseg);
     }
 
-    if (!transmit(self, sseg, now)) {
+    transmit_status = transmit(self, sseg, now);
+    if (transmit_status != 0) {
       DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "transmit failed");
-      // TODO: consider closing socket
+
+      // TODO: Is this the right thing ?
+      closedown (self, transmit_status, CLOSEDOWN_REMOTE);
       return;
     }
 
@@ -2147,6 +2280,9 @@ closedown (PseudoTcpSocket *self, guint32 err, ClosedownSource source)
 {
   PseudoTcpSocketPrivate *priv = self->priv;
 
+  DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Closing down socket %p with %s error %u.",
+      self, (source == CLOSEDOWN_LOCAL) ? "local" : "remote", err);
+
   if (source == CLOSEDOWN_LOCAL && priv->support_fin_ack) {
     queue_rst_message (self);
     attempt_send (self, sfRst);
@@ -2211,6 +2347,7 @@ apply_window_scale_option (PseudoTcpSocket *self, guint8 scale_factor)
    PseudoTcpSocketPrivate *priv = self->priv;
 
    priv->swnd_scale = scale_factor;
+   DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Setting scale factor to %u", scale_factor);
 }
 
 static void
@@ -2375,10 +2512,6 @@ pseudo_tcp_socket_get_available_bytes (PseudoTcpSocket *self)
 {
   PseudoTcpSocketPrivate *priv = self->priv;
 
-  if (priv->state != TCP_ESTABLISHED) {
-    return -1;
-  }
-
   return pseudo_tcp_fifo_get_buffered (&priv->rbuf);
 }
 
@@ -2394,11 +2527,11 @@ pseudo_tcp_socket_get_available_send_space (PseudoTcpSocket *self)
   PseudoTcpSocketPrivate *priv = self->priv;
   gsize ret;
 
-
-  if (priv->state == TCP_ESTABLISHED)
+  if (!pseudo_tcp_state_has_sent_fin (priv->state)) {
     ret = pseudo_tcp_fifo_get_write_remaining (&priv->sbuf);
-  else
+  } else {
     ret = 0;
+  }
 
   if (ret == 0)
     priv->bWriteEnable = TRUE;
diff --git a/agent/pseudotcp.h b/agent/pseudotcp.h
index 879276e..e7c8eaa 100644
--- a/agent/pseudotcp.h
+++ b/agent/pseudotcp.h
@@ -62,12 +62,24 @@
 #ifndef __GTK_DOC_IGNORE__
 #ifdef G_OS_WIN32
 #  include <winsock2.h>
+
+#ifndef ECONNABORTED
 #  define ECONNABORTED WSAECONNABORTED
+#endif
+
+#ifndef ENOTCONN
 #  define ENOTCONN WSAENOTCONN
+#endif
+
+#ifndef EWOULDBLOCK
 #  define EWOULDBLOCK WSAEWOULDBLOCK
+#endif
+
+#ifndef ECONNRESET
 #  define ECONNRESET WSAECONNRESET
 #endif
 #endif
+#endif
 
 #include "agent.h"
 
@@ -103,17 +115,6 @@ GType pseudo_tcp_socket_get_type (void);
   (G_TYPE_INSTANCE_GET_CLASS ((obj), PSEUDO_TCP_SOCKET_TYPE, \
                               PseudoTcpSocketClass))
 
-struct _PseudoTcpSocketClass {
-    GObjectClass parent_class;
-};
-
-typedef struct _PseudoTcpSocketPrivate PseudoTcpSocketPrivate;
-
-struct _PseudoTcpSocket {
-    GObject parent;
-    PseudoTcpSocketPrivate *priv;
-};
-
 /**
  * PseudoTcpDebugLevel:
  * @PSEUDO_TCP_DEBUG_NONE: Disable debug messages
diff --git a/agent/stream.c b/agent/stream.c
index 09f79b5..8121e12 100644
--- a/agent/stream.c
+++ b/agent/stream.c
@@ -48,63 +48,54 @@
 static volatile unsigned int n_streams_created = 0;
 static volatile unsigned int n_streams_destroyed = 0;
 
+G_DEFINE_TYPE (NiceStream, nice_stream, G_TYPE_OBJECT);
+
+static void
+nice_stream_finalize (GObject *obj);
+
 /*
  * @file stream.c
  * @brief ICE stream functionality
  */
-Stream *
-stream_new (guint n_components, NiceAgent *agent)
+NiceStream *
+nice_stream_new (guint n_components, NiceAgent *agent)
 {
-  Stream *stream;
+  NiceStream *stream = NULL;
   guint n;
-  Component *component;
 
-  g_atomic_int_inc (&n_streams_created);
-  nice_debug ("Created NiceStream (%u created, %u destroyed)",
-      n_streams_created, n_streams_destroyed);
+  stream = g_object_new (NICE_TYPE_STREAM, NULL);
 
-  stream = g_slice_new0 (Stream);
+  /* Create the components. */
   for (n = 0; n < n_components; n++) {
-    component = component_new (n + 1, agent, stream);
+    NiceComponent *component = NULL;
+
+    component = nice_component_new (n + 1, agent, stream);
     stream->components = g_slist_append (stream->components, component);
   }
 
   stream->n_components = n_components;
-  stream->initial_binding_request_received = FALSE;
 
   return stream;
 }
 
 void
-stream_close (Stream *stream)
+nice_stream_close (NiceStream *stream)
 {
   GSList *i;
 
   for (i = stream->components; i; i = i->next) {
-    Component *component = i->data;
-    component_close (component);
+    NiceComponent *component = i->data;
+    nice_component_close (component);
   }
 }
 
-void
-stream_free (Stream *stream)
-{
-  g_free (stream->name);
-  g_slist_free_full (stream->components, (GDestroyNotify) component_free);
-  g_slice_free (Stream, stream);
-
-  g_atomic_int_inc (&n_streams_destroyed);
-  nice_debug ("Destroyed NiceStream (%u created, %u destroyed)",
-      n_streams_created, n_streams_destroyed);
-}
-
-Component *
-stream_find_component_by_id (const Stream *stream, guint id)
+NiceComponent *
+nice_stream_find_component_by_id (NiceStream *stream, guint id)
 {
   GSList *i;
 
   for (i = stream->components; i; i = i->next) {
-    Component *component = i->data;
+    NiceComponent *component = i->data;
     if (component && component->id == id)
       return component;
   }
@@ -117,12 +108,12 @@ stream_find_component_by_id (const Stream *stream, guint id)
  * 'CONNECTED' or 'READY' (connected plus nominated).
  */
 gboolean
-stream_all_components_ready (const Stream *stream)
+nice_stream_all_components_ready (NiceStream *stream)
 {
   GSList *i;
 
   for (i = stream->components; i; i = i->next) {
-    Component *component = i->data;
+    NiceComponent *component = i->data;
     if (component &&
 	!(component->state == NICE_COMPONENT_STATE_CONNECTED ||
 	 component->state == NICE_COMPONENT_STATE_READY))
@@ -136,7 +127,8 @@ stream_all_components_ready (const Stream *stream)
 /*
  * Initialized the local crendentials for the stream.
  */
-void stream_initialize_credentials (Stream *stream, NiceRNG *rng)
+void
+nice_stream_initialize_credentials (NiceStream *stream, NiceRNG *rng)
 {
   /* note: generate ufrag/pwd for the stream (see ICE 15.4.
    *       '"ice-ufrag" and "ice-pwd" Attributes', ID-19) */
@@ -149,7 +141,7 @@ void stream_initialize_credentials (Stream *stream, NiceRNG *rng)
  * session.
  */
 void
-stream_restart (NiceAgent *agent, Stream *stream)
+nice_stream_restart (NiceStream *stream, NiceAgent *agent)
 {
   GSList *i;
 
@@ -158,12 +150,49 @@ stream_restart (NiceAgent *agent, Stream *stream)
 
   stream->initial_binding_request_received = FALSE;
 
-  stream_initialize_credentials (stream, agent->rng);
+  nice_stream_initialize_credentials (stream, agent->rng);
 
   for (i = stream->components; i; i = i->next) {
-    Component *component = i->data;
+    NiceComponent *component = i->data;
 
-    component_restart (component);
+    nice_component_restart (component);
   }
 }
 
+static void
+nice_stream_class_init (NiceStreamClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = nice_stream_finalize;
+}
+
+static void
+nice_stream_init (NiceStream *stream)
+{
+  g_atomic_int_inc (&n_streams_created);
+  nice_debug ("Created NiceStream (%u created, %u destroyed)",
+      n_streams_created, n_streams_destroyed);
+
+  stream->n_components = 0;
+  stream->initial_binding_request_received = FALSE;
+}
+
+/* Must be called with the agent lock released as it could dispose of
+ * NiceIOStreams. */
+static void
+nice_stream_finalize (GObject *obj)
+{
+  NiceStream *stream;
+
+  stream = NICE_STREAM (obj);
+
+  g_free (stream->name);
+  g_slist_free_full (stream->components, (GDestroyNotify) g_object_unref);
+
+  g_atomic_int_inc (&n_streams_destroyed);
+  nice_debug ("Destroyed NiceStream (%u created, %u destroyed)",
+      n_streams_created, n_streams_destroyed);
+
+  G_OBJECT_CLASS (nice_stream_parent_class)->finalize (obj);
+}
diff --git a/agent/stream.h b/agent/stream.h
index e220f43..e524f62 100644
--- a/agent/stream.h
+++ b/agent/stream.h
@@ -42,7 +42,7 @@
 
 #include <glib.h>
 
-typedef struct _Stream Stream;
+typedef struct _NiceStream NiceStream;
 
 #include "component.h"
 #include "random.h"
@@ -59,13 +59,27 @@ G_BEGIN_DECLS
 #define NICE_STREAM_DEF_UFRAG   4 + 1    /* ufrag + NULL */
 #define NICE_STREAM_DEF_PWD     22 + 1   /* pwd + NULL */
 
-struct _Stream
-{
+#define NICE_TYPE_STREAM nice_stream_get_type()
+#define NICE_STREAM(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), NICE_TYPE_STREAM, NiceStream))
+#define NICE_STREAM_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), NICE_TYPE_STREAM, NiceStreamClass))
+#define NICE_IS_STREAM(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NICE_TYPE_STREAM))
+#define NICE_IS_STREAM_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), NICE_TYPE_STREAM))
+#define NICE_STREAM_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), NICE_TYPE_STREAM, NiceStreamClass))
+
+struct _NiceStream {
+  /*< private >*/
+  GObject parent;
+
   gchar *name;
   guint id;
   guint n_components;
   gboolean initial_binding_request_received;
-  GSList *components; /* list of 'Component' structs */
+  GSList *components; /* list of 'NiceComponent' objects */
   GSList *conncheck_list;         /* list of CandidateCheckPair items */
   gchar local_ufrag[NICE_STREAM_MAX_UFRAG];
   gchar local_password[NICE_STREAM_MAX_PWD];
@@ -76,27 +90,29 @@ struct _Stream
   gint tos;
 };
 
+typedef struct {
+  GObjectClass parent_class;
+} NiceStreamClass;
 
-Stream *
-stream_new (guint n_components, NiceAgent *agent);
+GType nice_stream_get_type (void);
 
-void
-stream_close (Stream *stream);
+NiceStream *
+nice_stream_new (guint n_components, NiceAgent *agent);
 
 void
-stream_free (Stream *stream);
+nice_stream_close (NiceStream *stream);
 
 gboolean
-stream_all_components_ready (const Stream *stream);
+nice_stream_all_components_ready (NiceStream *stream);
 
-Component *
-stream_find_component_by_id (const Stream *stream, guint id);
+NiceComponent *
+nice_stream_find_component_by_id (NiceStream *stream, guint id);
 
 void
-stream_initialize_credentials (Stream *stream, NiceRNG *rng);
+nice_stream_initialize_credentials (NiceStream *stream, NiceRNG *rng);
 
 void
-stream_restart (NiceAgent *agent, Stream *stream);
+nice_stream_restart (NiceStream *stream, NiceAgent *agent);
 
 G_END_DECLS
 
diff --git a/autogen.sh b/autogen.sh
index 2f58146..b6efba6 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,27 +1,38 @@
 #!/bin/sh
-set -e
-
-test -d m4 || mkdir m4
-gtkdocize || exit 1
-
-autoreconf -fi
-
-# Honor NOCONFIGURE for compatibility with gnome-autogen.sh
-if test x"$NOCONFIGURE" = x; then
-    run_configure=true
-    for arg in $*; do
-        case $arg in
-            --no-configure)
-                run_configure=false
-                ;;
-            *)
-                ;;
-        esac
-    done
-else
-    run_configure=false
+# Run this to generate all the initial makefiles, etc.
+test -n "$srcdir" || srcdir=$(dirname "$0")
+test -n "$srcdir" || srcdir=.
+
+olddir=$(pwd)
+
+cd $srcdir
+
+(test -f configure.ac) || {
+	echo "*** ERROR: Directory '$srcdir' does not look like the top-level project directory ***"
+	exit 1
+}
+
+# shellcheck disable=SC2016
+PKG_NAME=$(autoconf --trace 'AC_INIT:$1' configure.ac)
+
+if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then
+	echo "*** WARNING: I am going to run 'configure' with no arguments." >&2
+	echo "*** If you wish to pass any to it, please specify them on the" >&2
+	echo "*** '$0' command line." >&2
+	echo "" >&2
 fi
 
-if test $run_configure = true; then
-    ./configure "$@"
+aclocal --install || exit 1
+gtkdocize --copy || exit 1
+autoreconf --verbose --force --install || exit 1
+
+cd "$olddir"
+if [ "$NOCONFIGURE" = "" ]; then
+	$srcdir/configure "$@" || exit 1
+
+	if [ "$1" = "--help" ]; then exit 0 else
+		echo "Now type 'make' to compile $PKG_NAME" || exit 1
+	fi
+else
+	echo "Skipping configure process."
 fi
diff --git a/configure.ac b/configure.ac
index 6031cec..6be4010 100644
--- a/configure.ac
+++ b/configure.ac
@@ -93,9 +93,9 @@ AC_CHECK_HEADERS([ifaddrs.h], \
 AC_CHECK_TYPES([size_t, ssize_t])
 
 # Also put matching version in LIBNICE_CFLAGS
-GLIB_REQ=2.30
+GLIB_REQ=2.44
 
-LIBNICE_CFLAGS="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_30 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36"
+LIBNICE_CFLAGS="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_44 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_44"
 
 dnl Support different levels of compiler error reporting.
 dnl This configure flag is designed to mimic one from gnome-common,
@@ -231,9 +231,6 @@ AS_IF([test "$with_gstreamer" != no], [
 		[
 		have_gst_check=no
 		])
-
-	AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes)
-
 ])
 
 AS_IF([test "$with_gstreamer010" != no], [
@@ -260,6 +257,7 @@ AC_SUBST(gstplugindir)
 AC_SUBST(gstplugin010dir)
 
 AM_CONDITIONAL(WITH_GSTREAMER, test "$with_gstreamer" = yes)
+AM_CONDITIONAL(HAVE_GST_CHECK, test "$have_gst_check" = yes)
 AM_CONDITIONAL(WITH_GSTREAMER010, test "$with_gstreamer010" = yes)
 
 GUPNP_IGD_REQUIRED=0.2.4
diff --git a/docs/design.txt b/docs/design.txt
index 4f43724..6a3bf12 100644
--- a/docs/design.txt
+++ b/docs/design.txt
@@ -90,7 +90,6 @@ NiceAgent GObject interface defined in 'nice/agent.h'.
 
 The rough order of control follow is as follows:
 
-- client should initialize glib with g_type_init()
 - creation of NiceAgent object instance
 - setting agent properties such as STUN and TURN server addresses
 - connecting the GObject signals with g_signal_connect() to application
diff --git a/docs/reference/libnice/Makefile.am b/docs/reference/libnice/Makefile.am
index 1d53e3b..19e479e 100644
--- a/docs/reference/libnice/Makefile.am
+++ b/docs/reference/libnice/Makefile.am
@@ -62,7 +62,7 @@ IGNORE_HFILES= conncheck.h discovery.h stream.h component.h agent-priv.h \
 
 # Images to copy into HTML directory.
 # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
-HTML_IMAGES=
+HTML_IMAGES = states.png
 
 # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
 # e.g. content_files=running.sgml building.sgml changes-2.0.sgml
@@ -94,13 +94,19 @@ include $(top_srcdir)/gtk-doc.make
 
 # Other files to distribute
 # e.g. EXTRA_DIST += version.xml.in
-#EXTRA_DIST +=
+EXTRA_DIST += states.gv
 
 # Files not to distribute
 # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
 # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
 #DISTCLEANFILES +=
 
+# If we ever need to regenerate this diagram.
+# Since it’s not expected to change much, let’s not depend on GraphViz to
+# build the docs.
+states.png: states.gv
+	dot -Tpng -Gsize=9.6,2.9\! -Gdpi=200 $^ > $@
+
 if ENABLE_GTK_DOC
 TESTS_ENVIRONMENT = cd $(builddir) &&
 TESTS = $(GTKDOC_CHECK)
diff --git a/docs/reference/libnice/states.gv b/docs/reference/libnice/states.gv
new file mode 100644
index 0000000..609be2e
--- /dev/null
+++ b/docs/reference/libnice/states.gv
@@ -0,0 +1,25 @@
+/* libnice state transition diagram for NiceComponentState. */
+digraph NiceComponentState {
+	rankdir=TB;
+	node [shape = doublecircle]; DISCONNECTED;
+	node [shape = circle];
+
+	/* Colour the normal control flow in green. */
+	DISCONNECTED -> GATHERING [ label = "nice_agent_gather_candidates()", color = chartreuse3 ];
+	GATHERING -> CONNECTING [ label = "nice_agent_set_remote_candidates()", color = chartreuse3 ];
+	CONNECTING -> CONNECTED [ label = "At least one candidate pair succeeds", color = chartreuse3 ];
+	CONNECTED -> READY [ label = "All candidate pairs checks finished", color = chartreuse3 ];
+
+	READY -> CONNECTED [ label = "Selected candidate pair fails" ];
+
+	FAILED -> CONNECTING [ label = "nice_agent_set_remote_candidates()" ];
+
+	DISCONNECTED -> CONNECTING [ label = "nice_agent_set_remote_candidates()" ];
+
+	/* Colour the failure paths in grey. */
+	DISCONNECTED -> FAILED [ label = "Failure", color = gray ];
+	GATHERING -> FAILED [ label = "Failure", color = gray ];
+	CONNECTING -> FAILED [ label = "Failure", color = gray ];
+	CONNECTED -> FAILED [ label = "Failure", color = gray ];
+	READY -> FAILED [ label = "Failure", color = gray ];
+}
diff --git a/docs/reference/libnice/states.png b/docs/reference/libnice/states.png
new file mode 100644
index 0000000..ba23739
Binary files /dev/null and b/docs/reference/libnice/states.png differ
diff --git a/examples/sdp-example.c b/examples/sdp-example.c
index 246341e..b6dd80a 100644
--- a/examples/sdp-example.c
+++ b/examples/sdp-example.c
@@ -44,9 +44,7 @@
 
 #include <agent.h>
 
-#if GLIB_CHECK_VERSION(2, 36, 0)
 #include <gio/gnetworking.h>
-#endif
 
 static GMainLoop *gloop;
 static gchar *stun_addr = NULL;
@@ -95,11 +93,7 @@ main(int argc, char *argv[])
     g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port);
   }
 
-#if GLIB_CHECK_VERSION(2, 36, 0)
   g_networking_init();
-#else
-  g_type_init();
-#endif
 
   gloop = g_main_loop_new(NULL, FALSE);
 
diff --git a/examples/simple-example.c b/examples/simple-example.c
index 6e13dc6..a511d29 100644
--- a/examples/simple-example.c
+++ b/examples/simple-example.c
@@ -44,9 +44,7 @@
 
 #include <agent.h>
 
-#if GLIB_CHECK_VERSION(2, 36, 0)
 #include <gio/gnetworking.h>
-#endif
 
 static GMainLoop *gloop;
 static GIOChannel* io_stdin;
@@ -105,11 +103,7 @@ main(int argc, char *argv[])
     g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port);
   }
 
-#if GLIB_CHECK_VERSION(2, 36, 0)
   g_networking_init();
-#else
-  g_type_init();
-#endif
 
   gloop = g_main_loop_new(NULL, FALSE);
 #ifdef G_OS_WIN32
@@ -226,7 +220,7 @@ cb_component_state_changed(NiceAgent *agent, guint _stream_id,
   g_debug("SIGNAL: state changed %d %d %s[%d]\n",
       _stream_id, component_id, state_name[state], state);
 
-  if (state == NICE_COMPONENT_STATE_READY) {
+  if (state == NICE_COMPONENT_STATE_CONNECTED) {
     NiceCandidate *local, *remote;
 
     // Get current selected candidate pair and print IP address used
diff --git a/examples/threaded-example.c b/examples/threaded-example.c
index 79eda8d..575b4dc 100644
--- a/examples/threaded-example.c
+++ b/examples/threaded-example.c
@@ -44,9 +44,7 @@
 
 #include <agent.h>
 
-#if GLIB_CHECK_VERSION(2, 36, 0)
 #include <gio/gnetworking.h>
-#endif
 
 static GMainLoop *gloop;
 static gchar *stun_addr = NULL;
@@ -104,11 +102,6 @@ main(int argc, char *argv[])
     g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port);
   }
 
-#if GLIB_CHECK_VERSION(2, 36, 0)
-  g_networking_init();
-#else
-  g_type_init();
-#endif
   g_networking_init();
 
   gloop = g_main_loop_new(NULL, FALSE);
diff --git a/nice/libnice.sym b/nice/libnice.sym
index efcfdc3..b04bb95 100644
--- a/nice/libnice.sym
+++ b/nice/libnice.sym
@@ -74,12 +74,16 @@ pseudo_tcp_socket_close
 pseudo_tcp_socket_connect
 pseudo_tcp_socket_get_error
 pseudo_tcp_socket_get_next_clock
+pseudo_tcp_socket_get_type
+pseudo_tcp_socket_is_closed
+pseudo_tcp_socket_is_closed_remotely
 pseudo_tcp_socket_new
 pseudo_tcp_socket_notify_clock
 pseudo_tcp_socket_notify_mtu
 pseudo_tcp_socket_notify_packet
 pseudo_tcp_socket_recv
 pseudo_tcp_socket_send
+pseudo_tcp_socket_shutdown
 stun_agent_build_unknown_attributes_error
 stun_agent_default_validater
 stun_agent_finish_message
diff --git a/socket/http.c b/socket/http.c
index 404d378..96ddfd8 100644
--- a/socket/http.c
+++ b/socket/http.c
@@ -95,6 +95,7 @@ static gboolean socket_is_reliable (NiceSocket *sock);
 static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
 static void socket_set_writable_callback (NiceSocket *sock,
     NiceSocketWritableCb callback, gpointer user_data);
+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other);
 
 NiceSocket *
 nice_http_socket_new (NiceSocket *base_socket,
@@ -126,6 +127,7 @@ nice_http_socket_new (NiceSocket *base_socket,
     sock->is_reliable = socket_is_reliable;
     sock->can_send = socket_can_send;
     sock->set_writable_callback = socket_set_writable_callback;
+    sock->is_based_on = socket_is_based_on;
     sock->close = socket_close;
 
     /* Send HTTP CONNECT */
@@ -281,9 +283,8 @@ socket_recv_messages (NiceSocket *sock,
   HttpPriv *priv = sock->priv;
   gint ret = -1;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return 0;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   if (priv->state == HTTP_STATE_CONNECTED) {
     guint i;
@@ -576,9 +577,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to,
 {
   HttpPriv *priv = sock->priv;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   if (priv->state == HTTP_STATE_CONNECTED) {
     /* Fast path. */
@@ -642,3 +642,12 @@ socket_set_writable_callback (NiceSocket *sock,
 
   nice_socket_set_writable_callback (priv->base_socket, callback, user_data);
 }
+
+static gboolean
+socket_is_based_on (NiceSocket *sock, NiceSocket *other)
+{
+  HttpPriv *priv = sock->priv;
+
+  return (sock == other) ||
+      (priv && nice_socket_is_based_on (priv->base_socket, other));
+}
diff --git a/socket/pseudossl.c b/socket/pseudossl.c
index 5ad4f97..052725c 100644
--- a/socket/pseudossl.c
+++ b/socket/pseudossl.c
@@ -116,6 +116,7 @@ static gboolean socket_is_reliable (NiceSocket *sock);
 static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
 static void socket_set_writable_callback (NiceSocket *sock,
     NiceSocketWritableCb callback, gpointer user_data);
+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other);
 
 NiceSocket *
 nice_pseudossl_socket_new (NiceSocket *base_socket,
@@ -152,6 +153,7 @@ nice_pseudossl_socket_new (NiceSocket *base_socket,
   sock->is_reliable = socket_is_reliable;
   sock->can_send = socket_can_send;
   sock->set_writable_callback = socket_set_writable_callback;
+  sock->is_based_on = socket_is_based_on;
   sock->close = socket_close;
 
   /* We send 'to' NULL because it will always be to an already connected
@@ -204,9 +206,8 @@ socket_recv_messages (NiceSocket *sock,
 {
   PseudoSSLPriv *priv = sock->priv;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return 0;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   if (priv->handshaken) {
     if (priv->base_socket) {
@@ -256,9 +257,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to,
 {
   PseudoSSLPriv *priv = sock->priv;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   if (priv->handshaken) {
     /* Fast path: pass directly through to the base socket once the handshake is
@@ -319,3 +319,12 @@ socket_set_writable_callback (NiceSocket *sock,
 
   nice_socket_set_writable_callback (priv->base_socket, callback, user_data);
 }
+
+static gboolean
+socket_is_based_on (NiceSocket *sock, NiceSocket *other)
+{
+  PseudoSSLPriv *priv = sock->priv;
+
+  return (sock == other) ||
+      (priv && nice_socket_is_based_on (priv->base_socket, other));
+}
diff --git a/socket/socket.c b/socket/socket.c
index 9c0d978..08ae31a 100644
--- a/socket/socket.c
+++ b/socket/socket.c
@@ -265,6 +265,14 @@ nice_socket_set_writable_callback (NiceSocket *sock,
     sock->set_writable_callback (sock, callback, user_data);
 }
 
+gboolean
+nice_socket_is_based_on (NiceSocket *sock, NiceSocket *other)
+{
+  if (sock->is_based_on)
+    return sock->is_based_on (sock, other);
+  return (sock == other);
+}
+
 void
 nice_socket_free (NiceSocket *sock)
 {
diff --git a/socket/socket.h b/socket/socket.h
index 41ea07b..fadcbc1 100644
--- a/socket/socket.h
+++ b/socket/socket.h
@@ -88,6 +88,7 @@ struct _NiceSocket
   gboolean (*can_send) (NiceSocket *sock, NiceAddress *addr);
   void (*set_writable_callback) (NiceSocket *sock,
       NiceSocketWritableCb callback, gpointer user_data);
+  gboolean (*is_based_on) (NiceSocket *sock, NiceSocket *other);
   void (*close) (NiceSocket *sock);
   void *priv;
 };
@@ -124,6 +125,23 @@ void
 nice_socket_set_writable_callback (NiceSocket *sock,
     NiceSocketWritableCb callback, gpointer user_data);
 
+/**
+ * nice_socket_is_based_on:
+ * @sock: a #NiceSocket
+ * @other: another #NiceSocket
+ *
+ * Checks whether @sock wraps @other as a source and destination of its read and
+ * write operations. The function traverses the whole chain of @sock's base
+ * sockets until @other is found or the end is reached.
+ *
+ * Returns: %TRUE if @sock is based on @other or if @sock and @other are
+ * the same socket, %FALSE otherwise.
+ *
+ * Since: UNRELEASED
+ */
+gboolean
+nice_socket_is_based_on (NiceSocket *sock, NiceSocket *other);
+
 void
 nice_socket_free (NiceSocket *sock);
 
diff --git a/socket/socks5.c b/socket/socks5.c
index 46d17fb..d15fc29 100644
--- a/socket/socks5.c
+++ b/socket/socks5.c
@@ -81,6 +81,7 @@ static gboolean socket_is_reliable (NiceSocket *sock);
 static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
 static void socket_set_writable_callback (NiceSocket *sock,
     NiceSocketWritableCb callback, gpointer user_data);
+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other);
 
 
 NiceSocket *
@@ -108,6 +109,7 @@ nice_socks5_socket_new (NiceSocket *base_socket,
     sock->is_reliable = socket_is_reliable;
     sock->can_send = socket_can_send;
     sock->set_writable_callback = socket_set_writable_callback;
+    sock->is_based_on = socket_is_based_on;
     sock->close = socket_close;
 
     /* Send SOCKS5 handshake */
@@ -167,9 +169,8 @@ socket_recv_messages (NiceSocket *sock,
   guint i;
   gint ret = -1;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return 0;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   switch (priv->state) {
     case SOCKS_STATE_CONNECTED:
@@ -423,9 +424,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to,
 {
   Socks5Priv *priv = sock->priv;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   if (priv->state == SOCKS_STATE_CONNECTED) {
     /* Fast path: pass through to the base socket once connected. */
@@ -488,3 +488,12 @@ socket_set_writable_callback (NiceSocket *sock,
 
   nice_socket_set_writable_callback (priv->base_socket, callback, user_data);
 }
+
+static gboolean
+socket_is_based_on (NiceSocket *sock, NiceSocket *other)
+{
+  Socks5Priv *priv = sock->priv;
+
+  return (sock == other) ||
+      (priv && nice_socket_is_based_on (priv->base_socket, other));
+}
diff --git a/socket/tcp-active.c b/socket/tcp-active.c
index 5144678..5402806 100644
--- a/socket/tcp-active.c
+++ b/socket/tcp-active.c
@@ -50,6 +50,11 @@
 #include <unistd.h>
 #endif
 
+/* FIXME: This should be defined in gio/gnetworking.h, which we should include;
+ * but we cannot do that without refactoring.
+ * (See: https://phabricator.freedesktop.org/D230). */
+#define TCP_NODELAY 1
+
 typedef struct {
   GSocketAddress *local_addr;
   GMainContext *context;
@@ -225,6 +230,9 @@ nice_tcp_active_socket_connect (NiceSocket *sock, NiceAddress *addr)
   /* GSocket: All socket file descriptors are set to be close-on-exec. */
   g_socket_set_blocking (gsock, false);
 
+  /* setting TCP_NODELAY to TRUE in order to avoid packet batching */
+  g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL);
+
   /* Allow g_socket_bind to fail */
   g_socket_bind (gsock, priv->local_addr, FALSE, NULL);
 
diff --git a/socket/tcp-bsd.c b/socket/tcp-bsd.c
index 20dd698..3e5f5a8 100644
--- a/socket/tcp-bsd.c
+++ b/socket/tcp-bsd.c
@@ -54,6 +54,11 @@
 #include <unistd.h>
 #endif
 
+/* FIXME: This should be defined in gio/gnetworking.h, which we should include;
+ * but we cannot do that without refactoring.
+ * (See: https://phabricator.freedesktop.org/D230). */
+#define TCP_NODELAY 1
+
 typedef struct {
   NiceAddress remote_addr;
   GQueue send_queue;
@@ -168,6 +173,9 @@ nice_tcp_bsd_socket_new (GMainContext *ctx, NiceAddress *local_addr,
   /* GSocket: All socket file descriptors are set to be close-on-exec. */
   g_socket_set_blocking (gsock, false);
 
+  /* setting TCP_NODELAY to TRUE in order to avoid packet batching */
+  g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL);
+
   gret = g_socket_connect (gsock, gaddr, NULL, &gerr);
   g_object_unref (gaddr);
 
@@ -229,9 +237,8 @@ socket_recv_messages (NiceSocket *sock,
   TcpPriv *priv = sock->priv;
   guint i;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return 0;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   /* Don't try to access the socket if it had an error */
   if (priv->error)
@@ -283,9 +290,8 @@ socket_send_message (NiceSocket *sock,
   GError *gerr = NULL;
   gsize message_len;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   /* Don't try to access the socket if it had an error, otherwise we risk a
    * crash with SIGPIPE (Broken pipe) */
@@ -344,9 +350,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to,
 {
   guint i;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   for (i = 0; i < n_messages; i++) {
     const NiceOutputMessage *message = &messages[i];
diff --git a/socket/tcp-passive.c b/socket/tcp-passive.c
index 30bfba8..131ff4b 100644
--- a/socket/tcp-passive.c
+++ b/socket/tcp-passive.c
@@ -50,6 +50,11 @@
 #include <unistd.h>
 #endif
 
+/* FIXME: This should be defined in gio/gnetworking.h, which we should include;
+ * but we cannot do that without refactoring.
+ * (See: https://phabricator.freedesktop.org/D230). */
+#define TCP_NODELAY 1
+
 typedef struct {
   GMainContext *context;
   GHashTable *connections;
@@ -176,6 +181,12 @@ socket_close (NiceSocket *sock)
 {
   TcpPassivePriv *priv = sock->priv;
 
+  if (sock->fileno != NULL) {
+    g_socket_close (sock->fileno, NULL);
+    g_object_unref (sock->fileno);
+    sock->fileno = NULL;
+  }
+
   if (priv->context)
     g_main_context_unref (priv->context);
   g_hash_table_unref (priv->connections);
@@ -278,6 +289,9 @@ nice_tcp_passive_socket_accept (NiceSocket *sock)
   /* GSocket: All socket file descriptors are set to be close-on-exec. */
   g_socket_set_blocking (gsock, false);
 
+  /* setting TCP_NODELAY to TRUE in order to avoid packet batching */
+  g_socket_set_option (gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL);
+
   gaddr = g_socket_get_remote_address (gsock, NULL);
   if (gaddr == NULL ||
       !g_socket_address_to_native (gaddr, &name.addr, sizeof (name), NULL)) {
diff --git a/socket/udp-bsd.c b/socket/udp-bsd.c
index d56f093..3fac544 100644
--- a/socket/udp-bsd.c
+++ b/socket/udp-bsd.c
@@ -183,9 +183,8 @@ socket_recv_messages (NiceSocket *sock,
   guint i;
   gboolean error = FALSE;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return 0;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   /* Read messages into recv_messages until one fails or would block, or we
    * reach the end. */
@@ -204,7 +203,10 @@ socket_recv_messages (NiceSocket *sock,
     recv_message->length = MAX (recvd, 0);
 
     if (recvd < 0) {
-      if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+      /* Handle ECONNRESET here as if it were EWOULDBLOCK; see
+       * https://phabricator.freedesktop.org/T121 */
+      if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
+          g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED))
         recvd = 0;
       else
         error = TRUE;
@@ -245,9 +247,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to,
   GError *child_error = NULL;
   gssize len;
 
-  /* Socket has been closed: */
-  if (priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   if (!nice_address_is_valid (&priv->niceaddr) ||
       !nice_address_equal (&priv->niceaddr, to)) {
@@ -289,9 +290,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to,
 {
   guint i;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   for (i = 0; i < n_messages; i++) {
     const NiceOutputMessage *message = &messages[i];
diff --git a/socket/udp-turn-over-tcp.c b/socket/udp-turn-over-tcp.c
index d97fa04..2b91f92 100644
--- a/socket/udp-turn-over-tcp.c
+++ b/socket/udp-turn-over-tcp.c
@@ -86,6 +86,7 @@ static gboolean socket_is_reliable (NiceSocket *sock);
 static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
 static void socket_set_writable_callback (NiceSocket *sock,
     NiceSocketWritableCb callback, gpointer user_data);
+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other);
 
 NiceSocket *
 nice_udp_turn_over_tcp_socket_new (NiceSocket *base_socket,
@@ -107,6 +108,7 @@ nice_udp_turn_over_tcp_socket_new (NiceSocket *base_socket,
   sock->is_reliable = socket_is_reliable;
   sock->can_send = socket_can_send;
   sock->set_writable_callback = socket_set_writable_callback;
+  sock->is_based_on = socket_is_based_on;
   sock->close = socket_close;
 
   return sock;
@@ -134,9 +136,8 @@ socket_recv_message (NiceSocket *sock, NiceInputMessage *recv_message)
   GInputVector local_recv_buf;
   NiceInputMessage local_recv_message;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return 0;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   if (priv->expecting_len == 0) {
     guint headerlen = 0;
@@ -241,9 +242,8 @@ socket_recv_messages (NiceSocket *nicesock,
   guint i;
   gboolean error = FALSE;
 
-  /* Socket has been closed: */
-  if (nicesock->priv == NULL)
-    return 0;
+  /* Make sure socket has not been freed: */
+  g_assert (nicesock->priv != NULL);
 
   for (i = 0; i < n_recv_messages; i++) {
     gssize len;
@@ -285,9 +285,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to,
   } header_buf;
   guint offset = 0;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   /* Count the number of buffers. */
   if (message->n_buffers == -1) {
@@ -301,7 +300,7 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to,
 
   /* Allocate a new array of buffers, covering all the buffers in the input
    * @message, but with an additional one for a header and one for a footer. */
-  local_bufs = g_malloc_n (n_bufs + 1, sizeof (GOutputVector));
+  local_bufs = g_alloca ((n_bufs + 1) * sizeof (GOutputVector));
   local_message.buffers = local_bufs;
   local_message.n_buffers = n_bufs + 1;
 
@@ -377,8 +376,6 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to,
   if (ret == 1)
     ret = output_message_get_size (&local_message);
 
-  g_free (local_bufs);
-
   return ret;
 }
 
@@ -388,9 +385,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to,
 {
   guint i;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   for (i = 0; i < n_messages; i++) {
     const NiceOutputMessage *message = &messages[i];
@@ -458,3 +454,12 @@ socket_set_writable_callback (NiceSocket *sock,
 
   nice_socket_set_writable_callback (priv->base_socket, callback, user_data);
 }
+
+static gboolean
+socket_is_based_on (NiceSocket *sock, NiceSocket *other)
+{
+  TurnTcpPriv *priv = sock->priv;
+
+  return (sock == other) ||
+      (priv && nice_socket_is_based_on (priv->base_socket, other));
+}
diff --git a/socket/udp-turn.c b/socket/udp-turn.c
index e640363..617e4f3 100644
--- a/socket/udp-turn.c
+++ b/socket/udp-turn.c
@@ -98,6 +98,11 @@ typedef struct {
   GHashTable *send_data_queues; /* stores a send data queue for per peer */
   GSource *permission_timeout_source;      /* timer used to invalidate
                                            permissions */
+
+  guint8 *cached_realm;
+  uint16_t cached_realm_len;
+  guint8 *cached_nonce;
+  uint16_t cached_nonce_len;
 } UdpTurnPriv;
 
 
@@ -125,15 +130,16 @@ static gboolean socket_is_reliable (NiceSocket *sock);
 static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
 static void socket_set_writable_callback (NiceSocket *sock,
     NiceSocketWritableCb callback, gpointer user_data);
+static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other);
 
 static void priv_process_pending_bindings (UdpTurnPriv *priv);
 static gboolean priv_retransmissions_tick_unlocked (UdpTurnPriv *priv);
 static gboolean priv_retransmissions_tick (gpointer pointer);
 static void priv_schedule_tick (UdpTurnPriv *priv);
 static void priv_send_turn_message (UdpTurnPriv *priv, TURNMessage *msg);
-static gboolean priv_send_create_permission (UdpTurnPriv *priv,  StunMessage *resp,
+static gboolean priv_send_create_permission (UdpTurnPriv *priv,
     const NiceAddress *peer);
-static gboolean priv_send_channel_bind (UdpTurnPriv *priv, StunMessage *resp,
+static gboolean priv_send_channel_bind (UdpTurnPriv *priv,
     uint16_t channel,
     const NiceAddress *peer);
 static gboolean priv_add_channel_binding (UdpTurnPriv *priv,
@@ -235,7 +241,7 @@ nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr,
           priv_send_data_queue_destroy);
 
   sock->type = NICE_SOCKET_TYPE_UDP_TURN;
-  sock->fileno = base_socket->fileno;
+  sock->fileno = NULL;
   sock->addr = *addr;
   sock->send_messages = socket_send_messages;
   sock->send_messages_reliable = socket_send_messages_reliable;
@@ -243,6 +249,7 @@ nice_udp_turn_socket_new (GMainContext *ctx, NiceAddress *addr,
   sock->is_reliable = socket_is_reliable;
   sock->can_send = socket_can_send;
   sock->set_writable_callback = socket_set_writable_callback;
+  sock->is_based_on = socket_is_based_on;
   sock->close = socket_close;
   sock->priv = (void *) priv;
 
@@ -317,6 +324,8 @@ socket_close (NiceSocket *sock)
   g_list_free(priv->pending_permissions);
   g_free (priv->username);
   g_free (priv->password);
+  g_free (priv->cached_realm);
+  g_free (priv->cached_nonce);
   g_free (priv);
 
   sock->priv = NULL;
@@ -332,11 +341,10 @@ socket_recv_messages (NiceSocket *sock,
   gboolean error = FALSE;
   guint n_valid_messages;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return 0;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
-  nice_debug ("received message on TURN socket");
+  nice_debug_verbose ("received message on TURN socket");
 
   n_messages = nice_socket_recv_messages (priv->base_socket,
       recv_messages, n_recv_messages);
@@ -374,7 +382,7 @@ socket_recv_messages (NiceSocket *sock,
       buffer = message->buffers[0].buffer;
       buffer_length = message->length;
     } else {
-      nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
+      nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
 
       buffer = compact_input_message (message, &buffer_length);
       allocated_buffer = TRUE;
@@ -575,7 +583,7 @@ _socket_send_messages_wrapped (NiceSocket *sock, const NiceAddress *to,
       n_bufs = message->n_buffers;
     }
 
-    local_bufs = g_malloc_n (n_bufs + 1, sizeof (GOutputVector));
+    local_bufs = g_alloca ((n_bufs + 1) * sizeof (GOutputVector));
     local_message.buffers = local_bufs;
     local_message.n_buffers = n_bufs + 1;
 
@@ -598,8 +606,6 @@ _socket_send_messages_wrapped (NiceSocket *sock, const NiceAddress *to,
     if (ret == 1)
       ret = message_len;
 
-    g_free (local_bufs);
-
     return ret;
   }
 }
@@ -663,7 +669,7 @@ socket_dequeue_all_data (UdpTurnPriv *priv, const NiceAddress *to)
       SendData *data =
           (SendData *) g_queue_pop_head(send_queue);
 
-      nice_debug ("dequeuing data");
+      nice_debug_verbose ("dequeuing data");
       _socket_send_wrapped (priv->base_socket, &priv->server_addr,
           data->data_len, data->data, data->reliable);
 
@@ -693,9 +699,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to,
   ChannelBinding *binding = NULL;
   gint ret;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   for (i = priv->channels; i; i = i->next) {
     ChannelBinding *b = i->data;
@@ -816,7 +821,8 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to,
     /* Finish the message. */
     msg_len = stun_agent_finish_message (&priv->agent, &msg,
         priv->password, priv->password_len);
-    if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST) {
+    if (msg_len > 0 && stun_message_get_class (&msg) == STUN_REQUEST &&
+        priv->compatibility != NICE_TURN_SOCKET_COMPATIBILITY_OC2007) {
       SendRequest *req = g_slice_new0 (SendRequest);
 
       req->priv = priv;
@@ -831,11 +837,11 @@ socket_send_message (NiceSocket *sock, const NiceAddress *to,
     if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766 &&
         !priv_has_permission_for_peer (priv, to)) {
       if (!priv_has_sent_permission_for_peer (priv, to)) {
-        priv_send_create_permission (priv, NULL, to);
+        priv_send_create_permission (priv, to);
       }
 
       /* enque data */
-      nice_debug ("enqueuing data");
+      nice_debug_verbose ("enqueuing data");
       socket_enqueue_data(priv, to, msg_len, (gchar *)buffer, reliable);
 
       return msg_len;
@@ -868,9 +874,8 @@ socket_send_messages (NiceSocket *sock, const NiceAddress *to,
 {
   guint i;
 
-  /* Socket has been closed: */
-  if (sock->priv == NULL)
-    return -1;
+  /* Make sure socket has not been freed: */
+  g_assert (sock->priv != NULL);
 
   for (i = 0; i < n_messages; i++) {
     const NiceOutputMessage *message = &messages[i];
@@ -951,6 +956,15 @@ socket_set_writable_callback (NiceSocket *sock,
 }
 
 static gboolean
+socket_is_based_on (NiceSocket *sock, NiceSocket *other)
+{
+  UdpTurnPriv *priv = sock->priv;
+
+  return (sock == other) ||
+      (priv && nice_socket_is_based_on (priv->base_socket, other));
+}
+
+static gboolean
 priv_forget_send_request (gpointer pointer)
 {
   SendRequest *req = pointer;
@@ -1079,13 +1093,20 @@ priv_binding_timeout (gpointer data)
     ChannelBinding *b = i->data;
     if (b->timeout_source == source) {
       b->renew = TRUE;
+
+      /* Remove any existing timer */
+      if (b->timeout_source) {
+        g_source_destroy (b->timeout_source);
+        g_source_unref (b->timeout_source);
+      }
+
       /* Install timer to expire the permission */
       b->timeout_source = priv_timeout_add_with_context (priv,
           STUN_EXPIRE_TIMEOUT, TRUE, priv_binding_expired_timeout, priv);
 
       /* Send renewal */
       if (!priv->current_binding_msg)
-        priv_send_channel_bind (priv, NULL, b->channel, &b->peer);
+        priv_send_channel_bind (priv, b->channel, &b->peer);
       break;
     }
   }
@@ -1095,6 +1116,31 @@ priv_binding_timeout (gpointer data)
   return FALSE;
 }
 
+void
+nice_udp_turn_socket_cache_realm_nonce (NiceSocket *sock, StunMessage *msg)
+{
+  UdpTurnPriv *priv = sock->priv;
+  gconstpointer tmp;
+
+  g_assert (sock->type == NICE_SOCKET_TYPE_UDP_TURN);
+
+  g_free (priv->cached_realm);
+  priv->cached_realm = NULL;
+  priv->cached_realm_len = 0;
+
+  g_free (priv->cached_nonce);
+  priv->cached_nonce = NULL;
+  priv->cached_nonce_len = 0;
+
+  tmp = stun_message_find (msg, STUN_ATTRIBUTE_REALM, &priv->cached_realm_len);
+  if (tmp && priv->cached_realm_len < 764)
+    priv->cached_realm = g_memdup (tmp, priv->cached_realm_len);
+
+  tmp = stun_message_find (msg, STUN_ATTRIBUTE_NONCE, &priv->cached_nonce_len);
+  if (tmp && priv->cached_nonce_len < 764)
+    priv->cached_nonce = g_memdup (tmp, priv->cached_nonce_len);
+}
+
 guint
 nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_sock,
     NiceInputMessage *message)
@@ -1121,7 +1167,7 @@ nice_udp_turn_socket_parse_recv_message (NiceSocket *sock, NiceSocket **from_soc
   }
 
   /* Slow path. */
-  nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
+  nice_debug_verbose ("%s: **WARNING: SLOW PATH**", G_STRFUNC);
 
   buf = compact_input_message (message, &buf_len);
   len = nice_udp_turn_socket_parse_recv (sock, from_sock,
@@ -1298,8 +1344,9 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
 
                 g_free (priv->current_binding_msg);
                 priv->current_binding_msg = NULL;
+                nice_udp_turn_socket_cache_realm_nonce (sock, &msg);
                 if (binding)
-                  priv_send_channel_bind (priv, &msg, binding->channel,
+                  priv_send_channel_bind (priv, binding->channel,
                       &binding->peer);
               } else {
                 g_free (priv->current_binding);
@@ -1357,12 +1404,17 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
             } peer;
             socklen_t peer_len = sizeof(peer);
             NiceAddress to;
+            gchar tmpbuf[INET6_ADDRSTRLEN];
 
-            nice_debug ("got response for CreatePermission");
             stun_message_find_xor_addr (
                 &current_create_permission_msg->message,
                 STUN_ATTRIBUTE_XOR_PEER_ADDRESS, &peer.storage, &peer_len);
             nice_address_set_from_sockaddr (&to, &peer.addr);
+            nice_address_to_string (&to, tmpbuf);
+            nice_debug ("TURN: got response for CreatePermission "
+                "with XOR_PEER_ADDRESS=[%s]:%u : %s",
+                tmpbuf, nice_address_get_port (&to),
+                stun_message_get_class (&msg) == STUN_ERROR ? "unauthorized" : "ok");
 
             /* unathorized => resend with realm and nonce */
             if (stun_message_get_class (&msg) == STUN_ERROR) {
@@ -1395,8 +1447,10 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
                     priv->pending_permissions, i);
                 g_free (current_create_permission_msg);
                 current_create_permission_msg = NULL;
+
+                nice_udp_turn_socket_cache_realm_nonce (sock, &msg);
                 /* resend CreatePermission */
-                priv_send_create_permission (priv, &msg, &to);
+                priv_send_create_permission (priv, &to);
                 return 0;
               }
             }
@@ -1463,7 +1517,7 @@ nice_udp_turn_socket_parse_recv (NiceSocket *sock, NiceSocket **from_sock,
         if (priv->compatibility == NICE_TURN_SOCKET_COMPATIBILITY_RFC5766 &&
                 !priv_has_permission_for_peer (priv, from)) {
           if (!priv_has_sent_permission_for_peer (priv, from)) {
-            priv_send_create_permission (priv, NULL, from);
+            priv_send_create_permission (priv, from);
           }
         }
 
@@ -1549,7 +1603,7 @@ priv_process_pending_bindings (UdpTurnPriv *priv)
     for (i = priv->channels ; i; i = i->next) {
       ChannelBinding *b = i->data;
       if (b->renew) {
-        priv_send_channel_bind (priv, NULL, b->channel, &b->peer);
+        priv_send_channel_bind (priv, b->channel, &b->peer);
         break;
       }
     }
@@ -1804,7 +1858,7 @@ priv_send_turn_message (UdpTurnPriv *priv, TURNMessage *msg)
 }
 
 static gboolean
-priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp,
+priv_send_create_permission(UdpTurnPriv *priv,
     const NiceAddress *peer)
 {
   guint msg_buf_len;
@@ -1814,17 +1868,6 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp,
     struct sockaddr_storage storage;
     struct sockaddr addr;
   } addr;
-  uint8_t *realm = NULL;
-  uint16_t realm_len = 0;
-  uint8_t *nonce = NULL;
-  uint16_t nonce_len = 0;
-
-  if (resp) {
-    realm = (uint8_t *) stun_message_find (resp,
-        STUN_ATTRIBUTE_REALM, &realm_len);
-    nonce = (uint8_t *) stun_message_find (resp,
-        STUN_ATTRIBUTE_NONCE, &nonce_len);
-  }
 
   /* register this peer as being pening a permission (if not already pending) */
   if (!priv_has_sent_permission_for_peer (priv, peer)) {
@@ -1841,8 +1884,8 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp,
       priv->username_len,
       priv->password,
       priv->password_len,
-      realm, realm_len,
-      nonce, nonce_len,
+      priv->cached_realm, priv->cached_realm_len,
+      priv->cached_nonce, priv->cached_nonce_len,
       &addr.storage,
       STUN_USAGE_TURN_COMPATIBILITY_RFC5766);
 
@@ -1876,8 +1919,8 @@ priv_send_create_permission(UdpTurnPriv *priv, StunMessage *resp,
 }
 
 static gboolean
-priv_send_channel_bind (UdpTurnPriv *priv,  StunMessage *resp,
-    uint16_t channel, const NiceAddress *peer)
+priv_send_channel_bind (UdpTurnPriv *priv, uint16_t channel,
+    const NiceAddress *peer)
 {
   uint32_t channel_attr = channel << 16;
   size_t stun_len;
@@ -1910,37 +1953,29 @@ priv_send_channel_bind (UdpTurnPriv *priv,  StunMessage *resp,
     return FALSE;
   }
 
-  if (priv->username != NULL && priv->username_len > 0) {
+  if (priv->username != NULL && priv->username_len > 0 &&
+      priv->cached_realm != NULL && priv->cached_realm_len > 0 &&
+      priv->cached_nonce != NULL && priv->cached_nonce_len > 0) {
+
     if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_USERNAME,
             priv->username, priv->username_len)
         != STUN_MESSAGE_RETURN_SUCCESS) {
       g_free (msg);
       return FALSE;
     }
-  }
-
-  if (resp) {
-    uint8_t *realm;
-    uint8_t *nonce;
-    uint16_t len;
 
-    realm = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_REALM, &len);
-    if (realm != NULL) {
-      if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM,
-              realm, len)
-          != STUN_MESSAGE_RETURN_SUCCESS) {
-        g_free (msg);
-        return 0;
-      }
+    if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_REALM,
+            priv->cached_realm,  priv->cached_realm_len)
+        != STUN_MESSAGE_RETURN_SUCCESS) {
+      g_free (msg);
+      return 0;
     }
-    nonce = (uint8_t *) stun_message_find (resp, STUN_ATTRIBUTE_NONCE, &len);
-    if (nonce != NULL) {
-      if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE,
-              nonce, len)
-          != STUN_MESSAGE_RETURN_SUCCESS) {
-        g_free (msg);
-        return 0;
-      }
+
+    if (stun_message_append_bytes (&msg->message, STUN_ATTRIBUTE_NONCE,
+            priv->cached_nonce, priv->cached_nonce_len)
+        != STUN_MESSAGE_RETURN_SUCCESS) {
+      g_free (msg);
+      return 0;
     }
   }
 
@@ -1988,7 +2023,7 @@ priv_add_channel_binding (UdpTurnPriv *priv, const NiceAddress *peer)
     }
 
     if (channel >= 0x4000 && channel < 0xffff) {
-      gboolean ret = priv_send_channel_bind (priv, NULL, channel, peer);
+      gboolean ret = priv_send_channel_bind (priv, channel, peer);
       if (ret) {
         priv->current_binding = g_new0 (ChannelBinding, 1);
         priv->current_binding->channel = channel;
diff --git a/socket/udp-turn.h b/socket/udp-turn.h
index ba31636..b1eeeb4 100644
--- a/socket/udp-turn.h
+++ b/socket/udp-turn.h
@@ -75,6 +75,9 @@ nice_udp_turn_socket_set_ms_realm(NiceSocket *sock, StunMessage *msg);
 void
 nice_udp_turn_socket_set_ms_connection_id (NiceSocket *sock, StunMessage *msg);
 
+void
+nice_udp_turn_socket_cache_realm_nonce (NiceSocket *sock, StunMessage *msg);
+
 
 G_END_DECLS
 
diff --git a/stun/debug.c b/stun/debug.c
index 2b4ec59..8efb576 100644
--- a/stun/debug.c
+++ b/stun/debug.c
@@ -46,7 +46,7 @@
 #include "debug.h"
 
 
-static int debug_enabled = 1;
+static int debug_enabled = 0;
 
 void stun_debug_enable (void) {
   debug_enabled = 1;
diff --git a/stun/stunagent.c b/stun/stunagent.c
index 2abcc29..bd243cb 100644
--- a/stun/stunagent.c
+++ b/stun/stunagent.c
@@ -513,8 +513,20 @@ size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg,
   uint32_t fpr;
   int saved_id_idx = 0;
   uint8_t md5[16];
+  bool remember_transaction;
 
-  if (stun_message_get_class (msg) == STUN_REQUEST) {
+  remember_transaction = (stun_message_get_class (msg) == STUN_REQUEST);
+
+  if (agent->compatibility == STUN_COMPATIBILITY_OC2007 &&
+      stun_message_get_method (msg) == STUN_SEND) {
+    /* As per [MS-TURN] Section 2.2.1, the TURN server doesn't send responses to
+     * STUN_SEND requests, so don't bother waiting for them. More details at
+     * https://msdn.microsoft.com/en-us/library/dd946797%28v=office.12%29.aspx.
+     */
+    remember_transaction = FALSE;
+  }
+
+  if (remember_transaction) {
     for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) {
       if (agent->sent_ids[saved_id_idx].valid == FALSE) {
         break;
@@ -620,7 +632,7 @@ size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg,
   }
 
 
-  if (stun_message_get_class (msg) == STUN_REQUEST) {
+  if (remember_transaction) {
     stun_message_id (msg, agent->sent_ids[saved_id_idx].id);
     agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg);
     agent->sent_ids[saved_id_idx].key = (uint8_t *) key;
diff --git a/stun/stunmessage.c b/stun/stunmessage.c
index 9faa64b..558fe5e 100644
--- a/stun/stunmessage.c
+++ b/stun/stunmessage.c
@@ -550,7 +550,6 @@ ssize_t stun_message_validate_buffer_length_fast (StunInputVector *buffers,
 
   if (buffers[0].buffer[0] >> 6)
   {
-    stun_debug ("STUN error: RTP or other non-protocol packet!");
     return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet
   }
 
diff --git a/stun/tests/test-bind.c b/stun/tests/test-bind.c
index 0c7646f..2cf4feb 100644
--- a/stun/tests/test-bind.c
+++ b/stun/tests/test-bind.c
@@ -438,7 +438,7 @@ static void keepalive (void)
 
 static void test (void (*func) (void), const char *name)
 {
-  alarm (20);
+  alarm (30);
 
   printf ("%s test... ", name);
   func ();
diff --git a/stun/tests/test-conncheck.c b/stun/tests/test-conncheck.c
index 610d43a..92b947c 100644
--- a/stun/tests/test-conncheck.c
+++ b/stun/tests/test-conncheck.c
@@ -213,7 +213,7 @@ int main (void)
 
   addr.ip4.sin_family = AF_INET;
 
-  /* Lost role conflict */
+  /* Role conflict, controlling + ICE-CONTROLLING, switching controlled */
   assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING));
   val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLING, tie + 1);
   assert (val == STUN_MESSAGE_RETURN_SUCCESS);
@@ -223,7 +223,6 @@ int main (void)
   rlen = stun_agent_finish_message (&agent, &req, pass, pass_len);
   assert (rlen > 0);
 
-
   len = sizeof (resp_buf);
   control = true;
   val2 = stun_usage_ice_conncheck_create_reply (&agent, &req,
@@ -236,7 +235,7 @@ int main (void)
           stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS);
   assert (stun_message_get_class (&resp) == STUN_RESPONSE);
 
-  /* Won role conflict */
+  /* Role conflict, controlled + ICE-CONTROLLED, switching controlling */
   assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING));
   val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLED, tie - 1);
   assert (val == STUN_MESSAGE_RETURN_SUCCESS);
@@ -251,15 +250,60 @@ int main (void)
   val2 = stun_usage_ice_conncheck_create_reply (&agent, &req,
       &resp, resp_buf, &len, &addr.storage,
       sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245);
-  assert (val2 == STUN_USAGE_ICE_RETURN_SUCCESS);
+  assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT);
   assert (len > 0);
-  assert (control == false);
+  assert (control == true);
+  assert (stun_agent_validate (&agent, &resp, resp_buf, len,
+          stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS);
+  assert (stun_message_get_class (&resp) == STUN_RESPONSE);
+
+  /* Role conflict, controlling + ICE-CONTROLLING, staying controlling */
+  assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING));
+  val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLING, tie - 1);
+  assert (val == STUN_MESSAGE_RETURN_SUCCESS);
+  val = stun_message_append_string (&req, STUN_ATTRIBUTE_USERNAME,
+     (char *) ufrag);
+  assert (val == STUN_MESSAGE_RETURN_SUCCESS);
+  rlen = stun_agent_finish_message (&agent, &req, pass, pass_len);
+  assert (rlen > 0);
+
+  len = sizeof (resp_buf);
+  control = true;
+  val2 = stun_usage_ice_conncheck_create_reply (&agent, &req,
+      &resp, resp_buf, &len, &addr.storage,
+      sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245);
+  assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT);
+  assert (len > 0);
+  assert (control == true);
   assert (stun_agent_validate (&agent, &resp, resp_buf, len,
           stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS);
   assert (stun_message_get_class (&resp) == STUN_ERROR);
   stun_message_find_error (&resp, &code);
   assert (code == STUN_ERROR_ROLE_CONFLICT);
 
+  /* Role conflict, controlled + ICE-CONTROLLED, staying controlling */
+  assert (stun_agent_init_request (&agent, &req, req_buf, sizeof(req_buf), STUN_BINDING));
+  val = stun_message_append64 (&req, STUN_ATTRIBUTE_ICE_CONTROLLED, tie + 1);
+  assert (val == STUN_MESSAGE_RETURN_SUCCESS);
+  val = stun_message_append_string (&req, STUN_ATTRIBUTE_USERNAME,
+      (char *) ufrag);
+  assert (val == STUN_MESSAGE_RETURN_SUCCESS);
+  rlen = stun_agent_finish_message (&agent, &req, pass, pass_len);
+  assert (rlen > 0);
+
+  len = sizeof (resp_buf);
+  control = false;
+  val2 = stun_usage_ice_conncheck_create_reply (&agent, &req,
+      &resp, resp_buf, &len, &addr.storage,
+      sizeof (addr.ip4), &control, tie, STUN_USAGE_ICE_COMPATIBILITY_RFC5245);
+  assert (val2 == STUN_USAGE_ICE_RETURN_ROLE_CONFLICT);
+  assert (len > 0);
+  assert (control == false);
+  assert (stun_agent_validate (&agent, &resp, resp_buf, len,
+          stun_agent_default_validater, validater_data) == STUN_VALIDATION_SUCCESS);
+  assert (stun_message_get_class (&resp) == STUN_ERROR);
+  stun_message_find_error (&resp, &code);
+  assert (code == STUN_ERROR_ROLE_CONFLICT);
 
   return 0;
 }
diff --git a/stun/usages/ice.c b/stun/usages/ice.c
index a628791..a7d0d19 100644
--- a/stun/usages/ice.c
+++ b/stun/usages/ice.c
@@ -265,9 +265,17 @@ stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req,
   if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING
           : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS)
   {
+    /* we have the ice-controlling/controlled attribute,
+     * and there's a role conflict
+     */
     stun_debug ("STUN Role Conflict detected:");
 
-    if (tie < q)
+    /* According to ICE RFC 5245, section 7.2.1.1, we consider the four
+     * possible cases when a role conflict is detected: two cases are
+     * resolved by switching role locally, and the two other cases are
+     * handled by responding with a STUN error.
+     */
+    if ((tie < q && *control) || (tie >= q && !*control))
     {
       stun_debug (" switching role from \"controll%s\" to \"controll%s\"",
            *control ? "ing" : "ed", *control ? "ed" : "ing");
@@ -279,10 +287,21 @@ stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req,
       stun_debug (" staying \"controll%s\" (sending error)",
            *control ? "ing" : "ed");
       err (STUN_ERROR_ROLE_CONFLICT);
-      return STUN_USAGE_ICE_RETURN_SUCCESS;
+      return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT;
     }
   } else {
-    stun_debug ("STUN Role not specified by peer!");
+    if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLED
+            : STUN_ATTRIBUTE_ICE_CONTROLLING, &q) != STUN_MESSAGE_RETURN_SUCCESS)
+    {
+      /* we don't have the expected ice-controlling/controlled
+       * attribute
+       */
+      if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_RFC5245 ||
+          compatibility == STUN_USAGE_ICE_COMPATIBILITY_WLM2009)
+      {
+        stun_debug ("STUN Role not specified by peer!");
+      }
+    }
   }
 
   if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) {
diff --git a/stun/usages/timer.c b/stun/usages/timer.c
index 82f3ea2..2862ab8 100644
--- a/stun/usages/timer.c
+++ b/stun/usages/timer.c
@@ -104,7 +104,7 @@ void stun_timer_start (StunTimer *timer, unsigned int initial_timeout,
     unsigned int max_retransmissions)
 {
   stun_gettime (&timer->deadline);
-  timer->retransmissions = 0;
+  timer->retransmissions = 1;
   timer->delay = initial_timeout;
   timer->max_retransmissions = max_retransmissions;
   add_delay (&timer->deadline, timer->delay);
diff --git a/stun/usages/timer.h b/stun/usages/timer.h
index e6501cb..e74353b 100644
--- a/stun/usages/timer.h
+++ b/stun/usages/timer.h
@@ -130,15 +130,18 @@ struct stun_timer_s {
  * STUN_TIMER_DEFAULT_TIMEOUT:
  *
  * 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.
  */
-#define STUN_TIMER_DEFAULT_TIMEOUT 600
+#define STUN_TIMER_DEFAULT_TIMEOUT 200
 
 /**
  * STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS:
  *
  * The default maximum retransmissions allowed before a timer decides to timeout
  */
-#define STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS 3
+#define STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS 7
 
 /**
  * STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT:
diff --git a/stun/usages/turn.c b/stun/usages/turn.c
index f242650..3b94959 100644
--- a/stun/usages/turn.c
+++ b/stun/usages/turn.c
@@ -152,7 +152,9 @@ size_t stun_usage_turn_create (StunAgent *agent, StunMessage *msg,
     }
   }
 
-  if (username != NULL && username_len > 0) {
+  if (username != NULL && username_len > 0 &&
+      (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
+       previous_response)) {
     if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
             username, username_len) != STUN_MESSAGE_RETURN_SUCCESS)
       return 0;
@@ -205,7 +207,9 @@ size_t stun_usage_turn_create_refresh (StunAgent *agent, StunMessage *msg,
   }
 
 
-  if (username != NULL && username_len > 0) {
+  if (username != NULL && username_len > 0 &&
+      (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
+       previous_response)) {
     if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
             username, username_len) != STUN_MESSAGE_RETURN_SUCCESS)
       return 0;
@@ -251,7 +255,9 @@ size_t stun_usage_turn_create_permission (StunAgent *agent, StunMessage *msg,
   }
 
   /* username */
-  if (username != NULL) {
+  if (username != NULL &&
+      (agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS ||
+       (nonce != NULL && realm != NULL))) {
     if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME,
             username, username_len) != STUN_MESSAGE_RETURN_SUCCESS)
       return 0;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 3644091..7bfe075 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -22,6 +22,10 @@ AM_CFLAGS = \
 	-I $(top_srcdir)/stun
 AM_CPPFLAGS = -DG_LOG_DOMAIN=\"libnice-tests\"
 
+AM_TESTS_ENVIRONMENT = \
+	G_MESSAGES_DEBUG=all \
+	NICE_DEBUG=all;
+
 COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS)
 
 check_PROGRAMS = \
@@ -39,6 +43,7 @@ check_PROGRAMS = \
 	test-io-stream-cancelling \
 	test-io-stream-pollable \
 	test-send-recv \
+	test-socket-is-based-on \
 	test-priority \
 	test-mainloop \
 	test-fullmode \
@@ -49,7 +54,8 @@ check_PROGRAMS = \
 	test-new-dribble \
 	test-tcp \
 	test-icetcp \
-	test-credentials
+	test-credentials \
+	test-turn
 
 dist_check_SCRIPTS = \
 	check-test-fullmode-with-stun.sh \
@@ -99,6 +105,8 @@ test_io_stream_pollable_LDADD = $(COMMON_LDADD)
 test_send_recv_SOURCES = test-send-recv.c test-io-stream-common.c
 test_send_recv_LDADD = $(COMMON_LDADD)
 
+test_socket_is_based_on_LDADD = $(COMMON_LDADD)
+
 test_priority_LDADD = $(COMMON_LDADD)
 
 test_mainloop_LDADD = $(COMMON_LDADD)
@@ -119,6 +127,8 @@ test_icetcp_LDADD = $(COMMON_LDADD)
 
 test_credentials_LDADD = $(COMMON_LDADD)
 
+test_turn_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-add-remove-stream.c b/tests/test-add-remove-stream.c
index 5ed3463..c97bc7b 100644
--- a/tests/test-add-remove-stream.c
+++ b/tests/test-add-remove-stream.c
@@ -54,8 +54,6 @@ main (void)
   WSAStartup(0x0202, &w);
 #endif
   nice_address_init (&addr);
-  g_type_init ();
-  g_thread_init (NULL);
 
   if (!nice_address_set_from_string (&addr, "127.0.0.1"))
     g_assert_not_reached ();
diff --git a/tests/test-bsd.c b/tests/test-bsd.c
index 6b747d0..c1ddf53 100644
--- a/tests/test-bsd.c
+++ b/tests/test-bsd.c
@@ -368,8 +368,6 @@ test_multi_message_recv (guint n_sends, guint n_receives,
 int
 main (void)
 {
-  g_type_init ();
-
   test_socket_initial_properties ();
   test_socket_address_properties ();
   test_simple_send_recv ();
diff --git a/tests/test-build-io-stream.c b/tests/test-build-io-stream.c
index 0c9c593..a3478ed 100644
--- a/tests/test-build-io-stream.c
+++ b/tests/test-build-io-stream.c
@@ -440,8 +440,6 @@ main (void)
   WSAStartup (0x0202, &w);
 #endif
   nice_address_init (&addr);
-  g_type_init ();
-  g_thread_init (NULL);
 
   g_assert (nice_address_set_from_string (&addr, "127.0.0.1"));
 
diff --git a/tests/test-credentials.c b/tests/test-credentials.c
index f678afa..1de4e49 100644
--- a/tests/test-credentials.c
+++ b/tests/test-credentials.c
@@ -172,8 +172,6 @@ int main (void)
   WSADATA w;
   WSAStartup(0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init (NULL);
 
   loop = g_main_loop_new (NULL, FALSE);
 
diff --git a/tests/test-dribble.c b/tests/test-dribble.c
index 6603129..d9de07b 100644
--- a/tests/test-dribble.c
+++ b/tests/test-dribble.c
@@ -215,9 +215,6 @@ int main (void)
   WSAStartup(0x0202, &w);
 #endif
 
-  g_type_init ();
-  g_thread_init (NULL);
-
   global_mainloop = g_main_loop_new (NULL, FALSE);
 
   /* step: create the agents L and R */
diff --git a/tests/test-fallback.c b/tests/test-fallback.c
index f97cb0d..34fd1d1 100644
--- a/tests/test-fallback.c
+++ b/tests/test-fallback.c
@@ -491,8 +491,6 @@ int main (void)
 
   WSAStartup(0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init (NULL);
 
   global_mainloop = g_main_loop_new (NULL, FALSE);
 
diff --git a/tests/test-fullmode.c b/tests/test-fullmode.c
index 03778f7..b599a24 100644
--- a/tests/test-fullmode.c
+++ b/tests/test-fullmode.c
@@ -834,8 +834,6 @@ int main (void)
 
   WSAStartup(0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init(NULL);
 
   global_mainloop = g_main_loop_new (NULL, FALSE);
 
diff --git a/tests/test-icetcp.c b/tests/test-icetcp.c
index 7ab3563..5b2b4b2 100644
--- a/tests/test-icetcp.c
+++ b/tests/test-icetcp.c
@@ -125,6 +125,14 @@ static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpoin
   (void)agent;
 }
 
+static void check_loop_quit_condition (void)
+{
+  if (global_ready_reached &&
+      global_lagent_cands >= 2 && global_ragent_cands >= 2) {
+    g_main_loop_quit (global_mainloop);
+  }
+}
+
 static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
 {
   gboolean ready_to_connected = FALSE;
@@ -158,10 +166,10 @@ static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint
       global_ready_reached == FALSE) {
     g_debug ("Components ready/failed achieved. Stopping mailoop");
     global_ready_reached = TRUE;
-    g_main_loop_quit (global_mainloop); 
-    return;
   }
 
+  check_loop_quit_condition ();
+
 #if 0
   /* signal status via a global variable */
   if (global_components_failed == global_components_failed_exit) {
@@ -184,6 +192,8 @@ static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint compon
   else if (GPOINTER_TO_UINT (data) == 2)
     ++global_ragent_cands;
 
+  check_loop_quit_condition ();
+
   /* XXX: dear compiler, these are for you: */
   (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation;
 }
@@ -397,10 +407,6 @@ int main (void)
 
   WSAStartup(0x0202, &w);
 #endif
-  g_type_init ();
-#if !GLIB_CHECK_VERSION(2,31,8)
-  g_thread_init(NULL);
-#endif
 
   global_mainloop = g_main_loop_new (NULL, FALSE);
 
diff --git a/tests/test-io-stream-cancelling.c b/tests/test-io-stream-cancelling.c
index 55fc1f3..d1b9a89 100644
--- a/tests/test-io-stream-cancelling.c
+++ b/tests/test-io-stream-cancelling.c
@@ -112,8 +112,6 @@ int main (void)
   WSADATA w;
   WSAStartup (0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init (NULL);
 
   l_data.cancellable = g_cancellable_new ();
   l_data.blocking = FALSE;
diff --git a/tests/test-io-stream-closing-read.c b/tests/test-io-stream-closing-read.c
index ec434dd..5acddec 100644
--- a/tests/test-io-stream-closing-read.c
+++ b/tests/test-io-stream-closing-read.c
@@ -127,8 +127,6 @@ int main (void)
   WSADATA w;
   WSAStartup (0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init (NULL);
 
   run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL);
 
diff --git a/tests/test-io-stream-closing-write.c b/tests/test-io-stream-closing-write.c
index 97e8347..6f92f28 100644
--- a/tests/test-io-stream-closing-write.c
+++ b/tests/test-io-stream-closing-write.c
@@ -127,8 +127,6 @@ int main (void)
   WSADATA w;
   WSAStartup (0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init (NULL);
 
   run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL);
 
diff --git a/tests/test-io-stream-common.c b/tests/test-io-stream-common.c
index bd0f3b5..efa6160 100644
--- a/tests/test-io-stream-common.c
+++ b/tests/test-io-stream-common.c
@@ -335,12 +335,7 @@ spawn_thread (const gchar *thread_name, GThreadFunc thread_func,
 {
   GThread *thread;
 
-#if !GLIB_CHECK_VERSION(2, 31, 8)
-  thread = g_thread_create (thread_func, user_data, TRUE, NULL);
-#else
   thread = g_thread_new (thread_name, thread_func, user_data);
-#endif
-
   g_assert (thread);
 
   return thread;
diff --git a/tests/test-io-stream-pollable.c b/tests/test-io-stream-pollable.c
index 614a546..a543f88 100644
--- a/tests/test-io-stream-pollable.c
+++ b/tests/test-io-stream-pollable.c
@@ -159,8 +159,6 @@ int main (void)
   WSADATA w;
   WSAStartup (0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init (NULL);
 
   l_data = g_malloc0 (sizeof (ThreadData));
   r_data = g_malloc0 (sizeof (ThreadData));
diff --git a/tests/test-io-stream-thread.c b/tests/test-io-stream-thread.c
index db14fe4..73ecd76 100644
--- a/tests/test-io-stream-thread.c
+++ b/tests/test-io-stream-thread.c
@@ -124,8 +124,6 @@ int main (void)
   WSADATA w;
   WSAStartup (0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init (NULL);
 
   l_data = g_malloc0 (sizeof (ThreadData));
   r_data = g_malloc0 (sizeof (ThreadData));
diff --git a/tests/test-mainloop.c b/tests/test-mainloop.c
index b4e4d05..7c52daa 100644
--- a/tests/test-mainloop.c
+++ b/tests/test-mainloop.c
@@ -72,8 +72,6 @@ main (void)
   guint stream;
 
   nice_address_init (&addr);
-  g_type_init ();
-  g_thread_init(NULL);
 
   loop = g_main_loop_new (NULL, FALSE);
 
diff --git a/tests/test-new-dribble.c b/tests/test-new-dribble.c
index 395e275..3e60ae3 100644
--- a/tests/test-new-dribble.c
+++ b/tests/test-new-dribble.c
@@ -56,21 +56,14 @@
 #define LEFT_AGENT GINT_TO_POINTER(1)
 #define RIGHT_AGENT GINT_TO_POINTER(2)
 
-#if !GLIB_CHECK_VERSION(2,31,8)
-  static GMutex *stun_mutex_ptr = NULL;
-  static GCond *stun_signal_ptr = NULL;
-  static GMutex *stun_thread_mutex_ptr = NULL;
-  static GCond *stun_thread_signal_ptr = NULL
-#else
-  static GMutex stun_mutex;
-  static GMutex *stun_mutex_ptr = &stun_mutex;
-  static GCond stun_signal;
-  static GCond *stun_signal_ptr = &stun_signal;
-  static GMutex stun_thread_mutex;
-  static GMutex *stun_thread_mutex_ptr = &stun_thread_mutex;
-  static GCond stun_thread_signal;
-  static GCond *stun_thread_signal_ptr = &stun_thread_signal;
-#endif
+static GMutex stun_mutex;
+static GMutex *stun_mutex_ptr = &stun_mutex;
+static GCond stun_signal;
+static GCond *stun_signal_ptr = &stun_signal;
+static GMutex stun_thread_mutex;
+static GMutex *stun_thread_mutex_ptr = &stun_thread_mutex;
+static GCond stun_thread_signal;
+static GCond *stun_thread_signal_ptr = &stun_thread_signal;
 
 static NiceComponentState global_lagent_state = NICE_COMPONENT_STATE_LAST;
 static NiceComponentState global_ragent_state = NICE_COMPONENT_STATE_LAST;
@@ -530,6 +523,10 @@ static void standard_test(NiceAgent *lagent, NiceAgent *ragent)
 
   g_assert (lagent_candidate_gathering_done);
 
+  while (global_ragent_state < NICE_COMPONENT_STATE_CONNECTED)
+    g_main_context_iteration (NULL, TRUE);
+  g_cancellable_reset (global_cancellable);
+
   g_assert (global_lagent_state == NICE_COMPONENT_STATE_READY);
   g_assert (global_ragent_state >= NICE_COMPONENT_STATE_CONNECTED);
 
@@ -725,8 +722,6 @@ int main(void)
   GSource *src;
   int sock;
 
-  g_type_init();
-
   global_cancellable = g_cancellable_new ();
   src = g_cancellable_source_new (global_cancellable);
   g_source_set_dummy_callback (src);
@@ -739,16 +734,8 @@ int main(void)
   }
 
 
-#if !GLIB_CHECK_VERSION(2,31,8)
-  g_thread_init (NULL);
-  stun_thread = g_thread_create (stun_thread_func, GINT_TO_POINTER (sock),
-      TRUE, NULL);
- stun_mutex_ptr = g_mutex_new ();
- stun_signal_ptr = g_cond_new ();
-#else
   stun_thread = g_thread_new ("listen for STUN requests",
       stun_thread_func, GINT_TO_POINTER (sock));
-#endif
 
   // Once the the thread is forked, we want to listen for a signal 
   // that the socket was opened successfully
@@ -803,10 +790,6 @@ int main(void)
   g_object_unref (ragent);
 
   g_thread_join (stun_thread);
-#if !GLIB_CHECK_VERSION(2,31,8)
-  g_mutex_free (stun_mutex_ptr);
-  g_cond_free (stun_signal_ptr);
-#endif
   g_object_unref (global_cancellable);
 
   g_source_destroy (src);
diff --git a/tests/test-priority.c b/tests/test-priority.c
index f7d3273..1700dd0 100644
--- a/tests/test-priority.c
+++ b/tests/test-priority.c
@@ -47,38 +47,41 @@ main (void)
 {
   NiceCandidate *candidate;
 
-  /* test 1 */
   candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
-  g_assert (nice_candidate_jingle_priority (candidate) == 1000);
+  nice_address_set_from_string (&candidate->addr, "127.0.0.1");
+  nice_address_set_from_string (&candidate->base_addr, "127.0.0.1");
+
+  /* test 1 */
+  g_assert_cmpuint (nice_candidate_jingle_priority (candidate), ==, 1000);
   /* Host UDP */
   candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
   candidate->component_id = 1;
-  g_assert (nice_candidate_ice_priority (candidate, FALSE, FALSE) == 0x780001FF);
+  g_assert_cmpuint (nice_candidate_ice_priority (candidate, FALSE, FALSE), ==, 0x780001FF);
   /* Host UDP reliable */
-  g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x3C0001FF);
+  g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE), ==, 0x3C0001FF);
   /* Host tcp-active unreliable */
   candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
-  g_assert (nice_candidate_ice_priority (candidate, FALSE, FALSE) == 0x3CC001FF);
+  g_assert_cmpuint (nice_candidate_ice_priority (candidate, FALSE, FALSE) & 0xFFE000FF, ==, 0x3CC000FF);
   /* Host tcp-active reliable */
   candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
   /* Host tcp-active reliable */
-  g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x78C001FF);
+  g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE) & 0xFFE000FF, ==, 0x78C000FF);
   /* srv-reflexive tcp-active reliable */
   candidate->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
   candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
-  g_assert (nice_candidate_ice_priority (candidate, TRUE, FALSE) == 0x648001FF);
+  g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, FALSE) & 0xFFE000FF, ==, 0x648000FF);
   /* nat-assisted srv-reflexive tcp-active reliable */
-  g_assert (nice_candidate_ice_priority (candidate, TRUE, TRUE) == 0x698001FF);
+  g_assert_cmpuint (nice_candidate_ice_priority (candidate, TRUE, TRUE) & 0xFFE000FF, ==, 0x698000FF);
   nice_candidate_free (candidate);
 
   /* test 2 */
   /* 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0)
      = 2^32*1 + 2*5000 + 0
      = 4294977296 */
-  g_assert (nice_candidate_pair_priority (1,5000) == 4294977296LL);
+  g_assert_cmpuint (nice_candidate_pair_priority (1,5000), ==, 4294977296LL);
 
   /* 2^32*1 + 2*5000 + 1 = 4294977297 */
-  g_assert (nice_candidate_pair_priority (5000, 1) == 4294977297LL);
+  g_assert_cmpuint (nice_candidate_pair_priority (5000, 1), ==, 4294977297LL);
 
   return 0;
 }
diff --git a/tests/test-pseudotcp-fin.c b/tests/test-pseudotcp-fin.c
index b769161..d240c96 100644
--- a/tests/test-pseudotcp-fin.c
+++ b/tests/test-pseudotcp-fin.c
@@ -1,7 +1,7 @@
 /*
  * This file is part of the Nice GLib ICE library.
  *
- * (C) 2014 Collabora Ltd.
+ * © 2014, 2015 Collabora Ltd.
  *  Contact: Philip Withnall
  *
  * The contents of this file are subject to the Mozilla Public License Version
@@ -269,6 +269,13 @@ expect_syn_received (Data *data)
   expect_segment (data->right, data->right_sent, 0, 7, 7, FLAG_SYN);
 }
 
+static void
+assert_empty_queues (Data *data)
+{
+  g_assert_cmpuint (g_queue_get_length (data->left_sent), ==, 0);
+  g_assert_cmpuint (g_queue_get_length (data->right_sent), ==, 0);
+}
+
 /* Return whether the socket accepted the packet. */
 static gboolean
 forward_segment (GQueue/*<owned GBytes>*/ *from, PseudoTcpSocket *to)
@@ -453,6 +460,8 @@ establish_connection (Data *data)
   expect_ack (data->left,  data->left_sent, 7, 7);
   forward_segment_ltr (data);
   expect_sockets_connected (data);
+
+  assert_empty_queues (data);
 }
 
 /* Helper to close the LHS of a socket pair which has not transmitted any
@@ -638,7 +647,7 @@ pseudotcp_close_normal_recovery1 (void)
   expect_fin (data.left, data.left_sent, 7, 7);
   drop_segment (data.left, data.left_sent);
 
-  increment_time_both (&data, 300);  /* retransmit timeout */
+  increment_time_both (&data, 1100);  /* retransmit timeout */
 
   expect_fin (data.left, data.left_sent, 7, 7);
   forward_segment_ltr (&data);
@@ -673,7 +682,7 @@ pseudotcp_close_normal_recovery2 (void)
 
   expect_ack (data.right, data.right_sent, 7, 8);
   drop_segment (data.right, data.right_sent);
-  increment_time_both (&data, 300);  /* retransmit timeout */
+  increment_time_both (&data, 1100);  /* retransmit timeout */
   expect_fin (data.left, data.left_sent, 7, 7);
   forward_segment_ltr (&data);
   expect_ack (data.right, data.right_sent, 7, 8);
@@ -753,6 +762,76 @@ pseudotcp_close_normal_recovery4 (void)
   data_clear (&data);
 }
 
+/* Check that closing a connection recovers from a data segment being dropped
+ * immediately before the first FIN is sent. Based on: RFC 793, Figure 13. */
+static void
+pseudotcp_close_normal_recovery_data (void)
+{
+  Data data = { 0, };
+
+  /* Establish a connection. */
+  establish_connection (&data);
+
+  /* Send some data from LHS to RHS, but drop the segment. */
+  g_assert_cmpint (pseudo_tcp_socket_send (data.left, "foo", 3), ==, 3);
+  expect_data (data.left, data.left_sent, 7, 7, 3);
+  drop_segment (data.left, data.left_sent);
+
+  assert_empty_queues(&data);
+
+  /* Close the LHS. */
+  g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0);
+  g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 0);
+  close_socket (data.left);
+
+  expect_socket_state (data.left, TCP_FIN_WAIT_1);
+  expect_fin (data.left, data.left_sent, 10, 7);
+  forward_segment_ltr (&data);
+
+  expect_socket_state (data.right, TCP_ESTABLISHED);
+  expect_ack (data.right, data.right_sent, 7, 7);
+  forward_segment_rtl (&data);
+
+  expect_socket_state (data.left, TCP_FIN_WAIT_1);
+
+  assert_empty_queues(&data);
+
+  /* Close the RHS. */
+  close_socket (data.right);
+
+  expect_socket_state (data.right, TCP_FIN_WAIT_1);
+
+  expect_fin (data.right, data.right_sent, 7, 7);
+  forward_segment_rtl (&data);
+
+  expect_socket_state (data.left, TCP_CLOSING);
+
+  expect_ack (data.left, data.left_sent, 11, 8);
+  forward_segment_ltr (&data);
+
+  expect_socket_state (data.right, TCP_FIN_WAIT_2);
+
+  expect_data (data.right, data.right_sent, 8, 7, 0);
+  forward_segment_rtl (&data);
+  expect_socket_state (data.left, TCP_CLOSING);
+
+  expect_data (data.left, data.left_sent, 7, 8, 3);
+  forward_segment_ltr (&data);
+  expect_socket_state (data.right, TCP_TIME_WAIT);
+
+  increment_time_both (&data, 100);  /* Delayed ACK */
+
+  expect_ack (data.right, data.right_sent, 8, 11);
+  forward_segment_rtl (&data);
+  expect_socket_state (data.left, TCP_TIME_WAIT);
+
+  increment_time_both (&data, 10);  /* TIME-WAIT */
+
+  expect_sockets_closed (&data);
+
+  data_clear (&data);
+}
+
 /* Check that if both FIN segments from a simultaneous FIN handshake are
  * dropped, the handshake recovers and completes successfully.
  * See: RFC 793, Figure 14. */
@@ -773,7 +852,7 @@ pseudotcp_close_simultaneous_recovery1 (void)
   drop_segment (data.left, data.left_sent);
   drop_segment (data.right, data.right_sent);
 
-  increment_time_both (&data, 400);  /* retransmit timeout */
+  increment_time_both (&data, 1200);  /* retransmit timeout */
 
   expect_fin (data.left, data.left_sent, 7, 7);
   expect_fin (data.right, data.right_sent, 7, 7);
@@ -817,7 +896,7 @@ pseudotcp_close_simultaneous_recovery2 (void)
   drop_segment (data.left, data.left_sent);
   drop_segment (data.right, data.right_sent);
 
-  increment_time_both (&data, 400);  /* retransmit timeout */
+  increment_time_both (&data, 1200);  /* retransmit timeout */
 
   expect_fin (data.left, data.left_sent, 7, 8);
   expect_fin (data.right, data.right_sent, 7, 8);
@@ -977,11 +1056,14 @@ pseudotcp_close_rst_afterwards (void)
 
   /* Close the LHS. */
   g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0);
+  pseudo_tcp_socket_close (data.left, TRUE);
   close_socket (data.left);
 
-  expect_fin (data.left, data.left_sent, 7, 7);
+  expect_rst (data.left, data.left_sent, 7, 7);
   drop_segment (data.left, data.left_sent);  /* just to get it out of the way */
 
+  assert_empty_queues(&data);
+
   /* Send some data from RHS to LHS, which should result in an RST. */
   g_assert_cmpint (pseudo_tcp_socket_send (data.right, "foo", 3), ==, 3);
   expect_data (data.right, data.right_sent, 7, 7, 3);
@@ -1059,6 +1141,63 @@ pseudotcp_compatibility (void)
   data_clear (&data);
 }
 
+
+/* Check that after receiving a FIN, queued data can still be read */
+static void
+pseudotcp_close_recv_queued (void)
+{
+  Data data = { 0, };
+  guint8 buf[100];
+
+  /* Establish a connection. */
+  establish_connection (&data);
+
+  g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0);
+  g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 0);
+  g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.right), >,
+      0);
+  g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.left), >,
+      0);
+
+  g_assert_cmpint (pseudo_tcp_socket_send (data.left, "foo", 3), ==, 3);
+  expect_data (data.left, data.left_sent, 7, 7, 3);
+  forward_segment_ltr (&data);
+
+  increment_time_both (&data, 100);  /* Delayed ACK */
+  expect_ack (data.right, data.right_sent, 7, 10);
+  forward_segment_rtl (&data);
+
+  close_socket (data.left);
+  expect_fin (data.left, data.left_sent, 10, 7);
+  forward_segment_ltr (&data);
+
+  expect_socket_state (data.left, TCP_FIN_WAIT_1);
+  expect_socket_state (data.right, TCP_CLOSE_WAIT);
+
+  g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.left), ==, 0);
+  g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.left), ==,
+      0);
+
+  expect_ack (data.right, data.right_sent, 7, 11);
+  forward_segment_rtl (&data);
+
+  expect_socket_state (data.left, TCP_FIN_WAIT_2);
+
+
+  g_assert_cmpint (pseudo_tcp_socket_get_available_bytes (data.right), ==, 3);
+
+  g_assert_cmpint (pseudo_tcp_socket_get_available_send_space (data.right), >,
+      0);
+
+  /* Check that the data can be read */
+  g_assert_cmpint (pseudo_tcp_socket_recv (data.right, (char *) buf, sizeof (buf)), ==, 3);
+
+  /* Now the socket should be empty */
+  g_assert_cmpint (pseudo_tcp_socket_recv (data.right, (char *) buf, sizeof (buf)), ==, 0);
+
+  data_clear (&data);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -1109,6 +1248,8 @@ main (int argc, char *argv[])
       pseudotcp_close_normal_recovery3);
   g_test_add_func ("/pseudotcp/close/normal/recovery4",
       pseudotcp_close_normal_recovery4);
+  g_test_add_func ("/pseudotcp/close/normal/recovery-data",
+      pseudotcp_close_normal_recovery_data);
   g_test_add_func ("/pseudotcp/close/simultaneous/recovery1",
       pseudotcp_close_simultaneous_recovery1);
   g_test_add_func ("/pseudotcp/close/simultaneous/recovery2",
@@ -1125,6 +1266,9 @@ main (int argc, char *argv[])
   g_test_add_func ("/pseudotcp/close/rst-afterwards",
       pseudotcp_close_rst_afterwards);
 
+  g_test_add_func ("/pseudotcp/close/recv-queued",
+      pseudotcp_close_recv_queued);
+
   g_test_add_func ("/pseudotcp/compatibility",
       pseudotcp_compatibility);
 
diff --git a/tests/test-pseudotcp-fuzzy.c b/tests/test-pseudotcp-fuzzy.c
index fdee222..4211248 100644
--- a/tests/test-pseudotcp-fuzzy.c
+++ b/tests/test-pseudotcp-fuzzy.c
@@ -395,7 +395,6 @@ int main (int argc, char *argv[])
   GError *error = NULL;
 
   setlocale (LC_ALL, "");
-  g_type_init ();
 
   /* Configuration. */
   context = g_option_context_new ("— fuzz-test the pseudotcp socket");
diff --git a/tests/test-pseudotcp.c b/tests/test-pseudotcp.c
index e4dd613..1a8391a 100644
--- a/tests/test-pseudotcp.c
+++ b/tests/test-pseudotcp.c
@@ -259,8 +259,6 @@ int main (int argc, char *argv[])
 
   mainloop = g_main_loop_new (NULL, FALSE);
 
-  g_type_init ();
-
   pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE);
 
   left_closed = right_closed = FALSE;
diff --git a/tests/test-restart.c b/tests/test-restart.c
index c7f2f25..c2cbe9a 100644
--- a/tests/test-restart.c
+++ b/tests/test-restart.c
@@ -400,8 +400,6 @@ int main (void)
 
   WSAStartup(0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init(NULL);
 
   global_mainloop = g_main_loop_new (NULL, FALSE);
 
diff --git a/tests/test-send-recv.c b/tests/test-send-recv.c
index 55e6002..5841639 100644
--- a/tests/test-send-recv.c
+++ b/tests/test-send-recv.c
@@ -1202,8 +1202,6 @@ main (int argc, char *argv[])
   WSADATA w;
   WSAStartup (0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init (NULL);
 
   if (!long_mode) {
     /* Quick mode. Just test each of the stream APIs in reliable and
diff --git a/tests/test-socket-is-based-on.c b/tests/test-socket-is-based-on.c
new file mode 100644
index 0000000..6080fe3
--- /dev/null
+++ b/tests/test-socket-is-based-on.c
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * (C) 2016 Jakub Adam <jakub.adam@ktknet.cz>
+ *
+ * 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.
+ *
+ * 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 <locale.h>
+
+#include "socket.h"
+
+static NiceSocket *udp_bsd;
+static NiceSocket *tcp_active;
+static NiceSocket *pseudossl;
+static NiceSocket *udp_turn_over_tcp;
+
+static void
+socket_base_udp_bsd (void)
+{
+  g_assert (nice_socket_is_based_on (udp_bsd, udp_bsd));
+  g_assert (!nice_socket_is_based_on (udp_bsd, tcp_active));
+  g_assert (!nice_socket_is_based_on (udp_bsd, pseudossl));
+  g_assert (!nice_socket_is_based_on (udp_bsd, udp_turn_over_tcp));
+}
+
+static void
+socket_base_tcp_active (void)
+{
+  g_assert (!nice_socket_is_based_on (tcp_active, udp_bsd));
+  g_assert (nice_socket_is_based_on (tcp_active, tcp_active));
+  g_assert (!nice_socket_is_based_on (tcp_active, pseudossl));
+  g_assert (!nice_socket_is_based_on (tcp_active, udp_turn_over_tcp));
+}
+
+static void
+socket_base_pseudossl (void)
+{
+  g_assert (!nice_socket_is_based_on (pseudossl, udp_bsd));
+  g_assert (nice_socket_is_based_on (pseudossl, tcp_active));
+  g_assert (nice_socket_is_based_on (pseudossl, pseudossl));
+  g_assert (!nice_socket_is_based_on (pseudossl, udp_turn_over_tcp));
+}
+
+static void
+socket_base_udp_turn_over_tcp (void)
+{
+  g_assert (!nice_socket_is_based_on (udp_turn_over_tcp, udp_bsd));
+  g_assert (nice_socket_is_based_on (udp_turn_over_tcp, tcp_active));
+  g_assert (nice_socket_is_based_on (udp_turn_over_tcp, pseudossl));
+  g_assert (nice_socket_is_based_on (udp_turn_over_tcp, udp_turn_over_tcp));
+}
+
+int
+main (int argc, char *argv[])
+{
+  GMainLoop *mainloop = NULL;
+
+  NiceAddress addr;
+
+  setlocale (LC_ALL, "");
+  g_test_init (&argc, &argv, NULL);
+
+  mainloop = g_main_loop_new (NULL, TRUE);
+
+  nice_address_set_from_string (&addr, "127.0.0.1");
+
+  /* Standalone socket */
+  udp_bsd = nice_udp_bsd_socket_new (&addr);
+
+  /* tcp_passive -> pseudossl -> udp_turn_over_tcp */
+  tcp_active = nice_tcp_active_socket_new (g_main_loop_get_context (mainloop),
+      &addr);
+  pseudossl = nice_pseudossl_socket_new (tcp_active,
+      NICE_PSEUDOSSL_SOCKET_COMPATIBILITY_GOOGLE);
+  udp_turn_over_tcp = nice_udp_turn_over_tcp_socket_new (pseudossl,
+      NICE_TURN_SOCKET_COMPATIBILITY_GOOGLE);
+
+  g_test_add_func ("/socket/is-base-of/udp-bsd",
+      socket_base_udp_bsd);
+  g_test_add_func ("/socket/is-base-of/tcp-active",
+      socket_base_tcp_active);
+  g_test_add_func ("/socket/is-base-of/pseudossl",
+      socket_base_pseudossl);
+  g_test_add_func ("/socket/is-base-of/udp-turn-over-tcp",
+      socket_base_udp_turn_over_tcp);
+
+  g_test_run ();
+
+  nice_socket_free (udp_bsd);
+  nice_socket_free (udp_turn_over_tcp);
+
+  g_main_loop_unref (mainloop);
+
+  return 0;
+}
diff --git a/tests/test-tcp.c b/tests/test-tcp.c
index dd259a4..e2a1bfd 100644
--- a/tests/test-tcp.c
+++ b/tests/test-tcp.c
@@ -88,8 +88,6 @@ main (void)
   NiceAddress active_bind_addr, passive_bind_addr;
   GSource *srv_listen_source, *srv_input_source, *cli_input_source;
 
-  g_type_init ();
-
   mainloop = g_main_loop_new (NULL, FALSE);
 
   nice_address_init (&active_bind_addr);
diff --git a/tests/test-thread.c b/tests/test-thread.c
index df0b145..7493f97 100644
--- a/tests/test-thread.c
+++ b/tests/test-thread.c
@@ -211,8 +211,6 @@ int main (void)
   WSADATA w;
   WSAStartup(0x0202, &w);
 #endif
-  g_type_init ();
-  g_thread_init(NULL);
 
   lmainctx = g_main_context_new ();
   rmainctx = g_main_context_new ();
@@ -291,13 +289,8 @@ int main (void)
   /* step: run test the first time */
   g_debug ("test-thread: TEST STARTS / running test for the 1st time");
 
-#if !GLIB_CHECK_VERSION(2,31,8)
-  lthread = g_thread_create (mainloop_thread, lmainloop, TRUE, NULL);
-  rthread = g_thread_create (mainloop_thread, rmainloop, TRUE, NULL);
-#else
   lthread = g_thread_new ("lthread libnice", mainloop_thread, lmainloop);
   rthread = g_thread_new ("rthread libnice", mainloop_thread, rmainloop);
-#endif
 
   g_assert (lthread);
   g_assert (rthread);
@@ -318,13 +311,9 @@ int main (void)
   nice_agent_attach_recv (ragent, rs_id, 1, rdmainctx, cb_nice_recv,
       GUINT_TO_POINTER (2));
 
-#if !GLIB_CHECK_VERSION(2,31,8)
-  ldthread = g_thread_create (mainloop_thread, ldmainloop, TRUE, NULL);
-  rdthread = g_thread_create (mainloop_thread, rdmainloop, TRUE, NULL);
-#else
   ldthread = g_thread_new ("ldthread libnice", mainloop_thread, ldmainloop);
   rdthread = g_thread_new ("rdthread libnice", mainloop_thread, rdmainloop);
-#endif
+
   g_assert (ldthread);
   g_assert (rdthread);
 
diff --git a/tests/test-turn.c b/tests/test-turn.c
new file mode 100644
index 0000000..46d1bf2
--- /dev/null
+++ b/tests/test-turn.c
@@ -0,0 +1,379 @@
+#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;
+
+#define TURN_USER "toto"
+#define TURN_PASS "password"
+
+static gboolean timer_cb (gpointer pointer)
+{
+  g_debug ("test-turn:%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-fullmode:%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;
+
+#if 0
+  if (GPOINTER_TO_UINT (user_data) == 2) {
+    global_ragent_read += len;
+  }
+#endif
+}
+
+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data)
+{
+  g_debug ("test-fullmode:%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-fullmode:%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-turn: 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-turn:%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, gboolean remove_non_relay,
+    gboolean force_relay)
+{
+  GSList *cands = NULL, *i;
+
+  cands = nice_agent_get_local_candidates (from, from_stream, component);
+  if (remove_non_relay) {
+  restart:
+    for (i = cands; i; i = i->next) {
+      NiceCandidate *cand = i->data;
+      if (force_relay)
+        g_assert (cand->type == NICE_CANDIDATE_TYPE_RELAYED);
+      if (cand->type != NICE_CANDIDATE_TYPE_RELAYED) {
+        cands = g_slist_remove (cands, cand);
+        nice_candidate_free (cand);
+        goto restart;
+      }
+    }
+  }
+  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(guint turn_port, gboolean is_ipv6,
+    gboolean ice_udp, gboolean ice_tcp, gboolean force_relay,
+    gboolean remove_non_relay,
+    NiceRelayType turn_type)
+{
+  NiceAgent *lagent, *ragent;      /* agent's L and R */
+  const gchar *localhost;
+  NiceAddress localaddr;
+  guint ls_id, rs_id;
+  gulong timer_id;
+
+  if (is_ipv6)
+    localhost = "::1";
+  else
+    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 (NULL, NICE_COMPATIBILITY_RFC5245);
+  ragent = nice_agent_new (NULL, NICE_COMPATIBILITY_RFC5245);
+
+  g_object_set (G_OBJECT (lagent), "ice-tcp", ice_tcp, "ice-udp", ice_udp,
+      "force-relay", force_relay, NULL);
+  g_object_set (G_OBJECT (ragent), "ice-tcp", ice_tcp, "ice-udp", ice_udp,
+      "force-relay", force_relay, 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-turn, Left Agent");
+  nice_agent_set_software (ragent, "Test-turn, 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);
+  nice_agent_set_relay_info(lagent, ls_id, 1,
+      localhost, turn_port, TURN_USER, TURN_PASS, turn_type);
+  nice_agent_set_relay_info(ragent, rs_id, 1,
+      localhost, turn_port, TURN_USER, TURN_PASS, turn_type);
+
+  /* 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_assert (global_lagent_gathering_done == FALSE);
+  g_assert (global_ragent_gathering_done == FALSE);
+  g_debug ("test-turn: Added streams, running context until 'candidate-gathering-done'...");
+  while (!global_lagent_gathering_done && !global_ragent_gathering_done)
+    g_main_context_iteration (NULL, TRUE);
+  g_assert (global_lagent_gathering_done == 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,
+      remove_non_relay, force_relay);
+  set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP,
+      remove_non_relay, force_relay);
+
+  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);
+}
+
+guint global_turn_port;
+
+static void
+udp_no_force_no_remove_udp (void)
+{
+  run_test(global_turn_port, FALSE /* is_ipv6 */,
+      TRUE /* ice_udp */,
+      FALSE /* ice_tcp */,
+      FALSE /* force_relay */,
+      FALSE /* remove_non_relay */,
+      NICE_RELAY_TYPE_TURN_UDP);
+}
+
+static void
+udp_no_force_remove_udp (void)
+{
+  run_test(global_turn_port, FALSE /* is_ipv6 */,
+      TRUE /* ice_udp */,
+      FALSE /* ice_tcp */,
+      FALSE /* force_relay */,
+      TRUE /* remove_non_relay */,
+      NICE_RELAY_TYPE_TURN_UDP);
+}
+
+static void
+udp_force_no_remove_udp (void)
+{
+  run_test(global_turn_port, FALSE /* is_ipv6 */,
+      TRUE /* ice_udp */,
+      FALSE /* ice_tcp */,
+      TRUE /* force_relay */,
+      FALSE /* remove_non_relay */,
+      NICE_RELAY_TYPE_TURN_UDP);
+}
+
+static void
+udp_no_force_no_remove_tcp (void)
+{
+  run_test(global_turn_port, FALSE /* is_ipv6 */,
+      TRUE /* ice_udp */,
+      FALSE /* ice_tcp */,
+      FALSE /* force_relay */,
+      FALSE /* remove_non_relay */,
+      NICE_RELAY_TYPE_TURN_TCP);
+}
+
+static void
+udp_no_force_remove_tcp (void)
+{
+  run_test(global_turn_port, FALSE /* is_ipv6 */,
+      TRUE /* ice_udp */,
+      FALSE /* ice_tcp */,
+      FALSE /* force_relay */,
+      TRUE /* remove_non_relay */,
+      NICE_RELAY_TYPE_TURN_TCP);
+}
+
+static void
+udp_force_no_remove_tcp (void)
+{
+  run_test(global_turn_port, FALSE /* is_ipv6 */,
+      TRUE /* ice_udp */,
+      FALSE /* ice_tcp */,
+      TRUE /* force_relay */,
+      FALSE /* remove_non_relay */,
+      NICE_RELAY_TYPE_TURN_TCP);
+}
+
+
+
+
+
+int
+main (int argc, char **argv)
+{
+  GSubprocess *sp;
+  GError *error = NULL;
+  gchar portstr[10];
+  int ret;
+  gchar *out_str = NULL;
+  gchar *err_str = NULL;
+
+  g_test_init (&argc, &argv, NULL);
+
+  global_turn_port = g_random_int_range (10000, 60000);
+  snprintf(portstr, 9, "%u", global_turn_port);
+
+  if (g_spawn_command_line_sync ("turnserver --help", &out_str, &err_str, NULL,
+          NULL) && err_str) {
+    if (!strstr(err_str, "--user")) {
+      g_print ("rfc5766-turn-server not installed, skipping turn test\n");
+      return 0;
+    }
+  } else {
+    g_print ("rfc5766-turn-server not installed, skipping turn test\n");
+    return 0;
+  }
+  g_free (err_str);
+  g_free (out_str);
+
+  sp = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, &error,
+      "turnserver",
+      "--user", "toto:0xaae440b3348d50265b63703117c7bfd5",
+      "--realm", "realm",
+      "--listening-port", portstr,
+      NULL);
+
+  g_test_add_func ("/nice/turn/udp", udp_no_force_no_remove_udp);
+  g_test_add_func ("/nice/turn/udp/remove_non_turn",
+      udp_no_force_remove_udp);
+  g_test_add_func ("/nice/turn/udp/force_relay",
+      udp_force_no_remove_udp);
+  g_test_add_func ("/nice/turn/udp/over-tcp", udp_no_force_no_remove_tcp);
+  g_test_add_func ("/nice/turn/udp/over-tcp/remove_non_turn",
+      udp_no_force_remove_tcp);
+  g_test_add_func ("/nice/turn/udp/over-tcp/force_relay",
+      udp_force_no_remove_tcp);
+
+  ret = g_test_run ();
+
+  g_subprocess_force_exit (sp);
+  g_subprocess_wait (sp, NULL, NULL);
+  g_clear_object (&sp);
+
+  return ret;
+}
diff --git a/tests/test.c b/tests/test.c
index 31a9fc7..a92b33c 100644
--- a/tests/test.c
+++ b/tests/test.c
@@ -75,9 +75,6 @@ main (void)
 
   nice_address_init (&addr_local);
   nice_address_init (&addr_remote);
-  g_type_init ();
-
-  g_thread_init(NULL);
 
   g_assert (nice_address_set_from_string (&addr_local, "127.0.0.1"));
   g_assert (nice_address_set_from_string (&addr_remote, "127.0.0.1"));
diff --git a/win32/vs9/libnice.def b/win32/vs9/libnice.def
deleted file mode 100644
index 7065330..0000000
--- a/win32/vs9/libnice.def
+++ /dev/null
@@ -1,137 +0,0 @@
-LIBRARY libnice
-
-EXPORTS
-
-nice_address_copy_to_sockaddr
-nice_address_dup
-nice_address_equal
-nice_address_equal_no_port
-nice_address_free
-nice_address_get_port
-nice_address_init
-nice_address_ip_version
-nice_address_is_private
-nice_address_is_valid
-nice_address_new
-nice_address_set_from_sockaddr
-nice_address_set_from_string
-nice_address_set_ipv4
-nice_address_set_ipv6
-nice_address_set_port
-nice_address_to_string
-nice_agent_add_local_address
-nice_agent_add_stream
-nice_agent_attach_recv
-nice_agent_forget_relays
-nice_agent_gather_candidates
-nice_agent_generate_local_candidate_sdp
-nice_agent_generate_local_sdp
-nice_agent_generate_local_stream_sdp
-nice_agent_get_default_local_candidate
-nice_agent_get_local_candidates
-nice_agent_get_local_credentials
-nice_agent_get_remote_candidates
-nice_agent_get_selected_pair
-nice_agent_get_selected_socket
-nice_agent_get_stream_name
-nice_agent_get_type
-nice_agent_new
-nice_agent_new_reliable
-nice_agent_parse_remote_candidate_sdp
-nice_agent_parse_remote_sdp
-nice_agent_parse_remote_stream_sdp
-nice_agent_remove_stream
-nice_agent_restart
-nice_agent_send
-nice_agent_set_port_range
-nice_agent_set_relay_info
-nice_agent_set_remote_candidates
-nice_agent_set_remote_credentials
-nice_agent_set_local_credentials
-nice_agent_set_selected_pair
-nice_agent_set_selected_remote_candidate
-nice_agent_set_software
-nice_agent_set_stream_name
-nice_agent_set_stream_tos
-nice_candidate_copy
-nice_candidate_free
-nice_candidate_new
-nice_component_state_to_string
-nice_debug_disable
-nice_debug_enable
-nice_interfaces_get_ip_for_interface
-nice_interfaces_get_local_interfaces
-nice_interfaces_get_local_ips
-pseudo_tcp_set_debug_level
-pseudo_tcp_socket_close
-pseudo_tcp_socket_connect
-pseudo_tcp_socket_get_error
-pseudo_tcp_socket_get_next_clock
-pseudo_tcp_socket_new
-pseudo_tcp_socket_notify_clock
-pseudo_tcp_socket_notify_mtu
-pseudo_tcp_socket_notify_packet
-pseudo_tcp_socket_recv
-pseudo_tcp_socket_send
-stun_agent_build_unknown_attributes_error
-stun_agent_default_validater
-stun_agent_finish_message
-stun_agent_forget_transaction
-stun_agent_init
-stun_agent_init_error
-stun_agent_init_indication
-stun_agent_init_request
-stun_agent_init_response
-stun_agent_set_software
-stun_agent_validate
-stun_debug_disable
-stun_debug_enable
-stun_debug
-stun_debug_bytes
-stun_hash_creds
-stun_message_append
-stun_message_append32
-stun_message_append64
-stun_message_append_addr
-stun_message_append_bytes
-stun_message_append_error
-stun_message_append_flag
-stun_message_append_string
-stun_message_append_xor_addr
-stun_message_append_xor_addr_full
-stun_message_find
-stun_message_find32
-stun_message_find64
-stun_message_find_addr
-stun_message_find_error
-stun_message_find_flag
-stun_message_find_string
-stun_message_find_xor_addr
-stun_message_find_xor_addr_full
-stun_message_get_class
-stun_message_get_method
-stun_message_has_attribute
-stun_message_has_cookie
-stun_message_id
-stun_message_init
-stun_message_length
-stun_message_validate_buffer_length
-stun_optional
-stun_strerror
-stun_timer_refresh
-stun_timer_remainder
-stun_timer_start
-stun_timer_start_reliable
-stun_usage_bind_create
-stun_usage_bind_keepalive
-stun_usage_bind_process
-stun_usage_bind_run
-stun_usage_ice_conncheck_create
-stun_usage_ice_conncheck_create_reply
-stun_usage_ice_conncheck_priority
-stun_usage_ice_conncheck_process
-stun_usage_ice_conncheck_use_candidate
-stun_usage_turn_create
-stun_usage_turn_create_refresh
-stun_usage_turn_process
-stun_usage_turn_refresh_process