Skip to content

Commit 54b872f

Browse files
committed
Merge PR #433.
2 parents bd50c53 + f6b1f7c commit 54b872f

33 files changed

+1890
-375
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ cabal.sandbox.config
1919
.cabal-sandbox
2020
.stack-work/
2121
.ghc.*
22+
.vscode

.travis.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@ before_cache:
2626

2727
matrix:
2828
include:
29-
- compiler: "ghc-7.8.4"
30-
# env: TEST=--disable-tests BENCH=--disable-benchmarks
31-
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.8.4], sources: [hvr-ghc]}}
32-
- compiler: "ghc-7.10.3"
33-
# env: TEST=--disable-tests BENCH=--disable-benchmarks
34-
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-7.10.3], sources: [hvr-ghc]}}
3529
- compiler: "ghc-8.0.2"
3630
# env: TEST=--disable-tests BENCH=--disable-benchmarks
3731
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.0.2], sources: [hvr-ghc]}}
@@ -44,7 +38,7 @@ matrix:
4438
- compiler: "ghc-8.6.5"
4539
# env: TEST=--disable-tests BENCH=--disable-benchmarks
4640
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
47-
- compiler: "ghc-8.8.1"
41+
- compiler: "ghc-8.8.3"
4842
# env: TEST=--disable-tests BENCH=--disable-benchmarks
4943
addons: {apt: {packages: [ghc-ppa-tools,cabal-install-3.0,ghc-8.8.1], sources: [hvr-ghc]}}
5044
- compiler: "ghc-head"

Network/Socket.hs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,20 @@ module Network.Socket
131131
, ShutdownCmd(..)
132132

133133
-- * Socket options
134-
, SocketOption(..)
134+
, SocketOption(SockOpt
135+
,Debug,ReuseAddr,Type,SoError,DontRoute,Broadcast
136+
,SendBuffer,RecvBuffer,KeepAlive,OOBInline,TimeToLive
137+
,MaxSegment,NoDelay,Cork,Linger,ReusePort
138+
,RecvLowWater,SendLowWater,RecvTimeOut,SendTimeOut
139+
,UseLoopBack,UserTimeout,IPv6Only
140+
,RecvIPv4TTL,RecvIPv4TOS,RecvIPv4PktInfo
141+
,RecvIPv6HopLimit,RecvIPv6TClass,RecvIPv6PktInfo)
135142
, isSupportedSocketOption
143+
, whenSupported
136144
, getSocketOption
137145
, setSocketOption
146+
, getSockOpt
147+
, setSockOpt
138148

139149
-- * Socket
140150
, Socket
@@ -183,12 +193,14 @@ module Network.Socket
183193
, socketPortSafe
184194
, socketPort
185195

196+
#if !defined(mingw32_HOST_OS)
186197
-- * UNIX-domain socket
187198
, isUnixDomainSocketAvailable
188199
, socketPair
189200
, sendFd
190201
, recvFd
191202
, getPeerCredential
203+
#endif
192204

193205
-- * Name information
194206
, getNameInfo
@@ -205,14 +217,40 @@ module Network.Socket
205217
, recvBuf
206218
, sendBufTo
207219
, recvBufFrom
208-
220+
-- ** Advanced IO
221+
, sendBufMsg
222+
, recvBufMsg
223+
, MsgFlag(MSG_OOB,MSG_DONTROUTE,MSG_PEEK,MSG_EOR,MSG_TRUNC,MSG_CTRUNC,MSG_WAITALL)
224+
-- ** Control message (ancillary data)
225+
, Cmsg(..)
226+
, CmsgId(CmsgId
227+
,CmsgIdIPv4TTL
228+
,CmsgIdIPv6HopLimit
229+
,CmsgIdIPv4TOS
230+
,CmsgIdIPv6TClass
231+
,CmsgIdIPv4PktInfo
232+
,CmsgIdIPv6PktInfo)
233+
-- ** APIs for control message
234+
, lookupCmsg
235+
, filterCmsg
236+
, decodeCmsg
237+
, encodeCmsg
238+
-- ** Class and yypes for control message
239+
, ControlMessage(..)
240+
, IPv4TTL(..)
241+
, IPv6HopLimit(..)
242+
, IPv4TOS(..)
243+
, IPv6TClass(..)
244+
, IPv4PktInfo(..)
245+
, IPv6PktInfo(..)
209246
-- * Special constants
210247
, maxListenQueue
211248
) where
212249

