diff options
Diffstat (limited to 'src/Network/BitTorrent/Address.hs')
-rw-r--r-- | src/Network/BitTorrent/Address.hs | 242 |
1 files changed, 6 insertions, 236 deletions
diff --git a/src/Network/BitTorrent/Address.hs b/src/Network/BitTorrent/Address.hs index 560ac1ef..f364abbe 100644 --- a/src/Network/BitTorrent/Address.hs +++ b/src/Network/BitTorrent/Address.hs | |||
@@ -13,6 +13,7 @@ | |||
13 | {-# LANGUAGE FlexibleInstances #-} | 13 | {-# LANGUAGE FlexibleInstances #-} |
14 | {-# LANGUAGE FlexibleContexts #-} | 14 | {-# LANGUAGE FlexibleContexts #-} |
15 | {-# LANGUAGE RecordWildCards #-} | 15 | {-# LANGUAGE RecordWildCards #-} |
16 | {-# LANGUAGE ScopedTypeVariables #-} | ||
16 | {-# LANGUAGE StandaloneDeriving #-} | 17 | {-# LANGUAGE StandaloneDeriving #-} |
17 | {-# LANGUAGE ViewPatterns #-} | 18 | {-# LANGUAGE ViewPatterns #-} |
18 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | 19 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} |
@@ -60,13 +61,10 @@ module Network.BitTorrent.Address | |||
60 | -- * Node | 61 | -- * Node |
61 | -- ** Id | 62 | -- ** Id |
62 | , NodeId | 63 | , NodeId |
63 | , nodeIdSize | ||
64 | , testIdBit | 64 | , testIdBit |
65 | , genNodeId | 65 | , genNodeId |
66 | , bucketRange | 66 | , bucketRange |
67 | , genBucketSample | 67 | , genBucketSample |
68 | , bep42 | ||
69 | , bep42s | ||
70 | 68 | ||
71 | -- ** Info | 69 | -- ** Info |
72 | , NodeAddr (..) | 70 | , NodeAddr (..) |
@@ -129,47 +127,15 @@ import System.Locale (defaultTimeLocale) | |||
129 | #endif | 127 | #endif |
130 | import System.Entropy | 128 | import System.Entropy |
131 | import Data.Digest.CRC32C | 129 | import Data.Digest.CRC32C |
132 | import qualified Network.RPC as RPC | 130 | import Network.RPC as RPC |
133 | import Network.KRPC.Message (KMessageOf) | 131 | import Network.KRPC.Message (KMessageOf) |
134 | import Network.DHT.Mainline | 132 | -- import Network.DHT.Mainline |
135 | 133 | ||
136 | -- import Paths_bittorrent (version) | 134 | -- import Paths_bittorrent (version) |
137 | 135 | ||
138 | {----------------------------------------------------------------------- | ||
139 | -- Address | ||
140 | -----------------------------------------------------------------------} | ||
141 | |||
142 | instance Pretty UTCTime where | 136 | instance Pretty UTCTime where |
143 | pPrint = PP.text . show | 137 | pPrint = PP.text . show |
144 | 138 | ||
145 | class (Eq a, Serialize a, Typeable a, Hashable a, Pretty a) | ||
146 | => Address a where | ||
147 | toSockAddr :: a -> SockAddr | ||
148 | fromSockAddr :: SockAddr -> Maybe a | ||
149 | |||
150 | fromAddr :: (Address a, Address b) => a -> Maybe b | ||
151 | fromAddr = fromSockAddr . toSockAddr | ||
152 | |||
153 | -- | Note that port is zeroed. | ||
154 | instance Address IPv4 where | ||
155 | toSockAddr = SockAddrInet 0 . toHostAddress | ||
156 | fromSockAddr (SockAddrInet _ h) = Just (fromHostAddress h) | ||
157 | fromSockAddr _ = Nothing | ||
158 | |||
159 | -- | Note that port is zeroed. | ||
160 | instance Address IPv6 where | ||
161 | toSockAddr h = SockAddrInet6 0 0 (toHostAddress6 h) 0 | ||
162 | fromSockAddr (SockAddrInet6 _ _ h _) = Just (fromHostAddress6 h) | ||
163 | fromSockAddr _ = Nothing | ||
164 | |||
165 | -- | Note that port is zeroed. | ||
166 | instance Address IP where | ||
167 | toSockAddr (IPv4 h) = toSockAddr h | ||
168 | toSockAddr (IPv6 h) = toSockAddr h | ||
169 | fromSockAddr sa = | ||
170 | IPv4 <$> fromSockAddr sa | ||
171 | <|> IPv6 <$> fromSockAddr sa | ||
172 | |||
173 | setPort :: PortNumber -> SockAddr -> SockAddr | 139 | setPort :: PortNumber -> SockAddr -> SockAddr |
174 | setPort port (SockAddrInet _ h ) = SockAddrInet port h | 140 | setPort port (SockAddrInet _ h ) = SockAddrInet port h |
175 | setPort port (SockAddrInet6 _ f h s) = SockAddrInet6 port f h s | 141 | setPort port (SockAddrInet6 _ f h s) = SockAddrInet6 port f h s |
@@ -388,21 +354,6 @@ instance BEncode PortNumber where | |||
388 | = pure $ fromIntegral n | 354 | = pure $ fromIntegral n |
389 | | otherwise = decodingError $ "PortNumber: " ++ show n | 355 | | otherwise = decodingError $ "PortNumber: " ++ show n |
390 | #endif | 356 | #endif |
391 | |||
392 | instance Serialize PortNumber where | ||
393 | get = fromIntegral <$> getWord16be | ||
394 | {-# INLINE get #-} | ||
395 | put = putWord16be . fromIntegral | ||
396 | {-# INLINE put #-} | ||
397 | |||
398 | instance Hashable PortNumber where | ||
399 | hashWithSalt s = hashWithSalt s . fromEnum | ||
400 | {-# INLINE hashWithSalt #-} | ||
401 | |||
402 | instance Pretty PortNumber where | ||
403 | pPrint = PP.int . fromEnum | ||
404 | {-# INLINE pPrint #-} | ||
405 | |||
406 | {----------------------------------------------------------------------- | 357 | {----------------------------------------------------------------------- |
407 | -- IP addr | 358 | -- IP addr |
408 | -----------------------------------------------------------------------} | 359 | -----------------------------------------------------------------------} |
@@ -457,51 +408,6 @@ instance BEncode IPv6 where | |||
457 | {-# INLINE fromBEncode #-} | 408 | {-# INLINE fromBEncode #-} |
458 | #endif | 409 | #endif |
459 | 410 | ||
460 | -- | When 'get'ing an IP it must be 'isolate'd to the appropriate | ||
461 | -- number of bytes since we have no other way of telling which | ||
462 | -- address type we are trying to parse | ||
463 | instance Serialize IP where | ||
464 | put (IPv4 ip) = put ip | ||
465 | put (IPv6 ip) = put ip | ||
466 | |||
467 | get = do | ||
468 | n <- remaining | ||
469 | case n of | ||
470 | 4 -> IPv4 <$> get | ||
471 | 16 -> IPv6 <$> get | ||
472 | _ -> fail (show n ++ " is the wrong number of remaining bytes to parse IP") | ||
473 | |||
474 | instance Serialize IPv4 where | ||
475 | put = putWord32host . toHostAddress | ||
476 | get = fromHostAddress <$> getWord32host | ||
477 | |||
478 | instance Serialize IPv6 where | ||
479 | put ip = put $ toHostAddress6 ip | ||
480 | get = fromHostAddress6 <$> get | ||
481 | |||
482 | instance Pretty IPv4 where | ||
483 | pPrint = PP.text . show | ||
484 | {-# INLINE pPrint #-} | ||
485 | |||
486 | instance Pretty IPv6 where | ||
487 | pPrint = PP.text . show | ||
488 | {-# INLINE pPrint #-} | ||
489 | |||
490 | instance Pretty IP where | ||
491 | pPrint = PP.text . show | ||
492 | {-# INLINE pPrint #-} | ||
493 | |||
494 | instance Hashable IPv4 where | ||
495 | hashWithSalt = hashUsing toHostAddress | ||
496 | {-# INLINE hashWithSalt #-} | ||
497 | |||
498 | instance Hashable IPv6 where | ||
499 | hashWithSalt s a = hashWithSalt s (toHostAddress6 a) | ||
500 | |||
501 | instance Hashable IP where | ||
502 | hashWithSalt s (IPv4 h) = hashWithSalt s h | ||
503 | hashWithSalt s (IPv6 h) = hashWithSalt s h | ||
504 | |||
505 | {----------------------------------------------------------------------- | 411 | {----------------------------------------------------------------------- |
506 | -- Peer addr | 412 | -- Peer addr |
507 | -----------------------------------------------------------------------} | 413 | -----------------------------------------------------------------------} |
@@ -666,13 +572,6 @@ testIdBit :: FiniteBits bs => bs -> Word -> Bool | |||
666 | testIdBit bs i = testBit bs (fromIntegral (finiteBitSize bs - fromIntegral i)) | 572 | testIdBit bs i = testBit bs (fromIntegral (finiteBitSize bs - fromIntegral i)) |
667 | {-# INLINE testIdBit #-} | 573 | {-# INLINE testIdBit #-} |
668 | 574 | ||
669 | -- TODO WARN is the 'system' random suitable for this? | ||
670 | -- | Generate random NodeID used for the entire session. | ||
671 | -- Distribution of ID's should be as uniform as possible. | ||
672 | -- | ||
673 | genNodeId :: IO NodeId | ||
674 | genNodeId = NodeId . either error id . S.decode <$> getEntropy nodeIdSize | ||
675 | |||
676 | ------------------------------------------------------------------------ | 575 | ------------------------------------------------------------------------ |
677 | 576 | ||
678 | -- | Accepts a depth/index of a bucket and whether or not it is the last one, | 577 | -- | Accepts a depth/index of a bucket and whether or not it is the last one, |
@@ -693,54 +592,8 @@ bucketRange depth is_last = (q,m,b) | |||
693 | m = 2^(7-r) - 1 | 592 | m = 2^(7-r) - 1 |
694 | b = if is_last then 0 else 2^(7-r) | 593 | b = if is_last then 0 else 2^(7-r) |
695 | 594 | ||
696 | -- | Generate a random 'NodeId' within a range suitable for a bucket. To | ||
697 | -- obtain a sample for bucket number /index/ where /is_last/ indicates if this | ||
698 | -- is for the current deepest bucket in our routing table: | ||
699 | -- | ||
700 | -- > sample <- genBucketSample nid (bucketRange index is_last) | ||
701 | genBucketSample :: NodeId -> (Int,Word8,Word8) -> IO NodeId | ||
702 | genBucketSample n qmb = genBucketSample' getEntropy n qmb | ||
703 | |||
704 | -- | Generalizion of 'genBucketSample' that accepts a byte generator | ||
705 | -- function to use instead of the system entropy. | ||
706 | genBucketSample' :: Applicative m => | ||
707 | (Int -> m ByteString) -> NodeId -> (Int,Word8,Word8) -> m NodeId | ||
708 | genBucketSample' gen (NodeId self) (q,m,b) | ||
709 | | q <= 0 = NodeId . either error id . S.decode <$> gen nodeIdSize | ||
710 | | q >= nodeIdSize = pure (NodeId self) | ||
711 | | otherwise = NodeId . either error id . S.decode . build <$> gen (nodeIdSize - q + 1) | ||
712 | where | ||
713 | build tl = BS.init hd <> BS.cons (h .|. t) (BS.tail tl) | ||
714 | where | ||
715 | hd = BS.take q $ S.encode self | ||
716 | h = xor b (complement m .&. BS.last hd) | ||
717 | t = m .&. BS.head tl | ||
718 | |||
719 | ------------------------------------------------------------------------ | 595 | ------------------------------------------------------------------------ |
720 | 596 | ||
721 | data NodeAddr a = NodeAddr | ||
722 | { nodeHost :: !a | ||
723 | , nodePort :: {-# UNPACK #-} !PortNumber | ||
724 | } deriving (Eq, Ord, Typeable, Functor, Foldable, Traversable) | ||
725 | |||
726 | instance Show a => Show (NodeAddr a) where | ||
727 | showsPrec i NodeAddr {..} | ||
728 | = showsPrec i nodeHost <> showString ":" <> showsPrec i nodePort | ||
729 | |||
730 | instance Read (NodeAddr IPv4) where | ||
731 | readsPrec i x = [ (fromPeerAddr a, s) | (a, s) <- readsPrec i x ] | ||
732 | |||
733 | -- | @127.0.0.1:6882@ | ||
734 | instance Default (NodeAddr IPv4) where | ||
735 | def = "127.0.0.1:6882" | ||
736 | |||
737 | -- | KRPC compatible encoding. | ||
738 | instance Serialize a => Serialize (NodeAddr a) where | ||
739 | get = NodeAddr <$> get <*> get | ||
740 | {-# INLINE get #-} | ||
741 | put NodeAddr {..} = put nodeHost >> put nodePort | ||
742 | {-# INLINE put #-} | ||
743 | |||
744 | #ifdef VERSION_bencoding | 597 | #ifdef VERSION_bencoding |
745 | -- | Torrent file compatible encoding. | 598 | -- | Torrent file compatible encoding. |
746 | instance BEncode a => BEncode (NodeAddr a) where | 599 | instance BEncode a => BEncode (NodeAddr a) where |
@@ -750,20 +603,6 @@ instance BEncode a => BEncode (NodeAddr a) where | |||
750 | {-# INLINE fromBEncode #-} | 603 | {-# INLINE fromBEncode #-} |
751 | #endif | 604 | #endif |
752 | 605 | ||
753 | instance Hashable a => Hashable (NodeAddr a) where | ||
754 | hashWithSalt s NodeAddr {..} = hashWithSalt s (nodeHost, nodePort) | ||
755 | {-# INLINE hashWithSalt #-} | ||
756 | |||
757 | instance Pretty ip => Pretty (NodeAddr ip) where | ||
758 | pPrint NodeAddr {..} = pPrint nodeHost <> ":" <> pPrint nodePort | ||
759 | |||
760 | -- | Example: | ||
761 | -- | ||
762 | -- @nodePort \"127.0.0.1:6881\" == 6881@ | ||
763 | -- | ||
764 | instance IsString (NodeAddr IPv4) where | ||
765 | fromString = fromPeerAddr . fromString | ||
766 | |||
767 | fromPeerAddr :: PeerAddr a -> NodeAddr a | 606 | fromPeerAddr :: PeerAddr a -> NodeAddr a |
768 | fromPeerAddr PeerAddr {..} = NodeAddr | 607 | fromPeerAddr PeerAddr {..} = NodeAddr |
769 | { nodeHost = peerHost | 608 | { nodeHost = peerHost |
@@ -772,45 +611,10 @@ fromPeerAddr PeerAddr {..} = NodeAddr | |||
772 | 611 | ||
773 | ------------------------------------------------------------------------ | 612 | ------------------------------------------------------------------------ |
774 | 613 | ||
775 | data NodeInfo dht addr u = NodeInfo | ||
776 | { nodeId :: !(RPC.NodeId dht) | ||
777 | , nodeAddr :: !(NodeAddr addr) | ||
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) | ||
784 | |||
785 | mapAddress :: (addr -> b) -> NodeInfo dht addr u -> NodeInfo dht b u | ||
786 | mapAddress f ni = ni { nodeAddr = fmap f (nodeAddr ni) } | ||
787 | |||
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 | ||
796 | compare = comparing nodeId | ||
797 | |||
798 | -- | KRPC 'compact list' compatible encoding: contact information for | ||
799 | -- nodes is encoded as a 26-byte string. Also known as "Compact node | ||
800 | -- info" the 20-byte Node ID in network byte order has the compact | ||
801 | -- IP-address/port info concatenated to the end. | ||
802 | instance Serialize a => Serialize (NodeInfo KMessageOf a ()) where | ||
803 | get = (\a b -> NodeInfo a b ()) <$> get <*> get | ||
804 | put NodeInfo {..} = put nodeId >> put nodeAddr | ||
805 | |||
806 | instance Pretty ip => Pretty (NodeInfo KMessageOf ip ()) where | ||
807 | pPrint NodeInfo {..} = pPrint nodeId <> "@(" <> pPrint nodeAddr <> ")" | ||
808 | |||
809 | instance Pretty ip => Pretty [NodeInfo KMessageOf ip ()] where | ||
810 | pPrint = PP.vcat . PP.punctuate "," . L.map pPrint | ||
811 | |||
812 | -- | Order by closeness: nearest nodes first. | 614 | -- | Order by closeness: nearest nodes first. |
813 | rank :: (x -> NodeId) -> NodeId -> [x] -> [x] | 615 | rank :: ( Ord (NodeId dht) |
616 | , Bits (NodeId dht) | ||
617 | ) => (x -> NodeId dht) -> NodeId dht -> [x] -> [x] | ||
814 | rank f nid = L.sortBy (comparing (RPC.distance nid . f)) | 618 | rank f nid = L.sortBy (comparing (RPC.distance nid . f)) |
815 | 619 | ||
816 | {----------------------------------------------------------------------- | 620 | {----------------------------------------------------------------------- |
@@ -1219,40 +1023,6 @@ fingerprint pid = either (const def) id $ runGet getCI (getPeerId pid) | |||
1219 | return $ Version (catMaybes $ L.map decodeShadowVerNr str) [] | 1023 | return $ Version (catMaybes $ L.map decodeShadowVerNr str) [] |
1220 | 1024 | ||
1221 | 1025 | ||
1222 | -- | Yields all 8 DHT neighborhoods available to you given a particular ip | ||
1223 | -- address. | ||
1224 | bep42s :: Address a => a -> NodeId -> [NodeId] | ||
1225 | bep42s addr (NodeId r) = mapMaybe (bep42 addr) rs | ||
1226 | where | ||
1227 | rs = L.map (NodeId . change3bits r) [0..7] | ||
1228 | |||
1229 | -- change3bits :: ByteString -> Word8 -> ByteString | ||
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 | ||
1234 | |||
1235 | -- | Modifies a purely random 'NodeId' to one that is related to a given | ||
1236 | -- routable address in accordance with BEP 42. | ||
1237 | bep42 :: Address a => a -> NodeId -> Maybe NodeId | ||
1238 | bep42 addr (NodeId r) | ||
1239 | | Just ip <- fmap S.encode (fromAddr addr :: Maybe IPv4) | ||
1240 | <|> fmap S.encode (fromAddr addr :: Maybe IPv6) | ||
1241 | = genBucketSample' retr (NodeId $ crc $ applyMask ip) (3,0x07,0) | ||
1242 | | otherwise | ||
1243 | = Nothing | ||
1244 | where | ||
1245 | ip4mask = "\x03\x0f\x3f\xff" :: ByteString | ||
1246 | ip6mask = "\x01\x03\x07\x0f\x1f\x3f\x7f\xff" :: ByteString | ||
1247 | nbhood_select = (fromIntegral r :: Word8) .&. 7 | ||
1248 | retr n = pure $ BS.drop (nodeIdSize - n) $ S.encode r | ||
1249 | crc = flip shiftL (finiteBitSize (NodeId undefined) - 32) . fromIntegral . crc32c . BS.pack | ||
1250 | applyMask ip = case BS.zipWith (.&.) msk ip of | ||
1251 | (b:bs) -> (b .|. shiftL nbhood_select 5) : bs | ||
1252 | bs -> bs | ||
1253 | where msk | BS.length ip == 4 = ip4mask | ||
1254 | | otherwise = ip6mask | ||
1255 | |||
1256 | 1026 | ||
1257 | -- | Given a string specifying a port (numeric or service name) | 1027 | -- | Given a string specifying a port (numeric or service name) |
1258 | -- and a flag indicating whether you want to support IPv6, this | 1028 | -- and a flag indicating whether you want to support IPv6, this |