Network.hs 5.4 KB

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