213-
import Network.Socket.Buffer hiding (sendBufTo, recvBufFrom)
250+
import Network.Socket.Buffer hiding (sendBufTo, recvBufFrom, sendBufMsg, recvBufMsg)
214251
import Network.Socket.Cbits
215252
import Network.Socket.Fcntl
253+
import Network.Socket.Flag
216254
import Network.Socket.Handle
217255
import Network.Socket.If
218256
import Network.Socket.Info
@@ -223,4 +261,9 @@ import Network.Socket.Shutdown
223261
import Network.Socket.SockAddr
224262
import Network.Socket.Syscall hiding (connect, bind, accept)
225263
import Network.Socket.Types
264+
#if !defined(mingw32_HOST_OS)
265+
import Network.Socket.Posix.Cmsg
226266
import Network.Socket.Unix
267+
#else
268+
import Network.Socket.Win32.Cmsg
269+
#endif

Network/Socket/Address.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ module Network.Socket.Address (
1616
-- * Sending and receiving data from a buffer
1717
, sendBufTo
1818
, recvBufFrom
19+
-- * Advanced IO
20+
, sendBufMsg
21+
, recvBufMsg
1922
) where
2023

2124
import Network.Socket.ByteString.IO

Network/Socket/Buffer.hsc

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,41 @@ module Network.Socket.Buffer (
1111
, recvBufFrom
1212
, recvBuf
1313
, recvBufNoWait
14+
, sendBufMsg
15+
, recvBufMsg
1416
) where
1517

1618
#if !defined(mingw32_HOST_OS)
1719
import Foreign.C.Error (getErrno, eAGAIN, eWOULDBLOCK)
20+
#else
21+
import Foreign.Ptr (nullPtr)
1822
#endif
19-
import Foreign.Marshal.Alloc (alloca)
23+
import Foreign.Marshal.Alloc (alloca, allocaBytes)
24+
import Foreign.Marshal.Utils (with)
2025
import GHC.IO.Exception (IOErrorType(InvalidArgument))
2126
import System.IO.Error (mkIOError, ioeSetErrorString, catchIOError)
2227

2328
#if defined(mingw32_HOST_OS)
2429
import GHC.IO.FD (FD(..), readRawBufferPtr, writeRawBufferPtr)
30+
import Network.Socket.Win32.CmsgHdr
31+
import Network.Socket.Win32.MsgHdr
32+
import Network.Socket.Win32.WSABuf
33+
#else
34+
import Network.Socket.Posix.CmsgHdr
35+
import Network.Socket.Posix.MsgHdr
36+
import Network.Socket.Posix.IOVec
2537
#endif
2638

2739
import Network.Socket.Imports
2840
import Network.Socket.Internal
2941
import Network.Socket.Name
3042
import Network.Socket.Types
43+
import Network.Socket.Flag
44+
45+
#if defined(mingw32_HOST_OS)
46+
type DWORD = Word32
47+
type LPDWORD = Ptr DWORD
48+
#endif
3149

3250
-- | Send data to the socket. The recipient can be specified
3351
-- explicitly, so the socket need not be in a connected state.
@@ -178,18 +196,134 @@ mkInvalidRecvArgError loc = ioeSetErrorString (mkIOError
178196
InvalidArgument
179197
loc Nothing Nothing) "non-positive length"
180198

