summaryrefslogtreecommitdiff
path: root/OnionRouter.hs
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2018-06-19 23:41:17 -0400
committerjoe <joe@jerkface.net>2018-06-19 23:41:17 -0400
commitb7ca0fee54bc0f5a169972d559e7ca8c4c2b479f (patch)
tree65203fdce365b4658a425eea4b186e9078ebed1d /OnionRouter.hs
parentbf9d8626bfa0758c3374c1e329f94a6b520a4b3b (diff)
Remember which version of an onion route timed out.
Diffstat (limited to 'OnionRouter.hs')
-rw-r--r--OnionRouter.hs106
1 files changed, 69 insertions, 37 deletions
diff --git a/OnionRouter.hs b/OnionRouter.hs
index 428b6d8e..17cea8e8 100644
--- a/OnionRouter.hs
+++ b/OnionRouter.hs
@@ -46,10 +46,8 @@ import System.Timeout
46-- only-announcing. Instead, we maintain 12 multi-purpose routes. 46-- only-announcing. Instead, we maintain 12 multi-purpose routes.
47data OnionRouter = OnionRouter 47data OnionRouter = OnionRouter
48 { -- | For every query, we remember the destination and source keys 48 { -- | For every query, we remember the destination and source keys
49 -- so we can decrypt the response. Note, the RouteId field is not 49 -- so we can decrypt the response.
50 -- currently stored here. It is inferred from the destination NodeId. 50 pendingQueries :: TVar (Word64Map PendingQuery)
51 -- Instead, a 'Nothing' is stored.
52 pendingQueries :: TVar (Word64Map (OnionDestination RouteId))
53 -- | The current 12 routes that may be assigned to outgoing packets. 51 -- | The current 12 routes that may be assigned to outgoing packets.
54 , routeMap :: TVar (IntMap RouteRecord) 52 , routeMap :: TVar (IntMap RouteRecord)
55 -- | A set of nodes used to query for random route nodes. These aren't 53 -- | A set of nodes used to query for random route nodes. These aren't
@@ -71,9 +69,10 @@ data OnionRouter = OnionRouter
71 , onionDRG :: TVar ChaChaDRG 69 , onionDRG :: TVar ChaChaDRG
72 -- | Building onion routes happens in a dedicated thread. See 'forkRouteBuilder'. 70 -- | Building onion routes happens in a dedicated thread. See 'forkRouteBuilder'.
73 , routeThread :: ThreadId 71 , routeThread :: ThreadId
74 -- | Each of the 12 routes has a flag here that is set True when the 72 -- | Each of the 12 routes has a version number here that is set larger
75 -- route should be discarded and replaced with a fresh one. 73 -- than the 'routeVersion' set in 'routeMap' when the route should be
76 , pendingRoutes :: IntMap (TVar Bool) 74 -- discarded and replaced with a fresh one.
75 , pendingRoutes :: IntMap (TVar Int)
77 -- | Debug prints are written to this channel which is then flushed to 76 -- | Debug prints are written to this channel which is then flushed to
78 -- 'routeLogger'. 77 -- 'routeLogger'.
79 , routeLog :: TChan String 78 , routeLog :: TChan String
@@ -81,10 +80,17 @@ data OnionRouter = OnionRouter
81 , routeLogger :: String -> IO () 80 , routeLogger :: String -> IO ()
82 } 81 }
83 82
83data PendingQuery = PendingQuery
84 { pendingVersion :: !Int -- ^ Remembered version number so timeouts can signal a rebuild.
85 , pendingDestination :: OnionDestination RouteId
86 }
87 deriving Show
88
84data RouteRecord = RouteRecord 89data RouteRecord = RouteRecord
85 { storedRoute :: OnionRoute 90 { storedRoute :: OnionRoute
86 , responseCount :: !Int 91 , responseCount :: !Int
87 , timeoutCount :: !Int 92 , timeoutCount :: !Int
93 , routeVersion :: !Int
88 } 94 }
89 95
90-- Onion paths have different timeouts depending on whether the path is 96-- Onion paths have different timeouts depending on whether the path is
@@ -101,11 +107,12 @@ timeoutForRoute :: RouteRecord -> Int
101timeoutForRoute RouteRecord{ responseCount = 0 } = 4000000 107timeoutForRoute RouteRecord{ responseCount = 0 } = 4000000
102timeoutForRoute RouteRecord{ responseCount = _ } = 10000000 108timeoutForRoute RouteRecord{ responseCount = _ } = 10000000
103 109
104freshRoute :: OnionRoute -> RouteRecord 110freshRoute :: OnionRoute -> Maybe RouteRecord -> Maybe RouteRecord
105freshRoute r = RouteRecord 111freshRoute r mrec = Just $ RouteRecord
106 { storedRoute = r 112 { storedRoute = r
107 , responseCount = 0 113 , responseCount = 0
108 , timeoutCount = 0 114 , timeoutCount = 0
115 , routeVersion = maybe 0 succ $ routeVersion <$> mrec
109 } 116 }
110 117
111gotResponse :: RouteRecord -> RouteRecord 118gotResponse :: RouteRecord -> RouteRecord
@@ -125,15 +132,15 @@ newOnionRouter :: (String -> IO ()) -> IO OnionRouter
125newOnionRouter perror = do 132newOnionRouter perror = do
126 drg0 <- drgNew 133 drg0 <- drgNew
127 or <- atomically $ do 134 or <- atomically $ do
128 chan <- newTChan 135 -- chan <- newTChan
129 drg <- newTVar drg0 136 drg <- newTVar drg0
130 forM_ [0..11] $ \n -> writeTChan chan $ BuildRoute (RouteId n) 137 -- forM_ [0..11] $ \n -> writeTChan chan $ BuildRoute (RouteId n)
131 pq <- newTVar W64.empty 138 pq <- newTVar W64.empty
132 rm <- newTVar IntMap.empty 139 rm <- newTVar IntMap.empty
133 tn <- newTVar IntMap.empty 140 tn <- newTVar IntMap.empty
134 ti <- newTVar HashMap.empty 141 ti <- newTVar HashMap.empty
135 tc <- newTVar 0 142 tc <- newTVar 0
136 vs <- sequence $ replicate 12 (newTVar True) 143 vs <- sequence $ replicate 12 (newTVar 0)
137 rlog <- newTChan 144 rlog <- newTChan
138 return OnionRouter 145 return OnionRouter
139 { pendingRoutes = IntMap.fromList $ zip [0..11] vs 146 { pendingRoutes = IntMap.fromList $ zip [0..11] vs
@@ -156,9 +163,10 @@ forkRouteBuilder or getnodes = do
156 labelThread me "OnionRouter" 163 labelThread me "OnionRouter"
157 forever $ do 164 forever $ do
158 let checkRebuild rid want_build stm = flip orElse stm $ do 165 let checkRebuild rid want_build stm = flip orElse stm $ do
159 readTVar want_build >>= check 166 wanted_ver <- readTVar want_build
160 -- This was moved to handleEvent to allow retry on fail. 167 current_ver <- fmap routeVersion . IntMap.lookup rid <$> readTVar (routeMap or)
161 -- writeTVar want_build False -- Prevent redundant BuildRoute events. 168 writeTChan (routeLog or) $ "ONION checkRebuild "++show(current_ver,wanted_ver)
169 check $ maybe True (< wanted_ver) current_ver
162 return $ BuildRoute $ RouteId rid 170 return $ BuildRoute $ RouteId rid
163 io <- atomically $ 171 io <- atomically $
164 (readTChan (routeLog or) >>= return . routeLogger or) 172 (readTChan (routeLog or) >>= return . routeLogger or)
@@ -325,10 +333,11 @@ handleEvent getnodes or e@(BuildRoute (RouteId rid)) = do
325 _ -> return Nothing 333 _ -> return Nothing
326 writeTVar (onionDRG or) drg' 334 writeTVar (onionDRG or) drg'
327 return $ getr 335 return $ getr
328 atomically $ maybe (writeTVar (pendingRoutes or IntMap.! rid) True) 336 atomically $ maybe (return ()) -- writeTVar (pendingRoutes or IntMap.! rid) True)
329 (\r -> do modifyTVar' (routeMap or) 337 (\r -> do modifyTVar' (routeMap or)
330 (IntMap.insert rid $ freshRoute r) 338 (IntMap.alter (freshRoute r) rid)
331 writeTVar (pendingRoutes or IntMap.! rid) False 339 v <- routeVersion . (IntMap.! rid) <$> readTVar (routeMap or)
340 writeTVar (pendingRoutes or IntMap.! rid) v
332 ) 341 )
333 mb 342 mb
334 case mb of 343 case mb of
@@ -344,9 +353,9 @@ lookupSender or saddr (Nonce8 w8) = do
344 return r 353 return r
345 return $ do 354 return $ do
346 od <- result 355 od <- result
347 let nid = nodeId $ onionNodeInfo od 356 let nid = nodeId $ onionNodeInfo $ pendingDestination od
348 ni <- either (const Nothing) Just $ nodeInfo nid saddr 357 ni <- either (const Nothing) Just $ nodeInfo nid saddr
349 Just (OnionDestination (onionAliasSelector od) 358 Just (OnionDestination (onionAliasSelector $ pendingDestination od)
350 ni 359 ni
351 (Just $ routeId nid)) 360 (Just $ routeId nid))
352 361
@@ -365,42 +374,65 @@ lookupTimeout or n8 (OnionDestination asel ni Nothing) = do
365 Nothing -> return ( OnionDestination asel ni Nothing , 0 ) 374 Nothing -> return ( OnionDestination asel ni Nothing , 0 )
366 375
367hookQueries :: OnionRouter -> (tid -> Nonce8) 376hookQueries :: OnionRouter -> (tid -> Nonce8)
368 -> TransactionMethods d tid (OnionDestination r) x 377 -> TransactionMethods d tid (OnionDestination RouteId) x
369 -> TransactionMethods d tid (OnionDestination r) x 378 -> TransactionMethods d tid (OnionDestination RouteId) x
370hookQueries or t8 tmethods = TransactionMethods 379hookQueries or t8 tmethods = TransactionMethods
371 { dispatchRegister = \mvar od d -> do -- :: MVar x -> d -> STM (tid, d) 380 { dispatchRegister = \mvar od d -> do -- :: MVar x -> d -> STM (tid, d)
372 (tid,d') <- dispatchRegister tmethods mvar od d 381 (tid,d') <- dispatchRegister tmethods mvar od d
373 let Nonce8 w8 = t8 tid 382 let Nonce8 w8 = t8 tid
374 od' = case od of OnionDestination {} -> od { onionRouteSpec = Nothing }
375 OnionToOwner a b -> OnionToOwner a b -- Type cast.
376 ni = onionNodeInfo od 383 ni = onionNodeInfo od
377 modifyTVar' (pendingQueries or) (W64.insert w8 od') 384 rid@(RouteId ridn) = fromMaybe (routeId (nodeId ni)) $ onionRouteSpec od
378 writeTChan (routeLog or) $ "ONION query add " ++ unwords [ show w8, ":=", show ni ] 385 od' = case od of
386 OnionDestination {} -> od { onionRouteSpec = Just rid }
387 OnionToOwner a b -> OnionToOwner a b -- Type cast.
388 wanted <- readTVar (pendingRoutes or IntMap.! ridn)
389 mr <- IntMap.lookup ridn <$> readTVar (routeMap or)
390 -- Block query until a route is ready.
391 check $ fromMaybe False $ do
392 RouteRecord{routeVersion=rv} <- mr
393 return $ wanted <= rv
394 let pq = PendingQuery { pendingDestination = od'
395 , pendingVersion = maybe 0 routeVersion mr
396 }
397 pqs <- readTVar (pendingQueries or)
398 -- check $ W64.size pqs < 20
399 modifyTVar' (pendingQueries or) (W64.insert w8 pq)
400 writeTChan (routeLog or) $ "ONION query add " ++ unwords [ show (Just $ pendingVersion pq,w8), ":=", show ni ]
379 return (tid,d') 401 return (tid,d')
380 , dispatchResponse = \tid x d -> do -- :: tid -> x -> d -> STM (d, IO ()) 402 , dispatchResponse = \tid x d -> do -- :: tid -> x -> d -> STM (d, IO ())
381 let Nonce8 w8 = t8 tid 403 let Nonce8 w8 = t8 tid
382 mb <- W64.lookup w8 <$> readTVar (pendingQueries or) 404 mb <- W64.lookup w8 <$> readTVar (pendingQueries or)
383 modifyTVar' (pendingQueries or) (W64.delete w8) 405 modifyTVar' (pendingQueries or) (W64.delete w8)
384 forM_ mb $ \od -> do 406 forM_ mb $ \pq -> do
385 let RouteId rid = routeId (nodeId (onionNodeInfo od)) 407 let od = pendingDestination pq
408 RouteId rid = fromMaybe (routeId (nodeId (onionNodeInfo od)))
409 $ onionRouteSpec od
386 modifyTVar' (routeMap or) (IntMap.adjust gotResponse rid) 410 modifyTVar' (routeMap or) (IntMap.adjust gotResponse rid)
387 writeTChan (routeLog or) $ "ONION query del " ++ show w8 411 writeTChan (routeLog or) $ "ONION query del " ++ show (fmap pendingVersion mb, w8)
388 dispatchResponse tmethods tid x d 412 dispatchResponse tmethods tid x d
389 , dispatchCancel = \tid d -> do -- :: tid -> d -> STM d 413 , dispatchCancel = \tid d -> do -- :: tid -> d -> STM d
390 let Nonce8 w8 = t8 tid 414 let Nonce8 w8 = t8 tid
391 mb <- W64.lookup w8 <$> readTVar (pendingQueries or) 415 mb <- W64.lookup w8 <$> readTVar (pendingQueries or)
392 modifyTVar' (pendingQueries or) (W64.delete w8) 416 modifyTVar' (pendingQueries or) (W64.delete w8)
393 forM_ mb $ \od -> do 417 forM_ mb $ \pq -> do
394 let RouteId rid = routeId (nodeId (onionNodeInfo od)) 418 let od = pendingDestination pq
419 RouteId rid = fromMaybe (routeId (nodeId (onionNodeInfo od)))
420 $ onionRouteSpec od
421 mrr <- IntMap.lookup rid <$> readTVar (routeMap or)
422 forM_ mrr $ \rr -> do
423 when (routeVersion rr == pendingVersion pq) $ do
424 let expireRoute = modifyTVar' (pendingRoutes or IntMap.! rid) expire
425 expire ver | ver <= succ (pendingVersion pq) = succ (pendingVersion pq)
426 | otherwise = ver
395 modifyTVar' (routeMap or) (IntMap.adjust gotTimeout rid) 427 modifyTVar' (routeMap or) (IntMap.adjust gotTimeout rid)
396 let expireRoute = writeTVar (pendingRoutes or IntMap.! rid) True
397 rr <- IntMap.lookup rid <$> readTVar (routeMap or)
398 case rr of 428 case rr of
399 Just RouteRecord{ responseCount=0 429 RouteRecord{ responseCount = 0
400 , timeoutCount = c } | c >= 2 -> expireRoute 430 , timeoutCount = c
401 Just RouteRecord{ timeoutCount = c } | c >= 4 -> expireRoute 431 , routeVersion = v } | c >= 2 -> expireRoute
432 RouteRecord{ timeoutCount = c
433 , routeVersion = v } | c >= 5 -> expireRoute
402 _ -> return () 434 _ -> return ()
403 writeTChan (routeLog or) $ "ONION query can " ++ show w8 435 writeTChan (routeLog or) $ "ONION query can " ++ show (fmap pendingVersion mb, w8)
404 dispatchCancel tmethods tid d 436 dispatchCancel tmethods tid d
405 } 437 }
406 438