\section{Host Address}
A Host Address is either an IPv4 or an IPv6 address. The binary representation
of an IPv4 address is a Big Endian 32 bit unsigned integer (4 bytes). For an
IPv6 address, it is a Big Endian 128 bit unsigned integer (16 bytes). The
binary representation of a Host Address is a 7 bit unsigned integer specifying
the address family (2 for IPv4, 10 for IPv6), followed by the address itself.
Thus, when packed together with the Transport Protocol, the first bit of the
packed byte is the protocol and the next 7 bits are the address family.
\begin{code}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE Trustworthy #-}
module Network.Tox.NodeInfo.HostAddress where
import Control.Applicative ((<$>))
import Control.Arrow ((&&&))
import Data.Binary (Binary)
import qualified Data.Binary as Binary (get, put)
import qualified Data.Binary.Bits.Get as Bits
import qualified Data.Binary.Bits.Put as Bits
import qualified Data.Binary.Get as Bytes
import qualified Data.Binary.Put as Bytes
import qualified Data.IP as IP
import Data.MessagePack (MessagePack)
import Data.Typeable (Typeable)
import GHC.Generics (Generic)
import qualified Network.Socket as Socket (HostAddress, HostAddress6)
import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
import qualified Test.QuickCheck.Gen as Gen
import Text.Read (readMaybe, readPrec)
data HostAddress
= IPv4 Socket.HostAddress
| IPv6 Socket.HostAddress6
deriving (Eq, Ord, Generic, Typeable)
instance Binary HostAddress
instance MessagePack HostAddress
instance Show HostAddress where
show (IPv4 addr) = show . show . IP.fromHostAddress $ addr
show (IPv6 addr) = show . show . IP.fromHostAddress6 $ addr
instance Read HostAddress where
readPrec = do
str <- readPrec
case readMaybe str of
Nothing -> fail "HostAddress"
Just (IP.IPv4 ipv4) -> return . IPv4 . IP.toHostAddress $ ipv4
Just (IP.IPv6 ipv6) -> return . IPv6 . IP.toHostAddress6 $ ipv6
getHostAddressGetter :: Bits.BitGet (Bytes.Get HostAddress)
getHostAddressGetter =
Bits.getWord8 7 >>= \case
2 -> return $ IPv4 <$> Binary.get
10 -> return $ IPv6 <$> Binary.get
n -> fail $ "Invalid address family: " ++ show n
putAddressFamily :: HostAddress -> Bits.BitPut ()
putAddressFamily (IPv4 _) = Bits.putWord8 7 2
putAddressFamily (IPv6 _) = Bits.putWord8 7 10
putHostAddressValue :: HostAddress -> Bytes.Put
putHostAddressValue (IPv4 addr) = Binary.put addr
putHostAddressValue (IPv6 addr) = Binary.put addr
putHostAddress :: HostAddress -> (Bits.BitPut (), Bytes.Put)
putHostAddress = putAddressFamily &&& putHostAddressValue
instance Arbitrary HostAddress where
arbitrary =
Gen.oneof
[ IPv4 <$> arbitrary
, IPv6 <$> arbitrary
]
\end{code}