diff options
author | Joe Crayne <joe@jerkface.net> | 2019-12-21 00:29:53 -0500 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2020-01-01 23:27:49 -0500 |
commit | ca7f03b9b35ca1d47c51ed6b63c8d08a2b27b230 (patch) | |
tree | 624037e6edaa8c64ed3dc9a5a950f4e4a5aecba0 | |
parent | f5c9b738e489bced6189b85124952918414d8c8b (diff) |
Bittorrent udp tracker server transport.
-rw-r--r-- | dht/src/Network/BitTorrent/Tracker/Message.hs | 8 | ||||
-rw-r--r-- | dht/src/Network/BitTorrent/Tracker/Transport.hs | 76 | ||||
-rw-r--r-- | dht/src/Network/QueryResponse.hs | 41 |
3 files changed, 109 insertions, 16 deletions
diff --git a/dht/src/Network/BitTorrent/Tracker/Message.hs b/dht/src/Network/BitTorrent/Tracker/Message.hs index e9d12006..f44c7700 100644 --- a/dht/src/Network/BitTorrent/Tracker/Message.hs +++ b/dht/src/Network/BitTorrent/Tracker/Message.hs | |||
@@ -102,6 +102,7 @@ module Network.BitTorrent.Tracker.Message | |||
102 | 102 | ||
103 | import Control.Applicative | 103 | import Control.Applicative |
104 | import Control.Monad | 104 | import Control.Monad |
105 | import Crypto.Random | ||
105 | import Data.BEncode as BE hiding (Result) | 106 | import Data.BEncode as BE hiding (Result) |
106 | import Data.BEncode.BDict as BE | 107 | import Data.BEncode.BDict as BE |
107 | import Data.ByteString as BS | 108 | import Data.ByteString as BS |
@@ -810,8 +811,11 @@ newtype TransactionId = TransactionId Word32 | |||
810 | instance Show TransactionId where | 811 | instance Show TransactionId where |
811 | showsPrec _ (TransactionId tid) = showString "0x" <> showHex tid | 812 | showsPrec _ (TransactionId tid) = showString "0x" <> showHex tid |
812 | 813 | ||
813 | genTransactionId :: IO TransactionId | 814 | genTransactionId :: DRG g => g -> (TransactionId, g) |
814 | genTransactionId = (TransactionId . fromIntegral) <$> genToken | 815 | genTransactionId g = let (ws, g') = randomBytesGenerate 4 g |
816 | Right w = S.runGet S.getWord32be ws | ||
817 | in (TransactionId w, g') | ||
818 | |||
815 | 819 | ||
816 | data Request | 820 | data Request |
817 | = Connect | 821 | = Connect |
diff --git a/dht/src/Network/BitTorrent/Tracker/Transport.hs b/dht/src/Network/BitTorrent/Tracker/Transport.hs new file mode 100644 index 00000000..60cb832d --- /dev/null +++ b/dht/src/Network/BitTorrent/Tracker/Transport.hs | |||
@@ -0,0 +1,76 @@ | |||
1 | {-# LANGUAGE TupleSections #-} | ||
2 | module Network.BitTorrent.Tracker.Transport where | ||
3 | |||
4 | import Control.Concurrent.STM | ||
5 | import Crypto.Random | ||
6 | import Data.ByteString (ByteString) | ||
7 | import Data.Functor.Contravariant | ||
8 | import Data.IntMap.Strict (IntMap) | ||
9 | import qualified Data.Serialize as S | ||
10 | import Network.Socket | ||
11 | |||
12 | import Data.QueryResponse.Table | ||
13 | import Data.TableMethods | ||
14 | import Network.Address | ||
15 | import Network.BitTorrent.Tracker.Message | ||
16 | import Network.QueryResponse as QR | ||
17 | |||
18 | parseTracker :: ByteString -> SockAddr -> Either String (Transaction Request, SockAddr) | ||
19 | parseTracker bs saddr = fmap (, saddr) $ S.decode bs | ||
20 | |||
21 | encodeTracker :: Transaction Response -> SockAddr -> (ByteString, SockAddr) | ||
22 | encodeTracker resp saddr = (S.encode resp,saddr) | ||
23 | |||
24 | type Handler = MethodHandlerA String TransactionId SockAddr (Transaction Request) (Transaction Response) | ||
25 | |||
26 | type Client = QR.ClientA String MessageId TransactionId SockAddr (Transaction Request) (Transaction Response) | ||
27 | |||
28 | handlers :: MessageId -> Maybe Handler | ||
29 | handlers ConnectId = Nothing | ||
30 | handlers AnnounceId = Nothing | ||
31 | handlers ScrapeId = Nothing | ||
32 | handlers ErrorId = Nothing | ||
33 | handlers _ = Nothing | ||
34 | |||
35 | methodOfRequest :: Request -> MessageId | ||
36 | methodOfRequest Connect {} = ConnectId | ||
37 | methodOfRequest Announce {} = AnnounceId | ||
38 | methodOfRequest Scrape {} = ScrapeId | ||
39 | |||
40 | classify :: Transaction Request -> MessageClass String MessageId TransactionId SockAddr (Transaction Request) | ||
41 | classify qry = IsQuery (methodOfRequest $ request qry) (transIdQ qry) | ||
42 | |||
43 | dispatch :: DRG g => | ||
44 | DispatchMethodsA | ||
45 | (g, IntMap (Maybe (Transaction Response) -> IO ())) | ||
46 | String | ||
47 | MessageId | ||
48 | TransactionId | ||
49 | SockAddr | ||
50 | (Transaction Request) | ||
51 | (Transaction Response) | ||
52 | dispatch = DispatchMethods | ||
53 | { classifyInbound = classify | ||
54 | , lookupHandler = handlers | ||
55 | , tableMethods = transactionMethods (contramap fromEnum intMapMethods) genTransactionId | ||
56 | } | ||
57 | |||
58 | newClient :: ErrorReporter SockAddr (Transaction Request) MessageId TransactionId String | ||
59 | -> TransportA String SockAddr (Transaction Request) (Transaction Response) | ||
60 | -> IO (ClientA String | ||
61 | MessageId | ||
62 | TransactionId | ||
63 | SockAddr | ||
64 | (Transaction Request) | ||
65 | (Transaction Response)) | ||
66 | newClient err net = do | ||
67 | drg <- drgNew | ||
68 | state <- newTVarIO (drg, mempty) | ||
69 | return QR.Client | ||
70 | { clientNet = net | ||
71 | , clientDispatcher = dispatch | ||
72 | , clientErrorReporter = err | ||
73 | , clientPending = state | ||
74 | , clientAddress = const $ return localhost4 | ||
75 | , clientResponseId = return | ||
76 | } | ||
diff --git a/dht/src/Network/QueryResponse.hs b/dht/src/Network/QueryResponse.hs index 44039ee0..877c7ab6 100644 --- a/dht/src/Network/QueryResponse.hs +++ b/dht/src/Network/QueryResponse.hs | |||
@@ -271,12 +271,16 @@ data MessageClass err meth tid addr x | |||
271 | | IsUnknown err -- ^ None of the above. | 271 | | IsUnknown err -- ^ None of the above. |
272 | 272 | ||
273 | -- | Handler for an inbound query of type /x/ from an address of type _addr_. | 273 | -- | Handler for an inbound query of type /x/ from an address of type _addr_. |
274 | data MethodHandler err tid addr x = forall a b. MethodHandler | 274 | type MethodHandler err tid addr x = MethodHandlerA err tid addr x x |
275 | |||
276 | -- | Handler for an inbound query of type /x/ with outbound response of type | ||
277 | -- /y/ to an address of type /addr/. | ||
278 | data MethodHandlerA err tid addr x y = forall a b. MethodHandler | ||
275 | { -- | Parse the query into a more specific type for this method. | 279 | { -- | Parse the query into a more specific type for this method. |
276 | methodParse :: x -> Either err a | 280 | methodParse :: x -> Either err a |
277 | -- | Serialize the response for transmission, given a context /ctx/ and the origin | 281 | -- | Serialize the response for transmission, given a context /ctx/ and the origin |
278 | -- and destination addresses. | 282 | -- and destination addresses. |
279 | , methodSerialize :: tid -> addr -> addr -> b -> x | 283 | , methodSerialize :: tid -> addr -> addr -> b -> y |
280 | -- | Fully typed action to perform upon the query. The remote origin | 284 | -- | Fully typed action to perform upon the query. The remote origin |
281 | -- address of the query is provided to the handler. | 285 | -- address of the query is provided to the handler. |
282 | , methodAction :: addr -> a -> IO b | 286 | , methodAction :: addr -> a -> IO b |
@@ -290,49 +294,56 @@ data MethodHandler err tid addr x = forall a b. MethodHandler | |||
290 | , noreplyAction :: addr -> a -> IO () | 294 | , noreplyAction :: addr -> a -> IO () |
291 | } | 295 | } |
292 | 296 | ||
297 | |||
293 | -- | To dispatch responses to our outbound queries, we require three | 298 | -- | To dispatch responses to our outbound queries, we require three |
294 | -- primitives. See the 'transactionMethods' function to create these | 299 | -- primitives. See the 'transactionMethods' function to create these |
295 | -- primitives out of a lookup table and a generator for transaction ids. | 300 | -- primitives out of a lookup table and a generator for transaction ids. |
296 | -- | 301 | -- |
297 | -- The type variable /d/ is used to represent the current state of the | 302 | -- The type variable /d/ is used to represent the current state of the |
298 | -- transaction generator and the table of pending transactions. | 303 | -- transaction generator and the table of pending transactions. |
299 | data TransactionMethods d qid addr x = TransactionMethods | 304 | data TransactionMethods d qid addr y = TransactionMethods |
300 | { | 305 | { |
301 | -- | Before a query is sent, this function stores an 'MVar' to which the | 306 | -- | Before a query is sent, this function stores an 'MVar' to which the |
302 | -- response will be written too. The returned /qid/ is a transaction id | 307 | -- response will be written too. The returned /qid/ is a transaction id |
303 | -- that can be used to forget the 'MVar' if the remote peer is not | 308 | -- that can be used to forget the 'MVar' if the remote peer is not |
304 | -- responding. | 309 | -- responding. |
305 | dispatchRegister :: POSIXTime -- time of expiry | 310 | dispatchRegister :: POSIXTime -- time of expiry |
306 | -> (Maybe x -> IO ()) -- callback upon response (or timeout) | 311 | -> (Maybe y -> IO ()) -- callback upon response (or timeout) |
307 | -> addr | 312 | -> addr |
308 | -> d | 313 | -> d |
309 | -> STM (qid, d) | 314 | -> STM (qid, d) |
310 | -- | This method is invoked when an incoming packet /x/ indicates it is | 315 | -- | This method is invoked when an incoming packet /y/ indicates it is |
311 | -- a response to the transaction with id /qid/. The returned IO action | 316 | -- a response to the transaction with id /qid/. The returned IO action |
312 | -- will write the packet to the correct 'MVar' thus completing the | 317 | -- will write the packet to the correct 'MVar' thus completing the |
313 | -- dispatch. | 318 | -- dispatch. |
314 | , dispatchResponse :: qid -> x -> d -> STM (d, IO ()) | 319 | , dispatchResponse :: qid -> y -> d -> STM (d, IO ()) |
315 | -- | When a timeout interval elapses, this method is called to remove the | 320 | -- | When a timeout interval elapses, this method is called to remove the |
316 | -- transaction from the table. | 321 | -- transaction from the table. |
317 | , dispatchCancel :: qid -> d -> STM d | 322 | , dispatchCancel :: qid -> d -> STM d |
318 | } | 323 | } |
319 | 324 | ||
320 | -- | A set of methods necessary for dispatching incoming packets. | 325 | -- | A set of methods necessary for dispatching incoming packets. |
321 | data DispatchMethods tbl err meth tid addr x = DispatchMethods | 326 | type DispatchMethods tbl err meth tid addr x = DispatchMethodsA tbl err meth tid addr x x |
327 | |||
328 | -- | A set of methods necessary for dispatching incoming packets. | ||
329 | data DispatchMethodsA tbl err meth tid addr x y = DispatchMethods | ||
322 | { -- | Classify an inbound packet as a query or response. | 330 | { -- | Classify an inbound packet as a query or response. |
323 | classifyInbound :: x -> MessageClass err meth tid addr x | 331 | classifyInbound :: x -> MessageClass err meth tid addr x |
324 | -- | Lookup the handler for a inbound query. | 332 | -- | Lookup the handler for a inbound query. |
325 | , lookupHandler :: meth -> Maybe (MethodHandler err tid addr x) | 333 | , lookupHandler :: meth -> Maybe (MethodHandlerA err tid addr x y) |
326 | -- | Methods for handling incoming responses. | 334 | -- | Methods for handling incoming responses. |
327 | , tableMethods :: TransactionMethods tbl tid addr x | 335 | , tableMethods :: TransactionMethods tbl tid addr y |
328 | } | 336 | } |
329 | 337 | ||
330 | -- | All inputs required to implement a query\/response client. | 338 | -- | All inputs required to implement a query\/response client. |
331 | data Client err meth tid addr x = forall tbl. Client | 339 | type Client err meth tid addr x = ClientA err meth tid addr x x |
340 | |||
341 | -- | All inputs required to implement a query\/response client. | ||
342 | data ClientA err meth tid addr x y = forall tbl. Client | ||
332 | { -- | The 'Transport' used to dispatch and receive packets. | 343 | { -- | The 'Transport' used to dispatch and receive packets. |
333 | clientNet :: Transport err addr x | 344 | clientNet :: TransportA err addr x y |
334 | -- | Methods for handling inbound packets. | 345 | -- | Methods for handling inbound packets. |
335 | , clientDispatcher :: DispatchMethods tbl err meth tid addr x | 346 | , clientDispatcher :: DispatchMethodsA tbl err meth tid addr x y |
336 | -- | Methods for reporting various conditions. | 347 | -- | Methods for reporting various conditions. |
337 | , clientErrorReporter :: ErrorReporter addr x meth tid err | 348 | , clientErrorReporter :: ErrorReporter addr x meth tid err |
338 | -- | State necessary for routing inbound responses and assigning unique | 349 | -- | State necessary for routing inbound responses and assigning unique |
@@ -355,7 +366,7 @@ data Client err meth tid addr x = forall tbl. Client | |||
355 | -- | These four parameters are required to implement an outgoing query. A | 366 | -- | These four parameters are required to implement an outgoing query. A |
356 | -- peer-to-peer algorithm will define a 'MethodSerializer' for every 'MethodHandler' that | 367 | -- peer-to-peer algorithm will define a 'MethodSerializer' for every 'MethodHandler' that |
357 | -- might be returned by 'lookupHandler'. | 368 | -- might be returned by 'lookupHandler'. |
358 | data MethodSerializer tid addr x meth a b = MethodSerializer | 369 | data MethodSerializerA tid addr x y meth a b = MethodSerializer |
359 | { -- | Returns the microseconds to wait for a response to this query being | 370 | { -- | Returns the microseconds to wait for a response to this query being |
360 | -- sent to the given address. The /addr/ may also be modified to add | 371 | -- sent to the given address. The /addr/ may also be modified to add |
361 | -- routing information. | 372 | -- routing information. |
@@ -369,9 +380,11 @@ data MethodSerializer tid addr x meth a b = MethodSerializer | |||
369 | -- auxiliary notations on all outgoing packets. | 380 | -- auxiliary notations on all outgoing packets. |
370 | , wrapQuery :: tid -> addr -> addr -> a -> x | 381 | , wrapQuery :: tid -> addr -> addr -> a -> x |
371 | -- | Parse an inbound packet /x/ into a response /b/ for this query. | 382 | -- | Parse an inbound packet /x/ into a response /b/ for this query. |
372 | , unwrapResponse :: x -> b | 383 | , unwrapResponse :: y -> b |
373 | } | 384 | } |
374 | 385 | ||
386 | type MethodSerializer tid addr x meth a b = MethodSerializerA tid addr x x meth a b | ||
387 | |||
375 | microsecondsDiff :: Int -> POSIXTime | 388 | microsecondsDiff :: Int -> POSIXTime |
376 | microsecondsDiff us = fromIntegral us / 1000000 | 389 | microsecondsDiff us = fromIntegral us / 1000000 |
377 | 390 | ||