{-# 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)