diff options
Diffstat (limited to 'Mainline.hs')
-rw-r--r-- | Mainline.hs | 151 |
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 #-} | ||
7 | module Mainline where | ||
8 | |||
9 | import Control.Arrow | ||
10 | import Data.BEncode as BE | ||
11 | import Data.BEncode.BDict as BE | ||
12 | import Data.Bool | ||
13 | import Data.ByteArray | ||
14 | import Data.ByteString (ByteString) | ||
15 | import Data.ByteString as B | ||
16 | import Data.ByteString.Lazy (toStrict) | ||
17 | import Data.Data | ||
18 | import Data.IP | ||
19 | import qualified Data.Serialize as S | ||
20 | import Data.Typeable | ||
21 | import Data.Word | ||
22 | import Network.Address (Address, fromSockAddr, sockAddrPort, | ||
23 | toSockAddr, withPort) | ||
24 | import Network.QueryResponse | ||
25 | import Network.Socket | ||
26 | |||
27 | newtype NodeId = NodeId ByteString | ||
28 | deriving (Eq,Ord,Show,ByteArrayAccess, BEncode) | ||
29 | |||
30 | data NodeInfo = NodeInfo | ||
31 | { nodeId :: NodeId | ||
32 | , nodeIP :: IP | ||
33 | , nodePort :: PortNumber | ||
34 | } | ||
35 | |||
36 | nodeAddr :: NodeInfo -> SockAddr | ||
37 | nodeAddr (NodeInfo _ ip port) = toSockAddr ip `withPort` port | ||
38 | |||
39 | nodeInfo :: NodeId -> SockAddr -> Either String NodeInfo | ||
40 | nodeInfo 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. | ||
46 | data 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> | ||
62 | instance 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 | |||
75 | instance BEncode ErrorCode where | ||
76 | toBEncode = toBEncode . fromEnum | ||
77 | {-# INLINE toBEncode #-} | ||
78 | fromBEncode b = toEnum <$> fromBEncode b | ||
79 | {-# INLINE fromBEncode #-} | ||
80 | |||
81 | data 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 | |||
86 | newtype TransactionId = TransactionId Word16 | ||
87 | deriving (Eq, Ord, Show, BEncode) | ||
88 | |||
89 | newtype Method = Method ByteString | ||
90 | deriving (Eq, Ord, Show, BEncode) | ||
91 | |||
92 | data 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 | |||
103 | instance BE.BEncode (Message BValue) where | ||
104 | toBEncode = encodeMessage | ||
105 | fromBEncode = error "fromBEncode" | ||
106 | |||
107 | encodeMessage (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. | ||
111 | encodeMessage (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 | |||
116 | encodeAddr :: SockAddr -> ByteString | ||
117 | encodeAddr (SockAddrInet port addr) | ||
118 | = S.runPut (S.putWord32host addr >> S.put (fromIntegral port :: Word16)) | ||
119 | encodeAddr (SockAddrInet6 port _ addr _) | ||
120 | = S.runPut (S.put addr >> S.put (fromIntegral port :: Word16)) | ||
121 | encodeAddr _ = B.empty | ||
122 | |||
123 | genericArgs nodeid ro = | ||
124 | "id" .=! nodeid | ||
125 | .: "ro" .=? bool Nothing (Just (1 :: Int)) ro | ||
126 | .: endDict | ||
127 | |||
128 | encodeError tid (Error ecode emsg) = encodeAny tid "e" (ecode,emsg) id | ||
129 | encodeResponse tid rvals rip = encodeAny tid "r" rvals ("ip" .=? rip .:) | ||
130 | encodeQuery tid qmeth qargs = encodeAny tid "q" qmeth ("a" .=! qargs .:) | ||
131 | |||
132 | encodeAny tid key val aux = toDict $ | ||
133 | aux $ key .=! val | ||
134 | .: "t" .=! tid | ||
135 | .: "y" .=! key | ||
136 | .: endDict | ||
137 | |||
138 | parsePacket :: ByteString -> SockAddr -> Either String (Message BValue, NodeInfo) | ||
139 | parsePacket bs addr = do pkt <- BE.decode bs | ||
140 | ni <- nodeInfo (msgOrigin pkt) addr | ||
141 | return (pkt, ni) | ||
142 | |||
143 | encodePacket :: Message BValue -> NodeInfo -> (ByteString, SockAddr) | ||
144 | encodePacket msg ni = ( toStrict $ BE.encode msg | ||
145 | , nodeAddr ni ) | ||
146 | |||
147 | newClient addr = do | ||
148 | udp <- udpTransport addr | ||
149 | let net = layerTransport parsePacket encodePacket udp | ||
150 | return net | ||
151 | |||