diff options
author | joe <joe@jerkface.net> | 2017-01-17 18:42:09 -0500 |
---|---|---|
committer | joe <joe@jerkface.net> | 2017-01-17 18:42:09 -0500 |
commit | 5d0791e6ed2e500c08e7dadda39a254c8340cef5 (patch) | |
tree | 1232e01ea7452473941e488af01b98bc90202554 /src/Network/KRPC/Message.hs | |
parent | 5c54f6570a27e1509ddf048a91bd69c05052f2f1 (diff) |
Handle reflected IP addresses (see bep 42).
Diffstat (limited to 'src/Network/KRPC/Message.hs')
-rw-r--r-- | src/Network/KRPC/Message.hs | 45 |
1 files changed, 41 insertions, 4 deletions
diff --git a/src/Network/KRPC/Message.hs b/src/Network/KRPC/Message.hs index ebf5573e..6f4ae620 100644 --- a/src/Network/KRPC/Message.hs +++ b/src/Network/KRPC/Message.hs | |||
@@ -35,17 +35,22 @@ module Network.KRPC.Message | |||
35 | 35 | ||
36 | -- * Response | 36 | -- * Response |
37 | , KResponse(..) | 37 | , KResponse(..) |
38 | , ReflectedIP(..) | ||
38 | 39 | ||
39 | -- * Message | 40 | -- * Message |
40 | , KMessage (..) | 41 | , KMessage (..) |
41 | ) where | 42 | ) where |
42 | 43 | ||
43 | import Control.Applicative | 44 | import Control.Applicative |
45 | import Control.Arrow | ||
44 | import Control.Exception.Lifted as Lifted | 46 | import Control.Exception.Lifted as Lifted |
45 | import Data.BEncode as BE | 47 | import Data.BEncode as BE |
46 | import Data.ByteString as B | 48 | import Data.ByteString as B |
47 | import Data.ByteString.Char8 as BC | 49 | import Data.ByteString.Char8 as BC |
50 | import qualified Data.Serialize as S | ||
51 | import Data.Word | ||
48 | import Data.Typeable | 52 | import Data.Typeable |
53 | import Network.Socket (SockAddr (..),PortNumber,HostAddress) | ||
49 | 54 | ||
50 | 55 | ||
51 | -- | This transaction ID is generated by the querying node and is | 56 | -- | This transaction ID is generated by the querying node and is |
@@ -188,6 +193,35 @@ instance BEncode KQuery where | |||
188 | KQuery <$>! "a" <*>! "q" <*>! "t" | 193 | KQuery <$>! "a" <*>! "q" <*>! "t" |
189 | {-# INLINE fromBEncode #-} | 194 | {-# INLINE fromBEncode #-} |
190 | 195 | ||
196 | newtype ReflectedIP = ReflectedIP SockAddr | ||
197 | deriving (Eq, Ord, Show) | ||
198 | |||
199 | instance BEncode ReflectedIP where | ||
200 | toBEncode (ReflectedIP addr) = BString (encodeAddr addr) | ||
201 | fromBEncode (BString bs) = ReflectedIP <$> decodeAddr bs | ||
202 | fromBEncode _ = Left "ReflectedIP should be a bencoded string" | ||
203 | |||
204 | port16 :: Word16 -> PortNumber | ||
205 | port16 = fromIntegral | ||
206 | |||
207 | decodeAddr :: ByteString -> Either String SockAddr | ||
208 | decodeAddr bs | B.length bs == 6 | ||
209 | = ( \(a,p) -> SockAddrInet <$> fmap port16 p <*> a ) | ||
210 | $ (S.runGet S.getWord32host *** S.decode ) | ||
211 | $ B.splitAt 4 bs | ||
212 | decodeAddr bs | B.length bs == 18 | ||
213 | = ( \(a,p) -> flip SockAddrInet6 0 <$> fmap port16 p <*> a <*> pure 0 ) | ||
214 | $ (S.decode *** S.decode ) | ||
215 | $ B.splitAt 16 bs | ||
216 | decodeAddr _ = Left "incorrectly sized address and port" | ||
217 | |||
218 | encodeAddr :: SockAddr -> ByteString | ||
219 | encodeAddr (SockAddrInet port addr) | ||
220 | = S.runPut (S.putWord32host addr >> S.put (fromIntegral port :: Word16)) | ||
221 | encodeAddr (SockAddrInet6 port _ addr _) | ||
222 | = S.runPut (S.put addr >> S.put (fromIntegral port :: Word16)) | ||
223 | encodeAddr _ = B.empty | ||
224 | |||
191 | {----------------------------------------------------------------------- | 225 | {----------------------------------------------------------------------- |
192 | -- Response messages | 226 | -- Response messages |
193 | -----------------------------------------------------------------------} | 227 | -----------------------------------------------------------------------} |
@@ -206,7 +240,8 @@ instance BEncode KQuery where | |||
206 | data KResponse = KResponse | 240 | data KResponse = KResponse |
207 | { respVals :: BValue -- ^ 'BDict' containing return values; | 241 | { respVals :: BValue -- ^ 'BDict' containing return values; |
208 | , respId :: TransactionId -- ^ match to the corresponding 'queryId'. | 242 | , respId :: TransactionId -- ^ match to the corresponding 'queryId'. |
209 | } deriving (Show, Read, Eq, Ord, Typeable) | 243 | , respIP :: Maybe ReflectedIP |
244 | } deriving (Show, Eq, Ord, Typeable) | ||
210 | 245 | ||
211 | -- | Responses, or KRPC message dictionaries with a \"y\" value of | 246 | -- | Responses, or KRPC message dictionaries with a \"y\" value of |
212 | -- \"r\", contain one additional key \"r\". The value of \"r\" is a | 247 | -- \"r\", contain one additional key \"r\". The value of \"r\" is a |
@@ -218,7 +253,8 @@ data KResponse = KResponse | |||
218 | -- | 253 | -- |
219 | instance BEncode KResponse where | 254 | instance BEncode KResponse where |
220 | toBEncode KResponse {..} = toDict $ | 255 | toBEncode KResponse {..} = toDict $ |
221 | "r" .=! respVals | 256 | "ip" .=? respIP |
257 | .: "r" .=! respVals | ||
222 | .: "t" .=! respId | 258 | .: "t" .=! respId |
223 | .: "y" .=! ("r" :: ByteString) | 259 | .: "y" .=! ("r" :: ByteString) |
224 | .: endDict | 260 | .: endDict |
@@ -226,7 +262,8 @@ instance BEncode KResponse where | |||
226 | 262 | ||
227 | fromBEncode = fromDict $ do | 263 | fromBEncode = fromDict $ do |
228 | lookAhead $ match "y" (BString "r") | 264 | lookAhead $ match "y" (BString "r") |
229 | KResponse <$>! "r" <*>! "t" | 265 | addr <- optional (field (req "ip")) |
266 | (\r t -> KResponse r t addr) <$>! "r" <*>! "t" | ||
230 | {-# INLINE fromBEncode #-} | 267 | {-# INLINE fromBEncode #-} |
231 | 268 | ||
232 | {----------------------------------------------------------------------- | 269 | {----------------------------------------------------------------------- |
@@ -249,4 +286,4 @@ instance BEncode KMessage where | |||
249 | Q <$> fromBEncode b | 286 | Q <$> fromBEncode b |
250 | <|> R <$> fromBEncode b | 287 | <|> R <$> fromBEncode b |
251 | <|> E <$> fromBEncode b | 288 | <|> E <$> fromBEncode b |
252 | <|> decodingError "KMessage: unknown message or message tag" \ No newline at end of file | 289 | <|> decodingError "KMessage: unknown message or message tag" |