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