diff options
Diffstat (limited to 'examples/dhtd.hs')
-rw-r--r-- | examples/dhtd.hs | 165 |
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 | |||
21 | import Control.Arrow | 21 | import Control.Arrow |
22 | import Control.Applicative | 22 | import Control.Applicative |
23 | import Control.Concurrent.STM | 23 | import Control.Concurrent.STM |
24 | import Control.DeepSeq | ||
25 | import Control.Exception | 24 | import Control.Exception |
26 | import Control.Monad | 25 | import Control.Monad |
26 | import Control.Monad.Trans.Resource (runResourceT) | ||
27 | import Data.Bool | 27 | import Data.Bool |
28 | import Data.Char | 28 | import Data.Char |
29 | import Data.Function | ||
29 | import Data.Hashable | 30 | import Data.Hashable |
30 | import Data.List | 31 | import Data.List |
31 | import qualified Data.IntMap.Strict as IntMap | 32 | import qualified Data.IntMap.Strict as IntMap |
@@ -53,6 +54,8 @@ import qualified Data.HashMap.Strict as HashMap | |||
53 | import qualified Data.Vector as V | 54 | import qualified Data.Vector as V |
54 | import qualified Data.Text as T | 55 | import qualified Data.Text as T |
55 | import qualified Data.Text.Encoding as T | 56 | import qualified Data.Text.Encoding as T |
57 | import System.Posix.Signals | ||
58 | |||
56 | 59 | ||
57 | import Announcer | 60 | import Announcer |
58 | import Crypto.Tox -- (zeros32,SecretKey,PublicKey, generateSecretKey, toPublic, encodeSecret, decodeSecret, userKeys) | 61 | import Crypto.Tox -- (zeros32,SecretKey,PublicKey, generateSecretKey, toPublic, encodeSecret, decodeSecret, userKeys) |
@@ -60,8 +63,6 @@ import Network.UPNP as UPNP | |||
60 | import Network.Address hiding (NodeId, NodeInfo(..)) | 63 | import Network.Address hiding (NodeId, NodeInfo(..)) |
61 | import Network.QueryResponse | 64 | import Network.QueryResponse |
62 | import Network.StreamServer | 65 | import Network.StreamServer |
63 | import Network.Kademlia | ||
64 | import Network.Kademlia.Bootstrap | ||
65 | import Network.Kademlia.Search | 66 | import Network.Kademlia.Search |
66 | import qualified Network.BitTorrent.MainlineDHT as Mainline | 67 | import qualified Network.BitTorrent.MainlineDHT as Mainline |
67 | import qualified Network.Tox as Tox | 68 | import qualified Network.Tox as Tox |
@@ -71,6 +72,7 @@ import qualified Data.Aeson as J | |||
71 | import qualified Data.ByteString.Lazy as L | 72 | import qualified Data.ByteString.Lazy as L |
72 | import qualified Data.ByteString.Char8 as B | 73 | import qualified Data.ByteString.Char8 as B |
73 | import Control.Concurrent.Tasks | 74 | import Control.Concurrent.Tasks |
75 | import Control.Monad.Trans.Control | ||
74 | import System.IO.Error | 76 | import System.IO.Error |
75 | import qualified Data.Serialize as S | 77 | import qualified Data.Serialize as S |
76 | import Network.BitTorrent.DHT.ContactInfo as Peers | 78 | import Network.BitTorrent.DHT.ContactInfo as Peers |
@@ -88,6 +90,13 @@ import Data.Typeable | |||
88 | import Roster | 90 | import Roster |
89 | import OnionRouter | 91 | import OnionRouter |
90 | 92 | ||
93 | -- Presence imports. | ||
94 | import ConsoleWriter | ||
95 | import Presence | ||
96 | import XMPPServer | ||
97 | import Connection | ||
98 | |||
99 | |||
91 | showReport :: [(String,String)] -> String | 100 | showReport :: [(String,String)] -> String |
92 | showReport kvs = showColumns $ map (\(x,y)->[x,y]) kvs | 101 | showReport 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 | ||
149 | data 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 | |||
164 | data DHTSearch nid ni = forall addr tok r. DHTSearch | 158 | data 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 | ||
371 | data ConnectionManager = forall status k. ConnectionManager { typedManager :: Connection.Manager status k } | ||
372 | |||
378 | data Session = Session | 373 | data 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 | ||
392 | exceptionsToClient :: ClientHandle -> IO () -> IO () | 388 | exceptionsToClient :: 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 | ||
959 | data Options = Options | 937 | data 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 | ||
968 | sensibleDefaults :: Options | 954 | sensibleDefaults :: Options |
969 | sensibleDefaults = Options | 955 | sensibleDefaults = 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 |
979 | parseArgs :: [String] -> Options -> Options | 966 | parseArgs :: [String] -> Options -> Options |
980 | parseArgs [] opts = opts | 967 | parseArgs [] opts = opts |
981 | parseArgs ("--dhtkey":k:args) opts = parseArgs args opts | 968 | parseArgs ("--dhtkey":k:args) opts = parseArgs args opts |
982 | { dhtkey = decodeSecret $ B.pack k } | 969 | { dhtkey = decodeSecret $ B.pack k } |
983 | parseArgs ("-4":args) opts = parseArgs args opts | 970 | parseArgs ("-4":args) opts = parseArgs args opts |
@@ -996,7 +983,7 @@ noArgPing f [] x = f x | |||
996 | noArgPing _ _ _ = return Nothing | 983 | noArgPing _ _ _ = return Nothing |
997 | 984 | ||
998 | main :: IO () | 985 | main :: IO () |
999 | main = do | 986 | main = 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 |