1 {-# LANGUAGE OverloadedStrings #-}
2 module FuncTorrent.Peer
7 import Prelude hiding (lookup, concat, replicate, splitAt)
10 import Data.ByteString (ByteString, unpack, concat, hGet, hPut, singleton)
11 import Data.ByteString.Char8 (replicate, pack)
12 import Network (connectTo, PortID(..))
13 import Data.Binary (Binary(..))
14 import Data.Binary.Put (putWord32be, putWord8)
20 data PeerState = PeerState { handle :: Handle
22 , am_interested :: Bool
23 , peer_choking :: Bool
24 , peer_interested :: Bool }
26 -- Maintain info on every piece and the current state of it.
27 -- should probably be a TVar.
28 type Pieces = [PieceData]
30 data PieceState = Pending
35 data PieceData = PieceData { index :: Int -- ^ Piece number
36 , peers :: [Peer] -- ^ list of peers who have this piece
37 , state :: PieceState } -- ^ state of the piece from download perspective.
39 -- | Peer is a PeerID, IP address, port tuple
40 data Peer = Peer ID IP Port
43 data PeerMsg = KeepAliveMsg
49 | BitFieldMsg ByteString
50 | RequestMsg Integer Integer Integer
51 | PieceMsg Integer Integer ByteString
52 | CancelMsg Integer Integer Integer
56 genHandShakeMsg :: ByteString -> String -> ByteString
57 genHandShakeMsg infoHash peer_id = concat [pstrlen, pstr, reserved, infoHash, peerID]
58 where pstrlen = singleton 19
59 pstr = pack "BitTorrent protocol"
60 reserved = replicate 8 '\0'
63 handShake :: Peer -> ByteString -> String -> IO Handle
64 handShake (Peer _ ip port) infoHash peerid = do
65 let hs = genHandShakeMsg infoHash peerid
66 handle <- connectTo ip (PortNumber (fromIntegral port))
67 hSetBuffering handle LineBuffering
69 rlenBS <- hGet handle 1
70 let rlen = fromIntegral $ (unpack rlenBS) !! 0
74 instance Binary PeerMsg where
76 KeepAliveMsg -> putWord32be 0
77 ChokeMsg -> do putWord32be 1
79 UnChokeMsg -> do putWord32be 1
81 InterestedMsg -> do putWord32be 1
83 NotInterestedMsg -> do putWord32be 1
85 HaveMsg index -> do putWord32be 5
87 putWord32be (fromIntegral index)
88 BitFieldMsg bf -> do putWord32be $ fromIntegral (1 + bfListLen)
91 where bfList = unpack bf
92 bfListLen = length bfList
93 RequestMsg i o l -> do putWord32be 13
95 putWord32be (fromIntegral i)
96 putWord32be (fromIntegral o)
97 putWord32be (fromIntegral l)
98 PieceMsg i o b -> do putWord32be $ fromIntegral (9 + blocklen)
100 putWord32be (fromIntegral i)
101 putWord32be (fromIntegral o)
102 mapM_ putWord8 blockList
103 where blockList = unpack b
104 blocklen = length blockList
105 CancelMsg i o l -> undefined
106 PortMsg p -> undefined
109 -- loop1 :: shake hands with all peers, find out the pieces they have, form PieceData.
110 -- recvMsg :: Peer -> Handle -> Msg