diff options
Diffstat (limited to 'src/Network/BitTorrent/DHT')
-rw-r--r-- | src/Network/BitTorrent/DHT/Message.hs | 509 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Query.hs | 2 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Session.hs | 2 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Token.hs | 2 |
4 files changed, 3 insertions, 512 deletions
diff --git a/src/Network/BitTorrent/DHT/Message.hs b/src/Network/BitTorrent/DHT/Message.hs deleted file mode 100644 index e21e0e70..00000000 --- a/src/Network/BitTorrent/DHT/Message.hs +++ /dev/null | |||
@@ -1,509 +0,0 @@ | |||
1 | -- | | ||
2 | -- Copyright : (c) Sam Truzjan 2013 | ||
3 | -- License : BSD3 | ||
4 | -- Maintainer : pxqr.sta@gmail.com | ||
5 | -- Stability : experimental | ||
6 | -- Portability : portable | ||
7 | -- | ||
8 | -- This module provides message datatypes which is used for /Node to | ||
9 | -- Node/ communication. Bittorrent DHT is based on Kademlia | ||
10 | -- specification, but have a slightly different set of messages | ||
11 | -- which have been adopted for /peer/ discovery mechanism. Messages | ||
12 | -- are sent over "Network.KRPC" protocol, but normally you should | ||
13 | -- use "Network.BitTorrent.DHT.Session" to send and receive | ||
14 | -- messages. | ||
15 | -- | ||
16 | -- DHT queries are not /recursive/, they are /iterative/. This means | ||
17 | -- that /querying/ node . While original specification (namely BEP5) | ||
18 | -- do not impose any restrictions for /quered/ node behaviour, a | ||
19 | -- good DHT implementation should follow some rules to guarantee | ||
20 | -- that unlimit recursion will never happen. The following set of | ||
21 | -- restrictions: | ||
22 | -- | ||
23 | -- * 'Ping' query must not trigger any message. | ||
24 | -- | ||
25 | -- * 'FindNode' query /may/ trigger 'Ping' query to check if a | ||
26 | -- list of nodes to return is /good/. See | ||
27 | -- 'Network.DHT.Routing.Routing' for further explanation. | ||
28 | -- | ||
29 | -- * 'GetPeers' query may trigger 'Ping' query for the same reason. | ||
30 | -- | ||
31 | -- * 'Announce' query must trigger 'Ping' query for the same reason. | ||
32 | -- | ||
33 | -- It is easy to see that the most long RPC chain is: | ||
34 | -- | ||
35 | -- @ | ||
36 | -- | | | | ||
37 | -- Node_A | | | ||
38 | -- | FindNode or GetPeers or Announce | | | ||
39 | -- | ------------------------------------> Node_B | | ||
40 | -- | | Ping | | ||
41 | -- | | -----------> | | ||
42 | -- | | Node_C | ||
43 | -- | | Pong | | ||
44 | -- | NodeFound or GotPeers or Announced | <----------- | | ||
45 | -- | <------------------------------------- Node_B | | ||
46 | -- Node_A | | | ||
47 | -- | | | | ||
48 | -- @ | ||
49 | -- | ||
50 | -- where in some cases 'Node_C' is 'Node_A'. | ||
51 | -- | ||
52 | -- For more info see: | ||
53 | -- <http://www.bittorrent.org/beps/bep_0005.html#dht-queries> | ||
54 | -- | ||
55 | -- For Kamelia messages see original Kademlia paper: | ||
56 | -- <http://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf> | ||
57 | -- | ||
58 | {-# LANGUAGE CPP #-} | ||
59 | {-# LANGUAGE DeriveDataTypeable #-} | ||
60 | {-# LANGUAGE FlexibleInstances #-} | ||
61 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
62 | {-# LANGUAGE UndecidableInstances #-} | ||
63 | {-# LANGUAGE ScopedTypeVariables #-} | ||
64 | {-# LANGUAGE TypeFamilies #-} | ||
65 | module Network.BitTorrent.DHT.Message | ||
66 | ( -- * Envelopes | ||
67 | Query (..) | ||
68 | , Response (..) | ||
69 | |||
70 | -- * Queries | ||
71 | -- ** ping | ||
72 | , Ping (..) | ||
73 | |||
74 | -- ** find_node | ||
75 | , FindNode (..) | ||
76 | , NodeFound (..) | ||
77 | , bep42s | ||
78 | -- , bep42 | ||
79 | |||
80 | |||
81 | #ifdef VERSION_bencoding | ||
82 | -- ** get_peers | ||
83 | , PeerList | ||
84 | , GetPeers (..) | ||
85 | , GotPeers (..) | ||
86 | |||
87 | -- ** announce_peer | ||
88 | , Announce (..) | ||
89 | , Announced (..) | ||
90 | #endif | ||
91 | ) where | ||
92 | |||
93 | import Control.Applicative | ||
94 | import Data.Bool | ||
95 | #ifdef VERSION_bencoding | ||
96 | import Data.BEncode as BE | ||
97 | import Data.BEncode.BDict as BDict hiding (map) | ||
98 | #else | ||
99 | import qualified Network.DatagramServer.Tox as Tox | ||
100 | import Network.DatagramServer.Tox (NodeId) | ||
101 | import Data.Word | ||
102 | import Control.Monad | ||
103 | #endif | ||
104 | import Network.KRPC.Method | ||
105 | import Network.Address hiding (NodeId) | ||
106 | import Data.Bits | ||
107 | import Data.ByteString (ByteString) | ||
108 | import qualified Data.ByteString as BS | ||
109 | import Data.Digest.CRC32C | ||
110 | import Data.List as L | ||
111 | import Data.Monoid | ||
112 | import Data.Serialize as S | ||
113 | import Data.Typeable | ||
114 | import Data.Word | ||
115 | import Network | ||
116 | import Network.DatagramServer | ||
117 | import Network.DatagramServer.Mainline | ||
118 | import Data.Maybe | ||
119 | |||
120 | import Data.Torrent (InfoHash) | ||
121 | import Network.BitTorrent.DHT.Token | ||
122 | #ifdef VERSION_bencoding | ||
123 | import Network.DatagramServer () | ||
124 | #endif | ||
125 | import Network.DatagramServer.Types hiding (Query,Response) | ||
126 | |||
127 | {----------------------------------------------------------------------- | ||
128 | -- envelopes | ||
129 | -----------------------------------------------------------------------} | ||
130 | |||
131 | #ifndef VERSION_bencoding | ||
132 | type BKey = ByteString | ||
133 | #endif | ||
134 | |||
135 | node_id_key :: BKey | ||
136 | node_id_key = "id" | ||
137 | |||
138 | read_only_key :: BKey | ||
139 | read_only_key = "ro" | ||
140 | |||
141 | |||
142 | #ifdef VERSION_bencoding | ||
143 | -- | All queries have an \"id\" key and value containing the node ID | ||
144 | -- of the querying node. | ||
145 | data Query a = Query | ||
146 | { queringNodeId :: NodeId KMessageOf -- ^ node id of /quering/ node; | ||
147 | , queryIsReadOnly :: Bool -- ^ node is read-only as per BEP 43 | ||
148 | , queryParams :: a -- ^ query parameters. | ||
149 | } deriving (Show, Eq, Typeable) | ||
150 | |||
151 | instance BEncode a => BEncode (Query a) where | ||
152 | toBEncode Query {..} = toDict $ | ||
153 | BDict.union ( node_id_key .=! queringNodeId | ||
154 | .: read_only_key .=? bool Nothing (Just (1 :: Integer)) queryIsReadOnly | ||
155 | .: endDict) | ||
156 | (dict (toBEncode queryParams)) | ||
157 | where | ||
158 | dict (BDict d) = d | ||
159 | dict _ = error "impossible: instance BEncode (Query a)" | ||
160 | |||
161 | fromBEncode v = do | ||
162 | Query <$> fromDict (field (req node_id_key)) v | ||
163 | <*> fromDict (fromMaybe False <$>? read_only_key) v | ||
164 | <*> fromBEncode v | ||
165 | #else | ||
166 | data Query a = Query a | ||
167 | #endif | ||
168 | |||
169 | #ifdef VERSION_bencoding | ||
170 | -- | All responses have an \"id\" key and value containing the node ID | ||
171 | -- of the responding node. | ||
172 | data Response a = Response | ||
173 | { queredNodeId :: NodeId KMessageOf -- ^ node id of /quered/ node; | ||
174 | , responseVals :: a -- ^ query result. | ||
175 | } deriving (Show, Eq, Typeable) | ||
176 | |||
177 | instance BEncode a => BEncode (Response a) where | ||
178 | toBEncode = toBEncode . toQuery | ||
179 | where | ||
180 | toQuery (Response nid a) = Query nid False a | ||
181 | |||
182 | fromBEncode b = fromQuery <$> fromBEncode b | ||
183 | where | ||
184 | fromQuery (Query nid _ a) = Response nid a | ||
185 | #else | ||
186 | data Response a = Response a | ||
187 | #endif | ||
188 | |||
189 | {----------------------------------------------------------------------- | ||
190 | -- ping method | ||
191 | -----------------------------------------------------------------------} | ||
192 | |||
193 | -- | The most basic query is a ping. Ping query is used to check if a | ||
194 | -- quered node is still alive. | ||
195 | #ifdef VERSION_bencoding | ||
196 | data Ping = Ping | ||
197 | #else | ||
198 | data Ping = Ping Tox.Nonce8 | ||
199 | #endif | ||
200 | deriving (Show, Eq, Typeable) | ||
201 | |||
202 | #ifdef VERSION_bencoding | ||
203 | instance BEncode Ping where | ||
204 | toBEncode Ping = toDict endDict | ||
205 | fromBEncode _ = pure Ping | ||
206 | #else | ||
207 | instance Serialize (Query Ping) where | ||
208 | get = do | ||
209 | b <- get | ||
210 | when ( (b::Word8) /= 0) $ fail "Bad ping request" | ||
211 | nonce <- get | ||
212 | return $ Query (Ping nonce) | ||
213 | put (Query (Ping nonce)) = do | ||
214 | put (0 :: Word8) | ||
215 | put nonce | ||
216 | instance Serialize (Response Ping) where | ||
217 | get = do | ||
218 | b <- get | ||
219 | when ( (b::Word8) /= 1) $ fail "Bad ping response" | ||
220 | nonce <- get | ||
221 | return $ Response (Ping nonce) | ||
222 | put (Response (Ping nonce)) = do | ||
223 | put (1 :: Word8) | ||
224 | put nonce | ||
225 | #endif | ||
226 | |||
227 | -- | \"q\" = \"ping\" | ||
228 | instance KRPC (Query Ping) (Response Ping) where | ||
229 | #ifdef VERSION_bencoding | ||
230 | type Envelope (Query Ping) (Response Ping) = BValue | ||
231 | seal = toBEncode | ||
232 | unseal = fromBEncode | ||
233 | method = "ping" | ||
234 | #else | ||
235 | method = Method Tox.Ping -- response: Tox.Pong | ||
236 | #endif | ||
237 | |||
238 | {----------------------------------------------------------------------- | ||
239 | -- find_node method | ||
240 | -----------------------------------------------------------------------} | ||
241 | |||
242 | -- | Find node is used to find the contact information for a node | ||
243 | -- given its ID. | ||
244 | #ifdef VERSION_bencoding | ||
245 | newtype FindNode ip = FindNode (NodeId KMessageOf) | ||
246 | #else | ||
247 | data FindNode ip = FindNode (NodeId Tox.Message) Tox.Nonce8 -- Tox: Get Nodes | ||
248 | #endif | ||
249 | deriving (Show, Eq, Typeable) | ||
250 | |||
251 | target_key :: BKey | ||
252 | target_key = "target" | ||
253 | |||
254 | #ifdef VERSION_bencoding | ||
255 | instance Typeable ip => BEncode (FindNode ip) where | ||
256 | toBEncode (FindNode nid) = toDict $ target_key .=! nid .: endDict | ||
257 | fromBEncode = fromDict $ FindNode <$>! target_key | ||
258 | #else | ||
259 | instance Serialize (Query (FindNode ip)) where | ||
260 | get = do | ||
261 | nid <- get | ||
262 | nonce <- get | ||
263 | return $ Query (FindNode nid nonce) | ||
264 | put (Query (FindNode nid nonce)) = do | ||
265 | put nid | ||
266 | put nonce | ||
267 | #endif | ||
268 | |||
269 | -- | When a node receives a 'FindNode' query, it should respond with a | ||
270 | -- the compact node info for the target node or the K (8) closest good | ||
271 | -- nodes in its own routing table. | ||
272 | -- | ||
273 | #ifdef VERSION_bencoding | ||
274 | newtype NodeFound ip = NodeFound [NodeInfo KMessageOf ip ()] | ||
275 | #else | ||
276 | data NodeFound ip = NodeFound [Tox.NodeFormat] Tox.Nonce8 | ||
277 | #endif | ||
278 | -- Tox: send_nodes | ||
279 | deriving (Show, Eq, Typeable) | ||
280 | |||
281 | nodes_key :: BKey | ||
282 | nodes_key = "nodes" | ||
283 | |||
284 | -- Convert IPv4 address. Useful for using variadic IP type. | ||
285 | from4 :: forall dht u s. Address s => NodeInfo dht IPv4 u -> Either String (NodeInfo dht s u) | ||
286 | from4 n = maybe (Left "Error converting IPv4") Right | ||
287 | $ traverseAddress (fromAddr :: IPv4 -> Maybe s) n | ||
288 | |||
289 | #ifdef VERSION_bencoding | ||
290 | binary :: Serialize a => BKey -> BE.Get [a] | ||
291 | binary k = field (req k) >>= either (fail . format) return . | ||
292 | runGet (many get) | ||
293 | where | ||
294 | format str = "fail to deserialize " ++ show k ++ " field: " ++ str | ||
295 | |||
296 | instance Address ip => BEncode (NodeFound ip) where | ||
297 | toBEncode (NodeFound ns) = toDict $ | ||
298 | nodes_key .=! runPut (mapM_ put ns) | ||
299 | .: endDict | ||
300 | |||
301 | -- TODO: handle IPv6 by reading the "nodes6" key (see bep 32) | ||
302 | fromBEncode bval = NodeFound <$> (traverse from4 =<< fromDict (binary nodes_key) bval) | ||
303 | #else | ||
304 | instance Serialize (Response (NodeFound ip)) where | ||
305 | get = do | ||
306 | count <- get :: Get Word8 | ||
307 | nodes <- sequence $ replicate (fromIntegral count) get | ||
308 | nonce <- get :: Get Tox.Nonce8 | ||
309 | return $ Response $ NodeFound nodes nonce | ||
310 | |||
311 | put (Response (NodeFound nodes nonce)) = do | ||
312 | put (fromIntegral (length nodes) :: Word8) | ||
313 | mapM_ put nodes | ||
314 | put nonce | ||
315 | |||
316 | #endif | ||
317 | |||
318 | -- | \"q\" == \"find_node\" | ||
319 | instance (Address ip, Typeable ip) | ||
320 | => KRPC (Query (FindNode ip)) (Response (NodeFound ip)) where | ||
321 | #ifdef VERSION_bencoding | ||
322 | type Envelope (Query (FindNode ip)) (Response (NodeFound ip)) = BValue | ||
323 | seal = toBEncode | ||
324 | unseal = fromBEncode | ||
325 | method = "find_node" | ||
326 | #else | ||
327 | method = Method Tox.GetNodes -- response: Tox.SendNodes | ||
328 | #endif | ||
329 | |||
330 | #ifdef VERSION_bencoding | ||
331 | {----------------------------------------------------------------------- | ||
332 | -- get_peers method | ||
333 | -----------------------------------------------------------------------} | ||
334 | |||
335 | -- | Get peers associated with a torrent infohash. | ||
336 | newtype GetPeers ip = GetPeers InfoHash | ||
337 | deriving (Show, Eq, Typeable) | ||
338 | |||
339 | info_hash_key :: BKey | ||
340 | info_hash_key = "info_hash" | ||
341 | |||
342 | instance Typeable ip => BEncode (GetPeers ip) where | ||
343 | toBEncode (GetPeers ih) = toDict $ info_hash_key .=! ih .: endDict | ||
344 | fromBEncode = fromDict $ GetPeers <$>! info_hash_key | ||
345 | |||
346 | type PeerList ip = Either [NodeInfo KMessageOf ip ()] [PeerAddr ip] | ||
347 | |||
348 | data GotPeers ip = GotPeers | ||
349 | { -- | If the queried node has no peers for the infohash, returned | ||
350 | -- the K nodes in the queried nodes routing table closest to the | ||
351 | -- infohash supplied in the query. | ||
352 | peers :: PeerList ip | ||
353 | |||
354 | -- | The token value is a required argument for a future | ||
355 | -- announce_peer query. | ||
356 | , grantedToken :: Token | ||
357 | } deriving (Show, Eq, Typeable) | ||
358 | |||
359 | peers_key :: BKey | ||
360 | peers_key = "values" | ||
361 | |||
362 | token_key :: BKey | ||
363 | token_key = "token" | ||
364 | |||
365 | name_key :: BKey | ||
366 | name_key = "name" | ||
367 | |||
368 | instance (Typeable ip, Serialize ip) => BEncode (GotPeers ip) where | ||
369 | toBEncode GotPeers {..} = toDict $ | ||
370 | case peers of | ||
371 | Left ns -> | ||
372 | nodes_key .=! runPut (mapM_ put ns) | ||
373 | .: token_key .=! grantedToken | ||
374 | .: endDict | ||
375 | Right ps -> | ||
376 | token_key .=! grantedToken | ||
377 | .: peers_key .=! L.map S.encode ps | ||
378 | .: endDict | ||
379 | |||
380 | fromBEncode = fromDict $ do | ||
381 | mns <- optional (binary nodes_key) -- "nodes" | ||
382 | tok <- field (req token_key) -- "token" | ||
383 | mps <- optional (field (req peers_key) >>= decodePeers) -- "values" | ||
384 | case (Right <$> mps) <|> (Left <$> mns) of | ||
385 | Nothing -> fail "get_peers: neihter peers nor nodes key is valid" | ||
386 | Just xs -> pure $ GotPeers xs tok | ||
387 | where | ||
388 | decodePeers = either fail pure . mapM S.decode | ||
389 | |||
390 | -- | \"q" = \"get_peers\" | ||
391 | instance (Typeable ip, Serialize ip) => | ||
392 | KRPC (Query (GetPeers ip)) (Response (GotPeers ip)) where | ||
393 | type Envelope (Query (GetPeers ip)) (Response (GotPeers ip)) = BValue | ||
394 | seal = toBEncode | ||
395 | unseal = fromBEncode | ||
396 | method = "get_peers" | ||
397 | |||
398 | {----------------------------------------------------------------------- | ||
399 | -- announce method | ||
400 | -----------------------------------------------------------------------} | ||
401 | |||
402 | -- | Announce that the peer, controlling the querying node, is | ||
403 | -- downloading a torrent on a port. | ||
404 | data Announce = Announce | ||
405 | { -- | If set, the 'port' field should be ignored and the source | ||
406 | -- port of the UDP packet should be used as the peer's port | ||
407 | -- instead. This is useful for peers behind a NAT that may not | ||
408 | -- know their external port, and supporting uTP, they accept | ||
409 | -- incoming connections on the same port as the DHT port. | ||
410 | impliedPort :: Bool | ||
411 | |||
412 | -- | infohash of the torrent; | ||
413 | , topic :: InfoHash | ||
414 | |||
415 | -- | some clients announce the friendly name of the torrent here. | ||
416 | , announcedName :: Maybe ByteString | ||
417 | |||
418 | -- | the port /this/ peer is listening; | ||
419 | , port :: PortNumber | ||
420 | |||
421 | -- TODO: optional boolean "seed" key | ||
422 | |||
423 | -- | received in response to a previous get_peers query. | ||
424 | , sessionToken :: Token | ||
425 | |||
426 | } deriving (Show, Eq, Typeable) | ||
427 | |||
428 | port_key :: BKey | ||
429 | port_key = "port" | ||
430 | |||
431 | implied_port_key :: BKey | ||
432 | implied_port_key = "implied_port" | ||
433 | |||
434 | instance BEncode Announce where | ||
435 | toBEncode Announce {..} = toDict $ | ||
436 | implied_port_key .=? flagField impliedPort | ||
437 | .: info_hash_key .=! topic | ||
438 | .: name_key .=? announcedName | ||
439 | .: port_key .=! port | ||
440 | .: token_key .=! sessionToken | ||
441 | .: endDict | ||
442 | where | ||
443 | flagField flag = if flag then Just (1 :: Int) else Nothing | ||
444 | |||
445 | fromBEncode = fromDict $ do | ||
446 | Announce <$> (boolField <$> optional (field (req implied_port_key))) | ||
447 | <*>! info_hash_key | ||
448 | <*>? name_key | ||
449 | <*>! port_key | ||
450 | <*>! token_key | ||
451 | where | ||
452 | boolField = maybe False (/= (0 :: Int)) | ||
453 | |||
454 | -- | The queried node must verify that the token was previously sent | ||
455 | -- to the same IP address as the querying node. Then the queried node | ||
456 | -- should store the IP address of the querying node and the supplied | ||
457 | -- port number under the infohash in its store of peer contact | ||
458 | -- information. | ||
459 | data Announced = Announced | ||
460 | deriving (Show, Eq, Typeable) | ||
461 | |||
462 | instance BEncode Announced where | ||
463 | toBEncode _ = toBEncode Ping | ||
464 | fromBEncode _ = pure Announced | ||
465 | |||
466 | -- | \"q" = \"announce\" | ||
467 | instance KRPC (Query Announce) (Response Announced) where | ||
468 | type Envelope (Query Announce) (Response Announced) = BValue | ||
469 | seal = toBEncode | ||
470 | unseal = fromBEncode | ||
471 | method = "announce_peer" | ||
472 | |||
473 | -- endif VERSION_bencoding | ||
474 | #endif | ||
475 | |||
476 | -- | Yields all 8 DHT neighborhoods available to you given a particular ip | ||
477 | -- address. | ||
478 | bep42s :: Address a => a -> NodeId KMessageOf -> [NodeId KMessageOf] | ||
479 | bep42s addr (NodeId r) = mapMaybe (bep42 addr) rs | ||
480 | where | ||
481 | rs = map (NodeId . change3bits r) [0..7] | ||
482 | |||
483 | -- change3bits :: ByteString -> Word8 -> ByteString | ||
484 | -- change3bits bs n = BS.snoc (BS.init bs) (BS.last bs .&. 0xF8 .|. n) | ||
485 | |||
486 | change3bits :: (Num b, Bits b) => b -> b -> b | ||
487 | change3bits bs n = (bs .&. complement 7) .|. n | ||
488 | |||
489 | -- | Modifies a purely random 'NodeId' to one that is related to a given | ||
490 | -- routable address in accordance with BEP 42. | ||
491 | bep42 :: Address a => a -> NodeId KMessageOf -> Maybe (NodeId KMessageOf) | ||
492 | bep42 addr (NodeId r) | ||
493 | | Just ip <- fmap S.encode (fromAddr addr :: Maybe IPv4) | ||
494 | <|> fmap S.encode (fromAddr addr :: Maybe IPv6) | ||
495 | = genBucketSample' retr (NodeId $ crc $ applyMask ip) (3,0x07,0) | ||
496 | | otherwise | ||
497 | = Nothing | ||
498 | where | ||
499 | ip4mask = "\x03\x0f\x3f\xff" :: ByteString | ||
500 | ip6mask = "\x01\x03\x07\x0f\x1f\x3f\x7f\xff" :: ByteString | ||
501 | nbhood_select = (fromIntegral r :: Word8) .&. 7 | ||
502 | retr n = pure $ BS.drop (nodeIdSize - n) $ S.encode r | ||
503 | crc = flip shiftL (finiteBitSize (NodeId undefined) - 32) . fromIntegral . crc32c . BS.pack | ||
504 | applyMask ip = case BS.zipWith (.&.) msk ip of | ||
505 | (b:bs) -> (b .|. shiftL nbhood_select 5) : bs | ||
506 | bs -> bs | ||
507 | where msk | BS.length ip == 4 = ip4mask | ||
508 | | otherwise = ip6mask | ||
509 | |||
diff --git a/src/Network/BitTorrent/DHT/Query.hs b/src/Network/BitTorrent/DHT/Query.hs index edc238e6..ae072db0 100644 --- a/src/Network/BitTorrent/DHT/Query.hs +++ b/src/Network/BitTorrent/DHT/Query.hs | |||
@@ -86,7 +86,7 @@ import Network.KRPC.Method as KRPC | |||
86 | import Network.DatagramServer.Mainline (ReflectedIP(..)) | 86 | import Network.DatagramServer.Mainline (ReflectedIP(..)) |
87 | import Network.DatagramServer (QueryFailure(..)) | 87 | import Network.DatagramServer (QueryFailure(..)) |
88 | import Data.Torrent | 88 | import Data.Torrent |
89 | import Network.BitTorrent.DHT.Message | 89 | import Network.DHT.Mainline |
90 | import Network.DHT.Routing as R | 90 | import Network.DHT.Routing as R |
91 | import Network.BitTorrent.DHT.Session | 91 | import Network.BitTorrent.DHT.Session |
92 | import Control.Concurrent.STM | 92 | import Control.Concurrent.STM |
diff --git a/src/Network/BitTorrent/DHT/Session.hs b/src/Network/BitTorrent/DHT/Session.hs index e205add4..2d290a95 100644 --- a/src/Network/BitTorrent/DHT/Session.hs +++ b/src/Network/BitTorrent/DHT/Session.hs | |||
@@ -116,7 +116,7 @@ import Network.DatagramServer.Tox as Tox | |||
116 | import Network.Address | 116 | import Network.Address |
117 | import Network.BitTorrent.DHT.ContactInfo (PeerStore) | 117 | import Network.BitTorrent.DHT.ContactInfo (PeerStore) |
118 | import qualified Network.BitTorrent.DHT.ContactInfo as P | 118 | import qualified Network.BitTorrent.DHT.ContactInfo as P |
119 | import Network.BitTorrent.DHT.Message | 119 | import Network.DHT.Mainline |
120 | import Network.DHT.Routing as R | 120 | import Network.DHT.Routing as R |
121 | import Network.BitTorrent.DHT.Token as T | 121 | import Network.BitTorrent.DHT.Token as T |
122 | 122 | ||
diff --git a/src/Network/BitTorrent/DHT/Token.hs b/src/Network/BitTorrent/DHT/Token.hs index 75eadd1a..e3a6b1f6 100644 --- a/src/Network/BitTorrent/DHT/Token.hs +++ b/src/Network/BitTorrent/DHT/Token.hs | |||
@@ -109,7 +109,7 @@ lookup addr TokenMap {..} = makeToken addr curSecret | |||
109 | 109 | ||
110 | -- | Check if token is valid. | 110 | -- | Check if token is valid. |
111 | -- | 111 | -- |
112 | -- Typically used to handle 'Network.BitTorrent.DHT.Message.Announce' | 112 | -- Typically used to handle 'Network.DHT.Mainline.Announce' |
113 | -- query. If token is invalid the 'Network.KRPC.ProtocolError' should | 113 | -- query. If token is invalid the 'Network.KRPC.ProtocolError' should |
114 | -- be sent back to the malicious node. | 114 | -- be sent back to the malicious node. |
115 | member :: Hashable a => NodeAddr a -> Token -> TokenMap -> Bool | 115 | member :: Hashable a => NodeAddr a -> Token -> TokenMap -> Bool |