Network.hs 5.1 KB

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