diff options
Diffstat (limited to 'src/Network')
-rw-r--r-- | src/Network/BitTorrent/Exchange/Protocol.hs | 162 |
1 files changed, 122 insertions, 40 deletions
diff --git a/src/Network/BitTorrent/Exchange/Protocol.hs b/src/Network/BitTorrent/Exchange/Protocol.hs index 4cf4685d..83774f06 100644 --- a/src/Network/BitTorrent/Exchange/Protocol.hs +++ b/src/Network/BitTorrent/Exchange/Protocol.hs | |||
@@ -72,9 +72,14 @@ import qualified Data.ByteString as B | |||
72 | import qualified Data.ByteString.Char8 as BC | 72 | import qualified Data.ByteString.Char8 as BC |
73 | import qualified Data.ByteString.Lazy as Lazy | 73 | import qualified Data.ByteString.Lazy as Lazy |
74 | import Data.Default | 74 | import Data.Default |
75 | import Data.Serialize as S | ||
76 | import Data.Int | 75 | import Data.Int |
77 | import Data.Word | 76 | import Data.Word |
77 | |||
78 | import Data.Binary as B | ||
79 | import Data.Binary.Get as B | ||
80 | import Data.Binary.Put as B | ||
81 | import Data.Serialize as S | ||
82 | |||
78 | import Text.PrettyPrint | 83 | import Text.PrettyPrint |
79 | 84 | ||
80 | import Network | 85 | import Network |
@@ -117,18 +122,18 @@ data Handshake = Handshake { | |||
117 | 122 | ||
118 | instance Serialize Handshake where | 123 | instance Serialize Handshake where |
119 | put hs = do | 124 | put hs = do |
120 | putWord8 (fromIntegral (B.length (hsProtocol hs))) | 125 | S.putWord8 (fromIntegral (B.length (hsProtocol hs))) |
121 | putByteString (hsProtocol hs) | 126 | S.putByteString (hsProtocol hs) |
122 | putWord64be (hsReserved hs) | 127 | S.putWord64be (hsReserved hs) |
123 | put (hsInfoHash hs) | 128 | S.put (hsInfoHash hs) |
124 | put (hsPeerID hs) | 129 | S.put (hsPeerID hs) |
125 | 130 | ||
126 | get = do | 131 | get = do |
127 | len <- getWord8 | 132 | len <- S.getWord8 |
128 | Handshake <$> getBytes (fromIntegral len) | 133 | Handshake <$> S.getBytes (fromIntegral len) |
129 | <*> getWord64be | 134 | <*> S.getWord64be |
130 | <*> get | 135 | <*> S.get |
131 | <*> get | 136 | <*> S.get |
132 | 137 | ||
133 | -- | Extract capabilities from a peer handshake message. | 138 | -- | Extract capabilities from a peer handshake message. |
134 | handshakeCaps :: Handshake -> Capabilities | 139 | handshakeCaps :: Handshake -> Capabilities |
@@ -204,14 +209,22 @@ data BlockIx = BlockIx { | |||
204 | , ixLength :: {-# UNPACK #-} !Int | 209 | , ixLength :: {-# UNPACK #-} !Int |
205 | } deriving (Show, Eq) | 210 | } deriving (Show, Eq) |
206 | 211 | ||
207 | getInt :: Get Int | 212 | getInt :: S.Get Int |
208 | getInt = fromIntegral <$> getWord32be | 213 | getInt = fromIntegral <$> S.getWord32be |
209 | {-# INLINE getInt #-} | 214 | {-# INLINE getInt #-} |
210 | 215 | ||
211 | putInt :: Putter Int | 216 | putInt :: S.Putter Int |
212 | putInt = putWord32be . fromIntegral | 217 | putInt = S.putWord32be . fromIntegral |
213 | {-# INLINE putInt #-} | 218 | {-# INLINE putInt #-} |
214 | 219 | ||
220 | getIntB :: B.Get Int | ||
221 | getIntB = fromIntegral <$> B.getWord32be | ||
222 | {-# INLINE getIntB #-} | ||
223 | |||
224 | putIntB :: Int -> B.Put | ||
225 | putIntB = B.putWord32be . fromIntegral | ||
226 | {-# INLINE putIntB #-} | ||
227 | |||
215 | instance Serialize BlockIx where | 228 | instance Serialize BlockIx where |
216 | {-# SPECIALIZE instance Serialize BlockIx #-} | 229 | {-# SPECIALIZE instance Serialize BlockIx #-} |
217 | get = BlockIx <$> getInt <*> getInt <*> getInt | 230 | get = BlockIx <$> getInt <*> getInt <*> getInt |
@@ -222,6 +235,16 @@ instance Serialize BlockIx where | |||
222 | putInt (ixLength i) | 235 | putInt (ixLength i) |
223 | {-# INLINE put #-} | 236 | {-# INLINE put #-} |
224 | 237 | ||
238 | instance Binary BlockIx where | ||
239 | {-# SPECIALIZE instance Binary BlockIx #-} | ||
240 | get = BlockIx <$> getIntB <*> getIntB <*> getIntB | ||
241 | {-# INLINE get #-} | ||
242 | |||
243 | put BlockIx {..} = do | ||
244 | putIntB ixPiece | ||
245 | putIntB ixOffset | ||
246 | putIntB ixLength | ||
247 | |||
225 | -- | Format block index in human readable form. | 248 | -- | Format block index in human readable form. |
226 | ppBlockIx :: BlockIx -> Doc | 249 | ppBlockIx :: BlockIx -> Doc |
227 | ppBlockIx BlockIx {..} = | 250 | ppBlockIx BlockIx {..} = |
@@ -355,61 +378,120 @@ instance Serialize Message where | |||
355 | -- _ <- lookAhead $ ensure len | 378 | -- _ <- lookAhead $ ensure len |
356 | if len == 0 then return KeepAlive | 379 | if len == 0 then return KeepAlive |
357 | else do | 380 | else do |
358 | mid <- getWord8 | 381 | mid <- S.getWord8 |
359 | case mid of | 382 | case mid of |
360 | 0x00 -> return Choke | 383 | 0x00 -> return Choke |
361 | 0x01 -> return Unchoke | 384 | 0x01 -> return Unchoke |
362 | 0x02 -> return Interested | 385 | 0x02 -> return Interested |
363 | 0x03 -> return NotInterested | 386 | 0x03 -> return NotInterested |
364 | 0x04 -> Have <$> getInt | 387 | 0x04 -> Have <$> getInt |
365 | 0x05 -> (Bitfield . fromBitmap) <$> getByteString (pred len) | 388 | 0x05 -> (Bitfield . fromBitmap) <$> S.getByteString (pred len) |
366 | 0x06 -> Request <$> get | 389 | 0x06 -> Request <$> S.get |
367 | 0x07 -> Piece <$> getBlock (len - 9) | 390 | 0x07 -> Piece <$> getBlock (len - 9) |
368 | 0x08 -> Cancel <$> get | 391 | 0x08 -> Cancel <$> S.get |
369 | 0x09 -> (Port . fromIntegral) <$> getWord16be | 392 | 0x09 -> (Port . fromIntegral) <$> S.getWord16be |
370 | 0x0E -> return HaveAll | 393 | 0x0E -> return HaveAll |
371 | 0x0F -> return HaveNone | 394 | 0x0F -> return HaveNone |
372 | 0x0D -> SuggestPiece <$> getInt | 395 | 0x0D -> SuggestPiece <$> getInt |
373 | 0x10 -> RejectRequest <$> get | 396 | 0x10 -> RejectRequest <$> S.get |
374 | 0x11 -> AllowedFast <$> getInt | 397 | 0x11 -> AllowedFast <$> getInt |
375 | _ -> do | 398 | _ -> do |
376 | rm <- remaining >>= getBytes | 399 | rm <- S.remaining >>= S.getBytes |
377 | fail $ "unknown message ID: " ++ show mid ++ "\n" | 400 | fail $ "unknown message ID: " ++ show mid ++ "\n" |
378 | ++ "remaining available bytes: " ++ show rm | 401 | ++ "remaining available bytes: " ++ show rm |
379 | 402 | ||
380 | where | 403 | where |
381 | getBlock :: Int -> Get Block | 404 | getBlock :: Int -> S.Get Block |
382 | getBlock len = Block <$> getInt <*> getInt <*> getBytes len | 405 | getBlock len = Block <$> getInt <*> getInt <*> S.getBytes len |
383 | {-# INLINE getBlock #-} | 406 | {-# INLINE getBlock #-} |
384 | 407 | ||
385 | 408 | ||
386 | put KeepAlive = putInt 0 | 409 | put KeepAlive = putInt 0 |
387 | put Choke = putInt 1 >> putWord8 0x00 | 410 | put Choke = putInt 1 >> S.putWord8 0x00 |
388 | put Unchoke = putInt 1 >> putWord8 0x01 | 411 | put Unchoke = putInt 1 >> S.putWord8 0x01 |
389 | put Interested = putInt 1 >> putWord8 0x02 | 412 | put Interested = putInt 1 >> S.putWord8 0x02 |
390 | put NotInterested = putInt 1 >> putWord8 0x03 | 413 | put NotInterested = putInt 1 >> S.putWord8 0x03 |
391 | put (Have i) = putInt 5 >> putWord8 0x04 >> putInt i | 414 | put (Have i) = putInt 5 >> S.putWord8 0x04 >> putInt i |
392 | put (Bitfield bf) = putInt l >> putWord8 0x05 >> putLazyByteString b | 415 | put (Bitfield bf) = putInt l >> S.putWord8 0x05 >> S.putLazyByteString b |
393 | where b = toBitmap bf | 416 | where b = toBitmap bf |
394 | l = succ (fromIntegral (Lazy.length b)) | 417 | l = succ (fromIntegral (Lazy.length b)) |
395 | {-# INLINE l #-} | 418 | {-# INLINE l #-} |
396 | put (Request blk) = putInt 13 >> putWord8 0x06 >> put blk | 419 | put (Request blk) = putInt 13 >> S.putWord8 0x06 >> S.put blk |
397 | put (Piece blk) = putInt l >> putWord8 0x07 >> putBlock | 420 | put (Piece blk) = putInt l >> S.putWord8 0x07 >> putBlock |
398 | where l = 9 + B.length (blkData blk) | 421 | where l = 9 + B.length (blkData blk) |
399 | {-# INLINE l #-} | 422 | {-# INLINE l #-} |
400 | putBlock = do putInt (blkPiece blk) | 423 | putBlock = do putInt (blkPiece blk) |
401 | putInt (blkOffset blk) | 424 | putInt (blkOffset blk) |
402 | putByteString (blkData blk) | 425 | S.putByteString (blkData blk) |
403 | {-# INLINE putBlock #-} | 426 | {-# INLINE putBlock #-} |
404 | 427 | ||
405 | put (Cancel blk) = putInt 13 >> putWord8 0x08 >> put blk | 428 | put (Cancel blk) = putInt 13 >> S.putWord8 0x08 >> S.put blk |
406 | put (Port p ) = putInt 3 >> putWord8 0x09 >> putWord16be (fromIntegral p) | 429 | put (Port p ) = putInt 3 >> S.putWord8 0x09 >> S.putWord16be (fromIntegral p) |
407 | put HaveAll = putInt 1 >> putWord8 0x0E | 430 | put HaveAll = putInt 1 >> S.putWord8 0x0E |
408 | put HaveNone = putInt 1 >> putWord8 0x0F | 431 | put HaveNone = putInt 1 >> S.putWord8 0x0F |
409 | put (SuggestPiece pix) = putInt 5 >> putWord8 0x0D >> putInt pix | 432 | put (SuggestPiece pix) = putInt 5 >> S.putWord8 0x0D >> putInt pix |
410 | put (RejectRequest i ) = putInt 13 >> putWord8 0x10 >> put i | 433 | put (RejectRequest i ) = putInt 13 >> S.putWord8 0x10 >> S.put i |
411 | put (AllowedFast i ) = putInt 5 >> putWord8 0x11 >> putInt i | 434 | put (AllowedFast i ) = putInt 5 >> S.putWord8 0x11 >> putInt i |
435 | |||
436 | instance Binary Message where | ||
437 | get = do | ||
438 | len <- undefined --getInt | ||
439 | -- _ <- lookAhead $ ensure len | ||
440 | if len == 0 then return KeepAlive | ||
441 | else do | ||
442 | mid <- B.getWord8 | ||
443 | case mid of | ||
444 | 0x00 -> return Choke | ||
445 | 0x01 -> return Unchoke | ||
446 | 0x02 -> return Interested | ||
447 | 0x03 -> return NotInterested | ||
448 | 0x04 -> Have <$> getIntB | ||
449 | 0x05 -> (Bitfield . fromBitmap) <$> B.getByteString (pred len) | ||
450 | 0x06 -> Request <$> B.get | ||
451 | 0x07 -> Piece <$> getBlock (len - 9) | ||
452 | 0x08 -> Cancel <$> B.get | ||
453 | 0x09 -> (Port . fromIntegral) <$> B.getWord16be | ||
454 | 0x0E -> return HaveAll | ||
455 | 0x0F -> return HaveNone | ||
456 | 0x0D -> SuggestPiece <$> getIntB | ||
457 | 0x10 -> RejectRequest <$> B.get | ||
458 | 0x11 -> AllowedFast <$> getIntB | ||
459 | _ -> do | ||
460 | rm <- B.remaining >>= B.getBytes . fromIntegral | ||
461 | fail $ "unknown message ID: " ++ show mid ++ "\n" | ||
462 | ++ "remaining available bytes: " ++ show rm | ||
463 | |||
464 | where | ||
465 | getBlock :: Int -> B.Get Block | ||
466 | getBlock len = Block <$> getIntB <*> getIntB <*> B.getBytes len | ||
467 | {-# INLINE getBlock #-} | ||
468 | |||
469 | put KeepAlive = putIntB 0 | ||
470 | put Choke = putIntB 1 >> B.putWord8 0x00 | ||
471 | put Unchoke = putIntB 1 >> B.putWord8 0x01 | ||
472 | put Interested = putIntB 1 >> B.putWord8 0x02 | ||
473 | put NotInterested = putIntB 1 >> B.putWord8 0x03 | ||
474 | put (Have i) = putIntB 5 >> B.putWord8 0x04 >> putIntB i | ||
475 | put (Bitfield bf) = putIntB l >> B.putWord8 0x05 >> B.putLazyByteString b | ||
476 | where b = toBitmap bf | ||
477 | l = succ (fromIntegral (Lazy.length b)) | ||
478 | {-# INLINE l #-} | ||
479 | put (Request blk) = putIntB 13 >> B.putWord8 0x06 >> B.put blk | ||
480 | put (Piece blk) = putIntB l >> B.putWord8 0x07 >> putBlock | ||
481 | where l = 9 + B.length (blkData blk) | ||
482 | {-# INLINE l #-} | ||
483 | putBlock = do putIntB (blkPiece blk) | ||
484 | putIntB (blkOffset blk) | ||
485 | B.putByteString (blkData blk) | ||
486 | {-# INLINE putBlock #-} | ||
412 | 487 | ||
488 | put (Cancel blk) = putIntB 13 >> B.putWord8 0x08 >> B.put blk | ||
489 | put (Port p ) = putIntB 3 >> B.putWord8 0x09 >> B.putWord16be (fromIntegral p) | ||
490 | put HaveAll = putIntB 1 >> B.putWord8 0x0E | ||
491 | put HaveNone = putIntB 1 >> B.putWord8 0x0F | ||
492 | put (SuggestPiece pix) = putIntB 5 >> B.putWord8 0x0D >> putIntB pix | ||
493 | put (RejectRequest i ) = putIntB 13 >> B.putWord8 0x10 >> B.put i | ||
494 | put (AllowedFast i ) = putIntB 5 >> B.putWord8 0x11 >> putIntB i | ||
413 | 495 | ||
414 | -- | Format messages in human readable form. Note that output is | 496 | -- | Format messages in human readable form. Note that output is |
415 | -- compact and suitable for logging: only useful information but not | 497 | -- compact and suitable for logging: only useful information but not |