summaryrefslogtreecommitdiff
path: root/src/Network/Tox
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/Tox')
-rw-r--r--src/Network/Tox/DHT/Transport.hs29
-rw-r--r--src/Network/Tox/NodeId.hs6
-rw-r--r--src/Network/Tox/Onion/Handlers.hs30
-rw-r--r--src/Network/Tox/Onion/Transport.hs72
-rw-r--r--src/Network/Tox/Transport.hs5
5 files changed, 122 insertions, 20 deletions
diff --git a/src/Network/Tox/DHT/Transport.hs b/src/Network/Tox/DHT/Transport.hs
index 187e23f2..16af0e3f 100644
--- a/src/Network/Tox/DHT/Transport.hs
+++ b/src/Network/Tox/DHT/Transport.hs
@@ -16,7 +16,8 @@ module Network.Tox.DHT.Transport
16 , Pong(..) 16 , Pong(..)
17 , GetNodes(..) 17 , GetNodes(..)
18 , SendNodes(..) 18 , SendNodes(..)
19 , DHTPublicKey 19 , DHTPublicKey(..)
20 , FriendRequest(..)
20 , CookieRequest 21 , CookieRequest
21 , Cookie 22 , Cookie
22 , DHTRequest 23 , DHTRequest
@@ -35,10 +36,12 @@ import Network.QueryResponse
35import Control.Arrow 36import Control.Arrow
36import Control.Monad 37import Control.Monad
37import Data.Bool 38import Data.Bool
38import qualified Data.ByteString as B 39import qualified Data.ByteString as B
39 ;import Data.ByteString (ByteString) 40 ;import Data.ByteString (ByteString)
41import Data.Functor.Contravariant
42import Data.Monoid
43import Data.Serialize as S
40import Data.Tuple 44import Data.Tuple
41import Data.Serialize as S
42import Data.Word 45import Data.Word
43import Network.Socket 46import Network.Socket
44 47
@@ -203,6 +206,13 @@ data DHTPublicKey = DHTPublicKey
203 , dhtpkNodes :: SendNodes -- other reachable nodes 206 , dhtpkNodes :: SendNodes -- other reachable nodes
204 } 207 }
205 208
209-- int8_t (0x20 sent over onion, 0x12 for sent over net_crypto)
210-- [uint32_t nospam][Message (UTF8) 1 to ONION_CLIENT_MAX_DATA_SIZE bytes]
211data FriendRequest = FriendRequest
212 { friendNoSpam :: Word32
213 , friendRequestText :: ByteString -- UTF8
214 }
215
206-- When sent as a DHT request packet (this is the data sent in the DHT request 216-- When sent as a DHT request packet (this is the data sent in the DHT request
207-- packet): 217-- packet):
208-- 218--
@@ -231,6 +241,13 @@ instance Sized DHTPublicKey where
231 ConstSize nodes -> nodes 241 ConstSize nodes -> nodes
232 VarSize sznodes -> sznodes nodes 242 VarSize sznodes -> sznodes nodes
233 243
244instance Sized Word32 where size = ConstSize 4
245
246-- FIXME: Inconsitently, this type does not include the 0x20 or 0x12 tag byte
247-- where the DHTPublicKey type does include its tag.
248instance Sized FriendRequest where
249 size = contramap friendNoSpam size <> contramap friendRequestText (VarSize B.length)
250
234instance Serialize DHTPublicKey where 251instance Serialize DHTPublicKey where
235 -- TODO: This should agree with Sized instance. 252 -- TODO: This should agree with Sized instance.
236 get = DHTPublicKey <$> get <*> getPublicKey <*> get 253 get = DHTPublicKey <$> get <*> getPublicKey <*> get
@@ -239,6 +256,10 @@ instance Serialize DHTPublicKey where
239 putPublicKey key 256 putPublicKey key
240 put nodes 257 put nodes
241 258
259instance Serialize FriendRequest where
260 get = FriendRequest <$> get <*> (remaining >>= getBytes)
261 put (FriendRequest nospam txt) = put nospam >> putByteString txt
262
242newtype GetNodes = GetNodes NodeId 263newtype GetNodes = GetNodes NodeId
243 deriving (Eq,Ord,Show,Read,S.Serialize) 264 deriving (Eq,Ord,Show,Read,S.Serialize)
244 265
diff --git a/src/Network/Tox/NodeId.hs b/src/Network/Tox/NodeId.hs
index bba56e27..20dc1854 100644
--- a/src/Network/Tox/NodeId.hs
+++ b/src/Network/Tox/NodeId.hs
@@ -213,8 +213,10 @@ instance FromJSON NodeInfo where
213 ip <- maybe empty (return . IPv6) (ip6str >>= readMaybe) 213 ip <- maybe empty (return . IPv6) (ip6str >>= readMaybe)
214 <|> maybe empty (return . IPv4) (ip4str >>= readMaybe) 214 <|> maybe empty (return . IPv4) (ip4str >>= readMaybe)
215 let (bs,_) = Base16.decode (C8.pack nidstr) 215 let (bs,_) = Base16.decode (C8.pack nidstr)
216 guard (B.length bs == 32) 216 enid = Base64.decode (C8.pack $ 'A' : map (nmtoken64 False) nidstr)
217 return $ NodeInfo (bs2id bs) ip (fromIntegral (portnum :: Word16)) 217 idbs <- (guard (B.length bs == 32) >> return bs)
218 <|> either fail (return . B.drop 1) enid
219 return $ NodeInfo (bs2id idbs) ip (fromIntegral (portnum :: Word16))
218 220
219getIP :: Word8 -> S.Get IP 221getIP :: Word8 -> S.Get IP
220getIP 0x02 = IPv4 <$> S.get 222getIP 0x02 = IPv4 <$> S.get
diff --git a/src/Network/Tox/Onion/Handlers.hs b/src/Network/Tox/Onion/Handlers.hs
index 167df336..439de709 100644
--- a/src/Network/Tox/Onion/Handlers.hs
+++ b/src/Network/Tox/Onion/Handlers.hs
@@ -130,7 +130,7 @@ toOnionDestination :: AnnouncedRoute -> OnionDestination r
130toOnionDestination (AnnouncedRoute ni rpath) = OnionToOwner ni rpath 130toOnionDestination (AnnouncedRoute ni rpath) = OnionToOwner ni rpath
131 131
132data AnnouncedKeys = AnnouncedKeys 132data AnnouncedKeys = AnnouncedKeys
133 { keyByAge :: !(PSQ NodeId (Down POSIXTime)) -- timeout of 300 seconds 133 { keyByAge :: !(PSQ NodeId (Down POSIXTime)) -- TODO: timeout of 300 seconds
134 , keyAssoc :: !(MinMaxPSQ' NodeId NodeDistance (Int,AnnouncedRoute)) 134 , keyAssoc :: !(MinMaxPSQ' NodeId NodeDistance (Int,AnnouncedRoute))
135 } 135 }
136 136
@@ -160,15 +160,6 @@ handlers net routing toks keydb AnnounceType
160handlers net _ _ keydb _ = Just $ NoReply Right $ dataToRouteH keydb net 160handlers net _ _ keydb _ = Just $ NoReply Right $ dataToRouteH keydb net
161 161
162 162
163data Rendezvous = Rendezvous
164 { rendezvousKey :: PublicKey
165 , rendezvousNode :: NodeInfo
166 }
167 deriving Eq
168
169instance Show Rendezvous where
170 show (Rendezvous k ni) = concat [show $ key2id k, ":", show ni]
171
172toxidSearch :: (TransactionId -> OnionDestination r -> STM (OnionDestination r, Int)) 163toxidSearch :: (TransactionId -> OnionDestination r -> STM (OnionDestination r, Int))
173 -> Client r 164 -> Client r
174 -> Search NodeId (IP, PortNumber) (Maybe Nonce32) NodeInfo Rendezvous 165 -> Search NodeId (IP, PortNumber) (Maybe Nonce32) NodeInfo Rendezvous
@@ -211,6 +202,25 @@ unwrapAnnounceResponse ni (AnnounceResponse is_stored (SendNodes ns))
211 SendBackKey k -> (ns, [Rendezvous k ni], Nothing) 202 SendBackKey k -> (ns, [Rendezvous k ni], Nothing)
212 Acknowledged n32 -> (ns, [], Just n32) 203 Acknowledged n32 -> (ns, [], Just n32)
213 204
205-- TODO Announce key to announce peers.
206--
207-- Announce Peers are only put in the 8 closest peers array if they respond
208-- to an announce request. If the peers fail to respond to 3 announce
209-- requests they are deemed timed out and removed.
210--
211-- ...
212--
213-- For this reason, after the peer is announced successfully for 17 seconds,
214-- announce packets are sent aggressively every 3 seconds to each known close
215-- peer (in the list of 8 peers) to search aggressively for peers that know
216-- the peer we are searching for.
217
218-- TODO
219-- If toxcore goes offline (no onion traffic for 20 seconds) toxcore will
220-- aggressively reannounce itself and search for friends as if it was just
221-- started.
222
223
214announce :: (TransactionId -> OnionDestination r -> STM (OnionDestination r, Int)) 224announce :: (TransactionId -> OnionDestination r -> STM (OnionDestination r, Int))
215 -> Client r 225 -> Client r
216 -> NodeId 226 -> NodeId
diff --git a/src/Network/Tox/Onion/Transport.hs b/src/Network/Tox/Onion/Transport.hs
index 989b06fd..3e3596a6 100644
--- a/src/Network/Tox/Onion/Transport.hs
+++ b/src/Network/Tox/Onion/Transport.hs
@@ -17,9 +17,12 @@
17module Network.Tox.Onion.Transport 17module Network.Tox.Onion.Transport
18 ( parseOnionAddr 18 ( parseOnionAddr
19 , encodeOnionAddr 19 , encodeOnionAddr
20 , parseDataToRoute
21 , encodeDataToRoute
20 , forwardOnions 22 , forwardOnions
21 , OnionDestination(..) 23 , OnionDestination(..)
22 , OnionMessage(..) 24 , OnionMessage(..)
25 , Rendezvous(..)
23 , DataToRoute(..) 26 , DataToRoute(..)
24 , AnnounceResponse(..) 27 , AnnounceResponse(..)
25 , AnnounceRequest(..) 28 , AnnounceRequest(..)
@@ -44,7 +47,7 @@ import Network.QueryResponse
44import Crypto.Tox hiding (encrypt,decrypt) 47import Crypto.Tox hiding (encrypt,decrypt)
45import Network.Tox.NodeId 48import Network.Tox.NodeId
46import qualified Crypto.Tox as ToxCrypto 49import qualified Crypto.Tox as ToxCrypto
47import Network.Tox.DHT.Transport (NodeInfo(..),NodeId(..),SendNodes(..),nodeInfo,DHTPublicKey,asymNodeInfo) 50import Network.Tox.DHT.Transport (NodeInfo(..),NodeId(..),SendNodes(..),nodeInfo,DHTPublicKey,FriendRequest,asymNodeInfo)
48 51
49import Debug.Trace 52import Debug.Trace
50import Control.Arrow 53import Control.Arrow
@@ -561,13 +564,39 @@ instance Serialize DataToRoute where
561 get = DataToRoute <$> getPublicKey <*> get 564 get = DataToRoute <$> getPublicKey <*> get
562 put (DataToRoute k dta) = putPublicKey k >> put dta 565 put (DataToRoute k dta) = putPublicKey k >> put dta
563 566
564data OnionData = OnionDHTPublicKey DHTPublicKey -- 0x9c 567data OnionData
568 = -- | type 0x9c
569 --
570 -- We send this packet every 30 seconds if there is more than one peer (in
571 -- the 8) that says they our friend is announced on them. This packet can
572 -- also be sent through the DHT module as a DHT request packet (see DHT) if
573 -- we know the DHT public key of the friend and are looking for them in the
574 -- DHT but have not connected to them yet. 30 second is a reasonable
575 -- timeout to not flood the network with too many packets while making sure
576 -- the other will eventually receive the packet. Since packets are sent
577 -- through every peer that knows the friend, resending it right away
578 -- without waiting has a high likelihood of failure as the chances of
579 -- packet loss happening to all (up to to 8) packets sent is low.
580 --
581 -- If a friend is online and connected to us, the onion will stop all of
582 -- its actions for that friend. If the peer goes offline it will restart
583 -- searching for the friend as if toxcore was just started.
584 OnionDHTPublicKey DHTPublicKey
585 | -- | type 0x20
586 --
587 --
588 OnionFriendRequest FriendRequest -- 0x20
565 589
566instance Sized OnionData where 590instance Sized OnionData where
567 size = VarSize $ \(OnionDHTPublicKey dhtpk) -> case size of 591 size = VarSize $ \case
592 OnionDHTPublicKey dhtpk -> case size of
568 ConstSize n -> n -- Override because OnionData probably 593 ConstSize n -> n -- Override because OnionData probably
569 -- should be treated as variable sized. 594 -- should be treated as variable sized.
570 VarSize f -> f dhtpk 595 VarSize f -> f dhtpk
596 -- FIXME: inconsitantly, we have to add in the tag byte for this case.
597 OnionFriendRequest req -> 1 + case size of
598 ConstSize n -> n
599 VarSize f -> f req
571 600
572encrypt :: TransportCrypto -> OnionMessage Identity -> OnionDestination r -> (OnionMessage Encrypted, OnionDestination r) 601encrypt :: TransportCrypto -> OnionMessage Identity -> OnionDestination r -> (OnionMessage Encrypted, OnionDestination r)
573encrypt crypto msg rpath = ( transcode ( (. (runIdentity . either id assymData)) 602encrypt crypto msg rpath = ( transcode ( (. (runIdentity . either id assymData))
@@ -625,12 +654,14 @@ sequenceMessage (OnionAnnounce a) = fmap OnionAnnounce $ sequenceA $ fmap
625sequenceMessage (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 <$> uncomposed dta 654sequenceMessage (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 <$> uncomposed dta
626sequenceMessage (OnionToRoute pub a) = pure $ OnionToRoute pub a 655sequenceMessage (OnionToRoute pub a) = pure $ OnionToRoute pub a
627sequenceMessage (OnionToRouteResponse a) = pure $ OnionToRouteResponse a 656sequenceMessage (OnionToRouteResponse a) = pure $ OnionToRouteResponse a
657-- sequenceMessage (OnionToRouteResponse a) = fmap OnionToRouteResponse $ sequenceA $ fmap uncomposed a
628 658
629transcode :: forall f g. (forall a. Serialize a => Nonce24 -> Either (f a) (Assym (f a)) -> g a) -> OnionMessage f -> OnionMessage g 659transcode :: forall f g. (forall a. Serialize a => Nonce24 -> Either (f a) (Assym (f a)) -> g a) -> OnionMessage f -> OnionMessage g
630transcode f (OnionAnnounce a) = OnionAnnounce $ a { assymData = f (assymNonce a) (Right a) } 660transcode f (OnionAnnounce a) = OnionAnnounce $ a { assymData = f (assymNonce a) (Right a) }
631transcode f (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 $ f n24 $ Left dta 661transcode f (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 $ f n24 $ Left dta
632transcode f (OnionToRoute pub a) = OnionToRoute pub a 662transcode f (OnionToRoute pub a) = OnionToRoute pub a
633transcode f (OnionToRouteResponse a) = OnionToRouteResponse a 663transcode f (OnionToRouteResponse a) = OnionToRouteResponse a
664-- transcode f (OnionToRouteResponse a) = OnionToRouteResponse $ a { assymData = f (assymNonce a) (Right a) }
634 665
635 666
636data OnionRoute = OnionRoute 667data OnionRoute = OnionRoute
@@ -673,3 +704,38 @@ wrapOnion :: Serialize (Forwarding n msg) =>
673 -> Forwarding (S n) msg 704 -> Forwarding (S n) msg
674wrapOnion skey nonce destkey saddr fwd = 705wrapOnion skey nonce destkey saddr fwd =
675 Forwarding (toPublic skey) $ encryptMessage skey destkey nonce (Addressed saddr fwd) 706 Forwarding (toPublic skey) $ encryptMessage skey destkey nonce (Addressed saddr fwd)
707
708
709-- TODO
710-- Two types of packets may be sent to Rendezvous via OnionToRoute requests.
711--
712-- (1) DHT public key packet (0x9c)
713--
714-- (2) Friend request
715data Rendezvous = Rendezvous
716 { rendezvousKey :: PublicKey
717 , rendezvousNode :: NodeInfo
718 }
719 deriving Eq
720
721instance Show Rendezvous where
722 show (Rendezvous k ni) = concat [show $ key2id k, ":", show ni]
723
724
725
726parseDataToRoute
727 :: TransportCrypto
728 -> (OnionMessage Encrypted,OnionDestination r)
729 -> Either (Assym (Encrypted DataToRoute),Rendezvous) (OnionMessage Encrypted, OnionDestination r)
730parseDataToRoute crypto (OnionToRouteResponse dta, od)
731 = Left ( dta
732 , Rendezvous (onionAliasPublic crypto) $ onionNodeInfo od )
733parseDataToRoute _ msg = Right msg
734
735encodeDataToRoute :: TransportCrypto
736 -> (Assym (Encrypted DataToRoute),Rendezvous)
737 -> Maybe (OnionMessage Encrypted,OnionDestination r)
738encodeDataToRoute crypto (dta, Rendezvous pub ni)
739 = Just ( OnionToRoute pub -- Public key of destination node
740 dta
741 , OnionDestination ni Nothing )
diff --git a/src/Network/Tox/Transport.hs b/src/Network/Tox/Transport.hs
index d4e1a754..d915561f 100644
--- a/src/Network/Tox/Transport.hs
+++ b/src/Network/Tox/Transport.hs
@@ -24,15 +24,18 @@ toxTransport ::
24 -> UDPTransport 24 -> UDPTransport
25 -> IO ( Transport String NodeInfo (DHTMessage Encrypted8) 25 -> IO ( Transport String NodeInfo (DHTMessage Encrypted8)
26 , Transport String (OnionDestination RouteId) (OnionMessage Encrypted) 26 , Transport String (OnionDestination RouteId) (OnionMessage Encrypted)
27 , Transport String Rendezvous (Assym (Encrypted DataToRoute))
27 , Transport String SockAddr NetCrypto ) 28 , Transport String SockAddr NetCrypto )
28toxTransport crypto orouter closeLookup udp = do 29toxTransport crypto orouter closeLookup udp = do
29 (dht,udp1) <- partitionTransport parseDHTAddr (Just . encodeDHTAddr) $ forwardOnions crypto udp 30 (dht,udp1) <- partitionTransport parseDHTAddr (Just . encodeDHTAddr) $ forwardOnions crypto udp
30 (onion,udp2) <- partitionTransportM (parseOnionAddr $ lookupSender orouter) 31 (onion1,udp2) <- partitionTransportM (parseOnionAddr $ lookupSender orouter)
31 (encodeOnionAddr $ lookupRoute orouter) 32 (encodeOnionAddr $ lookupRoute orouter)
32 udp1 33 udp1
34 (dta,onion) <- partitionTransport (parseDataToRoute crypto) (encodeDataToRoute crypto) onion1
33 let netcrypto = layerTransport parseNetCrypto encodeNetCrypto udp2 35 let netcrypto = layerTransport parseNetCrypto encodeNetCrypto udp2
34 return ( forwardDHTRequests crypto closeLookup dht 36 return ( forwardDHTRequests crypto closeLookup dht
35 , onion 37 , onion
38 , dta
36 , netcrypto 39 , netcrypto
37 ) 40 )
38 41