-
Notifications
You must be signed in to change notification settings - Fork 208
Expand file tree
/
Copy pathBuffer.hsc
More file actions
515 lines (486 loc) · 21.6 KB
/
Copy pathBuffer.hsc
File metadata and controls
515 lines (486 loc) · 21.6 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
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
##include "HsNetDef.h"
#if defined(mingw32_HOST_OS)
# include "winsock2.h"
# include "windows.h"
# include "mswsock.h"
# include "ntstatus.h"
#endif
module Network.Socket.Buffer (
sendBufTo
, sendBuf
, recvBufFrom
, recvBuf
, recvBufNoWait
, sendBufMsg
, recvBufMsg
) where
#if !defined(mingw32_HOST_OS)
import Foreign.C.Error (getErrno, eAGAIN, eWOULDBLOCK)
#else
import Foreign.Ptr (nullPtr)
#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 Network.Socket.Win32.CmsgHdr
import Network.Socket.Win32.MsgHdr
import Network.Socket.Win32.WSABuf
# if defined(HAS_WINIO)
import qualified GHC.Event.Windows as Mgr
import GHC.IO.SubSystem ((<!>))
import Foreign.Ptr (wordPtrToPtr)
# endif
#else
import Network.Socket.Posix.CmsgHdr
import Network.Socket.Posix.MsgHdr
import Network.Socket.Posix.IOVec
#endif
import Network.Socket.Imports
import Network.Socket.Internal
import Network.Socket.Name
import Network.Socket.Types
import Network.Socket.Flag
#if defined(mingw32_HOST_OS)
type DWORD = Word32
type LPDWORD = Ptr DWORD
#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
-- TODO: remove fromIntegral for WinIO
return $ FD{ fdFD = fromIntegral 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 = do
#if defined(mingw32_HOST_OS)
# if defined(HAS_WINIO)
recvBufFromMIO s ptr nbytes <!> recvBufFromWinIO s ptr nbytes
# else
recvBufFromMIO s ptr nbytes
# endif
#else
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)
#endif
#if defined(mingw32_HOST_OS)
-- MIO (old I/O manager) implementation
recvBufFromMIO :: SocketAddress sa => Socket -> Ptr a -> Int -> IO (Int, sa)
recvBufFromMIO s ptr nbytes =
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)
# if defined(HAS_WINIO)
recvBufFromWinIO :: SocketAddress sa => Socket -> Ptr a -> Int -> IO (Int, sa)
recvBufFromWinIO s ptr nbytes =
withNewSocketAddress $ \ptr_sa sz -> alloca $ \ptr_len ->
withFdSocket s $ \sock -> do
poke ptr_len (fromIntegral sz)
len <- fmap fromIntegral $ Mgr.withException "recvBufFrom" $
Mgr.withOverlapped "recvBufFrom" (wordPtrToPtr $ fromIntegral sock) 0
(startCB sock ptr_sa ptr_len) completionCB
sockaddr <- peekSocketAddress ptr_sa
`catchIOError` \_ -> getPeerName s
return (len, sockaddr)
where
startCB :: CSocket -> Ptr sa -> Ptr CInt -> Mgr.LPOVERLAPPED -> IO (Mgr.CbResult Int)
startCB sock ptr_sa ptr_len lpOverlapped = do
alloca $ \flags -> do
poke flags 0
with (WSABuf (castPtr ptr) (fromIntegral nbytes)) $ \pWsaBuf -> do
ret <- c_WSARecvFrom sock pWsaBuf 1 nullPtr flags ptr_sa ptr_len (castPtr lpOverlapped) nullPtr
-- Check WSAGetLastError immediately: if the operation didn't
-- complete synchronously (ret /= 0), we must distinguish
-- ERROR_IO_PENDING (async completion forthcoming) from real
-- errors (no IOCP notification will arrive, so CbPending
-- would hang forever).
err <- c_WSAGetLastError
if ret == 0
then return $ Mgr.CbDone Nothing
else if err == #{const ERROR_IO_PENDING}
then return Mgr.CbPending
else return $ Mgr.CbError (fromIntegral err)
completionCB err dwBytes
| err == #{const ERROR_SUCCESS} = Mgr.ioSuccess $ fromIntegral dwBytes
| err == #{const WSAECONNABORTED} = Mgr.ioSuccess 0
| err == #{const WSAECONNRESET} = Mgr.ioSuccess 0
| err == #{const WSAEDISCON} = Mgr.ioSuccess 0
| err == #{const ERROR_HANDLE_EOF} = Mgr.ioSuccess 0
| err == #{const ERROR_BROKEN_PIPE} = Mgr.ioSuccess 0
| err == #{const ERROR_NO_MORE_ITEMS} = Mgr.ioSuccess 0
| err == #{const ERROR_OPERATION_ABORTED} = Mgr.ioSuccess 0
| err == #{const ERROR_IO_INCOMPLETE} = Mgr.ioSuccess 0
| otherwise = Mgr.ioFailed err
# endif /* HAS_WINIO */
#endif /* mingw32_HOST_OS */
-- | 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)
# if defined(HAS_WINIO)
recvBufMIO s ptr nbytes <!> recvBufWinIO s ptr nbytes
# else
recvBufMIO s ptr nbytes
# endif
#else
len <- withFdSocket s $ \fd ->
throwSocketErrorWaitRead s "Network.Socket.recvBuf" $
c_recv fd (castPtr ptr) (fromIntegral nbytes) 0{-flags-}
return $ fromIntegral len
#endif
#if defined(mingw32_HOST_OS)
-- MIO (old I/O manager) implementation
recvBufMIO :: Socket -> Ptr Word8 -> Int -> IO Int
recvBufMIO s ptr nbytes = do
-- 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
return $ fromIntegral len
# if defined(HAS_WINIO)
recvBufWinIO :: Socket -> Ptr Word8 -> Int -> IO Int
recvBufWinIO s ptr nbytes = withFdSocket s $ \sock ->
fmap fromIntegral $ Mgr.withException "recvBuf" $
Mgr.withOverlapped "recvBuf" (wordPtrToPtr $ fromIntegral sock) 0 (startCB sock) completionCB
where
startCB :: CSocket -> Mgr.LPOVERLAPPED -> IO (Mgr.CbResult Int)
startCB sock lpOverlapped = do
alloca $ \flags -> do
poke flags 0
with (WSABuf (castPtr ptr) (fromIntegral nbytes)) $ \pWsaBuf -> do
ret <- c_WSARecv sock pWsaBuf 1 nullPtr flags (castPtr lpOverlapped) nullPtr
-- Check WSAGetLastError immediately: if the operation didn't
-- complete synchronously (ret /= 0), we must distinguish
-- ERROR_IO_PENDING (async completion forthcoming) from real
-- errors (no IOCP notification will arrive, so CbPending
-- would hang forever).
err <- c_WSAGetLastError
if ret == 0
then return $ Mgr.CbDone Nothing
else if err == #{const ERROR_IO_PENDING}
then return Mgr.CbPending
else return $ Mgr.CbError (fromIntegral err)
-- https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsarecv#return-value
completionCB err dwBytes
| err == #{const ERROR_SUCCESS} = Mgr.ioSuccess $ fromIntegral dwBytes
| err == #{const WSAECONNABORTED} = Mgr.ioSuccess 0
| err == #{const WSAECONNRESET} = Mgr.ioSuccess 0
| err == #{const WSAEDISCON} = Mgr.ioSuccess 0
| err == #{const ERROR_HANDLE_EOF} = Mgr.ioSuccess 0
| err == #{const ERROR_BROKEN_PIPE} = Mgr.ioSuccess 0
| err == #{const ERROR_NO_MORE_ITEMS} = Mgr.ioSuccess 0
| err == #{const ERROR_OPERATION_ABORTED} = Mgr.ioSuccess 0
| err == #{const ERROR_IO_INCOMPLETE} = Mgr.ioSuccess 0
| otherwise = Mgr.ioFailed err
# endif /* HAS_WINIO */
#endif /* mingw32_HOST_OS */
-- | 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 <-
#if !defined(mingw32_HOST_OS)
fmap fromIntegral <$>
throwSocketErrorWaitRead s "Network.Socket.Buffer.recvmsg" $
c_recvmsg fd msgHdrPtr _cflags
#else
# if defined(HAS_WINIO)
(recvBufMsgMIO fd msgHdrPtr <!> recvBufMsgWinIO fd msgHdrPtr)
# else
recvBufMsgMIO fd msgHdrPtr
# endif
#endif
sockaddr <- peekSocketAddress addrPtr `catchIOError` \_ -> getPeerName s
hdr <- peek msgHdrPtr
#if !defined(mingw32_HOST_OS)
cmsgs <- parseCmsgs msgHdrPtr
let flags' = MsgFlag $ fromIntegral $ msgFlags hdr
#else
let flags' = MsgFlag $ fromIntegral (msgFlags hdr)
-- If the control buffer was truncated (MSG_CTRUNC), the
-- control data may be invalid and parsing could segfault.
cmsgs <- if msgCtrl hdr == nullPtr || (msgFlags hdr .&. #{const MSG_CTRUNC}) /= 0
then return []
else parseCmsgs msgHdrPtr
#endif
return (sockaddr, len, cmsgs, flags')
#if !defined(mingw32_HOST_OS)
foreign import ccall unsafe "send"
c_send :: CSocket -> Ptr a -> CSize -> CInt -> IO CInt
foreign import ccall unsafe "sendmsg"
c_sendmsg :: CSocket -> Ptr (MsgHdr sa) -> CInt -> IO CInt -- fixme CSsize
foreign import ccall unsafe "recvmsg"
c_recvmsg :: CSocket -> Ptr (MsgHdr sa) -> CInt -> IO CInt
#else
foreign import CALLCONV SAFE_ON_WIN "ioctlsocket"
c_ioctlsocket :: CSocket -> 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 :: CSocket -> Ptr (MsgHdr sa) -> DWORD -> LPDWORD -> Ptr () -> Ptr () -> IO CInt
foreign import CALLCONV SAFE_ON_WIN "WSARecvMsg"
c_recvmsg :: CSocket -> Ptr (MsgHdr sa) -> LPDWORD -> Ptr () -> Ptr () -> IO CInt
foreign import CALLCONV unsafe "WSARecv"
c_WSARecv :: CSocket -> Ptr WSABuf -> DWORD -> LPDWORD -> LPDWORD -> Ptr () -> Ptr () -> IO CInt
foreign import CALLCONV unsafe "WSARecvFrom"
c_WSARecvFrom :: CSocket -> Ptr WSABuf -> DWORD -> LPDWORD -> LPDWORD -> Ptr sa -> Ptr CInt -> Ptr () -> Ptr () -> IO CInt
-- Helper functions for recvBufMsg on Windows
recvBufMsgMIO :: CSocket -> Ptr (MsgHdr sa) -> IO Int
recvBufMsgMIO fd msgHdrPtr = alloca $ \len_ptr -> do
_ <- throwSocketErrorIfMinus1Retry "Network.Socket.Buffer.recvmsg" $
c_recvmsg fd msgHdrPtr len_ptr nullPtr nullPtr
fromIntegral <$> peek len_ptr
# if defined(HAS_WINIO)
recvBufMsgWinIO :: CSocket -> Ptr (MsgHdr sa) -> IO Int
recvBufMsgWinIO fd msgHdrPtr = do
-- Perform async WSARecvMsg using withOverlapped
-- (socket already associated in socket creation)
fmap fromIntegral $ Mgr.withException "recvMsg" $
Mgr.withOverlapped "recvMsg" (wordPtrToPtr $ fromIntegral fd) 0 startCB completionCB
where
startCB :: Mgr.LPOVERLAPPED -> IO (Mgr.CbResult Int)
startCB lpOverlapped = do
ret <- c_recvmsg fd msgHdrPtr nullPtr (castPtr lpOverlapped) nullPtr
-- Check WSAGetLastError immediately: if the operation didn't
-- complete synchronously (ret /= 0), we must distinguish
-- ERROR_IO_PENDING (async completion forthcoming) from real
-- errors (no IOCP notification will arrive, so CbPending
-- would hang forever).
err <- c_WSAGetLastError
if ret == 0
then return $ Mgr.CbDone Nothing
else if err == #{const ERROR_IO_PENDING}
then return Mgr.CbPending
else return $ Mgr.CbError (fromIntegral err)
completionCB err dwBytes
| err == #{const ERROR_SUCCESS} = Mgr.ioSuccess $ fromIntegral dwBytes
| err == #{const WSAEMSGSIZE} = Mgr.ioSuccess $ fromIntegral dwBytes
| err == #{const STATUS_BUFFER_OVERFLOW} = Mgr.ioSuccess $ fromIntegral dwBytes -- truncated msg
| err == #{const WSAECONNRESET} = Mgr.ioSuccess 0
| err == #{const WSAECONNABORTED} = Mgr.ioSuccess 0
| err == #{const WSAESHUTDOWN} = Mgr.ioSuccess 0
| err == #{const WSAEDISCON} = Mgr.ioSuccess 0
| err == #{const ERROR_HANDLE_EOF} = Mgr.ioSuccess 0
| err == #{const ERROR_BROKEN_PIPE} = Mgr.ioSuccess 0
| err == #{const ERROR_NO_MORE_ITEMS} = Mgr.ioSuccess 0
| err == #{const ERROR_OPERATION_ABORTED} = Mgr.ioSuccess 0
| err == #{const ERROR_IO_INCOMPLETE} = Mgr.ioSuccess 0
| otherwise = Mgr.ioFailed err
# endif /* HAS_WINIO */
#endif /* mingw32_HOST_OS */
foreign import ccall unsafe "recv"
c_recv :: CSocket -> Ptr CChar -> CSize -> CInt -> IO CInt
foreign import CALLCONV SAFE_ON_WIN "sendto"
c_sendto :: CSocket -> Ptr a -> CSize -> CInt -> Ptr sa -> CInt -> IO CInt
foreign import CALLCONV SAFE_ON_WIN "recvfrom"
c_recvfrom :: CSocket -> Ptr a -> CSize -> CInt -> Ptr sa -> Ptr CInt -> IO CInt