Root cause
Other / insufficient public mechanism detail introduced
The new relay server launched its UDP receive loop as an unsupervised background task and handled datagram failures only outside the loop.
How AI contributed: Direct introductionPublished Aug 12, 2026 in
sipsorcery-org/sipsorcery
The in-process TURN relay server added in this change started its UDP receive loop from Start() as a fire-and-forget task. Each datagram was handed straight to HandleUdpDatagram, and the only catch clause for a failed parse sat outside the receive loop. A relay socket is reachable by anyone who can send to the port, so a single datagram the STUN parser rejects threw out of the loop body, unwound the loop, and left the listener stopped. Nothing supervises the task, so the UDP relay stayed down for every allocation until the server was restarted. The fix isolates processing of one datagram inside the loop and logs the failure, so the listener keeps running.
The new relay called HandleUdpDatagram from inside ReceiveUdpAsync, which Start() launches without awaiting. The STUN parser throws on datagrams it does not recognise, and that exception unwound past the while loop, so one malformed packet ended the loop and the UDP relay stayed down for every client until the process restarted.
Root cause
The new relay server launched its UDP receive loop as an unsupervised background task and handled datagram failures only outside the loop.
Fix
Processing one datagram now runs inside its own try/catch, so a packet the parser rejects can no longer end the receive loop.
Case facts
@@ -0,0 +429,24 @@++ private async Task ReceiveUdpAsync()+ {+ try+ {+ while (_running)+ {+ UdpReceiveResult result;+ try+ {+ result = await _udpSocket.ReceiveAsync().ConfigureAwait(false);+ }+ catch (ObjectDisposedException) { break; }+ catch (SocketException) { break; }++ HandleUdpDatagram(result.Buffer, result.RemoteEndPoint);+ }+ }+ catch (ObjectDisposedException) { }+ catch (Exception ex)+ {+ logger.LogError(ex, "TURN UDP receive loop error. {ErrorMessage}", ex.Message);+ }+ }@@ -583,7 +614,21 @@ namespace SIPSorcery.Net catch (ObjectDisposedException) { break; } catch (SocketException) { break; } - HandleUdpDatagram(result.Buffer, result.RemoteEndPoint);+ // Processing a datagram is isolated from the receive loop. The datagram is+ // unauthenticated and arbitrary, and the STUN parser throws on input it does not+ // recognise, so without this a single malformed datagram from anyone who can reach+ // the port unwinds past the loop. The loop is started fire and forget with no+ // supervision, so nothing would restart it and the UDP relay would stay down for+ // every client until the process was restarted.+ try+ {+ HandleUdpDatagram(result.Buffer, result.RemoteEndPoint);+ }+ catch (Exception datagramExcp)+ {+ logger.LogWarning(datagramExcp, "TURN server dropped a UDP datagram from {Remote} that could not be processed. {ErrorMessage}",+ result.RemoteEndPoint, datagramExcp.Message);+ } } } catch (ObjectDisposedException) { }AI-assisted change 463e93033770e7416550fb26dfc24a431809f39804a7e13c2c4ad9e8ca395567 · Fix ae84b37acc59f81623d835fbd84962a9adf50a1b940d0e2da650e364088a750b