diff options
Diffstat (limited to 'src/Erebos/Discovery.hs')
-rw-r--r-- | src/Erebos/Discovery.hs | 147 |
1 files changed, 115 insertions, 32 deletions
diff --git a/src/Erebos/Discovery.hs b/src/Erebos/Discovery.hs index f0535fe..cbb12ca 100644 --- a/src/Erebos/Discovery.hs +++ b/src/Erebos/Discovery.hs @@ -2,6 +2,7 @@ module Erebos.Discovery ( DiscoveryService(..), + DiscoveryAttributes(..), DiscoveryConnection(..) ) where @@ -16,6 +17,7 @@ import Data.Map.Strict qualified as M import Data.Maybe import Data.Text (Text) import Data.Text qualified as T +import Data.Word import Network.Socket @@ -29,19 +31,35 @@ import Erebos.Service import Erebos.Storable -data DiscoveryService = DiscoverySelf [ Text ] (Maybe Int) - | DiscoveryAcknowledged Text - | DiscoverySearch Ref - | DiscoveryResult Ref [ Text ] - | DiscoveryConnectionRequest DiscoveryConnection - | DiscoveryConnectionResponse DiscoveryConnection +data DiscoveryService + = DiscoverySelf [ Text ] (Maybe Int) + | DiscoveryAcknowledged [ Text ] (Maybe Text) (Maybe Word16) (Maybe Text) (Maybe Word16) + | DiscoverySearch Ref + | DiscoveryResult Ref [ Text ] + | DiscoveryConnectionRequest DiscoveryConnection + | DiscoveryConnectionResponse DiscoveryConnection + +data DiscoveryAttributes = DiscoveryAttributes + { discoveryStunPort :: Maybe Word16 + , discoveryStunServer :: Maybe Text + , discoveryTurnPort :: Maybe Word16 + , discoveryTurnServer :: Maybe Text + } + +defaultDiscoveryAttributes :: DiscoveryAttributes +defaultDiscoveryAttributes = DiscoveryAttributes + { discoveryStunPort = Nothing + , discoveryStunServer = Nothing + , discoveryTurnPort = Nothing + , discoveryTurnServer = Nothing + } data DiscoveryConnection = DiscoveryConnection { dconnSource :: Ref , dconnTarget :: Ref , dconnAddress :: Maybe Text #ifdef ENABLE_ICE_SUPPORT - , dconnIceSession :: Maybe IceRemoteInfo + , dconnIceInfo :: Maybe IceRemoteInfo #endif } @@ -50,7 +68,7 @@ emptyConnection dconnSource dconnTarget = DiscoveryConnection {..} where dconnAddress = Nothing #ifdef ENABLE_ICE_SUPPORT - dconnIceSession = Nothing + dconnIceInfo = Nothing #endif instance Storable DiscoveryService where @@ -59,8 +77,13 @@ instance Storable DiscoveryService where DiscoverySelf addrs priority -> do mapM_ (storeText "self") addrs mapM_ (storeInt "priority") priority - DiscoveryAcknowledged addr -> do - storeText "ack" addr + DiscoveryAcknowledged addrs stunServer stunPort turnServer turnPort -> do + if null addrs then storeEmpty "ack" + else mapM_ (storeText "ack") addrs + storeMbText "stun-server" stunServer + storeMbInt "stun-port" stunPort + storeMbText "turn-server" turnServer + storeMbInt "turn-port" turnPort DiscoverySearch ref -> storeRawRef "search" ref DiscoveryResult ref addr -> do storeRawRef "result" ref @@ -74,7 +97,7 @@ instance Storable DiscoveryService where storeRawRef "target" $ dconnTarget conn storeMbText "address" $ dconnAddress conn #ifdef ENABLE_ICE_SUPPORT - storeMbRef "ice-session" $ dconnIceSession conn + storeMbRef "ice-info" $ dconnIceInfo conn #endif load' = loadRec $ msum @@ -83,8 +106,16 @@ instance Storable DiscoveryService where guard (not $ null addrs) DiscoverySelf addrs <$> loadMbInt "priority" - , DiscoveryAcknowledged - <$> loadText "ack" + , do + addrs <- loadTexts "ack" + mbEmpty <- loadMbEmpty "ack" + guard (not (null addrs) || isJust mbEmpty) + DiscoveryAcknowledged + <$> pure addrs + <*> loadMbText "stun-server" + <*> loadMbInt "stun-port" + <*> loadMbText "turn-server" + <*> loadMbInt "turn-port" , DiscoverySearch <$> loadRawRef "search" , DiscoveryResult <$> loadRawRef "result" @@ -100,7 +131,7 @@ instance Storable DiscoveryService where <*> loadRawRef "target" <*> loadMbText "address" #ifdef ENABLE_ICE_SUPPORT - <*> loadMbRef "ice-session" + <*> loadMbRef "ice-info" #endif data DiscoveryPeer = DiscoveryPeer @@ -115,6 +146,14 @@ data DiscoveryPeer = DiscoveryPeer instance Service DiscoveryService where serviceID _ = mkServiceID "dd59c89c-69cc-4703-b75b-4ddcd4b3c23c" + type ServiceAttributes DiscoveryService = DiscoveryAttributes + defaultServiceAttributes _ = defaultDiscoveryAttributes + +#ifdef ENABLE_ICE_SUPPORT + type ServiceState DiscoveryService = Maybe IceConfig + emptyServiceState _ = Nothing +#endif + type ServiceGlobalState DiscoveryService = Map RefDigest DiscoveryPeer emptyServiceGlobalState _ = M.empty @@ -124,7 +163,7 @@ instance Service DiscoveryService where peer <- asks svcPeer let insertHelper new old | dpPriority new > dpPriority old = new | otherwise = old - mbaddr <- fmap (listToMaybe . catMaybes) $ forM addrs $ \addr -> case words (T.unpack addr) of + matchedAddrs <- fmap catMaybes $ forM addrs $ \addr -> case words (T.unpack addr) of [ipaddr, port] | DatagramAddress paddr <- peerAddress peer -> do saddr <- liftIO $ head <$> getAddrInfo (Just $ defaultHints { addrSocketType = Datagram }) (Just ipaddr) (Just port) return $ if paddr == addrAddress saddr @@ -140,9 +179,40 @@ instance Service DiscoveryService where , dpIceSession = Nothing #endif } - replyPacket $ DiscoveryAcknowledged $ fromMaybe (T.pack "ICE") mbaddr - - DiscoveryAcknowledged _ -> do + let matchedAddrs' = matchedAddrs +#ifdef ENABLE_ICE_SUPPORT + ++ filter (== T.pack "ICE") addrs +#endif + attrs <- asks svcAttributes + replyPacket $ DiscoveryAcknowledged matchedAddrs' + (discoveryStunServer attrs) + (discoveryStunPort attrs) + (discoveryTurnServer attrs) + (discoveryTurnPort attrs) + + DiscoveryAcknowledged addrs stunServer stunPort turnServer turnPort -> do +#ifdef ENABLE_ICE_SUPPORT + when (T.pack "ICE" `elem` addrs) $ do + paddr <- asks (peerAddress . svcPeer) >>= return . \case + (DatagramAddress saddr) -> case IP.fromSockAddr saddr of + Just (IP.IPv6 ipv6, _) + | (0, 0, 0xffff, ipv4) <- IP.fromIPv6w ipv6 + -> Just $ T.pack $ show (IP.toIPv4w ipv4) + Just (addr, _) + -> Just $ T.pack $ show addr + _ -> Nothing + _ -> Nothing + + let toIceServer Nothing Nothing = Nothing + toIceServer Nothing (Just port) = ( , port) <$> paddr + toIceServer (Just server) Nothing = Just ( server, 0 ) + toIceServer (Just server) (Just port) = Just ( server, port ) + + cfg <- liftIO $ iceCreateConfig + (toIceServer stunServer stunPort) + (toIceServer turnServer turnPort) + svcSet cfg +#endif return () DiscoverySearch ref -> do @@ -156,16 +226,19 @@ instance Service DiscoveryService where -- TODO: check if we really requested that server <- asks svcServer self <- svcSelf + mbIceConfig <- svcGet discoveryPeer <- asks svcPeer let runAsService = runPeerService @DiscoveryService discoveryPeer liftIO $ void $ forkIO $ forM_ addrs $ \addr -> if - | addr == T.pack "ICE" -> do + | addr == T.pack "ICE" #ifdef ENABLE_ICE_SUPPORT - ice <- iceCreate PjIceSessRoleControlling $ \ice -> do + , Just config <- mbIceConfig + -> do + ice <- iceCreateSession config PjIceSessRoleControlling $ \ice -> do rinfo <- iceRemoteInfo ice res <- runExceptT $ sendToPeer discoveryPeer $ - DiscoveryConnectionRequest (emptyConnection (storedRef $ idData self) ref) { dconnIceSession = Just rinfo } + DiscoveryConnectionRequest (emptyConnection (storedRef $ idData self) ref) { dconnIceInfo = Just rinfo } case res of Right _ -> return () Left err -> putStrLn $ "Discovery: failed to send connection request: " ++ err @@ -178,6 +251,7 @@ instance Service DiscoveryService where , dpIceSession = Just ice } #else + -> do return () #endif @@ -208,15 +282,19 @@ instance Service DiscoveryService where -- request for us, create ICE sesssion server <- asks svcServer peer <- asks svcPeer - liftIO $ void $ iceCreate PjIceSessRoleControlled $ \ice -> do - rinfo <- iceRemoteInfo ice - res <- runExceptT $ sendToPeer peer $ DiscoveryConnectionResponse rconn { dconnIceSession = Just rinfo } - case res of - Right _ -> do - case dconnIceSession conn of - Just prinfo -> iceConnect ice prinfo $ void $ serverPeerIce server ice - Nothing -> putStrLn $ "Discovery: connection request without ICE remote info" - Left err -> putStrLn $ "Discovery: failed to send connection response: " ++ err + svcGet >>= \case + Just config -> do + liftIO $ void $ iceCreateSession config PjIceSessRoleControlled $ \ice -> do + rinfo <- iceRemoteInfo ice + res <- runExceptT $ sendToPeer peer $ DiscoveryConnectionResponse rconn { dconnIceInfo = Just rinfo } + case res of + Right _ -> do + case dconnIceInfo conn of + Just prinfo -> iceConnect ice prinfo $ void $ serverPeerIce server ice + Nothing -> putStrLn $ "Discovery: connection request without ICE remote info" + Left err -> putStrLn $ "Discovery: failed to send connection response: " ++ err + Nothing -> do + svcPrint $ "Discovery: ICE request from peer without ICE configuration" else do -- request to some of our peers, relay @@ -250,7 +328,7 @@ instance Service DiscoveryService where | Just dp <- M.lookup (refDigest $ dconnTarget conn) dpeers , Just ice <- dpIceSession dp - , Just rinfo <- dconnIceSession conn -> do + , Just rinfo <- dconnIceInfo conn -> do liftIO $ iceConnect ice rinfo $ void $ serverPeerIce server ice | otherwise -> svcPrint $ "Discovery: connection request failed" @@ -271,6 +349,11 @@ instance Service DiscoveryService where let addrToText saddr = do ( addr, port ) <- IP.fromSockAddr saddr Just $ T.pack $ show addr <> " " <> show port - addrs <- catMaybes . map addrToText <$> liftIO (getServerAddresses server) + addrs <- concat <$> sequence + [ catMaybes . map addrToText <$> liftIO (getServerAddresses server) +#ifdef ENABLE_ICE_SUPPORT + , return [ T.pack "ICE" ] +#endif + ] sendToPeer peer $ DiscoverySelf addrs Nothing |