summaryrefslogtreecommitdiff
path: root/src/Network/DatagramServer
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/DatagramServer')
-rw-r--r--src/Network/DatagramServer/Mainline.hs366
1 files changed, 366 insertions, 0 deletions
diff --git a/src/Network/DatagramServer/Mainline.hs b/src/Network/DatagramServer/Mainline.hs
new file mode 100644
index 00000000..2177d076
--- /dev/null
+++ b/src/Network/DatagramServer/Mainline.hs
@@ -0,0 +1,366 @@
1-- |
2-- Copyright : (c) Sam Truzjan 2013, 2014
3-- License : BSD3
4-- Maintainer : pxqr.sta@gmail.com
5-- Stability : experimental
6-- Portability : portable
7--
8-- KRPC messages types used in communication. All messages are
9-- encoded as bencode dictionary.
10--
11-- Normally, you don't need to import this module.
12--
13-- See <http://www.bittorrent.org/beps/bep_0005.html#krpc-protocol>
14--
15{-# LANGUAGE CPP #-}
16{-# LANGUAGE DefaultSignatures #-}
17{-# LANGUAGE DeriveDataTypeable #-}
18{-# LANGUAGE DeriveFunctor #-}
19{-# LANGUAGE DeriveTraversable #-}
20{-# LANGUAGE FlexibleContexts #-}
21{-# LANGUAGE FlexibleInstances #-}
22{-# LANGUAGE FunctionalDependencies #-}
23{-# LANGUAGE MultiParamTypeClasses #-}
24{-# LANGUAGE OverloadedStrings #-}
25{-# LANGUAGE TypeSynonymInstances #-}
26module Network.DatagramServer.Mainline
27 ( -- * Transaction
28 TransactionId
29
30 -- * Error
31 , ErrorCode (..)
32 , KError(..)
33 , decodeError
34 , unknownMessage
35
36 -- * Query
37#ifdef VERSION_bencoding
38 , KQueryOf(..)
39#endif
40 , KQuery
41#ifndef VERSION_bencoding
42 , queryArgs
43 , queryMethod
44 , queryId
45#endif
46 , MethodName
47
48 -- * Response
49#ifdef VERSION_bencoding
50 , KResponseOf(..)
51#endif
52 , KResponse
53 , ReflectedIP(..)
54
55 -- * Message
56 , KMessageOf (..)
57 , KMessage
58 , KQueryArgs
59
60 ) where
61
62import Control.Applicative
63import Control.Arrow
64import Control.Exception.Lifted as Lifted
65#ifdef VERSION_bencoding
66import Data.BEncode as BE
67#else
68import qualified Data.Tox as Tox
69#endif
70import Data.ByteString as B
71import Data.ByteString.Char8 as BC
72import qualified Data.Serialize as S
73import Data.Word
74import Data.Typeable
75import Network.Socket (SockAddr (..),PortNumber,HostAddress)
76
77
78#ifdef VERSION_bencoding
79-- | This transaction ID is generated by the querying node and is
80-- echoed in the response, so responses may be correlated with
81-- multiple queries to the same node. The transaction ID should be
82-- encoded as a short string of binary numbers, typically 2 characters
83-- are enough as they cover 2^16 outstanding queries.
84type TransactionId = ByteString
85#else
86type TransactionId = Tox.Nonce24 -- msgNonce
87#endif
88
89unknownTransaction :: TransactionId
90#ifdef VERSION_bencoding
91unknownTransaction = ""
92#else
93unknownTransaction = 0
94#endif
95
96{-----------------------------------------------------------------------
97-- Error messages
98-----------------------------------------------------------------------}
99
100-- | Types of RPC errors.
101data ErrorCode
102 -- | Some error doesn't fit in any other category.
103 = GenericError
104
105 -- | Occur when server fail to process procedure call.
106 | ServerError
107
108 -- | Malformed packet, invalid arguments or bad token.
109 | ProtocolError
110
111 -- | Occur when client trying to call method server don't know.
112 | MethodUnknown
113 deriving (Show, Read, Eq, Ord, Bounded, Typeable)
114
115-- | According to the table:
116-- <http://bittorrent.org/beps/bep_0005.html#errors>
117instance Enum ErrorCode where
118 fromEnum GenericError = 201
119 fromEnum ServerError = 202
120 fromEnum ProtocolError = 203
121 fromEnum MethodUnknown = 204
122 {-# INLINE fromEnum #-}
123
124 toEnum 201 = GenericError
125 toEnum 202 = ServerError
126 toEnum 203 = ProtocolError
127 toEnum 204 = MethodUnknown
128 toEnum _ = GenericError
129 {-# INLINE toEnum #-}
130
131#ifdef VERSION_bencoding
132instance BEncode ErrorCode where
133 toBEncode = toBEncode . fromEnum
134 {-# INLINE toBEncode #-}
135
136 fromBEncode b = toEnum <$> fromBEncode b
137 {-# INLINE fromBEncode #-}
138#endif
139
140#ifdef VERSION_bencoding
141-- | Errors are sent when a query cannot be fulfilled. Error message
142-- can be send only from server to client but not in the opposite
143-- direction.
144--
145data KError = KError
146 { errorCode :: !ErrorCode -- ^ the type of error;
147 , errorMessage :: !ByteString -- ^ human-readable text message;
148 , errorId :: !TransactionId -- ^ match to the corresponding 'queryId'.
149 } deriving ( Show, Eq, Ord, Typeable, Read )
150#else
151type KError = Tox.Message ByteString -- TODO TOX unused
152#endif
153
154-- | Errors, or KRPC message dictionaries with a \"y\" value of \"e\",
155-- contain one additional key \"e\". The value of \"e\" is a
156-- list. The first element is an integer representing the error
157-- code. The second element is a string containing the error
158-- message.
159--
160-- Example Error Packet:
161--
162-- > { "t": "aa", "y":"e", "e":[201, "A Generic Error Ocurred"]}
163--
164-- or bencoded:
165--
166-- > d1:eli201e23:A Generic Error Ocurrede1:t2:aa1:y1:ee
167--
168#ifdef VERSION_bencoding
169instance BEncode KError where
170 toBEncode KError {..} = toDict $
171 "e" .=! (errorCode, errorMessage)
172 .: "t" .=! errorId
173 .: "y" .=! ("e" :: ByteString)
174 .: endDict
175 {-# INLINE toBEncode #-}
176
177 fromBEncode = fromDict $ do
178 lookAhead $ match "y" (BString "e")
179 (code, msg) <- field (req "e")
180 KError code msg <$>! "t"
181 {-# INLINE fromBEncode #-}
182#endif
183
184instance Exception KError
185
186-- | Received 'queryArgs' or 'respVals' can not be decoded.
187decodeError :: String -> TransactionId -> KError
188#ifdef VERSION_bencoding
189decodeError msg = KError ProtocolError (BC.pack msg)
190#else
191decodeError msg = error "TODO TOX Error packet"
192#endif
193
194-- | A remote node has send some 'KMessage' this node is unable to
195-- decode.
196unknownMessage :: String -> KError
197#ifdef VERSION_bencoding
198unknownMessage msg = KError ProtocolError (BC.pack msg) unknownTransaction
199#else
200unknownMessage msg = error "TODO TOX Protocol error"
201#endif
202
203{-----------------------------------------------------------------------
204-- Query messages
205-----------------------------------------------------------------------}
206
207#ifdef VERSION_bencoding
208type MethodName = ByteString
209type KQueryArgs = BValue
210#else
211type MethodName = Tox.MessageType -- msgType
212type KQueryArgs = ByteString -- msgPayload
213#endif
214
215#ifdef VERSION_bencoding
216-- | Query used to signal that caller want to make procedure call to
217-- callee and pass arguments in. Therefore query may be only sent from
218-- client to server but not in the opposite direction.
219--
220data KQueryOf a = KQuery
221 { queryArgs :: !a -- ^ values to be passed to method;
222 , queryMethod :: !MethodName -- ^ method to call;
223 , queryId :: !TransactionId -- ^ one-time query token.
224 } deriving ( Show, Eq, Ord, Typeable, Read, Functor, Foldable, Traversable )
225
226type KQuery = KQueryOf KQueryArgs
227
228-- | Queries, or KRPC message dictionaries with a \"y\" value of
229-- \"q\", contain two additional keys; \"q\" and \"a\". Key \"q\" has
230-- a string value containing the method name of the query. Key \"a\"
231-- has a dictionary value containing named arguments to the query.
232--
233-- Example Query packet:
234--
235-- > { "t" : "aa", "y" : "q", "q" : "ping", "a" : { "msg" : "hi!" } }
236--
237instance (Typeable a, BEncode a) => BEncode (KQueryOf a) where
238 toBEncode KQuery {..} = toDict $
239 "a" .=! queryArgs
240 .: "q" .=! queryMethod
241 .: "t" .=! queryId
242 .: "y" .=! ("q" :: ByteString)
243 .: endDict
244 {-# INLINE toBEncode #-}
245
246 fromBEncode = fromDict $ do
247 lookAhead $ match "y" (BString "q")
248 KQuery <$>! "a" <*>! "q" <*>! "t"
249 {-# INLINE fromBEncode #-}
250
251instance BEncode ReflectedIP where
252 toBEncode (ReflectedIP addr) = BString (encodeAddr addr)
253 fromBEncode (BString bs) = ReflectedIP <$> decodeAddr bs
254 fromBEncode _ = Left "ReflectedIP should be a bencoded string"
255#else
256type KQuery = Tox.Message KQueryArgs
257queryArgs = Tox.msgPayload
258queryMethod = Tox.msgType
259queryId = Tox.msgNonce
260#endif
261
262newtype ReflectedIP = ReflectedIP SockAddr
263 deriving (Eq, Ord, Show)
264
265port16 :: Word16 -> PortNumber
266port16 = fromIntegral
267
268decodeAddr :: ByteString -> Either String SockAddr
269decodeAddr bs | B.length bs == 6
270 = ( \(a,p) -> SockAddrInet <$> fmap port16 p <*> a )
271 $ (S.runGet S.getWord32host *** S.decode )
272 $ B.splitAt 4 bs
273decodeAddr bs | B.length bs == 18
274 = ( \(a,p) -> flip SockAddrInet6 0 <$> fmap port16 p <*> a <*> pure 0 )
275 $ (S.decode *** S.decode )
276 $ B.splitAt 16 bs
277decodeAddr _ = Left "incorrectly sized address and port"
278
279encodeAddr :: SockAddr -> ByteString
280encodeAddr (SockAddrInet port addr)
281 = S.runPut (S.putWord32host addr >> S.put (fromIntegral port :: Word16))
282encodeAddr (SockAddrInet6 port _ addr _)
283 = S.runPut (S.put addr >> S.put (fromIntegral port :: Word16))
284encodeAddr _ = B.empty
285
286{-----------------------------------------------------------------------
287-- Response messages
288-----------------------------------------------------------------------}
289
290-- | Response messages are sent upon successful completion of a
291-- query:
292--
293-- * KResponse used to signal that callee successufully process a
294-- procedure call and to return values from procedure.
295--
296-- * KResponse should not be sent if error occurred during RPC,
297-- 'KError' should be sent instead.
298--
299-- * KResponse can be only sent from server to client.
300--
301#ifdef VERSION_bencoding
302data KResponseOf a = KResponse
303 { respVals :: a -- ^ 'BDict' containing return values;
304 , respId :: TransactionId -- ^ match to the corresponding 'queryId'.
305 , respIP :: Maybe ReflectedIP
306 } deriving (Show, Eq, Ord, Typeable, Functor, Foldable, Traversable)
307
308type KResponse = KResponseOf KQueryArgs
309
310-- | Responses, or KRPC message dictionaries with a \"y\" value of
311-- \"r\", contain one additional key \"r\". The value of \"r\" is a
312-- dictionary containing named return values.
313--
314-- Example Response packet:
315--
316-- > { "t" : "aa", "y" : "r", "r" : { "msg" : "you've sent: hi!" } }
317--
318instance (Typeable a, BEncode a) => BEncode (KResponseOf a) where
319 toBEncode KResponse {..} = toDict $
320 "ip" .=? respIP
321 .: "r" .=! respVals
322 .: "t" .=! respId
323 .: "y" .=! ("r" :: ByteString)
324 .: endDict
325 {-# INLINE toBEncode #-}
326
327 fromBEncode = fromDict $ do
328 lookAhead $ match "y" (BString "r")
329 addr <- optional (field (req "ip"))
330 (\r t -> KResponse r t addr) <$>! "r" <*>! "t"
331 {-# INLINE fromBEncode #-}
332#else
333type KResponse = Tox.Message KQueryArgs
334respVals = Tox.msgPayload
335respId = Tox.msgNonce
336respIP = Nothing :: Maybe ReflectedIP
337#endif
338
339{-----------------------------------------------------------------------
340-- Summed messages
341-----------------------------------------------------------------------}
342
343#ifdef VERSION_bencoding
344-- | Generic KRPC message.
345data KMessageOf a
346 = Q (KQueryOf a)
347 | R (KResponseOf a)
348 | E KError
349 deriving (Show, Eq, Functor, Foldable, Traversable)
350
351type KMessage = KMessageOf KQueryArgs
352
353instance BEncode KMessage where
354 toBEncode (Q q) = toBEncode q
355 toBEncode (R r) = toBEncode r
356 toBEncode (E e) = toBEncode e
357
358 fromBEncode b =
359 Q <$> fromBEncode b
360 <|> R <$> fromBEncode b
361 <|> E <$> fromBEncode b
362 <|> decodingError "KMessage: unknown message or message tag"
363#else
364type KMessageOf = Tox.Message
365type KMessage = KMessageOf B.ByteString
366#endif