summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Network/Tox.hs255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/Network/Tox.hs b/src/Network/Tox.hs
new file mode 100644
index 00000000..c88dbcd4
--- /dev/null
+++ b/src/Network/Tox.hs
@@ -0,0 +1,255 @@
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveFoldable #-}
3{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveGeneric #-}
5{-# LANGUAGE DeriveTraversable #-}
6{-# LANGUAGE ExistentialQuantification #-}
7{-# LANGUAGE FlexibleInstances #-}
8{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9{-# LANGUAGE LambdaCase #-}
10{-# LANGUAGE NamedFieldPuns #-}
11{-# LANGUAGE PatternSynonyms #-}
12{-# LANGUAGE RankNTypes #-}
13{-# LANGUAGE ScopedTypeVariables #-}
14{-# LANGUAGE TupleSections #-}
15module Network.Tox where
16
17import Debug.Trace
18import Control.Exception hiding (Handler)
19import Control.Applicative
20import Control.Arrow
21import Control.Concurrent (MVar)
22import Control.Concurrent.STM
23import Control.Monad
24import qualified Crypto.Cipher.ChaChaPoly1305 as Symmetric
25import qualified Crypto.Cipher.Salsa as Salsa
26import qualified Crypto.Cipher.XSalsa as XSalsa
27import Crypto.ECC.Class
28import qualified Crypto.Error as Cryptonite
29import Crypto.Error.Types
30import qualified Crypto.MAC.Poly1305 as Poly1305
31import Crypto.PubKey.Curve25519
32import Crypto.PubKey.ECC.Types
33import Crypto.Random
34import qualified Data.Aeson as JSON
35 ;import Data.Aeson (FromJSON, ToJSON, (.=))
36import Data.Bitraversable (bisequence)
37import Data.Bits
38import Data.Bits.ByteString ()
39import Data.Bool
40import qualified Data.ByteArray as BA
41 ;import Data.ByteArray (ByteArrayAccess, Bytes)
42import qualified Data.ByteString as B
43 ;import Data.ByteString (ByteString)
44import qualified Data.ByteString.Base16 as Base16
45import qualified Data.ByteString.Char8 as C8
46import Data.ByteString.Lazy (toStrict)
47import Data.Char
48import Data.Data
49import Data.Hashable
50import Data.IP
51import Data.Maybe
52import qualified Data.MinMaxPSQ as MinMaxPSQ
53 ;import Data.MinMaxPSQ (MinMaxPSQ')
54import Data.Monoid
55import Data.Ord
56import qualified Data.Serialize as S
57import Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime)
58import Data.Typeable
59import Data.Word
60import qualified Data.Wrapper.PSQ as PSQ
61 ;import Data.Wrapper.PSQ (PSQ)
62import qualified Data.Wrapper.PSQInt as Int
63import Foreign.Marshal.Alloc
64import Foreign.Ptr
65import Foreign.Storable
66import GHC.Generics (Generic)
67import System.Global6
68import Network.Kademlia
69import Network.Address (Address, WantIP (..), either4or6,
70 fromSockAddr, ipFamily, setPort,
71 sockAddrPort, testIdBit,
72 toSockAddr, un4map)
73import Network.BitTorrent.DHT.Search (Search (..))
74import qualified Network.DHT.Routing as R
75import Network.QueryResponse
76import Network.Socket
77import System.Endian
78import System.IO
79import qualified Text.ParserCombinators.ReadP as RP
80import Text.Printf
81import Text.Read
82import Control.TriadCommittee
83import Network.BitTorrent.DHT.Token as Token
84import GHC.TypeLits
85
86import Crypto.Tox hiding (Assym)
87import Network.Tox.Transport
88import Network.Tox.Address
89import qualified Network.Tox.DHT.Transport as DHT
90import qualified Network.Tox.DHT.Handlers as DHT
91import qualified Network.Tox.Onion.Transport as Onion
92import qualified Network.Tox.Onion.Handlers as Onion
93import Network.Tox.Crypto.Transport (NetCrypto)
94import Text.XXD
95
96newCrypto :: IO TransportCrypto
97newCrypto = do
98 secret <- generateSecretKey
99 let pubkey = toPublic secret
100 (symkey, drg) <- do
101 drg0 <- getSystemDRG
102 return $ randomBytesGenerate 32 drg0 :: IO (ByteString, SystemDRG)
103 noncevar <- atomically $ newTVar $ fst $ withDRG drg drgNew
104 hPutStrLn stderr $ "secret(tox) = " ++ DHT.showHex secret
105 hPutStrLn stderr $ "public(tox) = " ++ DHT.showHex pubkey
106 hPutStrLn stderr $ "symmetric(tox) = " ++ DHT.showHex symkey
107 return TransportCrypto
108 { transportSecret = secret
109 , transportPublic = pubkey
110 , transportSymmetric = return $ SymmetricKey symkey
111 , transportNewNonce = do
112 drg1 <- readTVar noncevar
113 let (nonce, drg2) = withDRG drg1 (Nonce24 <$> getRandomBytes 24)
114 writeTVar noncevar drg2
115 return nonce
116 }
117
118updateIP :: TVar (R.BucketList NodeInfo) -> SockAddr -> STM ()
119updateIP tblvar a = do
120 bkts <- readTVar tblvar
121 case nodeInfo (nodeId (R.thisNode bkts)) a of
122 Right ni -> writeTVar tblvar (bkts { R.thisNode = ni })
123 Left _ -> return ()
124
125genNonce24 :: DRG g =>
126 TVar (g, pending) -> DHT.TransactionId -> IO DHT.TransactionId
127genNonce24 var (DHT.TransactionId nonce8 _) = atomically $ do
128 (g,pending) <- readTVar var
129 let (bs, g') = randomBytesGenerate 24 g
130 writeTVar var (g',pending)
131 return $ DHT.TransactionId nonce8 (Nonce24 bs)
132
133
134gen :: forall gen. DRG gen => gen -> (DHT.TransactionId, gen)
135gen g = let (bs, g') = randomBytesGenerate 24 g
136 (ws, g'') = randomBytesGenerate 8 g'
137 Right w = S.runGet S.getWord64be ws
138 in ( DHT.TransactionId (Nonce8 w) (Nonce24 bs), g'' )
139
140intKey :: DHT.TransactionId -> Int
141intKey (DHT.TransactionId (Nonce8 w) _) = fromIntegral w
142
143nonceKey :: DHT.TransactionId -> Nonce8
144nonceKey (DHT.TransactionId n _) = n
145
146myAddr :: DHT.Routing -> Maybe NodeInfo -> IO NodeInfo
147myAddr routing maddr = atomically $ do
148 let var = case flip DHT.prefer4or6 Nothing <$> maddr of
149 Just Want_IP6 -> DHT.routing6 routing
150 _ -> DHT.routing4 routing
151 a <- readTVar var
152 return $ R.thisNode a
153
154newClient :: (DRG g, Show addr, Show meth) =>
155 g -> Transport String addr x
156 -> (x -> MessageClass String meth DHT.TransactionId)
157 -> (Maybe addr -> IO addr)
158 -> (meth -> Maybe (MethodHandler String DHT.TransactionId addr x))
159 -> (Client String meth DHT.TransactionId addr x -> Transport String addr x -> Transport String addr x)
160 -> IO (Client String meth DHT.TransactionId addr x)
161newClient drg net classify selfAddr handlers modifynet = do
162 -- If we have 8-byte keys for IntMap, then use it for transaction lookups.
163 -- Otherwise, use ordinary Map. The details of which will be hidden by an
164 -- existential closure (see mkclient below).
165 tblvar <-
166 if fitsInInt (Proxy :: Proxy Word64)
167 then do
168 let intmapT = transactionMethods (contramapT intKey intMapMethods) gen
169 intmap_var <- atomically $ newTVar (drg, mempty)
170 return $ Right (intmapT,intmap_var)
171 else do
172 let mapT = transactionMethods (contramapT nonceKey mapMethods) gen
173 map_var <- atomically $ newTVar (drg, mempty)
174 return $ Left (mapT,map_var)
175 let dispatch tbl var handlers = DispatchMethods
176 { classifyInbound = classify
177 , lookupHandler = handlers -- var
178 , tableMethods = tbl
179 }
180 mkclient (tbl,var) handlers =
181 let client = Client
182 { clientNet = addHandler (handleMessage client) $ modifynet client net
183 , clientDispatcher = dispatch tbl var handlers -- (fmap (contramapAddr (\(ToxPath ni _) -> ni)) . handlers)
184 , clientErrorReporter = (printErrors stderr) { reportTimeout = reportTimeout ignoreErrors }
185 , clientPending = var
186 , clientAddress = selfAddr
187 , clientResponseId = genNonce24 var
188 }
189 in client
190 return $ either mkclient mkclient tblvar handlers
191
192data Tox = Tox
193 { toxDHT :: DHT.Client
194 , toxOnion :: Onion.Client
195 , toxCrypto :: Transport String SockAddr NetCrypto
196 , toxRouting :: DHT.Routing
197 , toxTokens :: TVar SessionTokens
198 , toxAnnouncedKeys :: TVar Onion.AnnouncedKeys
199 }
200
201addVerbosity :: Show addr => Transport err addr ByteString -> Transport err addr ByteString
202addVerbosity tr =
203 tr { awaitMessage = \kont -> awaitMessage tr $ \m -> do
204 forM_ m $ mapM_ $ \(msg,addr) -> do
205 when (not (B.null msg || elem (B.head msg) [0,1,2,4])) $ do
206 mapM_ (\x -> hPutStrLn stderr ( (show addr) ++ " --> " ++ x))
207 $ xxd 0 msg
208 kont m
209 , sendMessage = \addr msg -> do
210 when (not (B.null msg || elem (B.head msg) [0,1,2,4])) $ do
211 mapM_ (\x -> hPutStrLn stderr ( (show addr) ++ " <-- " ++ x))
212 $ xxd 0 msg
213 sendMessage tr addr msg
214 }
215
216newKeysDatabase :: IO (TVar Onion.AnnouncedKeys)
217newKeysDatabase =
218 atomically $ newTVar $ Onion.AnnouncedKeys PSQ.empty MinMaxPSQ.empty
219
220newTox :: TVar Onion.AnnouncedKeys -> SockAddr -> IO Tox
221newTox keydb addr = do
222 udp <- addVerbosity <$> udpTransport addr
223 crypto <- newCrypto
224 drg <- drgNew
225 let lookupClose _ = return Nothing
226 (dhtcrypt,onioncrypt,cryptonet) <- toxTransport crypto lookupClose udp
227
228 routing <- DHT.newRouting addr crypto updateIP updateIP
229 let dhtnet0 = layerTransport (DHT.decrypt crypto) (DHT.encrypt crypto) dhtcrypt
230 dhtclient <- newClient drg dhtnet0 DHT.classify (myAddr routing) (DHT.handlers routing)
231 $ \client net -> onInbound (DHT.updateRouting client routing) net
232
233 toks <- do
234 nil <- nullSessionTokens
235 atomically $ newTVar nil { maxInterval = 20 } -- 20 second timeout on announce ping-ids.
236 oniondrg <- drgNew
237 let onionnet = layerTransport (Onion.decrypt crypto) (Onion.encrypt crypto) onioncrypt
238 onionclient <- newClient oniondrg onionnet Onion.classify (const $ return $ Onion.OnionToMe addr)
239 (Onion.handlers onionnet routing toks keydb)
240 (const id)
241 return Tox
242 { toxDHT = dhtclient
243 , toxOnion = onionclient
244 , toxCrypto = cryptonet
245 , toxRouting = routing
246 , toxTokens = toks
247 , toxAnnouncedKeys = keydb
248 }
249
250forkTox :: Tox -> IO (IO ())
251forkTox tox = do
252 _ <- forkListener "toxCrypto" (toxCrypto tox)
253 _ <- forkListener "toxOnion" (clientNet $ toxOnion tox)
254 forkListener "toxDHT" (clientNet $ toxDHT tox)
255