2007-09-10 01:52:34 +02:00
|
|
|
/*
|
|
|
|
|
2018-04-09 09:04:33 +02:00
|
|
|
Copyright (c) 2007-2018, Arvid Norberg
|
2007-09-10 01:52:34 +02:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2012-09-24 18:13:57 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
2015-08-02 21:55:05 +02:00
|
|
|
|
|
|
|
#include "libtorrent/aux_/disable_warnings_push.hpp"
|
|
|
|
#include <boost/asio/ip/multicast.hpp>
|
2016-08-26 06:14:11 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
2009-11-27 08:08:47 +01:00
|
|
|
|
|
|
|
#include "libtorrent/socket.hpp"
|
|
|
|
#include "libtorrent/enum_net.hpp"
|
|
|
|
#include "libtorrent/broadcast_socket.hpp"
|
|
|
|
#include "libtorrent/assert.hpp"
|
2010-11-28 02:47:30 +01:00
|
|
|
#include "libtorrent/debug.hpp"
|
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
using namespace std::placeholders;
|
|
|
|
|
2017-04-12 19:00:57 +02:00
|
|
|
namespace libtorrent {
|
|
|
|
|
2017-03-18 16:07:17 +01:00
|
|
|
bool is_ip_address(std::string const& host)
|
2016-05-05 23:09:11 +02:00
|
|
|
{
|
|
|
|
error_code ec;
|
2018-01-04 10:48:22 +01:00
|
|
|
make_address(host, ec);
|
2016-05-05 23:09:11 +02:00
|
|
|
return !ec;
|
|
|
|
}
|
|
|
|
|
2020-02-18 22:50:19 +01:00
|
|
|
bool is_global(address const& a)
|
|
|
|
{
|
|
|
|
if (a.is_v6())
|
|
|
|
{
|
|
|
|
// https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
|
|
|
|
address_v6 const a6 = a.to_v6();
|
|
|
|
return (a6.to_bytes()[0] & 0xe0) == 0x20;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
address_v4 const a4 = a.to_v4();
|
|
|
|
return !(a4.is_multicast() || a4.is_unspecified() || is_local(a));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_link_local(address const& a)
|
|
|
|
{
|
|
|
|
if (a.is_v6())
|
|
|
|
{
|
|
|
|
address_v6 const a6 = a.to_v6();
|
|
|
|
return a6.is_link_local()
|
|
|
|
|| a6.is_multicast_link_local();
|
|
|
|
}
|
|
|
|
address_v4 const a4 = a.to_v4();
|
|
|
|
unsigned long ip = a4.to_ulong();
|
|
|
|
return (ip & 0xffff0000) == 0xa9fe0000; // 169.254.x.x
|
|
|
|
}
|
|
|
|
|
2007-09-10 01:52:34 +02:00
|
|
|
bool is_local(address const& a)
|
|
|
|
{
|
2020-02-26 10:59:57 +01:00
|
|
|
if (a.is_v6())
|
|
|
|
{
|
|
|
|
// NOTE: site local is deprecated but by
|
|
|
|
// https://www.ietf.org/rfc/rfc3879.txt:
|
|
|
|
// routers SHOULD be configured to prevent
|
|
|
|
// routing of this prefix by default.
|
|
|
|
|
|
|
|
address_v6 const a6 = a.to_v6();
|
|
|
|
return a6.is_loopback()
|
|
|
|
|| a6.is_link_local()
|
|
|
|
|| a6.is_site_local()
|
|
|
|
|| a6.is_multicast_link_local()
|
|
|
|
|| a6.is_multicast_site_local()
|
|
|
|
// fc00::/7, unique local address
|
|
|
|
|| (a6.to_bytes()[0] & 0xfe) == 0xfc;
|
|
|
|
}
|
|
|
|
address_v4 a4 = a.to_v4();
|
|
|
|
unsigned long ip = a4.to_ulong();
|
|
|
|
return ((ip & 0xff000000) == 0x0a000000 // 10.x.x.x
|
|
|
|
|| (ip & 0xfff00000) == 0xac100000 // 172.16.x.x
|
|
|
|
|| (ip & 0xffff0000) == 0xc0a80000 // 192.168.x.x
|
|
|
|
|| (ip & 0xffff0000) == 0xa9fe0000 // 169.254.x.x
|
|
|
|
|| (ip & 0xff000000) == 0x7f000000); // 127.x.x.x
|
2007-09-10 01:52:34 +02:00
|
|
|
}
|
|
|
|
|
2020-02-26 10:59:57 +01:00
|
|
|
// TODO: this function is pointless
|
2007-09-19 23:54:26 +02:00
|
|
|
bool is_loopback(address const& addr)
|
|
|
|
{
|
2020-02-26 10:59:57 +01:00
|
|
|
return addr.is_loopback();
|
2007-09-19 23:54:26 +02:00
|
|
|
}
|
|
|
|
|
2007-09-22 18:26:03 +02:00
|
|
|
bool is_any(address const& addr)
|
|
|
|
{
|
|
|
|
if (addr.is_v4())
|
|
|
|
return addr.to_v4() == address_v4::any();
|
2008-09-20 18:21:16 +02:00
|
|
|
else if (addr.to_v6().is_v4_mapped())
|
|
|
|
return (addr.to_v6().to_v4() == address_v4::any());
|
2007-09-22 18:26:03 +02:00
|
|
|
else
|
|
|
|
return addr.to_v6() == address_v6::any();
|
2009-04-13 07:11:44 +02:00
|
|
|
}
|
|
|
|
|
2013-10-01 21:37:17 +02:00
|
|
|
bool is_teredo(address const& addr)
|
2010-11-29 02:33:05 +01:00
|
|
|
{
|
2020-02-26 10:59:57 +01:00
|
|
|
if (!addr.is_v6()) return false;
|
|
|
|
static const std::uint8_t teredo_prefix[] = {0x20, 0x01, 0, 0};
|
|
|
|
address_v6::bytes_type b = addr.to_v6().to_bytes();
|
|
|
|
return std::memcmp(b.data(), teredo_prefix, 4) == 0;
|
2010-11-29 02:33:05 +01:00
|
|
|
}
|
|
|
|
|
2009-04-13 07:11:44 +02:00
|
|
|
bool supports_ipv6()
|
|
|
|
{
|
2018-08-15 00:02:37 +02:00
|
|
|
#if defined TORRENT_BUILD_SIMULATOR
|
2015-11-13 05:45:57 +01:00
|
|
|
return true;
|
|
|
|
#elif defined TORRENT_WINDOWS
|
2011-02-25 18:00:36 +01:00
|
|
|
TORRENT_TRY {
|
|
|
|
error_code ec;
|
2018-01-04 10:48:22 +01:00
|
|
|
make_address("::1", ec);
|
2011-02-25 18:00:36 +01:00
|
|
|
return !ec;
|
2016-09-25 15:50:48 +02:00
|
|
|
} TORRENT_CATCH(std::exception const&) { return false; }
|
2015-09-07 02:53:06 +02:00
|
|
|
#else
|
|
|
|
io_service ios;
|
|
|
|
tcp::socket test(ios);
|
|
|
|
error_code ec;
|
|
|
|
test.open(tcp::v6(), ec);
|
2017-03-15 12:15:36 +01:00
|
|
|
if (ec) return false;
|
2018-01-04 10:48:22 +01:00
|
|
|
error_code ignore;
|
|
|
|
test.bind(tcp::endpoint(make_address_v6("::1", ignore), 0), ec);
|
2015-09-07 02:53:06 +02:00
|
|
|
return !bool(ec);
|
2009-04-13 07:11:44 +02:00
|
|
|
#endif
|
2007-09-22 18:26:03 +02:00
|
|
|
}
|
|
|
|
|
2016-12-04 21:58:51 +01:00
|
|
|
address ensure_v6(address const& a)
|
|
|
|
{
|
|
|
|
return a == address_v4() ? address_v6() : a;
|
|
|
|
}
|
|
|
|
|
2012-10-09 06:16:37 +02:00
|
|
|
broadcast_socket::broadcast_socket(
|
2015-01-06 09:08:49 +01:00
|
|
|
udp::endpoint const& multicast_endpoint)
|
2007-09-10 01:52:34 +02:00
|
|
|
: m_multicast_endpoint(multicast_endpoint)
|
2011-10-25 07:55:32 +02:00
|
|
|
, m_outstanding_operations(0)
|
|
|
|
, m_abort(false)
|
2007-09-10 01:52:34 +02:00
|
|
|
{
|
2016-05-02 00:15:25 +02:00
|
|
|
TORRENT_ASSERT(m_multicast_endpoint.address().is_multicast());
|
2012-10-09 06:16:37 +02:00
|
|
|
}
|
|
|
|
|
2018-01-11 01:35:15 +01:00
|
|
|
void broadcast_socket::open(receive_handler_t handler
|
2015-01-06 09:08:49 +01:00
|
|
|
, io_service& ios, error_code& ec, bool loopback)
|
2012-10-09 06:16:37 +02:00
|
|
|
{
|
2018-01-11 01:35:15 +01:00
|
|
|
m_on_receive = std::move(handler);
|
2015-01-06 09:08:49 +01:00
|
|
|
|
2008-01-11 07:49:37 +01:00
|
|
|
std::vector<ip_interface> interfaces = enum_net_interfaces(ios, ec);
|
2007-09-10 01:52:34 +02:00
|
|
|
|
2018-04-01 13:48:17 +02:00
|
|
|
if (is_v6(m_multicast_endpoint))
|
2009-11-29 20:41:07 +01:00
|
|
|
open_multicast_socket(ios, address_v6::any(), loopback, ec);
|
2009-12-13 17:29:58 +01:00
|
|
|
else
|
|
|
|
open_multicast_socket(ios, address_v4::any(), loopback, ec);
|
2014-08-27 07:57:37 +02:00
|
|
|
|
2017-08-16 02:13:32 +02:00
|
|
|
for (auto const& i : interfaces)
|
2007-09-10 01:52:34 +02:00
|
|
|
{
|
2007-09-19 23:54:26 +02:00
|
|
|
// only multicast on compatible networks
|
2018-04-01 13:48:17 +02:00
|
|
|
if (i.interface_address.is_v4() != is_v4(m_multicast_endpoint)) continue;
|
2007-09-19 23:54:26 +02:00
|
|
|
// ignore any loopback interface
|
2017-08-16 02:13:32 +02:00
|
|
|
if (!loopback && is_loopback(i.interface_address)) continue;
|
2007-09-10 01:52:34 +02:00
|
|
|
|
2009-11-16 08:42:50 +01:00
|
|
|
ec = error_code();
|
2014-08-28 09:11:34 +02:00
|
|
|
|
2017-08-16 02:13:32 +02:00
|
|
|
open_multicast_socket(ios, i.interface_address, loopback, ec);
|
|
|
|
open_unicast_socket(ios, i.interface_address
|
|
|
|
, i.netmask.is_v4() ? i.netmask.to_v4() : address_v4());
|
2010-03-26 18:45:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-09 19:51:15 +01:00
|
|
|
void broadcast_socket::open_multicast_socket(io_service& ios
|
2009-11-14 21:35:24 +01:00
|
|
|
, address const& addr, bool loopback, error_code& ec)
|
2008-01-09 19:51:15 +01:00
|
|
|
{
|
2015-06-06 07:22:53 +02:00
|
|
|
using namespace boost::asio::ip::multicast;
|
2008-01-09 19:51:15 +01:00
|
|
|
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<udp::socket> s = std::make_shared<udp::socket>(ios);
|
2009-04-04 18:59:53 +02:00
|
|
|
s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
|
2008-01-09 19:51:15 +01:00
|
|
|
if (ec) return;
|
2015-06-06 19:49:18 +02:00
|
|
|
s->set_option(udp::socket::reuse_address(true), ec);
|
2008-01-09 19:51:15 +01:00
|
|
|
if (ec) return;
|
|
|
|
s->bind(udp::endpoint(addr, m_multicast_endpoint.port()), ec);
|
|
|
|
if (ec) return;
|
|
|
|
s->set_option(join_group(m_multicast_endpoint.address()), ec);
|
|
|
|
if (ec) return;
|
|
|
|
s->set_option(hops(255), ec);
|
|
|
|
if (ec) return;
|
|
|
|
s->set_option(enable_loopback(loopback), ec);
|
|
|
|
if (ec) return;
|
2017-08-29 18:06:56 +02:00
|
|
|
m_sockets.emplace_back(s);
|
2008-01-09 19:51:15 +01:00
|
|
|
socket_entry& se = m_sockets.back();
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("broadcast_socket::on_receive");
|
2018-04-04 22:25:57 +02:00
|
|
|
s->async_receive_from(boost::asio::buffer(se.buffer)
|
2016-05-25 06:31:52 +02:00
|
|
|
, se.remote, std::bind(&broadcast_socket::on_receive, this, &se, _1, _2));
|
2011-10-25 07:55:32 +02:00
|
|
|
++m_outstanding_operations;
|
2007-12-22 18:33:04 +01:00
|
|
|
}
|
|
|
|
|
2010-03-26 18:45:16 +01:00
|
|
|
void broadcast_socket::open_unicast_socket(io_service& ios, address const& addr
|
|
|
|
, address_v4 const& mask)
|
2007-12-22 18:33:04 +01:00
|
|
|
{
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2016-09-01 03:42:18 +02:00
|
|
|
std::shared_ptr<udp::socket> s = std::make_shared<udp::socket>(ios);
|
2007-12-22 18:33:04 +01:00
|
|
|
s->open(addr.is_v4() ? udp::v4() : udp::v6(), ec);
|
|
|
|
if (ec) return;
|
2011-02-17 07:47:26 +01:00
|
|
|
|
2017-08-29 18:06:56 +02:00
|
|
|
m_unicast_sockets.emplace_back(s, mask);
|
2007-12-22 18:33:04 +01:00
|
|
|
socket_entry& se = m_unicast_sockets.back();
|
2011-02-17 07:47:26 +01:00
|
|
|
|
|
|
|
// allow sending broadcast messages
|
2015-06-06 07:22:53 +02:00
|
|
|
boost::asio::socket_base::broadcast option(true);
|
2011-02-17 07:47:26 +01:00
|
|
|
s->set_option(option, ec);
|
|
|
|
if (!ec) se.broadcast = true;
|
|
|
|
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("broadcast_socket::on_receive");
|
2018-04-04 22:25:57 +02:00
|
|
|
s->async_receive_from(boost::asio::buffer(se.buffer)
|
2016-05-25 06:31:52 +02:00
|
|
|
, se.remote, std::bind(&broadcast_socket::on_receive, this, &se, _1, _2));
|
2011-10-25 07:55:32 +02:00
|
|
|
++m_outstanding_operations;
|
2007-09-10 01:52:34 +02:00
|
|
|
}
|
|
|
|
|
2020-01-14 23:06:19 +01:00
|
|
|
void broadcast_socket::send_to(char const* buffer, int size
|
|
|
|
, udp::endpoint const& to, error_code& ec)
|
|
|
|
{
|
|
|
|
bool all_fail = true;
|
|
|
|
error_code e;
|
|
|
|
for (auto& s : m_sockets)
|
|
|
|
{
|
|
|
|
if (!s.socket) continue;
|
|
|
|
s.socket->send_to(boost::asio::buffer(buffer, std::size_t(size)), to, 0, e);
|
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
s.socket->close(e);
|
|
|
|
s.socket.reset();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
all_fail = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (all_fail) ec = e;
|
|
|
|
}
|
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
void broadcast_socket::send(char const* buffer, int const size
|
|
|
|
, error_code& ec, int const flags)
|
2007-09-10 01:52:34 +02:00
|
|
|
{
|
2015-05-18 03:30:32 +02:00
|
|
|
bool all_fail = true;
|
|
|
|
error_code e;
|
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
for (auto& s : m_unicast_sockets)
|
2007-09-10 01:52:34 +02:00
|
|
|
{
|
2016-09-07 23:51:18 +02:00
|
|
|
if (!s.socket) continue;
|
2017-01-09 15:32:52 +01:00
|
|
|
s.socket->send_to(boost::asio::buffer(buffer, std::size_t(size)), m_multicast_endpoint, 0, e);
|
2011-02-17 07:47:26 +01:00
|
|
|
|
|
|
|
// if the user specified the broadcast flag, send one to the broadcast
|
|
|
|
// address as well
|
2016-09-07 23:51:18 +02:00
|
|
|
if ((flags & broadcast_socket::flag_broadcast) && s.can_broadcast())
|
2017-01-09 15:32:52 +01:00
|
|
|
s.socket->send_to(boost::asio::buffer(buffer, std::size_t(size))
|
2016-09-07 23:51:18 +02:00
|
|
|
, udp::endpoint(s.broadcast_address(), m_multicast_endpoint.port()), 0, e);
|
2011-02-17 07:47:26 +01:00
|
|
|
|
2009-11-29 20:41:07 +01:00
|
|
|
if (e)
|
|
|
|
{
|
2016-09-07 23:51:18 +02:00
|
|
|
s.socket->close(e);
|
|
|
|
s.socket.reset();
|
2009-11-29 20:41:07 +01:00
|
|
|
}
|
2015-05-18 03:30:32 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
all_fail = false;
|
|
|
|
}
|
2009-11-29 20:41:07 +01:00
|
|
|
}
|
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
for (auto& s : m_sockets)
|
2009-11-29 20:41:07 +01:00
|
|
|
{
|
2016-09-07 23:51:18 +02:00
|
|
|
if (!s.socket) continue;
|
2017-01-09 15:32:52 +01:00
|
|
|
s.socket->send_to(boost::asio::buffer(buffer, std::size_t(size)), m_multicast_endpoint, 0, e);
|
2008-01-03 11:58:16 +01:00
|
|
|
if (e)
|
|
|
|
{
|
2016-09-07 23:51:18 +02:00
|
|
|
s.socket->close(e);
|
|
|
|
s.socket.reset();
|
2008-01-03 11:58:16 +01:00
|
|
|
}
|
2015-05-18 03:30:32 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
all_fail = false;
|
|
|
|
}
|
2007-09-10 01:52:34 +02:00
|
|
|
}
|
2015-05-18 03:30:32 +02:00
|
|
|
|
|
|
|
if (all_fail) ec = e;
|
2007-09-10 01:52:34 +02:00
|
|
|
}
|
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
void broadcast_socket::on_receive(socket_entry* s, error_code const& ec
|
2007-09-10 01:52:34 +02:00
|
|
|
, std::size_t bytes_transferred)
|
|
|
|
{
|
2016-04-23 23:29:25 +02:00
|
|
|
COMPLETE_ASYNC("broadcast_socket::on_receive");
|
2011-10-25 07:55:32 +02:00
|
|
|
TORRENT_ASSERT(m_outstanding_operations > 0);
|
|
|
|
--m_outstanding_operations;
|
|
|
|
|
|
|
|
if (ec || bytes_transferred == 0 || !m_on_receive)
|
|
|
|
{
|
|
|
|
maybe_abort();
|
|
|
|
return;
|
|
|
|
}
|
2018-11-01 23:05:30 +01:00
|
|
|
m_on_receive(s->remote, {s->buffer.data(), int(bytes_transferred)});
|
2011-10-25 07:55:32 +02:00
|
|
|
|
|
|
|
if (maybe_abort()) return;
|
2008-01-03 11:58:16 +01:00
|
|
|
if (!s->socket) return;
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("broadcast_socket::on_receive");
|
2018-04-04 22:25:57 +02:00
|
|
|
s->socket->async_receive_from(boost::asio::buffer(s->buffer)
|
2016-05-25 06:31:52 +02:00
|
|
|
, s->remote, std::bind(&broadcast_socket::on_receive, this, s, _1, _2));
|
2011-10-25 07:55:32 +02:00
|
|
|
++m_outstanding_operations;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool broadcast_socket::maybe_abort()
|
|
|
|
{
|
2011-11-19 23:17:13 +01:00
|
|
|
bool ret = m_abort;
|
2011-10-25 07:55:32 +02:00
|
|
|
if (m_abort && m_outstanding_operations == 0)
|
2011-10-25 12:28:54 +02:00
|
|
|
{
|
|
|
|
// it's important that m_on_receive is cleared
|
|
|
|
// before the object is destructed, since it may
|
|
|
|
// hold a reference to ourself, which would otherwise
|
|
|
|
// cause an infinite recursion destructing the objects
|
|
|
|
receive_handler_t().swap(m_on_receive);
|
|
|
|
}
|
2011-11-19 23:17:13 +01:00
|
|
|
return ret;
|
2007-09-10 01:52:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void broadcast_socket::close()
|
|
|
|
{
|
2016-05-25 06:31:52 +02:00
|
|
|
std::for_each(m_sockets.begin(), m_sockets.end(), std::bind(&socket_entry::close, _1));
|
|
|
|
std::for_each(m_unicast_sockets.begin(), m_unicast_sockets.end(), std::bind(&socket_entry::close, _1));
|
2008-01-07 03:33:45 +01:00
|
|
|
|
2011-10-25 07:55:32 +02:00
|
|
|
m_abort = true;
|
|
|
|
maybe_abort();
|
2007-09-10 01:52:34 +02:00
|
|
|
}
|
|
|
|
}
|