summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRoman Smrž <roman.smrz@seznam.cz>2026-03-18 20:47:03 +0100
committerRoman Smrž <roman.smrz@seznam.cz>2026-03-18 21:41:57 +0100
commit9a070c02dcad62c446b3b475a91d97beee171b57 (patch)
tree78cf8af5e6c8fd783592be9a77e3330c351a0d51 /src
parentcf2a95bcec2a7e23204865e26d57a5fb16b92fe6 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/Erebos/Network.hs14
1 files changed, 12 insertions, 2 deletions
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