summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Network/BitTorrent/Address.hs71
1 files changed, 69 insertions, 2 deletions
diff --git a/src/Network/BitTorrent/Address.hs b/src/Network/BitTorrent/Address.hs
index c0c0a3f8..3d2616cc 100644
--- a/src/Network/BitTorrent/Address.hs
+++ b/src/Network/BitTorrent/Address.hs
@@ -58,9 +58,12 @@ module Network.BitTorrent.Address
58 -- ** Id 58 -- ** Id
59 , NodeId 59 , NodeId
60 , testIdBit 60 , testIdBit
61 , genNodeId
62 , NodeDistance 61 , NodeDistance
63 , distance 62 , distance
63 , genNodeId
64 , bucketRange
65 , genBucketSample
66 , bep42
64 67
65 -- ** Info 68 -- ** Info
66 , NodeAddr (..) 69 , NodeAddr (..)
@@ -118,6 +121,7 @@ import Text.PrettyPrint.HughesPJClass hiding ((<>),($$))
118import System.Locale (defaultTimeLocale) 121import System.Locale (defaultTimeLocale)
119#endif 122#endif
120import System.Entropy 123import System.Entropy
124import Data.Digest.CRC32C
121 125
122-- import Paths_bittorrent (version) 126-- import Paths_bittorrent (version)
123 127
@@ -662,7 +666,7 @@ testIdBit :: NodeId -> Word -> Bool
662testIdBit (NodeId bs) i 666testIdBit (NodeId bs) i
663 | fromIntegral i < nodeIdSize * 8 667 | fromIntegral i < nodeIdSize * 8
664 , (q, r) <- quotRem (fromIntegral i) 8 668 , (q, r) <- quotRem (fromIntegral i) 8
665 = testBit (BS.index bs q) r 669 = testBit (BS.index bs q) (7 - r)
666 | otherwise = False 670 | otherwise = False
667{-# INLINE testIdBit #-} 671{-# INLINE testIdBit #-}
668 672
@@ -690,6 +694,47 @@ instance Pretty NodeDistance where
690distance :: NodeId -> NodeId -> NodeDistance 694distance :: NodeId -> NodeId -> NodeDistance
691distance (NodeId a) (NodeId b) = NodeDistance (BS.pack (BS.zipWith xor a b)) 695distance (NodeId a) (NodeId b) = NodeDistance (BS.pack (BS.zipWith xor a b))
692 696
697-- | Accepts a depth/index of a bucket and whether or not it is the last one,
698-- yields:
699--
700-- count of leading bytes to be copied from your node id.
701--
702-- mask to clear the extra bits of the last copied byte
703--
704-- mask to toggle the last copied bit if it is not the last bucket
705--
706-- Normally this is used with 'genBucketSample' to obtain a random id suitable
707-- for refreshing a particular bucket.
708bucketRange :: Int -> Bool -> (Int, Word8, Word8)
709bucketRange depth is_last = (q,m,b)
710 where
711 (q,r) = divMod ((if is_last then (+7) else (+8)) depth) 8
712 m = 2^(7-r) - 1
713 b = if is_last then 0 else 2^(7-r)
714
715-- | Generate a random 'NodeId' within a range suitable for a bucket. To
716-- obtain a sample for bucket number /index/ where /is_last/ indicates if this
717-- is for the current deepest bucket in our routing table:
718--
719-- > sample <- genBucketSample nid (bucketRange index is_last)
720genBucketSample :: NodeId -> (Int,Word8,Word8) -> IO NodeId
721genBucketSample n qmb = genBucketSample' getEntropy n qmb
722
723-- | Generalizion of 'genBucketSample' that accepts a byte generator
724-- function to use instead of the system entropy.
725genBucketSample' :: Applicative m =>
726 (Int -> m ByteString) -> NodeId -> (Int,Word8,Word8) -> m NodeId
727genBucketSample' gen (NodeId self) (q,m,b)
728 | q <= 0 = NodeId <$> gen nodeIdSize
729 | q >= nodeIdSize = pure (NodeId self)
730 | otherwise = NodeId . build <$> gen (nodeIdSize - q + 1)
731 where
732 build tl = BS.init hd <> BS.cons (h .|. t) (BS.tail tl)
733 where
734 hd = BS.take q self
735 h = xor b (complement m .&. BS.last hd)
736 t = m .&. BS.head tl
737
693------------------------------------------------------------------------ 738------------------------------------------------------------------------
694 739
695data NodeAddr a = NodeAddr 740data NodeAddr a = NodeAddr
@@ -1176,3 +1221,25 @@ fingerprint pid = either (const def) id $ runGet getCI (getPeerId pid)
1176 getShadowVersion = do 1221 getShadowVersion = do
1177 str <- BC.unpack <$> getByteString 5 1222 str <- BC.unpack <$> getByteString 5
1178 return $ Version (catMaybes $ L.map decodeShadowVerNr str) [] 1223 return $ Version (catMaybes $ L.map decodeShadowVerNr str) []
1224
1225
1226-- | Modifies a purely random 'NodeId' to one that is related to a given
1227-- routable address in accordance with BEP 42.
1228bep42 :: Address a => a -> NodeId -> Maybe NodeId
1229bep42 addr (NodeId r)
1230 | Just ip <- fmap S.encode (fromAddr addr :: Maybe IPv4)
1231 <|> fmap S.encode (fromAddr addr :: Maybe IPv6)
1232 = genBucketSample' retr (NodeId $ crc $ masked ip) (3,0x07,0)
1233 | otherwise
1234 = Nothing
1235 where
1236 ip4mask = "\x03\x0f\x3f\xff" :: ByteString
1237 ip6mask = "\x01\x03\x07\x0f\x1f\x3f\x7f\xff" :: ByteString
1238 rbyte = BS.last r
1239 retr n = pure $ BS.drop (BS.length r - n) r
1240 crc = S.encode . crc32c . BS.pack
1241 masked ip = case BS.zipWith (.&.) msk ip of
1242 (b:bs) -> (b .|. shiftL (rbyte .&. 7) 5) : bs
1243 bs -> bs
1244 where msk | BS.length ip == 4 = ip4mask
1245 | otherwise = ip6mask