Network.hs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. -- | UniformIO functions for TCP connections
  2. module System.IO.Uniform.Network (
  3. SocketIO,
  4. BoundedPort,
  5. connectTo,
  6. connectToHost,
  7. bindPort,
  8. accept,
  9. closePort,
  10. getPeer
  11. ) where
  12. import System.IO.Uniform
  13. import System.IO.Uniform.External
  14. import Foreign
  15. import Foreign.C.Types
  16. import Foreign.C.String
  17. import Foreign.C.Error
  18. import qualified Data.IP as IP
  19. import qualified Data.ByteString as BS
  20. import qualified Data.List as L
  21. import Control.Exception
  22. import Control.Monad
  23. import qualified Network.Socket as Soc
  24. import System.IO.Error
  25. import System.Posix.Types (Fd(..))
  26. -- | UniformIO TCP connections.
  27. instance UniformIO SocketIO where
  28. uRead (SocketIO s) n = allocaArray n (
  29. \b -> do
  30. count <- c_recv s b (fromIntegral n)
  31. if count < 0
  32. then throwErrno "could not read"
  33. else BS.packCStringLen (b, fromIntegral count)
  34. )
  35. uRead (TlsSocketIO s) n = allocaArray n (
  36. \b -> do
  37. count <- c_recvTls s b $ fromIntegral n
  38. if count < 0
  39. then throwErrno "could not read"
  40. else BS.packCStringLen (b, fromIntegral count)
  41. )
  42. uPut (SocketIO s) t = BS.useAsCStringLen t (
  43. \(str, n) -> do
  44. count <- c_send s str $ fromIntegral n
  45. when (count < 0) $ throwErrno "could not write"
  46. )
  47. uPut (TlsSocketIO s) t = BS.useAsCStringLen t (
  48. \(str, n) -> do
  49. count <- c_sendTls s str $ fromIntegral n
  50. when (count < 0) $ throwErrno "could not write"
  51. )
  52. uClose (SocketIO s) = do
  53. f <- Fd <$> c_prepareToClose s
  54. closeFd f
  55. uClose (TlsSocketIO s) = do
  56. d <- c_closeTls s
  57. f <- Fd <$> c_prepareToClose d
  58. closeFd f
  59. startTls st (SocketIO s) = withCString (tlsCertificateChainFile st) (
  60. \cert -> withCString (tlsPrivateKeyFile st) (
  61. \key -> withCString (tlsDHParametersFile st) (
  62. \para -> do
  63. r <- c_startSockTls s cert key para
  64. if r == nullPtr
  65. then throwErrno "could not start TLS"
  66. else return . TlsSocketIO $ r
  67. )
  68. )
  69. )
  70. startTls _ s@(TlsSocketIO _) = return s
  71. isSecure (SocketIO _) = False
  72. isSecure (TlsSocketIO _) = True
  73. isEOF (SocketIO s) = c_isDsEof s
  74. isEOF (TlsSocketIO t) = c_isTlsEof t
  75. -- | connectToHost hostName port
  76. --
  77. -- Connects to the given host and port.
  78. connectToHost :: String -> Int -> IO SocketIO
  79. connectToHost host port = do
  80. ip <- getAddr
  81. connectTo ip port
  82. where
  83. getAddr :: IO IP.IP
  84. getAddr = do
  85. add <- Soc.getAddrInfo Nothing (Just host) Nothing
  86. case add of
  87. [] -> throwIO $ mkIOError doesNotExistErrorType "host not found" Nothing Nothing
  88. (a:_) -> case Soc.addrAddress a of
  89. Soc.SockAddrInet _ a' -> return . IP.IPv4 . IP.fromHostAddress $ a'
  90. Soc.SockAddrInet6 _ _ a' _ -> return . IP.IPv6 . IP.fromHostAddress6 $ a'
  91. _ -> throwIO $ mkIOError doesNotExistErrorType "host not found" Nothing Nothing
  92. -- | ConnecctTo ipAddress port
  93. --
  94. -- Connects to the given port of the host at the given IP address.
  95. connectTo :: IP.IP -> Int -> IO SocketIO
  96. connectTo host port = do
  97. r <- case host of
  98. IP.IPv4 host' -> SocketIO <$> c_connect4 (fromIntegral . IP.toHostAddress $ host') (fromIntegral port)
  99. IP.IPv6 host' -> SocketIO <$> withArray (ipToArray host') (
  100. \add -> c_connect6 add (fromIntegral port)
  101. )
  102. if sock r == nullPtr
  103. then throwErrno "could not connect to host"
  104. else return r
  105. where
  106. ipToArray :: IP.IPv6 -> [CUChar]
  107. ipToArray ip = let
  108. (w0, w1, w2, w3) = IP.toHostAddress6 ip
  109. in L.concat [wtoc w0, wtoc w1, wtoc w2, wtoc w3]
  110. wtoc :: Word32 -> [CUChar]
  111. wtoc w = let
  112. c0 = fromIntegral $ mod w 256
  113. w1 = div w 256
  114. c1 = fromIntegral $ mod w1 256
  115. w2 = div w1 256
  116. c2 = fromIntegral $ mod w2 256
  117. c3 = fromIntegral $ div w2 256
  118. in [c3, c2, c1, c0]
  119. -- | bindPort port
  120. -- Binds to the given IP port, becoming ready to accept connections on it.
  121. -- Binding to port numbers under 1024 will fail unless performed by the superuser,
  122. -- once bounded, a process can reduce its privileges and still accept clients on that port.
  123. bindPort :: Int -> IO BoundedPort
  124. bindPort port = do
  125. r <- fmap BoundedPort $ c_getPort $ fromIntegral port
  126. if lis r == nullPtr
  127. then throwErrno "could not bind to port"
  128. else return r
  129. -- | accept port
  130. --
  131. -- Accept clients on a port previously bound with bindPort.
  132. accept :: BoundedPort -> IO SocketIO
  133. accept port = do
  134. r <- SocketIO <$> c_accept (lis port)
  135. if sock r == nullPtr
  136. then throwErrno "could not accept connection"
  137. else return r
  138. -- | Gets the address of the peer socket of a internet connection.
  139. getPeer :: SocketIO -> IO (IP.IP, Int)
  140. getPeer s = allocaArray 16 (
  141. \p6 -> alloca (
  142. \p4 -> alloca (
  143. \iptype -> do
  144. p <- c_getPeer (sock s) p4 p6 iptype
  145. if p == -1
  146. then throwErrno "could not get peer address"
  147. else do
  148. iptp <- peek iptype
  149. if iptp == 1
  150. then do --IPv6
  151. add <- peekArray 16 p6
  152. return (IP.IPv6 . IP.toIPv6b $ map fromIntegral add, fromIntegral p)
  153. else do --IPv4
  154. add <- peek p4
  155. return (IP.IPv4 . IP.fromHostAddress . fromIntegral $ add, fromIntegral p)
  156. )
  157. )
  158. )