From 3555b01b8571d288ed42f94ae8e00ec6dec39f9c Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Wed, 10 Oct 2012 04:40:18 +0000 Subject: [PATCH] merged utp_socket and simultaneous connections fix from RC_0_16 --- ChangeLog | 2 + include/libtorrent/utp_socket_manager.hpp | 3 +- src/policy.cpp | 60 ++++++++++++++++++++--- src/utp_socket_manager.cpp | 50 ++++++++++++++++++- src/utp_stream.cpp | 11 ++++- 5 files changed, 115 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index ce813929e..108f803ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,8 @@ * fix uTP edge case where udp socket buffer fills up * fix nagle implementation in uTP + * consistently disconnect the same peer when two peers simultaneously connect + * fix local endpoint queries for uTP connections * small optimization to local peer discovery to ignore our own broadcasts * try harder to bind the udp socket (uTP, DHT, UDP-trackers, LSD) to the same port as TCP * relax file timestamp requirements for accepting resume data diff --git a/include/libtorrent/utp_socket_manager.hpp b/include/libtorrent/utp_socket_manager.hpp index 28c5085fa..c97ee759f 100644 --- a/include/libtorrent/utp_socket_manager.hpp +++ b/include/libtorrent/utp_socket_manager.hpp @@ -65,7 +65,8 @@ namespace libtorrent void tick(ptime now); - tcp::endpoint local_endpoint(error_code& ec) const; + tcp::endpoint local_endpoint(address const& remote, error_code& ec) const; + int local_port(error_code& ec) const; // flags for send_packet enum { dont_fragment = 1 }; diff --git a/src/policy.cpp b/src/policy.cpp index dd3da8b99..0ddc77a1e 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -871,20 +871,66 @@ namespace libtorrent i->connection->disconnect(ec2); TORRENT_ASSERT(i->connection == 0); } - else if (!i->connection->is_connecting() || c.is_outgoing()) + else if (i->connection->is_outgoing() == c.is_outgoing()) { + // if the other end connected to us both times, just drop + // the second one. Or if we made both connections. c.disconnect(errors::duplicate_peer_id); return false; } else { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - m_torrent->debug_log("duplicate connection. existing connection" - " is connecting and this connection is incoming. closing existing " - "connection in favour of this one"); + // at this point, we need to disconnect either + // i->connection or c. In order for both this client + // and the client on the other end to decide to + // disconnect the same one, we need a consistent rule to + // select which one. + + bool outgoing1 = c.is_outgoing(); + + // for this, we compare our endpoints (IP and port) + // and whoever has the lower IP,port should be the + // one keeping its outgoing connection. Since outgoing + // ports are selected at random by the OS, we need + // to be careful to only look at the target end of a + // connection for the endpoint. + + tcp::endpoint our_ep = outgoing1 ? other_socket->local_endpoint(ec1) : this_socket->local_endpoint(ec1); + tcp::endpoint other_ep = outgoing1 ? this_socket->remote_endpoint(ec1) : other_socket->remote_endpoint(ec1); + + if (our_ep < other_ep) + { +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** DUPLICATE PEER RESOLUTION [ \"%s\" < \"%s\" ]" + , print_endpoint(our_ep).c_str(), print_endpoint(other_ep).c_str()); + i->connection->peer_log("*** DUPLICATE PEER RESOLUTION [ \"%s\" < \"%s\" ]" + , print_endpoint(our_ep).c_str(), print_endpoint(other_ep).c_str()); #endif - i->connection->disconnect(errors::duplicate_peer_id); - TORRENT_ASSERT(i->connection == 0); + + // we should keep our outgoing connection + if (!outgoing1) + { + c.disconnect(errors::duplicate_peer_id); + return false; + } + i->connection->disconnect(errors::duplicate_peer_id); + } + else + { +#ifdef TORRENT_VERBOSE_LOGGING + c.peer_log("*** DUPLICATE PEER RESOLUTION [ \"%s\" >= \"%s\" ]" + , print_endpoint(our_ep).c_str(), print_endpoint(other_ep).c_str()); + i->connection->peer_log("*** DUPLICATE PEER RESOLUTION [ \"%s\" >= \"%s\" ]" + , print_endpoint(our_ep).c_str(), print_endpoint(other_ep).c_str()); +#endif + // they should keep their outgoing connection + if (outgoing1) + { + c.disconnect(errors::duplicate_peer_id); + return false; + } + i->connection->disconnect(errors::duplicate_peer_id); + } } } diff --git a/src/utp_socket_manager.cpp b/src/utp_socket_manager.cpp index e43127c0e..f58e0d7fc 100644 --- a/src/utp_socket_manager.cpp +++ b/src/utp_socket_manager.cpp @@ -194,9 +194,55 @@ namespace libtorrent #endif } - tcp::endpoint utp_socket_manager::local_endpoint(error_code& ec) const + int utp_socket_manager::local_port(error_code& ec) const { - return m_sock.local_endpoint(ec); + return m_sock.local_endpoint(ec).port(); + } + + tcp::endpoint utp_socket_manager::local_endpoint(address const& remote, error_code& ec) const + { + tcp::endpoint socket_ep = m_sock.local_endpoint(ec); + + // first enumerate the routes in the routing table + std::vector routes = enum_routes(m_sock.get_io_service(), ec); + if (ec) return socket_ep; + + if (routes.empty()) return socket_ep; + // then find the best match + ip_route* best = &routes[0]; + for (std::vector::iterator i = routes.begin() + , end(routes.end()); i != end; ++i) + { + if (is_any(i->destination) && i->destination.is_v4() == remote.is_v4()) + { + best = &*i; + continue; + } + + if (match_addr_mask(remote, i->destination, i->netmask)) + { + best = &*i; + continue; + } + } + + // best now tells us which interface we would send over + // for this target. Now figure out what the local address + // is for that interface + + std::vector net = enum_net_interfaces(m_sock.get_io_service(), ec); + if (ec) return socket_ep; + + for (std::vector::iterator i = net.begin() + , end(net.end()); i != end; ++i) + { + if (i->interface_address.is_v4() != remote.is_v4()) + continue; + + if (strcmp(best->name, i->name) == 0) + return tcp::endpoint(i->interface_address, socket_ep.port()); + } + return socket_ep; } bool utp_socket_manager::incoming_packet(error_code const& ec, udp::endpoint const& ep diff --git a/src/utp_stream.cpp b/src/utp_stream.cpp index 37693315e..f54a88139 100644 --- a/src/utp_stream.cpp +++ b/src/utp_stream.cpp @@ -398,6 +398,9 @@ struct utp_socket_impl // the address of the remote endpoint address m_remote_address; + // the local address + address m_local_address; + // the send and receive buffers // maps packet sequence numbers packet_buffer m_inbuf; @@ -789,7 +792,7 @@ utp_stream::endpoint_type utp_stream::local_endpoint(error_code& ec) const ec = asio::error::not_connected; return endpoint_type(); } - return m_impl->m_sm->local_endpoint(ec); + return tcp::endpoint(m_impl->m_local_address, m_impl->m_sm->local_port(ec)); } utp_stream::~utp_stream() @@ -1063,6 +1066,9 @@ void utp_stream::do_connect(tcp::endpoint const& ep, utp_stream::connect_handler m_impl->m_port = ep.port(); m_impl->m_connect_handler = handler; + error_code ec; + m_impl->m_local_address = m_impl->m_sm->local_endpoint(m_impl->m_remote_address, ec).address(); + if (m_impl->test_socket_state()) return; m_impl->send_syn(); } @@ -2710,6 +2716,9 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size m_remote_address = ep.address(); m_port = ep.port(); + error_code ec; + m_local_address = m_sm->local_endpoint(m_remote_address, ec).address(); + m_ack_nr = ph->seq_nr; m_seq_nr = random(); m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK;