From 9a070c02dcad62c446b3b475a91d97beee171b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Smr=C5=BE?= Date: Wed, 18 Mar 2026 20:47:03 +0100 Subject: Retry with ephemeral port if default one is already in use Changelog: Avoid binding the same UDP port from multiple server instances on a single host. --- src/Erebos/Network.hs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src/Erebos') diff --git a/src/Erebos/Network.hs b/src/Erebos/Network.hs index 518e992..7c8c1e1 100644 --- a/src/Erebos/Network.hs +++ b/src/Erebos/Network.hs @@ -62,6 +62,8 @@ import GHC.Conc.Sync (unsafeIOToSTM) import Network.Socket hiding (ControlMessage) import Network.Socket.ByteString qualified as S +import System.IO.Error + import Erebos.Error import Erebos.Identity import Erebos.Network.Address @@ -115,6 +117,7 @@ getNextPeerChange = atomically . readTChan . serverChanPeer data ServerOptions = ServerOptions { serverPort :: PortNumber + , serverRetryUnspecifiedPort :: Bool , serverLocalDiscovery :: Bool , serverErrorPrefix :: String , serverTestLog :: Bool @@ -123,6 +126,7 @@ data ServerOptions = ServerOptions defaultServerOptions :: ServerOptions defaultServerOptions = ServerOptions { serverPort = discoveryPort + , serverRetryUnspecifiedPort = True , serverLocalDiscovery = True , serverErrorPrefix = "" , serverTestLog = False @@ -293,10 +297,16 @@ startServer serverOptions serverOrigHead logd' serverServices = do let open addr = do sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr) putMVar serverSocket sock - setSocketOption sock ReuseAddr 1 setSocketOption sock Broadcast 1 withFdSocket sock setCloseOnExecIfNeeded - bind sock (addrAddress addr) + bind sock (addrAddress addr) `catchIOError` \e -> if + | isAlreadyInUseError e && serverRetryUnspecifiedPort serverOptions + , SockAddrInet6 _ f h s <- addrAddress addr + -> do atomically $ logd $ if serverPort serverOptions == discoveryPort + then "Failed to bind default discovery port, will not receive discovery packets on local network." + else "Failed to bind port " <> show (serverPort serverOptions) <> ", retrying with ephemeral one." + bind sock (SockAddrInet6 0 f h s) + | otherwise -> ioError e return sock loop sock = do -- cgit v1.2.3