1 {-# LANGUAGE OverloadedStrings #-}
2 module FuncTorrent.PeerMsgs
10 import Prelude hiding (lookup, concat, replicate, splitAt, take)
12 import System.IO (Handle)
13 import Data.ByteString (ByteString, pack, unpack, concat, hGet, hPut, singleton)
14 import Data.ByteString.Lazy (fromStrict, fromChunks, toStrict)
15 import qualified Data.ByteString.Char8 as BC (replicate, pack)
16 import Control.Monad (replicateM, liftM)
17 import Control.Applicative (liftA3)
19 import Data.Binary (Binary(..), decode, encode)
20 import Data.Binary.Put (putWord32be, putWord16be, putWord8)
21 import Data.Binary.Get (getWord32be, getWord16be, getWord8, runGet)
23 -- | Peer is a PeerID, IP address, port tuple
24 data Peer = Peer ID IP Port
31 data PeerMsg = KeepAliveMsg
37 | BitFieldMsg ByteString
38 | RequestMsg Integer Integer Integer
39 | PieceMsg Integer Integer ByteString
40 | CancelMsg Integer Integer Integer
44 instance Binary PeerMsg where
46 KeepAliveMsg -> putWord32be 0
47 ChokeMsg -> do putWord32be 1
49 UnChokeMsg -> do putWord32be 1
51 InterestedMsg -> do putWord32be 1
53 NotInterestedMsg -> do putWord32be 1
55 HaveMsg i -> do putWord32be 5
57 putWord32be (fromIntegral i)
58 BitFieldMsg bf -> do putWord32be $ fromIntegral (1 + bfListLen)
61 where bfList = unpack bf
62 bfListLen = length bfList
63 RequestMsg i o l -> do putWord32be 13
65 putWord32be (fromIntegral i)
66 putWord32be (fromIntegral o)
67 putWord32be (fromIntegral l)
68 PieceMsg i o b -> do putWord32be $ fromIntegral (9 + blocklen)
70 putWord32be (fromIntegral i)
71 putWord32be (fromIntegral o)
72 mapM_ putWord8 blockList
73 where blockList = unpack b
74 blocklen = length blockList
75 CancelMsg i o l -> do putWord32be 13
77 putWord32be (fromIntegral i)
78 putWord32be (fromIntegral o)
79 putWord32be (fromIntegral l)
80 PortMsg p -> do putWord32be 3
82 putWord16be (fromIntegral p)
88 1 -> return UnChokeMsg
89 2 -> return InterestedMsg
90 3 -> return NotInterestedMsg
91 4 -> liftM (HaveMsg . fromIntegral) getWord32be
92 5 -> liftM (BitFieldMsg . pack) (replicateM (fromIntegral l - 1) getWord8)
93 6 -> liftA3 RequestMsg getInteger getInteger getInteger
94 where getInteger = fromIntegral <$> getWord32be
95 7 -> liftA3 PieceMsg getInteger getInteger (pack <$> replicateM (fromIntegral l - 9) getWord8)
96 where getInteger = fromIntegral <$> getWord32be
97 8 -> liftA3 CancelMsg getInteger getInteger getInteger
98 where getInteger = fromIntegral <$> getWord32be
99 9 -> liftM (PortMsg . fromIntegral) getWord16be
100 _ -> error ("unknown message ID: " ++ show msgid)
102 getMsg :: Handle -> IO PeerMsg
107 then return KeepAliveMsg
110 return $ decode $ fromStrict $ concat [lBS, msg]
112 sendMsg :: Handle -> PeerMsg -> IO ()
113 sendMsg h msg = hPut h bsMsg
114 where bsMsg = toStrict $ encode msg
116 genHandshakeMsg :: ByteString -> String -> ByteString
117 genHandshakeMsg infoHash peer_id = concat [pstrlen, pstr, reserved, infoHash, peerID]
118 where pstrlen = singleton 19
119 pstr = BC.pack "BitTorrent protocol"
120 reserved = BC.replicate 8 '\0'
121 peerID = BC.pack peer_id
123 bsToInt :: ByteString -> Int
124 bsToInt x = fromIntegral (runGet getWord32be (fromChunks (return x)))