Ignore ICMP Port Unreachable messages on our UDP sockets

These messages can falsely indicate failure in cases where the UDP socket
for audio or video hasn't opened yet when we start to send our PING packets.

It is also problematic when sending STUN requests to multiple IP addresses.
If one returns an ICMP Port Unreachable, it can cause us to fail the entire set
of requests even when other IP addresses do respond.
This commit is contained in:
Cameron Gutman
2020-09-06 13:49:26 -07:00
parent 7b024c967e
commit 6ca0b93809
+36 -26
View File
@@ -133,36 +133,46 @@ int pollSockets(struct pollfd* pollFds, int pollFdsCount, int timeoutMs) {
int recvUdpSocket(SOCKET s, char* buffer, int size, int useSelect) { int recvUdpSocket(SOCKET s, char* buffer, int size, int useSelect) {
int err; int err;
if (useSelect) { do {
struct pollfd pfd; if (useSelect) {
struct pollfd pfd;
// Wait up to 100 ms for the socket to be readable // Wait up to 100 ms for the socket to be readable
pfd.fd = s; pfd.fd = s;
pfd.events = POLLIN; pfd.events = POLLIN;
err = pollSockets(&pfd, 1, UDP_RECV_POLL_TIMEOUT_MS); err = pollSockets(&pfd, 1, UDP_RECV_POLL_TIMEOUT_MS);
if (err <= 0) { if (err <= 0) {
// Return if an error or timeout occurs // Return if an error or timeout occurs
return err; return err;
}
// This won't block since the socket is readable
err = (int)recvfrom(s, buffer, size, 0, NULL, NULL);
}
else {
// The caller has already configured a timeout on this
// socket via SO_RCVTIMEO, so we can avoid a syscall
// for each packet.
err = (int)recvfrom(s, buffer, size, 0, NULL, NULL);
if (err < 0 &&
(LastSocketError() == EWOULDBLOCK ||
LastSocketError() == EINTR ||
LastSocketError() == EAGAIN)) {
// Return 0 for timeout
return 0;
}
} }
// This won't block since the socket is readable // We may receive an error due to a previous ICMP Port Unreachable error received
return (int)recv(s, buffer, size, 0); // by this socket. We want to ignore those and continue reading. If the remote party
} // is really dead, ENet or TCP connection failures will trigger connection teardown.
else { #if defined(LC_WINDOWS)
// The caller has already configured a timeout on this } while (err < 0 && LastSocketError() == WSAECONNRESET);
// socket via SO_RCVTIMEO, so we can avoid a syscall #else
// for each packet. } while (err < 0 && LastSocketError() == ECONNREFUSED);
err = (int)recv(s, buffer, size, 0); #endif
if (err < 0 &&
(LastSocketError() == EWOULDBLOCK ||
LastSocketError() == EINTR ||
LastSocketError() == EAGAIN)) {
// Return 0 for timeout
return 0;
}
return err; return err;
}
} }
void closeSocket(SOCKET s) { void closeSocket(SOCKET s) {