diff options
Diffstat (limited to 'src/Network')
-rw-r--r-- | src/Network/BitTorrent/Core/PeerAddr.hs | 41 | ||||
-rw-r--r-- | src/Network/BitTorrent/Exchange/Wire.hs | 2 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker/Message.hs | 67 |
3 files changed, 80 insertions, 30 deletions
diff --git a/src/Network/BitTorrent/Core/PeerAddr.hs b/src/Network/BitTorrent/Core/PeerAddr.hs index 6c6056c9..88239d0b 100644 --- a/src/Network/BitTorrent/Core/PeerAddr.hs +++ b/src/Network/BitTorrent/Core/PeerAddr.hs | |||
@@ -21,11 +21,6 @@ module Network.BitTorrent.Core.PeerAddr | |||
21 | PeerAddr(..) | 21 | PeerAddr(..) |
22 | , defaultPorts | 22 | , defaultPorts |
23 | , peerSockAddr | 23 | , peerSockAddr |
24 | |||
25 | -- * IP | ||
26 | , mergeIPLists | ||
27 | , splitIPList | ||
28 | , IPAddress () | ||
29 | ) where | 24 | ) where |
30 | 25 | ||
31 | import Control.Applicative | 26 | import Control.Applicative |
@@ -36,8 +31,6 @@ import Data.BEncode.BDict (BKey) | |||
36 | import Data.ByteString.Char8 as BS8 | 31 | import Data.ByteString.Char8 as BS8 |
37 | import Data.Char | 32 | import Data.Char |
38 | import Data.Default | 33 | import Data.Default |
39 | import Data.Either | ||
40 | import Data.Foldable | ||
41 | import Data.IP | 34 | import Data.IP |
42 | import Data.List as L | 35 | import Data.List as L |
43 | import Data.List.Split | 36 | import Data.List.Split |
@@ -86,13 +79,16 @@ class IPAddress i where | |||
86 | 79 | ||
87 | instance IPAddress IPv4 where | 80 | instance IPAddress IPv4 where |
88 | toHostAddr = Left . toHostAddress | 81 | toHostAddr = Left . toHostAddress |
82 | {-# INLINE toHostAddr #-} | ||
89 | 83 | ||
90 | instance IPAddress IPv6 where | 84 | instance IPAddress IPv6 where |
91 | toHostAddr = Right . toHostAddress6 | 85 | toHostAddr = Right . toHostAddress6 |
86 | {-# INLINE toHostAddr #-} | ||
92 | 87 | ||
93 | instance IPAddress IP where | 88 | instance IPAddress IP where |
94 | toHostAddr (IPv4 ip) = toHostAddr ip | 89 | toHostAddr (IPv4 ip) = toHostAddr ip |
95 | toHostAddr (IPv6 ip) = toHostAddr ip | 90 | toHostAddr (IPv6 ip) = toHostAddr ip |
91 | {-# INLINE toHostAddr #-} | ||
96 | 92 | ||
97 | deriving instance Typeable IP | 93 | deriving instance Typeable IP |
98 | deriving instance Typeable IPv4 | 94 | deriving instance Typeable IPv4 |
@@ -100,6 +96,7 @@ deriving instance Typeable IPv6 | |||
100 | 96 | ||
101 | ipToBEncode :: Show i => i -> BValue | 97 | ipToBEncode :: Show i => i -> BValue |
102 | ipToBEncode ip = BString $ BS8.pack $ show ip | 98 | ipToBEncode ip = BString $ BS8.pack $ show ip |
99 | {-# INLINE ipToBEncode #-} | ||
103 | 100 | ||
104 | ipFromBEncode :: Read a => BValue -> BS.Result a | 101 | ipFromBEncode :: Read a => BValue -> BS.Result a |
105 | ipFromBEncode (BString (BS8.unpack -> ipStr)) | 102 | ipFromBEncode (BString (BS8.unpack -> ipStr)) |
@@ -107,17 +104,25 @@ ipFromBEncode (BString (BS8.unpack -> ipStr)) | |||
107 | | otherwise = decodingError $ "IP: " ++ ipStr | 104 | | otherwise = decodingError $ "IP: " ++ ipStr |
108 | ipFromBEncode _ = decodingError $ "IP: addr should be a bstring" | 105 | ipFromBEncode _ = decodingError $ "IP: addr should be a bstring" |
109 | 106 | ||
107 | instance Ord IP where | ||
108 | |||
110 | instance BEncode IP where | 109 | instance BEncode IP where |
111 | toBEncode = ipToBEncode | 110 | toBEncode = ipToBEncode |
111 | {-# INLINE toBEncode #-} | ||
112 | fromBEncode = ipFromBEncode | 112 | fromBEncode = ipFromBEncode |
113 | {-# INLINE fromBEncode #-} | ||
113 | 114 | ||
114 | instance BEncode IPv4 where | 115 | instance BEncode IPv4 where |
115 | toBEncode = ipToBEncode | 116 | toBEncode = ipToBEncode |
117 | {-# INLINE toBEncode #-} | ||
116 | fromBEncode = ipFromBEncode | 118 | fromBEncode = ipFromBEncode |
119 | {-# INLINE fromBEncode #-} | ||
117 | 120 | ||
118 | instance BEncode IPv6 where | 121 | instance BEncode IPv6 where |
119 | toBEncode = ipToBEncode | 122 | toBEncode = ipToBEncode |
123 | {-# INLINE toBEncode #-} | ||
120 | fromBEncode = ipFromBEncode | 124 | fromBEncode = ipFromBEncode |
125 | {-# INLINE fromBEncode #-} | ||
121 | 126 | ||
122 | instance Serialize IPv4 where | 127 | instance Serialize IPv4 where |
123 | put = putWord32host . toHostAddress | 128 | put = putWord32host . toHostAddress |
@@ -136,9 +141,14 @@ instance Serialize IPv6 where | |||
136 | -- compact list encoding. | 141 | -- compact list encoding. |
137 | data PeerAddr a = PeerAddr | 142 | data PeerAddr a = PeerAddr |
138 | { peerId :: !(Maybe PeerId) | 143 | { peerId :: !(Maybe PeerId) |
144 | |||
145 | -- | This is usually 'IPv4', 'IPv6', 'IP' or unresolved | ||
146 | -- 'HostName'. | ||
139 | , peerHost :: !a | 147 | , peerHost :: !a |
148 | |||
149 | -- | The port the peer listenning for incoming P2P sessions. | ||
140 | , peerPort :: {-# UNPACK #-} !PortNumber | 150 | , peerPort :: {-# UNPACK #-} !PortNumber |
141 | } deriving (Show, Eq, Typeable, Functor) | 151 | } deriving (Show, Eq, Ord, Typeable, Functor) |
142 | 152 | ||
143 | peer_ip_key, peer_id_key, peer_port_key :: BKey | 153 | peer_ip_key, peer_id_key, peer_port_key :: BKey |
144 | peer_ip_key = "ip" | 154 | peer_ip_key = "ip" |
@@ -160,17 +170,6 @@ instance (Typeable a, BEncode a) => BEncode (PeerAddr a) where | |||
160 | where | 170 | where |
161 | peerAddr = flip PeerAddr | 171 | peerAddr = flip PeerAddr |
162 | 172 | ||
163 | mergeIPLists :: [PeerAddr IPv4] -> Maybe [PeerAddr IPv6] -> [PeerAddr IP] | ||
164 | mergeIPLists v4 v6 = (fmap IPv4 `L.map` v4) | ||
165 | ++ (fmap IPv6 `L.map` Data.Foldable.concat v6) | ||
166 | |||
167 | splitIPList :: [PeerAddr IP] -> ([PeerAddr IPv4],[PeerAddr IPv6]) | ||
168 | splitIPList xs = partitionEithers $ toEither <$> xs | ||
169 | where | ||
170 | toEither :: PeerAddr IP -> Either (PeerAddr IPv4) (PeerAddr IPv6) | ||
171 | toEither pa@(PeerAddr _ (IPv4 _) _) = Left (ipv4 <$> pa) | ||
172 | toEither pa@(PeerAddr _ (IPv6 _) _) = Right (ipv6 <$> pa) | ||
173 | |||
174 | -- | The tracker's 'compact peer list' compatible encoding. The | 173 | -- | The tracker's 'compact peer list' compatible encoding. The |
175 | -- 'peerId' is always 'Nothing'. | 174 | -- 'peerId' is always 'Nothing'. |
176 | -- | 175 | -- |
@@ -231,8 +230,8 @@ defaultPorts = [6881..6889] | |||
231 | _resolvePeerAddr :: (IPAddress i) => PeerAddr HostName -> PeerAddr i | 230 | _resolvePeerAddr :: (IPAddress i) => PeerAddr HostName -> PeerAddr i |
232 | _resolvePeerAddr = undefined | 231 | _resolvePeerAddr = undefined |
233 | 232 | ||
234 | -- | Convert peer info from tracker response to socket address. Used | 233 | -- | Convert peer info from tracker or DHT announce query response to |
235 | -- for establish connection between peers. | 234 | -- socket address. Usually used to intiate connection between peers. |
236 | -- | 235 | -- |
237 | peerSockAddr :: PeerAddr IP -> SockAddr | 236 | peerSockAddr :: PeerAddr IP -> SockAddr |
238 | peerSockAddr PeerAddr {..} = | 237 | peerSockAddr PeerAddr {..} = |
diff --git a/src/Network/BitTorrent/Exchange/Wire.hs b/src/Network/BitTorrent/Exchange/Wire.hs index 2a7d2aeb..5f7b0ebe 100644 --- a/src/Network/BitTorrent/Exchange/Wire.hs +++ b/src/Network/BitTorrent/Exchange/Wire.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 | -- |
676 | acceptWire :: (IPAddress i) => Socket -> PeerAddr i -> Wire () -> IO () | 676 | acceptWire :: Socket -> PeerAddr IP -> Wire () -> IO () |
677 | acceptWire sock peerAddr wire = do | 677 | acceptWire 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 d0be1c36..da46628b 100644 --- a/src/Network/BitTorrent/Tracker/Message.hs +++ b/src/Network/BitTorrent/Tracker/Message.hs | |||
@@ -70,7 +70,6 @@ module Network.BitTorrent.Tracker.Message | |||
70 | 70 | ||
71 | import Control.Applicative | 71 | import Control.Applicative |
72 | import Control.Monad | 72 | import Control.Monad |
73 | import Data.Aeson (ToJSON(..), FromJSON(..)) | ||
74 | import Data.Aeson.TH | 73 | import Data.Aeson.TH |
75 | import Data.BEncode as BE hiding (Result) | 74 | import Data.BEncode as BE hiding (Result) |
76 | import Data.BEncode.BDict as BE | 75 | import Data.BEncode.BDict as BE |
@@ -79,8 +78,10 @@ import Data.ByteString.Char8 as BC | |||
79 | import Data.Char as Char | 78 | import Data.Char as Char |
80 | import Data.Convertible | 79 | import Data.Convertible |
81 | import Data.Default | 80 | import Data.Default |
81 | import Data.Either | ||
82 | import Data.List as L | 82 | import Data.List as L |
83 | import Data.Maybe | 83 | import Data.Maybe |
84 | import Data.Monoid | ||
84 | import Data.Serialize as S hiding (Result) | 85 | import Data.Serialize as S hiding (Result) |
85 | import Data.String | 86 | import Data.String |
86 | import Data.Text (Text) | 87 | import Data.Text (Text) |
@@ -193,9 +194,9 @@ instance Serialize AnnounceQuery where | |||
193 | put reqPeerId | 194 | put reqPeerId |
194 | put reqProgress | 195 | put reqProgress |
195 | putEvent reqEvent | 196 | putEvent reqEvent |
196 | putWord32be $ fromMaybe 0 reqIP | 197 | putWord32host $ fromMaybe 0 reqIP |
197 | putWord32be $ 0 -- TODO what the fuck is "key"? | 198 | putWord32be $ 0 -- TODO what the fuck is "key"? |
198 | putWord32be $ fromIntegral $ fromMaybe (-1) reqNumWant | 199 | putWord32be $ fromIntegral $ fromMaybe (-1) reqNumWant |
199 | 200 | ||
200 | put reqPort | 201 | put reqPort |
201 | 202 | ||
@@ -415,7 +416,9 @@ data AnnounceRequest = AnnounceRequest | |||
415 | } deriving (Show, Eq, Typeable) | 416 | } deriving (Show, Eq, Typeable) |
416 | 417 | ||
417 | instance QueryLike AnnounceRequest where | 418 | instance QueryLike AnnounceRequest where |
418 | toQuery AnnounceRequest{..} = toQuery announceAdvises ++ toQuery announceQuery | 419 | toQuery AnnounceRequest{..} = |
420 | toQuery announceAdvises <> | ||
421 | toQuery announceQuery | ||
419 | 422 | ||
420 | -- | Parse announce request from query string. | 423 | -- | Parse announce request from query string. |
421 | parseAnnounceRequest :: SimpleQuery -> ParseResult AnnounceRequest | 424 | parseAnnounceRequest :: SimpleQuery -> ParseResult AnnounceRequest |
@@ -441,6 +444,11 @@ data PeerList ip | |||
441 | | CompactPeerList [PeerAddr ip] | 444 | | CompactPeerList [PeerAddr ip] |
442 | deriving (Show, Eq, Typeable, Functor) | 445 | deriving (Show, Eq, Typeable, Functor) |
443 | 446 | ||
447 | -- | The empty non-compact peer list. | ||
448 | instance Default (PeerList IP) where | ||
449 | def = PeerList [] | ||
450 | {-# INLINE def #-} | ||
451 | |||
444 | getPeerList :: PeerList IP -> [PeerAddr IP] | 452 | getPeerList :: PeerList IP -> [PeerAddr IP] |
445 | getPeerList (PeerList xs) = xs | 453 | getPeerList (PeerList xs) = xs |
446 | getPeerList (CompactPeerList xs) = xs | 454 | getPeerList (CompactPeerList xs) = xs |
@@ -482,6 +490,17 @@ data AnnounceInfo = | |||
482 | , respWarning :: !(Maybe Text) | 490 | , respWarning :: !(Maybe Text) |
483 | } deriving (Show, Eq, Typeable) | 491 | } deriving (Show, Eq, Typeable) |
484 | 492 | ||
493 | -- | Empty peer list with default reannounce interval. | ||
494 | instance Default AnnounceInfo where | ||
495 | def = AnnounceInfo | ||
496 | { respComplete = Nothing | ||
497 | , respIncomplete = Nothing | ||
498 | , respInterval = defaultReannounceInterval | ||
499 | , respMinInterval = Nothing | ||
500 | , respPeers = def | ||
501 | , respWarning = Nothing | ||
502 | } | ||
503 | |||
485 | -- | HTTP tracker protocol compatible encoding. | 504 | -- | HTTP tracker protocol compatible encoding. |
486 | instance BEncode AnnounceInfo where | 505 | instance BEncode AnnounceInfo where |
487 | toBEncode (Failure t) = toDict $ | 506 | toBEncode (Failure t) = toDict $ |
@@ -494,10 +513,24 @@ instance BEncode AnnounceInfo where | |||
494 | .: "interval" .=! respInterval | 513 | .: "interval" .=! respInterval |
495 | .: "min interval" .=? respMinInterval | 514 | .: "min interval" .=? respMinInterval |
496 | .: "peers" .=! peers | 515 | .: "peers" .=! peers |
497 | .: "peers6" .=! peers6 | 516 | .: "peers6" .=? peers6 |
498 | .: "warning message" .=? respWarning | 517 | .: "warning message" .=? respWarning |
499 | .: endDict | 518 | .: endDict |
500 | where (peers,peers6) = splitIPList $ getPeerList respPeers | 519 | where |
520 | (peers, peers6) = prttn respPeers | ||
521 | |||
522 | prttn :: PeerList IP -> (PeerList IPv4, Maybe (PeerList IPv6)) | ||
523 | prttn (PeerList xs) = (PeerList xs, Nothing) | ||
524 | prttn (CompactPeerList xs) = mk $ partitionEithers $ toEither <$> xs | ||
525 | where | ||
526 | mk (v4s, v6s) | ||
527 | | L.null v6s = (CompactPeerList v4s, Nothing) | ||
528 | | otherwise = (CompactPeerList v4s, Just (CompactPeerList v6s)) | ||
529 | |||
530 | toEither :: PeerAddr IP -> Either (PeerAddr IPv4) (PeerAddr IPv6) | ||
531 | toEither PeerAddr {..} = case peerHost of | ||
532 | IPv4 ipv4 -> Left $ PeerAddr peerId ipv4 peerPort | ||
533 | IPv6 ipv6 -> Right $ PeerAddr peerId ipv6 peerPort | ||
501 | 534 | ||
502 | fromBEncode (BDict d) | 535 | fromBEncode (BDict d) |
503 | | Just t <- BE.lookup "failure reason" d = Failure <$> fromBEncode t | 536 | | Just t <- BE.lookup "failure reason" d = Failure <$> fromBEncode t |
@@ -507,8 +540,26 @@ instance BEncode AnnounceInfo where | |||
507 | <*>? "incomplete" | 540 | <*>? "incomplete" |
508 | <*>! "interval" | 541 | <*>! "interval" |
509 | <*>? "min interval" | 542 | <*>? "min interval" |
510 | <*> (PeerList <$> (mergeIPLists <$>! "peers" <*>? "peers6")) | 543 | <*> (uncurry merge =<< (,) <$>! "peers" <*>? "peers6") |
511 | <*>? "warning message" | 544 | <*>? "warning message" |
545 | where | ||
546 | merge :: PeerList IPv4 -> Maybe (PeerList IPv6) -> BE.Get (PeerList IP) | ||
547 | merge (PeerList ips) Nothing = pure (PeerList ips) | ||
548 | merge (PeerList _ ) (Just _) | ||
549 | = fail "PeerList: non-compact peer list provided, \ | ||
550 | \but the `peers6' field present" | ||
551 | |||
552 | merge (CompactPeerList ipv4s) Nothing | ||
553 | = pure $ CompactPeerList (fmap IPv4 <$> ipv4s) | ||
554 | |||
555 | merge (CompactPeerList _ ) (Just (PeerList _)) | ||
556 | = fail "PeerList: the `peers6' field value \ | ||
557 | \should contain *compact* peer list" | ||
558 | |||
559 | merge (CompactPeerList ipv4s) (Just (CompactPeerList ipv6s)) | ||
560 | = pure $ CompactPeerList $ | ||
561 | (fmap IPv4 <$> ipv4s) <> (fmap IPv6 <$> ipv6s) | ||
562 | |||
512 | fromBEncode _ = decodingError "Announce info" | 563 | fromBEncode _ = decodingError "Announce info" |
513 | 564 | ||
514 | -- | UDP tracker protocol compatible encoding. | 565 | -- | UDP tracker protocol compatible encoding. |