summaryrefslogtreecommitdiff
path: root/src/Network/DHT
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2017-06-29 10:37:07 -0400
committerjoe <joe@jerkface.net>2017-06-29 13:00:16 -0400
commit3195c0877b443e5ccd4d489f03944fc059d4d7aa (patch)
tree2a05c35a9b43d8f0725c52fc860b30ae191f3871 /src/Network/DHT
parent05e70386c2248d87a61a8e8267e0211597f2fa88 (diff)
WIP: Generalizing DHT monad.
Diffstat (limited to 'src/Network/DHT')
-rw-r--r--src/Network/DHT/Mainline.hs89
-rw-r--r--src/Network/DHT/Types.hs51
2 files changed, 94 insertions, 46 deletions
diff --git a/src/Network/DHT/Mainline.hs b/src/Network/DHT/Mainline.hs
index d118ceb0..29d4231d 100644
--- a/src/Network/DHT/Mainline.hs
+++ b/src/Network/DHT/Mainline.hs
@@ -123,6 +123,8 @@ import Network.BitTorrent.DHT.Token
123import Network.DatagramServer () 123import Network.DatagramServer ()
124#endif 124#endif
125import Network.DatagramServer.Types hiding (Query,Response) 125import Network.DatagramServer.Types hiding (Query,Response)
126import Network.DHT.Types
127import Network.DHT.Routing
126 128
127{----------------------------------------------------------------------- 129{-----------------------------------------------------------------------
128-- envelopes 130-- envelopes
@@ -140,15 +142,7 @@ read_only_key = "ro"
140 142
141 143
142#ifdef VERSION_bencoding 144#ifdef VERSION_bencoding
143-- | All queries have an \"id\" key and value containing the node ID 145instance BEncode a => BEncode (Query KMessageOf a) where
144-- of the querying node.
145data 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
151instance BEncode a => BEncode (Query a) where
152 toBEncode Query {..} = toDict $ 146 toBEncode Query {..} = toDict $
153 BDict.union ( node_id_key .=! queringNodeId 147 BDict.union ( node_id_key .=! queringNodeId
154 .: read_only_key .=? bool Nothing (Just (1 :: Integer)) queryIsReadOnly 148 .: read_only_key .=? bool Nothing (Just (1 :: Integer)) queryIsReadOnly
@@ -167,14 +161,7 @@ data Query a = Query a
167#endif 161#endif
168 162
169#ifdef VERSION_bencoding 163#ifdef VERSION_bencoding
170-- | All responses have an \"id\" key and value containing the node ID 164instance BEncode a => BEncode (Response KMessageOf a) where
171-- of the responding node.
172data Response a = Response
173 { queredNodeId :: NodeId KMessageOf -- ^ node id of /quered/ node;
174 , responseVals :: a -- ^ query result.
175 } deriving (Show, Eq, Typeable)
176
177instance BEncode a => BEncode (Response a) where
178 toBEncode = toBEncode . toQuery 165 toBEncode = toBEncode . toQuery
179 where 166 where
180 toQuery (Response nid a) = Query nid False a 167 toQuery (Response nid a) = Query nid False a
@@ -183,28 +170,23 @@ instance BEncode a => BEncode (Response a) where
183 where 170 where
184 fromQuery (Query nid _ a) = Response nid a 171 fromQuery (Query nid _ a) = Response nid a
185#else 172#else
186data Response a = Response a 173data Response KMessageOf a = Response KMessageOf a
187#endif 174#endif
188 175
189{----------------------------------------------------------------------- 176{-----------------------------------------------------------------------
190-- ping method 177-- ping method
191-----------------------------------------------------------------------} 178-----------------------------------------------------------------------}
192 179
193-- | The most basic query is a ping. Ping query is used to check if a 180-- / The most basic query is a ping. Ping query is used to check if a
194-- quered node is still alive. 181-- quered node is still alive.
195#ifdef VERSION_bencoding 182-- data Ping = Ping Tox.Nonce8 deriving (Show, Eq, Typeable)
196data Ping = Ping
197#else
198data Ping = Ping Tox.Nonce8
199#endif
200 deriving (Show, Eq, Typeable)
201 183
202#ifdef VERSION_bencoding 184#ifdef VERSION_bencoding
203instance BEncode Ping where 185instance BEncode (Ping KMessageOf) where
204 toBEncode Ping = toDict endDict 186 toBEncode Ping = toDict endDict
205 fromBEncode _ = pure Ping 187 fromBEncode _ = pure Ping
206#else 188#else
207instance Serialize (Query Ping) where 189instance Serialize (Query (Ping KMessageOf)) where
208 get = do 190 get = do
209 b <- get 191 b <- get
210 when ( (b::Word8) /= 0) $ fail "Bad ping request" 192 when ( (b::Word8) /= 0) $ fail "Bad ping request"
@@ -225,7 +207,7 @@ instance Serialize (Response Ping) where
225#endif 207#endif
226 208
227-- | \"q\" = \"ping\" 209-- | \"q\" = \"ping\"
228instance KRPC (Query Ping) (Response Ping) where 210instance KRPC (Query KMessageOf (Ping KMessageOf)) (Response KMessageOf (Ping KMessageOf)) where
229#ifdef VERSION_bencoding 211#ifdef VERSION_bencoding
230 method = "ping" 212 method = "ping"
231#else 213#else
@@ -236,24 +218,20 @@ instance KRPC (Query Ping) (Response Ping) where
236-- find_node method 218-- find_node method
237-----------------------------------------------------------------------} 219-----------------------------------------------------------------------}
238 220
239-- | Find node is used to find the contact information for a node 221-- / Find node is used to find the contact information for a node
240-- given its ID. 222-- given its ID.
241#ifdef VERSION_bencoding 223-- data FindNode KMessageOf ip = FindNode (NodeId Tox.Message) Tox.Nonce8 -- Tox: Get Nodes
242newtype FindNode ip = FindNode (NodeId KMessageOf) 224 -- deriving (Show, Eq, Typeable)
243#else
244data FindNode ip = FindNode (NodeId Tox.Message) Tox.Nonce8 -- Tox: Get Nodes
245#endif
246 deriving (Show, Eq, Typeable)
247 225
248target_key :: BKey 226target_key :: BKey
249target_key = "target" 227target_key = "target"
250 228
251#ifdef VERSION_bencoding 229#ifdef VERSION_bencoding
252instance Typeable ip => BEncode (FindNode ip) where 230instance Typeable ip => BEncode (FindNode KMessageOf ip) where
253 toBEncode (FindNode nid) = toDict $ target_key .=! nid .: endDict 231 toBEncode (FindNode nid) = toDict $ target_key .=! nid .: endDict
254 fromBEncode = fromDict $ FindNode <$>! target_key 232 fromBEncode = fromDict $ FindNode <$>! target_key
255#else 233#else
256instance Serialize (Query (FindNode ip)) where 234instance Serialize (Query KMessageOf (FindNode KMessageOf ip)) where
257 get = do 235 get = do
258 nid <- get 236 nid <- get
259 nonce <- get 237 nonce <- get
@@ -268,12 +246,11 @@ instance Serialize (Query (FindNode ip)) where
268-- nodes in its own routing table. 246-- nodes in its own routing table.
269-- 247--
270#ifdef VERSION_bencoding 248#ifdef VERSION_bencoding
271newtype NodeFound ip = NodeFound [NodeInfo KMessageOf ip ()] 249-- newtype NodeFound KMessageOf ip = NodeFound [NodeInfo KMessageOf ip ()] deriving (Show, Eq, Typeable)
272#else 250#else
273data NodeFound ip = NodeFound [Tox.NodeFormat] Tox.Nonce8 251data NodeFound KMessageOf ip = NodeFound [Tox.NodeFormat] Tox.Nonce8 deriving (Show, Eq, Typeable)
274#endif 252#endif
275-- Tox: send_nodes 253-- Tox: send_nodes
276 deriving (Show, Eq, Typeable)
277 254
278nodes_key :: BKey 255nodes_key :: BKey
279nodes_key = "nodes" 256nodes_key = "nodes"
@@ -290,7 +267,7 @@ binary k = field (req k) >>= either (fail . format) return .
290 where 267 where
291 format str = "fail to deserialize " ++ show k ++ " field: " ++ str 268 format str = "fail to deserialize " ++ show k ++ " field: " ++ str
292 269
293instance Address ip => BEncode (NodeFound ip) where 270instance Address ip => BEncode (NodeFound KMessageOf ip) where
294 toBEncode (NodeFound ns) = toDict $ 271 toBEncode (NodeFound ns) = toDict $
295 nodes_key .=! runPut (mapM_ put ns) 272 nodes_key .=! runPut (mapM_ put ns)
296 .: endDict 273 .: endDict
@@ -298,7 +275,7 @@ instance Address ip => BEncode (NodeFound ip) where
298 -- TODO: handle IPv6 by reading the "nodes6" key (see bep 32) 275 -- TODO: handle IPv6 by reading the "nodes6" key (see bep 32)
299 fromBEncode bval = NodeFound <$> (traverse from4 =<< fromDict (binary nodes_key) bval) 276 fromBEncode bval = NodeFound <$> (traverse from4 =<< fromDict (binary nodes_key) bval)
300#else 277#else
301instance Serialize (Response (NodeFound ip)) where 278instance Serialize (Response KMessageOf (NodeFound KMessageOf ip)) where
302 get = do 279 get = do
303 count <- get :: Get Word8 280 count <- get :: Get Word8
304 nodes <- sequence $ replicate (fromIntegral count) get 281 nodes <- sequence $ replicate (fromIntegral count) get
@@ -314,7 +291,7 @@ instance Serialize (Response (NodeFound ip)) where
314 291
315-- | \"q\" == \"find_node\" 292-- | \"q\" == \"find_node\"
316instance (Address ip, Typeable ip) 293instance (Address ip, Typeable ip)
317 => KRPC (Query (FindNode ip)) (Response (NodeFound ip)) where 294 => KRPC (Query KMessageOf (FindNode KMessageOf ip)) (Response KMessageOf (NodeFound KMessageOf ip)) where
318#ifdef VERSION_bencoding 295#ifdef VERSION_bencoding
319 method = "find_node" 296 method = "find_node"
320#else 297#else
@@ -383,7 +360,7 @@ instance (Typeable ip, Serialize ip) => BEncode (GotPeers ip) where
383 360
384-- | \"q" = \"get_peers\" 361-- | \"q" = \"get_peers\"
385instance (Typeable ip, Serialize ip) => 362instance (Typeable ip, Serialize ip) =>
386 KRPC (Query (GetPeers ip)) (Response (GotPeers ip)) where 363 KRPC (Query KMessageOf (GetPeers ip)) (Response KMessageOf (GotPeers ip)) where
387 method = "get_peers" 364 method = "get_peers"
388 365
389{----------------------------------------------------------------------- 366{-----------------------------------------------------------------------
@@ -455,7 +432,7 @@ instance BEncode Announced where
455 fromBEncode _ = pure Announced 432 fromBEncode _ = pure Announced
456 433
457-- | \"q" = \"announce\" 434-- | \"q" = \"announce\"
458instance KRPC (Query Announce) (Response Announced) where 435instance KRPC (Query KMessageOf Announce) (Response KMessageOf Announced) where
459 method = "announce_peer" 436 method = "announce_peer"
460 437
461-- endif VERSION_bencoding 438-- endif VERSION_bencoding
@@ -495,3 +472,25 @@ bep42 addr (NodeId r)
495 where msk | BS.length ip == 4 = ip4mask 472 where msk | BS.length ip == 4 = ip4mask
496 | otherwise = ip6mask 473 | otherwise = ip6mask
497 474
475instance Kademlia KMessageOf where
476 data Ping KMessageOf = Ping
477 deriving (Show, Eq, Typeable)
478 newtype FindNode KMessageOf ip = FindNode (NodeId KMessageOf)
479 deriving (Show, Eq, Typeable)
480 newtype NodeFound KMessageOf ip = NodeFound [NodeInfo KMessageOf ip ()]
481 deriving (Show, Eq, Typeable)
482 pingMessage _ = Ping
483 pongMessage _ = Ping
484 findNodeMessage _ k = FindNode (toNodeId k)
485 foundNodes (NodeFound ns) = ns
486
487 dhtAdjustID _ fallback ip0 arrival
488 = fromMaybe fallback $ do
489 ip <- fromSockAddr ip0 -- :: Maybe ip
490 let _ = ip `asTypeOf` nodeAddr (foreignNode arrival)
491 listToMaybe
492 $ rank id (nodeId $ foreignNode arrival)
493 $ bep42s ip fallback
494
495 namePing _ = "ping"
496 nameFindNodes _ = "find-nodes"
diff --git a/src/Network/DHT/Types.hs b/src/Network/DHT/Types.hs
index ed2dc175..79f9e1d3 100644
--- a/src/Network/DHT/Types.hs
+++ b/src/Network/DHT/Types.hs
@@ -1,8 +1,17 @@
1module Network.DHT.Types where 1{-# LANGUAGE TypeFamilies #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE StandaloneDeriving #-}
4{-# LANGUAGE FlexibleContexts #-}
5module Network.DHT.Types
6 ( module Network.DHT.Types
7 , TableKey
8 , toNodeId
9 ) where
2 10
3import Network.Socket (SockAddr) 11import Network.Socket (SockAddr)
4import Network.DatagramServer.Types 12import Network.DatagramServer.Types
5import Network.DHT.Routing 13import Network.DHT.Routing
14import Data.Typeable
6 15
7data TableParameters msg ip u = TableParameters 16data TableParameters msg ip u = TableParameters
8 { maxBuckets :: Int 17 { maxBuckets :: Int
@@ -11,3 +20,43 @@ data TableParameters msg ip u = TableParameters
11 , logMessage :: Char -> String -> IO () 20 , logMessage :: Char -> String -> IO ()
12 , adjustID :: SockAddr -> Event msg ip u -> NodeId msg 21 , adjustID :: SockAddr -> Event msg ip u -> NodeId msg
13 } 22 }
23
24-- | All queries have an \"id\" key and value containing the node ID
25-- of the querying node.
26data Query dht a = Query
27 { queringNodeId :: NodeId dht -- ^ node id of /quering/ node;
28 , queryIsReadOnly :: Bool -- ^ node is read-only as per BEP 43
29 , queryParams :: a -- ^ query parameters.
30 } deriving (Typeable)
31
32deriving instance (Eq (NodeId dht), Eq a ) => Eq (Query dht a)
33deriving instance (Show (NodeId dht), Show a ) => Show (Query dht a)
34
35-- | All responses have an \"id\" key and value containing the node ID
36-- of the responding node.
37data Response dht a = Response
38 { queredNodeId :: NodeId dht -- ^ node id of /quered/ node;
39 , responseVals :: a -- ^ query result.
40 } deriving (Typeable)
41
42deriving instance (Eq (NodeId dht), Eq a ) => Eq (Response dht a)
43deriving instance (Show (NodeId dht), Show a ) => Show (Response dht a)
44
45
46class Kademlia dht where
47 -- | The most basic query is a ping. Ping query is used to check if a
48 -- quered node is still alive.
49 data Ping dht
50 -- | Find node is used to find the contact information for a node
51 -- given its ID.
52 data FindNode dht ip
53 data NodeFound dht ip
54 pingMessage :: Proxy dht -> Ping dht
55 pongMessage :: Proxy dht -> Ping dht
56 findNodeMessage :: TableKey dht k => Proxy dht -> k -> FindNode dht ip
57 foundNodesMessage :: [NodeInfo dht ip ()] -> NodeFound dht ip
58 foundNodes :: NodeFound dht ip -> [NodeInfo dht ip ()]
59 findWho :: FindNode dht ip -> NodeId dht
60 dhtAdjustID :: Address ip => Proxy dht -> NodeId dht -> SockAddr -> Event dht ip u -> NodeId dht
61 namePing :: Proxy dht -> QueryMethod dht
62 nameFindNodes :: Proxy dht -> QueryMethod dht