summaryrefslogtreecommitdiff
path: root/src/Network
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network')
-rw-r--r--src/Network/BitTorrent/Client/Swarm.hs2
-rw-r--r--src/Network/BitTorrent/Core/PeerAddr.hs133
-rw-r--r--src/Network/BitTorrent/Exchange/Assembler.hs9
-rw-r--r--src/Network/BitTorrent/Exchange/Message.hs4
-rw-r--r--src/Network/BitTorrent/Exchange/Wire.hs6
-rw-r--r--src/Network/BitTorrent/Tracker/Message.hs41
6 files changed, 142 insertions, 53 deletions
diff --git a/src/Network/BitTorrent/Client/Swarm.hs b/src/Network/BitTorrent/Client/Swarm.hs
index a9dca048..1901905c 100644
--- a/src/Network/BitTorrent/Client/Swarm.hs
+++ b/src/Network/BitTorrent/Client/Swarm.hs
@@ -43,7 +43,7 @@ getAnnounceQuery Swarm {..} = AnnounceQuery
43 , reqEvent = Nothing 43 , reqEvent = Nothing
44 } 44 }
45 45
46askPeers :: Swarm -> IO [PeerAddr] 46askPeers :: Swarm -> IO [PeerAddr IP]
47askPeers s @ Swarm {..} = do 47askPeers s @ Swarm {..} = do
48 AnnounceInfo {..} <- RPC.announce (getAnnounceQuery s) trackerConn 48 AnnounceInfo {..} <- RPC.announce (getAnnounceQuery s) trackerConn
49 return (getPeerList respPeers) 49 return (getPeerList respPeers)
diff --git a/src/Network/BitTorrent/Core/PeerAddr.hs b/src/Network/BitTorrent/Core/PeerAddr.hs
index e7a4ea61..1da4c81a 100644
--- a/src/Network/BitTorrent/Core/PeerAddr.hs
+++ b/src/Network/BitTorrent/Core/PeerAddr.hs
@@ -12,12 +12,18 @@
12{-# LANGUAGE StandaloneDeriving #-} 12{-# LANGUAGE StandaloneDeriving #-}
13{-# LANGUAGE GeneralizedNewtypeDeriving #-} 13{-# LANGUAGE GeneralizedNewtypeDeriving #-}
14{-# LANGUAGE DeriveDataTypeable #-} 14{-# LANGUAGE DeriveDataTypeable #-}
15{-# LANGUAGE FlexibleInstances #-}
16{-# LANGUAGE DeriveFunctor #-}
15{-# OPTIONS -fno-warn-orphans #-} -- for PortNumber instances 17{-# OPTIONS -fno-warn-orphans #-} -- for PortNumber instances
16module Network.BitTorrent.Core.PeerAddr 18module Network.BitTorrent.Core.PeerAddr
17 ( -- * Peer address 19 ( -- * Peer address
18 PeerAddr(..) 20 PeerAddr(..)
19 , defaultPorts 21 , defaultPorts
20 , peerSockAddr 22 , peerSockAddr
23 , mergeIPLists
24 , splitIPList
25 , IP, IPv4, IPv6 --re-export Data.IP constructors
26 , IPAddress ()
21 ) where 27 ) where
22 28
23import Control.Applicative 29import Control.Applicative
@@ -26,6 +32,8 @@ import Data.Aeson (ToJSON, FromJSON)
26import Data.Aeson.TH 32import Data.Aeson.TH
27import Data.BEncode as BS 33import Data.BEncode as BS
28import Data.BEncode.BDict (BKey) 34import Data.BEncode.BDict (BKey)
35import Data.ByteString
36import Data.ByteString.Char8 as BS8
29import Data.Bits 37import Data.Bits
30import Data.Char 38import Data.Char
31import Data.Default 39import Data.Default
@@ -35,10 +43,15 @@ import Data.Serialize as S
35import Data.String 43import Data.String
36import Data.Typeable 44import Data.Typeable
37import Data.Word 45import Data.Word
46import Data.IP
47import Data.Maybe
48import Data.Foldable
49import Data.Either
38import Network.Socket 50import Network.Socket
39import Text.PrettyPrint 51import Text.PrettyPrint
40import Text.PrettyPrint.Class 52import Text.PrettyPrint.Class
41import Text.Read (readMaybe) 53import Text.Read (readMaybe)
54import qualified Text.ParserCombinators.ReadP as RP
42import System.IO.Unsafe 55import System.IO.Unsafe
43 56
44import Data.Torrent.JSON 57import Data.Torrent.JSON
@@ -58,30 +71,74 @@ instance Serialize PortNumber where
58 put = putWord16be . fromIntegral 71 put = putWord16be . fromIntegral
59 {-# INLINE put #-} 72 {-# INLINE put #-}
60 73
74class (Show i, Read i) => IPAddress i where
75 showIp :: i -> String
76 showIp = show
77
78 readIp :: String -> i
79 readIp = read
80
81 toHostAddr :: i -> Either HostAddress HostAddress6
82
83instance IPAddress IPv4 where
84 toHostAddr = Left . toHostAddress
85
86instance IPAddress IPv6 where
87 toHostAddr = Right . toHostAddress6
88
89instance IPAddress IP where
90 toHostAddr (IPv4 ip) = toHostAddr ip
91 toHostAddr (IPv6 ip) = toHostAddr ip
92
93
94deriving instance Typeable IP
95deriving instance Typeable IPv4
96deriving instance Typeable IPv6
97
98ipToBEncode ip = BString $ BS8.pack $ showIp ip
99ipFromBEncode (BString ip) = return $ readIp $ BS8.unpack ip
100
101instance BEncode IP where
102 toBEncode = ipToBEncode
103 fromBEncode = ipFromBEncode
104
105instance BEncode IPv4 where
106 toBEncode = ipToBEncode
107 fromBEncode = ipFromBEncode
108
109instance BEncode IPv6 where
110 toBEncode = ipToBEncode
111 fromBEncode = ipFromBEncode
112
113instance Serialize IPv4 where
114 put ip = put $ toHostAddress ip
115 get = fromHostAddress <$> get
116
117instance Serialize IPv6 where
118 put ip = put $ toHostAddress6 ip
119 get = fromHostAddress6 <$> get
120
61-- TODO check semantic of ord and eq instances 121-- TODO check semantic of ord and eq instances
62-- TODO use SockAddr instead of peerIP and peerPort 122-- TODO use SockAddr instead of peerIP and peerPort
63 123
64-- | Peer address info normally extracted from peer list or peer 124-- | Peer address info normally extracted from peer list or peer
65-- compact list encoding. 125-- compact list encoding.
66data PeerAddr = PeerAddr 126data PeerAddr a = PeerAddr
67 { peerId :: !(Maybe PeerId) 127 { peerId :: !(Maybe PeerId)
68 , peerIP :: {-# UNPACK #-} !HostAddress 128 , peerAddr :: a
69 , peerPort :: {-# UNPACK #-} !PortNumber 129 , peerPort :: {-# UNPACK #-} !PortNumber
70 } deriving (Show, Eq, Ord, Typeable) 130 } deriving (Show, Eq, Typeable, Functor)
71
72$(deriveJSON omitRecordPrefix ''PeerAddr)
73 131
74peer_id_key, peer_ip_key, peer_port_key :: BKey 132peer_id_key, peer_ip_key, peer_port_key :: BKey
75peer_id_key = "peer id" 133peer_id_key = "peer id"
76peer_ip_key = "ip" 134peer_ip_key = "ip"
77peer_port_key = "port" 135peer_port_key = "port"
78 136
79-- FIXME do we need to byteswap peerIP in bencode instance?
80-- | The tracker's 'announce response' compatible encoding. 137-- | The tracker's 'announce response' compatible encoding.
81instance BEncode PeerAddr where 138instance (Typeable a, BEncode a) => BEncode (PeerAddr a) where
82 toBEncode PeerAddr {..} = toDict $ 139 toBEncode PeerAddr {..} = toDict $
83 peer_id_key .=? peerId 140 peer_id_key .=? peerId
84 .: peer_ip_key .=! peerIP 141 .: peer_ip_key .=! peerAddr
85 .: peer_port_key .=! peerPort 142 .: peer_port_key .=! peerPort
86 .: endDict 143 .: endDict
87 144
@@ -90,19 +147,32 @@ instance BEncode PeerAddr where
90 <*>! peer_ip_key 147 <*>! peer_ip_key
91 <*>! peer_port_key 148 <*>! peer_port_key
92 149
150mergeIPLists :: [PeerAddr IPv4] -> Maybe [PeerAddr IPv6] -> [PeerAddr IP]
151mergeIPLists v4 v6 = (fmap IPv4 `L.map` v4)
152 ++ (fmap IPv6 `L.map` Data.Foldable.concat v6)
153
154splitIPList :: [PeerAddr IP] -> ([PeerAddr IPv4],[PeerAddr IPv6])
155splitIPList xs = partitionEithers $ toEither <$> xs
156 where
157 toEither :: PeerAddr IP -> Either (PeerAddr IPv4) (PeerAddr IPv6)
158 toEither pa@(PeerAddr _ (IPv4 _) _) = Left (ipv4 <$> pa)
159 toEither pa@(PeerAddr _ (IPv6 _) _) = Right (ipv6 <$> pa)
160
161
93-- | The tracker's 'compact peer list' compatible encoding. The 162-- | The tracker's 'compact peer list' compatible encoding. The
94-- 'peerId' is always 'Nothing'. 163-- 'peerId' is always 'Nothing'.
95-- 164--
96-- For more info see: <http://www.bittorrent.org/beps/bep_0023.html> 165-- For more info see: <http://www.bittorrent.org/beps/bep_0023.html>
97-- 166--
98instance Serialize PeerAddr where 167-- TODO: test byte order
99 put PeerAddr {..} = putWord32host peerIP >> put peerPort 168instance (Serialize a) => Serialize (PeerAddr a) where
100 {-# INLINE put #-} 169 put PeerAddr {..} =
101 get = PeerAddr Nothing <$> getWord32host <*> get 170 put peerAddr >> put peerPort
102 {-# INLINE get #-} 171 get =
172 PeerAddr Nothing <$> get <*> get
103 173
104-- | @127.0.0.1:6881@ 174-- | @127.0.0.1:6881@
105instance Default PeerAddr where 175instance Default (PeerAddr IPv4) where
106 def = "127.0.0.1:6881" 176 def = "127.0.0.1:6881"
107 177
108-- inet_addr is pure; so it is safe to throw IO 178-- inet_addr is pure; so it is safe to throw IO
@@ -117,28 +187,49 @@ unsafeCatchIO m = unsafePerformIO $
117-- 187--
118-- @peerPort \"127.0.0.1:6881\" == 6881@ 188-- @peerPort \"127.0.0.1:6881\" == 6881@
119-- 189--
120instance IsString PeerAddr where 190instance IsString (PeerAddr IPv4) where
121 fromString str 191 fromString str
122 | [hostAddrStr, portStr] <- splitWhen (== ':') str 192 | [hostAddrStr, portStr] <- splitWhen (== ':') str
123 , Just hostAddr <- unsafeCatchIO $ inet_addr hostAddrStr 193 , hostAddr <- read hostAddrStr
124 , Just portNum <- toEnum <$> readMaybe portStr 194 , Just portNum <- toEnum <$> readMaybe portStr
125 = PeerAddr Nothing hostAddr portNum 195 = PeerAddr Nothing hostAddr portNum
126 | otherwise = error $ "fromString: unable to parse PeerAddr: " ++ str 196 | otherwise = error $ "fromString: unable to parse (PeerAddr IPv4): " ++ str
197
198readsIPv6_port :: String -> [((IPv6, PortNumber), String)]
199readsIPv6_port = RP.readP_to_S $ do
200 ip <- RP.char '[' *> (RP.readS_to_P reads) <* RP.char ']'
201 RP.char ':'
202 port <- toEnum <$> read <$> (RP.many1 $ RP.satisfy isDigit) <* RP.eof
203 return (ip,port)
204
205instance IsString (PeerAddr IPv6) where
206 fromString str
207 | [((ip,port),"")] <- readsIPv6_port str =
208 PeerAddr Nothing ip port
209 | otherwise = error $ "fromString: unable to parse (PeerAddr IPv6): " ++ str
127 210
128-- | fingerprint + "at" + dotted.host.inet.addr:port 211-- | fingerprint + "at" + dotted.host.inet.addr:port
129instance Pretty PeerAddr where 212-- TODO: instances for IPv6, HostName
213instance Pretty (PeerAddr IP) where
130 pretty p @ PeerAddr {..} 214 pretty p @ PeerAddr {..}
131 | Just pid <- peerId = pretty (fingerprint pid) <+> "at" <+> paddr 215 | Just pid <- peerId = pretty (fingerprint pid) <+> "at" <+> paddr
132 | otherwise = paddr 216 | otherwise = paddr
133 where 217 where
134 paddr = text (show (peerSockAddr p)) 218 paddr = text (show peerAddr ++ ":" ++ show peerPort)
135 219
136-- | Ports typically reserved for bittorrent P2P listener. 220-- | Ports typically reserved for bittorrent P2P listener.
137defaultPorts :: [PortNumber] 221defaultPorts :: [PortNumber]
138defaultPorts = [6881..6889] 222defaultPorts = [6881..6889]
139 223
224resolvePeerAddr :: (IPAddress i) => PeerAddr HostName -> PeerAddr i
225resolvePeerAddr = undefined
226
140-- | Convert peer info from tracker response to socket address. Used 227-- | Convert peer info from tracker response to socket address. Used
141-- for establish connection between peers. 228-- for establish connection between peers.
142-- 229--
143peerSockAddr :: PeerAddr -> SockAddr 230peerSockAddr :: (IPAddress i) => PeerAddr i -> SockAddr
144peerSockAddr = SockAddrInet <$> peerPort <*> peerIP 231peerSockAddr PeerAddr {..}
232 | Left hAddr <- toHostAddr peerAddr =
233 SockAddrInet peerPort hAddr
234 | Right hAddr <- toHostAddr peerAddr =
235 SockAddrInet6 peerPort 0 hAddr 0
diff --git a/src/Network/BitTorrent/Exchange/Assembler.hs b/src/Network/BitTorrent/Exchange/Assembler.hs
index 5dc7c5ca..aa009f49 100644
--- a/src/Network/BitTorrent/Exchange/Assembler.hs
+++ b/src/Network/BitTorrent/Exchange/Assembler.hs
@@ -65,6 +65,7 @@ import Data.IntMap.Strict as IM
65import Data.List as L 65import Data.List as L
66import Data.Map as M 66import Data.Map as M
67import Data.Maybe 67import Data.Maybe
68import Data.IP
68 69
69import Data.Torrent.Piece 70import Data.Torrent.Piece
70import Network.BitTorrent.Core 71import Network.BitTorrent.Core
@@ -79,7 +80,7 @@ type PieceMap = IntMap
79 80
80data Assembler = Assembler 81data Assembler = Assembler
81 { -- | A set of blocks that have been 'Request'ed but not yet acked. 82 { -- | A set of blocks that have been 'Request'ed but not yet acked.
82 _inflight :: Map PeerAddr (PieceMap [BlockRange]) 83 _inflight :: Map (PeerAddr IP) (PieceMap [BlockRange])
83 84
84 -- | A set of blocks that but not yet assembled. 85 -- | A set of blocks that but not yet assembled.
85 , _pending :: PieceMap Bucket 86 , _pending :: PieceMap Bucket
@@ -114,7 +115,7 @@ allowPiece pix a @ Assembler {..} = over pending (IM.insert pix bkt) a
114 where 115 where
115 bkt = B.empty (piPieceLength info) 116 bkt = B.empty (piPieceLength info)
116 117
117allowedSet :: PeerAddr -> Assembler -> [BlockIx] 118allowedSet :: (PeerAddr IP) -> Assembler -> [BlockIx]
118allowedSet = undefined 119allowedSet = undefined
119 120
120--inflight :: PeerAddr -> BlockIx -> Assembler -> Assembler 121--inflight :: PeerAddr -> BlockIx -> Assembler -> Assembler
@@ -123,7 +124,7 @@ allowedSet = undefined
123-- You should check if a returned by peer block is actually have 124-- You should check if a returned by peer block is actually have
124-- been requested and in-flight. This is needed to avoid "I send 125-- been requested and in-flight. This is needed to avoid "I send
125-- random corrupted block" attacks. 126-- random corrupted block" attacks.
126insert :: PeerAddr -> Block a -> Assembler -> Assembler 127insert :: (PeerAddr IP) -> Block a -> Assembler -> Assembler
127insert = undefined 128insert = undefined
128 129
129{- 130{-
@@ -156,4 +157,4 @@ inserta :: Block a
156 -> (PieceMap [Block a], Maybe (Piece a)) 157 -> (PieceMap [Block a], Maybe (Piece a))
157inserta = undefined 158inserta = undefined
158 159
159-} \ No newline at end of file 160-}
diff --git a/src/Network/BitTorrent/Exchange/Message.hs b/src/Network/BitTorrent/Exchange/Message.hs
index 0adb8299..fb3a5c82 100644
--- a/src/Network/BitTorrent/Exchange/Message.hs
+++ b/src/Network/BitTorrent/Exchange/Message.hs
@@ -678,8 +678,8 @@ instance Default ExtendedHandshake where
678 678
679instance BEncode ExtendedHandshake where 679instance BEncode ExtendedHandshake where
680 toBEncode ExtendedHandshake {..} = toDict $ 680 toBEncode ExtendedHandshake {..} = toDict $
681 "ipv4" .=? ehsIPv4 -- FIXME invalid encoding 681 "ipv4" .=? (S.encode <$> ehsIPv4)
682 .: "ipv6" .=? ehsIPv6 -- FIXME invalid encoding 682 .: "ipv6" .=? (S.encode <$> ehsIPv6)
683 .: "m" .=! ehsCaps 683 .: "m" .=! ehsCaps
684 .: "metadata_size" .=? ehsMetadataSize 684 .: "metadata_size" .=? ehsMetadataSize
685 .: "p" .=? ehsPort 685 .: "p" .=? ehsPort
diff --git a/src/Network/BitTorrent/Exchange/Wire.hs b/src/Network/BitTorrent/Exchange/Wire.hs
index ae9babb3..27b4be12 100644
--- a/src/Network/BitTorrent/Exchange/Wire.hs
+++ b/src/Network/BitTorrent/Exchange/Wire.hs
@@ -505,7 +505,7 @@ initiateHandshake sock hs = do
505 recvHandshake sock 505 recvHandshake sock
506 506
507-- | Tries to connect to peer using reasonable default parameters. 507-- | Tries to connect to peer using reasonable default parameters.
508connectToPeer :: PeerAddr -> IO Socket 508connectToPeer :: (IPAddress i) => PeerAddr i -> IO Socket
509connectToPeer p = do 509connectToPeer p = do
510 sock <- socket AF_INET Stream Network.Socket.defaultProtocol 510 sock <- socket AF_INET Stream Network.Socket.defaultProtocol
511 connect sock (peerSockAddr p) 511 connect sock (peerSockAddr p)
@@ -628,7 +628,7 @@ reconnect = undefined
628-- 628--
629-- This function can throw 'WireFailure' exception. 629-- This function can throw 'WireFailure' exception.
630-- 630--
631connectWire :: Handshake -> PeerAddr -> ExtendedCaps -> Wire () -> IO () 631connectWire :: (IPAddress i) => Handshake -> PeerAddr i -> ExtendedCaps -> Wire () -> IO ()
632connectWire hs addr extCaps wire = 632connectWire hs addr extCaps wire =
633 bracket (connectToPeer addr) close $ \ sock -> do 633 bracket (connectToPeer addr) close $ \ sock -> do
634 hs' <- initiateHandshake sock hs 634 hs' <- initiateHandshake sock hs
@@ -673,7 +673,7 @@ connectWire hs addr extCaps wire =
673-- 673--
674-- This function can throw 'WireFailure' exception. 674-- This function can throw 'WireFailure' exception.
675-- 675--
676acceptWire :: Socket -> PeerAddr -> Wire () -> IO () 676acceptWire :: (IPAddress i) => Socket -> PeerAddr i -> Wire () -> IO ()
677acceptWire sock peerAddr wire = do 677acceptWire sock peerAddr wire = do
678 bracket (return sock) close $ \ _ -> do 678 bracket (return sock) close $ \ _ -> do
679 error "acceptWire: not implemented" 679 error "acceptWire: not implemented"
diff --git a/src/Network/BitTorrent/Tracker/Message.hs b/src/Network/BitTorrent/Tracker/Message.hs
index fe7686cb..95b9c7ca 100644
--- a/src/Network/BitTorrent/Tracker/Message.hs
+++ b/src/Network/BitTorrent/Tracker/Message.hs
@@ -22,6 +22,8 @@
22{-# LANGUAGE GeneralizedNewtypeDeriving #-} 22{-# LANGUAGE GeneralizedNewtypeDeriving #-}
23{-# LANGUAGE TemplateHaskell #-} 23{-# LANGUAGE TemplateHaskell #-}
24{-# LANGUAGE DeriveDataTypeable #-} 24{-# LANGUAGE DeriveDataTypeable #-}
25{-# LANGUAGE DeriveFunctor #-}
26{-# LANGUAGE ScopedTypeVariables #-}
25{-# OPTIONS -fno-warn-orphans #-} 27{-# OPTIONS -fno-warn-orphans #-}
26module Network.BitTorrent.Tracker.Message 28module Network.BitTorrent.Tracker.Message
27 ( -- * Announce 29 ( -- * Announce
@@ -83,6 +85,7 @@ import Data.Text (Text)
83import Data.Text.Encoding 85import Data.Text.Encoding
84import Data.Typeable 86import Data.Typeable
85import Data.Word 87import Data.Word
88import Data.IP
86import Network 89import Network
87import Network.HTTP.Types.QueryLike 90import Network.HTTP.Types.QueryLike
88import Network.HTTP.Types.URI hiding (urlEncode) 91import Network.HTTP.Types.URI hiding (urlEncode)
@@ -431,24 +434,18 @@ renderAnnounceRequest = queryToSimpleQuery . toQuery
431-- 434--
432-- For more info see: <http://www.bittorrent.org/beps/bep_0023.html> 435-- For more info see: <http://www.bittorrent.org/beps/bep_0023.html>
433-- 436--
434data PeerList 437data PeerList a
435 = PeerList { getPeerList :: [PeerAddr] } 438 = PeerList { getPeerList :: [PeerAddr a] }
436 | CompactPeerList { getPeerList :: [PeerAddr] } 439 | CompactPeerList { getPeerList :: [PeerAddr a] }
437 deriving (Show, Eq, Typeable) 440 deriving (Show, Eq, Typeable, Functor)
438 441
439instance ToJSON PeerList where 442putCompactPeerList :: (Serialize a) => S.Putter [PeerAddr a]
440 toJSON = toJSON . getPeerList
441
442instance FromJSON PeerList where
443 parseJSON v = PeerList <$> parseJSON v
444
445putCompactPeerList :: S.Putter [PeerAddr]
446putCompactPeerList = mapM_ put 443putCompactPeerList = mapM_ put
447 444
448getCompactPeerList :: S.Get [PeerAddr] 445getCompactPeerList :: (Serialize a) => S.Get [PeerAddr a]
449getCompactPeerList = many get 446getCompactPeerList = many get
450 447
451instance BEncode PeerList where 448instance (Typeable a, BEncode a, Serialize a) => BEncode (PeerList a) where
452 toBEncode (PeerList xs) = toBEncode xs 449 toBEncode (PeerList xs) = toBEncode xs
453 toBEncode (CompactPeerList xs) = toBEncode $ runPut (putCompactPeerList xs) 450 toBEncode (CompactPeerList xs) = toBEncode $ runPut (putCompactPeerList xs)
454 451
@@ -479,14 +476,12 @@ data AnnounceInfo =
479 , respMinInterval :: !(Maybe Int) 476 , respMinInterval :: !(Maybe Int)
480 477
481 -- | Peers that must be contacted. 478 -- | Peers that must be contacted.
482 , respPeers :: !PeerList 479 , respPeers :: !(PeerList IP)
483 480
484 -- | Human readable warning. 481 -- | Human readable warning.
485 , respWarning :: !(Maybe Text) 482 , respWarning :: !(Maybe Text)
486 } deriving (Show, Typeable) 483 } deriving (Show, Typeable)
487 484
488$(deriveJSON omitRecordPrefix ''AnnounceInfo)
489
490-- | HTTP tracker protocol compatible encoding. 485-- | HTTP tracker protocol compatible encoding.
491instance BEncode AnnounceInfo where 486instance BEncode AnnounceInfo where
492 toBEncode (Failure t) = toDict $ 487 toBEncode (Failure t) = toDict $
@@ -498,19 +493,21 @@ instance BEncode AnnounceInfo where
498 .: "incomplete" .=? respIncomplete 493 .: "incomplete" .=? respIncomplete
499 .: "interval" .=! respInterval 494 .: "interval" .=! respInterval
500 .: "min interval" .=? respMinInterval 495 .: "min interval" .=? respMinInterval
501 .: "peers" .=! respPeers 496 .: "peers" .=! peers
497 .: "peers6" .=! peers6
502 .: "warning message" .=? respWarning 498 .: "warning message" .=? respWarning
503 .: endDict 499 .: endDict
500 where (peers,peers6) = splitIPList $ getPeerList respPeers
504 501
505 fromBEncode (BDict d) 502 fromBEncode (BDict d)
506 | Just t <- BE.lookup "failure reason" d = Failure <$> fromBEncode t 503 | Just t <- BE.lookup "failure reason" d = Failure <$> fromBEncode t
507 | otherwise = (`fromDict` (BDict d)) $ do 504 | otherwise = (`fromDict` (BDict d)) $
508 AnnounceInfo 505 AnnounceInfo
509 <$>? "complete" 506 <$>? "complete"
510 <*>? "incomplete" 507 <*>? "incomplete"
511 <*>! "interval" 508 <*>! "interval"
512 <*>? "min interval" 509 <*>? "min interval"
513 <*>! "peers" 510 <*> (PeerList <$> (mergeIPLists <$>! "peers" <*>? "peers6"))
514 <*>? "warning message" 511 <*>? "warning message"
515 fromBEncode _ = decodingError "Announce info" 512 fromBEncode _ = decodingError "Announce info"
516 513
@@ -521,13 +518,13 @@ instance Serialize AnnounceInfo where
521 putWord32be $ fromIntegral respInterval 518 putWord32be $ fromIntegral respInterval
522 putWord32be $ fromIntegral $ fromMaybe 0 respIncomplete 519 putWord32be $ fromIntegral $ fromMaybe 0 respIncomplete
523 putWord32be $ fromIntegral $ fromMaybe 0 respComplete 520 putWord32be $ fromIntegral $ fromMaybe 0 respComplete
524 forM_ (getPeerList respPeers) put 521 forM_ (fmap ipv4 <$> getPeerList respPeers) put
525 522
526 get = do 523 get = do
527 interval <- getWord32be 524 interval <- getWord32be
528 leechers <- getWord32be 525 leechers <- getWord32be
529 seeders <- getWord32be 526 seeders <- getWord32be
530 peers <- many get 527 peers <- many $ fmap IPv4 <$> get
531 528
532 return $ AnnounceInfo { 529 return $ AnnounceInfo {
533 respWarning = Nothing 530 respWarning = Nothing