From 6ca0b93809d6cea852d854578e33a43ab843f316 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 6 Sep 2020 13:49:26 -0700 Subject: [PATCH] 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. --- src/PlatformSockets.c | 62 +++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/PlatformSockets.c b/src/PlatformSockets.c index 450412a..021fcaf 100644 --- a/src/PlatformSockets.c +++ b/src/PlatformSockets.c @@ -133,36 +133,46 @@ int pollSockets(struct pollfd* pollFds, int pollFdsCount, int timeoutMs) { int recvUdpSocket(SOCKET s, char* buffer, int size, int useSelect) { int err; - if (useSelect) { - struct pollfd pfd; + do { + if (useSelect) { + struct pollfd pfd; - // Wait up to 100 ms for the socket to be readable - pfd.fd = s; - pfd.events = POLLIN; - err = pollSockets(&pfd, 1, UDP_RECV_POLL_TIMEOUT_MS); - if (err <= 0) { - // Return if an error or timeout occurs - return err; + // Wait up to 100 ms for the socket to be readable + pfd.fd = s; + pfd.events = POLLIN; + err = pollSockets(&pfd, 1, UDP_RECV_POLL_TIMEOUT_MS); + if (err <= 0) { + // Return if an error or timeout occurs + 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 - return (int)recv(s, buffer, size, 0); - } - 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)recv(s, buffer, size, 0); - if (err < 0 && - (LastSocketError() == EWOULDBLOCK || - LastSocketError() == EINTR || - LastSocketError() == EAGAIN)) { - // Return 0 for timeout - return 0; - } + // We may receive an error due to a previous ICMP Port Unreachable error received + // 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. +#if defined(LC_WINDOWS) + } while (err < 0 && LastSocketError() == WSAECONNRESET); +#else + } while (err < 0 && LastSocketError() == ECONNREFUSED); +#endif - return err; - } + return err; } void closeSocket(SOCKET s) {