|
1 | 1 | #include "resolve_attempt_udp.h" |
2 | 2 | #include "api_config.h" |
| 3 | +#include "netinterfaces.h" |
3 | 4 | #include "resolver_impl.h" |
4 | 5 | #include "socket_utils.h" |
5 | 6 | #include <boost/asio/io_context.hpp> |
|
10 | 11 | #include <sstream> |
11 | 12 |
|
12 | 13 | using namespace lsl; |
| 14 | +namespace asio = lslboost::asio; |
| 15 | +using err_t = const lslboost::system::error_code &; |
| 16 | +using asio::ip::multicast::outbound_interface; |
13 | 17 |
|
14 | 18 | resolve_attempt_udp::resolve_attempt_udp(asio::io_context &io, const udp &protocol, |
15 | 19 | const std::vector<udp::endpoint> &targets, const std::string &query, result_container &results, |
16 | 20 | std::mutex &results_mut, double cancel_after, cancellable_registry *registry) |
17 | 21 | : io_(io), results_(results), results_mut_(results_mut), cancel_after_(cancel_after), |
18 | 22 | cancelled_(false), targets_(targets), query_(query), unicast_socket_(io), |
19 | | - broadcast_socket_(io), multicast_socket_(io), recv_socket_(io), cancel_timer_(io) { |
| 23 | + broadcast_socket_(io), multicast_socket_(io), |
| 24 | + multicast_interfaces(api_config::get_instance()->multicast_interfaces), recv_socket_(io), |
| 25 | + cancel_timer_(io) { |
20 | 26 | // open the sockets that we might need |
21 | 27 | recv_socket_.open(protocol); |
22 | 28 | try { |
@@ -70,7 +76,7 @@ void resolve_attempt_udp::begin() { |
70 | 76 | // initiate the result gathering chain |
71 | 77 | receive_next_result(); |
72 | 78 | // initiate the send chain |
73 | | - send_next_query(targets_.begin()); |
| 79 | + send_next_query(targets_.begin(), multicast_interfaces.begin()); |
74 | 80 |
|
75 | 81 | // also initiate the cancel event, if desired |
76 | 82 | if (cancel_after_ != FOREVER) { |
@@ -144,27 +150,41 @@ void resolve_attempt_udp::handle_receive_outcome(err_t err, std::size_t len) { |
144 | 150 |
|
145 | 151 | // === send loop === |
146 | 152 |
|
147 | | -void resolve_attempt_udp::send_next_query(endpoint_list::const_iterator next) { |
148 | | - if (next == targets_.end() || cancelled_) return; |
149 | | - |
150 | | - udp::endpoint ep(*next++); |
151 | | - // endpoint matches our active protocol? |
152 | | - if (ep.protocol() == recv_socket_.local_endpoint().protocol()) { |
153 | | - // select socket to use |
154 | | - udp::socket &sock = |
155 | | - (ep.address() == asio::ip::address_v4::broadcast()) |
156 | | - ? broadcast_socket_ |
157 | | - : (ep.address().is_multicast() ? multicast_socket_ : unicast_socket_); |
158 | | - // and send the query over it |
159 | | - sock.async_send_to(asio::buffer(query_msg_), ep, |
160 | | - [shared_this = shared_from_this(), next](err_t err, size_t /*unused*/) { |
161 | | - if (!shared_this->cancelled_ && err != asio::error::operation_aborted && |
162 | | - err != asio::error::not_connected && err != asio::error::not_socket) |
163 | | - shared_this->send_next_query(next); |
164 | | - }); |
| 153 | +void resolve_attempt_udp::send_next_query( |
| 154 | + endpoint_list::const_iterator next, mcast_interface_list::const_iterator mcit) { |
| 155 | + if (cancelled_ || mcit == multicast_interfaces.end()) return; |
| 156 | + auto proto = recv_socket_.local_endpoint().protocol(); |
| 157 | + if (next == targets_.begin()) { |
| 158 | + // Mismatching protocols? Skip this round |
| 159 | + if (mcit->addr.is_v4() != (proto == asio::ip::udp::v4())) |
| 160 | + next = targets_.end(); |
| 161 | + else |
| 162 | + multicast_socket_.set_option(mcit->addr.is_v4() ? outbound_interface(mcit->addr.to_v4()) |
| 163 | + : outbound_interface(mcit->ifindex)); |
| 164 | + } |
| 165 | + if (next != targets_.end()) { |
| 166 | + udp::endpoint ep(*next++); |
| 167 | + // endpoint matches our active protocol? |
| 168 | + if (ep.protocol() == recv_socket_.local_endpoint().protocol()) { |
| 169 | + // select socket to use |
| 170 | + udp::socket &sock = |
| 171 | + (ep.address() == asio::ip::address_v4::broadcast()) |
| 172 | + ? broadcast_socket_ |
| 173 | + : (ep.address().is_multicast() ? multicast_socket_ : unicast_socket_); |
| 174 | + // and send the query over it |
| 175 | + auto keepalive(shared_from_this()); |
| 176 | + sock.async_send_to(lslboost::asio::buffer(query_msg_), ep, |
| 177 | + [shared_this = shared_from_this(), next, mcit](err_t err, size_t /*unused*/) { |
| 178 | + if (!shared_this->cancelled_ && err != asio::error::operation_aborted && |
| 179 | + err != asio::error::not_connected && err != asio::error::not_socket) |
| 180 | + shared_this->send_next_query(next, mcit); |
| 181 | + }); |
| 182 | + } else |
| 183 | + // otherwise just go directly to the next query |
| 184 | + send_next_query(next, mcit); |
165 | 185 | } else |
166 | | - // otherwise just go directly to the next query |
167 | | - send_next_query(next); |
| 186 | + // Restart from the next interface |
| 187 | + send_next_query(targets_.begin(), ++mcit); |
168 | 188 | } |
169 | 189 |
|
170 | 190 | void resolve_attempt_udp::do_cancel() { |
|
0 commit comments