Network.hs 5.0 KB

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