summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Data/Torrent/InfoHash.hs1
-rw-r--r--src/Network/BitTorrent/DHT/Message.hs38
-rw-r--r--tests/Network/BitTorrent/CoreSpec.hs3
-rw-r--r--tests/Network/BitTorrent/DHT/MessageSpec.hs75
4 files changed, 91 insertions, 26 deletions
diff --git a/src/Data/Torrent/InfoHash.hs b/src/Data/Torrent/InfoHash.hs
index 4d49fcb7..9eec631c 100644
--- a/src/Data/Torrent/InfoHash.hs
+++ b/src/Data/Torrent/InfoHash.hs
@@ -120,6 +120,7 @@ instance Convertible BS.ByteString InfoHash where
120-- | Parse infohash from base16\/base32\/base64 encoded string. 120-- | Parse infohash from base16\/base32\/base64 encoded string.
121instance Convertible Text InfoHash where 121instance Convertible Text InfoHash where
122 safeConvert t 122 safeConvert t
123 | 20 == hashLen = pure (InfoHash hashStr)
123 | 26 <= hashLen && hashLen <= 28 = 124 | 26 <= hashLen && hashLen <= 28 =
124 case Base64.decode hashStr of 125 case Base64.decode hashStr of
125 Left msg -> convError ("invalid base64 encoding " ++ msg) t 126 Left msg -> convError ("invalid base64 encoding " ++ msg) t
diff --git a/src/Network/BitTorrent/DHT/Message.hs b/src/Network/BitTorrent/DHT/Message.hs
index 0733a85d..15d1099c 100644
--- a/src/Network/BitTorrent/DHT/Message.hs
+++ b/src/Network/BitTorrent/DHT/Message.hs
@@ -31,6 +31,7 @@ import Control.Applicative
31import Data.BEncode as BE 31import Data.BEncode as BE
32import Data.BEncode.BDict 32import Data.BEncode.BDict
33import Data.ByteString as BS 33import Data.ByteString as BS
34import Data.List as L
34import Data.Monoid 35import Data.Monoid
35import Data.Serialize as S 36import Data.Serialize as S
36import Data.Typeable 37import Data.Typeable
@@ -138,7 +139,7 @@ binary k = field (req k) >>= either (fail . format) return .
138 139
139instance (Typeable ip, Serialize ip) => BEncode (NodeFound ip) where 140instance (Typeable ip, Serialize ip) => BEncode (NodeFound ip) where
140 toBEncode (NodeFound ns) = toDict $ 141 toBEncode (NodeFound ns) = toDict $
141 nodes_key .=! S.encode ns 142 nodes_key .=! runPut (mapM_ put ns)
142 .: endDict 143 .: endDict
143 144
144 fromBEncode = fromDict $ NodeFound <$> binary nodes_key 145 fromBEncode = fromDict $ NodeFound <$> binary nodes_key
@@ -154,7 +155,7 @@ instance (Serialize ip, Typeable ip)
154 155
155-- | Get peers associated with a torrent infohash. 156-- | Get peers associated with a torrent infohash.
156newtype GetPeers = GetPeers InfoHash 157newtype GetPeers = GetPeers InfoHash
157 deriving Typeable 158 deriving (Show, Eq, Typeable)
158 159
159info_hash_key :: BKey 160info_hash_key :: BKey
160info_hash_key = "info_hash" 161info_hash_key = "info_hash"
@@ -172,7 +173,7 @@ data GotPeers ip = GotPeers
172 -- | The token value is a required argument for a future 173 -- | The token value is a required argument for a future
173 -- announce_peer query. 174 -- announce_peer query.
174 , grantedToken :: Token 175 , grantedToken :: Token
175 } deriving Typeable 176 } deriving (Show, Eq, Typeable)
176 177
177peers_key :: BKey 178peers_key :: BKey
178peers_key = "values" 179peers_key = "values"
@@ -182,17 +183,25 @@ token_key = "token"
182 183
183instance (Typeable ip, Serialize ip) => BEncode (GotPeers ip) where 184instance (Typeable ip, Serialize ip) => BEncode (GotPeers ip) where
184 toBEncode GotPeers {..} = toDict $ 185 toBEncode GotPeers {..} = toDict $
185 putPeerList peers 186 case peers of
186 .: token_key .=! grantedToken 187 Left ns ->
187 .: endDict 188 nodes_key .=! runPut (mapM_ put ns)
188 where 189 .: token_key .=! grantedToken
189 putPeerList (Right ps) = peers_key .=! S.encode ps 190 .: endDict
190 putPeerList (Left ns) = nodes_key .=! S.encode ns 191 Right ps ->
192 token_key .=! grantedToken
193 .: peers_key .=! L.map S.encode ps
194 .: endDict
191 195
192 fromBEncode = fromDict $ GotPeers <$> getPeerList <*>! token_key 196 fromBEncode = fromDict $ do
193 where 197 mns <- optional (binary nodes_key) -- "nodes"
194 getPeerList = Right <$> binary peers_key 198 tok <- field (req token_key) -- "token"
195 <|> Left <$> binary nodes_key 199 mps <- optional (field (req peers_key) >>= decodePeers) -- "values"
200 case (Right <$> mps) <|> (Left <$> mns) of
201 Nothing -> fail "get_peers: neihter peers nor nodes key is valid"
202 Just xs -> pure $ GotPeers xs tok
203 where
204 decodePeers = either fail pure . mapM S.decode
196 205
197instance (Typeable ip, Serialize ip) => 206instance (Typeable ip, Serialize ip) =>
198 KRPC (Query GetPeers) (Response (GotPeers ip)) where 207 KRPC (Query GetPeers) (Response (GotPeers ip)) where
@@ -213,7 +222,7 @@ data Announce = Announce
213 222
214 -- | received in response to a previous get_peers query. 223 -- | received in response to a previous get_peers query.
215 , sessionToken :: Token 224 , sessionToken :: Token
216 } deriving Typeable 225 } deriving (Show, Eq, Typeable)
217 226
218port_key :: BKey 227port_key :: BKey
219port_key = "port" 228port_key = "port"
@@ -235,6 +244,7 @@ instance BEncode Announce where
235-- port number under the infohash in its store of peer contact 244-- port number under the infohash in its store of peer contact
236-- information. 245-- information.
237data Announced = Announced 246data Announced = Announced
247 deriving (Show, Eq, Typeable)
238 248
239instance BEncode Announced where 249instance BEncode Announced where
240 toBEncode _ = toBEncode Ping 250 toBEncode _ = toBEncode Ping
diff --git a/tests/Network/BitTorrent/CoreSpec.hs b/tests/Network/BitTorrent/CoreSpec.hs
index 917ed5f5..9f9eb0eb 100644
--- a/tests/Network/BitTorrent/CoreSpec.hs
+++ b/tests/Network/BitTorrent/CoreSpec.hs
@@ -1,6 +1,9 @@
1-- | Re-export modules. 1-- | Re-export modules.
2module Network.BitTorrent.CoreSpec (spec) where 2module Network.BitTorrent.CoreSpec (spec) where
3import Network.BitTorrent.Core.FingerprintSpec as CoreSpec ()
4import Network.BitTorrent.Core.NodeSpec as CoreSpec ()
3import Network.BitTorrent.Core.PeerIdSpec as CoreSpec () 5import Network.BitTorrent.Core.PeerIdSpec as CoreSpec ()
6import Network.BitTorrent.Core.PeerAddrSpec as CoreSpec ()
4 7
5import Test.Hspec (Spec) 8import Test.Hspec (Spec)
6 9
diff --git a/tests/Network/BitTorrent/DHT/MessageSpec.hs b/tests/Network/BitTorrent/DHT/MessageSpec.hs
index 9f4c58b0..52bda932 100644
--- a/tests/Network/BitTorrent/DHT/MessageSpec.hs
+++ b/tests/Network/BitTorrent/DHT/MessageSpec.hs
@@ -1,14 +1,19 @@
1{-# LANGUAGE RecordWildCards #-} 1{-# LANGUAGE RecordWildCards #-}
2module Network.BitTorrent.DHT.MessageSpec (spec) where 2module Network.BitTorrent.DHT.MessageSpec (spec) where
3import Control.Monad.Reader 3import Control.Monad.Reader
4import Test.Hspec
5import Data.BEncode as BE 4import Data.BEncode as BE
5import Data.ByteString.Lazy as BL
6import Data.Default 6import Data.Default
7import Data.List as L 7import Data.List as L
8import Network.BitTorrent.Core 8import Network.BitTorrent.Core
9import Network.BitTorrent.DHT.Message 9import Network.BitTorrent.DHT.Message
10import Network.KRPC 10import Network.KRPC
11import Network.Socket (PortNumber) 11import Network.Socket (PortNumber)
12import Test.Hspec
13import Test.QuickCheck
14
15import Network.BitTorrent.CoreSpec ()
16import Data.Torrent.InfoHashSpec ()
12 17
13 18
14remoteAddr :: SockAddr 19remoteAddr :: SockAddr
@@ -29,6 +34,9 @@ rpc action = do
29isProtocolError :: KError -> Bool 34isProtocolError :: KError -> Bool
30isProtocolError KError {..} = errorCode == ProtocolError 35isProtocolError KError {..} = errorCode == ProtocolError
31 36
37prop_bencode :: Eq a => Show a => BEncode a => a -> Expectation
38prop_bencode x = BE.decode (BL.toStrict (BE.encode x)) `shouldBe` Right x
39
32spec :: Spec 40spec :: Spec
33spec = do 41spec = do
34 context ("you need running DHT node at " ++ show remoteAddr) $ do 42 context ("you need running DHT node at " ++ show remoteAddr) $ do
@@ -46,8 +54,9 @@ spec = do
46 BE.encode (Response "mnopqrstuvwxyz123456" Ping) 54 BE.encode (Response "mnopqrstuvwxyz123456" Ping)
47 `shouldBe` "d2:id20:mnopqrstuvwxyz123456e" 55 `shouldBe` "d2:id20:mnopqrstuvwxyz123456e"
48 56
49 it "properly bencoded (iso)" $ do 57 it "properly bencoded (iso)" $ property $ \ nid -> do
50 pending 58 prop_bencode (Query nid Ping)
59 prop_bencode (Response nid Ping)
51 60
52 it "does compatible with existing DHT" $ do 61 it "does compatible with existing DHT" $ do
53 nid <- genNodeId 62 nid <- genNodeId
@@ -74,8 +83,9 @@ spec = do
74 \e" 83 \e"
75 `shouldBe` Right (Response nid (NodeFound [NodeInfo nid' naddr])) 84 `shouldBe` Right (Response nid (NodeFound [NodeInfo nid' naddr]))
76 85
77 it "properly bencoded (iso)" $ do 86 it "properly bencoded (iso)" $ property $ \ nid x xs -> do
78 pending 87 prop_bencode (Query nid (FindNode x))
88 prop_bencode (Response nid (NodeFound (xs :: [NodeInfo IPv4] )))
79 89
80 it "does compatible with existing DHT" $ do 90 it "does compatible with existing DHT" $ do
81 nid <- genNodeId 91 nid <- genNodeId
@@ -85,10 +95,38 @@ spec = do
85 95
86 describe "get_peers" $ do 96 describe "get_peers" $ do
87 it "properly bencoded" $ do 97 it "properly bencoded" $ do
88 pending 98 BE.decode "d2:id20:abcdefghij0123456789\
89 99 \9:info_hash20:mnopqrstuvwxyz123456\
90 it "properly bencoded (iso)" $ do 100 \e"
91 pending 101 `shouldBe` Right (Query "abcdefghij0123456789"
102 (GetPeers "mnopqrstuvwxyz123456"))
103
104 BE.decode "d2:id20:abcdefghij0123456789\
105 \5:token8:aoeusnth\
106 \6:valuesl6:\127\0\0\1\1\2\&6:\192\168\1\100\1\2e\
107 \e"
108 `shouldBe` Right (Response "abcdefghij0123456789"
109 (GotPeers (Right [ "127.0.0.1:258" :: PeerAddr IPv4
110 , "192.168.1.100:258"
111 ]) "aoeusnth"))
112
113 BE.decode "d2:id20:abcdefghij0123456789\
114 \5:nodes26:mnopqrstuvwxyz123456\127\0\0\1\1\2\
115 \5:token8:aoeusnth\
116 \e"
117 `shouldBe` Right (Response "abcdefghij0123456789"
118 (GotPeers
119 { peers = Left [NodeInfo "mnopqrstuvwxyz123456" "127.0.0.1:258"
120 :: NodeInfo IPv4]
121 , grantedToken = "aoeusnth"
122 }))
123
124 it "properly bencoded (iso)" $ property $ \ nid topic exs token -> do
125 prop_bencode (Query nid (GetPeers topic))
126 let _ = exs :: Either [NodeInfo IPv4] [PeerAddr IPv4]
127 let nullPeerId paddr = paddr {peerId = Nothing}
128 let nullPeerIds = either Left (Right . L.map nullPeerId)
129 prop_bencode (Response nid (GotPeers (nullPeerIds exs) token))
92 130
93 it "does compatible with existing DHT" $ do 131 it "does compatible with existing DHT" $ do
94 nid <- genNodeId 132 nid <- genNodeId
@@ -99,10 +137,23 @@ spec = do
99 137
100 describe "announce" $ do 138 describe "announce" $ do
101 it "properly bencoded" $ do 139 it "properly bencoded" $ do
102 pending 140 BE.decode "d2:id20:abcdefghij0123456789\
141 \9:info_hash20:mnopqrstuvwxyz123456\
142 \4:porti6881e\
143 \5:token8:aoeusnth\
144 \e" `shouldBe` Right
145 (Query "abcdefghij0123456789"
146 (Announce "mnopqrstuvwxyz123456" 6881 "aoeusnth"))
147
148
149 BE.decode "d2:id20:mnopqrstuvwxyz123456e"
150 `shouldBe` Right
151 (Response "mnopqrstuvwxyz123456" Announced)
152
153 it "properly bencoded (iso)" $ property $ \ nid topic port token -> do
154 prop_bencode (Query nid (Announce topic port token))
155 prop_bencode (Response nid (Announced))
103 156
104 it "properly bencoded (iso)" $ do
105 pending
106 157
107 it "does compatible with existing DHT" $ do 158 it "does compatible with existing DHT" $ do
108 nid <- genNodeId 159 nid <- genNodeId