network 2.6.2.1 → 2.6.3.0
raw patch · 21 files changed
+528/−315 lines, 21 filesdep +doctestdep ~base
Dependencies added: doctest
Dependency ranges changed: base
Files
- CHANGELOG.md +30/−0
- Network.hs +31/−39
- Network/BSD.hsc +15/−15
- Network/Socket.hsc +168/−153
- Network/Socket/ByteString.hsc +29/−45
- Network/Socket/ByteString/Lazy.hs +2/−0
- Network/Socket/Internal.hsc +7/−5
- Network/Socket/Types.hsc +77/−11
- README.md +3/−1
- cbits/asyncAccept.c +1/−1
- cbits/initWinSock.c +1/−1
- cbits/winSockErr.c +1/−1
- configure +13/−1
- examples/EchoClient.hs +1/−1
- examples/EchoServer.hs +3/−3
- include/HsNet.h +4/−4
- include/HsNetworkConfig.h +10/−7
- network.cabal +14/−2
- tests/Regression.hs +29/−1
- tests/Simple.hs +69/−24
- tests/doctests.hs +20/−0
CHANGELOG.md view
@@ -1,3 +1,33 @@+## Version 2.6.3.0++ * New maintainers: Evan Borden (@eborden) and Kazu Yamamoto (@kazu-yamamoto).+ The maintainer for a long period, Johan Tibell (@tibbe) stepped down.+ Thank you, Johan, for your hard work for a long time.++ * New APIs: ntohl, htonl,hostAddressToTuple{,6} and tupleToHostAddress{,6}.+ [#210](https://github.com/haskell/network/pull/210)++ * Added a Read instance for PortNumber. [#145](https://github.com/haskell/network/pull/145)++ * We only set the IPV6_V6ONLY flag to 0 for stream and datagram socket types,+ as opposed to all of them. This makes it possible to use ICMPv6.+ [#180](https://github.com/haskell/network/pull/180)+ [#181](https://github.com/haskell/network/pull/181)++ * Work around GHC bug #12020. Socket errors no longer cause segfaults or+ hangs on Windows. [#192](https://github.com/haskell/network/pull/192)++ * Various documentation improvements and the deprecated pragmas.+ [#186](https://github.com/haskell/network/pull/186)+ [#201](https://github.com/haskell/network/issues/201)+ [#205](https://github.com/haskell/network/pull/205)+ [#206](https://github.com/haskell/network/pull/206)+ [#211](https://github.com/haskell/network/issues/211)++ * Various internal improvements.+ [#193](https://github.com/haskell/network/pull/193)+ [#200](https://github.com/haskell/network/pull/200)+ ## Version 2.6.2.1 * Regenerate configure and HsNetworkConfig.h.in.
Network.hs view
@@ -57,11 +57,9 @@ -- ** Improving I\/O Performance over sockets {-$performance-}-- -- ** @SIGPIPE@- {-$sigpipe-} ) where +import Control.Exception (throwIO) import Control.Monad (liftM) import Data.Maybe (fromJust) import Network.BSD@@ -83,7 +81,7 @@ data PortID = Service String -- Service Name eg "ftp" | PortNumber PortNumber -- User defined Port Number-#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) | UnixSocket String -- Unix family socket in file system #endif deriving (Show, Eq)@@ -130,7 +128,7 @@ ) #endif -#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) connectTo _ (UnixSocket path) = do bracketOnError (socket AF_UNIX Stream 0)@@ -198,7 +196,7 @@ (\sock -> do port <- getServicePortNumber serv setSocketOption sock ReuseAddr 1- bindSocket sock (SockAddrInet port iNADDR_ANY)+ bind sock (SockAddrInet port iNADDR_ANY) listen sock maxListenQueue return sock )@@ -210,20 +208,20 @@ (sClose) (\sock -> do setSocketOption sock ReuseAddr 1- bindSocket sock (SockAddrInet port iNADDR_ANY)+ bind sock (SockAddrInet port iNADDR_ANY) listen sock maxListenQueue return sock ) #endif -#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) listenOn (UnixSocket path) = bracketOnError (socket AF_UNIX Stream 0) (sClose) (\sock -> do setSocketOption sock ReuseAddr 1- bindSocket sock (SockAddrUnix path)+ bind sock (SockAddrUnix path) listen sock maxListenQueue return sock )@@ -250,7 +248,7 @@ (sClose) (\sock -> do setSocketOption sock ReuseAddr 1- bindSocket sock (addrAddress addr)+ bind sock (addrAddress addr) listen sock maxListenQueue return sock )@@ -292,9 +290,16 @@ \_ -> case addr of SockAddrInet _ a -> inet_ntoa a SockAddrInet6 _ _ a _ -> return (show a)-# if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+#if defined(mingw32_HOST_OS)+ SockAddrUnix {} -> throwIO $ userError "accept: socket address not supported on this platform."+#else SockAddrUnix a -> return a-# endif+#endif+#if defined(CAN_SOCKET_SUPPORT)+ SockAddrCan {} -> throwIO $ userError "accept: unsupported for CAN peer."+#else+ SockAddrCan {} -> throwIO $ userError "accept: socket address not supported on this platform."+#endif handle <- socketToHandle sock' ReadWriteMode let port = case addr of SockAddrInet p _ -> p@@ -302,7 +307,7 @@ _ -> -1 return (handle, peer, port) #endif-#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) accept sock@(MkSocket _ AF_UNIX _ _ _) = do ~(sock', (SockAddrUnix path)) <- Socket.accept sock handle <- socketToHandle sock' ReadWriteMode@@ -312,10 +317,10 @@ error $ "Sorry, address family " ++ (show family) ++ " is not supported!" --- | Close the socket. All future operations on the socket object will fail.--- The remote end will receive no more data (after queued data is flushed).+-- | Close the socket. Sending data to or receiving data from closed socket+-- may lead to undefined behaviour. sClose :: Socket -> IO ()-sClose = close -- Explicit redefinition because Network.sClose is deperecated,+sClose = close -- Explicit redefinition because Network.sClose is deprecated, -- hence the re-export would also be marked as such. -- -----------------------------------------------------------------------------@@ -395,17 +400,19 @@ socketPort :: Socket -> IO PortID socketPort s = do sockaddr <- getSocketName s- return (portID sockaddr)- where- portID sa =- case sa of- SockAddrInet port _ -> PortNumber port+ case sockaddr of+ SockAddrInet port _ -> return $ PortNumber port #if defined(IPV6_SOCKET_SUPPORT)- SockAddrInet6 port _ _ _ -> PortNumber port+ SockAddrInet6 port _ _ _ -> return $ PortNumber port+#else+ SockAddrInet6 {} -> throwIO $ userError "socketPort: socket address not supported on this platform." #endif-#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)- SockAddrUnix path -> UnixSocket path+#if defined(mingw32_HOST_OS)+ SockAddrUnix {} -> throwIO $ userError "socketPort: socket address not supported on this platform."+#else+ SockAddrUnix path -> return $ UnixSocket path #endif+ SockAddrCan {} -> throwIO $ userError "socketPort: CAN address not supported." -- --------------------------------------------------------------------------- -- Utils@@ -437,21 +444,6 @@ For really fast I\/O, it might be worth looking at the 'hGetBuf' and 'hPutBuf' family of functions in "System.IO".--}--{-$sigpipe--On Unix, when writing to a socket and the reading end is-closed by the remote client, the program is normally sent a-@SIGPIPE@ signal by the operating system. The-default behaviour when a @SIGPIPE@ is received is-to terminate the program silently, which can be somewhat confusing-if you haven't encountered this before. The solution is to-specify that @SIGPIPE@ is to be ignored, using-the POSIX library:--> import Posix-> main = do installHandler sigPIPE Ignore Nothing; ... -} catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a
Network/BSD.hsc view
@@ -27,7 +27,7 @@ , getHostByAddr , hostAddress -#if defined(HAVE_GETHOSTENT) && !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+#if defined(HAVE_GETHOSTENT) && !defined(mingw32_HOST_OS) , getHostEntries -- ** Low level functionality@@ -43,7 +43,7 @@ , getServiceByPort , getServicePortNumber -#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) , getServiceEntries -- ** Low level functionality@@ -61,7 +61,7 @@ , getProtocolNumber , defaultProtocol -#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) , getProtocolEntries -- ** Low level functionality , setProtocolEntry@@ -77,7 +77,7 @@ , NetworkAddr , NetworkEntry(..) -#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) , getNetworkByName , getNetworkByAddr , getNetworkEntries@@ -99,7 +99,7 @@ import Control.Concurrent (MVar, newMVar, withMVar) import qualified Control.Exception as E import Foreign.C.String (CString, peekCString, withCString)-#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+#if defined(HAVE_WINSOCK2_H) import Foreign.C.Types ( CShort ) #endif import Foreign.C.Types ( CInt(..), CUInt(..), CULong(..), CSize(..) )@@ -158,12 +158,12 @@ return (ServiceEntry { serviceName = s_name, serviceAliases = s_aliases,-#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)- servicePort = PortNum (fromIntegral (s_port :: CShort)),+#if defined(HAVE_WINSOCK2_H)+ servicePort = (fromIntegral (s_port :: CShort)), #else -- s_port is already in network byte order, but it -- might be the wrong size.- servicePort = PortNum (fromIntegral (s_port :: CInt)),+ servicePort = (fromIntegral (s_port :: CInt)), #endif serviceProtocol = s_proto })@@ -187,7 +187,7 @@ -- | Get the service given a 'PortNumber' and 'ProtocolName'. getServiceByPort :: PortNumber -> ProtocolName -> IO ServiceEntry-getServiceByPort (PortNum port) proto = withLock $ do+getServiceByPort port proto = withLock $ do withCString proto $ \ cstr_proto -> do throwNoSuchThingIfNull "getServiceByPort" "no such service entry" $ c_getservbyport (fromIntegral port) cstr_proto@@ -202,7 +202,7 @@ (ServiceEntry _ _ port _) <- getServiceByName name "tcp" return port -#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) getServiceEntry :: IO ServiceEntry getServiceEntry = withLock $ do throwNoSuchThingIfNull "getServiceEntry" "no such service entry"@@ -255,7 +255,7 @@ p_aliases <- (#peek struct protoent, p_aliases) p >>= peekArray0 nullPtr >>= mapM peekCString-#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+#if defined(HAVE_WINSOCK2_H) -- With WinSock, the protocol number is only a short; -- hoist it in as such, but represent it on the Haskell side -- as a CInt.@@ -298,7 +298,7 @@ (ProtocolEntry _ _ num) <- getProtocolByName proto return num -#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) getProtocolEntry :: IO ProtocolEntry -- Next Protocol Entry from DB getProtocolEntry = withLock $ do ent <- throwNoSuchThingIfNull "getProtocolEntry" "no such protocol entry"@@ -351,7 +351,7 @@ return (HostEntry { hostName = h_name, hostAliases = h_aliases,-#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+#if defined(HAVE_WINSOCK2_H) hostFamily = unpackFamily (fromIntegral (h_addrtype :: CShort)), #else hostFamily = unpackFamily h_addrtype,@@ -397,7 +397,7 @@ foreign import CALLCONV safe "gethostbyaddr" c_gethostbyaddr :: Ptr HostAddress -> CInt -> CInt -> IO (Ptr HostEntry) -#if defined(HAVE_GETHOSTENT) && !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+#if defined(HAVE_GETHOSTENT) && !defined(mingw32_HOST_OS) getHostEntry :: IO HostEntry getHostEntry = withLock $ do throwNoSuchThingIfNull "getHostEntry" "unable to retrieve host entry"@@ -463,7 +463,7 @@ poke _p = error "Storable.poke(BSD.NetEntry) not implemented" -#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+#if !defined(mingw32_HOST_OS) getNetworkByName :: NetworkName -> IO NetworkEntry getNetworkByName name = withLock $ do withCString name $ \ name_cstr -> do
Network/Socket.hsc view
@@ -36,11 +36,17 @@ , isSupportedSockAddr , SocketStatus(..) , HostAddress+ , hostAddressToTuple+ , tupleToHostAddress #if defined(IPV6_SOCKET_SUPPORT) , HostAddress6+ , hostAddress6ToTuple+ , tupleToHostAddress6 , FlowInfo , ScopeID #endif+ , htonl+ , ntohl , ShutdownCmd(..) , ProtocolNumber , defaultProtocol@@ -83,7 +89,7 @@ #if defined(HAVE_STRUCT_UCRED) || defined(HAVE_GETPEEREID) -- get the credentials of our domain socket peer. , getPeerCred-#if defined(HAVE_GETPEEREID) +#if defined(HAVE_GETPEEREID) , getPeerEid #endif #endif@@ -93,19 +99,21 @@ , socketToHandle -- ** Sending and receiving data+ -- *** Sending and receiving with String -- $sendrecv- , sendTo- , sendBufTo-- , recvFrom- , recvBufFrom- , send+ , sendTo , recv+ , recvFrom , recvLen++ -- *** Sending and receiving with a buffer , sendBuf , recvBuf+ , sendBufTo+ , recvBufFrom + -- ** Misc , inet_addr , inet_ntoa @@ -174,7 +182,8 @@ ) where import Data.Bits-import Data.List (delete, foldl')+import Data.Functor+import Data.List (foldl') import Data.Maybe (isJust) import Data.Word (Word8, Word32) import Foreign.Ptr (Ptr, castPtr, nullPtr)@@ -190,7 +199,6 @@ import System.IO import Control.Monad (liftM, when) -import qualified Control.Exception as E import Control.Concurrent.MVar import Data.Typeable import System.IO.Error@@ -200,20 +208,25 @@ import GHC.Conc (closeFdWith) ##endif # if defined(mingw32_HOST_OS)+import qualified Control.Exception as E import GHC.Conc (asyncDoProc)+import GHC.IO.FD (FD(..), readRawBufferPtr, writeRawBufferPtr) import Foreign (FunPtr) # endif+# if defined(darwin_HOST_OS)+import Data.List (delete)+# endif import qualified GHC.IO.Device import GHC.IO.Handle.FD import GHC.IO.Exception import GHC.IO import qualified System.Posix.Internals -import GHC.IO.FD- import Network.Socket.Internal import Network.Socket.Types +import Prelude -- Silence AMP warnings+ -- | Either a host name e.g., @\"haskell.org\"@ or a numeric host -- address string consisting of a dotted decimal IPv4 address or an -- IPv6 address e.g., @\"192.168.0.1\"@.@@ -288,6 +301,9 @@ . showString "]:" . shows port #endif+#if defined(CAN_SOCKET_SUPPORT)+ showsPrec _ (SockAddrCan ifidx) = shows ifidx+#endif ----------------------------------------------------------------------------- -- Connection Functions@@ -303,8 +319,20 @@ -- and protocol number. The address family is usually 'AF_INET', -- 'AF_INET6', or 'AF_UNIX'. The socket type is usually 'Stream' or -- 'Datagram'. The protocol number is usually 'defaultProtocol'.--- If 'AF_INET6' is used, the 'IPv6Only' socket option is set to 0--- so that both IPv4 and IPv6 can be handled with one socket.+-- If 'AF_INET6' is used and the socket type is 'Stream' or 'Datagram',+-- the 'IPv6Only' socket option is set to 0 so that both IPv4 and IPv6+-- can be handled with one socket.+--+-- >>> let hints = defaultHints { addrFlags = [AI_NUMERICHOST, AI_NUMERICSERV], addrSocketType = Stream }+-- >>> addr:_ <- getAddrInfo (Just hints) (Just "127.0.0.1") (Just "5000")+-- >>> sock@(MkSocket _ fam stype _ _) <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)+-- >>> fam+-- AF_INET+-- >>> stype+-- Stream+-- >>> bind sock (addrAddress addr)+-- >>> getSocketName sock+-- 127.0.0.1:5000 socket :: Family -- Family Name (usually AF_INET) -> SocketType -- Socket Type (usually Stream) -> ProtocolNumber -- Protocol Number (getProtocolByName to find value)@@ -318,13 +346,16 @@ withSocketsDo $ return () let sock = MkSocket fd family stype protocol socket_status #if HAVE_DECL_IPV6_V6ONLY+ -- The default value of the IPv6Only option is platform specific,+ -- so we explicitly set it to 0 to provide a common default. # if defined(mingw32_HOST_OS)- -- the IPv6Only option is only supported on Windows Vista and later,- -- so trying to change it might throw an error- when (family == AF_INET6) $- E.catch (setSocketOption sock IPv6Only 0) $ (\(_ :: E.IOException) -> return ())+ -- The IPv6Only option is only supported on Windows Vista and later,+ -- so trying to change it might throw an error.+ when (family == AF_INET6 && (stype == Stream || stype == Datagram)) $+ E.catch (setSocketOption sock IPv6Only 0) $ (\(_ :: E.IOException) -> return ()) # else- when (family == AF_INET6) $ setSocketOption sock IPv6Only 0+ when (family == AF_INET6 && (stype == Stream || stype == Datagram)) $+ setSocketOption sock IPv6Only 0 `onException` close sock # endif #endif return sock@@ -405,7 +436,7 @@ r <- c_connect s p_addr (fromIntegral sz) if r == -1 then do-#if !(defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS))+#if !(defined(HAVE_WINSOCK2_H)) err <- getErrno case () of _ | err == eINTR -> connectLoop@@ -520,11 +551,11 @@ -- $sendrecv ----- Do not use the @send@ and @recv@ functions defined in this module+-- Do not use the @send@ and @recv@ functions defined in this section -- in new code, as they incorrectly represent binary data as a Unicode -- string. As a result, these functions are inefficient and may lead -- to bugs in the program. Instead use the @send@ and @recv@--- functions defined in the 'Network.Socket.ByteString' module.+-- functions defined in the "Network.Socket.ByteString" module. ----------------------------------------------------------------------------- -- sendTo & recvFrom@@ -536,6 +567,7 @@ -- -- NOTE: blocking on Windows unless you compile with -threaded (see -- GHC ticket #1129)+{-# WARNING sendTo "Use sendTo defined in \"Network.Socket.ByteString\"" #-} sendTo :: Socket -- (possibly) bound/connected Socket -> String -- Data to send -> SockAddr@@ -567,6 +599,7 @@ -- -- NOTE: blocking on Windows unless you compile with -threaded (see -- GHC ticket #1129)+{-# WARNING recvFrom "Use recvFrom defined in \"Network.Socket.ByteString\"" #-} recvFrom :: Socket -> Int -> IO (String, Int, SockAddr) recvFrom sock nbytes = allocaBytes nbytes $ \ptr -> do@@ -613,27 +646,20 @@ -- | Send data to the socket. The socket must be connected to a remote -- socket. Returns the number of bytes sent. Applications are -- responsible for ensuring that all data has been sent.+--+-- Sending data to closed socket may lead to undefined behaviour.+{-# WARNING send "Use send defined in \"Network.Socket.ByteString\"" #-} send :: Socket -- Bound/Connected Socket -> String -- Data to send -> IO Int -- Number of Bytes sent-send sock@(MkSocket s _family _stype _protocol _status) xs = do- withCStringLen xs $ \(str, len) -> do- liftM fromIntegral $-#if defined(mingw32_HOST_OS)- writeRawBufferPtr- "Network.Socket.send"- (socket2FD sock)- (castPtr str)- 0- (fromIntegral len)-#else- throwSocketErrorWaitWrite sock "send" $- c_send s str (fromIntegral len) 0{-flags-}-#endif+send sock xs = withCStringLen xs $ \(str, len) ->+ sendBuf sock (castPtr str) len -- | Send data to the socket. The socket must be connected to a remote -- socket. Returns the number of bytes sent. Applications are -- responsible for ensuring that all data has been sent.+--+-- Sending data to closed socket may lead to undefined behaviour. sendBuf :: Socket -- Bound/Connected Socket -> Ptr Word8 -- Pointer to the data to send -> Int -- Length of the buffer@@ -641,7 +667,11 @@ sendBuf sock@(MkSocket s _family _stype _protocol _status) str len = do liftM fromIntegral $ #if defined(mingw32_HOST_OS)- writeRawBufferPtr+-- writeRawBufferPtr is supposed to handle checking for errors, but it's broken+-- on x86_64 because of GHC bug #12010 so we duplicate the check here. The call+-- to throwSocketErrorIfMinus1Retry can be removed when no GHC version with the+-- bug is supported.+ throwSocketErrorIfMinus1Retry "Network.Socket.sendBuf" $ writeRawBufferPtr "Network.Socket.sendBuf" (socket2FD sock) (castPtr str)@@ -664,28 +694,19 @@ -- -- For TCP sockets, a zero length return value means the peer has -- closed its half side of the connection.+--+-- Receiving data from closed socket may lead to undefined behaviour.+{-# WARNING recv "Use recv defined in \"Network.Socket.ByteString\"" #-} recv :: Socket -> Int -> IO String-recv sock l = recvLen sock l >>= \ (s,_) -> return s+recv sock l = fst <$> recvLen sock l +{-# WARNING recvLen "Use recvLen defined in \"Network.Socket.ByteString\"" #-} recvLen :: Socket -> Int -> IO (String, Int)-recvLen sock@(MkSocket s _family _stype _protocol _status) nbytes- | nbytes <= 0 = ioError (mkInvalidRecvArgError "Network.Socket.recv")- | otherwise = do+recvLen sock nbytes = allocaBytes nbytes $ \ptr -> do- len <--#if defined(mingw32_HOST_OS)- readRawBufferPtr "Network.Socket.recvLen" (socket2FD sock) ptr 0- (fromIntegral nbytes)-#else- throwSocketErrorWaitRead sock "recv" $- c_recv s ptr (fromIntegral nbytes) 0{-flags-}-#endif- let len' = fromIntegral len- if len' == 0- then ioError (mkEOFError "Network.Socket.recv")- else do- s' <- peekCStringLen (castPtr ptr,len')- return (s', len')+ len <- recvBuf sock ptr nbytes+ s <- peekCStringLen (castPtr ptr,len)+ return (s, len) -- | Receive data from the socket. The socket must be in a connected -- state. This function may return fewer bytes than specified. If the@@ -698,17 +719,18 @@ -- -- For TCP sockets, a zero length return value means the peer has -- closed its half side of the connection.+--+-- Receiving data from closed socket may lead to undefined behaviour. recvBuf :: Socket -> Ptr Word8 -> Int -> IO Int-recvBuf sock p l = recvLenBuf sock p l--recvLenBuf :: Socket -> Ptr Word8 -> Int -> IO Int-recvLenBuf sock@(MkSocket s _family _stype _protocol _status) ptr nbytes+recvBuf sock@(MkSocket s _family _stype _protocol _status) ptr nbytes | nbytes <= 0 = ioError (mkInvalidRecvArgError "Network.Socket.recvBuf") | otherwise = do len <- #if defined(mingw32_HOST_OS)- readRawBufferPtr "Network.Socket.recvLenBuf" (socket2FD sock) ptr 0- (fromIntegral nbytes)+-- see comment in sendBuf above.+ throwSocketErrorIfMinus1Retry "Network.Socket.recvBuf" $+ readRawBufferPtr "Network.Socket.recvBuf"+ (socket2FD sock) ptr 0 (fromIntegral nbytes) #else throwSocketErrorWaitRead sock "recvBuf" $ c_recv s (castPtr ptr) (fromIntegral nbytes) 0{-flags-}@@ -946,11 +968,10 @@ getPeerCred sock = do #ifdef HAVE_STRUCT_UCRED let fd = fdSocket sock- let sz = (fromIntegral (#const sizeof(struct ucred)))- with sz $ \ ptr_cr ->- alloca $ \ ptr_sz -> do- poke ptr_sz sz- throwSocketErrorIfMinus1Retry "getPeerCred" $+ let sz = (#const sizeof(struct ucred))+ allocaBytes sz $ \ ptr_cr ->+ with (fromIntegral sz) $ \ ptr_sz -> do+ _ <- ($) throwSocketErrorIfMinus1Retry "getPeerCred" $ c_getsockopt fd (#const SOL_SOCKET) (#const SO_PEERCRED) ptr_cr ptr_sz pid <- (#peek struct ucred, pid) ptr_cr uid <- (#peek struct ucred, uid) ptr_cr@@ -965,7 +986,7 @@ -- | The getpeereid() function returns the effective user and group IDs of the -- peer connected to a UNIX-domain socket getPeerEid :: Socket -> IO (CUInt, CUInt)-getPeerEid sock = do +getPeerEid sock = do let fd = fdSocket sock alloca $ \ ptr_uid -> alloca $ \ ptr_gid -> do@@ -986,7 +1007,7 @@ -- for transmitting file descriptors, mainly. sendFd :: Socket -> CInt -> IO () sendFd sock outfd = do- throwSocketErrorWaitWrite sock "sendFd" $+ _ <- ($) throwSocketErrorWaitWrite sock "sendFd" $ c_sendFd (fdSocket sock) outfd -- Note: If Winsock supported FD-passing, thi would have been -- incorrect (since socket FDs need to be closed via closesocket().)@@ -1014,7 +1035,10 @@ iNADDR_ANY :: HostAddress iNADDR_ANY = htonl (#const INADDR_ANY) +-- | Converts the from host byte order to network byte order. foreign import CALLCONV unsafe "htonl" htonl :: Word32 -> Word32+-- | Converts the from network byte order to host byte order.+foreign import CALLCONV unsafe "ntohl" ntohl :: Word32 -> Word32 #if defined(IPV6_SOCKET_SUPPORT) -- | The IPv6 wild card address.@@ -1065,9 +1089,8 @@ -- ----------------------------------------------------------------------------- --- | Close the socket. All future operations on the socket object--- will fail. The remote end will receive no more data (after queued--- data is flushed).+-- | Close the socket. Sending data to or receiving data from closed socket+-- may lead to undefined behaviour. close :: Socket -> IO () close (MkSocket s _ _ _ socketStatus) = do modifyMVar_ socketStatus $ \ status ->@@ -1186,13 +1209,37 @@ #if defined(IPV6_SOCKET_SUPPORT) -- | Flags that control the querying behaviour of 'getAddrInfo'.-data AddrInfoFlag- = AI_ADDRCONFIG+-- For more information, see <https://tools.ietf.org/html/rfc3493#page-25>+data AddrInfoFlag =+ -- | The list of returned 'AddrInfo' values will+ -- only contain IPv4 addresses if the local system has at least+ -- one IPv4 interface configured, and likewise for IPv6.+ -- (Only some platforms support this.)+ AI_ADDRCONFIG+ -- | If 'AI_ALL' is specified, return all matching IPv6 and+ -- IPv4 addresses. Otherwise, this flag has no effect.+ -- (Only some platforms support this.) | AI_ALL+ -- | The 'addrCanonName' field of the first returned+ -- 'AddrInfo' will contain the "canonical name" of the host. | AI_CANONNAME+ -- | The 'HostName' argument /must/ be a numeric+ -- address in string form, and network name lookups will not be+ -- attempted. | AI_NUMERICHOST+ -- | The 'ServiceName' argument /must/ be a port+ -- number in string form, and service name lookups will not be+ -- attempted. (Only some platforms support this.) | AI_NUMERICSERV+ -- | If no 'HostName' value is provided, the network+ -- address in each 'SockAddr'+ -- will be left as a "wild card", i.e. as either 'iNADDR_ANY'+ -- or 'iN6ADDR_ANY'. This is useful for server applications that+ -- will accept connections from any client. | AI_PASSIVE+ -- | If an IPv6 lookup is performed, and no IPv6+ -- addresses are found, IPv6-mapped IPv4 addresses will be+ -- returned. (Only some platforms support this.) | AI_V4MAPPED deriving (Eq, Read, Show, Typeable) @@ -1283,11 +1330,27 @@ (#poke struct addrinfo, ai_canonname) p nullPtr (#poke struct addrinfo, ai_next) p nullPtr -data NameInfoFlag- = NI_DGRAM+-- | Flags that control the querying behaviour of 'getNameInfo'.+-- For more information, see <https://tools.ietf.org/html/rfc3493#page-30>+data NameInfoFlag =+ -- | Resolve a datagram-based service name. This is+ -- required only for the few protocols that have different port+ -- numbers for their datagram-based versions than for their+ -- stream-based versions.+ NI_DGRAM+ -- | If the hostname cannot be looked up, an IO error is thrown. | NI_NAMEREQD+ -- | If a host is local, return only the hostname part of the FQDN. | NI_NOFQDN+ -- | The name of the host is not looked up.+ -- Instead, a numeric representation of the host's+ -- address is returned. For an IPv4 address, this will be a+ -- dotted-quad string. For IPv6, it will be colon-separated+ -- hexadecimal. | NI_NUMERICHOST+ -- | The name of the service is not+ -- looked up. Instead, a numeric representation of the+ -- service is returned. | NI_NUMERICSERV deriving (Eq, Read, Show, Typeable) @@ -1302,9 +1365,17 @@ -- | Default hints for address lookup with 'getAddrInfo'. The values -- of the 'addrAddress' and 'addrCanonName' fields are 'undefined', -- and are never inspected by 'getAddrInfo'.+--+-- >>> addrFlags defaultHints+-- []+-- >>> addrFamily defaultHints+-- AF_UNSPEC+-- >>> addrSocketType defaultHints+-- NoSocketType+-- >>> addrProtocol defaultHints+-- 0 defaultHints :: AddrInfo- defaultHints = AddrInfo { addrFlags = [], addrFamily = AF_UNSPEC,@@ -1327,45 +1398,7 @@ -- using Haskell's record update syntax on 'defaultHints', for example -- as follows: ----- @--- myHints = defaultHints { addrFlags = [AI_ADDRCONFIG, AI_CANONNAME] }--- @------ Values for 'addrFlags' control query behaviour. The supported--- flags are as follows:------ [@AI_PASSIVE@] If no 'HostName' value is provided, the network--- address in each 'SockAddr'--- will be left as a "wild card", i.e. as either 'iNADDR_ANY'--- or 'iN6ADDR_ANY'. This is useful for server applications that--- will accept connections from any client.------ [@AI_CANONNAME@] The 'addrCanonName' field of the first returned--- 'AddrInfo' will contain the "canonical name" of the host.------ [@AI_NUMERICHOST@] The 'HostName' argument /must/ be a numeric--- address in string form, and network name lookups will not be--- attempted.------ /Note/: Although the following flags are required by RFC 3493, they--- may not have an effect on all platforms, because the underlying--- network stack may not support them. To see whether a flag from the--- list below will have any effect, call 'addrInfoFlagImplemented'.------ [@AI_NUMERICSERV@] The 'ServiceName' argument /must/ be a port--- number in string form, and service name lookups will not be--- attempted.------ [@AI_ADDRCONFIG@] The list of returned 'AddrInfo' values will--- only contain IPv4 addresses if the local system has at least--- one IPv4 interface configured, and likewise for IPv6.------ [@AI_V4MAPPED@] If an IPv6 lookup is performed, and no IPv6--- addresses are found, IPv6-mapped IPv4 addresses will be--- returned.------ [@AI_ALL@] If 'AI_ALL' is specified, return all matching IPv6 and--- IPv4 addresses. Otherwise, this flag has no effect.+-- >>> let hints = defaultHints { addrFlags = [AI_NUMERICHOST], addrSocketType = Stream } -- -- You must provide a 'Just' value for at least one of the 'HostName' -- or 'ServiceName' arguments. 'HostName' can be either a numeric@@ -1388,14 +1421,9 @@ -- for @getaddrinfo@ in RFC 2553. The 'AddrInfo' parameter comes first -- to make partial application easier. ----- Example:--- @--- let hints = defaultHints { addrFlags = [AI_ADDRCONFIG, AI_CANONNAME] }--- addrs <- getAddrInfo (Just hints) (Just "www.haskell.org") (Just "http")--- let addr = head addrs--- sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)--- connect sock (addrAddress addr)--- @+-- >>> addr:_ <- getAddrInfo (Just hints) (Just "127.0.0.1") (Just "http")+-- >>> addrAddress addr+-- 127.0.0.1:80 getAddrInfo :: Maybe AddrInfo -- ^ preferred socket type or protocol -> Maybe HostName -- ^ host name to look up@@ -1461,35 +1489,7 @@ -- | Resolve an address to a host or service name. -- This function is protocol independent.------ The list of 'NameInfoFlag' values controls query behaviour. The--- supported flags are as follows:------ [@NI_NOFQDN@] If a host is local, return only the--- hostname part of the FQDN.------ [@NI_NUMERICHOST@] The name of the host is not--- looked up. Instead, a numeric representation of the host's--- address is returned. For an IPv4 address, this will be a--- dotted-quad string. For IPv6, it will be colon-separated--- hexadecimal.------ [@NI_NUMERICSERV@] The name of the service is not--- looked up. Instead, a numeric representation of the--- service is returned.------ [@NI_NAMEREQD@] If the hostname cannot be looked up, an IO error--- is thrown.------ [@NI_DGRAM@] Resolve a datagram-based service name. This is--- required only for the few protocols that have different port--- numbers for their datagram-based versions than for their--- stream-based versions.------ Hostname and service name lookups can be expensive. You can--- specify which lookups to perform via the two 'Bool' arguments. If--- one of these is 'False', the corresponding value in the returned--- tuple will be 'Nothing', and no lookup will be performed.+-- The list of 'NameInfoFlag' values controls query behaviour. -- -- If a host or service's name cannot be looked up, then the numeric -- form of the address or service will be returned.@@ -1568,11 +1568,12 @@ c_bind :: CInt -> Ptr SockAddr -> CInt{-CSockLen???-} -> IO CInt foreign import CALLCONV SAFE_ON_WIN "connect" c_connect :: CInt -> Ptr SockAddr -> CInt{-CSockLen???-} -> IO CInt-foreign import CALLCONV unsafe "accept"- c_accept :: CInt -> Ptr SockAddr -> Ptr CInt{-CSockLen???-} -> IO CInt #ifdef HAVE_ACCEPT4 foreign import CALLCONV unsafe "accept4" c_accept4 :: CInt -> Ptr SockAddr -> Ptr CInt{-CSockLen???-} -> CInt -> IO CInt+#else+foreign import CALLCONV unsafe "accept"+ c_accept :: CInt -> Ptr SockAddr -> Ptr CInt{-CSockLen???-} -> IO CInt #endif foreign import CALLCONV unsafe "listen" c_listen :: CInt -> CInt -> IO CInt@@ -1614,31 +1615,45 @@ -- These aliases are deprecated and should not be used in new code. -- They will be removed in some future version of the package. +{-# DEPRECATED bindSocket "use 'bind'" #-}+ -- | Deprecated alias for 'bind'. bindSocket :: Socket -- Unconnected Socket -> SockAddr -- Address to Bind to -> IO () bindSocket = bind +{-# DEPRECATED sClose "use 'close'" #-}+ -- | Deprecated alias for 'close'. sClose :: Socket -> IO () sClose = close +{-# DEPRECATED sIsConnected "use 'isConnected'" #-}+ -- | Deprecated alias for 'isConnected'. sIsConnected :: Socket -> IO Bool sIsConnected = isConnected +{-# DEPRECATED sIsBound "use 'isBound'" #-}+ -- | Deprecated alias for 'isBound'. sIsBound :: Socket -> IO Bool sIsBound = isBound +{-# DEPRECATED sIsListening "use 'isListening'" #-}+ -- | Deprecated alias for 'isListening'. sIsListening :: Socket -> IO Bool sIsListening = isListening +{-# DEPRECATED sIsReadable "use 'isReadable'" #-}+ -- | Deprecated alias for 'isReadable'. sIsReadable :: Socket -> IO Bool sIsReadable = isReadable++{-# DEPRECATED sIsWritable "use 'isWritable'" #-} -- | Deprecated alias for 'isWritable'. sIsWritable :: Socket -> IO Bool
Network/Socket/ByteString.hsc view
@@ -43,15 +43,13 @@ -- $example ) where -import Control.Monad (liftM, when)+import Control.Monad (when) import Data.ByteString (ByteString) import Data.ByteString.Internal (createAndTrim) import Data.ByteString.Unsafe (unsafeUseAsCStringLen)-import Data.Word (Word8)-import Foreign.C.Types (CInt(..)) import Foreign.Marshal.Alloc (allocaBytes)-import Foreign.Ptr (Ptr, castPtr)-import Network.Socket (SockAddr, Socket(..), sendBufTo, recvBufFrom)+import Foreign.Ptr (castPtr)+import Network.Socket (sendBuf, sendBufTo, recvBuf, recvBufFrom) import qualified Data.ByteString as B @@ -60,53 +58,38 @@ import Network.Socket.Types #if !defined(mingw32_HOST_OS)-import Control.Monad (zipWithM_)-import Foreign.C.Types (CChar)-import Foreign.C.Types (CSize(..))+import Control.Monad (liftM, zipWithM_) import Foreign.Marshal.Array (allocaArray) import Foreign.Marshal.Utils (with)-import Foreign.Ptr (plusPtr)+import Foreign.Ptr (Ptr, plusPtr) import Foreign.Storable (Storable(..)) import Network.Socket.ByteString.IOVec (IOVec(..)) import Network.Socket.ByteString.MsgHdr (MsgHdr(..)) -#else-import GHC.IO.FD #endif -#if !defined(mingw32_HOST_OS)-foreign import CALLCONV unsafe "send"- c_send :: CInt -> Ptr a -> CSize -> CInt -> IO CInt-foreign import CALLCONV unsafe "recv"- c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt-#endif- -- ---------------------------------------------------------------------------- -- Sending -- | Send data to the socket. The socket must be connected to a -- remote socket. Returns the number of bytes sent. Applications are -- responsible for ensuring that all data has been sent.+--+-- Sending data to closed socket may lead to undefined behaviour. send :: Socket -- ^ Connected socket -> ByteString -- ^ Data to send -> IO Int -- ^ Number of bytes sent-send sock@(MkSocket s _ _ _ _) xs =- unsafeUseAsCStringLen xs $ \(str, len) ->- liftM fromIntegral $-#if defined(mingw32_HOST_OS)- writeRawBufferPtr "Network.Socket.ByteString.send"- (FD s 1) (castPtr str) 0 (fromIntegral len)-#else- throwSocketErrorWaitWrite sock "send" $- c_send s str (fromIntegral len) 0-#endif+send sock xs = unsafeUseAsCStringLen xs $ \(str, len) ->+ sendBuf sock (castPtr str) len -- | Send data to the socket. The socket must be connected to a -- remote socket. Unlike 'send', this function continues to send data -- until either all data has been sent or an error occurs. On error, -- an exception is raised, and there is no way to determine how much -- data, if any, was successfully sent.+--+-- Sending data to closed socket may lead to undefined behaviour. sendAll :: Socket -- ^ Connected socket -> ByteString -- ^ Data to send -> IO ()@@ -118,6 +101,8 @@ -- explicitly, so the socket need not be in a connected state. -- Returns the number of bytes sent. Applications are responsible for -- ensuring that all data has been sent.+--+-- Sending data to closed socket may lead to undefined behaviour. sendTo :: Socket -- ^ Socket -> ByteString -- ^ Data to send -> SockAddr -- ^ Recipient address@@ -131,6 +116,8 @@ -- data has been sent or an error occurs. On error, an exception is -- raised, and there is no way to determine how much data, if any, was -- successfully sent.+--+-- Sending data to closed socket may lead to undefined behaviour. sendAllTo :: Socket -- ^ Socket -> ByteString -- ^ Data to send -> SockAddr -- ^ Recipient address@@ -167,6 +154,8 @@ -- sent or an error occurs. On error, an exception is raised, and -- there is no way to determine how much data, if any, was -- successfully sent.+--+-- Sending data to closed socket may lead to undefined behaviour. sendMany :: Socket -- ^ Connected socket -> [ByteString] -- ^ Data to send -> IO ()@@ -190,6 +179,8 @@ -- continues to send data until either all data has been sent or an -- error occurs. On error, an exception is raised, and there is no -- way to determine how much data, if any, was successfully sent.+--+-- Sending data to closed socket may lead to undefined behaviour. sendManyTo :: Socket -- ^ Socket -> [ByteString] -- ^ Data to send -> SockAddr -- ^ Recipient address@@ -226,29 +217,22 @@ -- -- For TCP sockets, a zero length return value means the peer has -- closed its half side of the connection.+--+-- Receiving data from closed socket may lead to undefined behaviour. recv :: Socket -- ^ Connected socket -> Int -- ^ Maximum number of bytes to receive -> IO ByteString -- ^ Data received recv sock nbytes | nbytes < 0 = ioError (mkInvalidRecvArgError "Network.Socket.ByteString.recv")- | otherwise = createAndTrim nbytes $ recvInner sock nbytes--recvInner :: Socket -> Int -> Ptr Word8 -> IO Int-recvInner sock nbytes ptr =- fmap fromIntegral $-#if defined(mingw32_HOST_OS)- readRawBufferPtr "Network.Socket.ByteString.recv" (FD s 1) ptr 0 (fromIntegral nbytes)-#else- throwSocketErrorWaitRead sock "recv" $- c_recv s (castPtr ptr) (fromIntegral nbytes) 0-#endif- where- s = sockFd sock+ | otherwise = createAndTrim nbytes $ \ptr ->+ recvBuf sock ptr nbytes -- | Receive data from the socket. The socket need not be in a -- connected state. Returns @(bytes, address)@ where @bytes@ is a -- 'ByteString' representing the data received and @address@ is a -- 'SockAddr' representing the address of the sending socket.+--+-- Receiving data from closed socket may lead to undefined behaviour. recvFrom :: Socket -- ^ Socket -> Int -- ^ Maximum number of bytes to receive -> IO (ByteString, SockAddr) -- ^ Data received and sender address@@ -318,12 +302,12 @@ -- > Nothing (Just "3000") -- > let serveraddr = head addrinfos -- > sock <- socket (addrFamily serveraddr) Stream defaultProtocol--- > bindSocket sock (addrAddress serveraddr)+-- > bind sock (addrAddress serveraddr) -- > listen sock 1 -- > (conn, _) <- accept sock -- > talk conn--- > sClose conn--- > sClose sock+-- > close conn+-- > close sock -- > -- > where -- > talk :: Socket -> IO ()@@ -346,6 +330,6 @@ -- > connect sock (addrAddress serveraddr) -- > sendAll sock $ C.pack "Hello, world!" -- > msg <- recv sock 1024--- > sClose sock+-- > close sock -- > putStr "Received " -- > C.putStrLn msg
Network/Socket/ByteString/Lazy.hs view
@@ -77,6 +77,8 @@ -- until a message arrives. -- -- If there is no more data to be received, returns an empty 'ByteString'.+--+-- Receiving data from closed socket may lead to undefined behaviour. recv :: Socket -- ^ Connected socket -> Int64 -- ^ Maximum number of bytes to receive -> IO ByteString -- ^ Data received
Network/Socket/Internal.hsc view
@@ -43,7 +43,7 @@ , Family(..) -- * Socket error functions-#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+#if defined(HAVE_WINSOCK2_H) , c_getLastError #endif , throwSocketError@@ -72,12 +72,14 @@ import Foreign.C.Error (throwErrno, throwErrnoIfMinus1Retry, throwErrnoIfMinus1RetryMayBlock, throwErrnoIfMinus1_, Errno(..), errnoToIOError)+#if defined(HAVE_WINSOCK2_H) import Foreign.C.String (peekCString)-import Foreign.C.Types (CInt(..)) import Foreign.Ptr (Ptr)+#endif+import Foreign.C.Types (CInt(..)) import GHC.Conc (threadWaitRead, threadWaitWrite) -#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+#if defined(HAVE_WINSOCK2_H) import Control.Exception ( evaluate ) import System.IO.Unsafe ( unsafePerformIO ) import Control.Monad ( when )@@ -154,7 +156,7 @@ {-# SPECIALIZE throwSocketErrorIfMinus1RetryMayBlock :: String -> IO b -> IO CInt -> IO CInt #-} -#if (!defined(HAVE_WINSOCK2_H) || defined(cygwin32_HOST_OS))+#if (!defined(HAVE_WINSOCK2_H)) throwSocketErrorIfMinus1RetryMayBlock name on_block act = throwErrnoIfMinus1RetryMayBlock name act on_block@@ -177,7 +179,7 @@ throwSocketErrorIfMinus1Retry name act return () -# if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+# if defined(HAVE_WINSOCK2_H) throwSocketErrorIfMinus1Retry name act = do r <- act if (r == -1)
Network/Socket/Types.hsc view
@@ -34,8 +34,12 @@ , SockAddr(..) , isSupportedSockAddr , HostAddress+ , hostAddressToTuple+ , tupleToHostAddress #if defined(IPV6_SOCKET_SUPPORT) , HostAddress6+ , hostAddress6ToTuple+ , tupleToHostAddress6 , FlowInfo , ScopeID #endif@@ -55,6 +59,7 @@ ) where import Control.Concurrent.MVar+import Control.Exception (throwIO) import Control.Monad import Data.Bits import Data.Maybe@@ -729,6 +734,11 @@ -- @PortNumber@ value with the correct network-byte-ordering. You -- should not use the PortNum constructor. It will be removed in the -- next release.+--+-- >>> 1 :: PortNumber+-- 1+-- >>> read "1" :: PortNumber+-- 1 newtype PortNumber = PortNum Word16 deriving (Eq, Ord, Typeable) -- newtyped to prevent accidental use of sane-looking -- port numbers that haven't actually been converted to@@ -739,6 +749,9 @@ instance Show PortNumber where showsPrec p pn = showsPrec p (portNumberToInt pn) +instance Read PortNumber where+ readsPrec n = map (\(x,y) -> (intToPortNumber x, y)) . readsPrec n+ intToPortNumber :: Int -> PortNumber intToPortNumber v = PortNum (htons (fromIntegral v)) @@ -747,7 +760,8 @@ foreign import CALLCONV unsafe "ntohs" ntohs :: Word16 -> Word16 foreign import CALLCONV unsafe "htons" htons :: Word16 -> Word16---foreign import CALLCONV unsafe "ntohl" ntohl :: Word32 -> Word32+foreign import CALLCONV unsafe "ntohl" ntohl :: Word32 -> Word32+foreign import CALLCONV unsafe "htonl" htonl :: Word32 -> Word32 instance Enum PortNumber where toEnum = intToPortNumber@@ -834,9 +848,12 @@ #if defined(CAN_SOCKET_SUPPORT) SockAddrCan{} -> True #endif+#if !(defined(IPV6_SOCKET_SUPPORT) \+ && defined(DOMAIN_SOCKET_SUPPORT) && defined(CAN_SOCKET_SUPPORT)) _ -> False+#endif -#if defined(WITH_WINSOCK) || defined(cygwin32_HOST_OS)+#if defined(WITH_WINSOCK) type CSaFamily = (#type unsigned short) #elif defined(darwin_HOST_OS) type CSaFamily = (#type u_char)@@ -875,6 +892,8 @@ #if defined(CAN_SOCKET_SUPPORT) sizeOfSockAddrByFamily AF_CAN = #const sizeof(struct sockaddr_can) #endif+sizeOfSockAddrByFamily family =+ error $ "sizeOfSockAddrByFamily: " ++ show family ++ " not supported." -- | Use a 'SockAddr' with a function requiring a pointer to a -- 'SockAddr' and the length of that 'SockAddr'.@@ -933,7 +952,7 @@ (#poke struct sockaddr_in6, sin6_family) p ((#const AF_INET6) :: CSaFamily) (#poke struct sockaddr_in6, sin6_port) p port (#poke struct sockaddr_in6, sin6_flowinfo) p flow- (#poke struct sockaddr_in6, sin6_addr) p addr+ (#poke struct sockaddr_in6, sin6_addr) p (In6Addr addr) (#poke struct sockaddr_in6, sin6_scope_id) p scope #endif #if defined(CAN_SOCKET_SUPPORT)@@ -962,7 +981,7 @@ (#const AF_INET6) -> do port <- (#peek struct sockaddr_in6, sin6_port) p flow <- (#peek struct sockaddr_in6, sin6_flowinfo) p- addr <- (#peek struct sockaddr_in6, sin6_addr) p+ In6Addr addr <- (#peek struct sockaddr_in6, sin6_addr) p scope <- (#peek struct sockaddr_in6, sin6_scope_id) p return (SockAddrInet6 (PortNum port) flow addr scope) #endif@@ -971,16 +990,56 @@ ifidx <- (#peek struct sockaddr_can, can_ifindex) p return (SockAddrCan ifidx) #endif+ _ -> throwIO $ userError $ "peekSockAddr: " ++ show family ++ " not supported on this platform." ------------------------------------------------------------------------ --- | Network byte order.+-- | The raw network byte order number is read using host byte order.+-- Therefore on little-endian architectures the byte order is swapped. For+-- example @127.0.0.1@ is represented as @0x0100007f@ on little-endian hosts+-- and as @0x7f000001@ on big-endian hosts.+--+-- For direct manipulation prefer 'hostAddressToTuple' and+-- 'tupleToHostAddress'. type HostAddress = Word32 +-- | Converts 'HostAddress' to representation-independent IPv4 quadruple.+-- For example for @127.0.0.1@ the function will return @(0x7f, 0, 0, 1)@+-- regardless of host endianness.+hostAddressToTuple :: HostAddress -> (Word8, Word8, Word8, Word8)+hostAddressToTuple ha' =+ let ha = htonl ha'+ byte i = fromIntegral (ha `shiftR` i) :: Word8+ in (byte 24, byte 16, byte 8, byte 0)++-- | Converts IPv4 quadruple to 'HostAddress'.+tupleToHostAddress :: (Word8, Word8, Word8, Word8) -> HostAddress+tupleToHostAddress (b3, b2, b1, b0) =+ let x `sl` i = fromIntegral x `shiftL` i :: Word32+ in ntohl $ (b3 `sl` 24) .|. (b2 `sl` 16) .|. (b1 `sl` 8) .|. (b0 `sl` 0)+ #if defined(IPV6_SOCKET_SUPPORT)--- | Host byte order.+-- | Independent of endianness. For example @::1@ is stored as @(0, 0, 0, 1)@.+--+-- For direct manipulation prefer 'hostAddress6ToTuple' and+-- 'tupleToHostAddress6'. type HostAddress6 = (Word32, Word32, Word32, Word32) +hostAddress6ToTuple :: HostAddress6 -> (Word16, Word16, Word16, Word16,+ Word16, Word16, Word16, Word16)+hostAddress6ToTuple (w3, w2, w1, w0) =+ let high, low :: Word32 -> Word16+ high w = fromIntegral (w `shiftR` 16)+ low w = fromIntegral w+ in (high w3, low w3, high w2, low w2, high w1, low w1, high w0, low w0)++tupleToHostAddress6 :: (Word16, Word16, Word16, Word16,+ Word16, Word16, Word16, Word16) -> HostAddress6+tupleToHostAddress6 (w7, w6, w5, w4, w3, w2, w1, w0) =+ let add :: Word16 -> Word16 -> Word32+ high `add` low = (fromIntegral high `shiftL` 16) .|. (fromIntegral low)+ in (w7 `add` w6, w5 `add` w4, w3 `add` w2, w1 `add` w0)+ -- The peek32 and poke32 functions work around the fact that the RFCs -- don't require 32-bit-wide address fields to be present. We can -- only portably rely on an 8-bit field, s6_addr.@@ -1009,18 +1068,25 @@ pokeByte 2 (a `sr` 8) pokeByte 3 (a `sr` 0) -instance Storable HostAddress6 where- sizeOf _ = (#const sizeof(struct in6_addr))- alignment _ = alignment (undefined :: CInt)+-- | Private newtype proxy for the Storable instance. To avoid orphan instances.+newtype In6Addr = In6Addr HostAddress6 +#if __GLASGOW_HASKELL__ < 800+#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)+#endif++instance Storable In6Addr where+ sizeOf _ = #const sizeof(struct in6_addr)+ alignment _ = #alignment struct in6_addr+ peek p = do a <- peek32 p 0 b <- peek32 p 1 c <- peek32 p 2 d <- peek32 p 3- return (a, b, c, d)+ return $ In6Addr (a, b, c, d) - poke p (a, b, c, d) = do+ poke p (In6Addr (a, b, c, d)) = do poke32 p 0 a poke32 p 1 b poke32 p 2 c
README.md view
@@ -1,4 +1,6 @@-# [`network`](http://hackage.haskell.org/package/network) [](https://travis-ci.org/haskell/network)+# [`network`](http://hackage.haskell.org/package/network) [](https://travis-ci.org/haskell/network) [](https://ci.appveyor.com/project/eborden/network/branch/master)++ To build this package using Cabal directly from git, you must run `autoreconf` before the usual Cabal build steps
cbits/asyncAccept.c view
@@ -5,7 +5,7 @@ #include "HsNet.h" #include "HsFFI.h" -#if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+#if defined(HAVE_WINSOCK2_H) /* all the way to the end */
cbits/initWinSock.c view
@@ -1,7 +1,7 @@ #include "HsNet.h" #include "HsFFI.h" -#if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+#if defined(HAVE_WINSOCK2_H) static int winsock_inited = 0;
cbits/winSockErr.c view
@@ -1,7 +1,7 @@ #include "HsNet.h" #include "HsFFI.h" -#if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+#if defined(HAVE_WINSOCK2_H) #include <stdio.h> /* to the end */
configure view
@@ -665,6 +665,7 @@ docdir oldincludedir includedir+runstatedir localstatedir sharedstatedir sysconfdir@@ -736,6 +737,7 @@ sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var'+runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'@@ -988,6 +990,15 @@ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \+ | --runstate | --runstat | --runsta | --runst | --runs \+ | --run | --ru | --r)+ ac_prev=runstatedir ;;+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \+ | --run=* | --ru=* | --r=*)+ runstatedir=$ac_optarg ;;+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \@@ -1125,7 +1136,7 @@ for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \- libdir localedir mandir+ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes.@@ -1278,6 +1289,7 @@ --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var]+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include]
examples/EchoClient.hs view
@@ -13,6 +13,6 @@ connect sock (addrAddress serveraddr) sendAll sock $ C.pack "Hello, world!" msg <- recv sock 1024- sClose sock+ close sock putStr "Received " C.putStrLn msg
examples/EchoServer.hs view
@@ -13,12 +13,12 @@ Nothing (Just "3000") let serveraddr = head addrinfos sock <- socket (addrFamily serveraddr) Stream defaultProtocol- bindSocket sock (addrAddress serveraddr)+ bind sock (addrAddress serveraddr) listen sock 1 (conn, _) <- accept sock talk conn- sClose conn- sClose sock+ close conn+ close sock where talk :: Socket -> IO ()
include/HsNet.h view
@@ -36,7 +36,7 @@ # undef IPV6_SOCKET_SUPPORT #endif -#if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+#if defined(HAVE_WINSOCK2_H) #include <winsock2.h> # ifdef HAVE_WS2TCPIP_H # include <ws2tcpip.h>@@ -115,7 +115,7 @@ extern int recvFd(int sock); -#endif /* HAVE_WINSOCK2_H && !__CYGWIN */+#endif /* HAVE_WINSOCK2_H */ INLINE char * my_inet_ntoa(@@ -138,7 +138,7 @@ #ifdef HAVE_GETADDRINFO INLINE int hsnet_getnameinfo(const struct sockaddr* a,socklen_t b, char* c,-# if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+# if defined(HAVE_WINSOCK2_H) DWORD d, char* e, DWORD f, int g) # else socklen_t d, char* e, socklen_t f, int g)@@ -161,7 +161,7 @@ } #endif -#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+#if defined(HAVE_WINSOCK2_H) # define WITH_WINSOCK 1 #endif
include/HsNetworkConfig.h view
@@ -2,13 +2,13 @@ /* include/HsNetworkConfig.h.in. Generated from configure.ac by autoheader. */ /* Define to 1 if you have the `accept4' function. */-/* #undef HAVE_ACCEPT4 */+#define HAVE_ACCEPT4 1 /* Define to 1 if you have the <arpa/inet.h> header file. */ #define HAVE_ARPA_INET_H 1 /* Define to 1 if you have a BSDish sendfile(2) implementation. */-#define HAVE_BSD_SENDFILE 1+/* #undef HAVE_BSD_SENDFILE */ /* Define to 1 if you have the declaration of `AI_ADDRCONFIG', and to 0 if you don't. */@@ -55,7 +55,7 @@ #define HAVE_GETHOSTENT 1 /* Define to 1 if you have getpeereid. */-#define HAVE_GETPEEREID 1+/* #undef HAVE_GETPEEREID */ /* Define to 1 if you have the `if_nametoindex' function. */ #define HAVE_IF_NAMETOINDEX 1@@ -73,11 +73,14 @@ #define HAVE_LIMITS_H 1 /* Define to 1 if you have the <linux/can.h> header file. */-/* #undef HAVE_LINUX_CAN_H */+#define HAVE_LINUX_CAN_H 1 /* Define to 1 if you have a Linux sendfile(2) implementation. */-/* #undef HAVE_LINUX_SENDFILE */+#define HAVE_LINUX_SENDFILE 1 +/* Define to 1 if you have the <linux/tcp.h> header file. */+#define HAVE_LINUX_TCP_H 1+ /* Define to 1 if you have the <memory.h> header file. */ #define HAVE_MEMORY_H 1 @@ -115,10 +118,10 @@ #define HAVE_STRUCT_MSGHDR_MSG_CONTROL 1 /* Define to 1 if `sa_len' is a member of `struct sockaddr'. */-#define HAVE_STRUCT_SOCKADDR_SA_LEN 1+/* #undef HAVE_STRUCT_SOCKADDR_SA_LEN */ /* Define to 1 if you have both SO_PEERCRED and struct ucred. */-/* #undef HAVE_STRUCT_UCRED */+#define HAVE_STRUCT_UCRED 1 /* Define to 1 if you have the `symlink' function. */ #define HAVE_SYMLINK 1
network.cabal view
@@ -1,8 +1,8 @@ name: network-version: 2.6.2.1+version: 2.6.3.0 license: BSD3 license-file: LICENSE-maintainer: Johan Tibell <johan.tibell@gmail.com>+maintainer: Kazu Yamamoto, Evan Borden synopsis: Low-level networking interface description: This package provides a low-level networking interface.@@ -41,6 +41,7 @@ cbits/winSockErr.c homepage: https://github.com/haskell/network bug-reports: https://github.com/haskell/network/issues+tested-with: GHC == 7.0.4, GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.1, GHC == 7.10.2, GHC == 8.0.1 library exposed-modules:@@ -104,6 +105,17 @@ network, test-framework, test-framework-hunit+ -- Some of the bugs only occur in the threaded RTS+ ghc-options: -Wall -threaded++test-suite doctest+ hs-source-dirs: tests+ main-is: doctests.hs+ type: exitcode-stdio-1.0++ build-depends:+ base < 5,+ doctest >= 0.10.1 ghc-options: -Wall
tests/Regression.hs view
@@ -2,8 +2,13 @@ module Main where import Network.Socket+import qualified Network.Socket.ByteString as BS++import Control.Exception+ import Test.Framework (Test, defaultMain) import Test.Framework.Providers.HUnit (testCase)+import Test.HUnit (assertFailure) ------------------------------------------------------------------------ -- Tests@@ -16,12 +21,35 @@ _ <- getAddrInfo (Just hints) (Just "localhost") Nothing return () +mkBadSocketAndTry :: (Socket -> IO a) -> IO (Either IOException a)+mkBadSocketAndTry f = do+ sock <- socket AF_INET Stream defaultProtocol+ try $ f sock++-- Because of 64/32 bitness issues, -1 wasn't correctly checked for on Windows.+-- See also GHC ticket #12010+badRecvShouldThrow :: IO ()+badRecvShouldThrow = do+ res <- mkBadSocketAndTry $ flip recv 1024+ case res of+ Left _ex -> return ()+ Right _ -> assertFailure "recv didn't throw an exception"++badSendShouldThrow :: IO ()+badSendShouldThrow = do+ res <- mkBadSocketAndTry $ flip send "hello"+ case res of+ Left _ex -> return ()+ Right _ -> assertFailure "send didn't throw an exception"+ ------------------------------------------------------------------------ -- List of all tests tests :: [Test] tests =- [ testCase "testGetAddrInfo" testGetAddrInfo+ [ testCase "testGetAddrInfo" testGetAddrInfo,+ testCase "badRecvShouldThrow" badRecvShouldThrow,+ testCase "badSendShouldThrow" badSendShouldThrow ] ------------------------------------------------------------------------
tests/Simple.hs view
@@ -5,10 +5,12 @@ import Control.Concurrent (ThreadId, forkIO, myThreadId) import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar, readMVar) import qualified Control.Exception as E-import Control.Monad (liftM, when)+import Control.Monad import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as C+#if defined(HAVE_LINUX_CAN_H) import Data.Maybe (fromJust)+#endif import Network.Socket hiding (recv, recvFrom, send, sendTo) import Network.Socket.ByteString @@ -137,7 +139,7 @@ getSocketOption sock UserTimeout >>= (@=?) 1000 setSocketOption sock UserTimeout 2000 getSocketOption sock UserTimeout >>= (@=?) 2000- sClose sock+ close sock {- testGetPeerCred:: Assertion@@ -146,22 +148,22 @@ where clientSetup = do sock <- socket AF_UNIX Stream defaultProtocol- connect sock $ SockAddrUnix addr + connect sock $ SockAddrUnix addr return sock serverSetup = do sock <- socket AF_UNIX Stream defaultProtocol- bindSocket sock $ SockAddrUnix addr + bind sock $ SockAddrUnix addr listen sock 1 return sock server sock = do (clientSock, _) <- accept sock- serverAct clientSock- sClose clientSock+ _ <- serverAct clientSock+ close clientSock addr = "/tmp/testAddr1"- clientAct sock = withSocketsDo $ do + clientAct sock = withSocketsDo $ do sendAll sock testMsg (pid,uid,gid) <- getPeerCred sock putStrLn $ unwords ["pid=",show pid,"uid=",show uid, "gid=", show gid]@@ -171,27 +173,27 @@ testGetPeerEid :: Assertion-testGetPeerEid = +testGetPeerEid = test clientSetup clientAct serverSetup server where clientSetup = do sock <- socket AF_UNIX Stream defaultProtocol- connect sock $ SockAddrUnix addr + connect sock $ SockAddrUnix addr return sock serverSetup = do sock <- socket AF_UNIX Stream defaultProtocol- bindSocket sock $ SockAddrUnix addr + bind sock $ SockAddrUnix addr listen sock 1 return sock server sock = do (clientSock, _) <- accept sock- serverAct clientSock- sClose clientSock+ _ <- serverAct clientSock+ close clientSock addr = "/tmp/testAddr2"- clientAct sock = withSocketsDo $ do + clientAct sock = withSocketsDo $ do sendAll sock testMsg (uid,gid) <- getPeerEid sock putStrLn $ unwords ["uid=",show uid, "gid=", show gid]@@ -223,11 +225,46 @@ -- bind the socket to the interface bind sock (SockAddrCan $ fromIntegral $ ifIndex) return sock- + serverSetup = clientSetup #endif ------------------------------------------------------------------------+-- Conversions of IP addresses++testHostAddressToTuple :: Assertion+testHostAddressToTuple = do+ -- Look up a numeric IPv4 host+ let hints = defaultHints { addrFlags = [AI_NUMERICHOST, AI_ADDRCONFIG] }+ (AddrInfo{addrAddress = (SockAddrInet _ hostAddr)} : _) <-+ getAddrInfo (Just hints) (Just "127.128.129.130") Nothing+ -- and check that the decoded address matches the expected representation+ (0x7f, 0x80, 0x81, 0x82) @=? hostAddressToTuple hostAddr++testHostAddressToTupleInv :: Assertion+testHostAddressToTupleInv = do+ let addr = (0x7f, 0x80, 0x81, 0x82)+ addr @=? (hostAddressToTuple . tupleToHostAddress) addr++#if defined(IPV6_SOCKET_SUPPORT)+testHostAddress6ToTuple :: Assertion+testHostAddress6ToTuple = do+ -- Look up a numeric IPv6 host+ let hints = defaultHints { addrFlags = [AI_NUMERICHOST, AI_ADDRCONFIG] }+ host = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"+ (AddrInfo{addrAddress = (SockAddrInet6 _ _ hostAddr _)} : _) <-+ getAddrInfo (Just hints) (Just host) Nothing+ -- and check that the decoded address matches the expected representation+ (0x2001, 0x0db8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7334)+ @=? hostAddress6ToTuple hostAddr++testHostAddress6ToTupleInv :: Assertion+testHostAddress6ToTupleInv = do+ let addr = (0x2001, 0x0db8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7334)+ addr @=? (hostAddress6ToTuple . tupleToHostAddress6) addr+#endif++------------------------------------------------------------------------ -- Other ------------------------------------------------------------------------@@ -251,8 +288,16 @@ -- , testCase "testGetPeerCred" testGetPeerCred -- , testCase "testGetPeerEid" testGetPeerEid #if defined(HAVE_LINUX_CAN_H)- , testCase "testCanSend" testCanSend + , testCase "testCanSend" testCanSend #endif+ -- conversions of IP addresses+ , testCase "testHostAddressToTuple" testHostAddressToTuple+ , testCase "testHostAddressToTupleInv" testHostAddressToTupleInv+#if defined(IPV6_SOCKET_SUPPORT)+ , testCase "testHostAddress6ToTuple" testHostAddress6ToTuple+ , testCase "testHostAddress6ToTupleInv" testHostAddress6ToTupleInv+#endif+ -- other ] tests :: [Test]@@ -292,7 +337,7 @@ sock <- socket AF_INET Stream defaultProtocol setSocketOption sock ReuseAddr 1 addr <- inet_addr serverAddr- bindSocket sock (SockAddrInet aNY_PORT addr)+ bind sock (SockAddrInet aNY_PORT addr) listen sock 1 serverPort <- socketPort sock putMVar portVar serverPort@@ -300,8 +345,8 @@ server sock = do (clientSock, _) <- accept sock- serverAct clientSock- sClose clientSock+ _ <- serverAct clientSock+ close clientSock -- | Create an unconnected 'Socket' for sending UDP and receiving -- datagrams and then run 'clientAct' and 'serverAct'.@@ -320,7 +365,7 @@ sock <- socket AF_INET Datagram defaultProtocol setSocketOption sock ReuseAddr 1 addr <- inet_addr serverAddr- bindSocket sock (SockAddrInet aNY_PORT addr)+ bind sock (SockAddrInet aNY_PORT addr) serverPort <- socketPort sock putMVar portVar serverPort return sock@@ -332,13 +377,13 @@ test clientSetup clientAct serverSetup serverAct = do tid <- myThreadId barrier <- newEmptyMVar- forkIO $ server barrier+ _ <- forkIO $ server barrier client tid barrier where server barrier = do- E.bracket serverSetup sClose $ \sock -> do+ E.bracket serverSetup close $ \sock -> do serverReady- serverAct sock+ _ <- serverAct sock putMVar barrier () where -- | Signal to the client that it can proceed.@@ -347,8 +392,8 @@ client tid barrier = do takeMVar barrier -- Transfer exceptions to the main thread.- bracketWithReraise tid clientSetup sClose $ \res -> do- clientAct res+ bracketWithReraise tid clientSetup close $ \res -> do+ _ <- clientAct res takeMVar barrier -- | Like 'bracket' but catches and reraises the exception in another
+ tests/doctests.hs view
@@ -0,0 +1,20 @@+import Test.DocTest++main :: IO ()+main = doctest [+ "-i"+ , "-idist/build"+ , "-i."+ , "-idist/build/autogen"+ , "-Idist/build/autogen"+ , "-Idist/build"+ , "-Iinclude"+ , "-optP-include"+ , "-optPdist/build/autogen/cabal_macros.h"+ , "-DCALLCONV=ccall"+ , "-XCPP"+ , "-XDeriveDataTypeable"+ , "-package-db dist/package.conf.inplace"+ , "-package network"+ , "Network"+ ]