summaryrefslogtreecommitdiff
path: root/DHTHandlers.hs
diff options
context:
space:
mode:
Diffstat (limited to 'DHTHandlers.hs')
-rw-r--r--DHTHandlers.hs182
1 files changed, 182 insertions, 0 deletions
diff --git a/DHTHandlers.hs b/DHTHandlers.hs
new file mode 100644
index 00000000..d98a5e60
--- /dev/null
+++ b/DHTHandlers.hs
@@ -0,0 +1,182 @@
1{-# LANGUAGE PatternSynonyms #-}
2module DHTHandlers where
3
4import DHTTransport
5import Network.QueryResponse as QR hiding (Client)
6import qualified Network.QueryResponse as QR (Client)
7import ToxCrypto
8import ToxMessage as Tox (PacketKind(..), pattern PingType, pattern GetNodesType, pattern DHTRequestType)
9
10import qualified Data.Wrapper.PSQInt as Int
11import Kademlia
12import Network.Address (WantIP (..), ipFamily, testIdBit)
13import qualified Network.DHT.Routing as R
14import TriadCommittee
15
16import Control.Monad
17import Control.Concurrent.STM
18import Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime)
19import Network.Socket
20import Data.IP
21import Data.Maybe
22import Data.Bits
23
24data TransactionId = TransactionId
25 { transactionKey :: Nonce8 -- ^ Used to lookup pending query.
26 , cryptoNonce :: Nonce24 -- ^ Used during the encryption layer.
27 }
28
29classify :: DHTMessage ((,) Nonce8) -> MessageClass String PacketKind TransactionId
30classify msg = mapMessage (\nonce24 (nonce8,_) -> go msg (TransactionId nonce8 nonce24)) msg
31 where
32 go (DHTPing {}) = IsQuery PingType
33 go (DHTGetNodes {}) = IsQuery GetNodesType
34 go (DHTPong {}) = IsResponse
35 go (DHTSendNodes {}) = IsResponse
36 go (DHTCookieRequest {}) = IsQuery (PacketKind 0x18)
37 go (DHTCookie {}) = IsResponse
38 go (DHTDHTRequest {}) = IsQuery DHTRequestType
39
40data Routing = Routing
41 { tentativeId :: NodeInfo
42 , sched4 :: !( TVar (Int.PSQ POSIXTime) )
43 , routing4 :: !( TVar (R.BucketList NodeInfo) )
44 , committee4 :: TriadCommittee NodeId SockAddr
45 , sched6 :: !( TVar (Int.PSQ POSIXTime) )
46 , routing6 :: !( TVar (R.BucketList NodeInfo) )
47 , committee6 :: TriadCommittee NodeId SockAddr
48 }
49
50
51-- TODO: This should cover more cases
52isLocal (IPv6 ip6) = (ip6 == toEnum 0)
53isLocal (IPv4 ip4) = (ip4 == toEnum 0)
54
55isGlobal = not . isLocal
56
57prefer4or6 :: NodeInfo -> Maybe WantIP -> WantIP
58prefer4or6 addr iptyp = fromMaybe (ipFamily $ nodeIP addr) iptyp
59
60toxSpace :: R.KademliaSpace NodeId NodeInfo
61toxSpace = R.KademliaSpace
62 { R.kademliaLocation = nodeId
63 , R.kademliaTestBit = testIdBit
64 , R.kademliaXor = xor
65 }
66
67
68pingH :: NodeInfo -> Ping -> IO Pong
69pingH _ Ping = return Pong
70
71getNodesH :: Routing -> NodeInfo -> GetNodes -> IO SendNodes
72getNodesH routing addr (GetNodes nid) = do
73 let preferred = prefer4or6 addr Nothing
74
75 (append4,append6) <- atomically $ do
76 ni4 <- R.thisNode <$> readTVar (routing4 routing)
77 ni6 <- R.thisNode <$> readTVar (routing6 routing)
78 return $ case ipFamily (nodeIP addr) of
79 Want_IP4 | isGlobal (nodeIP ni6) -> (id, (++ [ni6]))
80 Want_IP6 | isGlobal (nodeIP ni4) -> ((++ [ni4]), id)
81 _ -> (id, id)
82 ks <- go append4 $ routing4 routing
83 ks6 <- go append6 $ routing6 routing
84 let (ns1,ns2) = case preferred of Want_IP6 -> (ks6,ks)
85 Want_IP4 -> (ks,ks6)
86 return $ SendNodes
87 $ if null ns2 then ns1
88 else take 4 (take 3 ns1 ++ ns2)
89 where
90 go f var = f . R.kclosest toxSpace k nid <$> atomically (readTVar var)
91
92 k = 4
93
94type Message = DHTMessage ((,) Nonce8)
95
96type Client = QR.Client String PacketKind TransactionId NodeInfo Message
97
98
99wrapAssym :: TransactionId -> NodeInfo -> NodeInfo -> dta -> Assym (Nonce8,dta)
100wrapAssym (TransactionId n8 n24) src dst dta = Assym
101 { senderKey = let NodeId pubkey = nodeId src in pubkey
102 , assymNonce = n24
103 , assymData = (n8, dta)
104 }
105
106serializer :: PacketKind
107 -> (Assym (Nonce8,ping) -> Message)
108 -> (Message -> Maybe (Assym (Nonce8,pong)))
109 -> MethodSerializer TransactionId NodeInfo Message PacketKind ping (Maybe pong)
110serializer pktkind mkping mkpong = MethodSerializer
111 { methodTimeout = 5
112 , method = pktkind
113 -- wrapQuery :: tid -> addr -> addr -> qry -> x
114 , wrapQuery = \tid src dst ping -> mkping $ wrapAssym tid src dst ping
115 -- unwrapResponse :: x -> b
116 , unwrapResponse = fmap (snd . assymData) . mkpong
117 }
118
119
120unpong :: Message -> Maybe (Assym (Nonce8,Pong))
121unpong (DHTPong assym) = Just assym
122unpong _ = Nothing
123
124ping :: Client -> NodeInfo -> IO Bool
125ping client addr = do
126 reply <- QR.sendQuery client (serializer PingType DHTPing unpong) Ping addr
127 maybe (return False) (\Pong -> return True) $ join reply
128
129unsendNodes :: Message -> Maybe (Assym (Nonce8,SendNodes))
130unsendNodes (DHTSendNodes assym) = Just assym
131unsendNodes _ = Nothing
132
133unwrapNodes :: SendNodes -> ( [NodeInfo], [NodeInfo], () )
134unwrapNodes (SendNodes ns) = (ns,ns,())
135
136getNodes :: Client -> NodeId -> NodeInfo -> IO (Maybe ([NodeInfo],[NodeInfo],()))
137getNodes client nid addr = do
138 reply <- QR.sendQuery client (serializer GetNodesType DHTGetNodes unsendNodes) (GetNodes nid) addr
139 return $ fmap unwrapNodes $ join reply
140
141updateRouting :: Client -> Routing -> NodeInfo -> Message -> IO ()
142updateRouting client routing naddr msg = do
143 -- hPutStrLn stderr $ "updateRouting "++show typ
144 -- TODO: check msg type
145 case prefer4or6 naddr Nothing of
146 Want_IP4 -> updateTable client naddr (routing4 routing) (committee4 routing) (sched4 routing)
147 Want_IP6 -> updateTable client naddr (routing6 routing) (committee6 routing) (sched6 routing)
148
149updateTable :: Client -> NodeInfo -> TVar (R.BucketList NodeInfo) -> TriadCommittee NodeId SockAddr -> TVar (Int.PSQ POSIXTime) -> IO ()
150updateTable client naddr tbl committee sched = do
151 self <- atomically $ R.thisNode <$> readTVar tbl
152 when (nodeIP self /= nodeIP naddr) $ do
153 -- TODO: IP address vote?
154 insertNode (toxKademlia client committee tbl sched) naddr
155
156toxKademlia :: Client -> TriadCommittee NodeId SockAddr -> TVar (R.BucketList NodeInfo) -> TVar (Int.PSQ POSIXTime) -> Kademlia NodeId NodeInfo
157toxKademlia client committee var sched
158 = Kademlia quietInsertions
159 toxSpace
160 (vanillaIO var $ ping client)
161 { tblTransition = \tr -> do
162 io1 <- transitionCommittee committee tr
163 io2 <- touchBucket toxSpace (15*60) var sched tr
164 return $ do
165 io1 >> io2
166 {-
167 hPutStrLn stderr $ unwords
168 [ show (transitionedTo tr)
169 , show (transitioningNode tr)
170 ]
171 -}
172 return ()
173 }
174
175transitionCommittee :: TriadCommittee NodeId SockAddr -> RoutingTransition NodeInfo -> STM (IO ())
176transitionCommittee committee (RoutingTransition ni Stranger) = do
177 delVote committee (nodeId ni)
178 return $ do
179 -- hPutStrLn stderr $ "delVote "++show (nodeId ni)
180 return ()
181transitionCommittee committee _ = return $ return ()
182