diff --git a/Network/Socket/Buffer.hsc b/Network/Socket/Buffer.hsc index 81759e4a..ea330f57 100644 --- a/Network/Socket/Buffer.hsc +++ b/Network/Socket/Buffer.hsc @@ -1,5 +1,6 @@ {-# LANGUAGE CPP #-} +#include "HsNet.h" ##include "HsNetDef.h" #if defined(mingw32_HOST_OS) # include "windows.h" @@ -57,6 +58,7 @@ sendBufTo :: SocketAddress sa => -> Int -- Data to send -> sa -> IO Int -- Number of Bytes sent +#ifdef HAVE_SENDTO sendBufTo s ptr nbytes sa = withSocketAddress sa $ \p_sa siz -> fromIntegral <$> do withFdSocket s $ \fd -> do @@ -65,6 +67,10 @@ sendBufTo s ptr nbytes sa = flags = 0 throwSocketErrorWaitWrite s "Network.Socket.sendBufTo" $ c_sendto fd ptr n flags p_sa sz +#else +sendBufTo _ _ _ _ = unsupported "sendBufTo" +{-# WARNING sendBufTo "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif #if defined(mingw32_HOST_OS) socket2FD :: Socket -> IO FD @@ -112,6 +118,7 @@ sendBuf s str len = fromIntegral <$> do -- NOTE: blocking on Windows unless you compile with -threaded (see -- GHC ticket #1129) recvBufFrom :: SocketAddress sa => Socket -> Ptr a -> Int -> IO (Int, sa) +#ifdef HAVE_RECVFROM recvBufFrom s ptr nbytes | nbytes <= 0 = ioError (mkInvalidRecvArgError "Network.Socket.recvBufFrom") | otherwise = withNewSocketAddress $ \ptr_sa sz -> alloca $ \ptr_len -> @@ -124,6 +131,10 @@ recvBufFrom s ptr nbytes sockaddr <- peekSocketAddress ptr_sa `catchIOError` \_ -> getPeerName s return (fromIntegral len, sockaddr) +#else +recvBufFrom _ _ _ = unsupported "recvBufFrom" +{-# WARNING recvBufFrom "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif -- | Receive data from the socket. The socket must be in a connected -- state. This function may return fewer bytes than specified. If the @@ -204,6 +215,7 @@ sendBufMsg :: SocketAddress sa -> [Cmsg] -- ^ Control messages -> MsgFlag -- ^ Message flags -> IO Int -- ^ The length actually sent +#ifdef HAVE_STRUCT_CMSGHDR sendBufMsg s sa bufsizs cmsgs flags = do sz <- withSocketAddress sa $ \addrPtr addrSize -> #if !defined(mingw32_HOST_OS) @@ -237,6 +249,10 @@ sendBufMsg s sa bufsizs cmsgs flags = do c_sendmsg fd msgHdrPtr (fromIntegral cflags) send_ptr nullPtr nullPtr #endif return $ fromIntegral sz +#else +sendBufMsg _ _ _ _ _ = unsupported "sendBufMsg" +{-# WARNING sendBufMsg "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif -- | Receive data from the socket using recvmsg(2). The supplied -- buffers are filled in order, with subsequent buffers used only @@ -252,6 +268,7 @@ recvBufMsg :: SocketAddress sa -- 'MSG_CTRUNC' is returned -> MsgFlag -- ^ Message flags -> IO (sa,Int,[Cmsg],MsgFlag) -- ^ Source address, total bytes received, control messages and message flags +#ifdef HAVE_STRUCT_CMSGHDR recvBufMsg s bufsizs clen flags = do withNewSocketAddress $ \addrPtr addrSize -> allocaBytes clen $ \ctrlPtr -> @@ -295,6 +312,10 @@ recvBufMsg s bufsizs clen flags = do cmsgs <- parseCmsgs msgHdrPtr let flags' = MsgFlag $ fromIntegral $ msgFlags hdr return (sockaddr, len, cmsgs, flags') +#else +recvBufMsg _ _ _ _ = unsupported "recvBufMsg" +{-# WARNING recvBufMsg "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif #if !defined(mingw32_HOST_OS) foreign import ccall unsafe "send" @@ -317,8 +338,11 @@ foreign import CALLCONV SAFE_ON_WIN "WSARecvMsg" foreign import ccall unsafe "recv" c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt +#ifdef HAVE_SENDTO foreign import CALLCONV SAFE_ON_WIN "sendto" c_sendto :: CInt -> Ptr a -> CSize -> CInt -> Ptr sa -> CInt -> IO CInt +#endif +#ifdef HAVE_RECVFROM foreign import CALLCONV SAFE_ON_WIN "recvfrom" c_recvfrom :: CInt -> Ptr a -> CSize -> CInt -> Ptr sa -> Ptr CInt -> IO CInt - +#endif diff --git a/Network/Socket/Cbits.hsc b/Network/Socket/Cbits.hsc index 7a68a9cd..c0007685 100644 --- a/Network/Socket/Cbits.hsc +++ b/Network/Socket/Cbits.hsc @@ -7,8 +7,14 @@ import Network.Socket.Imports -- | This is the value of SOMAXCONN, typically 128. -- 128 is good enough for normal network servers but -- is too small for high performance servers. +-- +-- TODO what if not present maxListenQueue :: Int +#ifdef SOMAXCONN maxListenQueue = #const SOMAXCONN +#else +maxListenQueue = 1 +#endif #if defined(mingw32_HOST_OS) wsaNotInitialized :: CInt diff --git a/Network/Socket/If.hs b/Network/Socket/If.hs index 53f5c34a..15226373 100644 --- a/Network/Socket/If.hs +++ b/Network/Socket/If.hs @@ -10,29 +10,46 @@ module Network.Socket.If ( import Foreign.Marshal.Alloc (allocaBytes) import Network.Socket.Imports +#if !defined(HAVE_IF_NAMETOINDEX) || !defined(HAVE_IF_INDEXTONAME) +import Network.Socket.Internal (unsupported) +#endif -- | Returns the index corresponding to the interface name. -- -- Since 2.7.0.0. ifNameToIndex :: String -> IO (Maybe Int) +#ifdef HAVE_IF_NAMETOINDEX ifNameToIndex ifname = do index <- withCString ifname c_if_nametoindex -- On failure zero is returned. We'll return Nothing. return $ if index == 0 then Nothing else Just $ fromIntegral index +#else +ifNameToIndex _ = unsupported "ifNameToIndex" +{-# WARNING ifNameToIndex "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif -- | Returns the interface name corresponding to the index. -- -- Since 2.7.0.0. ifIndexToName :: Int -> IO (Maybe String) +#ifdef HAVE_IF_INDEXTONAME ifIndexToName ifn = allocaBytes 16 $ \ptr -> do -- 16 == IFNAMSIZ r <- c_if_indextoname (fromIntegral ifn) ptr if r == nullPtr then return Nothing else Just <$> peekCString ptr +#else +ifIndexToName _ = unsupported "ifIndexToName" +{-# WARNING ifIndexToName "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif +#ifdef HAVE_IF_NAMETOINDEX foreign import CALLCONV safe "if_nametoindex" c_if_nametoindex :: CString -> IO CUInt +#endif +#ifdef HAVE_IF_INDEXTONAME foreign import CALLCONV safe "if_indextoname" c_if_indextoname :: CUInt -> CString -> IO CString +#endif diff --git a/Network/Socket/Info.hsc b/Network/Socket/Info.hsc index a7eadeed..6dc4192a 100644 --- a/Network/Socket/Info.hsc +++ b/Network/Socket/Info.hsc @@ -81,14 +81,26 @@ aiFlagMapping = #else (AI_ALL, 0), #endif +#if HAVE_DECL_AI_CANONNAME (AI_CANONNAME, #const AI_CANONNAME), +#else + (AI_CANONNAME, 0), +#endif +#if HAVE_DECL_AI_NUMERICHOST (AI_NUMERICHOST, #const AI_NUMERICHOST), +#else + (AI_NUMERICHOST, 0), +#endif #if HAVE_DECL_AI_NUMERICSERV (AI_NUMERICSERV, #const AI_NUMERICSERV), #else (AI_NUMERICSERV, 0), #endif +#if HAVE_DECL_AI_PASSIVE (AI_PASSIVE, #const AI_PASSIVE), +#else + (AI_PASSIVE, 0), +#endif #if HAVE_DECL_AI_V4MAPPED (AI_V4MAPPED, #const AI_V4MAPPED) #else @@ -110,6 +122,7 @@ data AddrInfo = AddrInfo { , addrCanonName :: Maybe String } deriving (Eq, Show) +#if HAVE_STRUCT_ADDRINFO instance Storable AddrInfo where sizeOf ~_ = #const sizeof(struct addrinfo) alignment ~_ = alignment (0 :: CInt) @@ -149,6 +162,8 @@ instance Storable AddrInfo where (#poke struct addrinfo, ai_addr) p nullPtr (#poke struct addrinfo, ai_canonname) p nullPtr (#poke struct addrinfo, ai_next) p nullPtr +#else +#endif -- | Flags that control the querying behaviour of 'getNameInfo'. -- For more information, see @@ -176,11 +191,34 @@ data NameInfoFlag = niFlagMapping :: [(NameInfoFlag, CInt)] -niFlagMapping = [(NI_DGRAM, #const NI_DGRAM), - (NI_NAMEREQD, #const NI_NAMEREQD), - (NI_NOFQDN, #const NI_NOFQDN), - (NI_NUMERICHOST, #const NI_NUMERICHOST), - (NI_NUMERICSERV, #const NI_NUMERICSERV)] +niFlagMapping = + [ +#if HAVE_DECL_NI_ + (NI_DGRAM, #const NI_DGRAM), +#else + (NI_DGRAM, 0), +#endif +#if HAVE_DECL_NI_NAMEREQD + (NI_NAMEREQD, #const NI_NAMEREQD), +#else + (NI_NAMEREQD, 0), +#endif +#if HAVE_DECL_NI_NOFQDN + (NI_NOFQDN, #const NI_NOFQDN), +#else + (NI_NOFQDN, 0), +#endif +#if HAVE_DECL_NI_NUMERICHOST + (NI_NUMERICHOST, #const NI_NUMERICHOST), +#else + (NI_NUMERICHOST, 0), +#endif +#if HAVE_DECL_NI_NUMERICSERV + (NI_NUMERICSERV, #const NI_NUMERICSERV) +#else + (NI_NUMERICSERV, 0) +#endif + ] -- | Default hints for address lookup with 'getAddrInfo'. -- @@ -268,6 +306,7 @@ getAddrInfoNE -> Maybe HostName -- ^ host name to look up -> Maybe ServiceName -- ^ service name to look up -> IO (NonEmpty AddrInfo) -- ^ resolved addresses, with "best" first +#if HAVE_GETADDRINFO getAddrInfoNE hints node service = alloc getaddrinfo where alloc body = withSocketsDo $ maybeWith withCString node $ \c_node -> @@ -308,15 +347,6 @@ getAddrInfoNE hints node service = alloc getaddrinfo filteredHints = hints #endif -getAddrInfoList - :: Maybe AddrInfo - -> Maybe HostName - -> Maybe ServiceName - -> IO [AddrInfo] -getAddrInfoList hints node service = - -- getAddrInfo never returns an empty list. - NE.toList <$> getAddrInfoNE hints node service - followAddrInfo :: Ptr AddrInfo -> IO (NonEmpty AddrInfo) followAddrInfo ptr_ai -- POSIX requires that getaddrinfo(3) returns at least one addrinfo. @@ -342,6 +372,10 @@ foreign import ccall safe "hsnet_getaddrinfo" foreign import ccall safe "hsnet_freeaddrinfo" c_freeaddrinfo :: Ptr AddrInfo -> IO () +#else +getAddrInfoNE _ _ _ = unsupported "getAddrInfoNE" +{-# WARNING getAddrInfo "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif gai_strerror :: CInt -> IO String @@ -354,6 +388,15 @@ foreign import ccall safe "gai_strerror" gai_strerror n = ioError $ userError $ "Network.Socket.gai_strerror not supported: " ++ show n #endif +getAddrInfoList + :: Maybe AddrInfo + -> Maybe HostName + -> Maybe ServiceName + -> IO [AddrInfo] +getAddrInfoList hints node service = + -- getAddrInfo never returns an empty list. + NE.toList <$> getAddrInfoNE hints node service + ----------------------------------------------------------------------------- withCStringIf :: Bool -> Int -> (CSize -> CString -> IO a) -> IO a @@ -382,6 +425,7 @@ getNameInfo -> Bool -- ^ whether to look up a service name -> SockAddr -- ^ the address to look up -> IO (Maybe HostName, Maybe ServiceName) +#if HAVE_GETNAMEINFO getNameInfo flags doHost doService addr = alloc getnameinfo where alloc body = withSocketsDo $ @@ -423,6 +467,10 @@ getNameInfo flags doHost doService addr = alloc getnameinfo foreign import ccall safe "hsnet_getnameinfo" c_getnameinfo :: Ptr SockAddr -> CInt{-CSockLen???-} -> CString -> CSize -> CString -> CSize -> CInt -> IO CInt +#else +getNameInfo _ _ _ _ = unsupported "getNameInfo" +{-# WARNING getNameInfo "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif -- | Pack a list of values into a bitmask. The possible mappings from -- value to bit-to-set are given as the first argument. We assume diff --git a/Network/Socket/Internal.hs b/Network/Socket/Internal.hs index 94769e85..318df54f 100644 --- a/Network/Socket/Internal.hs +++ b/Network/Socket/Internal.hs @@ -51,6 +51,9 @@ module Network.Socket.Internal -- * Null socket address type , NullSockAddr (..) + -- * Unsupported functionality + , unsupported + -- * Low-level helpers , zeroMemory ) where diff --git a/Network/Socket/Name.hs b/Network/Socket/Name.hs index ae6cd6e8..08fa5194 100644 --- a/Network/Socket/Name.hs +++ b/Network/Socket/Name.hs @@ -17,6 +17,7 @@ import Network.Socket.Types -- | Getting peer's socket address. getPeerName :: SocketAddress sa => Socket -> IO sa +#ifdef HAVE_GETPEERNAME getPeerName s = withNewSocketAddress $ \ptr sz -> with (fromIntegral sz) $ \int_star -> withFdSocket s $ \fd -> do @@ -24,6 +25,10 @@ getPeerName s = c_getpeername fd ptr int_star _sz <- peek int_star peekSocketAddress ptr +#else +getPeerName _ = unsupported "getPeerName" +{-# WARNING getPeerName "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif -- | Getting my socket address. getSocketName :: SocketAddress sa => Socket -> IO sa @@ -34,8 +39,10 @@ getSocketName s = c_getsockname fd ptr int_star peekSocketAddress ptr +#ifdef HAVE_GETPEERNAME foreign import CALLCONV unsafe "getpeername" c_getpeername :: CInt -> Ptr sa -> Ptr CInt -> IO CInt +#endif foreign import CALLCONV unsafe "getsockname" c_getsockname :: CInt -> Ptr sa -> Ptr CInt -> IO CInt diff --git a/Network/Socket/Options.hsc b/Network/Socket/Options.hsc index 21c6d61f..c8763cee 100644 --- a/Network/Socket/Options.hsc +++ b/Network/Socket/Options.hsc @@ -404,12 +404,17 @@ setSockOpt :: Storable a -> SocketOption -> a -> IO () +#ifdef HAVE_SETSOCKOPT setSockOpt s (SockOpt level opt) v = do with v $ \ptr -> void $ do let sz = fromIntegral $ sizeOf v withFdSocket s $ \fd -> throwSocketErrorIfMinus1_ "Network.Socket.setSockOpt" $ c_setsockopt fd level opt ptr sz +#else +setSockOpt _ _ _ = unsupported "setSockOpt" +{-# WARNING setSockOpt "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif -- | Set a socket option value -- @@ -474,7 +479,7 @@ getSocketType s = unpackSocketType <$> getSockOpt s Type #if __GLASGOW_HASKELL__ >= 806 {-# COMPLETE CustomSockOpt #-} #endif -#ifdef SO_LINGER + -- | Low level @SO_LINGER@ option value, which can be used with 'setSockOpt' or -- @'setSockOptValue' . 'SockOptValue'@. data StructLinger = StructLinger { @@ -486,6 +491,7 @@ data StructLinger = StructLinger { } deriving (Eq, Ord, Show) +#ifdef SO_LINGER instance Storable StructLinger where sizeOf ~_ = (#const sizeof(struct linger)) alignment ~_ = alignment (0 :: CInt) @@ -540,5 +546,7 @@ instance Storable SocketTimeout where foreign import CALLCONV unsafe "getsockopt" c_getsockopt :: CInt -> CInt -> CInt -> Ptr a -> Ptr CInt -> IO CInt +#ifdef HAVE_SETSOCKOPT foreign import CALLCONV unsafe "setsockopt" c_setsockopt :: CInt -> CInt -> CInt -> Ptr a -> CInt -> IO CInt +#endif diff --git a/Network/Socket/Posix/Cmsg.hsc b/Network/Socket/Posix/Cmsg.hsc index 868ca6ba..069766cf 100644 --- a/Network/Socket/Posix/Cmsg.hsc +++ b/Network/Socket/Posix/Cmsg.hsc @@ -86,7 +86,11 @@ pattern CmsgIdIPv6PktInfo = CmsgId (-1) (-1) -- | The identifier for 'Fds'. pattern CmsgIdFds :: CmsgId +#if defined(SCM_RIGHTS) pattern CmsgIdFds = CmsgId (#const SOL_SOCKET) (#const SCM_RIGHTS) +#else +pattern CmsgIdFds = CmsgId (-1) (-1) +#endif ---------------------------------------------------------------- diff --git a/Network/Socket/Posix/CmsgHdr.hsc b/Network/Socket/Posix/CmsgHdr.hsc index 1343f95a..6021290b 100644 --- a/Network/Socket/Posix/CmsgHdr.hsc +++ b/Network/Socket/Posix/CmsgHdr.hsc @@ -4,8 +4,10 @@ module Network.Socket.Posix.CmsgHdr ( Cmsg(..) +#ifdef HAVE_STRUCT_CMSGHDR , withCmsgs , parseCmsgs +#endif ) where #include @@ -22,6 +24,8 @@ import Network.Socket.Posix.Cmsg import Network.Socket.Posix.MsgHdr import Network.Socket.Types +#ifdef HAVE_STRUCT_CMSGHDR + data CmsgHdr = CmsgHdr { #ifdef __linux__ cmsgHdrLen :: CSize @@ -105,3 +109,5 @@ foreign import ccall unsafe "cmsg_space" foreign import ccall unsafe "cmsg_len" c_cmsg_len :: CSize -> CSize + +#endif diff --git a/Network/Socket/Syscall.hs b/Network/Socket/Syscall.hs index fa6bb971..ecd50530 100644 --- a/Network/Socket/Syscall.hs +++ b/Network/Socket/Syscall.hs @@ -75,6 +75,7 @@ socket :: Family -- Family Name (usually AF_INET) -> SocketType -- Socket Type (usually Stream) -> ProtocolNumber -- Protocol Number (getProtocolByName to find value) -> IO Socket -- Unconnected Socket +#if HAVE_SOCKET socket family stype protocol = E.bracketOnError create c_close $ \fd -> do -- Let's ensure that the socket (file descriptor) is closed even on -- asynchronous exceptions. @@ -119,6 +120,10 @@ socket family stype protocol = E.bracketOnError create c_close $ \fd -> do #else unsetIPv6Only _ = return () #endif +#else +socket _ _ _ = unsupported "socket" +{-# WARNING socket "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif ----------------------------------------------------------------------------- -- Binding a socket @@ -129,15 +134,21 @@ socket family stype protocol = E.bracketOnError create c_close $ \fd -> do -- 'defaultPort' is passed then the system assigns the next available -- use port. bind :: SocketAddress sa => Socket -> sa -> IO () +#ifdef HAVE_BIND bind s sa = withSocketAddress sa $ \p_sa siz -> void $ withFdSocket s $ \fd -> do let sz = fromIntegral siz throwSocketErrorIfMinus1Retry "Network.Socket.bind" $ c_bind fd p_sa sz +#else +bind _ _ = unsupported "bind" +{-# WARNING bind "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif ----------------------------------------------------------------------------- -- Connecting a socket -- | Connect to a remote socket at address. connect :: SocketAddress sa => Socket -> sa -> IO () +#ifdef HAVE_CONNECT connect s sa = withSocketsDo $ withSocketAddress sa $ \p_sa sz -> connectLoop s p_sa (fromIntegral sz) @@ -163,6 +174,10 @@ connectLoop s p_sa sz = withFdSocket s $ \fd -> loop fd err <- getSocketOption s SoError when (err /= 0) $ throwSocketErrorCode errLoc (fromIntegral err) #endif +#else +connect _ _ = unsupported "connect" +{-# WARNING connect "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif ----------------------------------------------------------------------------- -- Listen @@ -171,9 +186,14 @@ connectLoop s p_sa sz = withFdSocket s $ \fd -> loop fd -- specifies the maximum number of queued connections and should be at -- least 1; the maximum value is system-dependent (usually 5). listen :: Socket -> Int -> IO () +#ifdef HAVE_LISTEN listen s backlog = withFdSocket s $ \fd -> do throwSocketErrorIfMinus1Retry_ "Network.Socket.listen" $ c_listen fd $ fromIntegral backlog +#else +listen _ _ = unsupported "listen" +{-# WARNING listen "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif ----------------------------------------------------------------------------- -- Accept @@ -223,14 +243,22 @@ accept listing_sock = withNewSocketAddress $ \new_sa sz -> # endif /* HAVE_ADVANCED_SOCKET_FLAGS */ #endif +#ifdef HAVE_SOCKET foreign import CALLCONV unsafe "socket" c_socket :: CInt -> CInt -> CInt -> IO CInt +#endif +#ifdef HAVE_BIND foreign import CALLCONV unsafe "bind" c_bind :: CInt -> Ptr sa -> CInt{-CSockLen???-} -> IO CInt +#endif +#ifdef HAVE_CONNECT foreign import CALLCONV SAFE_ON_WIN "connect" c_connect :: CInt -> Ptr sa -> CInt{-CSockLen???-} -> IO CInt +#endif +#ifdef HAVE_LISTEN foreign import CALLCONV unsafe "listen" c_listen :: CInt -> CInt -> IO CInt +#endif #ifdef HAVE_ADVANCED_SOCKET_FLAGS foreign import CALLCONV unsafe "accept4" diff --git a/Network/Socket/Types.hsc b/Network/Socket/Types.hsc index a0efb968..490e1407 100644 --- a/Network/Socket/Types.hsc +++ b/Network/Socket/Types.hsc @@ -78,6 +78,7 @@ module Network.Socket.Types ( , defaultPort -- * Low-level helpers + , unsupported , zeroMemory , htonl , ntohl @@ -88,16 +89,19 @@ import Data.IORef (IORef, newIORef, readIORef, atomicModifyIORef', mkWeakIORef) import Foreign.C.Error (throwErrno) import Foreign.Marshal.Alloc import GHC.Conc (closeFdWith) +import System.IO.Error (ioeSetLocation) import System.Posix.Types (Fd) import Control.DeepSeq (NFData (..)) import GHC.Exts (touch##) import GHC.IORef (IORef (..)) import GHC.STRef (STRef (..)) import GHC.IO (IO (..)) +import GHC.IO.Exception (unsupportedOperation) import qualified Text.Read as P import Foreign.Marshal.Array +import Foreign.Marshal.Utils (fillBytes) import Network.Socket.Imports @@ -203,6 +207,7 @@ socketToFd s = do foreign import ccall unsafe "wsaDuplicate" c_wsaDuplicate :: CInt -> IO CInt #else +#if HAVE_DUP fd <- unsafeFdSocket s -- FIXME: throw error no if -1 fd2 <- c_dup fd @@ -211,6 +216,10 @@ foreign import ccall unsafe "wsaDuplicate" foreign import ccall unsafe "dup" c_dup :: CInt -> IO CInt +#else + unsupported "socketToFd" +{-# WARNING socketToFd "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif #endif -- | Creating a socket from a file descriptor. @@ -1140,12 +1149,14 @@ withSockAddr addr f = do let sz = sizeOfSockAddr addr allocaBytes sz $ \p -> pokeSockAddr p addr >> f (castPtr p) sz +#if defined(HAVE_STRUCT_SOCKADDR_UN_SUN_PATH) -- We cannot bind sun_paths longer than than the space in the sockaddr_un -- structure, and attempting to do so could overflow the allocated storage -- space. This constant holds the maximum allowable path length. -- unixPathMax :: Int unixPathMax = #const sizeof(((struct sockaddr_un *)NULL)->sun_path) +#endif -- We can't write an instance of 'Storable' for 'SockAddr' because -- @sockaddr@ is a sum type of variable size but @@ -1157,18 +1168,22 @@ unixPathMax = #const sizeof(((struct sockaddr_un *)NULL)->sun_path) -- | Write the given 'SockAddr' to the given memory location. pokeSockAddr :: Ptr a -> SockAddr -> IO () pokeSockAddr p sa@(SockAddrUnix path) = do +#if defined(HAVE_STRUCT_SOCKADDR_UN_SUN_PATH) let pathC = map castCharToCChar path len = length pathC when (len >= unixPathMax) $ error $ "pokeSockAddr: path is too long in SockAddrUnix " <> show path <> ", length " <> show len <> ", unixPathMax " <> show unixPathMax +#endif zeroMemory p $ fromIntegral $ sizeOfSockAddr sa # if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) (#poke struct sockaddr_un, sun_len) p ((#const sizeof(struct sockaddr_un)) :: Word8) # endif (#poke struct sockaddr_un, sun_family) p ((#const AF_UNIX) :: CSaFamily) +#if defined(HAVE_STRUCT_SOCKADDR_UN_SUN_PATH) -- the buffer is already filled with nulls. pokeArray ((#ptr struct sockaddr_un, sun_path) p) pathC +#endif pokeSockAddr p (SockAddrInet port addr) = do zeroMemory p (#const sizeof(struct sockaddr_in)) #if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) @@ -1194,7 +1209,11 @@ peekSockAddr p = do family <- (#peek struct sockaddr, sa_family) p case family :: CSaFamily of (#const AF_UNIX) -> do +#if defined(HAVE_STRUCT_SOCKADDR_UN_SUN_PATH) str <- peekCAString ((#ptr struct sockaddr_un, sun_path) p) +#else + let str = "" +#endif return (SockAddrUnix str) (#const AF_INET) -> do addr <- (#peek struct sockaddr_in, sin_addr) p @@ -1440,8 +1459,11 @@ instance Read PortNumber where ------------------------------------------------------------------------ -- Helper functions -foreign import ccall unsafe "string.h" memset :: Ptr a -> CInt -> CSize -> IO () +-- | Throw an 'IOError' to signal that an operation is unsupported. +unsupported :: String -> IO a +unsupported func = ioError $ ioeSetLocation unsupportedOperation $ + "Network.Socket." ++ func -- | Zero a structure. zeroMemory :: Ptr a -> CSize -> IO () -zeroMemory dest nbytes = memset dest 0 (fromIntegral nbytes) +zeroMemory dest nbytes = fillBytes dest 0 (fromIntegral nbytes) diff --git a/Network/Socket/Unix.hsc b/Network/Socket/Unix.hsc index 2562f117..8cb66892 100644 --- a/Network/Socket/Unix.hsc +++ b/Network/Socket/Unix.hsc @@ -177,6 +177,7 @@ socketPair _ _ _ = withSystemTempFile "temp-for-pair" $ \file hdl -> do withFdSocket serverSock setNonBlockIfNeeded return (clientSock, serverSock) #else +#if HAVE_SOCKETPAIR socketPair family stype protocol = allocaBytes (2 * sizeOf (1 :: CInt)) $ \ fdArr -> do let c_stype = packSocketType stype @@ -191,4 +192,8 @@ socketPair family stype protocol = foreign import ccall unsafe "socketpair" c_socketpair :: CInt -> CInt -> CInt -> Ptr CInt -> IO CInt +#else +socketPair _ _ _ = unsupported "socketPair" +{-# WARNING socketPair "operation will throw 'IOError' \"unsupported operation\"" #-} +#endif #endif diff --git a/cbits/cmsg.c b/cbits/cmsg.c index 105dd197..b5dea985 100644 --- a/cbits/cmsg.c +++ b/cbits/cmsg.c @@ -75,6 +75,7 @@ WSARecvMsg (SOCKET s, LPWSAMSG lpMsg, LPDWORD lpdwNumberOfBytesRecvd, return res; } #else +#ifdef HAVE_STRUCT_CMSGHDR struct cmsghdr *cmsg_firsthdr(struct msghdr *mhdr) { return (CMSG_FIRSTHDR(mhdr)); } @@ -94,4 +95,5 @@ size_t cmsg_space(size_t l) { size_t cmsg_len(size_t l) { return (CMSG_LEN(l)); } +#endif #endif /* _WIN32 */ diff --git a/configure.ac b/configure.ac index c8e45cfc..c6f904a4 100644 --- a/configure.ac +++ b/configure.ac @@ -77,18 +77,28 @@ AC_CHECK_HEADERS([sys/uio.h sys/socket.h netinet/in.h netinet/tcp.h]) AC_CHECK_HEADERS([sys/un.h arpa/inet.h netdb.h]) AC_CHECK_HEADERS([net/if.h netioapi.h]) -AC_CHECK_TYPES([struct ucred]) +AC_CHECK_TYPES([struct ucred, struct addrinfo, struct cmsghdr]) AC_CHECK_FUNCS([gai_strerror gethostent accept4]) AC_CHECK_FUNCS([getpeereid]) +AC_CHECK_FUNCS([getnameinfo getaddrinfo]) +AC_CHECK_FUNCS([socket bind connect listen]) +AC_CHECK_FUNCS([sendto recvfrom]) +AC_CHECK_FUNCS([setsockopt]) +AC_CHECK_FUNCS([if_nametoindex if_indextoname]) +AC_CHECK_FUNCS([getpeername]) +AC_CHECK_FUNCS([socketpair]) +AC_CHECK_FUNCS([dup]) -AC_CHECK_DECLS([AI_ADDRCONFIG, AI_ALL, AI_NUMERICSERV, AI_V4MAPPED]) +AC_CHECK_DECLS([AI_ADDRCONFIG, AI_ALL, AI_ADDRCONFIG, AI_NUMERICHOST, AI_NUMERICSERV, AI_PASSIVE, AI_V4MAPPED]) +AC_CHECK_DECLS([NI_DGRAM, NI_NAMEREQD, NI_NOFQDN, NI_NUMERICHOST, NI_NUMERICSERV]) AC_CHECK_DECLS([IPV6_V6ONLY]) AC_CHECK_DECLS([IPPROTO_IP, IPPROTO_TCP, IPPROTO_IPV6]) AC_CHECK_DECLS([SO_PEERCRED]) AC_CHECK_MEMBERS([struct msghdr.msg_control, struct msghdr.msg_accrights]) AC_CHECK_MEMBERS([struct sockaddr.sa_len]) +AC_CHECK_MEMBERS([struct sockaddr_un.sun_path]) dnl This is a necessary hack AC_MSG_NOTICE([creating ./network.buildinfo]) diff --git a/include/HsNet.h b/include/HsNet.h index 6531302f..a644489e 100644 --- a/include/HsNet.h +++ b/include/HsNet.h @@ -120,6 +120,7 @@ sendFd(int sock, int outfd); extern int recvFd(int sock); +#ifdef HAVE_STRUCT_CMSGHDR extern struct cmsghdr * cmsg_firsthdr(struct msghdr *mhdr); @@ -134,8 +135,10 @@ cmsg_space(size_t l); extern size_t cmsg_len(size_t l); +#endif #endif /* _WIN32 */ +#ifdef HAVE_GETNAMEINFO INLINE int hsnet_getnameinfo(const struct sockaddr* a,socklen_t b, char* c, # if defined(_WIN32) @@ -146,7 +149,9 @@ hsnet_getnameinfo(const struct sockaddr* a,socklen_t b, char* c, { return getnameinfo(a,b,c,d,e,f,g); } +#endif +#ifdef HAVE_GETADDRINFO INLINE int hsnet_getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) @@ -159,6 +164,7 @@ hsnet_freeaddrinfo(struct addrinfo *ai) { freeaddrinfo(ai); } +#endif #ifndef IOV_MAX # define IOV_MAX 1024