diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Data/Tox/Onion.hs | 1028 | ||||
-rw-r--r-- | src/Data/Tox/Relay.hs | 47 | ||||
-rw-r--r-- | src/Network/QueryResponse.hs | 46 | ||||
-rw-r--r-- | src/Network/Tox.hs | 56 | ||||
-rw-r--r-- | src/Network/Tox/Onion/Transport.hs | 1069 | ||||
-rw-r--r-- | src/Network/Tox/TCP.hs | 57 | ||||
-rw-r--r-- | src/Network/Tox/Transport.hs | 22 |
7 files changed, 1234 insertions, 1091 deletions
diff --git a/src/Data/Tox/Onion.hs b/src/Data/Tox/Onion.hs new file mode 100644 index 00000000..85a9d21e --- /dev/null +++ b/src/Data/Tox/Onion.hs | |||
@@ -0,0 +1,1028 @@ | |||
1 | {-# LANGUAGE CPP #-} | ||
2 | {-# LANGUAGE DataKinds #-} | ||
3 | {-# LANGUAGE DeriveDataTypeable #-} | ||
4 | {-# LANGUAGE FlexibleContexts #-} | ||
5 | {-# LANGUAGE FlexibleInstances #-} | ||
6 | {-# LANGUAGE GADTs #-} | ||
7 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
8 | {-# LANGUAGE KindSignatures #-} | ||
9 | {-# LANGUAGE LambdaCase #-} | ||
10 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
11 | {-# LANGUAGE PartialTypeSignatures #-} | ||
12 | {-# LANGUAGE RankNTypes #-} | ||
13 | {-# LANGUAGE ScopedTypeVariables #-} | ||
14 | {-# LANGUAGE StandaloneDeriving #-} | ||
15 | {-# LANGUAGE TupleSections #-} | ||
16 | {-# LANGUAGE TypeFamilies #-} | ||
17 | {-# LANGUAGE TypeOperators #-} | ||
18 | {-# LANGUAGE UndecidableInstances #-} | ||
19 | module Data.Tox.Onion where | ||
20 | |||
21 | |||
22 | import Network.Address (fromSockAddr,toSockAddr,setPort,either4or6,sockAddrPort) | ||
23 | import Network.QueryResponse | ||
24 | import Crypto.Tox hiding (encrypt,decrypt) | ||
25 | import Network.Tox.NodeId | ||
26 | import qualified Crypto.Tox as ToxCrypto | ||
27 | import Network.Tox.DHT.Transport (NodeInfo(..),NodeId(..),SendNodes(..),nodeInfo,DHTPublicKey(..),FriendRequest,asymNodeInfo) | ||
28 | |||
29 | import Control.Applicative | ||
30 | import Control.Arrow | ||
31 | import Control.Concurrent.STM | ||
32 | import Control.Monad | ||
33 | import qualified Data.ByteString as B | ||
34 | ;import Data.ByteString (ByteString) | ||
35 | import Data.Data | ||
36 | import Data.Function | ||
37 | import Data.Functor.Contravariant | ||
38 | import Data.Functor.Identity | ||
39 | #if MIN_VERSION_iproute(1,7,4) | ||
40 | import Data.IP hiding (fromSockAddr) | ||
41 | #else | ||
42 | import Data.IP | ||
43 | #endif | ||
44 | import Data.Maybe | ||
45 | import Data.Monoid | ||
46 | import Data.Serialize as S | ||
47 | import Data.Type.Equality | ||
48 | import Data.Typeable | ||
49 | import Data.Word | ||
50 | import GHC.Generics () | ||
51 | import GHC.TypeLits | ||
52 | import Network.Socket | ||
53 | import qualified Text.ParserCombinators.ReadP as RP | ||
54 | import Data.Hashable | ||
55 | import DPut | ||
56 | import DebugTag | ||
57 | import Data.Word64Map (fitsInInt) | ||
58 | import Data.Bits (shiftR,shiftL) | ||
59 | import qualified Rank2 | ||
60 | |||
61 | type HandleLo a = Maybe (Either String (ByteString, SockAddr)) -> IO a | ||
62 | |||
63 | type UDPTransport = Transport String SockAddr ByteString | ||
64 | |||
65 | |||
66 | getOnionAsymm :: Get (Asymm (Encrypted DataToRoute)) | ||
67 | getOnionAsymm = getAliasedAsymm | ||
68 | |||
69 | putOnionAsymm :: Serialize a => Word8 -> Put -> Asymm a -> Put | ||
70 | putOnionAsymm typ p a = put typ >> p >> putAliasedAsymm a | ||
71 | |||
72 | data OnionMessage (f :: * -> *) | ||
73 | = OnionAnnounce (Asymm (f (AnnounceRequest,Nonce8))) | ||
74 | | OnionAnnounceResponse Nonce8 Nonce24 (f AnnounceResponse) -- XXX: Why is Nonce8 transmitted in the clear? | ||
75 | | OnionToRoute PublicKey (Asymm (Encrypted DataToRoute)) -- destination key, aliased Asymm | ||
76 | | OnionToRouteResponse (Asymm (Encrypted DataToRoute)) | ||
77 | |||
78 | deriving instance ( Eq (f (AnnounceRequest, Nonce8)) | ||
79 | , Eq (f AnnounceResponse) | ||
80 | , Eq (f DataToRoute) | ||
81 | ) => Eq (OnionMessage f) | ||
82 | |||
83 | deriving instance ( Ord (f (AnnounceRequest, Nonce8)) | ||
84 | , Ord (f AnnounceResponse) | ||
85 | , Ord (f DataToRoute) | ||
86 | ) => Ord (OnionMessage f) | ||
87 | |||
88 | deriving instance ( Show (f (AnnounceRequest, Nonce8)) | ||
89 | , Show (f AnnounceResponse) | ||
90 | , Show (f DataToRoute) | ||
91 | ) => Show (OnionMessage f) | ||
92 | |||
93 | instance Data (OnionMessage Encrypted) where | ||
94 | gfoldl f z txt = z (either error id . S.decode) `f` S.encode txt | ||
95 | toConstr _ = error "OnionMessage.toConstr" | ||
96 | gunfold _ _ = error "OnionMessage.gunfold" | ||
97 | #if MIN_VERSION_base(4,2,0) | ||
98 | dataTypeOf _ = mkNoRepType "Network.Tox.Onion.Transport.OnionMessage" | ||
99 | #else | ||
100 | dataTypeOf _ = mkNorepType "Network.Tox.Onion.Transport.OnionMessage" | ||
101 | #endif | ||
102 | |||
103 | instance Rank2.Functor OnionMessage where | ||
104 | f <$> m = mapPayload (Proxy :: Proxy Serialize) f m | ||
105 | |||
106 | instance Payload Serialize OnionMessage where | ||
107 | mapPayload _ f (OnionAnnounce a) = OnionAnnounce (fmap f a) | ||
108 | mapPayload _ f (OnionAnnounceResponse n8 n24 a) = OnionAnnounceResponse n8 n24 (f a) | ||
109 | mapPayload _ f (OnionToRoute k a) = OnionToRoute k a | ||
110 | mapPayload _ f (OnionToRouteResponse a) = OnionToRouteResponse a | ||
111 | |||
112 | |||
113 | msgNonce :: OnionMessage f -> Nonce24 | ||
114 | msgNonce (OnionAnnounce a) = asymmNonce a | ||
115 | msgNonce (OnionAnnounceResponse _ n24 _) = n24 | ||
116 | msgNonce (OnionToRoute _ a) = asymmNonce a | ||
117 | msgNonce (OnionToRouteResponse a) = asymmNonce a | ||
118 | |||
119 | data AliasSelector = SearchingAlias | AnnouncingAlias SecretKey PublicKey | ||
120 | deriving (Eq,Show) | ||
121 | |||
122 | data OnionDestination r | ||
123 | = OnionToOwner | ||
124 | { onionNodeInfo :: NodeInfo | ||
125 | , onionReturnPath :: ReturnPath N3 -- ^ Somebody else's path to us. | ||
126 | } | ||
127 | | OnionDestination | ||
128 | { onionAliasSelector' :: AliasSelector | ||
129 | , onionNodeInfo :: NodeInfo | ||
130 | , onionRouteSpec :: Maybe r -- ^ Our own onion-path. | ||
131 | } | ||
132 | deriving Show | ||
133 | |||
134 | onionAliasSelector :: OnionDestination r -> AliasSelector | ||
135 | onionAliasSelector (OnionToOwner {} ) = SearchingAlias | ||
136 | onionAliasSelector (OnionDestination{onionAliasSelector' = sel}) = sel | ||
137 | |||
138 | onionKey :: OnionDestination r -> PublicKey | ||
139 | onionKey od = id2key . nodeId $ onionNodeInfo od | ||
140 | |||
141 | instance Sized (OnionMessage Encrypted) where | ||
142 | size = VarSize $ \case | ||
143 | OnionAnnounce a -> case size of ConstSize n -> n + 1 | ||
144 | VarSize f -> f a + 1 | ||
145 | OnionAnnounceResponse n8 n24 x -> case size of ConstSize n -> n + 33 | ||
146 | VarSize f -> f x + 33 | ||
147 | OnionToRoute pubkey a -> case size of ConstSize n -> n + 33 | ||
148 | VarSize f -> f a + 33 | ||
149 | OnionToRouteResponse a -> case size of ConstSize n -> n + 1 | ||
150 | VarSize f -> f a + 1 | ||
151 | |||
152 | instance Serialize (OnionMessage Encrypted) where | ||
153 | get = do | ||
154 | typ <- get | ||
155 | case typ :: Word8 of | ||
156 | 0x83 -> OnionAnnounce <$> getAliasedAsymm | ||
157 | 0x85 -> OnionToRoute <$> getPublicKey <*> getAliasedAsymm | ||
158 | t -> fail ("Unknown onion payload: " ++ show t) | ||
159 | `fromMaybe` getOnionReply t | ||
160 | put (OnionAnnounce a) = putWord8 0x83 >> putAliasedAsymm a | ||
161 | put (OnionToRoute k a) = putWord8 0x85 >> putPublicKey k >> putAliasedAsymm a | ||
162 | put (OnionAnnounceResponse n8 n24 x) = putWord8 0x84 >> put n8 >> put n24 >> put x | ||
163 | put (OnionToRouteResponse a) = putWord8 0x86 >> putAliasedAsymm a | ||
164 | |||
165 | onionToOwner :: Asymm a -> ReturnPath N3 -> SockAddr -> Either String (OnionDestination r) | ||
166 | onionToOwner asymm ret3 saddr = do | ||
167 | ni <- nodeInfo (key2id $ senderKey asymm) saddr | ||
168 | return $ OnionToOwner ni ret3 | ||
169 | -- data CookieAddress = WithoutCookie NodeInfo | CookieAddress Cookie SockAddr | ||
170 | |||
171 | |||
172 | onion :: Sized msg => | ||
173 | ByteString | ||
174 | -> SockAddr | ||
175 | -> Get (Asymm (Encrypted msg) -> t) | ||
176 | -> Either String (t, OnionDestination r) | ||
177 | onion bs saddr getf = do (f,(asymm,ret3)) <- runGet ((,) <$> getf <*> getOnionRequest) bs | ||
178 | oaddr <- onionToOwner asymm ret3 saddr | ||
179 | return (f asymm, oaddr) | ||
180 | |||
181 | parseOnionAddr :: (SockAddr -> Nonce8 -> IO (Maybe (OnionDestination r))) | ||
182 | -> (ByteString, SockAddr) | ||
183 | -> IO (Either (OnionMessage Encrypted,OnionDestination r) | ||
184 | (ByteString,SockAddr)) | ||
185 | parseOnionAddr lookupSender (msg,saddr) | ||
186 | | Just (typ,bs) <- B.uncons msg | ||
187 | , let right = Right (msg,saddr) | ||
188 | query = return . either (const right) Left | ||
189 | = case typ of | ||
190 | 0x83 -> query $ onion bs saddr (pure OnionAnnounce) -- Announce Request | ||
191 | 0x85 -> query $ onion bs saddr (OnionToRoute <$> getPublicKey) -- Onion Data Request | ||
192 | _ -> case flip runGet bs <$> getOnionReply typ of | ||
193 | Just (Right msg@(OnionAnnounceResponse n8 _ _)) -> do | ||
194 | maddr <- lookupSender saddr n8 | ||
195 | maybe (return right) -- Response unsolicited or too late. | ||
196 | (return . Left . \od -> (msg,od)) | ||
197 | maddr | ||
198 | Just (Right msg@(OnionToRouteResponse asym)) -> do | ||
199 | let ni = asymNodeInfo saddr asym | ||
200 | return $ Left (msg, OnionDestination SearchingAlias ni Nothing) | ||
201 | _ -> return right | ||
202 | |||
203 | getOnionReply :: Word8 -> Maybe (Get (OnionMessage Encrypted)) | ||
204 | getOnionReply 0x84 = Just $ OnionAnnounceResponse <$> get <*> get <*> get | ||
205 | getOnionReply 0x86 = Just $ OnionToRouteResponse <$> getOnionAsymm | ||
206 | getOnionReply _ = Nothing | ||
207 | |||
208 | putOnionMsg :: OnionMessage Encrypted -> Put | ||
209 | putOnionMsg (OnionAnnounce a) = putOnionAsymm 0x83 (return ()) a | ||
210 | putOnionMsg (OnionToRoute pubkey a) = putOnionAsymm 0x85 (putPublicKey pubkey) a | ||
211 | putOnionMsg (OnionAnnounceResponse n8 n24 x) = put (0x84 :: Word8) >> put n8 >> put n24 >> put x | ||
212 | putOnionMsg (OnionToRouteResponse a) = putOnionAsymm 0x86 (return ()) a | ||
213 | |||
214 | newtype RouteId = RouteId Int | ||
215 | deriving Show | ||
216 | |||
217 | |||
218 | -- We used to derive the RouteId from the Nonce8 associated with the query. | ||
219 | -- This is problematic because a nonce generated by toxcore will not validate | ||
220 | -- if it is received via a different route than it was issued. This is | ||
221 | -- described by the Tox spec: | ||
222 | -- | ||
223 | -- Toxcore generates `ping_id`s by taking a 32 byte sha hash of the current | ||
224 | -- time, some secret bytes generated when the instance is created, the | ||
225 | -- current time divided by a 20 second timeout, the public key of the | ||
226 | -- requester and the source ip/port that the packet was received from. Since | ||
227 | -- the ip/port that the packet was received from is in the `ping_id`, the | ||
228 | -- announce packets being sent with a ping id must be sent using the same | ||
229 | -- path as the packet that we received the `ping_id` from or announcing will | ||
230 | -- fail. | ||
231 | -- | ||
232 | -- The original idea was: | ||
233 | -- | ||
234 | -- > routeId :: Nonce8 -> RouteId | ||
235 | -- > routeId (Nonce8 w8) = RouteId $ mod (fromIntegral w8) 12 | ||
236 | -- | ||
237 | -- Instead, we'll just hash the destination node id. | ||
238 | routeId :: NodeId -> RouteId | ||
239 | routeId nid = RouteId $ mod (hash nid) 12 | ||
240 | |||
241 | |||
242 | |||
243 | forwardOnions :: TransportCrypto -> UDPTransport -> (Int -> OnionMessage Encrypted -> IO ()) {- ^ TCP relay send -} -> UDPTransport | ||
244 | forwardOnions crypto udp sendTCP = udp { awaitMessage = forwardAwait crypto udp sendTCP } | ||
245 | |||
246 | forwardAwait :: TransportCrypto -> UDPTransport -> (Int -> OnionMessage Encrypted -> IO ()) {- ^ TCP relay send -} -> HandleLo a -> IO a | ||
247 | forwardAwait crypto udp sendTCP kont = do | ||
248 | fix $ \another -> do | ||
249 | awaitMessage udp $ \case | ||
250 | m@(Just (Right (bs,saddr))) -> case B.head bs of | ||
251 | 0x80 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N0) crypto (Addressed saddr) udp another | ||
252 | 0x81 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N1) crypto (Addressed saddr) udp another | ||
253 | 0x82 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N2) crypto (Addressed saddr) udp another | ||
254 | 0x8c -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N3) crypto saddr udp sendTCP another | ||
255 | 0x8d -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N2) crypto saddr udp sendTCP another | ||
256 | 0x8e -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N1) crypto saddr udp sendTCP another | ||
257 | _ -> kont m | ||
258 | m -> kont m | ||
259 | |||
260 | forward :: forall c b b1. (Serialize b, Show b) => | ||
261 | (Maybe (Either String b1) -> c) -> ByteString -> (b -> c) -> c | ||
262 | forward kont bs f = either (kont . Just . Left) f $ decode $ B.tail bs | ||
263 | |||
264 | class SumToThree a b | ||
265 | |||
266 | instance SumToThree N0 N3 | ||
267 | instance SumToThree (S a) b => SumToThree a (S b) | ||
268 | |||
269 | class ( Serialize (ReturnPath n) | ||
270 | , Serialize (ReturnPath (S n)) | ||
271 | , Serialize (Forwarding (ThreeMinus (S n)) (OnionMessage Encrypted)) | ||
272 | , ThreeMinus n ~ S (ThreeMinus (S n)) | ||
273 | ) => LessThanThree n | ||
274 | |||
275 | instance LessThanThree N0 | ||
276 | instance LessThanThree N1 | ||
277 | instance LessThanThree N2 | ||
278 | |||
279 | type family ThreeMinus n where | ||
280 | ThreeMinus N3 = N0 | ||
281 | ThreeMinus N2 = N1 | ||
282 | ThreeMinus N1 = N2 | ||
283 | ThreeMinus N0 = N3 | ||
284 | |||
285 | -- n = 0, 1, 2 | ||
286 | data OnionRequest n = OnionRequest | ||
287 | { onionNonce :: Nonce24 | ||
288 | , onionForward :: Forwarding (ThreeMinus n) (OnionMessage Encrypted) | ||
289 | , pathFromOwner :: ReturnPath n | ||
290 | } | ||
291 | deriving (Eq,Ord) | ||
292 | |||
293 | |||
294 | {- | ||
295 | instance (Typeable n, Sized (ReturnPath n), Serialize (ReturnPath n) | ||
296 | , Serialize (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | ||
297 | ) => Data (OnionRequest n) where | ||
298 | gfoldl f z txt = z (either error id . S.decode) `f` S.encode txt | ||
299 | toConstr _ = error "OnionRequest.toConstr" | ||
300 | gunfold _ _ = error "OnionRequest.gunfold" | ||
301 | #if MIN_VERSION_base(4,2,0) | ||
302 | dataTypeOf _ = mkNoRepType "Network.Tox.Onion.Transport.OnionRequest" | ||
303 | #else | ||
304 | dataTypeOf _ = mkNorepType "Network.Tox.Onion.Transport.OnionRequest" | ||
305 | #endif | ||
306 | -} | ||
307 | |||
308 | |||
309 | instance (Typeable n, Serialize (ReturnPath n)) => Data (OnionResponse n) where | ||
310 | gfoldl f z txt = z (either error id . S.decode) `f` S.encode txt | ||
311 | toConstr _ = error "OnionResponse.toConstr" | ||
312 | gunfold _ _ = error "OnionResponse.gunfold" | ||
313 | #if MIN_VERSION_base(4,2,0) | ||
314 | dataTypeOf _ = mkNoRepType "Network.Tox.Onion.Transport.OnionResponse" | ||
315 | #else | ||
316 | dataTypeOf _ = mkNorepType "Network.Tox.Onion.Transport.OnionResponse" | ||
317 | #endif | ||
318 | |||
319 | deriving instance ( Show (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | ||
320 | , KnownNat (PeanoNat n) | ||
321 | ) => Show (OnionRequest n) | ||
322 | |||
323 | instance Sized (OnionRequest N0) where -- N1 and N2 are the same, N3 does not encode the nonce. | ||
324 | size = contramap onionNonce size | ||
325 | <> contramap onionForward size | ||
326 | <> contramap pathFromOwner size | ||
327 | |||
328 | instance ( Serialize (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | ||
329 | , Sized (ReturnPath n) | ||
330 | , Serialize (ReturnPath n) | ||
331 | , Typeable n | ||
332 | ) => Serialize (OnionRequest n) where | ||
333 | get = do | ||
334 | -- TODO share code with 'getOnionRequest' | ||
335 | n24 <- case eqT :: Maybe (n :~: N3) of | ||
336 | Just Refl -> return $ Nonce24 zeros24 | ||
337 | Nothing -> get | ||
338 | cnt <- remaining | ||
339 | let fwdsize = case size :: Size (ReturnPath n) of ConstSize n -> cnt - n | ||
340 | fwd <- isolate fwdsize get | ||
341 | rpath <- get | ||
342 | return $ OnionRequest n24 fwd rpath | ||
343 | put (OnionRequest n f p) = maybe (put n) (\Refl -> return ()) (eqT :: Maybe (n :~: N3)) >> put f >> put p | ||
344 | |||
345 | -- getRequest :: _ | ||
346 | -- getRequest = OnionRequest <$> get <*> get <*> get | ||
347 | |||
348 | -- n = 1, 2, 3 | ||
349 | -- Attributed (Encrypted ( | ||
350 | |||
351 | data OnionResponse n = OnionResponse | ||
352 | { pathToOwner :: ReturnPath n | ||
353 | , msgToOwner :: OnionMessage Encrypted | ||
354 | } | ||
355 | deriving (Eq,Ord) | ||
356 | |||
357 | deriving instance KnownNat (PeanoNat n) => Show (OnionResponse n) | ||
358 | |||
359 | instance ( Serialize (ReturnPath n) ) => Serialize (OnionResponse n) where | ||
360 | get = OnionResponse <$> get <*> (get >>= fromMaybe (fail "illegal onion forwarding") | ||
361 | . getOnionReply) | ||
362 | put (OnionResponse p m) = put p >> putOnionMsg m | ||
363 | |||
364 | instance (Sized (ReturnPath n)) => Sized (OnionResponse (S n)) where | ||
365 | size = contramap pathToOwner size <> contramap msgToOwner size | ||
366 | |||
367 | data Addressed a = Addressed { sockAddr :: SockAddr, unaddressed :: a } | ||
368 | | TCPIndex { tcpIndex :: Int, unaddressed :: a } | ||
369 | deriving (Eq,Ord,Show) | ||
370 | |||
371 | instance (Typeable a, Serialize a) => Data (Addressed a) where | ||
372 | gfoldl f z a = z (either error id . S.decode) `f` S.encode a | ||
373 | toConstr _ = error "Addressed.toConstr" | ||
374 | gunfold _ _ = error "Addressed.gunfold" | ||
375 | #if MIN_VERSION_base(4,2,0) | ||
376 | dataTypeOf _ = mkNoRepType "Network.Tox.Onion.Transport.Addressed" | ||
377 | #else | ||
378 | dataTypeOf _ = mkNorepType "Network.Tox.Onion.Transport.Addressed" | ||
379 | #endif | ||
380 | |||
381 | instance Sized a => Sized (Addressed a) where | ||
382 | size = case size :: Size a of | ||
383 | ConstSize n -> ConstSize $ 1{-family-} + 16{-ip-} + 2{-port-} + n | ||
384 | VarSize f -> VarSize $ \x -> 1{-family-} + 16{-ip-} + 2{-port-} + f (unaddressed x) | ||
385 | |||
386 | getForwardAddr :: S.Get SockAddr | ||
387 | getForwardAddr = do | ||
388 | addrfam <- S.get :: S.Get Word8 | ||
389 | ip <- getIP addrfam | ||
390 | case ip of IPv4 _ -> S.skip 12 -- compliant peers would zero-fill this. | ||
391 | IPv6 _ -> return () | ||
392 | port <- S.get :: S.Get PortNumber | ||
393 | return $ setPort port $ toSockAddr ip | ||
394 | |||
395 | |||
396 | putForwardAddr :: SockAddr -> S.Put | ||
397 | putForwardAddr saddr = fromMaybe (return $ error "unsupported SockAddr family") $ do | ||
398 | port <- sockAddrPort saddr | ||
399 | ip <- fromSockAddr $ either id id $ either4or6 saddr | ||
400 | return $ do | ||
401 | case ip of | ||
402 | IPv4 ip4 -> S.put (0x02 :: Word8) >> S.put ip4 >> S.putByteString (B.replicate 12 0) | ||
403 | IPv6 ip6 -> S.put (0x0a :: Word8) >> S.put ip6 | ||
404 | S.put port | ||
405 | |||
406 | addrToIndex :: SockAddr -> Int | ||
407 | addrToIndex (SockAddrInet6 _ _ (lo, hi, _, _) _) = | ||
408 | if fitsInInt (Proxy :: Proxy Word64) | ||
409 | then fromIntegral lo + (fromIntegral hi `shiftL` 32) | ||
410 | else fromIntegral lo | ||
411 | addrToIndex _ = 0 | ||
412 | |||
413 | indexToAddr :: Int -> SockAddr | ||
414 | indexToAddr x = SockAddrInet6 0 0 (fromIntegral x, fromIntegral (x `shiftR` 32),0,0) 0 | ||
415 | |||
416 | -- Note, toxcore would check an address family byte here to detect a TCP-bound | ||
417 | -- packet, but we instead use the IPv6 id and rely on the port number being | ||
418 | -- zero. Since it will be symmetrically encrypted for our eyes only, it's not | ||
419 | -- important to conform on this point. | ||
420 | instance Serialize a => Serialize (Addressed a) where | ||
421 | get = do saddr <- getForwardAddr | ||
422 | a <- get | ||
423 | case sockAddrPort saddr of | ||
424 | Just 0 -> return $ TCPIndex (addrToIndex saddr) a | ||
425 | _ -> return $ Addressed saddr a | ||
426 | put (Addressed addr x) = putForwardAddr addr >> put x | ||
427 | put (TCPIndex idx x) = putForwardAddr (indexToAddr idx) >> put x | ||
428 | |||
429 | data N0 | ||
430 | data S n | ||
431 | type N1 = S N0 | ||
432 | type N2 = S N1 | ||
433 | type N3 = S N2 | ||
434 | |||
435 | deriving instance Data N0 | ||
436 | deriving instance Data n => Data (S n) | ||
437 | |||
438 | class KnownPeanoNat n where | ||
439 | peanoVal :: p n -> Int | ||
440 | |||
441 | instance KnownPeanoNat N0 where | ||
442 | peanoVal _ = 0 | ||
443 | instance KnownPeanoNat n => KnownPeanoNat (S n) where | ||
444 | peanoVal _ = 1 + peanoVal (Proxy :: Proxy n) | ||
445 | |||
446 | type family PeanoNat p where | ||
447 | PeanoNat N0 = 0 | ||
448 | PeanoNat (S n) = 1 + PeanoNat n | ||
449 | |||
450 | data ReturnPath n where | ||
451 | NoReturnPath :: ReturnPath N0 | ||
452 | ReturnPath :: Nonce24 -> Encrypted (Addressed (ReturnPath n)) -> ReturnPath (S n) | ||
453 | |||
454 | deriving instance Eq (ReturnPath n) | ||
455 | deriving instance Ord (ReturnPath n) | ||
456 | |||
457 | -- Size: 59 = 1(family) + 16(ip) + 2(port) +16(mac) + 24(nonce) | ||
458 | instance Sized (ReturnPath N0) where size = ConstSize 0 | ||
459 | instance Sized (ReturnPath n) => Sized (ReturnPath (S n)) where | ||
460 | size = ConstSize 59 <> contramap (\x -> let _ = x :: ReturnPath (S n) | ||
461 | in error "non-constant ReturnPath size") | ||
462 | (size :: Size (ReturnPath n)) | ||
463 | |||
464 | {- | ||
465 | instance KnownNat (PeanoNat n) => Sized (ReturnPath n) where | ||
466 | size = ConstSize $ 59 * fromIntegral (natVal (Proxy :: Proxy (PeanoNat n))) | ||
467 | -} | ||
468 | |||
469 | instance Serialize (ReturnPath N0) where get = pure NoReturnPath | ||
470 | put NoReturnPath = pure () | ||
471 | |||
472 | instance Serialize (ReturnPath N1) where | ||
473 | get = ReturnPath <$> get <*> get | ||
474 | put (ReturnPath n24 p) = put n24 >> put p | ||
475 | |||
476 | instance (Sized (ReturnPath n), Serialize (ReturnPath n)) => Serialize (ReturnPath (S (S n))) where | ||
477 | get = ReturnPath <$> get <*> get | ||
478 | put (ReturnPath n24 p) = put n24 >> put p | ||
479 | |||
480 | |||
481 | {- | ||
482 | -- This doesn't work because it tried to infer it for (0 - 1) | ||
483 | instance (Serialize (Encrypted (Addressed (ReturnPath (n - 1))))) => Serialize (ReturnPath n) where | ||
484 | get = ReturnPath <$> get <*> get | ||
485 | put (ReturnPath n24 p) = put n24 >> put p | ||
486 | -} | ||
487 | |||
488 | instance KnownNat (PeanoNat n) => Show (ReturnPath n) where | ||
489 | show rpath = "ReturnPath" ++ show (natVal (Proxy :: Proxy (PeanoNat n))) | ||
490 | |||
491 | |||
492 | -- instance KnownNat n => Serialize (ReturnPath n) where | ||
493 | -- -- Size: 59 = 1(family) + 16(ip) + 2(port) +16(mac) + 24(nonce) | ||
494 | -- get = ReturnPath <$> getBytes ( 59 * (fromIntegral $ natVal $ Proxy @n) ) | ||
495 | -- put (ReturnPath bs) = putByteString bs | ||
496 | |||
497 | |||
498 | data Forwarding n msg where | ||
499 | NotForwarded :: msg -> Forwarding N0 msg | ||
500 | Forwarding :: PublicKey -> Encrypted (Addressed (Forwarding n msg)) -> Forwarding (S n) msg | ||
501 | |||
502 | deriving instance Eq msg => Eq (Forwarding n msg) | ||
503 | deriving instance Ord msg => Ord (Forwarding n msg) | ||
504 | |||
505 | instance Show msg => Show (Forwarding N0 msg) where | ||
506 | show (NotForwarded x) = "NotForwarded "++show x | ||
507 | |||
508 | instance ( KnownNat (PeanoNat (S n)) | ||
509 | , Show (Encrypted (Addressed (Forwarding n msg))) | ||
510 | ) => Show (Forwarding (S n) msg) where | ||
511 | show (Forwarding k a) = unwords [ "Forwarding" | ||
512 | , "("++show (natVal (Proxy :: Proxy (PeanoNat (S n))))++")" | ||
513 | , show (key2id k) | ||
514 | , show a | ||
515 | ] | ||
516 | |||
517 | instance Sized msg => Sized (Forwarding N0 msg) | ||
518 | where size = case size :: Size msg of | ||
519 | ConstSize n -> ConstSize n | ||
520 | VarSize f -> VarSize $ \(NotForwarded x) -> f x | ||
521 | |||
522 | instance Sized (Forwarding n msg) => Sized (Forwarding (S n) msg) | ||
523 | where size = ConstSize 32 | ||
524 | <> contramap (\(Forwarding _ e) -> e) | ||
525 | (size :: Size (Encrypted (Addressed (Forwarding n msg)))) | ||
526 | |||
527 | instance Serialize msg => Serialize (Forwarding N0 msg) where | ||
528 | get = NotForwarded <$> get | ||
529 | put (NotForwarded msg) = put msg | ||
530 | |||
531 | instance (Serialize (Encrypted (Addressed (Forwarding n msg)))) => Serialize (Forwarding (S n) msg) where | ||
532 | get = Forwarding <$> getPublicKey <*> get | ||
533 | put (Forwarding k x) = putPublicKey k >> put x | ||
534 | |||
535 | {- | ||
536 | rewrap :: (ThreeMinus n ~ S (ThreeMinus (S n)), | ||
537 | Serialize (ReturnPath n), | ||
538 | Serialize | ||
539 | (Forwarding (ThreeMinus (S n)) (OnionMessage Encrypted))) => | ||
540 | TransportCrypto | ||
541 | -> (forall x. x -> Addressed x) | ||
542 | -> OnionRequest n | ||
543 | -> IO (Either String (OnionRequest (S n), SockAddr)) | ||
544 | rewrap crypto saddr (OnionRequest nonce msg rpath) = do | ||
545 | (sym, snonce) <- atomically ( (,) <$> transportSymmetric crypto | ||
546 | <*> transportNewNonce crypto ) | ||
547 | peeled <- peelOnion crypto nonce msg | ||
548 | return $ peeled >>= \case | ||
549 | Addressed dst msg' | ||
550 | -> Right (OnionRequest nonce msg' $ wrapSymmetric sym snonce saddr rpath, dst) | ||
551 | _ -> Left "Onion forward to TCP client?" | ||
552 | -} | ||
553 | |||
554 | handleOnionRequest :: forall a proxy n. | ||
555 | ( LessThanThree n | ||
556 | , KnownPeanoNat n | ||
557 | , Sized (ReturnPath n) | ||
558 | , Typeable n | ||
559 | ) => proxy n -> TransportCrypto -> (forall x. x -> Addressed x) -> UDPTransport -> IO a -> OnionRequest n -> IO a | ||
560 | handleOnionRequest proxy crypto saddr udp kont (OnionRequest nonce msg rpath) = do | ||
561 | let n = peanoVal rpath | ||
562 | dput XOnion $ "handleOnionRequest " ++ show n | ||
563 | (sym, snonce) <- atomically ( (,) <$> transportSymmetric crypto | ||
564 | <*> transportNewNonce crypto ) | ||
565 | peeled <- peelOnion crypto nonce msg | ||
566 | let showDestination = case saddr () of | ||
567 | Addressed a _ -> either show show $ either4or6 a | ||
568 | TCPIndex i _ -> "TCP" ++ show [i] | ||
569 | |||
570 | case peeled of | ||
571 | Left e -> do | ||
572 | dput XOnion $ unwords [ "peelOnion:", show n, showDestination, e] | ||
573 | kont | ||
574 | Right (Addressed dst msg') -> do | ||
575 | dput XOnion $ unwords [ "peelOnion:", show n, showDestination, "-->", either show show (either4or6 dst), "SUCCESS"] | ||
576 | sendMessage udp dst (runPut $ putRequest $ OnionRequest nonce msg' $ wrapSymmetric sym snonce saddr rpath) | ||
577 | kont | ||
578 | Right (TCPIndex {}) -> do | ||
579 | dput XUnexpected "handleOnionRequest: Onion forward to TCP client?" | ||
580 | kont | ||
581 | |||
582 | wrapSymmetric :: Serialize (ReturnPath n) => | ||
583 | SymmetricKey -> Nonce24 -> (forall x. x -> Addressed x) -> ReturnPath n -> ReturnPath (S n) | ||
584 | wrapSymmetric sym n saddr rpath = ReturnPath n $ encryptSymmetric sym n (encodePlain $ saddr rpath) | ||
585 | |||
586 | peelSymmetric :: Serialize (Addressed (ReturnPath n)) | ||
587 | => SymmetricKey -> ReturnPath (S n) -> Either String (Addressed (ReturnPath n)) | ||
588 | peelSymmetric sym (ReturnPath nonce e) = decryptSymmetric sym nonce e >>= decodePlain | ||
589 | |||
590 | |||
591 | peelOnion :: Serialize (Addressed (Forwarding n t)) | ||
592 | => TransportCrypto | ||
593 | -> Nonce24 | ||
594 | -> Forwarding (S n) t | ||
595 | -> IO (Either String (Addressed (Forwarding n t))) | ||
596 | peelOnion crypto nonce (Forwarding k fwd) = do | ||
597 | fmap runIdentity . uncomposed <$> decryptMessage crypto (dhtKey crypto) nonce (Right $ Asymm k nonce fwd) | ||
598 | |||
599 | handleOnionResponse :: (KnownPeanoNat n, Sized (ReturnPath n), Serialize (ReturnPath n), Typeable n) => | ||
600 | proxy (S n) | ||
601 | -> TransportCrypto | ||
602 | -> SockAddr | ||
603 | -> UDPTransport | ||
604 | -> (Int -> OnionMessage Encrypted -> IO ()) -- ^ TCP-relay onion send. | ||
605 | -> IO a | ||
606 | -> OnionResponse (S n) | ||
607 | -> IO a | ||
608 | handleOnionResponse proxy crypto saddr udp sendTCP kont (OnionResponse path msg) = do | ||
609 | sym <- atomically $ transportSymmetric crypto | ||
610 | case peelSymmetric sym path of | ||
611 | Left e -> do | ||
612 | -- todo report encryption error | ||
613 | let n = peanoVal path | ||
614 | dput XMisc $ unwords [ "peelSymmetric:", show n, either show show (either4or6 saddr), e] | ||
615 | kont | ||
616 | Right (Addressed dst path') -> do | ||
617 | sendMessage udp dst (runPut $ putResponse $ OnionResponse path' msg) | ||
618 | kont | ||
619 | Right (TCPIndex dst path') -> do | ||
620 | case peanoVal path' of | ||
621 | 0 -> sendTCP dst msg | ||
622 | n -> dput XUnexpected $ "handleOnionResponse: TCP-bound OnionResponse" ++ show n ++ " not supported." | ||
623 | kont | ||
624 | |||
625 | |||
626 | data AnnounceRequest = AnnounceRequest | ||
627 | { announcePingId :: Nonce32 -- Ping ID | ||
628 | , announceSeeking :: NodeId -- Public key we are searching for | ||
629 | , announceKey :: NodeId -- Public key that we want those sending back data packets to use | ||
630 | } | ||
631 | deriving Show | ||
632 | |||
633 | instance Sized AnnounceRequest where size = ConstSize (32*3) | ||
634 | |||
635 | instance S.Serialize AnnounceRequest where | ||
636 | get = AnnounceRequest <$> S.get <*> S.get <*> S.get | ||
637 | put (AnnounceRequest p s k) = S.put (p,s,k) | ||
638 | |||
639 | getOnionRequest :: Sized msg => Get (Asymm (Encrypted msg), ReturnPath N3) | ||
640 | getOnionRequest = do | ||
641 | -- Assumes return path is constant size so that we can isolate | ||
642 | -- the variable-sized prefix. | ||
643 | cnt <- remaining | ||
644 | a <- isolate (case size :: Size (ReturnPath N3) of ConstSize n -> cnt - n) | ||
645 | getAliasedAsymm | ||
646 | path <- get | ||
647 | return (a,path) | ||
648 | |||
649 | putRequest :: ( KnownPeanoNat n | ||
650 | , Serialize (OnionRequest n) | ||
651 | , Typeable n | ||
652 | ) => OnionRequest n -> Put | ||
653 | putRequest req = do | ||
654 | let tag = 0x80 + fromIntegral (peanoVal req) | ||
655 | when (tag <= 0x82) (putWord8 tag) | ||
656 | put req | ||
657 | |||
658 | putResponse :: (KnownPeanoNat n, Serialize (OnionResponse n)) => OnionResponse n -> Put | ||
659 | putResponse resp = do | ||
660 | let tag = 0x8f - fromIntegral (peanoVal resp) | ||
661 | -- OnionResponse N0 is an alias for the OnionMessage Encrypted type which includes a tag | ||
662 | -- in it's Serialize instance. | ||
663 | when (tag /= 0x8f) (putWord8 tag) | ||
664 | put resp | ||
665 | |||
666 | |||
667 | data KeyRecord = NotStored Nonce32 | ||
668 | | SendBackKey PublicKey | ||
669 | | Acknowledged Nonce32 | ||
670 | deriving Show | ||
671 | |||
672 | instance Sized KeyRecord where size = ConstSize 33 | ||
673 | |||
674 | instance S.Serialize KeyRecord where | ||
675 | get = do | ||
676 | is_stored <- S.get :: S.Get Word8 | ||
677 | case is_stored of | ||
678 | 1 -> SendBackKey <$> getPublicKey | ||
679 | 2 -> Acknowledged <$> S.get | ||
680 | _ -> NotStored <$> S.get | ||
681 | put (NotStored n32) = S.put (0 :: Word8) >> S.put n32 | ||
682 | put (SendBackKey key) = S.put (1 :: Word8) >> putPublicKey key | ||
683 | put (Acknowledged n32) = S.put (2 :: Word8) >> S.put n32 | ||
684 | |||
685 | data AnnounceResponse = AnnounceResponse | ||
686 | { is_stored :: KeyRecord | ||
687 | , announceNodes :: SendNodes | ||
688 | } | ||
689 | deriving Show | ||
690 | |||
691 | instance Sized AnnounceResponse where | ||
692 | size = contramap is_stored size <> contramap announceNodes size | ||
693 | |||
694 | getNodeList :: S.Get [NodeInfo] | ||
695 | getNodeList = do | ||
696 | n <- S.get | ||
697 | (:) n <$> (getNodeList <|> pure []) | ||
698 | |||
699 | instance S.Serialize AnnounceResponse where | ||
700 | get = AnnounceResponse <$> S.get <*> (SendNodes <$> getNodeList) | ||
701 | put (AnnounceResponse st (SendNodes ns)) = S.put st >> mapM_ S.put ns | ||
702 | |||
703 | data DataToRoute = DataToRoute | ||
704 | { dataFromKey :: PublicKey -- Real public key of sender | ||
705 | , dataToRoute :: Encrypted OnionData -- (Word8,ByteString) -- DHTPK 0x9c | ||
706 | } | ||
707 | |||
708 | instance Sized DataToRoute where | ||
709 | size = ConstSize 32 <> contramap dataToRoute size | ||
710 | |||
711 | instance Serialize DataToRoute where | ||
712 | get = DataToRoute <$> getPublicKey <*> get | ||
713 | put (DataToRoute k dta) = putPublicKey k >> put dta | ||
714 | |||
715 | data OnionData | ||
716 | = -- | type 0x9c | ||
717 | -- | ||
718 | -- We send this packet every 30 seconds if there is more than one peer (in | ||
719 | -- the 8) that says they our friend is announced on them. This packet can | ||
720 | -- also be sent through the DHT module as a DHT request packet (see DHT) if | ||
721 | -- we know the DHT public key of the friend and are looking for them in the | ||
722 | -- DHT but have not connected to them yet. 30 second is a reasonable | ||
723 | -- timeout to not flood the network with too many packets while making sure | ||
724 | -- the other will eventually receive the packet. Since packets are sent | ||
725 | -- through every peer that knows the friend, resending it right away | ||
726 | -- without waiting has a high likelihood of failure as the chances of | ||
727 | -- packet loss happening to all (up to to 8) packets sent is low. | ||
728 | -- | ||
729 | -- If a friend is online and connected to us, the onion will stop all of | ||
730 | -- its actions for that friend. If the peer goes offline it will restart | ||
731 | -- searching for the friend as if toxcore was just started. | ||
732 | OnionDHTPublicKey DHTPublicKey | ||
733 | | -- | type 0x20 | ||
734 | -- | ||
735 | -- | ||
736 | OnionFriendRequest FriendRequest -- 0x20 | ||
737 | deriving (Eq,Show) | ||
738 | |||
739 | instance Sized OnionData where | ||
740 | size = VarSize $ \case | ||
741 | OnionDHTPublicKey dhtpk -> case size of | ||
742 | ConstSize n -> n -- Override because OnionData probably | ||
743 | -- should be treated as variable sized. | ||
744 | VarSize f -> f dhtpk | ||
745 | -- FIXME: inconsitantly, we have to add in the tag byte for this case. | ||
746 | OnionFriendRequest req -> 1 + case size of | ||
747 | ConstSize n -> n | ||
748 | VarSize f -> f req | ||
749 | |||
750 | instance Serialize OnionData where | ||
751 | get = do | ||
752 | tag <- get | ||
753 | case tag :: Word8 of | ||
754 | 0x9c -> OnionDHTPublicKey <$> get | ||
755 | 0x20 -> OnionFriendRequest <$> get | ||
756 | _ -> fail $ "Unknown onion data: "++show tag | ||
757 | put (OnionDHTPublicKey dpk) = put (0x9c :: Word8) >> put dpk | ||
758 | put (OnionFriendRequest fr) = put (0x20 :: Word8) >> put fr | ||
759 | |||
760 | selectKey :: TransportCrypto -> OnionMessage f -> OnionDestination r -> IO (SecretKey, PublicKey) | ||
761 | selectKey crypto _ rpath@(OnionDestination (AnnouncingAlias skey pkey) _ _) | ||
762 | = return (skey, pkey) | ||
763 | selectKey crypto msg rpath = return $ aliasKey crypto rpath | ||
764 | |||
765 | encrypt :: TransportCrypto | ||
766 | -> OnionMessage Identity | ||
767 | -> OnionDestination r | ||
768 | -> IO (OnionMessage Encrypted, OnionDestination r) | ||
769 | encrypt crypto msg rpath = do | ||
770 | (skey,pkey) <- selectKey crypto msg rpath -- source key | ||
771 | let okey = onionKey rpath -- destination key | ||
772 | encipher1 :: Serialize a => SecretKey -> PublicKey -> Nonce24 -> a -> (IO ∘ Encrypted) a | ||
773 | encipher1 sk pk n a = Composed $ do | ||
774 | secret <- lookupSharedSecret crypto sk pk n | ||
775 | return $ ToxCrypto.encrypt secret $ encodePlain a | ||
776 | encipher :: Serialize a => Nonce24 -> Either (Identity a) (Asymm (Identity a)) -> (IO ∘ Encrypted) a | ||
777 | encipher n d = encipher1 skey okey n $ either runIdentity (runIdentity . asymmData) d | ||
778 | m <- sequenceMessage $ transcode encipher msg | ||
779 | return (m, rpath) | ||
780 | |||
781 | decrypt :: TransportCrypto -> OnionMessage Encrypted -> OnionDestination r -> IO (Either String (OnionMessage Identity, OnionDestination r)) | ||
782 | decrypt crypto msg addr = do | ||
783 | (skey,pkey) <- selectKey crypto msg addr | ||
784 | let decipher1 :: Serialize a => | ||
785 | TransportCrypto -> SecretKey -> Nonce24 | ||
786 | -> Either (PublicKey,Encrypted a) (Asymm (Encrypted a)) | ||
787 | -> (IO ∘ Either String ∘ Identity) a | ||
788 | decipher1 crypto k n arg = Composed $ do | ||
789 | let (sender,e) = either id (senderKey &&& asymmData) arg | ||
790 | secret <- lookupSharedSecret crypto k sender n | ||
791 | return $ Composed $ do | ||
792 | plain <- ToxCrypto.decrypt secret e | ||
793 | Identity <$> decodePlain plain | ||
794 | decipher :: Serialize a | ||
795 | => Nonce24 -> Either (Encrypted a) (Asymm (Encrypted a)) | ||
796 | -> (IO ∘ Either String ∘ Identity) a | ||
797 | decipher = (\n -> decipher1 crypto skey n . left (senderkey addr)) | ||
798 | foo <- sequenceMessage $ transcode decipher msg | ||
799 | return $ do | ||
800 | msg <- sequenceMessage foo | ||
801 | Right (msg, addr) | ||
802 | |||
803 | senderkey :: OnionDestination r -> t -> (PublicKey, t) | ||
804 | senderkey addr e = (onionKey addr, e) | ||
805 | |||
806 | aliasKey :: TransportCrypto -> OnionDestination r -> (SecretKey,PublicKey) | ||
807 | aliasKey crypto (OnionToOwner {}) = (transportSecret &&& transportPublic) crypto | ||
808 | aliasKey crypto (OnionDestination {}) = (onionAliasSecret &&& onionAliasPublic) crypto | ||
809 | |||
810 | dhtKey :: TransportCrypto -> (SecretKey,PublicKey) | ||
811 | dhtKey crypto = (transportSecret &&& transportPublic) crypto | ||
812 | |||
813 | decryptMessage :: Serialize x => | ||
814 | TransportCrypto | ||
815 | -> (SecretKey,PublicKey) | ||
816 | -> Nonce24 | ||
817 | -> Either (PublicKey, Encrypted x) | ||
818 | (Asymm (Encrypted x)) | ||
819 | -> IO ((Either String ∘ Identity) x) | ||
820 | decryptMessage crypto (sk,pk) n arg = do | ||
821 | let (sender,e) = either id (senderKey &&& asymmData) arg | ||
822 | plain = Composed . fmap Identity . (>>= decodePlain) | ||
823 | secret <- lookupSharedSecret crypto sk sender n | ||
824 | return $ plain $ ToxCrypto.decrypt secret e | ||
825 | |||
826 | sequenceMessage :: Applicative m => OnionMessage (m ∘ f) -> m (OnionMessage f) | ||
827 | sequenceMessage (OnionAnnounce a) = fmap OnionAnnounce $ sequenceA $ fmap uncomposed a | ||
828 | sequenceMessage (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 <$> uncomposed dta | ||
829 | sequenceMessage (OnionToRoute pub a) = pure $ OnionToRoute pub a | ||
830 | sequenceMessage (OnionToRouteResponse a) = pure $ OnionToRouteResponse a | ||
831 | -- sequenceMessage (OnionToRouteResponse a) = fmap OnionToRouteResponse $ sequenceA $ fmap uncomposed a | ||
832 | |||
833 | transcode :: forall f g. (forall a. Serialize a => Nonce24 -> Either (f a) (Asymm (f a)) -> g a) -> OnionMessage f -> OnionMessage g | ||
834 | transcode f (OnionAnnounce a) = OnionAnnounce $ a { asymmData = f (asymmNonce a) (Right a) } | ||
835 | transcode f (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 $ f n24 $ Left dta | ||
836 | transcode f (OnionToRoute pub a) = OnionToRoute pub a | ||
837 | transcode f (OnionToRouteResponse a) = OnionToRouteResponse a | ||
838 | -- transcode f (OnionToRouteResponse a) = OnionToRouteResponse $ a { asymmData = f (asymmNonce a) (Right a) } | ||
839 | |||
840 | |||
841 | data OnionRoute = OnionRoute | ||
842 | { routeAliasA :: SecretKey | ||
843 | , routeAliasB :: SecretKey | ||
844 | , routeAliasC :: SecretKey | ||
845 | , routeNodeA :: NodeInfo | ||
846 | , routeNodeB :: NodeInfo | ||
847 | , routeNodeC :: NodeInfo | ||
848 | , routeRelayPort :: Maybe PortNumber | ||
849 | } | ||
850 | |||
851 | |||
852 | wrapOnion :: Serialize (Forwarding n msg) => | ||
853 | TransportCrypto | ||
854 | -> SecretKey | ||
855 | -> Nonce24 | ||
856 | -> PublicKey | ||
857 | -> SockAddr | ||
858 | -> Forwarding n msg | ||
859 | -> IO (Forwarding (S n) msg) | ||
860 | wrapOnion crypto skey nonce destkey saddr fwd = do | ||
861 | let plain = encodePlain $ Addressed saddr fwd | ||
862 | secret <- lookupSharedSecret crypto skey destkey nonce | ||
863 | return $ Forwarding (toPublic skey) $ ToxCrypto.encrypt secret plain | ||
864 | |||
865 | wrapOnionPure :: Serialize (Forwarding n msg) => | ||
866 | SecretKey | ||
867 | -> ToxCrypto.State | ||
868 | -> SockAddr | ||
869 | -> Forwarding n msg | ||
870 | -> Forwarding (S n) msg | ||
871 | wrapOnionPure skey st saddr fwd = Forwarding (toPublic skey) (ToxCrypto.encrypt st plain) | ||
872 | where | ||
873 | plain = encodePlain $ Addressed saddr fwd | ||
874 | |||
875 | |||
876 | |||
877 | -- TODO | ||
878 | -- Two types of packets may be sent to Rendezvous via OnionToRoute requests. | ||
879 | -- | ||
880 | -- (1) DHT public key packet (0x9c) | ||
881 | -- | ||
882 | -- (2) Friend request | ||
883 | data Rendezvous = Rendezvous | ||
884 | { rendezvousKey :: PublicKey | ||
885 | , rendezvousNode :: NodeInfo | ||
886 | } | ||
887 | deriving Eq | ||
888 | |||
889 | instance Show Rendezvous where | ||
890 | showsPrec d (Rendezvous k ni) | ||
891 | = showsPrec d (key2id k) | ||
892 | . (':' :) | ||
893 | . showsPrec d ni | ||
894 | |||
895 | instance Read Rendezvous where | ||
896 | readsPrec d = RP.readP_to_S $ do | ||
897 | rkstr <- RP.munch (/=':') | ||
898 | RP.char ':' | ||
899 | nistr <- RP.munch (const True) | ||
900 | return Rendezvous | ||
901 | { rendezvousKey = id2key $ read rkstr | ||
902 | , rendezvousNode = read nistr | ||
903 | } | ||
904 | |||
905 | |||
906 | data AnnouncedRendezvous = AnnouncedRendezvous | ||
907 | { remoteUserKey :: PublicKey | ||
908 | , rendezvous :: Rendezvous | ||
909 | } | ||
910 | deriving Eq | ||
911 | |||
912 | instance Show AnnouncedRendezvous where | ||
913 | showsPrec d (AnnouncedRendezvous remote rendez) | ||
914 | = showsPrec d (key2id remote) | ||
915 | . (':' :) | ||
916 | . showsPrec d rendez | ||
917 | |||
918 | instance Read AnnouncedRendezvous where | ||
919 | readsPrec d = RP.readP_to_S $ do | ||
920 | ukstr <- RP.munch (/=':') | ||
921 | RP.char ':' | ||
922 | rkstr <- RP.munch (/=':') | ||
923 | RP.char ':' | ||
924 | nistr <- RP.munch (const True) | ||
925 | return AnnouncedRendezvous | ||
926 | { remoteUserKey = id2key $ read ukstr | ||
927 | , rendezvous = Rendezvous | ||
928 | { rendezvousKey = id2key $ read rkstr | ||
929 | , rendezvousNode = read nistr | ||
930 | } | ||
931 | } | ||
932 | |||
933 | |||
934 | selectAlias :: TransportCrypto -> NodeId -> STM AliasSelector | ||
935 | selectAlias crypto pkey = do | ||
936 | ks <- filter (\(sk,pk) -> pk == id2key pkey) | ||
937 | <$> userKeys crypto | ||
938 | maybe (return SearchingAlias) | ||
939 | (return . uncurry AnnouncingAlias) | ||
940 | (listToMaybe ks) | ||
941 | |||
942 | |||
943 | parseDataToRoute | ||
944 | :: TransportCrypto | ||
945 | -> (OnionMessage Encrypted,OnionDestination r) | ||
946 | -> IO (Either ((PublicKey,OnionData),AnnouncedRendezvous) (OnionMessage Encrypted, OnionDestination r)) | ||
947 | parseDataToRoute crypto (OnionToRouteResponse dta, od) = do | ||
948 | ks <- atomically $ userKeys crypto | ||
949 | |||
950 | omsg0 <- decryptMessage crypto (rendezvousSecret crypto,rendezvousPublic crypto) | ||
951 | (asymmNonce dta) | ||
952 | (Right dta) -- using Asymm{senderKey} as remote key | ||
953 | let eOuter = fmap runIdentity $ uncomposed omsg0 | ||
954 | |||
955 | anyRight [] f = return $ Left "parseDataToRoute: no user key" | ||
956 | anyRight (x:xs) f = f x >>= either (const $ anyRight xs f) (return . Right) | ||
957 | |||
958 | -- TODO: We don't currently have a way to look up which user key we | ||
959 | -- announced using along this onion route. Therefore, for now, we will | ||
960 | -- try all our user keys to see if any can decrypt the packet. | ||
961 | eInner <- case eOuter of | ||
962 | Left e -> return $ Left e | ||
963 | Right dtr -> anyRight ks $ \(sk,pk) -> do | ||
964 | omsg0 <- decryptMessage crypto | ||
965 | (sk,pk) | ||
966 | (asymmNonce dta) | ||
967 | (Left (dataFromKey dtr, dataToRoute dtr)) | ||
968 | return $ do | ||
969 | omsg <- fmap runIdentity . uncomposed $ omsg0 | ||
970 | Right (pk,dtr,omsg) | ||
971 | |||
972 | let e = do | ||
973 | (pk,dtr,omsg) <- eInner | ||
974 | return ( (pk, omsg) | ||
975 | , AnnouncedRendezvous | ||
976 | (dataFromKey dtr) | ||
977 | $ Rendezvous (rendezvousPublic crypto) $ onionNodeInfo od ) | ||
978 | r = either (const $ Right (OnionToRouteResponse dta,od)) Left e | ||
979 | -- parseDataToRoute OnionToRouteResponse decipherAndAuth: auth fail | ||
980 | case e of | ||
981 | Left _ -> dput XMisc $ "Failed keys: " ++ show (map (key2id . snd) ks) | ||
982 | Right _ -> return () | ||
983 | dput XMisc $ unlines | ||
984 | [ "parseDataToRoute " ++ either id (const "Right") e | ||
985 | , " crypto inner.me = " ++ either id (\(pk,_,_) -> show $ key2id pk) eInner | ||
986 | , " inner.them = " ++ either id (show . key2id . dataFromKey) eOuter | ||
987 | , " outer.me = " ++ show (key2id $ rendezvousPublic crypto) | ||
988 | , " outer.them = " ++ show (key2id $ senderKey dta) | ||
989 | ] | ||
990 | return r | ||
991 | parseDataToRoute _ msg = return $ Right msg | ||
992 | |||
993 | encodeDataToRoute :: TransportCrypto | ||
994 | -> ((PublicKey,OnionData),AnnouncedRendezvous) | ||
995 | -> IO (Maybe (OnionMessage Encrypted,OnionDestination r)) | ||
996 | encodeDataToRoute crypto ((me,omsg), AnnouncedRendezvous toxid (Rendezvous pub ni)) = do | ||
997 | nonce <- atomically $ transportNewNonce crypto | ||
998 | asel <- atomically $ selectAlias crypto (key2id me) | ||
999 | let (sk,pk) = case asel of | ||
1000 | AnnouncingAlias sk pk -> (sk,pk) | ||
1001 | _ -> (onionAliasSecret crypto, onionAliasPublic crypto) | ||
1002 | innerSecret <- lookupSharedSecret crypto sk toxid nonce | ||
1003 | let plain = encodePlain $ DataToRoute { dataFromKey = pk | ||
1004 | , dataToRoute = ToxCrypto.encrypt innerSecret $ encodePlain omsg | ||
1005 | } | ||
1006 | outerSecret <- lookupSharedSecret crypto (onionAliasSecret crypto) pub nonce | ||
1007 | let dta = ToxCrypto.encrypt outerSecret plain | ||
1008 | dput XOnion $ unlines | ||
1009 | [ "encodeDataToRoute me=" ++ show (key2id me) | ||
1010 | , " dhtpk=" ++ case omsg of | ||
1011 | OnionDHTPublicKey dmsg -> show (key2id $ dhtpk dmsg) | ||
1012 | OnionFriendRequest fr -> "friend request" | ||
1013 | , " ns=" ++ case omsg of | ||
1014 | OnionDHTPublicKey dmsg -> show (dhtpkNodes dmsg) | ||
1015 | OnionFriendRequest fr -> "friend request" | ||
1016 | , " crypto inner.me =" ++ show (key2id pk) | ||
1017 | , " inner.you=" ++ show (key2id toxid) | ||
1018 | , " outer.me =" ++ show (key2id $ onionAliasPublic crypto) | ||
1019 | , " outer.you=" ++ show (key2id pub) | ||
1020 | , " " ++ show (AnnouncedRendezvous toxid (Rendezvous pub ni)) | ||
1021 | , " " ++ show dta | ||
1022 | ] | ||
1023 | return $ Just ( OnionToRoute toxid -- Public key of destination node | ||
1024 | Asymm { senderKey = onionAliasPublic crypto | ||
1025 | , asymmNonce = nonce | ||
1026 | , asymmData = dta | ||
1027 | } | ||
1028 | , OnionDestination SearchingAlias ni Nothing ) | ||
diff --git a/src/Data/Tox/Relay.hs b/src/Data/Tox/Relay.hs index 02300866..d1e9fb99 100644 --- a/src/Data/Tox/Relay.hs +++ b/src/Data/Tox/Relay.hs | |||
@@ -8,16 +8,24 @@ | |||
8 | {-# LANGUAGE UndecidableInstances #-} | 8 | {-# LANGUAGE UndecidableInstances #-} |
9 | module Data.Tox.Relay where | 9 | module Data.Tox.Relay where |
10 | 10 | ||
11 | import Data.Aeson (ToJSON(..),FromJSON(..)) | ||
12 | import qualified Data.Aeson as JSON | ||
11 | import Data.ByteString as B | 13 | import Data.ByteString as B |
12 | import Data.Data | 14 | import Data.Data |
13 | import Data.Functor.Contravariant | 15 | import Data.Functor.Contravariant |
16 | import Data.Hashable | ||
17 | import qualified Data.HashMap.Strict as HashMap | ||
14 | import Data.Monoid | 18 | import Data.Monoid |
15 | import Data.Serialize | 19 | import Data.Serialize |
20 | import qualified Data.Vector as Vector | ||
16 | import Data.Word | 21 | import Data.Word |
22 | import Network.Socket | ||
17 | import qualified Rank2 | 23 | import qualified Rank2 |
24 | import qualified Text.ParserCombinators.ReadP as RP | ||
18 | 25 | ||
19 | import Crypto.Tox | 26 | import Crypto.Tox |
20 | import Network.Tox.Onion.Transport | 27 | import Data.Tox.Onion |
28 | import qualified Network.Tox.NodeId as UDP | ||
21 | 29 | ||
22 | newtype ConId = ConId Word8 | 30 | newtype ConId = ConId Word8 |
23 | deriving (Eq,Show,Ord,Data,Serialize) | 31 | deriving (Eq,Show,Ord,Data,Serialize) |
@@ -178,3 +186,40 @@ instance Sized (Welcome Encrypted) where | |||
178 | instance Serialize (Welcome Encrypted) where | 186 | instance Serialize (Welcome Encrypted) where |
179 | get = Welcome <$> get <*> get | 187 | get = Welcome <$> get <*> get |
180 | put (Welcome n dta) = put n >> put dta | 188 | put (Welcome n dta) = put n >> put dta |
189 | |||
190 | data NodeInfo = NodeInfo | ||
191 | { udpNodeInfo :: UDP.NodeInfo | ||
192 | , tcpPort :: PortNumber | ||
193 | } | ||
194 | deriving (Eq,Ord) | ||
195 | |||
196 | instance Read NodeInfo where | ||
197 | readsPrec _ = RP.readP_to_S $ do | ||
198 | udp <- RP.readS_to_P reads | ||
199 | port <- RP.between (RP.char '{') (RP.char '}') $ do | ||
200 | mapM_ RP.char ("tcp:" :: String) | ||
201 | w16 <- RP.readS_to_P reads | ||
202 | return $ fromIntegral (w16 :: Word16) | ||
203 | return $ NodeInfo udp port | ||
204 | |||
205 | instance ToJSON NodeInfo where | ||
206 | toJSON (NodeInfo udp port) = case (toJSON udp) of | ||
207 | JSON.Object tbl -> JSON.Object $ HashMap.insert "tcp_ports" | ||
208 | (JSON.Array $ Vector.fromList | ||
209 | [JSON.Number (fromIntegral port)]) | ||
210 | tbl | ||
211 | x -> x -- Shouldn't happen. | ||
212 | |||
213 | instance FromJSON NodeInfo where | ||
214 | parseJSON json = do | ||
215 | udp <- parseJSON json | ||
216 | port <- case json of | ||
217 | JSON.Object v -> do | ||
218 | portnum:_ <- v JSON..: "tcp_ports" | ||
219 | return (fromIntegral (portnum :: Word16)) | ||
220 | _ -> fail "TCP.NodeInfo: Expected JSON object." | ||
221 | return $ NodeInfo udp port | ||
222 | |||
223 | instance Hashable NodeInfo where | ||
224 | hashWithSalt s n = hashWithSalt s (udpNodeInfo n) | ||
225 | |||
diff --git a/src/Network/QueryResponse.hs b/src/Network/QueryResponse.hs index 4e110ec3..0fbbc929 100644 --- a/src/Network/QueryResponse.hs +++ b/src/Network/QueryResponse.hs | |||
@@ -134,6 +134,35 @@ partitionTransportM parse encodex tr = do | |||
134 | } | 134 | } |
135 | return (xtr, ytr) | 135 | return (xtr, ytr) |
136 | 136 | ||
137 | partitionAndForkTransport :: | ||
138 | (dst -> msg -> IO ()) | ||
139 | -> ((b,a) -> IO (Either (x,xaddr) (b,a))) | ||
140 | -> ((x,xaddr) -> IO (Maybe (Either (msg,dst) (b,a)))) | ||
141 | -> Transport err a b | ||
142 | -> IO (Transport err xaddr x, Transport err a b) | ||
143 | partitionAndForkTransport forkedSend parse encodex tr = do | ||
144 | mvar <- newEmptyMVar | ||
145 | let xtr = tr { awaitMessage = \kont -> fix $ \again -> do | ||
146 | awaitMessage tr $ \m -> case m of | ||
147 | Just (Right msg) -> parse msg >>= | ||
148 | either (kont . Just . Right) | ||
149 | (\y -> putMVar mvar y >> again) | ||
150 | Just (Left e) -> kont $ Just (Left e) | ||
151 | Nothing -> kont Nothing | ||
152 | , sendMessage = \addr' msg' -> do | ||
153 | msg_addr <- encodex (msg',addr') | ||
154 | case msg_addr of | ||
155 | Just (Right (b,a)) -> sendMessage tr a b | ||
156 | Just (Left (msg,dst)) -> forkedSend dst msg | ||
157 | Nothing -> return () | ||
158 | } | ||
159 | ytr = Transport | ||
160 | { awaitMessage = \kont -> takeMVar mvar >>= kont . Just . Right | ||
161 | , sendMessage = sendMessage tr | ||
162 | , closeTransport = return () | ||
163 | } | ||
164 | return (xtr, ytr) | ||
165 | |||
137 | -- | | 166 | -- | |
138 | -- * f add x --> Nothing, consume x | 167 | -- * f add x --> Nothing, consume x |
139 | -- --> Just id, leave x to a different handler | 168 | -- --> Just id, leave x to a different handler |
@@ -376,16 +405,27 @@ transactionMethods :: | |||
376 | TableMethods t tid -- ^ Table methods to lookup values by /tid/. | 405 | TableMethods t tid -- ^ Table methods to lookup values by /tid/. |
377 | -> (g -> (tid,g)) -- ^ Generate a new unique /tid/ value and update the generator state /g/. | 406 | -> (g -> (tid,g)) -- ^ Generate a new unique /tid/ value and update the generator state /g/. |
378 | -> TransactionMethods (g,t (MVar x)) tid addr x | 407 | -> TransactionMethods (g,t (MVar x)) tid addr x |
379 | transactionMethods (TableMethods insert delete lookup) generate = TransactionMethods | 408 | transactionMethods methods generate = transactionMethods' id tryPutMVar methods generate |
409 | |||
410 | -- | Like 'transactionMethods' but allows extra information to be stored in the | ||
411 | -- table of pending transactions. This also enables multiple 'Client's to | ||
412 | -- share a single transaction table. | ||
413 | transactionMethods' :: | ||
414 | (MVar x -> a) -- ^ store MVar into table entry | ||
415 | -> (a -> x -> IO void) -- ^ load MVar from table entry | ||
416 | -> TableMethods t tid -- ^ Table methods to lookup values by /tid/. | ||
417 | -> (g -> (tid,g)) -- ^ Generate a new unique /tid/ value and update the generator state /g/. | ||
418 | -> TransactionMethods (g,t a) tid addr x | ||
419 | transactionMethods' store load (TableMethods insert delete lookup) generate = TransactionMethods | ||
380 | { dispatchCancel = \tid (g,t) -> return (g, delete tid t) | 420 | { dispatchCancel = \tid (g,t) -> return (g, delete tid t) |
381 | , dispatchRegister = \v _ (g,t) -> | 421 | , dispatchRegister = \v _ (g,t) -> |
382 | let (tid,g') = generate g | 422 | let (tid,g') = generate g |
383 | t' = insert tid v t | 423 | t' = insert tid (store v) t |
384 | in return ( tid, (g',t') ) | 424 | in return ( tid, (g',t') ) |
385 | , dispatchResponse = \tid x (g,t) -> | 425 | , dispatchResponse = \tid x (g,t) -> |
386 | case lookup tid t of | 426 | case lookup tid t of |
387 | Just v -> let t' = delete tid t | 427 | Just v -> let t' = delete tid t |
388 | in return ((g,t'),void $ tryPutMVar v x) | 428 | in return ((g,t'),void $ load v x) |
389 | Nothing -> return ((g,t), return ()) | 429 | Nothing -> return ((g,t), return ()) |
390 | } | 430 | } |
391 | 431 | ||
diff --git a/src/Network/Tox.hs b/src/Network/Tox.hs index ef74b9c6..6e2a42c5 100644 --- a/src/Network/Tox.hs +++ b/src/Network/Tox.hs | |||
@@ -1,4 +1,5 @@ | |||
1 | {-# LANGUAGE CPP #-} | 1 | {-# LANGUAGE CPP #-} |
2 | {-# LANGUAGE FlexibleContexts #-} | ||
2 | {-# LANGUAGE DeriveDataTypeable #-} | 3 | {-# LANGUAGE DeriveDataTypeable #-} |
3 | {-# LANGUAGE DeriveFoldable #-} | 4 | {-# LANGUAGE DeriveFoldable #-} |
4 | {-# LANGUAGE DeriveFunctor #-} | 5 | {-# LANGUAGE DeriveFunctor #-} |
@@ -32,6 +33,7 @@ import qualified Data.ByteString as B | |||
32 | ;import Data.ByteString (ByteString) | 33 | ;import Data.ByteString (ByteString) |
33 | import qualified Data.ByteString.Char8 as C8 | 34 | import qualified Data.ByteString.Char8 as C8 |
34 | import Data.Data | 35 | import Data.Data |
36 | import Data.Functor.Identity | ||
35 | import Data.Functor.Contravariant | 37 | import Data.Functor.Contravariant |
36 | import Data.Maybe | 38 | import Data.Maybe |
37 | import qualified Data.MinMaxPSQ as MinMaxPSQ | 39 | import qualified Data.MinMaxPSQ as MinMaxPSQ |
@@ -42,6 +44,7 @@ import Network.Socket | |||
42 | import System.Endian | 44 | import System.Endian |
43 | import System.IO.Error | 45 | import System.IO.Error |
44 | 46 | ||
47 | import qualified Data.Word64Map | ||
45 | import Network.BitTorrent.DHT.Token as Token | 48 | import Network.BitTorrent.DHT.Token as Token |
46 | import qualified Data.Wrapper.PSQ as PSQ | 49 | import qualified Data.Wrapper.PSQ as PSQ |
47 | import System.Global6 | 50 | import System.Global6 |
@@ -68,6 +71,7 @@ import DebugTag | |||
68 | import TCPProber | 71 | import TCPProber |
69 | import Network.Tox.Avahi | 72 | import Network.Tox.Avahi |
70 | import Network.Tox.Session | 73 | import Network.Tox.Session |
74 | import qualified Data.Tox.Relay as TCP | ||
71 | import Network.Tox.Relay | 75 | import Network.Tox.Relay |
72 | import Network.SessionTransports | 76 | import Network.SessionTransports |
73 | import Network.Kademlia.Search | 77 | import Network.Kademlia.Search |
@@ -238,6 +242,37 @@ getOnionAlias crypto dhtself remoteNode = atomically $ do | |||
238 | _ -> ni { nodeId = key2id (onionAliasPublic crypto) } | 242 | _ -> ni { nodeId = key2id (onionAliasPublic crypto) } |
239 | return $ Onion.OnionDestination Onion.SearchingAlias alias Nothing | 243 | return $ Onion.OnionDestination Onion.SearchingAlias alias Nothing |
240 | 244 | ||
245 | newOnionClient :: DRG g => | ||
246 | TransportCrypto | ||
247 | -> Transport String (Onion.OnionDestination RouteId) Onion.Message | ||
248 | -> DHT.Routing | ||
249 | -> TVar SessionTokens | ||
250 | -> TVar Onion.AnnouncedKeys | ||
251 | -> OnionRouter | ||
252 | -> TVar (g, Data.Word64Map.Word64Map a) | ||
253 | -> (MVar Onion.Message -> a) | ||
254 | -> (a -> Onion.Message -> IO void) | ||
255 | -> Client String | ||
256 | DHT.PacketKind | ||
257 | DHT.TransactionId | ||
258 | (Onion.OnionDestination RouteId) | ||
259 | Onion.Message | ||
260 | newOnionClient crypto net r toks keydb orouter map_var store load = Client | ||
261 | { clientNet = net | ||
262 | , clientDispatcher = DispatchMethods | ||
263 | { classifyInbound = Onion.classify | ||
264 | , lookupHandler = Onion.handlers net r toks keydb | ||
265 | , tableMethods = hookQueries orouter DHT.transactionKey | ||
266 | $ transactionMethods' store load (contramap w64Key w64MapMethods) gen | ||
267 | } | ||
268 | , clientErrorReporter = logErrors { reportTimeout = reportTimeout ignoreErrors } | ||
269 | , clientPending = map_var | ||
270 | , clientAddress = getOnionAlias crypto $ R.thisNode <$> readTVar (DHT.routing4 r) | ||
271 | , clientResponseId = genNonce24 map_var | ||
272 | , clientEnterQuery = \_ -> return () | ||
273 | , clientLeaveQuery = \_ _ -> return () | ||
274 | } | ||
275 | |||
241 | newTox :: TVar Onion.AnnouncedKeys -- ^ Store of announced keys we are a rendezvous for. | 276 | newTox :: TVar Onion.AnnouncedKeys -- ^ Store of announced keys we are a rendezvous for. |
242 | -> [String] -- ^ Bind-address to listen on. Must provide at least one. | 277 | -> [String] -- ^ Bind-address to listen on. Must provide at least one. |
243 | -> ( ContactInfo extra -> SockAddr -> Session -> IO () ) | 278 | -> ( ContactInfo extra -> SockAddr -> Session -> IO () ) |
@@ -287,8 +322,11 @@ newToxOverTransport keydb addr onNewSession suppliedDHTKey udp tcp = do | |||
287 | let lookupClose _ = return Nothing | 322 | let lookupClose _ = return Nothing |
288 | 323 | ||
289 | mkrouting <- DHT.newRouting addr crypto updateIP updateIP | 324 | mkrouting <- DHT.newRouting addr crypto updateIP updateIP |
290 | orouter <- newOnionRouter crypto $ dput XRoutes | 325 | (orouter,otbl) <- newOnionRouter crypto (dput XRoutes) |
291 | (cryptonet,dhtcrypt,onioncrypt,dtacrypt,handshakes) <- toxTransport crypto orouter lookupClose udp tcp | 326 | (cryptonet,dhtcrypt,onioncrypt,dtacrypt,handshakes) |
327 | <- toxTransport crypto orouter lookupClose udp | ||
328 | (sendMessage (clientNet $ tcpClient $ tcpKademliaClient orouter)) | ||
329 | tcp | ||
292 | sessions <- initSessions (sendMessage cryptonet) | 330 | sessions <- initSessions (sendMessage cryptonet) |
293 | 331 | ||
294 | let dhtnet0 = layerTransportM (DHT.decrypt crypto) (DHT.encrypt crypto) dhtcrypt | 332 | let dhtnet0 = layerTransportM (DHT.decrypt crypto) (DHT.encrypt crypto) dhtcrypt |
@@ -296,7 +334,7 @@ newToxOverTransport keydb addr onNewSession suppliedDHTKey udp tcp = do | |||
296 | tbl6 = DHT.routing6 $ mkrouting (error "missing client") | 334 | tbl6 = DHT.routing6 $ mkrouting (error "missing client") |
297 | updateOnion bkts tr = hookBucketList DHT.toxSpace bkts orouter (trampolinesUDP orouter) tr | 335 | updateOnion bkts tr = hookBucketList DHT.toxSpace bkts orouter (trampolinesUDP orouter) tr |
298 | dhtclient <- newClient drg dhtnet0 DHT.classify (myAddr tbl4 tbl6) (DHT.handlers crypto . mkrouting) id | 336 | dhtclient <- newClient drg dhtnet0 DHT.classify (myAddr tbl4 tbl6) (DHT.handlers crypto . mkrouting) id |
299 | $ \client net -> onInbound (DHT.updateRouting client (mkrouting client) updateOnion) net | 337 | (\client net -> onInbound (DHT.updateRouting client (mkrouting client) updateOnion) net) |
300 | 338 | ||
301 | hscache <- newHandshakeCache crypto (sendMessage handshakes) | 339 | hscache <- newHandshakeCache crypto (sendMessage handshakes) |
302 | let sparams = SessionParams | 340 | let sparams = SessionParams |
@@ -315,13 +353,13 @@ newToxOverTransport keydb addr onNewSession suppliedDHTKey udp tcp = do | |||
315 | toks <- do | 353 | toks <- do |
316 | nil <- nullSessionTokens | 354 | nil <- nullSessionTokens |
317 | atomically $ newTVar nil { maxInterval = 20 } -- 20 second timeout on announce ping-ids. | 355 | atomically $ newTVar nil { maxInterval = 20 } -- 20 second timeout on announce ping-ids. |
318 | oniondrg <- drgNew | ||
319 | let onionnet = layerTransportM (Onion.decrypt crypto) (Onion.encrypt crypto) onioncrypt | 356 | let onionnet = layerTransportM (Onion.decrypt crypto) (Onion.encrypt crypto) onioncrypt |
320 | onionclient <- newClient oniondrg onionnet (const Onion.classify) | 357 | let onionclient = newOnionClient crypto onionnet (mkrouting dhtclient) toks keydb orouter' otbl |
321 | (getOnionAlias crypto $ R.thisNode <$> readTVar (DHT.routing4 $ mkrouting dhtclient)) | 358 | Right $ \case |
322 | (const $ Onion.handlers onionnet (mkrouting dhtclient) toks keydb) | 359 | Right v -> tryPutMVar v |
323 | (hookQueries orouter' DHT.transactionKey) | 360 | Left v -> \_ -> do |
324 | (const id) | 361 | dput XUnexpected "TCP-sent onion query got response over UDP?" |
362 | return False | ||
325 | 363 | ||
326 | return Tox | 364 | return Tox |
327 | { toxDHT = dhtclient | 365 | { toxDHT = dhtclient |
diff --git a/src/Network/Tox/Onion/Transport.hs b/src/Network/Tox/Onion/Transport.hs index 8918f913..e746c414 100644 --- a/src/Network/Tox/Onion/Transport.hs +++ b/src/Network/Tox/Onion/Transport.hs | |||
@@ -1,21 +1,3 @@ | |||
1 | {-# LANGUAGE CPP #-} | ||
2 | {-# LANGUAGE DataKinds #-} | ||
3 | {-# LANGUAGE DeriveDataTypeable #-} | ||
4 | {-# LANGUAGE FlexibleContexts #-} | ||
5 | {-# LANGUAGE FlexibleInstances #-} | ||
6 | {-# LANGUAGE GADTs #-} | ||
7 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
8 | {-# LANGUAGE KindSignatures #-} | ||
9 | {-# LANGUAGE LambdaCase #-} | ||
10 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
11 | {-# LANGUAGE PartialTypeSignatures #-} | ||
12 | {-# LANGUAGE RankNTypes #-} | ||
13 | {-# LANGUAGE ScopedTypeVariables #-} | ||
14 | {-# LANGUAGE StandaloneDeriving #-} | ||
15 | {-# LANGUAGE TupleSections #-} | ||
16 | {-# LANGUAGE TypeFamilies #-} | ||
17 | {-# LANGUAGE TypeOperators #-} | ||
18 | {-# LANGUAGE UndecidableInstances #-} | ||
19 | module Network.Tox.Onion.Transport | 1 | module Network.Tox.Onion.Transport |
20 | ( parseOnionAddr | 2 | ( parseOnionAddr |
21 | , encodeOnionAddr | 3 | , encodeOnionAddr |
@@ -58,856 +40,51 @@ module Network.Tox.Onion.Transport | |||
58 | , wrapOnionPure | 40 | , wrapOnionPure |
59 | ) where | 41 | ) where |
60 | 42 | ||
61 | import Network.Address (fromSockAddr,toSockAddr,setPort,either4or6,sockAddrPort) | 43 | import Data.ByteString (ByteString) |
62 | import Network.QueryResponse | 44 | import Data.Serialize |
63 | import Crypto.Tox hiding (encrypt,decrypt) | ||
64 | import Network.Tox.NodeId | ||
65 | import qualified Crypto.Tox as ToxCrypto | ||
66 | import Network.Tox.DHT.Transport (NodeInfo(..),NodeId(..),SendNodes(..),nodeInfo,DHTPublicKey(..),FriendRequest,asymNodeInfo) | ||
67 | |||
68 | import Control.Applicative | ||
69 | import Control.Arrow | ||
70 | import Control.Concurrent.STM | ||
71 | import Control.Monad | ||
72 | import qualified Data.ByteString as B | ||
73 | ;import Data.ByteString (ByteString) | ||
74 | import Data.Data | ||
75 | import Data.Function | ||
76 | import Data.Functor.Contravariant | ||
77 | import Data.Functor.Identity | ||
78 | #if MIN_VERSION_iproute(1,7,4) | ||
79 | import Data.IP hiding (fromSockAddr) | ||
80 | #else | ||
81 | import Data.IP | ||
82 | #endif | ||
83 | import Data.Maybe | ||
84 | import Data.Monoid | ||
85 | import Data.Serialize as S | ||
86 | import Data.Type.Equality | ||
87 | import Data.Typeable | ||
88 | import Data.Word | ||
89 | import GHC.Generics () | ||
90 | import GHC.TypeLits | ||
91 | import Network.Socket | 45 | import Network.Socket |
92 | import qualified Text.ParserCombinators.ReadP as RP | ||
93 | import Data.Hashable | ||
94 | import DPut | ||
95 | import DebugTag | ||
96 | import Data.Word64Map (fitsInInt) | ||
97 | import Data.Bits (shiftR,shiftL) | ||
98 | import qualified Rank2 | ||
99 | |||
100 | type HandleLo a = Maybe (Either String (ByteString, SockAddr)) -> IO a | ||
101 | |||
102 | type UDPTransport = Transport String SockAddr ByteString | ||
103 | |||
104 | |||
105 | getOnionAsymm :: Get (Asymm (Encrypted DataToRoute)) | ||
106 | getOnionAsymm = getAliasedAsymm | ||
107 | |||
108 | putOnionAsymm :: Serialize a => Word8 -> Put -> Asymm a -> Put | ||
109 | putOnionAsymm typ p a = put typ >> p >> putAliasedAsymm a | ||
110 | |||
111 | data OnionMessage (f :: * -> *) | ||
112 | = OnionAnnounce (Asymm (f (AnnounceRequest,Nonce8))) | ||
113 | | OnionAnnounceResponse Nonce8 Nonce24 (f AnnounceResponse) -- XXX: Why is Nonce8 transmitted in the clear? | ||
114 | | OnionToRoute PublicKey (Asymm (Encrypted DataToRoute)) -- destination key, aliased Asymm | ||
115 | | OnionToRouteResponse (Asymm (Encrypted DataToRoute)) | ||
116 | |||
117 | deriving instance ( Eq (f (AnnounceRequest, Nonce8)) | ||
118 | , Eq (f AnnounceResponse) | ||
119 | , Eq (f DataToRoute) | ||
120 | ) => Eq (OnionMessage f) | ||
121 | |||
122 | deriving instance ( Ord (f (AnnounceRequest, Nonce8)) | ||
123 | , Ord (f AnnounceResponse) | ||
124 | , Ord (f DataToRoute) | ||
125 | ) => Ord (OnionMessage f) | ||
126 | |||
127 | deriving instance ( Show (f (AnnounceRequest, Nonce8)) | ||
128 | , Show (f AnnounceResponse) | ||
129 | , Show (f DataToRoute) | ||
130 | ) => Show (OnionMessage f) | ||
131 | |||
132 | instance Data (OnionMessage Encrypted) where | ||
133 | gfoldl f z txt = z (either error id . S.decode) `f` S.encode txt | ||
134 | toConstr _ = error "OnionMessage.toConstr" | ||
135 | gunfold _ _ = error "OnionMessage.gunfold" | ||
136 | #if MIN_VERSION_base(4,2,0) | ||
137 | dataTypeOf _ = mkNoRepType "Network.Tox.Onion.Transport.OnionMessage" | ||
138 | #else | ||
139 | dataTypeOf _ = mkNorepType "Network.Tox.Onion.Transport.OnionMessage" | ||
140 | #endif | ||
141 | |||
142 | instance Rank2.Functor OnionMessage where | ||
143 | f <$> m = mapPayload (Proxy :: Proxy Serialize) f m | ||
144 | |||
145 | instance Payload Serialize OnionMessage where | ||
146 | mapPayload _ f (OnionAnnounce a) = OnionAnnounce (fmap f a) | ||
147 | mapPayload _ f (OnionAnnounceResponse n8 n24 a) = OnionAnnounceResponse n8 n24 (f a) | ||
148 | mapPayload _ f (OnionToRoute k a) = OnionToRoute k a | ||
149 | mapPayload _ f (OnionToRouteResponse a) = OnionToRouteResponse a | ||
150 | |||
151 | |||
152 | msgNonce :: OnionMessage f -> Nonce24 | ||
153 | msgNonce (OnionAnnounce a) = asymmNonce a | ||
154 | msgNonce (OnionAnnounceResponse _ n24 _) = n24 | ||
155 | msgNonce (OnionToRoute _ a) = asymmNonce a | ||
156 | msgNonce (OnionToRouteResponse a) = asymmNonce a | ||
157 | |||
158 | data AliasSelector = SearchingAlias | AnnouncingAlias SecretKey PublicKey | ||
159 | deriving (Eq,Show) | ||
160 | |||
161 | data OnionDestination r | ||
162 | = OnionToOwner | ||
163 | { onionNodeInfo :: NodeInfo | ||
164 | , onionReturnPath :: ReturnPath N3 -- ^ Somebody else's path to us. | ||
165 | } | ||
166 | | OnionDestination | ||
167 | { onionAliasSelector' :: AliasSelector | ||
168 | , onionNodeInfo :: NodeInfo | ||
169 | , onionRouteSpec :: Maybe r -- ^ Our own onion-path. | ||
170 | } | ||
171 | deriving Show | ||
172 | |||
173 | onionAliasSelector :: OnionDestination r -> AliasSelector | ||
174 | onionAliasSelector (OnionToOwner {} ) = SearchingAlias | ||
175 | onionAliasSelector (OnionDestination{onionAliasSelector' = sel}) = sel | ||
176 | |||
177 | onionKey :: OnionDestination r -> PublicKey | ||
178 | onionKey od = id2key . nodeId $ onionNodeInfo od | ||
179 | |||
180 | instance Sized (OnionMessage Encrypted) where | ||
181 | size = VarSize $ \case | ||
182 | OnionAnnounce a -> case size of ConstSize n -> n + 1 | ||
183 | VarSize f -> f a + 1 | ||
184 | OnionAnnounceResponse n8 n24 x -> case size of ConstSize n -> n + 33 | ||
185 | VarSize f -> f x + 33 | ||
186 | OnionToRoute pubkey a -> case size of ConstSize n -> n + 33 | ||
187 | VarSize f -> f a + 33 | ||
188 | OnionToRouteResponse a -> case size of ConstSize n -> n + 1 | ||
189 | VarSize f -> f a + 1 | ||
190 | |||
191 | instance Serialize (OnionMessage Encrypted) where | ||
192 | get = do | ||
193 | typ <- get | ||
194 | case typ :: Word8 of | ||
195 | 0x83 -> OnionAnnounce <$> getAliasedAsymm | ||
196 | 0x85 -> OnionToRoute <$> getPublicKey <*> getAliasedAsymm | ||
197 | t -> fail ("Unknown onion payload: " ++ show t) | ||
198 | `fromMaybe` getOnionReply t | ||
199 | put (OnionAnnounce a) = putWord8 0x83 >> putAliasedAsymm a | ||
200 | put (OnionToRoute k a) = putWord8 0x85 >> putPublicKey k >> putAliasedAsymm a | ||
201 | put (OnionAnnounceResponse n8 n24 x) = putWord8 0x84 >> put n8 >> put n24 >> put x | ||
202 | put (OnionToRouteResponse a) = putWord8 0x86 >> putAliasedAsymm a | ||
203 | |||
204 | onionToOwner :: Asymm a -> ReturnPath N3 -> SockAddr -> Either String (OnionDestination r) | ||
205 | onionToOwner asymm ret3 saddr = do | ||
206 | ni <- nodeInfo (key2id $ senderKey asymm) saddr | ||
207 | return $ OnionToOwner ni ret3 | ||
208 | -- data CookieAddress = WithoutCookie NodeInfo | CookieAddress Cookie SockAddr | ||
209 | |||
210 | |||
211 | onion :: Sized msg => | ||
212 | ByteString | ||
213 | -> SockAddr | ||
214 | -> Get (Asymm (Encrypted msg) -> t) | ||
215 | -> Either String (t, OnionDestination r) | ||
216 | onion bs saddr getf = do (f,(asymm,ret3)) <- runGet ((,) <$> getf <*> getOnionRequest) bs | ||
217 | oaddr <- onionToOwner asymm ret3 saddr | ||
218 | return (f asymm, oaddr) | ||
219 | |||
220 | parseOnionAddr :: (SockAddr -> Nonce8 -> IO (Maybe (OnionDestination r))) | ||
221 | -> (ByteString, SockAddr) | ||
222 | -> IO (Either (OnionMessage Encrypted,OnionDestination r) | ||
223 | (ByteString,SockAddr)) | ||
224 | parseOnionAddr lookupSender (msg,saddr) | ||
225 | | Just (typ,bs) <- B.uncons msg | ||
226 | , let right = Right (msg,saddr) | ||
227 | query = return . either (const right) Left | ||
228 | = case typ of | ||
229 | 0x83 -> query $ onion bs saddr (pure OnionAnnounce) -- Announce Request | ||
230 | 0x85 -> query $ onion bs saddr (OnionToRoute <$> getPublicKey) -- Onion Data Request | ||
231 | _ -> case flip runGet bs <$> getOnionReply typ of | ||
232 | Just (Right msg@(OnionAnnounceResponse n8 _ _)) -> do | ||
233 | maddr <- lookupSender saddr n8 | ||
234 | maybe (return right) -- Response unsolicited or too late. | ||
235 | (return . Left . \od -> (msg,od)) | ||
236 | maddr | ||
237 | Just (Right msg@(OnionToRouteResponse asym)) -> do | ||
238 | let ni = asymNodeInfo saddr asym | ||
239 | return $ Left (msg, OnionDestination SearchingAlias ni Nothing) | ||
240 | _ -> return right | ||
241 | |||
242 | getOnionReply :: Word8 -> Maybe (Get (OnionMessage Encrypted)) | ||
243 | getOnionReply 0x84 = Just $ OnionAnnounceResponse <$> get <*> get <*> get | ||
244 | getOnionReply 0x86 = Just $ OnionToRouteResponse <$> getOnionAsymm | ||
245 | getOnionReply _ = Nothing | ||
246 | |||
247 | putOnionMsg :: OnionMessage Encrypted -> Put | ||
248 | putOnionMsg (OnionAnnounce a) = putOnionAsymm 0x83 (return ()) a | ||
249 | putOnionMsg (OnionToRoute pubkey a) = putOnionAsymm 0x85 (putPublicKey pubkey) a | ||
250 | putOnionMsg (OnionAnnounceResponse n8 n24 x) = put (0x84 :: Word8) >> put n8 >> put n24 >> put x | ||
251 | putOnionMsg (OnionToRouteResponse a) = putOnionAsymm 0x86 (return ()) a | ||
252 | |||
253 | newtype RouteId = RouteId Int | ||
254 | deriving Show | ||
255 | |||
256 | |||
257 | -- We used to derive the RouteId from the Nonce8 associated with the query. | ||
258 | -- This is problematic because a nonce generated by toxcore will not validate | ||
259 | -- if it is received via a different route than it was issued. This is | ||
260 | -- described by the Tox spec: | ||
261 | -- | ||
262 | -- Toxcore generates `ping_id`s by taking a 32 byte sha hash of the current | ||
263 | -- time, some secret bytes generated when the instance is created, the | ||
264 | -- current time divided by a 20 second timeout, the public key of the | ||
265 | -- requester and the source ip/port that the packet was received from. Since | ||
266 | -- the ip/port that the packet was received from is in the `ping_id`, the | ||
267 | -- announce packets being sent with a ping id must be sent using the same | ||
268 | -- path as the packet that we received the `ping_id` from or announcing will | ||
269 | -- fail. | ||
270 | -- | ||
271 | -- The original idea was: | ||
272 | -- | ||
273 | -- > routeId :: Nonce8 -> RouteId | ||
274 | -- > routeId (Nonce8 w8) = RouteId $ mod (fromIntegral w8) 12 | ||
275 | -- | ||
276 | -- Instead, we'll just hash the destination node id. | ||
277 | routeId :: NodeId -> RouteId | ||
278 | routeId nid = RouteId $ mod (hash nid) 12 | ||
279 | 46 | ||
47 | import Crypto.Tox hiding (encrypt,decrypt) | ||
48 | import qualified Data.Tox.Relay as TCP | ||
49 | import Data.Tox.Onion | ||
50 | import Network.Tox.NodeId | ||
280 | 51 | ||
52 | {- | ||
281 | encodeOnionAddr :: TransportCrypto | 53 | encodeOnionAddr :: TransportCrypto |
282 | -> (NodeInfo -> RouteId -> IO (Maybe OnionRoute)) | 54 | -> (NodeInfo -> RouteId -> IO (Maybe OnionRoute)) |
283 | -> (OnionMessage Encrypted,OnionDestination RouteId) | 55 | -> (OnionMessage Encrypted,OnionDestination RouteId) |
284 | -> IO (Maybe (ByteString, SockAddr)) | 56 | -> IO (Maybe (ByteString, SockAddr)) |
57 | -} | ||
58 | encodeOnionAddr :: TransportCrypto | ||
59 | -> (NodeInfo -> RouteId -> IO (Maybe OnionRoute)) | ||
60 | -> (OnionMessage Encrypted, OnionDestination RouteId) | ||
61 | -> IO (Maybe | ||
62 | (Either (TCP.RelayPacket, TCP.NodeInfo) (ByteString, SockAddr))) | ||
285 | encodeOnionAddr crypto _ (msg,OnionToOwner ni p) = | 63 | encodeOnionAddr crypto _ (msg,OnionToOwner ni p) = |
286 | return $ Just ( runPut $ putResponse (OnionResponse p msg) | 64 | return $ Just $ Right ( runPut $ putResponse (OnionResponse p msg) |
287 | , nodeAddr ni ) | 65 | , nodeAddr ni ) |
288 | encodeOnionAddr crypto getRoute (msg,OnionDestination x ni Nothing) = do | 66 | encodeOnionAddr crypto getRoute (msg,OnionDestination x ni Nothing) = do |
289 | encodeOnionAddr crypto getRoute (msg,OnionDestination x ni (Just $ routeId $ nodeId ni) ) | 67 | encodeOnionAddr crypto getRoute (msg,OnionDestination x ni (Just $ routeId $ nodeId ni) ) |
290 | -- dput XMisc $ "ONION encode missing routeid" | 68 | -- dput XMisc $ "ONION encode missing routeid" |
291 | -- return Nothing | 69 | -- return Nothing |
292 | encodeOnionAddr crypto getRoute (msg,OnionDestination _ ni (Just rid)) = do | 70 | encodeOnionAddr crypto getRoute (msg,OnionDestination _ ni (Just rid)) = do |
293 | let go route = do | 71 | let go route = do |
294 | req <- wrapForRoute crypto msg ni route | 72 | mreq <- wrapForRoute crypto msg ni route |
295 | return ( runPut $ putRequest req | 73 | case mreq of |
296 | , nodeAddr $ routeNodeA route) | 74 | Right req -> return $ Right ( runPut $ putRequest req , nodeAddr $ routeNodeA route) |
75 | Left o | Just port <- routeRelayPort route | ||
76 | -> return $ Left ( o, TCP.NodeInfo (routeNodeA route) port) | ||
297 | m <- {-# SCC "encodeOnionAddr.getRoute" #-} getRoute ni rid | 77 | m <- {-# SCC "encodeOnionAddr.getRoute" #-} getRoute ni rid |
298 | x <- {-# SCC "encodeOnionAddr.wrapForRoute" #-} mapM go m | 78 | x <- {-# SCC "encodeOnionAddr.wrapForRoute" #-} mapM go m |
299 | return x | 79 | return x |
300 | 80 | ||
301 | 81 | -- wrapForRoute :: TransportCrypto -> OnionMessage Encrypted -> NodeInfo -> OnionRoute -> IO (OnionRequest N0) | |
302 | forwardOnions :: TransportCrypto -> UDPTransport -> (Int -> OnionMessage Encrypted -> IO ()) {- ^ TCP relay send -} -> UDPTransport | 82 | wrapForRoute :: TransportCrypto |
303 | forwardOnions crypto udp sendTCP = udp { awaitMessage = forwardAwait crypto udp sendTCP } | 83 | -> OnionMessage Encrypted |
304 | 84 | -> NodeInfo | |
305 | forwardAwait :: TransportCrypto -> UDPTransport -> (Int -> OnionMessage Encrypted -> IO ()) {- ^ TCP relay send -} -> HandleLo a -> IO a | 85 | -> OnionRoute |
306 | forwardAwait crypto udp sendTCP kont = do | 86 | -> IO (Either TCP.RelayPacket (OnionRequest N0)) |
307 | fix $ \another -> do | 87 | wrapForRoute crypto msg ni r@OnionRoute{routeRelayPort=Nothing} = do |
308 | awaitMessage udp $ \case | ||
309 | m@(Just (Right (bs,saddr))) -> case B.head bs of | ||
310 | 0x80 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N0) crypto (Addressed saddr) udp another | ||
311 | 0x81 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N1) crypto (Addressed saddr) udp another | ||
312 | 0x82 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N2) crypto (Addressed saddr) udp another | ||
313 | 0x8c -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N3) crypto saddr udp sendTCP another | ||
314 | 0x8d -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N2) crypto saddr udp sendTCP another | ||
315 | 0x8e -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N1) crypto saddr udp sendTCP another | ||
316 | _ -> kont m | ||
317 | m -> kont m | ||
318 | |||
319 | forward :: forall c b b1. (Serialize b, Show b) => | ||
320 | (Maybe (Either String b1) -> c) -> ByteString -> (b -> c) -> c | ||
321 | forward kont bs f = either (kont . Just . Left) f $ decode $ B.tail bs | ||
322 | |||
323 | class SumToThree a b | ||
324 | |||
325 | instance SumToThree N0 N3 | ||
326 | instance SumToThree (S a) b => SumToThree a (S b) | ||
327 | |||
328 | class ( Serialize (ReturnPath n) | ||
329 | , Serialize (ReturnPath (S n)) | ||
330 | , Serialize (Forwarding (ThreeMinus (S n)) (OnionMessage Encrypted)) | ||
331 | , ThreeMinus n ~ S (ThreeMinus (S n)) | ||
332 | ) => LessThanThree n | ||
333 | |||
334 | instance LessThanThree N0 | ||
335 | instance LessThanThree N1 | ||
336 | instance LessThanThree N2 | ||
337 | |||
338 | type family ThreeMinus n where | ||
339 | ThreeMinus N3 = N0 | ||
340 | ThreeMinus N2 = N1 | ||
341 | ThreeMinus N1 = N2 | ||
342 | ThreeMinus N0 = N3 | ||
343 | |||
344 | -- n = 0, 1, 2 | ||
345 | data OnionRequest n = OnionRequest | ||
346 | { onionNonce :: Nonce24 | ||
347 | , onionForward :: Forwarding (ThreeMinus n) (OnionMessage Encrypted) | ||
348 | , pathFromOwner :: ReturnPath n | ||
349 | } | ||
350 | deriving (Eq,Ord) | ||
351 | |||
352 | |||
353 | {- | ||
354 | instance (Typeable n, Sized (ReturnPath n), Serialize (ReturnPath n) | ||
355 | , Serialize (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | ||
356 | ) => Data (OnionRequest n) where | ||
357 | gfoldl f z txt = z (either error id . S.decode) `f` S.encode txt | ||
358 | toConstr _ = error "OnionRequest.toConstr" | ||
359 | gunfold _ _ = error "OnionRequest.gunfold" | ||
360 | #if MIN_VERSION_base(4,2,0) | ||
361 | dataTypeOf _ = mkNoRepType "Network.Tox.Onion.Transport.OnionRequest" | ||
362 | #else | ||
363 | dataTypeOf _ = mkNorepType "Network.Tox.Onion.Transport.OnionRequest" | ||
364 | #endif | ||
365 | -} | ||
366 | |||
367 | |||
368 | instance (Typeable n, Serialize (ReturnPath n)) => Data (OnionResponse n) where | ||
369 | gfoldl f z txt = z (either error id . S.decode) `f` S.encode txt | ||
370 | toConstr _ = error "OnionResponse.toConstr" | ||
371 | gunfold _ _ = error "OnionResponse.gunfold" | ||
372 | #if MIN_VERSION_base(4,2,0) | ||
373 | dataTypeOf _ = mkNoRepType "Network.Tox.Onion.Transport.OnionResponse" | ||
374 | #else | ||
375 | dataTypeOf _ = mkNorepType "Network.Tox.Onion.Transport.OnionResponse" | ||
376 | #endif | ||
377 | |||
378 | deriving instance ( Show (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | ||
379 | , KnownNat (PeanoNat n) | ||
380 | ) => Show (OnionRequest n) | ||
381 | |||
382 | instance Sized (OnionRequest N0) where -- N1 and N2 are the same, N3 does not encode the nonce. | ||
383 | size = contramap onionNonce size | ||
384 | <> contramap onionForward size | ||
385 | <> contramap pathFromOwner size | ||
386 | |||
387 | instance ( Serialize (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | ||
388 | , Sized (ReturnPath n) | ||
389 | , Serialize (ReturnPath n) | ||
390 | , Typeable n | ||
391 | ) => Serialize (OnionRequest n) where | ||
392 | get = do | ||
393 | -- TODO share code with 'getOnionRequest' | ||
394 | n24 <- case eqT :: Maybe (n :~: N3) of | ||
395 | Just Refl -> return $ Nonce24 zeros24 | ||
396 | Nothing -> get | ||
397 | cnt <- remaining | ||
398 | let fwdsize = case size :: Size (ReturnPath n) of ConstSize n -> cnt - n | ||
399 | fwd <- isolate fwdsize get | ||
400 | rpath <- get | ||
401 | return $ OnionRequest n24 fwd rpath | ||
402 | put (OnionRequest n f p) = maybe (put n) (\Refl -> return ()) (eqT :: Maybe (n :~: N3)) >> put f >> put p | ||
403 | |||
404 | -- getRequest :: _ | ||
405 | -- getRequest = OnionRequest <$> get <*> get <*> get | ||
406 | |||
407 | -- n = 1, 2, 3 | ||
408 | -- Attributed (Encrypted ( | ||
409 | |||
410 | data OnionResponse n = OnionResponse | ||
411 | { pathToOwner :: ReturnPath n | ||
412 | , msgToOwner :: OnionMessage Encrypted | ||
413 | } | ||
414 | deriving (Eq,Ord) | ||
415 | |||
416 | deriving instance KnownNat (PeanoNat n) => Show (OnionResponse n) | ||
417 | |||
418 | instance ( Serialize (ReturnPath n) ) => Serialize (OnionResponse n) where | ||
419 | get = OnionResponse <$> get <*> (get >>= fromMaybe (fail "illegal onion forwarding") | ||
420 | . getOnionReply) | ||
421 | put (OnionResponse p m) = put p >> putOnionMsg m | ||
422 | |||
423 | instance (Sized (ReturnPath n)) => Sized (OnionResponse (S n)) where | ||
424 | size = contramap pathToOwner size <> contramap msgToOwner size | ||
425 | |||
426 | data Addressed a = Addressed { sockAddr :: SockAddr, unaddressed :: a } | ||
427 | | TCPIndex { tcpIndex :: Int, unaddressed :: a } | ||
428 | deriving (Eq,Ord,Show) | ||
429 | |||
430 | instance (Typeable a, Serialize a) => Data (Addressed a) where | ||
431 | gfoldl f z a = z (either error id . S.decode) `f` S.encode a | ||
432 | toConstr _ = error "Addressed.toConstr" | ||
433 | gunfold _ _ = error "Addressed.gunfold" | ||
434 | #if MIN_VERSION_base(4,2,0) | ||
435 | dataTypeOf _ = mkNoRepType "Network.Tox.Onion.Transport.Addressed" | ||
436 | #else | ||
437 | dataTypeOf _ = mkNorepType "Network.Tox.Onion.Transport.Addressed" | ||
438 | #endif | ||
439 | |||
440 | instance Sized a => Sized (Addressed a) where | ||
441 | size = case size :: Size a of | ||
442 | ConstSize n -> ConstSize $ 1{-family-} + 16{-ip-} + 2{-port-} + n | ||
443 | VarSize f -> VarSize $ \x -> 1{-family-} + 16{-ip-} + 2{-port-} + f (unaddressed x) | ||
444 | |||
445 | getForwardAddr :: S.Get SockAddr | ||
446 | getForwardAddr = do | ||
447 | addrfam <- S.get :: S.Get Word8 | ||
448 | ip <- getIP addrfam | ||
449 | case ip of IPv4 _ -> S.skip 12 -- compliant peers would zero-fill this. | ||
450 | IPv6 _ -> return () | ||
451 | port <- S.get :: S.Get PortNumber | ||
452 | return $ setPort port $ toSockAddr ip | ||
453 | |||
454 | |||
455 | putForwardAddr :: SockAddr -> S.Put | ||
456 | putForwardAddr saddr = fromMaybe (return $ error "unsupported SockAddr family") $ do | ||
457 | port <- sockAddrPort saddr | ||
458 | ip <- fromSockAddr $ either id id $ either4or6 saddr | ||
459 | return $ do | ||
460 | case ip of | ||
461 | IPv4 ip4 -> S.put (0x02 :: Word8) >> S.put ip4 >> S.putByteString (B.replicate 12 0) | ||
462 | IPv6 ip6 -> S.put (0x0a :: Word8) >> S.put ip6 | ||
463 | S.put port | ||
464 | |||
465 | addrToIndex :: SockAddr -> Int | ||
466 | addrToIndex (SockAddrInet6 _ _ (lo, hi, _, _) _) = | ||
467 | if fitsInInt (Proxy :: Proxy Word64) | ||
468 | then fromIntegral lo + (fromIntegral hi `shiftL` 32) | ||
469 | else fromIntegral lo | ||
470 | addrToIndex _ = 0 | ||
471 | |||
472 | indexToAddr :: Int -> SockAddr | ||
473 | indexToAddr x = SockAddrInet6 0 0 (fromIntegral x, fromIntegral (x `shiftR` 32),0,0) 0 | ||
474 | |||
475 | -- Note, toxcore would check an address family byte here to detect a TCP-bound | ||
476 | -- packet, but we instead use the IPv6 id and rely on the port number being | ||
477 | -- zero. Since it will be symmetrically encrypted for our eyes only, it's not | ||
478 | -- important to conform on this point. | ||
479 | instance Serialize a => Serialize (Addressed a) where | ||
480 | get = do saddr <- getForwardAddr | ||
481 | a <- get | ||
482 | case sockAddrPort saddr of | ||
483 | Just 0 -> return $ TCPIndex (addrToIndex saddr) a | ||
484 | _ -> return $ Addressed saddr a | ||
485 | put (Addressed addr x) = putForwardAddr addr >> put x | ||
486 | put (TCPIndex idx x) = putForwardAddr (indexToAddr idx) >> put x | ||
487 | |||
488 | data N0 | ||
489 | data S n | ||
490 | type N1 = S N0 | ||
491 | type N2 = S N1 | ||
492 | type N3 = S N2 | ||
493 | |||
494 | deriving instance Data N0 | ||
495 | deriving instance Data n => Data (S n) | ||
496 | |||
497 | class KnownPeanoNat n where | ||
498 | peanoVal :: p n -> Int | ||
499 | |||
500 | instance KnownPeanoNat N0 where | ||
501 | peanoVal _ = 0 | ||
502 | instance KnownPeanoNat n => KnownPeanoNat (S n) where | ||
503 | peanoVal _ = 1 + peanoVal (Proxy :: Proxy n) | ||
504 | |||
505 | type family PeanoNat p where | ||
506 | PeanoNat N0 = 0 | ||
507 | PeanoNat (S n) = 1 + PeanoNat n | ||
508 | |||
509 | data ReturnPath n where | ||
510 | NoReturnPath :: ReturnPath N0 | ||
511 | ReturnPath :: Nonce24 -> Encrypted (Addressed (ReturnPath n)) -> ReturnPath (S n) | ||
512 | |||
513 | deriving instance Eq (ReturnPath n) | ||
514 | deriving instance Ord (ReturnPath n) | ||
515 | |||
516 | -- Size: 59 = 1(family) + 16(ip) + 2(port) +16(mac) + 24(nonce) | ||
517 | instance Sized (ReturnPath N0) where size = ConstSize 0 | ||
518 | instance Sized (ReturnPath n) => Sized (ReturnPath (S n)) where | ||
519 | size = ConstSize 59 <> contramap (\x -> let _ = x :: ReturnPath (S n) | ||
520 | in error "non-constant ReturnPath size") | ||
521 | (size :: Size (ReturnPath n)) | ||
522 | |||
523 | {- | ||
524 | instance KnownNat (PeanoNat n) => Sized (ReturnPath n) where | ||
525 | size = ConstSize $ 59 * fromIntegral (natVal (Proxy :: Proxy (PeanoNat n))) | ||
526 | -} | ||
527 | |||
528 | instance Serialize (ReturnPath N0) where get = pure NoReturnPath | ||
529 | put NoReturnPath = pure () | ||
530 | |||
531 | instance Serialize (ReturnPath N1) where | ||
532 | get = ReturnPath <$> get <*> get | ||
533 | put (ReturnPath n24 p) = put n24 >> put p | ||
534 | |||
535 | instance (Sized (ReturnPath n), Serialize (ReturnPath n)) => Serialize (ReturnPath (S (S n))) where | ||
536 | get = ReturnPath <$> get <*> get | ||
537 | put (ReturnPath n24 p) = put n24 >> put p | ||
538 | |||
539 | |||
540 | {- | ||
541 | -- This doesn't work because it tried to infer it for (0 - 1) | ||
542 | instance (Serialize (Encrypted (Addressed (ReturnPath (n - 1))))) => Serialize (ReturnPath n) where | ||
543 | get = ReturnPath <$> get <*> get | ||
544 | put (ReturnPath n24 p) = put n24 >> put p | ||
545 | -} | ||
546 | |||
547 | instance KnownNat (PeanoNat n) => Show (ReturnPath n) where | ||
548 | show rpath = "ReturnPath" ++ show (natVal (Proxy :: Proxy (PeanoNat n))) | ||
549 | |||
550 | |||
551 | -- instance KnownNat n => Serialize (ReturnPath n) where | ||
552 | -- -- Size: 59 = 1(family) + 16(ip) + 2(port) +16(mac) + 24(nonce) | ||
553 | -- get = ReturnPath <$> getBytes ( 59 * (fromIntegral $ natVal $ Proxy @n) ) | ||
554 | -- put (ReturnPath bs) = putByteString bs | ||
555 | |||
556 | |||
557 | data Forwarding n msg where | ||
558 | NotForwarded :: msg -> Forwarding N0 msg | ||
559 | Forwarding :: PublicKey -> Encrypted (Addressed (Forwarding n msg)) -> Forwarding (S n) msg | ||
560 | |||
561 | deriving instance Eq msg => Eq (Forwarding n msg) | ||
562 | deriving instance Ord msg => Ord (Forwarding n msg) | ||
563 | |||
564 | instance Show msg => Show (Forwarding N0 msg) where | ||
565 | show (NotForwarded x) = "NotForwarded "++show x | ||
566 | |||
567 | instance ( KnownNat (PeanoNat (S n)) | ||
568 | , Show (Encrypted (Addressed (Forwarding n msg))) | ||
569 | ) => Show (Forwarding (S n) msg) where | ||
570 | show (Forwarding k a) = unwords [ "Forwarding" | ||
571 | , "("++show (natVal (Proxy :: Proxy (PeanoNat (S n))))++")" | ||
572 | , show (key2id k) | ||
573 | , show a | ||
574 | ] | ||
575 | |||
576 | instance Sized msg => Sized (Forwarding N0 msg) | ||
577 | where size = case size :: Size msg of | ||
578 | ConstSize n -> ConstSize n | ||
579 | VarSize f -> VarSize $ \(NotForwarded x) -> f x | ||
580 | |||
581 | instance Sized (Forwarding n msg) => Sized (Forwarding (S n) msg) | ||
582 | where size = ConstSize 32 | ||
583 | <> contramap (\(Forwarding _ e) -> e) | ||
584 | (size :: Size (Encrypted (Addressed (Forwarding n msg)))) | ||
585 | |||
586 | instance Serialize msg => Serialize (Forwarding N0 msg) where | ||
587 | get = NotForwarded <$> get | ||
588 | put (NotForwarded msg) = put msg | ||
589 | |||
590 | instance (Serialize (Encrypted (Addressed (Forwarding n msg)))) => Serialize (Forwarding (S n) msg) where | ||
591 | get = Forwarding <$> getPublicKey <*> get | ||
592 | put (Forwarding k x) = putPublicKey k >> put x | ||
593 | |||
594 | {- | ||
595 | rewrap :: (ThreeMinus n ~ S (ThreeMinus (S n)), | ||
596 | Serialize (ReturnPath n), | ||
597 | Serialize | ||
598 | (Forwarding (ThreeMinus (S n)) (OnionMessage Encrypted))) => | ||
599 | TransportCrypto | ||
600 | -> (forall x. x -> Addressed x) | ||
601 | -> OnionRequest n | ||
602 | -> IO (Either String (OnionRequest (S n), SockAddr)) | ||
603 | rewrap crypto saddr (OnionRequest nonce msg rpath) = do | ||
604 | (sym, snonce) <- atomically ( (,) <$> transportSymmetric crypto | ||
605 | <*> transportNewNonce crypto ) | ||
606 | peeled <- peelOnion crypto nonce msg | ||
607 | return $ peeled >>= \case | ||
608 | Addressed dst msg' | ||
609 | -> Right (OnionRequest nonce msg' $ wrapSymmetric sym snonce saddr rpath, dst) | ||
610 | _ -> Left "Onion forward to TCP client?" | ||
611 | -} | ||
612 | |||
613 | handleOnionRequest :: forall a proxy n. | ||
614 | ( LessThanThree n | ||
615 | , KnownPeanoNat n | ||
616 | , Sized (ReturnPath n) | ||
617 | , Typeable n | ||
618 | ) => proxy n -> TransportCrypto -> (forall x. x -> Addressed x) -> UDPTransport -> IO a -> OnionRequest n -> IO a | ||
619 | handleOnionRequest proxy crypto saddr udp kont (OnionRequest nonce msg rpath) = do | ||
620 | let n = peanoVal rpath | ||
621 | dput XOnion $ "handleOnionRequest " ++ show n | ||
622 | (sym, snonce) <- atomically ( (,) <$> transportSymmetric crypto | ||
623 | <*> transportNewNonce crypto ) | ||
624 | peeled <- peelOnion crypto nonce msg | ||
625 | let showDestination = case saddr () of | ||
626 | Addressed a _ -> either show show $ either4or6 a | ||
627 | TCPIndex i _ -> "TCP" ++ show [i] | ||
628 | |||
629 | case peeled of | ||
630 | Left e -> do | ||
631 | dput XOnion $ unwords [ "peelOnion:", show n, showDestination, e] | ||
632 | kont | ||
633 | Right (Addressed dst msg') -> do | ||
634 | dput XOnion $ unwords [ "peelOnion:", show n, showDestination, "-->", either show show (either4or6 dst), "SUCCESS"] | ||
635 | sendMessage udp dst (runPut $ putRequest $ OnionRequest nonce msg' $ wrapSymmetric sym snonce saddr rpath) | ||
636 | kont | ||
637 | Right (TCPIndex {}) -> do | ||
638 | dput XUnexpected "handleOnionRequest: Onion forward to TCP client?" | ||
639 | kont | ||
640 | |||
641 | wrapSymmetric :: Serialize (ReturnPath n) => | ||
642 | SymmetricKey -> Nonce24 -> (forall x. x -> Addressed x) -> ReturnPath n -> ReturnPath (S n) | ||
643 | wrapSymmetric sym n saddr rpath = ReturnPath n $ encryptSymmetric sym n (encodePlain $ saddr rpath) | ||
644 | |||
645 | peelSymmetric :: Serialize (Addressed (ReturnPath n)) | ||
646 | => SymmetricKey -> ReturnPath (S n) -> Either String (Addressed (ReturnPath n)) | ||
647 | peelSymmetric sym (ReturnPath nonce e) = decryptSymmetric sym nonce e >>= decodePlain | ||
648 | |||
649 | |||
650 | peelOnion :: Serialize (Addressed (Forwarding n t)) | ||
651 | => TransportCrypto | ||
652 | -> Nonce24 | ||
653 | -> Forwarding (S n) t | ||
654 | -> IO (Either String (Addressed (Forwarding n t))) | ||
655 | peelOnion crypto nonce (Forwarding k fwd) = do | ||
656 | fmap runIdentity . uncomposed <$> decryptMessage crypto (dhtKey crypto) nonce (Right $ Asymm k nonce fwd) | ||
657 | |||
658 | handleOnionResponse :: (KnownPeanoNat n, Sized (ReturnPath n), Serialize (ReturnPath n), Typeable n) => | ||
659 | proxy (S n) | ||
660 | -> TransportCrypto | ||
661 | -> SockAddr | ||
662 | -> UDPTransport | ||
663 | -> (Int -> OnionMessage Encrypted -> IO ()) -- ^ TCP-relay onion send. | ||
664 | -> IO a | ||
665 | -> OnionResponse (S n) | ||
666 | -> IO a | ||
667 | handleOnionResponse proxy crypto saddr udp sendTCP kont (OnionResponse path msg) = do | ||
668 | sym <- atomically $ transportSymmetric crypto | ||
669 | case peelSymmetric sym path of | ||
670 | Left e -> do | ||
671 | -- todo report encryption error | ||
672 | let n = peanoVal path | ||
673 | dput XMisc $ unwords [ "peelSymmetric:", show n, either show show (either4or6 saddr), e] | ||
674 | kont | ||
675 | Right (Addressed dst path') -> do | ||
676 | sendMessage udp dst (runPut $ putResponse $ OnionResponse path' msg) | ||
677 | kont | ||
678 | Right (TCPIndex dst path') -> do | ||
679 | case peanoVal path' of | ||
680 | 0 -> sendTCP dst msg | ||
681 | n -> dput XUnexpected $ "handleOnionResponse: TCP-bound OnionResponse" ++ show n ++ " not supported." | ||
682 | kont | ||
683 | |||
684 | |||
685 | data AnnounceRequest = AnnounceRequest | ||
686 | { announcePingId :: Nonce32 -- Ping ID | ||
687 | , announceSeeking :: NodeId -- Public key we are searching for | ||
688 | , announceKey :: NodeId -- Public key that we want those sending back data packets to use | ||
689 | } | ||
690 | deriving Show | ||
691 | |||
692 | instance Sized AnnounceRequest where size = ConstSize (32*3) | ||
693 | |||
694 | instance S.Serialize AnnounceRequest where | ||
695 | get = AnnounceRequest <$> S.get <*> S.get <*> S.get | ||
696 | put (AnnounceRequest p s k) = S.put (p,s,k) | ||
697 | |||
698 | getOnionRequest :: Sized msg => Get (Asymm (Encrypted msg), ReturnPath N3) | ||
699 | getOnionRequest = do | ||
700 | -- Assumes return path is constant size so that we can isolate | ||
701 | -- the variable-sized prefix. | ||
702 | cnt <- remaining | ||
703 | a <- isolate (case size :: Size (ReturnPath N3) of ConstSize n -> cnt - n) | ||
704 | getAliasedAsymm | ||
705 | path <- get | ||
706 | return (a,path) | ||
707 | |||
708 | putRequest :: ( KnownPeanoNat n | ||
709 | , Serialize (OnionRequest n) | ||
710 | , Typeable n | ||
711 | ) => OnionRequest n -> Put | ||
712 | putRequest req = do | ||
713 | let tag = 0x80 + fromIntegral (peanoVal req) | ||
714 | when (tag <= 0x82) (putWord8 tag) | ||
715 | put req | ||
716 | |||
717 | putResponse :: (KnownPeanoNat n, Serialize (OnionResponse n)) => OnionResponse n -> Put | ||
718 | putResponse resp = do | ||
719 | let tag = 0x8f - fromIntegral (peanoVal resp) | ||
720 | -- OnionResponse N0 is an alias for the OnionMessage Encrypted type which includes a tag | ||
721 | -- in it's Serialize instance. | ||
722 | when (tag /= 0x8f) (putWord8 tag) | ||
723 | put resp | ||
724 | |||
725 | |||
726 | data KeyRecord = NotStored Nonce32 | ||
727 | | SendBackKey PublicKey | ||
728 | | Acknowledged Nonce32 | ||
729 | deriving Show | ||
730 | |||
731 | instance Sized KeyRecord where size = ConstSize 33 | ||
732 | |||
733 | instance S.Serialize KeyRecord where | ||
734 | get = do | ||
735 | is_stored <- S.get :: S.Get Word8 | ||
736 | case is_stored of | ||
737 | 1 -> SendBackKey <$> getPublicKey | ||
738 | 2 -> Acknowledged <$> S.get | ||
739 | _ -> NotStored <$> S.get | ||
740 | put (NotStored n32) = S.put (0 :: Word8) >> S.put n32 | ||
741 | put (SendBackKey key) = S.put (1 :: Word8) >> putPublicKey key | ||
742 | put (Acknowledged n32) = S.put (2 :: Word8) >> S.put n32 | ||
743 | |||
744 | data AnnounceResponse = AnnounceResponse | ||
745 | { is_stored :: KeyRecord | ||
746 | , announceNodes :: SendNodes | ||
747 | } | ||
748 | deriving Show | ||
749 | |||
750 | instance Sized AnnounceResponse where | ||
751 | size = contramap is_stored size <> contramap announceNodes size | ||
752 | |||
753 | getNodeList :: S.Get [NodeInfo] | ||
754 | getNodeList = do | ||
755 | n <- S.get | ||
756 | (:) n <$> (getNodeList <|> pure []) | ||
757 | |||
758 | instance S.Serialize AnnounceResponse where | ||
759 | get = AnnounceResponse <$> S.get <*> (SendNodes <$> getNodeList) | ||
760 | put (AnnounceResponse st (SendNodes ns)) = S.put st >> mapM_ S.put ns | ||
761 | |||
762 | data DataToRoute = DataToRoute | ||
763 | { dataFromKey :: PublicKey -- Real public key of sender | ||
764 | , dataToRoute :: Encrypted OnionData -- (Word8,ByteString) -- DHTPK 0x9c | ||
765 | } | ||
766 | |||
767 | instance Sized DataToRoute where | ||
768 | size = ConstSize 32 <> contramap dataToRoute size | ||
769 | |||
770 | instance Serialize DataToRoute where | ||
771 | get = DataToRoute <$> getPublicKey <*> get | ||
772 | put (DataToRoute k dta) = putPublicKey k >> put dta | ||
773 | |||
774 | data OnionData | ||
775 | = -- | type 0x9c | ||
776 | -- | ||
777 | -- We send this packet every 30 seconds if there is more than one peer (in | ||
778 | -- the 8) that says they our friend is announced on them. This packet can | ||
779 | -- also be sent through the DHT module as a DHT request packet (see DHT) if | ||
780 | -- we know the DHT public key of the friend and are looking for them in the | ||
781 | -- DHT but have not connected to them yet. 30 second is a reasonable | ||
782 | -- timeout to not flood the network with too many packets while making sure | ||
783 | -- the other will eventually receive the packet. Since packets are sent | ||
784 | -- through every peer that knows the friend, resending it right away | ||
785 | -- without waiting has a high likelihood of failure as the chances of | ||
786 | -- packet loss happening to all (up to to 8) packets sent is low. | ||
787 | -- | ||
788 | -- If a friend is online and connected to us, the onion will stop all of | ||
789 | -- its actions for that friend. If the peer goes offline it will restart | ||
790 | -- searching for the friend as if toxcore was just started. | ||
791 | OnionDHTPublicKey DHTPublicKey | ||
792 | | -- | type 0x20 | ||
793 | -- | ||
794 | -- | ||
795 | OnionFriendRequest FriendRequest -- 0x20 | ||
796 | deriving (Eq,Show) | ||
797 | |||
798 | instance Sized OnionData where | ||
799 | size = VarSize $ \case | ||
800 | OnionDHTPublicKey dhtpk -> case size of | ||
801 | ConstSize n -> n -- Override because OnionData probably | ||
802 | -- should be treated as variable sized. | ||
803 | VarSize f -> f dhtpk | ||
804 | -- FIXME: inconsitantly, we have to add in the tag byte for this case. | ||
805 | OnionFriendRequest req -> 1 + case size of | ||
806 | ConstSize n -> n | ||
807 | VarSize f -> f req | ||
808 | |||
809 | instance Serialize OnionData where | ||
810 | get = do | ||
811 | tag <- get | ||
812 | case tag :: Word8 of | ||
813 | 0x9c -> OnionDHTPublicKey <$> get | ||
814 | 0x20 -> OnionFriendRequest <$> get | ||
815 | _ -> fail $ "Unknown onion data: "++show tag | ||
816 | put (OnionDHTPublicKey dpk) = put (0x9c :: Word8) >> put dpk | ||
817 | put (OnionFriendRequest fr) = put (0x20 :: Word8) >> put fr | ||
818 | |||
819 | selectKey :: TransportCrypto -> OnionMessage f -> OnionDestination r -> IO (SecretKey, PublicKey) | ||
820 | selectKey crypto _ rpath@(OnionDestination (AnnouncingAlias skey pkey) _ _) | ||
821 | = return (skey, pkey) | ||
822 | selectKey crypto msg rpath = return $ aliasKey crypto rpath | ||
823 | |||
824 | encrypt :: TransportCrypto | ||
825 | -> OnionMessage Identity | ||
826 | -> OnionDestination r | ||
827 | -> IO (OnionMessage Encrypted, OnionDestination r) | ||
828 | encrypt crypto msg rpath = do | ||
829 | (skey,pkey) <- selectKey crypto msg rpath -- source key | ||
830 | let okey = onionKey rpath -- destination key | ||
831 | encipher1 :: Serialize a => SecretKey -> PublicKey -> Nonce24 -> a -> (IO ∘ Encrypted) a | ||
832 | encipher1 sk pk n a = Composed $ do | ||
833 | secret <- lookupSharedSecret crypto sk pk n | ||
834 | return $ ToxCrypto.encrypt secret $ encodePlain a | ||
835 | encipher :: Serialize a => Nonce24 -> Either (Identity a) (Asymm (Identity a)) -> (IO ∘ Encrypted) a | ||
836 | encipher n d = encipher1 skey okey n $ either runIdentity (runIdentity . asymmData) d | ||
837 | m <- sequenceMessage $ transcode encipher msg | ||
838 | return (m, rpath) | ||
839 | |||
840 | decrypt :: TransportCrypto -> OnionMessage Encrypted -> OnionDestination r -> IO (Either String (OnionMessage Identity, OnionDestination r)) | ||
841 | decrypt crypto msg addr = do | ||
842 | (skey,pkey) <- selectKey crypto msg addr | ||
843 | let decipher1 :: Serialize a => | ||
844 | TransportCrypto -> SecretKey -> Nonce24 | ||
845 | -> Either (PublicKey,Encrypted a) (Asymm (Encrypted a)) | ||
846 | -> (IO ∘ Either String ∘ Identity) a | ||
847 | decipher1 crypto k n arg = Composed $ do | ||
848 | let (sender,e) = either id (senderKey &&& asymmData) arg | ||
849 | secret <- lookupSharedSecret crypto k sender n | ||
850 | return $ Composed $ do | ||
851 | plain <- ToxCrypto.decrypt secret e | ||
852 | Identity <$> decodePlain plain | ||
853 | decipher :: Serialize a | ||
854 | => Nonce24 -> Either (Encrypted a) (Asymm (Encrypted a)) | ||
855 | -> (IO ∘ Either String ∘ Identity) a | ||
856 | decipher = (\n -> decipher1 crypto skey n . left (senderkey addr)) | ||
857 | foo <- sequenceMessage $ transcode decipher msg | ||
858 | return $ do | ||
859 | msg <- sequenceMessage foo | ||
860 | Right (msg, addr) | ||
861 | |||
862 | senderkey :: OnionDestination r -> t -> (PublicKey, t) | ||
863 | senderkey addr e = (onionKey addr, e) | ||
864 | |||
865 | aliasKey :: TransportCrypto -> OnionDestination r -> (SecretKey,PublicKey) | ||
866 | aliasKey crypto (OnionToOwner {}) = (transportSecret &&& transportPublic) crypto | ||
867 | aliasKey crypto (OnionDestination {}) = (onionAliasSecret &&& onionAliasPublic) crypto | ||
868 | |||
869 | dhtKey :: TransportCrypto -> (SecretKey,PublicKey) | ||
870 | dhtKey crypto = (transportSecret &&& transportPublic) crypto | ||
871 | |||
872 | decryptMessage :: Serialize x => | ||
873 | TransportCrypto | ||
874 | -> (SecretKey,PublicKey) | ||
875 | -> Nonce24 | ||
876 | -> Either (PublicKey, Encrypted x) | ||
877 | (Asymm (Encrypted x)) | ||
878 | -> IO ((Either String ∘ Identity) x) | ||
879 | decryptMessage crypto (sk,pk) n arg = do | ||
880 | let (sender,e) = either id (senderKey &&& asymmData) arg | ||
881 | plain = Composed . fmap Identity . (>>= decodePlain) | ||
882 | secret <- lookupSharedSecret crypto sk sender n | ||
883 | return $ plain $ ToxCrypto.decrypt secret e | ||
884 | |||
885 | sequenceMessage :: Applicative m => OnionMessage (m ∘ f) -> m (OnionMessage f) | ||
886 | sequenceMessage (OnionAnnounce a) = fmap OnionAnnounce $ sequenceA $ fmap uncomposed a | ||
887 | sequenceMessage (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 <$> uncomposed dta | ||
888 | sequenceMessage (OnionToRoute pub a) = pure $ OnionToRoute pub a | ||
889 | sequenceMessage (OnionToRouteResponse a) = pure $ OnionToRouteResponse a | ||
890 | -- sequenceMessage (OnionToRouteResponse a) = fmap OnionToRouteResponse $ sequenceA $ fmap uncomposed a | ||
891 | |||
892 | transcode :: forall f g. (forall a. Serialize a => Nonce24 -> Either (f a) (Asymm (f a)) -> g a) -> OnionMessage f -> OnionMessage g | ||
893 | transcode f (OnionAnnounce a) = OnionAnnounce $ a { asymmData = f (asymmNonce a) (Right a) } | ||
894 | transcode f (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 $ f n24 $ Left dta | ||
895 | transcode f (OnionToRoute pub a) = OnionToRoute pub a | ||
896 | transcode f (OnionToRouteResponse a) = OnionToRouteResponse a | ||
897 | -- transcode f (OnionToRouteResponse a) = OnionToRouteResponse $ a { asymmData = f (asymmNonce a) (Right a) } | ||
898 | |||
899 | |||
900 | data OnionRoute = OnionRoute | ||
901 | { routeAliasA :: SecretKey | ||
902 | , routeAliasB :: SecretKey | ||
903 | , routeAliasC :: SecretKey | ||
904 | , routeNodeA :: NodeInfo | ||
905 | , routeNodeB :: NodeInfo | ||
906 | , routeNodeC :: NodeInfo | ||
907 | } | ||
908 | |||
909 | wrapForRoute :: TransportCrypto -> OnionMessage Encrypted -> NodeInfo -> OnionRoute -> IO (OnionRequest N0) | ||
910 | wrapForRoute crypto msg ni r = do | ||
911 | -- We needn't use the same nonce value here, but I think it is safe to do so. | 88 | -- We needn't use the same nonce value here, but I think it is safe to do so. |
912 | let nonce = msgNonce msg | 89 | let nonce = msgNonce msg |
913 | fwd <- wrapOnion crypto (routeAliasA r) | 90 | fwd <- wrapOnion crypto (routeAliasA r) |
@@ -923,186 +100,20 @@ wrapForRoute crypto msg ni r = do | |||
923 | (id2key . nodeId $ routeNodeC r) | 100 | (id2key . nodeId $ routeNodeC r) |
924 | (nodeAddr ni) | 101 | (nodeAddr ni) |
925 | (NotForwarded msg) | 102 | (NotForwarded msg) |
926 | return OnionRequest | 103 | return $ Right OnionRequest |
927 | { onionNonce = nonce | 104 | { onionNonce = nonce |
928 | , onionForward = fwd | 105 | , onionForward = fwd |
929 | , pathFromOwner = NoReturnPath | 106 | , pathFromOwner = NoReturnPath |
930 | } | 107 | } |
931 | 108 | wrapForRoute crypto msg ni r@OnionRoute{routeRelayPort = Just tcpport} = do | |
932 | wrapOnion :: Serialize (Forwarding n msg) => | 109 | let nonce = msgNonce msg |
933 | TransportCrypto | 110 | fwd <- wrapOnion crypto (routeAliasB r) |
934 | -> SecretKey | 111 | nonce |
935 | -> Nonce24 | 112 | (id2key . nodeId $ routeNodeB r) |
936 | -> PublicKey | 113 | (nodeAddr $ routeNodeC r) |
937 | -> SockAddr | 114 | =<< wrapOnion crypto (routeAliasC r) |
938 | -> Forwarding n msg | 115 | nonce |
939 | -> IO (Forwarding (S n) msg) | 116 | (id2key . nodeId $ routeNodeC r) |
940 | wrapOnion crypto skey nonce destkey saddr fwd = do | 117 | (nodeAddr ni) |
941 | let plain = encodePlain $ Addressed saddr fwd | 118 | (NotForwarded msg) |
942 | secret <- lookupSharedSecret crypto skey destkey nonce | 119 | return $ Left $ TCP.OnionPacket nonce $ Addressed (nodeAddr $ routeNodeB r) fwd |
943 | return $ Forwarding (toPublic skey) $ ToxCrypto.encrypt secret plain | ||
944 | |||
945 | wrapOnionPure :: Serialize (Forwarding n msg) => | ||
946 | SecretKey | ||
947 | -> ToxCrypto.State | ||
948 | -> SockAddr | ||
949 | -> Forwarding n msg | ||
950 | -> Forwarding (S n) msg | ||
951 | wrapOnionPure skey st saddr fwd = Forwarding (toPublic skey) (ToxCrypto.encrypt st plain) | ||
952 | where | ||
953 | plain = encodePlain $ Addressed saddr fwd | ||
954 | |||
955 | |||
956 | |||
957 | -- TODO | ||
958 | -- Two types of packets may be sent to Rendezvous via OnionToRoute requests. | ||
959 | -- | ||
960 | -- (1) DHT public key packet (0x9c) | ||
961 | -- | ||
962 | -- (2) Friend request | ||
963 | data Rendezvous = Rendezvous | ||
964 | { rendezvousKey :: PublicKey | ||
965 | , rendezvousNode :: NodeInfo | ||
966 | } | ||
967 | deriving Eq | ||
968 | |||
969 | instance Show Rendezvous where | ||
970 | showsPrec d (Rendezvous k ni) | ||
971 | = showsPrec d (key2id k) | ||
972 | . (':' :) | ||
973 | . showsPrec d ni | ||
974 | |||
975 | instance Read Rendezvous where | ||
976 | readsPrec d = RP.readP_to_S $ do | ||
977 | rkstr <- RP.munch (/=':') | ||
978 | RP.char ':' | ||
979 | nistr <- RP.munch (const True) | ||
980 | return Rendezvous | ||
981 | { rendezvousKey = id2key $ read rkstr | ||
982 | , rendezvousNode = read nistr | ||
983 | } | ||
984 | |||
985 | |||
986 | data AnnouncedRendezvous = AnnouncedRendezvous | ||
987 | { remoteUserKey :: PublicKey | ||
988 | , rendezvous :: Rendezvous | ||
989 | } | ||
990 | deriving Eq | ||
991 | |||
992 | instance Show AnnouncedRendezvous where | ||
993 | showsPrec d (AnnouncedRendezvous remote rendez) | ||
994 | = showsPrec d (key2id remote) | ||
995 | . (':' :) | ||
996 | . showsPrec d rendez | ||
997 | |||
998 | instance Read AnnouncedRendezvous where | ||
999 | readsPrec d = RP.readP_to_S $ do | ||
1000 | ukstr <- RP.munch (/=':') | ||
1001 | RP.char ':' | ||
1002 | rkstr <- RP.munch (/=':') | ||
1003 | RP.char ':' | ||
1004 | nistr <- RP.munch (const True) | ||
1005 | return AnnouncedRendezvous | ||
1006 | { remoteUserKey = id2key $ read ukstr | ||
1007 | , rendezvous = Rendezvous | ||
1008 | { rendezvousKey = id2key $ read rkstr | ||
1009 | , rendezvousNode = read nistr | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | |||
1014 | selectAlias :: TransportCrypto -> NodeId -> STM AliasSelector | ||
1015 | selectAlias crypto pkey = do | ||
1016 | ks <- filter (\(sk,pk) -> pk == id2key pkey) | ||
1017 | <$> userKeys crypto | ||
1018 | maybe (return SearchingAlias) | ||
1019 | (return . uncurry AnnouncingAlias) | ||
1020 | (listToMaybe ks) | ||
1021 | |||
1022 | |||
1023 | parseDataToRoute | ||
1024 | :: TransportCrypto | ||
1025 | -> (OnionMessage Encrypted,OnionDestination r) | ||
1026 | -> IO (Either ((PublicKey,OnionData),AnnouncedRendezvous) (OnionMessage Encrypted, OnionDestination r)) | ||
1027 | parseDataToRoute crypto (OnionToRouteResponse dta, od) = do | ||
1028 | ks <- atomically $ userKeys crypto | ||
1029 | |||
1030 | omsg0 <- decryptMessage crypto (rendezvousSecret crypto,rendezvousPublic crypto) | ||
1031 | (asymmNonce dta) | ||
1032 | (Right dta) -- using Asymm{senderKey} as remote key | ||
1033 | let eOuter = fmap runIdentity $ uncomposed omsg0 | ||
1034 | |||
1035 | anyRight [] f = return $ Left "parseDataToRoute: no user key" | ||
1036 | anyRight (x:xs) f = f x >>= either (const $ anyRight xs f) (return . Right) | ||
1037 | |||
1038 | -- TODO: We don't currently have a way to look up which user key we | ||
1039 | -- announced using along this onion route. Therefore, for now, we will | ||
1040 | -- try all our user keys to see if any can decrypt the packet. | ||
1041 | eInner <- case eOuter of | ||
1042 | Left e -> return $ Left e | ||
1043 | Right dtr -> anyRight ks $ \(sk,pk) -> do | ||
1044 | omsg0 <- decryptMessage crypto | ||
1045 | (sk,pk) | ||
1046 | (asymmNonce dta) | ||
1047 | (Left (dataFromKey dtr, dataToRoute dtr)) | ||
1048 | return $ do | ||
1049 | omsg <- fmap runIdentity . uncomposed $ omsg0 | ||
1050 | Right (pk,dtr,omsg) | ||
1051 | |||
1052 | let e = do | ||
1053 | (pk,dtr,omsg) <- eInner | ||
1054 | return ( (pk, omsg) | ||
1055 | , AnnouncedRendezvous | ||
1056 | (dataFromKey dtr) | ||
1057 | $ Rendezvous (rendezvousPublic crypto) $ onionNodeInfo od ) | ||
1058 | r = either (const $ Right (OnionToRouteResponse dta,od)) Left e | ||
1059 | -- parseDataToRoute OnionToRouteResponse decipherAndAuth: auth fail | ||
1060 | case e of | ||
1061 | Left _ -> dput XMisc $ "Failed keys: " ++ show (map (key2id . snd) ks) | ||
1062 | Right _ -> return () | ||
1063 | dput XMisc $ unlines | ||
1064 | [ "parseDataToRoute " ++ either id (const "Right") e | ||
1065 | , " crypto inner.me = " ++ either id (\(pk,_,_) -> show $ key2id pk) eInner | ||
1066 | , " inner.them = " ++ either id (show . key2id . dataFromKey) eOuter | ||
1067 | , " outer.me = " ++ show (key2id $ rendezvousPublic crypto) | ||
1068 | , " outer.them = " ++ show (key2id $ senderKey dta) | ||
1069 | ] | ||
1070 | return r | ||
1071 | parseDataToRoute _ msg = return $ Right msg | ||
1072 | |||
1073 | encodeDataToRoute :: TransportCrypto | ||
1074 | -> ((PublicKey,OnionData),AnnouncedRendezvous) | ||
1075 | -> IO (Maybe (OnionMessage Encrypted,OnionDestination r)) | ||
1076 | encodeDataToRoute crypto ((me,omsg), AnnouncedRendezvous toxid (Rendezvous pub ni)) = do | ||
1077 | nonce <- atomically $ transportNewNonce crypto | ||
1078 | asel <- atomically $ selectAlias crypto (key2id me) | ||
1079 | let (sk,pk) = case asel of | ||
1080 | AnnouncingAlias sk pk -> (sk,pk) | ||
1081 | _ -> (onionAliasSecret crypto, onionAliasPublic crypto) | ||
1082 | innerSecret <- lookupSharedSecret crypto sk toxid nonce | ||
1083 | let plain = encodePlain $ DataToRoute { dataFromKey = pk | ||
1084 | , dataToRoute = ToxCrypto.encrypt innerSecret $ encodePlain omsg | ||
1085 | } | ||
1086 | outerSecret <- lookupSharedSecret crypto (onionAliasSecret crypto) pub nonce | ||
1087 | let dta = ToxCrypto.encrypt outerSecret plain | ||
1088 | dput XOnion $ unlines | ||
1089 | [ "encodeDataToRoute me=" ++ show (key2id me) | ||
1090 | , " dhtpk=" ++ case omsg of | ||
1091 | OnionDHTPublicKey dmsg -> show (key2id $ dhtpk dmsg) | ||
1092 | OnionFriendRequest fr -> "friend request" | ||
1093 | , " ns=" ++ case omsg of | ||
1094 | OnionDHTPublicKey dmsg -> show (dhtpkNodes dmsg) | ||
1095 | OnionFriendRequest fr -> "friend request" | ||
1096 | , " crypto inner.me =" ++ show (key2id pk) | ||
1097 | , " inner.you=" ++ show (key2id toxid) | ||
1098 | , " outer.me =" ++ show (key2id $ onionAliasPublic crypto) | ||
1099 | , " outer.you=" ++ show (key2id pub) | ||
1100 | , " " ++ show (AnnouncedRendezvous toxid (Rendezvous pub ni)) | ||
1101 | , " " ++ show dta | ||
1102 | ] | ||
1103 | return $ Just ( OnionToRoute toxid -- Public key of destination node | ||
1104 | Asymm { senderKey = onionAliasPublic crypto | ||
1105 | , asymmNonce = nonce | ||
1106 | , asymmData = dta | ||
1107 | } | ||
1108 | , OnionDestination SearchingAlias ni Nothing ) | ||
diff --git a/src/Network/Tox/TCP.hs b/src/Network/Tox/TCP.hs index e3f5012b..1111d3b8 100644 --- a/src/Network/Tox/TCP.hs +++ b/src/Network/Tox/TCP.hs | |||
@@ -2,7 +2,10 @@ | |||
2 | {-# LANGUAGE PartialTypeSignatures #-} | 2 | {-# LANGUAGE PartialTypeSignatures #-} |
3 | {-# LANGUAGE LambdaCase #-} | 3 | {-# LANGUAGE LambdaCase #-} |
4 | {-# LANGUAGE FlexibleContexts #-} | 4 | {-# LANGUAGE FlexibleContexts #-} |
5 | module Network.Tox.TCP where | 5 | module Network.Tox.TCP |
6 | ( module Network.Tox.TCP | ||
7 | , NodeInfo(..) | ||
8 | ) where | ||
6 | 9 | ||
7 | import Control.Arrow | 10 | import Control.Arrow |
8 | import Control.Concurrent | 11 | import Control.Concurrent |
@@ -46,11 +49,6 @@ import qualified Network.Tox.NodeId as UDP | |||
46 | withSize :: Sized x => (Size x -> m (p x)) -> m (p x) | 49 | withSize :: Sized x => (Size x -> m (p x)) -> m (p x) |
47 | withSize f = case size of len -> f len | 50 | withSize f = case size of len -> f len |
48 | 51 | ||
49 | data NodeInfo = NodeInfo | ||
50 | { udpNodeInfo :: UDP.NodeInfo | ||
51 | , tcpPort :: PortNumber | ||
52 | } | ||
53 | deriving (Eq,Ord) | ||
54 | 52 | ||
55 | type NodeId = UDP.NodeId | 53 | type NodeId = UDP.NodeId |
56 | 54 | ||
@@ -59,36 +57,6 @@ type NodeId = UDP.NodeId | |||
59 | instance Show NodeInfo where | 57 | instance Show NodeInfo where |
60 | show (NodeInfo udp port) = show udp ++ "{tcp:"++show port++"}" | 58 | show (NodeInfo udp port) = show udp ++ "{tcp:"++show port++"}" |
61 | 59 | ||
62 | instance Read NodeInfo where | ||
63 | readsPrec _ = RP.readP_to_S $ do | ||
64 | udp <- RP.readS_to_P reads | ||
65 | port <- RP.between (RP.char '{') (RP.char '}') $ do | ||
66 | mapM_ RP.char ("tcp:" :: String) | ||
67 | w16 <- RP.readS_to_P reads | ||
68 | return $ fromIntegral (w16 :: Word16) | ||
69 | return $ NodeInfo udp port | ||
70 | |||
71 | instance ToJSON NodeInfo where | ||
72 | toJSON (NodeInfo udp port) = case (toJSON udp) of | ||
73 | JSON.Object tbl -> JSON.Object $ HashMap.insert "tcp_ports" | ||
74 | (JSON.Array $ Vector.fromList | ||
75 | [JSON.Number (fromIntegral port)]) | ||
76 | tbl | ||
77 | x -> x -- Shouldn't happen. | ||
78 | |||
79 | instance FromJSON NodeInfo where | ||
80 | parseJSON json = do | ||
81 | udp <- parseJSON json | ||
82 | port <- case json of | ||
83 | JSON.Object v -> do | ||
84 | portnum:_ <- v JSON..: "tcp_ports" | ||
85 | return (fromIntegral (portnum :: Word16)) | ||
86 | _ -> fail "TCP.NodeInfo: Expected JSON object." | ||
87 | return $ NodeInfo udp port | ||
88 | |||
89 | instance Hashable NodeInfo where | ||
90 | hashWithSalt s n = hashWithSalt s (udpNodeInfo n) | ||
91 | |||
92 | nodeId :: NodeInfo -> NodeId | 60 | nodeId :: NodeInfo -> NodeId |
93 | nodeId ni = UDP.nodeId $ udpNodeInfo ni | 61 | nodeId ni = UDP.nodeId $ udpNodeInfo ni |
94 | 62 | ||
@@ -275,12 +243,21 @@ tcpPing client dst = sendQuery client meth () dst | |||
275 | 243 | ||
276 | type RelayClient = Client String () Nonce8 NodeInfo RelayPacket | 244 | type RelayClient = Client String () Nonce8 NodeInfo RelayPacket |
277 | 245 | ||
278 | newClient :: TransportCrypto -> IO RelayClient | 246 | -- | Create a new TCP relay client. Because polymorphic existential record |
279 | newClient crypto = do | 247 | -- updates are currently hard with GHC, this function accepts parameters for |
248 | -- generalizing the table-entry type for pending transactions. Safe trivial | ||
249 | -- defaults are 'id' and 'tryPutMVar'. The resulting customized table state | ||
250 | -- will be returned to the caller along with the new client. | ||
251 | newClient :: TransportCrypto | ||
252 | -> (MVar RelayPacket -> a) -- ^ store mvar for query | ||
253 | -> (a -> RelayPacket -> IO void) -- ^ load mvar for query | ||
254 | -> IO ( TVar (ChaChaDRG, Data.Word64Map.Word64Map a) | ||
255 | , Client String () Nonce8 NodeInfo RelayPacket) | ||
256 | newClient crypto store load = do | ||
280 | net <- toxTCP crypto | 257 | net <- toxTCP crypto |
281 | drg <- drgNew | 258 | drg <- drgNew |
282 | map_var <- atomically $ newTVar (drg, Data.Word64Map.empty) | 259 | map_var <- atomically $ newTVar (drg, Data.Word64Map.empty) |
283 | return Client | 260 | return $ (,) map_var Client |
284 | { clientNet = net | 261 | { clientNet = net |
285 | , clientDispatcher = DispatchMethods | 262 | , clientDispatcher = DispatchMethods |
286 | { classifyInbound = \case | 263 | { classifyInbound = \case |
@@ -294,7 +271,7 @@ newClient crypto = do | |||
294 | , methodSerialize = \n8 src dst () -> RelayPong n8 | 271 | , methodSerialize = \n8 src dst () -> RelayPong n8 |
295 | , methodAction = \src () -> return () | 272 | , methodAction = \src () -> return () |
296 | } | 273 | } |
297 | , tableMethods = transactionMethods (contramap (\(Nonce8 w64) -> w64) w64MapMethods) | 274 | , tableMethods = transactionMethods' store load (contramap (\(Nonce8 w64) -> w64) w64MapMethods) |
298 | $ first (either error Nonce8 . decode) . randomBytesGenerate 8 | 275 | $ first (either error Nonce8 . decode) . randomBytesGenerate 8 |
299 | } | 276 | } |
300 | , clientErrorReporter = logErrors | 277 | , clientErrorReporter = logErrors |
diff --git a/src/Network/Tox/Transport.hs b/src/Network/Tox/Transport.hs index e79e4d8b..217d5b1d 100644 --- a/src/Network/Tox/Transport.hs +++ b/src/Network/Tox/Transport.hs | |||
@@ -10,7 +10,8 @@ module Network.Tox.Transport (toxTransport, RouteId) where | |||
10 | 10 | ||
11 | import Network.QueryResponse | 11 | import Network.QueryResponse |
12 | import Crypto.Tox | 12 | import Crypto.Tox |
13 | import Network.Tox.DHT.Transport | 13 | import Data.Tox.Relay as TCP |
14 | import Network.Tox.DHT.Transport as UDP | ||
14 | import Network.Tox.Onion.Transport | 15 | import Network.Tox.Onion.Transport |
15 | import Network.Tox.Crypto.Transport | 16 | import Network.Tox.Crypto.Transport |
16 | import OnionRouter | 17 | import OnionRouter |
@@ -20,20 +21,23 @@ import Network.Socket | |||
20 | toxTransport :: | 21 | toxTransport :: |
21 | TransportCrypto | 22 | TransportCrypto |
22 | -> OnionRouter | 23 | -> OnionRouter |
23 | -> (PublicKey -> IO (Maybe NodeInfo)) | 24 | -> (PublicKey -> IO (Maybe UDP.NodeInfo)) |
24 | -> UDPTransport | 25 | -> UDPTransport |
25 | -> (Int -> OnionMessage Encrypted -> IO ()) -- ^ TCP-bound callback. | 26 | -> (TCP.NodeInfo -> RelayPacket -> IO ()) -- ^ TCP server-bound callback. |
27 | -> (Int -> OnionMessage Encrypted -> IO ()) -- ^ TCP client-bound callback. | ||
26 | -> IO ( Transport String SockAddr (CryptoPacket Encrypted) | 28 | -> IO ( Transport String SockAddr (CryptoPacket Encrypted) |
27 | , Transport String NodeInfo (DHTMessage Encrypted8) | 29 | , Transport String UDP.NodeInfo (DHTMessage Encrypted8) |
28 | , Transport String (OnionDestination RouteId) (OnionMessage Encrypted) | 30 | , Transport String (OnionDestination RouteId) (OnionMessage Encrypted) |
29 | , Transport String AnnouncedRendezvous (PublicKey,OnionData) | 31 | , Transport String AnnouncedRendezvous (PublicKey,OnionData) |
30 | , Transport String SockAddr (Handshake Encrypted)) | 32 | , Transport String SockAddr (Handshake Encrypted)) |
31 | toxTransport crypto orouter closeLookup udp tcp = do | 33 | toxTransport crypto orouter closeLookup udp tcp2server tcp2client = do |
32 | (netcrypto, udp0) <- partitionTransport parseCrypto encodeCrypto udp | 34 | (netcrypto, udp0) <- partitionTransport parseCrypto encodeCrypto udp |
33 | (dht,udp1) <- partitionTransportM (parseDHTAddr crypto) (fmap Just . encodeDHTAddr) $ forwardOnions crypto udp0 tcp | 35 | (dht,udp1) <- partitionTransportM (parseDHTAddr crypto) (fmap Just . encodeDHTAddr) |
34 | (onion1,udp2) <- partitionTransportM (parseOnionAddr $ lookupSender orouter) | 36 | $ forwardOnions crypto udp0 tcp2client |
35 | (encodeOnionAddr crypto $ lookupRoute orouter) | 37 | (onion1,udp2) <- partitionAndForkTransport tcp2server |
36 | udp1 | 38 | (parseOnionAddr $ lookupSender orouter) |
39 | (encodeOnionAddr crypto $ lookupRoute orouter) | ||
40 | udp1 | ||
37 | (dta,onion) <- partitionTransportM (parseDataToRoute crypto) (encodeDataToRoute crypto) onion1 | 41 | (dta,onion) <- partitionTransportM (parseDataToRoute crypto) (encodeDataToRoute crypto) onion1 |
38 | let handshakes = layerTransport parseHandshakes encodeHandshakes udp2 | 42 | let handshakes = layerTransport parseHandshakes encodeHandshakes udp2 |
39 | return ( netcrypto | 43 | return ( netcrypto |