module Network.BitTorrent.Core.PeerAddr
(
PeerAddr(..)
, getCompactPeerList
, peerSockAddr
, connectToPeer
, ppPeer
) where
import Control.Applicative
import Data.Aeson (ToJSON, FromJSON)
import Data.Aeson.TH
import Data.BEncode as BS
import Data.Bits
import Data.Char
import Data.List as L
import Data.Serialize as S
import Data.Typeable
import Data.Word
import Network.Socket
import Text.PrettyPrint
import Data.Torrent.Client
import Network.BitTorrent.Core.PeerId
deriving instance ToJSON PortNumber
deriving instance FromJSON PortNumber
instance BEncode PortNumber where
toBEncode = toBEncode . fromEnum
fromBEncode b = toEnum <$> fromBEncode b
instance Serialize PortNumber where
get = fromIntegral <$> getWord16be
put = putWord16be . fromIntegral
data PeerAddr = PeerAddr {
peerID :: !(Maybe PeerId)
, peerIP :: !HostAddress
, peerPort :: !PortNumber
} deriving (Show, Eq, Ord, Typeable)
$(deriveJSON (L.map toLower . L.dropWhile isLower) ''PeerAddr)
instance BEncode PeerAddr where
toBEncode (PeerAddr pid pip pport) = toDict $
"peer id" .=? pid
.: "ip" .=! pip
.: "port" .=! pport
.: endDict
fromBEncode = fromDict $ do
PeerAddr <$>? "peer id"
<*>! "ip"
<*>! "port"
instance Serialize PeerAddr where
put PeerAddr {..} = put peerID >> put peerPort
get = PeerAddr Nothing <$> get <*> get
getCompactPeerList :: S.Get [PeerAddr]
getCompactPeerList = many get
peerSockAddr :: PeerAddr -> SockAddr
peerSockAddr = SockAddrInet <$> (g . peerPort) <*> (htonl . peerIP)
where
htonl :: Word32 -> Word32
htonl d =
((d .&. 0xff) `shiftL` 24) .|.
(((d `shiftR` 8 ) .&. 0xff) `shiftL` 16) .|.
(((d `shiftR` 16) .&. 0xff) `shiftL` 8) .|.
((d `shiftR` 24) .&. 0xff)
g :: PortNumber -> PortNumber
g = id
connectToPeer :: PeerAddr -> IO Socket
connectToPeer p = do
sock <- socket AF_INET Stream Network.Socket.defaultProtocol
connect sock (peerSockAddr p)
return sock
ppPeer :: PeerAddr -> Doc
ppPeer p @ PeerAddr {..}
| Just pid <- peerID = ppClientInfo (clientInfo pid) <+> "at" <+> paddr
| otherwise = paddr
where
paddr = text (show (peerSockAddr p))