summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent/Tracker/UDP.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/BitTorrent/Tracker/UDP.hs')
-rw-r--r--src/Network/BitTorrent/Tracker/UDP.hs155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/Network/BitTorrent/Tracker/UDP.hs b/src/Network/BitTorrent/Tracker/UDP.hs
new file mode 100644
index 00000000..369750bd
--- /dev/null
+++ b/src/Network/BitTorrent/Tracker/UDP.hs
@@ -0,0 +1,155 @@
1-- |
2-- Copyright : (c) Sam T. 2013
3-- License : MIT
4-- Maintainer : pxqr.sta@gmail.com
5-- Stability : experimental
6-- Portability : portable
7--
8-- This module implement low-level UDP tracker protocol.
9-- For more info see: http://www.bittorrent.org/beps/bep_0015.html
10--
11{-# LANGUAGE RecordWildCards #-}
12{-# LANGUAGE FlexibleInstances #-}
13{-# LANGUAGE GeneralizedNewtypeDeriving #-}
14module Network.BitTorrent.Tracker.UDP
15 ( Request(..), Response(..)
16 ) where
17
18import Control.Applicative
19import Control.Monad
20import Data.Serialize
21import Data.Word
22import Data.Text
23import Data.Text.Encoding
24
25import Data.Torrent ()
26import Network.BitTorrent.Tracker.Protocol
27
28
29-- | Connection Id is used for entire tracker session.
30newtype ConnId = ConnId { getConnId :: Word64 }
31 deriving (Show, Serialize)
32
33-- | Transaction Id is used for within UDP RPC.
34newtype TransId = TransId { getTransId :: Word32 }
35 deriving (Show, Serialize)
36
37genConnectionId :: IO ConnId
38genConnectionId = return (ConnId 0)
39
40genTransactionId :: IO TransId
41genTransactionId = return (TransId 0)
42
43data Request = Connect
44 | Announce AnnounceQuery
45 | Scrape ScrapeQuery
46
47data Response = Connected
48 | Announced AnnounceInfo
49 | Scraped [ScrapeInfo]
50 | Failed Text
51
52-- TODO rename to message?
53data Transaction a = Transaction
54 { connId :: !ConnId
55 , transId :: !TransId
56 , body :: !a
57 } deriving Show
58
59type MessageId = Word32
60
61connectId, announceId, scrapeId, errorId :: MessageId
62connectId = 0
63announceId = 1
64scrapeId = 2
65errorId = 3
66
67instance Serialize (Transaction Request) where
68 put Transaction {..} = do
69 case body of
70 Connect -> do
71 put connId
72 put connectId
73 put transId
74
75 Announce query -> do
76 put connId
77 put announceId
78 put transId
79 put query
80
81 Scrape hashes -> do
82 put connId
83 put announceId
84 put transId
85 forM_ hashes put
86
87 get = do
88 cid <- get
89 rid <- getWord32be
90 tid <- get
91 bod <- getBody rid
92
93 return $ Transaction {
94 connId = cid
95 , transId = tid
96 , body = bod
97 }
98 where
99 getBody :: MessageId -> Get Request
100 getBody msgId
101 | msgId == connectId = return Connect
102 | msgId == announceId = Announce <$> get
103 | msgId == scrapeId = Scrape <$> many get
104 | otherwise = fail "unknown message id"
105
106instance Serialize (Transaction Response) where
107 put Transaction {..} = do
108 case body of
109 Connected -> do
110 put connId
111 put connectId
112 put transId
113
114 Announced info -> do
115 put connId
116 put announceId
117 put transId
118 put info
119
120 Scraped infos -> do
121 put connId
122 put scrapeId
123 put transId
124 forM_ infos put
125
126 Failed info -> do
127 put connId
128 put errorId
129 put transId
130 put (encodeUtf8 info)
131
132
133 get = do
134 cid <- get
135 rid <- getWord32be
136 tid <- get
137 bod <- getBody rid
138
139 return $ Transaction {
140 connId = cid
141 , transId = tid
142 , body = bod
143 }
144 where
145 getBody :: MessageId -> Get Response
146 getBody msgId
147 | msgId == connectId = return $ Connected
148 | msgId == announceId = Announced <$> get
149 | msgId == scrapeId = Scraped <$> many get
150 | msgId == errorId = do
151 bs <- get
152 case decodeUtf8' bs of
153 Left ex -> fail (show ex)
154 Right msg -> return $ Failed msg
155 | otherwise = fail "unknown message id"