diff options
Diffstat (limited to 'src/Network/BitTorrent')
-rw-r--r-- | src/Network/BitTorrent/Peer.hs | 56 | ||||
-rw-r--r-- | src/Network/BitTorrent/PeerID.hs | 50 | ||||
-rw-r--r-- | src/Network/BitTorrent/PeerWire/Handshake.hs | 40 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker.hs | 1 |
4 files changed, 94 insertions, 53 deletions
diff --git a/src/Network/BitTorrent/Peer.hs b/src/Network/BitTorrent/Peer.hs new file mode 100644 index 00000000..e16329d2 --- /dev/null +++ b/src/Network/BitTorrent/Peer.hs | |||
@@ -0,0 +1,56 @@ | |||
1 | -- | | ||
2 | -- Copyright : (c) Sam T. 2013 | ||
3 | -- License : MIT | ||
4 | -- Maintainer : pxqr.sta@gmail.com | ||
5 | -- Stability : experimental | ||
6 | -- Portability : non-portable | ||
7 | -- | ||
8 | module Network.BitTorrent.Peer | ||
9 | ( Peer(..) | ||
10 | , peerSockAddr, connectToPeer | ||
11 | , ppPeer | ||
12 | ) where | ||
13 | |||
14 | import Control.Applicative | ||
15 | import Data.Word | ||
16 | import Data.Bits | ||
17 | import Network | ||
18 | import Network.Socket | ||
19 | |||
20 | import Network.BitTorrent.PeerID | ||
21 | import Network.BitTorrent.PeerWire.ClientInfo | ||
22 | |||
23 | |||
24 | data Peer = Peer { | ||
25 | peerID :: Maybe PeerID | ||
26 | , peerIP :: HostAddress | ||
27 | , peerPort :: PortNumber | ||
28 | } deriving Show | ||
29 | |||
30 | -- TODO make platform independent, clarify htonl | ||
31 | -- | Convert peer info from tracker response to socket address. | ||
32 | -- Used for establish connection between peers. | ||
33 | -- | ||
34 | peerSockAddr :: Peer -> SockAddr | ||
35 | peerSockAddr = SockAddrInet <$> (g . peerPort) <*> (htonl . peerIP) | ||
36 | where | ||
37 | htonl :: Word32 -> Word32 | ||
38 | htonl d = | ||
39 | ((d .&. 0xff) `shiftL` 24) .|. | ||
40 | (((d `shiftR` 8 ) .&. 0xff) `shiftL` 16) .|. | ||
41 | (((d `shiftR` 16) .&. 0xff) `shiftL` 8) .|. | ||
42 | ((d `shiftR` 24) .&. 0xff) | ||
43 | |||
44 | g :: PortNumber -> PortNumber | ||
45 | g = id | ||
46 | |||
47 | -- | Tries to connect to peer using reasonable default parameters. | ||
48 | connectToPeer :: Peer -> IO Socket | ||
49 | connectToPeer p = do | ||
50 | sock <- socket AF_INET Stream Network.Socket.defaultProtocol | ||
51 | connect sock (peerSockAddr p) | ||
52 | return sock | ||
53 | |||
54 | ppPeer :: Peer -> String | ||
55 | ppPeer p = maybe "" (++ " at ") ((ppClientInfo . clientInfo) <$> peerID p) | ||
56 | ++ show (peerSockAddr p) | ||
diff --git a/src/Network/BitTorrent/PeerID.hs b/src/Network/BitTorrent/PeerID.hs index cef1fa58..2c8818fe 100644 --- a/src/Network/BitTorrent/PeerID.hs +++ b/src/Network/BitTorrent/PeerID.hs | |||
@@ -8,17 +8,14 @@ | |||
8 | -- | 8 | -- |
9 | -- This module provides 'Peer' and 'PeerID' datatypes and all related | 9 | -- This module provides 'Peer' and 'PeerID' datatypes and all related |
10 | -- operations. | 10 | -- operations. |
11 | -- | ||
11 | -- Recommended method for generation of the peer ID's is 'newPeerID', | 12 | -- Recommended method for generation of the peer ID's is 'newPeerID', |
12 | -- though this module exports some other goodies for custom generation. | 13 | -- though this module exports some other goodies for custom generation. |
13 | -- | 14 | -- |
14 | {-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-} | 15 | {-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-} |
15 | module Network.BitTorrent.PeerID | 16 | module Network.BitTorrent.PeerID |
16 | ( -- * Peer addr | 17 | ( -- * Peer identification |
17 | Peer(..) | 18 | PeerID (getPeerID), ppPeerID |
18 | , peerSockAddr, connectToPeer | ||
19 | |||
20 | -- * Peer identification | ||
21 | , PeerID (getPeerID) | ||
22 | 19 | ||
23 | -- ** Encoding styles | 20 | -- ** Encoding styles |
24 | , azureusStyle, shadowStyle | 21 | , azureusStyle, shadowStyle |
@@ -34,8 +31,6 @@ module Network.BitTorrent.PeerID | |||
34 | ) where | 31 | ) where |
35 | 32 | ||
36 | import Control.Applicative | 33 | import Control.Applicative |
37 | import Data.Word | ||
38 | import Data.Bits | ||
39 | import Data.BEncode | 34 | import Data.BEncode |
40 | import Data.ByteString (ByteString) | 35 | import Data.ByteString (ByteString) |
41 | import qualified Data.ByteString as B | 36 | import qualified Data.ByteString as B |
@@ -50,8 +45,6 @@ import Data.Version (Version(Version), versionBranch) | |||
50 | import Data.Time.Clock (getCurrentTime) | 45 | import Data.Time.Clock (getCurrentTime) |
51 | import Data.Time.Format (formatTime) | 46 | import Data.Time.Format (formatTime) |
52 | import System.Locale (defaultTimeLocale) | 47 | import System.Locale (defaultTimeLocale) |
53 | import Network | ||
54 | import Network.Socket | ||
55 | 48 | ||
56 | 49 | ||
57 | -- TODO we have linker error here, so manual hardcoded version for a while. | 50 | -- TODO we have linker error here, so manual hardcoded version for a while. |
@@ -60,40 +53,6 @@ version :: Version | |||
60 | version = Version [0, 10, 0, 0] [] | 53 | version = Version [0, 10, 0, 0] [] |
61 | 54 | ||
62 | 55 | ||
63 | |||
64 | data Peer = Peer { | ||
65 | peerID :: Maybe PeerID | ||
66 | , peerIP :: HostAddress | ||
67 | , peerPort :: PortNumber | ||
68 | } deriving Show | ||
69 | |||
70 | -- TODO make platform independent, clarify htonl | ||
71 | -- | Convert peer info from tracker response to socket address. | ||
72 | -- Used for establish connection between peers. | ||
73 | -- | ||
74 | peerSockAddr :: Peer -> SockAddr | ||
75 | peerSockAddr = SockAddrInet <$> (g . peerPort) <*> (htonl . peerIP) | ||
76 | where | ||
77 | htonl :: Word32 -> Word32 | ||
78 | htonl d = | ||
79 | ((d .&. 0xff) `shiftL` 24) .|. | ||
80 | (((d `shiftR` 8 ) .&. 0xff) `shiftL` 16) .|. | ||
81 | (((d `shiftR` 16) .&. 0xff) `shiftL` 8) .|. | ||
82 | ((d `shiftR` 24) .&. 0xff) | ||
83 | |||
84 | g :: PortNumber -> PortNumber | ||
85 | g = id | ||
86 | |||
87 | -- ipv6 extension | ||
88 | -- | Tries to connect to peer using reasonable default parameters. | ||
89 | -- | ||
90 | connectToPeer :: Peer -> IO Socket | ||
91 | connectToPeer p = do | ||
92 | sock <- socket AF_INET Stream Network.Socket.defaultProtocol | ||
93 | connect sock (peerSockAddr p) | ||
94 | return sock | ||
95 | |||
96 | |||
97 | -- | Peer identifier is exactly 20 bytes long bytestring. | 56 | -- | Peer identifier is exactly 20 bytes long bytestring. |
98 | newtype PeerID = PeerID { getPeerID :: ByteString } | 57 | newtype PeerID = PeerID { getPeerID :: ByteString } |
99 | deriving (Show, Eq, Ord, BEncodable) | 58 | deriving (Show, Eq, Ord, BEncodable) |
@@ -105,6 +64,9 @@ instance Serialize PeerID where | |||
105 | instance URLShow PeerID where | 64 | instance URLShow PeerID where |
106 | urlShow = BC.unpack . getPeerID | 65 | urlShow = BC.unpack . getPeerID |
107 | 66 | ||
67 | ppPeerID :: PeerID -> String | ||
68 | ppPeerID = BC.unpack . getPeerID | ||
69 | |||
108 | 70 | ||
109 | -- | Azureus-style encoding have the following layout: | 71 | -- | Azureus-style encoding have the following layout: |
110 | -- | 72 | -- |
diff --git a/src/Network/BitTorrent/PeerWire/Handshake.hs b/src/Network/BitTorrent/PeerWire/Handshake.hs index 6ce37887..a80728aa 100644 --- a/src/Network/BitTorrent/PeerWire/Handshake.hs +++ b/src/Network/BitTorrent/PeerWire/Handshake.hs | |||
@@ -11,30 +11,47 @@ module Network.BitTorrent.PeerWire.Handshake | |||
11 | , handshakeMaxSize | 11 | , handshakeMaxSize |
12 | , defaultBTProtocol, defaultReserved, defaultHandshake | 12 | , defaultBTProtocol, defaultReserved, defaultHandshake |
13 | , handshake | 13 | , handshake |
14 | , ppHandshake | ||
14 | ) where | 15 | ) where |
15 | 16 | ||
16 | import Control.Applicative | 17 | import Control.Applicative |
17 | import Data.Word | 18 | import Data.Word |
18 | import Data.ByteString (ByteString) | 19 | import Data.ByteString (ByteString) |
19 | import qualified Data.ByteString as B | 20 | import qualified Data.ByteString as B |
21 | import qualified Data.ByteString.Char8 as BC | ||
20 | import Data.Serialize as S | 22 | import Data.Serialize as S |
21 | import Data.Torrent.InfoHash | 23 | import Data.Torrent.InfoHash |
22 | import Network | 24 | import Network |
23 | import Network.Socket.ByteString | 25 | import Network.Socket.ByteString |
24 | 26 | ||
25 | import Network.BitTorrent.PeerID | 27 | import Network.BitTorrent.PeerID |
28 | import Network.BitTorrent.PeerWire.ClientInfo | ||
26 | 29 | ||
27 | 30 | ||
28 | -- | In order to establish the connection between peers we should send 'Handshake' | 31 | -- | In order to establish the connection between peers we should send |
29 | -- message. The 'Handshake' is a required message and must be the first message | 32 | -- 'Handshake' message. The 'Handshake' is a required message and |
30 | -- transmitted by the peer to the another peer. | 33 | -- must be the first message transmitted by the peer to the another |
34 | -- peer. | ||
35 | -- | ||
31 | data Handshake = Handshake { | 36 | data Handshake = Handshake { |
32 | hsProtocol :: ByteString -- ^ Identifier of the protocol. | 37 | -- ^ Identifier of the protocol. |
33 | , hsReserved :: Word64 -- ^ Reserved bytes, rarely used. | 38 | hsProtocol :: ByteString |
34 | , hsInfoHash :: InfoHash -- ^ Hash from the metainfo file. | 39 | |
35 | -- This /should be/ same hash that is transmitted in tracker requests. | 40 | -- ^ Reserved bytes used to specify supported BEP's. |
36 | , hsPeerID :: PeerID -- ^ Peer id of the initiator. | 41 | , hsReserved :: Word64 |
37 | -- This is /usually the same peer id that is transmitted in tracker requests. | 42 | |
43 | -- ^ Info hash of the info part of the metainfo file. that is | ||
44 | -- transmitted in tracker requests. Info hash of the initiator | ||
45 | -- handshake and response handshake should match, otherwise | ||
46 | -- initiator should break the connection. | ||
47 | -- | ||
48 | , hsInfoHash :: InfoHash | ||
49 | |||
50 | -- ^ Peer id of the initiator. This is usually the same peer id | ||
51 | -- that is transmitted in tracker requests. | ||
52 | -- | ||
53 | , hsPeerID :: PeerID | ||
54 | |||
38 | } deriving (Show, Eq) | 55 | } deriving (Show, Eq) |
39 | 56 | ||
40 | instance Serialize Handshake where | 57 | instance Serialize Handshake where |
@@ -52,6 +69,11 @@ instance Serialize Handshake where | |||
52 | <*> get | 69 | <*> get |
53 | <*> get | 70 | <*> get |
54 | 71 | ||
72 | -- TODO add reserved bits info | ||
73 | ppHandshake :: Handshake -> String | ||
74 | ppHandshake hs = BC.unpack (hsProtocol hs) ++ " " | ||
75 | ++ ppClientInfo (clientInfo (hsPeerID hs)) | ||
76 | |||
55 | -- | Maximum size of handshake message in bytes. | 77 | -- | Maximum size of handshake message in bytes. |
56 | handshakeMaxSize :: Int | 78 | handshakeMaxSize :: Int |
57 | handshakeMaxSize = 1 + 256 + 8 + 20 + 20 | 79 | handshakeMaxSize = 1 + 256 + 8 + 20 + 20 |
diff --git a/src/Network/BitTorrent/Tracker.hs b/src/Network/BitTorrent/Tracker.hs index 643aca16..99ffc280 100644 --- a/src/Network/BitTorrent/Tracker.hs +++ b/src/Network/BitTorrent/Tracker.hs | |||
@@ -46,6 +46,7 @@ import Network.Socket | |||
46 | import Network.HTTP | 46 | import Network.HTTP |
47 | import Network.URI | 47 | import Network.URI |
48 | 48 | ||
49 | import Network.BitTorrent.Peer | ||
49 | import Network.BitTorrent.PeerID | 50 | import Network.BitTorrent.PeerID |
50 | import Network.BitTorrent.Tracker.Scrape | 51 | import Network.BitTorrent.Tracker.Scrape |
51 | 52 | ||