    {-# LANGUAGE CPP #-}
    {-# LANGUAGE Trustworthy #-}

    {- arch-tag: Network utilities main file
    Copyright (c) 2004-2011 John Goerzen <jgoerzen@complete.org>

    All rights reserved.

    For license and copyright information, see the file LICENSE
    -}
    {- |
       Module     : Network.Utils
       Copyright  : Copyright (C) 2004-2011 John Goerzen
       SPDX-License-Identifier: BSD-3-Clause

       Stability  : stable
       Portability: systems with networking

    This module provides various helpful utilities for dealing with networking

    Written by John Goerzen, jgoerzen\@complete.org

    -}

    module Network.Utils (niceSocketsDo, connectTCP, connectTCPAddr,
                            listenTCPAddr, showSockAddr)
        where

    import Network.BSD
        ( getHostByName,
          getProtocolNumber,
          hostAddress,
          HostName,
          Family(AF_INET),
          PortNumber )
    import Network.Socket
        ( getNameInfo,
          withSocketsDo,
          bind,
          connect,
          listen,
          socket,
          close,
          NameInfoFlag(NI_NUMERICHOST),
          SockAddr(..),
          Socket,
          SocketType(Stream) )
    #if !(defined(mingw32_HOST_OS) || defined(mingw32_TARGET_OS) || defined(__MINGW32__))
    import qualified System.Posix.Signals
    #endif
    import           Control.Exception (bracketOnError)

    {- | Sets up the system for networking.  Similar to the built-in
    withSocketsDo (and actually, calls it), but also sets the SIGPIPE
    handler so that signal is ignored.

    Example:

    > main = niceSocketsDo $ do { ... }
    -}

    -- FIXME integrate with WebCont.Util.UDP

    niceSocketsDo :: IO a -> IO a
    niceSocketsDo :: forall a. IO a -> IO a
    niceSocketsDo IO a
    func = do
    #if !(defined(mingw32_HOST_OS) || defined(mingw32_TARGET_OS) || defined(__MINGW32__))
                    -- No signals on Windows anyway
                    _ <- Signal -> Handler -> Maybe SignalSet -> IO Handler
    System.Posix.Signals.installHandler
                          Signal
    System.Posix.Signals.sigPIPE
                          Handler
    System.Posix.Signals.Ignore
                          Maybe SignalSet
    forall a. Maybe a
    Nothing
    #endif
                    withSocketsDo func

    connectTCP :: HostName -> PortNumber -> IO Socket
    connectTCP :: HostName -> PortNumber -> IO Socket
    connectTCP HostName
    host PortNumber
    port = do
                           he <- HostName -> IO HostEntry
    getHostByName HostName
    host
                           connectTCPAddr (SockAddrInet port (hostAddress he))

    connectTCPAddr :: SockAddr -> IO Socket
    connectTCPAddr :: SockAddr -> IO Socket
    connectTCPAddr SockAddr
    addr = do
                          proto <- HostName -> IO Signal
    getProtocolNumber HostName
    "tcp"
                          bracketOnError (socket AF_INET Stream proto) close
                            (\Socket
    s -> Socket -> SockAddr -> IO ()
    connect Socket
    s SockAddr
    addr IO () -> IO Socket -> IO Socket
    forall a b. IO a -> IO b -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> Socket -> IO Socket
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Socket
    s)

    listenTCPAddr :: SockAddr -> Int -> IO Socket
    listenTCPAddr :: SockAddr -> Int -> IO Socket
    listenTCPAddr SockAddr
    addr Int
    queuelen = do
                         proto <- HostName -> IO Signal
    getProtocolNumber HostName
    "tcp"
                         s <- socket AF_INET Stream proto
                         bind s addr
                         listen s queuelen
                         return s

    showSockAddr :: SockAddr -> IO String
    #if !(defined(mingw32_HOST_OS) || defined(mingw32_TARGET_OS) || defined(__MINGW32__))
    showSockAddr :: SockAddr -> IO HostName
    showSockAddr (SockAddrUnix HostName
    x) = HostName -> IO HostName
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (HostName -> IO HostName) -> HostName -> IO HostName
    forall a b. (a -> b) -> a -> b
    $ HostName
    "UNIX socket at " HostName -> HostName -> HostName
    forall a. [a] -> [a] -> [a]
    ++ HostName
    x
    #endif
    showSockAddr sa :: SockAddr
    sa@(SockAddrInet PortNumber
    port HostAddress
    _host) =
        do (Just h,_) <- [NameInfoFlag]
    -> Bool -> Bool -> SockAddr -> IO (Maybe HostName, Maybe HostName)
    getNameInfo [NameInfoFlag
    NI_NUMERICHOST] Bool
    True Bool
    False SockAddr
    sa
           return $ "IPv4 host " ++ h ++ ", port " ++ (show port)