summaryrefslogtreecommitdiff
path: root/dht
diff options
context:
space:
mode:
Diffstat (limited to 'dht')
-rw-r--r--dht/dht-client.cabal1
-rw-r--r--dht/src/Network/Tox/RelayPinger.hs99
2 files changed, 100 insertions, 0 deletions
diff --git a/dht/dht-client.cabal b/dht/dht-client.cabal
index dc9b7ca2..19f4f2b3 100644
--- a/dht/dht-client.cabal
+++ b/dht/dht-client.cabal
@@ -99,6 +99,7 @@ library
99 Network.Tox.Handshake 99 Network.Tox.Handshake
100 Network.Tox.NodeId 100 Network.Tox.NodeId
101 Network.Tox.Avahi 101 Network.Tox.Avahi
102 Network.Tox.RelayPinger
102 Network.UPNP 103 Network.UPNP
103 Network.QueryResponse.TCP 104 Network.QueryResponse.TCP
104 Network.Tox.Relay 105 Network.Tox.Relay
diff --git a/dht/src/Network/Tox/RelayPinger.hs b/dht/src/Network/Tox/RelayPinger.hs
new file mode 100644
index 00000000..00c6f65a
--- /dev/null
+++ b/dht/src/Network/Tox/RelayPinger.hs
@@ -0,0 +1,99 @@
1{-# LANGUAGE NamedFieldPuns #-}
2{-# LANGUAGE PatternSynonyms #-}
3module Network.Tox.RelayPinger where
4
5import Announcer
6import Control.Arrow
7import Control.Concurrent.STM
8import Control.Concurrent.ThreadUtil
9import Control.Monad
10import Data.Ord
11import qualified Data.Serialize as S
12import Data.Time.Clock (NominalDiffTime)
13import Data.Time.Clock.POSIX
14import qualified Data.Wrapper.PSQ as PSQ
15 ;import Data.Wrapper.PSQ (pattern (:->), PSQ)
16import Network.Kademlia.Routing
17import Network.QueryResponse
18import Network.Tox.TCP as TCP
19
20data RelayPriority = RelayPriority
21 { tcpPortRank :: Int
22 , xorDeltaDistance :: NodeId
23 }
24 deriving (Eq,Ord)
25
26data RelayPinger = RelayPinger
27 { relayAnnouncer :: Announcer
28 , relayKademliaSpace :: KademliaSpace NodeId NodeInfo
29 , relayClient :: RelayClient
30 , relayQueue :: TVar (Int, PSQ NodeInfo (Down RelayPriority))
31 , relaySelf :: NodeId
32 }
33
34forkRelayPinger :: KademliaSpace NodeId NodeInfo -> RelayClient -> IO RelayPinger
35forkRelayPinger kad cli = do
36 a <- forkAnnouncer
37 q <- newTVarIO (0,PSQ.empty)
38 self <- kademliaLocation kad <$> clientAddress cli Nothing
39 return RelayPinger
40 { relayAnnouncer = a
41 , relayKademliaSpace = kad
42 , relayClient = cli
43 , relayQueue = q
44 , relaySelf = self
45 }
46
47pinginterval :: NominalDiffTime
48pinginterval = 120.0 -- 2 minutes
49
50
51rankTCPPort :: NodeInfo -> Int
52rankTCPPort NodeInfo{tcpPort = 443} = 1
53rankTCPPort NodeInfo{tcpPort = 80} = 1
54rankTCPPort NodeInfo{tcpPort = 3389} = 2
55rankTCPPort _ = 3
56
57addRelay :: RelayPinger -> TCP.NodeInfo -> STM ()
58addRelay (RelayPinger a kad cli que self) ni = do
59 let nid = kademliaLocation kad ni
60 nidk = decodeAnnounceKey a $ S.encode nid
61 pingit = ScheduledItem $ \a nidk tm -> do
62 scheduleRel a nidk pingit pinginterval -- Reschedule the next ping.
63 return $ do
64 _ <- tcpPing cli ni
65 -- TODO: Remove after ping timeout?
66 return ()
67 modifyTVar' que $ \(n,q) ->
68 ( (,) $! (if PSQ.member ni q then n else n+1) )
69 $ PSQ.insert
70 ni
71 (Down $ RelayPriority (rankTCPPort ni) (kademliaXor kad self nid))
72 q
73 scheduleRel a nidk pingit pinginterval
74
75delRelay :: RelayPinger -> TCP.NodeInfo -> STM ()
76delRelay (RelayPinger a kad _ que _) ni = do
77 let nid = kademliaLocation kad ni
78 nidk = decodeAnnounceKey a $ S.encode nid
79 (n,q) <- readTVar que
80 writeTVar que $ ( (,) $! (if PSQ.member ni q then n - 1 else n) )
81 $ PSQ.delete ni q
82 unschedule a nidk
83
84bumpRelay :: RelayPinger -> TCP.NodeInfo -> STM ()
85bumpRelay rp@(RelayPinger a kad cli que self) ni = do
86 let nid = kademliaLocation kad ni
87 (cnt,q) <- readTVar que
88 if cnt < 4
89 then addRelay rp ni
90 else case PSQ.minView q of
91 Nothing -> addRelay rp ni
92 Just (r :-> Down p, q')
93 | let pnew = RelayPriority (rankTCPPort ni) (kademliaXor kad self nid)
94 , pnew < p
95 -> delRelay rp r >> addRelay rp ni
96 _ -> return ()
97
98currentRelays :: RelayPinger -> STM (Int,[TCP.NodeInfo])
99currentRelays rp = second PSQ.keys <$> readTVar (relayQueue rp)