diff options
Diffstat (limited to 'src/Network/BitTorrent/DHT')
-rw-r--r-- | src/Network/BitTorrent/DHT/ContactInfo.hs | 59 | ||||
-rw-r--r-- | src/Network/BitTorrent/DHT/Session.hs | 22 |
2 files changed, 68 insertions, 13 deletions
diff --git a/src/Network/BitTorrent/DHT/ContactInfo.hs b/src/Network/BitTorrent/DHT/ContactInfo.hs index 4302288c..117325fc 100644 --- a/src/Network/BitTorrent/DHT/ContactInfo.hs +++ b/src/Network/BitTorrent/DHT/ContactInfo.hs | |||
@@ -15,6 +15,8 @@ import Data.Serialize | |||
15 | import Data.PSQueue as PSQ | 15 | import Data.PSQueue as PSQ |
16 | import Data.Time.Clock.POSIX | 16 | import Data.Time.Clock.POSIX |
17 | import Data.ByteString (ByteString) | 17 | import Data.ByteString (ByteString) |
18 | import Data.Word | ||
19 | import Network.Socket (SockAddr(..)) | ||
18 | 20 | ||
19 | import Data.Torrent | 21 | import Data.Torrent |
20 | import Network.BitTorrent.Address | 22 | import Network.BitTorrent.Address |
@@ -122,6 +124,40 @@ data SwarmData ip = SwarmData | |||
122 | , name :: Maybe ByteString | 124 | , name :: Maybe ByteString |
123 | } | 125 | } |
124 | 126 | ||
127 | |||
128 | newtype SerializeAddress a = SerializeAddress { unserializeAddress :: a } | ||
129 | |||
130 | instance Address a => Serialize (SerializeAddress a) where | ||
131 | get = SerializeAddress <$> do | ||
132 | c <- get | ||
133 | case (c::Word8) of | ||
134 | 0x34 -> do ip4 <- get | ||
135 | return $ fromJust $ fromAddr (ip4::IPv4) | ||
136 | 0x36 -> do ip6 <- get | ||
137 | return $ fromJust $ fromAddr (ip6::IPv6) | ||
138 | _ -> return $ error "cannot deserialize non-IP SerializeAddress" | ||
139 | put (SerializeAddress a) | ||
140 | | Just ip4 <- fromAddr a | ||
141 | = put (0x34::Word8) >> put (ip4::IPv4) | ||
142 | | Just ip6 <- fromAddr a | ||
143 | = put (0x36::Word8) >> put (ip6::IPv6) | ||
144 | | otherwise = return $ error "cannot serialize non-IP SerializeAddress" | ||
145 | |||
146 | |||
147 | instance (Ord ip, Address ip) => Serialize (SwarmData ip) where | ||
148 | get = flip SwarmData <$> get | ||
149 | <*> ( PSQ.fromList . L.map parseAddr <$> get ) | ||
150 | where | ||
151 | parseAddr addr = (unserializeAddress <$> addr) | ||
152 | :-> 0 | ||
153 | |||
154 | put SwarmData{..} = do | ||
155 | put name | ||
156 | put $ L.map (\(addr :-> _) -> (SerializeAddress <$> addr)) | ||
157 | -- XXX: should we serialize the timestamp? | ||
158 | $ PSQ.toList peers | ||
159 | |||
160 | |||
125 | knownSwarms :: PeerStore ip -> [ (InfoHash, Int, Maybe ByteString) ] | 161 | knownSwarms :: PeerStore ip -> [ (InfoHash, Int, Maybe ByteString) ] |
126 | knownSwarms (PeerStore m) = L.map (\(ih,SwarmData q n) -> (ih,PSQ.size q,n)) $ HM.toList m | 162 | knownSwarms (PeerStore m) = L.map (\(ih,SwarmData q n) -> (ih,PSQ.size q,n)) $ HM.toList m |
127 | 163 | ||
@@ -136,26 +172,27 @@ swarmInsert old new = SwarmData | |||
136 | , name = name new <|> name old -- TODO: decodeUtf8' check | 172 | , name = name new <|> name old -- TODO: decodeUtf8' check |
137 | } | 173 | } |
138 | 174 | ||
175 | isSwarmOccupied SwarmData{..} = not $ PSQ.null peers | ||
139 | 176 | ||
140 | -- | Empty store. | 177 | -- | Empty store. |
141 | instance Default (PeerStore a) where | 178 | instance Default (PeerStore a) where |
142 | def = PeerStore HM.empty | 179 | def = PeerStore HM.empty |
143 | {-# INLINE def #-} | 180 | {-# INLINE def #-} |
144 | 181 | ||
145 | -- -- | Monoid under union operation. | 182 | -- | Monoid under union operation. |
146 | -- instance Eq a => Monoid (PeerStore a) where | 183 | instance Ord a => Monoid (PeerStore a) where |
147 | -- mempty = def | 184 | mempty = def |
148 | -- {-# INLINE mempty #-} | 185 | {-# INLINE mempty #-} |
149 | -- | 186 | |
150 | -- mappend (PeerStore a) (PeerStore b) = | 187 | mappend (PeerStore a) (PeerStore b) = |
151 | -- PeerStore (HM.unionWith L.union a b) | 188 | PeerStore (HM.unionWith swarmInsert a b) |
152 | -- {-# INLINE mappend #-} | 189 | {-# INLINE mappend #-} |
153 | 190 | ||
154 | -- | Can be used to store peers between invocations of the client | 191 | -- | Can be used to store peers between invocations of the client |
155 | -- software. | 192 | -- software. |
156 | instance Serialize (PeerStore a) where | 193 | instance (Ord a, Address a) => Serialize (PeerStore a) where |
157 | get = undefined | 194 | get = PeerStore . HM.fromList <$> get |
158 | put = undefined | 195 | put (PeerStore m) = put (L.filter (isSwarmOccupied . snd) $ HM.toList m) |
159 | 196 | ||
160 | -- | Returns all peers associated with a given info hash. | 197 | -- | Returns all peers associated with a given info hash. |
161 | lookup :: Ord a => InfoHash -> PeerStore a -> [PeerAddr a] | 198 | lookup :: Ord a => InfoHash -> PeerStore a -> [PeerAddr a] |
diff --git a/src/Network/BitTorrent/DHT/Session.hs b/src/Network/BitTorrent/DHT/Session.hs index bc9fda91..c08021c7 100644 --- a/src/Network/BitTorrent/DHT/Session.hs +++ b/src/Network/BitTorrent/DHT/Session.hs | |||
@@ -54,14 +54,16 @@ module Network.BitTorrent.DHT.Session | |||
54 | -- ** Routing table | 54 | -- ** Routing table |
55 | , getTable | 55 | , getTable |
56 | , getClosest | 56 | , getClosest |
57 | , getSwarms | ||
58 | , allPeers | ||
59 | 57 | ||
60 | -- ** Peer storage | 58 | -- ** Peer storage |
61 | , insertPeer | 59 | , insertPeer |
62 | , getPeerList | 60 | , getPeerList |
63 | , insertTopic | 61 | , insertTopic |
64 | , deleteTopic | 62 | , deleteTopic |
63 | , getSwarms | ||
64 | , savePeerStore | ||
65 | , mergeSavedPeers | ||
66 | , allPeers | ||
65 | 67 | ||
66 | -- ** Messaging | 68 | -- ** Messaging |
67 | , queryParallel | 69 | , queryParallel |
@@ -84,6 +86,7 @@ import Data.Fixed | |||
84 | import Data.Hashable | 86 | import Data.Hashable |
85 | import Data.List as L | 87 | import Data.List as L |
86 | import Data.Maybe | 88 | import Data.Maybe |
89 | import Data.Monoid | ||
87 | import Data.Set as S | 90 | import Data.Set as S |
88 | import Data.Time | 91 | import Data.Time |
89 | import Network (PortNumber) | 92 | import Network (PortNumber) |
@@ -91,6 +94,7 @@ import System.Random (randomIO) | |||
91 | import Data.Time.Clock.POSIX | 94 | import Data.Time.Clock.POSIX |
92 | import Data.Text as Text | 95 | import Data.Text as Text |
93 | import Text.PrettyPrint.HughesPJClass hiding ((<>),($$)) | 96 | import Text.PrettyPrint.HughesPJClass hiding ((<>),($$)) |
97 | import Data.Serialize as S | ||
94 | 98 | ||
95 | 99 | ||
96 | import Data.Torrent as Torrent | 100 | import Data.Torrent as Torrent |
@@ -409,6 +413,20 @@ getSwarms = do | |||
409 | store <- asks contactInfo >>= liftIO . atomically . readTVar | 413 | store <- asks contactInfo >>= liftIO . atomically . readTVar |
410 | return $ P.knownSwarms store | 414 | return $ P.knownSwarms store |
411 | 415 | ||
416 | savePeerStore :: (Ord ip, Address ip) => DHT ip ByteString | ||
417 | savePeerStore = do | ||
418 | var <- asks contactInfo | ||
419 | peers <- liftIO $ atomically $ readTVar var | ||
420 | return $ S.encode peers | ||
421 | |||
422 | mergeSavedPeers :: (Ord ip, Address ip) => ByteString -> DHT ip () | ||
423 | mergeSavedPeers bs = do | ||
424 | var <- asks contactInfo | ||
425 | case S.decode bs of | ||
426 | Right newbies -> liftIO $ atomically $ modifyTVar' var (<> newbies) | ||
427 | Left _ -> return () | ||
428 | |||
429 | |||
412 | allPeers :: Ord ip => InfoHash -> DHT ip [ PeerAddr ip ] | 430 | allPeers :: Ord ip => InfoHash -> DHT ip [ PeerAddr ip ] |
413 | allPeers ih = do | 431 | allPeers ih = do |
414 | store <- asks contactInfo >>= liftIO . atomically . readTVar | 432 | store <- asks contactInfo >>= liftIO . atomically . readTVar |