]> git.rkrishnan.org Git - functorrent.git/commitdiff
Merge pull request #31 from gitter-badger/gitter-badge
authorRamakrishnan Muthukrishnan <ram@rkrishnan.org>
Wed, 6 May 2015 05:06:53 +0000 (10:36 +0530)
committerRamakrishnan Muthukrishnan <ram@rkrishnan.org>
Wed, 6 May 2015 05:06:53 +0000 (10:36 +0530)
Add a Gitter chat badge to README.md

README.md
default.nix [new file with mode: 0644]
functorrent.cabal
shell.nix
src/FuncTorrent.hs
src/FuncTorrent/Peer.hs
src/FuncTorrent/Tracker.hs
src/Main.hs
test/Test.hs

index 077a9e136d0a7a62684fd49b2be928c80b509cbf..06af0f49bf7ddc02c0f56b6c7a6ef3d066012302 100644 (file)
--- a/README.md
+++ b/README.md
@@ -32,6 +32,8 @@ shell which has all the package dependencies installed.
 
 ## Goals
 
+- [Optimized for Fun](http://www.slideshare.net/autang/ofun-optimizing-for-fun).
+  (should have called it "funtorrent")
 - Become more profient with Haskell.
 - Implement something non-trivial with Haskell (crypto, file operations, network
   operations, concurrency, bit twiddling, DHT).
@@ -46,11 +48,13 @@ shell which has all the package dependencies installed.
 - talk to the tracker and get the peer list
 - the `main' program takes a torrent file (in the local file system) as input and
   prints the {ip,port} for each peer, after talking to the tracker.
+- can handshake with the peer.
+- peer wire protocol (in progress)
 
 ## TODO
 
 *    Test suite.
-*    Peer protocol.
+*    Peer protocol (in progress).
 *    Get the file download working in the simplest possible way.
 *    Concurrency (threads per peer)
 *    other advanced features of Bit Torrent (like DHT).
diff --git a/default.nix b/default.nix
new file mode 100644 (file)
index 0000000..32a0078
--- /dev/null
@@ -0,0 +1,27 @@
+# This file was auto-generated by cabal2nix. Please do NOT edit manually!
+
+{ cabal, base16Bytestring, binary, cryptohash, doctest, hlint, HTTP
+, networkUri, parsec, QuickCheck, tasty, tastyHunit
+, tastyQuickcheck, testFrameworkQuickcheck2
+}:
+
+cabal.mkDerivation (self: {
+  pname = "functorrent";
+  version = "0.1.0.0";
+  src = ./.;
+  isLibrary = true;
+  isExecutable = true;
+  buildDepends = [
+    base16Bytestring binary cryptohash HTTP networkUri parsec
+    QuickCheck tasty tastyHunit
+  ];
+  testDepends = [
+    doctest hlint QuickCheck tasty tastyHunit tastyQuickcheck
+    testFrameworkQuickcheck2
+  ];
+  meta = {
+    description = "A Bit-torrent client";
+    license = self.stdenv.lib.licenses.gpl3;
+    platforms = self.ghc.meta.platforms;
+  };
+})
index 2d2db982730d5f14bfe059e3fade923f1a38fa49..5e3bf0d959616a3381e3a5beb95ed9bc2a2eec45 100644 (file)
@@ -37,6 +37,7 @@ library
                        cryptohash,
                        directory,
                        HTTP,
+                       network,
                        network-uri,
                        parsec,
                        QuickCheck,
@@ -58,6 +59,7 @@ executable functorrent
                        directory,
                        HTTP,
                        QuickCheck,
+                       network,
                        network-uri,
                        parsec
 
@@ -73,6 +75,7 @@ test-suite functorrent-test
                      containers,
                      directory,
                      doctest,
+                     QuickCheck,
                      tasty,
                      tasty-hunit,
                      QuickCheck,
index cf892c6a0f9de8605c6b054a9def68e6ba8f40c2..b3b0789721d813912c9894c84dce52dee8f07353 100644 (file)
--- a/shell.nix
+++ b/shell.nix
@@ -1,22 +1,12 @@
-# This file was auto-generated by cabal2nix. Please do NOT edit manually!
-
-{ haskellPackages ? (import <nixpkgs> {}).haskellPackages }:
-
-with haskellPackages; cabal.mkDerivation (self: {
-  pname = "functorrent";
-  version = "0.1.0.0";
-  src = "./.";
-  isLibrary = true;
-  isExecutable = true;
-  buildTools = [ cabalInstall ];
-  buildDepends = [
-    base16Bytestring binary cryptohash HTTP networkUri parsec tasty
-    tastyHunit
+let pkgs = (import <nixpkgs> {});
+    haskellPackages = pkgs.recurseIntoAttrs (pkgs.haskellPackages.override {
+        extension = self : super :
+        let callPackage = self.callPackage;
+        in {
+          thisPackage = haskellPackages.callPackage (import ./default.nix) {};
+        };});
+in pkgs.lib.overrideDerivation haskellPackages.thisPackage (old: {
+  buildInputs = old.buildInputs ++ [
+    haskellPackages.cabalInstall
   ];
-  testDepends = [ doctest hlint tasty tastyHunit ];
-  meta = {
-    description = "A Bit-torrent client";
-    license = self.stdenv.lib.licenses.gpl3;
-    platforms = self.ghc.meta.platforms;
-  };
 })
index e17b024052f971e4a885b77f714cb306d063109c..8289da5bae88ce73801cb0edb9d11c407ebf6855 100644 (file)
@@ -7,7 +7,7 @@ module FuncTorrent
      tracker,
      decode,
      encode,
-     handShakeMsg,
+     handShake,
      initLogger,
      logMessage,
      logStop,
index b0c546b9b5abae16d8260290ec0054f06b1db55a..798f60acf0712eef0c0644b320c8fe97750cb212 100644 (file)
@@ -1,26 +1,59 @@
 {-# LANGUAGE OverloadedStrings #-}
 module FuncTorrent.Peer
     (Peer(..),
-     handShakeMsg
+     handShake
     ) where
 
 import Prelude hiding (lookup, concat, replicate, splitAt)
 
-import Data.ByteString.Char8 (ByteString, pack, concat, replicate)
-import Data.ByteString.Lazy (toChunks)
-import Data.Int (Int8)
-import qualified Data.Binary as Bin (encode)
+import System.IO
+import Data.ByteString (ByteString, unpack, concat, hGet, hPut, singleton)
+import Data.ByteString.Char8 (replicate, pack)
+import Network (connectTo, PortID(..))
 
-import FuncTorrent.Metainfo (Metainfo(..))
+type ID = String
+type IP = String
+type Port = Integer
 
--- | Peer is a IP address, port tuple
-data Peer = Peer String Integer
-            deriving (Show, Eq)
+data PeerState = PeerState { am_choking :: Bool
+                           , am_interested :: Bool
+                           , peer_choking :: Bool
+                           , peer_interested :: Bool }
 
-handShakeMsg :: Metainfo -> String -> ByteString
-handShakeMsg m peer_id = concat [pstrlen, pstr, reserved, infoH, peerID]
-    where pstrlen = concat $ toChunks $ Bin.encode (19 :: Int8)
-          pstr = pack "BitTorrent protocol"
-          reserved = replicate 8 '\0'
-          infoH = infoHash m
-          peerID = pack peer_id
+-- | Peer is a PeerID, IP address, port tuple
+data Peer = Peer ID IP Port
+          deriving (Show, Eq)
+
+data Msg = HandShakeMsg ByteString ID
+         | KeepAliveMsg
+         | ChokeMsg
+         | UnChokeMsg
+         | InterestedMsg
+         | NotInterestedMsg
+         | HaveMsg Integer
+         | BitFieldMsg Integer
+         | RequestMsg Integer Integer Integer
+         | PieceMsg Integer Integer Integer
+         | CancelMsg Integer Integer Integer
+         | PortMsg Port
+         deriving (Show)
+
+genHandShakeMsg :: ByteString -> String -> ByteString
+genHandShakeMsg infoHash peer_id = concat [pstrlen, pstr, reserved, infoHash, peerID]
+  where pstrlen = singleton 19
+        pstr = pack "BitTorrent protocol"
+        reserved = replicate 8 '\0'
+        peerID = pack peer_id
+
+handShake :: Peer -> ByteString -> String -> IO ByteString
+handShake (Peer _ ip port) infoHash peerid = do
+  let hs = genHandShakeMsg infoHash peerid
+  handle <- connectTo ip (PortNumber (fromIntegral port))
+  hSetBuffering handle LineBuffering
+  hPut handle hs
+  rlenBS <- hGet handle 1
+  let rlen = fromIntegral $ (unpack rlenBS) !! 0
+  hGet handle rlen
+
+-- sendMsg :: Peer -> Handle -> PeerMsg -> IO ()
+-- recvMsg :: Peer -> Handle -> Msg
index 57c8f9d3b42d2607deec6ddf1672a4dc082259a8..b8166508d20980df85480c8047478388fa265b99 100644 (file)
@@ -62,7 +62,7 @@ mkTrackerResponse resp =
                  splitN 2 . B16.encode
 
       makePeer :: ByteString -> Peer
-      makePeer peer = Peer (toIP ip') (toPort port')
+      makePeer peer = Peer "" (toIP ip') (toPort port')
           where (ip', port') = splitAt 4 peer
 
 -- | Connect to a tracker and get peer info
index 9cb5908c00809d328ccd72bda5e1c4b10ba6a2fa..65e2dd7cb194321305b3028e88ee1dbe7e7a71fa 100644 (file)
@@ -2,7 +2,7 @@
 module Main where
 
 import Prelude hiding (log, length, readFile, writeFile)
-import Data.ByteString.Char8 (ByteString, readFile, writeFile, length, unpack)
+import Data.ByteString.Char8 (ByteString, readFile, writeFile, unpack)
 import System.Environment (getArgs)
 import System.Exit (exitSuccess)
 import System.Directory (doesFileExist)
@@ -11,7 +11,7 @@ import Text.ParserCombinators.Parsec (ParseError)
 import FuncTorrent.Bencode (decode)
 import FuncTorrent.Logger (initLogger, logMessage, logStop)
 import FuncTorrent.Metainfo (Info(..), Metainfo(..), mkMetaInfo)
-import FuncTorrent.Peer (handShakeMsg)
+import FuncTorrent.Peer (handShake)
 import FuncTorrent.Tracker (tracker, peers, mkTrackerResponse)
 
 logError :: ParseError -> (String -> IO ()) -> IO ()
@@ -55,17 +55,18 @@ main = do
               log $ "Trackers: " ++ head (announceList m)
               response <- tracker m peerId
 
-              let hsMsgLen = show $ length $ handShakeMsg m peerId
-              log $ "Hand-shake message length : " ++ hsMsgLen
-
               -- TODO: Write to ~/.functorrent/caches
               writeFile (name (info m) ++ ".cache") response
 
               case decode response of
                 Right trackerInfo ->
                     case mkTrackerResponse trackerInfo of
-                      Right peerResp ->
+                      Right peerResp -> do
                           log $ "Peers List : " ++ (show . peers $ peerResp)
+                          let p1 = head (peers peerResp)
+                          msg <- handShake p1 (infoHash m) peerId
+                          log $ "handshake: " ++ (show msg)
+                          return ()
                       Left e -> log $ "Error" ++ unpack e
                 Left e -> logError e log
 
index 9915d977133dde2d0f23dbcd3bfa67361db2130a..71b9f1c6ef5c0991c2a507387c29555f27349fc9 100644 (file)
@@ -74,7 +74,7 @@ testResponse1 = testCase "Should parse valid tracker response" $ do
                     expectation :: Either a TrackerResponse
                     expectation = Right TrackerResponse {
                                     interval = Just 900,
-                                    peers = [Peer "85.25.201.101" 51413, Peer "37.59.28.236" 22222, Peer "76.21.149.43" 51866, Peer "31.183.33.205" 43467, Peer "213.210.120.86" 27480, Peer "213.239.216.205" 6914, Peer "91.192.163.152" 11834, Peer "62.210.240.65" 6999, Peer "84.250.103.161" 6949, Peer "88.195.241.192" 51413, Peer "88.165.61.223" 6881, Peer "86.157.234.243" 59583, Peer "213.41.137.242" 51413, Peer "91.10.84.195" 46941, Peer "64.56.249.183" 7023, Peer "202.62.16.71" 59929, Peer "31.43.126.122" 57816, Peer "68.169.133.72" 50222, Peer "223.135.97.177" 58813, Peer "5.166.93.118" 64459, Peer "200.148.109.141" 51413, Peer "109.226.236.160" 44444, Peer "78.58.139.154" 22818, Peer "188.244.47.186" 39643, Peer "203.86.204.111" 52411, Peer "80.110.40.98" 6918, Peer "68.187.142.217" 58352, Peer "71.115.139.180" 63065, Peer "70.169.35.173" 51413, Peer "185.3.135.186" 10889, Peer "88.198.224.202" 51413, Peer "183.157.65.217" 9179, Peer "87.251.189.150" 46680, Peer "87.114.202.174" 12393, Peer "93.58.5.16" 51411, Peer "89.102.9.69" 10044, Peer "94.159.19.222" 15783, Peer "95.28.49.176" 58794, Peer "217.114.58.135" 6881, Peer "79.141.162.38" 35806, Peer "136.169.50.72" 54927, Peer "187.67.188.151" 51413, Peer "79.111.218.50" 53636, Peer "62.75.137.129" 51413, Peer "14.204.20.156" 11600, Peer "79.141.162.34" 24531, Peer "82.144.192.7" 63208, Peer "212.34.231.10" 20684, Peer "95.225.246.221" 51413, Peer "124.41.237.102" 24874],
+                                    peers = [Peer "" "85.25.201.101" 51413, Peer "" "37.59.28.236" 22222, Peer "" "76.21.149.43" 51866, Peer "" "31.183.33.205" 43467, Peer "" "213.210.120.86" 27480, Peer "" "213.239.216.205" 6914, Peer "" "91.192.163.152" 11834, Peer "" "62.210.240.65" 6999, Peer "" "84.250.103.161" 6949, Peer "" "88.195.241.192" 51413, Peer "" "88.165.61.223" 6881, Peer "" "86.157.234.243" 59583, Peer "" "213.41.137.242" 51413, Peer "" "91.10.84.195" 46941, Peer "" "64.56.249.183" 7023, Peer "" "202.62.16.71" 59929, Peer "" "31.43.126.122" 57816, Peer "" "68.169.133.72" 50222, Peer "" "223.135.97.177" 58813, Peer "" "5.166.93.118" 64459, Peer "" "200.148.109.141" 51413, Peer "" "109.226.236.160" 44444, Peer "" "78.58.139.154" 22818, Peer "" "188.244.47.186" 39643, Peer "" "203.86.204.111" 52411, Peer "" "80.110.40.98" 6918, Peer "" "68.187.142.217" 58352, Peer "" "71.115.139.180" 63065, Peer "" "70.169.35.173" 51413, Peer "" "185.3.135.186" 10889, Peer "" "88.198.224.202" 51413, Peer "" "183.157.65.217" 9179, Peer "" "87.251.189.150" 46680, Peer "" "87.114.202.174" 12393, Peer "" "93.58.5.16" 51411, Peer "" "89.102.9.69" 10044, Peer "" "94.159.19.222" 15783, Peer "" "95.28.49.176" 58794, Peer "" "217.114.58.135" 6881, Peer "" "79.141.162.38" 35806, Peer "" "136.169.50.72" 54927, Peer "" "187.67.188.151" 51413, Peer "" "79.111.218.50" 53636, Peer "" "62.75.137.129" 51413, Peer "" "14.204.20.156" 11600, Peer "" "79.141.162.34" 24531, Peer "" "82.144.192.7" 63208, Peer "" "212.34.231.10" 20684, Peer "" "95.225.246.221" 51413, Peer "" "124.41.237.102" 24874],
                                     complete = Nothing,
                                     incomplete = Nothing
                                 }