{-# LANGUAGE CPP #-} {-# LANGUAGE ForeignFunctionInterface #-} {-# OPTIONS_GHC -fno-warn-orphans #-} ----------------------------------------------------------------------------- -- | -- Module : Network.Socket.Internal -- Copyright : (c) The University of Glasgow 2001 -- License : BSD-style (see the file libraries/network/LICENSE) -- -- Maintainer : libraries@haskell.org -- Stability : provisional -- Portability : portable -- -- A module containing semi-public 'Network.Socket' internals. -- Modules which extend the 'Network.Socket' module will need to use -- this module while ideally most users will be able to make do with -- the public interface. -- ----------------------------------------------------------------------------- #include "HsNet.h" module Network.Socket.Internal ( -- * Socket addresses HostAddress #if defined(IPV6_SOCKET_SUPPORTNO) , HostAddress6 , FlowInfo , ScopeID #endif , PortNumber(..) , SockAddr(..) , peekSockAddr , pokeSockAddr , sizeOfSockAddr , sizeOfSockAddrByFamily , withSockAddr , withNewSockAddr -- * Protocol families , Family(..) -- * Socket error functions #if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS) , c_getLastError #endif , throwSocketError , throwSocketErrorCode -- * Guards for socket operations that may fail , throwSocketErrorIfMinus1_ , throwSocketErrorIfMinus1Retry , throwSocketErrorIfMinus1RetryMayBlock -- ** Guards that wait and retry if the operation would block -- | These guards are based on 'throwSocketErrorIfMinus1RetryMayBlock'. -- They wait for socket readiness if the action fails with @EWOULDBLOCK@ -- or similar. , throwSocketErrorWaitRead , throwSocketErrorWaitWrite -- * Initialization , withSocketsDo -- * Low-level helpers , zeroMemory ) where import Data.Bits ( (.|.), shiftL, shiftR ) import Data.Word ( Word8, Word16, Word32 ) import Data.Typeable (Typeable) import Foreign.C.Error (throwErrno, throwErrnoIfMinus1Retry, throwErrnoIfMinus1RetryMayBlock, throwErrnoIfMinus1_, Errno(..), errnoToIOError) import Foreign.C.String ( castCharToCChar, peekCString ) #if __GLASGOW_HASKELL__ >= 703 import Foreign.C.Types ( CInt(..), CSize(..) ) #else import Foreign.C.Types ( CInt, CSize ) #endif import Foreign.Marshal.Alloc ( allocaBytes ) import Foreign.Marshal.Array ( pokeArray, pokeArray0 ) import Foreign.Ptr ( Ptr, castPtr, plusPtr ) import Foreign.Storable ( Storable(..) ) import GHC.Conc (threadWaitRead, threadWaitWrite) #if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS) import Control.Exception ( finally ) # if __GLASGOW_HASKELL__ >= 707 import GHC.IO.Exception ( IOErrorType(..) ) # else import GHC.IOBase ( IOErrorType(..) ) # endif import Foreign.C.Types ( CChar ) import System.IO.Error ( ioeSetErrorString, mkIOError ) #endif import Network.Socket.Types -- --------------------------------------------------------------------- -- Guards for socket operations that may fail -- | Throw an 'IOError' corresponding to the current socket error. throwSocketError :: String -- ^ textual description of the error location -> IO a -- | Like 'throwSocketError', but the error code is supplied as an argument. -- -- On Windows, do not use errno. Use a system error code instead. throwSocketErrorCode :: String -> CInt -> IO a -- | Throw an 'IOError' corresponding to the current socket error if -- the IO action returns a result of @-1@. Discards the result of the -- IO action after error handling. throwSocketErrorIfMinus1_ :: (Eq a, Num a) => String -- ^ textual description of the location -> IO a -- ^ the 'IO' operation to be executed -> IO () {-# SPECIALIZE throwSocketErrorIfMinus1_ :: String -> IO CInt -> IO () #-} -- | Throw an 'IOError' corresponding to the current socket error if -- the IO action returns a result of @-1@, but retries in case of an -- interrupted operation. throwSocketErrorIfMinus1Retry :: (Eq a, Num a) => String -- ^ textual description of the location -> IO a -- ^ the 'IO' operation to be executed -> IO a {-# SPECIALIZE throwSocketErrorIfMinus1Retry :: String -> IO CInt -> IO CInt #-} -- | Throw an 'IOError' corresponding to the current socket error if -- the IO action returns a result of @-1@, but retries in case of an -- interrupted operation. Checks for operations that would block and -- executes an alternative action before retrying in that case. throwSocketErrorIfMinus1RetryMayBlock :: (Eq a, Num a) => String -- ^ textual description of the location -> IO b -- ^ action to execute before retrying if an -- immediate retry would block -> IO a -- ^ the 'IO' operation to be executed -> IO a {-# SPECIALIZE throwSocketErrorIfMinus1RetryMayBlock :: String -> IO b -> IO CInt -> IO CInt #-} #if (!defined(HAVE_WINSOCK2_H) || defined(cygwin32_HOST_OS)) throwSocketErrorIfMinus1RetryMayBlock name on_block act = throwErrnoIfMinus1RetryMayBlock name act on_block throwSocketErrorIfMinus1Retry = throwErrnoIfMinus1Retry throwSocketErrorIfMinus1_ = throwErrnoIfMinus1_ throwSocketError = throwErrno throwSocketErrorCode loc errno = ioError (errnoToIOError loc (Errno errno) Nothing Nothing) #else throwSocketErrorIfMinus1RetryMayBlock name _ act = throwSocketErrorIfMinus1Retry name act throwSocketErrorIfMinus1_ name act = do throwSocketErrorIfMinus1Retry name act return () # if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS) throwSocketErrorIfMinus1Retry name act = do r <- act if (r == -1) then do rc <- c_getLastError case rc of #{const WSANOTINITIALISED} -> do withSocketsDo (return ()) r <- act if (r == -1) then throwSocketError name else return r _ -> throwSocketError name else return r throwSocketErrorCode name rc = do pstr <- c_getWSError rc str <- peekCString pstr ioError (ioeSetErrorString (mkIOError OtherError name Nothing Nothing) str) throwSocketError name = c_getLastError >>= throwSocketErrorCode name foreign import CALLCONV unsafe "WSAGetLastError" c_getLastError :: IO CInt foreign import ccall unsafe "getWSErrorDescr" c_getWSError :: CInt -> IO (Ptr CChar) # else throwSocketErrorIfMinus1Retry = throwErrnoIfMinus1Retry throwSocketError = throwErrno throwSocketErrorCode loc errno = ioError (errnoToIOError loc (Errno errno) Nothing Nothing) # endif #endif -- | Like 'throwSocketErrorIfMinus1Retry', but if the action fails with -- @EWOULDBLOCK@ or similar, wait for the socket to be read-ready, -- and try again. throwSocketErrorWaitRead :: (Eq a, Num a) => Socket -> String -> IO a -> IO a throwSocketErrorWaitRead sock name io = throwSocketErrorIfMinus1RetryMayBlock name (threadWaitRead $ fromIntegral $ sockFd sock) io -- | Like 'throwSocketErrorIfMinus1Retry', but if the action fails with -- @EWOULDBLOCK@ or similar, wait for the socket to be write-ready, -- and try again. throwSocketErrorWaitWrite :: (Eq a, Num a) => Socket -> String -> IO a -> IO a throwSocketErrorWaitWrite sock name io = throwSocketErrorIfMinus1RetryMayBlock name (threadWaitWrite $ fromIntegral $ sockFd sock) io -- --------------------------------------------------------------------------- -- WinSock support {-| On Windows operating systems, the networking subsystem has to be initialised using 'withSocketsDo' before any networking operations can be used. eg. > main = withSocketsDo $ do {...} Although this is only strictly necessary on Windows platforms, it is harmless on other platforms, so for portability it is good practice to use it all the time. -} withSocketsDo :: IO a -> IO a #if !defined(WITH_WINSOCK) withSocketsDo x = x #else withSocketsDo act = do x <- initWinSock if x /= 0 then ioError (userError "Failed to initialise WinSock") else act `finally` shutdownWinSock foreign import ccall unsafe "initWinSock" initWinSock :: IO Int foreign import ccall unsafe "shutdownWinSock" shutdownWinSock :: IO () #endif