diff options
-rw-r--r-- | src/Data/Kademlia/Common.hs | 48 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT.hs | 165 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Server.hs | 28 | ||||
-rw-r--r-- | src/Network/BitTorrent/Internal.lhs | 2 |
4 files changed, 164 insertions, 79 deletions
diff --git a/src/Data/Kademlia/Common.hs b/src/Data/Kademlia/Common.hs deleted file mode 100644 index 874120d8..00000000 --- a/src/Data/Kademlia/Common.hs +++ /dev/null | |||
@@ -1,48 +0,0 @@ | |||
1 | {-# OPTIONS -fno-warn-orphans #-} | ||
2 | {-# LANGUAGE RecordWildCards #-} | ||
3 | module Data.Kademlia.Common | ||
4 | (NodeID, NodeInfo | ||
5 | ) where | ||
6 | |||
7 | import Control.Applicative | ||
8 | import Data.ByteString | ||
9 | import Network | ||
10 | import Network.Socket | ||
11 | import Data.Serialize | ||
12 | |||
13 | |||
14 | type NodeID = ByteString | ||
15 | type Distance = NodeID | ||
16 | |||
17 | -- WARN is the 'system' random suitable for this? | ||
18 | -- | Generate random NodeID used for the entire session. | ||
19 | -- Distribution of ID's should be as uniform as possible. | ||
20 | -- | ||
21 | genNodeID :: IO NodeID | ||
22 | genNodeID = undefined -- randomIO | ||
23 | |||
24 | instance Serialize PortNumber where | ||
25 | get = fromIntegral <$> getWord16be | ||
26 | put = putWord16be . fromIntegral | ||
27 | |||
28 | |||
29 | data NodeAddr = NodeAddr { | ||
30 | nodeIP :: HostAddress | ||
31 | , nodePort :: PortNumber | ||
32 | } deriving (Show, Eq) | ||
33 | |||
34 | instance Serialize NodeAddr where | ||
35 | get = NodeAddr <$> getWord32be <*> get | ||
36 | put NodeAddr {..} = do | ||
37 | putWord32be nodeIP | ||
38 | put nodePort | ||
39 | |||
40 | |||
41 | data NodeInfo = NodeInfo { | ||
42 | nodeID :: NodeID | ||
43 | , nodeAddr :: NodeAddr | ||
44 | } deriving (Show, Eq) | ||
45 | |||
46 | instance Serialize NodeInfo where | ||
47 | get = NodeInfo <$> getByteString 20 <*> get | ||
48 | put NodeInfo {..} = put nodeID >> put nodeAddr | ||
diff --git a/src/Network/BitTorrent/DHT.hs b/src/Network/BitTorrent/DHT.hs index 2d2073f0..60f4566a 100644 --- a/src/Network/BitTorrent/DHT.hs +++ b/src/Network/BitTorrent/DHT.hs | |||
@@ -1,14 +1,175 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | 1 | {-# LANGUAGE OverloadedStrings #-} |
2 | {-# LANGUAGE RecordWildCards #-} | ||
2 | module Network.BitTorrent.DHT | 3 | module Network.BitTorrent.DHT |
3 | ( | 4 | ( dhtServer |
4 | ) where | 5 | ) where |
5 | 6 | ||
7 | import Control.Applicative | ||
8 | import Control.Concurrent.STM | ||
6 | import Data.ByteString | 9 | import Data.ByteString |
10 | import Data.Serialize as S | ||
11 | import Data.Function | ||
12 | import Data.Ord | ||
13 | import Data.HashMap.Strict as HM | ||
14 | |||
7 | import Network | 15 | import Network |
16 | import Network.Socket | ||
17 | import Remote.KRPC | ||
8 | 18 | ||
19 | import Data.BEncode | ||
20 | import Data.Torrent | ||
9 | import Data.Kademlia.Routing.Table | 21 | import Data.Kademlia.Routing.Table |
10 | 22 | ||
23 | {----------------------------------------------------------------------- | ||
24 | Node | ||
25 | -----------------------------------------------------------------------} | ||
26 | |||
27 | type NodeId = ByteString | ||
28 | |||
29 | -- WARN is the 'system' random suitable for this? | ||
30 | -- | Generate random NodeID used for the entire session. | ||
31 | -- Distribution of ID's should be as uniform as possible. | ||
32 | -- | ||
33 | genNodeID :: IO NodeId | ||
34 | genNodeID = undefined -- randomIO | ||
35 | |||
36 | instance Serialize PortNumber where | ||
37 | get = fromIntegral <$> getWord16be | ||
38 | put = putWord16be . fromIntegral | ||
39 | |||
40 | |||
41 | data NodeAddr = NodeAddr { | ||
42 | nodeIP :: !HostAddress | ||
43 | , nodePort :: !PortNumber | ||
44 | } deriving (Show, Eq) | ||
45 | |||
46 | instance Serialize NodeAddr where | ||
47 | get = NodeAddr <$> getWord32be <*> get | ||
48 | put NodeAddr {..} = do | ||
49 | putWord32be nodeIP | ||
50 | put nodePort | ||
51 | |||
52 | |||
53 | data NodeInfo = NodeInfo { | ||
54 | nodeID :: !NodeId | ||
55 | , nodeAddr :: !NodeAddr | ||
56 | } deriving (Show, Eq) | ||
57 | |||
58 | instance Serialize NodeInfo where | ||
59 | get = NodeInfo <$> getByteString 20 <*> get | ||
60 | put NodeInfo {..} = put nodeID >> put nodeAddr | ||
61 | |||
62 | type CompactInfo = ByteString | ||
63 | |||
64 | decodeCompact :: CompactInfo -> Either String [NodeInfo] | ||
65 | decodeCompact = S.runGet (many get) | ||
66 | |||
67 | encodeCompact :: [NodeId] -> CompactInfo | ||
68 | encodeCompact = S.runPut . mapM_ put | ||
69 | |||
70 | type Distance = NodeId | ||
71 | |||
11 | --type DHT k v = StateT (Table k v) IO | 72 | --type DHT k v = StateT (Table k v) IO |
12 | 73 | ||
13 | --findNode :: NodeID -> DHT k v [NodeInfo] | 74 | --findNode :: NodeID -> DHT k v [NodeInfo] |
14 | --findNode = undefined \ No newline at end of file | 75 | --findNode = undefined |
76 | |||
77 | genSecret :: IO Secret | ||
78 | genSecret = undefined | ||
79 | |||
80 | type Token = Int | ||
81 | type Secret = Int | ||
82 | |||
83 | token :: NodeAddr -> Secret -> Token | ||
84 | token = return undefined | ||
85 | |||
86 | defaultToken :: Token | ||
87 | defaultToken = 0xdeadbeef | ||
88 | |||
89 | {----------------------------------------------------------------------- | ||
90 | Routing table | ||
91 | -----------------------------------------------------------------------} | ||
92 | |||
93 | -- TODO use more compact routing table | ||
94 | type RoutingTable = HashMap NodeId NodeAddr | ||
95 | |||
96 | type Alpha = Int | ||
97 | |||
98 | kclosest :: Int -> NodeId -> RoutingTable -> [NodeId] | ||
99 | kclosest = undefined | ||
100 | |||
101 | {----------------------------------------------------------------------- | ||
102 | Node session | ||
103 | -----------------------------------------------------------------------} | ||
104 | |||
105 | data NodeSession = NodeSession { | ||
106 | nodeId :: !NodeId | ||
107 | , routingTable :: !(TVar RoutingTable) | ||
108 | , alpha :: !Alpha | ||
109 | } | ||
110 | |||
111 | instance Eq NodeSession where | ||
112 | (==) = (==) `on` nodeId | ||
113 | |||
114 | instance Ord NodeSession where | ||
115 | compare = comparing nodeId | ||
116 | |||
117 | {----------------------------------------------------------------------- | ||
118 | Queries | ||
119 | -----------------------------------------------------------------------} | ||
120 | |||
121 | instance BEncodable PortNumber where | ||
122 | |||
123 | pingM :: Method NodeId NodeId | ||
124 | pingM = method "ping" ["id"] ["id"] | ||
125 | |||
126 | findNodeM :: Method (NodeId, NodeId) (NodeId, CompactInfo) | ||
127 | findNodeM = method "find_node" ["id", "target"] ["id", "nodes"] | ||
128 | |||
129 | -- | Lookup peers by a torrent infohash. | ||
130 | getPeersM :: Method (NodeId, InfoHash) (NodeId, Token, CompactInfo) -- use Map ByteString BEncode | ||
131 | getPeersM = method "get_peers" ["id", "info_hash"] ["id", "token", "nodes"] | ||
132 | |||
133 | -- | Used to announce that the peer, controlling the quering node is | ||
134 | -- downloading a torrent on a port. | ||
135 | announcePeerM :: Method (NodeId, InfoHash, PortNumber, Token) NodeId | ||
136 | announcePeerM = method "announce_peer" ["id", "info_hash", "port", "token"] ["id"] | ||
137 | |||
138 | pingC :: NodeSession -> NodeAddr -> IO () | ||
139 | pingC NodeSession {..} addr @ NodeAddr {..} = do | ||
140 | nid <- call (nodeIP, nodePort) pingM nodeId | ||
141 | atomically $ modifyTVar' routingTable $ HM.insert nid addr | ||
142 | |||
143 | --getPeerC :: NodeSession -> NodeAddr -> InfoHash -> IO (Either CompactInfo ) | ||
144 | getPeerC NodeSession {..} addr @ NodeAddr {..} ih = do | ||
145 | call (nodeIP, nodePort) getPeersM | ||
146 | |||
147 | type ServerHandler a b = NodeSession -> NodeAddr -> a -> IO b | ||
148 | |||
149 | pingS :: ServerHandler NodeId NodeId | ||
150 | pingS NodeSession {..} addr nid = do | ||
151 | atomically $ modifyTVar' routingTable $ HM.insert nid addr | ||
152 | return nodeId | ||
153 | |||
154 | findNodeS :: ServerHandler (NodeId, NodeId) (NodeId, CompactInfo) | ||
155 | findNodeS NodeSession {..} addr (nid, qnid) = do | ||
156 | rt <- atomically $ readTVar routingTable | ||
157 | return (nodeId, encodeCompact $ kclosest alpha qnid rt) | ||
158 | |||
159 | getPeersS :: ServerHandler (NodeId, InfoHash) (NodeId, Token, CompactInfo) | ||
160 | getPeersS NodeSession {..} addr (nid, ih) = do | ||
161 | |||
162 | return (nodeId, defaultToken, error "compact info") | ||
163 | |||
164 | announcePeerS :: ServerHandler (NodeId, InfoHash, PortNumber, Token) NodeId | ||
165 | announcePeerS NodeSession {..} addr (nid, ih, port, token) = do | ||
166 | let right = (error "checkToken") | ||
167 | return nodeId | ||
168 | |||
169 | dhtServer :: PortNumber -> NodeSession -> IO () | ||
170 | dhtServer p s = server p | ||
171 | [ pingM ==> pingS s undefined | ||
172 | , findNodeM ==> findNodeS s undefined | ||
173 | , getPeersM ==> getPeersS s undefined | ||
174 | , announcePeerM ==> announcePeerS s undefined | ||
175 | ] \ No newline at end of file | ||
diff --git a/src/Network/BitTorrent/DHT/Server.hs b/src/Network/BitTorrent/DHT/Server.hs deleted file mode 100644 index 1f73af8a..00000000 --- a/src/Network/BitTorrent/DHT/Server.hs +++ /dev/null | |||
@@ -1,28 +0,0 @@ | |||
1 | module Network.BitTorrent.DHT.Server | ||
2 | ( | ||
3 | ) where | ||
4 | |||
5 | import Control.Monad.Trans.State | ||
6 | import Data.Kademlia.Routing.Table | ||
7 | import Data.Kademlia.Common | ||
8 | |||
9 | |||
10 | |||
11 | type DHT k v = StateT (Table NodeInfo InfoHash) IO | ||
12 | |||
13 | ping :: NodeID -> DHT k v NodeID | ||
14 | ping nid = do | ||
15 | -- update nid | ||
16 | -- gets nodeID | ||
17 | undefined | ||
18 | |||
19 | findNode :: NodeID -> DHT k v [NodeInfo] | ||
20 | findNode = undefined | ||
21 | |||
22 | -- | Bittorrent /get_peers/ RPC is special case of the /find_value/. | ||
23 | findValue :: NodeID -> DHT k v (Either [NodeID] v) | ||
24 | findValue = undefined | ||
25 | |||
26 | -- | Bittorrent /announce_peer/ RPC is special case of the /store/. | ||
27 | store :: NodeID -> (k, v) -> DHT k v NodeID | ||
28 | store = undefined \ No newline at end of file | ||
diff --git a/src/Network/BitTorrent/Internal.lhs b/src/Network/BitTorrent/Internal.lhs index a69016ff..24fecac7 100644 --- a/src/Network/BitTorrent/Internal.lhs +++ b/src/Network/BitTorrent/Internal.lhs | |||
@@ -307,7 +307,7 @@ and different enabled extensions at the same time. | |||
307 | > -- | Port where client listen for other peers | 307 | > -- | Port where client listen for other peers |
308 | > , listenerPort :: PortNumber | 308 | > , listenerPort :: PortNumber |
309 | > -- TODO restart listener if it fail | 309 | > -- TODO restart listener if it fail |
310 | 310 | > -- , dhtListenerPort | |
311 | > -- | Semaphor used to bound number of active P2P sessions. | 311 | > -- | Semaphor used to bound number of active P2P sessions. |
312 | > , activeThreads :: !(MSem ThreadCount) | 312 | > , activeThreads :: !(MSem ThreadCount) |
313 | 313 | ||