diff options
author | Sam Truzjan <pxqr.sta@gmail.com> | 2014-03-15 20:37:11 +0400 |
---|---|---|
committer | Sam Truzjan <pxqr.sta@gmail.com> | 2014-03-15 20:37:11 +0400 |
commit | 442a7c0941b4f2659988193404263348593551a2 (patch) | |
tree | b22870011b7a27a508f1500670bf7513f34df3b2 /src/Network/BitTorrent/Tracker/Message.hs | |
parent | e2c997424da60cae727a934fd9627f00ec1be0fd (diff) |
Expose UDP tracker specific message types
Those can be used to implement UDP tracker server by third party
libraries or projects.
Diffstat (limited to 'src/Network/BitTorrent/Tracker/Message.hs')
-rw-r--r-- | src/Network/BitTorrent/Tracker/Message.hs | 163 |
1 files changed, 162 insertions, 1 deletions
diff --git a/src/Network/BitTorrent/Tracker/Message.hs b/src/Network/BitTorrent/Tracker/Message.hs index edafdaba..8131ecf0 100644 --- a/src/Network/BitTorrent/Tracker/Message.hs +++ b/src/Network/BitTorrent/Tracker/Message.hs | |||
@@ -25,6 +25,7 @@ | |||
25 | {-# LANGUAGE DeriveDataTypeable #-} | 25 | {-# LANGUAGE DeriveDataTypeable #-} |
26 | {-# LANGUAGE DeriveFunctor #-} | 26 | {-# LANGUAGE DeriveFunctor #-} |
27 | {-# LANGUAGE ScopedTypeVariables #-} | 27 | {-# LANGUAGE ScopedTypeVariables #-} |
28 | {-# LANGUAGE TypeFamilies #-} | ||
28 | {-# OPTIONS -fno-warn-orphans #-} | 29 | {-# OPTIONS -fno-warn-orphans #-} |
29 | module Network.BitTorrent.Tracker.Message | 30 | module Network.BitTorrent.Tracker.Message |
30 | ( -- * Announce | 31 | ( -- * Announce |
@@ -76,6 +77,21 @@ module Network.BitTorrent.Tracker.Message | |||
76 | 77 | ||
77 | -- ** Extra | 78 | -- ** Extra |
78 | , queryToSimpleQuery | 79 | , queryToSimpleQuery |
80 | |||
81 | -- * UDP specific | ||
82 | -- ** Connection | ||
83 | , ConnectionId | ||
84 | , initialConnectionId | ||
85 | |||
86 | -- ** Messages | ||
87 | , Request (..) | ||
88 | , Response (..) | ||
89 | , responseName | ||
90 | |||
91 | -- ** Transaction | ||
92 | , genTransactionId | ||
93 | , TransactionId | ||
94 | , Transaction (..) | ||
79 | ) | 95 | ) |
80 | where | 96 | where |
81 | 97 | ||
@@ -104,7 +120,9 @@ import Network | |||
104 | import Network.HTTP.Types.QueryLike | 120 | import Network.HTTP.Types.QueryLike |
105 | import Network.HTTP.Types.URI hiding (urlEncode) | 121 | import Network.HTTP.Types.URI hiding (urlEncode) |
106 | import Network.HTTP.Types.Status | 122 | import Network.HTTP.Types.Status |
107 | import Network.Socket | 123 | import Network.Socket hiding (Connected) |
124 | import Numeric | ||
125 | import System.Entropy | ||
108 | import Text.Read (readMaybe) | 126 | import Text.Read (readMaybe) |
109 | 127 | ||
110 | import Data.Torrent.InfoHash | 128 | import Data.Torrent.InfoHash |
@@ -765,3 +783,146 @@ scrapeType = "text/plain" | |||
765 | -- | 783 | -- |
766 | parseFailureStatus :: ParamParseFailure -> Status | 784 | parseFailureStatus :: ParamParseFailure -> Status |
767 | parseFailureStatus = mkStatus <$> parseFailureCode <*> parseFailureMessage | 785 | parseFailureStatus = mkStatus <$> parseFailureCode <*> parseFailureMessage |
786 | |||
787 | {----------------------------------------------------------------------- | ||
788 | -- UDP specific message types | ||
789 | -----------------------------------------------------------------------} | ||
790 | |||
791 | genToken :: IO Word64 | ||
792 | genToken = do | ||
793 | bs <- getEntropy 8 | ||
794 | either err return $ runGet getWord64be bs | ||
795 | where | ||
796 | err = error "genToken: impossible happen" | ||
797 | |||
798 | -- | Connection Id is used for entire tracker session. | ||
799 | newtype ConnectionId = ConnectionId Word64 | ||
800 | deriving (Eq, Serialize) | ||
801 | |||
802 | instance Show ConnectionId where | ||
803 | showsPrec _ (ConnectionId cid) = showString "0x" <> showHex cid | ||
804 | |||
805 | initialConnectionId :: ConnectionId | ||
806 | initialConnectionId = ConnectionId 0x41727101980 | ||
807 | |||
808 | -- | Transaction Id is used within a UDP RPC. | ||
809 | newtype TransactionId = TransactionId Word32 | ||
810 | deriving (Eq, Ord, Enum, Bounded, Serialize) | ||
811 | |||
812 | instance Show TransactionId where | ||
813 | showsPrec _ (TransactionId tid) = showString "0x" <> showHex tid | ||
814 | |||
815 | genTransactionId :: IO TransactionId | ||
816 | genTransactionId = (TransactionId . fromIntegral) <$> genToken | ||
817 | |||
818 | data Request | ||
819 | = Connect | ||
820 | | Announce AnnounceQuery | ||
821 | | Scrape ScrapeQuery | ||
822 | deriving Show | ||
823 | |||
824 | data Response | ||
825 | = Connected ConnectionId | ||
826 | | Announced AnnounceInfo | ||
827 | | Scraped [ScrapeEntry] | ||
828 | | Failed Text | ||
829 | deriving Show | ||
830 | |||
831 | responseName :: Response -> String | ||
832 | responseName (Connected _) = "connected" | ||
833 | responseName (Announced _) = "announced" | ||
834 | responseName (Scraped _) = "scraped" | ||
835 | responseName (Failed _) = "failed" | ||
836 | |||
837 | data family Transaction a | ||
838 | data instance Transaction Request = TransactionQ | ||
839 | { connIdQ :: {-# UNPACK #-} !ConnectionId | ||
840 | , transIdQ :: {-# UNPACK #-} !TransactionId | ||
841 | , request :: !Request | ||
842 | } deriving Show | ||
843 | data instance Transaction Response = TransactionR | ||
844 | { transIdR :: {-# UNPACK #-} !TransactionId | ||
845 | , response :: !Response | ||
846 | } deriving Show | ||
847 | |||
848 | -- TODO newtype | ||
849 | newtype MessageId = MessageId Word32 | ||
850 | deriving (Show, Eq, Num, Serialize) | ||
851 | |||
852 | connectId, announceId, scrapeId, errorId :: MessageId | ||
853 | connectId = 0 | ||
854 | announceId = 1 | ||
855 | scrapeId = 2 | ||
856 | errorId = 3 | ||
857 | |||
858 | instance Serialize (Transaction Request) where | ||
859 | put TransactionQ {..} = do | ||
860 | case request of | ||
861 | Connect -> do | ||
862 | put initialConnectionId | ||
863 | put connectId | ||
864 | put transIdQ | ||
865 | |||
866 | Announce ann -> do | ||
867 | put connIdQ | ||
868 | put announceId | ||
869 | put transIdQ | ||
870 | put ann | ||
871 | |||
872 | Scrape hashes -> do | ||
873 | put connIdQ | ||
874 | put scrapeId | ||
875 | put transIdQ | ||
876 | forM_ hashes put | ||
877 | |||
878 | get = do | ||
879 | cid <- get | ||
880 | mid <- get | ||
881 | TransactionQ cid <$> S.get <*> getBody mid | ||
882 | where | ||
883 | getBody :: MessageId -> S.Get Request | ||
884 | getBody msgId | ||
885 | | msgId == connectId = pure Connect | ||
886 | | msgId == announceId = Announce <$> get | ||
887 | | msgId == scrapeId = Scrape <$> many get | ||
888 | | otherwise = fail errMsg | ||
889 | where | ||
890 | errMsg = "unknown request: " ++ show msgId | ||
891 | |||
892 | instance Serialize (Transaction Response) where | ||
893 | put TransactionR {..} = do | ||
894 | case response of | ||
895 | Connected conn -> do | ||
896 | put connectId | ||
897 | put transIdR | ||
898 | put conn | ||
899 | |||
900 | Announced info -> do | ||
901 | put announceId | ||
902 | put transIdR | ||
903 | put info | ||
904 | |||
905 | Scraped infos -> do | ||
906 | put scrapeId | ||
907 | put transIdR | ||
908 | forM_ infos put | ||
909 | |||
910 | Failed info -> do | ||
911 | put errorId | ||
912 | put transIdR | ||
913 | put (encodeUtf8 info) | ||
914 | |||
915 | |||
916 | get = do | ||
917 | mid <- get | ||
918 | TransactionR <$> get <*> getBody mid | ||
919 | where | ||
920 | getBody :: MessageId -> S.Get Response | ||
921 | getBody msgId | ||
922 | | msgId == connectId = Connected <$> get | ||
923 | | msgId == announceId = Announced <$> get | ||
924 | | msgId == scrapeId = Scraped <$> many get | ||
925 | | msgId == errorId = (Failed . decodeUtf8) <$> get | ||
926 | | otherwise = fail msg | ||
927 | where | ||
928 | msg = "unknown response: " ++ show msgId | ||