diff --git a/DnsServerCore/Dhcp/DhcpServer.cs b/DnsServerCore/Dhcp/DhcpServer.cs index ece2fcb6..54c46c6d 100644 --- a/DnsServerCore/Dhcp/DhcpServer.cs +++ b/DnsServerCore/Dhcp/DhcpServer.cs @@ -695,7 +695,7 @@ private bool UnbindUdpListener(IPEndPoint dhcpEP) { if (_udpListeners.TryRemove(dhcpEP.Address, out Socket socket)) { - socket.Dispose(); + socket.CloseWorkAround(); return true; } diff --git a/DnsServerCore/Dns/DnsServer.cs b/DnsServerCore/Dns/DnsServer.cs index db587ab7..3612a5a9 100644 --- a/DnsServerCore/Dns/DnsServer.cs +++ b/DnsServerCore/Dns/DnsServer.cs @@ -2292,20 +2292,67 @@ public void Stop() _cacheMaintenanceTimer = null; } - foreach (Socket udpListener in _udpListeners) - udpListener.Dispose(); + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + foreach (Socket udpListener in _udpListeners) + udpListener.Dispose(); - foreach (Socket tcpListener in _tcpListeners) - tcpListener.Dispose(); + foreach (Socket tcpListener in _tcpListeners) + tcpListener.Dispose(); - foreach (Socket httpListener in _httpListeners) - httpListener.Dispose(); + foreach (Socket httpListener in _httpListeners) + httpListener.Dispose(); - foreach (Socket tlsListener in _tlsListeners) - tlsListener.Dispose(); + foreach (Socket tlsListener in _tlsListeners) + tlsListener.Dispose(); - foreach (Socket httpsListener in _httpsListeners) - httpsListener.Dispose(); + foreach (Socket httpsListener in _httpsListeners) + httpsListener.Dispose(); + } + else + { + //issue: https://github.com/dotnet/runtime/issues/37873 + + foreach (Socket udpListener in _udpListeners) + { + ThreadPool.QueueUserWorkItem(delegate (object state) + { + udpListener.Dispose(); + }); + } + + foreach (Socket tcpListener in _tcpListeners) + { + ThreadPool.QueueUserWorkItem(delegate (object state) + { + tcpListener.Dispose(); + }); + } + + foreach (Socket httpListener in _httpListeners) + { + ThreadPool.QueueUserWorkItem(delegate (object state) + { + httpListener.Dispose(); + }); + } + + foreach (Socket tlsListener in _tlsListeners) + { + ThreadPool.QueueUserWorkItem(delegate (object state) + { + tlsListener.Dispose(); + }); + } + + foreach (Socket httpsListener in _httpsListeners) + { + ThreadPool.QueueUserWorkItem(delegate (object state) + { + httpsListener.Dispose(); + }); + } + } _listenerThreads.Clear(); _udpListeners.Clear(); diff --git a/DnsServerCore/SocketExtension.cs b/DnsServerCore/SocketExtension.cs new file mode 100644 index 00000000..01b71678 --- /dev/null +++ b/DnsServerCore/SocketExtension.cs @@ -0,0 +1,67 @@ +/* +Technitium DNS Server +Copyright (C) 2020 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace DnsServerCore +{ + static class SocketExtension + { + public static void CloseWorkAround(this Socket socket) + { + //issue: https://github.com/dotnet/runtime/issues/37873 + + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + socket.Dispose(); + } + else + { + IPEndPoint localEP = socket.LocalEndPoint as IPEndPoint; + EventWaitHandle waitHandle = new AutoResetEvent(false); + + ThreadPool.QueueUserWorkItem(delegate (object state) + { + waitHandle.Set(); + socket.Dispose(); + waitHandle.Set(); + }); + + waitHandle.WaitOne(); + Thread.Sleep(1000); //wait to ensure the above thread has called socket.Dispose() + + //send empty UDP packet to release thread blocking on Socket.ReceiveMessageFrom() call + using (Socket s = new Socket(localEP.AddressFamily, SocketType.Dgram, ProtocolType.Udp)) + { + if (localEP.Address.Equals(IPAddress.Any)) + localEP = new IPEndPoint(IPAddress.Loopback, localEP.Port); + else if (localEP.Address.Equals(IPAddress.IPv6Any)) + localEP = new IPEndPoint(IPAddress.IPv6Loopback, localEP.Port); + + s.SendTo(new byte[] { }, localEP); + } + + waitHandle.WaitOne(); + } + } + } +}