summaryrefslogtreecommitdiff
path: root/src/Network
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network')
-rw-r--r--src/Network/BitTorrent/DHT.hs6
-rw-r--r--src/Network/BitTorrent/DHT/Query.hs103
-rw-r--r--src/Network/DHT.hs2
-rw-r--r--src/Network/DHT/Types.hs2
-rw-r--r--src/Network/DatagramServer.hs20
-rw-r--r--src/Network/DatagramServer/Mainline.hs23
-rw-r--r--src/Network/DatagramServer/Tox.hs118
-rw-r--r--src/Network/DatagramServer/Types.hs33
8 files changed, 255 insertions, 52 deletions
diff --git a/src/Network/BitTorrent/DHT.hs b/src/Network/BitTorrent/DHT.hs
index fa8071d5..2535c05c 100644
--- a/src/Network/BitTorrent/DHT.hs
+++ b/src/Network/BitTorrent/DHT.hs
@@ -234,7 +234,7 @@ bootstrap :: forall raw dht u ip.
234 , Show u 234 , Show u
235 , Default u 235 , Default u
236 , Serialize u 236 , Serialize u
237 ) => Maybe BS.ByteString -> [NodeAddr ip] -> DHT raw dht u ip () 237 ) => Maybe BS.ByteString -> [PacketDestination dht] -> DHT raw dht u ip ()
238bootstrap mbs startNodes = do 238bootstrap mbs startNodes = do
239 restored <- 239 restored <-
240 case decode <$> mbs of 240 case decode <$> mbs of
@@ -250,7 +250,7 @@ bootstrap mbs startNodes = do
250 return ( ns :: [NodeInfo dht ip u] ) 250 return ( ns :: [NodeInfo dht ip u] )
251 input_nodes <- (restored ++) . T.toList <$> getTable 251 input_nodes <- (restored ++) . T.toList <$> getTable
252 -- Step 1: Use iterative searches to flesh out the table.. 252 -- Step 1: Use iterative searches to flesh out the table..
253 do let knowns = map (map $ nodeAddr . fst) input_nodes 253 do let knowns = map (map $ fst) input_nodes
254 -- Below, we reverse the nodes since the table serialization puts the 254 -- Below, we reverse the nodes since the table serialization puts the
255 -- nearest nodes last and we want to choose a similar node id to bootstrap 255 -- nearest nodes last and we want to choose a similar node id to bootstrap
256 -- faster. 256 -- faster.
@@ -265,7 +265,7 @@ bootstrap mbs startNodes = do
265 when (null ns) $ do 265 when (null ns) $ do
266 -- TODO filter duplicated in startNodes list 266 -- TODO filter duplicated in startNodes list
267 -- TODO retransmissions for startNodes 267 -- TODO retransmissions for startNodes
268 (aliveNodes,_) <- unzip <$> queryParallel (pingQ <$> startNodes) 268 (aliveNodes,_) <- unzip <$> queryParallel (coldPingQ <$> startNodes)
269 _ <- searchAll $ take 2 aliveNodes 269 _ <- searchAll $ take 2 aliveNodes
270 return () 270 return ()
271 -- Step 2: Repeatedly refresh incomplete buckets until the table is full. 271 -- Step 2: Repeatedly refresh incomplete buckets until the table is full.
diff --git a/src/Network/BitTorrent/DHT/Query.hs b/src/Network/BitTorrent/DHT/Query.hs
index 77fede94..60b772b3 100644
--- a/src/Network/BitTorrent/DHT/Query.hs
+++ b/src/Network/BitTorrent/DHT/Query.hs
@@ -35,6 +35,7 @@ module Network.BitTorrent.DHT.Query
35 -- single response. 35 -- single response.
36 , Iteration 36 , Iteration
37 , pingQ 37 , pingQ
38 , coldPingQ
38 , findNodeQ 39 , findNodeQ
39 , getPeersQ 40 , getPeersQ
40 , announceQ 41 , announceQ
@@ -316,44 +317,72 @@ pingQ :: forall raw dht u ip.
316 , FiniteBits (NodeId dht) 317 , FiniteBits (NodeId dht)
317 , Show (NodeId dht) 318 , Show (NodeId dht)
318 , Show (QueryMethod dht) 319 , Show (QueryMethod dht)
319 ) => NodeAddr ip -> DHT raw dht u ip (NodeInfo dht ip u , Maybe ReflectedIP) 320 ) => NodeInfo dht ip u -> DHT raw dht u ip (NodeInfo dht ip u , Maybe ReflectedIP)
320pingQ addr = do 321pingQ ni = do
321 let ping = DHT.pingMessage (Proxy :: Proxy dht) 322 let ping = DHT.pingMessage (Proxy :: Proxy dht)
322 (nid, pong, mip) <- queryNode' addr ping 323 (nid, pong, mip) <- queryNode' ni ping
323 let _ = pong `asTypeOf` ping 324 let _ = pong `asTypeOf` ping
324 -- (nid, PingPayload{}, mip) <- queryNode' addr PingPayload {isPong=False, pingId=pid} 325 -- (nid, PingPayload{}, mip) <- queryNode' addr PingPayload {isPong=False, pingId=pid}
325 return (NodeInfo nid addr def, mip) 326 return (NodeInfo nid (nodeAddr ni) def, mip)
327
328-- | The most basic query. May be used to check if the given node is
329-- alive or get its 'NodeId'.
330coldPingQ :: forall raw dht u ip.
331 ( DHT.Kademlia dht
332 , Address ip
333 , KRPC dht (Query dht (Ping dht)) (Response dht (Ping dht))
334 , Default u
335 , Show u
336 , Ord (TransactionID dht)
337 , Serialize (TransactionID dht)
338 , WireFormat raw dht
339 , SerializableTo raw (Response dht (Ping dht))
340 , SerializableTo raw (Query dht (Ping dht))
341 , Ord (NodeId dht)
342 , FiniteBits (NodeId dht)
343 , Show (NodeId dht)
344 , Show (QueryMethod dht)
345 ) => PacketDestination dht -> DHT raw dht u ip (NodeInfo dht ip u , Maybe ReflectedIP)
346coldPingQ dest = do
347 let ping = DHT.pingMessage (Proxy :: Proxy dht)
348 naddr <- maybe (throwIO $ QueryFailed ProtocolError "unable to construct NodeAddr from PacketDestination")
349 return
350 $ fromAddr dest
351 (nid, pong, mip) <- coldQueryNode' naddr dest ping
352 let _ = pong `asTypeOf` ping
353 -- (nid, PingPayload{}, mip) <- queryNode' addr PingPayload {isPong=False, pingId=pid}
354 return (NodeInfo nid naddr def, mip)
326 355
327-- TODO [robustness] match range of returned node ids with the 356-- TODO [robustness] match range of returned node ids with the
328-- expected range and either filter bad nodes or discard response at 357-- expected range and either filter bad nodes or discard response at
329-- all throwing an exception 358-- all throwing an exception
330-- findNodeQ :: Address ip => TableKey key => key -> IterationI ip NodeInfo 359-- findNodeQ :: Address ip => TableKey key => key -> IterationI ip NodeInfo
331findNodeQ proxy key NodeInfo {..} = do 360findNodeQ proxy key ni = do
332 closest <- fmap DHT.foundNodes $ DHT.findNodeMessage proxy key <@> nodeAddr 361 closest <- fmap DHT.foundNodes $ DHT.findNodeMessage proxy key <@> ni
333 $(logInfoS) "findNodeQ" $ "NodeFound\n" 362 $(logInfoS) "findNodeQ" $ "NodeFound\n"
334 <> T.pack (L.unlines $ L.map ((' ' :) . show . pPrint) closest) 363 <> T.pack (L.unlines $ L.map ((' ' :) . show . pPrint) closest)
335 return $ Right closest 364 return $ Right closest
336 365
337#ifdef VERSION_bencoding 366#ifdef VERSION_bencoding
338getPeersQ :: Address ip => InfoHash -> Iteration BValue KMessageOf () ip PeerAddr 367getPeersQ :: Address ip => InfoHash -> Iteration BValue KMessageOf () ip PeerAddr
339getPeersQ topic NodeInfo {..} = do 368getPeersQ topic ni = do
340 GotPeers {..} <- GetPeers topic <@> nodeAddr 369 GotPeers {..} <- GetPeers topic <@> ni
341 let dist = distance (toNodeId topic) nodeId 370 let dist = distance (toNodeId topic) (nodeId ni)
342 $(logInfoS) "getPeersQ" $ T.pack 371 $(logInfoS) "getPeersQ" $ T.pack
343 $ "distance: " <> render (pPrint dist) <> " , result: " 372 $ "distance: " <> render (pPrint dist) <> " , result: "
344 <> case peers of { Left _ -> "NODES"; Right _ -> "PEERS" } 373 <> case peers of { Left _ -> "NODES"; Right _ -> "PEERS" }
345 return peers 374 return peers
346 375
347announceQ :: Address ip => InfoHash -> PortNumber -> Iteration BValue KMessageOf () ip NodeAddr 376announceQ :: Address ip => InfoHash -> PortNumber -> Iteration BValue KMessageOf () ip NodeAddr
348announceQ ih p NodeInfo {..} = do 377announceQ ih p ni = do
349 GotPeers {..} <- GetPeers ih <@> nodeAddr 378 GotPeers {..} <- GetPeers ih <@> ni
350 case peers of 379 case peers of
351 Left ns 380 Left ns
352 | False -> undefined -- TODO check if we can announce 381 | False -> undefined -- TODO check if we can announce
353 | otherwise -> return (Left ns) 382 | otherwise -> return (Left ns)
354 Right _ -> do -- TODO *probably* add to peer cache 383 Right _ -> do -- TODO *probably* add to peer cache
355 Announced <- Announce False ih Nothing p grantedToken <@> nodeAddr 384 Announced <- Announce False ih Nothing p grantedToken <@> ni
356 return (Right [nodeAddr]) 385 return (Right [nodeAddr ni])
357#endif 386#endif
358 387
359{----------------------------------------------------------------------- 388{-----------------------------------------------------------------------
@@ -393,7 +422,7 @@ ioFindNode :: ( DHT.Kademlia dht
393ioFindNode ih = do 422ioFindNode ih = do
394 session <- ask 423 session <- ask
395 return $ \ni -> runDHT session $ do 424 return $ \ni -> runDHT session $ do
396 ns <- fmap DHT.foundNodes $ DHT.findNodeMessage Proxy ih <@> nodeAddr ni 425 ns <- fmap DHT.foundNodes $ DHT.findNodeMessage Proxy ih <@> ni
397 let ns' = L.map (fmap (const def)) ns 426 let ns' = L.map (fmap (const def)) ns
398 return $ L.partition (\n -> nodeId n /= toNodeId ih) ns' 427 return $ L.partition (\n -> nodeId n /= toNodeId ih) ns'
399 428
@@ -422,7 +451,7 @@ ioFindNodes :: ( DHT.Kademlia dht
422ioFindNodes ih = do 451ioFindNodes ih = do
423 session <- ask 452 session <- ask
424 return $ \ni -> runDHT session $ do 453 return $ \ni -> runDHT session $ do
425 ns <- fmap DHT.foundNodes $ DHT.findNodeMessage Proxy ih <@> nodeAddr ni 454 ns <- fmap DHT.foundNodes $ DHT.findNodeMessage Proxy ih <@> ni
426 let ns' = L.map (fmap (const def)) ns 455 let ns' = L.map (fmap (const def)) ns
427 return ([], ns') 456 return ([], ns')
428 457
@@ -504,9 +533,9 @@ probeNode :: ( Default u
504 , FiniteBits (NodeId dht) 533 , FiniteBits (NodeId dht)
505 , Show (NodeId dht) 534 , Show (NodeId dht)
506 , Show (QueryMethod dht) 535 , Show (QueryMethod dht)
507 ) => NodeAddr ip -> DHT raw dht u ip (Bool , Maybe ReflectedIP) 536 ) => NodeInfo dht ip u -> DHT raw dht u ip (Bool , Maybe ReflectedIP)
508probeNode addr = do 537probeNode addr = do
509 $(logDebugS) "routing.questionable_node" (T.pack (render (pPrint addr))) 538 $(logDebugS) "routing.questionable_node" (T.pack (render (pPrint $ nodeAddr addr)))
510 result <- try $ pingQ addr 539 result <- try $ pingQ addr
511 let _ = fmap (const ()) result :: Either QueryFailure () 540 let _ = fmap (const ()) result :: Either QueryFailure ()
512 return $ either (const (False,Nothing)) (\(_,mip)->(True,mip)) result 541 return $ either (const (False,Nothing)) (\(_,mip)->(True,mip)) result
@@ -549,7 +578,7 @@ refreshNodes nid = do
549 $(logWarnS) "refreshNodes" $ "received " <> T.pack (show (L.length ns)) <> " nodes." 578 $(logWarnS) "refreshNodes" $ "received " <> T.pack (show (L.length ns)) <> " nodes."
550 _ <- queryParallel $ flip L.map ns $ \n -> do 579 _ <- queryParallel $ flip L.map ns $ \n -> do
551 $(logWarnS) "refreshNodes" $ "received node: " <> T.pack (show (pPrint n)) 580 $(logWarnS) "refreshNodes" $ "received node: " <> T.pack (show (pPrint n))
552 pingQ (nodeAddr n) 581 pingQ n
553 -- pingQ takes care of inserting the node. 582 -- pingQ takes care of inserting the node.
554 return () 583 return ()
555 return () -- \$ L.concat nss 584 return () -- \$ L.concat nss
@@ -622,7 +651,7 @@ insertNode1 = do
622 , fallbackID = nid :: NodeId dht 651 , fallbackID = nid :: NodeId dht
623 , adjustID = dhtAdjustID Proxy (DHT.fallbackID params) :: SockAddr -> Event dht ip u -> NodeId dht 652 , adjustID = dhtAdjustID Proxy (DHT.fallbackID params) :: SockAddr -> Event dht ip u -> NodeId dht
624 , logMessage = logm :: Char -> String -> IO () 653 , logMessage = logm :: Char -> String -> IO ()
625 , pingProbe = probe :: NodeAddr ip -> IO (Bool, Maybe ReflectedIP) 654 , pingProbe = probe :: NodeInfo dht ip u -> IO (Bool, Maybe ReflectedIP)
626 } 655 }
627 tbl <- asks routingInfo 656 tbl <- asks routingInfo
628 let state = DHT.TableKeeper 657 let state = DHT.TableKeeper
@@ -651,7 +680,7 @@ queryNode :: forall raw dht u a b ip.
651 , Show (QueryMethod dht) 680 , Show (QueryMethod dht)
652 , SerializableTo raw (Response dht (Ping dht)) 681 , SerializableTo raw (Response dht (Ping dht))
653 , SerializableTo raw (Query dht (Ping dht)) 682 , SerializableTo raw (Query dht (Ping dht))
654 ) => NodeAddr ip -> a -> DHT raw dht u ip (NodeId dht, b) 683 ) => NodeInfo dht ip u -> a -> DHT raw dht u ip (NodeId dht, b)
655queryNode addr q = fmap (\(n,b,_) -> (n,b)) $ queryNode' addr q 684queryNode addr q = fmap (\(n,b,_) -> (n,b)) $ queryNode' addr q
656 685
657queryNode' :: forall raw dht u a b ip. 686queryNode' :: forall raw dht u a b ip.
@@ -672,15 +701,39 @@ queryNode' :: forall raw dht u a b ip.
672 , Show (QueryMethod dht) 701 , Show (QueryMethod dht)
673 , SerializableTo raw (Response dht (Ping dht)) 702 , SerializableTo raw (Response dht (Ping dht))
674 , SerializableTo raw (Query dht (Ping dht)) 703 , SerializableTo raw (Query dht (Ping dht))
675 ) => NodeAddr ip -> a -> DHT raw dht u ip (NodeId dht, b, Maybe ReflectedIP) 704 ) => NodeInfo dht ip u -> a -> DHT raw dht u ip (NodeId dht, b, Maybe ReflectedIP)
676queryNode' addr q = do 705queryNode' ni q = do
677 nid <- myNodeIdAccordingTo addr 706 let addr = nodeAddr ni
707 dest = makeAddress (Left $ nodeId ni) (toSockAddr addr)
708 coldQueryNode' addr dest q
709
710coldQueryNode' :: forall raw dht u a b ip.
711 ( Address ip
712 , Default u
713 , Show u
714 , DHT.Kademlia dht
715 , KRPC dht (Query dht a) (Response dht b)
716 , KRPC dht (Query dht (Ping dht)) (Response dht (Ping dht))
717 , Ord (TransactionID dht)
718 , Serialize (TransactionID dht)
719 , WireFormat raw dht
720 , SerializableTo raw (Response dht b)
721 , SerializableTo raw (Query dht a)
722 , Ord (NodeId dht)
723 , FiniteBits (NodeId dht)
724 , Show (NodeId dht)
725 , Show (QueryMethod dht)
726 , SerializableTo raw (Response dht (Ping dht))
727 , SerializableTo raw (Query dht (Ping dht))
728 ) => NodeAddr ip -> PacketDestination dht -> a -> DHT raw dht u ip (NodeId dht, b, Maybe ReflectedIP)
729coldQueryNode' addr dest q = do
730 nid <- myNodeIdAccordingTo $ fromMaybe (error "TODO: coldQueryNode' myNodeIdAccordingTo") $ fromAddr dest
678 dta <- asks dhtData 731 dta <- asks dhtData
679 qextra <- liftIO $ makeQueryExtra dta nid (Proxy :: Proxy (Query dht q)) (Proxy :: Proxy (Response dht b)) 732 qextra <- liftIO $ makeQueryExtra dta nid (Proxy :: Proxy (Query dht q)) (Proxy :: Proxy (Response dht b))
680 let read_only = False -- TODO: check for NAT issues. (BEP 43) 733 let read_only = False -- TODO: check for NAT issues. (BEP 43)
681 -- let KRPC.Method name = KRPC.method :: KRPC.Method dht (Query dht a) (Response dht b) 734 -- let KRPC.Method name = KRPC.method :: KRPC.Method dht (Query dht a) (Response dht b)
682 mgr <- asks manager 735 mgr <- asks manager
683 (Response rextra r, remoteId, witnessed_ip) <- liftIO $ query' mgr (toSockAddr addr) (Query qextra q) 736 (Response rextra r, remoteId, witnessed_ip) <- liftIO $ query' mgr dest (Query qextra q)
684 -- \$(logDebugS) "queryNode" $ "Witnessed address: " <> T.pack (show witnessed_ip) 737 -- \$(logDebugS) "queryNode" $ "Witnessed address: " <> T.pack (show witnessed_ip)
685 -- <> " by " <> T.pack (show (toSockAddr addr)) 738 -- <> " by " <> T.pack (show (toSockAddr addr))
686 _ <- insertNode (NodeInfo remoteId addr def) witnessed_ip 739 _ <- insertNode (NodeInfo remoteId addr def) witnessed_ip
@@ -704,6 +757,6 @@ queryNode' addr q = do
704 , SerializableTo raw (Query dht (Ping dht)) 757 , SerializableTo raw (Query dht (Ping dht))
705 , WireFormat raw dht 758 , WireFormat raw dht
706 , Kademlia dht 759 , Kademlia dht
707 ) => a -> NodeAddr ip -> DHT raw dht u ip b 760 ) => a -> NodeInfo dht ip u -> DHT raw dht u ip b
708q <@> addr = snd <$> queryNode addr q 761q <@> addr = snd <$> queryNode addr q
709{-# INLINE (<@>) #-} 762{-# INLINE (<@>) #-}
diff --git a/src/Network/DHT.hs b/src/Network/DHT.hs
index 0dab29cd..285cf9ff 100644
--- a/src/Network/DHT.hs
+++ b/src/Network/DHT.hs
@@ -115,7 +115,7 @@ insertNode param@TableParameters{..} state info witnessed_ip0 = do
115 myThreadId >>= flip labelThread "DHT.insertNode.pingResults" 115 myThreadId >>= flip labelThread "DHT.insertNode.pingResults"
116 forM_ ps $ \(CheckPing ns)-> do 116 forM_ ps $ \(CheckPing ns)-> do
117 forM_ ns $ \n -> do 117 forM_ ns $ \n -> do
118 (b,mip) <- pingProbe (nodeAddr n) 118 (b,mip) <- pingProbe n
119 let alive = PingResult n b 119 let alive = PingResult n b
120 logMessage 'D' $ "PingResult "++show (nodeId n,b) 120 logMessage 'D' $ "PingResult "++show (nodeId n,b)
121 _ <- join $ atomically $ atomicInsert param state tm alive mip 121 _ <- join $ atomically $ atomicInsert param state tm alive mip
diff --git a/src/Network/DHT/Types.hs b/src/Network/DHT/Types.hs
index 0102a53f..47f98ebe 100644
--- a/src/Network/DHT/Types.hs
+++ b/src/Network/DHT/Types.hs
@@ -18,7 +18,7 @@ import GHC.Generics
18data TableParameters msg ip u = TableParameters 18data TableParameters msg ip u = TableParameters
19 { maxBuckets :: Int 19 { maxBuckets :: Int
20 , fallbackID :: NodeId msg 20 , fallbackID :: NodeId msg
21 , pingProbe :: NodeAddr ip -> IO (Bool, Maybe ReflectedIP) 21 , pingProbe :: NodeInfo msg ip u -> IO (Bool, Maybe ReflectedIP)
22 , logMessage :: Char -> String -> IO () 22 , logMessage :: Char -> String -> IO ()
23 , adjustID :: SockAddr -> Event msg ip u -> NodeId msg 23 , adjustID :: SockAddr -> Event msg ip u -> NodeId msg
24 } 24 }
diff --git a/src/Network/DatagramServer.hs b/src/Network/DatagramServer.hs
index 1376748f..ca968a8c 100644
--- a/src/Network/DatagramServer.hs
+++ b/src/Network/DatagramServer.hs
@@ -329,7 +329,7 @@ query :: forall h a b raw msg.
329 , SerializableTo raw a 329 , SerializableTo raw a
330 , WireFormat raw msg 330 , WireFormat raw msg
331 , KRPC msg a b 331 , KRPC msg a b
332 ) => Manager raw msg -> SockAddr -> a -> IO b 332 ) => Manager raw msg -> PacketDestination msg -> a -> IO b
333query mgr addr params = queryK mgr addr params (\_ x _ _ -> x) 333query mgr addr params = queryK mgr addr params (\_ x _ _ -> x)
334 334
335-- | Like 'query' but possibly returns your externally routable IP address. 335-- | Like 'query' but possibly returns your externally routable IP address.
@@ -340,7 +340,7 @@ query' :: forall h a b raw msg.
340 , Serialize (TransactionID msg) 340 , Serialize (TransactionID msg)
341 , SerializableTo raw a , WireFormat raw msg 341 , SerializableTo raw a , WireFormat raw msg
342 , KRPC msg a b 342 , KRPC msg a b
343 ) => Manager raw msg -> SockAddr -> a -> IO (b , NodeId msg, Maybe ReflectedIP) 343 ) => Manager raw msg -> PacketDestination msg -> a -> IO (b , NodeId msg, Maybe ReflectedIP)
344query' mgr addr params = queryK mgr addr params (\_ b nid ip -> (b,nid,ip)) 344query' mgr addr params = queryK mgr addr params (\_ b nid ip -> (b,nid,ip))
345 345
346-- | Enqueue a query, but give us the complete BEncoded content sent by the 346-- | Enqueue a query, but give us the complete BEncoded content sent by the
@@ -354,7 +354,7 @@ queryRaw :: forall h a b raw msg.
354 , SerializableTo raw a 354 , SerializableTo raw a
355 , WireFormat raw msg 355 , WireFormat raw msg
356 , KRPC msg a b 356 , KRPC msg a b
357 ) => Manager raw msg -> SockAddr -> a -> IO (b , raw) 357 ) => Manager raw msg -> PacketDestination msg -> a -> IO (b , raw)
358queryRaw mgr addr params = queryK mgr addr params (\raw x _ _ -> (x,raw)) 358queryRaw mgr addr params = queryK mgr addr params (\raw x _ _ -> (x,raw))
359 359
360queryK :: forall h a b x raw msg. 360queryK :: forall h a b x raw msg.
@@ -366,11 +366,12 @@ queryK :: forall h a b x raw msg.
366 , Serialize (TransactionID msg) 366 , Serialize (TransactionID msg)
367 , KRPC msg a b 367 , KRPC msg a b
368 ) => 368 ) =>
369 Manager raw msg -> SockAddr -> a -> (raw -> b -> NodeId msg -> Maybe ReflectedIP -> x) -> IO x 369 Manager raw msg -> PacketDestination msg -> a -> (raw -> b -> NodeId msg -> Maybe ReflectedIP -> x) -> IO x
370queryK mgr@Manager{..} addr params kont = do 370queryK mgr@Manager{..} dest params kont = do
371 tid <- liftIO $ genTransactionId transactionCounter 371 tid <- liftIO $ genTransactionId transactionCounter
372 let Method meth = method :: Method msg a b 372 let addr = toSockAddr dest
373 let signature = querySignature meth tid addr 373 Method meth = method :: Method msg a b
374 signature = querySignature meth tid addr
374 logMsg 'D' "query.sending" signature 375 logMsg 'D' "query.sending" signature
375 376
376 mres <- liftIO $ do 377 mres <- liftIO $ do
@@ -380,7 +381,7 @@ queryK mgr@Manager{..} addr params kont = do
380 ctx = error "TODO TOX ToxCipherContext or () for Mainline" 381 ctx = error "TODO TOX ToxCipherContext or () for Mainline"
381 q <- buildQuery cli addr meth tid params 382 q <- buildQuery cli addr meth tid params
382 let qb = encodePayload (q :: msg a) :: msg raw 383 let qb = encodePayload (q :: msg a) :: msg raw
383 qbs = encodeHeaders ctx qb 384 qbs = encodeHeaders ctx qb dest
384 sendQuery sock addr qbs 385 sendQuery sock addr qbs
385 `onException` unregisterQuery (tid, addr) pendingCalls 386 `onException` unregisterQuery (tid, addr) pendingCalls
386 387
@@ -528,7 +529,8 @@ handleQuery mgr@Manager{..} hs meth raw q addr = void $ fork $ do
528 res <- dispatchHandler mgr hs meth q addr 529 res <- dispatchHandler mgr hs meth q addr
529 let res' = either buildError Just res 530 let res' = either buildError Just res
530 ctx = error "TODO TOX ToxCipherContext 2 or () for Mainline" 531 ctx = error "TODO TOX ToxCipherContext 2 or () for Mainline"
531 resbs = fmap (encodeHeaders ctx) res' :: Maybe BS.ByteString 532 dest = makeAddress (Right q) addr
533 resbs = fmap (\raw -> encodeHeaders ctx raw dest) res' :: Maybe BS.ByteString
532 -- TODO: Generalize this debug print. 534 -- TODO: Generalize this debug print.
533 -- resbe = either toBEncode toBEncode res 535 -- resbe = either toBEncode toBEncode res
534 -- .(logOther "q") \$ T.unlines 536 -- .(logOther "q") \$ T.unlines
diff --git a/src/Network/DatagramServer/Mainline.hs b/src/Network/DatagramServer/Mainline.hs
index 89a275c1..1f07b13f 100644
--- a/src/Network/DatagramServer/Mainline.hs
+++ b/src/Network/DatagramServer/Mainline.hs
@@ -79,6 +79,7 @@ import Data.Typeable
79import Network.Socket (SockAddr (..),PortNumber,HostAddress) 79import Network.Socket (SockAddr (..),PortNumber,HostAddress)
80import Text.PrettyPrint as PP hiding ((<>)) 80import Text.PrettyPrint as PP hiding ((<>))
81import Text.PrettyPrint.HughesPJClass hiding (($$), (<>)) 81import Text.PrettyPrint.HughesPJClass hiding (($$), (<>))
82import Data.Hashable
82 83
83 84
84-- | This transaction ID is generated by the querying node and is 85-- | This transaction ID is generated by the querying node and is
@@ -290,6 +291,9 @@ instance Envelope KMessageOf where
290 } 291 }
291 deriving (Show, Eq, Ord, Typeable) 292 deriving (Show, Eq, Ord, Typeable)
292 293
294 newtype PacketDestination KMessageOf = MainlineNode SockAddr
295 deriving (Show, Eq, Ord, Typeable)
296
293 envelopePayload (Q q) = queryArgs q 297 envelopePayload (Q q) = queryArgs q
294 envelopePayload (R r) = respVals r 298 envelopePayload (R r) = respVals r
295 envelopePayload (E _) = error "TODO: messagePayload for KError" 299 envelopePayload (E _) = error "TODO: messagePayload for KError"
@@ -302,6 +306,9 @@ instance Envelope KMessageOf where
302 envelopeClass (R r) = Response (respIP r) 306 envelopeClass (R r) = Response (respIP r)
303 envelopeClass (E e) = Error e 307 envelopeClass (E e) = Error e
304 308
309 -- replyAddress :: envelope a -> SockAddr -> PacketDestination envelope
310 makeAddress _ addr = MainlineNode addr
311
305 buildReply self addr qry response = 312 buildReply self addr qry response =
306 (R (KResponse response (envelopeTransaction qry) (Just $ ReflectedIP addr))) 313 (R (KResponse response (envelopeTransaction qry) (Just $ ReflectedIP addr)))
307 314
@@ -311,6 +318,20 @@ instance Envelope KMessageOf where
311 318
312 fromRoutableNode = not . queryIsReadOnly 319 fromRoutableNode = not . queryIsReadOnly
313 320
321instance Hashable (PacketDestination KMessageOf) where
322 hashWithSalt s (MainlineNode sockaddr) = hashWithSalt s (show sockaddr)
323
324-- Serialize, Pretty) PacketDestination KMessageOf = MainlineNode SockAddr
325instance Serialize (PacketDestination KMessageOf) where
326 put (MainlineNode addr) = putSockAddr addr
327 get = MainlineNode <$> getSockAddr
328
329instance Pretty (PacketDestination KMessageOf) where
330 pPrint (MainlineNode addr) = PP.text $ show addr
331
332instance Address (PacketDestination KMessageOf) where
333 toSockAddr (MainlineNode addr) = addr
334 fromSockAddr addr = Just $ MainlineNode addr
314 335
315instance WireFormat BValue KMessageOf where 336instance WireFormat BValue KMessageOf where
316 type SerializableTo BValue = BEncode 337 type SerializableTo BValue = BEncode
@@ -323,7 +344,7 @@ instance WireFormat BValue KMessageOf where
323 decodeHeaders _ = BE.fromBEncode 344 decodeHeaders _ = BE.fromBEncode
324 decodePayload kmsg = mapM BE.fromBEncode kmsg 345 decodePayload kmsg = mapM BE.fromBEncode kmsg
325 346
326 encodeHeaders _ kmsg = L.toStrict $ BE.encode kmsg 347 encodeHeaders _ kmsg _ = L.toStrict $ BE.encode kmsg
327 encodePayload msg = fmap BE.toBEncode msg 348 encodePayload msg = fmap BE.toBEncode msg
328 349
329-- | KRPC 'compact list' compatible encoding: contact information for 350-- | KRPC 'compact list' compatible encoding: contact information for
diff --git a/src/Network/DatagramServer/Tox.hs b/src/Network/DatagramServer/Tox.hs
index f666b951..8d2f9289 100644
--- a/src/Network/DatagramServer/Tox.hs
+++ b/src/Network/DatagramServer/Tox.hs
@@ -11,10 +11,13 @@
11{-# LANGUAGE TupleSections #-} 11{-# LANGUAGE TupleSections #-}
12{-# LANGUAGE TypeFamilies #-} 12{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE UnboxedTuples #-} 13{-# LANGUAGE UnboxedTuples #-}
14{-# LANGUAGE TemplateHaskell #-}
15{-# LANGUAGE RankNTypes #-}
14module Network.DatagramServer.Tox where 16module Network.DatagramServer.Tox where
15 17
16import Data.Bits 18import Data.Bits
17import Data.ByteString (ByteString) 19import Data.ByteString (ByteString)
20import Data.ByteArray as BA (ByteArrayAccess,length,withByteArray)
18import qualified Data.Serialize as S 21import qualified Data.Serialize as S
19-- import qualified Data.ByteString.Lazy as L 22-- import qualified Data.ByteString.Lazy as L
20import qualified Data.ByteString.Char8 as Char8 23import qualified Data.ByteString.Char8 as Char8
@@ -23,12 +26,25 @@ import Data.Word
23import Data.LargeWord 26import Data.LargeWord
24import Data.IP 27import Data.IP
25import Data.Serialize 28import Data.Serialize
26-- import Network.Address (NodeInfo(..)) -- Serialize IP 29import Network.Address
27import GHC.Generics (Generic) 30import GHC.Generics (Generic)
28import Network.Socket 31import Network.Socket
29import Network.DatagramServer.Types 32import Network.DatagramServer.Types
30import qualified Network.DatagramServer.Types as Envelope (NodeId) 33import qualified Network.DatagramServer.Types as Envelope (NodeId)
31import Crypto.PubKey.ECC.Types 34import Crypto.PubKey.ECC.Types
35import Crypto.PubKey.Curve25519
36import Crypto.ECC.Class
37import qualified Crypto.Cipher.XSalsa as Salsa20
38import Data.LargeWord
39import Foreign.Ptr
40import Foreign.Storable
41import Foreign.Marshal.Alloc
42import Data.Typeable
43import StaticAssert
44import Crypto.Error.Types
45import Data.Hashable
46import Text.PrettyPrint as PP hiding ((<>))
47import Text.PrettyPrint.HughesPJClass hiding (($$), (<>))
32 48
33 49
34type Key32 = Word256 -- 32 byte key 50type Key32 = Word256 -- 32 byte key
@@ -203,7 +219,9 @@ instance Serialize NodeFormat where
203-- [Sendback data, length=8 bytes] 219-- [Sendback data, length=8 bytes]
204-- ] 220-- ]
205 221
206data ToxCipherContext = ToxCipherContext -- TODO 222data ToxCipherContext = ToxCipherContext
223 { dhtSecretKey :: SecretKey
224 }
207 225
208newtype Ciphered = Ciphered { cipheredBytes :: ByteString } 226newtype Ciphered = Ciphered { cipheredBytes :: ByteString }
209 227
@@ -227,29 +245,51 @@ putMessage (Message {..}) = do
227 let Ciphered bs = msgPayload 245 let Ciphered bs = msgPayload
228 putByteString bs 246 putByteString bs
229 247
248id2key :: NodeId Message -> PublicKey
249id2key recipient = case publicKey recipient of
250 CryptoPassed key -> key
251 CryptoFailed e -> error ("id2key: "++show e)
252
253lookupSecret :: ToxCipherContext -> NodeId Message -> TransactionID Message -> Salsa20.State
254lookupSecret ctx recipient nonce = Salsa20.initialize 20 key nonce
255 where
256 key = ecdh (Proxy :: Proxy Curve_X25519) (dhtSecretKey ctx) (id2key recipient) -- ByteArrayAccess b => b
257
230decipher :: ToxCipherContext -> Message Ciphered -> Either String (Message ByteString) 258decipher :: ToxCipherContext -> Message Ciphered -> Either String (Message ByteString)
231decipher = error "TODO TOX: decipher" 259decipher ctx ciphered = Right (fst . Salsa20.combine st . cipheredBytes <$> ciphered)
260 where
261 st = lookupSecret ctx (msgClient ciphered) (msgNonce ciphered)
232 262
233encipher :: ToxCipherContext -> Message ByteString -> Message Ciphered 263encipher :: ToxCipherContext -> NodeId Message -> Message ByteString -> Message Ciphered
234encipher = error "TODO TOX: encipher" 264encipher ctx recipient plain = Ciphered . fst . Salsa20.combine st <$> plain
265 where
266 st = lookupSecret ctx recipient (msgNonce plain)
235 267
236-- see rfc7748 268-- see rfc7748
269--
270-- Crypto.ECC
271-- Crypto.PubKey.Curve25519
272-- Crypto.Cipher.XSalsa
273--
237curve25519 :: Curve 274curve25519 :: Curve
238curve25519 = CurveFP (CurvePrime prime curvecommon) 275curve25519 = CurveFP (CurvePrime prime curvecommon)
239 where 276 where
240 prime = 2^255 - 19 -- (≅ 1 modulo 4) 277 prime = 2^255 - 19 -- (≅ 1 modulo 4)
241 278
279 sqrt_of_39420360 = 14781619447589544791020593568409986887264606134616475288964881837755586237401
280
242 -- 1 * v^2 = u^3 + 486662*u^2 + u 281 -- 1 * v^2 = u^3 + 486662*u^2 + u
243 282
244 curvecommon = CurveCommon 283 curvecommon = CurveCommon
245 { ecc_a = 486662 284 { ecc_a = 486662
246 , ecc_b = 1 285 , ecc_b = 1
247 , ecc_g = Point 9 14781619447589544791020593568409986887264606134616475288964881837755586237401 -- base point 286 , ecc_g = Point 9 sqrt_of_39420360 -- base point
248 , ecc_n = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed -- order 287 , ecc_n = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed -- order
249 , ecc_h = 8 -- cofactor 288 , ecc_h = 8 -- cofactor
250 } 289 }
251 290
252 291-- crypto_box uses xsalsa20 symmetric encryption and poly1305 authentication.
292-- https://en.wikipedia.org/wiki/Poly1305
253 293
254instance Envelope Message where 294instance Envelope Message where
255 newtype TransactionID Message = TID Nonce24 295 newtype TransactionID Message = TID Nonce24
@@ -263,6 +303,11 @@ instance Envelope Message where
263 newtype QueryExtra Message = QueryNonce { qryNonce :: Nonce8 } 303 newtype QueryExtra Message = QueryNonce { qryNonce :: Nonce8 }
264 newtype ResponseExtra Message = ResponseNonce { rspNonce :: Nonce8 } 304 newtype ResponseExtra Message = ResponseNonce { rspNonce :: Nonce8 }
265 305
306 data PacketDestination Message = ToxAddr { toxID :: NodeId Message
307 , toxSockAddr :: SockAddr
308 }
309 deriving (Eq,Ord,Show)
310
266 envelopePayload = msgPayload 311 envelopePayload = msgPayload
267 312
268 envelopeTransaction = msgNonce 313 envelopeTransaction = msgNonce
@@ -272,15 +317,70 @@ instance Envelope Message where
272 envelopeClass Message { msgType = GetNodes } = Query GetNodes 317 envelopeClass Message { msgType = GetNodes } = Query GetNodes
273 envelopeClass Message { msgType = SendNodes } = Response Nothing 318 envelopeClass Message { msgType = SendNodes } = Response Nothing
274 319
320 makeAddress qry = ToxAddr (either id msgClient qry)
321
275 buildReply self addr qry payload = (fmap (const payload) qry) { msgClient = self } 322 buildReply self addr qry payload = (fmap (const payload) qry) { msgClient = self }
276 323
277 -- buildQuery :: NodeId envelope -> SockAddr -> QueryMethod envelope -> TransactionID envelope -> a -> IO (envelope a) 324 -- buildQuery :: NodeId envelope -> SockAddr -> QueryMethod envelope -> TransactionID envelope -> a -> IO (envelope a)
278 -- buildQuery nid addr meth tid q = todo 325 buildQuery nid addr meth tid q = return $ Message
326 { msgType = meth
327 , msgClient = nid
328 , msgNonce = tid
329 , msgPayload = q
330 }
279 331
280 uniqueTransactionId cnt = do 332 uniqueTransactionId cnt = do
281 return $ either (error "failed to create TransactionId") TID 333 return $ either (error "failed to create TransactionId") TID
282 $ S.decode $ Char8.pack (take 24 $ show cnt ++ repeat ' ') 334 $ S.decode $ Char8.pack (take 24 $ show cnt ++ repeat ' ')
283 335
336
337staticAssert isLittleEndian -- assumed by 'withWord64Ptr'
338
339with3Word64Ptr :: Nonce24 -> (Ptr Word64 -> IO a) -> IO a
340with3Word64Ptr (LargeKey wlo (LargeKey wmid whi)) kont =
341 allocaBytes (sizeOf wlo * 3) $ \p -> do
342 pokeElemOff p 0 wlo
343 pokeElemOff p 1 wmid
344 pokeElemOff p 2 whi
345 kont p
346
347with4Word64Ptr :: Key32 -> (Ptr Word64 -> IO a) -> IO a
348with4Word64Ptr (LargeKey wlo (LargeKey wmid (LargeKey whi whighest))) kont =
349 allocaBytes (sizeOf wlo * 4) $ \p -> do
350 pokeElemOff p 0 wlo
351 pokeElemOff p 1 wmid
352 pokeElemOff p 2 whi
353 pokeElemOff p 3 whighest
354 kont p
355
356
357instance ByteArrayAccess (TransactionID Message) where
358 length _ = 24
359 withByteArray (TID nonce) kont = with3Word64Ptr nonce (kont . castPtr)
360
361instance ByteArrayAccess (NodeId Message) where
362 length _ = 32
363 withByteArray (NodeId nonce) kont = with4Word64Ptr nonce (kont . castPtr)
364
365
366instance Hashable (NodeId Message) where
367 hashWithSalt s (NodeId (LargeKey a (LargeKey b (LargeKey c d)))) =
368 hashWithSalt s (a,b,c,d)
369
370instance Hashable (PacketDestination Message) where
371 hashWithSalt s (ToxAddr nid addr) = hashWithSalt s nid
372
373instance Serialize (PacketDestination Message) where
374 put (ToxAddr (NodeId nid) addr) = put nid >> putSockAddr addr
375 get = ToxAddr <$> (NodeId <$> get) <*> getSockAddr
376
377instance Pretty (PacketDestination Message) where
378 pPrint = PP.text . show
379
380instance Address (PacketDestination Message) where
381 toSockAddr (ToxAddr _ addr) = addr
382 fromSockAddr _ = Nothing
383
284instance WireFormat ByteString Message where 384instance WireFormat ByteString Message where
285 type SerializableTo ByteString = Serialize 385 type SerializableTo ByteString = Serialize
286 type CipherContext ByteString Message = ToxCipherContext 386 type CipherContext ByteString Message = ToxCipherContext
@@ -289,6 +389,6 @@ instance WireFormat ByteString Message where
289 encodePayload = fmap encode 389 encodePayload = fmap encode
290 390
291 decodeHeaders ctx bs = runGet getMessage bs >>= decipher ctx 391 decodeHeaders ctx bs = runGet getMessage bs >>= decipher ctx
292 encodeHeaders ctx msg = runPut $ putMessage $ encipher ctx msg 392 encodeHeaders ctx msg recipient = runPut $ putMessage $ encipher ctx (toxID recipient) msg
293 393
294instance Read (NodeId Message) where readsPrec d s = map (\(w,xs) -> (NodeId w, xs)) $ decodeHex s 394instance Read (NodeId Message) where readsPrec d s = map (\(w,xs) -> (NodeId w, xs)) $ decodeHex s
diff --git a/src/Network/DatagramServer/Types.hs b/src/Network/DatagramServer/Types.hs
index 13f79afb..14968764 100644
--- a/src/Network/DatagramServer/Types.hs
+++ b/src/Network/DatagramServer/Types.hs
@@ -96,11 +96,21 @@ class Envelope envelope where
96 data NodeId envelope 96 data NodeId envelope
97 data QueryExtra envelope 97 data QueryExtra envelope
98 data ResponseExtra envelope 98 data ResponseExtra envelope
99 data PacketDestination envelope
99 100
100 envelopePayload :: envelope a -> a 101 envelopePayload :: envelope a -> a
101 envelopeTransaction :: envelope a -> TransactionID envelope 102 envelopeTransaction :: envelope a -> TransactionID envelope
102 envelopeClass :: envelope a -> MessageClass envelope 103 envelopeClass :: envelope a -> MessageClass envelope
103 104
105 -- | > replyAddress qry addr
106 --
107 -- [ qry ] received query message
108 --
109 -- [ addr ] SockAddr of query origin
110 --
111 -- Returns: Destination address for reply.
112 makeAddress :: Either (NodeId envelope) (envelope a) -> SockAddr -> PacketDestination envelope
113
104 -- | > buildReply self addr qry response 114 -- | > buildReply self addr qry response
105 -- 115 --
106 -- [ self ] this node's id. 116 -- [ self ] this node's id.
@@ -320,8 +330,7 @@ genBucketSample' gen self (q,m,b)
320 h = xor b (complement m .&. BS.last hd) 330 h = xor b (complement m .&. BS.last hd)
321 t = m .&. BS.head tl 331 t = m .&. BS.head tl
322 332
323 333class (Envelope envelope, Address (PacketDestination envelope)) => WireFormat raw envelope where
324class Envelope envelope => WireFormat raw envelope where
325 type SerializableTo raw :: * -> Constraint 334 type SerializableTo raw :: * -> Constraint
326 type CipherContext raw envelope 335 type CipherContext raw envelope
327 336
@@ -336,7 +345,7 @@ class Envelope envelope => WireFormat raw envelope where
336 decodeHeaders :: CipherContext raw envelope -> raw -> Either String (envelope raw) 345 decodeHeaders :: CipherContext raw envelope -> raw -> Either String (envelope raw)
337 decodePayload :: SerializableTo raw a => envelope raw -> Either String (envelope a) 346 decodePayload :: SerializableTo raw a => envelope raw -> Either String (envelope a)
338 347
339 encodeHeaders :: CipherContext raw envelope -> envelope raw -> ByteString 348 encodeHeaders :: CipherContext raw envelope -> envelope raw -> PacketDestination envelope -> ByteString
340 encodePayload :: SerializableTo raw a => envelope a -> envelope raw 349 encodePayload :: SerializableTo raw a => envelope a -> envelope raw
341 350
342encodeHexDoc :: Serialize x => x -> Doc 351encodeHexDoc :: Serialize x => x -> Doc
@@ -359,3 +368,21 @@ instance (Pretty ip, Pretty (NodeId dht)) => Pretty [NodeInfo dht ip u] where
359 pPrint = PP.vcat . PP.punctuate "," . map pPrint 368 pPrint = PP.vcat . PP.punctuate "," . map pPrint
360 369
361 370
371
372putSockAddr (SockAddrInet port addr)
373 = put (0x34 :: Word8) >> put port >> put addr
374putSockAddr (SockAddrInet6 port flow addr scope)
375 = put (0x36 :: Word8) >> put port >> put addr >> put scope >> put flow
376putSockAddr (SockAddrUnix path)
377 = put (0x75 :: Word8) >> put path
378putSockAddr (SockAddrCan num)
379 = put (0x63 :: Word8) >> put num
380
381getSockAddr = do
382 c <- get
383 case c :: Word8 of
384 0x34 -> SockAddrInet <$> get <*> get
385 0x36 -> (\p a s f -> SockAddrInet6 p f a s) <$> get <*> get <*> get <*> get
386 0x75 -> SockAddrUnix <$> get
387 0x63 -> SockAddrCan <$> get
388 _ -> fail "getSockAddr"