diff options
-rw-r--r-- | src/Network/BitTorrent/DHT/Message.hs | 29 | ||||
-rw-r--r-- | tests/Network/BitTorrent/DHT/MessageSpec.hs | 19 |
2 files changed, 37 insertions, 11 deletions
diff --git a/src/Network/BitTorrent/DHT/Message.hs b/src/Network/BitTorrent/DHT/Message.hs index 15d1099c..49629755 100644 --- a/src/Network/BitTorrent/DHT/Message.hs +++ b/src/Network/BitTorrent/DHT/Message.hs | |||
@@ -214,8 +214,15 @@ instance (Typeable ip, Serialize ip) => | |||
214 | -- | Announce that the peer, controlling the querying node, is | 214 | -- | Announce that the peer, controlling the querying node, is |
215 | -- downloading a torrent on a port. | 215 | -- downloading a torrent on a port. |
216 | data Announce = Announce | 216 | data Announce = Announce |
217 | { -- | infohash of the torrent; | 217 | { -- | If set, the 'port' field should be ignored and the source |
218 | topic :: InfoHash | 218 | -- port of the UDP packet should be used as the peer's port |
219 | -- instead. This is useful for peers behind a NAT that may not | ||
220 | -- know their external port, and supporting uTP, they accept | ||
221 | -- incoming connections on the same port as the DHT port. | ||
222 | impliedPort :: Bool | ||
223 | |||
224 | -- | infohash of the torrent; | ||
225 | , topic :: InfoHash | ||
219 | 226 | ||
220 | -- | the port /this/ peer is listening; | 227 | -- | the port /this/ peer is listening; |
221 | , port :: PortNumber | 228 | , port :: PortNumber |
@@ -227,16 +234,26 @@ data Announce = Announce | |||
227 | port_key :: BKey | 234 | port_key :: BKey |
228 | port_key = "port" | 235 | port_key = "port" |
229 | 236 | ||
237 | implied_port_key :: BKey | ||
238 | implied_port_key = "implied_port" | ||
239 | |||
230 | instance BEncode Announce where | 240 | instance BEncode Announce where |
231 | toBEncode Announce {..} = toDict $ | 241 | toBEncode Announce {..} = toDict $ |
232 | info_hash_key .=! topic | 242 | implied_port_key .=? flagField impliedPort |
233 | .: port_key .=! port | 243 | .: info_hash_key .=! topic |
234 | .: token_key .=! sessionToken | 244 | .: port_key .=! port |
245 | .: token_key .=! sessionToken | ||
235 | .: endDict | 246 | .: endDict |
247 | where | ||
248 | flagField flag = if flag then Just (1 :: Int) else Nothing | ||
249 | |||
236 | fromBEncode = fromDict $ do | 250 | fromBEncode = fromDict $ do |
237 | Announce <$>! info_hash_key | 251 | Announce <$> (boolField <$> optional (field (req implied_port_key))) |
252 | <*>! info_hash_key | ||
238 | <*>! port_key | 253 | <*>! port_key |
239 | <*>! token_key | 254 | <*>! token_key |
255 | where | ||
256 | boolField = maybe False (/= (0 :: Int)) | ||
240 | 257 | ||
241 | -- | The queried node must verify that the token was previously sent | 258 | -- | The queried node must verify that the token was previously sent |
242 | -- to the same IP address as the querying node. Then the queried node | 259 | -- to the same IP address as the querying node. Then the queried node |
diff --git a/tests/Network/BitTorrent/DHT/MessageSpec.hs b/tests/Network/BitTorrent/DHT/MessageSpec.hs index 52bda932..11a8fb15 100644 --- a/tests/Network/BitTorrent/DHT/MessageSpec.hs +++ b/tests/Network/BitTorrent/DHT/MessageSpec.hs | |||
@@ -143,15 +143,24 @@ spec = do | |||
143 | \5:token8:aoeusnth\ | 143 | \5:token8:aoeusnth\ |
144 | \e" `shouldBe` Right | 144 | \e" `shouldBe` Right |
145 | (Query "abcdefghij0123456789" | 145 | (Query "abcdefghij0123456789" |
146 | (Announce "mnopqrstuvwxyz123456" 6881 "aoeusnth")) | 146 | (Announce False "mnopqrstuvwxyz123456" 6881 "aoeusnth")) |
147 | |||
148 | BE.decode "d2:id20:abcdefghij0123456789\ | ||
149 | \12:implied_porti1e\ | ||
150 | \9:info_hash20:mnopqrstuvwxyz123456\ | ||
151 | \4:porti6881e\ | ||
152 | \5:token8:aoeusnth\ | ||
153 | \e" `shouldBe` Right | ||
154 | (Query "abcdefghij0123456789" | ||
155 | (Announce True "mnopqrstuvwxyz123456" 6881 "aoeusnth")) | ||
147 | 156 | ||
148 | 157 | ||
149 | BE.decode "d2:id20:mnopqrstuvwxyz123456e" | 158 | BE.decode "d2:id20:mnopqrstuvwxyz123456e" |
150 | `shouldBe` Right | 159 | `shouldBe` Right |
151 | (Response "mnopqrstuvwxyz123456" Announced) | 160 | (Response "mnopqrstuvwxyz123456" Announced) |
152 | 161 | ||
153 | it "properly bencoded (iso)" $ property $ \ nid topic port token -> do | 162 | it "properly bencoded (iso)" $ property $ \ nid flag topic port token -> do |
154 | prop_bencode (Query nid (Announce topic port token)) | 163 | prop_bencode (Query nid (Announce flag topic port token)) |
155 | prop_bencode (Response nid (Announced)) | 164 | prop_bencode (Response nid (Announced)) |
156 | 165 | ||
157 | 166 | ||
@@ -160,7 +169,7 @@ spec = do | |||
160 | Response _remoteId Announced <- rpc $ do | 169 | Response _remoteId Announced <- rpc $ do |
161 | Response _ GotPeers {..} <- query remoteAddr (Query nid (GetPeers def)) | 170 | Response _ GotPeers {..} <- query remoteAddr (Query nid (GetPeers def)) |
162 | let _ = peers :: Either [NodeInfo IPv4] [PeerAddr IPv4] | 171 | let _ = peers :: Either [NodeInfo IPv4] [PeerAddr IPv4] |
163 | query remoteAddr (Query nid (Announce def thisPort grantedToken)) | 172 | query remoteAddr (Query nid (Announce False def thisPort grantedToken)) |
164 | return () | 173 | return () |
165 | 174 | ||
166 | it "does fail on invalid token" $ do | 175 | it "does fail on invalid token" $ do |
@@ -169,6 +178,6 @@ spec = do | |||
169 | Response _ GotPeers {..} <- query remoteAddr (Query nid (GetPeers def)) | 178 | Response _ GotPeers {..} <- query remoteAddr (Query nid (GetPeers def)) |
170 | let _ = peers :: Either [NodeInfo IPv4] [PeerAddr IPv4] | 179 | let _ = peers :: Either [NodeInfo IPv4] [PeerAddr IPv4] |
171 | let invalidToken = "" | 180 | let invalidToken = "" |
172 | query remoteAddr (Query nid (Announce def thisPort invalidToken))) | 181 | query remoteAddr (Query nid (Announce False def thisPort invalidToken))) |
173 | `shouldThrow` isProtocolError | 182 | `shouldThrow` isProtocolError |
174 | return () | 183 | return () |