summaryrefslogtreecommitdiff
path: root/dht/src/Network/SocketLike.hs
diff options
context:
space:
mode:
authorJames Crayne <jim.crayne@gmail.com>2019-09-28 13:43:29 -0400
committerJoe Crayne <joe@jerkface.net>2020-01-01 19:27:53 -0500
commit11987749fc6e6d3e53ea737d46d5ab13a16faeb8 (patch)
tree5716463275c2d3e902889db619908ded2a73971c /dht/src/Network/SocketLike.hs
parentadd2c76bced51fde5e9917e7449ef52be70faf87 (diff)
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
Diffstat (limited to 'dht/src/Network/SocketLike.hs')
-rw-r--r--dht/src/Network/SocketLike.hs124
1 files changed, 124 insertions, 0 deletions
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 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE CPP #-}
3-- |
4--
5-- A socket could be used indirectly via a 'System.IO.Handle' or a conduit from
6-- Michael Snoyman's conduit package. But doing so presents an encapsulation
7-- problem. Do we allow access to the underlying socket and trust that it wont
8-- be used in an unsafe way? Or do we protect it at the higher level and deny
9-- access to various state information?
10--
11-- The 'SocketLike' class enables the approach that provides a safe wrapper to
12-- the underlying socket and gives access to various state information without
13-- enabling direct reads or writes.
14module Network.SocketLike
15 ( SocketLike(..)
16 , RestrictedSocket
17 , restrictSocket
18 , restrictHandleSocket
19 -- * Re-exports
20 --
21 -- | To make the 'SocketLike' methods less awkward to use, the types
22 -- 'CUInt', 'SockAddr', and 'PortNumber' are re-exported.
23 , CUInt
24 , PortNumber
25 , SockAddr(..)
26 ) where
27
28import Network.Socket
29 ( PortNumber
30 , SockAddr
31 )
32import Foreign.C.Types ( CUInt )
33
34import qualified Network.Socket as NS
35import System.IO (Handle,hClose,hIsOpen)
36
37-- | A safe (mostly read-only) interface to a 'NS.Socket'. Note that despite
38-- how this class is named, it provides no access to typical 'NS.Socket' uses
39-- like sending or receiving network packets.
40class SocketLike sock where
41 -- | See 'NS.getSocketName'
42 getSocketName :: sock -> IO SockAddr
43 -- | See 'NS.getPeerName'
44 getPeerName :: sock -> IO SockAddr
45 -- | See 'NS.getPeerCred'
46 getPeerCred :: sock -> IO (CUInt, CUInt, CUInt)
47 -- | See 'NS.socketPort'
48 socketPort :: sock -> IO PortNumber
49 -- | See 'NS.sIsConnected'
50 --
51 -- __Warning__: Don't rely on this method if it's possible the socket was
52 -- converted into a 'Handle'.
53 sIsConnected :: sock -> IO Bool
54 -- | See 'NS.sIsBound'
55 sIsBound :: sock -> IO Bool
56 -- | See 'NS.sIsListening'
57 sIsListening :: sock -> IO Bool
58 -- | See 'NS.sIsReadable'
59 sIsReadable :: sock -> IO Bool
60 -- | See 'NS.sIsWritable'
61 sIsWritable :: sock -> IO Bool
62
63 -- | This is the only exposed write-access method to the
64 -- underlying state. Usually implemented by 'NS.close'
65 sClose :: sock -> IO ()
66
67instance SocketLike NS.Socket where
68 getSocketName = NS.getSocketName
69 getPeerName = NS.getPeerName
70 getPeerCred = NS.getPeerCred
71 socketPort = NS.socketPort
72#if MIN_VERSION_network(2,4,0)
73 sIsConnected = NS.isConnected -- warning: this is always False if the socket
74 -- was converted to a Handle
75 sIsBound = NS.isBound
76 sIsListening = NS.isListening
77 sIsReadable = NS.isReadable
78 sIsWritable = NS.isWritable
79 sClose = NS.close
80#else
81 sIsConnected = NS.sIsConnected -- warning: this is always False if the socket
82 -- was converted to a Handle
83 sIsBound = NS.sIsBound
84 sIsListening = NS.sIsListening
85 sIsReadable = NS.sIsReadable
86 sIsWritable = NS.sIsWritable
87 sClose = NS.sClose
88#endif
89
90
91-- | An encapsulated socket. Data reads and writes are not possible.
92data RestrictedSocket = Restricted (Maybe Handle) NS.Socket deriving Show
93
94instance SocketLike RestrictedSocket where
95 getSocketName (Restricted mb sock) = NS.getSocketName sock
96 getPeerName (Restricted mb sock) = NS.getPeerName sock
97 getPeerCred (Restricted mb sock) = NS.getPeerCred sock
98 socketPort (Restricted mb sock) = NS.socketPort sock
99#if MIN_VERSION_network(2,4,0)
100 sIsConnected (Restricted mb sock) = maybe (NS.isConnected sock) (hIsOpen) mb
101 sIsBound (Restricted mb sock) = NS.isBound sock
102 sIsListening (Restricted mb sock) = NS.isListening sock
103 sIsReadable (Restricted mb sock) = NS.isReadable sock
104 sIsWritable (Restricted mb sock) = NS.isWritable sock
105 sClose (Restricted mb sock) = maybe (NS.close sock) (\h -> hClose h >> NS.close sock) mb
106#else
107 sIsConnected (Restricted mb sock) = maybe (NS.sIsConnected sock) (hIsOpen) mb
108 sIsBound (Restricted mb sock) = NS.sIsBound sock
109 sIsListening (Restricted mb sock) = NS.sIsListening sock
110 sIsReadable (Restricted mb sock) = NS.sIsReadable sock
111 sIsWritable (Restricted mb sock) = NS.sIsWritable sock
112 sClose (Restricted mb sock) = maybe (NS.sClose sock) (\h -> hClose h >> NS.sClose sock) mb
113#endif
114
115-- | Create a 'RestrictedSocket' that explicitly disallows sending or
116-- receiving data.
117restrictSocket :: NS.Socket -> RestrictedSocket
118restrictSocket socket = Restricted Nothing socket
119
120-- | Build a 'RestrictedSocket' for which 'sClose' will close the given
121-- 'Handle'. It is intended that this 'Handle' was obtained via
122-- 'NS.socketToHandle'.
123restrictHandleSocket :: Handle -> NS.Socket -> RestrictedSocket
124restrictHandleSocket h socket = Restricted (Just h) socket