summaryrefslogtreecommitdiff
path: root/examples/dhtd.hs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/dhtd.hs')
-rw-r--r--examples/dhtd.hs165
1 files changed, 91 insertions, 74 deletions
diff --git a/examples/dhtd.hs b/examples/dhtd.hs
index 837cb210..219221e5 100644
--- a/examples/dhtd.hs
+++ b/examples/dhtd.hs
@@ -21,11 +21,12 @@ module Main where
21import Control.Arrow 21import Control.Arrow
22import Control.Applicative 22import Control.Applicative
23import Control.Concurrent.STM 23import Control.Concurrent.STM
24import Control.DeepSeq
25import Control.Exception 24import Control.Exception
26import Control.Monad 25import Control.Monad
26import Control.Monad.Trans.Resource (runResourceT)
27import Data.Bool 27import Data.Bool
28import Data.Char 28import Data.Char
29import Data.Function
29import Data.Hashable 30import Data.Hashable
30import Data.List 31import Data.List
31import qualified Data.IntMap.Strict as IntMap 32import qualified Data.IntMap.Strict as IntMap
@@ -53,6 +54,8 @@ import qualified Data.HashMap.Strict as HashMap
53import qualified Data.Vector as V 54import qualified Data.Vector as V
54import qualified Data.Text as T 55import qualified Data.Text as T
55import qualified Data.Text.Encoding as T 56import qualified Data.Text.Encoding as T
57import System.Posix.Signals
58
56 59
57import Announcer 60import Announcer
58import Crypto.Tox -- (zeros32,SecretKey,PublicKey, generateSecretKey, toPublic, encodeSecret, decodeSecret, userKeys) 61import Crypto.Tox -- (zeros32,SecretKey,PublicKey, generateSecretKey, toPublic, encodeSecret, decodeSecret, userKeys)
@@ -60,8 +63,6 @@ import Network.UPNP as UPNP
60import Network.Address hiding (NodeId, NodeInfo(..)) 63import Network.Address hiding (NodeId, NodeInfo(..))
61import Network.QueryResponse 64import Network.QueryResponse
62import Network.StreamServer 65import Network.StreamServer
63import Network.Kademlia
64import Network.Kademlia.Bootstrap
65import Network.Kademlia.Search 66import Network.Kademlia.Search
66import qualified Network.BitTorrent.MainlineDHT as Mainline 67import qualified Network.BitTorrent.MainlineDHT as Mainline
67import qualified Network.Tox as Tox 68import qualified Network.Tox as Tox
@@ -71,6 +72,7 @@ import qualified Data.Aeson as J
71import qualified Data.ByteString.Lazy as L 72import qualified Data.ByteString.Lazy as L
72import qualified Data.ByteString.Char8 as B 73import qualified Data.ByteString.Char8 as B
73import Control.Concurrent.Tasks 74import Control.Concurrent.Tasks
75import Control.Monad.Trans.Control
74import System.IO.Error 76import System.IO.Error
75import qualified Data.Serialize as S 77import qualified Data.Serialize as S
76import Network.BitTorrent.DHT.ContactInfo as Peers 78import Network.BitTorrent.DHT.ContactInfo as Peers
@@ -88,6 +90,13 @@ import Data.Typeable
88import Roster 90import Roster
89import OnionRouter 91import OnionRouter
90 92
93-- Presence imports.
94import ConsoleWriter
95import Presence
96import XMPPServer
97import Connection
98
99
91showReport :: [(String,String)] -> String 100showReport :: [(String,String)] -> String
92showReport kvs = showColumns $ map (\(x,y)->[x,y]) kvs 101showReport kvs = showColumns $ map (\(x,y)->[x,y]) kvs
93 102
@@ -146,21 +155,6 @@ data DHTAnnouncable nid = forall dta tok ni r.
146 , qresultAddr :: dta -> nid 155 , qresultAddr :: dta -> nid
147 } 156 }
148 157
149data DHTLink = forall status linkid params.
150 ( Show status
151 , Show linkid
152 , Typeable status
153 , Typeable linkid
154 , Typeable params
155 ) => DHTLink
156 { linkInit :: params -> IO (Either String status)
157 , linkParamParser :: [String] -> Either String params
158 , linkStatus :: IO (Either String status)
159 , showLinkStatus :: status -> String
160 , linkNewPipe :: String -> linkid -> IO (Either String status)
161 , linkUnPipe :: linkid -> IO (Either String status)
162 }
163
164data DHTSearch nid ni = forall addr tok r. DHTSearch 158data DHTSearch nid ni = forall addr tok r. DHTSearch
165 { searchThread :: ThreadId 159 { searchThread :: ThreadId
166 , searchState :: SearchState nid addr tok ni r 160 , searchState :: SearchState nid addr tok ni r
@@ -191,7 +185,6 @@ data DHT = forall nid ni. ( Show ni
191 , dhtPing :: Map.Map String (DHTPing ni) 185 , dhtPing :: Map.Map String (DHTPing ni)
192 , dhtQuery :: Map.Map String (DHTQuery nid ni) 186 , dhtQuery :: Map.Map String (DHTQuery nid ni)
193 , dhtAnnouncables :: Map.Map String (DHTAnnouncable nid) 187 , dhtAnnouncables :: Map.Map String (DHTAnnouncable nid)
194 , dhtLinks :: Map.Map String DHTLink
195 , dhtParseId :: String -> Either String nid 188 , dhtParseId :: String -> Either String nid
196 , dhtSearches :: TVar (Map.Map (String,nid) (DHTSearch nid ni)) 189 , dhtSearches :: TVar (Map.Map (String,nid) (DHTSearch nid ni))
197 , dhtFallbackNodes :: IO [ni] 190 , dhtFallbackNodes :: IO [ni]
@@ -375,6 +368,8 @@ reportSearchResults meth h DHTSearch{searchShowTok,searchState,searchResults} =
375 ns' = map showN ns 368 ns' = map showN ns
376 reportResult meth id (const Nothing) id h (Right (ns',rs, Just ())) 369 reportResult meth id (const Nothing) id h (Right (ns',rs, Just ()))
377 370
371data ConnectionManager = forall status k. ConnectionManager { typedManager :: Connection.Manager status k }
372
378data Session = Session 373data Session = Session
379 { netname :: String 374 { netname :: String
380 , dhts :: Map.Map String DHT 375 , dhts :: Map.Map String DHT
@@ -384,9 +379,10 @@ data Session = Session
384 , toxkeys :: TVar Tox.AnnouncedKeys 379 , toxkeys :: TVar Tox.AnnouncedKeys
385 , userkeys :: TVar [(SecretKey,PublicKey)] 380 , userkeys :: TVar [(SecretKey,PublicKey)]
386 , roster :: Roster 381 , roster :: Roster
382 , connectionManager :: ConnectionManager
387 , onionRouter :: OnionRouter 383 , onionRouter :: OnionRouter
388 , announcer :: Announcer 384 , announcer :: Announcer
389 , signalQuit :: MVar () 385 , signalQuit :: IO ()
390 } 386 }
391 387
392exceptionsToClient :: ClientHandle -> IO () -> IO () 388exceptionsToClient :: ClientHandle -> IO () -> IO ()
@@ -481,7 +477,7 @@ clientSession s@Session{..} sock cnum h = do
481 477
482 ("stop", _) -> do hPutClient h "Terminating DHT Daemon." 478 ("stop", _) -> do hPutClient h "Terminating DHT Daemon."
483 hCloseClient h 479 hCloseClient h
484 putMVar signalQuit () 480 signalQuit
485 481
486 ("throw", er) -> cmd0 $ do 482 ("throw", er) -> cmd0 $ do
487 throwIO $ userError er 483 throwIO $ userError er
@@ -633,7 +629,6 @@ clientSession s@Session{..} sock cnum h = do
633 hPutClientChunk h $ "trampolines: " ++ show (IntMap.size ts) ++ "\n" 629 hPutClientChunk h $ "trampolines: " ++ show (IntMap.size ts) ++ "\n"
634 hPutClient h $ showColumns $ ["","responses","timeouts"]:r 630 hPutClient h $ showColumns $ ["","responses","timeouts"]:r
635 631
636
637 ("g", s) | Just DHT{..} <- Map.lookup netname dhts 632 ("g", s) | Just DHT{..} <- Map.lookup netname dhts
638 -> cmd0 $ do 633 -> cmd0 $ do
639 -- arguments: method 634 -- arguments: method
@@ -901,46 +896,29 @@ clientSession s@Session{..} sock cnum h = do
901 mkentry (k :-> Down tm) = [ show cnt, show k, show (now - tm) ] 896 mkentry (k :-> Down tm) = [ show cnt, show k, show (now - tm) ]
902 where Just (_,(cnt,_)) = MM.lookup' k (Tox.keyAssoc keydb) 897 where Just (_,(cnt,_)) = MM.lookup' k (Tox.keyAssoc keydb)
903 hPutClient h $ showColumns entries 898 hPutClient h $ showColumns entries
904 ("c", s) | "" <- strp s -> cmd0 $ do 899
905 let combinedLinkMap = Map.unions $map (dhtLinks . snd) (Map.toList dhts) 900 ("c", s) | "" <- strp s -> cmd0 $ join $ atomically $ do
906 -- TODO: list all connections 901 ConnectionManager mgr <- return connectionManager
907 let connections = [[{-TODO-}]] 902 cmap <- connections mgr
908 hPutClient h $ showColumns connections 903 cs <- Map.toList <$> mapM connStatus cmap
909 ("c", s) -> cmd0 $ do 904 let mkrow = Connection.showKey mgr *** Connection.showStatus mgr
910 let combinedLinkMap = Map.unions $map (dhtLinks . snd) (Map.toList dhts) 905 rs = map mkrow cs
911 -- form new connection according of type corresponding to parameter 906 return $ do
912 let ws = words s 907 hPutClient h $ showReport rs
913 result
914 <- case ws of
915 (linktype:rest)
916 -> case (Map.lookup (head ws) combinedLinkMap) of
917 Nothing -> return . Left $ "I don't know a '" ++ head ws ++ "' link type."
918 Just l@(DHTLink
919 { linkInit {- :: params -> IO (Either String status) -}
920 , linkParamParser {- :: [String] -> Either String params -}
921 , showLinkStatus {- :: status -> String -}
922 }) -> case linkParamParser rest of
923 Left er -> return $ Left er
924 Right params -> fmap showLinkStatus <$> linkInit params
925 _ -> return $ Left "parse error"
926 case result of
927 Left er -> hPutClient h er
928 Right statusstr -> hPutClient h statusstr
929 908
930 ("help", s) | Just DHT{..} <- Map.lookup netname dhts 909 ("help", s) | Just DHT{..} <- Map.lookup netname dhts
931 -> cmd0 $ do 910 -> cmd0 $ do
932 let tolist :: a -> [a] 911 let tolist :: a -> [a]
933 tolist = (:[]) 912 tolist = (:[])
934 913
935 dhtkeys, announcables, links, ks, allcommands :: [[String]] 914 dhtkeys, announcables, ks, allcommands :: [[String]]
936 dhtkeys = map tolist $ Map.keys dhts 915 dhtkeys = map tolist $ Map.keys dhts
937 queries = map (tolist . ("s "++)) $ Map.keys dhtQuery 916 queries = map (tolist . ("s "++)) $ Map.keys dhtQuery
938 xs = map (tolist . ("x "++)) $ Map.keys dhtQuery 917 xs = map (tolist . ("x "++)) $ Map.keys dhtQuery
939 gs = map (tolist . ("g "++)) $ Map.keys dhtQuery 918 gs = map (tolist . ("g "++)) $ Map.keys dhtQuery
940 announcables = map (tolist . ("p "++)) $ Map.keys dhtAnnouncables 919 announcables = map (tolist . ("p "++)) $ Map.keys dhtAnnouncables
941 links = map (tolist . ("c "++)) $ Map.keys dhtLinks
942 ks = [["k gen"],["k public"],["k secret"]] 920 ks = [["k gen"],["k public"],["k secret"]]
943 allcommands = sortBy (comparing head) $ concat [sessionCommands, dhtkeys, announcables, links, ks, queries, gs,xs] 921 allcommands = sortBy (comparing (take 1)) $ concat [sessionCommands, dhtkeys, announcables, ks, queries, gs,xs]
944 922
945 hPutClient h ("Available commands:\n" ++ showColumns allcommands) 923 hPutClient h ("Available commands:\n" ++ showColumns allcommands)
946 924
@@ -957,27 +935,36 @@ readExternals nodeAddr vars = do
957 return $ filter (not . unspecified) as 935 return $ filter (not . unspecified) as
958 936
959data Options = Options 937data Options = Options
960 { portbt :: String 938 { portbt :: String
961 , porttox :: String 939 , porttox :: String
962 , ip6bt :: Bool 940 , ip6bt :: Bool
963 , ip6tox :: Bool 941 , ip6tox :: Bool
964 , dhtkey :: Maybe SecretKey 942 , dhtkey :: Maybe SecretKey
943 -- | Currently only relevant to XMPP server code.
944 --
945 -- [ 0 ] Don't log XMPP stanzas.
946 --
947 -- [ 1 ] Log non-ping stanzas.
948 --
949 -- [ 2 ] Log all stanzas, even pings.
950 , verbosity :: Int
965 } 951 }
966 deriving (Eq,Show) 952 deriving (Eq,Show)
967 953
968sensibleDefaults :: Options 954sensibleDefaults :: Options
969sensibleDefaults = Options 955sensibleDefaults = Options
970 { portbt = "6881" 956 { portbt = "6881"
971 , porttox = "33445" 957 , porttox = "33445"
972 , ip6bt = True 958 , ip6bt = True
973 , ip6tox = True 959 , ip6tox = True
974 , dhtkey = Nothing 960 , dhtkey = Nothing
961 , verbosity = 1
975 } 962 }
976 963
977-- bt=<port>,tox=<port> 964-- bt=<port>,tox=<port>
978-- -4 965-- -4
979parseArgs :: [String] -> Options -> Options 966parseArgs :: [String] -> Options -> Options
980parseArgs [] opts = opts 967parseArgs [] opts = opts
981parseArgs ("--dhtkey":k:args) opts = parseArgs args opts 968parseArgs ("--dhtkey":k:args) opts = parseArgs args opts
982 { dhtkey = decodeSecret $ B.pack k } 969 { dhtkey = decodeSecret $ B.pack k }
983parseArgs ("-4":args) opts = parseArgs args opts 970parseArgs ("-4":args) opts = parseArgs args opts
@@ -996,7 +983,7 @@ noArgPing f [] x = f x
996noArgPing _ _ _ = return Nothing 983noArgPing _ _ _ = return Nothing
997 984
998main :: IO () 985main :: IO ()
999main = do 986main = runResourceT $ liftBaseWith $ \resT -> do
1000 args <- getArgs 987 args <- getArgs
1001 let opts = parseArgs args sensibleDefaults 988 let opts = parseArgs args sensibleDefaults
1002 print opts 989 print opts
@@ -1010,6 +997,18 @@ main = do
1010 997
1011 announcer <- forkAnnouncer 998 announcer <- forkAnnouncer
1012 999
1000 -- XMPP initialization
1001 cw <- newConsoleWriter
1002 serverVar <- atomically $ newEmptyTMVar
1003 state <- newPresenceState cw serverVar
1004
1005 -- XMPP stanza handling
1006 sv <- resT $ xmppServer (presenceHooks state (verbosity opts))
1007 -- We now have a server object but it's not ready to use until
1008 -- we put it into the 'server' field of our /state/ record.
1009
1010 conns <- xmppConnections sv
1011
1013 (quitBt,btdhts,btips,baddrs) <- case portbt opts of 1012 (quitBt,btdhts,btips,baddrs) <- case portbt opts of
1014 "" -> return (return (), Map.empty,return [],[]) 1013 "" -> return (return (), Map.empty,return [],[])
1015 p -> do 1014 p -> do
@@ -1080,9 +1079,6 @@ main = do
1080 , qresultAddr = const $ Mainline.zeroID 1079 , qresultAddr = const $ Mainline.zeroID
1081 })] 1080 })]
1082 1081
1083 , dhtLinks = Map.fromList
1084 [ {- TODO -}
1085 ]
1086 , dhtSecretKey = return Nothing 1082 , dhtSecretKey = return Nothing
1087 , dhtBootstrap = case wantip of 1083 , dhtBootstrap = case wantip of
1088 Want_IP4 -> btBootstrap4 1084 Want_IP4 -> btBootstrap4
@@ -1262,9 +1258,6 @@ main = do
1262 1258
1263 , announceInterval = 8 1259 , announceInterval = 8
1264 })] 1260 })]
1265 , dhtLinks = Map.fromList
1266 [ {- TODO -}
1267 ]
1268 , dhtSecretKey = return $ Just $ transportSecret (Tox.toxCryptoKeys tox) 1261 , dhtSecretKey = return $ Just $ transportSecret (Tox.toxCryptoKeys tox)
1269 , dhtBootstrap = case wantip of 1262 , dhtBootstrap = case wantip of
1270 Want_IP4 -> toxStrap4 1263 Want_IP4 -> toxStrap4
@@ -1283,8 +1276,11 @@ main = do
1283 1276
1284 let dhts = Map.union btdhts toxdhts 1277 let dhts = Map.union btdhts toxdhts
1285 1278
1286 waitForSignal <- do 1279 (waitForSignal, checkQuit) <- do
1287 signalQuit <- newEmptyMVar 1280 signalQuit <- atomically $ newTVar False
1281 let quitCommand = atomically $ writeTVar signalQuit True
1282 installHandler sigTERM (CatchOnce (atomically $ writeTVar signalQuit True)) Nothing
1283 installHandler sigINT (CatchOnce (atomically $ writeTVar signalQuit True)) Nothing
1288 let defaultToxData = do 1284 let defaultToxData = do
1289 toxids <- atomically $ newTVar [] 1285 toxids <- atomically $ newTVar []
1290 rster <- newRoster 1286 rster <- newRoster
@@ -1296,20 +1292,22 @@ main = do
1296 let session = clientSession0 $ Session 1292 let session = clientSession0 $ Session
1297 { netname = concat $ take 1 $ Map.keys dhts -- initial default DHT 1293 { netname = concat $ take 1 $ Map.keys dhts -- initial default DHT
1298 , dhts = dhts -- all DHTs 1294 , dhts = dhts -- all DHTs
1299 , signalQuit = signalQuit 1295 , signalQuit = quitCommand
1300 , swarms = swarms 1296 , swarms = swarms
1301 , cryptosessions = netCryptoSessionsState 1297 , cryptosessions = netCryptoSessionsState
1302 , toxkeys = keysdb 1298 , toxkeys = keysdb
1303 , userkeys = toxids 1299 , userkeys = toxids
1304 , roster = rstr 1300 , roster = rstr
1301 , connectionManager = ConnectionManager conns
1305 , onionRouter = orouter 1302 , onionRouter = orouter
1306 , externalAddresses = liftM2 (++) btips toxips 1303 , externalAddresses = liftM2 (++) btips toxips
1307 , announcer = announcer 1304 , announcer = announcer
1308 } 1305 }
1309 srv <- streamServer (withSession session) (SockAddrUnix "dht.sock") 1306 srv <- streamServer (withSession session) (SockAddrUnix "dht.sock")
1310 return $ do 1307 return ( do atomically $ readTVar signalQuit >>= check
1311 () <- takeMVar signalQuit 1308 quitListening srv
1312 quitListening srv 1309 , readTVar signalQuit >>= check
1310 )
1313 1311
1314 1312
1315 forM_ (Map.toList dhts) 1313 forM_ (Map.toList dhts)
@@ -1331,6 +1329,25 @@ main = do
1331 bootstrap btSaved fallbackNodes 1329 bootstrap btSaved fallbackNodes
1332 return () 1330 return ()
1333 1331
1332 atomically $ do
1333 putTMVar serverVar (sv,conns) -- Okay, now it's ready. :)
1334 -- FIXME: This is error prone.
1335
1336 forkIO $ do
1337 myThreadId >>= flip labelThread "XMPP.stanzas"
1338 let console = cwPresenceChan <$> consoleWriter state
1339 fix $ \loop -> do
1340 what <- atomically
1341 $ orElse (do (client,stanza) <- maybe retry takeTMVar console
1342 return $ do informClientPresence0 state Nothing client stanza
1343 loop)
1344 (checkQuit >> return (return ()))
1345 what
1346
1347 hPutStrLn stderr "Started XMPP server."
1348
1349 -- Wait for DHT and XMPP threads to finish.
1350 -- Use ResourceT to clean-up XMPP server.
1334 waitForSignal 1351 waitForSignal
1335 1352
1336 stopAnnouncer announcer 1353 stopAnnouncer announcer