-- | Converting an address in 'SockAddr'.

module Network.SockAddr (
    showSockAddr
  , showSockAddrBS
  ) where

import Data.Bits (shift, (.&.))
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS (pack)
import Network.Socket (SockAddr(..), HostAddress, HostAddress6)
import System.ByteOrder
import Text.Printf

isReversed :: Bool
isReversed :: Bool
isReversed = ByteOrder
byteOrder ByteOrder -> ByteOrder -> Bool
forall a. Eq a => a -> a -> Bool
== ByteOrder
LittleEndian

----------------------------------------------------------------

-- | Convert 'SockAddr' to 'String'. If the address is
--   an IPv4-embedded IPv6 address, the IPv4 is extracted.
--
-- >>> import Network.Socket
-- >>> as <- getAddrInfo (Just defaultHints) (Just "example.org") (Just "http")
-- >>> map (showSockAddr.addrAddress) as
-- ["93.184.216.119","93.184.216.119","2606:2800:220:6d:26bf:1447:1097:aa7","2606:2800:220:6d:26bf:1447:1097:aa7"]
showSockAddr :: SockAddr -> String
showSockAddr :: SockAddr -> String
showSockAddr (SockAddrInet _ addr4 :: HostAddress
addr4)                       = HostAddress -> Bool -> String
showIPv4 HostAddress
addr4 Bool
isReversed
showSockAddr (SockAddrInet6 _ _ (0,0,0x0000ffff,addr4 :: HostAddress
addr4) _) = HostAddress -> Bool -> String
showIPv4 HostAddress
addr4 Bool
False
showSockAddr (SockAddrInet6 _ _ (0,0,0,1) _)              = "::1"
showSockAddr (SockAddrInet6 _ _ addr6 :: HostAddress6
addr6 _)                  = HostAddress6 -> String
showIPv6 HostAddress6
addr6
showSockAddr _                                            = "unknownSocket"

----------------------------------------------------------------

-- HostAddress is network byte order.
showIPv4 :: HostAddress -> Bool-> String
showIPv4 :: HostAddress -> Bool -> String
showIPv4 w32 :: HostAddress
w32 reversed :: Bool
reversed
  | Bool
reversed  = HostAddress -> String
forall a. Show a => a -> String
show HostAddress
b1 String -> String -> String
forall a. [a] -> [a] -> [a]
++ "." String -> String -> String
forall a. [a] -> [a] -> [a]
++ HostAddress -> String
forall a. Show a => a -> String
show HostAddress
b2 String -> String -> String
forall a. [a] -> [a] -> [a]
++ "." String -> String -> String
forall a. [a] -> [a] -> [a]
++ HostAddress -> String
forall a. Show a => a -> String
show HostAddress
b3 String -> String -> String
forall a. [a] -> [a] -> [a]
++ "." String -> String -> String
forall a. [a] -> [a] -> [a]
++ HostAddress -> String
forall a. Show a => a -> String
show HostAddress
b4
  | Bool
otherwise = HostAddress -> String
forall a. Show a => a -> String
show HostAddress
b4 String -> String -> String
forall a. [a] -> [a] -> [a]
++ "." String -> String -> String
forall a. [a] -> [a] -> [a]
++ HostAddress -> String
forall a. Show a => a -> String
show HostAddress
b3 String -> String -> String
forall a. [a] -> [a] -> [a]
++ "." String -> String -> String
forall a. [a] -> [a] -> [a]
++ HostAddress -> String
forall a. Show a => a -> String
show HostAddress
b2 String -> String -> String
forall a. [a] -> [a] -> [a]
++ "." String -> String -> String
forall a. [a] -> [a] -> [a]
++ HostAddress -> String
forall a. Show a => a -> String
show HostAddress
b1
  where
    t1 :: HostAddress
t1 = HostAddress
w32
    t2 :: HostAddress
t2 = HostAddress -> Int -> HostAddress
forall a. Bits a => a -> Int -> a
shift HostAddress
t1 (-8)
    t3 :: HostAddress
