summaryrefslogtreecommitdiff
path: root/Mainline.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Mainline.hs')
-rw-r--r--Mainline.hs151
1 files changed, 151 insertions, 0 deletions
diff --git a/Mainline.hs b/Mainline.hs
new file mode 100644
index 00000000..01d683d1
--- /dev/null
+++ b/Mainline.hs
@@ -0,0 +1,151 @@
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveFoldable #-}
3{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveTraversable #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7module Mainline where
8
9import Control.Arrow
10import Data.BEncode as BE
11import Data.BEncode.BDict as BE
12import Data.Bool
13import Data.ByteArray
14import Data.ByteString (ByteString)
15import Data.ByteString as B
16import Data.ByteString.Lazy (toStrict)
17import Data.Data
18import Data.IP
19import qualified Data.Serialize as S
20import Data.Typeable
21import Data.Word
22import Network.Address (Address, fromSockAddr, sockAddrPort,
23 toSockAddr, withPort)
24import Network.QueryResponse
25import Network.Socket
26
27newtype NodeId = NodeId ByteString
28 deriving (Eq,Ord,Show,ByteArrayAccess, BEncode)
29
30data NodeInfo = NodeInfo
31 { nodeId :: NodeId
32 , nodeIP :: IP
33 , nodePort :: PortNumber
34 }
35
36nodeAddr :: NodeInfo -> SockAddr
37nodeAddr (NodeInfo _ ip port) = toSockAddr ip `withPort` port
38
39nodeInfo :: NodeId -> SockAddr -> Either String NodeInfo
40nodeInfo nid saddr
41 | Just ip <- fromSockAddr saddr
42 , Just port <- sockAddrPort saddr = Right $ NodeInfo nid ip port
43 | otherwise = Left "Address family not supported."
44
45-- | Types of RPC errors.
46data ErrorCode
47 -- | Some error doesn't fit in any other category.
48 = GenericError
49
50 -- | Occurs when server fail to process procedure call.
51 | ServerError
52
53 -- | Malformed packet, invalid arguments or bad token.
54 | ProtocolError
55
56 -- | Occurs when client trying to call method server don't know.
57 | MethodUnknown
58 deriving (Show, Read, Eq, Ord, Bounded, Typeable, Data)
59
60-- | According to the table:
61-- <http://bittorrent.org/beps/bep_0005.html#errors>
62instance Enum ErrorCode where
63 fromEnum GenericError = 201
64 fromEnum ServerError = 202
65 fromEnum ProtocolError = 203
66 fromEnum MethodUnknown = 204
67 {-# INLINE fromEnum #-}
68 toEnum 201 = GenericError
69 toEnum 202 = ServerError
70 toEnum 203 = ProtocolError
71 toEnum 204 = MethodUnknown
72 toEnum _ = GenericError
73 {-# INLINE toEnum #-}
74
75instance BEncode ErrorCode where
76 toBEncode = toBEncode . fromEnum
77 {-# INLINE toBEncode #-}
78 fromBEncode b = toEnum <$> fromBEncode b
79 {-# INLINE fromBEncode #-}
80
81data Error = Error
82 { errorCode :: !ErrorCode -- ^ The type of error.
83 , errorMessage :: !ByteString -- ^ Human-readable text message.
84 } deriving ( Show, Eq, Ord, Typeable, Data, Read )
85
86newtype TransactionId = TransactionId Word16
87 deriving (Eq, Ord, Show, BEncode)
88
89newtype Method = Method ByteString
90 deriving (Eq, Ord, Show, BEncode)
91
92data Message a = Q { msgOrigin :: NodeId
93 , msgID :: TransactionId
94 , qryPayload :: a
95 , qryMethod :: Method
96 , qryReadOnly :: Bool }
97
98 | R { msgOrigin :: NodeId
99 , msgID :: TransactionId
100 , rspPayload :: Either Error a
101 , rspReflectedIP :: Maybe SockAddr }
102
103instance BE.BEncode (Message BValue) where
104 toBEncode = encodeMessage
105 fromBEncode = error "fromBEncode"
106
107encodeMessage (Q origin tid a meth ro)
108 = case a of
109 BDict args -> encodeQuery tid meth (BDict $ genericArgs origin ro `union` args)
110 _ -> encodeQuery tid meth a -- XXX: Not really a valid query.
111encodeMessage (R origin tid v ip)
112 = case v of
113 Right vals -> encodeResponse tid vals (BString . encodeAddr <$> ip)
114 Left err -> encodeError tid err
115
116encodeAddr :: SockAddr -> ByteString
117encodeAddr (SockAddrInet port addr)
118 = S.runPut (S.putWord32host addr >> S.put (fromIntegral port :: Word16))
119encodeAddr (SockAddrInet6 port _ addr _)
120 = S.runPut (S.put addr >> S.put (fromIntegral port :: Word16))
121encodeAddr _ = B.empty
122
123genericArgs nodeid ro =
124 "id" .=! nodeid
125 .: "ro" .=? bool Nothing (Just (1 :: Int)) ro
126 .: endDict
127
128encodeError tid (Error ecode emsg) = encodeAny tid "e" (ecode,emsg) id
129encodeResponse tid rvals rip = encodeAny tid "r" rvals ("ip" .=? rip .:)
130encodeQuery tid qmeth qargs = encodeAny tid "q" qmeth ("a" .=! qargs .:)
131
132encodeAny tid key val aux = toDict $
133 aux $ key .=! val
134 .: "t" .=! tid
135 .: "y" .=! key
136 .: endDict
137
138parsePacket :: ByteString -> SockAddr -> Either String (Message BValue, NodeInfo)
139parsePacket bs addr = do pkt <- BE.decode bs
140 ni <- nodeInfo (msgOrigin pkt) addr
141 return (pkt, ni)
142
143encodePacket :: Message BValue -> NodeInfo -> (ByteString, SockAddr)
144encodePacket msg ni = ( toStrict $ BE.encode msg
145 , nodeAddr ni )
146
147newClient addr = do
148 udp <- udpTransport addr
149 let net = layerTransport parsePacket encodePacket udp
150 return net
151