From 5a0f0863eadb27b901d3b2c551f26f2864871299 Mon Sep 17 00:00:00 2001 From: joe Date: Wed, 26 Jul 2017 05:11:57 -0400 Subject: Read instance for Mainline NodeInfo. --- Mainline.hs | 87 ++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 29 deletions(-) (limited to 'Mainline.hs') diff --git a/Mainline.hs b/Mainline.hs index b24f847d..30e18a09 100644 --- a/Mainline.hs +++ b/Mainline.hs @@ -16,65 +16,67 @@ import Control.Arrow import Control.Concurrent.STM import Control.Monad import Crypto.Random -import Data.BEncode as BE -import qualified Data.BEncode.BDict as BE - ;import Data.BEncode.BDict (BKey) +import Data.BEncode as BE +import qualified Data.BEncode.BDict as BE + ;import Data.BEncode.BDict (BKey) import Data.BEncode.Pretty -import Data.BEncode.Types (BDict) +import Data.BEncode.Types (BDict) import Data.Bits import Data.Bits.ByteString import Data.Bool -import qualified Data.ByteArray as BA - ;import Data.ByteArray (ByteArrayAccess) -import qualified Data.ByteString as B - ;import Data.ByteString (ByteString) -import qualified Data.ByteString.Base16 as Base16 -import qualified Data.ByteString.Char8 as Char8 -import Data.ByteString.Lazy (toStrict) -import qualified Data.ByteString.Lazy.Char8 as L8 +import qualified Data.ByteArray as BA + ;import Data.ByteArray (ByteArrayAccess) +import qualified Data.ByteString as B + ;import Data.ByteString (ByteString) +import qualified Data.ByteString.Base16 as Base16 +import qualified Data.ByteString.Char8 as Char8 +import Data.ByteString.Lazy (toStrict) +import qualified Data.ByteString.Lazy.Char8 as L8 +import Data.Char import Data.Coerce import Data.Data import Data.Default import Data.Digest.CRC32C -import Data.Function (fix) +import Data.Function (fix) import Data.Hashable import Data.IP import Data.List import Data.Maybe import Data.Monoid import Data.Ord -import qualified Data.Serialize as S -import Data.Set (Set) -import Data.Time.Clock.POSIX (POSIXTime) +import qualified Data.Serialize as S +import Data.Set (Set) +import Data.Time.Clock.POSIX (POSIXTime) import Data.Torrent import Data.Typeable import Data.Word -import qualified Data.Wrapper.PSQInt as Int +import qualified Data.Wrapper.PSQInt as Int import Debug.Trace import Kademlia -import Network.Address (Address, fromAddr, fromSockAddr, - setPort, sockAddrPort, testIdBit, - toSockAddr) -import Network.BitTorrent.DHT.ContactInfo as Peers -import Network.BitTorrent.DHT.Search (Search (..)) -import Network.BitTorrent.DHT.Token as Token -import Network.DatagramServer.Types (genBucketSample') -import qualified Network.DHT.Routing as R - ;import Network.DHT.Routing (Info, Timestamp, getTimestamp) +import Network.Address (Address, fromAddr, fromSockAddr, + setPort, sockAddrPort, testIdBit, + toSockAddr) +import Network.BitTorrent.DHT.ContactInfo as Peers +import Network.BitTorrent.DHT.Search (Search (..)) +import Network.BitTorrent.DHT.Token as Token +import Network.DatagramServer.Types (genBucketSample') +import qualified Network.DHT.Routing as R + ;import Network.DHT.Routing (Info, Timestamp, getTimestamp) import Network.QueryResponse import Network.Socket import System.IO import System.IO.Error -import System.IO.Unsafe (unsafeInterleaveIO) +import System.IO.Unsafe (unsafeInterleaveIO) +import qualified Text.ParserCombinators.ReadP as RP #ifdef THREAD_DEBUG import Control.Concurrent.Lifted.Instrument #else import Control.Concurrent.Lifted import GHC.Conc (labelThread) #endif -import Control.Exception (SomeException(..),handle) -import Data.Aeson (FromJSON,ToJSON,(.=)) +import Control.Exception (SomeException (..), handle) import qualified Data.Aeson as JSON + ;import Data.Aeson (FromJSON, ToJSON, (.=)) import Text.Read newtype NodeId = NodeId ByteString @@ -129,6 +131,33 @@ instance FromJSON NodeInfo where guard (B.length bs == 20) return $ NodeInfo (NodeId bs) ip (fromIntegral (portnum :: Word16)) +hexdigit :: Char -> Bool +hexdigit c = ('0' <= c && c <= '9') || ( 'a' <= c && c <= 'f') || ( 'A' <= c && c <= 'F') + +instance Read NodeInfo where + readsPrec i = RP.readP_to_S $ do + RP.skipSpaces + let n = 40 + hexhash <- sequence $ replicate n (RP.satisfy hexdigit) + RP.char '@' RP.+++ RP.satisfy isSpace + addrstr <- RP.between (RP.char '(') (RP.char ')') (RP.munch (/=')')) + RP.+++ RP.munch (not . isSpace) + let raddr = do + -- TODO: Support IPv6 + ipv4 <- RP.readS_to_P (readsPrec i) + _ <- RP.char ':' + port <- toEnum <$> RP.readS_to_P (readsPrec i) + return (IPv4 ipv4, port) + + (ip,port) <- case RP.readP_to_S raddr addrstr of + [] -> fail "Bad address." + ((ip,port),_):_ -> return (ip,port) + nid <- case Base16.decode $ Char8.pack hexhash of + (bs,_) | B.length bs==20 -> return (NodeId bs) + _ -> fail "Bad node id." + return $ NodeInfo nid ip port + + -- The Hashable instance depends only on the IP address and port number. It is -- used to compute the announce token. -- cgit v1.2.3