t3 = HostAddress -> Int -> HostAddress
forall a. Bits a => a -> Int -> a
shift HostAddress
t2 (-8)
    t4 :: HostAddress
t4 = HostAddress -> Int -> HostAddress
forall a. Bits a => a -> Int -> a
shift HostAddress
t3 (-8)
    b1 :: HostAddress
b1 = HostAddress
t1 HostAddress -> HostAddress -> HostAddress
forall a. Bits a => a -> a -> a
.&. 0x000000ff
    b2 :: HostAddress
b2 = HostAddress
t2 HostAddress -> HostAddress -> HostAddress
forall a. Bits a => a -> a -> a
.&. 0x000000ff
    b3 :: HostAddress
b3 = HostAddress
t3 HostAddress -> HostAddress -> HostAddress
forall a. Bits a => a -> a -> a
.&. 0x000000ff
    b4 :: HostAddress
b4 = HostAddress
t4 HostAddress -> HostAddress -> HostAddress
forall a. Bits a => a -> a -> a
.&. 0x000000ff

-- HostAddress6 is host byte order.
showIPv6 :: HostAddress6 -> String
showIPv6 :: HostAddress6 -> String
showIPv6 (w1 :: HostAddress
w1,w2 :: HostAddress
w2,w3 :: HostAddress
w3,w4 :: HostAddress
w4) =
    String
-> HostAddress
-> HostAddress
-> HostAddress
-> HostAddress
-> HostAddress
-> HostAddress
-> HostAddress
-> HostAddress
-> String
forall r. PrintfType r => String -> r
printf "%x:%x:%x:%x:%x:%x:%x:%x" HostAddress
s1 HostAddress
s2 HostAddress
s3 HostAddress
s4 HostAddress
s5 HostAddress
s6 HostAddress
s7 HostAddress
s8
  where
    (s1 :: HostAddress
s1,s2 :: HostAddress
s2) = HostAddress -> (HostAddress, HostAddress)
forall a. (Bits a, Num a) => a -> (a, a)
split16 HostAddress
w1
    (s3 :: HostAddress
s3,s4 :: HostAddress
s4) = HostAddress -> (HostAddress, HostAddress)
forall a. (Bits a, Num a) => a -> (a, a)
split16 HostAddress
w2
    (s5 :: HostAddress
s5,s6 :: HostAddress
s6) = HostAddress -> (HostAddress, HostAddress)
forall a. (Bits a, Num a) => a -> (a, a)
split16 HostAddress
w3
    (s7 :: HostAddress
s7,s8 :: HostAddress
s8) = HostAddress -> (HostAddress, HostAddress)
forall a. (Bits a, Num a) => a -> (a, a)
split16 HostAddress
w4
    split16 :: a -> (a, a)
split16 w :: a
w = (a
h1,a
h2)
      where
        h1 :: a
h1 = a -> Int -> a
forall a. Bits a => a -> Int -> a
shift a
w (-16) a -> a -> a
forall a. Bits a => a -> a -> a
.&. 0x0000ffff
        h2 :: a
h2 = a
w a -> a -> a
forall a. Bits a => a -> a -> a
.&. 0x0000ffff

----------------------------------------------------------------

-- | Convert 'SockAddr' to 'ByteString'. If the address is
--   an IPv4-embedded IPv6 address, the IPv4 is extracted.
--
-- >>> import Network.Socket
-- >>> as <- getAddrInfo (Just defaultHints) (Just "localhost") (Just "http")
-- >>> map (showSockAddrBS.addrAddress) as
-- ["127.0.0.1","::1","fe80:0:0:0:0:0:0:1"]

showSockAddrBS :: SockAddr -> ByteString
showSockAddrBS :: SockAddr -> ByteString
showSockAddrBS = String -> ByteString
BS.pack (String -> ByteString)
-> (SockAddr -> String) -> SockAddr -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SockAddr -> String
showSockAddr