PCP or NAT-PMP send-receive loop.
| 222 | |
| 223 | //! PCP or NAT-PMP send-receive loop. |
| 224 | std::optional<std::vector<uint8_t>> PCPSendRecv(Sock &sock, const std::string &protocol, std::span<const uint8_t> request, int num_tries, |
| 225 | std::chrono::milliseconds timeout_per_try, |
| 226 | std::function<bool(std::span<const uint8_t>)> check_packet, |
| 227 | CThreadInterrupt& interrupt) |
| 228 | { |
| 229 | using namespace std::chrono; |
| 230 | // UDP is a potentially lossy protocol, so we try to send again a few times. |
| 231 | uint8_t response[PCP_MAX_SIZE]; |
| 232 | bool got_response = false; |
| 233 | int recvsz = 0; |
| 234 | for (int ntry = 0; !got_response && ntry < num_tries; ++ntry) { |
| 235 | if (ntry > 0) { |
| 236 | LogDebug(BCLog::NET, "%s: Retrying (%d)\n", protocol, ntry); |
| 237 | } |
| 238 | // Dispatch packet to gateway. |
| 239 | if (sock.Send(request.data(), request.size(), 0) != static_cast<ssize_t>(request.size())) { |
| 240 | LogDebug(BCLog::NET, "%s: Could not send request: %s\n", protocol, NetworkErrorString(WSAGetLastError())); |
| 241 | return std::nullopt; // Network-level error, probably no use retrying. |
| 242 | } |
| 243 | |
| 244 | // Wait for response(s) until we get a valid response, a network error, or time out. |
| 245 | auto cur_time = time_point_cast<milliseconds>(MockableSteadyClock::now()); |
| 246 | auto deadline = cur_time + timeout_per_try; |
| 247 | while ((cur_time = time_point_cast<milliseconds>(MockableSteadyClock::now())) < deadline) { |
| 248 | if (interrupt) return std::nullopt; |
| 249 | Sock::Event occurred = 0; |
| 250 | if (!sock.Wait(deadline - cur_time, Sock::RecvEvent, &occurred)) { |
| 251 | LogWarning("%s: Could not wait on socket: %s\n", protocol, NetworkErrorString(WSAGetLastError())); |
| 252 | return std::nullopt; // Network-level error, probably no use retrying. |
| 253 | } |
| 254 | if (!occurred) { |
| 255 | LogDebug(BCLog::NET, "%s: Timeout\n", protocol); |
| 256 | break; // Retry. |
| 257 | } |
| 258 | |
| 259 | // Receive response. |
| 260 | recvsz = sock.Recv(response, sizeof(response), MSG_DONTWAIT); |
| 261 | if (recvsz < 0) { |
| 262 | LogDebug(BCLog::NET, "%s: Could not receive response: %s\n", protocol, NetworkErrorString(WSAGetLastError())); |
| 263 | return std::nullopt; // Network-level error, probably no use retrying. |
| 264 | } |
| 265 | LogDebug(BCLog::NET, "%s: Received response of %d bytes: %s\n", protocol, recvsz, HexStr(std::span(response, recvsz))); |
| 266 | |
| 267 | if (check_packet(std::span<uint8_t>(response, recvsz))) { |
| 268 | got_response = true; // Got expected response, break from receive loop as well as from retry loop. |
| 269 | break; |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | if (!got_response) { |
| 274 | LogDebug(BCLog::NET, "%s: Giving up after %d tries\n", protocol, num_tries); |
| 275 | return std::nullopt; |
| 276 | } |
| 277 | return std::vector<uint8_t>(response, response + recvsz); |
| 278 | } |
| 279 | |
| 280 | } |
| 281 |