199+
-- | Send data to the socket using sendmsg(2).
200+
sendBufMsg :: SocketAddress sa
201+
=> Socket -- ^ Socket
202+
-> sa -- ^ Destination address
203+
-> [(Ptr Word8,Int)] -- ^ Data to be sent
204+
-> [Cmsg] -- ^ Control messages
205+
-> MsgFlag -- ^ Message flags
206+
-> IO Int -- ^ The length actually sent
207+
sendBufMsg s sa bufsizs cmsgs flags = do
208+
sz <- withSocketAddress sa $ \addrPtr addrSize ->
209+
#if !defined(mingw32_HOST_OS)
210+
withIOVec bufsizs $ \(iovsPtr, iovsLen) -> do
211+
#else
212+
withWSABuf bufsizs $ \(wsaBPtr, wsaBLen) -> do
213+
#endif
214+
withCmsgs cmsgs $ \ctrlPtr ctrlLen -> do
215+
let msgHdr = MsgHdr {
216+
msgName = addrPtr
217+
, msgNameLen = fromIntegral addrSize
218+
#if !defined(mingw32_HOST_OS)
219+
, msgIov = iovsPtr
220+
, msgIovLen = fromIntegral iovsLen
221+
#else
222+
, msgBuffer = wsaBPtr
223+
, msgBufferLen = fromIntegral wsaBLen
224+
#endif
225+
, msgCtrl = castPtr ctrlPtr
226+
, msgCtrlLen = fromIntegral ctrlLen
227+
, msgFlags = 0
228+
}
229+
cflags = fromMsgFlag flags
230+
withFdSocket s $ \fd ->
231+
with msgHdr $ \msgHdrPtr ->
232+
throwSocketErrorWaitWrite s "Network.Socket.Buffer.sendMsg" $
233+
#if !defined(mingw32_HOST_OS)
234+
c_sendmsg fd msgHdrPtr cflags
235+
#else
236+
alloca $ \send_ptr ->
237+
c_sendmsg fd msgHdrPtr (fromIntegral cflags) send_ptr nullPtr nullPtr
238+
#endif
239+
return $ fromIntegral sz
240+
241+
-- | Receive data from the socket using recvmsg(2). The supplied
242+
-- buffers are filled in order, with subsequent buffers used only
243+
-- after all the preceding buffers are full. If the message is short
244+
-- enough some of the supplied buffers may remain unused.
245+
recvBufMsg :: SocketAddress sa
246+
=> Socket -- ^ Socket
247+
-> [(Ptr Word8,Int)] -- ^ A list of (buffer, buffer-length) pairs.
248+
-- If the total length is not large enough,
249+
-- 'MSG_TRUNC' is returned
250+
-> Int -- ^ The buffer size for control messages.
251+
-- If the length is not large enough,
252+
-- 'MSG_CTRUNC' is returned
253+
-> MsgFlag -- ^ Message flags
254+
-> IO (sa,Int,[Cmsg],MsgFlag) -- ^ Source address, total bytes received, control messages and message flags
255+
recvBufMsg s bufsizs clen flags = do
256+
withNewSocketAddress $ \addrPtr addrSize ->
257+
allocaBytes clen $ \ctrlPtr ->
258+
#if !defined(mingw32_HOST_OS)
259+
withIOVec bufsizs $ \(iovsPtr, iovsLen) -> do
260+
#else
261+
withWSABuf bufsizs $ \(wsaBPtr, wsaBLen) -> do
262+
#endif
263+
let msgHdr = MsgHdr {
264+
msgName = addrPtr
265+
, msgNameLen = fromIntegral addrSize
266+
#if !defined(mingw32_HOST_OS)
267+
, msgIov = iovsPtr
268+
, msgIovLen = fromIntegral iovsLen
269+
#else
270+
, msgBuffer = wsaBPtr
271+
, msgBufferLen = fromIntegral wsaBLen
272+
#endif
273+
#if !defined(mingw32_HOST_OS)
274+
, msgCtrl = castPtr ctrlPtr
275+
#else
276+
, msgCtrl = if clen == 0 then nullPtr else castPtr ctrlPtr
277+
#endif
278+
, msgCtrlLen = fromIntegral clen
279+
#if !defined(mingw32_HOST_OS)
280+
, msgFlags = 0
281+
#else
282+
, msgFlags = fromIntegral $ fromMsgFlag flags
283+
#endif
284+
}
285+
_cflags = fromMsgFlag flags
286+
withFdSocket s $ \fd -> do
287+
with msgHdr $ \msgHdrPtr -> do
288+
len <- (fmap fromIntegral) <$>
289+
#if !defined(mingw32_HOST_OS)
290+
throwSocketErrorWaitRead s "Network.Socket.Buffer.recvmg" $
291+
c_recvmsg fd msgHdrPtr _cflags
292+
#else
293+
alloca $ \len_ptr -> do
294+
_ <- throwSocketErrorWaitReadBut (== #{const WSAEMSGSIZE}) s "Network.Socket.Buffer.recvmg" $
295+
c_recvmsg fd msgHdrPtr len_ptr nullPtr nullPtr
296+
peek len_ptr
297+
#endif
298+
sockaddr <- peekSocketAddress addrPtr `catchIOError` \_ -> getPeerName s
299+
hdr <- peek msgHdrPtr
300+
cmsgs <- parseCmsgs msgHdrPtr
301+
let flags' = MsgFlag $ fromIntegral $ msgFlags hdr
302+
return (sockaddr, len, cmsgs, flags')
303+
181304
#if !defined(mingw32_HOST_OS)
182305
foreign import ccall unsafe "send"
183306
c_send :: CInt -> Ptr a -> CSize -> CInt -> IO CInt
307+
foreign import ccall unsafe "sendmsg"
308+
c_sendmsg :: CInt -> Ptr (MsgHdr sa) -> CInt -> IO CInt -- fixme CSsize
309+
foreign import ccall unsafe "recvmsg"
310+
c_recvmsg :: CInt -> Ptr (MsgHdr sa) -> CInt -> IO CInt
184311
#else
185312
foreign import CALLCONV SAFE_ON_WIN "ioctlsocket"
186313
c_ioctlsocket :: CInt -> CLong -> Ptr CULong -> IO CInt
187314
foreign import CALLCONV SAFE_ON_WIN "WSAGetLastError"
188315
c_WSAGetLastError :: IO CInt
316+
foreign import CALLCONV SAFE_ON_WIN "WSASendMsg"
317+
-- fixme Handle for SOCKET, see #426
318+
c_sendmsg :: CInt -> Ptr (MsgHdr sa) -> DWORD -> LPDWORD -> Ptr () -> Ptr () -> IO CInt
319+
foreign import CALLCONV SAFE_ON_WIN "WSARecvMsg"
320+
c_recvmsg :: CInt -> Ptr (MsgHdr sa) -> LPDWORD -> Ptr () -> Ptr () -> IO CInt
189321
#endif
322+
190323
foreign import ccall unsafe "recv"
191324
c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt
192325
foreign import CALLCONV SAFE_ON_WIN "sendto"
193326
c_sendto :: CInt -> Ptr a -> CSize -> CInt -> Ptr sa -> CInt -> IO CInt
194327
foreign import CALLCONV SAFE_ON_WIN "recvfrom"
195328
c_recvfrom :: CInt -> Ptr a -> CSize -> CInt -> Ptr sa -> Ptr CInt -> IO CInt
329+

Network/Socket/ByteString.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,16 @@ module Network.Socket.ByteString
3333
-- * Receive data from a socket
3434
, recv
3535
, recvFrom
36+
37+
-- * Advanced send and recv
38+
, sendMsg
39+
, recvMsg
3640
) where
3741

3842
import Data.ByteString (ByteString)
3943

40-
import Network.Socket.ByteString.IO hiding (sendTo, sendAllTo, recvFrom)
4144
import qualified Network.Socket.ByteString.IO as G
45+
import Network.Socket.ByteString.IO hiding (sendTo, sendAllTo, recvFrom)
4246
import Network.Socket.Types
4347

4448
-- ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)