1 {-# LANGUAGE OverloadedStrings #-}
2 module FuncTorrent.Peer
7 import Prelude hiding (lookup, concat, replicate, splitAt)
10 import Data.ByteString (ByteString, pack, unpack, concat, hGet, hPut, singleton, append)
11 import Data.ByteString.Lazy (fromStrict)
12 import qualified Data.ByteString.Char8 as BC (replicate, pack, readInt)
13 import Network (connectTo, PortID(..))
14 import Data.Binary (Binary(..), decode)
15 import Data.Binary.Put (putWord32be, putWord16be, putWord8)
16 import Data.Binary.Get (getWord32be, getWord16be, getWord8)
17 import Control.Monad (replicateM, liftM)
18 import Control.Applicative ((<$>), liftA3)
24 data PeerState = PeerState { handle :: Handle
26 , am_interested :: Bool
27 , peer_choking :: Bool
28 , peer_interested :: Bool}
30 -- Maintain info on every piece and the current state of it.
31 -- should probably be a TVar.
32 type Pieces = [PieceData]
34 data PieceState = Pending
39 data PieceData = PieceData { index :: Int -- ^ Piece number
40 , peers :: [Peer] -- ^ list of peers who have this piece
41 , state :: PieceState } -- ^ state of the piece from download perspective.
43 -- | Peer is a PeerID, IP address, port tuple
44 data Peer = Peer ID IP Port
47 data PeerMsg = KeepAliveMsg
53 | BitFieldMsg ByteString
54 | RequestMsg Integer Integer Integer
55 | PieceMsg Integer Integer ByteString
56 | CancelMsg Integer Integer Integer
60 genHandShakeMsg :: ByteString -> String -> ByteString
61 genHandShakeMsg infoHash peer_id = concat [pstrlen, pstr, reserved, infoHash, peerID]
62 where pstrlen = singleton 19
63 pstr = BC.pack "BitTorrent protocol"
64 reserved = BC.replicate 8 '\0'
65 peerID = BC.pack peer_id
67 handShake :: Peer -> ByteString -> String -> IO Handle
68 handShake (Peer _ ip port) infoHash peerid = do
69 let hs = genHandShakeMsg infoHash peerid
70 h <- connectTo ip (PortNumber (fromIntegral port))
71 hSetBuffering h LineBuffering
74 let rlen = fromIntegral $ (unpack rlenBS) !! 0
78 instance Binary PeerMsg where
80 KeepAliveMsg -> putWord32be 0
81 ChokeMsg -> do putWord32be 1
83 UnChokeMsg -> do putWord32be 1
85 InterestedMsg -> do putWord32be 1
87 NotInterestedMsg -> do putWord32be 1
89 HaveMsg i -> do putWord32be 5
91 putWord32be (fromIntegral i)
92 BitFieldMsg bf -> do putWord32be $ fromIntegral (1 + bfListLen)
95 where bfList = unpack bf
96 bfListLen = length bfList
97 RequestMsg i o l -> do putWord32be 13
99 putWord32be (fromIntegral i)
100 putWord32be (fromIntegral o)
101 putWord32be (fromIntegral l)
102 PieceMsg i o b -> do putWord32be $ fromIntegral (9 + blocklen)
104 putWord32be (fromIntegral i)
105 putWord32be (fromIntegral o)
106 mapM_ putWord8 blockList
107 where blockList = unpack b
108 blocklen = length blockList
109 CancelMsg i o l -> do putWord32be 13
111 putWord32be (fromIntegral i)
112 putWord32be (fromIntegral o)
113 putWord32be (fromIntegral l)
114 PortMsg p -> do putWord32be 3
116 putWord16be (fromIntegral p)
122 1 -> return UnChokeMsg
123 2 -> return InterestedMsg
124 3 -> return NotInterestedMsg
125 4 -> liftM (HaveMsg . fromIntegral) getWord32be
126 5 -> liftM (BitFieldMsg . pack) (replicateM (fromIntegral l - 1) getWord8)
127 6 -> liftA3 RequestMsg getInteger getInteger getInteger
128 where getInteger = fromIntegral <$> getWord32be
129 7 -> liftA3 PieceMsg getInteger getInteger (pack <$> replicateM (fromIntegral l - 9) getWord8)
130 where getInteger = fromIntegral <$> getWord32be
131 8 -> liftA3 CancelMsg getInteger getInteger getInteger
132 where getInteger = fromIntegral <$> getWord32be
133 9 -> liftM (PortMsg . fromIntegral) getWord16be
134 _ -> error "unknown message ID"
136 getMsg :: Handle -> IO PeerMsg
139 let (Just (l, _)) = BC.readInt lBS
141 return $ decode $ fromStrict $ append lBS msg
144 -- loop1 :: shake hands with all peers, find out the pieces they have, form PieceData.
145 -- recvMsg :: Peer -> Handle -> Msg