diff options
Diffstat (limited to 'OnionHandlers.hs')
-rw-r--r-- | OnionHandlers.hs | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/OnionHandlers.hs b/OnionHandlers.hs new file mode 100644 index 00000000..5e0951e8 --- /dev/null +++ b/OnionHandlers.hs | |||
@@ -0,0 +1,148 @@ | |||
1 | {-# LANGUAGE PatternSynonyms #-} | ||
2 | module OnionHandlers where | ||
3 | |||
4 | import Network.Tox.DHT.Transport | ||
5 | import DHTHandlers hiding (Message,Client) | ||
6 | import OnionTransport | ||
7 | import Network.QueryResponse as QR hiding (Client) | ||
8 | import qualified Network.QueryResponse as QR (Client) | ||
9 | import Crypto.Tox | ||
10 | import qualified Data.Wrapper.PSQ as PSQ | ||
11 | ;import Data.Wrapper.PSQ (PSQ) | ||
12 | import Crypto.Error.Types (CryptoFailable (..), | ||
13 | throwCryptoError) | ||
14 | |||
15 | import System.IO | ||
16 | import qualified Data.ByteArray as BA | ||
17 | import Data.Serialize as S | ||
18 | import qualified Data.Wrapper.PSQInt as Int | ||
19 | import Network.Kademlia | ||
20 | import Network.Address (WantIP (..), ipFamily, testIdBit) | ||
21 | import qualified Network.DHT.Routing as R | ||
22 | import Control.TriadCommittee | ||
23 | import qualified Data.MinMaxPSQ as MinMaxPSQ | ||
24 | ;import Data.MinMaxPSQ (MinMaxPSQ') | ||
25 | import Network.BitTorrent.DHT.Token as Token | ||
26 | |||
27 | import Control.Exception hiding (Handler) | ||
28 | import Control.Monad | ||
29 | import Control.Concurrent.STM | ||
30 | import Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime) | ||
31 | import Network.Socket | ||
32 | import Data.IP | ||
33 | import Data.Maybe | ||
34 | import Data.Bits | ||
35 | import Data.Ord | ||
36 | import Data.Functor.Identity | ||
37 | |||
38 | type Client = QR.Client String PacketKind TransactionId OnionToOwner Message | ||
39 | type Message = OnionMessage Identity | ||
40 | |||
41 | classify :: Message -> MessageClass String PacketKind TransactionId | ||
42 | classify msg = go msg | ||
43 | where | ||
44 | go (OnionAnnounce announce) = IsQuery AnnounceType | ||
45 | $ TransactionId (snd $ runIdentity $ assymData announce) | ||
46 | (assymNonce announce) | ||
47 | go (OnionAnnounceResponse n8 n24 resp) = IsResponse (TransactionId n8 n24) | ||
48 | go (OnionToRoute {}) = IsQuery DataRequestType (TransactionId (Nonce8 0) (Nonce24 zeros24)) | ||
49 | go (OnionToRouteResponse {}) = IsResponse (TransactionId (Nonce8 0) (Nonce24 zeros24)) | ||
50 | |||
51 | -- Toxcore generates `ping_id`s by taking a 32 byte sha hash of the current time, | ||
52 | -- some secret bytes generated when the instance is created, the current time | ||
53 | -- divided by a 20 second timeout, the public key of the requester and the source | ||
54 | -- ip/port that the packet was received from. Since the ip/port that the packet | ||
55 | -- was received from is in the `ping_id`, the announce packets being sent with a | ||
56 | -- ping id must be sent using the same path as the packet that we received the | ||
57 | -- `ping_id` from or announcing will fail. | ||
58 | -- | ||
59 | -- 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 | ||
61 | -- all the possible delays with some extra seconds. | ||
62 | announceH :: Routing -> TVar SessionTokens -> TVar AnnouncedKeys -> OnionToOwner -> AnnounceRequest -> IO AnnounceResponse | ||
63 | announceH routing toks keydb (OnionToOwner naddr retpath) req = do | ||
64 | case () of | ||
65 | _ | announcePingId req == zeros32 | ||
66 | -> go False | ||
67 | |||
68 | _ -> let Nonce32 bs = announcePingId req | ||
69 | tok = fromPaddedByteString 32 bs | ||
70 | in checkToken toks naddr tok >>= go | ||
71 | `catch` (\(SomeException e) -> hPutStrLn stderr ("announceH Exception! "++show e) >> throw e) | ||
72 | where | ||
73 | go withTok = do | ||
74 | ns <- getNodesH routing naddr (GetNodes (announceSeeking req)) | ||
75 | tm <- getPOSIXTime | ||
76 | let storing = (nodeId naddr == announceSeeking req) | ||
77 | record <- atomically $ do | ||
78 | when (withTok && storing) $ do | ||
79 | let toxpath = OnionToOwner naddr{ nodeId = announceKey req } retpath | ||
80 | -- Note: The following distance calculation assumes that | ||
81 | -- our nodeid doesn't change and is the same for both | ||
82 | -- routing4 and routing6. | ||
83 | d = xorNodeId (nodeId (tentativeId routing)) | ||
84 | (announceSeeking req) | ||
85 | modifyTVar' keydb (insertKey tm (announceSeeking req) toxpath d) | ||
86 | ks <- readTVar keydb | ||
87 | return $ snd . snd <$> MinMaxPSQ.lookup' (announceSeeking req) (keyAssoc ks) | ||
88 | newtok <- if storing | ||
89 | then Nonce32 . toPaddedByteString 32 <$> grantToken toks naddr | ||
90 | else return $ zeros32 | ||
91 | let k = case record of | ||
92 | Nothing -> NotStored newtok | ||
93 | Just (OnionToOwner {}) | storing -> Acknowledged newtok | ||
94 | Just (OnionToOwner ni _) -> SendBackKey $ id2key (nodeId ni) | ||
95 | let response = AnnounceResponse k ns | ||
96 | hPutStrLn stderr $ unwords ["Announce:", show req, "-reply->", show response] | ||
97 | return response | ||
98 | |||
99 | dataToRouteH :: | ||
100 | TVar AnnouncedKeys | ||
101 | -> Transport err OnionToOwner (OnionMessage f) | ||
102 | -> addr | ||
103 | -> OnionMessage f | ||
104 | -> IO () | ||
105 | dataToRouteH keydb udp _ (OnionToRoute pub assym) = do | ||
106 | let k = key2id pub | ||
107 | mb <- atomically $ do | ||
108 | ks <- readTVar keydb | ||
109 | forM (MinMaxPSQ.lookup' k (keyAssoc ks)) $ \(p,(cnt,rpath)) -> do | ||
110 | writeTVar keydb $ ks { keyAssoc = MinMaxPSQ.insert' k (cnt + 1, rpath) p (keyAssoc ks) } | ||
111 | return rpath | ||
112 | forM_ mb $ \rpath -> do | ||
113 | -- forward | ||
114 | sendMessage udp rpath $ OnionToRouteResponse assym | ||
115 | hPutStrLn stderr $ "Forwarding data-to-route -->"++show k | ||
116 | |||
117 | type NodeDistance = NodeId | ||
118 | |||
119 | data AnnouncedKeys = AnnouncedKeys | ||
120 | { keyByAge :: !(PSQ NodeId (Down POSIXTime)) -- timeout of 300 seconds | ||
121 | , keyAssoc :: !(MinMaxPSQ' NodeId NodeDistance (Int,OnionToOwner)) | ||
122 | } | ||
123 | |||
124 | |||
125 | insertKey :: POSIXTime -> NodeId -> OnionToOwner -> NodeDistance -> AnnouncedKeys -> AnnouncedKeys | ||
126 | insertKey tm pub toxpath d keydb = AnnouncedKeys | ||
127 | { keyByAge = PSQ.insert pub (Down tm) (keyByAge keydb) | ||
128 | , keyAssoc = case MinMaxPSQ.lookup' pub (keyAssoc keydb) of | ||
129 | Just (_,(cnt,_)) -> MinMaxPSQ.insert' pub (cnt,toxpath) d (keyAssoc keydb) | ||
130 | Nothing -> MinMaxPSQ.insert' pub (0 ,toxpath) d (keyAssoc keydb) | ||
131 | } | ||
132 | |||
133 | areq :: Message -> Either String AnnounceRequest | ||
134 | areq (OnionAnnounce assym) = Right $ fst $ runIdentity $ assymData assym | ||
135 | areq _ = Left "Unexpected non-announce OnionMessage" | ||
136 | |||
137 | handlers :: Transport err OnionToOwner Message | ||
138 | -> Routing | ||
139 | -> TVar SessionTokens | ||
140 | -> TVar AnnouncedKeys | ||
141 | -> PacketKind | ||
142 | -> Maybe (MethodHandler String TransactionId OnionToOwner Message) | ||
143 | handlers net routing toks keydb AnnounceType | ||
144 | = Just | ||
145 | $ MethodHandler areq (\(TransactionId n8 n24) src dst -> OnionAnnounceResponse n8 n24 . Identity) | ||
146 | $ announceH routing toks keydb | ||
147 | handlers net _ _ keydb _ = Just $ NoReply Right $ dataToRouteH keydb net | ||
148 | |||