-- | 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 PortNumber
_ Word32
addr4)                       = Word32 -> Bool -> String
showIPv4 Word32
addr4 Bool
isReversed
showSockAddr (SockAddrInet6 PortNumber
_ Word32
_ (Word32
0,Word32
0,Word32
0x0000ffff,Word32
addr4) Word32
_) = Word32 -> Bool -> String
showIPv4 Word32
addr4 Bool
False
showSockAddr (SockAddrInet6 PortNumber
_ Word32
_ (Word32
0,Word32
0,Word32
0,Word32
1) Word32
_)              = String
"::1"
showSockAddr (SockAddrInet6 PortNumber
_ Word32
_ (Word32, Word32, Word32, Word32)
addr6 Word32
_)                  = (Word32, Word32, Word32, Word32) -> String
showIPv6 (Word32, Word32, Word32, Word32)
addr6
showSockAddr SockAddr
_                                            = String
"unknownSocket"

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

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

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