summaryrefslogtreecommitdiff
path: root/src/Network/BitTorrent/Core/PeerAddr.hs
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-12-15 19:44:12 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-12-15 19:44:12 +0400
commitc1e3c9762eb5fea16188a0fb21ad01dd3240ab88 (patch)
treef65cffdb5156c1140dead382d4a29da845e70e53 /src/Network/BitTorrent/Core/PeerAddr.hs
parentaee6069785bd552100824e36995e55e72bdbb42e (diff)
Fix bugs in PeerAddr encoding.
Also: * PeerAddr.hs internals and export list have been simplified; * tests added.
Diffstat (limited to 'src/Network/BitTorrent/Core/PeerAddr.hs')
-rw-r--r--src/Network/BitTorrent/Core/PeerAddr.hs109
1 files changed, 64 insertions, 45 deletions
diff --git a/src/Network/BitTorrent/Core/PeerAddr.hs b/src/Network/BitTorrent/Core/PeerAddr.hs
index 60ada54d..3c3e98c5 100644
--- a/src/Network/BitTorrent/Core/PeerAddr.hs
+++ b/src/Network/BitTorrent/Core/PeerAddr.hs
@@ -14,6 +14,7 @@
14{-# LANGUAGE DeriveDataTypeable #-} 14{-# LANGUAGE DeriveDataTypeable #-}
15{-# LANGUAGE FlexibleInstances #-} 15{-# LANGUAGE FlexibleInstances #-}
16{-# LANGUAGE DeriveFunctor #-} 16{-# LANGUAGE DeriveFunctor #-}
17{-# LANGUAGE ViewPatterns #-}
17{-# OPTIONS -fno-warn-orphans #-} -- for PortNumber instances 18{-# OPTIONS -fno-warn-orphans #-} -- for PortNumber instances
18module Network.BitTorrent.Core.PeerAddr 19module Network.BitTorrent.Core.PeerAddr
19 ( -- * Peer address 20 ( -- * Peer address
@@ -24,25 +25,26 @@ module Network.BitTorrent.Core.PeerAddr
24 -- * IP 25 -- * IP
25 , mergeIPLists 26 , mergeIPLists
26 , splitIPList 27 , splitIPList
27 , IP, IPv4, IPv6 --re-export Data.IP constructors
28 , IPAddress () 28 , IPAddress ()
29 ) where 29 ) where
30 30
31import Control.Applicative 31import Control.Applicative
32import Control.Monad
32import Data.Aeson (ToJSON, FromJSON) 33import Data.Aeson (ToJSON, FromJSON)
33import Data.BEncode as BS 34import Data.BEncode as BS
34import Data.BEncode.BDict (BKey) 35import Data.BEncode.BDict (BKey)
35import Data.ByteString.Char8 as BS8 36import Data.ByteString.Char8 as BS8
36import Data.Char 37import Data.Char
37import Data.Default 38import Data.Default
39import Data.Either
40import Data.Foldable
41import Data.IP
38import Data.List as L 42import Data.List as L
39import Data.List.Split 43import Data.List.Split
40import Data.Serialize as S 44import Data.Serialize as S
41import Data.String 45import Data.String
42import Data.Typeable 46import Data.Typeable
43import Data.IP 47import Data.Word
44import Data.Foldable
45import Data.Either
46import Network.Socket 48import Network.Socket
47import Text.PrettyPrint 49import Text.PrettyPrint
48import Text.PrettyPrint.Class 50import Text.PrettyPrint.Class
@@ -52,12 +54,22 @@ import qualified Text.ParserCombinators.ReadP as RP
52import Network.BitTorrent.Core.PeerId 54import Network.BitTorrent.Core.PeerId
53 55
54 56
57{-----------------------------------------------------------------------
58-- Port number
59-----------------------------------------------------------------------}
60
55deriving instance ToJSON PortNumber 61deriving instance ToJSON PortNumber
56deriving instance FromJSON PortNumber 62deriving instance FromJSON PortNumber
57 63
58instance BEncode PortNumber where 64instance BEncode PortNumber where
59 toBEncode = toBEncode . fromEnum 65 toBEncode = toBEncode . fromEnum
60 fromBEncode b = toEnum <$> fromBEncode b 66 fromBEncode = fromBEncode >=> portNumber
67 where
68 portNumber :: Integer -> BS.Result PortNumber
69 portNumber n
70 | 0 <= n && n <= fromIntegral (maxBound :: Word16)
71 = pure $ fromIntegral n
72 | otherwise = decodingError $ "PortNumber: " ++ show n
61 73
62instance Serialize PortNumber where 74instance Serialize PortNumber where
63 get = fromIntegral <$> getWord16be 75 get = fromIntegral <$> getWord16be
@@ -65,57 +77,59 @@ instance Serialize PortNumber where
65 put = putWord16be . fromIntegral 77 put = putWord16be . fromIntegral
66 {-# INLINE put #-} 78 {-# INLINE put #-}
67 79
68class (Show i, Read i) => IPAddress i where 80{-----------------------------------------------------------------------
69 showIp :: i -> String 81-- IP addr
70 showIp = show 82-----------------------------------------------------------------------}
71 83
72 readIp :: String -> i 84class IPAddress i where
73 readIp = read 85 toHostAddr :: i -> Either HostAddress HostAddress6
74
75 toHostAddr :: i -> Either HostAddress HostAddress6
76 86
77instance IPAddress IPv4 where 87instance IPAddress IPv4 where
78 toHostAddr = Left . toHostAddress 88 toHostAddr = Left . toHostAddress
79 89
80instance IPAddress IPv6 where 90instance IPAddress IPv6 where
81 toHostAddr = Right . toHostAddress6 91 toHostAddr = Right . toHostAddress6
82 92
83instance IPAddress IP where 93instance IPAddress IP where
84 toHostAddr (IPv4 ip) = toHostAddr ip 94 toHostAddr (IPv4 ip) = toHostAddr ip
85 toHostAddr (IPv6 ip) = toHostAddr ip 95 toHostAddr (IPv6 ip) = toHostAddr ip
86
87 96
88deriving instance Typeable IP 97deriving instance Typeable IP
89deriving instance Typeable IPv4 98deriving instance Typeable IPv4
90deriving instance Typeable IPv6 99deriving instance Typeable IPv6
91 100
92ipToBEncode :: IPAddress i => i -> BValue 101ipToBEncode :: Show i => i -> BValue
93ipToBEncode ip = BString $ BS8.pack $ showIp ip 102ipToBEncode ip = BString $ BS8.pack $ show ip
94 103
95ipFromBEncode :: Monad m => IPAddress a => BValue -> m a 104ipFromBEncode :: Read a => BValue -> BS.Result a
96ipFromBEncode (BString ip) = return $ readIp $ BS8.unpack ip 105ipFromBEncode (BString (BS8.unpack -> ipStr))
97ipFromBEncode _ = fail "ipFromBEncode" 106 | Just ip <- readMaybe (ipStr) = pure ip
107 | otherwise = decodingError $ "IP: " ++ ipStr
108ipFromBEncode _ = decodingError $ "IP: addr should be a bstring"
98 109
99instance BEncode IP where 110instance BEncode IP where
100 toBEncode = ipToBEncode 111 toBEncode = ipToBEncode
101 fromBEncode = ipFromBEncode 112 fromBEncode = ipFromBEncode
102 113
103instance BEncode IPv4 where 114instance BEncode IPv4 where
104 toBEncode = ipToBEncode 115 toBEncode = ipToBEncode
105 fromBEncode = ipFromBEncode 116 fromBEncode = ipFromBEncode
106 117
107instance BEncode IPv6 where 118instance BEncode IPv6 where
108 toBEncode = ipToBEncode 119 toBEncode = ipToBEncode
109 fromBEncode = ipFromBEncode 120 fromBEncode = ipFromBEncode
110 121
111instance Serialize IPv4 where 122instance Serialize IPv4 where
112 put ip = put $ toHostAddress ip 123 put = putWord32host . toHostAddress
113 get = fromHostAddress <$> get 124 get = fromHostAddress <$> getWord32host
114 125
115instance Serialize IPv6 where 126instance Serialize IPv6 where
116 put ip = put $ toHostAddress6 ip 127 put ip = put $ toHostAddress6 ip
117 get = fromHostAddress6 <$> get 128 get = fromHostAddress6 <$> get
118 129
130{-----------------------------------------------------------------------
131-- Peer addr
132-----------------------------------------------------------------------}
119-- TODO check semantic of ord and eq instances 133-- TODO check semantic of ord and eq instances
120 134
121-- | Peer address info normally extracted from peer list or peer 135-- | Peer address info normally extracted from peer list or peer
@@ -126,23 +140,25 @@ data PeerAddr a = PeerAddr
126 , peerPort :: {-# UNPACK #-} !PortNumber 140 , peerPort :: {-# UNPACK #-} !PortNumber
127 } deriving (Show, Eq, Typeable, Functor) 141 } deriving (Show, Eq, Typeable, Functor)
128 142
129peer_id_key, peer_ip_key, peer_port_key :: BKey 143peer_ip_key, peer_id_key, peer_port_key :: BKey
130peer_id_key = "peer id"
131peer_ip_key = "ip" 144peer_ip_key = "ip"
145peer_id_key = "peer id"
132peer_port_key = "port" 146peer_port_key = "port"
133 147
134-- | The tracker's 'announce response' compatible encoding. 148-- | The tracker's 'announce response' compatible encoding.
135instance (Typeable a, BEncode a) => BEncode (PeerAddr a) where 149instance (Typeable a, BEncode a) => BEncode (PeerAddr a) where
136 toBEncode PeerAddr {..} = toDict $ 150 toBEncode PeerAddr {..} = toDict $
137 peer_id_key .=? peerId 151 peer_ip_key .=! peerAddr
138 .: peer_ip_key .=! peerAddr 152 .: peer_id_key .=? peerId
139 .: peer_port_key .=! peerPort 153 .: peer_port_key .=! peerPort
140 .: endDict 154 .: endDict
141 155
142 fromBEncode = fromDict $ do 156 fromBEncode = fromDict $ do
143 PeerAddr <$>? peer_id_key 157 peerAddr <$>? peer_id_key
144 <*>! peer_ip_key 158 <*>! peer_ip_key
145 <*>! peer_port_key 159 <*>! peer_port_key
160 where
161 peerAddr ip pid port = PeerAddr ip pid port
146 162
147mergeIPLists :: [PeerAddr IPv4] -> Maybe [PeerAddr IPv6] -> [PeerAddr IP] 163mergeIPLists :: [PeerAddr IPv4] -> Maybe [PeerAddr IPv6] -> [PeerAddr IP]
148mergeIPLists v4 v6 = (fmap IPv4 `L.map` v4) 164mergeIPLists v4 v6 = (fmap IPv4 `L.map` v4)
@@ -162,10 +178,8 @@ splitIPList xs = partitionEithers $ toEither <$> xs
162-- 178--
163-- TODO: test byte order 179-- TODO: test byte order
164instance (Serialize a) => Serialize (PeerAddr a) where 180instance (Serialize a) => Serialize (PeerAddr a) where
165 put PeerAddr {..} = 181 put PeerAddr {..} = put peerAddr >> put peerPort
166 put peerAddr >> put peerPort 182 get = PeerAddr Nothing <$> get <*> get
167 get =
168 PeerAddr Nothing <$> get <*> get
169 183
170-- | @127.0.0.1:6881@ 184-- | @127.0.0.1:6881@
171instance Default (PeerAddr IPv4) where 185instance Default (PeerAddr IPv4) where
@@ -178,7 +192,7 @@ instance Default (PeerAddr IPv4) where
178instance IsString (PeerAddr IPv4) where 192instance IsString (PeerAddr IPv4) where
179 fromString str 193 fromString str
180 | [hostAddrStr, portStr] <- splitWhen (== ':') str 194 | [hostAddrStr, portStr] <- splitWhen (== ':') str
181 , hostAddr <- read hostAddrStr 195 , Just hostAddr <- readMaybe hostAddrStr
182 , Just portNum <- toEnum <$> readMaybe portStr 196 , Just portNum <- toEnum <$> readMaybe portStr
183 = PeerAddr Nothing hostAddr portNum 197 = PeerAddr Nothing hostAddr portNum
184 | otherwise = error $ "fromString: unable to parse (PeerAddr IPv4): " ++ str 198 | otherwise = error $ "fromString: unable to parse (PeerAddr IPv4): " ++ str
@@ -196,6 +210,11 @@ instance IsString (PeerAddr IPv6) where
196 PeerAddr Nothing ip port 210 PeerAddr Nothing ip port
197 | otherwise = error $ "fromString: unable to parse (PeerAddr IPv6): " ++ str 211 | otherwise = error $ "fromString: unable to parse (PeerAddr IPv6): " ++ str
198 212
213instance IsString (PeerAddr IP) where
214 fromString str
215 | '[' `L.elem` str = IPv6 <$> fromString str
216 | otherwise = IPv4 <$> fromString str
217
199-- | fingerprint + "at" + dotted.host.inet.addr:port 218-- | fingerprint + "at" + dotted.host.inet.addr:port
200-- TODO: instances for IPv6, HostName 219-- TODO: instances for IPv6, HostName
201instance Pretty (PeerAddr IP) where 220instance Pretty (PeerAddr IP) where
@@ -215,8 +234,8 @@ _resolvePeerAddr = undefined
215-- | Convert peer info from tracker response to socket address. Used 234-- | Convert peer info from tracker response to socket address. Used
216-- for establish connection between peers. 235-- for establish connection between peers.
217-- 236--
218peerSockAddr :: (IPAddress i) => PeerAddr i -> SockAddr 237peerSockAddr :: PeerAddr IP -> SockAddr
219peerSockAddr PeerAddr {..} = 238peerSockAddr PeerAddr {..} =
220 case toHostAddr peerAddr of 239 case peerAddr of
221 Left host4 -> SockAddrInet peerPort host4 240 IPv4 ipv4 -> SockAddrInet peerPort (toHostAddress ipv4)
222 Right host6 -> SockAddrInet6 peerPort 0 host6 0 241 IPv6 ipv6 -> SockAddrInet6 peerPort 0 (toHostAddress6 ipv6) 0