diff options
Diffstat (limited to 'src/Network/BitTorrent')
-rw-r--r-- | src/Network/BitTorrent/Exchange/Message.hs | 100 | ||||
-rw-r--r-- | src/Network/BitTorrent/Exchange/Wire.hs | 2 |
2 files changed, 77 insertions, 25 deletions
diff --git a/src/Network/BitTorrent/Exchange/Message.hs b/src/Network/BitTorrent/Exchange/Message.hs index 8f6e1a5a..af212c3b 100644 --- a/src/Network/BitTorrent/Exchange/Message.hs +++ b/src/Network/BitTorrent/Exchange/Message.hs | |||
@@ -73,6 +73,7 @@ module Network.BitTorrent.Exchange.Message | |||
73 | 73 | ||
74 | -- *** Metadata | 74 | -- *** Metadata |
75 | , ExtendedMetadata (..) | 75 | , ExtendedMetadata (..) |
76 | , metadataPieceSize | ||
76 | ) where | 77 | ) where |
77 | 78 | ||
78 | import Control.Applicative | 79 | import Control.Applicative |
@@ -508,6 +509,9 @@ instance Capabilities ExtendedCaps where | |||
508 | allowed e (ExtendedCaps caps) = M.member e caps | 509 | allowed e (ExtendedCaps caps) = M.member e caps |
509 | {-# INLINE allowed #-} | 510 | {-# INLINE allowed #-} |
510 | 511 | ||
512 | remoteMessageId :: ExtendedExtension -> ExtendedCaps -> ExtendedMessageId | ||
513 | remoteMessageId ext = fromMaybe (extId ext) . M.lookup ext . extendedCaps | ||
514 | |||
511 | {----------------------------------------------------------------------- | 515 | {----------------------------------------------------------------------- |
512 | -- Extended handshake | 516 | -- Extended handshake |
513 | -----------------------------------------------------------------------} | 517 | -----------------------------------------------------------------------} |
@@ -532,6 +536,11 @@ data ExtendedHandshake = ExtendedHandshake | |||
532 | -- message. | 536 | -- message. |
533 | , ehsCaps :: ExtendedCaps | 537 | , ehsCaps :: ExtendedCaps |
534 | 538 | ||
539 | -- | Size of 'Data.Torrent.InfoDict' in bytes. This field should | ||
540 | -- be added if ExtMetadata is enabled in current session /and/ | ||
541 | -- peer have the torrent file. | ||
542 | , ehsMetadataSize :: Maybe Int | ||
543 | |||
535 | -- | Local TCP /listen/ port. Allows each side to learn about the | 544 | -- | Local TCP /listen/ port. Allows each side to learn about the |
536 | -- TCP port number of the other side. | 545 | -- TCP port number of the other side. |
537 | , ehsPort :: Maybe PortNumber | 546 | , ehsPort :: Maybe PortNumber |
@@ -556,13 +565,14 @@ defaultQueueLength = 0 | |||
556 | 565 | ||
557 | -- | All fields are empty. | 566 | -- | All fields are empty. |
558 | instance Default ExtendedHandshake where | 567 | instance Default ExtendedHandshake where |
559 | def = ExtendedHandshake def def def def def def | 568 | def = ExtendedHandshake def def def def def def def |
560 | 569 | ||
561 | instance BEncode ExtendedHandshake where | 570 | instance BEncode ExtendedHandshake where |
562 | toBEncode ExtendedHandshake {..} = toDict $ | 571 | toBEncode ExtendedHandshake {..} = toDict $ |
563 | "ipv4" .=? ehsIPv4 -- FIXME invalid encoding | 572 | "ipv4" .=? ehsIPv4 -- FIXME invalid encoding |
564 | .: "ipv6" .=? ehsIPv6 -- FIXME invalid encoding | 573 | .: "ipv6" .=? ehsIPv6 -- FIXME invalid encoding |
565 | .: "m" .=! ehsCaps | 574 | .: "m" .=! ehsCaps |
575 | .: "metadata_size" .=? ehsMetadataSize | ||
566 | .: "p" .=? ehsPort | 576 | .: "p" .=? ehsPort |
567 | .: "reqq" .=? ehsQueueLength | 577 | .: "reqq" .=? ehsQueueLength |
568 | .: "v" .=? ehsVersion | 578 | .: "v" .=? ehsVersion |
@@ -573,6 +583,7 @@ instance BEncode ExtendedHandshake where | |||
573 | <$>? "ipv4" | 583 | <$>? "ipv4" |
574 | <*>? "ipv6" | 584 | <*>? "ipv6" |
575 | <*>! "m" | 585 | <*>! "m" |
586 | <*>? "metadata_size" | ||
576 | <*>? "p" | 587 | <*>? "p" |
577 | <*>? "reqq" | 588 | <*>? "reqq" |
578 | <*>? "v" | 589 | <*>? "v" |
@@ -591,56 +602,92 @@ instance PeerMessage ExtendedHandshake where | |||
591 | -- | Set default values and the specified 'ExtendedCaps'. | 602 | -- | Set default values and the specified 'ExtendedCaps'. |
592 | nullExtendedHandshake :: ExtendedCaps -> ExtendedHandshake | 603 | nullExtendedHandshake :: ExtendedCaps -> ExtendedHandshake |
593 | nullExtendedHandshake caps = ExtendedHandshake | 604 | nullExtendedHandshake caps = ExtendedHandshake |
594 | { ehsIPv4 = Nothing | 605 | { ehsIPv4 = Nothing |
595 | , ehsIPv6 = Nothing | 606 | , ehsIPv6 = Nothing |
596 | , ehsCaps = caps | 607 | , ehsCaps = caps |
597 | , ehsPort = Nothing | 608 | , ehsMetadataSize = Nothing |
598 | , ehsQueueLength = Just defaultQueueLength | 609 | , ehsPort = Nothing |
599 | , ehsVersion = Just $ T.pack $ render $ pretty libFingerprint | 610 | , ehsQueueLength = Just defaultQueueLength |
611 | , ehsVersion = Just $ T.pack $ render $ pretty libFingerprint | ||
600 | } | 612 | } |
601 | 613 | ||
602 | {----------------------------------------------------------------------- | 614 | {----------------------------------------------------------------------- |
603 | -- Metadata exchange extension | 615 | -- Metadata exchange extension |
604 | -----------------------------------------------------------------------} | 616 | -----------------------------------------------------------------------} |
605 | 617 | ||
606 | type MetadataId = Int | 618 | -- | A peer MUST verify that any piece it sends passes the info-hash |
607 | 619 | -- verification. i.e. until the peer has the entire metadata, it | |
608 | metadataPieceSize :: Int | 620 | -- cannot run SHA-1 to verify that it yields the same hash as the |
609 | metadataPieceSize = 16 * 1024 | 621 | -- info-hash. |
610 | 622 | -- | |
611 | data ExtendedMetadata | 623 | data ExtendedMetadata |
624 | -- | This message requests the a specified metadata piece. The | ||
625 | -- response to this message, from a peer supporting the extension, | ||
626 | -- is either a 'MetadataReject' or a 'MetadataData' message. | ||
612 | = MetadataRequest PieceIx | 627 | = MetadataRequest PieceIx |
613 | | MetadataData PieceIx Int | 628 | |
629 | | MetadataData | ||
630 | { -- | FIXME add piece data | ||
631 | piece :: PieceIx | ||
632 | |||
633 | -- | This key has the same semantics as the 'ehsMetadataSize' in | ||
634 | -- the 'ExtendedHandshake' — it is size of the torrent info | ||
635 | -- dict. | ||
636 | , totalSize :: Int | ||
637 | } | ||
638 | |||
639 | -- | Peers that do not have the entire metadata MUST respond with | ||
640 | -- a reject message to any metadata request. | ||
641 | -- | ||
642 | -- Clients MAY implement flood protection by rejecting request | ||
643 | -- messages after a certain number of them have been | ||
644 | -- served. Typically the number of pieces of metadata times a | ||
645 | -- factor. | ||
614 | | MetadataReject PieceIx | 646 | | MetadataReject PieceIx |
647 | |||
648 | -- | Reserved. | ||
615 | | MetadataUnknown BValue | 649 | | MetadataUnknown BValue |
616 | deriving (Show, Eq, Typeable) | 650 | deriving (Show, Eq, Typeable) |
617 | 651 | ||
652 | -- | Extended metadata message id used in 'msg_type_key'. | ||
653 | type MetadataId = Int | ||
654 | |||
655 | msg_type_key, piece_key, total_size_key :: BKey | ||
656 | msg_type_key = "msg_type" | ||
657 | piece_key = "piece" | ||
658 | total_size_key = "total_size" | ||
659 | |||
660 | -- | BEP9 compatible encoding. | ||
618 | instance BEncode ExtendedMetadata where | 661 | instance BEncode ExtendedMetadata where |
619 | toBEncode (MetadataRequest pix) = toDict $ | 662 | toBEncode (MetadataRequest pix) = toDict $ |
620 | "msg_type" .=! (0 :: MetadataId) | 663 | msg_type_key .=! (0 :: MetadataId) |
621 | .: "piece" .=! pix | 664 | .: piece_key .=! pix |
622 | .: endDict | 665 | .: endDict |
623 | toBEncode (MetadataData pix totalSize) = toDict $ | 666 | toBEncode (MetadataData pix totalSize) = toDict $ |
624 | "msg_type" .=! (1 :: MetadataId) | 667 | msg_type_key .=! (1 :: MetadataId) |
625 | .: "piece" .=! pix | 668 | .: piece_key .=! pix |
626 | .: "total_size" .=! totalSize | 669 | .: total_size_key .=! totalSize |
627 | .: endDict | 670 | .: endDict |
628 | toBEncode (MetadataReject pix) = toDict $ | 671 | toBEncode (MetadataReject pix) = toDict $ |
629 | "msg_type" .=! (2 :: MetadataId) | 672 | msg_type_key .=! (2 :: MetadataId) |
630 | .: "piece" .=! pix | 673 | .: piece_key .=! pix |
631 | .: endDict | 674 | .: endDict |
632 | toBEncode (MetadataUnknown bval) = bval | 675 | toBEncode (MetadataUnknown bval) = bval |
633 | 676 | ||
634 | fromBEncode = undefined | 677 | fromBEncode bval = (`fromDict` bval) $ do |
678 | mid <- field $ req msg_type_key | ||
679 | case mid :: MetadataId of | ||
680 | 0 -> MetadataRequest <$>! piece_key | ||
681 | 1 -> MetadataData <$>! piece_key <*>! total_size_key | ||
682 | 2 -> MetadataReject <$>! piece_key | ||
683 | _ -> pure (MetadataUnknown bval) | ||
635 | 684 | ||
685 | -- | Piece data bytes are omitted. | ||
636 | instance Pretty ExtendedMetadata where | 686 | instance Pretty ExtendedMetadata where |
637 | pretty (MetadataRequest pix ) = "Request" <+> PP.int pix | 687 | pretty (MetadataRequest pix ) = "Request" <+> PP.int pix |
638 | pretty (MetadataData pix s) = "Data" <+> PP.int pix <+> PP.int s | 688 | pretty (MetadataData pix s) = "Data" <+> PP.int pix <+> PP.int s |
639 | pretty (MetadataReject pix ) = "Reject" <+> PP.int pix | 689 | pretty (MetadataReject pix ) = "Reject" <+> PP.int pix |
640 | pretty (MetadataUnknown bval ) = ppBEncode bval | 690 | pretty (MetadataUnknown bval ) = "Unknown" <+> ppBEncode bval |
641 | |||
642 | remoteMessageId :: ExtendedExtension -> ExtendedCaps -> ExtendedMessageId | ||
643 | remoteMessageId ext = fromMaybe (extId ext) . M.lookup ext . extendedCaps | ||
644 | 691 | ||
645 | instance PeerMessage ExtendedMetadata where | 692 | instance PeerMessage ExtendedMetadata where |
646 | envelop c = envelop c . EMetadata (remoteMessageId ExtMetadata c) | 693 | envelop c = envelop c . EMetadata (remoteMessageId ExtMetadata c) |
@@ -649,6 +696,9 @@ instance PeerMessage ExtendedMetadata where | |||
649 | requires _ = Just ExtExtended | 696 | requires _ = Just ExtExtended |
650 | {-# INLINE requires #-} | 697 | {-# INLINE requires #-} |
651 | 698 | ||
699 | metadataPieceSize :: Int | ||
700 | metadataPieceSize = 16 * 1024 | ||
701 | |||
652 | {----------------------------------------------------------------------- | 702 | {----------------------------------------------------------------------- |
653 | -- Extension protocol messages | 703 | -- Extension protocol messages |
654 | -----------------------------------------------------------------------} | 704 | -----------------------------------------------------------------------} |
diff --git a/src/Network/BitTorrent/Exchange/Wire.hs b/src/Network/BitTorrent/Exchange/Wire.hs index 6f80a567..1cf14809 100644 --- a/src/Network/BitTorrent/Exchange/Wire.hs +++ b/src/Network/BitTorrent/Exchange/Wire.hs | |||
@@ -54,6 +54,8 @@ import Network.BitTorrent.Exchange.Message | |||
54 | -- TODO handle port message? | 54 | -- TODO handle port message? |
55 | -- TODO handle limits? | 55 | -- TODO handle limits? |
56 | -- TODO filter not requested PIECE messages | 56 | -- TODO filter not requested PIECE messages |
57 | -- TODO metadata piece request flood protection | ||
58 | -- TODO piece request flood protection | ||
57 | {----------------------------------------------------------------------- | 59 | {----------------------------------------------------------------------- |
58 | -- Exceptions | 60 | -- Exceptions |
59 | -----------------------------------------------------------------------} | 61 | -----------------------------------------------------------------------} |