diff options
Diffstat (limited to 'src/Network/BitTorrent/Core/PeerAddr.hs')
-rw-r--r-- | src/Network/BitTorrent/Core/PeerAddr.hs | 109 |
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 |
18 | module Network.BitTorrent.Core.PeerAddr | 19 | module 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 | ||
31 | import Control.Applicative | 31 | import Control.Applicative |
32 | import Control.Monad | ||
32 | import Data.Aeson (ToJSON, FromJSON) | 33 | import Data.Aeson (ToJSON, FromJSON) |
33 | import Data.BEncode as BS | 34 | import Data.BEncode as BS |
34 | import Data.BEncode.BDict (BKey) | 35 | import Data.BEncode.BDict (BKey) |
35 | import Data.ByteString.Char8 as BS8 | 36 | import Data.ByteString.Char8 as BS8 |
36 | import Data.Char | 37 | import Data.Char |
37 | import Data.Default | 38 | import Data.Default |
39 | import Data.Either | ||
40 | import Data.Foldable | ||
41 | import Data.IP | ||
38 | import Data.List as L | 42 | import Data.List as L |
39 | import Data.List.Split | 43 | import Data.List.Split |
40 | import Data.Serialize as S | 44 | import Data.Serialize as S |
41 | import Data.String | 45 | import Data.String |
42 | import Data.Typeable | 46 | import Data.Typeable |
43 | import Data.IP | 47 | import Data.Word |
44 | import Data.Foldable | ||
45 | import Data.Either | ||
46 | import Network.Socket | 48 | import Network.Socket |
47 | import Text.PrettyPrint | 49 | import Text.PrettyPrint |
48 | import Text.PrettyPrint.Class | 50 | import Text.PrettyPrint.Class |
@@ -52,12 +54,22 @@ import qualified Text.ParserCombinators.ReadP as RP | |||
52 | import Network.BitTorrent.Core.PeerId | 54 | import Network.BitTorrent.Core.PeerId |
53 | 55 | ||
54 | 56 | ||
57 | {----------------------------------------------------------------------- | ||
58 | -- Port number | ||
59 | -----------------------------------------------------------------------} | ||
60 | |||
55 | deriving instance ToJSON PortNumber | 61 | deriving instance ToJSON PortNumber |
56 | deriving instance FromJSON PortNumber | 62 | deriving instance FromJSON PortNumber |
57 | 63 | ||
58 | instance BEncode PortNumber where | 64 | instance 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 | ||
62 | instance Serialize PortNumber where | 74 | instance 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 | ||
68 | class (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 | 84 | class IPAddress i where |
73 | readIp = read | 85 | toHostAddr :: i -> Either HostAddress HostAddress6 |
74 | |||
75 | toHostAddr :: i -> Either HostAddress HostAddress6 | ||
76 | 86 | ||
77 | instance IPAddress IPv4 where | 87 | instance IPAddress IPv4 where |
78 | toHostAddr = Left . toHostAddress | 88 | toHostAddr = Left . toHostAddress |
79 | 89 | ||
80 | instance IPAddress IPv6 where | 90 | instance IPAddress IPv6 where |
81 | toHostAddr = Right . toHostAddress6 | 91 | toHostAddr = Right . toHostAddress6 |
82 | 92 | ||
83 | instance IPAddress IP where | 93 | instance 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 | ||
88 | deriving instance Typeable IP | 97 | deriving instance Typeable IP |
89 | deriving instance Typeable IPv4 | 98 | deriving instance Typeable IPv4 |
90 | deriving instance Typeable IPv6 | 99 | deriving instance Typeable IPv6 |
91 | 100 | ||
92 | ipToBEncode :: IPAddress i => i -> BValue | 101 | ipToBEncode :: Show i => i -> BValue |
93 | ipToBEncode ip = BString $ BS8.pack $ showIp ip | 102 | ipToBEncode ip = BString $ BS8.pack $ show ip |
94 | 103 | ||
95 | ipFromBEncode :: Monad m => IPAddress a => BValue -> m a | 104 | ipFromBEncode :: Read a => BValue -> BS.Result a |
96 | ipFromBEncode (BString ip) = return $ readIp $ BS8.unpack ip | 105 | ipFromBEncode (BString (BS8.unpack -> ipStr)) |
97 | ipFromBEncode _ = fail "ipFromBEncode" | 106 | | Just ip <- readMaybe (ipStr) = pure ip |
107 | | otherwise = decodingError $ "IP: " ++ ipStr | ||
108 | ipFromBEncode _ = decodingError $ "IP: addr should be a bstring" | ||
98 | 109 | ||
99 | instance BEncode IP where | 110 | instance BEncode IP where |
100 | toBEncode = ipToBEncode | 111 | toBEncode = ipToBEncode |
101 | fromBEncode = ipFromBEncode | 112 | fromBEncode = ipFromBEncode |
102 | 113 | ||
103 | instance BEncode IPv4 where | 114 | instance BEncode IPv4 where |
104 | toBEncode = ipToBEncode | 115 | toBEncode = ipToBEncode |
105 | fromBEncode = ipFromBEncode | 116 | fromBEncode = ipFromBEncode |
106 | 117 | ||
107 | instance BEncode IPv6 where | 118 | instance BEncode IPv6 where |
108 | toBEncode = ipToBEncode | 119 | toBEncode = ipToBEncode |
109 | fromBEncode = ipFromBEncode | 120 | fromBEncode = ipFromBEncode |
110 | 121 | ||
111 | instance Serialize IPv4 where | 122 | instance Serialize IPv4 where |
112 | put ip = put $ toHostAddress ip | 123 | put = putWord32host . toHostAddress |
113 | get = fromHostAddress <$> get | 124 | get = fromHostAddress <$> getWord32host |
114 | 125 | ||
115 | instance Serialize IPv6 where | 126 | instance 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 | ||
129 | peer_id_key, peer_ip_key, peer_port_key :: BKey | 143 | peer_ip_key, peer_id_key, peer_port_key :: BKey |
130 | peer_id_key = "peer id" | ||
131 | peer_ip_key = "ip" | 144 | peer_ip_key = "ip" |
145 | peer_id_key = "peer id" | ||
132 | peer_port_key = "port" | 146 | peer_port_key = "port" |
133 | 147 | ||
134 | -- | The tracker's 'announce response' compatible encoding. | 148 | -- | The tracker's 'announce response' compatible encoding. |
135 | instance (Typeable a, BEncode a) => BEncode (PeerAddr a) where | 149 | instance (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 | ||
147 | mergeIPLists :: [PeerAddr IPv4] -> Maybe [PeerAddr IPv6] -> [PeerAddr IP] | 163 | mergeIPLists :: [PeerAddr IPv4] -> Maybe [PeerAddr IPv6] -> [PeerAddr IP] |
148 | mergeIPLists v4 v6 = (fmap IPv4 `L.map` v4) | 164 | mergeIPLists 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 |
164 | instance (Serialize a) => Serialize (PeerAddr a) where | 180 | instance (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@ |
171 | instance Default (PeerAddr IPv4) where | 185 | instance Default (PeerAddr IPv4) where |
@@ -178,7 +192,7 @@ instance Default (PeerAddr IPv4) where | |||
178 | instance IsString (PeerAddr IPv4) where | 192 | instance 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 | ||
213 | instance 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 |
201 | instance Pretty (PeerAddr IP) where | 220 | instance 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 | -- |
218 | peerSockAddr :: (IPAddress i) => PeerAddr i -> SockAddr | 237 | peerSockAddr :: PeerAddr IP -> SockAddr |
219 | peerSockAddr PeerAddr {..} = | 238 | peerSockAddr 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 |