diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Network/Tox.hs | 255 |
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 #-} | ||
15 | module Network.Tox where | ||
16 | |||
17 | import Debug.Trace | ||
18 | import Control.Exception hiding (Handler) | ||
19 | import Control.Applicative | ||
20 | import Control.Arrow | ||
21 | import Control.Concurrent (MVar) | ||
22 | import Control.Concurrent.STM | ||
23 | import Control.Monad | ||
24 | import qualified Crypto.Cipher.ChaChaPoly1305 as Symmetric | ||
25 | import qualified Crypto.Cipher.Salsa as Salsa | ||
26 | import qualified Crypto.Cipher.XSalsa as XSalsa | ||
27 | import Crypto.ECC.Class | ||
28 | import qualified Crypto.Error as Cryptonite | ||
29 | import Crypto.Error.Types | ||
30 | import qualified Crypto.MAC.Poly1305 as Poly1305 | ||
31 | import Crypto.PubKey.Curve25519 | ||
32 | import Crypto.PubKey.ECC.Types | ||
33 | import Crypto.Random | ||
34 | import qualified Data.Aeson as JSON | ||
35 | ;import Data.Aeson (FromJSON, ToJSON, (.=)) | ||
36 | import Data.Bitraversable (bisequence) | ||
37 | import Data.Bits | ||
38 | import Data.Bits.ByteString () | ||
39 | import Data.Bool | ||
40 | import qualified Data.ByteArray as BA | ||
41 | ;import Data.ByteArray (ByteArrayAccess, Bytes) | ||
42 | import qualified Data.ByteString as B | ||
43 | ;import Data.ByteString (ByteString) | ||
44 | import qualified Data.ByteString.Base16 as Base16 | ||
45 | import qualified Data.ByteString.Char8 as C8 | ||
46 | import Data.ByteString.Lazy (toStrict) | ||
47 | import Data.Char | ||
48 | import Data.Data | ||
49 | import Data.Hashable | ||
50 | import Data.IP | ||
51 | import Data.Maybe | ||
52 | import qualified Data.MinMaxPSQ as MinMaxPSQ | ||
53 | ;import Data.MinMaxPSQ (MinMaxPSQ') | ||
54 | import Data.Monoid | ||
55 | import Data.Ord | ||
56 | import qualified Data.Serialize as S | ||
57 | import Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime) | ||
58 | import Data.Typeable | ||
59 | import Data.Word | ||
60 | import qualified Data.Wrapper.PSQ as PSQ | ||
61 | ;import Data.Wrapper.PSQ (PSQ) | ||
62 | import qualified Data.Wrapper.PSQInt as Int | ||
63 | import Foreign.Marshal.Alloc | ||
64 | import Foreign.Ptr | ||
65 | import Foreign.Storable | ||
66 | import GHC.Generics (Generic) | ||
67 | import System.Global6 | ||
68 | import Network.Kademlia | ||
69 | import Network.Address (Address, WantIP (..), either4or6, | ||
70 | fromSockAddr, ipFamily, setPort, | ||
71 | sockAddrPort, testIdBit, | ||
72 | toSockAddr, un4map) | ||
73 | import Network.BitTorrent.DHT.Search (Search (..)) | ||
74 | import qualified Network.DHT.Routing as R | ||
75 | import Network.QueryResponse | ||
76 | import Network.Socket | ||
77 | import System.Endian | ||
78 | import System.IO | ||
79 | import qualified Text.ParserCombinators.ReadP as RP | ||
80 | import Text.Printf | ||
81 | import Text.Read | ||
82 | import Control.TriadCommittee | ||
83 | import Network.BitTorrent.DHT.Token as Token | ||
84 | import GHC.TypeLits | ||
85 | |||
86 | import Crypto.Tox hiding (Assym) | ||
87 | import Network.Tox.Transport | ||
88 | import Network.Tox.Address | ||
89 | import qualified Network.Tox.DHT.Transport as DHT | ||
90 | import qualified Network.Tox.DHT.Handlers as DHT | ||
91 | import qualified Network.Tox.Onion.Transport as Onion | ||
92 | import qualified Network.Tox.Onion.Handlers as Onion | ||
93 | import Network.Tox.Crypto.Transport (NetCrypto) | ||
94 | import Text.XXD | ||
95 | |||
96 | newCrypto :: IO TransportCrypto | ||
97 | newCrypto = 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 | |||
118 | updateIP :: TVar (R.BucketList NodeInfo) -> SockAddr -> STM () | ||
119 | updateIP 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 | |||
125 | genNonce24 :: DRG g => | ||
126 | TVar (g, pending) -> DHT.TransactionId -> IO DHT.TransactionId | ||
127 | genNonce24 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 | |||
134 | gen :: forall gen. DRG gen => gen -> (DHT.TransactionId, gen) | ||
135 | gen 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 | |||
140 | intKey :: DHT.TransactionId -> Int | ||
141 | intKey (DHT.TransactionId (Nonce8 w) _) = fromIntegral w | ||
142 | |||
143 | nonceKey :: DHT.TransactionId -> Nonce8 | ||
144 | nonceKey (DHT.TransactionId n _) = n | ||
145 | |||
146 | myAddr :: DHT.Routing -> Maybe NodeInfo -> IO NodeInfo | ||
147 | myAddr 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 | |||
154 | newClient :: (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) | ||
161 | newClient 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 | |||
192 | data 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 | |||
201 | addVerbosity :: Show addr => Transport err addr ByteString -> Transport err addr ByteString | ||
202 | addVerbosity 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 | |||
216 | newKeysDatabase :: IO (TVar Onion.AnnouncedKeys) | ||
217 | newKeysDatabase = | ||
218 | atomically $ newTVar $ Onion.AnnouncedKeys PSQ.empty MinMaxPSQ.empty | ||
219 | |||
220 | newTox :: TVar Onion.AnnouncedKeys -> SockAddr -> IO Tox | ||
221 | newTox 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 | |||
250 | forkTox :: Tox -> IO (IO ()) | ||
251 | forkTox tox = do | ||
252 | _ <- forkListener "toxCrypto" (toxCrypto tox) | ||
253 | _ <- forkListener "toxOnion" (clientNet $ toxOnion tox) | ||
254 | forkListener "toxDHT" (clientNet $ toxDHT tox) | ||
255 | |||