|
Packit |
090c59 |
{-# LANGUAGE CPP #-}
|
|
Packit |
090c59 |
-----------------------------------------------------------------------------
|
|
Packit |
090c59 |
-- |
|
|
Packit |
090c59 |
-- Module : Network
|
|
Packit |
090c59 |
-- Copyright : (c) The University of Glasgow 2001
|
|
Packit |
090c59 |
-- License : BSD-style (see the file libraries/network/LICENSE)
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
-- Maintainer : libraries@haskell.org
|
|
Packit |
090c59 |
-- Stability : provisional
|
|
Packit |
090c59 |
-- Portability : portable
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
-- This module is kept for backwards-compatibility. New users are
|
|
Packit |
090c59 |
-- encouraged to use "Network.Socket" instead.
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
-- "Network" was intended as a \"higher-level\" interface to networking
|
|
Packit |
090c59 |
-- facilities, and only supports TCP.
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
-----------------------------------------------------------------------------
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#include "HsNetworkConfig.h"
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#ifdef HAVE_GETADDRINFO
|
|
Packit |
090c59 |
-- Use IPv6-capable function definitions if the OS supports it.
|
|
Packit |
090c59 |
#define IPV6_SOCKET_SUPPORT 1
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
module Network
|
|
Packit |
090c59 |
(
|
|
Packit |
090c59 |
-- * Basic data types
|
|
Packit |
090c59 |
Socket
|
|
Packit |
090c59 |
, PortID(..)
|
|
Packit |
090c59 |
, HostName
|
|
Packit |
090c59 |
, PortNumber
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- * Initialisation
|
|
Packit |
090c59 |
, withSocketsDo
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- * Server-side connections
|
|
Packit |
090c59 |
, listenOn
|
|
Packit |
090c59 |
, accept
|
|
Packit |
090c59 |
, sClose
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- * Client-side connections
|
|
Packit |
090c59 |
, connectTo
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- * Simple sending and receiving
|
|
Packit |
090c59 |
{-$sendrecv-}
|
|
Packit |
090c59 |
, sendTo
|
|
Packit |
090c59 |
, recvFrom
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- * Miscellaneous
|
|
Packit |
090c59 |
, socketPort
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- * Networking Issues
|
|
Packit |
090c59 |
-- ** Buffering
|
|
Packit |
090c59 |
{-$buffering-}
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- ** Improving I\/O Performance over sockets
|
|
Packit |
090c59 |
{-$performance-}
|
|
Packit |
090c59 |
) where
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
import Control.Monad (liftM)
|
|
Packit |
090c59 |
import Data.Maybe (fromJust)
|
|
Packit |
090c59 |
import Network.BSD
|
|
Packit |
090c59 |
import Network.Socket hiding (accept, socketPort, recvFrom,
|
|
Packit |
090c59 |
sendTo, PortNumber, sClose)
|
|
Packit |
090c59 |
import qualified Network.Socket as Socket (accept)
|
|
Packit |
090c59 |
import System.IO
|
|
Packit |
090c59 |
import Prelude
|
|
Packit |
090c59 |
import qualified Control.Exception as Exception
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- ---------------------------------------------------------------------------
|
|
Packit |
090c59 |
-- High Level ``Setup'' functions
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- If the @PortID@ specifies a unix family socket and the @Hostname@
|
|
Packit |
090c59 |
-- differs from that returned by @getHostname@ then an error is
|
|
Packit |
090c59 |
-- raised. Alternatively an empty string may be given to @connectTo@
|
|
Packit |
090c59 |
-- signalling that the current hostname applies.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
data PortID =
|
|
Packit |
090c59 |
Service String -- Service Name eg "ftp"
|
|
Packit |
090c59 |
| PortNumber PortNumber -- User defined Port Number
|
|
Packit |
090c59 |
#if !defined(mingw32_HOST_OS)
|
|
Packit |
090c59 |
| UnixSocket String -- Unix family socket in file system
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
deriving (Show, Eq)
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- | Calling 'connectTo' creates a client side socket which is
|
|
Packit |
090c59 |
-- connected to the given host and port. The Protocol and socket type is
|
|
Packit |
090c59 |
-- derived from the given port identifier. If a port number is given
|
|
Packit |
090c59 |
-- then the result is always an internet family 'Stream' socket.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
connectTo :: HostName -- Hostname
|
|
Packit |
090c59 |
-> PortID -- Port Identifier
|
|
Packit |
090c59 |
-> IO Handle -- Connected Socket
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#if defined(IPV6_SOCKET_SUPPORT)
|
|
Packit |
090c59 |
-- IPv6 and IPv4.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
connectTo hostname (Service serv) = connect' "Network.connectTo" hostname serv
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
connectTo hostname (PortNumber port) = connect' "Network.connectTo" hostname (show port)
|
|
Packit |
090c59 |
#else
|
|
Packit |
090c59 |
-- IPv4 only.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
connectTo hostname (Service serv) = do
|
|
Packit |
090c59 |
proto <- getProtocolNumber "tcp"
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
(socket AF_INET Stream proto)
|
|
Packit |
090c59 |
(sClose) -- only done if there's an error
|
|
Packit |
090c59 |
(\sock -> do
|
|
Packit |
090c59 |
port <- getServicePortNumber serv
|
|
Packit |
090c59 |
he <- getHostByName hostname
|
|
Packit |
090c59 |
connect sock (SockAddrInet port (hostAddress he))
|
|
Packit |
090c59 |
socketToHandle sock ReadWriteMode
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
connectTo hostname (PortNumber port) = do
|
|
Packit |
090c59 |
proto <- getProtocolNumber "tcp"
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
(socket AF_INET Stream proto)
|
|
Packit |
090c59 |
(sClose) -- only done if there's an error
|
|
Packit |
090c59 |
(\sock -> do
|
|
Packit |
090c59 |
he <- getHostByName hostname
|
|
Packit |
090c59 |
connect sock (SockAddrInet port (hostAddress he))
|
|
Packit |
090c59 |
socketToHandle sock ReadWriteMode
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#if !defined(mingw32_HOST_OS)
|
|
Packit |
090c59 |
connectTo _ (UnixSocket path) = do
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
(socket AF_UNIX Stream 0)
|
|
Packit |
090c59 |
(sClose)
|
|
Packit |
090c59 |
(\sock -> do
|
|
Packit |
090c59 |
connect sock (SockAddrUnix path)
|
|
Packit |
090c59 |
socketToHandle sock ReadWriteMode
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#if defined(IPV6_SOCKET_SUPPORT)
|
|
Packit |
090c59 |
connect' :: String -> HostName -> ServiceName -> IO Handle
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
connect' caller host serv = do
|
|
Packit |
090c59 |
proto <- getProtocolNumber "tcp"
|
|
Packit |
090c59 |
let hints = defaultHints { addrFlags = [AI_ADDRCONFIG]
|
|
Packit |
090c59 |
, addrProtocol = proto
|
|
Packit |
090c59 |
, addrSocketType = Stream }
|
|
Packit |
090c59 |
addrs <- getAddrInfo (Just hints) (Just host) (Just serv)
|
|
Packit |
090c59 |
firstSuccessful caller $ map tryToConnect addrs
|
|
Packit |
090c59 |
where
|
|
Packit |
090c59 |
tryToConnect addr =
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
(socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
|
|
Packit |
090c59 |
(sClose) -- only done if there's an error
|
|
Packit |
090c59 |
(\sock -> do
|
|
Packit |
090c59 |
connect sock (addrAddress addr)
|
|
Packit |
090c59 |
socketToHandle sock ReadWriteMode
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- | Creates the server side socket which has been bound to the
|
|
Packit |
090c59 |
-- specified port.
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
-- 'maxListenQueue' (typically 128) is specified to the listen queue.
|
|
Packit |
090c59 |
-- This is good enough for normal network servers but is too small
|
|
Packit |
090c59 |
-- for high performance servers.
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
-- To avoid the \"Address already in use\" problems,
|
|
Packit |
090c59 |
-- the 'ReuseAddr' socket option is set on the listening socket.
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
-- If available, the 'IPv6Only' socket option is set to 0
|
|
Packit |
090c59 |
-- so that both IPv4 and IPv6 can be accepted with this socket.
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
-- If you don't like the behavior above, please use the lower level
|
|
Packit |
090c59 |
-- 'Network.Socket.listen' instead.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
listenOn :: PortID -- ^ Port Identifier
|
|
Packit |
090c59 |
-> IO Socket -- ^ Listening Socket
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#if defined(IPV6_SOCKET_SUPPORT)
|
|
Packit |
090c59 |
-- IPv6 and IPv4.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
listenOn (Service serv) = listen' serv
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
listenOn (PortNumber port) = listen' (show port)
|
|
Packit |
090c59 |
#else
|
|
Packit |
090c59 |
-- IPv4 only.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
listenOn (Service serv) = do
|
|
Packit |
090c59 |
proto <- getProtocolNumber "tcp"
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
(socket AF_INET Stream proto)
|
|
Packit |
090c59 |
(sClose)
|
|
Packit |
090c59 |
(\sock -> do
|
|
Packit |
090c59 |
port <- getServicePortNumber serv
|
|
Packit |
090c59 |
setSocketOption sock ReuseAddr 1
|
|
Packit |
090c59 |
bind sock (SockAddrInet port iNADDR_ANY)
|
|
Packit |
090c59 |
listen sock maxListenQueue
|
|
Packit |
090c59 |
return sock
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
listenOn (PortNumber port) = do
|
|
Packit |
090c59 |
proto <- getProtocolNumber "tcp"
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
(socket AF_INET Stream proto)
|
|
Packit |
090c59 |
(sClose)
|
|
Packit |
090c59 |
(\sock -> do
|
|
Packit |
090c59 |
setSocketOption sock ReuseAddr 1
|
|
Packit |
090c59 |
bind sock (SockAddrInet port iNADDR_ANY)
|
|
Packit |
090c59 |
listen sock maxListenQueue
|
|
Packit |
090c59 |
return sock
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#if !defined(mingw32_HOST_OS)
|
|
Packit |
090c59 |
listenOn (UnixSocket path) =
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
(socket AF_UNIX Stream 0)
|
|
Packit |
090c59 |
(sClose)
|
|
Packit |
090c59 |
(\sock -> do
|
|
Packit |
090c59 |
setSocketOption sock ReuseAddr 1
|
|
Packit |
090c59 |
bind sock (SockAddrUnix path)
|
|
Packit |
090c59 |
listen sock maxListenQueue
|
|
Packit |
090c59 |
return sock
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#if defined(IPV6_SOCKET_SUPPORT)
|
|
Packit |
090c59 |
listen' :: ServiceName -> IO Socket
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
listen' serv = do
|
|
Packit |
090c59 |
proto <- getProtocolNumber "tcp"
|
|
Packit |
090c59 |
-- We should probably specify addrFamily = AF_INET6 and the filter
|
|
Packit |
090c59 |
-- code below should be removed. AI_ADDRCONFIG is probably not
|
|
Packit |
090c59 |
-- necessary. But this code is well-tested. So, let's keep it.
|
|
Packit |
090c59 |
let hints = defaultHints { addrFlags = [AI_ADDRCONFIG, AI_PASSIVE]
|
|
Packit |
090c59 |
, addrSocketType = Stream
|
|
Packit |
090c59 |
, addrProtocol = proto }
|
|
Packit |
090c59 |
addrs <- getAddrInfo (Just hints) Nothing (Just serv)
|
|
Packit |
090c59 |
-- Choose an IPv6 socket if exists. This ensures the socket can
|
|
Packit |
090c59 |
-- handle both IPv4 and IPv6 if v6only is false.
|
|
Packit |
090c59 |
let addrs' = filter (\x -> addrFamily x == AF_INET6) addrs
|
|
Packit |
090c59 |
addr = if null addrs' then head addrs else head addrs'
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
(socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
|
|
Packit |
090c59 |
(sClose)
|
|
Packit |
090c59 |
(\sock -> do
|
|
Packit |
090c59 |
setSocketOption sock ReuseAddr 1
|
|
Packit |
090c59 |
bind sock (addrAddress addr)
|
|
Packit |
090c59 |
listen sock maxListenQueue
|
|
Packit |
090c59 |
return sock
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- -----------------------------------------------------------------------------
|
|
Packit |
090c59 |
-- accept
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- | Accept a connection on a socket created by 'listenOn'. Normal
|
|
Packit |
090c59 |
-- I\/O operations (see "System.IO") can be used on the 'Handle'
|
|
Packit |
090c59 |
-- returned to communicate with the client.
|
|
Packit |
090c59 |
-- Notice that although you can pass any Socket to Network.accept,
|
|
Packit |
090c59 |
-- only sockets of either AF_UNIX, AF_INET, or AF_INET6 will work
|
|
Packit |
090c59 |
-- (this shouldn't be a problem, though). When using AF_UNIX, HostName
|
|
Packit |
090c59 |
-- will be set to the path of the socket and PortNumber to -1.
|
|
Packit |
090c59 |
--
|
|
Packit |
090c59 |
accept :: Socket -- ^ Listening Socket
|
|
Packit |
090c59 |
-> IO (Handle,
|
|
Packit |
090c59 |
HostName,
|
|
Packit |
090c59 |
PortNumber) -- ^ Triple of: read\/write 'Handle' for
|
|
Packit |
090c59 |
-- communicating with the client,
|
|
Packit |
090c59 |
-- the 'HostName' of the peer socket, and
|
|
Packit |
090c59 |
-- the 'PortNumber' of the remote connection.
|
|
Packit |
090c59 |
accept sock@(MkSocket _ AF_INET _ _ _) = do
|
|
Packit |
090c59 |
~(sock', (SockAddrInet port haddr)) <- Socket.accept sock
|
|
Packit |
090c59 |
peer <- catchIO
|
|
Packit |
090c59 |
(do
|
|
Packit |
090c59 |
(HostEntry peer _ _ _) <- getHostByAddr AF_INET haddr
|
|
Packit |
090c59 |
return peer
|
|
Packit |
090c59 |
)
|
|
Packit |
090c59 |
(\_e -> inet_ntoa haddr)
|
|
Packit |
090c59 |
-- if getHostByName fails, we fall back to the IP address
|
|
Packit |
090c59 |
handle <- socketToHandle sock' ReadWriteMode
|
|
Packit |
090c59 |
return (handle, peer, port)
|
|
Packit |
090c59 |
#if defined(IPV6_SOCKET_SUPPORT)
|
|
Packit |
090c59 |
accept sock@(MkSocket _ AF_INET6 _ _ _) = do
|
|
Packit |
090c59 |
(sock', addr) <- Socket.accept sock
|
|
Packit |
090c59 |
peer <- catchIO ((fromJust . fst) `liftM` getNameInfo [] True False addr) $
|
|
Packit |
090c59 |
\_ -> case addr of
|
|
Packit |
090c59 |
SockAddrInet _ a -> inet_ntoa a
|
|
Packit |
090c59 |
SockAddrInet6 _ _ a _ -> return (show a)
|
|
Packit |
090c59 |
#if defined(mingw32_HOST_OS)
|
|
Packit |
090c59 |
SockAddrUnix {} -> ioError $ userError "Network.accept: peer socket address 'SockAddrUnix' not supported on this platform."
|
|
Packit |
090c59 |
#else
|
|
Packit |
090c59 |
SockAddrUnix a -> return a
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
#if defined(CAN_SOCKET_SUPPORT)
|
|
Packit |
090c59 |
SockAddrCan {} -> ioError $ userError "Network.accept: peer socket address 'SockAddrCan' not supported."
|
|
Packit |
090c59 |
#else
|
|
Packit |
090c59 |
SockAddrCan {} -> ioError $ userError "Network.accept: peer socket address 'SockAddrCan' not supported on this platform."
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
handle <- socketToHandle sock' ReadWriteMode
|
|
Packit |
090c59 |
let port = case addr of
|
|
Packit |
090c59 |
SockAddrInet p _ -> p
|
|
Packit |
090c59 |
SockAddrInet6 p _ _ _ -> p
|
|
Packit |
090c59 |
_ -> -1
|
|
Packit |
090c59 |
return (handle, peer, port)
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
#if !defined(mingw32_HOST_OS)
|
|
Packit |
090c59 |
accept sock@(MkSocket _ AF_UNIX _ _ _) = do
|
|
Packit |
090c59 |
~(sock', (SockAddrUnix path)) <- Socket.accept sock
|
|
Packit |
090c59 |
handle <- socketToHandle sock' ReadWriteMode
|
|
Packit |
090c59 |
return (handle, path, -1)
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
accept (MkSocket _ family _ _ _) =
|
|
Packit |
090c59 |
ioError $ userError $ "Network.accept: address family '" ++
|
|
Packit |
090c59 |
show family ++ "' not supported."
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- | Close the socket. Sending data to or receiving data from closed socket
|
|
Packit |
090c59 |
-- may lead to undefined behaviour.
|
|
Packit |
090c59 |
sClose :: Socket -> IO ()
|
|
Packit |
090c59 |
sClose = close -- Explicit redefinition because Network.sClose is deprecated,
|
|
Packit |
090c59 |
-- hence the re-export would also be marked as such.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- -----------------------------------------------------------------------------
|
|
Packit |
090c59 |
-- sendTo/recvFrom
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
{-$sendrecv
|
|
Packit |
090c59 |
Send and receive data from\/to the given host and port number. These
|
|
Packit |
090c59 |
should normally only be used where the socket will not be required for
|
|
Packit |
090c59 |
further calls. Also, note that due to the use of 'hGetContents' in 'recvFrom'
|
|
Packit |
090c59 |
the socket will remain open (i.e. not available) even if the function already
|
|
Packit |
090c59 |
returned. Their use is strongly discouraged except for small test-applications
|
|
Packit |
090c59 |
or invocations from the command line.
|
|
Packit |
090c59 |
-}
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
sendTo :: HostName -- Hostname
|
|
Packit |
090c59 |
-> PortID -- Port Number
|
|
Packit |
090c59 |
-> String -- Message to send
|
|
Packit |
090c59 |
-> IO ()
|
|
Packit |
090c59 |
sendTo h p msg = do
|
|
Packit |
090c59 |
s <- connectTo h p
|
|
Packit |
090c59 |
hPutStr s msg
|
|
Packit |
090c59 |
hClose s
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
recvFrom :: HostName -- Hostname
|
|
Packit |
090c59 |
-> PortID -- Port Number
|
|
Packit |
090c59 |
-> IO String -- Received Data
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
#if defined(IPV6_SOCKET_SUPPORT)
|
|
Packit |
090c59 |
recvFrom host port = do
|
|
Packit |
090c59 |
proto <- getProtocolNumber "tcp"
|
|
Packit |
090c59 |
let hints = defaultHints { addrFlags = [AI_ADDRCONFIG]
|
|
Packit |
090c59 |
, addrProtocol = proto
|
|
Packit |
090c59 |
, addrSocketType = Stream }
|
|
Packit |
090c59 |
allowed <- map addrAddress `liftM` getAddrInfo (Just hints) (Just host)
|
|
Packit |
090c59 |
Nothing
|
|
Packit |
090c59 |
s <- listenOn port
|
|
Packit |
090c59 |
let waiting = do
|
|
Packit |
090c59 |
(s', addr) <- Socket.accept s
|
|
Packit |
090c59 |
if not (addr `oneOf` allowed)
|
|
Packit |
090c59 |
then sClose s' >> waiting
|
|
Packit |
090c59 |
else socketToHandle s' ReadMode >>= hGetContents
|
|
Packit |
090c59 |
waiting
|
|
Packit |
090c59 |
where
|
|
Packit |
090c59 |
a@(SockAddrInet _ ha) `oneOf` ((SockAddrInet _ hb):bs)
|
|
Packit |
090c59 |
| ha == hb = True
|
|
Packit |
090c59 |
| otherwise = a `oneOf` bs
|
|
Packit |
090c59 |
a@(SockAddrInet6 _ _ ha _) `oneOf` ((SockAddrInet6 _ _ hb _):bs)
|
|
Packit |
090c59 |
| ha == hb = True
|
|
Packit |
090c59 |
| otherwise = a `oneOf` bs
|
|
Packit |
090c59 |
_ `oneOf` _ = False
|
|
Packit |
090c59 |
#else
|
|
Packit |
090c59 |
recvFrom host port = do
|
|
Packit |
090c59 |
ip <- getHostByName host
|
|
Packit |
090c59 |
let ipHs = hostAddresses ip
|
|
Packit |
090c59 |
s <- listenOn port
|
|
Packit |
090c59 |
let
|
|
Packit |
090c59 |
waiting = do
|
|
Packit |
090c59 |
~(s', SockAddrInet _ haddr) <- Socket.accept s
|
|
Packit |
090c59 |
he <- getHostByAddr AF_INET haddr
|
|
Packit |
090c59 |
if not (any (`elem` ipHs) (hostAddresses he))
|
|
Packit |
090c59 |
then do
|
|
Packit |
090c59 |
sClose s'
|
|
Packit |
090c59 |
waiting
|
|
Packit |
090c59 |
else do
|
|
Packit |
090c59 |
h <- socketToHandle s' ReadMode
|
|
Packit |
090c59 |
msg <- hGetContents h
|
|
Packit |
090c59 |
return msg
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
message <- waiting
|
|
Packit |
090c59 |
return message
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- ---------------------------------------------------------------------------
|
|
Packit |
090c59 |
-- Access function returning the port type/id of socket.
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- | Returns the 'PortID' associated with a given socket.
|
|
Packit |
090c59 |
socketPort :: Socket -> IO PortID
|
|
Packit |
090c59 |
socketPort s = do
|
|
Packit |
090c59 |
sockaddr <- getSocketName s
|
|
Packit |
090c59 |
case sockaddr of
|
|
Packit |
090c59 |
SockAddrInet port _ -> return $ PortNumber port
|
|
Packit |
090c59 |
#if defined(IPV6_SOCKET_SUPPORT)
|
|
Packit |
090c59 |
SockAddrInet6 port _ _ _ -> return $ PortNumber port
|
|
Packit |
090c59 |
#else
|
|
Packit |
090c59 |
SockAddrInet6 {} -> ioError $ userError "Network.socketPort: socket address 'SockAddrInet6' not supported on this platform."
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
#if defined(mingw32_HOST_OS)
|
|
Packit |
090c59 |
SockAddrUnix {} -> ioError $ userError "Network.socketPort: socket address 'SockAddrUnix' not supported on this platform."
|
|
Packit |
090c59 |
#else
|
|
Packit |
090c59 |
SockAddrUnix path -> return $ UnixSocket path
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
SockAddrCan {} -> ioError $ userError "Network.socketPort: socket address 'SockAddrCan' not supported."
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- ---------------------------------------------------------------------------
|
|
Packit |
090c59 |
-- Utils
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- Like bracket, but only performs the final action if there was an
|
|
Packit |
090c59 |
-- exception raised by the middle bit.
|
|
Packit |
090c59 |
bracketOnError
|
|
Packit |
090c59 |
:: IO a -- ^ computation to run first (\"acquire resource\")
|
|
Packit |
090c59 |
-> (a -> IO b) -- ^ computation to run last (\"release resource\")
|
|
Packit |
090c59 |
-> (a -> IO c) -- ^ computation to run in-between
|
|
Packit |
090c59 |
-> IO c -- returns the value from the in-between computation
|
|
Packit |
090c59 |
bracketOnError = Exception.bracketOnError
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-----------------------------------------------------------------------------
|
|
Packit |
090c59 |
-- Extra documentation
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
{-$buffering
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
The 'Handle' returned by 'connectTo' and 'accept' is block-buffered by
|
|
Packit |
090c59 |
default. For an interactive application you may want to set the
|
|
Packit |
090c59 |
buffering mode on the 'Handle' to
|
|
Packit |
090c59 |
'LineBuffering' or 'NoBuffering', like so:
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
> h <- connectTo host port
|
|
Packit |
090c59 |
> hSetBuffering h LineBuffering
|
|
Packit |
090c59 |
-}
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
{-$performance
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
For really fast I\/O, it might be worth looking at the 'hGetBuf' and
|
|
Packit |
090c59 |
'hPutBuf' family of functions in "System.IO".
|
|
Packit |
090c59 |
-}
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a
|
|
Packit |
090c59 |
#if MIN_VERSION_base(4,0,0)
|
|
Packit |
090c59 |
catchIO = Exception.catch
|
|
Packit |
090c59 |
#else
|
|
Packit |
090c59 |
catchIO = Exception.catchJust Exception.ioErrors
|
|
Packit |
090c59 |
#endif
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- Version of try implemented in terms of the locally defined catchIO
|
|
Packit |
090c59 |
tryIO :: IO a -> IO (Either Exception.IOException a)
|
|
Packit |
090c59 |
tryIO m = catchIO (liftM Right m) (return . Left)
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- Returns the first action from a list which does not throw an exception.
|
|
Packit |
090c59 |
-- If all the actions throw exceptions (and the list of actions is not empty),
|
|
Packit |
090c59 |
-- the last exception is thrown.
|
|
Packit |
090c59 |
-- The operations are run outside of the catchIO cleanup handler because
|
|
Packit |
090c59 |
-- catchIO masks asynchronous exceptions in the cleanup handler.
|
|
Packit |
090c59 |
-- In the case of complete failure, the last exception is actually thrown.
|
|
Packit |
090c59 |
firstSuccessful :: String -> [IO a] -> IO a
|
|
Packit |
090c59 |
firstSuccessful caller = go Nothing
|
|
Packit |
090c59 |
where
|
|
Packit |
090c59 |
-- Attempt the next operation, remember exception on failure
|
|
Packit |
090c59 |
go _ (p:ps) =
|
|
Packit |
090c59 |
do r <- tryIO p
|
|
Packit |
090c59 |
case r of
|
|
Packit |
090c59 |
Right x -> return x
|
|
Packit |
090c59 |
Left e -> go (Just e) ps
|
|
Packit |
090c59 |
|
|
Packit |
090c59 |
-- All operations failed, throw error if one exists
|
|
Packit |
090c59 |
go Nothing [] = ioError $ userError $ caller ++ ": firstSuccessful: empty list"
|
|
Packit |
090c59 |
go (Just e) [] = Exception.throwIO e
|