summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent/Address.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/BitTorrent/Address.hs')
-rw-r--r--src/Network/BitTorrent/Address.hs128
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)
98import Data.Bits 98import Data.Bits
99import qualified Data.ByteString as BS 99import qualified Data.ByteString as BS
100import qualified Data.ByteString.Internal as BS 100import qualified Data.ByteString.Internal as BS
101import Data.ByteString.Base16 as Base16
102import Data.ByteString.Char8 as BC 101import Data.ByteString.Char8 as BC
103import Data.ByteString.Char8 as BS8 102import Data.ByteString.Char8 as BS8
104import qualified Data.ByteString.Lazy as BL 103import qualified Data.ByteString.Lazy as BL
@@ -130,6 +129,9 @@ import System.Locale (defaultTimeLocale)
130#endif 129#endif
131import System.Entropy 130import System.Entropy
132import Data.Digest.CRC32C 131import Data.Digest.CRC32C
132import qualified Network.RPC as RPC
133import Network.KRPC.Message (KMessageOf)
134import 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.
656newtype NodeId = NodeId ByteString
657 deriving (Show, Eq, Ord, Typeable
658#ifdef VERSION_bencoding
659 , BEncode
660#endif
661 )
662
663
664nodeIdSize :: Int
665nodeIdSize = 20
666
667asNodeId :: ByteString -> NodeId
668asNodeId bs = NodeId $ BS.take nodeIdSize bs
669
670-- | Meaningless node id, for testing purposes only.
671instance Default NodeId where
672 def = NodeId (BS.replicate nodeIdSize 0)
673
674instance Serialize NodeId where
675 get = NodeId <$> getByteString nodeIdSize
676 {-# INLINE get #-}
677 put (NodeId bs) = putByteString bs
678 {-# INLINE put #-}
679
680-- | ASCII encoded.
681instance 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.
689instance 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.
693testIdBit :: NodeId -> Word -> Bool 657testIdBit :: 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
665testIdBit :: FiniteBits bs => bs -> Word -> Bool
666testIdBit 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--
705genNodeId :: IO NodeId 673genNodeId :: IO NodeId
706genNodeId = NodeId <$> getEntropy nodeIdSize 674genNodeId = 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.
712newtype NodeDistance = NodeDistance BS.ByteString
713 deriving (Eq, Ord)
714
715instance Pretty NodeDistance where
716 pPrint (NodeDistance bs) = text $ BC.unpack (Base16.encode bs)
717
718instance Show NodeDistance where
719 show (NodeDistance bs) = BC.unpack (Base16.encode bs)
720
721-- | distance(A,B) = |A xor B| Smaller values are closer.
722distance :: NodeId -> NodeId -> NodeDistance
723distance (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
753genBucketSample' :: Applicative m => 706genBucketSample' :: Applicative m =>
754 (Int -> m ByteString) -> NodeId -> (Int,Word8,Word8) -> m NodeId 707 (Int -> m ByteString) -> NodeId -> (Int,Word8,Word8) -> m NodeId
755genBucketSample' gen (NodeId self) (q,m,b) 708genBucketSample' 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
822data NodeInfo a = NodeInfo 775data 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
781deriving instance ( Show (RPC.NodeId dht)
782 , Show addr
783 , Show u ) => Show (NodeInfo dht addr u)
826 784
785mapAddress :: (addr -> b) -> NodeInfo dht addr u -> NodeInfo dht b u
786mapAddress f ni = ni { nodeAddr = fmap f (nodeAddr ni) }
827 787
828instance Eq a => Ord (NodeInfo a) where 788traverseAddress :: Applicative f => (addr -> f b) -> NodeInfo dht addr u -> f (NodeInfo dht b u)
789traverseAddress f ni = fmap (\addr -> ni { nodeAddr = addr }) $ traverse f (nodeAddr ni)
790
791-- Warning: Eq and Ord only look at the nodeId field.
792instance Eq (RPC.NodeId dht) => Eq (NodeInfo dht a u) where
793 a == b = (nodeId a == nodeId b)
794
795instance 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.
835instance Serialize a => Serialize (NodeInfo a) where 802instance 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
839instance Pretty ip => Pretty (NodeInfo ip) where 806instance Pretty ip => Pretty (NodeInfo KMessageOf ip ()) where
840 pPrint NodeInfo {..} = pPrint nodeId <> "@(" <> pPrint nodeAddr <> ")" 807 pPrint NodeInfo {..} = pPrint nodeId <> "@(" <> pPrint nodeAddr <> ")"
841 808
842instance Pretty ip => Pretty [NodeInfo ip] where 809instance 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.
846rank :: (x -> NodeId) -> NodeId -> [x] -> [x] 813rank :: (x -> NodeId) -> NodeId -> [x] -> [x]
847rank f nid = L.sortBy (comparing (distance nid . f)) 814rank 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
1262change3bits :: ByteString -> Word8 -> ByteString 1229-- change3bits :: ByteString -> Word8 -> ByteString
1263change3bits 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
1232change3bits :: (Num b, Bits b) => b -> b -> b
1233change3bits 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