From 11987749fc6e6d3e53ea737d46d5ab13a16faeb8 Mon Sep 17 00:00:00 2001 From: James Crayne Date: Sat, 28 Sep 2019 13:43:29 -0400 Subject: Factor out some new libraries word64-map: Data.Word64Map network-addr: Network.Address tox-crypto: Crypto.Tox lifted-concurrent: Control.Concurrent.Lifted.Instrument Control.Concurrent.Async.Lifted.Instrument psq-wrap: Data.Wrapper.PSQInt Data.Wrapper.PSQ minmax-psq: Data.MinMaxPSQ tasks: Control.Concurrent.Tasks kad: Network.Kademlia Network.Kademlia.Bootstrap Network.Kademlia.Routing Network.Kademlia.CommonAPI Network.Kademlia.Persistence Network.Kademlia.Search --- dht/src/Network/SocketLike.hs | 124 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 dht/src/Network/SocketLike.hs (limited to 'dht/src/Network/SocketLike.hs') diff --git a/dht/src/Network/SocketLike.hs b/dht/src/Network/SocketLike.hs new file mode 100644 index 00000000..d533dd7f --- /dev/null +++ b/dht/src/Network/SocketLike.hs @@ -0,0 +1,124 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE CPP #-} +-- | +-- +-- A socket could be used indirectly via a 'System.IO.Handle' or a conduit from +-- Michael Snoyman's conduit package. But doing so presents an encapsulation +-- problem. Do we allow access to the underlying socket and trust that it wont +-- be used in an unsafe way? Or do we protect it at the higher level and deny +-- access to various state information? +-- +-- The 'SocketLike' class enables the approach that provides a safe wrapper to +-- the underlying socket and gives access to various state information without +-- enabling direct reads or writes. +module Network.SocketLike + ( SocketLike(..) + , RestrictedSocket + , restrictSocket + , restrictHandleSocket + -- * Re-exports + -- + -- | To make the 'SocketLike' methods less awkward to use, the types + -- 'CUInt', 'SockAddr', and 'PortNumber' are re-exported. + , CUInt + , PortNumber + , SockAddr(..) + ) where + +import Network.Socket + ( PortNumber + , SockAddr + ) +import Foreign.C.Types ( CUInt ) + +import qualified Network.Socket as NS +import System.IO (Handle,hClose,hIsOpen) + +-- | A safe (mostly read-only) interface to a 'NS.Socket'. Note that despite +-- how this class is named, it provides no access to typical 'NS.Socket' uses +-- like sending or receiving network packets. +class SocketLike sock where + -- | See 'NS.getSocketName' + getSocketName :: sock -> IO SockAddr + -- | See 'NS.getPeerName' + getPeerName :: sock -> IO SockAddr + -- | See 'NS.getPeerCred' + getPeerCred :: sock -> IO (CUInt, CUInt, CUInt) + -- | See 'NS.socketPort' + socketPort :: sock -> IO PortNumber + -- | See 'NS.sIsConnected' + -- + -- __Warning__: Don't rely on this method if it's possible the socket was + -- converted into a 'Handle'. + sIsConnected :: sock -> IO Bool + -- | See 'NS.sIsBound' + sIsBound :: sock -> IO Bool + -- | See 'NS.sIsListening' + sIsListening :: sock -> IO Bool + -- | See 'NS.sIsReadable' + sIsReadable :: sock -> IO Bool + -- | See 'NS.sIsWritable' + sIsWritable :: sock -> IO Bool + + -- | This is the only exposed write-access method to the + -- underlying state. Usually implemented by 'NS.close' + sClose :: sock -> IO () + +instance SocketLike NS.Socket where + getSocketName = NS.getSocketName + getPeerName = NS.getPeerName + getPeerCred = NS.getPeerCred + socketPort = NS.socketPort +#if MIN_VERSION_network(2,4,0) + sIsConnected = NS.isConnected -- warning: this is always False if the socket + -- was converted to a Handle + sIsBound = NS.isBound + sIsListening = NS.isListening + sIsReadable = NS.isReadable + sIsWritable = NS.isWritable + sClose = NS.close +#else + sIsConnected = NS.sIsConnected -- warning: this is always False if the socket + -- was converted to a Handle + sIsBound = NS.sIsBound + sIsListening = NS.sIsListening + sIsReadable = NS.sIsReadable + sIsWritable = NS.sIsWritable + sClose = NS.sClose +#endif + + +-- | An encapsulated socket. Data reads and writes are not possible. +data RestrictedSocket = Restricted (Maybe Handle) NS.Socket deriving Show + +instance SocketLike RestrictedSocket where + getSocketName (Restricted mb sock) = NS.getSocketName sock + getPeerName (Restricted mb sock) = NS.getPeerName sock + getPeerCred (Restricted mb sock) = NS.getPeerCred sock + socketPort (Restricted mb sock) = NS.socketPort sock +#if MIN_VERSION_network(2,4,0) + sIsConnected (Restricted mb sock) = maybe (NS.isConnected sock) (hIsOpen) mb + sIsBound (Restricted mb sock) = NS.isBound sock + sIsListening (Restricted mb sock) = NS.isListening sock + sIsReadable (Restricted mb sock) = NS.isReadable sock + sIsWritable (Restricted mb sock) = NS.isWritable sock + sClose (Restricted mb sock) = maybe (NS.close sock) (\h -> hClose h >> NS.close sock) mb +#else + sIsConnected (Restricted mb sock) = maybe (NS.sIsConnected sock) (hIsOpen) mb + sIsBound (Restricted mb sock) = NS.sIsBound sock + sIsListening (Restricted mb sock) = NS.sIsListening sock + sIsReadable (Restricted mb sock) = NS.sIsReadable sock + sIsWritable (Restricted mb sock) = NS.sIsWritable sock + sClose (Restricted mb sock) = maybe (NS.sClose sock) (\h -> hClose h >> NS.sClose sock) mb +#endif + +-- | Create a 'RestrictedSocket' that explicitly disallows sending or +-- receiving data. +restrictSocket :: NS.Socket -> RestrictedSocket +restrictSocket socket = Restricted Nothing socket + +-- | Build a 'RestrictedSocket' for which 'sClose' will close the given +-- 'Handle'. It is intended that this 'Handle' was obtained via +-- 'NS.socketToHandle'. +restrictHandleSocket :: Handle -> NS.Socket -> RestrictedSocket +restrictHandleSocket h socket = Restricted (Just h) socket -- cgit v1.2.3