summaryrefslogtreecommitdiff
path: root/bittorrent/src/Network/BitTorrent/Exchange/Message.hs
diff options
context:
space:
mode:
Diffstat (limited to 'bittorrent/src/Network/BitTorrent/Exchange/Message.hs')
-rw-r--r--bittorrent/src/Network/BitTorrent/Exchange/Message.hs1232
1 files changed, 1232 insertions, 0 deletions
diff --git a/bittorrent/src/Network/BitTorrent/Exchange/Message.hs b/bittorrent/src/Network/BitTorrent/Exchange/Message.hs
new file mode 100644
index 00000000..2c6770f7
--- /dev/null
+++ b/bittorrent/src/Network/BitTorrent/Exchange/Message.hs
@@ -0,0 +1,1232 @@
1-- |
2-- Copyright : (c) Sam Truzjan 2013
3-- License : BSD3
4-- Maintainer : pxqr.sta@gmail.com
5-- Stability : experimental
6-- Portability : portable
7--
8-- Normally peer to peer communication consisting of the following
9-- steps:
10--
11-- * In order to establish the connection between peers we should
12-- send 'Handshake' message. The 'Handshake' is a required message
13-- and must be the first message transmitted by the peer to the
14-- another peer. Another peer should reply with a handshake as well.
15--
16-- * Next peer might sent bitfield message, but might not. In the
17-- former case we should update bitfield peer have. Again, if we
18-- have some pieces we should send bitfield. Normally bitfield
19-- message should sent after the handshake message.
20--
21-- * Regular exchange messages. TODO docs
22--
23-- For more high level API see "Network.BitTorrent.Exchange" module.
24--
25-- For more infomation see:
26-- <https://wiki.theory.org/BitTorrentSpecification#Peer_wire_protocol_.28TCP.29>
27--
28{-# LANGUAGE ViewPatterns #-}
29{-# LANGUAGE FlexibleInstances #-}
30{-# LANGUAGE FlexibleContexts #-}
31{-# LANGUAGE TypeFamilies #-}
32{-# LANGUAGE GeneralizedNewtypeDeriving #-}
33{-# LANGUAGE DeriveDataTypeable #-}
34{-# LANGUAGE TemplateHaskell #-}
35{-# OPTIONS -fno-warn-orphans #-}
36module Network.BitTorrent.Exchange.Message
37 ( -- * Capabilities
38 Capabilities (..)
39 , Extension (..)
40 , Caps
41
42 -- * Handshake
43 , ProtocolName
44 , Handshake(..)
45 , defaultHandshake
46 , handshakeSize
47 , handshakeMaxSize
48 , handshakeStats
49
50 -- * Stats
51 , ByteCount
52 , ByteStats (..)
53 , byteLength
54
55 -- * Messages
56 , Message (..)
57 , defaultKeepAliveTimeout
58 , defaultKeepAliveInterval
59 , PeerMessage (..)
60
61 -- ** Core messages
62 , StatusUpdate (..)
63 , Available (..)
64 , Transfer (..)
65 , defaultRequestQueueLength
66
67 -- ** Fast extension
68 , FastMessage (..)
69
70 -- ** Extension protocol
71 , ExtendedMessage (..)
72
73 -- *** Capabilities
74 , ExtendedExtension (..)
75 , ExtendedCaps (..)
76
77 -- *** Handshake
78 , ExtendedHandshake (..)
79 , defaultQueueLength
80 , nullExtendedHandshake
81
82 -- *** Metadata
83 , ExtendedMetadata (..)
84 , metadataPieceSize
85 , defaultMetadataFactor
86 , defaultMaxInfoDictSize
87 , isLastPiece
88 , isValidPiece
89 ) where
90
91import Control.Applicative
92import Control.Arrow ((&&&), (***))
93import Control.Monad (when)
94import Data.Attoparsec.ByteString.Char8 as BS
95import Data.BEncode as BE
96import Data.BEncode.BDict as BE
97import Data.BEncode.Internal as BE (ppBEncode, parser)
98import Data.BEncode.Types (BDict)
99import Data.Bits
100import Data.ByteString as BS
101import Data.ByteString.Char8 as BC
102import Data.ByteString.Lazy as BL
103import Data.Default
104import Data.List as L
105import Data.Map.Strict as M
106import Data.Maybe
107import Data.Monoid
108import Data.Ord
109import Data.Serialize as S
110import Data.String
111import Data.Text as T
112import Data.Typeable
113import Data.Word
114import Data.IP
115import Network
116import Network.Socket hiding (KeepAlive)
117import Text.PrettyPrint as PP hiding ((<>))
118import Text.PrettyPrint.HughesPJClass hiding ((<>),($$))
119
120import Data.Torrent hiding (Piece (..))
121import qualified Data.Torrent as P (Piece (..))
122import Network.Address
123import Network.BitTorrent.Exchange.Bitfield
124import Network.BitTorrent.Exchange.Block
125
126{-----------------------------------------------------------------------
127-- Capabilities
128-----------------------------------------------------------------------}
129
130-- |
131class Capabilities caps where
132 type Ext caps :: *
133
134 -- | Pack extensions to caps.
135 toCaps :: [Ext caps] -> caps
136
137 -- | Unpack extensions from caps.
138 fromCaps :: caps -> [Ext caps]
139
140 -- | Check if an extension is a member of the specified set.
141 allowed :: Ext caps -> caps -> Bool
142
143ppCaps :: Capabilities caps => Pretty (Ext caps) => caps -> Doc
144ppCaps = hcat . punctuate ", " . L.map pPrint . fromCaps
145
146{-----------------------------------------------------------------------
147-- Extensions
148-----------------------------------------------------------------------}
149
150-- | Enumeration of message extension protocols.
151--
152-- For more info see: <http://www.bittorrent.org/beps/bep_0004.html>
153--
154data Extension
155 = ExtDHT -- ^ BEP 5: allow to send PORT messages.
156 | ExtFast -- ^ BEP 6: allow to send FAST messages.
157 | ExtExtended -- ^ BEP 10: allow to send the extension protocol messages.
158 deriving (Show, Eq, Ord, Enum, Bounded)
159
160-- | Full extension names, suitable for logging.
161instance Pretty Extension where
162 pPrint ExtDHT = "Distributed Hash Table Protocol"
163 pPrint ExtFast = "Fast Extension"
164 pPrint ExtExtended = "Extension Protocol"
165
166-- | Extension bitmask as specified by BEP 4.
167extMask :: Extension -> Word64
168extMask ExtDHT = 0x01
169extMask ExtFast = 0x04
170extMask ExtExtended = 0x100000
171
172{-----------------------------------------------------------------------
173-- Capabilities
174-----------------------------------------------------------------------}
175
176-- | Capabilities is a set of 'Extension's usually sent in 'Handshake'
177-- messages.
178newtype Caps = Caps Word64
179 deriving (Show, Eq)
180
181-- | Render set of extensions as comma separated list.
182instance Pretty Caps where
183 pPrint = ppCaps
184 {-# INLINE pPrint #-}
185
186-- | The empty set.
187instance Default Caps where
188 def = Caps 0
189 {-# INLINE def #-}
190
191-- | Monoid under intersection. 'mempty' includes all known extensions.
192instance Monoid Caps where
193 mempty = toCaps [minBound .. maxBound]
194 {-# INLINE mempty #-}
195
196 mappend (Caps a) (Caps b) = Caps (a .&. b)
197 {-# INLINE mappend #-}
198
199-- | 'Handshake' compatible encoding.
200instance Serialize Caps where
201 put (Caps caps) = S.putWord64be caps
202 {-# INLINE put #-}
203
204 get = Caps <$> S.getWord64be
205 {-# INLINE get #-}
206
207instance Capabilities Caps where
208 type Ext Caps = Extension
209
210 allowed e (Caps caps) = (extMask e .&. caps) /= 0
211 {-# INLINE allowed #-}
212
213 toCaps = Caps . L.foldr (.|.) 0 . L.map extMask
214 fromCaps caps = L.filter (`allowed` caps) [minBound..maxBound]
215
216{-----------------------------------------------------------------------
217 Handshake
218-----------------------------------------------------------------------}
219
220maxProtocolNameSize :: Word8
221maxProtocolNameSize = maxBound
222
223-- | The protocol name is used to identify to the local peer which
224-- version of BTP the remote peer uses.
225newtype ProtocolName = ProtocolName BS.ByteString
226 deriving (Eq, Ord, Typeable)
227
228-- | In BTP/1.0 the name is 'BitTorrent protocol'. If this string is
229-- different from the local peers own protocol name, then the
230-- connection is to be dropped.
231instance Default ProtocolName where
232 def = ProtocolName "BitTorrent protocol"
233
234instance Show ProtocolName where
235 show (ProtocolName bs) = show bs
236
237instance Pretty ProtocolName where
238 pPrint (ProtocolName bs) = PP.text $ BC.unpack bs
239
240instance IsString ProtocolName where
241 fromString str
242 | L.length str <= fromIntegral maxProtocolNameSize
243 = ProtocolName (fromString str)
244 | otherwise = error $ "fromString: ProtocolName too long: " ++ str
245
246instance Serialize ProtocolName where
247 put (ProtocolName bs) = do
248 putWord8 $ fromIntegral $ BS.length bs
249 putByteString bs
250
251 get = do
252 len <- getWord8
253 bs <- getByteString $ fromIntegral len
254 return (ProtocolName bs)
255
256-- | Handshake message is used to exchange all information necessary
257-- to establish connection between peers.
258--
259data Handshake = Handshake {
260 -- | Identifier of the protocol. This is usually equal to 'def'.
261 hsProtocol :: ProtocolName
262
263 -- | Reserved bytes used to specify supported BEP's.
264 , hsReserved :: Caps
265
266 -- | Info hash of the info part of the metainfo file. that is
267 -- transmitted in tracker requests. Info hash of the initiator
268 -- handshake and response handshake should match, otherwise
269 -- initiator should break the connection.
270 --
271 , hsInfoHash :: InfoHash
272
273 -- | Peer id of the initiator. This is usually the same peer id
274 -- that is transmitted in tracker requests.
275 --
276 , hsPeerId :: PeerId
277
278 } deriving (Show, Eq)
279
280instance Serialize Handshake where
281 put Handshake {..} = do
282 put hsProtocol
283 put hsReserved
284 put hsInfoHash
285 put hsPeerId
286 get = Handshake <$> get <*> get <*> get <*> get
287
288-- | Show handshake protocol string, caps and fingerprint.
289instance Pretty Handshake where
290 pPrint Handshake {..}
291 = pPrint hsProtocol $$
292 pPrint hsReserved $$
293 pPrint (fingerprint hsPeerId)
294
295-- | Get handshake message size in bytes from the length of protocol
296-- string.
297handshakeSize :: Word8 -> Int
298handshakeSize n = 1 + fromIntegral n + 8 + 20 + 20
299
300-- | Maximum size of handshake message in bytes.
301handshakeMaxSize :: Int
302handshakeMaxSize = handshakeSize maxProtocolNameSize
303
304-- | Handshake with default protocol string and reserved bitmask.
305defaultHandshake :: InfoHash -> PeerId -> Handshake
306defaultHandshake = Handshake def def
307
308handshakeStats :: Handshake -> ByteStats
309handshakeStats (Handshake (ProtocolName bs) _ _ _)
310 = ByteStats 1 (BS.length bs + 8 + 20 + 20) 0
311
312{-----------------------------------------------------------------------
313-- Stats
314-----------------------------------------------------------------------}
315
316-- | Number of bytes.
317type ByteCount = Int
318
319-- | Summary of encoded message byte layout can be used to collect
320-- stats about message flow in both directions. This data can be
321-- retrieved using 'stats' function.
322data ByteStats = ByteStats
323 { -- | Number of bytes used to help encode 'control' and 'payload'
324 -- bytes: message size, message ID's, etc
325 overhead :: {-# UNPACK #-} !ByteCount
326
327 -- | Number of bytes used to exchange peers state\/options: piece
328 -- and block indexes, infohash, port numbers, peer ID\/IP, etc.
329 , control :: {-# UNPACK #-} !ByteCount
330
331 -- | Number of payload bytes: torrent data blocks and infodict
332 -- metadata.
333 , payload :: {-# UNPACK #-} !ByteCount
334 } deriving Show
335
336instance Pretty ByteStats where
337 pPrint s @ ByteStats {..} = fsep
338 [ PP.int overhead, "overhead"
339 , PP.int control, "control"
340 , PP.int payload, "payload"
341 , "bytes"
342 ] $+$ fsep
343 [ PP.int (byteLength s), "total bytes"
344 ]
345
346-- | Empty byte sequences.
347instance Default ByteStats where
348 def = ByteStats 0 0 0
349
350-- | Monoid under addition.
351instance Monoid ByteStats where
352 mempty = def
353 mappend a b = ByteStats
354 { overhead = overhead a + overhead b
355 , control = control a + control b
356 , payload = payload a + payload b
357 }
358
359-- | Sum of the all byte sequences.
360byteLength :: ByteStats -> Int
361byteLength ByteStats {..} = overhead + control + payload
362
363{-----------------------------------------------------------------------
364-- Regular messages
365-----------------------------------------------------------------------}
366
367-- | Messages which can be sent after handshaking. Minimal complete
368-- definition: 'envelop'.
369class PeerMessage a where
370 -- | Construct a message to be /sent/. Note that if 'ExtendedCaps'
371 -- do not contain mapping for this message the default
372 -- 'ExtendedMessageId' is used.
373 envelop :: ExtendedCaps -- ^ The /receiver/ extended capabilities;
374 -> a -- ^ An regular message;
375 -> Message -- ^ Enveloped message to sent.
376
377 -- | Find out the extension this message belong to. Can be used to
378 -- check if this message is allowed to send\/recv in current
379 -- session.
380 requires :: a -> Maybe Extension
381 requires _ = Nothing
382
383 -- | Get sizes of overhead\/control\/payload byte sequences of
384 -- binary message representation without encoding message to binary
385 -- bytestring.
386 --
387 -- This function should obey one law:
388 --
389 -- * 'byteLength' ('stats' msg) == 'BL.length' ('encode' msg)
390 --
391 stats :: a -> ByteStats
392 stats _ = ByteStats 4 0 0
393
394{-----------------------------------------------------------------------
395-- Status messages
396-----------------------------------------------------------------------}
397
398-- | Notification that the sender have updated its
399-- 'Network.BitTorrent.Exchange.Status.PeerStatus'.
400data StatusUpdate
401 -- | Notification that the sender will not upload data to the
402 -- receiver until unchoking happen.
403 = Choking !Bool
404
405 -- | Notification that the sender is interested (or not interested)
406 -- in any of the receiver's data pieces.
407 | Interested !Bool
408 deriving (Show, Eq, Ord, Typeable)
409
410instance Pretty StatusUpdate where
411 pPrint (Choking False) = "not choking"
412 pPrint (Choking True ) = "choking"
413 pPrint (Interested False) = "not interested"
414 pPrint (Interested True ) = "interested"
415
416instance PeerMessage StatusUpdate where
417 envelop _ = Status
418 {-# INLINE envelop #-}
419
420 stats _ = ByteStats 4 1 0
421 {-# INLINE stats #-}
422
423{-----------------------------------------------------------------------
424-- Available messages
425-----------------------------------------------------------------------}
426
427-- | Messages used to inform receiver which pieces of the torrent
428-- sender have.
429data Available =
430 -- | Zero-based index of a piece that has just been successfully
431 -- downloaded and verified via the hash.
432 Have ! PieceIx
433
434 -- | The bitfield message may only be sent immediately after the
435 -- handshaking sequence is complete, and before any other message
436 -- are sent. If client have no pieces then bitfield need not to be
437 -- sent.
438 | Bitfield !Bitfield
439 deriving (Show, Eq)
440
441instance Pretty Available where
442 pPrint (Have ix ) = "Have" <+> int ix
443 pPrint (Bitfield _ ) = "Bitfield"
444
445instance PeerMessage Available where
446 envelop _ = Available
447 {-# INLINE envelop #-}
448
449 stats (Have _) = ByteStats (4 + 1) 4 0
450 stats (Bitfield bf) = ByteStats (4 + 1) (q + trailing) 0
451 where
452 trailing = if r == 0 then 0 else 1
453 (q, r) = quotRem (totalCount bf) 8
454
455{-----------------------------------------------------------------------
456-- Transfer messages
457-----------------------------------------------------------------------}
458
459-- | Messages used to transfer 'Block's.
460data Transfer
461 -- | Request for a particular block. If a client is requested a
462 -- block that another peer do not have the peer might not answer
463 -- at all.
464 = Request ! BlockIx
465
466 -- | Response to a request for a block.
467 | Piece !(Block BL.ByteString)
468
469 -- | Used to cancel block requests. It is typically used during
470 -- "End Game".
471 | Cancel !BlockIx
472 deriving (Show, Eq)
473
474instance Pretty Transfer where
475 pPrint (Request ix ) = "Request" <+> pPrint ix
476 pPrint (Piece blk) = "Piece" <+> pPrint blk
477 pPrint (Cancel i ) = "Cancel" <+> pPrint i
478
479instance PeerMessage Transfer where
480 envelop _ = Transfer
481 {-# INLINE envelop #-}
482
483 stats (Request _ ) = ByteStats (4 + 1) (3 * 4) 0
484 stats (Piece p ) = ByteStats (4 + 1) (4 + 4 + blockSize p) 0
485 stats (Cancel _ ) = ByteStats (4 + 1) (3 * 4) 0
486
487-- TODO increase
488-- | Max number of pending 'Request's inflight.
489defaultRequestQueueLength :: Int
490defaultRequestQueueLength = 1
491
492{-----------------------------------------------------------------------
493-- Fast messages
494-----------------------------------------------------------------------}
495
496-- | BEP6 messages.
497data FastMessage =
498 -- | If a peer have all pieces it might send the 'HaveAll' message
499 -- instead of 'Bitfield' message. Used to save bandwidth.
500 HaveAll
501
502 -- | If a peer have no pieces it might send 'HaveNone' message
503 -- intead of 'Bitfield' message. Used to save bandwidth.
504 | HaveNone
505
506 -- | This is an advisory message meaning "you might like to
507 -- download this piece." Used to avoid excessive disk seeks and
508 -- amount of IO.
509 | SuggestPiece !PieceIx
510
511 -- | Notifies a requesting peer that its request will not be
512 -- satisfied.
513 | RejectRequest !BlockIx
514
515 -- | This is an advisory messsage meaning \"if you ask for this
516 -- piece, I'll give it to you even if you're choked.\" Used to
517 -- shorten starting phase.
518 | AllowedFast !PieceIx
519 deriving (Show, Eq)
520
521instance Pretty FastMessage where
522 pPrint (HaveAll ) = "Have all"
523 pPrint (HaveNone ) = "Have none"
524 pPrint (SuggestPiece pix) = "Suggest" <+> int pix
525 pPrint (RejectRequest bix) = "Reject" <+> pPrint bix
526 pPrint (AllowedFast pix) = "Allowed fast" <+> int pix
527
528instance PeerMessage FastMessage where
529 envelop _ = Fast
530 {-# INLINE envelop #-}
531
532 requires _ = Just ExtFast
533 {-# INLINE requires #-}
534
535 stats HaveAll = ByteStats 4 1 0
536 stats HaveNone = ByteStats 4 1 0
537 stats (SuggestPiece _) = ByteStats 5 4 0
538 stats (RejectRequest _) = ByteStats 5 12 0
539 stats (AllowedFast _) = ByteStats 5 4 0
540
541{-----------------------------------------------------------------------
542-- Extension protocol
543-----------------------------------------------------------------------}
544
545{-----------------------------------------------------------------------
546-- Extended capabilities
547-----------------------------------------------------------------------}
548
549data ExtendedExtension
550 = ExtMetadata -- ^ BEP 9: Extension for Peers to Send Metadata Files
551 deriving (Show, Eq, Ord, Enum, Bounded, Typeable)
552
553instance IsString ExtendedExtension where
554 fromString = fromMaybe (error msg) . fromKey . fromString
555 where
556 msg = "fromString: could not parse ExtendedExtension"
557
558instance Pretty ExtendedExtension where
559 pPrint ExtMetadata = "Extension for Peers to Send Metadata Files"
560
561fromKey :: BKey -> Maybe ExtendedExtension
562fromKey "ut_metadata" = Just ExtMetadata
563fromKey _ = Nothing
564{-# INLINE fromKey #-}
565
566toKey :: ExtendedExtension -> BKey
567toKey ExtMetadata = "ut_metadata"
568{-# INLINE toKey #-}
569
570type ExtendedMessageId = Word8
571
572extId :: ExtendedExtension -> ExtendedMessageId
573extId ExtMetadata = 1
574{-# INLINE extId #-}
575
576type ExtendedMap = Map ExtendedExtension ExtendedMessageId
577
578-- | The extension IDs must be stored for every peer, because every
579-- peer may have different IDs for the same extension.
580--
581newtype ExtendedCaps = ExtendedCaps { extendedCaps :: ExtendedMap }
582 deriving (Show, Eq)
583
584instance Pretty ExtendedCaps where
585 pPrint = ppCaps
586 {-# INLINE pPrint #-}
587
588-- | The empty set.
589instance Default ExtendedCaps where
590 def = ExtendedCaps M.empty
591
592-- | Monoid under intersection:
593--
594-- * The 'mempty' caps includes all known extensions;
595--
596-- * the 'mappend' operation is NOT commutative: it return message
597-- id from the first caps for the extensions existing in both caps.
598--
599instance Monoid ExtendedCaps where
600 mempty = toCaps [minBound..maxBound]
601 mappend (ExtendedCaps a) (ExtendedCaps b) =
602 ExtendedCaps (M.intersection a b)
603
604appendBDict :: BDict -> ExtendedMap -> ExtendedMap
605appendBDict (Cons key val xs) caps
606 | Just ext <- fromKey key
607 , Right eid <- fromBEncode val = M.insert ext eid (appendBDict xs caps)
608 | otherwise = appendBDict xs caps
609appendBDict Nil caps = caps
610
611-- | Handshake compatible encoding.
612instance BEncode ExtendedCaps where
613 toBEncode = BDict . BE.fromAscList . L.sortBy (comparing fst)
614 . L.map (toKey *** toBEncode) . M.toList . extendedCaps
615
616 fromBEncode (BDict bd) = pure $ ExtendedCaps $ appendBDict bd M.empty
617 fromBEncode _ = decodingError "ExtendedCaps"
618
619instance Capabilities ExtendedCaps where
620 type Ext ExtendedCaps = ExtendedExtension
621
622 toCaps = ExtendedCaps . M.fromList . L.map (id &&& extId)
623
624 fromCaps = M.keys . extendedCaps
625 {-# INLINE fromCaps #-}
626
627 allowed e (ExtendedCaps caps) = M.member e caps
628 {-# INLINE allowed #-}
629
630remoteMessageId :: ExtendedExtension -> ExtendedCaps -> ExtendedMessageId
631remoteMessageId ext = fromMaybe (extId ext) . M.lookup ext . extendedCaps
632
633{-----------------------------------------------------------------------
634-- Extended handshake
635-----------------------------------------------------------------------}
636
637-- | This message should be sent immediately after the standard
638-- bittorrent handshake to any peer that supports this extension
639-- protocol. Extended handshakes can be sent more than once, however
640-- an implementation may choose to ignore subsequent handshake
641-- messages.
642--
643data ExtendedHandshake = ExtendedHandshake
644 { -- | If this peer has an IPv4 interface, this is the compact
645 -- representation of that address.
646 ehsIPv4 :: Maybe HostAddress
647
648 -- | If this peer has an IPv6 interface, this is the compact
649 -- representation of that address.
650 , ehsIPv6 :: Maybe HostAddress6
651
652 -- | Dictionary of supported extension messages which maps names
653 -- of extensions to an extended message ID for each extension
654 -- message.
655 , ehsCaps :: ExtendedCaps
656
657 -- | Size of 'Data.Torrent.InfoDict' in bytes. This field should
658 -- be added if 'ExtMetadata' is enabled in current session /and/
659 -- peer have the torrent file.
660 , ehsMetadataSize :: Maybe Int
661
662 -- | Local TCP /listen/ port. Allows each side to learn about the
663 -- TCP port number of the other side.
664 , ehsPort :: Maybe PortNumber
665
666 -- | Request queue the number of outstanding 'Request' messages
667 -- this client supports without dropping any.
668 , ehsQueueLength :: Maybe Int
669
670 -- | Client name and version.
671 , ehsVersion :: Maybe Text
672
673 -- | IP of the remote end
674 , ehsYourIp :: Maybe IP
675 } deriving (Show, Eq, Typeable)
676
677extHandshakeId :: ExtendedMessageId
678extHandshakeId = 0
679
680-- | Default 'Request' queue size.
681defaultQueueLength :: Int
682defaultQueueLength = 1
683
684-- | All fields are empty.
685instance Default ExtendedHandshake where
686 def = ExtendedHandshake def def def def def def def def
687
688instance Monoid ExtendedHandshake where
689 mempty = def { ehsCaps = mempty }
690 mappend old new = ExtendedHandshake {
691 ehsCaps = ehsCaps old <> ehsCaps new,
692 ehsIPv4 = ehsIPv4 old `mergeOld` ehsIPv4 new,
693 ehsIPv6 = ehsIPv6 old `mergeOld` ehsIPv6 new,
694 ehsMetadataSize = ehsMetadataSize old `mergeNew` ehsMetadataSize new,
695 ehsPort = ehsPort old `mergeOld` ehsPort new,
696 ehsQueueLength = ehsQueueLength old `mergeNew` ehsQueueLength new,
697 ehsVersion = ehsVersion old `mergeOld` ehsVersion new,
698 ehsYourIp = ehsYourIp old `mergeOld` ehsYourIp new
699 }
700 where
701 mergeOld mold mnew = mold <|> mnew
702 mergeNew mold mnew = mnew <|> mold
703
704
705instance BEncode ExtendedHandshake where
706 toBEncode ExtendedHandshake {..} = toDict $
707 "ipv4" .=? (S.encode <$> ehsIPv4)
708 .: "ipv6" .=? (S.encode <$> ehsIPv6)
709 .: "m" .=! ehsCaps
710 .: "metadata_size" .=? ehsMetadataSize
711 .: "p" .=? ehsPort
712 .: "reqq" .=? ehsQueueLength
713 .: "v" .=? ehsVersion
714 .: "yourip" .=? (runPut <$> either put put <$> toEither <$> ehsYourIp)
715 .: endDict
716 where
717 toEither (IPv4 v4) = Left v4
718 toEither (IPv6 v6) = Right v6
719
720 fromBEncode = fromDict $ ExtendedHandshake
721 <$>? "ipv4"
722 <*>? "ipv6"
723 <*>! "m"
724 <*>? "metadata_size"
725 <*>? "p"
726 <*>? "reqq"
727 <*>? "v"
728 <*> (opt "yourip" >>= getYourIp)
729
730getYourIp :: Maybe BValue -> BE.Get (Maybe IP)
731getYourIp f =
732 return $ do
733 BString ip <- f
734 either (const Nothing) Just $
735 case BS.length ip of
736 4 -> IPv4 <$> S.decode ip
737 16 -> IPv6 <$> S.decode ip
738 _ -> fail ""
739
740instance Pretty ExtendedHandshake where
741 pPrint = PP.text . show
742
743-- | NOTE: Approximated 'stats'.
744instance PeerMessage ExtendedHandshake where
745 envelop c = envelop c . EHandshake
746 {-# INLINE envelop #-}
747
748 requires _ = Just ExtExtended
749 {-# INLINE requires #-}
750
751 stats _ = ByteStats (4 + 1 + 1) 100 {- is it ok? -} 0 -- FIXME
752 {-# INLINE stats #-}
753
754-- | Set default values and the specified 'ExtendedCaps'.
755nullExtendedHandshake :: ExtendedCaps -> ExtendedHandshake
756nullExtendedHandshake caps = ExtendedHandshake
757 { ehsIPv4 = Nothing
758 , ehsIPv6 = Nothing
759 , ehsCaps = caps
760 , ehsMetadataSize = Nothing
761 , ehsPort = Nothing
762 , ehsQueueLength = Just defaultQueueLength
763 , ehsVersion = Just $ T.pack $ render $ pPrint libFingerprint
764 , ehsYourIp = Nothing
765 }
766
767{-----------------------------------------------------------------------
768-- Metadata exchange extension
769-----------------------------------------------------------------------}
770
771-- | A peer MUST verify that any piece it sends passes the info-hash
772-- verification. i.e. until the peer has the entire metadata, it
773-- cannot run SHA-1 to verify that it yields the same hash as the
774-- info-hash.
775--
776data ExtendedMetadata
777 -- | This message requests the a specified metadata piece. The
778 -- response to this message, from a peer supporting the extension,
779 -- is either a 'MetadataReject' or a 'MetadataData' message.
780 = MetadataRequest PieceIx
781
782 -- | If sender requested a valid 'PieceIx' and receiver have the
783 -- corresponding piece then receiver should respond with this
784 -- message.
785 | MetadataData
786 { -- | A piece of 'Data.Torrent.InfoDict'.
787 piece :: P.Piece BS.ByteString
788
789 -- | This key has the same semantics as the 'ehsMetadataSize' in
790 -- the 'ExtendedHandshake' — it is size of the torrent info
791 -- dict.
792 , totalSize :: Int
793 }
794
795 -- | Peers that do not have the entire metadata MUST respond with
796 -- a reject message to any metadata request.
797 --
798 -- Clients MAY implement flood protection by rejecting request
799 -- messages after a certain number of them have been
800 -- served. Typically the number of pieces of metadata times a
801 -- factor.
802 | MetadataReject PieceIx
803
804 -- | Reserved. By specification we should ignore unknown metadata
805 -- messages.
806 | MetadataUnknown BValue
807 deriving (Show, Eq, Typeable)
808
809-- | Extended metadata message id used in 'msg_type_key'.
810type MetadataId = Int
811
812msg_type_key, piece_key, total_size_key :: BKey
813msg_type_key = "msg_type"
814piece_key = "piece"
815total_size_key = "total_size"
816
817-- | BEP9 compatible encoding.
818instance BEncode ExtendedMetadata where
819 toBEncode (MetadataRequest pix) = toDict $
820 msg_type_key .=! (0 :: MetadataId)
821 .: piece_key .=! pix
822 .: endDict
823 toBEncode (MetadataData (P.Piece pix _) totalSize) = toDict $
824 msg_type_key .=! (1 :: MetadataId)
825 .: piece_key .=! pix
826 .: total_size_key .=! totalSize
827 .: endDict
828 toBEncode (MetadataReject pix) = toDict $
829 msg_type_key .=! (2 :: MetadataId)
830 .: piece_key .=! pix
831 .: endDict
832 toBEncode (MetadataUnknown bval) = bval
833
834 fromBEncode bval = (`fromDict` bval) $ do
835 mid <- field $ req msg_type_key
836 case mid :: MetadataId of
837 0 -> MetadataRequest <$>! piece_key
838 1 -> metadataData <$>! piece_key <*>! total_size_key
839 2 -> MetadataReject <$>! piece_key
840 _ -> pure (MetadataUnknown bval)
841 where
842 metadataData pix s = MetadataData (P.Piece pix BS.empty) s
843
844-- | Piece data bytes are omitted.
845instance Pretty ExtendedMetadata where
846 pPrint (MetadataRequest pix ) = "Request" <+> PP.int pix
847 pPrint (MetadataData p t) = "Data" <+> pPrint p <+> PP.int t
848 pPrint (MetadataReject pix ) = "Reject" <+> PP.int pix
849 pPrint (MetadataUnknown bval ) = "Unknown" <+> ppBEncode bval
850
851-- | NOTE: Approximated 'stats'.
852instance PeerMessage ExtendedMetadata where
853 envelop c = envelop c . EMetadata (remoteMessageId ExtMetadata c)
854 {-# INLINE envelop #-}
855
856 requires _ = Just ExtExtended
857 {-# INLINE requires #-}
858
859 stats (MetadataRequest _) = ByteStats (4 + 1 + 1) {- ~ -} 25 0
860 stats (MetadataData p _) = ByteStats (4 + 1 + 1) {- ~ -} 41 $
861 BS.length (P.pieceData p)
862 stats (MetadataReject _) = ByteStats (4 + 1 + 1) {- ~ -} 25 0
863 stats (MetadataUnknown _) = ByteStats (4 + 1 + 1) {- ? -} 0 0
864
865-- | All 'Piece's in 'MetadataData' messages MUST have size equal to
866-- this value. The last trailing piece can be shorter.
867metadataPieceSize :: PieceSize
868metadataPieceSize = 16 * 1024
869
870isLastPiece :: P.Piece a -> Int -> Bool
871isLastPiece P.Piece {..} total = succ pieceIndex == pcnt
872 where
873 pcnt = q + if r > 0 then 1 else 0
874 (q, r) = quotRem total metadataPieceSize
875
876-- TODO we can check if the piece payload bytestring have appropriate
877-- length; otherwise serialization MUST fail.
878isValidPiece :: P.Piece BL.ByteString -> Int -> Bool
879isValidPiece p @ P.Piece {..} total
880 | isLastPiece p total = pieceSize p <= metadataPieceSize
881 | otherwise = pieceSize p == metadataPieceSize
882
883setMetadataPayload :: BS.ByteString -> ExtendedMetadata -> ExtendedMetadata
884setMetadataPayload bs (MetadataData (P.Piece pix _) t) =
885 MetadataData (P.Piece pix bs) t
886setMetadataPayload _ msg = msg
887
888getMetadataPayload :: ExtendedMetadata -> Maybe BS.ByteString
889getMetadataPayload (MetadataData (P.Piece _ bs) _) = Just bs
890getMetadataPayload _ = Nothing
891
892-- | Metadata BDict usually contain only 'msg_type_key', 'piece_key'
893-- and 'total_size_key' fields so it normally should take less than
894-- 100 bytes. This limit is two order of magnitude larger to be
895-- permissive to 'MetadataUnknown' messages.
896--
897-- See 'maxMessageSize' for further explanation.
898--
899maxMetadataBDictSize :: Int
900maxMetadataBDictSize = 16 * 1024
901
902maxMetadataSize :: Int
903maxMetadataSize = maxMetadataBDictSize + metadataPieceSize
904
905-- to make MetadataData constructor fields a little bit prettier we
906-- cheat here: first we read empty 'pieceData' from bdict, but then we
907-- fill that field with the actual piece data — trailing bytes of
908-- the message
909getMetadata :: Int -> S.Get ExtendedMetadata
910getMetadata len
911 | len > maxMetadataSize = fail $ parseError "size exceeded limit"
912 | otherwise = do
913 bs <- getByteString len
914 parseRes $ BS.parse BE.parser bs
915 where
916 parseError reason = "unable to parse metadata message: " ++ reason
917
918 parseRes (BS.Fail _ _ m) = fail $ parseError $ "bdict: " ++ m
919 parseRes (BS.Partial _) = fail $ parseError "bdict: not enough bytes"
920 parseRes (BS.Done piece bvalueBS)
921 | BS.length piece > metadataPieceSize
922 = fail "infodict piece: size exceeded limit"
923 | otherwise = do
924 metadata <- either (fail . parseError) pure $ fromBEncode bvalueBS
925 return $ setMetadataPayload piece metadata
926
927putMetadata :: ExtendedMetadata -> BL.ByteString
928putMetadata msg
929 | Just bs <- getMetadataPayload msg = BE.encode msg <> BL.fromStrict bs
930 | otherwise = BE.encode msg
931
932-- | Allows a requesting peer to send 2 'MetadataRequest's for the
933-- each piece.
934--
935-- See 'Network.BitTorrent.Wire.Options.metadataFactor' for
936-- explanation why do we need this limit.
937defaultMetadataFactor :: Int
938defaultMetadataFactor = 2
939
940-- | Usually torrent size do not exceed 1MB. This value limit torrent
941-- /content/ size to about 8TB.
942--
943-- See 'Network.BitTorrent.Wire.Options.maxInfoDictSize' for
944-- explanation why do we need this limit.
945defaultMaxInfoDictSize :: Int
946defaultMaxInfoDictSize = 10 * 1024 * 1024
947
948{-----------------------------------------------------------------------
949-- Extension protocol messages
950-----------------------------------------------------------------------}
951
952-- | For more info see <http://www.bittorrent.org/beps/bep_0010.html>
953data ExtendedMessage
954 = EHandshake ExtendedHandshake
955 | EMetadata ExtendedMessageId ExtendedMetadata
956 | EUnknown ExtendedMessageId BS.ByteString
957 deriving (Show, Eq, Typeable)
958
959instance Pretty ExtendedMessage where
960 pPrint (EHandshake ehs) = pPrint ehs
961 pPrint (EMetadata _ msg) = "Metadata" <+> pPrint msg
962 pPrint (EUnknown mid _ ) = "Unknown" <+> PP.text (show mid)
963
964instance PeerMessage ExtendedMessage where
965 envelop _ = Extended
966 {-# INLINE envelop #-}
967
968 requires _ = Just ExtExtended
969 {-# INLINE requires #-}
970
971 stats (EHandshake hs) = stats hs
972 stats (EMetadata _ msg) = stats msg
973 stats (EUnknown _ msg) = ByteStats (4 + 1 + 1) (BS.length msg) 0
974
975{-----------------------------------------------------------------------
976-- The message datatype
977-----------------------------------------------------------------------}
978
979type MessageId = Word8
980
981-- | Messages used in communication between peers.
982--
983-- Note: If some extensions are disabled (not present in extension
984-- mask) and client receive message used by the disabled
985-- extension then the client MUST close the connection.
986--
987data Message
988 -- | Peers may close the TCP connection if they have not received
989 -- any messages for a given period of time, generally 2
990 -- minutes. Thus, the KeepAlive message is sent to keep the
991 -- connection between two peers alive, if no /other/ message has
992 -- been sent in a given period of time.
993 = KeepAlive
994 | Status !StatusUpdate -- ^ Messages used to update peer status.
995 | Available !Available -- ^ Messages used to inform availability.
996 | Transfer !Transfer -- ^ Messages used to transfer 'Block's.
997
998 -- | Peer receiving a handshake indicating the remote peer
999 -- supports the 'ExtDHT' should send a 'Port' message. Peers that
1000 -- receive this message should attempt to ping the node on the
1001 -- received port and IP address of the remote peer.
1002 | Port !PortNumber
1003 | Fast !FastMessage
1004 | Extended !ExtendedMessage
1005 deriving (Show, Eq)
1006
1007instance Default Message where
1008 def = KeepAlive
1009 {-# INLINE def #-}
1010
1011-- | Payload bytes are omitted.
1012instance Pretty Message where
1013 pPrint (KeepAlive ) = "Keep alive"
1014 pPrint (Status m) = "Status" <+> pPrint m
1015 pPrint (Available m) = pPrint m
1016 pPrint (Transfer m) = pPrint m
1017 pPrint (Port p) = "Port" <+> int (fromEnum p)
1018 pPrint (Fast m) = pPrint m
1019 pPrint (Extended m) = pPrint m
1020
1021instance PeerMessage Message where
1022 envelop _ = id
1023 {-# INLINE envelop #-}
1024
1025 requires KeepAlive = Nothing
1026 requires (Status _) = Nothing
1027 requires (Available _) = Nothing
1028 requires (Transfer _) = Nothing
1029 requires (Port _) = Just ExtDHT
1030 requires (Fast _) = Just ExtFast
1031 requires (Extended _) = Just ExtExtended
1032
1033 stats KeepAlive = ByteStats 4 0 0
1034 stats (Status m) = stats m
1035 stats (Available m) = stats m
1036 stats (Transfer m) = stats m
1037 stats (Port _) = ByteStats 5 2 0
1038 stats (Fast m) = stats m
1039 stats (Extended m) = stats m
1040
1041-- | PORT message.
1042instance PeerMessage PortNumber where
1043 envelop _ = Port
1044 {-# INLINE envelop #-}
1045
1046 requires _ = Just ExtDHT
1047 {-# INLINE requires #-}
1048
1049-- | How long /this/ peer should wait before dropping connection, in
1050-- seconds.
1051defaultKeepAliveTimeout :: Int
1052defaultKeepAliveTimeout = 2 * 60
1053
1054-- | How often /this/ peer should send 'KeepAlive' messages, in
1055-- seconds.
1056defaultKeepAliveInterval :: Int
1057defaultKeepAliveInterval = 60
1058
1059getInt :: S.Get Int
1060getInt = fromIntegral <$> S.getWord32be
1061{-# INLINE getInt #-}
1062
1063putInt :: S.Putter Int
1064putInt = S.putWord32be . fromIntegral
1065{-# INLINE putInt #-}
1066
1067-- | This limit should protect against "out-of-memory" attacks: if a
1068-- malicious peer have sent a long varlength message then receiver can
1069-- accumulate too long bytestring in the 'Get'.
1070--
1071-- Normal messages should never exceed this limits.
1072--
1073-- See also 'maxBitfieldSize', 'maxBlockSize' limits.
1074--
1075maxMessageSize :: Int
1076maxMessageSize = 20 + 1024 * 1024
1077
1078-- | This also limit max torrent size to:
1079--
1080-- max_bitfield_size * piece_ix_per_byte * max_piece_size =
1081-- 2 ^ 20 * 8 * 1MB =
1082-- 8TB
1083--
1084maxBitfieldSize :: Int
1085maxBitfieldSize = 1024 * 1024
1086
1087getBitfield :: Int -> S.Get Bitfield
1088getBitfield len
1089 | len > maxBitfieldSize = fail "BITFIELD message size exceeded limit"
1090 | otherwise = fromBitmap <$> getByteString len
1091
1092maxBlockSize :: Int
1093maxBlockSize = 4 * defaultTransferSize
1094
1095getBlock :: Int -> S.Get (Block BL.ByteString)
1096getBlock len
1097 | len > maxBlockSize = fail "BLOCK message size exceeded limit"
1098 | otherwise = Block <$> getInt <*> getInt
1099 <*> getLazyByteString (fromIntegral len)
1100{-# INLINE getBlock #-}
1101
1102instance Serialize Message where
1103 get = do
1104 len <- getInt
1105
1106 when (len > maxMessageSize) $ do
1107 fail "message body size exceeded the limit"
1108
1109 if len == 0 then return KeepAlive
1110 else do
1111 mid <- S.getWord8
1112 case mid of
1113 0x00 -> return $ Status (Choking True)
1114 0x01 -> return $ Status (Choking False)
1115 0x02 -> return $ Status (Interested True)
1116 0x03 -> return $ Status (Interested False)
1117 0x04 -> (Available . Have) <$> getInt
1118 0x05 -> (Available . Bitfield) <$> getBitfield (pred len)
1119 0x06 -> (Transfer . Request) <$> S.get
1120 0x07 -> (Transfer . Piece) <$> getBlock (len - 9)
1121 0x08 -> (Transfer . Cancel) <$> S.get
1122 0x09 -> Port <$> S.get
1123 0x0D -> (Fast . SuggestPiece) <$> getInt
1124 0x0E -> return $ Fast HaveAll
1125 0x0F -> return $ Fast HaveNone
1126 0x10 -> (Fast . RejectRequest) <$> S.get
1127 0x11 -> (Fast . AllowedFast) <$> getInt
1128 0x14 -> Extended <$> getExtendedMessage (pred len)
1129 _ -> do
1130 rm <- S.remaining >>= S.getBytes
1131 fail $ "unknown message ID: " ++ show mid ++ "\n"
1132 ++ "remaining available bytes: " ++ show rm
1133
1134 put KeepAlive = putInt 0
1135 put (Status msg) = putStatus msg
1136 put (Available msg) = putAvailable msg
1137 put (Transfer msg) = putTransfer msg
1138 put (Port p ) = putPort p
1139 put (Fast msg) = putFast msg
1140 put (Extended m ) = putExtendedMessage m
1141
1142statusUpdateId :: StatusUpdate -> MessageId
1143statusUpdateId (Choking choking) = fromIntegral (0 + fromEnum choking)
1144statusUpdateId (Interested choking) = fromIntegral (2 + fromEnum choking)
1145
1146putStatus :: Putter StatusUpdate
1147putStatus su = do
1148 putInt 1
1149 putWord8 (statusUpdateId su)
1150
1151putAvailable :: Putter Available
1152putAvailable (Have i) = do
1153 putInt 5
1154 putWord8 0x04
1155 putInt i
1156putAvailable (Bitfield (toBitmap -> bs)) = do
1157 putInt $ 1 + fromIntegral (BL.length bs)
1158 putWord8 0x05
1159 putLazyByteString bs
1160
1161putBlock :: Putter (Block BL.ByteString)
1162putBlock Block {..} = do
1163 putInt blkPiece
1164 putInt blkOffset
1165 putLazyByteString blkData
1166
1167putTransfer :: Putter Transfer
1168putTransfer (Request blk) = putInt 13 >> S.putWord8 0x06 >> S.put blk
1169putTransfer (Piece blk) = do
1170 putInt (9 + blockSize blk)
1171 putWord8 0x07
1172 putBlock blk
1173putTransfer (Cancel blk) = putInt 13 >> S.putWord8 0x08 >> S.put blk
1174
1175putPort :: Putter PortNumber
1176putPort p = do
1177 putInt 3
1178 putWord8 0x09
1179 put p
1180
1181putFast :: Putter FastMessage
1182putFast HaveAll = putInt 1 >> putWord8 0x0E
1183putFast HaveNone = putInt 1 >> putWord8 0x0F
1184putFast (SuggestPiece pix) = putInt 5 >> putWord8 0x0D >> putInt pix
1185putFast (RejectRequest i ) = putInt 13 >> putWord8 0x10 >> put i
1186putFast (AllowedFast i ) = putInt 5 >> putWord8 0x11 >> putInt i
1187
1188maxEHandshakeSize :: Int
1189maxEHandshakeSize = 16 * 1024
1190
1191getExtendedHandshake :: Int -> S.Get ExtendedHandshake
1192getExtendedHandshake messageSize
1193 | messageSize > maxEHandshakeSize
1194 = fail "extended handshake size exceeded limit"
1195 | otherwise = do
1196 bs <- getByteString messageSize
1197 either fail pure $ BE.decode bs
1198
1199maxEUnknownSize :: Int
1200maxEUnknownSize = 64 * 1024
1201
1202getExtendedUnknown :: Int -> S.Get BS.ByteString
1203getExtendedUnknown len
1204 | len > maxEUnknownSize = fail "unknown extended message size exceeded limit"
1205 | otherwise = getByteString len
1206
1207getExtendedMessage :: Int -> S.Get ExtendedMessage
1208getExtendedMessage messageSize = do
1209 msgId <- getWord8
1210 let msgBodySize = messageSize - 1
1211 case msgId of
1212 0 -> EHandshake <$> getExtendedHandshake msgBodySize
1213 1 -> EMetadata msgId <$> getMetadata msgBodySize
1214 _ -> EUnknown msgId <$> getExtendedUnknown msgBodySize
1215
1216-- | By spec.
1217extendedMessageId :: MessageId
1218extendedMessageId = 20
1219
1220putExt :: ExtendedMessageId -> BL.ByteString -> Put
1221putExt mid lbs = do
1222 putWord32be $ fromIntegral (1 + 1 + BL.length lbs)
1223 putWord8 extendedMessageId
1224 putWord8 mid
1225 putLazyByteString lbs
1226
1227-- NOTE: in contrast to getExtendedMessage this function put length
1228-- and message id too!
1229putExtendedMessage :: Putter ExtendedMessage
1230putExtendedMessage (EHandshake hs) = putExt extHandshakeId $ BE.encode hs
1231putExtendedMessage (EMetadata mid msg) = putExt mid $ putMetadata msg
1232putExtendedMessage (EUnknown mid bs) = putExt mid $ BL.fromStrict bs