packages feed

netw-0.1.0.0: src/Netw/Inet.hs

{-# OPTIONS_GHC -Wno-missing-pattern-synonym-signatures #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE TemplateHaskell #-}
{-|
Module      : Netw.Inet
Description : Internet

This module contains internet address types and some utility functions.
-}
module Netw.Inet where
import Netw.Internal.Type
import Netw.Internal.Port

import Data.Bits
import Data.Primitive.ByteArray.Unaligned
import Numeric

import GHC.Exts
import GHC.ByteOrder
import GHC.Word

-- Conversion from host endianness to network endianness (BE)

-- | Convert 16-bit word from host endianness to network endianness (Big endian)
hton16# :: Word16# -> Word16#
-- | Convert 32-bit word from host endianness to network endianness (Big endian)
hton32# :: Word32# -> Word32#
-- | Convert 64-bit word from host endianness to network endianness (Big endian)
hton64# :: Word64# -> Word64#

hton16# = $(case targetByteOrder of LittleEndian -> [| \ w# -> wordToWord16# (byteSwap16# (word16ToWord# w#)) |]; BigEndian -> [| \ w# -> w# |])
hton32# = $(case targetByteOrder of LittleEndian -> [| \ w# -> wordToWord32# (byteSwap32# (word32ToWord# w#)) |]; BigEndian -> [| \ w# -> w# |])
hton64# = $(case targetByteOrder of LittleEndian -> [| byteSwap64# |]; BigEndian -> [| \ w# -> w# |])

-- | Convert 16-bit word from host endianness to network endianness (Big endian)
hton16 :: Word16 -> Word16
hton16 (W16# w) = W16# (hton16# w)

-- | Convert 32-bit word from host endianness to network endianness (Big endian)
hton32 :: Word32 -> Word32
hton32 (W32# w) = W32# (hton32# w)

-- | Convert 64-bit word from host endianness to network endianness (Big endian)
hton64 :: Word64 -> Word64
hton64 (W64# w) = W64# (hton64# w)

-- Conversion from network endianness (BE) to host endianness
-- | Convert 16-bit word from network endianness (Big endian) to host endianness
ntoh16# :: Word16# -> Word16#
-- | Convert 32-bit word from network endianness (Big endian) to host endianness
ntoh32# :: Word32# -> Word32#
-- | Convert 64-bit word from network endianness (Big endian) to host endianness
ntoh64# :: Word64# -> Word64#

ntoh16# = $(case targetByteOrder of LittleEndian -> [| \ w# -> wordToWord16# (byteSwap16# (word16ToWord# w#)) |]; BigEndian -> [| \ w# -> w# |])
ntoh32# = $(case targetByteOrder of LittleEndian -> [| \ w# -> wordToWord32# (byteSwap32# (word32ToWord# w#)) |]; BigEndian -> [| \ w# -> w# |])
ntoh64# = $(case targetByteOrder of LittleEndian -> [| byteSwap64# |]; BigEndian -> [| \ w# -> w# |])

-- | Convert 16-bit word from network endianness (Big endian) to host endianness
ntoh16 :: Word16 -> Word16
ntoh16 (W16# w) = W16# (ntoh16# w)

-- | Convert 32-bit word from network endianness (Big endian) to host endianness
ntoh32 :: Word32 -> Word32
ntoh32 (W32# w) = W32# (ntoh32# w)

-- | Convert 64-bit word from network endianness (Big endian) to host endianness
ntoh64 :: Word64 -> Word64
ntoh64 (W64# w) = W64# (ntoh64# w)

-- | Get the port number in network byteorder 
getPortInBE :: Port -> Word16
getPortInBE (Port p) = hton16 p

-- | Get the port number from network byteorder
portFromBE :: Word16 -> Port
portFromBE = Port . ntoh16


-- | Evaluate to True if port is reserved for super user use.
isReserved :: Port -> Bool
isReserved (Port p) = p < _IPPORT_RESERVED

-- | Evaluate to True if port is reserved for explicit use and is never automatically allocated.
isUserReserved :: Port -> Bool
isUserReserved (Port p) = p >= _IPPORT_USERRESERVED
-- NOTE: Check if >= or > is correct since documents conflict

-- | Port number zero. The OS will assign a random port.
pattern PortRandom :: Port
pattern PortRandom = Port 0

-- | IP version 4 address. Stored in network byteorder.
newtype InAddr = InAddr { inAddrContent :: Word32 } deriving PrimUnaligned

-- | 0.0.0.0
pattern InAddrAny :: InAddr
pattern InAddrAny = InAddr 0

-- | 255.255.255.255
pattern InAddrBroadcast :: InAddr
pattern InAddrBroadcast = InAddr 0xff_ff_ff_ff

-- | 127.0.0.1
pattern InAddrLoopback :: InAddr
pattern InAddrLoopback = InAddr $(case targetByteOrder of LittleEndian -> [p| 0x01_00_00_7f |]; BigEndian -> [p| 0x7f_00_00_01 |])

-- | IP version 6 address. Stored in network byteorder
data In6Addr = In6Addr# (# Word64#, Word64# #)
-- ^ In6Addr# (# high, low #)

-- | Make an IP version 4 address
ip :: Word8 -> Word8 -> Word8 -> Word8 -> InAddr
ip (fromIntegral -> _0) (fromIntegral -> _1) (fromIntegral -> _2) (fromIntegral -> _3) =
  InAddr (shiftL _0 24 .|. shiftL _1 16 .|. shiftL _2 8 .|. shiftL _3 0)

-- | View an IP version 4 address
unIp :: InAddr -> (Word8, Word8, Word8, Word8)
unIp (InAddr addr) = (fromIntegral _0, fromIntegral _1, fromIntegral _2, fromIntegral _3)
  where _0 = shiftR addr 24
        _1 = shiftR addr 16
        _2 = shiftR addr 8
        _3 = shiftR addr 0
instance Show InAddr where
  show (unIp -> (_0, _1, _2, _3)) = show _0 ++ '.' : show _1 ++ '.' : show _2 ++ '.' : show _3

-- | Make an IP version 6 address
ip6 :: Word16 -> Word16 -> Word16 -> Word16 -> Word16 -> Word16 -> Word16 -> Word16 -> In6Addr
ip6 = ip6'
  where cv :: Int -> Word16 -> Word64
        cv n = (`shiftL` (n * 16)) . fromIntegral . hton16
        ip6' (cv 3 -> _0) (cv 2 -> _1) (cv 1 -> _2) (cv 0 -> _3) (cv 3 -> _4) (cv 2 -> _5) (cv 1 -> _6) (cv 0 -> _7) =
          let !(W64# high#) = _0 .|. _1 .|. _2 .|. _3
              !(W64# low#)  = _4 .|. _5 .|. _6 .|. _7
          in  In6Addr# (# high#, low# #)

-- | View an IP version 6 address
unIp6 :: In6Addr -> (Word16, Word16, Word16, Word16, Word16, Word16, Word16, Word16)
unIp6 (In6Addr# (# high#, low# #)) = (vc 3 high#, vc 2 high#, vc 1 high#, vc 0 high#, vc 3 low#, vc 2 low#, vc 1 low#, vc 0 low#)
  where vc :: Int -> Word64# -> Word16
        vc n (W64# -> w) = fromIntegral (shiftR w (8 * n))
instance Show In6Addr where
  showsPrec _ addr = foldr (\ a as -> showHex a . (':' :) . as ) id groups
    where groups = let (a, b, c, d, e, f, g, h) = unIp6 addr in [a, b, c, d, e, f, g, h]


-- |
-- Port number in internet addresses.
-- The port number is stored in host byteorder and is converted into network
-- byteorder when needed.
newtype Port = Port Word16 deriving (Eq, Ord, Num, Show)

-- Ports
$(declarePorts 'Port)