From 24424c5121b3187f4756f7579f4ff04f2a15f4f4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Jun 10 2016 18:59:12 +0000 Subject: More updates from git --- diff --git a/libnice-0.1.13-20160606.patch b/libnice-0.1.13-20160606.patch deleted file mode 100644 index 3761e68..0000000 --- a/libnice-0.1.13-20160606.patch +++ /dev/null @@ -1,10675 +0,0 @@ -commit fad72879fa4a0896c55ac6fc5f77f6c05e369a2b -Author: Olivier Crête -Date: Fri Jun 3 18:42:59 2016 -0400 - - pseudotcp: it's still a GObject - -commit f645ea6b11c167b1d7f4c5034f79664bdb8706d6 -Author: Olivier Crête -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 -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 -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 -Date: Wed Apr 6 01:59:36 2016 +0300 - - pseudotcp: Implement full NewReno - -commit e31932bdf25ce545a88fe6078b9557bc4d9e6365 -Author: Olivier Crête -Date: Tue Feb 2 16:59:18 2016 -0500 - - pseudotcp: Make debug more useful - -commit 8ccb2c1711a2e5cdebf9411764fd92d5a089ffbf -Author: Olivier Crête -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 -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 -Date: Thu Sep 17 21:26:36 2015 -0400 - - pseudotcp: Export more symbols for PseudoTCP - -commit 026c15a838554c30ea96a59b08a2064b61d62736 -Author: Olivier Crête -Date: Thu Sep 17 15:00:27 2015 -0400 - - pseudotcp: Make structs definitions private - -commit 11d4bb9783a69363de80ff49638030ba892a93fe -Author: Philip Withnall -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -Date: Tue May 31 17:31:18 2016 -0400 - - Revert "WIP" - - This reverts commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a. - -commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a -Author: Olivier Crête -Date: Tue May 31 17:31:12 2016 -0400 - - WIP - -commit 955323915c43b1a066399e61d0ab091f0b1d112b -Author: Jakub Adam -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 -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 -Date: Fri Jan 15 22:40:27 2016 -0500 - - component: Add API to cleanly remove a base socket - -commit 93330f1f97e4f2a9ff09b602765e620cb279574b -Author: Olivier Crête -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -Date: Tue Mar 1 15:27:46 2016 -0500 - - candidate: Give lower priority to TCP relayed candidates - -commit 8ee6d1bbed87fda37ade7b5c5d9483f41037b06a -Author: Olivier Crête -Date: Mon Feb 29 16:11:18 2016 -0500 - - udp-turn: Fix binding timeout leak - -commit 8f1f615e92cd56ad4d8487457c2fde2c4aaa51d9 -Author: Olivier Crête -Date: Wed Feb 24 22:53:08 2016 -0500 - - conncheck: Update selected pair if necessary - -commit 65f2eda04c1c73cc7ebc3df2032d528eedc236e1 -Author: Olivier Crête -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 -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 -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 -Date: Thu Feb 18 14:20:52 2016 -0500 - - conncheck: Make very frequent debug verbose-only - -commit b7c2eabfd0bd8c1321d9e8450caa8fa6b6ecb5ab -Author: Olivier Crête -Date: Mon Feb 15 19:09:58 2016 -0500 - - debug: Enable based on G_MESSAGES_DEBUG - -commit acfe2b1f366d5f33314db7e9878225f2a70358ef -Author: Olivier Crête -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 -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 -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 -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 -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 -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 -Date: Mon Feb 8 21:04:24 2016 -0500 - - conncheck: Deduplicate conncheck stopping code - -commit f122e4174d19c60bc434f3986f5c08f8673344bd -Author: Olivier Crête -Date: Mon Feb 8 19:41:28 2016 -0500 - - Reset to connecting if reconnected after failed - -commit 9c1a41b06ab459ce33f32d25ce258fb21ba49047 -Author: Olivier Crête -Date: Thu Jan 14 17:59:16 2016 -0500 - - agent: Add warning on ignored result - -commit a357b17f5f3415320b9ec7122738396ccd998521 -Author: Fabrice Bellet -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 - Differential Revision: https://phabricator.freedesktop.org/D877 - -commit b986d6e5f2ee0b7b0e09031c1a369bf89153e4c5 -Author: Fabrice Bellet -Date: Mon Apr 4 22:38:07 2016 +0100 - - agent: remove newline from debug output - - Just a cosmetic fix. - - Reviewed-by: Philip Withnall - Differential Revision: https://phabricator.freedesktop.org/D872 - -commit e0ed4fb3a236710846d9129438e0077782569633 -Author: Jakub Adam -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 - Differential Revision: https://phabricator.freedesktop.org/D866 - -commit 38268e53fde8cd97055d88d2066c0016fe04b31b -Author: Jakub Adam -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 - Reviewed-by: José Antonio Santos Cadenas - Reviewed-by: Philip Withnall - Reviewed-by: José Antonio Santos Cadenas - Differential Revision: https://phabricator.freedesktop.org/D785 - -commit 7037ab4cf384edd9f700bc221a9d980b30d9c64f -Author: Fabrice Bellet -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 - Reviewed-by: Philip Withnall - Differential Revision: https://phabricator.freedesktop.org/D802 - -commit 1732c7d6a7a104438412309373818e493a2504c9 -Author: Olivier Crête -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 -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 - Differential Revision: https://phabricator.freedesktop.org/D807 - -commit ed75d55cf279613bb736f7646d3010d816797ddf -Author: Jakub Adam -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 - Differential Revision: https://phabricator.freedesktop.org/D786 - -commit 1493a381d5bf6e15348c2bc17270f45b69cb70d2 -Author: Philip Withnall -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 -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 - Differential Revision: https://phabricator.freedesktop.org/D822 - -commit 38c5e66886cb8138d6be57b8a4721d9b42a358b7 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D742 - -commit 70981d41edf46a09393017f3de34748fcecea046 -Author: Philip Withnall -Date: Tue Mar 1 23:05:20 2016 +0000 - - simple-example: transmission can begin earlier than in ready state - - Reviewed-by: Olivier Crête - Differential Revision: https://phabricator.freedesktop.org/D819 - -commit 47eaf50a99d91c4a666f05c4de24613706847c88 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D817 - -commit 4e68244a51698aefdf44dd1ceb17b95275e655bf -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D806 - -commit 0f1e6c64515871298b115b497b019506b8065235 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D804 - -commit 17488a20d8db8ea1f8fa2d7a44090070407d6db8 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D815 - -commit 3ce45c25af238fb4d9a040abb44597531140db2d -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D818 - -commit c309905ff446bed2dc47811023b98bc586a02d63 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D808 - -commit 80973c096de872983bc60cd28a84653931f8d601 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D803 - -commit 41ab61def82aa275649afd5f4a3fcb43e75fb360 -Author: Mike Ruprecht -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 - -commit 9638bc132f909177f6ecfb86cdd17af557a35c56 -Author: Jose Antonio Santos Cadenas -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 - https://phabricator.freedesktop.org/T3492 - -commit 3e71f42c8b15790e252d850ba42a7ae7e7cc69a9 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D345 - -commit 53283c218a5eb7a29e7019ac320e74f9dbe4b3fc -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D292 - -commit aef748ec7d0b06067312e5cc761a6375cd0f699c -Author: Philip Withnall -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 -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 - Differential Revision: https://phabricator.freedesktop.org/D309 - -commit 529bc193d56522b10a4de83409396b3316863934 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D308 - -commit ef2b58f64887546e426dd8cda382f2908f84caca -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D307 - -commit 1f08419382c52c7e796da06c9271a362aa60333d -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D306 - -commit da70716162b2085ca4db6e7efd446fcda7bd488d -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D301 - -commit 66e47aa39f9cd3666e610fab78caa0c7d8f9c410 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D300 - -commit 1ae15f66af2af17e991ab028ca16a1200fd5f4e5 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D299 - -commit ea3348020da586f20e1a64c9732405e730563616 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D298 - -commit 9f10231fc787e4683e896bf35f086906a5b16c03 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D297 - -commit dac3e280d6f3fd792f1d79313debd03c0df282c4 -Author: Philip Withnall -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 - Differential Revision: https://phabricator.freedesktop.org/D296 - -commit fae0f9d37c6e34f34c92dee85384c29e565bdbce -Author: Jakub Adam -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 - -commit 02852728ef347f5cb4c9227848b266c72b5fe38b -Author: Jakub Adam -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 - -commit 84eaa12b0b1a76c45ce2d77294e0477c10552cd4 -Author: Jakub Adam -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 - Differential Revision: https://phabricator.freedesktop.org/D241 - -commit 837c8953fe87bdd5d5bccc444e72739100578ef8 -Author: Jakub Adam -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 - Differential Revision: https://phabricator.freedesktop.org/D240 - -commit c6bc33c031493e0db11a1f57055a656f6428c60a -Author: Philip Withnall -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 on the mailing list. - -commit 757f8aecdb4fda86b2bbac828a3acc528d8eb8bc -Author: Philip Withnall -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -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 -Date: Wed Aug 19 09:24:27 2015 +0100 - - build: Update .gitignore - - Add stun/usages/.dirstamp. - -commit 6c9afb4db35168cc699ff0aa2e56b127f3706083 -Author: Philip Withnall -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 -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 -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 -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 -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 -Date: Tue Aug 18 14:03:41 2015 +0100 - - agent: Remove unused inet_pton() function - - Summary: - As spotted by Felix . 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 -Date: Tue Jun 30 14:39:51 2015 +0100 - - agent: Remove unused inet_pton() function - - As spotted by Felix . 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 -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 -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 - Reviewed-by: Olivier Crête - - http://phabricator.freedesktop.org/T117 - -commit cd61af3114a51df53619fb0460ace2b3660fc5da -Author: Philip Withnall -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 and Felix - . - - Reviewed-by: Olivier Crete - Reviewed-by: Felix Schlitter - - http://phabricator.freedesktop.org/T122 - -commit 3cccc311b1becf4307f5a4004734d8f7b2cf84f6 -Author: Felix Schlitter -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 -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 -Date: Thu Sep 18 19:33:06 2014 -0400 - - stun: Remove annoying non-error on non-STUN packet - -commit 81a929ac141aae66b6450e8ce93cb357ed404cda -Author: Timo Gurr -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 -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 -Date: Tue May 5 15:07:10 2015 -0400 - - Removing no-op assignment - -commit 91a7b9324244844baf35d8fcef019a4ea3872d30 -Author: Youness Alaoui -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 -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 -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/**/ *ips = NULL; -+ GList/**/ *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 - --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..5cba478 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; -@@ -3379,7 +3675,8 @@ conn_check_prune_socket (NiceAgent *agent, Stream *stream, Component *component, - CandidateCheckPair *p = l->data; - - 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); -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, "<-- " -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Sending " - "", - 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, "--> " -+ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, -+ "Received " - "", - 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 -+ -+#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 - --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 - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include --#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 - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include --#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 - --#if GLIB_CHECK_VERSION(2, 36, 0) - #include --#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 - #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 - #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 - #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 ( - ¤t_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-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/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/**/ *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 -+ * -+ * 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 -+ -+#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 -+#include -+#include -+ -+#include -+#include -+ -+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 diff --git a/libnice-0.1.13-20160610.patch b/libnice-0.1.13-20160610.patch new file mode 100644 index 0000000..a5f2edb --- /dev/null +++ b/libnice-0.1.13-20160610.patch @@ -0,0 +1,10771 @@ +commit 30a0c230ae9b70c572060ad3037f68e102e4759a +Author: Olivier Crête +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 +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 +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 +Date: Fri Jun 3 18:42:59 2016 -0400 + + pseudotcp: it's still a GObject + +commit f645ea6b11c167b1d7f4c5034f79664bdb8706d6 +Author: Olivier Crête +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 +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 +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 +Date: Wed Apr 6 01:59:36 2016 +0300 + + pseudotcp: Implement full NewReno + +commit e31932bdf25ce545a88fe6078b9557bc4d9e6365 +Author: Olivier Crête +Date: Tue Feb 2 16:59:18 2016 -0500 + + pseudotcp: Make debug more useful + +commit 8ccb2c1711a2e5cdebf9411764fd92d5a089ffbf +Author: Olivier Crête +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 +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 +Date: Thu Sep 17 21:26:36 2015 -0400 + + pseudotcp: Export more symbols for PseudoTCP + +commit 026c15a838554c30ea96a59b08a2064b61d62736 +Author: Olivier Crête +Date: Thu Sep 17 15:00:27 2015 -0400 + + pseudotcp: Make structs definitions private + +commit 11d4bb9783a69363de80ff49638030ba892a93fe +Author: Philip Withnall +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +Date: Tue May 31 17:31:18 2016 -0400 + + Revert "WIP" + + This reverts commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a. + +commit 01519677ba4d8df46e2c07bc20a5ef03ee2d9c3a +Author: Olivier Crête +Date: Tue May 31 17:31:12 2016 -0400 + + WIP + +commit 955323915c43b1a066399e61d0ab091f0b1d112b +Author: Jakub Adam +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 +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 +Date: Fri Jan 15 22:40:27 2016 -0500 + + component: Add API to cleanly remove a base socket + +commit 93330f1f97e4f2a9ff09b602765e620cb279574b +Author: Olivier Crête +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +Date: Tue Mar 1 15:27:46 2016 -0500 + + candidate: Give lower priority to TCP relayed candidates + +commit 8ee6d1bbed87fda37ade7b5c5d9483f41037b06a +Author: Olivier Crête +Date: Mon Feb 29 16:11:18 2016 -0500 + + udp-turn: Fix binding timeout leak + +commit 8f1f615e92cd56ad4d8487457c2fde2c4aaa51d9 +Author: Olivier Crête +Date: Wed Feb 24 22:53:08 2016 -0500 + + conncheck: Update selected pair if necessary + +commit 65f2eda04c1c73cc7ebc3df2032d528eedc236e1 +Author: Olivier Crête +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 +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 +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 +Date: Thu Feb 18 14:20:52 2016 -0500 + + conncheck: Make very frequent debug verbose-only + +commit b7c2eabfd0bd8c1321d9e8450caa8fa6b6ecb5ab +Author: Olivier Crête +Date: Mon Feb 15 19:09:58 2016 -0500 + + debug: Enable based on G_MESSAGES_DEBUG + +commit acfe2b1f366d5f33314db7e9878225f2a70358ef +Author: Olivier Crête +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 +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 +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 +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 +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 +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 +Date: Mon Feb 8 21:04:24 2016 -0500 + + conncheck: Deduplicate conncheck stopping code + +commit f122e4174d19c60bc434f3986f5c08f8673344bd +Author: Olivier Crête +Date: Mon Feb 8 19:41:28 2016 -0500 + + Reset to connecting if reconnected after failed + +commit 9c1a41b06ab459ce33f32d25ce258fb21ba49047 +Author: Olivier Crête +Date: Thu Jan 14 17:59:16 2016 -0500 + + agent: Add warning on ignored result + +commit a357b17f5f3415320b9ec7122738396ccd998521 +Author: Fabrice Bellet +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 + Differential Revision: https://phabricator.freedesktop.org/D877 + +commit b986d6e5f2ee0b7b0e09031c1a369bf89153e4c5 +Author: Fabrice Bellet +Date: Mon Apr 4 22:38:07 2016 +0100 + + agent: remove newline from debug output + + Just a cosmetic fix. + + Reviewed-by: Philip Withnall + Differential Revision: https://phabricator.freedesktop.org/D872 + +commit e0ed4fb3a236710846d9129438e0077782569633 +Author: Jakub Adam +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 + Differential Revision: https://phabricator.freedesktop.org/D866 + +commit 38268e53fde8cd97055d88d2066c0016fe04b31b +Author: Jakub Adam +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 + Reviewed-by: José Antonio Santos Cadenas + Reviewed-by: Philip Withnall + Reviewed-by: José Antonio Santos Cadenas + Differential Revision: https://phabricator.freedesktop.org/D785 + +commit 7037ab4cf384edd9f700bc221a9d980b30d9c64f +Author: Fabrice Bellet +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 + Reviewed-by: Philip Withnall + Differential Revision: https://phabricator.freedesktop.org/D802 + +commit 1732c7d6a7a104438412309373818e493a2504c9 +Author: Olivier Crête +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 +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 + Differential Revision: https://phabricator.freedesktop.org/D807 + +commit ed75d55cf279613bb736f7646d3010d816797ddf +Author: Jakub Adam +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 + Differential Revision: https://phabricator.freedesktop.org/D786 + +commit 1493a381d5bf6e15348c2bc17270f45b69cb70d2 +Author: Philip Withnall +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 +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 + Differential Revision: https://phabricator.freedesktop.org/D822 + +commit 38c5e66886cb8138d6be57b8a4721d9b42a358b7 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D742 + +commit 70981d41edf46a09393017f3de34748fcecea046 +Author: Philip Withnall +Date: Tue Mar 1 23:05:20 2016 +0000 + + simple-example: transmission can begin earlier than in ready state + + Reviewed-by: Olivier Crête + Differential Revision: https://phabricator.freedesktop.org/D819 + +commit 47eaf50a99d91c4a666f05c4de24613706847c88 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D817 + +commit 4e68244a51698aefdf44dd1ceb17b95275e655bf +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D806 + +commit 0f1e6c64515871298b115b497b019506b8065235 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D804 + +commit 17488a20d8db8ea1f8fa2d7a44090070407d6db8 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D815 + +commit 3ce45c25af238fb4d9a040abb44597531140db2d +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D818 + +commit c309905ff446bed2dc47811023b98bc586a02d63 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D808 + +commit 80973c096de872983bc60cd28a84653931f8d601 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D803 + +commit 41ab61def82aa275649afd5f4a3fcb43e75fb360 +Author: Mike Ruprecht +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 + +commit 9638bc132f909177f6ecfb86cdd17af557a35c56 +Author: Jose Antonio Santos Cadenas +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 + https://phabricator.freedesktop.org/T3492 + +commit 3e71f42c8b15790e252d850ba42a7ae7e7cc69a9 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D345 + +commit 53283c218a5eb7a29e7019ac320e74f9dbe4b3fc +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D292 + +commit aef748ec7d0b06067312e5cc761a6375cd0f699c +Author: Philip Withnall +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 +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 + Differential Revision: https://phabricator.freedesktop.org/D309 + +commit 529bc193d56522b10a4de83409396b3316863934 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D308 + +commit ef2b58f64887546e426dd8cda382f2908f84caca +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D307 + +commit 1f08419382c52c7e796da06c9271a362aa60333d +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D306 + +commit da70716162b2085ca4db6e7efd446fcda7bd488d +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D301 + +commit 66e47aa39f9cd3666e610fab78caa0c7d8f9c410 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D300 + +commit 1ae15f66af2af17e991ab028ca16a1200fd5f4e5 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D299 + +commit ea3348020da586f20e1a64c9732405e730563616 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D298 + +commit 9f10231fc787e4683e896bf35f086906a5b16c03 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D297 + +commit dac3e280d6f3fd792f1d79313debd03c0df282c4 +Author: Philip Withnall +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 + Differential Revision: https://phabricator.freedesktop.org/D296 + +commit fae0f9d37c6e34f34c92dee85384c29e565bdbce +Author: Jakub Adam +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 + +commit 02852728ef347f5cb4c9227848b266c72b5fe38b +Author: Jakub Adam +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 + +commit 84eaa12b0b1a76c45ce2d77294e0477c10552cd4 +Author: Jakub Adam +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 + Differential Revision: https://phabricator.freedesktop.org/D241 + +commit 837c8953fe87bdd5d5bccc444e72739100578ef8 +Author: Jakub Adam +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 + Differential Revision: https://phabricator.freedesktop.org/D240 + +commit c6bc33c031493e0db11a1f57055a656f6428c60a +Author: Philip Withnall +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 on the mailing list. + +commit 757f8aecdb4fda86b2bbac828a3acc528d8eb8bc +Author: Philip Withnall +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +Date: Wed Aug 19 09:24:27 2015 +0100 + + build: Update .gitignore + + Add stun/usages/.dirstamp. + +commit 6c9afb4db35168cc699ff0aa2e56b127f3706083 +Author: Philip Withnall +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 +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 +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 +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 +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 +Date: Tue Aug 18 14:03:41 2015 +0100 + + agent: Remove unused inet_pton() function + + Summary: + As spotted by Felix . 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 +Date: Tue Jun 30 14:39:51 2015 +0100 + + agent: Remove unused inet_pton() function + + As spotted by Felix . 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 +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 +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 + Reviewed-by: Olivier Crête + + http://phabricator.freedesktop.org/T117 + +commit cd61af3114a51df53619fb0460ace2b3660fc5da +Author: Philip Withnall +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 and Felix + . + + Reviewed-by: Olivier Crete + Reviewed-by: Felix Schlitter + + http://phabricator.freedesktop.org/T122 + +commit 3cccc311b1becf4307f5a4004734d8f7b2cf84f6 +Author: Felix Schlitter +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 +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 +Date: Thu Sep 18 19:33:06 2014 -0400 + + stun: Remove annoying non-error on non-STUN packet + +commit 81a929ac141aae66b6450e8ce93cb357ed404cda +Author: Timo Gurr +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 +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 +Date: Tue May 5 15:07:10 2015 -0400 + + Removing no-op assignment + +commit 91a7b9324244844baf35d8fcef019a4ea3872d30 +Author: Youness Alaoui +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 +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 +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/**/ *ips = NULL; ++ GList/**/ *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 + +-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, "<-- " ++ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Sending " + "", + 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, "--> " ++ DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, ++ "Received " + "", + 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 ++ ++#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 + +-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 + +-#if GLIB_CHECK_VERSION(2, 36, 0) + #include +-#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 + +-#if GLIB_CHECK_VERSION(2, 36, 0) + #include +-#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 + +-#if GLIB_CHECK_VERSION(2, 36, 0) + #include +-#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 + #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 + #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 + #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 ( + ¤t_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/**/ *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 ++ * ++ * 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 ++ ++#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 ++#include ++#include ++ ++#include ++#include ++ ++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 diff --git a/libnice.spec b/libnice.spec index 17ffee5..a4894cf 100644 --- a/libnice.spec +++ b/libnice.spec @@ -1,13 +1,13 @@ Name: libnice Version: 0.1.13 -Release: 5%{?dist} +Release: 6%{?dist} Summary: GLib ICE implementation Group: System Environment/Libraries License: LGPLv2 and MPLv1.1 URL: http://nice.freedesktop.org/wiki/ Source0: http://nice.freedesktop.org/releases/%{name}-%{version}.tar.gz -Patch1: libnice-0.1.13-20160606.patch +Patch1: libnice-0.1.13-20160610.patch BuildRequires: glib2-devel BuildRequires: gobject-introspection-devel @@ -124,6 +124,9 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %changelog +* Fri Jun 10 2016 David Woodhouse - 0.1.13-6 +- More updates from libnice git; use-after-free fixes + * Mon Jun 06 2016 David Woodhouse - 0.1.13-5 - Wholesale update to git HEAD, which fixes SIPE again.