(TState(..),
initialTrackerState,
trackerLoop,
+ udpTrackerLoop
) where
import Prelude hiding (lookup, splitAt)
+
import Control.Applicative (liftA2)
import Control.Concurrent (threadDelay)
import Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar, readMVar, putMVar)
-import Data.Binary (Binary(..))
+import Control.Exception (try)
+import Data.Binary (Binary(..), encode, decode)
import Data.Binary.Put (putWord16be, putWord32be, putWord64be, putByteString)
import Data.Binary.Get (Get, isEmpty, getByteString, getWord16be, getWord32be)
-import Data.ByteString (ByteString)
+import Data.ByteString (ByteString, hGet, hPut)
import Data.ByteString.Char8 as BC (pack, unpack, splitAt)
+import Data.ByteString.Lazy (fromStrict, toStrict)
import Data.Char (chr)
-import Data.List (intercalate)
+import Data.List (intercalate, isPrefixOf)
import Data.Map as M (lookup)
-import Network (PortNumber)
+import Network (connectTo, PortID(..), PortNumber, Socket)
+import Network.Socket (defaultProtocol, SocketType( Datagram ), Family( AF_INET ), SockAddr( SockAddrInet ), inet_addr, socket)
+import Network.Socket.ByteString (sendTo, recv)
import Network.HTTP.Base (urlEncode)
import qualified Data.ByteString.Base16 as B16 (encode)
-import FuncTorrent.Bencode (BVal(..), decode)
+import FuncTorrent.Bencode (BVal(..))
+import qualified FuncTorrent.Bencode as Benc
import FuncTorrent.Metainfo (Info(..), Metainfo(..))
import FuncTorrent.Network (sendGetRequest)
import FuncTorrent.Peer (Peer(..))
import FuncTorrent.Utils (splitN)
+data TrackerProtocol = Http
+ | Udp
+ | UnknownProtocol
+ deriving (Show)
+
-- | Tracker response
data TrackerResponse = TrackerResponse {
interval :: Integer
, downloaded = down
, left = sz }
--- | Deserialize tracker response
-mkTrackerResponse :: BVal -> Either ByteString TrackerResponse
-mkTrackerResponse resp =
+-- | Deserialize HTTP tracker response
+parseTrackerResponse :: BVal -> Either ByteString TrackerResponse
+parseTrackerResponse resp =
case lookup "failure reason" body of
Just (Bstr err) -> Left err
Just _ -> Left "Unknown failure"
up <- readMVar $ uploaded st
down <- readMVar $ downloaded st
resp <- sendGetRequest (head . announceList $ m) $ mkArgs port peerId up down m
- case decode resp of
+ case Benc.decode resp of
Left e -> return $ pack (show e)
Right trackerInfo ->
- case mkTrackerResponse trackerInfo of
+ case parseTrackerResponse trackerInfo of
Left e -> return e
Right tresp -> do
_ <- threadDelay $ fromIntegral (interval tresp)
_ <- putMVar (connectedPeers st) (peers tresp)
trackerLoop port peerId m st
+-- udp tracker
+getResponse :: Socket -> IO UDPResponse
+getResponse s = do
+ -- connect packet is 16 bytes long
+ -- announce packet is atleast 20 bytes long
+ bs <- recv s (16*1024)
+ return $ decode $ fromStrict bs
+
+sendRequest :: Socket -> String -> PortNumber -> UDPRequest -> IO ()
+sendRequest s ip port req = do
+ hostaddr <- inet_addr ip
+ _ <- sendTo s bsReq $ SockAddrInet (fromIntegral port) hostaddr
+ return ()
+ where bsReq = toStrict $ encode req
+
+getTrackerType :: String -> TrackerProtocol
+getTrackerType url | isPrefixOf "http://" url = Http
+ | isPrefixOf "udp://" url = Udp
+ | otherwise = UnknownProtocol
+
+udpTrackerLoop :: PortNumber -> String -> Metainfo -> TState -> IO String
+udpTrackerLoop port peerId m st = do
+ -- h <- connectTo "exodus.desync.com" (PortNumber 6969)
+ s <- socket AF_INET Datagram defaultProtocol
+ hostAddr <- inet_addr "185.37.101.229"
+ putStrLn "connected to tracker"
+ _ <- sendTo s (toStrict $ encode (ConnectReq 42)) (SockAddrInet 2710 hostAddr)
+ putStrLn "--> sent ConnectReq to tracker"
+ resp <- recv s 16
+ putStrLn "<-- recv ConnectResp from tracker"
+ return $ show resp