diff options
Diffstat (limited to 'src/Network/DatagramServer')
-rw-r--r-- | src/Network/DatagramServer/Mainline.hs | 366 |
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 #-} | ||
26 | module 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 | |||
62 | import Control.Applicative | ||
63 | import Control.Arrow | ||
64 | import Control.Exception.Lifted as Lifted | ||
65 | #ifdef VERSION_bencoding | ||
66 | import Data.BEncode as BE | ||
67 | #else | ||
68 | import qualified Data.Tox as Tox | ||
69 | #endif | ||
70 | import Data.ByteString as B | ||
71 | import Data.ByteString.Char8 as BC | ||
72 | import qualified Data.Serialize as S | ||
73 | import Data.Word | ||
74 | import Data.Typeable | ||
75 | import 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. | ||
84 | type TransactionId = ByteString | ||
85 | #else | ||
86 | type TransactionId = Tox.Nonce24 -- msgNonce | ||
87 | #endif | ||
88 | |||
89 | unknownTransaction :: TransactionId | ||
90 | #ifdef VERSION_bencoding | ||
91 | unknownTransaction = "" | ||
92 | #else | ||
93 | unknownTransaction = 0 | ||
94 | #endif | ||
95 | |||
96 | {----------------------------------------------------------------------- | ||
97 | -- Error messages | ||
98 | -----------------------------------------------------------------------} | ||
99 | |||
100 | -- | Types of RPC errors. | ||
101 | data 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> | ||
117 | instance 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 | ||
132 | instance 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 | -- | ||
145 | data 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 | ||
151 | type 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 | ||
169 | instance 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 | |||
184 | instance Exception KError | ||
185 | |||
186 | -- | Received 'queryArgs' or 'respVals' can not be decoded. | ||
187 | decodeError :: String -> TransactionId -> KError | ||
188 | #ifdef VERSION_bencoding | ||
189 | decodeError msg = KError ProtocolError (BC.pack msg) | ||
190 | #else | ||
191 | decodeError msg = error "TODO TOX Error packet" | ||
192 | #endif | ||
193 | |||
194 | -- | A remote node has send some 'KMessage' this node is unable to | ||
195 | -- decode. | ||
196 | unknownMessage :: String -> KError | ||
197 | #ifdef VERSION_bencoding | ||
198 | unknownMessage msg = KError ProtocolError (BC.pack msg) unknownTransaction | ||
199 | #else | ||
200 | unknownMessage msg = error "TODO TOX Protocol error" | ||
201 | #endif | ||
202 | |||
203 | {----------------------------------------------------------------------- | ||
204 | -- Query messages | ||
205 | -----------------------------------------------------------------------} | ||
206 | |||
207 | #ifdef VERSION_bencoding | ||
208 | type MethodName = ByteString | ||
209 | type KQueryArgs = BValue | ||
210 | #else | ||
211 | type MethodName = Tox.MessageType -- msgType | ||
212 | type 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 | -- | ||
220 | data 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 | |||
226 | type 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 | -- | ||
237 | instance (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 | |||
251 | instance 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 | ||
256 | type KQuery = Tox.Message KQueryArgs | ||
257 | queryArgs = Tox.msgPayload | ||
258 | queryMethod = Tox.msgType | ||
259 | queryId = Tox.msgNonce | ||
260 | #endif | ||
261 | |||
262 | newtype ReflectedIP = ReflectedIP SockAddr | ||
263 | deriving (Eq, Ord, Show) | ||
264 | |||
265 | port16 :: Word16 -> PortNumber | ||
266 | port16 = fromIntegral | ||
267 | |||
268 | decodeAddr :: ByteString -> Either String SockAddr | ||
269 | decodeAddr 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 | ||
273 | decodeAddr 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 | ||
277 | decodeAddr _ = Left "incorrectly sized address and port" | ||
278 | |||
279 | encodeAddr :: SockAddr -> ByteString | ||
280 | encodeAddr (SockAddrInet port addr) | ||
281 | = S.runPut (S.putWord32host addr >> S.put (fromIntegral port :: Word16)) | ||
282 | encodeAddr (SockAddrInet6 port _ addr _) | ||
283 | = S.runPut (S.put addr >> S.put (fromIntegral port :: Word16)) | ||
284 | encodeAddr _ = 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 | ||
302 | data 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 | |||
308 | type 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 | -- | ||
318 | instance (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 | ||
333 | type KResponse = Tox.Message KQueryArgs | ||
334 | respVals = Tox.msgPayload | ||
335 | respId = Tox.msgNonce | ||
336 | respIP = Nothing :: Maybe ReflectedIP | ||
337 | #endif | ||
338 | |||
339 | {----------------------------------------------------------------------- | ||
340 | -- Summed messages | ||
341 | -----------------------------------------------------------------------} | ||
342 | |||
343 | #ifdef VERSION_bencoding | ||
344 | -- | Generic KRPC message. | ||
345 | data KMessageOf a | ||
346 | = Q (KQueryOf a) | ||
347 | | R (KResponseOf a) | ||
348 | | E KError | ||
349 | deriving (Show, Eq, Functor, Foldable, Traversable) | ||
350 | |||
351 | type KMessage = KMessageOf KQueryArgs | ||
352 | |||
353 | instance 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 | ||
364 | type KMessageOf = Tox.Message | ||
365 | type KMessage = KMessageOf B.ByteString | ||
366 | #endif | ||