diff options
author | joe <joe@jerkface.net> | 2017-06-08 00:00:56 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2017-06-08 00:00:56 -0400 |
commit | d6fac9a8df0ce872ede54d6a71ca6d6c750eadc9 (patch) | |
tree | c4a7cd804714796bc918091ebb29f4ad4009a401 /src | |
parent | 05345c643d0bcebe17f9474d9561da6e90fff34e (diff) |
WIP: Adapting DHT to Tox network (part 5).
Diffstat (limited to 'src')
-rw-r--r-- | src/Data/Tox.hs | 28 | ||||
-rw-r--r-- | src/Network/BitTorrent/Address.hs | 128 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT.hs | 7 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Message.hs | 20 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Query.hs | 63 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Routing.hs | 36 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Search.hs | 29 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Session.hs | 2 | ||||
-rw-r--r-- | src/Network/DHT/Mainline.hs | 72 | ||||
-rw-r--r-- | src/Network/KRPC/Manager.hs | 44 | ||||
-rw-r--r-- | src/Network/RPC.hs | 24 |
11 files changed, 262 insertions, 191 deletions
diff --git a/src/Data/Tox.hs b/src/Data/Tox.hs index 4449ce65..888ca3b6 100644 --- a/src/Data/Tox.hs +++ b/src/Data/Tox.hs | |||
@@ -1,11 +1,13 @@ | |||
1 | {-# LANGUAGE DeriveDataTypeable #-} | 1 | {-# LANGUAGE DeriveDataTypeable #-} |
2 | {-# LANGUAGE DeriveGeneric #-} | 2 | {-# LANGUAGE DeriveFunctor #-} |
3 | {-# LANGUAGE DeriveTraversable #-} | 3 | {-# LANGUAGE DeriveGeneric #-} |
4 | {-# LANGUAGE DeriveFunctor #-} | 4 | {-# LANGUAGE DeriveTraversable #-} |
5 | {-# LANGUAGE PatternSynonyms #-} | 5 | {-# LANGUAGE MultiParamTypeClasses #-} |
6 | {-# LANGUAGE RecordWildCards #-} | 6 | {-# LANGUAGE PatternSynonyms #-} |
7 | {-# LANGUAGE TupleSections #-} | 7 | {-# LANGUAGE RecordWildCards #-} |
8 | {-# LANGUAGE UnboxedTuples #-} | 8 | {-# LANGUAGE TupleSections #-} |
9 | {-# LANGUAGE TypeFamilies #-} | ||
10 | {-# LANGUAGE UnboxedTuples #-} | ||
9 | module Data.Tox where | 11 | module Data.Tox where |
10 | 12 | ||
11 | import Data.ByteString (ByteString) | 13 | import Data.ByteString (ByteString) |
@@ -14,7 +16,7 @@ import Data.Word | |||
14 | import Data.LargeWord | 16 | import Data.LargeWord |
15 | import Data.IP | 17 | import Data.IP |
16 | import Data.Serialize | 18 | import Data.Serialize |
17 | import Network.BitTorrent.Address () -- Serialize IP | 19 | import Network.BitTorrent.Address (NodeInfo(..)) -- Serialize IP |
18 | import GHC.Generics (Generic) | 20 | import GHC.Generics (Generic) |
19 | import Network.Socket | 21 | import Network.Socket |
20 | import Network.RPC hiding (NodeId) | 22 | import Network.RPC hiding (NodeId) |
@@ -27,7 +29,7 @@ type Nonce24 = Word192 -- 24 bytes | |||
27 | type NodeId = Word256 -- 32 bytes (mainline uses only 20-byte node IDs) | 29 | type NodeId = Word256 -- 32 bytes (mainline uses only 20-byte node IDs) |
28 | 30 | ||
29 | 31 | ||
30 | data NodeFormat = NodeFormat | 32 | data NodeFormat = NodeFormat |
31 | { nodePublicKey :: Key32 -- 32 byte public key | 33 | { nodePublicKey :: Key32 -- 32 byte public key |
32 | , nodeIsTCP :: Bool -- This has no analog in mainline NodeInfo structure | 34 | , nodeIsTCP :: Bool -- This has no analog in mainline NodeInfo structure |
33 | , nodeIP :: IP -- IPv4 or IPv6 address | 35 | , nodeIP :: IP -- IPv4 or IPv6 address |
@@ -133,11 +135,11 @@ instance Serialize NodeFormat where | |||
133 | typ <- get :: Get Word8 | 135 | typ <- get :: Get Word8 |
134 | (ip,istcp) <- | 136 | (ip,istcp) <- |
135 | case typ :: Word8 of | 137 | case typ :: Word8 of |
136 | 2 -> (,False) . IPv4 <$> get | 138 | 2 -> (,False) . IPv4 <$> get |
137 | 130 -> (,True) . IPv4 <$> get | 139 | 130 -> (,True) . IPv4 <$> get |
138 | 10 -> (,False) . IPv6 <$> get | 140 | 10 -> (,False) . IPv6 <$> get |
139 | 138 -> (,True) . IPv6 <$> get | 141 | 138 -> (,True) . IPv6 <$> get |
140 | _ -> fail "Unsupported type of Tox node_format structure" | 142 | _ -> fail "Unsupported type of Tox node_format structure" |
141 | port <- get | 143 | port <- get |
142 | pubkey <- get | 144 | pubkey <- get |
143 | return $ NodeFormat { nodeIsTCP = istcp | 145 | return $ NodeFormat { nodeIsTCP = istcp |
diff --git a/src/Network/BitTorrent/Address.hs b/src/Network/BitTorrent/Address.hs index 2132f8f9..560ac1ef 100644 --- a/src/Network/BitTorrent/Address.hs +++ b/src/Network/BitTorrent/Address.hs | |||
@@ -11,6 +11,7 @@ | |||
11 | -- | 11 | -- |
12 | {-# LANGUAGE CPP #-} | 12 | {-# LANGUAGE CPP #-} |
13 | {-# LANGUAGE FlexibleInstances #-} | 13 | {-# LANGUAGE FlexibleInstances #-} |
14 | {-# LANGUAGE FlexibleContexts #-} | ||
14 | {-# LANGUAGE RecordWildCards #-} | 15 | {-# LANGUAGE RecordWildCards #-} |
15 | {-# LANGUAGE StandaloneDeriving #-} | 16 | {-# LANGUAGE StandaloneDeriving #-} |
16 | {-# LANGUAGE ViewPatterns #-} | 17 | {-# LANGUAGE ViewPatterns #-} |
@@ -59,11 +60,8 @@ module Network.BitTorrent.Address | |||
59 | -- * Node | 60 | -- * Node |
60 | -- ** Id | 61 | -- ** Id |
61 | , NodeId | 62 | , NodeId |
62 | , asNodeId | ||
63 | , nodeIdSize | 63 | , nodeIdSize |
64 | , testIdBit | 64 | , testIdBit |
65 | , NodeDistance | ||
66 | , distance | ||
67 | , genNodeId | 65 | , genNodeId |
68 | , bucketRange | 66 | , bucketRange |
69 | , genBucketSample | 67 | , genBucketSample |
@@ -73,6 +71,8 @@ module Network.BitTorrent.Address | |||
73 | -- ** Info | 71 | -- ** Info |
74 | , NodeAddr (..) | 72 | , NodeAddr (..) |
75 | , NodeInfo (..) | 73 | , NodeInfo (..) |
74 | , mapAddress | ||
75 | , traverseAddress | ||
76 | , rank | 76 | , rank |
77 | 77 | ||
78 | -- * Fingerprint | 78 | -- * Fingerprint |
@@ -98,7 +98,6 @@ import Data.BEncode.BDict (BKey) | |||
98 | import Data.Bits | 98 | import Data.Bits |
99 | import qualified Data.ByteString as BS | 99 | import qualified Data.ByteString as BS |
100 | import qualified Data.ByteString.Internal as BS | 100 | import qualified Data.ByteString.Internal as BS |
101 | import Data.ByteString.Base16 as Base16 | ||
102 | import Data.ByteString.Char8 as BC | 101 | import Data.ByteString.Char8 as BC |
103 | import Data.ByteString.Char8 as BS8 | 102 | import Data.ByteString.Char8 as BS8 |
104 | import qualified Data.ByteString.Lazy as BL | 103 | import qualified Data.ByteString.Lazy as BL |
@@ -130,6 +129,9 @@ import System.Locale (defaultTimeLocale) | |||
130 | #endif | 129 | #endif |
131 | import System.Entropy | 130 | import System.Entropy |
132 | import Data.Digest.CRC32C | 131 | import Data.Digest.CRC32C |
132 | import qualified Network.RPC as RPC | ||
133 | import Network.KRPC.Message (KMessageOf) | ||
134 | import Network.DHT.Mainline | ||
133 | 135 | ||
134 | -- import Paths_bittorrent (version) | 136 | -- import Paths_bittorrent (version) |
135 | 137 | ||
@@ -646,48 +648,10 @@ peerSocket socketType pa = do | |||
646 | -- in the DHT to get the location of peers to download from using | 648 | -- in the DHT to get the location of peers to download from using |
647 | -- the BitTorrent protocol. | 649 | -- the BitTorrent protocol. |
648 | 650 | ||
649 | -- TODO more compact representation ('ShortByteString's?) | 651 | -- asNodeId :: ByteString -> NodeId |
652 | -- asNodeId bs = NodeId $ BS.take nodeIdSize bs | ||
650 | 653 | ||
651 | -- | Each node has a globally unique identifier known as the \"node | 654 | {- |
652 | -- ID.\" | ||
653 | -- | ||
654 | -- Normally, /this/ node id should be saved between invocations | ||
655 | -- of the client software. | ||
656 | newtype NodeId = NodeId ByteString | ||
657 | deriving (Show, Eq, Ord, Typeable | ||
658 | #ifdef VERSION_bencoding | ||
659 | , BEncode | ||
660 | #endif | ||
661 | ) | ||
662 | |||
663 | |||
664 | nodeIdSize :: Int | ||
665 | nodeIdSize = 20 | ||
666 | |||
667 | asNodeId :: ByteString -> NodeId | ||
668 | asNodeId bs = NodeId $ BS.take nodeIdSize bs | ||
669 | |||
670 | -- | Meaningless node id, for testing purposes only. | ||
671 | instance Default NodeId where | ||
672 | def = NodeId (BS.replicate nodeIdSize 0) | ||
673 | |||
674 | instance Serialize NodeId where | ||
675 | get = NodeId <$> getByteString nodeIdSize | ||
676 | {-# INLINE get #-} | ||
677 | put (NodeId bs) = putByteString bs | ||
678 | {-# INLINE put #-} | ||
679 | |||
680 | -- | ASCII encoded. | ||
681 | instance IsString NodeId where | ||
682 | fromString str | ||
683 | | L.length str == nodeIdSize = NodeId (fromString str) | ||
684 | | L.length str == 2 * nodeIdSize = NodeId (fst $ Base16.decode $ fromString str) | ||
685 | | otherwise = error "fromString: invalid NodeId length" | ||
686 | {-# INLINE fromString #-} | ||
687 | |||
688 | -- | base16 encoded. | ||
689 | instance Pretty NodeId where | ||
690 | pPrint (NodeId nid) = PP.text $ BC.unpack $ Base16.encode nid | ||
691 | 655 | ||
692 | -- | Test if the nth bit is set. | 656 | -- | Test if the nth bit is set. |
693 | testIdBit :: NodeId -> Word -> Bool | 657 | testIdBit :: NodeId -> Word -> Bool |
@@ -696,6 +660,10 @@ testIdBit (NodeId bs) i | |||
696 | , (q, r) <- quotRem (fromIntegral i) 8 | 660 | , (q, r) <- quotRem (fromIntegral i) 8 |
697 | = testBit (BS.index bs q) (7 - r) | 661 | = testBit (BS.index bs q) (7 - r) |
698 | | otherwise = False | 662 | | otherwise = False |
663 | -} | ||
664 | |||
665 | testIdBit :: FiniteBits bs => bs -> Word -> Bool | ||
666 | testIdBit bs i = testBit bs (fromIntegral (finiteBitSize bs - fromIntegral i)) | ||
699 | {-# INLINE testIdBit #-} | 667 | {-# INLINE testIdBit #-} |
700 | 668 | ||
701 | -- TODO WARN is the 'system' random suitable for this? | 669 | -- TODO WARN is the 'system' random suitable for this? |
@@ -703,25 +671,10 @@ testIdBit (NodeId bs) i | |||
703 | -- Distribution of ID's should be as uniform as possible. | 671 | -- Distribution of ID's should be as uniform as possible. |
704 | -- | 672 | -- |
705 | genNodeId :: IO NodeId | 673 | genNodeId :: IO NodeId |
706 | genNodeId = NodeId <$> getEntropy nodeIdSize | 674 | genNodeId = NodeId . either error id . S.decode <$> getEntropy nodeIdSize |
707 | 675 | ||
708 | ------------------------------------------------------------------------ | 676 | ------------------------------------------------------------------------ |
709 | 677 | ||
710 | -- | In Kademlia, the distance metric is XOR and the result is | ||
711 | -- interpreted as an unsigned integer. | ||
712 | newtype NodeDistance = NodeDistance BS.ByteString | ||
713 | deriving (Eq, Ord) | ||
714 | |||
715 | instance Pretty NodeDistance where | ||
716 | pPrint (NodeDistance bs) = text $ BC.unpack (Base16.encode bs) | ||
717 | |||
718 | instance Show NodeDistance where | ||
719 | show (NodeDistance bs) = BC.unpack (Base16.encode bs) | ||
720 | |||
721 | -- | distance(A,B) = |A xor B| Smaller values are closer. | ||
722 | distance :: NodeId -> NodeId -> NodeDistance | ||
723 | distance (NodeId a) (NodeId b) = NodeDistance (BS.pack (BS.zipWith xor a b)) | ||
724 | |||
725 | -- | Accepts a depth/index of a bucket and whether or not it is the last one, | 678 | -- | Accepts a depth/index of a bucket and whether or not it is the last one, |
726 | -- yields: | 679 | -- yields: |
727 | -- | 680 | -- |
@@ -753,13 +706,13 @@ genBucketSample n qmb = genBucketSample' getEntropy n qmb | |||
753 | genBucketSample' :: Applicative m => | 706 | genBucketSample' :: Applicative m => |
754 | (Int -> m ByteString) -> NodeId -> (Int,Word8,Word8) -> m NodeId | 707 | (Int -> m ByteString) -> NodeId -> (Int,Word8,Word8) -> m NodeId |
755 | genBucketSample' gen (NodeId self) (q,m,b) | 708 | genBucketSample' gen (NodeId self) (q,m,b) |
756 | | q <= 0 = NodeId <$> gen nodeIdSize | 709 | | q <= 0 = NodeId . either error id . S.decode <$> gen nodeIdSize |
757 | | q >= nodeIdSize = pure (NodeId self) | 710 | | q >= nodeIdSize = pure (NodeId self) |
758 | | otherwise = NodeId . build <$> gen (nodeIdSize - q + 1) | 711 | | otherwise = NodeId . either error id . S.decode . build <$> gen (nodeIdSize - q + 1) |
759 | where | 712 | where |
760 | build tl = BS.init hd <> BS.cons (h .|. t) (BS.tail tl) | 713 | build tl = BS.init hd <> BS.cons (h .|. t) (BS.tail tl) |
761 | where | 714 | where |
762 | hd = BS.take q self | 715 | hd = BS.take q $ S.encode self |
763 | h = xor b (complement m .&. BS.last hd) | 716 | h = xor b (complement m .&. BS.last hd) |
764 | t = m .&. BS.head tl | 717 | t = m .&. BS.head tl |
765 | 718 | ||
@@ -819,32 +772,46 @@ fromPeerAddr PeerAddr {..} = NodeAddr | |||
819 | 772 | ||
820 | ------------------------------------------------------------------------ | 773 | ------------------------------------------------------------------------ |
821 | 774 | ||
822 | data NodeInfo a = NodeInfo | 775 | data NodeInfo dht addr u = NodeInfo |
823 | { nodeId :: !NodeId | 776 | { nodeId :: !(RPC.NodeId dht) |
824 | , nodeAddr :: !(NodeAddr a) | 777 | , nodeAddr :: !(NodeAddr addr) |
825 | } deriving (Show, Eq, Functor, Foldable, Traversable) | 778 | , nodeAnnotation :: u |
779 | } deriving (Functor, Foldable, Traversable) | ||
780 | |||
781 | deriving instance ( Show (RPC.NodeId dht) | ||
782 | , Show addr | ||
783 | , Show u ) => Show (NodeInfo dht addr u) | ||
826 | 784 | ||
785 | mapAddress :: (addr -> b) -> NodeInfo dht addr u -> NodeInfo dht b u | ||
786 | mapAddress f ni = ni { nodeAddr = fmap f (nodeAddr ni) } | ||
827 | 787 | ||
828 | instance Eq a => Ord (NodeInfo a) where | 788 | traverseAddress :: Applicative f => (addr -> f b) -> NodeInfo dht addr u -> f (NodeInfo dht b u) |
789 | traverseAddress f ni = fmap (\addr -> ni { nodeAddr = addr }) $ traverse f (nodeAddr ni) | ||
790 | |||
791 | -- Warning: Eq and Ord only look at the nodeId field. | ||
792 | instance Eq (RPC.NodeId dht) => Eq (NodeInfo dht a u) where | ||
793 | a == b = (nodeId a == nodeId b) | ||
794 | |||
795 | instance Ord (RPC.NodeId dht) => Ord (NodeInfo dht a u) where | ||
829 | compare = comparing nodeId | 796 | compare = comparing nodeId |
830 | 797 | ||
831 | -- | KRPC 'compact list' compatible encoding: contact information for | 798 | -- | KRPC 'compact list' compatible encoding: contact information for |
832 | -- nodes is encoded as a 26-byte string. Also known as "Compact node | 799 | -- nodes is encoded as a 26-byte string. Also known as "Compact node |
833 | -- info" the 20-byte Node ID in network byte order has the compact | 800 | -- info" the 20-byte Node ID in network byte order has the compact |
834 | -- IP-address/port info concatenated to the end. | 801 | -- IP-address/port info concatenated to the end. |
835 | instance Serialize a => Serialize (NodeInfo a) where | 802 | instance Serialize a => Serialize (NodeInfo KMessageOf a ()) where |
836 | get = NodeInfo <$> get <*> get | 803 | get = (\a b -> NodeInfo a b ()) <$> get <*> get |
837 | put NodeInfo {..} = put nodeId >> put nodeAddr | 804 | put NodeInfo {..} = put nodeId >> put nodeAddr |
838 | 805 | ||
839 | instance Pretty ip => Pretty (NodeInfo ip) where | 806 | instance Pretty ip => Pretty (NodeInfo KMessageOf ip ()) where |
840 | pPrint NodeInfo {..} = pPrint nodeId <> "@(" <> pPrint nodeAddr <> ")" | 807 | pPrint NodeInfo {..} = pPrint nodeId <> "@(" <> pPrint nodeAddr <> ")" |
841 | 808 | ||
842 | instance Pretty ip => Pretty [NodeInfo ip] where | 809 | instance Pretty ip => Pretty [NodeInfo KMessageOf ip ()] where |
843 | pPrint = PP.vcat . PP.punctuate "," . L.map pPrint | 810 | pPrint = PP.vcat . PP.punctuate "," . L.map pPrint |
844 | 811 | ||
845 | -- | Order by closeness: nearest nodes first. | 812 | -- | Order by closeness: nearest nodes first. |
846 | rank :: (x -> NodeId) -> NodeId -> [x] -> [x] | 813 | rank :: (x -> NodeId) -> NodeId -> [x] -> [x] |
847 | rank f nid = L.sortBy (comparing (distance nid . f)) | 814 | rank f nid = L.sortBy (comparing (RPC.distance nid . f)) |
848 | 815 | ||
849 | {----------------------------------------------------------------------- | 816 | {----------------------------------------------------------------------- |
850 | -- Fingerprint | 817 | -- Fingerprint |
@@ -1259,8 +1226,11 @@ bep42s addr (NodeId r) = mapMaybe (bep42 addr) rs | |||
1259 | where | 1226 | where |
1260 | rs = L.map (NodeId . change3bits r) [0..7] | 1227 | rs = L.map (NodeId . change3bits r) [0..7] |
1261 | 1228 | ||
1262 | change3bits :: ByteString -> Word8 -> ByteString | 1229 | -- change3bits :: ByteString -> Word8 -> ByteString |
1263 | change3bits bs n = BS.snoc (BS.init bs) (BS.last bs .&. 0xF8 .|. n) | 1230 | -- change3bits bs n = BS.snoc (BS.init bs) (BS.last bs .&. 0xF8 .|. n) |
1231 | |||
1232 | change3bits :: (Num b, Bits b) => b -> b -> b | ||
1233 | change3bits bs n = (bs .&. complement 7) .|. n | ||
1264 | 1234 | ||
1265 | -- | Modifies a purely random 'NodeId' to one that is related to a given | 1235 | -- | Modifies a purely random 'NodeId' to one that is related to a given |
1266 | -- routable address in accordance with BEP 42. | 1236 | -- routable address in accordance with BEP 42. |
@@ -1274,9 +1244,9 @@ bep42 addr (NodeId r) | |||
1274 | where | 1244 | where |
1275 | ip4mask = "\x03\x0f\x3f\xff" :: ByteString | 1245 | ip4mask = "\x03\x0f\x3f\xff" :: ByteString |
1276 | ip6mask = "\x01\x03\x07\x0f\x1f\x3f\x7f\xff" :: ByteString | 1246 | ip6mask = "\x01\x03\x07\x0f\x1f\x3f\x7f\xff" :: ByteString |
1277 | nbhood_select = BS.last r .&. 7 | 1247 | nbhood_select = (fromIntegral r :: Word8) .&. 7 |
1278 | retr n = pure $ BS.drop (BS.length r - n) r | 1248 | retr n = pure $ BS.drop (nodeIdSize - n) $ S.encode r |
1279 | crc = S.encode . crc32c . BS.pack | 1249 | crc = flip shiftL (finiteBitSize (NodeId undefined) - 32) . fromIntegral . crc32c . BS.pack |
1280 | applyMask ip = case BS.zipWith (.&.) msk ip of | 1250 | applyMask ip = case BS.zipWith (.&.) msk ip of |
1281 | (b:bs) -> (b .|. shiftL nbhood_select 5) : bs | 1251 | (b:bs) -> (b .|. shiftL nbhood_select 5) : bs |
1282 | bs -> bs | 1252 | bs -> bs |
diff --git a/src/Network/BitTorrent/DHT.hs b/src/Network/BitTorrent/DHT.hs index aaa1cf33..ab948a2d 100644 --- a/src/Network/BitTorrent/DHT.hs +++ b/src/Network/BitTorrent/DHT.hs | |||
@@ -17,6 +17,7 @@ | |||
17 | {-# LANGUAGE FlexibleInstances #-} | 17 | {-# LANGUAGE FlexibleInstances #-} |
18 | {-# LANGUAGE TemplateHaskell #-} | 18 | {-# LANGUAGE TemplateHaskell #-} |
19 | {-# LANGUAGE TypeOperators #-} | 19 | {-# LANGUAGE TypeOperators #-} |
20 | {-# LANGUAGE ScopedTypeVariables #-} | ||
20 | module Network.BitTorrent.DHT | 21 | module Network.BitTorrent.DHT |
21 | ( -- * Distributed Hash Table | 22 | ( -- * Distributed Hash Table |
22 | DHT | 23 | DHT |
@@ -70,6 +71,7 @@ import Network.BitTorrent.DHT.Session | |||
70 | import Network.BitTorrent.DHT.Routing as T hiding (null) | 71 | import Network.BitTorrent.DHT.Routing as T hiding (null) |
71 | import qualified Data.Text as Text | 72 | import qualified Data.Text as Text |
72 | import Data.Monoid | 73 | import Data.Monoid |
74 | import Network.KRPC.Message (KMessageOf) | ||
73 | 75 | ||
74 | 76 | ||
75 | {----------------------------------------------------------------------- | 77 | {----------------------------------------------------------------------- |
@@ -166,7 +168,7 @@ resolveHostName NodeAddr {..} = do | |||
166 | -- | 168 | -- |
167 | -- This operation do block, use | 169 | -- This operation do block, use |
168 | -- 'Control.Concurrent.Async.Lifted.async' if needed. | 170 | -- 'Control.Concurrent.Async.Lifted.async' if needed. |
169 | bootstrap :: Address ip => Maybe BS.ByteString -> [NodeAddr ip] -> DHT ip () | 171 | bootstrap :: forall ip. Address ip => Maybe BS.ByteString -> [NodeAddr ip] -> DHT ip () |
170 | bootstrap mbs startNodes = do | 172 | bootstrap mbs startNodes = do |
171 | restored <- | 173 | restored <- |
172 | case decode <$> mbs of | 174 | case decode <$> mbs of |
@@ -178,7 +180,8 @@ bootstrap mbs startNodes = do | |||
178 | $(logInfoS) "bootstrap" "Start node bootstrapping" | 180 | $(logInfoS) "bootstrap" "Start node bootstrapping" |
179 | let searchAll aliveNodes = do | 181 | let searchAll aliveNodes = do |
180 | nid <- myNodeIdAccordingTo (error "FIXME") | 182 | nid <- myNodeIdAccordingTo (error "FIXME") |
181 | C.sourceList [aliveNodes] $= search nid (findNodeQ nid) $$ C.consume | 183 | nss <- C.sourceList [aliveNodes] $= search nid (findNodeQ nid) $$ C.consume |
184 | return ( nss :: [[NodeInfo KMessageOf ip ()]] ) | ||
182 | input_nodes <- (restored ++) . T.toList <$> getTable | 185 | input_nodes <- (restored ++) . T.toList <$> getTable |
183 | -- Step 1: Use iterative searches to flesh out the table.. | 186 | -- Step 1: Use iterative searches to flesh out the table.. |
184 | do let knowns = map (map $ nodeAddr . fst) input_nodes | 187 | do let knowns = map (map $ nodeAddr . fst) input_nodes |
diff --git a/src/Network/BitTorrent/DHT/Message.hs b/src/Network/BitTorrent/DHT/Message.hs index 0e2bfdd9..c3df683a 100644 --- a/src/Network/BitTorrent/DHT/Message.hs +++ b/src/Network/BitTorrent/DHT/Message.hs | |||
@@ -93,15 +93,14 @@ import Data.Bool | |||
93 | #ifdef VERSION_bencoding | 93 | #ifdef VERSION_bencoding |
94 | import Data.BEncode as BE | 94 | import Data.BEncode as BE |
95 | import Data.BEncode.BDict as BDict | 95 | import Data.BEncode.BDict as BDict |
96 | import Network.BitTorrent.Address | ||
97 | #else | 96 | #else |
98 | import qualified Data.Tox as Tox | 97 | import qualified Data.Tox as Tox |
99 | import Data.Tox (NodeId) | 98 | import Data.Tox (NodeId) |
100 | import Data.Word | 99 | import Data.Word |
101 | import Control.Monad | 100 | import Control.Monad |
102 | import Network.KRPC.Method | 101 | import Network.KRPC.Method |
103 | import Network.BitTorrent.Address hiding (NodeId) | ||
104 | #endif | 102 | #endif |
103 | import Network.BitTorrent.Address hiding (NodeId) | ||
105 | import Data.ByteString (ByteString) | 104 | import Data.ByteString (ByteString) |
106 | import Data.List as L | 105 | import Data.List as L |
107 | import Data.Monoid | 106 | import Data.Monoid |
@@ -109,11 +108,14 @@ import Data.Serialize as S | |||
109 | import Data.Typeable | 108 | import Data.Typeable |
110 | import Network | 109 | import Network |
111 | import Network.KRPC | 110 | import Network.KRPC |
111 | import Network.KRPC.Message (KMessageOf) | ||
112 | import Data.Maybe | 112 | import Data.Maybe |
113 | 113 | ||
114 | import Data.Torrent (InfoHash) | 114 | import Data.Torrent (InfoHash) |
115 | import Network.BitTorrent.DHT.Token | 115 | import Network.BitTorrent.DHT.Token |
116 | import Network.KRPC () | 116 | import Network.KRPC () |
117 | import Network.DHT.Mainline () | ||
118 | import Network.RPC hiding (Query,Response) | ||
117 | 119 | ||
118 | {----------------------------------------------------------------------- | 120 | {----------------------------------------------------------------------- |
119 | -- envelopes | 121 | -- envelopes |
@@ -134,7 +136,7 @@ read_only_key = "ro" | |||
134 | -- | All queries have an \"id\" key and value containing the node ID | 136 | -- | All queries have an \"id\" key and value containing the node ID |
135 | -- of the querying node. | 137 | -- of the querying node. |
136 | data Query a = Query | 138 | data Query a = Query |
137 | { queringNodeId :: NodeId -- ^ node id of /quering/ node; | 139 | { queringNodeId :: NodeId KMessageOf -- ^ node id of /quering/ node; |
138 | , queryIsReadOnly :: Bool -- ^ node is read-only as per BEP 43 | 140 | , queryIsReadOnly :: Bool -- ^ node is read-only as per BEP 43 |
139 | , queryParams :: a -- ^ query parameters. | 141 | , queryParams :: a -- ^ query parameters. |
140 | } deriving (Show, Eq, Typeable) | 142 | } deriving (Show, Eq, Typeable) |
@@ -161,7 +163,7 @@ data Query a = Query a | |||
161 | -- | All responses have an \"id\" key and value containing the node ID | 163 | -- | All responses have an \"id\" key and value containing the node ID |
162 | -- of the responding node. | 164 | -- of the responding node. |
163 | data Response a = Response | 165 | data Response a = Response |
164 | { queredNodeId :: NodeId -- ^ node id of /quered/ node; | 166 | { queredNodeId :: NodeId KMessageOf -- ^ node id of /quered/ node; |
165 | , responseVals :: a -- ^ query result. | 167 | , responseVals :: a -- ^ query result. |
166 | } deriving (Show, Eq, Typeable) | 168 | } deriving (Show, Eq, Typeable) |
167 | 169 | ||
@@ -233,7 +235,7 @@ instance KRPC (Query Ping) (Response Ping) where | |||
233 | -- | Find node is used to find the contact information for a node | 235 | -- | Find node is used to find the contact information for a node |
234 | -- given its ID. | 236 | -- given its ID. |
235 | #ifdef VERSION_bencoding | 237 | #ifdef VERSION_bencoding |
236 | newtype FindNode ip = FindNode NodeId | 238 | newtype FindNode ip = FindNode (NodeId KMessageOf) |
237 | #else | 239 | #else |
238 | data FindNode ip = FindNode NodeId Tox.Nonce8 -- Tox: Get Nodes | 240 | data FindNode ip = FindNode NodeId Tox.Nonce8 -- Tox: Get Nodes |
239 | #endif | 241 | #endif |
@@ -262,7 +264,7 @@ instance Serialize (Query (FindNode ip)) where | |||
262 | -- nodes in its own routing table. | 264 | -- nodes in its own routing table. |
263 | -- | 265 | -- |
264 | #ifdef VERSION_bencoding | 266 | #ifdef VERSION_bencoding |
265 | newtype NodeFound ip = NodeFound [NodeInfo ip] | 267 | newtype NodeFound ip = NodeFound [NodeInfo KMessageOf ip ()] |
266 | #else | 268 | #else |
267 | data NodeFound ip = NodeFound [Tox.NodeFormat] Tox.Nonce8 | 269 | data NodeFound ip = NodeFound [Tox.NodeFormat] Tox.Nonce8 |
268 | #endif | 270 | #endif |
@@ -273,9 +275,9 @@ nodes_key :: BKey | |||
273 | nodes_key = "nodes" | 275 | nodes_key = "nodes" |
274 | 276 | ||
275 | -- Convert IPv4 address. Useful for using variadic IP type. | 277 | -- Convert IPv4 address. Useful for using variadic IP type. |
276 | from4 :: forall s. Address s => NodeInfo IPv4 -> Either String (NodeInfo s) | 278 | from4 :: forall dht u s. Address s => NodeInfo dht IPv4 u -> Either String (NodeInfo dht s u) |
277 | from4 n = maybe (Left "Error converting IPv4") Right | 279 | from4 n = maybe (Left "Error converting IPv4") Right |
278 | $ traverse (fromAddr :: IPv4 -> Maybe s) n | 280 | $ traverseAddress (fromAddr :: IPv4 -> Maybe s) n |
279 | 281 | ||
280 | #ifdef VERSION_bencoding | 282 | #ifdef VERSION_bencoding |
281 | binary :: Serialize a => BKey -> BE.Get [a] | 283 | binary :: Serialize a => BKey -> BE.Get [a] |
@@ -334,7 +336,7 @@ instance Typeable ip => BEncode (GetPeers ip) where | |||
334 | toBEncode (GetPeers ih) = toDict $ info_hash_key .=! ih .: endDict | 336 | toBEncode (GetPeers ih) = toDict $ info_hash_key .=! ih .: endDict |
335 | fromBEncode = fromDict $ GetPeers <$>! info_hash_key | 337 | fromBEncode = fromDict $ GetPeers <$>! info_hash_key |
336 | 338 | ||
337 | type PeerList ip = Either [NodeInfo ip] [PeerAddr ip] | 339 | type PeerList ip = Either [NodeInfo KMessageOf ip ()] [PeerAddr ip] |
338 | 340 | ||
339 | data GotPeers ip = GotPeers | 341 | data GotPeers ip = GotPeers |
340 | { -- | If the queried node has no peers for the infohash, returned | 342 | { -- | If the queried node has no peers for the infohash, returned |
diff --git a/src/Network/BitTorrent/DHT/Query.hs b/src/Network/BitTorrent/DHT/Query.hs index 820db8ba..4b386cdc 100644 --- a/src/Network/BitTorrent/DHT/Query.hs +++ b/src/Network/BitTorrent/DHT/Query.hs | |||
@@ -80,7 +80,7 @@ import Text.PrettyPrint.HughesPJClass hiding ((<>),($$)) | |||
80 | import Data.Time | 80 | import Data.Time |
81 | import Data.Time.Clock.POSIX | 81 | import Data.Time.Clock.POSIX |
82 | 82 | ||
83 | import Network.KRPC hiding (Options, def) | 83 | import Network.KRPC as KRPC hiding (Options, def) |
84 | import Network.KRPC.Message (ReflectedIP(..)) | 84 | import Network.KRPC.Message (ReflectedIP(..)) |
85 | import Network.KRPC.Manager (QueryFailure(..)) | 85 | import Network.KRPC.Manager (QueryFailure(..)) |
86 | import Data.Torrent | 86 | import Data.Torrent |
@@ -90,14 +90,15 @@ import Network.BitTorrent.DHT.Session | |||
90 | import Control.Concurrent.STM | 90 | import Control.Concurrent.STM |
91 | import qualified Network.BitTorrent.DHT.Search as Search | 91 | import qualified Network.BitTorrent.DHT.Search as Search |
92 | #ifdef VERSION_bencoding | 92 | #ifdef VERSION_bencoding |
93 | import Network.BitTorrent.Address | ||
94 | import Data.BEncode (BValue) | 93 | import Data.BEncode (BValue) |
95 | import Network.DHT.Mainline | 94 | import Network.DHT.Mainline hiding (NodeId) |
95 | import Network.KRPC.Message (KMessageOf) | ||
96 | #else | 96 | #else |
97 | import Network.BitTorrent.Address hiding (NodeId) | ||
98 | import Data.ByteString (ByteString) | 97 | import Data.ByteString (ByteString) |
99 | import Data.Tox | 98 | import Data.Tox |
100 | #endif | 99 | #endif |
100 | import Network.BitTorrent.Address hiding (NodeId) | ||
101 | import Network.RPC as RPC hiding (Query,Response) | ||
101 | 102 | ||
102 | {----------------------------------------------------------------------- | 103 | {----------------------------------------------------------------------- |
103 | -- Handlers | 104 | -- Handlers |
@@ -106,18 +107,17 @@ import Data.Tox | |||
106 | nodeHandler :: ( Address ip | 107 | nodeHandler :: ( Address ip |
107 | , KRPC (Query a) (Response b) | 108 | , KRPC (Query a) (Response b) |
108 | #ifdef VERSION_bencoding | 109 | #ifdef VERSION_bencoding |
109 | , Envelope (Query a) (Response b) ~ BValue ) | 110 | , KRPC.Envelope (Query a) (Response b) ~ BValue ) |
110 | #else | 111 | #else |
111 | , Envelope (Query a) (Response b) ~ ByteString ) | 112 | , KPRC.Envelope (Query a) (Response b) ~ ByteString ) |
112 | #endif | 113 | #endif |
113 | => (NodeAddr ip -> a -> DHT ip b) -> NodeHandler ip | 114 | => (NodeAddr ip -> a -> DHT ip b) -> NodeHandler ip |
114 | #ifdef VERSION_bencoding | ||
115 | nodeHandler action = handler $ \ sockAddr qry -> do | 115 | nodeHandler action = handler $ \ sockAddr qry -> do |
116 | #ifdef VERSION_bencoding | ||
116 | let remoteId = queringNodeId qry | 117 | let remoteId = queringNodeId qry |
117 | read_only = queryIsReadOnly qry | 118 | read_only = queryIsReadOnly qry |
118 | q = queryParams qry | 119 | q = queryParams qry |
119 | #else | 120 | #else |
120 | nodeHandler action = handler (error "TODO TOX Messaging") $ \ sockAddr qry -> do | ||
121 | let remoteId = msgClient qry | 121 | let remoteId = msgClient qry |
122 | read_only = False | 122 | read_only = False |
123 | q = msgPayload qry | 123 | q = msgPayload qry |
@@ -125,7 +125,7 @@ nodeHandler action = handler (error "TODO TOX Messaging") $ \ sockAddr qry -> do | |||
125 | case fromSockAddr sockAddr of | 125 | case fromSockAddr sockAddr of |
126 | Nothing -> throwIO BadAddress | 126 | Nothing -> throwIO BadAddress |
127 | Just naddr -> do | 127 | Just naddr -> do |
128 | let ni = NodeInfo remoteId naddr | 128 | let ni = NodeInfo remoteId naddr () |
129 | -- Do not route read-only nodes. (bep 43) | 129 | -- Do not route read-only nodes. (bep 43) |
130 | if read_only | 130 | if read_only |
131 | then $(logWarnS) "nodeHandler" $ "READ-ONLY " <> T.pack (show $ pPrint ni) | 131 | then $(logWarnS) "nodeHandler" $ "READ-ONLY " <> T.pack (show $ pPrint ni) |
@@ -136,8 +136,11 @@ nodeHandler action = handler (error "TODO TOX Messaging") $ \ sockAddr qry -> do | |||
136 | 136 | ||
137 | -- | Default 'Ping' handler. | 137 | -- | Default 'Ping' handler. |
138 | pingH :: Address ip => NodeHandler ip | 138 | pingH :: Address ip => NodeHandler ip |
139 | pingH = nodeHandler $ \ _ Ping -> do | 139 | #ifdef VERSION_bencoding |
140 | return Ping | 140 | pingH = nodeHandler $ \ _ Ping -> return Ping |
141 | #else | ||
142 | pingH = nodeHandler $ \ _ p@PingPayload{} -> return p { isPong = True } | ||
143 | #endif | ||
141 | 144 | ||
142 | -- | Default 'FindNode' handler. | 145 | -- | Default 'FindNode' handler. |
143 | findNodeH :: Address ip => NodeHandler ip | 146 | findNodeH :: Address ip => NodeHandler ip |
@@ -177,19 +180,23 @@ defaultHandlers = [pingH, findNodeH] | |||
177 | -- Basic queries | 180 | -- Basic queries |
178 | -----------------------------------------------------------------------} | 181 | -----------------------------------------------------------------------} |
179 | 182 | ||
180 | type Iteration ip o = NodeInfo ip -> DHT ip (Either [NodeInfo ip] [o ip]) | 183 | type Iteration ip o = NodeInfo KMessageOf ip () -> DHT ip (Either [NodeInfo KMessageOf ip ()] [o ip]) |
181 | 184 | ||
182 | -- | The most basic query. May be used to check if the given node is | 185 | -- | The most basic query. May be used to check if the given node is |
183 | -- alive or get its 'NodeId'. | 186 | -- alive or get its 'NodeId'. |
184 | pingQ :: Address ip => NodeAddr ip -> DHT ip (NodeInfo ip, Maybe ReflectedIP) | 187 | pingQ :: Address ip => NodeAddr ip -> DHT ip (NodeInfo KMessageOf ip (), Maybe ReflectedIP) |
185 | pingQ addr = do | 188 | pingQ addr = do |
189 | #ifdef VERSION_bencoding | ||
186 | (nid, Ping, mip) <- queryNode' addr Ping | 190 | (nid, Ping, mip) <- queryNode' addr Ping |
187 | return (NodeInfo nid addr, mip) | 191 | #else |
192 | (nid, PingPayload{}, mip) <- queryNode' addr PingPayload {isPong=False, pingId=pid} | ||
193 | #endif | ||
194 | return (NodeInfo nid addr (), mip) | ||
188 | 195 | ||
189 | -- TODO [robustness] match range of returned node ids with the | 196 | -- TODO [robustness] match range of returned node ids with the |
190 | -- expected range and either filter bad nodes or discard response at | 197 | -- expected range and either filter bad nodes or discard response at |
191 | -- all throwing an exception | 198 | -- all throwing an exception |
192 | findNodeQ :: Address ip => TableKey key => key -> Iteration ip NodeInfo | 199 | -- findNodeQ :: Address ip => TableKey key => key -> IterationI ip NodeInfo |
193 | findNodeQ key NodeInfo {..} = do | 200 | findNodeQ key NodeInfo {..} = do |
194 | NodeFound closest <- FindNode (toNodeId key) <@> nodeAddr | 201 | NodeFound closest <- FindNode (toNodeId key) <@> nodeAddr |
195 | $(logInfoS) "findNodeQ" $ "NodeFound\n" | 202 | $(logInfoS) "findNodeQ" $ "NodeFound\n" |
@@ -223,7 +230,7 @@ announceQ ih p NodeInfo {..} = do | |||
223 | -----------------------------------------------------------------------} | 230 | -----------------------------------------------------------------------} |
224 | 231 | ||
225 | 232 | ||
226 | ioGetPeers :: Address ip => InfoHash -> DHT ip (NodeInfo ip -> IO ([NodeInfo ip], [PeerAddr ip])) | 233 | ioGetPeers :: Address ip => InfoHash -> DHT ip (NodeInfo KMessageOf ip () -> IO ([NodeInfo KMessageOf ip ()], [PeerAddr ip])) |
227 | ioGetPeers ih = do | 234 | ioGetPeers ih = do |
228 | session <- ask | 235 | session <- ask |
229 | return $ \ni -> runDHT session $ do | 236 | return $ \ni -> runDHT session $ do |
@@ -232,7 +239,7 @@ ioGetPeers ih = do | |||
232 | Right e -> return $ either (,[]) ([],) e | 239 | Right e -> return $ either (,[]) ([],) e |
233 | Left e -> let _ = e :: QueryFailure in return ([],[]) | 240 | Left e -> let _ = e :: QueryFailure in return ([],[]) |
234 | 241 | ||
235 | ioFindNode :: Address ip => InfoHash -> DHT ip (NodeInfo ip -> IO ([NodeInfo ip], [NodeInfo ip])) | 242 | ioFindNode :: Address ip => InfoHash -> DHT ip (NodeInfo KMessageOf ip () -> IO ([NodeInfo KMessageOf ip ()], [NodeInfo KMessageOf ip ()])) |
236 | ioFindNode ih = do | 243 | ioFindNode ih = do |
237 | session <- ask | 244 | session <- ask |
238 | return $ \ni -> runDHT session $ do | 245 | return $ \ni -> runDHT session $ do |
@@ -240,7 +247,7 @@ ioFindNode ih = do | |||
240 | return $ L.partition (\n -> nodeId n /= toNodeId ih) ns | 247 | return $ L.partition (\n -> nodeId n /= toNodeId ih) ns |
241 | 248 | ||
242 | isearch :: (Ord r, Ord ip) => | 249 | isearch :: (Ord r, Ord ip) => |
243 | (InfoHash -> DHT ip (NodeInfo ip -> IO ([NodeInfo ip], [r]))) | 250 | (InfoHash -> DHT ip (NodeInfo KMessageOf ip () -> IO ([NodeInfo KMessageOf ip ()], [r]))) |
244 | -> InfoHash | 251 | -> InfoHash |
245 | -> DHT ip (ThreadId, Search.IterativeSearch ip r) | 252 | -> DHT ip (ThreadId, Search.IterativeSearch ip r) |
246 | isearch f ih = do | 253 | isearch f ih = do |
@@ -255,10 +262,10 @@ isearch f ih = do | |||
255 | return (a, s) | 262 | return (a, s) |
256 | 263 | ||
257 | 264 | ||
258 | type Search ip o = Conduit [NodeInfo ip] (DHT ip) [o ip] | 265 | type Search ip o = Conduit [NodeInfo KMessageOf ip ()] (DHT ip) [o KMessageOf ip ()] |
259 | 266 | ||
260 | -- TODO: use reorder and filter (Traversal option) leftovers | 267 | -- TODO: use reorder and filter (Traversal option) leftovers |
261 | search :: k -> Iteration ip o -> Search ip o | 268 | -- search :: k -> IterationI ip o -> Search ip o |
262 | search _ action = do | 269 | search _ action = do |
263 | awaitForever $ \ batch -> unless (L.null batch) $ do | 270 | awaitForever $ \ batch -> unless (L.null batch) $ do |
264 | $(logWarnS) "search" "start query" | 271 | $(logWarnS) "search" "start query" |
@@ -285,15 +292,15 @@ probeNode addr = do | |||
285 | 292 | ||
286 | 293 | ||
287 | -- FIXME do not use getClosest sinse we should /refresh/ them | 294 | -- FIXME do not use getClosest sinse we should /refresh/ them |
288 | refreshNodes :: Address ip => NodeId -> DHT ip () -- [NodeInfo ip] | 295 | refreshNodes :: Address ip => NodeId KMessageOf -> DHT ip () -- [NodeInfo KMessageOf ip ()] |
289 | refreshNodes nid = do | 296 | refreshNodes nid = do |
290 | $(logDebugS) "routing.refresh_bucket" (T.pack (render (pPrint nid))) | 297 | $(logDebugS) "routing.refresh_bucket" (T.pack (render (pPrint nid))) |
291 | nodes <- getClosest nid | 298 | nodes <- getClosest nid |
292 | do | 299 | do |
293 | -- forM (L.take 1 nodes) $ \ addr -> do | 300 | -- forM (L.take 1 nodes) $ \ addr -> do |
294 | -- NodeFound ns <- FindNode nid <@> addr | 301 | -- NodeFound ns <- FindNode nid <@> addr |
295 | -- Expected type: ConduitM [NodeAddr ip] [NodeInfo ip] (DHT ip) () | 302 | -- Expected type: ConduitM [NodeAddr ip] [NodeInfo KMessageOf ip ()] (DHT ip) () |
296 | -- Actual type: ConduitM [NodeInfo ip] [NodeInfo ip] (DHT ip) () | 303 | -- Actual type: ConduitM [NodeInfo KMessageOf ip ()] [NodeInfo KMessageOf ip ()] (DHT ip) () |
297 | -- nss <- sourceList [[addr]] $= search nid (findNodeQ nid) $$ C.consume | 304 | -- nss <- sourceList [[addr]] $= search nid (findNodeQ nid) $$ C.consume |
298 | nss <- sourceList [nodes] $= search nid (findNodeQ nid) $$ C.consume | 305 | nss <- sourceList [nodes] $= search nid (findNodeQ nid) $$ C.consume |
299 | $(logWarnS) "refreshNodes" $ "received " <> T.pack (show (L.length (L.concat nss))) <> " nodes." | 306 | $(logWarnS) "refreshNodes" $ "received " <> T.pack (show (L.length (L.concat nss))) <> " nodes." |
@@ -306,7 +313,7 @@ refreshNodes nid = do | |||
306 | 313 | ||
307 | -- | This operation do not block but acquire exclusive access to | 314 | -- | This operation do not block but acquire exclusive access to |
308 | -- routing table. | 315 | -- routing table. |
309 | insertNode :: forall ip. Address ip => NodeInfo ip -> Maybe ReflectedIP -> DHT ip () | 316 | insertNode :: forall ip. Address ip => NodeInfo KMessageOf ip () -> Maybe ReflectedIP -> DHT ip () |
310 | insertNode info witnessed_ip0 = do | 317 | insertNode info witnessed_ip0 = do |
311 | var <- asks routingInfo | 318 | var <- asks routingInfo |
312 | tm <- getTimestamp | 319 | tm <- getTimestamp |
@@ -315,7 +322,7 @@ insertNode info witnessed_ip0 = do | |||
315 | let logMsg = "Routing table: " <> pPrint t | 322 | let logMsg = "Routing table: " <> pPrint t |
316 | $(logDebugS) "insertNode" (T.pack (render logMsg)) | 323 | $(logDebugS) "insertNode" (T.pack (render logMsg)) |
317 | let arrival0 = TryInsert info | 324 | let arrival0 = TryInsert info |
318 | arrival4 = TryInsert (fmap fromAddr info) :: Event (Maybe IPv4) | 325 | arrival4 = TryInsert (mapAddress fromAddr info) :: Event (Maybe IPv4) |
319 | $(logDebugS) "insertNode" $ T.pack (show arrival4) | 326 | $(logDebugS) "insertNode" $ T.pack (show arrival4) |
320 | maxbuckets <- asks (optBucketCount . options) | 327 | maxbuckets <- asks (optBucketCount . options) |
321 | fallbackid <- asks tentativeNodeId | 328 | fallbackid <- asks tentativeNodeId |
@@ -380,18 +387,18 @@ insertNode info witnessed_ip0 = do | |||
380 | 387 | ||
381 | -- | Throws exception if node is not responding. | 388 | -- | Throws exception if node is not responding. |
382 | queryNode :: forall a b ip. Address ip => KRPC (Query a) (Response b) | 389 | queryNode :: forall a b ip. Address ip => KRPC (Query a) (Response b) |
383 | => NodeAddr ip -> a -> DHT ip (NodeId, b) | 390 | => NodeAddr ip -> a -> DHT ip (NodeId KMessageOf, b) |
384 | queryNode addr q = fmap (\(n,b,_) -> (n,b)) $ queryNode' addr q | 391 | queryNode addr q = fmap (\(n,b,_) -> (n,b)) $ queryNode' addr q |
385 | 392 | ||
386 | queryNode' :: forall a b ip. Address ip => KRPC (Query a) (Response b) | 393 | queryNode' :: forall a b ip. Address ip => KRPC (Query a) (Response b) |
387 | => NodeAddr ip -> a -> DHT ip (NodeId, b, Maybe ReflectedIP) | 394 | => NodeAddr ip -> a -> DHT ip (NodeId KMessageOf, b, Maybe ReflectedIP) |
388 | queryNode' addr q = do | 395 | queryNode' addr q = do |
389 | nid <- myNodeIdAccordingTo addr | 396 | nid <- myNodeIdAccordingTo addr |
390 | let read_only = False -- TODO: check for NAT issues. (BEP 43) | 397 | let read_only = False -- TODO: check for NAT issues. (BEP 43) |
391 | (Response remoteId r, witnessed_ip) <- query' (toSockAddr addr) (Query nid read_only q) | 398 | (Response remoteId r, witnessed_ip) <- query' (toSockAddr addr) (Query nid read_only q) |
392 | -- $(logDebugS) "queryNode" $ "Witnessed address: " <> T.pack (show witnessed_ip) | 399 | -- $(logDebugS) "queryNode" $ "Witnessed address: " <> T.pack (show witnessed_ip) |
393 | -- <> " by " <> T.pack (show (toSockAddr addr)) | 400 | -- <> " by " <> T.pack (show (toSockAddr addr)) |
394 | _ <- insertNode (NodeInfo remoteId addr) witnessed_ip | 401 | _ <- insertNode (NodeInfo remoteId addr ()) witnessed_ip |
395 | return (remoteId, r, witnessed_ip) | 402 | return (remoteId, r, witnessed_ip) |
396 | 403 | ||
397 | -- | Infix version of 'queryNode' function. | 404 | -- | Infix version of 'queryNode' function. |
diff --git a/src/Network/BitTorrent/DHT/Routing.hs b/src/Network/BitTorrent/DHT/Routing.hs index cf4a4de3..6cf7f122 100644 --- a/src/Network/BitTorrent/DHT/Routing.hs +++ b/src/Network/BitTorrent/DHT/Routing.hs | |||
@@ -83,8 +83,10 @@ import Text.PrettyPrint.HughesPJClass (pPrint,Pretty) | |||
83 | import qualified Data.ByteString as BS | 83 | import qualified Data.ByteString as BS |
84 | import Data.Bits | 84 | import Data.Bits |
85 | 85 | ||
86 | import Network.KRPC.Message (KMessageOf) | ||
86 | import Data.Torrent | 87 | import Data.Torrent |
87 | import Network.BitTorrent.Address | 88 | import Network.BitTorrent.Address |
89 | import Network.DHT.Mainline | ||
88 | 90 | ||
89 | {----------------------------------------------------------------------- | 91 | {----------------------------------------------------------------------- |
90 | -- Routing monad | 92 | -- Routing monad |
@@ -180,7 +182,7 @@ runRouting ping_node find_nodes timestamper = go | |||
180 | -- other words: new nodes are used only when older nodes disappear. | 182 | -- other words: new nodes are used only when older nodes disappear. |
181 | 183 | ||
182 | -- | Timestamp - last time this node is pinged. | 184 | -- | Timestamp - last time this node is pinged. |
183 | type NodeEntry ip = Binding (NodeInfo ip) Timestamp | 185 | type NodeEntry ip = Binding (NodeInfo KMessageOf ip ()) Timestamp |
184 | 186 | ||
185 | -- TODO instance Pretty where | 187 | -- TODO instance Pretty where |
186 | 188 | ||
@@ -211,7 +213,7 @@ fromQ embed project QueueMethods{..} = | |||
211 | } | 213 | } |
212 | -} | 214 | -} |
213 | 215 | ||
214 | seqQ :: QueueMethods Identity (NodeInfo ip) (Seq.Seq (NodeInfo ip)) | 216 | seqQ :: QueueMethods Identity (NodeInfo KMessageOf ip ()) (Seq.Seq (NodeInfo KMessageOf ip ())) |
215 | seqQ = QueueMethods | 217 | seqQ = QueueMethods |
216 | { pushBack = \e fifo -> pure (fifo Seq.|> e) | 218 | { pushBack = \e fifo -> pure (fifo Seq.|> e) |
217 | , popFront = \fifo -> case Seq.viewl fifo of | 219 | , popFront = \fifo -> case Seq.viewl fifo of |
@@ -220,9 +222,9 @@ seqQ = QueueMethods | |||
220 | , emptyQueue = pure Seq.empty | 222 | , emptyQueue = pure Seq.empty |
221 | } | 223 | } |
222 | 224 | ||
223 | type BucketQueue ip = Seq.Seq (NodeInfo ip) | 225 | type BucketQueue ip = Seq.Seq (NodeInfo KMessageOf ip ()) |
224 | 226 | ||
225 | bucketQ :: QueueMethods Identity (NodeInfo ip) (BucketQueue ip) | 227 | bucketQ :: QueueMethods Identity (NodeInfo KMessageOf ip ()) (BucketQueue ip) |
226 | bucketQ = seqQ | 228 | bucketQ = seqQ |
227 | 229 | ||
228 | -- | Bucket is also limited in its length — thus it's called k-bucket. | 230 | -- | Bucket is also limited in its length — thus it's called k-bucket. |
@@ -232,7 +234,7 @@ bucketQ = seqQ | |||
232 | -- very unlikely that all nodes in bucket fail within an hour of | 234 | -- very unlikely that all nodes in bucket fail within an hour of |
233 | -- each other. | 235 | -- each other. |
234 | -- | 236 | -- |
235 | data Bucket ip = Bucket { bktNodes :: !(PSQ (NodeInfo ip) Timestamp) | 237 | data Bucket ip = Bucket { bktNodes :: !(PSQ (NodeInfo KMessageOf ip ()) Timestamp) |
236 | , bktQ :: !(BucketQueue ip) | 238 | , bktQ :: !(BucketQueue ip) |
237 | } deriving (Show,Generic) | 239 | } deriving (Show,Generic) |
238 | 240 | ||
@@ -303,7 +305,7 @@ insertBucket curTime (PingResult bad_node got_response) bucket | |||
303 | pure $ PSQ.insert info curTime nodes' | 305 | pure $ PSQ.insert info curTime nodes' |
304 | | otherwise = id | 306 | | otherwise = id |
305 | 307 | ||
306 | updateStamps :: Eq ip => Timestamp -> [NodeInfo ip] -> PSQ (NodeInfo ip) Timestamp -> PSQ (NodeInfo ip) Timestamp | 308 | updateStamps :: Eq ip => Timestamp -> [NodeInfo KMessageOf ip ()] -> PSQ (NodeInfo KMessageOf ip ()) Timestamp -> PSQ (NodeInfo KMessageOf ip ()) Timestamp |
307 | updateStamps curTime stales nodes = foldl' (\q n -> PSQ.insert n curTime q) nodes stales | 309 | updateStamps curTime stales nodes = foldl' (\q n -> PSQ.insert n curTime q) nodes stales |
308 | 310 | ||
309 | 311 | ||
@@ -330,6 +332,12 @@ split i b = (Bucket ns qs, Bucket ms rs) | |||
330 | where | 332 | where |
331 | (ns,ms) = (PSQ.fromList *** PSQ.fromList) . partition (spanBit . key) . PSQ.toList $ bktNodes b | 333 | (ns,ms) = (PSQ.fromList *** PSQ.fromList) . partition (spanBit . key) . PSQ.toList $ bktNodes b |
332 | (qs,rs) = runIdentity $ partitionQ bucketQ spanBit $ bktQ b | 334 | (qs,rs) = runIdentity $ partitionQ bucketQ spanBit $ bktQ b |
335 | {- | ||
336 | spanBit :: forall (dht :: * -> *) addr u. | ||
337 | FiniteBits (Network.RPC.NodeId dht) => | ||
338 | NodeInfo dht addr u -> Bool | ||
339 | -} | ||
340 | spanBit :: NodeInfo KMessageOf addr () -> Bool | ||
333 | spanBit entry = testIdBit (nodeId entry) i | 341 | spanBit entry = testIdBit (nodeId entry) i |
334 | 342 | ||
335 | {----------------------------------------------------------------------- | 343 | {----------------------------------------------------------------------- |
@@ -458,7 +466,7 @@ compatibleNodeId tbl = genBucketSample prefix br | |||
458 | where | 466 | where |
459 | br = bucketRange (L.length (shape tbl) - 1) True | 467 | br = bucketRange (L.length (shape tbl) - 1) True |
460 | bs = BS.pack $ take nodeIdSize $ tablePrefix tbl ++ repeat 0 | 468 | bs = BS.pack $ take nodeIdSize $ tablePrefix tbl ++ repeat 0 |
461 | prefix = asNodeId bs | 469 | prefix = either error id $ S.decode bs |
462 | 470 | ||
463 | tablePrefix :: Table ip -> [Word8] | 471 | tablePrefix :: Table ip -> [Word8] |
464 | tablePrefix = map (packByte . take 8 . (++repeat False)) | 472 | tablePrefix = map (packByte . take 8 . (++repeat False)) |
@@ -503,7 +511,7 @@ instance TableKey InfoHash where | |||
503 | 511 | ||
504 | -- | Get a list of /K/ closest nodes using XOR metric. Used in | 512 | -- | Get a list of /K/ closest nodes using XOR metric. Used in |
505 | -- 'find_node' and 'get_peers' queries. | 513 | -- 'find_node' and 'get_peers' queries. |
506 | kclosest :: Eq ip => TableKey a => K -> a -> Table ip -> [NodeInfo ip] | 514 | kclosest :: Eq ip => TableKey a => K -> a -> Table ip -> [NodeInfo KMessageOf ip ()] |
507 | kclosest k (toNodeId -> nid) tbl = take k $ rank nodeId nid (L.concat bucket) | 515 | kclosest k (toNodeId -> nid) tbl = take k $ rank nodeId nid (L.concat bucket) |
508 | ++ rank nodeId nid (L.concat everyone) | 516 | ++ rank nodeId nid (L.concat everyone) |
509 | where | 517 | where |
@@ -547,19 +555,19 @@ modifyBucket nodeId f = go (0 :: BitIx) | |||
547 | <|> go i (splitTip nid n i bucket) | 555 | <|> go i (splitTip nid n i bucket) |
548 | 556 | ||
549 | -- | Triggering event for atomic table update | 557 | -- | Triggering event for atomic table update |
550 | data Event ip = TryInsert { foreignNode :: NodeInfo ip } | 558 | data Event ip = TryInsert { foreignNode :: NodeInfo KMessageOf ip () } |
551 | | PingResult { foreignNode :: NodeInfo ip | 559 | | PingResult { foreignNode :: NodeInfo KMessageOf ip () |
552 | , ponged :: Bool | 560 | , ponged :: Bool |
553 | } | 561 | } |
554 | deriving (Eq,Ord,Show) | 562 | deriving (Eq,Show) -- Ord |
555 | 563 | ||
556 | eventId :: Event ip -> NodeId | 564 | eventId :: Event ip -> NodeId |
557 | eventId (TryInsert NodeInfo{..}) = nodeId | 565 | eventId (TryInsert NodeInfo{..}) = nodeId |
558 | eventId (PingResult NodeInfo{..} _) = nodeId | 566 | eventId (PingResult NodeInfo{..} _) = nodeId |
559 | 567 | ||
560 | -- | Actions requested by atomic table update | 568 | -- | Actions requested by atomic table update |
561 | data CheckPing ip = CheckPing [NodeInfo ip] | 569 | data CheckPing ip = CheckPing [NodeInfo KMessageOf ip ()] |
562 | deriving (Eq,Ord,Show) | 570 | deriving (Eq,Show) -- Ord |
563 | 571 | ||
564 | 572 | ||
565 | -- | Atomic 'Table' update | 573 | -- | Atomic 'Table' update |
@@ -571,7 +579,7 @@ insert tm event tbl = pure $ fromMaybe ([],tbl) $ modifyBucket (eventId event) ( | |||
571 | -- Conversion | 579 | -- Conversion |
572 | -----------------------------------------------------------------------} | 580 | -----------------------------------------------------------------------} |
573 | 581 | ||
574 | type TableEntry ip = (NodeInfo ip, Timestamp) | 582 | type TableEntry ip = (NodeInfo KMessageOf ip (), Timestamp) |
575 | 583 | ||
576 | tableEntry :: NodeEntry ip -> TableEntry ip | 584 | tableEntry :: NodeEntry ip -> TableEntry ip |
577 | tableEntry (a :-> b) = (a, b) | 585 | tableEntry (a :-> b) = (a, b) |
diff --git a/src/Network/BitTorrent/DHT/Search.hs b/src/Network/BitTorrent/DHT/Search.hs index 79cc9489..854f26c7 100644 --- a/src/Network/BitTorrent/DHT/Search.hs +++ b/src/Network/BitTorrent/DHT/Search.hs | |||
@@ -1,5 +1,6 @@ | |||
1 | {-# LANGUAGE PatternSynonyms #-} | 1 | {-# LANGUAGE PatternSynonyms #-} |
2 | {-# LANGUAGE RecordWildCards #-} | 2 | {-# LANGUAGE RecordWildCards #-} |
3 | {-# LANGUAGE ScopedTypeVariables #-} | ||
3 | module Network.BitTorrent.DHT.Search where | 4 | module Network.BitTorrent.DHT.Search where |
4 | 5 | ||
5 | import Control.Concurrent | 6 | import Control.Concurrent |
@@ -21,20 +22,23 @@ import qualified Data.MinMaxPSQ as MM | |||
21 | ;import Data.MinMaxPSQ (MinMaxPSQ) | 22 | ;import Data.MinMaxPSQ (MinMaxPSQ) |
22 | import qualified Data.Wrapper.PSQ as PSQ | 23 | import qualified Data.Wrapper.PSQ as PSQ |
23 | ;import Data.Wrapper.PSQ (pattern (:->), Binding, PSQ) | 24 | ;import Data.Wrapper.PSQ (pattern (:->), Binding, PSQ) |
24 | import Network.BitTorrent.Address | 25 | import Network.BitTorrent.Address hiding (NodeId) |
26 | import Network.RPC | ||
27 | import Network.KRPC.Message (KMessageOf) | ||
28 | import Network.DHT.Mainline () | ||
25 | 29 | ||
26 | data IterativeSearch ip r = IterativeSearch | 30 | data IterativeSearch ip r = IterativeSearch |
27 | { searchTarget :: NodeId | 31 | { searchTarget :: NodeId KMessageOf |
28 | , searchQuery :: NodeInfo ip -> IO ([NodeInfo ip], [r]) | 32 | , searchQuery :: NodeInfo KMessageOf ip () -> IO ([NodeInfo KMessageOf ip ()], [r]) |
29 | , searchPendingCount :: TVar Int | 33 | , searchPendingCount :: TVar Int |
30 | , searchQueued :: TVar (MinMaxPSQ (NodeInfo ip) NodeDistance) | 34 | , searchQueued :: TVar (MinMaxPSQ (NodeInfo KMessageOf ip ()) (NodeDistance (NodeId KMessageOf))) |
31 | , searchInformant :: TVar (MinMaxPSQ (NodeInfo ip) NodeDistance) | 35 | , searchInformant :: TVar (MinMaxPSQ (NodeInfo KMessageOf ip ()) (NodeDistance (NodeId KMessageOf))) |
32 | , searchVisited :: TVar (Set (NodeAddr ip)) | 36 | , searchVisited :: TVar (Set (NodeAddr ip)) |
33 | , searchResults :: TVar (Set r) | 37 | , searchResults :: TVar (Set r) |
34 | } | 38 | } |
35 | 39 | ||
36 | newSearch :: Eq ip => (NodeInfo ip -> IO ([NodeInfo ip], [r])) | 40 | newSearch :: Eq ip => (NodeInfo KMessageOf ip () -> IO ([NodeInfo KMessageOf ip ()], [r])) |
37 | -> NodeId -> [NodeInfo ip] -> IO (IterativeSearch ip r) | 41 | -> NodeId KMessageOf -> [NodeInfo KMessageOf ip ()] -> IO (IterativeSearch ip r) |
38 | newSearch qry target ns = atomically $ do | 42 | newSearch qry target ns = atomically $ do |
39 | c <- newTVar 0 | 43 | c <- newTVar 0 |
40 | q <- newTVar $ MM.fromList $ map (\n -> n :-> distance target (nodeId n)) ns | 44 | q <- newTVar $ MM.fromList $ map (\n -> n :-> distance target (nodeId n)) ns |
@@ -49,9 +53,9 @@ searchAlpha = 3 | |||
49 | searchK :: Int | 53 | searchK :: Int |
50 | searchK = 8 | 54 | searchK = 8 |
51 | 55 | ||
52 | sendQuery :: (Ord a, Ord t) => | 56 | sendQuery :: forall a ip. (Ord a, Ord ip) => |
53 | IterativeSearch t a | 57 | IterativeSearch ip a |
54 | -> Binding (NodeInfo t) NodeDistance | 58 | -> Binding (NodeInfo KMessageOf ip ()) (NodeDistance (NodeId KMessageOf)) |
55 | -> IO () | 59 | -> IO () |
56 | sendQuery IterativeSearch{..} (ni :-> d) = do | 60 | sendQuery IterativeSearch{..} (ni :-> d) = do |
57 | (ns,rs) <- handle (\(SomeException e) -> return ([],[])) | 61 | (ns,rs) <- handle (\(SomeException e) -> return ([],[])) |
@@ -60,7 +64,10 @@ sendQuery IterativeSearch{..} (ni :-> d) = do | |||
60 | modifyTVar searchPendingCount pred | 64 | modifyTVar searchPendingCount pred |
61 | vs <- readTVar searchVisited | 65 | vs <- readTVar searchVisited |
62 | -- We only queue a node if it is not yet visited | 66 | -- We only queue a node if it is not yet visited |
63 | let insertFoundNode n q | 67 | let insertFoundNode :: NodeInfo KMessageOf ip u |
68 | -> MinMaxPSQ (NodeInfo KMessageOf ip u) (NodeDistance (NodeId KMessageOf)) | ||
69 | -> MinMaxPSQ (NodeInfo KMessageOf ip u) (NodeDistance (NodeId KMessageOf)) | ||
70 | insertFoundNode n q | ||
64 | | nodeAddr n `Set.member` vs = q | 71 | | nodeAddr n `Set.member` vs = q |
65 | | otherwise = MM.insertTake searchK n (distance searchTarget $ nodeId n) q | 72 | | otherwise = MM.insertTake searchK n (distance searchTarget $ nodeId n) q |
66 | modifyTVar searchQueued $ \q -> foldr insertFoundNode q ns | 73 | modifyTVar searchQueued $ \q -> foldr insertFoundNode q ns |
diff --git a/src/Network/BitTorrent/DHT/Session.hs b/src/Network/BitTorrent/DHT/Session.hs index 20dba595..aa6ee396 100644 --- a/src/Network/BitTorrent/DHT/Session.hs +++ b/src/Network/BitTorrent/DHT/Session.hs | |||
@@ -452,7 +452,7 @@ allPeers ih = do | |||
452 | -- | 452 | -- |
453 | -- This operation used for 'find_nodes' query. | 453 | -- This operation used for 'find_nodes' query. |
454 | -- | 454 | -- |
455 | getClosest :: Eq ip => TableKey k => k -> DHT ip [NodeInfo ip] | 455 | getClosest :: Eq ip => TableKey k => k -> DHT ip [NodeInfo KMessageOf ip ()] |
456 | getClosest node = do | 456 | getClosest node = do |
457 | k <- asks (optK . options) | 457 | k <- asks (optK . options) |
458 | kclosest k node <$> getTable | 458 | kclosest k node <$> getTable |
diff --git a/src/Network/DHT/Mainline.hs b/src/Network/DHT/Mainline.hs index 540b74f9..d7aed430 100644 --- a/src/Network/DHT/Mainline.hs +++ b/src/Network/DHT/Mainline.hs | |||
@@ -1,18 +1,76 @@ | |||
1 | {-# LANGUAGE LambdaCase #-} | 1 | {-# LANGUAGE LambdaCase #-} |
2 | {-# LANGUAGE MultiParamTypeClasses #-} | 2 | {-# LANGUAGE MultiParamTypeClasses #-} |
3 | {-# LANGUAGE DeriveDataTypeable #-} | ||
3 | {-# LANGUAGE TypeFamilies #-} | 4 | {-# LANGUAGE TypeFamilies #-} |
5 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
4 | module Network.DHT.Mainline where | 6 | module Network.DHT.Mainline where |
5 | 7 | ||
6 | import Network.Socket | 8 | import Data.BEncode as BE |
7 | import Network.RPC | 9 | import Data.Bits |
8 | import Network.KRPC.Message as KRPC | 10 | import Data.ByteString (ByteString) |
9 | import Data.BEncode as BE | 11 | import Data.ByteString.Base16 as Base16 |
10 | import qualified Data.ByteString.Lazy as L | 12 | import qualified Data.ByteString.Char8 as Char8 |
11 | import Network.BitTorrent.Address as BT (NodeId) | 13 | import qualified Data.ByteString.Lazy as L |
14 | import Data.Default | ||
15 | import Data.LargeWord | ||
16 | import Data.Serialize as S | ||
17 | import Data.String | ||
18 | import Data.Typeable | ||
19 | import Network.KRPC.Message as KRPC | ||
20 | import qualified Network.RPC as RPC (NodeId) | ||
21 | ;import Network.RPC as RPC hiding (NodeId) | ||
22 | import Text.PrettyPrint as PP hiding ((<>)) | ||
23 | import Text.PrettyPrint.HughesPJClass hiding (($$), (<>)) | ||
24 | |||
25 | -- | Each node has a globally unique identifier known as the \"node | ||
26 | -- ID.\" | ||
27 | -- | ||
28 | -- Normally, /this/ node id should be saved between invocations | ||
29 | -- of the client software. | ||
30 | newtype NodeId = NodeId Word160 | ||
31 | deriving (Show, Eq, Ord, Typeable, Bits, FiniteBits) | ||
32 | |||
33 | instance BEncode NodeId where | ||
34 | toBEncode (NodeId w) = toBEncode $ S.encode w | ||
35 | fromBEncode bval = fromBEncode bval >>= S.decode | ||
36 | |||
37 | -- | NodeId size in bytes. | ||
38 | nodeIdSize :: Int | ||
39 | nodeIdSize = 20 | ||
40 | |||
41 | |||
42 | -- instance BEncode NodeId where TODO | ||
43 | |||
44 | -- TODO: put this somewhere appropriate | ||
45 | instance (Serialize a, Serialize b) => Serialize (LargeKey a b) where | ||
46 | put (LargeKey lo hi) = put hi >> put lo | ||
47 | get = flip LargeKey <$> get <*> get | ||
48 | |||
49 | instance Serialize NodeId where | ||
50 | get = NodeId <$> get | ||
51 | {-# INLINE get #-} | ||
52 | put (NodeId bs) = put bs | ||
53 | {-# INLINE put #-} | ||
54 | |||
55 | -- | ASCII encoded. | ||
56 | instance IsString NodeId where | ||
57 | fromString str | ||
58 | | length str == nodeIdSize = NodeId (either error id $ S.decode (fromString str :: ByteString)) | ||
59 | | length str == 2 * nodeIdSize = NodeId (either error id $ S.decode (fst $ Base16.decode $ fromString str)) | ||
60 | | otherwise = error "fromString: invalid NodeId length" | ||
61 | {-# INLINE fromString #-} | ||
62 | |||
63 | -- | Meaningless node id, for testing purposes only. | ||
64 | instance Default NodeId where | ||
65 | def = NodeId 0 | ||
66 | |||
67 | -- | base16 encoded. | ||
68 | instance Pretty NodeId where | ||
69 | pPrint (NodeId nid) = PP.text $ Char8.unpack $ Base16.encode $ S.encode nid | ||
12 | 70 | ||
13 | instance Envelope KMessageOf where | 71 | instance Envelope KMessageOf where |
14 | type TransactionID KMessageOf = KRPC.TransactionId | 72 | type TransactionID KMessageOf = KRPC.TransactionId |
15 | type NodeId KMessageOf = BT.NodeId | 73 | type NodeId KMessageOf = Network.DHT.Mainline.NodeId |
16 | 74 | ||
17 | envelopePayload (Q q) = queryArgs q | 75 | envelopePayload (Q q) = queryArgs q |
18 | envelopePayload (R r) = respVals r | 76 | envelopePayload (R r) = respVals r |
diff --git a/src/Network/KRPC/Manager.hs b/src/Network/KRPC/Manager.hs index f31a3cd6..efd59f32 100644 --- a/src/Network/KRPC/Manager.hs +++ b/src/Network/KRPC/Manager.hs | |||
@@ -84,7 +84,9 @@ import Network.Socket hiding (listen) | |||
84 | import Network.Socket.ByteString as BS | 84 | import Network.Socket.ByteString as BS |
85 | import System.IO.Error | 85 | import System.IO.Error |
86 | import System.Timeout | 86 | import System.Timeout |
87 | #ifdef VERSION_bencoding | ||
87 | import Network.DHT.Mainline | 88 | import Network.DHT.Mainline |
89 | #endif | ||
88 | 90 | ||
89 | 91 | ||
90 | {----------------------------------------------------------------------- | 92 | {----------------------------------------------------------------------- |
@@ -268,15 +270,9 @@ data QueryFailure | |||
268 | 270 | ||
269 | instance Exception QueryFailure | 271 | instance Exception QueryFailure |
270 | 272 | ||
271 | #ifdef VERSION_bencoding | ||
272 | sendMessage :: MonadIO m => BEncode a => Socket -> SockAddr -> a -> m () | ||
273 | sendMessage sock addr a = do | ||
274 | liftIO $ sendManyTo sock (BL.toChunks (BE.encode a)) addr | ||
275 | #else | ||
276 | sendMessage :: MonadIO m => Socket -> SockAddr -> BC.ByteString -> m () | 273 | sendMessage :: MonadIO m => Socket -> SockAddr -> BC.ByteString -> m () |
277 | sendMessage sock addr a = do | 274 | sendMessage sock addr a = do |
278 | liftIO $ sendManyTo sock [a] addr | 275 | liftIO $ sendManyTo sock [a] addr |
279 | #endif | ||
280 | 276 | ||
281 | genTransactionId :: TransactionCounter -> IO TransactionId | 277 | genTransactionId :: TransactionCounter -> IO TransactionId |
282 | genTransactionId ref = do | 278 | genTransactionId ref = do |
@@ -309,13 +305,8 @@ unregisterQuery cid ref = do | |||
309 | 305 | ||
310 | 306 | ||
311 | -- (sendmsg EINVAL) | 307 | -- (sendmsg EINVAL) |
312 | #ifdef VERSION_bencoding | 308 | sendQuery :: Socket -> SockAddr -> BC.ByteString -> IO () |
313 | sendQuery :: BEncode a => Socket -> SockAddr -> a -> IO () | ||
314 | sendQuery sock addr q = handle sockError $ sendMessage sock addr q | 309 | sendQuery sock addr q = handle sockError $ sendMessage sock addr q |
315 | #else | ||
316 | sendQuery :: Serialize a => Socket -> SockAddr -> a -> IO () | ||
317 | sendQuery sock addr q = handle sockError $ sendMessage sock addr (S.encode q) | ||
318 | #endif | ||
319 | where | 310 | where |
320 | sockError :: IOError -> IO () | 311 | sockError :: IOError -> IO () |
321 | sockError _ = throwIO SendFailed | 312 | sockError _ = throwIO SendFailed |
@@ -351,12 +342,17 @@ queryK addr params kont = do | |||
351 | ares <- registerQuery (tid, addr) pendingCalls | 342 | ares <- registerQuery (tid, addr) pendingCalls |
352 | 343 | ||
353 | #ifdef VERSION_bencoding | 344 | #ifdef VERSION_bencoding |
354 | let q = KQuery (toBEncode params) (methodName queryMethod) tid | 345 | let q = Q (KQuery (toBEncode params) (methodName queryMethod) tid) |
346 | qb = encodePayload q :: KMessage | ||
347 | qbs = encodeHeaders () qb :: BC.ByteString | ||
355 | #else | 348 | #else |
356 | let q = Tox.Message (methodName queryMethod) cli tid params | 349 | let q = Tox.Message (methodName queryMethod) cli tid params |
357 | cli = error "TODO TOX client node id" | 350 | cli = error "TODO TOX client node id" |
351 | ctx = error "TODO TOX ToxCipherContext" | ||
352 | qb = encodePayload q :: Tox.Message BC.ByteString | ||
353 | qbs = encodeHeaders ctx qb :: BC.ByteString | ||
358 | #endif | 354 | #endif |
359 | sendQuery sock addr q | 355 | sendQuery sock addr qbs |
360 | `onException` unregisterQuery (tid, addr) pendingCalls | 356 | `onException` unregisterQuery (tid, addr) pendingCalls |
361 | 357 | ||
362 | timeout (optQueryTimeout options * 10 ^ (6 :: Int)) $ do | 358 | timeout (optQueryTimeout options * 10 ^ (6 :: Int)) $ do |
@@ -463,14 +459,7 @@ runHandler h addr m = Lifted.catches wrapper failbacks | |||
463 | 459 | ||
464 | Right a -> do -- KQueryArgs | 460 | Right a -> do -- KQueryArgs |
465 | $(logDebugS) "handler.success" signature | 461 | $(logDebugS) "handler.success" signature |
466 | #ifdef VERSION_bencoding | ||
467 | return $ Right a | 462 | return $ Right a |
468 | #else | ||
469 | let cli = error "TODO TOX client node id" | ||
470 | messageid = error "TODO TOX message response id" | ||
471 | -- TODO: ReflectedIP addr ?? | ||
472 | return $ Right $ Tox.Message messageid cli (queryId m) a | ||
473 | #endif | ||
474 | 463 | ||
475 | failbacks = | 464 | failbacks = |
476 | [ E.Handler $ \ (e :: HandlerFailure) -> do | 465 | [ E.Handler $ \ (e :: HandlerFailure) -> do |
@@ -528,16 +517,18 @@ handleQuery raw q addr = void $ fork $ do | |||
528 | Manager {..} <- getManager | 517 | Manager {..} <- getManager |
529 | res <- dispatchHandler q addr | 518 | res <- dispatchHandler q addr |
530 | #ifdef VERSION_bencoding | 519 | #ifdef VERSION_bencoding |
531 | let resbe = either toBEncode toBEncode res | 520 | let res' = either E id res |
521 | resbe = either toBEncode toBEncode res | ||
532 | $(logOther "q") $ T.unlines | 522 | $(logOther "q") $ T.unlines |
533 | [ either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode raw) | 523 | [ either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode raw) |
534 | , "==>" | 524 | , "==>" |
535 | , either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode resbe) | 525 | , either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode resbe) |
536 | ] | 526 | ] |
537 | sendMessage sock addr resbe | 527 | sendMessage sock addr $ encodeHeaders () res' |
538 | #else | 528 | #else |
539 | -- Errors not sent for Tox. | 529 | -- Errors not sent for Tox. |
540 | either (const $ return ()) (sendMessage sock addr . S.encode) res | 530 | let ctx = error "TODO TOX ToxCipherContext 2" |
531 | either (const $ return ()) (sendMessage sock addr . encodeHeaders ctx) res | ||
541 | #endif | 532 | #endif |
542 | 533 | ||
543 | handleResponse :: MonadKRPC h m => KQueryArgs -> KResult -> SockAddr -> m () | 534 | handleResponse :: MonadKRPC h m => KQueryArgs -> KResult -> SockAddr -> m () |
@@ -570,16 +561,17 @@ listener :: MonadKRPC h m => m () | |||
570 | listener = do | 561 | listener = do |
571 | Manager {..} <- getManager | 562 | Manager {..} <- getManager |
572 | fix $ \again -> do | 563 | fix $ \again -> do |
564 | let ctx = error "TODO TOX ToxCipherContext 3" | ||
573 | (bs, addr) <- liftIO $ do | 565 | (bs, addr) <- liftIO $ do |
574 | handle exceptions $ BS.recvFrom sock (optMaxMsgSize options) | 566 | handle exceptions $ BS.recvFrom sock (optMaxMsgSize options) |
575 | #ifdef VERSION_bencoding | 567 | #ifdef VERSION_bencoding |
576 | case BE.parse bs >>= \r -> (,) r <$> BE.decode bs of | 568 | case BE.parse bs >>= \r -> (,) r <$> BE.decode bs of |
577 | #else | 569 | #else |
578 | case return bs >>= \r -> (,) r <$> decode bs of | 570 | case return bs >>= \r -> (,) r <$> decodeHeaders ctx bs of |
579 | #endif | 571 | #endif |
580 | -- TODO ignore unknown messages at all? | 572 | -- TODO ignore unknown messages at all? |
581 | #ifdef VERSION_bencoding | 573 | #ifdef VERSION_bencoding |
582 | Left e -> liftIO $ sendMessage sock addr $ unknownMessage e | 574 | Left e -> liftIO $ sendMessage sock addr $ encodeHeaders () (E (unknownMessage e) :: KMessage) |
583 | #else | 575 | #else |
584 | Left _ -> return () -- TODO TOX send unknownMessage error | 576 | Left _ -> return () -- TODO TOX send unknownMessage error |
585 | #endif | 577 | #endif |
diff --git a/src/Network/RPC.hs b/src/Network/RPC.hs index 727422fd..7fb0e571 100644 --- a/src/Network/RPC.hs +++ b/src/Network/RPC.hs | |||
@@ -1,16 +1,22 @@ | |||
1 | {-# LANGUAGE ConstraintKinds #-} | 1 | {-# LANGUAGE ConstraintKinds #-} |
2 | {-# LANGUAGE DeriveDataTypeable #-} | ||
3 | {-# LANGUAGE FlexibleContexts #-} | ||
2 | {-# LANGUAGE FunctionalDependencies #-} | 4 | {-# LANGUAGE FunctionalDependencies #-} |
3 | {-# LANGUAGE MultiParamTypeClasses #-} | 5 | {-# LANGUAGE MultiParamTypeClasses #-} |
4 | {-# LANGUAGE RankNTypes #-} | 6 | {-# LANGUAGE RankNTypes #-} |
5 | {-# LANGUAGE ScopedTypeVariables #-} | 7 | {-# LANGUAGE ScopedTypeVariables #-} |
6 | {-# LANGUAGE TypeFamilies #-} | 8 | {-# LANGUAGE TypeFamilies #-} |
7 | {-# LANGUAGE DeriveDataTypeable #-} | ||
8 | module Network.RPC where | 9 | module Network.RPC where |
9 | 10 | ||
11 | import Data.Bits | ||
10 | import Data.ByteString (ByteString) | 12 | import Data.ByteString (ByteString) |
11 | import Data.Kind (Constraint) | 13 | import Data.Kind (Constraint) |
12 | import Data.Data | 14 | import Data.Data |
13 | import Network.Socket | 15 | import Network.Socket |
16 | import Text.PrettyPrint.HughesPJClass hiding (($$), (<>)) | ||
17 | import Data.Serialize as S | ||
18 | import qualified Data.ByteString.Char8 as Char8 | ||
19 | import Data.ByteString.Base16 as Base16 | ||
14 | 20 | ||
15 | data MessageClass = Error | Query | Response | 21 | data MessageClass = Error | Query | Response |
16 | deriving (Eq,Ord,Enum,Bounded,Data,Show,Read) | 22 | deriving (Eq,Ord,Enum,Bounded,Data,Show,Read) |
@@ -36,6 +42,22 @@ class Envelope envelope where | |||
36 | -- Returns: response message envelope | 42 | -- Returns: response message envelope |
37 | buildReply :: NodeId envelope -> SockAddr -> envelope a -> b -> envelope b | 43 | buildReply :: NodeId envelope -> SockAddr -> envelope a -> b -> envelope b |
38 | 44 | ||
45 | -- | In Kademlia, the distance metric is XOR and the result is | ||
46 | -- interpreted as an unsigned integer. | ||
47 | newtype NodeDistance nodeid = NodeDistance nodeid | ||
48 | deriving (Eq, Ord) | ||
49 | |||
50 | -- | distance(A,B) = |A xor B| Smaller values are closer. | ||
51 | distance :: Bits nid => nid -> nid -> NodeDistance nid | ||
52 | distance a b = NodeDistance $ xor a b | ||
53 | |||
54 | instance Serialize nodeid => Show (NodeDistance nodeid) where | ||
55 | show (NodeDistance w) = Char8.unpack $ Base16.encode $ S.encode w | ||
56 | |||
57 | instance Serialize nodeid => Pretty (NodeDistance nodeid) where | ||
58 | pPrint n = text $ show n | ||
59 | |||
60 | |||
39 | class Envelope envelope => WireFormat raw envelope where | 61 | class Envelope envelope => WireFormat raw envelope where |
40 | type SerializableTo raw :: * -> Constraint | 62 | type SerializableTo raw :: * -> Constraint |
41 | type CipherContext raw envelope | 63 | type CipherContext raw envelope |