{-# LINE 1 "Network/Socket/Info.hsc" #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
#include "HsNetDef.h"
module Network.Socket.Info where
import Foreign.Marshal.Alloc (alloca, allocaBytes)
import Foreign.Marshal.Utils (maybeWith, with)
import GHC.IO.Exception (IOErrorType(NoSuchThing))
import System.IO.Error (ioeSetErrorString, mkIOError)
import Network.Socket.Imports
import Network.Socket.Internal
import Network.Socket.Syscall
import Network.Socket.Types
type HostName       = String
type ServiceName    = String
data AddrInfoFlag =
    
    
    
    
      AI_ADDRCONFIG
    
    
    
    | AI_ALL
    
    
    | AI_CANONNAME
    
    
    
    | AI_NUMERICHOST
    
    
    
    | AI_NUMERICSERV
    
    
    
    
    
    | AI_PASSIVE
    
    
    
    | AI_V4MAPPED
    deriving (Eq, Read, Show)
aiFlagMapping :: [(AddrInfoFlag, CInt)]
aiFlagMapping =
    [
{-# LINE 72 "Network/Socket/Info.hsc" #-}
     (AI_ADDRCONFIG, 32),
{-# LINE 73 "Network/Socket/Info.hsc" #-}
{-# LINE 76 "Network/Socket/Info.hsc" #-}
{-# LINE 77 "Network/Socket/Info.hsc" #-}
     (AI_ALL, 16),
{-# LINE 78 "Network/Socket/Info.hsc" #-}
{-# LINE 81 "Network/Socket/Info.hsc" #-}
     (AI_CANONNAME, 2),
{-# LINE 82 "Network/Socket/Info.hsc" #-}
     (AI_NUMERICHOST, 4),
{-# LINE 83 "Network/Socket/Info.hsc" #-}
{-# LINE 84 "Network/Socket/Info.hsc" #-}
     (AI_NUMERICSERV, 1024),
{-# LINE 85 "Network/Socket/Info.hsc" #-}
{-# LINE 88 "Network/Socket/Info.hsc" #-}
     (AI_PASSIVE, 1),
{-# LINE 89 "Network/Socket/Info.hsc" #-}
{-# LINE 90 "Network/Socket/Info.hsc" #-}
     (AI_V4MAPPED, 8)
{-# LINE 91 "Network/Socket/Info.hsc" #-}
{-# LINE 94 "Network/Socket/Info.hsc" #-}
    ]
addrInfoFlagImplemented :: AddrInfoFlag -> Bool
addrInfoFlagImplemented f = packBits aiFlagMapping [f] /= 0
data AddrInfo = AddrInfo {
    addrFlags :: [AddrInfoFlag]
  , addrFamily :: Family
  , addrSocketType :: SocketType
  , addrProtocol :: ProtocolNumber
  , addrAddress :: SockAddr
  , addrCanonName :: Maybe String
  } deriving (Eq, Show)
instance Storable AddrInfo where
    sizeOf    _ = 48
{-# LINE 112 "Network/Socket/Info.hsc" #-}
    alignment _ = alignment (0 :: CInt)
    peek p = do
        ai_flags <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) p
{-# LINE 116 "Network/Socket/Info.hsc" #-}
        ai_family <- ((\hsc_ptr -> peekByteOff hsc_ptr 4)) p
{-# LINE 117 "Network/Socket/Info.hsc" #-}
        ai_socktype <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) p
{-# LINE 118 "Network/Socket/Info.hsc" #-}
        ai_protocol <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) p
{-# LINE 119 "Network/Socket/Info.hsc" #-}
        ai_addr <- ((\hsc_ptr -> peekByteOff hsc_ptr 24)) p >>= peekSockAddr
{-# LINE 120 "Network/Socket/Info.hsc" #-}
        ai_canonname_ptr <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) p
{-# LINE 121 "Network/Socket/Info.hsc" #-}
        ai_canonname <- if ai_canonname_ptr == nullPtr
                        then return Nothing
                        else Just <$> peekCString ai_canonname_ptr
        return $ AddrInfo {
            addrFlags = unpackBits aiFlagMapping ai_flags
          , addrFamily = unpackFamily ai_family
          , addrSocketType = unpackSocketType ai_socktype
          , addrProtocol = ai_protocol
          , addrAddress = ai_addr
          , addrCanonName = ai_canonname
          }
    poke p (AddrInfo flags family sockType protocol _ _) = do
        let c_stype = packSocketType sockType
        ((\hsc_ptr -> pokeByteOff hsc_ptr 0)) p (packBits aiFlagMapping flags)
{-# LINE 139 "Network/Socket/Info.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 4)) p (packFamily family)
{-# LINE 140 "Network/Socket/Info.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 8)) p c_stype
{-# LINE 141 "Network/Socket/Info.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 12)) p protocol
{-# LINE 142 "Network/Socket/Info.hsc" #-}
        
        ((\hsc_ptr -> pokeByteOff hsc_ptr 16)) p (0::CSize)
{-# LINE 146 "Network/Socket/Info.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 24)) p nullPtr
{-# LINE 147 "Network/Socket/Info.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 32)) p nullPtr
{-# LINE 148 "Network/Socket/Info.hsc" #-}
        ((\hsc_ptr -> pokeByteOff hsc_ptr 40)) p nullPtr
{-# LINE 149 "Network/Socket/Info.hsc" #-}
data NameInfoFlag =
    
    
    
    
      NI_DGRAM
    
    | NI_NAMEREQD
    
    | NI_NOFQDN
    
    
    
    
    
    | NI_NUMERICHOST
    
    
    
    | NI_NUMERICSERV
    deriving (Eq, Read, Show)
niFlagMapping :: [(NameInfoFlag, CInt)]
niFlagMapping = [(NI_DGRAM, 16),
{-# LINE 177 "Network/Socket/Info.hsc" #-}
                 (NI_NAMEREQD, 8),
{-# LINE 178 "Network/Socket/Info.hsc" #-}
                 (NI_NOFQDN, 4),
{-# LINE 179 "Network/Socket/Info.hsc" #-}
                 (NI_NUMERICHOST, 1),
{-# LINE 180 "Network/Socket/Info.hsc" #-}
                 (NI_NUMERICSERV, 2)]
{-# LINE 181 "Network/Socket/Info.hsc" #-}
defaultHints :: AddrInfo
defaultHints = AddrInfo {
    addrFlags      = []
  , addrFamily     = AF_UNSPEC
  , addrSocketType = NoSocketType
  , addrProtocol   = defaultProtocol
  , addrAddress    = SockAddrInet 0 0
  , addrCanonName  = Nothing
  }
getAddrInfo
    :: Maybe AddrInfo 
    -> Maybe HostName 
    -> Maybe ServiceName 
    -> IO [AddrInfo] 
getAddrInfo hints node service = alloc getaddrinfo
  where
    alloc body = withSocketsDo $ maybeWith withCString node $ \c_node ->
        maybeWith withCString service                       $ \c_service ->
            maybeWith with filteredHints                    $ \c_hints ->
                  alloca                                    $ \ptr_ptr_addrs ->
                      body c_node c_service c_hints ptr_ptr_addrs
    getaddrinfo c_node c_service c_hints ptr_ptr_addrs = do
        ret <- c_getaddrinfo c_node c_service c_hints ptr_ptr_addrs
        if ret == 0 then do
            ptr_addrs <- peek ptr_ptr_addrs
            ais       <- followAddrInfo ptr_addrs
            c_freeaddrinfo ptr_addrs
            
            
            case ais of
              [] -> ioError $ mkIOError NoSuchThing message Nothing Nothing
              _ -> return ais
          else do
            err <- gai_strerror ret
            ioError $ ioeSetErrorString
                        (mkIOError NoSuchThing message Nothing Nothing)
                        err
    message = concat [
        "Network.Socket.getAddrInfo (called with preferred socket type/protocol: "
      , maybe "Nothing" show hints
      , ", host name: "
      , show node
      , ", service name: "
      , show service
      , ")"
      ]
{-# LINE 290 "Network/Socket/Info.hsc" #-}
    filteredHints = hints
{-# LINE 292 "Network/Socket/Info.hsc" #-}
followAddrInfo :: Ptr AddrInfo -> IO [AddrInfo]
followAddrInfo ptr_ai
    | ptr_ai == nullPtr = return []
    | otherwise = do
        a  <- peek ptr_ai
        as <- ((\hsc_ptr -> peekByteOff hsc_ptr 40)) ptr_ai >>= followAddrInfo
{-# LINE 299 "Network/Socket/Info.hsc" #-}
        return (a : as)
foreign import ccall safe "hsnet_getaddrinfo"
    c_getaddrinfo :: CString -> CString -> Ptr AddrInfo -> Ptr (Ptr AddrInfo)
                  -> IO CInt
foreign import ccall safe "hsnet_freeaddrinfo"
    c_freeaddrinfo :: Ptr AddrInfo -> IO ()
gai_strerror :: CInt -> IO String
{-# LINE 311 "Network/Socket/Info.hsc" #-}
gai_strerror n = c_gai_strerror n >>= peekCString
foreign import ccall safe "gai_strerror"
    c_gai_strerror :: CInt -> IO CString
{-# LINE 318 "Network/Socket/Info.hsc" #-}
withCStringIf :: Bool -> Int -> (CSize -> CString -> IO a) -> IO a
withCStringIf False _ f = f 0 nullPtr
withCStringIf True  n f = allocaBytes n (f (fromIntegral n))
getNameInfo
    :: [NameInfoFlag] 
    -> Bool 
    -> Bool 
    -> SockAddr 
    -> IO (Maybe HostName, Maybe ServiceName)
getNameInfo flags doHost doService addr = alloc getnameinfo
  where
    alloc body = withSocketsDo $
        withCStringIf doHost (1025)        $ \c_hostlen c_host ->
{-# LINE 351 "Network/Socket/Info.hsc" #-}
            withCStringIf doService (32) $ \c_servlen c_serv ->
{-# LINE 352 "Network/Socket/Info.hsc" #-}
                withSockAddr addr                        $ \ptr_addr sz ->
                  body c_hostlen c_host c_servlen c_serv ptr_addr sz
    getnameinfo c_hostlen c_host c_servlen c_serv ptr_addr sz = do
        ret <- c_getnameinfo ptr_addr
                             (fromIntegral sz)
                             c_host
                             c_hostlen
                             c_serv
                             c_servlen
                             (packBits niFlagMapping flags)
        if ret == 0 then do
            let peekIf doIf c_val =
                    if doIf then Just <$> peekCString c_val else return Nothing
            host <- peekIf doHost c_host
            serv <- peekIf doService c_serv
            return (host, serv)
          else do
            err <- gai_strerror ret
            ioError $ ioeSetErrorString
                        (mkIOError NoSuchThing message Nothing Nothing)
                        err
    message = concat [
        "Network.Socket.getNameInfo (called with flags: "
      , show flags
      , ", hostname lookup: "
      , show doHost
      , ", service name lookup: "
      , show doService
      , ", socket address: "
      , show addr
      , ")"
      ]
foreign import ccall safe "hsnet_getnameinfo"
    c_getnameinfo :: Ptr SockAddr -> CInt -> CString -> CSize -> CString
                  -> CSize -> CInt -> IO CInt
packBits :: (Eq a, Num b, Bits b) => [(a, b)] -> [a] -> b
packBits mapping xs = foldl' pack 0 mapping
  where
    pack acc (k, v) | k `elem` xs = acc .|. v
                    | otherwise   = acc
unpackBits :: (Num b, Bits b) => [(a, b)] -> b -> [a]
unpackBits [] _    = []
unpackBits ((k,v):xs) r
    | r .&. v /= 0 = k : unpackBits xs (r .&. complement v)
    | otherwise    = unpackBits xs r
instance Show SockAddr where
  showsPrec _ (SockAddrUnix str) = showString str
  showsPrec _ (SockAddrInet port ha)
   = showHostAddress ha
   . showString ":"
   . shows port
  showsPrec _ (SockAddrInet6 port _ ha6 _)
   = showChar '['
   . showHostAddress6 ha6
   . showString "]:"
   . shows port
showHostAddress :: HostAddress -> ShowS
showHostAddress ip =
  let (u3, u2, u1, u0) = hostAddressToTuple ip in
  foldr1 (.) . intersperse (showChar '.') $ map showInt [u3, u2, u1, u0]
showHostAddress6 :: HostAddress6 -> ShowS
showHostAddress6 ha6@(a1, a2, a3, a4)
    
    | a1 == 0 && a2 == 0 && a3 == 0xffff =
      showString "::ffff:" . showHostAddress a4
    
    | a1 == 0 && a2 == 0 && a3 == 0 && a4 >= 0x10000 =
        showString "::" . showHostAddress a4
    
    | end - begin > 1 =
        showFields prefix . showString "::" . showFields suffix
    | otherwise =
        showFields fields
  where
    fields =
        let (u7, u6, u5, u4, u3, u2, u1, u0) = hostAddress6ToTuple ha6 in
        [u7, u6, u5, u4, u3, u2, u1, u0]
    showFields = foldr (.) id . intersperse (showChar ':') . map showHex
    prefix = take begin fields  
    suffix = drop end fields    
    begin = end + diff          
    (diff, end) = minimum $
        scanl (\c i -> if i == 0 then c - 1 else 0) 0 fields `zip` [0..]
openSocket :: AddrInfo -> IO Socket
openSocket addr = socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)