summaryrefslogtreecommitdiff
path: root/src/Network
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-12-27 12:58:17 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-12-27 12:58:17 +0400
commit1e22d4d33ad6b7da93f7fd1fd757f10d74d98309 (patch)
tree5a95c01d10ecb44d49af6e698ae42920218eedf9 /src/Network
parent12c32c9b6c25c9768b01cca583061f19908d7151 (diff)
Add naive session tokens implementation
Diffstat (limited to 'src/Network')
-rw-r--r--src/Network/BitTorrent/Core/Node.hs8
-rw-r--r--src/Network/BitTorrent/Core/PeerAddr.hs5
-rw-r--r--src/Network/BitTorrent/DHT/Message.hs4
-rw-r--r--src/Network/BitTorrent/DHT/Token.hs91
4 files changed, 104 insertions, 4 deletions
diff --git a/src/Network/BitTorrent/Core/Node.hs b/src/Network/BitTorrent/Core/Node.hs
index f6ab7d82..f163a895 100644
--- a/src/Network/BitTorrent/Core/Node.hs
+++ b/src/Network/BitTorrent/Core/Node.hs
@@ -39,6 +39,7 @@ import Data.Bits
39import Data.ByteString as BS 39import Data.ByteString as BS
40import Data.BEncode as BE 40import Data.BEncode as BE
41import Data.Default 41import Data.Default
42import Data.Hashable
42import Data.IP 43import Data.IP
43import Data.List as L 44import Data.List as L
44import Data.Ord 45import Data.Ord
@@ -126,6 +127,10 @@ instance BEncode a => BEncode (NodeAddr a) where
126 fromBEncode b = uncurry NodeAddr <$> fromBEncode b 127 fromBEncode b = uncurry NodeAddr <$> fromBEncode b
127 {-# INLINE fromBEncode #-} 128 {-# INLINE fromBEncode #-}
128 129
130instance Hashable a => Hashable (NodeAddr a) where
131 hashWithSalt s NodeAddr {..} = hashWithSalt s (nodeHost, nodePort)
132 {-# INLINE hashWithSalt #-}
133
129-- | Example: 134-- | Example:
130-- 135--
131-- @nodePort \"127.0.0.1:6881\" == 6881@ 136-- @nodePort \"127.0.0.1:6881\" == 6881@
@@ -160,7 +165,7 @@ instance Eq a => Ord (NodeInfo a) where
160instance Serialize a => Serialize (NodeInfo a) where 165instance Serialize a => Serialize (NodeInfo a) where
161 get = NodeInfo <$> get <*> get 166 get = NodeInfo <$> get <*> get
162 put NodeInfo {..} = put nodeId >> put nodeAddr 167 put NodeInfo {..} = put nodeId >> put nodeAddr
163 168{-
164type CompactInfo = ByteString 169type CompactInfo = ByteString
165 170
166data NodeList a = CompactNodeList [NodeInfo a] 171data NodeList a = CompactNodeList [NodeInfo a]
@@ -176,3 +181,4 @@ encodeCompact = S.runPut . mapM_ put
176 181
177--encodePeerList :: [PeerAddr] -> [BEncode] 182--encodePeerList :: [PeerAddr] -> [BEncode]
178--encodePeerList = undefined 183--encodePeerList = undefined
184-} \ No newline at end of file
diff --git a/src/Network/BitTorrent/Core/PeerAddr.hs b/src/Network/BitTorrent/Core/PeerAddr.hs
index 261df8d0..9f5c7c5d 100644
--- a/src/Network/BitTorrent/Core/PeerAddr.hs
+++ b/src/Network/BitTorrent/Core/PeerAddr.hs
@@ -38,6 +38,7 @@ import Data.BEncode.BDict (BKey)
38import Data.ByteString.Char8 as BS8 38import Data.ByteString.Char8 as BS8
39import Data.Char 39import Data.Char
40import Data.Default 40import Data.Default
41import Data.Hashable
41import Data.HashMap.Strict as HM 42import Data.HashMap.Strict as HM
42import Data.IP 43import Data.IP
43import Data.List as L 44import Data.List as L
@@ -81,6 +82,10 @@ instance Serialize PortNumber where
81 put = putWord16be . fromIntegral 82 put = putWord16be . fromIntegral
82 {-# INLINE put #-} 83 {-# INLINE put #-}
83 84
85instance Hashable PortNumber where
86 hashWithSalt s = hashWithSalt s . fromEnum
87 {-# INLINE hashWithSalt #-}
88
84{----------------------------------------------------------------------- 89{-----------------------------------------------------------------------
85-- IP addr 90-- IP addr
86-----------------------------------------------------------------------} 91-----------------------------------------------------------------------}
diff --git a/src/Network/BitTorrent/DHT/Message.hs b/src/Network/BitTorrent/DHT/Message.hs
index 49629755..83413752 100644
--- a/src/Network/BitTorrent/DHT/Message.hs
+++ b/src/Network/BitTorrent/DHT/Message.hs
@@ -30,7 +30,6 @@ module Network.BitTorrent.DHT.Message
30import Control.Applicative 30import Control.Applicative
31import Data.BEncode as BE 31import Data.BEncode as BE
32import Data.BEncode.BDict 32import Data.BEncode.BDict
33import Data.ByteString as BS
34import Data.List as L 33import Data.List as L
35import Data.Monoid 34import Data.Monoid
36import Data.Serialize as S 35import Data.Serialize as S
@@ -40,14 +39,13 @@ import Network.KRPC
40 39
41import Data.Torrent.InfoHash 40import Data.Torrent.InfoHash
42import Network.BitTorrent.Core 41import Network.BitTorrent.Core
42import Network.BitTorrent.DHT.Token
43import Network.KRPC () 43import Network.KRPC ()
44 44
45{----------------------------------------------------------------------- 45{-----------------------------------------------------------------------
46-- envelopes 46-- envelopes
47-----------------------------------------------------------------------} 47-----------------------------------------------------------------------}
48 48
49type Token = ByteString
50
51node_id_key :: BKey 49node_id_key :: BKey
52node_id_key = "id" 50node_id_key = "id"
53 51
diff --git a/src/Network/BitTorrent/DHT/Token.hs b/src/Network/BitTorrent/DHT/Token.hs
new file mode 100644
index 00000000..6705f932
--- /dev/null
+++ b/src/Network/BitTorrent/DHT/Token.hs
@@ -0,0 +1,91 @@
1-- | The return value for a query for peers includes an opaque value
2-- known as the \"token.\" For a node to announce that its controlling
3-- peer is downloading a torrent, it must present the token received
4-- from the same queried node in a recent query for peers. When a node
5-- attempts to "announce" a torrent, the queried node checks the token
6-- against the querying node's IP address. This is to prevent
7-- malicious hosts from signing up other hosts for torrents. Since the
8-- token is merely returned by the querying node to the same node it
9-- received the token from, the implementation is not defined. Tokens
10-- must be accepted for a reasonable amount of time after they have
11-- been distributed.
12--
13{-# LANGUAGE GeneralizedNewtypeDeriving #-}
14module Network.BitTorrent.DHT.Token
15 ( Token
16 , TokenMap
17 , Network.BitTorrent.DHT.Token.tokens
18 , Network.BitTorrent.DHT.Token.lookup
19 , Network.BitTorrent.DHT.Token.member
20 , Network.BitTorrent.DHT.Token.updateInterval
21 , Network.BitTorrent.DHT.Token.update
22 ) where
23
24import Control.Applicative
25import Control.Monad.State
26import Data.BEncode (BEncode)
27import Data.ByteString as BS
28import Data.ByteString.Lazy as BL
29import Data.ByteString.Lazy.Builder as BS
30import Data.Default
31import Data.List as L
32import Data.Hashable
33import Data.String
34import Data.Time
35import System.Random
36
37import Network.BitTorrent.Core
38
39
40type Secret = Int
41
42-- TODO use ShortByteString
43newtype Token = Token BS.ByteString
44 deriving (Show, Eq, BEncode, IsString)
45
46-- | Meaningless token, for testsing purposes only.
47instance Default Token where
48 def = Token "0xdeadbeef"
49
50-- The BitTorrent implementation uses the SHA1 hash of the IP address
51-- concatenated onto a secret, we use hashable instead.
52makeToken :: Hashable a => NodeAddr a -> Secret -> Token
53makeToken n s = Token $ toBS $ hashWithSalt s n
54 where
55 toBS = toStrict . toLazyByteString . int64BE . fromIntegral
56
57-- | Constant space token map based on secret.
58data TokenMap = TokenMap
59 { prevSecret :: {-# UNPACK #-} !Secret
60 , curSecret :: {-# UNPACK #-} !Secret
61 , generator :: {-# UNPACK #-} !StdGen
62 } deriving Show
63
64tokens :: Int -> TokenMap
65tokens seed = (`evalState` mkStdGen seed) $
66 TokenMap <$> state next
67 <*> state next
68 <*> get
69
70lookup :: Hashable a => NodeAddr a -> TokenMap -> Token
71lookup addr TokenMap {..} = makeToken addr curSecret
72
73-- | If token is not set 'Network.KRPC.ProtocolError' should be sent
74-- back.
75member :: Hashable a => NodeAddr a -> Token -> TokenMap -> Bool
76member addr token TokenMap {..} = token `L.elem` valid
77 where valid = makeToken addr <$> [curSecret, prevSecret]
78
79-- Secret changes every five minutes and tokens up to ten minutes old
80-- are accepted.
81updateInterval :: NominalDiffTime
82updateInterval = 5 * 60
83
84update :: TokenMap -> TokenMap
85update TokenMap {..} = TokenMap
86 { prevSecret = curSecret
87 , curSecret = newSecret
88 , generator = newGen
89 }
90 where
91 (newSecret, newGen) = next generator \ No newline at end of file