diff options
author | Joe Crayne <joe@jerkface.net> | 2018-09-08 04:31:51 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2018-09-08 06:05:10 -0400 |
commit | ad20be57786ad34f80192206c480d575392b4ebb (patch) | |
tree | 722333a152705572f7437586772a9173e84a92e2 | |
parent | e5add92a477060d9bba10de7b980c89c24012691 (diff) |
ToxManager rewrite: use aggregated netcrypto sessions.
-rw-r--r-- | ToxManager.hs | 209 | ||||
-rw-r--r-- | dht-client.cabal | 2 | ||||
-rw-r--r-- | examples/dhtd.hs | 83 | ||||
-rw-r--r-- | src/Network/Tox.hs | 11 |
4 files changed, 240 insertions, 65 deletions
diff --git a/ToxManager.hs b/ToxManager.hs index cfdb4f50..78049010 100644 --- a/ToxManager.hs +++ b/ToxManager.hs | |||
@@ -9,10 +9,10 @@ module ToxManager where | |||
9 | import Announcer | 9 | import Announcer |
10 | import Announcer.Tox | 10 | import Announcer.Tox |
11 | import ClientState | 11 | import ClientState |
12 | import Connection | ||
13 | import Control.Concurrent.STM | 12 | import Control.Concurrent.STM |
14 | import Control.Monad | 13 | import Control.Monad |
15 | import Crypto.Tox | 14 | import Crypto.Tox |
15 | import qualified Data.ByteArray as BA | ||
16 | import Data.Bits | 16 | import Data.Bits |
17 | import Data.Function | 17 | import Data.Function |
18 | import qualified Data.HashMap.Strict as HashMap | 18 | import qualified Data.HashMap.Strict as HashMap |
@@ -24,6 +24,8 @@ import qualified Data.Text as T | |||
24 | import Data.Time.Clock.POSIX | 24 | import Data.Time.Clock.POSIX |
25 | import Data.Word | 25 | import Data.Word |
26 | import DPut | 26 | import DPut |
27 | import Foreign.Storable | ||
28 | import HandshakeCache | ||
27 | import Network.Address | 29 | import Network.Address |
28 | import qualified Network.Kademlia.Routing as R | 30 | import qualified Network.Kademlia.Routing as R |
29 | ;import Network.Kademlia.Routing as R | 31 | ;import Network.Kademlia.Routing as R |
@@ -31,9 +33,9 @@ import Network.Kademlia.Search | |||
31 | import Network.QueryResponse | 33 | import Network.QueryResponse |
32 | import qualified Network.Tox as Tox | 34 | import qualified Network.Tox as Tox |
33 | ;import Network.Tox | 35 | ;import Network.Tox |
36 | import Network.Tox.AggregateSession | ||
34 | import Network.Tox.ContactInfo as Tox | 37 | import Network.Tox.ContactInfo as Tox |
35 | import qualified Network.Tox.Crypto.Handlers as Tox | 38 | import qualified Network.Tox.Crypto.Handlers as Tox |
36 | ;import Network.Tox.Crypto.Handlers (UponCookie (..)) | ||
37 | import Network.Tox.DHT.Handlers (nodeSearch, nodesOfInterest) | 39 | import Network.Tox.DHT.Handlers (nodeSearch, nodesOfInterest) |
38 | import Network.Tox.DHT.Handlers | 40 | import Network.Tox.DHT.Handlers |
39 | import qualified Network.Tox.DHT.Transport as Tox | 41 | import qualified Network.Tox.DHT.Transport as Tox |
@@ -53,7 +55,8 @@ import Control.Concurrent.Lifted.Instrument | |||
53 | import Control.Concurrent.Lifted | 55 | import Control.Concurrent.Lifted |
54 | import GHC.Conc (labelThread) | 56 | import GHC.Conc (labelThread) |
55 | #endif | 57 | #endif |
56 | 58 | import GHC.Conc (unsafeIOToSTM) | |
59 | import Connection | ||
57 | 60 | ||
58 | 61 | ||
59 | toxAnnounceSendData :: Tox.Tox JabberClients | 62 | toxAnnounceSendData :: Tox.Tox JabberClients |
@@ -84,12 +87,13 @@ stringToKey_ s = let (xs,ys) = break (==':') s | |||
84 | -- | 87 | -- |
85 | -- These hooks will be invoked in order to connect to *.tox hosts in a user's | 88 | -- These hooks will be invoked in order to connect to *.tox hosts in a user's |
86 | -- XMPP roster. | 89 | -- XMPP roster. |
87 | toxman :: Announcer | 90 | toxman :: TVar (Map.Map Uniq24 AggregateSession) |
91 | -> Announcer | ||
88 | -> [(String,TVar (BucketList Tox.NodeInfo))] | 92 | -> [(String,TVar (BucketList Tox.NodeInfo))] |
89 | -> Tox.Tox JabberClients | 93 | -> Tox.Tox JabberClients |
90 | -> PresenceState | 94 | -> PresenceState |
91 | -> ToxManager ClientAddress | 95 | -> ToxManager ClientAddress |
92 | toxman announcer toxbkts tox presence = ToxManager | 96 | toxman ssvar announcer toxbkts tox presence = ToxManager |
93 | { activateAccount = \k pubname seckey -> do | 97 | { activateAccount = \k pubname seckey -> do |
94 | dput XMan $ "toxman ACTIVATE " ++ show (Tox.key2id $ toPublic seckey) | 98 | dput XMan $ "toxman ACTIVATE " ++ show (Tox.key2id $ toPublic seckey) |
95 | let ContactInfo{ accounts } = Tox.toxContactInfo tox | 99 | let ContactInfo{ accounts } = Tox.toxContactInfo tox |
@@ -123,7 +127,7 @@ toxman announcer toxbkts tox presence = ToxManager | |||
123 | toxAnnounceInterval) | 127 | toxAnnounceInterval) |
124 | pub | 128 | pub |
125 | 129 | ||
126 | forkAccountWatcher acnt tox presence announcer | 130 | forkAccountWatcher ssvar acnt tox presence announcer |
127 | return () | 131 | return () |
128 | 132 | ||
129 | , deactivateAccount = \k pubname -> do | 133 | , deactivateAccount = \k pubname -> do |
@@ -168,23 +172,39 @@ toxman announcer toxbkts tox presence = ToxManager | |||
168 | Just acc -> setContactPolicy (Tox.id2key themid) TryingToConnect acc | 172 | Just acc -> setContactPolicy (Tox.id2key themid) TryingToConnect acc |
169 | -- If unscheduled and unconnected, schedule recurring search for this contact. | 173 | -- If unscheduled and unconnected, schedule recurring search for this contact. |
170 | _ -> return () -- Remove contact. | 174 | _ -> return () -- Remove contact. |
171 | , connections = _todo | 175 | , connections = do |
176 | let ContactInfo{ accounts } = Tox.toxContactInfo tox | ||
177 | as <- HashMap.toList <$> readTVar accounts | ||
178 | fmap concat $ forM as $ \(me,a) -> do | ||
179 | ks <- HashMap.keys <$> readTVar (contacts a) | ||
180 | return $ map (ToxContact me) ks | ||
181 | , status = \(ToxContact me them) -> do | ||
182 | ma <- HashMap.lookup me <$> readTVar (accounts $ Tox.toxContactInfo tox) | ||
183 | fmap (fromMaybe (Connection Dormant RefusingToConnect)) $ forM ma $ \a -> do | ||
184 | mc <- getContact (id2key them) a | ||
185 | let mek = id2key me | ||
186 | themk = id2key them | ||
187 | u <- xor24 <$> unsafeIOToSTM (hash24 mek) <*> unsafeIOToSTM (hash24 themk) | ||
188 | ag <- do ag <- Map.lookup u <$> readTVar ssvar | ||
189 | maybe (return Nothing) | ||
190 | (\c -> checkCompatible mek themk c >>= \case | ||
191 | Just False -> return Nothing | ||
192 | _ -> return ag) | ||
193 | ag | ||
194 | s <- getStatus mek themk ag mc (toxHandshakeCache tox) | ||
195 | mp <- join <$> mapM (readTVar . contactPolicy) mc | ||
196 | return $ Connection s (fromMaybe RefusingToConnect mp) | ||
172 | , stringToKey = stringToKey_ | 197 | , stringToKey = stringToKey_ |
173 | , showProgress = show | 198 | , showProgress = show |
174 | , showKey = show | 199 | , showKey = show |
175 | } | 200 | } |
176 | , resolveToxPeer = \me them -> do | 201 | , resolveToxPeer = \me them -> do |
177 | let lookupContact accs | 202 | let m = do meid <- readMaybe $ T.unpack me |
178 | = do meid <- readMaybe $ T.unpack me | 203 | themid <- readMaybe $ T.unpack them |
179 | themid <- readMaybe $ T.unpack them | 204 | return (id2key meid, id2key themid) |
180 | acc <- HashMap.lookup meid accs | 205 | forM m $ \(me,them) -> do |
181 | return $ HashMap.lookup themid <$> readTVar (contacts acc) | 206 | u <- xor24 <$> hash24 me <*> hash24 them |
182 | atomically $ do | 207 | return $ addrToPeerKey $ Remote $ uniqueAsKey u |
183 | accs <- let ContactInfo{ accounts } = Tox.toxContactInfo tox | ||
184 | in readTVar accounts | ||
185 | mc <- join <$> sequence (lookupContact accs) | ||
186 | maddr <- join <$> mapM (readTVar . contactLastSeenAddr) mc | ||
187 | return $ addrToPeerKey . Remote . Tox.nodeAddr . snd <$> maddr | ||
188 | } | 208 | } |
189 | 209 | ||
190 | key2jid :: Word32 -> PublicKey -> Text | 210 | key2jid :: Word32 -> PublicKey -> Text |
@@ -209,10 +229,11 @@ initPerClient = do | |||
209 | } | 229 | } |
210 | 230 | ||
211 | data ToxToXMPP = ToxToXMPP | 231 | data ToxToXMPP = ToxToXMPP |
212 | { txAnnouncer :: Announcer | 232 | { txAnnouncer :: Announcer |
213 | , txAccount :: Account JabberClients | 233 | , txAccount :: Account JabberClients |
214 | , txPresence :: PresenceState | 234 | , txPresence :: PresenceState |
215 | , txTox :: Tox JabberClients | 235 | , txTox :: Tox JabberClients |
236 | , txSessions :: TVar (Map.Map Uniq24 AggregateSession) | ||
216 | } | 237 | } |
217 | 238 | ||
218 | default_nospam :: Word32 | 239 | default_nospam :: Word32 |
@@ -316,26 +337,40 @@ gotAddr' ni@(nodeAddr -> addr) tx theirKey theirDhtKey = atomically blee | |||
316 | tox :: Tox JabberClients | 337 | tox :: Tox JabberClients |
317 | tox = txTox tx | 338 | tox = txTox tx |
318 | 339 | ||
340 | crypto = toxCryptoKeys tox | ||
341 | |||
342 | {- | ||
319 | byAddr :: TVar (Map.Map SockAddr Tox.NetCryptoSession) | 343 | byAddr :: TVar (Map.Map SockAddr Tox.NetCryptoSession) |
320 | byAddr = Tox.netCryptoSessions (toxCryptoSessions tox) | 344 | byAddr = Tox.netCryptoSessions (toxCryptoSessions tox) |
321 | 345 | ||
322 | crypto = Tox.transportCrypto $ toxCryptoSessions tox | ||
323 | |||
324 | readNcVar :: (Tox.NetCryptoSession -> TVar b) -> SockAddr -> STM (Maybe b) | 346 | readNcVar :: (Tox.NetCryptoSession -> TVar b) -> SockAddr -> STM (Maybe b) |
325 | readNcVar v addr = traverse readTVar =<< fmap v . Map.lookup addr <$> readTVar byAddr | 347 | readNcVar v addr = traverse readTVar =<< fmap v . Map.lookup addr <$> readTVar byAddr |
326 | 348 | -} | |
327 | chillSesh :: SockAddr -> STM (Maybe (Status Tox.ToxProgress)) | ||
328 | chillSesh = readNcVar Tox.ncState | ||
329 | 349 | ||
330 | activeSesh :: SockAddr -> STM Bool | 350 | activeSesh :: SockAddr -> STM Bool |
331 | activeSesh a = chillSesh a >>= return . \case | 351 | activeSesh a = do |
332 | Just Established -> True | 352 | ss <- readTVar (txSessions tx) |
333 | _ -> False | 353 | u <- xor24 <$> unsafeIOToSTM (hash24 myPublicKey) |
334 | 354 | <*> unsafeIOToSTM (hash24 theirKey) | |
355 | case Map.lookup u ss of | ||
356 | Nothing -> return False | ||
357 | -- TODO: Currently we consider the session active if it is actually established. | ||
358 | -- Perhaps it would be better to also consider it "active" when an incompatible | ||
359 | -- session is holding the Uniq24 slot in txSessions because the connection will | ||
360 | -- ultimately fail anyway in that case. Alternatively, we could drop the Uniq24 | ||
361 | -- map and use a full (PublicKey,PublicKey) key, but this would require changing | ||
362 | -- how XMPP connections are handled since they are currently distinguished by a | ||
363 | -- SockAddr which cannot hold more than a 24-byte key. (See XMPPServer.peerKey). | ||
364 | Just c -> checkCompatible myPublicKey theirKey c >>= \case | ||
365 | Just False -> return False | ||
366 | _ -> (== Established) <$> aggregateStatus c | ||
367 | |||
368 | {- | ||
335 | readCookie :: SockAddr -> STM (Maybe (UponCookie (Tox.Cookie Encrypted))) | 369 | readCookie :: SockAddr -> STM (Maybe (UponCookie (Tox.Cookie Encrypted))) |
336 | readCookie = readNcVar Tox.ncCookie | 370 | readCookie = readNcVar Tox.ncCookie |
337 | readCookie' :: SockAddr -> STM (Maybe (Tox.Cookie Encrypted)) | 371 | readCookie' :: SockAddr -> STM (Maybe (Tox.Cookie Encrypted)) |
338 | readCookie' = fmap join . (fmap.fmap) Tox.toMaybe . readCookie | 372 | readCookie' = fmap join . (fmap.fmap) Tox.toMaybe . readCookie |
373 | -} | ||
339 | 374 | ||
340 | client :: Network.Tox.DHT.Handlers.Client | 375 | client :: Network.Tox.DHT.Handlers.Client |
341 | client = toxDHT tox | 376 | client = toxDHT tox |
@@ -359,7 +394,15 @@ gotAddr' ni@(nodeAddr -> addr) tx theirKey theirDhtKey = atomically blee | |||
359 | active <- isActive | 394 | active <- isActive |
360 | return $ when (not active) getCookieIO | 395 | return $ when (not active) getCookieIO |
361 | 396 | ||
362 | callRealShakeHands = realShakeHands (userSecret (txAccount tx)) theirKey (dhtpk theirDhtKey) (toxCryptoSessions tox) (nodeAddr ni) | 397 | callRealShakeHands cookie = do |
398 | {- | ||
399 | forM_ (nodeInfo (key2id $ dhtpk theirDhtKey) (nodeAddr ni)) $ \ni' -> do | ||
400 | hs <- cacheHandshake (toxHandshakeCache tox) (userSecret (txAccount tx)) theirKey ni' cookie | ||
401 | dput XNetCrypto $ show addr ++ "<-- handshake " ++ show (key2id theirKey) | ||
402 | sendMessage (toxHandshakes tox) (nodeAddr ni) hs | ||
403 | -} | ||
404 | realShakeHands (userSecret (txAccount tx)) theirKey (dhtpk theirDhtKey) (toxCryptoSessions tox) (nodeAddr ni) cookie | ||
405 | |||
363 | 406 | ||
364 | reschedule n f = scheduleRel ann akey f n | 407 | reschedule n f = scheduleRel ann akey f n |
365 | reschedule' n f = reschedule n (ScheduledItem $ \_ _ now -> f now) | 408 | reschedule' n f = reschedule n (ScheduledItem $ \_ _ now -> f now) |
@@ -368,10 +411,16 @@ gotAddr' ni@(nodeAddr -> addr) tx theirKey theirDhtKey = atomically blee | |||
368 | 411 | ||
369 | getCookieIO :: IO () | 412 | getCookieIO :: IO () |
370 | getCookieIO = do | 413 | getCookieIO = do |
371 | dput XUnused "getCookieIO - entered" | 414 | dput XNetCrypto $ show addr ++ " <-- request cookie" |
415 | let pending flag = setPendingCookie (toxHandshakeCache tox) myPublicKey theirKey flag | ||
416 | atomically $ pending True | ||
372 | cookieRequest crypto client myPublicKey ni >>= \case | 417 | cookieRequest crypto client myPublicKey ni >>= \case |
373 | Nothing -> atomically $ reschedule' 5 (const getCookieAgain) | 418 | Nothing -> atomically $ do |
419 | pending False | ||
420 | reschedule' 5 (const getCookieAgain) | ||
374 | Just cookie -> do | 421 | Just cookie -> do |
422 | dput XNetCrypto $ show addr ++ "--> cookie" | ||
423 | atomically $ pending False | ||
375 | void $ callRealShakeHands cookie | 424 | void $ callRealShakeHands cookie |
376 | cookieCreationStamp <- getPOSIXTime | 425 | cookieCreationStamp <- getPOSIXTime |
377 | let shaker :: POSIXTime -> STM (IO ()) | 426 | let shaker :: POSIXTime -> STM (IO ()) |
@@ -388,6 +437,7 @@ gotAddr' ni@(nodeAddr -> addr) tx theirKey theirDhtKey = atomically blee | |||
388 | return . void $ callRealShakeHands cookie | 437 | return . void $ callRealShakeHands cookie |
389 | atomically $ reschedule' 5 shaker | 438 | atomically $ reschedule' 5 shaker |
390 | 439 | ||
440 | |||
391 | realShakeHands :: SecretKey -> PublicKey -> PublicKey -> Tox.NetCryptoSessions -> SockAddr -> Tox.Cookie Encrypted -> IO Bool | 441 | realShakeHands :: SecretKey -> PublicKey -> PublicKey -> Tox.NetCryptoSessions -> SockAddr -> Tox.Cookie Encrypted -> IO Bool |
392 | realShakeHands myseckey theirpubkey theirDhtKey allsessions saddr cookie = do | 442 | realShakeHands myseckey theirpubkey theirDhtKey allsessions saddr cookie = do |
393 | dput XUnused "realShakeHands" | 443 | dput XUnused "realShakeHands" |
@@ -409,6 +459,8 @@ realShakeHands myseckey theirpubkey theirDhtKey allsessions saddr cookie = do | |||
409 | -- send handshake | 459 | -- send handshake |
410 | isJust <$> forM myhandshake (Tox.sendHandshake allsessions saddr) | 460 | isJust <$> forM myhandshake (Tox.sendHandshake allsessions saddr) |
411 | 461 | ||
462 | |||
463 | |||
412 | dispatch :: ToxToXMPP -> ContactEvent -> IO () | 464 | dispatch :: ToxToXMPP -> ContactEvent -> IO () |
413 | dispatch tx (SessionEstablished theirKey) = stopConnecting tx theirKey "established" | 465 | dispatch tx (SessionEstablished theirKey) = stopConnecting tx theirKey "established" |
414 | dispatch tx (SessionTerminated theirKey) = startConnecting tx theirKey "terminated" | 466 | dispatch tx (SessionTerminated theirKey) = startConnecting tx theirKey "terminated" |
@@ -524,24 +576,20 @@ startConnecting0 tx them contact reason = do | |||
524 | dkey <- Tox.getContactInfo tox | 576 | dkey <- Tox.getContactInfo tox |
525 | let tr = Tox.toxToRoute tox | 577 | let tr = Tox.toxToRoute tox |
526 | route = Tox.AnnouncedRendezvous theirkey rendezvous | 578 | route = Tox.AnnouncedRendezvous theirkey rendezvous |
527 | dput XMan $ unwords [ take 8 (show $ key2id mypub) ++ ":" | 579 | dput XMan $ unwords [ take 8 (show $ key2id theirkey) |
528 | , "Sending my DHT-key" | 580 | , show (nodeAddr $ Tox.rendezvousNode rendezvous) |
529 | , show (key2id $ Tox.dhtpk dkey) | 581 | , "<--" |
530 | , "to" | 582 | , "DHTKey" |
531 | , show (key2id theirkey) | 583 | , take 8 (show $ key2id mypub) ++ "/" |
532 | , "via" | 584 | ++ take 8 (show $ key2id $ Tox.dhtpk dkey) |
533 | , show (Tox.rendezvousNode rendezvous) | ||
534 | ] | 585 | ] |
535 | sendMessage tr route (mypub,Tox.OnionDHTPublicKey dkey) | 586 | sendMessage tr route (mypub,Tox.OnionDHTPublicKey dkey) |
536 | forM_ soliciting $ \cksum@(NoSpam nospam _)-> do | 587 | forM_ soliciting $ \cksum@(NoSpam nospam _)-> do |
537 | dput XMan $ unwords [ take 8 (show $ key2id mypub) ++ ":" | 588 | dput XMan $ unwords [ take 8 (show $ key2id theirkey) |
538 | , "Sending friend-request" | 589 | , show (nodeAddr $ Tox.rendezvousNode rendezvous) |
539 | , "with nospam" | 590 | , "<-- FriendRequest" |
540 | , "(" ++ nospam64 cksum ++ "," ++nospam16 cksum ++ ")" | 591 | , take 8 (show $ key2id mypub) |
541 | , "to" | 592 | , "nospam=" ++ "(" ++ nospam64 cksum ++ "," ++nospam16 cksum ++ ")" |
542 | , show (key2id theirkey) | ||
543 | , "via" | ||
544 | , show (Tox.rendezvousNode rendezvous) | ||
545 | ] | 593 | ] |
546 | let fr = FriendRequest | 594 | let fr = FriendRequest |
547 | { friendNoSpam = nospam | 595 | { friendNoSpam = nospam |
@@ -559,7 +607,7 @@ startConnecting tx them reason = do | |||
559 | 607 | ||
560 | stopConnecting :: ToxToXMPP -> PublicKey -> String -> IO () | 608 | stopConnecting :: ToxToXMPP -> PublicKey -> String -> IO () |
561 | stopConnecting ToxToXMPP{txAnnouncer=announcer,txAccount=acnt} them reason = do | 609 | stopConnecting ToxToXMPP{txAnnouncer=announcer,txAccount=acnt} them reason = do |
562 | dput XMan $ "STOP CONNECTING " ++ show (key2id them) ++ "("++reason++")" | 610 | dput XMan $ "STOP("++reason++") CONNECTING " ++ show (key2id them) |
563 | let pub = toPublic $ userSecret acnt | 611 | let pub = toPublic $ userSecret acnt |
564 | me = key2id pub | 612 | me = key2id pub |
565 | akeyC = akeyConnect announcer me them | 613 | akeyC = akeyConnect announcer me them |
@@ -567,18 +615,20 @@ stopConnecting ToxToXMPP{txAnnouncer=announcer,txAccount=acnt} them reason = do | |||
567 | cancel announcer akeyC | 615 | cancel announcer akeyC |
568 | cancel announcer akeyD | 616 | cancel announcer akeyD |
569 | 617 | ||
570 | forkAccountWatcher :: Account JabberClients -> Tox JabberClients -> PresenceState -> Announcer -> IO ThreadId | 618 | forkAccountWatcher :: TVar (Map.Map Uniq24 AggregateSession) |
571 | forkAccountWatcher acc tox st announcer = forkIO $ do | 619 | -> Account JabberClients -> Tox JabberClients -> PresenceState -> Announcer -> IO ThreadId |
572 | myThreadId >>= flip labelThread ("tox-xmpp:" | 620 | forkAccountWatcher ssvar acc tox st announcer = forkIO $ do |
621 | myThreadId >>= flip labelThread ("online:" | ||
573 | ++ show (key2id $ toPublic $ userSecret acc)) | 622 | ++ show (key2id $ toPublic $ userSecret acc)) |
574 | (chan,cs) <- atomically $ do | 623 | (chan,cs) <- atomically $ do |
575 | chan <- dupTChan $ eventChan acc -- duplicate broadcast channel for reading. | 624 | chan <- dupTChan $ eventChan acc -- duplicate broadcast channel for reading. |
576 | contacts <- readTVar (contacts acc) | 625 | contacts <- readTVar (contacts acc) |
577 | return (chan,contacts) | 626 | return (chan,contacts) |
578 | let tx = ToxToXMPP { txAnnouncer = announcer | 627 | let tx = ToxToXMPP { txAnnouncer = announcer |
579 | , txAccount = acc | 628 | , txAccount = acc |
580 | , txPresence = st | 629 | , txPresence = st |
581 | , txTox = tox | 630 | , txTox = tox |
631 | , txSessions = ssvar | ||
582 | } | 632 | } |
583 | forM_ (HashMap.toList cs) $ \(them,c) -> do | 633 | forM_ (HashMap.toList cs) $ \(them,c) -> do |
584 | startConnecting0 tx (id2key them) c "enabled account" | 634 | startConnecting0 tx (id2key them) c "enabled account" |
@@ -597,13 +647,48 @@ forkAccountWatcher acc tox st announcer = forkIO $ do | |||
597 | cs <- atomically $ readTVar (contacts acc) | 647 | cs <- atomically $ readTVar (contacts acc) |
598 | forM_ (HashMap.toList cs) $ \(them,c) -> do | 648 | forM_ (HashMap.toList cs) $ \(them,c) -> do |
599 | stopConnecting tx (id2key them) "disabled account" | 649 | stopConnecting tx (id2key them) "disabled account" |
650 | -- TODO: closeAll for each relevant session in ssvar. | ||
600 | 651 | ||
601 | 652 | ||
602 | toxQSearch :: Tox extra -> Search Tox.NodeId (IP, PortNumber) Nonce32 Tox.NodeInfo Tox.Rendezvous | ||
603 | toxQSearch tox = Tox.toxidSearch (Tox.onionTimeout tox) (Tox.toxCryptoKeys tox) (Tox.toxOnion tox) | ||
604 | |||
605 | toxAnnounceInterval :: POSIXTime | 653 | toxAnnounceInterval :: POSIXTime |
606 | toxAnnounceInterval = 15 | 654 | toxAnnounceInterval = 15 |
607 | 655 | ||
608 | 656 | getStatus :: PublicKey -> PublicKey -> Maybe AggregateSession -> Maybe Contact -> HandshakeCache -> STM (Status ToxProgress) | |
609 | 657 | getStatus me them a c hs = do | |
658 | astat <- maybe (return Dormant) aggregateStatus a | ||
659 | policy <- fromMaybe RefusingToConnect <$> maybe (return Nothing) (readTVar . contactPolicy) c | ||
660 | mdht <- maybe (return Nothing) (readTVar . contactKeyPacket) c | ||
661 | maddr <- maybe (return Nothing) (readTVar . contactLastSeenAddr) c | ||
662 | haveCookie <- haveCachedCookie hs me them | ||
663 | return $ statusLogic astat policy mdht maddr haveCookie | ||
664 | |||
665 | statusLogic :: Status ToxProgress -> Policy -> Maybe dhtkey -> Maybe addr -> Bool -> Status ToxProgress | ||
666 | statusLogic astat policy mdht maddr haveCookie = case () of | ||
667 | () | Established <- astat -> Established | ||
668 | | InProgress AwaitingSessionPacket <- astat -> InProgress AwaitingSessionPacket | ||
669 | | RefusingToConnect <- policy -> Dormant | ||
670 | | Nothing <- mdht -> InProgress AwaitingDHTKey | ||
671 | | Nothing <- maddr -> InProgress AcquiringIPAddress | ||
672 | | not haveCookie -> InProgress AcquiringCookie | ||
673 | | otherwise -> InProgress AwaitingHandshake | ||
674 | |||
675 | |||
676 | hash24 :: BA.ByteArrayAccess ba => ba -> IO Uniq24 | ||
677 | hash24 them | let r = 32 - BA.length them, (r > 0) | ||
678 | = hash24 $ BA.append (BA.convert them :: BA.Bytes) | ||
679 | (BA.replicate r 0) -- XXX: It'd be better to insert ahead of last 8 bytes. | ||
680 | hash24 them = BA.withByteArray them $ \p -> do | ||
681 | x <- peek p | ||
682 | y <- peekElemOff p 1 | ||
683 | -- skipping word64 2 | ||
684 | z <- peekElemOff p 3 | ||
685 | return $! Uniq24 x y z | ||
686 | |||
687 | xor24 :: Uniq24 -> Uniq24 -> Uniq24 | ||
688 | xor24 (Uniq24 xa xb xc) (Uniq24 ya yb yc) = | ||
689 | Uniq24 (xor xa ya) (xor xb yb) (xor xc yc) | ||
690 | |||
691 | |||
692 | |||
693 | -- 321 | ||
694 | -- 357 | ||
diff --git a/dht-client.cabal b/dht-client.cabal index bddb07b3..0eef7cee 100644 --- a/dht-client.cabal +++ b/dht-client.cabal | |||
@@ -146,6 +146,8 @@ library | |||
146 | ToxManager | 146 | ToxManager |
147 | XMPPToTox | 147 | XMPPToTox |
148 | DebugUtil | 148 | DebugUtil |
149 | HandshakeCache | ||
150 | Network.Tox.AggregateSession | ||
149 | 151 | ||
150 | build-depends: base | 152 | build-depends: base |
151 | , containers | 153 | , containers |
diff --git a/examples/dhtd.hs b/examples/dhtd.hs index db8664e8..34b555f5 100644 --- a/examples/dhtd.hs +++ b/examples/dhtd.hs | |||
@@ -31,6 +31,7 @@ import Data.Array.MArray (getAssocs) | |||
31 | import Data.Bool | 31 | import Data.Bool |
32 | import Data.Char | 32 | import Data.Char |
33 | import Data.Conduit as C | 33 | import Data.Conduit as C |
34 | import qualified Data.Conduit.List as C | ||
34 | import Data.Function | 35 | import Data.Function |
35 | import Data.Hashable | 36 | import Data.Hashable |
36 | import Data.List | 37 | import Data.List |
@@ -105,6 +106,7 @@ import Network.Tox.ContactInfo as Tox | |||
105 | import OnionRouter | 106 | import OnionRouter |
106 | import Data.PacketQueue | 107 | import Data.PacketQueue |
107 | import qualified Data.Word64Map as W64 | 108 | import qualified Data.Word64Map as W64 |
109 | import Network.Tox.AggregateSession | ||
108 | import System.FilePath | 110 | import System.FilePath |
109 | import System.Process | 111 | import System.Process |
110 | import System.Posix.IO | 112 | import System.Posix.IO |
@@ -1622,6 +1624,71 @@ showMsg (n,(flg,(snapshot,iocm))) = B.concat [bool " " "h " flg, showmsg' (snap | |||
1622 | _ | o <= 122 && o >= 97 -> chr (o + 119737) | 1624 | _ | o <= 122 && o >= 97 -> chr (o + 119737) |
1623 | _ -> x | 1625 | _ -> x |
1624 | 1626 | ||
1627 | onNewToxSession :: XMPPServer | ||
1628 | -> TVar (Map.Map Uniq24 AggregateSession) | ||
1629 | -> ContactInfo extra | ||
1630 | -> SockAddr | ||
1631 | -> Tox.NetCryptoSession | ||
1632 | -> IO () | ||
1633 | onNewToxSession sv ssvar ContactInfo{accounts} addrTox netcrypto = do | ||
1634 | let them s = Tox.ncTheirPublicKey s | ||
1635 | |||
1636 | me s = Tox.ncMyPublicKey s | ||
1637 | |||
1638 | onStatusChange :: (Tox.NetCryptoSession -> Tcp.ConnectionEvent XML.Event -> STM ()) | ||
1639 | -> AggregateSession -> Tox.NetCryptoSession -> Status Tox.ToxProgress -> STM () | ||
1640 | onStatusChange announce c s Established = onConnect announce c s | ||
1641 | onStatusChange announce _ s _ = onEOF announce s | ||
1642 | |||
1643 | onEOF announce s = do | ||
1644 | HashMap.lookup (Tox.key2id $ me s) <$> readTVar accounts | ||
1645 | >>= mapM_ (setTerminated $ them s) | ||
1646 | announce s Tcp.EOF | ||
1647 | |||
1648 | onConnect announce c s = do | ||
1649 | HashMap.lookup (Tox.key2id $ me s) <$> readTVar accounts | ||
1650 | >>= mapM_ (setEstablished $ them s) | ||
1651 | announce s $ Tcp.Connection (return False) xmppSrc xmppSnk | ||
1652 | where | ||
1653 | toxSrc :: ConduitT () (Int, CryptoMessage) IO () | ||
1654 | toxSnk :: ConduitT (Maybe Int, CryptoMessage) Void IO () | ||
1655 | xmppSrc :: ConduitT () XML.Event IO () | ||
1656 | xmppSnk :: ConduitT (Flush XML.Event) Void IO () | ||
1657 | |||
1658 | toxSrc = ioToSource (atomically $ orElse (awaitAny c) | ||
1659 | $ aggregateStatus c >>= \case | ||
1660 | Dormant -> return Nothing | ||
1661 | _ -> retry) | ||
1662 | (return ()) | ||
1663 | toxSnk = C.mapM_ (uncurry $ dispatchMessage c) | ||
1664 | xmppSrc = toxSrc .| C.map snd .| toxToXmpp addrTox (me s) (xmppHostname $ them s) | ||
1665 | xmppSnk = flushPassThrough xmppToTox | ||
1666 | .| C.mapMaybe (\case Flush -> Nothing | ||
1667 | Chunk x -> Just (Nothing,x)) | ||
1668 | .| toxSnk | ||
1669 | |||
1670 | uniqkey <- xor24 <$> hash24 (them netcrypto) <*> hash24 (me netcrypto) | ||
1671 | |||
1672 | c <- atomically $ do | ||
1673 | mc <- Map.lookup uniqkey <$> readTVar ssvar | ||
1674 | case mc of | ||
1675 | Nothing -> do | ||
1676 | announce <- do | ||
1677 | v <- newTVar Nothing | ||
1678 | let ck = uniqueAsKey uniqkey | ||
1679 | condta s = ConnectionData (Left (Local addrTox)) | ||
1680 | XMPPServer.Tox | ||
1681 | (xmppHostname $ me s) | ||
1682 | v | ||
1683 | return $ \s e -> writeTChan (xmppEventChannel sv) ( (ck, condta s), e) | ||
1684 | c <- newAggregateSession $ onStatusChange announce | ||
1685 | modifyTVar' ssvar $ Map.insert uniqkey c | ||
1686 | return c | ||
1687 | Just c -> return c | ||
1688 | |||
1689 | addSession c netcrypto | ||
1690 | |||
1691 | return () | ||
1625 | 1692 | ||
1626 | main :: IO () | 1693 | main :: IO () |
1627 | main = do | 1694 | main = do |
@@ -1775,7 +1842,7 @@ main = do | |||
1775 | , qshowTok = (const Nothing) | 1842 | , qshowTok = (const Nothing) |
1776 | }) | 1843 | }) |
1777 | , ("toxid", DHTQuery | 1844 | , ("toxid", DHTQuery |
1778 | { qsearch = toxQSearch tox | 1845 | { qsearch = Tox.toxQSearch tox |
1779 | , qhandler = -- qhandler :: ni -> nid -> IO ([ni], [r], tok) | 1846 | , qhandler = -- qhandler :: ni -> nid -> IO ([ni], [r], tok) |
1780 | (\ni nid -> | 1847 | (\ni nid -> |
1781 | Tox.unwrapAnnounceResponse Nothing | 1848 | Tox.unwrapAnnounceResponse Nothing |
@@ -1932,6 +1999,7 @@ main = do | |||
1932 | 1999 | ||
1933 | _ <- UPNP.requestPorts "dht-client" $ map (Datagram,) $ baddrs ++ taddrs | 2000 | _ <- UPNP.requestPorts "dht-client" $ map (Datagram,) $ baddrs ++ taddrs |
1934 | 2001 | ||
2002 | ssvar <- atomically $ newTVar Map.empty | ||
1935 | (msv,mconns,mstate) <- case portxmpp opts of | 2003 | (msv,mconns,mstate) <- case portxmpp opts of |
1936 | "" -> return (Nothing,Nothing,Nothing) | 2004 | "" -> return (Nothing,Nothing,Nothing) |
1937 | p -> do | 2005 | p -> do |
@@ -1952,7 +2020,8 @@ main = do | |||
1952 | , lookupBkts "tox6" toxdhts | 2020 | , lookupBkts "tox6" toxdhts |
1953 | ] | 2021 | ] |
1954 | 2022 | ||
1955 | state <- newPresenceState cw (toxman announcer toxbkts <$> mbtox) serverVar | 2023 | let tman = toxman ssvar announcer toxbkts <$> mbtox |
2024 | state <- newPresenceState cw tman serverVar | ||
1956 | 2025 | ||
1957 | sv <- xmppServer Tcp.noCleanUp (presenceHooks state (verbosity opts) (Just cport) (Just sport)) | 2026 | sv <- xmppServer Tcp.noCleanUp (presenceHooks state (verbosity opts) (Just cport) (Just sport)) |
1958 | -- We now have a server object but it's not ready to use until | 2027 | -- We now have a server object but it's not ready to use until |
@@ -1965,6 +2034,7 @@ main = do | |||
1965 | 2034 | ||
1966 | forM_ (take 1 taddrs) $ \addrTox -> do | 2035 | forM_ (take 1 taddrs) $ \addrTox -> do |
1967 | atomically $ Tox.addNewSessionHook netCryptoSessionsState $ \mbNoSpam netcrypto -> do | 2036 | atomically $ Tox.addNewSessionHook netCryptoSessionsState $ \mbNoSpam netcrypto -> do |
2037 | {- | ||
1968 | -- allsessionsMap <- atomically $ readTVar (netCryptoSessions netCryptoSessionsState) | 2038 | -- allsessionsMap <- atomically $ readTVar (netCryptoSessions netCryptoSessionsState) |
1969 | let sockAddr = Tox.ncSockAddr netcrypto | 2039 | let sockAddr = Tox.ncSockAddr netcrypto |
1970 | pubKey = Tox.ncTheirPublicKey netcrypto | 2040 | pubKey = Tox.ncTheirPublicKey netcrypto |
@@ -1975,15 +2045,21 @@ main = do | |||
1975 | onEOF = return () -- setTerminate is called elsewhere. | 2045 | onEOF = return () -- setTerminate is called elsewhere. |
1976 | xmppSrc = ioToSource receiveCrypto onEOF | 2046 | xmppSrc = ioToSource receiveCrypto onEOF |
1977 | xmppSink = newXmmpSink netcrypto | 2047 | xmppSink = newXmmpSink netcrypto |
2048 | -} | ||
1978 | forM_ msv $ \sv -> do | 2049 | forM_ msv $ \sv -> do |
1979 | let Tox.HaveDHTKey saddr = Tox.ncSockAddr netcrypto | 2050 | let Tox.HaveDHTKey saddr = Tox.ncSockAddr netcrypto |
2051 | {- | ||
1980 | Tox.HaveDHTKey dkey = Tox.ncTheirDHTKey netcrypto | 2052 | Tox.HaveDHTKey dkey = Tox.ncTheirDHTKey netcrypto |
1981 | nid = Tox.key2id dkey | 2053 | nid = Tox.key2id dkey |
1982 | them = Tox.ncTheirPublicKey netcrypto | 2054 | them = Tox.ncTheirPublicKey netcrypto |
1983 | me = Tox.ncMyPublicKey netcrypto | 2055 | me = Tox.ncMyPublicKey netcrypto |
2056 | |||
1984 | announceToxJabberPeer me them (xmppEventChannel sv) addrTox saddr pingflag xmppSrc xmppSink | 2057 | announceToxJabberPeer me them (xmppEventChannel sv) addrTox saddr pingflag xmppSrc xmppSink |
2058 | -} | ||
2059 | |||
1985 | forM_ mbtox $ \tox -> do | 2060 | forM_ mbtox $ \tox -> do |
1986 | let ContactInfo{accounts} = Tox.toxContactInfo tox | 2061 | onNewToxSession sv ssvar (Tox.toxContactInfo tox) saddr netcrypto |
2062 | {- | ||
1987 | mbacc <- HashMap.lookup (Tox.key2id me) | 2063 | mbacc <- HashMap.lookup (Tox.key2id me) |
1988 | <$> atomically (readTVar accounts) | 2064 | <$> atomically (readTVar accounts) |
1989 | -- TODO: Add account if it doesn't exist? | 2065 | -- TODO: Add account if it doesn't exist? |
@@ -1998,6 +2074,7 @@ main = do | |||
1998 | let (listenerId,supply') = freshId supply | 2074 | let (listenerId,supply') = freshId supply |
1999 | writeTVar (Tox.listenerIDSupply netCryptoSessionsState) supply' | 2075 | writeTVar (Tox.listenerIDSupply netCryptoSessionsState) supply' |
2000 | modifyTVar' (Tox.ncListeners netcrypto) (IntMap.insert listenerId (0,tmchan)) | 2076 | modifyTVar' (Tox.ncListeners netcrypto) (IntMap.insert listenerId (0,tmchan)) |
2077 | -} | ||
2001 | return Nothing | 2078 | return Nothing |
2002 | 2079 | ||
2003 | let dhts = Map.union btdhts toxdhts | 2080 | let dhts = Map.union btdhts toxdhts |
diff --git a/src/Network/Tox.hs b/src/Network/Tox.hs index c1cdb151..3ad2b11e 100644 --- a/src/Network/Tox.hs +++ b/src/Network/Tox.hs | |||
@@ -32,6 +32,7 @@ import qualified Data.ByteString as B | |||
32 | import qualified Data.ByteString.Char8 as C8 | 32 | import qualified Data.ByteString.Char8 as C8 |
33 | import Data.Data | 33 | import Data.Data |
34 | import Data.Functor.Contravariant | 34 | import Data.Functor.Contravariant |
35 | import Data.IP | ||
35 | import Data.Maybe | 36 | import Data.Maybe |
36 | import qualified Data.MinMaxPSQ as MinMaxPSQ | 37 | import qualified Data.MinMaxPSQ as MinMaxPSQ |
37 | import qualified Data.Serialize as S | 38 | import qualified Data.Serialize as S |
@@ -50,7 +51,9 @@ import Connection | |||
50 | import Crypto.Tox | 51 | import Crypto.Tox |
51 | import Data.Word64Map (fitsInInt) | 52 | import Data.Word64Map (fitsInInt) |
52 | import qualified Data.Word64Map (empty) | 53 | import qualified Data.Word64Map (empty) |
54 | import HandshakeCache | ||
53 | import Network.Kademlia.Bootstrap (forkPollForRefresh, bootstrap) | 55 | import Network.Kademlia.Bootstrap (forkPollForRefresh, bootstrap) |
56 | import Network.Kademlia.Search | ||
54 | import Network.Tox.Crypto.Transport (Handshake(..),CryptoPacket) | 57 | import Network.Tox.Crypto.Transport (Handshake(..),CryptoPacket) |
55 | import Network.Tox.Handshake | 58 | import Network.Tox.Handshake |
56 | import Network.Tox.Crypto.Handlers | 59 | import Network.Tox.Crypto.Handlers |
@@ -203,6 +206,7 @@ data Tox extra = Tox | |||
203 | , toxToRoute :: Transport String Onion.AnnouncedRendezvous (PublicKey,Onion.OnionData) | 206 | , toxToRoute :: Transport String Onion.AnnouncedRendezvous (PublicKey,Onion.OnionData) |
204 | , toxCrypto :: Transport String SockAddr (CryptoPacket Encrypted) | 207 | , toxCrypto :: Transport String SockAddr (CryptoPacket Encrypted) |
205 | , toxHandshakes :: Transport String SockAddr (Handshake Encrypted) | 208 | , toxHandshakes :: Transport String SockAddr (Handshake Encrypted) |
209 | , toxHandshakeCache :: HandshakeCache | ||
206 | , toxCryptoSessions :: NetCryptoSessions | 210 | , toxCryptoSessions :: NetCryptoSessions |
207 | , toxCryptoKeys :: TransportCrypto | 211 | , toxCryptoKeys :: TransportCrypto |
208 | , toxRouting :: DHT.Routing | 212 | , toxRouting :: DHT.Routing |
@@ -442,6 +446,8 @@ newToxOverTransport keydb addr mbSessionsState suppliedDHTKey udp = do | |||
442 | dhtclient <- newClient drg dhtnet0 DHT.classify (myAddr tbl4 tbl6) (DHT.handlers crypto . mkrouting) id | 446 | dhtclient <- newClient drg dhtnet0 DHT.classify (myAddr tbl4 tbl6) (DHT.handlers crypto . mkrouting) id |
443 | $ \client net -> onInbound (DHT.updateRouting client (mkrouting client) orouter) net | 447 | $ \client net -> onInbound (DHT.updateRouting client (mkrouting client) orouter) net |
444 | 448 | ||
449 | hscache <- newHandshakeCache crypto (sendMessage handshakes) | ||
450 | |||
445 | let sessionsState = sessionsState0 { sendHandshake = sendMessage handshakes | 451 | let sessionsState = sessionsState0 { sendHandshake = sendMessage handshakes |
446 | , sendSessionPacket = sendMessage cryptonet | 452 | , sendSessionPacket = sendMessage cryptonet |
447 | , transportCrypto = crypto | 453 | , transportCrypto = crypto |
@@ -479,6 +485,7 @@ newToxOverTransport keydb addr mbSessionsState suppliedDHTKey udp = do | |||
479 | , toxToRoute = onInbound (updateContactInfo roster) dtacrypt | 485 | , toxToRoute = onInbound (updateContactInfo roster) dtacrypt |
480 | , toxCrypto = addHandler (dput XMisc) (sessionPacketH sessionsState) cryptonet | 486 | , toxCrypto = addHandler (dput XMisc) (sessionPacketH sessionsState) cryptonet |
481 | , toxHandshakes = addHandler (dput XMisc) (handshakeH sessionsState) handshakes | 487 | , toxHandshakes = addHandler (dput XMisc) (handshakeH sessionsState) handshakes |
488 | , toxHandshakeCache = hscache | ||
482 | , toxCryptoSessions = sessionsState | 489 | , toxCryptoSessions = sessionsState |
483 | , toxCryptoKeys = crypto | 490 | , toxCryptoKeys = crypto |
484 | , toxRouting = mkrouting dhtclient | 491 | , toxRouting = mkrouting dhtclient |
@@ -550,3 +557,7 @@ announceToLan sock nid = do | |||
550 | let broadcast = addrAddress broadcast_info | 557 | let broadcast = addrAddress broadcast_info |
551 | bs = S.runPut $ DHT.putMessage (DHT.DHTLanDiscovery nid) | 558 | bs = S.runPut $ DHT.putMessage (DHT.DHTLanDiscovery nid) |
552 | saferSendTo sock bs broadcast | 559 | saferSendTo sock bs broadcast |
560 | |||
561 | toxQSearch :: Tox extra -> Search NodeId (IP, PortNumber) Nonce32 NodeInfo Onion.Rendezvous | ||
562 | toxQSearch tox = Onion.toxidSearch (onionTimeout tox) (toxCryptoKeys tox) (toxOnion tox) | ||
563 | |||