diff options
Diffstat (limited to 'src/Network/Tox/TCP.hs')
-rw-r--r-- | src/Network/Tox/TCP.hs | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/Network/Tox/TCP.hs b/src/Network/Tox/TCP.hs new file mode 100644 index 00000000..0780f121 --- /dev/null +++ b/src/Network/Tox/TCP.hs | |||
@@ -0,0 +1,85 @@ | |||
1 | {-# LANGUAGE RecursiveDo #-} | ||
2 | {-# LANGUAGE PartialTypeSignatures #-} | ||
3 | {-# LANGUAGE LambdaCase #-} | ||
4 | {-# LANGUAGE FlexibleContexts #-} | ||
5 | module Network.Tox.TCP where | ||
6 | |||
7 | import Control.Concurrent | ||
8 | import Control.Concurrent.STM | ||
9 | import Data.Functor.Identity | ||
10 | import Data.Serialize | ||
11 | import System.IO (Handle) | ||
12 | |||
13 | import Crypto.Tox | ||
14 | import Data.ByteString (hPut,hGet,ByteString) | ||
15 | import Data.Tox.Relay | ||
16 | import Network.Address (setPort,PortNumber,SockAddr) | ||
17 | import Network.QueryResponse | ||
18 | import Network.QueryResponse.TCP | ||
19 | import qualified Network.Tox.NodeId as UDP | ||
20 | |||
21 | |||
22 | withSize :: Sized x => (Size x -> m (p x)) -> m (p x) | ||
23 | withSize f = case size of len -> f len | ||
24 | |||
25 | data NodeInfo = NodeInfo | ||
26 | { udpNodeInfo :: UDP.NodeInfo | ||
27 | , tcpPort :: PortNumber | ||
28 | } | ||
29 | |||
30 | type NodeId = UDP.NodeId | ||
31 | |||
32 | nodeId :: NodeInfo -> NodeId | ||
33 | nodeId ni = UDP.nodeId $ udpNodeInfo ni | ||
34 | |||
35 | nodeAddr :: NodeInfo -> SockAddr | ||
36 | nodeAddr ni = setPort (tcpPort ni) $ UDP.nodeAddr $ udpNodeInfo ni | ||
37 | |||
38 | tcpStream :: (Serialize y, Sized y, Serialize x, Sized x) => | ||
39 | TransportCrypto -> StreamHandshake NodeInfo x y | ||
40 | tcpStream crypto = StreamHandshake | ||
41 | { streamHello = \addr h -> do | ||
42 | (skey, hello) <- atomically $ do | ||
43 | n24 <- transportNewNonce crypto | ||
44 | skey <- transportNewKey crypto | ||
45 | base24 <- transportNewNonce crypto | ||
46 | return $ (,) skey $ Hello $ Asymm | ||
47 | { senderKey = transportPublic crypto | ||
48 | , asymmNonce = n24 | ||
49 | , asymmData = pure HelloData | ||
50 | { sessionPublicKey = toPublic $ skey | ||
51 | , sessionBaseNonce = base24 | ||
52 | } | ||
53 | } | ||
54 | noncef <- lookupNonceFunction crypto (transportSecret crypto) (UDP.id2key $ nodeId addr) | ||
55 | hPut h $ encode $ encryptPayload (noncef $ helloNonce hello) hello | ||
56 | welcomeE <- withSize $ fmap decode . hGet h . constSize | ||
57 | let Right welcome = welcomeE >>= \w -> decryptPayload (noncef $ welcomeNonce w) w | ||
58 | noncef' <- lookupNonceFunction crypto skey (sessionPublicKey $ runIdentity $ welcomeData welcome) | ||
59 | nsend <- newMVar (sessionBaseNonce $ runIdentity $ helloData hello) | ||
60 | nread <- newMVar (sessionBaseNonce $ runIdentity $ welcomeData welcome) | ||
61 | let them = sessionPublicKey $ runIdentity $ welcomeData welcome | ||
62 | return SessionProtocol | ||
63 | { streamGoodbye = \h -> return () -- No goodbye packet? Seems rude. | ||
64 | , streamDecode = \h -> do | ||
65 | decode <$> hGet h 2 >>= \case | ||
66 | Left _ -> return Nothing | ||
67 | Right len -> do | ||
68 | decode <$> hGet h len >>= \case | ||
69 | Left _ -> return Nothing | ||
70 | Right x -> do | ||
71 | n24 <- takeMVar nread | ||
72 | let r = decrypt (noncef' n24) x >>= decodePlain | ||
73 | putMVar nread (incrementNonce24 n24) | ||
74 | return $ either (const Nothing) Just r | ||
75 | , streamEncode = \y -> do | ||
76 | n24 <- takeMVar nsend | ||
77 | let bs = encode $ encrypt (noncef' n24) $ encodePlain y | ||
78 | putMVar nsend (incrementNonce24 n24) | ||
79 | return bs -- XXX: Should we wait until this bytestring is sent before putting the nonce back in the MVar? | ||
80 | } | ||
81 | , streamAddr = nodeAddr | ||
82 | } | ||
83 | |||
84 | toxTCP :: TransportCrypto -> IO (Transport err NodeInfo RelayPacket) | ||
85 | toxTCP crypto = tcpTransport 30 (tcpStream crypto) | ||