diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Network/BitTorrent/Address.hs | 71 |
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 ((<>),($$)) | |||
118 | import System.Locale (defaultTimeLocale) | 121 | import System.Locale (defaultTimeLocale) |
119 | #endif | 122 | #endif |
120 | import System.Entropy | 123 | import System.Entropy |
124 | import 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 | |||
662 | testIdBit (NodeId bs) i | 666 | testIdBit (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 | |||
690 | distance :: NodeId -> NodeId -> NodeDistance | 694 | distance :: NodeId -> NodeId -> NodeDistance |
691 | distance (NodeId a) (NodeId b) = NodeDistance (BS.pack (BS.zipWith xor a b)) | 695 | distance (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. | ||
708 | bucketRange :: Int -> Bool -> (Int, Word8, Word8) | ||
709 | bucketRange 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) | ||
720 | genBucketSample :: NodeId -> (Int,Word8,Word8) -> IO NodeId | ||
721 | genBucketSample 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. | ||
725 | genBucketSample' :: Applicative m => | ||
726 | (Int -> m ByteString) -> NodeId -> (Int,Word8,Word8) -> m NodeId | ||
727 | genBucketSample' 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 | ||
695 | data NodeAddr a = NodeAddr | 740 | data 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. | ||
1228 | bep42 :: Address a => a -> NodeId -> Maybe NodeId | ||
1229 | bep42 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 | ||