diff options
author | joe <joe@jerkface.net> | 2017-09-14 20:29:47 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2017-09-14 20:29:47 -0400 |
commit | f9ca5de790ea7d430b70471f476ad7b1823b8c0a (patch) | |
tree | 49a0b2143755e917a0b801bdeefce88716d0e93c /examples | |
parent | 7e44a19fae9bc9f90c38641cbc5cf8af9c540ecb (diff) |
Switched to the 3-transports (DHT,Onion,Crypto) Tox design.
Diffstat (limited to 'examples')
-rw-r--r-- | examples/dhtd.hs | 311 |
1 files changed, 205 insertions, 106 deletions
diff --git a/examples/dhtd.hs b/examples/dhtd.hs index 232abd6e..f651ba1b 100644 --- a/examples/dhtd.hs +++ b/examples/dhtd.hs | |||
@@ -14,6 +14,7 @@ | |||
14 | {-# LANGUAGE ScopedTypeVariables #-} | 14 | {-# LANGUAGE ScopedTypeVariables #-} |
15 | {-# LANGUAGE TupleSections #-} | 15 | {-# LANGUAGE TupleSections #-} |
16 | {-# LANGUAGE TypeFamilies #-} | 16 | {-# LANGUAGE TypeFamilies #-} |
17 | {-# LANGUAGE TypeOperators #-} | ||
17 | 18 | ||
18 | import Control.Arrow | 19 | import Control.Arrow |
19 | import Control.Concurrent.STM | 20 | import Control.Concurrent.STM |
@@ -64,6 +65,10 @@ import Data.Wrapper.PSQ as PSQ (pattern (:->)) | |||
64 | import qualified Data.Wrapper.PSQ as PSQ | 65 | import qualified Data.Wrapper.PSQ as PSQ |
65 | import Data.Ord | 66 | import Data.Ord |
66 | import Data.Time.Clock.POSIX | 67 | import Data.Time.Clock.POSIX |
68 | import qualified DHTTransport as Tox | ||
69 | import qualified DHTHandlers as Tox | ||
70 | import qualified OnionHandlers as Tox | ||
71 | import Data.Typeable | ||
67 | 72 | ||
68 | showReport :: [(String,String)] -> String | 73 | showReport :: [(String,String)] -> String |
69 | showReport kvs = showColumns $ map (\(x,y)->[x,y]) kvs | 74 | showReport kvs = showColumns $ map (\(x,y)->[x,y]) kvs |
@@ -88,7 +93,10 @@ hPutClient h s = hPutStr h ('.' : marshalForClient s) | |||
88 | hPutClientChunk :: Handle -> String -> IO () | 93 | hPutClientChunk :: Handle -> String -> IO () |
89 | hPutClientChunk h s = hPutStr h (' ' : marshalForClient s) | 94 | hPutClientChunk h s = hPutStr h (' ' : marshalForClient s) |
90 | 95 | ||
91 | data DHTQuery nid ni = forall addr r tok. Ord addr => DHTQuery | 96 | data DHTQuery nid ni = forall addr r tok. |
97 | ( Ord addr | ||
98 | , Typeable r | ||
99 | )=> DHTQuery | ||
92 | { qsearch :: Search nid addr tok ni r | 100 | { qsearch :: Search nid addr tok ni r |
93 | , qhandler :: ni -> nid -> IO ([ni], [r], tok) | 101 | , qhandler :: ni -> nid -> IO ([ni], [r], tok) |
94 | , qshowR :: r -> String | 102 | , qshowR :: r -> String |
@@ -111,13 +119,16 @@ data DHT = forall nid ni. ( Show ni | |||
111 | , Show nid | 119 | , Show nid |
112 | , Ord nid | 120 | , Ord nid |
113 | , Hashable nid | 121 | , Hashable nid |
122 | , Typeable ni | ||
123 | , S.Serialize nid | ||
114 | ) => | 124 | ) => |
115 | DHT | 125 | DHT |
116 | { dhtBuckets :: TVar (BucketList ni) | 126 | { dhtBuckets :: TVar (BucketList ni) |
117 | , dhtPing :: ni -> IO Bool | 127 | , dhtPing :: ni -> IO Bool |
118 | , dhtQuery :: Map.Map String (DHTQuery nid ni) | 128 | , dhtQuery :: Map.Map String (DHTQuery nid ni) |
119 | , dhtParseId :: String -> Either String nid | 129 | , dhtParseId :: String -> Either String nid |
120 | , dhtSearches :: TVar (Map.Map (String,nid) (DHTSearch nid ni)) | 130 | , dhtSearches :: TVar (Map.Map (String,nid) (DHTSearch nid ni)) |
131 | , dhtFallbackNodes :: IO [ni] | ||
121 | } | 132 | } |
122 | 133 | ||
123 | nodesFileName :: String -> String | 134 | nodesFileName :: String -> String |
@@ -459,140 +470,228 @@ clientSession s@Session{..} sock cnum h = do | |||
459 | _ -> cmd0 $ hPutClient h "error." | 470 | _ -> cmd0 $ hPutClient h "error." |
460 | 471 | ||
461 | 472 | ||
462 | readExternals :: [TVar (BucketList Mainline.NodeInfo)] -> IO [SockAddr] | 473 | readExternals :: (ni -> SockAddr) -> [TVar (BucketList ni)] -> IO [SockAddr] |
463 | readExternals vars = do | 474 | readExternals nodeAddr vars = do |
464 | as <- atomically $ mapM (fmap (Mainline.nodeAddr . selfNode) . readTVar) vars | 475 | as <- atomically $ mapM (fmap (nodeAddr . selfNode) . readTVar) vars |
465 | let unspecified (SockAddrInet _ 0) = True | 476 | let unspecified (SockAddrInet _ 0) = True |
466 | unspecified (SockAddrInet6 _ _ (0,0,0,0) _) = True | 477 | unspecified (SockAddrInet6 _ _ (0,0,0,0) _) = True |
467 | unspecified _ = False | 478 | unspecified _ = False |
468 | -- TODO: Filter to only global addresses? | 479 | -- TODO: Filter to only global addresses? |
469 | return $ filter (not . unspecified) as | 480 | return $ filter (not . unspecified) as |
470 | 481 | ||
471 | defaultPort :: String | 482 | data Options = Options |
472 | defaultPort = "6881" | 483 | { portbt :: String |
484 | , porttox :: String | ||
485 | , ip6bt :: Bool | ||
486 | , ip6tox :: Bool | ||
487 | } | ||
488 | deriving (Eq,Show) | ||
489 | |||
490 | sensibleDefaults :: Options | ||
491 | sensibleDefaults = Options | ||
492 | { portbt = "6881" | ||
493 | , porttox = "33445" | ||
494 | , ip6bt = True | ||
495 | , ip6tox = True | ||
496 | } | ||
497 | |||
498 | -- bt=<port>,tox=<port> | ||
499 | -- -4 | ||
500 | parseArgs :: [String] -> Options -> Options | ||
501 | parseArgs [] opts = opts | ||
502 | parseArgs ("-4":args) opts = parseArgs args opts | ||
503 | { ip6bt = False | ||
504 | , ip6tox = False } | ||
505 | parseArgs (arg:args) opts = parseArgs args opts | ||
506 | { portbt = fromMaybe (portbt opts) $ Prelude.lookup "bt" ports | ||
507 | , porttox = fromMaybe (porttox opts) $ Prelude.lookup "tox" ports } | ||
508 | where | ||
509 | ports = map ( (dropWhile (==',') *** dropWhile (=='=')) | ||
510 | . break (=='=') ) | ||
511 | $ groupBy (const (/= ',')) arg | ||
512 | |||
473 | 513 | ||
474 | main :: IO () | 514 | main :: IO () |
475 | main = do | 515 | main = do |
476 | args <- getArgs | 516 | args <- getArgs |
477 | p <- case take 2 (dropWhile (/="-p") args) of | 517 | let opts = parseArgs args sensibleDefaults |
478 | ["-p",port] | not ("-" `isPrefixOf` port) -> return port | 518 | print opts |
479 | ("-p":_) -> error "Port not specified! (-p PORT)" | ||
480 | _ -> return defaultPort | ||
481 | addr <- getBindAddress p True{- ipv6 -} | ||
482 | |||
483 | (bt,btR,swarms) <- Mainline.newClient addr | ||
484 | 519 | ||
520 | swarms <- Mainline.newSwarmsDatabase | ||
485 | -- Restore peer database before forking the listener thread. | 521 | -- Restore peer database before forking the listener thread. |
486 | peerdb <- left show <$> tryIOError (L.readFile "bt-peers.dat") | 522 | peerdb <- left show <$> tryIOError (L.readFile "bt-peers.dat") |
487 | either (hPutStrLn stderr . ("bt-peers.dat: "++)) | 523 | either (hPutStrLn stderr . ("bt-peers.dat: "++)) |
488 | (atomically . writeTVar (Mainline.contactInfo swarms)) | 524 | (atomically . writeTVar (Mainline.contactInfo swarms)) |
489 | (peerdb >>= S.decodeLazy) | 525 | (peerdb >>= S.decodeLazy) |
490 | 526 | ||
491 | quitBt <- forkListener (clientNet bt) | 527 | (quitBt,btdhts,btips) <- case portbt opts of |
492 | 528 | "" -> return (return (), Map.empty,return []) | |
493 | let toxport = succ $ fromMaybe 33445 (fromIntegral <$> sockAddrPort addr) | 529 | p -> do |
494 | addrTox <- getBindAddress (show toxport) True | 530 | addr <- getBindAddress p (ip6bt opts) |
495 | (tox,toxR,toxkeys) <- Tox.newClient addrTox | 531 | (bt,btR) <- Mainline.newClient swarms addr |
496 | 532 | quitBt <- forkListener "bt" (clientNet bt) | |
497 | quitTox <- forkListener (clientNet tox) | 533 | mainlineSearches <- atomically $ newTVar Map.empty |
498 | 534 | let mainlineDHT bkts wantip = DHT | |
499 | mainlineSearches <- atomically $ newTVar Map.empty | 535 | { dhtBuckets = bkts btR |
500 | toxSearches <- atomically $ newTVar Map.empty | 536 | , dhtPing = Mainline.ping bt |
501 | 537 | , dhtQuery = Map.fromList | |
502 | let mainlineDHT bkts = DHT | 538 | [ ("node", DHTQuery (Mainline.nodeSearch bt) |
503 | { dhtBuckets = bkts btR | 539 | (\ni -> fmap Mainline.unwrapNodes |
504 | , dhtPing = Mainline.ping bt | 540 | . Mainline.findNodeH btR ni |
505 | , dhtQuery = Map.fromList | 541 | . flip Mainline.FindNode (Just Want_Both)) |
506 | [ ("node", DHTQuery (Mainline.nodeSearch bt) | 542 | show |
507 | (\ni -> fmap Mainline.unwrapNodes | 543 | (const Nothing)) |
508 | . Mainline.findNodeH btR ni | 544 | , ("peer", DHTQuery (Mainline.peerSearch bt) |
509 | . flip Mainline.FindNode (Just Want_Both)) | 545 | (\ni -> fmap Mainline.unwrapPeers |
510 | show | 546 | . Mainline.getPeersH btR swarms ni |
511 | (const Nothing)) | 547 | . flip Mainline.GetPeers (Just Want_Both) |
512 | , ("peer", DHTQuery (Mainline.peerSearch bt) | 548 | . (read . show)) -- TODO: InfoHash -> NodeId |
513 | (\ni -> fmap Mainline.unwrapPeers | 549 | (show . pPrint) |
514 | . Mainline.getPeersH btR swarms ni | 550 | (Just . show)) |
515 | . flip Mainline.GetPeers (Just Want_Both) | 551 | ] |
516 | . (read . show)) -- TODO: InfoHash -> NodeId | 552 | , dhtParseId = readEither :: String -> Either String Mainline.NodeId |
517 | (show . pPrint) | 553 | , dhtSearches = mainlineSearches |
518 | (Just . show)) | 554 | , dhtFallbackNodes = Mainline.bootstrapNodes wantip |
519 | ] | 555 | } |
520 | , dhtParseId = readEither :: String -> Either String Mainline.NodeId | 556 | dhts = Map.fromList $ |
521 | , dhtSearches = mainlineSearches | 557 | ("bt4", mainlineDHT Mainline.routing4 Want_IP4) |
522 | } | 558 | : if ip6bt opts |
523 | toxDHT bkts = DHT | 559 | then [ ("bt6", mainlineDHT Mainline.routing6 Want_IP6) ] |
524 | { dhtBuckets = bkts toxR | 560 | else [] |
525 | , dhtPing = Tox.ping tox | 561 | ips :: IO [SockAddr] |
526 | , dhtQuery = Map.fromList | 562 | ips = readExternals Mainline.nodeAddr |
527 | [ ("node", DHTQuery (Tox.nodeSearch tox) | 563 | [ Mainline.routing4 btR |
528 | (\ni -> fmap Tox.unwrapNodes | 564 | , Mainline.routing6 btR |
529 | . Tox.getNodesH toxR ni | 565 | ] |
530 | . Tox.GetNodes) | 566 | return (quitBt,dhts,ips) |
531 | show | 567 | |
532 | (const Nothing)) | 568 | keysdb <- Tox.newKeysDatabase |
533 | ] | 569 | |
534 | , dhtParseId = readEither :: String -> Either String Tox.NodeId | 570 | (quitTox,toxdhts,toxips) <- case porttox opts of |
535 | , dhtSearches = toxSearches | 571 | "" -> return (return (), Map.empty, return []) |
536 | } | 572 | toxport -> do |
537 | dhts = Map.fromList | 573 | addrTox <- getBindAddress toxport (ip6tox opts) |
538 | [ ("bt4", mainlineDHT Mainline.routing4) | 574 | tox <- Tox.newTox keysdb addrTox |
539 | , ("bt6", mainlineDHT Mainline.routing6) | 575 | quitTox <- Tox.forkTox tox |
540 | , ("tox4", toxDHT Tox.routing4) | 576 | |
541 | , ("tox6", toxDHT Tox.routing6) | 577 | toxSearches <- atomically $ newTVar Map.empty |
542 | ] | 578 | |
579 | let toxDHT bkts = DHT | ||
580 | { dhtBuckets = bkts (Tox.toxRouting tox) | ||
581 | , dhtPing = Tox.ping (Tox.toxDHT tox) | ||
582 | , dhtQuery = Map.fromList | ||
583 | [ ("node", DHTQuery (Tox.nodeSearch $ Tox.toxDHT tox) | ||
584 | (\ni -> fmap Tox.unwrapNodes | ||
585 | . Tox.getNodesH (Tox.toxRouting tox) ni | ||
586 | . Tox.GetNodes) | ||
587 | show | ||
588 | (const Nothing)) | ||
589 | ] | ||
590 | , dhtParseId = readEither :: String -> Either String Tox.NodeId | ||
591 | , dhtSearches = toxSearches | ||
592 | , dhtFallbackNodes = return [] | ||
593 | } | ||
594 | dhts = Map.fromList $ | ||
595 | ("tox4", toxDHT Tox.routing4) | ||
596 | : if ip6tox opts | ||
597 | then [ ("tox6", toxDHT Tox.routing6) ] | ||
598 | else [] | ||
599 | ips :: IO [SockAddr] | ||
600 | ips = readExternals Tox.nodeAddr [ Tox.routing4 $ Tox.toxRouting tox | ||
601 | , Tox.routing6 $ Tox.toxRouting tox ] | ||
602 | return (quitTox, dhts, ips) | ||
603 | |||
604 | let dhts = Map.union btdhts toxdhts | ||
543 | 605 | ||
544 | waitForSignal <- do | 606 | waitForSignal <- do |
545 | signalQuit <- newEmptyMVar | 607 | signalQuit <- newEmptyMVar |
546 | let session = clientSession $ Session | 608 | let session = clientSession $ Session |
547 | { netname = "bt4" -- initial default DHT | 609 | { netname = concat $ take 1 $ Map.keys dhts -- initial default DHT |
548 | , dhts = dhts -- all DHTs | 610 | , dhts = dhts -- all DHTs |
549 | , signalQuit = signalQuit | 611 | , signalQuit = signalQuit |
550 | , swarms = swarms | 612 | , swarms = swarms |
551 | , toxkeys = toxkeys | 613 | , toxkeys = keysdb |
552 | , externalAddresses = readExternals | 614 | , externalAddresses = liftM2 (++) btips toxips |
553 | [ Mainline.routing4 btR | ||
554 | , Mainline.routing6 btR | ||
555 | ] | ||
556 | } | 615 | } |
557 | srv <- streamServer (withSession session) (SockAddrUnix "dht.sock") | 616 | srv <- streamServer (withSession session) (SockAddrUnix "dht.sock") |
558 | return $ do | 617 | return $ do |
559 | () <- takeMVar signalQuit | 618 | () <- takeMVar signalQuit |
560 | quitListening srv | 619 | quitListening srv |
561 | 620 | ||
621 | |||
622 | forM_ (Map.toList dhts) | ||
623 | $ \(netname, dht@DHT { dhtBuckets = bkts | ||
624 | , dhtQuery = qrys | ||
625 | , dhtPing = ping | ||
626 | , dhtFallbackNodes = getBootstrapNodes }) -> do | ||
627 | btSaved <- loadNodes netname -- :: IO [Mainline.NodeInfo] | ||
628 | putStrLn $ "Loaded "++show (length btSaved)++" nodes for "++netname++"." | ||
629 | fallbackNodes <- getBootstrapNodes | ||
630 | let isNodesSearch :: ni :~: r -> Search nid addr tok ni r -> Search nid addr tok ni ni | ||
631 | isNodesSearch Refl sch = sch | ||
632 | fork $ do | ||
633 | myThreadId >>= flip labelThread ("bootstrap."++netname) | ||
634 | case Map.lookup "node" qrys of | ||
635 | Just DHTQuery { qsearch = srch } -> do | ||
636 | case eqT of | ||
637 | Just witness -> bootstrap (isNodesSearch witness srch) bkts ping btSaved fallbackNodes | ||
638 | _ -> error $ "Missing node-search for "++netname++"." | ||
639 | saveNodes netname dht | ||
640 | Nothing -> return () | ||
641 | return () | ||
642 | |||
643 | {- | ||
562 | let bkts4 = Mainline.routing4 btR | 644 | let bkts4 = Mainline.routing4 btR |
563 | btSaved4 <- loadNodes "bt4" :: IO [Mainline.NodeInfo] | 645 | (fallbackNodes4,fallbackNodes6) <- case portbt opts of |
564 | putStrLn $ "Loaded "++show (length btSaved4)++" nodes for bt4." | 646 | [] -> return ([],[]) |
565 | fallbackNodes4 <- Mainline.bootstrapNodes Want_IP4 | 647 | _ -> do |
566 | fork $ do | 648 | btSaved4 <- loadNodes "bt4" :: IO [Mainline.NodeInfo] |
567 | myThreadId >>= flip labelThread "bootstrap.Mainline4" | 649 | putStrLn $ "Loaded "++show (length btSaved4)++" nodes for bt4." |
568 | bootstrap (Mainline.nodeSearch bt) bkts4 (Mainline.ping bt) btSaved4 fallbackNodes4 | 650 | fallbackNodes4 <- Mainline.bootstrapNodes Want_IP4 |
569 | saveNodes "bt4" (dhts Map.! "bt4") | 651 | fork $ do |
570 | 652 | myThreadId >>= flip labelThread "bootstrap.Mainline4" | |
571 | btSaved6 <- loadNodes "bt6" | 653 | bootstrap (Mainline.nodeSearch bt) bkts4 (Mainline.ping bt) btSaved4 fallbackNodes4 |
572 | putStrLn $ "Loaded "++show (length btSaved6)++" nodes for bt6." | 654 | saveNodes "bt4" (dhts Map.! "bt4") |
573 | let bkts6 = Mainline.routing6 btR | 655 | |
574 | fallbackNodes6 <- Mainline.bootstrapNodes Want_IP6 | 656 | fallbackNodes6 <- case ip6bt opts of |
575 | fork $ do | 657 | True -> do |
576 | myThreadId >>= flip labelThread "bootstrap.Mainline6" | 658 | btSaved6 <- loadNodes "bt6" |
577 | bootstrap (Mainline.nodeSearch bt) bkts6 (Mainline.ping bt) btSaved6 fallbackNodes6 | 659 | putStrLn $ "Loaded "++show (length btSaved6)++" nodes for bt6." |
578 | saveNodes "bt6" (dhts Map.! "bt6") | 660 | let bkts6 = Mainline.routing6 btR |
579 | 661 | fallbackNodes6 <- Mainline.bootstrapNodes Want_IP6 | |
580 | toxSaved4 <- loadNodes "tox4" | 662 | fork $ do |
581 | putStrLn $ "Loaded "++show (length toxSaved4)++" nodes for tox4" | 663 | myThreadId >>= flip labelThread "bootstrap.Mainline6" |
582 | fork $ do | 664 | bootstrap (Mainline.nodeSearch bt) bkts6 (Mainline.ping bt) btSaved6 fallbackNodes6 |
583 | myThreadId >>= flip labelThread "bootstrap.Tox4" | 665 | saveNodes "bt6" (dhts Map.! "bt6") |
584 | bootstrap (Tox.nodeSearch tox) (Tox.routing4 toxR) (Tox.ping tox) toxSaved4 [] | 666 | return fallbackNodes6 |
585 | saveNodes "tox4" (dhts Map.! "tox4") | 667 | False -> return [] |
586 | 668 | return (fallbackNodes4,fallbackNodes6) | |
587 | toxSaved6 <- loadNodes "tox6" | 669 | |
588 | putStrLn $ "Loaded "++show (length toxSaved6)++" nodes for tox6" | 670 | (toxSaved4, toxSaved6) <- case porttox opts of |
589 | fork $ do | 671 | [] -> return ([],[]) |
590 | myThreadId >>= flip labelThread "bootstrap.Tox6" | 672 | _ -> do |
591 | bootstrap (Tox.nodeSearch tox) (Tox.routing6 toxR) (Tox.ping tox) toxSaved6 [] | 673 | toxSaved4 <- loadNodes "tox4" |
592 | saveNodes "tox6" (dhts Map.! "tox6") | 674 | putStrLn $ "Loaded "++show (length toxSaved4)++" nodes for tox4" |
675 | fork $ do | ||
676 | myThreadId >>= flip labelThread "bootstrap.Tox4" | ||
677 | bootstrap (Tox.nodeSearch $ Tox.toxDHT tox) (Tox.routing4 (Tox.toxRouting tox)) (Tox.ping $ Tox.toxDHT tox) toxSaved4 [] | ||
678 | saveNodes "tox4" (dhts Map.! "tox4") | ||
679 | |||
680 | toxSaved6 <- case ip6tox opts of | ||
681 | True -> do | ||
682 | toxSaved6 <- loadNodes "tox6" | ||
683 | putStrLn $ "Loaded "++show (length toxSaved6)++" nodes for tox6" | ||
684 | fork $ do | ||
685 | myThreadId >>= flip labelThread "bootstrap.Tox6" | ||
686 | bootstrap (Tox.nodeSearch $ Tox.toxDHT tox) (Tox.routing6 (Tox.toxRouting tox)) (Tox.ping $ Tox.toxDHT tox) toxSaved6 [] | ||
687 | saveNodes "tox6" (dhts Map.! "tox6") | ||
688 | return toxSaved6 | ||
689 | False -> return [] | ||
690 | return (toxSaved4,toxSaved6) | ||
593 | 691 | ||
594 | hPutStr stderr $ showReport $ map (("bootstrap (IPv4)",) . show) fallbackNodes4 | 692 | hPutStr stderr $ showReport $ map (("bootstrap (IPv4)",) . show) fallbackNodes4 |
595 | ++ map (("bootstrap (IPv6)",) . show) fallbackNodes6 | 693 | ++ map (("bootstrap (IPv6)",) . show) fallbackNodes6 |
694 | -} | ||
596 | 695 | ||
597 | waitForSignal | 696 | waitForSignal |
598 | 697 | ||