diff options
author | joe <joe@jerkface.net> | 2017-10-12 05:41:09 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2017-10-12 05:41:09 -0400 |
commit | 37a7fa4978f89072d9231bcc9bd0848bb52c676c (patch) | |
tree | 48a2a934e5da1c6754915d5ad27417f604cbfd04 | |
parent | 3024b35b05d7f520666af20ced8d1f3080837bb2 (diff) |
WIP Onion routing.
-rw-r--r-- | OnionRouter.hs | 361 | ||||
-rw-r--r-- | examples/dhtd.hs | 19 | ||||
-rw-r--r-- | src/Data/Word64Map.hs | 62 | ||||
-rw-r--r-- | src/Network/BitTorrent/MainlineDHT.hs | 4 | ||||
-rw-r--r-- | src/Network/QueryResponse.hs | 47 | ||||
-rw-r--r-- | src/Network/Tox.hs | 44 | ||||
-rw-r--r-- | src/Network/Tox/DHT/Handlers.hs | 26 | ||||
-rw-r--r-- | src/Network/Tox/NodeId.hs | 5 | ||||
-rw-r--r-- | src/Network/Tox/Onion/Handlers.hs | 81 | ||||
-rw-r--r-- | src/Network/Tox/Onion/Transport.hs | 61 |
10 files changed, 617 insertions, 93 deletions
diff --git a/OnionRouter.hs b/OnionRouter.hs index 15304221..7a48aaab 100644 --- a/OnionRouter.hs +++ b/OnionRouter.hs | |||
@@ -1,5 +1,6 @@ | |||
1 | module OnionRouter where | 1 | module OnionRouter where |
2 | 2 | ||
3 | import Control.Concurrent.Lifted.Instrument | ||
3 | import Crypto.Tox | 4 | import Crypto.Tox |
4 | import Network.Kademlia | 5 | import Network.Kademlia |
5 | import Network.Kademlia.Routing | 6 | import Network.Kademlia.Routing |
@@ -7,25 +8,367 @@ import Network.QueryResponse | |||
7 | import Network.Tox.NodeId | 8 | import Network.Tox.NodeId |
8 | import Network.Tox.Onion.Transport | 9 | import Network.Tox.Onion.Transport |
9 | 10 | ||
10 | import Network.Socket (SockAddr) | 11 | import Control.Arrow |
11 | import Control.Concurrent.STM | 12 | import Control.Concurrent.STM |
13 | import Control.Monad | ||
14 | import Crypto.PubKey.Curve25519 | ||
15 | import Crypto.Random | ||
16 | import Data.Bits | ||
17 | import qualified Data.ByteString as B | ||
18 | import qualified Data.HashMap.Strict as HashMap | ||
19 | ;import Data.HashMap.Strict (HashMap) | ||
20 | import qualified Data.IntMap as IntMap | ||
21 | ;import Data.IntMap (IntMap) | ||
22 | import Data.Maybe | ||
23 | import qualified Data.Serialize as S | ||
24 | import Data.Typeable | ||
25 | import Data.Word | ||
26 | import qualified Data.Word64Map as W64 | ||
27 | ;import Data.Word64Map (Word64Map, fitsInInt) | ||
28 | import Network.Socket | ||
29 | import System.Endian | ||
30 | import System.IO | ||
12 | 31 | ||
13 | newtype RouteId = RouteId Int | 32 | newtype RouteId = RouteId Int |
14 | deriving Show | 33 | deriving Show |
15 | 34 | ||
16 | data OnionRouter | 35 | data OnionRouter = OnionRouter |
36 | { pendingQueries :: TVar (Word64Map NodeId) | ||
37 | , routeMap :: TVar (IntMap RouteRecord) | ||
38 | , trampolineNodes :: TVar (IntMap NodeInfo) | ||
39 | , trampolineIds :: TVar (HashMap NodeId Int) | ||
40 | , trampolineCount :: TVar Int | ||
41 | , onionDRG :: TVar ChaChaDRG | ||
42 | , routeThread :: ThreadId | ||
43 | , pendingRoutes :: IntMap (TVar Bool) | ||
44 | , routeLog :: TChan String | ||
45 | } | ||
46 | |||
47 | data RouteRecord = RouteRecord | ||
48 | { storedRoute :: OnionRoute | ||
49 | , responseCount :: Int | ||
50 | , timeoutCount :: Int | ||
51 | } | ||
52 | |||
53 | -- Onion paths have different timeouts depending on whether the path is | ||
54 | -- confirmed or unconfirmed. Unconfirmed paths (paths that core has never | ||
55 | -- received any responses from) have a timeout of 4 seconds with 2 tries before | ||
56 | -- they are deemed non working. This is because, due to network conditions, | ||
57 | -- there may be a large number of newly created paths that do not work and so | ||
58 | -- trying them a lot would make finding a working path take much longer. The | ||
59 | -- timeout for a confirmed path (from which a response was received) is 10 | ||
60 | -- seconds with 4 tries without a response. A confirmed path has a maximum | ||
61 | -- lifetime of 1200 seconds to make possible deanonimization attacks more | ||
62 | -- difficult. | ||
63 | timeoutForRoute :: RouteRecord -> Int | ||
64 | timeoutForRoute RouteRecord{ responseCount = 0 } = 4000000 | ||
65 | timeoutForRoute RouteRecord{ responseCount = _ } = 10000000 | ||
66 | |||
67 | freshRoute :: OnionRoute -> RouteRecord | ||
68 | freshRoute r = RouteRecord | ||
69 | { storedRoute = r | ||
70 | , responseCount = 0 | ||
71 | , timeoutCount = 0 | ||
72 | } | ||
73 | |||
74 | gotResponse :: RouteRecord -> RouteRecord | ||
75 | gotResponse rr = rr | ||
76 | { responseCount = succ $ responseCount rr | ||
77 | , timeoutCount = 0 | ||
78 | } | ||
79 | |||
80 | gotTimeout :: RouteRecord -> RouteRecord | ||
81 | gotTimeout rr = rr | ||
82 | { timeoutCount = succ $ timeoutCount rr | ||
83 | } | ||
84 | |||
85 | data RouteEvent = BuildRoute RouteId | ||
17 | 86 | ||
18 | newOnionRouter :: IO OnionRouter | 87 | newOnionRouter :: IO OnionRouter |
19 | newOnionRouter = return _todo | 88 | newOnionRouter = do |
89 | drg0 <- drgNew | ||
90 | or <- atomically $ do | ||
91 | chan <- newTChan | ||
92 | drg <- newTVar drg0 | ||
93 | forM_ [0..11] $ \n -> writeTChan chan $ BuildRoute (RouteId n) | ||
94 | pq <- newTVar W64.empty | ||
95 | rm <- newTVar IntMap.empty | ||
96 | tn <- newTVar IntMap.empty | ||
97 | ti <- newTVar HashMap.empty | ||
98 | tc <- newTVar 0 | ||
99 | vs <- sequence $ replicate 12 (newTVar True) | ||
100 | rlog <- newTChan | ||
101 | return OnionRouter | ||
102 | { pendingRoutes = IntMap.fromList $ zip [0..11] vs | ||
103 | , onionDRG = drg | ||
104 | , pendingQueries = pq | ||
105 | , routeMap = rm | ||
106 | , trampolineNodes = tn | ||
107 | , trampolineIds = ti | ||
108 | , trampolineCount = tc | ||
109 | , routeLog = rlog | ||
110 | , routeThread = error "Failed to invoke forkRouteBuilder" | ||
111 | } | ||
112 | return or | ||
113 | |||
114 | forkRouteBuilder :: OnionRouter -> (NodeId -> NodeInfo -> IO [NodeInfo]) -> IO OnionRouter | ||
115 | forkRouteBuilder or getnodes = do | ||
116 | tid <- forkIO $ do | ||
117 | me <- myThreadId | ||
118 | labelThread me "OnionRouter" | ||
119 | forever $ do | ||
120 | let checkRebuild rid want_build stm = flip orElse stm $ do | ||
121 | readTVar want_build >>= check | ||
122 | writeTVar want_build False | ||
123 | return $ BuildRoute $ RouteId rid | ||
124 | io <- atomically $ | ||
125 | (readTChan (routeLog or) >>= return . hPutStrLn stderr) | ||
126 | `orElse` | ||
127 | (IntMap.foldrWithKey checkRebuild retry (pendingRoutes or) | ||
128 | >>= return . handleEvent getnodes or { routeThread = me }) | ||
129 | io | ||
130 | return or { routeThread = tid } | ||
131 | |||
132 | generateNodeId :: MonadRandom m => m NodeId | ||
133 | generateNodeId = either (error "unable to make random nodeid") | ||
134 | id | ||
135 | . S.decode <$> getRandomBytes 32 | ||
136 | |||
137 | distinct3by :: Eq t => (a -> t) -> a -> a -> a -> Bool | ||
138 | distinct3by f a b c = f a /= f b && f b /= f c && f c /= f a | ||
139 | |||
140 | -- The two integer functions below take an [inclusive,inclusive] range. | ||
141 | randomR :: (DRG g, Integral a) => (a, a) -> g -> (a, g) | ||
142 | randomR (l,h) = randomIvalInteger (toInteger l, toInteger h) | ||
143 | |||
144 | next :: DRG g => g -> (Int,g) | ||
145 | next g = withDRG g $ do bs <- getRandomBytes $ if fitsInInt (Proxy :: Proxy Word64) | ||
146 | then 8 | ||
147 | else 4 | ||
148 | either (return . error) return $ S.decode bs | ||
149 | |||
150 | randomIvalInteger :: (DRG g, Num a) => (Integer, Integer) -> g -> (a, g) | ||
151 | randomIvalInteger (l,h) rng | ||
152 | | l > h = randomIvalInteger (h,l) rng | ||
153 | | otherwise = case (f 1 0 rng) of (v, rng') -> (fromInteger (l + v `mod` k), rng') | ||
154 | where | ||
155 | (genlo, genhi) = (minBound :: Int, maxBound :: Int) -- genRange :: RandomGen g => g -> (Int, Int) | ||
156 | b = fromIntegral genhi - fromIntegral genlo + 1 | ||
157 | |||
158 | -- Probabilities of the most likely and least likely result | ||
159 | -- will differ at most by a factor of (1 +- 1/q). Assuming the RandomGen | ||
160 | -- is uniform, of course | ||
161 | |||
162 | -- On average, log q / log b more random values will be generated | ||
163 | -- than the minimum | ||
164 | q = 1000 | ||
165 | k = h - l + 1 | ||
166 | magtgt = k * q | ||
167 | |||
168 | -- generate random values until we exceed the target magnitude | ||
169 | f mag v g | mag >= magtgt = (v, g) | ||
170 | | otherwise = v' `seq`f (mag*b) v' g' where | ||
171 | (x,g') = next g -- next :: RandomGen g => g -> (Int, g) | ||
172 | v' = (v * b + (fromIntegral x - fromIntegral genlo)) | ||
173 | |||
174 | selectTrampolines :: OnionRouter -> STM [NodeInfo] | ||
175 | selectTrampolines or = do | ||
176 | cnt <- readTVar (trampolineCount or) | ||
177 | drg0 <- readTVar (onionDRG or) | ||
178 | ts <- readTVar (trampolineNodes or) | ||
179 | let (a, drg1) = randomR (0,cnt - 1) drg0 | ||
180 | (b0, drg2) = randomR (0,cnt - 2) drg1 | ||
181 | (c0, drg ) = randomR (0,cnt - 3) drg2 | ||
182 | b | b0 < a = b0 | ||
183 | | otherwise = b0 + 1 | ||
184 | c1 | c0 < a = c0 | ||
185 | | otherwise = c0 + 1 | ||
186 | c | c1 < b = c1 | ||
187 | | otherwise = c1 + 1 | ||
188 | ns = mapMaybe (\n -> IntMap.lookup n ts) [a,b,c] | ||
189 | ns' <- case ns of | ||
190 | [an,bn,cn] -> do check $ distinct3by nodeClass an bn cn | ||
191 | return ns | ||
192 | _ -> retry | ||
193 | writeTVar (onionDRG or) drg | ||
194 | return ns' | ||
195 | |||
196 | handleEvent :: (NodeId -> NodeInfo -> IO [NodeInfo]) -> OnionRouter -> RouteEvent -> IO () | ||
197 | handleEvent getnodes or e@(BuildRoute (RouteId rid)) = do | ||
198 | hPutStrLn stderr $ "ONION Rebuilding RouteId " ++ show rid | ||
199 | mb <- join . atomically $ do | ||
200 | ts <- selectTrampolines or | ||
201 | drg <- readTVar (onionDRG or) | ||
202 | [av,bv,cv] <- sequence $ replicate 3 (newTVar Nothing) | ||
203 | let (getr, drg') = withDRG drg $ do | ||
204 | n24 <- Nonce24 <$> getRandomBytes 24 | ||
205 | asec <- generateSecretKey -- Three aliases | ||
206 | bsec <- generateSecretKey | ||
207 | csec <- generateSecretKey | ||
208 | aq <- generateNodeId -- Three queries | ||
209 | bq <- generateNodeId | ||
210 | cq <- generateNodeId | ||
211 | sel <- B.head <$> getRandomBytes 1 -- Three two-bit result selectors (6 bits) | ||
212 | let asel = sel .&. 0x3 | ||
213 | bsel = shiftR sel 2 .&. 0x3 | ||
214 | csel = shiftR sel 4 .&. 0x3 | ||
215 | sendq s q ni = listToMaybe . drop (fromIntegral s) <$> getnodes q ni | ||
216 | sendqs = do | ||
217 | forkIO $ sendq asel aq (ts !! 0) >>= atomically . writeTVar av . Just | ||
218 | forkIO $ sendq bsel bq (ts !! 1) >>= atomically . writeTVar bv . Just | ||
219 | forkIO $ sendq csel cq (ts !! 2) >>= atomically . writeTVar cv . Just | ||
220 | atomically $ do -- Wait for all 3 results. | ||
221 | rs <- catMaybes <$> sequence [readTVar av,readTVar bv,readTVar cv] | ||
222 | case rs of [_,_,_] -> do | ||
223 | return $ catMaybes rs | ||
224 | -- self <- IntMap.lookup (-1) <$> readTVar (trampolineNodes or) | ||
225 | -- return $ maybe (catMaybes rs) (\x -> [x,x,x]) self | ||
226 | _ -> retry | ||
227 | return $ do | ||
228 | nodes <- sendqs | ||
229 | hPutStr stderr $ unlines | ||
230 | [ "ONION trampolines: " ++ show ts | ||
231 | , "ONION query results: " ++ show nodes ] | ||
232 | case nodes of | ||
233 | [a,b,c] -> do -- | distinct3by nodeClass a b c -> do | ||
234 | return $ Just OnionRoute | ||
235 | { routeNonce = n24 | ||
236 | , routeAliasA = asec | ||
237 | , routeAliasB = bsec | ||
238 | , routeAliasC = csec | ||
239 | , routeNodeA = a | ||
240 | , routeNodeB = b | ||
241 | , routeNodeC = c | ||
242 | } | ||
243 | _ -> return Nothing | ||
244 | writeTVar (onionDRG or) drg' | ||
245 | return $ getr | ||
246 | atomically $ maybe (writeTVar (pendingRoutes or IntMap.! rid) True) | ||
247 | (\r -> modifyTVar' (routeMap or) | ||
248 | (IntMap.insert rid $ freshRoute r)) | ||
249 | mb | ||
250 | case mb of | ||
251 | Just _ -> hPutStrLn stderr $ "ONION Finished RouteId " ++ show rid | ||
252 | Nothing -> hPutStrLn stderr $ "ONION Failed RouteId " ++ show rid | ||
253 | |||
254 | routeId :: Nonce8 -> RouteId | ||
255 | routeId (Nonce8 w8) = RouteId $ mod (fromIntegral w8) 12 | ||
20 | 256 | ||
21 | lookupSender :: OnionRouter -> SockAddr -> Nonce8 -> IO (Maybe (NodeInfo, RouteId)) | 257 | lookupSender :: OnionRouter -> SockAddr -> Nonce8 -> IO (Maybe (NodeInfo, RouteId)) |
22 | lookupSender _ _ _ = return Nothing -- todo | 258 | lookupSender or saddr (Nonce8 w8) = do |
259 | result <- atomically $ do | ||
260 | ks <- readTVar (pendingQueries or) | ||
261 | let r = W64.lookup w8 ks | ||
262 | writeTChan (routeLog or) $ "ONION lookupSender " ++ unwords [show w8, "->", show r] | ||
263 | return r | ||
264 | return $ do | ||
265 | nid <- result | ||
266 | ni <- either (const Nothing) Just $ nodeInfo nid saddr | ||
267 | Just (ni, routeId (Nonce8 w8)) | ||
23 | 268 | ||
24 | lookupRoute :: OnionRouter -> NodeInfo -> RouteId -> IO (Maybe OnionRoute) | 269 | lookupRoute :: OnionRouter -> NodeInfo -> RouteId -> IO (Maybe OnionRoute) |
25 | lookupRoute _ _ _ = return Nothing -- todo | 270 | lookupRoute or ni (RouteId rid) = do |
271 | mb <- atomically $ IntMap.lookup rid <$> readTVar (routeMap or) | ||
272 | return $ storedRoute <$> mb | ||
273 | |||
274 | lookupTimeout :: OnionRouter -> Nonce8 -> OnionDestination r -> STM (OnionDestination RouteId, Int) | ||
275 | lookupTimeout or n8 (OnionDestination ni Nothing) = do | ||
276 | let RouteId rid = routeId n8 | ||
277 | mrr <- IntMap.lookup rid <$> readTVar (routeMap or) | ||
278 | readTVar (routeMap or) >>= \rm -> writeTChan (routeLog or) $ "ONION lookupTimeout " ++ unwords [show rid,show (IntMap.keys rm)] | ||
279 | case mrr of | ||
280 | Just rr -> return ( OnionDestination ni (Just $ routeId n8), timeoutForRoute rr) | ||
281 | Nothing -> return ( OnionDestination ni Nothing , 0 ) | ||
282 | |||
283 | hookQueries :: OnionRouter -> (tid -> Nonce8) | ||
284 | -> TransactionMethods d tid (OnionDestination r) x | ||
285 | -> TransactionMethods d tid (OnionDestination r) x | ||
286 | hookQueries or t8 tmethods = TransactionMethods | ||
287 | { dispatchRegister = \mvar od d -> do -- :: MVar x -> d -> STM (tid, d) | ||
288 | (tid,d') <- dispatchRegister tmethods mvar od d | ||
289 | let Nonce8 w8 = t8 tid | ||
290 | ni = onionNodeInfo od | ||
291 | modifyTVar' (pendingQueries or) (W64.insert w8 (nodeId ni)) | ||
292 | writeTChan (routeLog or) $ "ONION query add " ++ unwords [ show w8, ":=", show ni ] | ||
293 | return (tid,d') | ||
294 | , dispatchResponse = \tid x d -> do -- :: tid -> x -> d -> STM (d, IO ()) | ||
295 | let (Nonce8 w8, RouteId rid) = (id &&& routeId) $ t8 tid | ||
296 | modifyTVar' (pendingQueries or) (W64.delete w8) | ||
297 | modifyTVar' (routeMap or) (IntMap.adjust gotResponse rid) | ||
298 | writeTChan (routeLog or) $ "ONION query del " ++ show w8 | ||
299 | dispatchResponse tmethods tid x d | ||
300 | , dispatchCancel = \tid d -> do -- :: tid -> d -> STM d | ||
301 | let (Nonce8 w8, RouteId rid) = (id &&& routeId) $ t8 tid | ||
302 | modifyTVar' (pendingQueries or) (W64.delete w8) | ||
303 | writeTChan (routeLog or) $ "ONION query can " ++ show w8 | ||
304 | modifyTVar' (routeMap or) (IntMap.adjust gotTimeout rid) | ||
305 | let expireRoute = writeTVar (pendingRoutes or IntMap.! rid) True | ||
306 | rr <- IntMap.lookup rid <$> readTVar (routeMap or) | ||
307 | case rr of | ||
308 | Just RouteRecord{ responseCount=0 | ||
309 | , timeoutCount = c } | c >= 2 -> expireRoute | ||
310 | Just RouteRecord{ timeoutCount = c } | c >= 4 -> expireRoute | ||
311 | _ -> return () | ||
312 | dispatchCancel tmethods tid d | ||
313 | } | ||
314 | |||
315 | |||
316 | addtramp :: NodeInfo -> Maybe (HashMap NodeId NodeInfo) -> Maybe (HashMap NodeId NodeInfo) | ||
317 | addtramp ni Nothing = Just $ HashMap.singleton (nodeId ni) ni | ||
318 | addtramp ni (Just m) = Just $ HashMap.insert (nodeId ni) ni m | ||
319 | |||
320 | deltramp :: NodeInfo -> Maybe (HashMap NodeId v) -> Maybe (HashMap NodeId v) | ||
321 | deltramp ni Nothing = Nothing | ||
322 | deltramp ni (Just m) = case HashMap.delete (nodeId ni) m of | ||
323 | m' | HashMap.null m' -> Nothing | ||
324 | m' -> Just m' | ||
325 | |||
326 | hookBucketList :: KademliaSpace NodeId NodeInfo -> TVar (BucketList NodeInfo) -> OnionRouter -> RoutingTransition NodeInfo -> STM () | ||
327 | hookBucketList kademlia bkts0 or (RoutingTransition ni Accepted) = do | ||
328 | s <- do | ||
329 | drg0 <- readTVar (onionDRG or) | ||
330 | bkts <- readTVar bkts0 | ||
331 | let antibias = 2 ^ bucketNumber kademlia (nodeId ni) bkts | ||
332 | (s,drg) = randomR (0,antibias - 1) drg0 | ||
333 | writeTVar (onionDRG or) drg | ||
334 | -- let self = (thisNode bkts) { nodeIP = read "127.0.0.1" } | ||
335 | -- modifyTVar' (trampolineNodes or) (IntMap.insert (-1) self) | ||
336 | return s | ||
337 | -- debias via stochastic filter | ||
338 | when (s == 0) $ do | ||
339 | ns <- readTVar (trampolineIds or) | ||
340 | case HashMap.lookup (nodeId ni) ns of | ||
341 | Just _ -> return () | ||
342 | Nothing -> do | ||
343 | cnt <- readTVar (trampolineCount or) | ||
344 | writeTChan (routeLog or) $ "ONION trampoline Accepted " ++ unwords [show cnt, show ni] | ||
345 | modifyTVar' (trampolineIds or) (HashMap.insert (nodeId ni) cnt) | ||
346 | modifyTVar' (trampolineNodes or) (IntMap.insert cnt ni) | ||
347 | writeTVar (trampolineCount or) (succ cnt) | ||
348 | hookBucketList _ _ or (RoutingTransition ni Stranger) = do | ||
349 | ns <- readTVar (trampolineIds or) | ||
350 | case HashMap.lookup (nodeId ni) ns of | ||
351 | Just n -> do writeTVar (trampolineIds or) (HashMap.delete (nodeId ni) ns) | ||
352 | cnt <- pred <$> readTVar (trampolineCount or) | ||
353 | writeTVar (trampolineCount or) cnt | ||
354 | if n == cnt | ||
355 | then modifyTVar' (trampolineNodes or) (IntMap.delete n) | ||
356 | else do lastnode <- (IntMap.! cnt) <$> readTVar (trampolineNodes or) | ||
357 | modifyTVar' (trampolineNodes or) (IntMap.insert n lastnode . IntMap.delete cnt) | ||
358 | writeTChan (routeLog or) $ "ONION trampoline Stranger " ++ unwords [show n,show ni] | ||
359 | Nothing -> return () | ||
360 | hookBucketList _ _ _ _ = return () -- ignore Applicant event. | ||
361 | |||
362 | newtype IPClass = IPClass Word32 | ||
363 | deriving Eq | ||
364 | |||
365 | ipkey :: IPClass -> Int | ||
366 | ipkey (IPClass k) = fromIntegral k | ||
26 | 367 | ||
27 | hookQueries :: OnionRouter -> (tid -> Nonce8) -> TransactionMethods d tid x -> TransactionMethods d tid x | 368 | nodeClass :: NodeInfo -> IPClass |
28 | hookQueries _ n8 tmethods = tmethods -- todo | 369 | nodeClass = ipClass. nodeAddr |
29 | 370 | ||
30 | hookBucketList :: OnionRouter -> RoutingTransition ni -> STM () | 371 | ipClass :: SockAddr -> IPClass |
31 | hookBucketList _ _ = return () -- todo | 372 | ipClass (SockAddrInet _ addr) = IPClass (fromBE32 addr .&. 0xFF000000) |
373 | ipClass (SockAddrInet6 _ _ (hiword,_,_,_) _) = IPClass hiword | ||
374 | ipClass _ = IPClass 0 -- unreachable. | ||
diff --git a/examples/dhtd.hs b/examples/dhtd.hs index 0b208362..1ab2778a 100644 --- a/examples/dhtd.hs +++ b/examples/dhtd.hs | |||
@@ -45,6 +45,7 @@ import Control.Concurrent.Lifted | |||
45 | import GHC.Conc (labelThread) | 45 | import GHC.Conc (labelThread) |
46 | #endif | 46 | #endif |
47 | 47 | ||
48 | import Crypto.Tox (zeros32) | ||
48 | import Network.UPNP as UPNP | 49 | import Network.UPNP as UPNP |
49 | import Network.Address hiding (NodeId, NodeInfo(..)) | 50 | import Network.Address hiding (NodeId, NodeInfo(..)) |
50 | import Network.Kademlia.Search | 51 | import Network.Kademlia.Search |
@@ -68,6 +69,7 @@ import Data.Ord | |||
68 | import Data.Time.Clock.POSIX | 69 | import Data.Time.Clock.POSIX |
69 | import qualified Network.Tox.DHT.Transport as Tox | 70 | import qualified Network.Tox.DHT.Transport as Tox |
70 | import qualified Network.Tox.DHT.Handlers as Tox | 71 | import qualified Network.Tox.DHT.Handlers as Tox |
72 | import qualified Network.Tox.Onion.Transport as Tox | ||
71 | import qualified Network.Tox.Onion.Handlers as Tox | 73 | import qualified Network.Tox.Onion.Handlers as Tox |
72 | import Data.Typeable | 74 | import Data.Typeable |
73 | 75 | ||
@@ -99,7 +101,7 @@ data DHTQuery nid ni = forall addr r tok. | |||
99 | , Typeable r | 101 | , Typeable r |
100 | )=> DHTQuery | 102 | )=> DHTQuery |
101 | { qsearch :: Search nid addr tok ni r | 103 | { qsearch :: Search nid addr tok ni r |
102 | , qhandler :: ni -> nid -> IO ([ni], [r], tok) | 104 | , qhandler :: ni -> nid -> IO ([ni], [r], tok) -- ^ Invoked on local node, when there is no query destination. |
103 | , qshowR :: r -> String | 105 | , qshowR :: r -> String |
104 | , qshowTok :: tok -> Maybe String | 106 | , qshowTok :: tok -> Maybe String |
105 | } | 107 | } |
@@ -585,8 +587,21 @@ main = do | |||
585 | (\ni -> fmap Tox.unwrapNodes | 587 | (\ni -> fmap Tox.unwrapNodes |
586 | . Tox.getNodesH (Tox.toxRouting tox) ni | 588 | . Tox.getNodesH (Tox.toxRouting tox) ni |
587 | . Tox.GetNodes) | 589 | . Tox.GetNodes) |
588 | show | 590 | show -- NodeInfo |
589 | (const Nothing)) | 591 | (const Nothing)) |
592 | , ("toxid", DHTQuery (Tox.toxidSearch (Tox.onionTimeout tox) $ Tox.toxOnion tox) | ||
593 | -- qhandler :: ni -> nid -> IO ([ni], [r], tok) | ||
594 | (\ni nid -> | ||
595 | -- _todo :: IO Tox.AnnounceResponse | ||
596 | -- -> IO ([Tox.NodeInfo], [Crypto.PubKey.Curve25519.PublicKey], b0) | ||
597 | Tox.unwrapAnnounceResponse | ||
598 | <$> Tox.announceH (Tox.toxRouting tox) | ||
599 | (Tox.toxTokens tox) | ||
600 | (Tox.toxAnnouncedKeys tox) | ||
601 | (Tox.OnionDestination ni Nothing) | ||
602 | (Tox.AnnounceRequest zeros32 nid Tox.zeroID)) | ||
603 | show -- PublicKey | ||
604 | (const Nothing)) -- TODO: show token | ||
590 | ] | 605 | ] |
591 | , dhtParseId = readEither :: String -> Either String Tox.NodeId | 606 | , dhtParseId = readEither :: String -> Either String Tox.NodeId |
592 | , dhtSearches = toxSearches | 607 | , dhtSearches = toxSearches |
diff --git a/src/Data/Word64Map.hs b/src/Data/Word64Map.hs new file mode 100644 index 00000000..9e93c8c8 --- /dev/null +++ b/src/Data/Word64Map.hs | |||
@@ -0,0 +1,62 @@ | |||
1 | {-# LANGUAGE RankNTypes #-} | ||
2 | {-# LANGUAGE ScopedTypeVariables #-} | ||
3 | {-# LANGUAGE UnboxedTuples #-} | ||
4 | module Data.Word64Map where | ||
5 | |||
6 | import Data.Bits | ||
7 | import qualified Data.IntMap as IntMap | ||
8 | ;import Data.IntMap (IntMap) | ||
9 | import Data.Typeable | ||
10 | import Data.Word | ||
11 | |||
12 | -- | Since 'Int' may be 32 or 64 bits, this function is provided as a | ||
13 | -- convenience to test if an integral type, such as 'Data.Word.Word64', can be | ||
14 | -- safely transformed into an 'Int' for use with 'IntMap'. | ||
15 | -- | ||
16 | -- Returns 'True' if the proxied type can be losslessly converted to 'Int' using | ||
17 | -- 'fromIntegral'. | ||
18 | fitsInInt :: forall proxy word. (Bounded word, Integral word) => proxy word -> Bool | ||
19 | fitsInInt proxy = (original == casted) | ||
20 | where | ||
21 | original = div maxBound 2 :: word | ||
22 | casted = fromIntegral (fromIntegral original :: Int) :: word | ||
23 | |||
24 | newtype Word64Map a = Word64Map (IntMap (IntMap a)) | ||
25 | |||
26 | empty :: Word64Map a | ||
27 | empty = Word64Map IntMap.empty | ||
28 | |||
29 | -- Warning: This function assumes an 'Int' is either 64 or 32 bits. | ||
30 | keyFrom64 :: Word64 -> (# Int,Int #) | ||
31 | keyFrom64 w8 = | ||
32 | if fitsInInt (Proxy :: Proxy Word64) | ||
33 | then (# fromIntegral w8 , 0 #) | ||
34 | else (# fromIntegral (w8 `shiftR` 32), fromIntegral w8 #) | ||
35 | {-# INLINE keyFrom64 #-} | ||
36 | |||
37 | lookup :: Word64 -> Word64Map b -> Maybe b | ||
38 | lookup w8 (Word64Map m) | (# hi,lo #) <- keyFrom64 w8 = do | ||
39 | m' <- IntMap.lookup hi m | ||
40 | IntMap.lookup lo m' | ||
41 | {-# INLINE lookup #-} | ||
42 | |||
43 | insert :: Word64 -> b -> Word64Map b -> Word64Map b | ||
44 | insert w8 b (Word64Map m) | (# hi,lo #) <- keyFrom64 w8 | ||
45 | = Word64Map $ IntMap.alter (Just . maybe (IntMap.singleton lo b) | ||
46 | (IntMap.insert lo b)) | ||
47 | hi | ||
48 | m | ||
49 | {-# INLINE insert #-} | ||
50 | |||
51 | delete :: Word64 -> Word64Map b -> Word64Map b | ||
52 | delete w8 (Word64Map m) | (# hi,lo #) <- keyFrom64 w8 | ||
53 | = Word64Map $ IntMap.alter (maybe Nothing | ||
54 | (\m' -> case IntMap.delete lo m' of | ||
55 | m'' | IntMap.null m'' -> Nothing | ||
56 | m'' -> Just m'')) | ||
57 | hi | ||
58 | m | ||
59 | {-# INLINE delete #-} | ||
60 | |||
61 | |||
62 | |||
diff --git a/src/Network/BitTorrent/MainlineDHT.hs b/src/Network/BitTorrent/MainlineDHT.hs index 4566471a..f4ce4019 100644 --- a/src/Network/BitTorrent/MainlineDHT.hs +++ b/src/Network/BitTorrent/MainlineDHT.hs | |||
@@ -584,7 +584,7 @@ newClient swarms addr = do | |||
584 | gen cnt = (TransactionId $ S.encode cnt, cnt+1) | 584 | gen cnt = (TransactionId $ S.encode cnt, cnt+1) |
585 | 585 | ||
586 | client = Client | 586 | client = Client |
587 | { clientNet = addHandler (handleMessage client) net | 587 | { clientNet = addHandler ignoreErrors (handleMessage client) net |
588 | , clientDispatcher = dispatch | 588 | , clientDispatcher = dispatch |
589 | , clientErrorReporter = ignoreErrors -- printErrors stderr | 589 | , clientErrorReporter = ignoreErrors -- printErrors stderr |
590 | , clientPending = map_var | 590 | , clientPending = map_var |
@@ -1002,7 +1002,7 @@ mainlineSend meth unwrap msg client nid addr = do | |||
1002 | return $ join $ either (const Nothing) Just <$> reply | 1002 | return $ join $ either (const Nothing) Just <$> reply |
1003 | where | 1003 | where |
1004 | serializer = MethodSerializer | 1004 | serializer = MethodSerializer |
1005 | { methodTimeout = 5 | 1005 | { methodTimeout = \_ ni -> return (ni, 5000000) |
1006 | , method = meth | 1006 | , method = meth |
1007 | , wrapQuery = encodeQueryPayload meth (isReadonlyClient client) | 1007 | , wrapQuery = encodeQueryPayload meth (isReadonlyClient client) |
1008 | , unwrapResponse = (>>= either (Left . Error GenericError . C8.pack) | 1008 | , unwrapResponse = (>>= either (Left . Error GenericError . C8.pack) |
diff --git a/src/Network/QueryResponse.hs b/src/Network/QueryResponse.hs index 0fa1a05a..70d981e2 100644 --- a/src/Network/QueryResponse.hs +++ b/src/Network/QueryResponse.hs | |||
@@ -108,18 +108,18 @@ partitionTransportM parse encodex tr = do | |||
108 | } | 108 | } |
109 | return (xtr, ytr) | 109 | return (xtr, ytr) |
110 | 110 | ||
111 | addHandler :: (addr -> x -> IO (Maybe (x -> x))) -> Transport err addr x -> Transport err addr x | 111 | addHandler :: ErrorReporter addr x meth tid err -> (addr -> x -> IO (Maybe (x -> x))) -> Transport err addr x -> Transport err addr x |
112 | addHandler f tr = tr | 112 | addHandler err f tr = tr |
113 | { awaitMessage = \kont -> fix $ \eat -> awaitMessage tr $ \m -> do | 113 | { awaitMessage = \kont -> fix $ \eat -> awaitMessage tr $ \m -> do |
114 | case m of | 114 | case m of |
115 | Just (Right (x, addr)) -> f addr x >>= maybe eat (kont . Just . Right . (, addr) . ($ x)) | 115 | Just (Right (x, addr)) -> f addr x >>= maybe eat (kont . Just . Right . (, addr) . ($ x)) |
116 | Just (Left e ) -> kont $ Just (Left e) | 116 | Just (Left e ) -> reportParseError err e >> kont (Just $ Left e) |
117 | Nothing -> kont $ Nothing | 117 | Nothing -> kont $ Nothing |
118 | } | 118 | } |
119 | 119 | ||
120 | -- | Modify a 'Transport' to invoke an action upon every received packet. | 120 | -- | Modify a 'Transport' to invoke an action upon every received packet. |
121 | onInbound :: (addr -> x -> IO ()) -> Transport err addr x -> Transport err addr x | 121 | onInbound :: (addr -> x -> IO ()) -> Transport err addr x -> Transport err addr x |
122 | onInbound f tr = addHandler (\addr x -> f addr x >> return (Just id)) tr | 122 | onInbound f tr = addHandler ignoreErrors (\addr x -> f addr x >> return (Just id)) tr |
123 | 123 | ||
124 | -- * Using a query\/response client. | 124 | -- * Using a query\/response client. |
125 | 125 | ||
@@ -153,16 +153,17 @@ sendQuery :: | |||
153 | -> a -- ^ The outbound query. | 153 | -> a -- ^ The outbound query. |
154 | -> addr -- ^ Destination address of query. | 154 | -> addr -- ^ Destination address of query. |
155 | -> IO (Maybe b) -- ^ The response, or 'Nothing' if it timed out. | 155 | -> IO (Maybe b) -- ^ The response, or 'Nothing' if it timed out. |
156 | sendQuery (Client net d err pending whoami _) meth q addr = do | 156 | sendQuery (Client net d err pending whoami _) meth q addr0 = do |
157 | mvar <- newEmptyMVar | 157 | mvar <- newEmptyMVar |
158 | tid <- atomically $ do | 158 | (tid,addr,expiry) <- atomically $ do |
159 | tbl <- readTVar pending | 159 | tbl <- readTVar pending |
160 | (tid, tbl') <- dispatchRegister (tableMethods d) mvar tbl | 160 | (tid, tbl') <- dispatchRegister (tableMethods d) mvar addr0 tbl |
161 | (addr,expiry) <- methodTimeout meth tid addr0 | ||
161 | writeTVar pending tbl' | 162 | writeTVar pending tbl' |
162 | return tid | 163 | return (tid,addr,expiry) |
163 | self <- whoami (Just addr) | 164 | self <- whoami (Just addr) |
164 | sendMessage net addr (wrapQuery meth tid self addr q) | 165 | sendMessage net addr (wrapQuery meth tid self addr q) |
165 | mres <- timeout (1000000 * methodTimeout meth) $ takeMVar mvar | 166 | mres <- timeout expiry $ takeMVar mvar |
166 | case mres of | 167 | case mres of |
167 | Just x -> return $ Just $ unwrapResponse meth x | 168 | Just x -> return $ Just $ unwrapResponse meth x |
168 | Nothing -> do | 169 | Nothing -> do |
@@ -248,8 +249,10 @@ dispatchQuery (NoReply unwrapQ f) tid self x addr = | |||
248 | -- peer-to-peer algorithm will define a 'MethodSerializer' for every 'MethodHandler' that | 249 | -- peer-to-peer algorithm will define a 'MethodSerializer' for every 'MethodHandler' that |
249 | -- might be returned by 'lookupHandler'. | 250 | -- might be returned by 'lookupHandler'. |
250 | data MethodSerializer tid addr x meth a b = MethodSerializer | 251 | data MethodSerializer tid addr x meth a b = MethodSerializer |
251 | { -- | Seconds to wait for a response. | 252 | { -- | Returns the microseconds to wait for a response to this query being |
252 | methodTimeout :: Int | 253 | -- sent to the given address. The /addr/ may also be modified to add |
254 | -- routing information. | ||
255 | methodTimeout :: tid -> addr -> STM (addr,Int) | ||
253 | -- | A method identifier used for error reporting. This needn't be the | 256 | -- | A method identifier used for error reporting. This needn't be the |
254 | -- same as the /meth/ argument to 'MethodHandler', but it is suggested. | 257 | -- same as the /meth/ argument to 'MethodHandler', but it is suggested. |
255 | , method :: meth | 258 | , method :: meth |
@@ -269,13 +272,13 @@ data MethodSerializer tid addr x meth a b = MethodSerializer | |||
269 | -- | 272 | -- |
270 | -- The type variable /d/ is used to represent the current state of the | 273 | -- The type variable /d/ is used to represent the current state of the |
271 | -- transaction generator and the table of pending transactions. | 274 | -- transaction generator and the table of pending transactions. |
272 | data TransactionMethods d tid x = TransactionMethods | 275 | data TransactionMethods d tid addr x = TransactionMethods |
273 | { | 276 | { |
274 | -- | Before a query is sent, this function stores an 'MVar' to which the | 277 | -- | Before a query is sent, this function stores an 'MVar' to which the |
275 | -- response will be written too. The returned /tid/ is a transaction id | 278 | -- response will be written too. The returned /tid/ is a transaction id |
276 | -- that can be used to forget the 'MVar' if the remote peer is not | 279 | -- that can be used to forget the 'MVar' if the remote peer is not |
277 | -- responding. | 280 | -- responding. |
278 | dispatchRegister :: MVar x -> d -> STM (tid, d) | 281 | dispatchRegister :: MVar x -> addr -> d -> STM (tid, d) |
279 | -- | This method is invoked when an incoming packet /x/ indicates it is | 282 | -- | This method is invoked when an incoming packet /x/ indicates it is |
280 | -- a response to the transaction with id /tid/. The returned IO action | 283 | -- a response to the transaction with id /tid/. The returned IO action |
281 | -- is will write the packet to the correct 'MVar' thus completing the | 284 | -- is will write the packet to the correct 'MVar' thus completing the |
@@ -318,27 +321,15 @@ instance Contravariant (TableMethods t) where | |||
318 | (\k t -> del (f k) t) | 321 | (\k t -> del (f k) t) |
319 | (\k t -> lookup (f k) t) | 322 | (\k t -> lookup (f k) t) |
320 | 323 | ||
321 | -- | Since 'Int' may be 32 or 64 bits, this function is provided as a | ||
322 | -- convenience to test if an integral type, such as 'Data.Word.Word64', can be | ||
323 | -- safely transformed into an 'Int' for use with 'IntMap'. | ||
324 | -- | ||
325 | -- Returns 'True' if the proxied type can be losslessly converted to 'Int' using | ||
326 | -- 'fromIntegral'. | ||
327 | fitsInInt :: forall word. (Bounded word, Integral word) => Proxy word -> Bool | ||
328 | fitsInInt Proxy = (original == casted) | ||
329 | where | ||
330 | original = div maxBound 2 :: word | ||
331 | casted = fromIntegral (fromIntegral original :: Int) :: word | ||
332 | |||
333 | -- | Construct 'TransactionMethods' methods out of 3 lookup table primitives and a | 324 | -- | Construct 'TransactionMethods' methods out of 3 lookup table primitives and a |
334 | -- function for generating unique transaction ids. | 325 | -- function for generating unique transaction ids. |
335 | transactionMethods :: | 326 | transactionMethods :: |
336 | TableMethods t tid -- ^ Table methods to lookup values by /tid/. | 327 | TableMethods t tid -- ^ Table methods to lookup values by /tid/. |
337 | -> (g -> (tid,g)) -- ^ Generate a new unique /tid/ value and update the generator state /g/. | 328 | -> (g -> (tid,g)) -- ^ Generate a new unique /tid/ value and update the generator state /g/. |
338 | -> TransactionMethods (g,t (MVar x)) tid x | 329 | -> TransactionMethods (g,t (MVar x)) tid addr x |
339 | transactionMethods (TableMethods insert delete lookup) generate = TransactionMethods | 330 | transactionMethods (TableMethods insert delete lookup) generate = TransactionMethods |
340 | { dispatchCancel = \tid (g,t) -> return (g, delete tid t) | 331 | { dispatchCancel = \tid (g,t) -> return (g, delete tid t) |
341 | , dispatchRegister = \v (g,t) -> | 332 | , dispatchRegister = \v _ (g,t) -> |
342 | let (tid,g') = generate g | 333 | let (tid,g') = generate g |
343 | t' = insert tid v t | 334 | t' = insert tid v t |
344 | in return ( tid, (g',t') ) | 335 | in return ( tid, (g',t') ) |
@@ -356,7 +347,7 @@ data DispatchMethods tbl err meth tid addr x = DispatchMethods | |||
356 | -- | Lookup the handler for a inbound query. | 347 | -- | Lookup the handler for a inbound query. |
357 | , lookupHandler :: meth -> Maybe (MethodHandler err tid addr x) | 348 | , lookupHandler :: meth -> Maybe (MethodHandler err tid addr x) |
358 | -- | Methods for handling incoming responses. | 349 | -- | Methods for handling incoming responses. |
359 | , tableMethods :: TransactionMethods tbl tid x | 350 | , tableMethods :: TransactionMethods tbl tid addr x |
360 | } | 351 | } |
361 | 352 | ||
362 | -- | These methods indicate what should be done upon various conditions. Write | 353 | -- | These methods indicate what should be done upon various conditions. Write |
diff --git a/src/Network/Tox.hs b/src/Network/Tox.hs index 7814046e..3860d309 100644 --- a/src/Network/Tox.hs +++ b/src/Network/Tox.hs | |||
@@ -21,6 +21,7 @@ import Control.Arrow | |||
21 | import Control.Concurrent (MVar) | 21 | import Control.Concurrent (MVar) |
22 | import Control.Concurrent.STM | 22 | import Control.Concurrent.STM |
23 | import Control.Monad | 23 | import Control.Monad |
24 | import Control.Monad.Fix | ||
24 | import qualified Crypto.Cipher.ChaChaPoly1305 as Symmetric | 25 | import qualified Crypto.Cipher.ChaChaPoly1305 as Symmetric |
25 | import qualified Crypto.Cipher.Salsa as Salsa | 26 | import qualified Crypto.Cipher.Salsa as Salsa |
26 | import qualified Crypto.Cipher.XSalsa as XSalsa | 27 | import qualified Crypto.Cipher.XSalsa as XSalsa |
@@ -94,6 +95,7 @@ import qualified Network.Tox.Onion.Handlers as Onion | |||
94 | import Network.Tox.Crypto.Transport (NetCrypto) | 95 | import Network.Tox.Crypto.Transport (NetCrypto) |
95 | import Text.XXD | 96 | import Text.XXD |
96 | import OnionRouter | 97 | import OnionRouter |
98 | import Data.Word64Map (fitsInInt) | ||
97 | 99 | ||
98 | newCrypto :: IO TransportCrypto | 100 | newCrypto :: IO TransportCrypto |
99 | newCrypto = do | 101 | newCrypto = do |
@@ -158,7 +160,7 @@ newClient :: (DRG g, Show addr, Show meth) => | |||
158 | -> (x -> MessageClass String meth DHT.TransactionId) | 160 | -> (x -> MessageClass String meth DHT.TransactionId) |
159 | -> (Maybe addr -> IO addr) | 161 | -> (Maybe addr -> IO addr) |
160 | -> (meth -> Maybe (MethodHandler String DHT.TransactionId addr x)) | 162 | -> (meth -> Maybe (MethodHandler String DHT.TransactionId addr x)) |
161 | -> (forall d. TransactionMethods d DHT.TransactionId x -> TransactionMethods d DHT.TransactionId x) | 163 | -> (forall d. TransactionMethods d DHT.TransactionId addr x -> TransactionMethods d DHT.TransactionId addr x) |
162 | -> (Client String meth DHT.TransactionId addr x -> Transport String addr x -> Transport String addr x) | 164 | -> (Client String meth DHT.TransactionId addr x -> Transport String addr x -> Transport String addr x) |
163 | -> IO (Client String meth DHT.TransactionId addr x) | 165 | -> IO (Client String meth DHT.TransactionId addr x) |
164 | newClient drg net classify selfAddr handlers modifytbl modifynet = do | 166 | newClient drg net classify selfAddr handlers modifytbl modifynet = do |
@@ -180,11 +182,12 @@ newClient drg net classify selfAddr handlers modifytbl modifynet = do | |||
180 | , lookupHandler = handlers -- var | 182 | , lookupHandler = handlers -- var |
181 | , tableMethods = modifytbl tbl | 183 | , tableMethods = modifytbl tbl |
182 | } | 184 | } |
185 | eprinter = printErrors stderr | ||
183 | mkclient (tbl,var) handlers = | 186 | mkclient (tbl,var) handlers = |
184 | let client = Client | 187 | let client = Client |
185 | { clientNet = addHandler (handleMessage client) $ modifynet client net | 188 | { clientNet = addHandler eprinter (handleMessage client) $ modifynet client net |
186 | , clientDispatcher = dispatch tbl var handlers -- (fmap (contramapAddr (\(ToxPath ni _) -> ni)) . handlers) | 189 | , clientDispatcher = dispatch tbl var handlers -- (fmap (contramapAddr (\(ToxPath ni _) -> ni)) . handlers) |
187 | , clientErrorReporter = (printErrors stderr) { reportTimeout = reportTimeout ignoreErrors } | 190 | , clientErrorReporter = eprinter { reportTimeout = reportTimeout ignoreErrors } |
188 | , clientPending = var | 191 | , clientPending = var |
189 | , clientAddress = selfAddr | 192 | , clientAddress = selfAddr |
190 | , clientResponseId = genNonce24 var | 193 | , clientResponseId = genNonce24 var |
@@ -199,18 +202,22 @@ data Tox = Tox | |||
199 | , toxRouting :: DHT.Routing | 202 | , toxRouting :: DHT.Routing |
200 | , toxTokens :: TVar SessionTokens | 203 | , toxTokens :: TVar SessionTokens |
201 | , toxAnnouncedKeys :: TVar Onion.AnnouncedKeys | 204 | , toxAnnouncedKeys :: TVar Onion.AnnouncedKeys |
205 | , toxOnionRoutes :: OnionRouter | ||
202 | } | 206 | } |
203 | 207 | ||
204 | addVerbosity :: Show addr => Transport err addr ByteString -> Transport err addr ByteString | 208 | isLocalHost (SockAddrInet _ host32) = (fromBE32 host32 == 0x7f000001) |
209 | isLocalHost _ = False | ||
210 | |||
211 | addVerbosity :: Transport err SockAddr ByteString -> Transport err SockAddr ByteString | ||
205 | addVerbosity tr = | 212 | addVerbosity tr = |
206 | tr { awaitMessage = \kont -> awaitMessage tr $ \m -> do | 213 | tr { awaitMessage = \kont -> awaitMessage tr $ \m -> do |
207 | forM_ m $ mapM_ $ \(msg,addr) -> do | 214 | forM_ m $ mapM_ $ \(msg,addr) -> do |
208 | when (not (B.null msg || elem (B.head msg) [0,1,2,4])) $ do | 215 | when (isLocalHost addr || not (B.null msg || elem (B.head msg) [0,1,2,4,0x81,0x82,0x8c,0x8d])) $ do |
209 | mapM_ (\x -> hPutStrLn stderr ( (show addr) ++ " --> " ++ x)) | 216 | mapM_ (\x -> hPutStrLn stderr ( (show addr) ++ " --> " ++ x)) |
210 | $ xxd 0 msg | 217 | $ xxd 0 msg |
211 | kont m | 218 | kont m |
212 | , sendMessage = \addr msg -> do | 219 | , sendMessage = \addr msg -> do |
213 | when (not (B.null msg || elem (B.head msg) [0,1,2,4])) $ do | 220 | when (isLocalHost addr || not (B.null msg || elem (B.head msg) [0,1,2,4,0x81,0x82,0x8c,0x8d])) $ do |
214 | mapM_ (\x -> hPutStrLn stderr ( (show addr) ++ " <-- " ++ x)) | 221 | mapM_ (\x -> hPutStrLn stderr ( (show addr) ++ " <-- " ++ x)) |
215 | $ xxd 0 msg | 222 | $ xxd 0 msg |
216 | sendMessage tr addr msg | 223 | sendMessage tr addr msg |
@@ -226,13 +233,15 @@ newTox keydb addr = do | |||
226 | crypto <- newCrypto | 233 | crypto <- newCrypto |
227 | drg <- drgNew | 234 | drg <- drgNew |
228 | let lookupClose _ = return Nothing | 235 | let lookupClose _ = return Nothing |
229 | routing <- DHT.newRouting addr crypto updateIP updateIP | ||
230 | |||
231 | (dhtcrypt,onioncrypt,cryptonet) <- toxTransport crypto (DHT.orouter routing) lookupClose udp | ||
232 | 236 | ||
237 | routing <- DHT.newRouting addr crypto updateIP updateIP | ||
238 | orouter <- newOnionRouter | ||
239 | (dhtcrypt,onioncrypt,cryptonet) <- toxTransport crypto orouter lookupClose udp | ||
233 | let dhtnet0 = layerTransport (DHT.decrypt crypto) (DHT.encrypt crypto) dhtcrypt | 240 | let dhtnet0 = layerTransport (DHT.decrypt crypto) (DHT.encrypt crypto) dhtcrypt |
234 | dhtclient <- newClient drg dhtnet0 DHT.classify (myAddr routing) (DHT.handlers routing) id | 241 | dhtclient <- newClient drg dhtnet0 DHT.classify (myAddr routing) (DHT.handlers routing) id |
235 | $ \client net -> onInbound (DHT.updateRouting client routing) net | 242 | $ \client net -> onInbound (DHT.updateRouting client routing orouter) net |
243 | |||
244 | orouter <- forkRouteBuilder orouter $ \nid ni -> maybe [] (\(_,ns,_)->ns) <$> DHT.getNodes dhtclient nid ni | ||
236 | 245 | ||
237 | toks <- do | 246 | toks <- do |
238 | nil <- nullSessionTokens | 247 | nil <- nullSessionTokens |
@@ -240,13 +249,14 @@ newTox keydb addr = do | |||
240 | oniondrg <- drgNew | 249 | oniondrg <- drgNew |
241 | let onionnet = layerTransport (Onion.decrypt crypto) (Onion.encrypt crypto) onioncrypt | 250 | let onionnet = layerTransport (Onion.decrypt crypto) (Onion.encrypt crypto) onioncrypt |
242 | onionclient <- newClient oniondrg onionnet Onion.classify | 251 | onionclient <- newClient oniondrg onionnet Onion.classify |
243 | (const $ return | 252 | (const $ atomically |
244 | $ either (const $ error "bad sockaddr") | 253 | $ flip Onion.OnionDestination Nothing |
245 | (flip Onion.OnionDestination Nothing) | 254 | . R.thisNode |
246 | $ nodeInfo zeroID addr) | 255 | <$> readTVar (DHT.routing4 routing)) |
247 | (Onion.handlers onionnet routing toks keydb) | 256 | (Onion.handlers onionnet routing toks keydb) |
248 | (hookQueries (DHT.orouter routing) DHT.transactionKey) | 257 | (hookQueries orouter DHT.transactionKey) |
249 | (const id) | 258 | (const id) |
259 | |||
250 | return Tox | 260 | return Tox |
251 | { toxDHT = dhtclient | 261 | { toxDHT = dhtclient |
252 | , toxOnion = onionclient | 262 | , toxOnion = onionclient |
@@ -254,8 +264,12 @@ newTox keydb addr = do | |||
254 | , toxRouting = routing | 264 | , toxRouting = routing |
255 | , toxTokens = toks | 265 | , toxTokens = toks |
256 | , toxAnnouncedKeys = keydb | 266 | , toxAnnouncedKeys = keydb |
267 | , toxOnionRoutes = orouter | ||
257 | } | 268 | } |
258 | 269 | ||
270 | onionTimeout :: Tox -> DHT.TransactionId -> Onion.OnionDestination RouteId -> STM (Onion.OnionDestination RouteId, Int) | ||
271 | onionTimeout Tox { toxOnionRoutes = or } (DHT.TransactionId n8 _) od = lookupTimeout or n8 od | ||
272 | |||
259 | forkTox :: Tox -> IO (IO ()) | 273 | forkTox :: Tox -> IO (IO ()) |
260 | forkTox tox = do | 274 | forkTox tox = do |
261 | _ <- forkListener "toxCrypto" (toxCrypto tox) | 275 | _ <- forkListener "toxCrypto" (toxCrypto tox) |
diff --git a/src/Network/Tox/DHT/Handlers.hs b/src/Network/Tox/DHT/Handlers.hs index c9adc860..a3f13ac7 100644 --- a/src/Network/Tox/DHT/Handlers.hs +++ b/src/Network/Tox/DHT/Handlers.hs | |||
@@ -105,7 +105,6 @@ data Routing = Routing | |||
105 | , sched6 :: !( TVar (Int.PSQ POSIXTime) ) | 105 | , sched6 :: !( TVar (Int.PSQ POSIXTime) ) |
106 | , routing6 :: !( TVar (R.BucketList NodeInfo) ) | 106 | , routing6 :: !( TVar (R.BucketList NodeInfo) ) |
107 | , committee6 :: TriadCommittee NodeId SockAddr | 107 | , committee6 :: TriadCommittee NodeId SockAddr |
108 | , orouter :: OnionRouter | ||
109 | } | 108 | } |
110 | 109 | ||
111 | newRouting :: SockAddr -> TransportCrypto | 110 | newRouting :: SockAddr -> TransportCrypto |
@@ -124,8 +123,9 @@ newRouting addr crypto update4 update6 = do | |||
124 | tentative_info6 <- | 123 | tentative_info6 <- |
125 | maybe (tentative_info { nodeIP = tentative_ip6 }) | 124 | maybe (tentative_info { nodeIP = tentative_ip6 }) |
126 | (\ip6 -> tentative_info { nodeIP = IPv6 ip6 }) | 125 | (\ip6 -> tentative_info { nodeIP = IPv6 ip6 }) |
127 | <$> global6 | 126 | <$> case addr of |
128 | orouter <- newOnionRouter | 127 | SockAddrInet {} -> return Nothing |
128 | _ -> global6 | ||
129 | atomically $ do | 129 | atomically $ do |
130 | let nobkts = R.defaultBucketCount :: Int | 130 | let nobkts = R.defaultBucketCount :: Int |
131 | tbl4 <- newTVar $ R.nullTable (comparing nodeId) (\s -> hashWithSalt s . nodeId) tentative_info4 nobkts | 131 | tbl4 <- newTVar $ R.nullTable (comparing nodeId) (\s -> hashWithSalt s . nodeId) tentative_info4 nobkts |
@@ -134,7 +134,7 @@ newRouting addr crypto update4 update6 = do | |||
134 | committee6 <- newTriadCommittee (update6 tbl6) -- updateIPVote tbl6 addr6 | 134 | committee6 <- newTriadCommittee (update6 tbl6) -- updateIPVote tbl6 addr6 |
135 | sched4 <- newTVar Int.empty | 135 | sched4 <- newTVar Int.empty |
136 | sched6 <- newTVar Int.empty | 136 | sched6 <- newTVar Int.empty |
137 | return $ Routing tentative_info sched4 tbl4 committee4 sched6 tbl6 committee6 orouter | 137 | return $ Routing tentative_info sched4 tbl4 committee4 sched6 tbl6 committee6 |
138 | 138 | ||
139 | 139 | ||
140 | -- TODO: This should cover more cases | 140 | -- TODO: This should cover more cases |
@@ -200,7 +200,7 @@ serializer :: PacketKind | |||
200 | -> (Message -> Maybe (Assym (Nonce8,pong))) | 200 | -> (Message -> Maybe (Assym (Nonce8,pong))) |
201 | -> MethodSerializer TransactionId NodeInfo Message PacketKind ping (Maybe pong) | 201 | -> MethodSerializer TransactionId NodeInfo Message PacketKind ping (Maybe pong) |
202 | serializer pktkind mkping mkpong = MethodSerializer | 202 | serializer pktkind mkping mkpong = MethodSerializer |
203 | { methodTimeout = 5 | 203 | { methodTimeout = \tid addr -> return (addr, 5000000) |
204 | , method = pktkind | 204 | , method = pktkind |
205 | -- wrapQuery :: tid -> addr -> addr -> qry -> x | 205 | -- wrapQuery :: tid -> addr -> addr -> qry -> x |
206 | , wrapQuery = \tid src dst ping -> mkping $ wrapAssym tid src dst (, ping) | 206 | , wrapQuery = \tid src dst ping -> mkping $ wrapAssym tid src dst (, ping) |
@@ -232,20 +232,20 @@ unwrapNodes (SendNodes ns) = (ns,ns,()) | |||
232 | 232 | ||
233 | getNodes :: Client -> NodeId -> NodeInfo -> IO (Maybe ([NodeInfo],[NodeInfo],())) | 233 | getNodes :: Client -> NodeId -> NodeInfo -> IO (Maybe ([NodeInfo],[NodeInfo],())) |
234 | getNodes client nid addr = do | 234 | getNodes client nid addr = do |
235 | hPutStrLn stderr $ show addr ++ " <-- getnodes " ++ show nid | 235 | -- hPutStrLn stderr $ show addr ++ " <-- getnodes " ++ show nid |
236 | reply <- QR.sendQuery client (serializer GetNodesType DHTGetNodes unsendNodes) (GetNodes nid) addr | 236 | reply <- QR.sendQuery client (serializer GetNodesType DHTGetNodes unsendNodes) (GetNodes nid) addr |
237 | hPutStrLn stderr $ show addr ++ " -sendnodes-> " ++ show reply | 237 | -- hPutStrLn stderr $ show addr ++ " -sendnodes-> " ++ show reply |
238 | return $ fmap unwrapNodes $ join reply | 238 | return $ fmap unwrapNodes $ join reply |
239 | 239 | ||
240 | updateRouting :: Client -> Routing -> NodeInfo -> Message -> IO () | 240 | updateRouting :: Client -> Routing -> OnionRouter -> NodeInfo -> Message -> IO () |
241 | updateRouting client routing naddr msg = do | 241 | updateRouting client routing orouter naddr msg = do |
242 | let typ = fst $ dhtMessageType $ fst $ DHTTransport.encrypt (error "updateRouting") msg naddr | 242 | let typ = fst $ dhtMessageType $ fst $ DHTTransport.encrypt (error "updateRouting") msg naddr |
243 | tid = mapMessage (\n24 (n8,_) -> TransactionId n8 n24) msg | 243 | tid = mapMessage (\n24 (n8,_) -> TransactionId n8 n24) msg |
244 | hPutStrLn stderr $ "updateRouting "++show (typ,tid) | 244 | -- hPutStrLn stderr $ "updateRouting "++show (typ,tid) |
245 | -- TODO: check msg type | 245 | -- TODO: check msg type |
246 | case prefer4or6 naddr Nothing of | 246 | case prefer4or6 naddr Nothing of |
247 | Want_IP4 -> updateTable client naddr (orouter routing) (routing4 routing) (committee4 routing) (sched4 routing) | 247 | Want_IP4 -> updateTable client naddr orouter (routing4 routing) (committee4 routing) (sched4 routing) |
248 | Want_IP6 -> updateTable client naddr (orouter routing) (routing6 routing) (committee6 routing) (sched6 routing) | 248 | Want_IP6 -> updateTable client naddr orouter (routing6 routing) (committee6 routing) (sched6 routing) |
249 | 249 | ||
250 | updateTable :: Client -> NodeInfo -> OnionRouter -> TVar (R.BucketList NodeInfo) -> TriadCommittee NodeId SockAddr -> TVar (Int.PSQ POSIXTime) -> IO () | 250 | updateTable :: Client -> NodeInfo -> OnionRouter -> TVar (R.BucketList NodeInfo) -> TriadCommittee NodeId SockAddr -> TVar (Int.PSQ POSIXTime) -> IO () |
251 | updateTable client naddr orouter tbl committee sched = do | 251 | updateTable client naddr orouter tbl committee sched = do |
@@ -262,7 +262,7 @@ toxKademlia client committee orouter var sched | |||
262 | { tblTransition = \tr -> do | 262 | { tblTransition = \tr -> do |
263 | io1 <- transitionCommittee committee tr | 263 | io1 <- transitionCommittee committee tr |
264 | io2 <- touchBucket toxSpace (15*60) var sched tr | 264 | io2 <- touchBucket toxSpace (15*60) var sched tr |
265 | hookBucketList orouter tr | 265 | hookBucketList toxSpace var orouter tr |
266 | return $ do | 266 | return $ do |
267 | io1 >> io2 | 267 | io1 >> io2 |
268 | {- | 268 | {- |
diff --git a/src/Network/Tox/NodeId.hs b/src/Network/Tox/NodeId.hs index 959d689c..d0c57416 100644 --- a/src/Network/Tox/NodeId.hs +++ b/src/Network/Tox/NodeId.hs | |||
@@ -223,7 +223,10 @@ instance Sized NodeInfo where | |||
223 | instance S.Serialize NodeInfo where | 223 | instance S.Serialize NodeInfo where |
224 | get = do | 224 | get = do |
225 | addrfam <- S.get :: S.Get Word8 | 225 | addrfam <- S.get :: S.Get Word8 |
226 | ip <- getIP addrfam | 226 | let fallback = do -- FIXME: Handle unrecognized address families. |
227 | IPv6 <$> S.get | ||
228 | return $ IPv6 (read "::" :: IPv6) | ||
229 | ip <- getIP addrfam <|> fallback | ||
227 | port <- S.get :: S.Get PortNumber | 230 | port <- S.get :: S.Get PortNumber |
228 | nid <- S.get | 231 | nid <- S.get |
229 | return $ NodeInfo nid ip port | 232 | return $ NodeInfo nid ip port |
diff --git a/src/Network/Tox/Onion/Handlers.hs b/src/Network/Tox/Onion/Handlers.hs index 08f5cabd..91dd843e 100644 --- a/src/Network/Tox/Onion/Handlers.hs +++ b/src/Network/Tox/Onion/Handlers.hs | |||
@@ -1,6 +1,8 @@ | |||
1 | {-# LANGUAGE PatternSynonyms #-} | 1 | {-# LANGUAGE LambdaCase #-} |
2 | {-# LANGUAGE PatternSynonyms #-} | ||
2 | module Network.Tox.Onion.Handlers where | 3 | module Network.Tox.Onion.Handlers where |
3 | 4 | ||
5 | import Network.Kademlia.Search | ||
4 | import Network.Tox.DHT.Transport | 6 | import Network.Tox.DHT.Transport |
5 | import Network.Tox.DHT.Handlers hiding (Message,Client) | 7 | import Network.Tox.DHT.Handlers hiding (Message,Client) |
6 | import Network.Tox.Onion.Transport | 8 | import Network.Tox.Onion.Transport |
@@ -11,9 +13,11 @@ import qualified Data.Wrapper.PSQ as PSQ | |||
11 | ;import Data.Wrapper.PSQ (PSQ) | 13 | ;import Data.Wrapper.PSQ (PSQ) |
12 | import Crypto.Error.Types (CryptoFailable (..), | 14 | import Crypto.Error.Types (CryptoFailable (..), |
13 | throwCryptoError) | 15 | throwCryptoError) |
16 | import Control.Arrow | ||
14 | 17 | ||
15 | import System.IO | 18 | import System.IO |
16 | import qualified Data.ByteArray as BA | 19 | import qualified Data.ByteArray as BA |
20 | import Data.Function | ||
17 | import Data.Serialize as S | 21 | import Data.Serialize as S |
18 | import qualified Data.Wrapper.PSQInt as Int | 22 | import qualified Data.Wrapper.PSQInt as Int |
19 | import Network.Kademlia | 23 | import Network.Kademlia |
@@ -59,23 +63,27 @@ classify msg = go msg | |||
59 | -- The reason for this 20 second timeout in toxcore is that it gives a reasonable | 63 | -- The reason for this 20 second timeout in toxcore is that it gives a reasonable |
60 | -- time (20 to 40 seconds) for a peer to announce himself while taking in count | 64 | -- time (20 to 40 seconds) for a peer to announce himself while taking in count |
61 | -- all the possible delays with some extra seconds. | 65 | -- all the possible delays with some extra seconds. |
66 | -- dhtd: src/Network/Tox/Onion/Handlers.hs:(67,1)-(101,23): Non-exhaustive patterns in function announceH | ||
62 | announceH :: Routing -> TVar SessionTokens -> TVar AnnouncedKeys -> OnionDestination r -> AnnounceRequest -> IO AnnounceResponse | 67 | announceH :: Routing -> TVar SessionTokens -> TVar AnnouncedKeys -> OnionDestination r -> AnnounceRequest -> IO AnnounceResponse |
63 | announceH routing toks keydb (OnionToOwner naddr retpath) req = do | 68 | announceH routing toks keydb oaddr req = do |
64 | case () of | 69 | case () of |
65 | _ | announcePingId req == zeros32 | 70 | _ | announcePingId req == zeros32 |
66 | -> go False | 71 | -> go False |
67 | 72 | ||
68 | _ -> let Nonce32 bs = announcePingId req | 73 | _ -> let Nonce32 bs = announcePingId req |
69 | tok = fromPaddedByteString 32 bs | 74 | tok = fromPaddedByteString 32 bs |
70 | in checkToken toks naddr tok >>= go | 75 | in checkToken toks (onionNodeInfo oaddr) tok >>= go |
71 | `catch` (\(SomeException e) -> hPutStrLn stderr ("announceH Exception! "++show e) >> throw e) | 76 | `catch` (\(SomeException e) -> hPutStrLn stderr ("announceH Exception! "++show e) >> throw e) |
72 | where | 77 | where |
73 | go withTok = do | 78 | go withTok = do |
79 | let naddr = onionNodeInfo oaddr | ||
74 | ns <- getNodesH routing naddr (GetNodes (announceSeeking req)) | 80 | ns <- getNodesH routing naddr (GetNodes (announceSeeking req)) |
75 | tm <- getPOSIXTime | 81 | tm <- getPOSIXTime |
76 | let storing = (nodeId naddr == announceSeeking req) | 82 | let storing = case oaddr of |
83 | OnionToOwner _ pth -> guard (nodeId naddr == announceSeeking req) >> Just pth | ||
84 | _ -> Nothing | ||
77 | record <- atomically $ do | 85 | record <- atomically $ do |
78 | when (withTok && storing) $ do | 86 | forM_ storing $ \retpath -> when withTok $ do |
79 | let toxpath = AnnouncedRoute naddr{ nodeId = announceKey req } retpath | 87 | let toxpath = AnnouncedRoute naddr{ nodeId = announceKey req } retpath |
80 | -- Note: The following distance calculation assumes that | 88 | -- Note: The following distance calculation assumes that |
81 | -- our nodeid doesn't change and is the same for both | 89 | -- our nodeid doesn't change and is the same for both |
@@ -85,12 +93,12 @@ announceH routing toks keydb (OnionToOwner naddr retpath) req = do | |||
85 | modifyTVar' keydb (insertKey tm (announceSeeking req) toxpath d) | 93 | modifyTVar' keydb (insertKey tm (announceSeeking req) toxpath d) |
86 | ks <- readTVar keydb | 94 | ks <- readTVar keydb |
87 | return $ snd . snd <$> MinMaxPSQ.lookup' (announceSeeking req) (keyAssoc ks) | 95 | return $ snd . snd <$> MinMaxPSQ.lookup' (announceSeeking req) (keyAssoc ks) |
88 | newtok <- if storing | 96 | newtok <- maybe (return $ zeros32) |
89 | then Nonce32 . toPaddedByteString 32 <$> grantToken toks naddr | 97 | (const $ Nonce32 . toPaddedByteString 32 <$> grantToken toks naddr) |
90 | else return $ zeros32 | 98 | storing |
91 | let k = case record of | 99 | let k = case record of |
92 | Nothing -> NotStored newtok | 100 | Nothing -> NotStored newtok |
93 | Just _ | storing -> Acknowledged newtok | 101 | Just _ | isJust storing -> Acknowledged newtok |
94 | Just (AnnouncedRoute ni _) -> SendBackKey $ id2key (nodeId ni) | 102 | Just (AnnouncedRoute ni _) -> SendBackKey $ id2key (nodeId ni) |
95 | let response = AnnounceResponse k ns | 103 | let response = AnnounceResponse k ns |
96 | hPutStrLn stderr $ unwords ["Announce:", show req, "-reply->", show response] | 104 | hPutStrLn stderr $ unwords ["Announce:", show req, "-reply->", show response] |
@@ -151,3 +159,58 @@ handlers net routing toks keydb AnnounceType | |||
151 | $ announceH routing toks keydb | 159 | $ announceH routing toks keydb |
152 | handlers net _ _ keydb _ = Just $ NoReply Right $ dataToRouteH keydb net | 160 | handlers net _ _ keydb _ = Just $ NoReply Right $ dataToRouteH keydb net |
153 | 161 | ||
162 | toxidSearch :: (TransactionId -> OnionDestination r -> STM (OnionDestination r, Int)) | ||
163 | -> Client r | ||
164 | -> Search NodeId (IP, PortNumber) (Maybe Nonce32) NodeInfo PublicKey | ||
165 | toxidSearch getTimeout client = Search | ||
166 | { searchSpace = toxSpace | ||
167 | , searchNodeAddress = nodeIP &&& nodePort | ||
168 | , searchQuery = announce getTimeout client | ||
169 | } | ||
170 | |||
171 | announceSerializer :: (TransactionId -> OnionDestination r -> STM (OnionDestination r, Int)) | ||
172 | -> MethodSerializer | ||
173 | TransactionId | ||
174 | (OnionDestination r) | ||
175 | (OnionMessage Identity) | ||
176 | PacketKind | ||
177 | AnnounceRequest | ||
178 | (Maybe AnnounceResponse) | ||
179 | announceSerializer getTimeout = MethodSerializer | ||
180 | { methodTimeout = getTimeout | ||
181 | , method = AnnounceType | ||
182 | , wrapQuery = \(TransactionId n8 n24) src dst req -> | ||
183 | -- :: tid -> addr -> addr -> a -> OnionMessage Identity | ||
184 | OnionAnnounce $ Assym | ||
185 | { -- The public key is our real long term public key if we want to | ||
186 | -- announce ourselves, a temporary one if we are searching for | ||
187 | -- friends. | ||
188 | senderKey = fromJust $ onionKey src -- TODO: FIXME: this should be a temporary alias key | ||
189 | , assymNonce = n24 | ||
190 | , assymData = Identity (req, n8) | ||
191 | } | ||
192 | , unwrapResponse = \case -- :: OnionMessage Identity -> b | ||
193 | OnionAnnounceResponse _ _ resp -> Just $ runIdentity resp | ||
194 | _ -> Nothing | ||
195 | } | ||
196 | |||
197 | unwrapAnnounceResponse :: AnnounceResponse -> ([NodeInfo], [PublicKey], Maybe Nonce32) | ||
198 | unwrapAnnounceResponse (AnnounceResponse is_stored (SendNodes ns)) | ||
199 | = case is_stored of | ||
200 | NotStored n32 -> (ns, [], Just n32) | ||
201 | SendBackKey k -> (ns, [k], Nothing) | ||
202 | Acknowledged n32 -> (ns, [], Just n32) | ||
203 | |||
204 | announce :: (TransactionId -> OnionDestination r -> STM (OnionDestination r, Int)) | ||
205 | -> Client r | ||
206 | -> NodeId | ||
207 | -> NodeInfo | ||
208 | -> IO (Maybe ([NodeInfo],[PublicKey],Maybe Nonce32)) | ||
209 | announce getTimeout client nid ni = | ||
210 | -- Four tries and then we tap out. | ||
211 | flip fix 4 $ \loop n -> do | ||
212 | let oaddr = OnionDestination ni Nothing | ||
213 | mb <- QR.sendQuery client (announceSerializer getTimeout) (AnnounceRequest zeros32 nid zeroID) oaddr | ||
214 | maybe (if n>0 then loop $! n - 1 else return Nothing) | ||
215 | (return . Just . unwrapAnnounceResponse) | ||
216 | $ join mb | ||
diff --git a/src/Network/Tox/Onion/Transport.hs b/src/Network/Tox/Onion/Transport.hs index a3c1950f..b5ac748a 100644 --- a/src/Network/Tox/Onion/Transport.hs +++ b/src/Network/Tox/Onion/Transport.hs | |||
@@ -35,6 +35,8 @@ module Network.Tox.Onion.Transport | |||
35 | , peelSymmetric | 35 | , peelSymmetric |
36 | , OnionRoute(..) | 36 | , OnionRoute(..) |
37 | , N3 | 37 | , N3 |
38 | , onionKey | ||
39 | , onionNodeInfo | ||
38 | ) where | 40 | ) where |
39 | 41 | ||
40 | import Network.Address (fromSockAddr,toSockAddr,setPort,either4or6,sockAddrPort) | 42 | import Network.Address (fromSockAddr,toSockAddr,setPort,either4or6,sockAddrPort) |
@@ -42,10 +44,11 @@ import Network.QueryResponse | |||
42 | import Crypto.Tox hiding (encrypt,decrypt) | 44 | import Crypto.Tox hiding (encrypt,decrypt) |
43 | import Network.Tox.NodeId | 45 | import Network.Tox.NodeId |
44 | import qualified Crypto.Tox as ToxCrypto | 46 | import qualified Crypto.Tox as ToxCrypto |
45 | import Network.Tox.DHT.Transport (NodeInfo(..),NodeId(..),SendNodes,nodeInfo,DHTPublicKey,asymNodeInfo) | 47 | import Network.Tox.DHT.Transport (NodeInfo(..),NodeId(..),SendNodes(..),nodeInfo,DHTPublicKey,asymNodeInfo) |
46 | 48 | ||
47 | import Debug.Trace | 49 | import Debug.Trace |
48 | import Control.Arrow | 50 | import Control.Arrow |
51 | import Control.Applicative | ||
49 | import Control.Concurrent.STM | 52 | import Control.Concurrent.STM |
50 | import Control.Monad | 53 | import Control.Monad |
51 | import qualified Data.ByteString as B | 54 | import qualified Data.ByteString as B |
@@ -88,14 +91,23 @@ deriving instance ( Show (f (AnnounceRequest, Nonce8)) | |||
88 | , Show (f DataToRoute) | 91 | , Show (f DataToRoute) |
89 | ) => Show (OnionMessage f) | 92 | ) => Show (OnionMessage f) |
90 | 93 | ||
94 | msgNonce :: OnionMessage f -> Nonce24 | ||
95 | msgNonce (OnionAnnounce a) = assymNonce a | ||
96 | msgNonce (OnionAnnounceResponse _ n24 _) = n24 | ||
97 | msgNonce (OnionToRoute _ a) = assymNonce a | ||
98 | msgNonce (OnionToRouteResponse a) = assymNonce a | ||
99 | |||
91 | data OnionDestination r | 100 | data OnionDestination r |
92 | = OnionToOwner NodeInfo (ReturnPath N3) -- ^ Somebody else's path to us. | 101 | = OnionToOwner NodeInfo (ReturnPath N3) -- ^ Somebody else's path to us. |
93 | | OnionDestination NodeInfo (Maybe r) -- ^ Our own onion-path. | 102 | | OnionDestination NodeInfo (Maybe r) -- ^ Our own onion-path. |
94 | deriving Show | 103 | deriving Show |
95 | 104 | ||
105 | onionNodeInfo :: OnionDestination r -> NodeInfo | ||
106 | onionNodeInfo (OnionToOwner ni _) = ni | ||
107 | onionNodeInfo (OnionDestination ni _) = ni | ||
108 | |||
96 | onionKey :: OnionDestination r -> Maybe PublicKey | 109 | onionKey :: OnionDestination r -> Maybe PublicKey |
97 | onionKey (OnionToOwner ni _) = Just $ id2key (nodeId ni) | 110 | onionKey od = Just $ id2key . nodeId $ onionNodeInfo od |
98 | onionKey (OnionDestination ni _) = Just $ id2key (nodeId ni) | ||
99 | 111 | ||
100 | instance Sized (OnionMessage Encrypted) where | 112 | instance Sized (OnionMessage Encrypted) where |
101 | size = VarSize $ \case | 113 | size = VarSize $ \case |
@@ -176,11 +188,19 @@ encodeOnionAddr :: (NodeInfo -> r -> IO (Maybe OnionRoute)) | |||
176 | encodeOnionAddr _ (msg,OnionToOwner ni p) = | 188 | encodeOnionAddr _ (msg,OnionToOwner ni p) = |
177 | return $ Just ( runPut $ putResponse (OnionResponse p msg) | 189 | return $ Just ( runPut $ putResponse (OnionResponse p msg) |
178 | , nodeAddr ni ) | 190 | , nodeAddr ni ) |
179 | encodeOnionAddr _ (msg,OnionDestination _ Nothing ) = return Nothing | 191 | encodeOnionAddr _ (msg,OnionDestination _ Nothing ) = do |
192 | hPutStrLn stderr $ "ONION encode missing routeid" | ||
193 | return Nothing | ||
180 | encodeOnionAddr getRoute (msg,OnionDestination ni (Just rid)) = do | 194 | encodeOnionAddr getRoute (msg,OnionDestination ni (Just rid)) = do |
181 | let go route = do | 195 | let go route0 = do |
182 | return (runPut $ putRequest $ wrapForRoute msg ni route, nodeAddr ni) | 196 | let route = route0 { routeNonce = msgNonce msg } -- TODO: This necessary? |
183 | getRoute ni rid >>= mapM go | 197 | return ( runPut $ putRequest $ wrapForRoute msg ni route |
198 | , nodeAddr $ routeNodeA route) | ||
199 | mapM' f x = do | ||
200 | hPutStrLn stderr $ "ONION encode sending to " ++ show ni | ||
201 | hPutStrLn stderr $ "ONION encode getRoute -> " ++ show (mapM (\y -> map ($ y) [routeNodeA,routeNodeB,routeNodeC]) x) | ||
202 | mapM f x | ||
203 | getRoute ni rid >>= mapM' go | ||
184 | 204 | ||
185 | 205 | ||
186 | forwardOnions :: TransportCrypto -> UDPTransport -> UDPTransport | 206 | forwardOnions :: TransportCrypto -> UDPTransport -> UDPTransport |
@@ -239,16 +259,19 @@ deriving instance ( Show (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | |||
239 | instance ( Serialize (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | 259 | instance ( Serialize (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) |
240 | , Sized (ReturnPath n) | 260 | , Sized (ReturnPath n) |
241 | , Serialize (ReturnPath n) | 261 | , Serialize (ReturnPath n) |
262 | , Typeable n | ||
242 | ) => Serialize (OnionRequest n) where | 263 | ) => Serialize (OnionRequest n) where |
243 | get = do | 264 | get = do |
244 | -- TODO share code with 'getOnionRequest' | 265 | -- TODO share code with 'getOnionRequest' |
245 | n24 <- get | 266 | n24 <- case eqT :: Maybe (n :~: N3) of |
267 | Just Refl -> return $ Nonce24 zeros24 | ||
268 | Nothing -> get | ||
246 | cnt <- remaining | 269 | cnt <- remaining |
247 | let fwdsize = case size :: Size (ReturnPath n) of ConstSize n -> cnt - n | 270 | let fwdsize = case size :: Size (ReturnPath n) of ConstSize n -> cnt - n |
248 | fwd <- isolate fwdsize get | 271 | fwd <- isolate fwdsize get |
249 | rpath <- get | 272 | rpath <- get |
250 | return $ OnionRequest n24 fwd rpath | 273 | return $ OnionRequest n24 fwd rpath |
251 | put (OnionRequest n f p) = put n >> put f >> put p | 274 | put (OnionRequest n f p) = maybe (put n) (\Refl -> return ()) (eqT :: Maybe (n :~: N3)) >> put f >> put p |
252 | 275 | ||
253 | -- getRequest :: _ | 276 | -- getRequest :: _ |
254 | -- getRequest = OnionRequest <$> get <*> get <*> get | 277 | -- getRequest = OnionRequest <$> get <*> get <*> get |
@@ -402,6 +425,7 @@ handleOnionRequest :: forall a proxy n. | |||
402 | ( LessThanThree n | 425 | ( LessThanThree n |
403 | , KnownPeanoNat n | 426 | , KnownPeanoNat n |
404 | , Sized (ReturnPath n) | 427 | , Sized (ReturnPath n) |
428 | , Typeable n | ||
405 | ) => proxy n -> TransportCrypto -> SockAddr -> UDPTransport -> IO a -> OnionRequest n -> IO a | 429 | ) => proxy n -> TransportCrypto -> SockAddr -> UDPTransport -> IO a -> OnionRequest n -> IO a |
406 | handleOnionRequest proxy crypto saddr udp kont (OnionRequest nonce msg rpath) = do | 430 | handleOnionRequest proxy crypto saddr udp kont (OnionRequest nonce msg rpath) = do |
407 | let n = peanoVal rpath | 431 | let n = peanoVal rpath |
@@ -414,7 +438,7 @@ handleOnionRequest proxy crypto saddr udp kont (OnionRequest nonce msg rpath) = | |||
414 | hPutStrLn stderr $ unwords [ "peelOnion:", show n, either show show (either4or6 saddr), e] | 438 | hPutStrLn stderr $ unwords [ "peelOnion:", show n, either show show (either4or6 saddr), e] |
415 | kont | 439 | kont |
416 | Right (Addressed dst msg') -> do | 440 | Right (Addressed dst msg') -> do |
417 | hPutStrLn stderr $ unwords [ "peelOnion:", show n, either show show (either4or6 saddr), "SUCCESS"] | 441 | hPutStrLn stderr $ unwords [ "peelOnion:", show n, either show show (either4or6 saddr), "-->", either show show (either4or6 dst), "SUCCESS"] |
418 | sendMessage udp dst (runPut $ putRequest $ OnionRequest nonce msg' $ wrapSymmetric sym snonce saddr rpath) | 442 | sendMessage udp dst (runPut $ putRequest $ OnionRequest nonce msg' $ wrapSymmetric sym snonce saddr rpath) |
419 | kont | 443 | kont |
420 | 444 | ||
@@ -472,9 +496,13 @@ getOnionRequest = do | |||
472 | path <- get | 496 | path <- get |
473 | return (a,path) | 497 | return (a,path) |
474 | 498 | ||
475 | putRequest :: (KnownPeanoNat n, Serialize (OnionRequest n)) => OnionRequest n -> Put | 499 | putRequest :: ( KnownPeanoNat n |
500 | , Serialize (OnionRequest n) | ||
501 | , Typeable n | ||
502 | ) => OnionRequest n -> Put | ||
476 | putRequest req = do | 503 | putRequest req = do |
477 | putWord8 $ 0x80 + fromIntegral (peanoVal req) | 504 | let tag = 0x80 + fromIntegral (peanoVal req) |
505 | when (tag <= 0x82) (putWord8 tag) | ||
478 | put req | 506 | put req |
479 | 507 | ||
480 | putResponse :: (KnownPeanoNat n, Serialize (OnionResponse n)) => OnionResponse n -> Put | 508 | putResponse :: (KnownPeanoNat n, Serialize (OnionResponse n)) => OnionResponse n -> Put |
@@ -513,9 +541,14 @@ data AnnounceResponse = AnnounceResponse | |||
513 | instance Sized AnnounceResponse where | 541 | instance Sized AnnounceResponse where |
514 | size = contramap is_stored size <> contramap announceNodes size | 542 | size = contramap is_stored size <> contramap announceNodes size |
515 | 543 | ||
544 | getNodeList :: S.Get [NodeInfo] | ||
545 | getNodeList = do | ||
546 | n <- S.get | ||
547 | (:) n <$> (getNodeList <|> pure []) | ||
548 | |||
516 | instance S.Serialize AnnounceResponse where | 549 | instance S.Serialize AnnounceResponse where |
517 | get = AnnounceResponse <$> S.get <*> S.get | 550 | get = AnnounceResponse <$> S.get <*> (SendNodes <$> getNodeList) |
518 | put (AnnounceResponse st ns) = S.put st >> S.put ns | 551 | put (AnnounceResponse st (SendNodes ns)) = S.put st >> mapM_ S.put ns |
519 | 552 | ||
520 | data DataToRoute = DataToRoute | 553 | data DataToRoute = DataToRoute |
521 | { dataFromKey :: PublicKey -- Real public key of sender | 554 | { dataFromKey :: PublicKey -- Real public key of sender |