-
Notifications
You must be signed in to change notification settings - Fork 204
Expand file tree
/
Copy pathPosix.hsc
More file actions
350 lines (328 loc) · 13.8 KB
/
Posix.hsc
File metadata and controls
350 lines (328 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
{-# LANGUAGE CPP #-}
##include "HsNetDef.h"
#if defined(mingw32_HOST_OS)
# include "windows.h"
#endif
module Network.Socket.Buffer.Posix (
sendBufTo
, sendBuf
, recvBufFrom
, recvBuf
, recvBufNoWait
, sendBufMsg
, recvBufMsg
) where
#if !defined(mingw32_HOST_OS)
import Foreign.C.Error (getErrno, eAGAIN, eWOULDBLOCK)
#endif
import Foreign.Marshal.Alloc (alloca, allocaBytes)
import Foreign.Marshal.Utils (with)
import GHC.IO.Exception (IOErrorType(InvalidArgument))
import System.IO.Error (mkIOError, ioeSetErrorString, catchIOError)
#if defined(mingw32_HOST_OS)
import GHC.IO.FD (FD(..), readRawBufferPtr, writeRawBufferPtr)
import qualified Network.Socket.Types as Generic
import Network.Socket.Win32.CmsgHdr
import Network.Socket.Win32.MsgHdr
import Network.Socket.Win32.WSABuf
#else
import Network.Socket.Posix.CmsgHdr
import Network.Socket.Posix.MsgHdr
import Network.Socket.Posix.IOVec
#endif
import Network.Socket.Imports
#if defined(mingw32_HOST_OS)
import Network.Socket.Internal hiding (throwSocketErrorWaitRead, throwSocketErrorWaitWrite, throwSocketErrorWaitReadBut)
#else
import Network.Socket.Internal
#endif
import Network.Socket.Name (getPeerName)
import Network.Socket.Types (
SocketAddress,
withSocketAddress,
withNewSocketAddress,
peekSocketAddress,
)
import Network.Socket.Types.Posix
import Network.Socket.Flag
#if defined(mingw32_HOST_OS)
type DWORD = Word32
type LPDWORD = Ptr DWORD
-- On Windows, threadWaitRead is a no-op, so don't sweat wrapping Posix.Socket -> generic Socket.
throwSocketErrorWaitRead :: (Eq a, Num a) => Socket -> String -> IO a -> IO a
throwSocketErrorWaitRead _ = throwSocketErrorIfMinus1Retry
throwSocketErrorWaitWrite :: (Eq a, Num a) => Socket -> String -> IO a -> IO a
throwSocketErrorWaitWrite _ = throwSocketErrorIfMinus1Retry
throwSocketErrorWaitReadBut :: (Eq a, Num a) => (CInt -> Bool) -> Socket -> String -> IO a -> IO a
throwSocketErrorWaitReadBut exempt _ = throwSocketErrorIfMinus1ButRetry exempt
-- getPeerName takes the generic Socket; wrap our Posix.Socket.
getPeerName' :: SocketAddress sa => Socket -> IO sa
getPeerName' s = getPeerName (Generic.Socket (Left s))
#else
-- On non-Windows, Socket = Posix.Socket, so the generic versions work.
getPeerName' :: SocketAddress sa => Socket -> IO sa
getPeerName' = getPeerName
#endif
-- | Send data to the socket. The recipient can be specified
-- 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.
sendBufTo :: SocketAddress sa =>
Socket -- (possibly) bound/connected Socket
-> Ptr a
-> Int -- Data to send
-> sa
-> IO Int -- Number of Bytes sent
sendBufTo s ptr nbytes sa =
withSocketAddress sa $ \p_sa siz -> fromIntegral <$> do
withFdSocket s $ \fd -> do
let sz = fromIntegral siz
n = fromIntegral nbytes
flags = 0
throwSocketErrorWaitWrite s "Network.Socket.sendBufTo" $
c_sendto fd ptr n flags p_sa sz
#if defined(mingw32_HOST_OS)
socket2FD :: Socket -> IO FD
socket2FD s = do
fd <- unsafeFdSocket s
-- HACK, 1 means True
return $ FD{ fdFD = fd, fdIsSocket_ = 1 }
#endif
-- | 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.
sendBuf :: Socket -- Bound/Connected Socket
-> Ptr Word8 -- Pointer to the data to send
-> Int -- Length of the buffer
-> IO Int -- Number of Bytes sent
sendBuf s str len = fromIntegral <$> do
#if defined(mingw32_HOST_OS)
-- 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.
fd <- socket2FD s
let clen = fromIntegral len
throwSocketErrorIfMinus1Retry "Network.Socket.sendBuf" $
writeRawBufferPtr "Network.Socket.sendBuf" fd (castPtr str) 0 clen
#else
withFdSocket s $ \fd -> do
let flags = 0
clen = fromIntegral len
throwSocketErrorWaitWrite s "Network.Socket.sendBuf" $
c_send fd str clen flags
#endif
-- | Receive data from the socket, writing it into buffer instead of
-- creating a new string. The socket need not be in a connected
-- state. Returns @(nbytes, address)@ where @nbytes@ is the number of
-- bytes received and @address@ is a 'SockAddr' representing the
-- address of the sending socket.
--
-- If the first return value is zero, it means EOF.
--
-- For 'Stream' sockets, the second return value would be invalid.
--
-- NOTE: blocking on Windows unless you compile with -threaded (see
-- GHC ticket #1129)
recvBufFrom :: SocketAddress sa => Socket -> Ptr a -> Int -> IO (Int, sa)
recvBufFrom s ptr nbytes
| nbytes <= 0 = ioError (mkInvalidRecvArgError "Network.Socket.recvBufFrom")
| otherwise = withNewSocketAddress $ \ptr_sa sz -> alloca $ \ptr_len ->
withFdSocket s $ \fd -> do
poke ptr_len (fromIntegral sz)
let cnbytes = fromIntegral nbytes
flags = 0
len <- throwSocketErrorWaitRead s "Network.Socket.recvBufFrom" $
c_recvfrom fd ptr cnbytes flags ptr_sa ptr_len
sockaddr <- peekSocketAddress ptr_sa
`catchIOError` \_ -> getPeerName' s
return (fromIntegral len, sockaddr)
-- | Receive data from the socket. The socket must be in a connected
-- state. This function may return fewer bytes than specified. If the
-- message is longer than the specified length, it may be discarded
-- depending on the type of socket. This function may block until a
-- message arrives.
--
-- Considering hardware and network realities, the maximum number of
-- bytes to receive should be a small power of 2, e.g., 4096.
--
-- The return value is the length of received data. Zero means
-- EOF. Historical note: Version 2.8.x.y or earlier,
-- an EOF error was thrown. This was changed in version 3.0.
recvBuf :: Socket -> Ptr Word8 -> Int -> IO Int
recvBuf s ptr nbytes
| nbytes <= 0 = ioError (mkInvalidRecvArgError "Network.Socket.recvBuf")
| otherwise = do
#if defined(mingw32_HOST_OS)
-- see comment in sendBuf above.
fd <- socket2FD s
let cnbytes = fromIntegral nbytes
len <- throwSocketErrorIfMinus1Retry "Network.Socket.recvBuf" $
readRawBufferPtr "Network.Socket.recvBuf" fd ptr 0 cnbytes
#else
len <- withFdSocket s $ \fd ->
throwSocketErrorWaitRead s "Network.Socket.recvBuf" $
c_recv fd (castPtr ptr) (fromIntegral nbytes) 0{-flags-}
#endif
return $ fromIntegral len
-- | Receive data from the socket. This function returns immediately
-- even if data is not available. In other words, IO manager is NOT
-- involved. The length of data is returned if received.
-- -1 is returned in the case of EAGAIN or EWOULDBLOCK.
-- -2 is returned in other error cases.
recvBufNoWait :: Socket -> Ptr Word8 -> Int -> IO Int
recvBufNoWait s ptr nbytes = withFdSocket s $ \fd -> do
#if defined(mingw32_HOST_OS)
alloca $ \ptr_bytes -> do
res <- c_ioctlsocket fd #{const FIONREAD} ptr_bytes
avail <- peek ptr_bytes
r <- if res == #{const NO_ERROR} && avail > 0 then
c_recv fd (castPtr ptr) (fromIntegral nbytes) 0{-flags-}
else if avail == 0 then
-- Socket would block, could also mean socket is closed but
-- can't distinguish
return (-1)
else do err <- c_WSAGetLastError
if err == #{const WSAEWOULDBLOCK}
|| err == #{const WSAEINPROGRESS} then
return (-1)
else
return (-2)
return $ fromIntegral r
#else
r <- c_recv fd (castPtr ptr) (fromIntegral nbytes) 0{-flags-}
if r >= 0 then
return $ fromIntegral r
else do
err <- getErrno
if err == eAGAIN || err == eWOULDBLOCK then
return (-1)
else
return (-2)
#endif
mkInvalidRecvArgError :: String -> IOError
mkInvalidRecvArgError loc = ioeSetErrorString (mkIOError
InvalidArgument
loc Nothing Nothing) "non-positive length"
-- | Send data to the socket using sendmsg(2).
sendBufMsg :: SocketAddress sa
=> Socket -- ^ Socket
-> sa -- ^ Destination address
-> [(Ptr Word8,Int)] -- ^ Data to be sent
-> [Cmsg] -- ^ Control messages
-> MsgFlag -- ^ Message flags
-> IO Int -- ^ The length actually sent
sendBufMsg s sa bufsizs cmsgs flags = do
sz <- withSocketAddress sa $ \addrPtr addrSize ->
#if !defined(mingw32_HOST_OS)
withIOVec bufsizs $ \(iovsPtr, iovsLen) -> do
#else
withWSABuf bufsizs $ \(wsaBPtr, wsaBLen) -> do
#endif
withCmsgs cmsgs $ \ctrlPtr ctrlLen -> do
let msgHdr = MsgHdr {
msgName = addrPtr
, msgNameLen = fromIntegral addrSize
#if !defined(mingw32_HOST_OS)
, msgIov = iovsPtr
, msgIovLen = fromIntegral iovsLen
#else
, msgBuffer = wsaBPtr
, msgBufferLen = fromIntegral wsaBLen
#endif
, msgCtrl = castPtr ctrlPtr
, msgCtrlLen = fromIntegral ctrlLen
, msgFlags = 0
}
cflags = fromMsgFlag flags
withFdSocket s $ \fd ->
with msgHdr $ \msgHdrPtr ->
throwSocketErrorWaitWrite s "Network.Socket.Buffer.sendMsg" $
#if !defined(mingw32_HOST_OS)
c_sendmsg fd msgHdrPtr cflags
#else
alloca $ \send_ptr ->
c_sendmsg fd msgHdrPtr (fromIntegral cflags) send_ptr nullPtr nullPtr
#endif
return $ fromIntegral sz
-- | Receive data from the socket using recvmsg(2). The supplied
-- buffers are filled in order, with subsequent buffers used only
-- after all the preceding buffers are full. If the message is short
-- enough some of the supplied buffers may remain unused.
recvBufMsg :: SocketAddress sa
=> Socket -- ^ Socket
-> [(Ptr Word8,Int)] -- ^ A list of (buffer, buffer-length) pairs.
-- If the total length is not large enough,
-- 'MSG_TRUNC' is returned
-> Int -- ^ The buffer size for control messages.
-- If the length is not large enough,
-- 'MSG_CTRUNC' is returned
-> MsgFlag -- ^ Message flags
-> IO (sa,Int,[Cmsg],MsgFlag) -- ^ Source address, total bytes received, control messages and message flags
recvBufMsg s bufsizs clen flags = do
withNewSocketAddress $ \addrPtr addrSize ->
allocaBytes clen $ \ctrlPtr ->
#if !defined(mingw32_HOST_OS)
withIOVec bufsizs $ \(iovsPtr, iovsLen) -> do
let msgHdr = MsgHdr {
msgName = addrPtr
, msgNameLen = fromIntegral addrSize
, msgIov = iovsPtr
, msgIovLen = fromIntegral iovsLen
, msgCtrl = castPtr ctrlPtr
, msgCtrlLen = fromIntegral clen
, msgFlags = 0
#else
withWSABuf bufsizs $ \(wsaBPtr, wsaBLen) -> do
let msgHdr = MsgHdr {
msgName = addrPtr
, msgNameLen = fromIntegral addrSize
, msgBuffer = wsaBPtr
, msgBufferLen = fromIntegral wsaBLen
, msgCtrl = if clen == 0 then nullPtr else castPtr ctrlPtr
, msgCtrlLen = fromIntegral clen
, msgFlags = fromIntegral $ fromMsgFlag flags
#endif
}
_cflags = fromMsgFlag flags
withFdSocket s $ \fd -> do
with msgHdr $ \msgHdrPtr -> do
len <- (fmap fromIntegral) <$>
#if !defined(mingw32_HOST_OS)
throwSocketErrorWaitRead s "Network.Socket.Buffer.recvmsg" $
c_recvmsg fd msgHdrPtr _cflags
#else
alloca $ \len_ptr -> do
_ <- throwSocketErrorWaitReadBut (== #{const WSAEMSGSIZE}) s "Network.Socket.Buffer.recvmsg" $
c_recvmsg fd msgHdrPtr len_ptr nullPtr nullPtr
peek len_ptr
#endif
sockaddr <- peekSocketAddress addrPtr `catchIOError` \_ -> getPeerName' s
hdr <- peek msgHdrPtr
cmsgs' <- parseCmsgs msgHdrPtr
let flags' = MsgFlag $ fromIntegral $ msgFlags hdr
return (sockaddr, len, cmsgs', flags')
#if !defined(mingw32_HOST_OS)
foreign import ccall unsafe "send"
c_send :: CInt -> Ptr a -> CSize -> CInt -> IO CInt
foreign import ccall unsafe "sendmsg"
c_sendmsg :: CInt -> Ptr (MsgHdr sa) -> CInt -> IO CInt -- fixme CSsize
foreign import ccall unsafe "recvmsg"
c_recvmsg :: CInt -> Ptr (MsgHdr sa) -> CInt -> IO CInt
#else
foreign import CALLCONV SAFE_ON_WIN "ioctlsocket"
c_ioctlsocket :: CInt -> CLong -> Ptr CULong -> IO CInt
foreign import CALLCONV SAFE_ON_WIN "WSAGetLastError"
c_WSAGetLastError :: IO CInt
foreign import CALLCONV SAFE_ON_WIN "WSASendMsg"
-- fixme Handle for SOCKET, see #426
c_sendmsg :: CInt -> Ptr (MsgHdr sa) -> DWORD -> LPDWORD -> Ptr () -> Ptr () -> IO CInt
foreign import CALLCONV SAFE_ON_WIN "WSARecvMsg"
c_recvmsg :: CInt -> Ptr (MsgHdr sa) -> LPDWORD -> Ptr () -> Ptr () -> IO CInt
#endif
foreign import ccall unsafe "recv"
c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt
foreign import CALLCONV SAFE_ON_WIN "sendto"
c_sendto :: CInt -> Ptr a -> CSize -> CInt -> Ptr sa -> CInt -> IO CInt
foreign import CALLCONV SAFE_ON_WIN "recvfrom"
c_recvfrom :: CInt -> Ptr a -> CSize -> CInt -> Ptr sa -> Ptr CInt -> IO CInt