summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-12-08 06:10:22 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-12-08 06:10:22 +0400
commit6e5d3aacb36d7b6353a5a2b397fa237193f1e1e7 (patch)
tree63993ec100b809d854ed11388311d548e1d8256b /src
parent04104b0615ed13f823a85913f48fece29afb0c84 (diff)
Serialization for metadata exchange extension
Diffstat (limited to 'src')
-rw-r--r--src/Network/BitTorrent/Exchange/Message.hs89
1 files changed, 66 insertions, 23 deletions
diff --git a/src/Network/BitTorrent/Exchange/Message.hs b/src/Network/BitTorrent/Exchange/Message.hs
index 8659eb89..4d1694c6 100644
--- a/src/Network/BitTorrent/Exchange/Message.hs
+++ b/src/Network/BitTorrent/Exchange/Message.hs
@@ -78,9 +78,10 @@ module Network.BitTorrent.Exchange.Message
78 78
79import Control.Applicative 79import Control.Applicative
80import Control.Arrow ((&&&), (***)) 80import Control.Arrow ((&&&), (***))
81import Data.Attoparsec.ByteString.Char8 as BS
81import Data.BEncode as BE 82import Data.BEncode as BE
82import Data.BEncode.BDict as BE 83import Data.BEncode.BDict as BE
83import Data.BEncode.Internal (ppBEncode) 84import Data.BEncode.Internal as BE (ppBEncode, parser)
84import Data.BEncode.Types (BDict) 85import Data.BEncode.Types (BDict)
85import Data.Bits 86import Data.Bits
86import Data.ByteString as BS 87import Data.ByteString as BS
@@ -99,11 +100,12 @@ import Data.Typeable
99import Data.Word 100import Data.Word
100import Network 101import Network
101import Network.Socket hiding (KeepAlive) 102import Network.Socket hiding (KeepAlive)
102import Text.PrettyPrint as PP 103import Text.PrettyPrint as PP hiding ((<>))
103import Text.PrettyPrint.Class 104import Text.PrettyPrint.Class
104 105
105import Data.Torrent.Bitfield 106import Data.Torrent.Bitfield
106import Data.Torrent.InfoHash 107import Data.Torrent.InfoHash
108import qualified Data.Torrent.Piece as Data
107import Network.BitTorrent.Core 109import Network.BitTorrent.Core
108import Network.BitTorrent.Exchange.Block 110import Network.BitTorrent.Exchange.Block
109 111
@@ -556,8 +558,8 @@ data ExtendedHandshake = ExtendedHandshake
556-- , yourip :: Maybe (Either HostAddress HostAddress6) 558-- , yourip :: Maybe (Either HostAddress HostAddress6)
557 } deriving (Show, Eq, Typeable) 559 } deriving (Show, Eq, Typeable)
558 560
559extendedHandshakeId :: ExtendedMessageId 561extHandshakeId :: ExtendedMessageId
560extendedHandshakeId = 0 562extHandshakeId = 0
561 563
562-- | Default 'Request' queue size. 564-- | Default 'Request' queue size.
563defaultQueueLength :: Int 565defaultQueueLength :: Int
@@ -626,9 +628,12 @@ data ExtendedMetadata
626 -- is either a 'MetadataReject' or a 'MetadataData' message. 628 -- is either a 'MetadataReject' or a 'MetadataData' message.
627 = MetadataRequest PieceIx 629 = MetadataRequest PieceIx
628 630
631 -- | If sender requested a valid 'PieceIx' and receiver have the
632 -- corresponding piece then receiver should respond with this
633 -- message.
629 | MetadataData 634 | MetadataData
630 { -- | FIXME add piece data 635 { -- | A piece of 'Data.Torrent.InfoDict'.
631 piece :: PieceIx 636 piece :: Data.Piece BS.ByteString
632 637
633 -- | This key has the same semantics as the 'ehsMetadataSize' in 638 -- | This key has the same semantics as the 'ehsMetadataSize' in
634 -- the 'ExtendedHandshake' — it is size of the torrent info 639 -- the 'ExtendedHandshake' — it is size of the torrent info
@@ -663,7 +668,7 @@ instance BEncode ExtendedMetadata where
663 msg_type_key .=! (0 :: MetadataId) 668 msg_type_key .=! (0 :: MetadataId)
664 .: piece_key .=! pix 669 .: piece_key .=! pix
665 .: endDict 670 .: endDict
666 toBEncode (MetadataData pix totalSize) = toDict $ 671 toBEncode (MetadataData (Data.Piece pix _) totalSize) = toDict $
667 msg_type_key .=! (1 :: MetadataId) 672 msg_type_key .=! (1 :: MetadataId)
668 .: piece_key .=! pix 673 .: piece_key .=! pix
669 .: total_size_key .=! totalSize 674 .: total_size_key .=! totalSize
@@ -678,15 +683,17 @@ instance BEncode ExtendedMetadata where
678 mid <- field $ req msg_type_key 683 mid <- field $ req msg_type_key
679 case mid :: MetadataId of 684 case mid :: MetadataId of
680 0 -> MetadataRequest <$>! piece_key 685 0 -> MetadataRequest <$>! piece_key
681 1 -> MetadataData <$>! piece_key <*>! total_size_key 686 1 -> metadataData <$>! piece_key <*>! total_size_key
682 2 -> MetadataReject <$>! piece_key 687 2 -> MetadataReject <$>! piece_key
683 _ -> pure (MetadataUnknown bval) 688 _ -> pure (MetadataUnknown bval)
689 where
690 metadataData pix s = MetadataData (Data.Piece pix BS.empty) s
684 691
685-- | Piece data bytes are omitted. 692-- | Piece data bytes are omitted.
686instance Pretty ExtendedMetadata where 693instance Pretty ExtendedMetadata where
687 pretty (MetadataRequest pix ) = "Request" <+> PP.int pix 694 pretty (MetadataRequest pix ) = "Request" <+> PP.int pix
688 pretty (MetadataData pix s) = "Data" <+> PP.int pix <+> PP.int s 695 pretty (MetadataData pi t) = "Data" <+> pretty pi <+> PP.int t
689 pretty (MetadataReject pix ) = "Reject" <+> PP.int pix 696 pretty (MetadataReject pix ) = "Reject" <+> PP.int pix
690 pretty (MetadataUnknown bval ) = "Unknown" <+> ppBEncode bval 697 pretty (MetadataUnknown bval ) = "Unknown" <+> ppBEncode bval
691 698
692instance PeerMessage ExtendedMetadata where 699instance PeerMessage ExtendedMetadata where
@@ -699,6 +706,43 @@ instance PeerMessage ExtendedMetadata where
699metadataPieceSize :: Int 706metadataPieceSize :: Int
700metadataPieceSize = 16 * 1024 707metadataPieceSize = 16 * 1024
701 708
709-- TODO we can check if the piece payload bytestring have appropriate
710-- length; otherwise serialization MUST fail.
711isLastMetadata :: ExtendedMetadata -> Bool
712isLastMetadata = undefined -- FIXME
713
714checkPiece :: ExtendedMetadata -> Bool
715checkPiece = undefined -- FIXME
716
717setMetadataPayload :: BS.ByteString -> ExtendedMetadata -> ExtendedMetadata
718setMetadataPayload bs (MetadataData (Data.Piece pix _) t) =
719 MetadataData (Data.Piece pix bs) t
720setMetadataPayload _ msg = msg
721
722getMetadataPayload :: ExtendedMetadata -> Maybe BS.ByteString
723getMetadataPayload (MetadataData (Data.Piece _ bs) _) = Just bs
724getMetadataPayload _ = Nothing
725
726-- to make MetadataData constructor fields a little bit prettier we
727-- cheat here: first we read empty 'pieceData' from bdict, but then we
728-- fill that field with the actual piece data — trailing bytes of
729-- the message
730getMetadata :: Int -> S.Get ExtendedMetadata
731getMetadata len = do
732 bs <- getByteString len
733 case BS.parse BE.parser bs of
734 BS.Fail _ _ _ -> fail "unable to parse metadata bdict: possible corrupted"
735 BS.Partial c -> fail "unable to parse metadata bdict: not enough bytes"
736 BS.Done piece bvalueBS -> do
737 let msg = "metadata dictionary is invalid"
738 metadata <- either (fail msg) pure $ fromBEncode bvalueBS
739 pure $ setMetadataPayload piece metadata
740
741putMetadata :: ExtendedMetadata -> BL.ByteString
742putMetadata msg
743 | Just bs <- getMetadataPayload msg = BE.encode msg <> BL.fromStrict bs
744 | otherwise = BE.encode msg
745
702{----------------------------------------------------------------------- 746{-----------------------------------------------------------------------
703-- Extension protocol messages 747-- Extension protocol messages
704-----------------------------------------------------------------------} 748-----------------------------------------------------------------------}
@@ -900,23 +944,22 @@ getExtendedMessage messageSize = do
900 let msgBodySize = messageSize - 1 944 let msgBodySize = messageSize - 1
901 case msgId of 945 case msgId of
902 0 -> EHandshake <$> getExtendedHandshake msgBodySize 946 0 -> EHandshake <$> getExtendedHandshake msgBodySize
903 1 -> EMetadata msgId <$> undefined 947 1 -> EMetadata msgId <$> getMetadata msgBodySize
904 _ -> EUnknown msgId <$> getByteString msgBodySize 948 _ -> EUnknown msgId <$> getByteString msgBodySize
905 949
906extendedMessageId :: MessageId 950extendedMessageId :: MessageId
907extendedMessageId = 20 951extendedMessageId = 20
908 952
953putExt :: ExtendedMessageId -> BL.ByteString -> Put
954putExt mid lbs = do
955 putWord32be $ fromIntegral (1 + 1 + BL.length lbs)
956 putWord8 extendedMessageId
957 putWord8 mid
958 putLazyByteString lbs
959
909-- NOTE: in contrast to getExtendedMessage this function put length 960-- NOTE: in contrast to getExtendedMessage this function put length
910-- and message id too! 961-- and message id too!
911putExtendedMessage :: Putter ExtendedMessage 962putExtendedMessage :: Putter ExtendedMessage
912putExtendedMessage (EHandshake hs) = do 963putExtendedMessage (EHandshake hs) = putExt extHandshakeId (BE.encode hs)
913 putExtendedMessage $ EUnknown extendedHandshakeId 964putExtendedMessage (EMetadata mid msg) = putExt mid (BE.encode msg)
914 $ BL.toStrict $ BE.encode hs 965putExtendedMessage (EUnknown mid bs) = putExt mid (BL.fromStrict bs)
915putExtendedMessage (EMetadata mid msg) = do
916 putExtendedMessage $ EUnknown mid
917 $ BL.toStrict $ BE.encode msg
918putExtendedMessage (EUnknown mid bs) = do
919 putWord32be $ fromIntegral (1 + 1 + BS.length bs)
920 putWord8 extendedMessageId
921 putWord8 mid
922 putByteString bs