summaryrefslogtreecommitdiff
path: root/src/Network
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2017-06-08 23:26:30 -0400
committerjoe <joe@jerkface.net>2017-06-08 23:26:30 -0400
commit84798bfef62a001ded1bd628d846612f0b0ade80 (patch)
tree6a66e1d8fa014bea6f6562650134440a5a515f56 /src/Network
parentcb2bd0bf4b5977ef6ec7ca7ab9ac0189457c2250 (diff)
Generalized Network.DatagramServer
Diffstat (limited to 'src/Network')
-rw-r--r--src/Network/BitTorrent/DHT/Query.hs16
-rw-r--r--src/Network/BitTorrent/DHT/Session.hs8
-rw-r--r--src/Network/DHT/Mainline.hs71
-rw-r--r--src/Network/DatagramServer.hs292
-rw-r--r--src/Network/DatagramServer/Error.hs110
-rw-r--r--src/Network/DatagramServer/Mainline.hs236
-rw-r--r--src/Network/DatagramServer/Tox.hs11
-rw-r--r--src/Network/DatagramServer/Types.hs38
8 files changed, 414 insertions, 368 deletions
diff --git a/src/Network/BitTorrent/DHT/Query.hs b/src/Network/BitTorrent/DHT/Query.hs
index fc6625ae..9adad163 100644
--- a/src/Network/BitTorrent/DHT/Query.hs
+++ b/src/Network/BitTorrent/DHT/Query.hs
@@ -82,6 +82,7 @@ import Data.Time
82import Data.Time.Clock.POSIX 82import Data.Time.Clock.POSIX
83 83
84import Network.DatagramServer as KRPC hiding (Options, def) 84import Network.DatagramServer as KRPC hiding (Options, def)
85import Network.KRPC.Method as KRPC
85import Network.DatagramServer.Mainline (ReflectedIP(..)) 86import Network.DatagramServer.Mainline (ReflectedIP(..))
86import Network.DatagramServer (QueryFailure(..)) 87import Network.DatagramServer (QueryFailure(..))
87import Data.Torrent 88import Data.Torrent
@@ -112,8 +113,8 @@ nodeHandler :: ( Address ip
112#else 113#else
113 , KPRC.Envelope (Query a) (Response b) ~ ByteString ) 114 , KPRC.Envelope (Query a) (Response b) ~ ByteString )
114#endif 115#endif
115 => (NodeAddr ip -> a -> DHT ip b) -> NodeHandler ip 116 => QueryMethod KMessageOf -> (NodeAddr ip -> a -> DHT ip b) -> NodeHandler ip
116nodeHandler action = handler $ \ sockAddr qry -> do 117nodeHandler method action = handler method $ \ sockAddr qry -> do
117#ifdef VERSION_bencoding 118#ifdef VERSION_bencoding
118 let remoteId = queringNodeId qry 119 let remoteId = queringNodeId qry
119 read_only = queryIsReadOnly qry 120 read_only = queryIsReadOnly qry
@@ -138,27 +139,27 @@ nodeHandler action = handler $ \ sockAddr qry -> do
138-- | Default 'Ping' handler. 139-- | Default 'Ping' handler.
139pingH :: Address ip => NodeHandler ip 140pingH :: Address ip => NodeHandler ip
140#ifdef VERSION_bencoding 141#ifdef VERSION_bencoding
141pingH = nodeHandler $ \ _ Ping -> return Ping 142pingH = nodeHandler "ping" $ \ _ Ping -> return Ping
142#else 143#else
143pingH = nodeHandler $ \ _ p@PingPayload{} -> return p { isPong = True } 144pingH = nodeHandler $ \ _ p@PingPayload{} -> return p { isPong = True }
144#endif 145#endif
145 146
146-- | Default 'FindNode' handler. 147-- | Default 'FindNode' handler.
147findNodeH :: Address ip => NodeHandler ip 148findNodeH :: Address ip => NodeHandler ip
148findNodeH = nodeHandler $ \ _ (FindNode nid) -> do 149findNodeH = nodeHandler "find-nodes" $ \ _ (FindNode nid) -> do
149 NodeFound <$> getClosest nid 150 NodeFound <$> getClosest nid
150 151
151#ifdef VERSION_bencoding 152#ifdef VERSION_bencoding
152-- | Default 'GetPeers' handler. 153-- | Default 'GetPeers' handler.
153getPeersH :: Ord ip => Address ip => NodeHandler ip 154getPeersH :: Ord ip => Address ip => NodeHandler ip
154getPeersH = nodeHandler $ \ naddr (GetPeers ih) -> do 155getPeersH = nodeHandler "get_peers" $ \ naddr (GetPeers ih) -> do
155 ps <- getPeerList ih 156 ps <- getPeerList ih
156 tok <- grantToken naddr 157 tok <- grantToken naddr
157 return $ GotPeers ps tok 158 return $ GotPeers ps tok
158 159
159-- | Default 'Announce' handler. 160-- | Default 'Announce' handler.
160announceH :: Ord ip => Address ip => NodeHandler ip 161announceH :: Ord ip => Address ip => NodeHandler ip
161announceH = nodeHandler $ \ naddr @ NodeAddr {..} (Announce {..}) -> do 162announceH = nodeHandler "announce_peer" $ \ naddr @ NodeAddr {..} (Announce {..}) -> do
162 valid <- checkToken naddr sessionToken 163 valid <- checkToken naddr sessionToken
163 unless valid $ do 164 unless valid $ do
164 throwIO $ InvalidParameter "token" 165 throwIO $ InvalidParameter "token"
@@ -396,7 +397,8 @@ queryNode' :: forall a b ip. Address ip => KRPC (Query a) (Response b)
396queryNode' addr q = do 397queryNode' addr q = do
397 nid <- myNodeIdAccordingTo addr 398 nid <- myNodeIdAccordingTo addr
398 let read_only = False -- TODO: check for NAT issues. (BEP 43) 399 let read_only = False -- TODO: check for NAT issues. (BEP 43)
399 (Response remoteId r, witnessed_ip) <- query' (toSockAddr addr) (Query nid read_only q) 400 let KRPC.Method name = KRPC.method :: KRPC.Method (Query a) (Response b)
401 (Response remoteId r, witnessed_ip) <- query' name (toSockAddr addr) (Query nid read_only q)
400 -- $(logDebugS) "queryNode" $ "Witnessed address: " <> T.pack (show witnessed_ip) 402 -- $(logDebugS) "queryNode" $ "Witnessed address: " <> T.pack (show witnessed_ip)
401 -- <> " by " <> T.pack (show (toSockAddr addr)) 403 -- <> " by " <> T.pack (show (toSockAddr addr))
402 _ <- insertNode (NodeInfo remoteId addr ()) witnessed_ip 404 _ <- insertNode (NodeInfo remoteId addr ()) witnessed_ip
diff --git a/src/Network/BitTorrent/DHT/Session.hs b/src/Network/BitTorrent/DHT/Session.hs
index f874b62e..0c819620 100644
--- a/src/Network/BitTorrent/DHT/Session.hs
+++ b/src/Network/BitTorrent/DHT/Session.hs
@@ -85,6 +85,7 @@ import Control.Monad.Logger
85import Control.Monad.Reader 85import Control.Monad.Reader
86import Control.Monad.Trans.Control 86import Control.Monad.Trans.Control
87import Control.Monad.Trans.Resource 87import Control.Monad.Trans.Resource
88import Data.Typeable
88import Data.ByteString 89import Data.ByteString
89import Data.Conduit.Lazy 90import Data.Conduit.Lazy
90import Data.Default 91import Data.Default
@@ -266,10 +267,11 @@ data Node ip = Node
266#endif 267#endif
267 268
268 , resources :: !InternalState 269 , resources :: !InternalState
269 , manager :: !(Manager (DHT ip )) -- ^ RPC manager;
270#ifdef VERSION_bencoding 270#ifdef VERSION_bencoding
271 , manager :: !(Manager (DHT ip) BValue KMessageOf) -- ^ RPC manager;
271 , routingInfo :: !(TVar (Maybe (R.Info KMessageOf ip ()))) -- ^ search table; 272 , routingInfo :: !(TVar (Maybe (R.Info KMessageOf ip ()))) -- ^ search table;
272#else 273#else
274 , manager :: !(Manager (DHT ip) ByteString Tox.Message) -- ^ RPC manager;
273 , routingInfo :: !(TVar (Maybe (R.Info Tox.Message ip Bool))) -- ^ search table; 275 , routingInfo :: !(TVar (Maybe (R.Info Tox.Message ip Bool))) -- ^ search table;
274#endif 276#endif
275 , contactInfo :: !(TVar (PeerStore ip )) -- ^ published by other nodes; 277 , contactInfo :: !(TVar (PeerStore ip )) -- ^ published by other nodes;
@@ -318,7 +320,7 @@ instance MonadResource (DHT ip) where
318 s <- asks resources 320 s <- asks resources
319 liftIO $ runInternalState m s 321 liftIO $ runInternalState m s
320 322
321instance MonadKRPC (DHT ip) (DHT ip) where 323instance MonadKRPC (DHT ip) (DHT ip) BValue KMessageOf where
322 getManager = asks manager 324 getManager = asks manager
323 325
324instance MonadLogger (DHT ip) where 326instance MonadLogger (DHT ip) where
@@ -364,7 +366,7 @@ newNode hs opts naddr logger mbid = do
364 <*> newTVarIO S.empty 366 <*> newTVarIO S.empty
365 <*> (newTVarIO =<< nullSessionTokens) 367 <*> (newTVarIO =<< nullSessionTokens)
366 <*> pure logger 368 <*> pure logger
367 runReaderT (unDHT KRPC.listen) node 369 runReaderT (unDHT $ KRPC.listen (KRPC.Protocol Proxy Proxy)) node
368 return node 370 return node
369 371
370-- | Some resources like listener thread may live for 372-- | Some resources like listener thread may live for
diff --git a/src/Network/DHT/Mainline.hs b/src/Network/DHT/Mainline.hs
index 7b3d6d55..2ecb9845 100644
--- a/src/Network/DHT/Mainline.hs
+++ b/src/Network/DHT/Mainline.hs
@@ -24,46 +24,12 @@ import Data.LargeWord
24import Data.Serialize as S 24import Data.Serialize as S
25import Data.String 25import Data.String
26import Data.Typeable 26import Data.Typeable
27import Network.DatagramServer.Mainline (NodeId(..))
27import Network.DatagramServer.Mainline as KRPC 28import Network.DatagramServer.Mainline as KRPC
28import Network.DatagramServer.Types as RPC 29import Network.DatagramServer.Types as RPC
29import Text.PrettyPrint as PP hiding ((<>)) 30import Text.PrettyPrint as PP hiding ((<>))
30import Text.PrettyPrint.HughesPJClass hiding (($$), (<>)) 31import Text.PrettyPrint.HughesPJClass hiding (($$), (<>))
31 32
32nodeIdSize = finiteBitSize (undefined :: NodeId KMessageOf) `div` 8
33
34instance BEncode (NodeId KMessageOf) where
35 toBEncode (NodeId w) = toBEncode $ S.encode w
36 fromBEncode bval = fromBEncode bval >>= S.decode
37
38-- instance BEncode NodeId where TODO
39
40-- TODO: put this somewhere appropriate
41instance (Serialize a, Serialize b) => Serialize (LargeKey a b) where
42 put (LargeKey lo hi) = put hi >> put lo
43 get = flip LargeKey <$> get <*> get
44
45instance Serialize (NodeId KMessageOf) where
46 get = NodeId <$> get
47 {-# INLINE get #-}
48 put (NodeId bs) = put bs
49 {-# INLINE put #-}
50
51-- | ASCII encoded.
52instance IsString (NodeId KMessageOf) where
53 fromString str
54 | length str == nodeIdSize = NodeId (either error id $ S.decode (fromString str :: ByteString))
55 | length str == 2 * nodeIdSize = NodeId (either error id $ S.decode (fst $ Base16.decode $ fromString str))
56 | otherwise = error "fromString: invalid NodeId length"
57 {-# INLINE fromString #-}
58
59-- | Meaningless node id, for testing purposes only.
60instance Default (NodeId KMessageOf) where
61 def = NodeId 0
62
63-- | base16 encoded.
64instance Pretty (NodeId KMessageOf) where
65 pPrint (NodeId nid) = PP.text $ Char8.unpack $ Base16.encode $ S.encode nid
66
67-- | KRPC 'compact list' compatible encoding: contact information for 33-- | KRPC 'compact list' compatible encoding: contact information for
68-- nodes is encoded as a 26-byte string. Also known as "Compact node 34-- nodes is encoded as a 26-byte string. Also known as "Compact node
69-- info" the 20-byte Node ID in network byte order has the compact 35-- info" the 20-byte Node ID in network byte order has the compact
@@ -115,38 +81,3 @@ bep42 addr (NodeId r)
115 81
116 82
117 83
118instance Envelope KMessageOf where
119 type TransactionID KMessageOf = KRPC.TransactionId
120
121 -- | Each node has a globally unique identifier known as the \"node
122 -- ID.\"
123 --
124 -- Normally, /this/ node id should be saved between invocations
125 -- of the client software.
126 newtype NodeId KMessageOf = NodeId Word160
127 deriving (Show, Eq, Ord, Typeable, Bits, FiniteBits)
128
129 envelopePayload (Q q) = queryArgs q
130 envelopePayload (R r) = respVals r
131 envelopePayload (E _) = error "TODO: messagePayload for KError"
132
133 envelopeTransaction (Q q) = queryId q
134 envelopeTransaction (R r) = respId r
135 envelopeTransaction (E e) = errorId e
136
137 envelopeClass (Q _) = Query
138 envelopeClass (R _) = Response
139 envelopeClass (E _) = Error
140
141 buildReply self addr qry response =
142 (R (KResponse response (envelopeTransaction qry) (Just $ ReflectedIP addr)))
143
144instance WireFormat BValue KMessageOf where
145 type SerializableTo BValue = BEncode
146 type CipherContext BValue KMessageOf = ()
147
148 decodeHeaders _ bs = BE.decode bs >>= BE.fromBEncode
149 decodePayload kmsg = mapM BE.fromBEncode kmsg
150
151 encodeHeaders _ kmsg = L.toStrict $ BE.encode kmsg
152 encodePayload msg = fmap BE.toBEncode msg
diff --git a/src/Network/DatagramServer.hs b/src/Network/DatagramServer.hs
index 21300108..e1bf91c5 100644
--- a/src/Network/DatagramServer.hs
+++ b/src/Network/DatagramServer.hs
@@ -60,6 +60,7 @@
60{-# LANGUAGE FunctionalDependencies #-} 60{-# LANGUAGE FunctionalDependencies #-}
61{-# LANGUAGE DeriveDataTypeable #-} 61{-# LANGUAGE DeriveDataTypeable #-}
62{-# LANGUAGE TemplateHaskell #-} 62{-# LANGUAGE TemplateHaskell #-}
63{-# LANGUAGE KindSignatures #-}
63module Network.DatagramServer 64module Network.DatagramServer
64 ( -- * Methods 65 ( -- * Methods
65 Method 66 Method
@@ -88,6 +89,7 @@ module Network.DatagramServer
88 , withManager 89 , withManager
89 , isActive 90 , isActive
90 , listen 91 , listen
92 , Protocol(..)
91 93
92 -- * Re-exports 94 -- * Re-exports
93 , ErrorCode (..) 95 , ErrorCode (..)
@@ -96,7 +98,6 @@ module Network.DatagramServer
96 98
97import Data.Default.Class 99import Data.Default.Class
98import Network.DatagramServer.Mainline 100import Network.DatagramServer.Mainline
99import Network.KRPC.Method
100import Network.Socket (SockAddr (..)) 101import Network.Socket (SockAddr (..))
101 102
102import Control.Applicative 103import Control.Applicative
@@ -192,42 +193,38 @@ validateOptions Options {..}
192-- Options 193-- Options
193-----------------------------------------------------------------------} 194-----------------------------------------------------------------------}
194 195
195type KResult = Either KError KMessage -- Response 196type KResult msg raw = Either (KError (TransactionID msg)) (msg raw)-- Response
196 197
197type TransactionCounter = IORef Int 198type TransactionCounter = IORef Int
198type CallId = (TransactionId, SockAddr) 199type CallId msg = (TransactionID msg, SockAddr)
199type CallRes = MVar (KQueryArgs, KResult) -- (raw response, decoded response) 200type CallRes msg raw = MVar (raw, KResult msg raw) -- (raw response, decoded response)
200type PendingCalls = IORef (Map CallId CallRes) 201type PendingCalls msg raw = IORef (Map (CallId msg) (CallRes msg raw))
201 202
202type HandlerBody h msg v = SockAddr -> msg v -> h (Either String (msg v)) 203type HandlerBody h msg v = SockAddr -> msg v -> h (Either String (msg v))
203 204
204-- | Handler is a function which will be invoked then some /remote/ 205-- | Handler is a function which will be invoked then some /remote/
205-- node querying /this/ node. 206-- node querying /this/ node.
206type Handler h msg v = (MethodName, HandlerBody h msg v) 207type Handler h msg v = (QueryMethod msg, HandlerBody h msg v)
207 208
208-- | Keep track pending queries made by /this/ node and handle queries 209-- | Keep track pending queries made by /this/ node and handle queries
209-- made by /remote/ nodes. 210-- made by /remote/ nodes.
210data Manager h = Manager 211data Manager h raw msg = Manager
211 { sock :: !Socket 212 { sock :: !Socket
212 , options :: !Options 213 , options :: !Options
213 , listenerThread :: !(MVar ThreadId) 214 , listenerThread :: !(MVar ThreadId)
214 , transactionCounter :: {-# UNPACK #-} !TransactionCounter 215 , transactionCounter :: {-# UNPACK #-} !TransactionCounter
215 , pendingCalls :: {-# UNPACK #-} !PendingCalls 216 , pendingCalls :: {-# UNPACK #-} !(PendingCalls msg raw)
216#ifdef VERSION_bencoding 217 , handlers :: [Handler h msg raw]
217 , handlers :: [Handler h KMessageOf BValue]
218#else
219 , handlers :: [Handler h KMessageOf BC.ByteString]
220#endif
221 } 218 }
222 219
223-- | A monad which can perform or handle queries. 220-- | A monad which can perform or handle queries.
224class (MonadBaseControl IO m, MonadLogger m, MonadIO m) 221class (MonadBaseControl IO m, MonadLogger m, MonadIO m)
225 => MonadKRPC h m | m -> h where 222 => MonadKRPC h m raw msg | m -> h, m -> raw, m -> msg where
226 223
227 -- | Ask for manager. 224 -- | Ask for manager.
228 getManager :: m (Manager h) 225 getManager :: m (Manager h raw msg)
229 226
230 default getManager :: MonadReader (Manager h) m => m (Manager h) 227 default getManager :: MonadReader (Manager h raw msg) m => m (Manager h raw msg)
231 getManager = ask 228 getManager = ask
232 229
233 -- | Can be used to add logging for instance. 230 -- | Can be used to add logging for instance.
@@ -237,7 +234,7 @@ class (MonadBaseControl IO m, MonadLogger m, MonadIO m)
237 liftHandler = id 234 liftHandler = id
238 235
239instance (MonadBaseControl IO h, MonadLogger h, MonadIO h) 236instance (MonadBaseControl IO h, MonadLogger h, MonadIO h)
240 => MonadKRPC h (ReaderT (Manager h) h) where 237 => MonadKRPC h (ReaderT (Manager h raw msg) h) raw msg where
241 238
242 liftHandler = lift 239 liftHandler = lift
243 240
@@ -251,12 +248,8 @@ sockAddrFamily (SockAddrCan _ ) = AF_CAN
251-- run 'listen'. 248-- run 'listen'.
252newManager :: Options -- ^ various protocol options; 249newManager :: Options -- ^ various protocol options;
253 -> SockAddr -- ^ address to listen on; 250 -> SockAddr -- ^ address to listen on;
254#ifdef VERSION_bencoding 251 -> [Handler h msg raw] -- ^ handlers to run on incoming queries.
255 -> [Handler h KMessageOf BValue] -- ^ handlers to run on incoming queries. 252 -> IO (Manager h raw msg) -- ^ new rpc manager.
256#else
257 -> [Handler h KMessageOf BC.ByteString] -- ^ handlers to run on incoming queries.
258#endif
259 -> IO (Manager h) -- ^ new rpc manager.
260newManager opts @ Options {..} servAddr handlers = do 253newManager opts @ Options {..} servAddr handlers = do
261 validateOptions opts 254 validateOptions opts
262 sock <- bindServ 255 sock <- bindServ
@@ -274,7 +267,7 @@ newManager opts @ Options {..} servAddr handlers = do
274 return sock 267 return sock
275 268
276-- | Unblock all pending calls and close socket. 269-- | Unblock all pending calls and close socket.
277closeManager :: Manager m -> IO () 270closeManager :: Manager m raw msg -> IO ()
278closeManager Manager {..} = do 271closeManager Manager {..} = do
279 maybe (return ()) killThread =<< tryTakeMVar listenerThread 272 maybe (return ()) killThread =<< tryTakeMVar listenerThread
280 -- TODO unblock calls 273 -- TODO unblock calls
@@ -282,18 +275,14 @@ closeManager Manager {..} = do
282 275
283-- | Check if the manager is still active. Manager becomes active 276-- | Check if the manager is still active. Manager becomes active
284-- until 'closeManager' called. 277-- until 'closeManager' called.
285isActive :: Manager m -> IO Bool 278isActive :: Manager m raw msg -> IO Bool
286isActive Manager {..} = liftIO $ isBound sock 279isActive Manager {..} = liftIO $ isBound sock
287{-# INLINE isActive #-} 280{-# INLINE isActive #-}
288 281
289-- | Normally you should use Control.Monad.Trans.Resource.allocate 282-- | Normally you should use Control.Monad.Trans.Resource.allocate
290-- function. 283-- function.
291#ifdef VERSION_bencoding 284withManager :: Options -> SockAddr -> [Handler h msg raw]
292withManager :: Options -> SockAddr -> [Handler h KMessageOf BValue] 285 -> (Manager h raw msg -> IO a) -> IO a
293#else
294withManager :: Options -> SockAddr -> [Handler h KMessageOf BC.ByteString]
295#endif
296 -> (Manager h -> IO a) -> IO a
297withManager opts addr hs = bracket (newManager opts addr hs) closeManager 286withManager opts addr hs = bracket (newManager opts addr hs) closeManager
298 287
299{----------------------------------------------------------------------- 288{-----------------------------------------------------------------------
@@ -301,15 +290,12 @@ withManager opts addr hs = bracket (newManager opts addr hs) closeManager
301-----------------------------------------------------------------------} 290-----------------------------------------------------------------------}
302 291
303-- TODO prettify log messages 292-- TODO prettify log messages
304querySignature :: MethodName -> TransactionId -> SockAddr -> Text 293querySignature :: ( Show ( QueryMethod msg )
294 , Serialize ( TransactionID msg ) )
295 => QueryMethod msg -> TransactionID msg -> SockAddr -> Text
305querySignature name transaction addr = T.concat 296querySignature name transaction addr = T.concat
306#ifdef VERSION_bencoding
307 [ "&", T.decodeUtf8 name
308 , " #", T.decodeUtf8 (Base16.encode transaction) -- T.decodeUtf8 transaction
309#else
310 [ "&", T.pack (show name) 297 [ "&", T.pack (show name)
311 , " #", T.decodeUtf8 (Base16.encode $ S.encode transaction) 298 , " #", T.decodeUtf8 (Base16.encode $ S.encode transaction)
312#endif
313 , " @", T.pack (show addr) 299 , " @", T.pack (show addr)
314 ] 300 ]
315 301
@@ -332,23 +318,19 @@ sendMessage :: MonadIO m => Socket -> SockAddr -> BC.ByteString -> m ()
332sendMessage sock addr a = do 318sendMessage sock addr a = do
333 liftIO $ sendManyTo sock [a] addr 319 liftIO $ sendManyTo sock [a] addr
334 320
335genTransactionId :: TransactionCounter -> IO TransactionId 321genTransactionId :: Envelope msg => TransactionCounter -> IO (TransactionID msg)
336genTransactionId ref = do 322genTransactionId ref = do
337 cur <- atomicModifyIORef' ref $ \ cur -> (succ cur, cur) 323 cur <- atomicModifyIORef' ref $ \ cur -> (succ cur, cur)
338#ifdef VERSION_bencoding 324 uniqueTransactionId cur
339 return $ BC.pack (show cur)
340#else
341 return $ either (error "failed to create TransactionId") id $ S.decode $ BC.pack (L.take 24 $ show cur ++ L.repeat ' ')
342#endif
343 325
344-- | How many times 'query' call have been performed. 326-- | How many times 'query' call have been performed.
345getQueryCount :: MonadKRPC h m => m Int 327getQueryCount :: MonadKRPC h m raw msg => m Int
346getQueryCount = do 328getQueryCount = do
347 Manager {..} <- getManager 329 Manager {..} <- getManager
348 curTrans <- liftIO $ readIORef transactionCounter 330 curTrans <- liftIO $ readIORef transactionCounter
349 return $ curTrans - optSeedTransaction options 331 return $ curTrans - optSeedTransaction options
350 332
351registerQuery :: CallId -> PendingCalls -> IO CallRes 333registerQuery :: Ord (TransactionID msg) => CallId msg -> PendingCalls msg raw -> IO (CallRes msg raw)
352registerQuery cid ref = do 334registerQuery cid ref = do
353 ares <- newEmptyMVar 335 ares <- newEmptyMVar
354 atomicModifyIORef' ref $ \ m -> (M.insert cid ares m, ()) 336 atomicModifyIORef' ref $ \ m -> (M.insert cid ares m, ())
@@ -356,7 +338,7 @@ registerQuery cid ref = do
356 338
357-- simultaneous M.lookup and M.delete guarantees that we never get two 339-- simultaneous M.lookup and M.delete guarantees that we never get two
358-- or more responses to the same query 340-- or more responses to the same query
359unregisterQuery :: CallId -> PendingCalls -> IO (Maybe CallRes) 341unregisterQuery :: Ord (TransactionID msg) => CallId msg -> PendingCalls msg raw -> IO (Maybe (CallRes msg raw))
360unregisterQuery cid ref = do 342unregisterQuery cid ref = do
361 atomicModifyIORef' ref $ swap . 343 atomicModifyIORef' ref $ swap .
362 M.updateLookupWithKey (const (const Nothing)) cid 344 M.updateLookupWithKey (const (const Nothing)) cid
@@ -374,35 +356,37 @@ sendQuery sock addr q = handle sockError $ sendMessage sock addr q
374-- This function should throw 'QueryFailure' exception if quered node 356-- This function should throw 'QueryFailure' exception if quered node
375-- respond with @error@ message or the query timeout expires. 357-- respond with @error@ message or the query timeout expires.
376-- 358--
377query :: forall h m a b. (MonadKRPC h m, KRPC a b) => SockAddr -> a -> m b 359query :: forall h m a b raw msg. (SerializableTo raw b, Show (QueryMethod msg), Ord (TransactionID msg), Serialize (TransactionID msg), SerializableTo raw a, MonadKRPC h m raw msg, WireFormat raw msg, KRPC a b) => QueryMethod msg -> SockAddr -> a -> m b
378query addr params = queryK addr params (\_ x _ -> x) 360query meth addr params = queryK meth addr params (\_ x _ -> x)
379 361
380-- | Like 'query' but possibly returns your externally routable IP address. 362-- | Like 'query' but possibly returns your externally routable IP address.
381query' :: forall h m a b. (MonadKRPC h m, KRPC a b) => SockAddr -> a -> m (b, Maybe ReflectedIP) 363query' :: forall h m a b raw msg. (SerializableTo raw b, Show (QueryMethod msg), Ord (TransactionID msg), Serialize (TransactionID msg), SerializableTo raw a, MonadKRPC h m raw msg, WireFormat raw msg, KRPC a b) => QueryMethod msg -> SockAddr -> a -> m (b, Maybe ReflectedIP)
382query' addr params = queryK addr params (const (,)) 364query' meth addr params = queryK meth addr params (const (,))
383 365
384-- | Enqueue a query, but give us the complete BEncoded content sent by the 366-- | Enqueue a query, but give us the complete BEncoded content sent by the
385-- remote Node. This is useful for handling extensions that this library does 367-- remote Node. This is useful for handling extensions that this library does
386-- not otherwise support. 368-- not otherwise support.
387queryRaw :: forall h m a b. (MonadKRPC h m, KRPC a b) => SockAddr -> a -> m (b, KQueryArgs) 369queryRaw :: forall h m a b raw msg. (SerializableTo raw b, Show (QueryMethod msg), Ord (TransactionID msg), Serialize (TransactionID msg), SerializableTo raw a, MonadKRPC h m raw msg, WireFormat raw msg, KRPC a b) => QueryMethod msg -> SockAddr -> a -> m (b, raw)
388queryRaw addr params = queryK addr params (\raw x _ -> (x,raw)) 370queryRaw meth addr params = queryK meth addr params (\raw x _ -> (x,raw))
389 371
390queryK :: forall h m a b x. (MonadKRPC h m, KRPC a b) => 372queryK :: forall h m a b x raw msg. (SerializableTo raw b, Show (QueryMethod msg), Ord (TransactionID msg), Serialize (TransactionID msg), SerializableTo raw a, MonadKRPC h m raw msg, WireFormat raw msg, KRPC a b) =>
391 SockAddr -> a -> (KQueryArgs -> b -> Maybe ReflectedIP -> x) -> m x 373 QueryMethod msg -> SockAddr -> a -> (raw -> b -> Maybe ReflectedIP -> x) -> m x
392queryK addr params kont = do 374queryK meth addr params kont = do
393 Manager {..} <- getManager 375 Manager {..} <- getManager
394 tid <- liftIO $ genTransactionId transactionCounter 376 tid <- liftIO $ genTransactionId transactionCounter
395 let queryMethod = method :: Method a b 377 -- let queryMethod = method :: Method a b
396 let signature = querySignature (methodName queryMethod) tid addr 378 let signature = querySignature meth tid addr
397 $(logDebugS) "query.sending" signature 379 $(logDebugS) "query.sending" signature
398 380
399 mres <- liftIO $ do 381 mres <- liftIO $ do
400 ares <- registerQuery (tid, addr) pendingCalls 382 ares <- registerQuery (tid, addr) pendingCalls
401 383
384 let cli = error "TODO TOX client node id"
385 ctx = error "TODO TOX ToxCipherContext or () for Mainline"
386 q <- buildQuery cli addr meth tid params
387 let qb = encodePayload (q :: msg a) :: msg raw
388 qbs = encodeHeaders ctx qb
402#ifdef VERSION_bencoding 389#ifdef VERSION_bencoding
403 let q = Q (KQuery (toBEncode params) (methodName queryMethod) tid)
404 qb = encodePayload q :: KMessage
405 qbs = encodeHeaders () qb :: BC.ByteString
406#else 390#else
407 let q = Tox.Message (methodName queryMethod) cli tid params 391 let q = Tox.Message (methodName queryMethod) cli tid params
408 cli = error "TODO TOX client node id" 392 cli = error "TODO TOX client node id"
@@ -416,18 +400,13 @@ queryK addr params kont = do
416 timeout (optQueryTimeout options * 10 ^ (6 :: Int)) $ do 400 timeout (optQueryTimeout options * 10 ^ (6 :: Int)) $ do
417 (raw,res) <- readMVar ares -- MVar (KQueryArgs, KResult) 401 (raw,res) <- readMVar ares -- MVar (KQueryArgs, KResult)
418 case res of 402 case res of
419#ifdef VERSION_bencoding
420 Left (KError c m _) -> throwIO $ QueryFailed c (T.decodeUtf8 m) 403 Left (KError c m _) -> throwIO $ QueryFailed c (T.decodeUtf8 m)
421 Right (R (KResponse {..})) -> 404 Right m -> case decodePayload m of
422 case fromBEncode respVals of 405 Right r -> case envelopeClass (r :: msg b) of
423 Right r -> pure $ kont raw r respIP 406 Response reflectedAddr -> pure $ kont raw (envelopePayload r) reflectedAddr
424#else 407 Error (KError c m _) -> throwIO $ QueryFailed c (T.decodeUtf8 m) -- XXX neccessary?
425 Left _ -> throwIO $ QueryFailed GenericError "TODO: TOX ERROR" 408 Query _ -> throwIO $ QueryFailed ProtocolError "BUG!! UNREACHABLE"
426 Right (Tox.Message {..}) -> 409 Left e -> throwIO $ QueryFailed ProtocolError (T.pack e)
427 case S.decode msgPayload of
428 Right r -> pure $ kont raw r Nothing
429#endif
430 Left e -> throwIO $ QueryFailed ProtocolError (T.pack e)
431 410
432 case mres of 411 case mres of
433 Just res -> do 412 Just res -> do
@@ -477,43 +456,33 @@ prettyQF e = T.encodeUtf8 $ "handler fail while performing query: "
477-- If the handler make some 'query' normally it /should/ handle 456-- If the handler make some 'query' normally it /should/ handle
478-- corresponding 'QueryFailure's. 457-- corresponding 'QueryFailure's.
479-- 458--
480handler :: forall h a b msg raw. (KRPC a b, Applicative h, Functor msg, WireFormat raw msg, SerializableTo raw a, SerializableTo raw b) 459handler :: forall h a b msg raw. (Applicative h, Functor msg, WireFormat raw msg, SerializableTo raw a, SerializableTo raw b)
481 => (SockAddr -> a -> h b) -> Handler h msg raw 460 => QueryMethod msg -> (SockAddr -> a -> h b) -> Handler h msg raw
482handler body = (name, wrapper) 461handler name body = (name, wrapper)
483 where 462 where
484 Method name = method :: Method a b
485 wrapper :: SockAddr -> msg raw -> h (Either String (msg raw)) 463 wrapper :: SockAddr -> msg raw -> h (Either String (msg raw))
486 wrapper addr args = 464 wrapper addr args =
487 case decodePayload args of 465 case decodePayload args of
488 Left e -> pure $ Left e 466 Left e -> pure $ Left e
489 Right a -> Right . encodePayload . buildReply (error "self node-id") addr args <$> body addr (envelopePayload a) 467 Right a -> Right . encodePayload . buildReply (error "self node-id") addr args <$> body addr (envelopePayload a)
490 468
491runHandler :: MonadKRPC h m 469runHandler :: ( MonadKRPC h m raw msg
492#ifdef VERSION_bencoding 470 , Envelope msg
493 => HandlerBody h KMessageOf BValue -> SockAddr -> KQuery -> m KResult 471 , Show (QueryMethod msg)
494#else 472 , Serialize (TransactionID msg))
495 => HandlerBody h KMessageOf BC.ByteString -> SockAddr -> KQuery -> m KResult 473 => QueryMethod msg -> HandlerBody h msg raw -> SockAddr -> msg raw -> m (KResult msg raw)
496#endif 474runHandler meth h addr m = Lifted.catches wrapper failbacks
497runHandler h addr m = Lifted.catches wrapper failbacks
498 where 475 where
499 signature = querySignature (queryMethod m) (queryId m) addr 476 signature = querySignature meth (envelopeTransaction m) addr
500 477
501 wrapper = do 478 wrapper = do
502 $(logDebugS) "handler.quered" signature 479 $(logDebugS) "handler.quered" signature
503#ifdef VERSION_bencoding
504 result <- liftHandler (h addr (Q m))
505#else
506 result <- liftHandler (h addr m) 480 result <- liftHandler (h addr m)
507#endif
508 481
509 case result of 482 case result of
510 Left msg -> do 483 Left msg -> do
511 $(logDebugS) "handler.bad_query" $ signature <> " !" <> T.pack msg 484 $(logDebugS) "handler.bad_query" $ signature <> " !" <> T.pack msg
512#ifdef VERSION_bencoding 485 return $ Left $ KError ProtocolError (BC.pack msg) (envelopeTransaction m)
513 return $ Left $ KError ProtocolError (BC.pack msg) (queryId m)
514#else
515 return $ Left $ decodeError "TODO TOX ProtocolError" (queryId m)
516#endif
517 486
518 Right a -> do -- KQueryArgs 487 Right a -> do -- KQueryArgs
519 $(logDebugS) "handler.success" signature 488 $(logDebugS) "handler.success" signature
@@ -522,41 +491,30 @@ runHandler h addr m = Lifted.catches wrapper failbacks
522 failbacks = 491 failbacks =
523 [ E.Handler $ \ (e :: HandlerFailure) -> do 492 [ E.Handler $ \ (e :: HandlerFailure) -> do
524 $(logDebugS) "handler.failed" signature 493 $(logDebugS) "handler.failed" signature
525#ifdef VERSION_bencoding 494 return $ Left $ KError ProtocolError (prettyHF e) (envelopeTransaction m)
526 return $ Left $ KError ProtocolError (prettyHF e) (queryId m)
527#else
528 return $ Left $ decodeError "TODO TOX ProtocolError 2" (queryId m)
529#endif
530 495
531 496
532 -- may happen if handler makes query and fail 497 -- may happen if handler makes query and fail
533 , E.Handler $ \ (e :: QueryFailure) -> do 498 , E.Handler $ \ (e :: QueryFailure) -> do
534#ifdef VERSION_bencoding 499 return $ Left $ KError ServerError (prettyQF e) (envelopeTransaction m)
535 return $ Left $ KError ServerError (prettyQF e) (queryId m)
536#else
537 return $ Left $ decodeError "TODO TOX ServerError" (queryId m)
538#endif
539 500
540 -- since handler thread exit after sendMessage we can safely 501 -- since handler thread exit after sendMessage we can safely
541 -- suppress async exception here 502 -- suppress async exception here
542 , E.Handler $ \ (e :: SomeException) -> do 503 , E.Handler $ \ (e :: SomeException) -> do
543#ifdef VERSION_bencoding 504 return $ Left $ KError GenericError (BC.pack (show e)) (envelopeTransaction m)
544 return $ Left $ KError GenericError (BC.pack (show e)) (queryId m)
545#else
546 return $ Left $ decodeError "TODO TOX GenericError" (queryId m)
547#endif
548 ] 505 ]
549 506
550dispatchHandler :: MonadKRPC h m => KQuery -> SockAddr -> m KResult 507dispatchHandler :: ( MonadKRPC h m raw msg
551dispatchHandler q addr = do 508 , Eq (QueryMethod msg)
509 , Show (QueryMethod msg)
510 , Serialize (TransactionID msg)
511 , Envelope msg
512 ) => QueryMethod msg -> msg raw -> SockAddr -> m (KResult msg raw)
513dispatchHandler meth q addr = do
552 Manager {..} <- getManager 514 Manager {..} <- getManager
553 case L.lookup (queryMethod q) handlers of 515 case L.lookup meth handlers of
554#ifdef VERSION_bencoding 516 Nothing -> return $ Left $ KError MethodUnknown ("Unknown method " <> BC.pack (show meth)) (envelopeTransaction q)
555 Nothing -> return $ Left $ KError MethodUnknown (queryMethod q) (queryId q) 517 Just h -> runHandler meth h addr q
556#else
557 Nothing -> return $ Left $ decodeError "TODO TOX Error MethodUnknown" (queryId q)
558#endif
559 Just h -> runHandler h addr q
560 518
561{----------------------------------------------------------------------- 519{-----------------------------------------------------------------------
562-- Listener 520-- Listener
@@ -569,71 +527,75 @@ dispatchHandler q addr = do
569-- peer B fork too many threads 527-- peer B fork too many threads
570-- ... space leak 528-- ... space leak
571-- 529--
572handleQuery :: MonadKRPC h m => KQueryArgs -> KQuery -> SockAddr -> m () 530handleQuery :: ( MonadKRPC h m raw msg
573handleQuery raw q addr = void $ fork $ do 531 , WireFormat raw msg
532 , Eq (QueryMethod msg)
533 , Show (QueryMethod msg)
534 , Serialize (TransactionID msg)
535 ) => QueryMethod msg -> raw -> msg raw -> SockAddr -> m ()
536handleQuery meth raw q addr = void $ fork $ do
574 myThreadId >>= liftIO . flip labelThread "KRPC.handleQuery" 537 myThreadId >>= liftIO . flip labelThread "KRPC.handleQuery"
575 Manager {..} <- getManager 538 Manager {..} <- getManager
576 res <- dispatchHandler q addr 539 res <- dispatchHandler meth q addr
577#ifdef VERSION_bencoding 540#ifdef VERSION_bencoding
578 let res' = either E id res 541 let res' = either buildError Just res
579 resbe = either toBEncode toBEncode res 542 ctx = error "TODO TOX ToxCipherContext 2 or () for Mainline"
580 $(logOther "q") $ T.unlines 543 resbs = fmap (encodeHeaders ctx) res' :: Maybe BS.ByteString
581 [ either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode raw) 544-- TODO: Generalize this debug print.
582 , "==>" 545-- resbe = either toBEncode toBEncode res
583 , either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode resbe) 546-- $(logOther "q") $ T.unlines
584 ] 547-- [ either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode raw)
585 sendMessage sock addr $ encodeHeaders () res' 548-- , "==>"
549-- , either (const "<unicode-fail>") id $ T.decodeUtf8' (BL.toStrict $ showBEncode resbe)
550-- ]
551 maybe (return ()) (sendMessage sock addr) resbs
586#else 552#else
587 -- Errors not sent for Tox. 553 -- Errors not sent for Tox.
588 let ctx = error "TODO TOX ToxCipherContext 2" 554 let ctx = error "TODO TOX ToxCipherContext 2"
589 either (const $ return ()) (sendMessage sock addr . encodeHeaders ctx) res 555 either (const $ return ()) (sendMessage sock addr . encodeHeaders ctx) res
590#endif 556#endif
591 557
592handleResponse :: MonadKRPC h m => KQueryArgs -> KResult -> SockAddr -> m () 558handleResponse :: ( MonadKRPC h m raw msg
559 , Ord (TransactionID msg)
560 , Envelope msg
561 ) => raw -> KResult msg raw -> SockAddr -> m ()
593handleResponse raw result addr = do 562handleResponse raw result addr = do
594 Manager {..} <- getManager 563 Manager {..} <- getManager
595 liftIO $ do 564 liftIO $ do
596#ifdef VERSION_bencoding
597 let resultId = either errorId envelopeTransaction result 565 let resultId = either errorId envelopeTransaction result
598#else
599 let resultId = either Tox.msgNonce Tox.msgNonce result
600#endif
601 mcall <- unregisterQuery (resultId, addr) pendingCalls 566 mcall <- unregisterQuery (resultId, addr) pendingCalls
602 case mcall of 567 case mcall of
603 Nothing -> return () 568 Nothing -> return ()
604 Just ares -> putMVar ares (raw,result) 569 Just ares -> putMVar ares (raw,result)
605 570
606#ifdef VERSION_bencoding 571data Protocol raw (msg :: * -> *) = Protocol { rawProxy :: !(Proxy raw)
607handleMessage :: MonadKRPC h m => KQueryArgs -> KMessage -> SockAddr -> m () 572 , msgProxy :: !(Proxy msg)
608handleMessage raw (Q q) = handleQuery raw q 573 }
609handleMessage raw (R r) = handleResponse raw (Right (R r)) 574
610handleMessage raw (E e) = handleResponse raw (Left e) 575listener :: forall h m raw msg.
611#else 576 ( MonadKRPC h m raw msg
612handleMessage :: MonadKRPC h m => KQueryArgs -> Tox.Message BC.ByteString -> SockAddr -> m () 577 , WireFormat raw msg
613handleMessage raw q | Tox.isQuery q = handleQuery raw q 578 , Ord (TransactionID msg)
614handleMessage raw r | Tox.isResponse r = handleResponse raw (Right r) 579 , Eq (QueryMethod msg)
615handleMessage raw e | Tox.isError e = handleResponse raw (Left e) 580 , Show (QueryMethod msg)
616#endif 581 , Serialize (TransactionID msg)
617 582 ) => Protocol raw msg -> m ()
618listener :: MonadKRPC h m => m () 583listener p = do
619listener = do
620 Manager {..} <- getManager 584 Manager {..} <- getManager
621 fix $ \again -> do 585 fix $ \again -> do
622 let ctx = error "TODO TOX ToxCipherContext 3" 586 let ctx = error "TODO TOX ToxCipherContext or () for Mainline"
623 (bs, addr) <- liftIO $ do 587 (bs, addr) <- liftIO $ do
624 handle exceptions $ BS.recvFrom sock (optMaxMsgSize options) 588 handle exceptions $ BS.recvFrom sock (optMaxMsgSize options)
625#ifdef VERSION_bencoding 589 case parsePacket (msgProxy p) bs >>= \r -> (,) r <$> decodeHeaders ctx r of
626 case BE.parse bs >>= \r -> (,) r <$> BE.decode bs of 590 Left e -> -- XXX: Send parse failure message?
627#else 591 -- liftIO $ sendMessage sock addr $ encodeHeaders ctx (unknownMessage e)
628 case return bs >>= \r -> (,) r <$> decodeHeaders ctx bs of 592 return () -- Without transaction id, error message isn't very useful.
629#endif 593 Right (raw,m) ->
630 -- TODO ignore unknown messages at all? 594 case envelopeClass m of
631#ifdef VERSION_bencoding 595 Query meth -> handleQuery meth (raw::raw) m addr
632 Left e -> liftIO $ sendMessage sock addr $ encodeHeaders () (E (unknownMessage e) :: KMessage) 596 Response _ -> handleResponse raw (Right m) addr
633#else 597 Error e -> handleResponse raw (Left e) addr
634 Left _ -> return () -- TODO TOX send unknownMessage error 598
635#endif
636 Right (raw,m) -> handleMessage raw m addr
637 again 599 again
638 where 600 where
639 exceptions :: IOError -> IO (BS.ByteString, SockAddr) 601 exceptions :: IOError -> IO (BS.ByteString, SockAddr)
@@ -644,11 +606,17 @@ listener = do
644 606
645-- | Should be run before any 'query', otherwise they will never 607-- | Should be run before any 'query', otherwise they will never
646-- succeed. 608-- succeed.
647listen :: MonadKRPC h m => m () 609listen :: ( MonadKRPC h m raw msg
648listen = do 610 , WireFormat raw msg
611 , Ord (TransactionID msg)
612 , Eq (QueryMethod msg)
613 , Show (QueryMethod msg)
614 , Serialize (TransactionID msg)
615 ) => Protocol raw msg -> m ()
616listen p = do
649 Manager {..} <- getManager 617 Manager {..} <- getManager
650 tid <- fork $ do 618 tid <- fork $ do
651 myThreadId >>= liftIO . flip labelThread "KRPC.listen" 619 myThreadId >>= liftIO . flip labelThread "KRPC.listen"
652 listener `Lifted.finally` 620 listener p `Lifted.finally`
653 liftIO (takeMVar listenerThread) 621 liftIO (takeMVar listenerThread)
654 liftIO $ putMVar listenerThread tid 622 liftIO $ putMVar listenerThread tid
diff --git a/src/Network/DatagramServer/Error.hs b/src/Network/DatagramServer/Error.hs
new file mode 100644
index 00000000..2cbb76c3
--- /dev/null
+++ b/src/Network/DatagramServer/Error.hs
@@ -0,0 +1,110 @@
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3module Network.DatagramServer.Error where
4
5import Control.Exception.Lifted as Lifted
6#ifdef VERSION_bencoding
7import Data.BEncode as BE
8#endif
9import Data.ByteString (ByteString)
10import Data.ByteString.Char8 as Char8
11import Data.Data
12import Data.Default
13import Data.Typeable
14
15{-----------------------------------------------------------------------
16-- Error messages
17-----------------------------------------------------------------------}
18
19-- | Types of RPC errors.
20data ErrorCode
21 -- | Some error doesn't fit in any other category.
22 = GenericError
23
24 -- | Occur when server fail to process procedure call.
25 | ServerError
26
27 -- | Malformed packet, invalid arguments or bad token.
28 | ProtocolError
29
30 -- | Occur when client trying to call method server don't know.
31 | MethodUnknown
32 deriving (Show, Read, Eq, Ord, Bounded, Typeable, Data)
33
34-- | According to the table:
35-- <http://bittorrent.org/beps/bep_0005.html#errors>
36instance Enum ErrorCode where
37 fromEnum GenericError = 201
38 fromEnum ServerError = 202
39 fromEnum ProtocolError = 203
40 fromEnum MethodUnknown = 204
41 {-# INLINE fromEnum #-}
42
43 toEnum 201 = GenericError
44 toEnum 202 = ServerError
45 toEnum 203 = ProtocolError
46 toEnum 204 = MethodUnknown
47 toEnum _ = GenericError
48 {-# INLINE toEnum #-}
49
50#ifdef VERSION_bencoding
51instance BEncode ErrorCode where
52 toBEncode = toBEncode . fromEnum
53 {-# INLINE toBEncode #-}
54
55 fromBEncode b = toEnum <$> fromBEncode b
56 {-# INLINE fromBEncode #-}
57#endif
58
59-- | Errors are sent when a query cannot be fulfilled. Error message
60-- can be send only from server to client but not in the opposite
61-- direction.
62--
63data KError tid = KError
64 { errorCode :: !ErrorCode -- ^ the type of error;
65 , errorMessage :: !ByteString -- ^ human-readable text message;
66 , errorId :: !tid -- ^ match to the corresponding 'queryId'.
67 } deriving ( Show, Eq, Ord, Typeable, Data, Read )
68
69-- | Errors, or KRPC message dictionaries with a \"y\" value of \"e\",
70-- contain one additional key \"e\". The value of \"e\" is a
71-- list. The first element is an integer representing the error
72-- code. The second element is a string containing the error
73-- message.
74--
75-- Example Error Packet:
76--
77-- > { "t": "aa", "y":"e", "e":[201, "A Generic Error Ocurred"]}
78--
79-- or bencoded:
80--
81-- > d1:eli201e23:A Generic Error Ocurrede1:t2:aa1:y1:ee
82--
83#ifdef VERSION_bencoding
84instance (Typeable tid, BEncode tid) => BEncode (KError tid) where
85 toBEncode KError {..} = toDict $
86 "e" .=! (errorCode, errorMessage)
87 .: "t" .=! errorId
88 .: "y" .=! ("e" :: ByteString)
89 .: endDict
90 {-# INLINE toBEncode #-}
91
92 fromBEncode = fromDict $ do
93 lookAhead $ match "y" (BString "e")
94 (code, msg) <- field (req "e")
95 KError code msg <$>! "t"
96 {-# INLINE fromBEncode #-}
97#endif
98
99instance (Typeable tid, Show tid) => Exception (KError tid)
100
101-- | Received 'queryArgs' or 'respVals' can not be decoded.
102decodeError :: String -> tid -> KError tid
103decodeError msg = KError ProtocolError (Char8.pack msg)
104
105-- | A remote node has send some 'KMessage' this node is unable to
106-- decode.
107unknownMessage :: Default tid => String -> KError tid
108unknownMessage msg = KError ProtocolError (Char8.pack msg) def
109
110
diff --git a/src/Network/DatagramServer/Mainline.hs b/src/Network/DatagramServer/Mainline.hs
index 70b9b184..17f9dd60 100644
--- a/src/Network/DatagramServer/Mainline.hs
+++ b/src/Network/DatagramServer/Mainline.hs
@@ -23,6 +23,8 @@
23{-# LANGUAGE MultiParamTypeClasses #-} 23{-# LANGUAGE MultiParamTypeClasses #-}
24{-# LANGUAGE OverloadedStrings #-} 24{-# LANGUAGE OverloadedStrings #-}
25{-# LANGUAGE TypeSynonymInstances #-} 25{-# LANGUAGE TypeSynonymInstances #-}
26{-# LANGUAGE TypeFamilies #-}
27{-# LANGUAGE GeneralizedNewtypeDeriving #-}
26module Network.DatagramServer.Mainline 28module Network.DatagramServer.Mainline
27 ( -- * Transaction 29 ( -- * Transaction
28 TransactionId 30 TransactionId
@@ -57,6 +59,9 @@ module Network.DatagramServer.Mainline
57 , KMessage 59 , KMessage
58 , KQueryArgs 60 , KQueryArgs
59 61
62 , NodeId(..)
63 , nodeIdSize
64
60 ) where 65 ) where
61 66
62import Control.Applicative 67import Control.Applicative
@@ -67,12 +72,23 @@ import Data.BEncode as BE
67#else 72#else
68import qualified Network.DatagramServer.Tox as Tox 73import qualified Network.DatagramServer.Tox as Tox
69#endif 74#endif
70import Data.ByteString as B 75import Network.DatagramServer.Types
71import Data.ByteString.Char8 as BC 76import Data.Bits
77import Data.ByteString.Base16 as Base16
78import Data.ByteString (ByteString)
79import qualified Data.ByteString as BS
80import qualified Data.ByteString.Char8 as Char8
81import qualified Data.ByteString.Lazy as L
82import Data.Default
83import Data.LargeWord
72import qualified Data.Serialize as S 84import qualified Data.Serialize as S
85import Data.Serialize (Serialize, get, put, remaining, getBytes, putByteString)
86import Data.String
73import Data.Word 87import Data.Word
74import Data.Typeable 88import Data.Typeable
75import Network.Socket (SockAddr (..),PortNumber,HostAddress) 89import Network.Socket (SockAddr (..),PortNumber,HostAddress)
90import Text.PrettyPrint as PP hiding ((<>))
91import Text.PrettyPrint.HughesPJClass hiding (($$), (<>))
76 92
77 93
78#ifdef VERSION_bencoding 94#ifdef VERSION_bencoding
@@ -81,7 +97,7 @@ import Network.Socket (SockAddr (..),PortNumber,HostAddress)
81-- multiple queries to the same node. The transaction ID should be 97-- multiple queries to the same node. The transaction ID should be
82-- encoded as a short string of binary numbers, typically 2 characters 98-- encoded as a short string of binary numbers, typically 2 characters
83-- are enough as they cover 2^16 outstanding queries. 99-- are enough as they cover 2^16 outstanding queries.
84type TransactionId = ByteString 100type TransactionId = TransactionID KMessageOf
85#else 101#else
86type TransactionId = Tox.Nonce24 -- msgNonce 102type TransactionId = Tox.Nonce24 -- msgNonce
87#endif 103#endif
@@ -94,113 +110,6 @@ unknownTransaction = 0
94#endif 110#endif
95 111
96{----------------------------------------------------------------------- 112{-----------------------------------------------------------------------
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 113-- Query messages
205-----------------------------------------------------------------------} 114-----------------------------------------------------------------------}
206 115
@@ -259,21 +168,18 @@ queryMethod = Tox.msgType
259queryId = Tox.msgNonce 168queryId = Tox.msgNonce
260#endif 169#endif
261 170
262newtype ReflectedIP = ReflectedIP SockAddr
263 deriving (Eq, Ord, Show)
264
265port16 :: Word16 -> PortNumber 171port16 :: Word16 -> PortNumber
266port16 = fromIntegral 172port16 = fromIntegral
267 173
268decodeAddr :: ByteString -> Either String SockAddr 174decodeAddr :: ByteString -> Either String SockAddr
269decodeAddr bs | B.length bs == 6 175decodeAddr bs | BS.length bs == 6
270 = ( \(a,p) -> SockAddrInet <$> fmap port16 p <*> a ) 176 = ( \(a,p) -> SockAddrInet <$> fmap port16 p <*> a )
271 $ (S.runGet S.getWord32host *** S.decode ) 177 $ (S.runGet S.getWord32host *** S.decode )
272 $ B.splitAt 4 bs 178 $ BS.splitAt 4 bs
273decodeAddr bs | B.length bs == 18 179decodeAddr bs | BS.length bs == 18
274 = ( \(a,p) -> flip SockAddrInet6 0 <$> fmap port16 p <*> a <*> pure 0 ) 180 = ( \(a,p) -> flip SockAddrInet6 0 <$> fmap port16 p <*> a <*> pure 0 )
275 $ (S.decode *** S.decode ) 181 $ (S.decode *** S.decode )
276 $ B.splitAt 16 bs 182 $ BS.splitAt 16 bs
277decodeAddr _ = Left "incorrectly sized address and port" 183decodeAddr _ = Left "incorrectly sized address and port"
278 184
279encodeAddr :: SockAddr -> ByteString 185encodeAddr :: SockAddr -> ByteString
@@ -281,7 +187,7 @@ encodeAddr (SockAddrInet port addr)
281 = S.runPut (S.putWord32host addr >> S.put (fromIntegral port :: Word16)) 187 = S.runPut (S.putWord32host addr >> S.put (fromIntegral port :: Word16))
282encodeAddr (SockAddrInet6 port _ addr _) 188encodeAddr (SockAddrInet6 port _ addr _)
283 = S.runPut (S.put addr >> S.put (fromIntegral port :: Word16)) 189 = S.runPut (S.put addr >> S.put (fromIntegral port :: Word16))
284encodeAddr _ = B.empty 190encodeAddr _ = BS.empty
285 191
286{----------------------------------------------------------------------- 192{-----------------------------------------------------------------------
287-- Response messages 193-- Response messages
@@ -345,7 +251,7 @@ respIP = Nothing :: Maybe ReflectedIP
345data KMessageOf a 251data KMessageOf a
346 = Q (KQueryOf a) 252 = Q (KQueryOf a)
347 | R (KResponseOf a) 253 | R (KResponseOf a)
348 | E KError 254 | E (KError TransactionId)
349 deriving (Show, Eq, Functor, Foldable, Traversable) 255 deriving (Show, Eq, Functor, Foldable, Traversable)
350 256
351type KMessage = KMessageOf KQueryArgs 257type KMessage = KMessageOf KQueryArgs
@@ -364,3 +270,95 @@ instance BEncode KMessage where
364type KMessageOf = Tox.Message 270type KMessageOf = Tox.Message
365type KMessage = KMessageOf B.ByteString 271type KMessage = KMessageOf B.ByteString
366#endif 272#endif
273
274nodeIdSize :: Int
275nodeIdSize = finiteBitSize (undefined :: NodeId KMessageOf) `div` 8
276
277instance BEncode (NodeId KMessageOf) where
278 toBEncode (NodeId w) = toBEncode $ S.encode w
279 fromBEncode bval = fromBEncode bval >>= S.decode
280
281-- instance BEncode NodeId where TODO
282
283-- TODO: put this somewhere appropriate
284instance (Serialize a, Serialize b) => Serialize (LargeKey a b) where
285 put (LargeKey lo hi) = put hi >> put lo
286 get = flip LargeKey <$> get <*> get
287
288instance Serialize (NodeId KMessageOf) where
289 get = NodeId <$> get
290 {-# INLINE get #-}
291 put (NodeId bs) = put bs
292 {-# INLINE put #-}
293
294-- | ASCII encoded.
295instance IsString (NodeId KMessageOf) where
296 fromString str
297 | length str == nodeIdSize = NodeId (either error id $ S.decode (fromString str :: ByteString))
298 | length str == 2 * nodeIdSize = NodeId (either error id $ S.decode (fst $ Base16.decode $ fromString str))
299 | otherwise = error "fromString: invalid NodeId length"
300 {-# INLINE fromString #-}
301
302-- | Meaningless node id, for testing purposes only.
303instance Default (NodeId KMessageOf) where
304 def = NodeId 0
305
306-- | base16 encoded.
307instance Pretty (NodeId KMessageOf) where
308 pPrint (NodeId nid) = PP.text $ Char8.unpack $ Base16.encode $ S.encode nid
309
310
311instance Serialize (TransactionID KMessageOf) where
312 get = do
313 cnt <- remaining
314 TID <$> getBytes cnt
315
316 put (TID bs) = putByteString bs
317
318
319instance Envelope KMessageOf where
320 type QueryMethod KMessageOf = ByteString
321
322 newtype TransactionID KMessageOf = TID ByteString
323 deriving (Eq,Ord,IsString,Show,Read,BEncode)
324
325 -- | Each node has a globally unique identifier known as the \"node
326 -- ID.\"
327 --
328 -- Normally, /this/ node id should be saved between invocations
329 -- of the client software.
330 newtype NodeId KMessageOf = NodeId Word160
331 deriving (Show, Eq, Ord, Typeable, Bits, FiniteBits)
332
333 envelopePayload (Q q) = queryArgs q
334 envelopePayload (R r) = respVals r
335 envelopePayload (E _) = error "TODO: messagePayload for KError"
336
337 envelopeTransaction (Q q) = queryId q
338 envelopeTransaction (R r) = respId r
339 envelopeTransaction (E e) = errorId e
340
341 envelopeClass (Q q) = Query (queryMethod q)
342 envelopeClass (R r) = Response (respIP r)
343 envelopeClass (E e) = Error e
344
345 buildReply self addr qry response =
346 (R (KResponse response (envelopeTransaction qry) (Just $ ReflectedIP addr)))
347
348 buildQuery self addr meth tid qry = return $ Q (KQuery qry meth tid)
349
350 uniqueTransactionId cnt = return $ TID $ Char8.pack (show cnt)
351
352instance WireFormat BValue KMessageOf where
353 type SerializableTo BValue = BEncode
354 type CipherContext BValue KMessageOf = ()
355
356 parsePacket _ = BE.decode
357
358 buildError = Just . E
359
360 decodeHeaders _ = BE.fromBEncode
361 decodePayload kmsg = mapM BE.fromBEncode kmsg
362
363 encodeHeaders _ kmsg = L.toStrict $ BE.encode kmsg
364 encodePayload msg = fmap BE.toBEncode msg
diff --git a/src/Network/DatagramServer/Tox.hs b/src/Network/DatagramServer/Tox.hs
index ad376c68..2f48b512 100644
--- a/src/Network/DatagramServer/Tox.hs
+++ b/src/Network/DatagramServer/Tox.hs
@@ -15,6 +15,9 @@ module Network.DatagramServer.Tox where
15 15
16import Data.Bits 16import Data.Bits
17import Data.ByteString (ByteString) 17import Data.ByteString (ByteString)
18import qualified Data.Serialize as S
19import qualified Data.ByteString.Lazy as L
20import qualified Data.ByteString.Char8 as Char8
18import Data.Data (Data) 21import Data.Data (Data)
19import Data.Word 22import Data.Word
20import Data.LargeWord 23import Data.LargeWord
@@ -253,7 +256,9 @@ curve25519 = CurveFP (CurvePrime prime curvecommon)
253 256
254 257
255instance Envelope Message where 258instance Envelope Message where
256 type TransactionID Message = Nonce24 259 newtype TransactionID Message = TID Nonce24
260 deriving (Eq,Ord,Show,Read,Serialize)
261
257 newtype NodeId Message = NodeId Word256 262 newtype NodeId Message = NodeId Word256
258 deriving (Serialize, Eq, Ord, Bits, FiniteBits) 263 deriving (Serialize, Eq, Ord, Bits, FiniteBits)
259 264
@@ -268,6 +273,10 @@ instance Envelope Message where
268 273
269 buildReply self addr qry payload = (fmap (const payload) qry) { msgClient = self } 274 buildReply self addr qry payload = (fmap (const payload) qry) { msgClient = self }
270 275
276 uniqueTransactionId cnt = do
277 return $ either (error "failed to create TransactionId") TID
278 $ S.decode $ Char8.pack (L.take 24 $ show cur ++ L.repeat ' ')
279
271instance WireFormat ByteString Message where 280instance WireFormat ByteString Message where
272 type SerializableTo ByteString = Serialize 281 type SerializableTo ByteString = Serialize
273 type CipherContext ByteString Message = ToxCipherContext 282 type CipherContext ByteString Message = ToxCipherContext
diff --git a/src/Network/DatagramServer/Types.hs b/src/Network/DatagramServer/Types.hs
index ac18e6ce..9c8f3ded 100644
--- a/src/Network/DatagramServer/Types.hs
+++ b/src/Network/DatagramServer/Types.hs
@@ -3,6 +3,7 @@
3{-# LANGUAGE DeriveFunctor #-} 3{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveFoldable #-} 4{-# LANGUAGE DeriveFoldable #-}
5{-# LANGUAGE DeriveTraversable #-} 5{-# LANGUAGE DeriveTraversable #-}
6{-# LANGUAGE DefaultSignatures #-}
6{-# LANGUAGE FlexibleInstances #-} 7{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FlexibleContexts #-} 8{-# LANGUAGE FlexibleContexts #-}
8{-# LANGUAGE FunctionalDependencies #-} 9{-# LANGUAGE FunctionalDependencies #-}
@@ -11,7 +12,10 @@
11{-# LANGUAGE ScopedTypeVariables #-} 12{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE TypeFamilies #-} 13{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE StandaloneDeriving #-} 14{-# LANGUAGE StandaloneDeriving #-}
14module Network.DatagramServer.Types where 15module Network.DatagramServer.Types
16 ( module Network.DatagramServer.Types
17 , module Network.DatagramServer.Error
18 ) where
15 19
16import Control.Applicative 20import Control.Applicative
17import qualified Text.ParserCombinators.ReadP as RP 21import qualified Text.ParserCombinators.ReadP as RP
@@ -37,6 +41,7 @@ import qualified Data.ByteString.Char8 as Char8
37import qualified Data.ByteString as BS 41import qualified Data.ByteString as BS
38import Data.ByteString.Base16 as Base16 42import Data.ByteString.Base16 as Base16
39import System.Entropy 43import System.Entropy
44import Network.DatagramServer.Error
40 45
41class (Eq a, Serialize a, Typeable a, Hashable a, Pretty a) 46class (Eq a, Serialize a, Typeable a, Hashable a, Pretty a)
42 => Address a where 47 => Address a where
@@ -69,16 +74,25 @@ instance Address IP where
69 74
70 75
71 76
72data MessageClass = Error | Query | Response 77type MessageClass msg = MessageClassG (QueryMethod msg) (TransactionID msg)
73 deriving (Eq,Ord,Enum,Bounded,Data,Show,Read) 78
79newtype ReflectedIP = ReflectedIP SockAddr
80 deriving (Eq, Ord, Show)
81
82data MessageClassG meth tid = Query meth
83 | Response (Maybe ReflectedIP)
84 | Error (KError tid)
85 deriving (Eq,Ord,Show) -- ,Read, Data: not implemented by SockAddr
86
74 87
75class Envelope envelope where 88class Envelope envelope where
76 type TransactionID envelope 89 data TransactionID envelope
90 type QueryMethod envelope
77 data NodeId envelope 91 data NodeId envelope
78 92
79 envelopePayload :: envelope a -> a 93 envelopePayload :: envelope a -> a
80 envelopeTransaction :: envelope a -> TransactionID envelope 94 envelopeTransaction :: envelope a -> TransactionID envelope
81 envelopeClass :: envelope a -> MessageClass 95 envelopeClass :: envelope a -> MessageClass envelope
82 96
83 -- | > buildReply self addr qry response 97 -- | > buildReply self addr qry response
84 -- 98 --
@@ -93,6 +107,10 @@ class Envelope envelope where
93 -- Returns: response message envelope 107 -- Returns: response message envelope
94 buildReply :: NodeId envelope -> SockAddr -> envelope a -> b -> envelope b 108 buildReply :: NodeId envelope -> SockAddr -> envelope a -> b -> envelope b
95 109
110 buildQuery :: NodeId envelope -> SockAddr -> QueryMethod envelope -> TransactionID envelope -> a -> IO (envelope a)
111
112 uniqueTransactionId :: Int -> IO (TransactionID envelope)
113
96-- | In Kademlia, the distance metric is XOR and the result is 114-- | In Kademlia, the distance metric is XOR and the result is
97-- interpreted as an unsigned integer. 115-- interpreted as an unsigned integer.
98newtype NodeDistance nodeid = NodeDistance nodeid 116newtype NodeDistance nodeid = NodeDistance nodeid
@@ -294,7 +312,15 @@ class Envelope envelope => WireFormat raw envelope where
294 type SerializableTo raw :: * -> Constraint 312 type SerializableTo raw :: * -> Constraint
295 type CipherContext raw envelope 313 type CipherContext raw envelope
296 314
297 decodeHeaders :: CipherContext raw envelope -> ByteString -> Either String (envelope raw) 315 parsePacket :: Proxy envelope -> ByteString -> Either String raw
316
317 default parsePacket :: Proxy envelope -> ByteString -> Either String ByteString
318 parsePacket _ = Right
319
320 buildError :: KError (TransactionID envelope) -> Maybe (envelope raw)
321 buildError _ = Nothing
322
323 decodeHeaders :: CipherContext raw envelope -> raw -> Either String (envelope raw)
298 decodePayload :: SerializableTo raw a => envelope raw -> Either String (envelope a) 324 decodePayload :: SerializableTo raw a => envelope raw -> Either String (envelope a)
299 325
300 encodeHeaders :: CipherContext raw envelope -> envelope raw -> ByteString 326 encodeHeaders :: CipherContext raw envelope -> envelope raw -> ByteString