posix-api 0.1.0.0 → 0.2.0.0
raw patch · 9 files changed
+687/−175 lines, 9 files
Files
- CHANGELOG.md +11/−3
- README.md +60/−0
- include/custom.h +87/−0
- posix-api.cabal +3/−1
- src-linux/Posix/Socket/Platform.hsc +41/−30
- src/Linux/Socket.hs +31/−2
- src/Linux/Socket/Types.hsc +34/−4
- src/Posix/Socket.hs +344/−115
- src/Posix/Socket/Types.hsc +76/−20
CHANGELOG.md view
@@ -1,5 +1,13 @@-# Revision history for posix+# Changelog+All notable changes to this project will be documented in this file. -## 0.1.0.0 -- YYYY-mm-dd+The format is inspired by [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).+This changelog deviates from the recommendation by not grouping changes into+added, changed, deprecated, etc. subsections. -* First version. Released on an unsuspecting world.+This project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/).++## [0.1.0.0] - 2018-01-02+- Initial release.+- Includes a ton of sockets API stuff.+- Includes the get working directory function.
+ README.md view
@@ -0,0 +1,60 @@+# posix-api++## Objective++This library provides minimal bindings to system calls for POSIX-compliant+operating systems. All functions follow these design decisions:++* `String` is not used anywhere. `ByteArray` (from `primitive`) is used for+ serialized data. It is also used in certain filesystem function variants+ used in contexts where the paths are only ever handed over to other+ filesystem functions. `Addr` (from `primitive`) is used for pointers to+ data whose type is unknown. `Ptr` is used for pointers to data whose type+ is known.+* Functions should not throw errors. This library uses `IO (Either Errno a)`+ in places where some libraries would use `IO a`.+* The numeric types from `Foreign.C.Types` and `System.Posix.Types` are+ used in the type signatures of functions so that a haskell function's+ type signature matches its underlying POSIX equivalent exactly.+* Flags are newtypes over `CInt` (or whatever integral type matches the+ posix specification) rather than enumerations. The data constructors+ are exported, making the types extensible for operating system that+ have additional flags.+* There is some platform-specific code in this library. POSIX-specified data+ structures do not have the same in-memory representation on all platforms.+ Consequently, some of the code to serialize data to its C-struct+ representation must be written differently on different platforms.+ This is seldom needed. A viable alternative would be using the FFI+ to perform this serialization. However, the approach of using+ per-platform haskell code lets the serialization code inline better.++Pull requests that add bindings to POSIX APIs in a way that agrees+with these guidelines will be accepted. Unfortunately, there is some+grey area when it comes to what a "minimal binding" to a function+is. Discussion may sometimes be necessary to refine the guidelines.++## Build Instructions++This library relies on a currently-unreleased version of `hsc2hs` in+order to build. In order to try out this library, try:++```+~/dev $ git clone https://github.com/haskell/hsc2hs+~/dev $ cd hsc2hs+~/dev/hsc2hs $ cabal install+~/dev/hsc2hs $ cd ..+~/dev $ git clone https://github.com/andrewthad/posix-api+~/dev $ cd posix-api+~/dev/posix-api $ cabal new-build --with-hsc2hs=~/.cabal/bin/hsc2hs+```++This will build `posix-api` with the unreleased version of the `hsc2hs`+tool.++## Infelicities++This project currently includes some Linux-specific code. It in the+the `Linux.Socket`. The plan is to eventually move the `Linux.Socket` module+into its own library. Currently, a ton of POSIX APIs are missing.+These should be included.+
+ include/custom.h view
@@ -0,0 +1,87 @@+#include <stddef.h>++// This include file lets us use hsc2hs to generate code working+// with ByteArray (through Data.Primitive) instead of Ptr.++// The macro FIELD_SIZEOF is defined in the linux kernel.+// It is written out here for portability.+#define INTERNAL_FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))++// This is a lot like peek except that it is designed to+// work with MutableByteArray instead of Ptr. GHC requires that all+// indexing into ByteArrays is aligned. So, we do some trickery+// based on the size of what the user is requesting.+#define hsc_read(t, f) \+ switch (INTERNAL_FIELD_SIZEOF(t,f)) { \+ case 1: \+ hsc_printf ("(\\hsc_arr -> readByteArray hsc_arr %ld)", (long) offsetof (t, f)); \+ break; \+ case 2: \+ if (offsetof (t, f) % 2 == 0) \+ hsc_printf ("(\\hsc_arr -> readByteArray hsc_arr %ld)", ((long) offsetof (t, f)) / 2); \+ else \+ hsc_printf ("BAD_READ_ALIGNMENT"); \+ break; \+ default: \+ hsc_printf ("(BAD_READ_SIZE_%ld)", (long) INTERNAL_FIELD_SIZEOF(t,f)); \+ break; \+ }++// This is like peek except that it is designed to work with ByteArray+// instead of Ptr. See hsc_read. This function becomes indexByteArray.+#define hsc_index(t, f) \+ switch (INTERNAL_FIELD_SIZEOF(t,f)) { \+ case 1: \+ hsc_printf ("(\\hsc_arr -> indexByteArray hsc_arr %ld)", (long) offsetof (t, f)); \+ break; \+ case 2: \+ if (offsetof (t, f) % 2 == 0) \+ hsc_printf ("(\\hsc_arr -> indexByteArray hsc_arr %ld)", ((long) offsetof (t, f)) / 2); \+ else \+ hsc_printf ("BAD_INDEX_ALIGNMENT"); \+ break; \+ case 4: \+ if (offsetof (t, f) % 4 == 0) \+ hsc_printf ("(\\hsc_arr -> indexByteArray hsc_arr %ld)", ((long) offsetof (t, f)) / 4); \+ else \+ hsc_printf ("BAD_INDEX_ALIGNMENT"); \+ break; \+ default: \+ hsc_printf ("(BAD_INDEX_SIZE_%ld)", (long) INTERNAL_FIELD_SIZEOF(t,f)); \+ break; \+ }++// This is a lot like poke except that it is designed to+// work with MutableByteArray instead of Ptr. See hsc_read.+#define hsc_write(t, f) \+ switch (INTERNAL_FIELD_SIZEOF(t,f)) { \+ case 1: \+ hsc_printf ("(\\hsc_arr -> writeByteArray hsc_arr %ld)", (long) offsetof (t, f)); \+ break; \+ case 2: \+ if (offsetof (t, f) % 2 == 0) \+ hsc_printf ("(\\hsc_arr -> writeByteArray hsc_arr %ld)", ((long) offsetof (t, f)) / 2); \+ else \+ hsc_printf ("BAD_WRITE_ALIGNMENT"); \+ break; \+ case 4: \+ if (offsetof (t, f) % 4 == 0) \+ hsc_printf ("(\\hsc_arr -> writeByteArray hsc_arr %ld)", ((long) offsetof (t, f)) / 4); \+ else \+ hsc_printf ("BAD_WRITE_ALIGNMENT"); \+ break; \+ default: \+ hsc_printf ("(BAD_WRITE_SIZE_%ld)", (long) INTERNAL_FIELD_SIZEOF(t,f)); \+ break; \+ }++// Compute an element offset from a byte offset and the element size.+// This causes a compile-time failure if the element size does not+// divide evenly into the byte offset. This is commonly used as:+#define hsc_elementize(boff, sz) \+ if ((boff) % (sz) == 0) { \+ hsc_printf ("%ld", ((boff) / (sz))); \+ } else { \+ hsc_printf ("BAD_ELEMENT_OFFSET"); \+ }+
posix-api.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: posix-api-version: 0.1.0.0+version: 0.2.0.0 synopsis: posix bindings description: This library provides a very thin wrapper around POSIX APIs. It can be@@ -65,9 +65,11 @@ category: System build-type: Simple extra-source-files:+ README.md CHANGELOG.md cbits/HaskellPosix.c include/HaskellPosix.h+ include/custom.h library exposed-modules:
src-linux/Posix/Socket/Platform.hsc view
@@ -12,6 +12,7 @@ #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h>+#include "custom.h" -- | All of the data constructors provided by this module are unsafe. -- Only use them if you really know what you are doing.@@ -19,48 +20,64 @@ ( -- * Encoding Socket Addresses encodeSocketAddressInternet , encodeSocketAddressUnix+ -- * Decoding Socket Addresses+ , decodeSocketAddressInternet+ -- * Sizes+ , sizeofSocketAddressInternet ) where -import Data.Primitive (ByteArray(..),Addr(..))-import Data.Void (Void)+import Control.Monad (when)+import Data.Primitive (ByteArray(..),writeByteArray,indexByteArray) import Data.Word (Word8)-import Foreign.C.Types (CUShort)-import Foreign.Storable (pokeByteOff)-import GHC.Exts (ByteArray##,State##,RealWorld,Ptr(..),runRW##,touch##)-import GHC.IO (IO(..))-import Posix.Socket.Types (SocketAddressInternet(..),SocketAddressUnix(..))+import Foreign.C.Types (CUShort,CInt)+import GHC.Exts (ByteArray##,State##,RealWorld,runRW##)+import GHC.ST (ST(..)) import Posix.Socket.Types (SocketAddress(..))-import Control.Monad (when)+import Posix.Socket.Types (SocketAddressInternet(..),SocketAddressUnix(..)) import qualified Data.Primitive as PM import qualified Foreign.Storable as FS +-- | The size of a serialized internet socket address. +sizeofSocketAddressInternet :: CInt+sizeofSocketAddressInternet = #{size struct sockaddr_in}+ -- | Serialize a IPv4 socket address so that it may be passed to @bind@. -- This serialization is operating-system dependent. encodeSocketAddressInternet :: SocketAddressInternet -> SocketAddress encodeSocketAddressInternet (SocketAddressInternet {port, address}) =- SocketAddress $ runByteArrayIO $ unboxByteArrayIO $ do- bs <- PM.newPinnedByteArray #{size struct sockaddr_in}+ SocketAddress $ runByteArrayST $ unboxByteArrayST $ do+ bs <- PM.newByteArray #{size struct sockaddr_in} -- Initialize the bytearray by filling it with zeroes to ensure -- that the sin_zero padding that linux expects is properly zeroed. PM.setByteArray bs 0 #{size struct sockaddr_in} (0 :: Word8)- let !(Addr addr) = PM.mutableByteArrayContents bs- let !(ptr :: Ptr Void) = Ptr addr -- ATM: I cannot find a way to poke AF_INET into the socket address -- without hardcoding the expected length (CUShort). There may be -- a way to use hsc2hs to convert a size to a haskell type, but -- I am not sure of how to do this. At any rate, I do not expect -- that linux will ever change the bit size of sa_family_t, so I -- am not too concerned.- #{poke struct sockaddr_in, sin_family} ptr (#{const AF_INET} :: CUShort)+ #{write struct sockaddr_in, sin_family} bs (#{const AF_INET} :: CUShort) -- The port and the address are already supposed to be in network -- byte order in the SocketAddressInternet data type.- #{poke struct sockaddr_in, sin_port} ptr port- #{poke struct sockaddr_in, sin_addr.s_addr} ptr address+ #{write struct sockaddr_in, sin_port} bs port+ #{write struct sockaddr_in, sin_addr.s_addr} bs address r <- PM.unsafeFreezeByteArray bs- touchByteArray r pure r +decodeSocketAddressInternet :: SocketAddress -> Maybe SocketAddressInternet+decodeSocketAddressInternet (SocketAddress arr) =+ if PM.sizeofByteArray arr == (#{size struct sockaddr_in})+ -- We assume that AF_INET takes up 16 bits. See the comment in+ -- encodeSocketAddressInternet for more detail.+ then if (#{index struct sockaddr_in, sin_family} arr) == (#{const AF_INET} :: CUShort)+ then Just $ SocketAddressInternet+ { port = #{index struct sockaddr_in, sin_port} arr+ , address = #{index struct sockaddr_in, sin_addr.s_addr} arr+ }+ else Nothing+ else Nothing+ -- | Serialize a unix domain socket address so that it may be passed to @bind@. -- This serialization is operating-system dependent. If the path provided by -- the argument equals or exceeds the size of @sun_path@ (typically in the range 92@@ -69,7 +86,7 @@ -- error code. encodeSocketAddressUnix :: SocketAddressUnix -> SocketAddress encodeSocketAddressUnix (SocketAddressUnix !name) =- SocketAddress $ runByteArrayIO $ unboxByteArrayIO $ do + SocketAddress $ runByteArrayST $ unboxByteArrayST $ do -- On linux, sun_path always has exactly 108 bytes. It is a null-terminated -- string, so we initialize the byte array to zeroes to ensure this -- happens.@@ -77,7 +94,7 @@ -- Again, we hard-code the size of sa_family_t as the size of -- an unsigned short. let familySize = FS.sizeOf (undefined :: CUShort)- bs <- PM.newPinnedByteArray (pathSize + familySize)+ bs <- PM.newByteArray (pathSize + familySize) PM.setByteArray bs familySize pathSize (0 :: Word8) PM.writeByteArray bs 0 (#{const AF_UNIX} :: CUShort) let sz = PM.sizeofByteArray name@@ -85,18 +102,12 @@ PM.copyByteArray bs familySize name 0 sz PM.unsafeFreezeByteArray bs -touchByteArray :: ByteArray -> IO ()-touchByteArray (ByteArray x) = touchByteArray## x--touchByteArray## :: ByteArray## -> IO ()-touchByteArray## x = IO $ \s -> case touch## x s of s' -> (## s', () ##)--unboxByteArrayIO :: IO ByteArray -> State## RealWorld -> (## State## RealWorld, ByteArray## ##)-unboxByteArrayIO (IO f) s = case f s of+unboxByteArrayST :: ST s ByteArray -> State## s -> (## State## s, ByteArray## ##)+unboxByteArrayST (ST f) s = case f s of (## s', ByteArray b ##) -> (## s', b ##) --- In a general setting, this function is unsafe, but it is used kind of like--- runST in this module.-runByteArrayIO :: (State## RealWorld -> (## State## RealWorld, ByteArray## ##)) -> ByteArray-runByteArrayIO st_rep = case runRW## st_rep of (## _, a ##) -> ByteArray a+-- This is a specialization of runST that avoids a needless+-- data constructor allocation.+runByteArrayST :: (State## RealWorld -> (## State## RealWorld, ByteArray## ##)) -> ByteArray+runByteArrayST st_rep = case runRW## st_rep of (## _, a ##) -> ByteArray a
src/Linux/Socket.hs view
@@ -1,6 +1,35 @@ module Linux.Socket- ( -- * Flags- LST.dontWait+ ( -- * Types+ SocketFlags(..)+ -- * Message Flags+ , LST.dontWait+ , LST.truncate+ -- * Socket Flags+ , LST.closeOnExec+ , LST.nonblocking+ -- * Twiddle+ , applySocketFlags ) where +import Prelude hiding (truncate)++import Data.Bits ((.|.))+import Linux.Socket.Types (SocketFlags(..))+import Posix.Socket (Type(..))+ import qualified Linux.Socket.Types as LST++-- | Linux extends the @type@ argument of+-- <http://man7.org/linux/man-pages/man2/socket.2.html socket> to accept+-- flags. It is advisable to set @SOCK_CLOEXEC@ on when opening a socket+-- on linux. For example, we may open a TCP Internet socket with:+--+-- > uninterruptibleSocket internet (applySocketFlags closeOnExec stream) defaultProtocol+--+-- To additionally open the socket in nonblocking mode+-- (e.g. with @SOCK_NONBLOCK@):+--+-- > uninterruptibleSocket internet (applySocketFlags (closeOnExec <> nonblocking) stream) defaultProtocol+-- +applySocketFlags :: SocketFlags -> Type -> Type+applySocketFlags (SocketFlags s) (Type t) = Type (s .|. t)
src/Linux/Socket/Types.hsc view
@@ -1,14 +1,44 @@+{-# language DerivingStrategies #-}+{-# language GeneralizedNewtypeDeriving #-}+ #include <sys/socket.h> -- | All of the data constructors provided by this module are unsafe. -- Only use them if you really know what you are doing. module Linux.Socket.Types- ( dontWait+ ( SocketFlags(..)+ , dontWait+ , truncate+ , closeOnExec+ , nonblocking ) where -import Posix.Socket (Flags(..))+import Prelude hiding (truncate) +import Data.Bits (Bits((.|.)))+import Foreign.C.Types (CInt)+import Posix.Socket (MessageFlags(..))++newtype SocketFlags = SocketFlags CInt+ deriving stock (Eq)+ deriving newtype (Bits)++instance Semigroup SocketFlags where (<>) = (.|.)+instance Monoid SocketFlags where mempty = SocketFlags 0+ -- | The @MSG_DONTWAIT@ receive flag or send flag.-dontWait :: Flags m-dontWait = Flags #{const MSG_DONTWAIT}+dontWait :: MessageFlags m+dontWait = MessageFlags #{const MSG_DONTWAIT}++-- | The @MSG_TRUNC@ receive flag.+truncate :: MessageFlags m+truncate = MessageFlags #{const MSG_DONTWAIT}++-- | The @SOCK_CLOEXEC@ receive flag or send flag.+closeOnExec :: SocketFlags+closeOnExec = SocketFlags #{const SOCK_CLOEXEC}++-- | The @SOCK_NONBLOCK@ receive flag or send flag.+nonblocking :: SocketFlags+nonblocking = SocketFlags #{const SOCK_NONBLOCK}
src/Posix/Socket.hs view
@@ -1,6 +1,5 @@ {-# language BangPatterns #-} {-# language DataKinds #-}-{-# language InterruptibleFFI #-} {-# language MagicHash #-} {-# language ScopedTypeVariables #-} {-# language UnboxedTuples #-}@@ -18,47 +17,70 @@ -- buffer type. They all call @send@ or @recv@ exactly once. They do not -- repeatedly make syscalls like some of the functions in @network@. -- Users who want that behavior need to build on top of this package.+-- * There are no requirements on the pinnedness of @ByteArray@ arguments+-- passed to any of these functions. If wrappers of the safe FFI are+-- passed unpinned @ByteArray@ arguments, they will copy the contents+-- into pinned memory before invoking the foreign function. module Posix.Socket ( -- * Functions -- ** Socket- socket+ uninterruptibleSocket -- ** Socket Pair- , socketPair+ , uninterruptibleSocketPair -- ** Bind- , bind+ , uninterruptibleBind -- ** Connect , connect+ , uninterruptibleConnect -- ** Listen- , listen+ , uninterruptibleListen -- ** Accept , accept+ , uninterruptibleAccept , accept_+ -- ** Get Socket Name+ , uninterruptibleGetSocketName+ -- ** Get Socket Option+ , uninterruptibleGetSocketOption -- ** Close , close- , unsafeClose+ , uninterruptibleClose+ , uninterruptibleErrorlessClose+ -- ** Shutdown+ , uninterruptibleShutdown -- ** Send , send , sendByteArray , sendMutableByteArray- , unsafeSend- , unsafeSendByteArray- , unsafeSendMutableByteArray+ , uninterruptibleSend+ , uninterruptibleSendByteArray+ , uninterruptibleSendMutableByteArray -- ** Send To- , unsafeSendToByteArray- , unsafeSendToMutableByteArray+ , uninterruptibleSendToByteArray+ , uninterruptibleSendToMutableByteArray -- ** Receive , receive , receiveByteArray- , unsafeReceive- , unsafeReceiveMutableByteArray+ , uninterruptibleReceive+ , uninterruptibleReceiveMutableByteArray -- ** Receive From- , unsafeReceiveFromMutableByteArray- , unsafeReceiveFromMutableByteArray_+ , uninterruptibleReceiveFromMutableByteArray+ , uninterruptibleReceiveFromMutableByteArray_+ -- ** Byte-Order Conversion+ -- $conversion+ , hostToNetworkLong+ , hostToNetworkShort+ , networkToHostLong+ , networkToHostShort -- * Types , Domain(..) , Type(..) , Protocol(..)- , Flags(..)+ , OptionName(..)+ , OptionValue(..)+ , Level(..)+ , MessageFlags(..)+ , ShutdownType(..) -- * Socket Address -- ** Types , SocketAddress(..)@@ -67,11 +89,16 @@ -- ** Encoding , PSP.encodeSocketAddressInternet , PSP.encodeSocketAddressUnix+ -- ** Decoding+ , PSP.decodeSocketAddressInternet+ -- ** Sizes+ , PSP.sizeofSocketAddressInternet -- * Data Construction -- ** Socket Domains , PST.unix , PST.unspecified , PST.internet+ , PST.internet6 -- ** Socket Types , PST.stream , PST.datagram@@ -89,21 +116,34 @@ , PST.peek , PST.outOfBand , PST.waitAll+ -- ** Shutdown Types+ , PST.read+ , PST.write+ , PST.readWrite+ -- ** Socket Levels+ , PST.levelSocket+ -- ** Option Names+ , PST.optionError ) where +import GHC.ByteOrder (ByteOrder(BigEndian,LittleEndian),targetByteOrder) import GHC.IO (IO(..)) import Data.Primitive (MutablePrimArray(..),MutableByteArray(..),Addr(..),ByteArray(..))+import Data.Word (Word16,Word32,byteSwap16,byteSwap32) import Data.Void (Void) import Foreign.C.Error (Errno,getErrno) import Foreign.C.Types (CInt(..),CSize(..)) import Foreign.Ptr (nullPtr)-import GHC.Exts (Ptr,RealWorld,ByteArray#,MutableByteArray#,Addr#)+import GHC.Exts (Ptr,RealWorld,ByteArray#,MutableByteArray#,Addr#,Int(I#))+import GHC.Exts (shrinkMutableByteArray#) import Posix.Socket.Types (Domain(..),Protocol(..),Type(..),SocketAddress(..))-import Posix.Socket.Types (Flags(..),Message(..))+import Posix.Socket.Types (MessageFlags(..),Message(..),ShutdownType(..))+import Posix.Socket.Types (Level(..),OptionName(..),OptionValue(..)) import System.Posix.Types (Fd(..),CSsize(..)) import qualified Posix.Socket.Types as PST import qualified Data.Primitive as PM+import qualified Control.Monad.Primitive as PM -- This module include operating-system specific code used -- to serialize some of various kind of socket address types.@@ -118,12 +158,15 @@ foreign import ccall unsafe "sys/socket.h listen" c_listen :: Fd -> CInt -> IO CInt -foreign import ccall interruptible "unistd.h close"+foreign import ccall safe "unistd.h close" c_safe_close :: Fd -> IO CInt foreign import ccall unsafe "unistd.h close" c_unsafe_close :: Fd -> IO CInt +foreign import ccall unsafe "unistd.h shutdown"+ c_unsafe_shutdown :: Fd -> ShutdownType -> IO CInt+ -- Per the spec, the type signature of bind is: -- int bind(int socket, const struct sockaddr *address, socklen_t address_len); -- However, here we choose to represent the third argument as@@ -141,19 +184,44 @@ -- note on c_bind for why we use CInt for socklen_t. Remember that the -- first bytearray argument is actually SocketAddress in the function that -- wraps this one. The second bytearray argument is a pointer to the size.-foreign import ccall interruptible "sys/socket.h accept"+foreign import ccall safe "sys/socket.h accept" c_safe_accept :: Fd -> MutableByteArray# RealWorld -- SocketAddress -> MutableByteArray# RealWorld -- Ptr CInt -> IO Fd-foreign import ccall interruptible "sys/socket.h accept"+foreign import ccall unsafe "sys/socket.h accept"+ c_unsafe_accept :: Fd+ -> MutableByteArray# RealWorld -- SocketAddress+ -> MutableByteArray# RealWorld -- Ptr CInt+ -> IO Fd+-- This variant of accept is used when we do not care about the+-- remote sockaddr. We pass null.+foreign import ccall safe "sys/socket.h accept" c_safe_ptr_accept :: Fd -> Ptr Void -> Ptr CInt -> IO Fd +foreign import ccall unsafe "sys/socket.h getsockname"+ c_unsafe_getsockname :: Fd+ -> MutableByteArray# RealWorld -- SocketAddress+ -> MutableByteArray# RealWorld -- Addr length (Ptr CInt)+ -> IO CInt++foreign import ccall unsafe "sys/socket.h getsockopt"+ c_unsafe_getsockopt :: Fd+ -> Level+ -> OptionName+ -> MutableByteArray# RealWorld -- Option value+ -> MutableByteArray# RealWorld -- Option len (Ptr CInt)+ -> IO CInt+ -- Per the spec the type signature of connect is: -- int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); -- The bytearray argument is actually SocketAddress.-foreign import ccall interruptible "sys/socket.h connect"+foreign import ccall safe "sys/socket.h connect" c_safe_connect :: Fd -> ByteArray# -> CInt -> IO CInt+foreign import ccall safe "sys/socket.h connect"+ c_safe_mutablebytearray_connect :: Fd -> MutableByteArray# RealWorld -> CInt -> IO CInt+foreign import ccall unsafe "sys/socket.h connect"+ c_unsafe_connect :: Fd -> ByteArray# -> CInt -> IO CInt -- There are several options for wrapping send. Both safe and unsafe -- are useful. Additionally, in the unsafe category, we also@@ -162,65 +230,67 @@ -- while the FFI call is taking place. Safe FFI calls do not have -- this guarantee, so internally we must be careful when using these to only -- provide pinned byte arrays as arguments.-foreign import ccall interruptible "sys/socket.h send"- c_safe_addr_send :: Fd -> Addr# -> CSize -> Flags 'Send -> IO CSsize-foreign import ccall interruptible "sys/socket.h send_offset"- c_safe_bytearray_send :: Fd -> ByteArray# -> CInt -> CSize -> Flags 'Send -> IO CSsize-foreign import ccall interruptible "sys/socket.h send_offset"- c_safe_mutablebytearray_send :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> Flags 'Send -> IO CSsize-foreign import ccall interruptible "sys/socket.h send"- c_safe_mutablebytearray_no_offset_send :: Fd -> MutableByteArray# RealWorld -> CSize -> Flags 'Send -> IO CSsize+foreign import ccall safe "sys/socket.h send"+ c_safe_addr_send :: Fd -> Addr# -> CSize -> MessageFlags 'Send -> IO CSsize+foreign import ccall safe "sys/socket.h send_offset"+ c_safe_bytearray_send :: Fd -> ByteArray# -> CInt -> CSize -> MessageFlags 'Send -> IO CSsize+foreign import ccall safe "sys/socket.h send_offset"+ c_safe_mutablebytearray_send :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> MessageFlags 'Send -> IO CSsize+foreign import ccall safe "sys/socket.h send"+ c_safe_mutablebytearray_no_offset_send :: Fd -> MutableByteArray# RealWorld -> CSize -> MessageFlags 'Send -> IO CSsize foreign import ccall unsafe "sys/socket.h send"- c_unsafe_addr_send :: Fd -> Addr# -> CSize -> Flags 'Send -> IO CSsize+ c_unsafe_addr_send :: Fd -> Addr# -> CSize -> MessageFlags 'Send -> IO CSsize foreign import ccall unsafe "sys/socket.h send_offset"- c_unsafe_bytearray_send :: Fd -> ByteArray# -> CInt -> CSize -> Flags 'Send -> IO CSsize+ c_unsafe_bytearray_send :: Fd -> ByteArray# -> CInt -> CSize -> MessageFlags 'Send -> IO CSsize foreign import ccall unsafe "sys/socket.h send_offset"- c_unsafe_mutable_bytearray_send :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> Flags 'Send -> IO CSsize+ c_unsafe_mutable_bytearray_send :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> MessageFlags 'Send -> IO CSsize -- The ByteArray# (second to last argument) is a SocketAddress. foreign import ccall unsafe "sys/socket.h sendto_offset"- c_unsafe_bytearray_sendto :: Fd -> ByteArray# -> CInt -> CSize -> Flags 'Send -> ByteArray# -> CInt -> IO CSsize+ c_unsafe_bytearray_sendto :: Fd -> ByteArray# -> CInt -> CSize -> MessageFlags 'Send -> ByteArray# -> CInt -> IO CSsize -- The ByteArray# (second to last argument) is a SocketAddress. foreign import ccall unsafe "sys/socket.h sendto_offset"- c_unsafe_mutable_bytearray_sendto :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> Flags 'Send -> ByteArray# -> CInt -> IO CSsize+ c_unsafe_mutable_bytearray_sendto :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> MessageFlags 'Send -> ByteArray# -> CInt -> IO CSsize -- There are several ways to wrap recv.-foreign import ccall interruptible "sys/socket.h recv"- c_safe_addr_recv :: Fd -> Addr# -> CSize -> Flags 'Receive -> IO CSsize+foreign import ccall safe "sys/socket.h recv"+ c_safe_addr_recv :: Fd -> Addr# -> CSize -> MessageFlags 'Receive -> IO CSsize foreign import ccall unsafe "sys/socket.h recv"- c_unsafe_addr_recv :: Fd -> Addr# -> CSize -> Flags 'Receive -> IO CSsize+ c_unsafe_addr_recv :: Fd -> Addr# -> CSize -> MessageFlags 'Receive -> IO CSsize foreign import ccall unsafe "sys/socket.h recv_offset"- c_unsafe_mutable_byte_array_recv :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> Flags 'Receive -> IO CSsize+ c_unsafe_mutable_byte_array_recv :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> MessageFlags 'Receive -> IO CSsize -- The last two arguments are SocketAddress and Ptr CInt. foreign import ccall unsafe "sys/socket.h recvfrom_offset"- c_unsafe_mutable_byte_array_recvfrom :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> Flags 'Receive -> MutableByteArray# RealWorld -> MutableByteArray# RealWorld -> IO CSsize+ c_unsafe_mutable_byte_array_recvfrom :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> MessageFlags 'Receive -> MutableByteArray# RealWorld -> MutableByteArray# RealWorld -> IO CSsize foreign import ccall unsafe "sys/socket.h recvfrom_offset"- c_unsafe_mutable_byte_array_ptr_recvfrom :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> Flags 'Receive -> Ptr Void -> Ptr CInt -> IO CSsize+ c_unsafe_mutable_byte_array_ptr_recvfrom :: Fd -> MutableByteArray# RealWorld -> CInt -> CSize -> MessageFlags 'Receive -> Ptr Void -> Ptr CInt -> IO CSsize -- | Create an endpoint for communication, returning a file -- descriptor that refers to that endpoint. The -- <http://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html POSIX specification>--- includes more details. This is implemented as an unsafe FFI--- call since the author believes that it cannot block indefinitely.-socket ::+-- includes more details. No special preparation is required before calling+-- this function. The author believes that it cannot block for a prolonged+-- period of time.+uninterruptibleSocket :: Domain -- ^ Communications domain (e.g. 'internet', 'unix')- -> Type -- ^ Socket type (e.g. 'datagram', 'stream')+ -> Type -- ^ Socket type (e.g. 'datagram', 'stream') with flags -> Protocol -- ^ Protocol -> IO (Either Errno Fd)-socket dom typ prot = c_socket dom typ prot >>= errorsFromFd+uninterruptibleSocket dom typ prot = c_socket dom typ prot >>= errorsFromFd -- | Create an unbound pair of connected sockets in a specified domain, of -- a specified type, under the protocol optionally specified by the protocol -- argument. The <http://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html POSIX specification>--- includes more details. This is implemented as an unsafe FFI call--- since the author believes that it cannot block indefinitely.-socketPair ::+-- includes more details. No special preparation is required before calling+-- this function. The author believes that it cannot block for a prolonged+-- period of time.+uninterruptibleSocketPair :: Domain -- ^ Communications domain (probably 'unix')- -> Type -- ^ Socket type (e.g. 'datagram', 'stream')+ -> Type -- ^ Socket type (e.g. 'datagram', 'stream') with flags -> Protocol -- ^ Protocol -> IO (Either Errno (Fd,Fd))-socketPair dom typ prot = do+uninterruptibleSocketPair dom typ prot = do -- If this ever switches to the safe FFI, we will need to use -- a pinned array here instead. (sockets@(MutablePrimArray sockets#) :: MutablePrimArray RealWorld Fd) <- PM.newPrimArray 2@@ -237,38 +307,55 @@ -- <http://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html POSIX specification> -- includes more details. The 'SocketAddress' represents the @sockaddr@ pointer argument, together -- with its @socklen_t@ size, as a byte array. This allows @bind@ to--- be used with @sockaddr@ extensions on various platforms. This uses--- the unsafe FFI since the author believes it cannot block indefinitely.-bind ::+-- be used with @sockaddr@ extensions on various platforms. No special+-- preparation is required before calling this function. The author+-- believes that it cannot block for a prolonged period of time.+uninterruptibleBind :: Fd -- ^ Socket -> SocketAddress -- ^ Socket address, extensible tagged union -> IO (Either Errno ())-bind fd (SocketAddress b@(ByteArray b#)) =+uninterruptibleBind fd (SocketAddress b@(ByteArray b#)) = c_bind fd b# (intToCInt (PM.sizeofByteArray b)) >>= errorsFromInt -- | Mark the socket as a passive socket, that is, as a socket that -- will be used to accept incoming connection requests using @accept@. -- The <http://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html POSIX specification>--- includes more details. Listen uses the unsafe FFI since it cannot block--- and always returns promptly.-listen ::+-- includes more details. No special preparation is required before+-- calling this function. The author believes that it cannot block+-- for a prolonged period of time.+uninterruptibleListen :: Fd -- ^ Socket -> CInt -- ^ Backlog -> IO (Either Errno ())-listen fd backlog = c_listen fd backlog >>= errorsFromInt+uninterruptibleListen fd backlog = c_listen fd backlog >>= errorsFromInt -- | Connect the socket to the specified socket address. -- The <http://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html POSIX specification>--- includes more details. An @unsafeConnect@ using the unsafe FFI is--- not provided since there is no way to hook such a beast into the--- event manager.+-- includes more details. connect :: Fd -- ^ Fd -> SocketAddress -- ^ Socket address, extensible tagged union -> IO (Either Errno ()) connect fd (SocketAddress sockAddr@(ByteArray sockAddr#)) =- c_safe_connect fd sockAddr# (intToCInt (PM.sizeofByteArray sockAddr)) >>= errorsFromInt+ case PM.isByteArrayPinned sockAddr of+ True -> c_safe_connect fd sockAddr# (intToCInt (PM.sizeofByteArray sockAddr)) >>= errorsFromInt+ False -> do+ let len = PM.sizeofByteArray sockAddr+ x@(MutableByteArray x#) <- PM.newPinnedByteArray len+ PM.copyByteArray x 0 sockAddr 0 len+ c_safe_mutablebytearray_connect fd x# (intToCInt len) >>= errorsFromInt +-- | Connect the socket to the specified socket address.+-- The <http://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html POSIX specification>+-- includes more details. The only sensible way to use this is to+-- give a nonblocking socket as the argument.+uninterruptibleConnect ::+ Fd -- ^ Fd+ -> SocketAddress -- ^ Socket address, extensible tagged union+ -> IO (Either Errno ())+uninterruptibleConnect fd (SocketAddress sockAddr@(ByteArray sockAddr#)) =+ c_unsafe_connect fd sockAddr# (intToCInt (PM.sizeofByteArray sockAddr)) >>= errorsFromInt+ -- | Extract the first connection on the queue of pending connections. The -- <http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html POSIX specification> -- includes more details. This function\'s type differs slightly from@@ -297,8 +384,8 @@ accept :: Fd -- ^ Listening socket -> CInt -- ^ Maximum socket address size- -> IO (Either Errno (SocketAddress,Fd)) -- ^ Peer information and connected socket-accept sock maxSz = do+ -> IO (Either Errno (CInt,SocketAddress,Fd)) -- ^ Peer information and connected socket+accept !sock !maxSz = do sockAddrBuf@(MutableByteArray sockAddrBuf#) <- PM.newPinnedByteArray (cintToInt maxSz) lenBuf@(MutableByteArray lenBuf#) <- PM.newPinnedByteArray (PM.sizeOf (undefined :: CInt)) PM.writeByteArray lenBuf 0 maxSz@@ -306,10 +393,38 @@ if r > (-1) then do (sz :: CInt) <- PM.readByteArray lenBuf 0- sockAddr <- PM.unsafeFreezeByteArray =<< PM.resizeMutableByteArray sockAddrBuf (cintToInt sz)- pure (Right (SocketAddress sockAddr,r))+ -- Why copy when we could just shrink? We want to always return+ -- byte arrays that are not explicitly pinned.+ let minSz = min sz maxSz+ x <- PM.newByteArray (cintToInt minSz)+ PM.copyMutableByteArray x 0 sockAddrBuf 0 (cintToInt minSz)+ sockAddr <- PM.unsafeFreezeByteArray x+ -- sockAddr <- PM.unsafeFreezeByteArray =<< PM.resizeMutableByteArray sockAddrBuf (cintToInt sz)+ pure (Right (sz,SocketAddress sockAddr,r)) else fmap Left getErrno +-- | See 'accept'. This uses the unsafe FFI. Consequently, it does not+-- not need to allocate pinned memory. It only makes sense to call this+-- on a nonblocking socket.+uninterruptibleAccept ::+ Fd -- ^ Listening socket+ -> CInt -- ^ Maximum socket address size+ -> IO (Either Errno (CInt,SocketAddress,Fd)) -- ^ Peer information and connected socket+uninterruptibleAccept !sock !maxSz = do+ sockAddrBuf@(MutableByteArray sockAddrBuf#) <- PM.newByteArray (cintToInt maxSz)+ lenBuf@(MutableByteArray lenBuf#) <- PM.newByteArray (PM.sizeOf (undefined :: CInt))+ PM.writeByteArray lenBuf 0 maxSz+ r <- c_unsafe_accept sock sockAddrBuf# lenBuf#+ if r > (-1)+ then do+ (sz :: CInt) <- PM.readByteArray lenBuf 0+ if sz < maxSz+ then shrinkMutableByteArray sockAddrBuf (cintToInt sz)+ else pure ()+ sockAddr <- PM.unsafeFreezeByteArray sockAddrBuf+ pure (Right (sz,SocketAddress sockAddr,r))+ else fmap Left getErrno+ -- | A variant of 'accept' that does not provide the user with a -- 'SocketAddress' detailing the peer. accept_ ::@@ -318,9 +433,57 @@ accept_ sock = c_safe_ptr_accept sock nullPtr nullPtr >>= errorsFromFd +-- | Retrieve the locally-bound name of the specified socket. The+-- <http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html POSIX specification>+-- of @getsockname@ includes more details.+uninterruptibleGetSocketName ::+ Fd -- ^ Socket+ -> CInt -- ^ Maximum socket address size+ -> IO (Either Errno (CInt,SocketAddress))+uninterruptibleGetSocketName sock maxSz = do+ sockAddrBuf@(MutableByteArray sockAddrBuf#) <- PM.newByteArray (cintToInt maxSz)+ lenBuf@(MutableByteArray lenBuf#) <- PM.newByteArray (PM.sizeOf (undefined :: CInt))+ PM.writeByteArray lenBuf 0 maxSz+ r <- c_unsafe_getsockname sock sockAddrBuf# lenBuf#+ if r == 0+ then do+ (sz :: CInt) <- PM.readByteArray lenBuf 0+ if sz < maxSz+ then shrinkMutableByteArray sockAddrBuf (cintToInt sz)+ else pure ()+ sockAddr <- PM.unsafeFreezeByteArray sockAddrBuf+ pure (Right (sz,SocketAddress sockAddr))+ else fmap Left getErrno++-- | Retrieve the value for the option specified by the 'Option' argument for+-- the socket specified by the 'Fd' argument. The+-- <http://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html POSIX specification>+-- of @getsockopt@ includes more details.+uninterruptibleGetSocketOption ::+ Fd -- ^ Socket+ -> Level -- ^ Socket level+ -> OptionName -- Option name+ -> CInt -- ^ Maximum option value size+ -> IO (Either Errno (CInt,OptionValue))+uninterruptibleGetSocketOption sock level optName maxSz = do+ valueBuf@(MutableByteArray valueBuf#) <- PM.newByteArray (cintToInt maxSz)+ lenBuf@(MutableByteArray lenBuf#) <- PM.newByteArray (PM.sizeOf (undefined :: CInt))+ PM.writeByteArray lenBuf 0 maxSz+ r <- c_unsafe_getsockopt sock level optName valueBuf# lenBuf#+ if r == 0+ then do+ (sz :: CInt) <- PM.readByteArray lenBuf 0+ if sz < maxSz+ then shrinkMutableByteArray valueBuf (cintToInt sz)+ else pure ()+ value <- PM.unsafeFreezeByteArray valueBuf+ pure (Right (sz,OptionValue value))+ else fmap Left getErrno++ -- | Send data from a byte array over a network socket. Users -- may specify an offset and a length to send fewer bytes than are--- actually present in the array. Since this uses the safe interruptible+-- actually present in the array. Since this uses the safe -- FFI, it allocates a pinned copy of the bytearry if it was not -- already pinned. sendByteArray ::@@ -328,7 +491,7 @@ -> ByteArray -- ^ Source byte array -> CInt -- ^ Offset into source array -> CSize -- ^ Length in bytes- -> Flags 'Send -- ^ Flags+ -> MessageFlags 'Send -- ^ Flags -> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer sendByteArray fd b@(ByteArray b#) off len flags = if PM.isByteArrayPinned b then errorsFromSize =<< c_safe_bytearray_send fd b# off len flags@@ -339,7 +502,7 @@ -- | Send data from a mutable byte array over a network socket. Users -- may specify an offset and a length to send fewer bytes than are--- actually present in the array. Since this uses the safe interruptible+-- actually present in the array. Since this uses the safe -- FFI, it allocates a pinned copy of the bytearry if it was not -- already pinned. sendMutableByteArray ::@@ -347,7 +510,7 @@ -> MutableByteArray RealWorld -- ^ Source byte array -> CInt -- ^ Offset into source array -> CSize -- ^ Length in bytes- -> Flags 'Send -- ^ Flags+ -> MessageFlags 'Send -- ^ Flags -> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer sendMutableByteArray fd b@(MutableByteArray b#) off len flags = if PM.isMutableByteArrayPinned b then errorsFromSize =<< c_safe_mutablebytearray_send fd b# off len flags@@ -357,13 +520,13 @@ errorsFromSize =<< c_safe_mutablebytearray_no_offset_send fd x# len flags -- | Send data from an address over a network socket. This is not guaranteed--- to send the entire length. This uses the safe interruptible FFI since+-- to send the entire length. This uses the safe FFI since -- it may block indefinitely. send :: Fd -- ^ Connected socket -> Addr -- ^ Source address -> CSize -- ^ Length in bytes- -> Flags 'Send -- ^ Flags+ -> MessageFlags 'Send -- ^ Flags -> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer send fd (Addr addr) len flags = c_safe_addr_send fd addr len flags >>= errorsFromSize@@ -373,41 +536,41 @@ -- from blocking. On Linux this is accomplished with @O_NONBLOCK@. It is -- often desirable to call 'threadWaitWrite' on a nonblocking socket before -- calling @unsafeSend@ on it.-unsafeSend ::+uninterruptibleSend :: Fd -- ^ Socket -> Addr -- ^ Source address -> CSize -- ^ Length in bytes- -> Flags 'Send -- ^ Flags+ -> MessageFlags 'Send -- ^ Flags -> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer-unsafeSend fd (Addr addr) len flags =+uninterruptibleSend fd (Addr addr) len flags = c_unsafe_addr_send fd addr len flags >>= errorsFromSize -- | Send data from a byte array over a network socket. This uses the unsafe FFI; -- considerations pertaining to 'sendUnsafe' apply to this function as well. Users -- may specify a length to send fewer bytes than are actually present in the -- array.-unsafeSendByteArray ::+uninterruptibleSendByteArray :: Fd -- ^ Socket -> ByteArray -- ^ Source byte array -> CInt -- ^ Offset into source array -> CSize -- ^ Length in bytes- -> Flags 'Send -- ^ Flags+ -> MessageFlags 'Send -- ^ Flags -> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer-unsafeSendByteArray fd (ByteArray b) off len flags =+uninterruptibleSendByteArray fd (ByteArray b) off len flags = c_unsafe_bytearray_send fd b off len flags >>= errorsFromSize -- | Send data from a mutable byte array over a network socket. This uses the unsafe FFI; -- considerations pertaining to 'sendUnsafe' apply to this function as well. Users -- specify an offset and a length to send fewer bytes than are actually present in the -- array.-unsafeSendMutableByteArray ::+uninterruptibleSendMutableByteArray :: Fd -- ^ Socket -> MutableByteArray RealWorld -- ^ Source mutable byte array -> CInt -- ^ Offset into source array -> CSize -- ^ Length in bytes- -> Flags 'Send -- ^ Flags+ -> MessageFlags 'Send -- ^ Flags -> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer-unsafeSendMutableByteArray fd (MutableByteArray b) off len flags =+uninterruptibleSendMutableByteArray fd (MutableByteArray b) off len flags = c_unsafe_mutable_bytearray_send fd b off len flags >>= errorsFromSize -- | Send data from a byte array over an unconnected network socket.@@ -415,15 +578,15 @@ -- apply to this function as well. The offset and length arguments -- cause a slice of the byte array to be sent rather than the entire -- byte array.-unsafeSendToByteArray :: +uninterruptibleSendToByteArray :: Fd -- ^ Socket -> ByteArray -- ^ Source byte array -> CInt -- ^ Offset into source array -> CSize -- ^ Length in bytes- -> Flags 'Send -- ^ Flags+ -> MessageFlags 'Send -- ^ Flags -> SocketAddress -- ^ Socket Address -> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer-unsafeSendToByteArray fd (ByteArray b) off len flags (SocketAddress a@(ByteArray a#)) =+uninterruptibleSendToByteArray fd (ByteArray b) off len flags (SocketAddress a@(ByteArray a#)) = c_unsafe_bytearray_sendto fd b off len flags a# (intToCInt (PM.sizeofByteArray a)) >>= errorsFromSize -- | Send data from a mutable byte array over an unconnected network socket.@@ -431,38 +594,38 @@ -- apply to this function as well. The offset and length arguments -- cause a slice of the mutable byte array to be sent rather than the entire -- byte array.-unsafeSendToMutableByteArray :: +uninterruptibleSendToMutableByteArray :: Fd -- ^ Socket -> MutableByteArray RealWorld -- ^ Source byte array -> CInt -- ^ Offset into source array -> CSize -- ^ Length in bytes- -> Flags 'Send -- ^ Flags+ -> MessageFlags 'Send -- ^ Flags -> SocketAddress -- ^ Socket Address -> IO (Either Errno CSize) -- ^ Number of bytes pushed to send buffer-unsafeSendToMutableByteArray fd (MutableByteArray b) off len flags (SocketAddress a@(ByteArray a#)) =+uninterruptibleSendToMutableByteArray fd (MutableByteArray b) off len flags (SocketAddress a@(ByteArray a#)) = c_unsafe_mutable_bytearray_sendto fd b off len flags a# (intToCInt (PM.sizeofByteArray a)) >>= errorsFromSize -- | Receive data into an address from a network socket. This wraps @recv@ using--- the safe interruptible FFI. When the returned size is zero, there are no+-- the safe FFI. When the returned size is zero, there are no -- additional bytes to receive and the peer has performed an orderly shutdown. receive :: Fd -- ^ Socket -> Addr -- ^ Source address -> CSize -- ^ Length in bytes- -> Flags 'Receive -- ^ Flags+ -> MessageFlags 'Receive -- ^ Flags -> IO (Either Errno CSize) receive fd (Addr addr) len flags = c_safe_addr_recv fd addr len flags >>= errorsFromSize -- | Receive data into a byte array from a network socket. This wraps @recv@ using--- the safe interruptible FFI. When the returned size is zero, there are no+-- the safe FFI. When the returned size is zero, there are no -- additional bytes to receive and the peer has performed an orderly shutdown. receiveByteArray :: Fd -- ^ Socket -> CSize -- ^ Length in bytes- -> Flags 'Receive -- ^ Flags+ -> MessageFlags 'Receive -- ^ Flags -> IO (Either Errno ByteArray)-receiveByteArray fd len flags = do+receiveByteArray !fd !len !flags = do m <- PM.newPinnedByteArray (csizeToInt len) let !(Addr addr) = PM.mutableByteArrayContents m r <- c_safe_addr_recv fd addr len flags@@ -483,66 +646,75 @@ -- the @MSG_DONTWAIT@ flag and handling the resulting @EAGAIN@ or -- @EWOULDBLOCK@. When the returned size is zero, there are no additional -- bytes to receive and the peer has performed an orderly shutdown.-unsafeReceive ::+uninterruptibleReceive :: Fd -- ^ Socket -> Addr -- ^ Source address -> CSize -- ^ Length in bytes- -> Flags 'Receive -- ^ Flags+ -> MessageFlags 'Receive -- ^ Flags -> IO (Either Errno CSize)-unsafeReceive fd (Addr addr) len flags =+uninterruptibleReceive !fd (Addr !addr) !len !flags = c_unsafe_addr_recv fd addr len flags >>= errorsFromSize -- | Receive data into an address from a network socket. This uses the unsafe -- FFI; considerations pertaining to 'receiveUnsafe' apply to this function -- as well. Users may specify a length to receive fewer bytes than are -- actually present in the mutable byte array.-unsafeReceiveMutableByteArray ::+uninterruptibleReceiveMutableByteArray :: Fd -- ^ Socket -> MutableByteArray RealWorld -- ^ Destination byte array -> CInt -- ^ Destination offset -> CSize -- ^ Maximum bytes to receive- -> Flags 'Receive -- ^ Flags+ -> MessageFlags 'Receive -- ^ Flags -> IO (Either Errno CSize) -- ^ Bytes received into array-unsafeReceiveMutableByteArray fd (MutableByteArray b) off len flags =+uninterruptibleReceiveMutableByteArray !fd (MutableByteArray !b) !off !len !flags = c_unsafe_mutable_byte_array_recv fd b off len flags >>= errorsFromSize --- | Receive data into an address from an unconnected network socket. This uses the unsafe--- FFI. Users may specify an offset into the destination byte array.-unsafeReceiveFromMutableByteArray ::+-- | Receive data into an address from an unconnected network socket. This+-- uses the unsafe FFI. Users may specify an offset into the destination+-- byte array. This function does not resize the buffer.+uninterruptibleReceiveFromMutableByteArray :: Fd -- ^ Socket -> MutableByteArray RealWorld -- ^ Destination byte array -> CInt -- ^ Destination offset -> CSize -- ^ Maximum bytes to receive- -> Flags 'Receive -- ^ Flags+ -> MessageFlags 'Receive -- ^ Flags -> CInt -- ^ Maximum socket address size- -> IO (Either Errno (SocketAddress,CSize)) -- ^ Remote host, umber of bytes received into array-unsafeReceiveFromMutableByteArray fd (MutableByteArray b) off len flags maxSz = do- sockAddrBuf@(MutableByteArray sockAddrBuf#) <- PM.newPinnedByteArray (cintToInt maxSz)- lenBuf@(MutableByteArray lenBuf#) <- PM.newPinnedByteArray (PM.sizeOf (undefined :: CInt))+ -> IO (Either Errno (CInt,SocketAddress,CSize))+ -- ^ Remote host, bytes received into array, bytes needed for @addrlen@.+{-# INLINE uninterruptibleReceiveFromMutableByteArray #-}+-- GHC does not inline this unless we give it the pragma. We really+-- want this to inline since inlining typically avoids the Left/Right+-- data constructor allocation.+uninterruptibleReceiveFromMutableByteArray !fd (MutableByteArray !b) !off !len !flags !maxSz = do+ sockAddrBuf@(MutableByteArray sockAddrBuf#) <- PM.newByteArray (cintToInt maxSz)+ lenBuf@(MutableByteArray lenBuf#) <- PM.newByteArray (PM.sizeOf (undefined :: CInt)) PM.writeByteArray lenBuf 0 maxSz r <- c_unsafe_mutable_byte_array_recvfrom fd b off len flags sockAddrBuf# lenBuf# if r > (-1) then do (sz :: CInt) <- PM.readByteArray lenBuf 0- sockAddr <- PM.unsafeFreezeByteArray =<< PM.resizeMutableByteArray sockAddrBuf (cintToInt sz)- pure (Right (SocketAddress sockAddr,cssizeToCSize r))+ if sz < maxSz+ then shrinkMutableByteArray sockAddrBuf (cintToInt sz)+ else pure ()+ sockAddr <- PM.unsafeFreezeByteArray sockAddrBuf+ pure (Right (sz,SocketAddress sockAddr,cssizeToCSize r)) else fmap Left getErrno -- | Receive data into an address from a network socket. This uses the unsafe -- FFI. This does not return the socket address of the remote host that -- sent the packet received.-unsafeReceiveFromMutableByteArray_ ::+uninterruptibleReceiveFromMutableByteArray_ :: Fd -- ^ Socket -> MutableByteArray RealWorld -- ^ Destination byte array -> CInt -- ^ Destination offset -> CSize -- ^ Maximum bytes to receive- -> Flags 'Receive -- ^ Flags+ -> MessageFlags 'Receive -- ^ Flags -> IO (Either Errno CSize) -- ^ Number of bytes received into array-unsafeReceiveFromMutableByteArray_ fd (MutableByteArray b) off len flags =+uninterruptibleReceiveFromMutableByteArray_ !fd (MutableByteArray !b) !off !len !flags = c_unsafe_mutable_byte_array_ptr_recvfrom fd b off len flags nullPtr nullPtr >>= errorsFromSize -- | Close a socket. The <http://pubs.opengroup.org/onlinepubs/009696899/functions/close.html POSIX specification>--- includes more details. This uses the safe interruptible FFI.+-- includes more details. This uses the safe FFI. close :: Fd -- ^ Socket -> IO (Either Errno ())@@ -555,11 +727,32 @@ -- option is set for the socket with non-zero linger time, and the socket -- has untransmitted data, then @close()@ shall block for up to the current -- linger interval until all data is transmitted."-unsafeClose ::+uninterruptibleClose :: Fd -- ^ Socket -> IO (Either Errno ())-unsafeClose fd = c_unsafe_close fd >>= errorsFromInt+uninterruptibleClose fd = c_unsafe_close fd >>= errorsFromInt +-- | Close a socket with the unsafe FFI. Do not check for errors. It is only+-- appropriate to use this when a socket is being closed to handle an+-- exceptional case. Since the user will want the propogate the original+-- exception, the exception provided by 'uninterruptibleClose' would just+-- be discarded. This function allows us to potentially avoid an additional+-- FFI call to 'getErrno'.+uninterruptibleErrorlessClose ::+ Fd -- ^ Socket+ -> IO ()+uninterruptibleErrorlessClose fd = do+ _ <- c_unsafe_close fd+ pure ()++-- | Shutdown a socket. This uses the unsafe FFI.+uninterruptibleShutdown ::+ Fd+ -> ShutdownType+ -> IO (Either Errno ())+uninterruptibleShutdown fd typ =+ c_unsafe_shutdown fd typ >>= errorsFromInt+ errorsFromSize :: CSsize -> IO (Either Errno CSize) errorsFromSize r = if r > (-1) then pure (Right (cssizeToCSize r))@@ -599,4 +792,40 @@ -- -- touchByteArray# :: ByteArray# -> IO () -- touchByteArray# x = IO $ \s -> case touch# x s of s' -> (# s', () #)++shrinkMutableByteArray :: MutableByteArray RealWorld -> Int -> IO ()+shrinkMutableByteArray (MutableByteArray arr) (I# sz) =+ PM.primitive_ (shrinkMutableByteArray# arr sz)++-- | Convert a 16-bit word from host to network byte order (e.g. @htons@).+hostToNetworkShort :: Word16 -> Word16+hostToNetworkShort = case targetByteOrder of+ BigEndian -> id+ LittleEndian -> byteSwap16++-- | Convert a 16-bit word from network to host byte order (e.g. @ntohs@).+networkToHostShort :: Word16 -> Word16+networkToHostShort = case targetByteOrder of+ BigEndian -> id+ LittleEndian -> byteSwap16++-- | Convert a 32-bit word from host to network byte order (e.g. @htonl@).+hostToNetworkLong :: Word32 -> Word32+hostToNetworkLong = case targetByteOrder of+ BigEndian -> id+ LittleEndian -> byteSwap32++-- | Convert a 32-bit word from network to host byte order (e.g. @ntohl@).+networkToHostLong :: Word32 -> Word32+networkToHostLong = case targetByteOrder of+ BigEndian -> id+ LittleEndian -> byteSwap32++{- $conversion+These functions are used to convert IPv4 addresses and ports between network+byte order and host byte order. They are essential when working with+'SocketAddressInternet'. To avoid getting in the way of GHC compile-time+optimizations, these functions are not actually implemented with FFI+calls to @htonl@ and friends. Rather, they are reimplemented in haskell.+-}
src/Posix/Socket/Types.hsc view
@@ -5,6 +5,10 @@ {-# language GeneralizedNewtypeDeriving #-} {-# language KindSignatures #-} +-- This is needed because hsc2hs does not currently handle ticked+-- promoted data constructors correctly.+{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}+ #include <sys/socket.h> #include <netinet/in.h> @@ -14,16 +18,21 @@ ( Domain(..) , Type(..) , Protocol(..)+ , Level(..)+ , OptionName(..)+ , OptionValue(..) , SocketAddress(..) , SocketAddressInternet(..) , SocketAddressInternet6(..) , SocketAddressUnix(..)- , Flags(..)+ , MessageFlags(..) , Message(..)+ , ShutdownType(..) -- * Socket Families , unix , unspecified , internet+ , internet6 -- * Socket Types , stream , datagram@@ -41,12 +50,23 @@ , peek , outOfBand , waitAll+ -- * Shutdown Types+ , read+ , write+ , readWrite+ -- * Socket Levels+ , levelSocket+ -- * Option Names+ , optionError ) where -import Foreign.C.Types (CInt(..))-import Data.Primitive (ByteArray)+import Prelude hiding (read)+ import Data.Bits (Bits,(.|.))+import Data.Primitive (ByteArray) import Data.Word (Word16,Word32,Word64)+import Foreign.C.Types (CInt(..))+ import qualified Data.Kind -- | A socket communications domain, sometimes referred to as a family. The spec@@ -60,29 +80,37 @@ newtype Protocol = Protocol CInt +newtype Level = Level CInt++newtype OptionName = OptionName CInt++-- | Which end of the socket to shutdown.+newtype ShutdownType = ShutdownType CInt+ -- | The direction of a message. The data constructor are only used -- at the type level as phantom arguments. data Message = Send | Receive --- | Receive flags are given by @Flags Receive@ and send flags--- are given by @Flags Send@. This is done because there are+-- | Receive flags are given by @MessageFlags Receive@ and send flags+-- are given by @MessageFlags Send@. This is done because there are -- several flags that are applicable in either a receiving -- context or a sending context.-newtype Flags :: Message -> Data.Kind.Type where- Flags :: CInt -> Flags m+newtype MessageFlags :: Message -> Data.Kind.Type where+ MessageFlags :: CInt -> MessageFlags m deriving stock (Eq) deriving newtype (Bits) -instance Semigroup (Flags m) where (<>) = (.|.)-instance Monoid (Flags m) where mempty = Flags 0+instance Semigroup (MessageFlags m) where (<>) = (.|.)+instance Monoid (MessageFlags m) where mempty = MessageFlags 0 --- | The @sockaddr@ data. This is an extensible tagged union, so this--- library has chosen to represent it as byte array. It is up to--- platform-specific libraries to inhabit this type with values.--- The byte array representing the socket address must be pinned--- since @bind@ uses a safe FFI call.+-- | The @sockaddr@ data. This is an extensible tagged union, so this library+-- has chosen to represent it as byte array. It is up to platform-specific+-- libraries to inhabit this type with values. newtype SocketAddress = SocketAddress ByteArray +-- | The @option_value@ data.+newtype OptionValue = OptionValue ByteArray+ -- | An address for an Internet socket over IPv4. The -- <http://pubs.opengroup.org/onlinepubs/000095399/basedefs/netinet/in.h.html POSIX specification> -- mandates three fields:@@ -161,17 +189,25 @@ internet :: Domain internet = Domain #{const AF_INET} +-- | The @AF_INET6@ communications domain. POSIX declares raw sockets+-- optional. However, they are included here for convenience. Please+-- open an issue if this prevents this library from compiling on a+-- POSIX-compliant operating system that anyone uses for haskell+-- development.+internet6 :: Domain+internet6 = Domain #{const AF_INET6}+ -- | The @MSG_OOB@ receive flag or send flag.-outOfBand :: Flags m-outOfBand = Flags #{const MSG_OOB}+outOfBand :: MessageFlags m+outOfBand = MessageFlags #{const MSG_OOB} -- | The @MSG_PEEK@ receive flag.-peek :: Flags 'Receive-peek = Flags #{const MSG_PEEK}+peek :: MessageFlags Receive+peek = MessageFlags #{const MSG_PEEK} -- | The @MSG_WAITALL@ receive flag.-waitAll :: Flags 'Receive-waitAll = Flags #{const MSG_WAITALL}+waitAll :: MessageFlags Receive+waitAll = MessageFlags #{const MSG_WAITALL} -- | The default protocol for a socket type. defaultProtocol :: Protocol@@ -200,4 +236,24 @@ -- | The @IPPROTO_IPV6@ protocol. ipv6 :: Protocol ipv6 = Protocol #{const IPPROTO_IPV6}++-- | Disable further receive operations (e.g. @SHUT_RD@)+read :: ShutdownType+read = ShutdownType #{const SHUT_RD}++-- | Disable further send operations (e.g. @SHUT_WR@)+write :: ShutdownType+write = ShutdownType #{const SHUT_WR}++-- | Disable further send operations (e.g. @SHUT_RDWR@)+readWrite :: ShutdownType+readWrite = ShutdownType #{const SHUT_RDWR}++-- | Socket error status (e.g. @SOL_SOCKET@)+levelSocket :: Level+levelSocket = Level #{const SOL_SOCKET}++-- | Socket error status (e.g. @SO_ERROR@)+optionError :: OptionName+optionError = OptionName #{const SO_ERROR}