diff options
Diffstat (limited to 'src/Network/BitTorrent/Address.hs')
-rw-r--r-- | src/Network/BitTorrent/Address.hs | 128 |
1 files changed, 49 insertions, 79 deletions
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 |