PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/base/Network/Socket.idr

https://github.com/ejenk/Idris-dev
Idris | 349 lines | 256 code | 61 blank | 32 comment | 43 complexity | d6ec3e067a45235512fa2cf72289cbfb MD5 | raw file
Possible License(s): BSD-3-Clause
  1. -- Time to do this properly.
  2. -- Low-Level C Sockets bindings for Idris. Used by higher-level, cleverer things.
  3. -- (C) SimonJF, MIT Licensed, 2014
  4. module IdrisNet.Socket
  5. %include C "idris_net.h"
  6. %include C "sys/types.h"
  7. %include C "sys/socket.h"
  8. %include C "netdb.h"
  9. %access public
  10. ByteLength : Type
  11. ByteLength = Int
  12. class ToCode a where
  13. toCode : a -> Int
  14. -- Socket Families.
  15. -- The ones that people might actually use. We're not going to need US Government
  16. -- proprietary ones...
  17. data SocketFamily = AF_UNSPEC -- Unspecified
  18. | AF_INET -- IP / UDP etc. IPv4
  19. | AF_INET6 -- IP / UDP etc. IPv6
  20. instance Show SocketFamily where
  21. show AF_UNSPEC = "AF_UNSPEC"
  22. show AF_INET = "AF_INET"
  23. show AF_INET6 = "AF_INET4"
  24. instance ToCode SocketFamily where
  25. toCode AF_UNSPEC = 0
  26. toCode AF_INET = 2
  27. toCode AF_INET6 = 10
  28. getSocketFamily : Int -> Maybe SocketFamily
  29. getSocketFamily i = Prelude.List.lookup i [(0, AF_UNSPEC), (2, AF_INET), (10, AF_INET6)]
  30. -- Socket Types.
  31. data SocketType = NotASocket -- Not a socket, used in certain operations
  32. | Stream -- TCP
  33. | Datagram -- UDP
  34. | RawSocket -- Raw sockets. A guy can dream.
  35. instance Show SocketType where
  36. show NotASocket = "Not a socket"
  37. show Stream = "Stream"
  38. show Datagram = "Datagram"
  39. show Raw = "Raw"
  40. instance ToCode SocketType where
  41. toCode NotASocket = 0
  42. toCode Stream = 1
  43. toCode Datagram = 2
  44. toCode Raw = 3
  45. data RecvStructPtr = RSPtr Ptr
  46. data RecvfromStructPtr = RFPtr Ptr
  47. data BufPtr = BPtr Ptr
  48. data SockaddrPtr = SAPtr Ptr
  49. -- Protocol Number. Generally good enough to just set it to 0.
  50. ProtocolNumber : Type
  51. ProtocolNumber = Int
  52. -- SocketError: Error thrown by a socket operation
  53. SocketError : Type
  54. SocketError = Int
  55. -- SocketDescriptor: Native C Socket Descriptor
  56. SocketDescriptor : Type
  57. SocketDescriptor = Int
  58. data SocketAddress = IPv4Addr Int Int Int Int
  59. | IPv6Addr -- Not implemented (yet)
  60. | Hostname String
  61. | InvalidAddress -- Used when there's a parse error
  62. instance Show SocketAddress where
  63. show (IPv4Addr i1 i2 i3 i4) = concat $ Prelude.List.intersperse "." (map show [i1, i2, i3, i4])
  64. show IPv6Addr = "NOT IMPLEMENTED YET"
  65. show (Hostname host) = host
  66. show InvalidAddress = "Invalid"
  67. Port : Type
  68. Port = Int
  69. -- Backlog used within listen() call -- number of incoming calls
  70. BACKLOG : Int
  71. BACKLOG = 20
  72. -- FIXME: This *must* be pulled in from C
  73. EAGAIN : Int
  74. EAGAIN = 11
  75. -- TODO: Expand to non-string payloads
  76. record UDPRecvData : Type where
  77. MkUDPRecvData :
  78. (remote_addr : SocketAddress) ->
  79. (remote_port : Port) ->
  80. (recv_data : String) ->
  81. (data_len : Int) ->
  82. UDPRecvData
  83. record UDPAddrInfo : Type where
  84. MkUDPAddrInfo :
  85. (remote_addr : SocketAddress) ->
  86. (remote_port : Port) ->
  87. UDPAddrInfo
  88. -- Frees a given pointer
  89. public
  90. sock_free : BufPtr -> IO ()
  91. sock_free (BPtr ptr) = mkForeign (FFun "idrnet_free" [FPtr] FUnit) ptr
  92. public
  93. sockaddr_free : SockaddrPtr -> IO ()
  94. sockaddr_free (SAPtr ptr) = mkForeign (FFun "idrnet_free" [FPtr] FUnit) ptr
  95. -- Allocates an amount of memory given by the ByteLength parameter.
  96. -- Used to allocate a mutable pointer to be given to the Recv functions.
  97. public
  98. sock_alloc : ByteLength -> IO BufPtr
  99. sock_alloc bl = map BPtr $ mkForeign (FFun "idrnet_malloc" [FInt] FPtr) bl
  100. record Socket : Type where
  101. MkSocket : (descriptor : SocketDescriptor) ->
  102. (family : SocketFamily) ->
  103. (socketType : SocketType) ->
  104. (protocolNumber : ProtocolNumber) ->
  105. Socket
  106. getErrno : IO Int
  107. getErrno = mkForeign (FFun "idrnet_errno" [] FInt)
  108. -- Creates a UNIX socket with the given family, socket type and protocol number.
  109. -- Returns either a socket or an error.
  110. socket : SocketFamily -> SocketType -> ProtocolNumber -> IO (Either SocketError Socket)
  111. socket sf st pn = do
  112. socket_res <- mkForeign (FFun "socket" [FInt, FInt, FInt] FInt) (toCode sf) (toCode st) pn
  113. if socket_res == -1 then -- error
  114. map Left getErrno
  115. else
  116. return $ Right (MkSocket socket_res sf st pn)
  117. close : Socket -> IO ()
  118. close sock = mkForeign (FFun "close" [FInt] FUnit) (descriptor sock)
  119. private
  120. saString : (Maybe SocketAddress) -> String
  121. saString (Just sa) = show sa
  122. saString Nothing = ""
  123. -- Binds a socket to the given socket address and port.
  124. -- Returns 0 on success, an error code otherwise.
  125. bind : Socket -> (Maybe SocketAddress) -> Port -> IO Int
  126. bind sock addr port = do
  127. bind_res <- (mkForeign (FFun "idrnet_bind" [FInt, FInt, FInt, FString, FInt] FInt)
  128. (descriptor sock) (toCode $ family sock) (toCode $ socketType sock) (saString addr) port)
  129. if bind_res == (-1) then -- error
  130. getErrno
  131. else return 0 -- Success
  132. -- Connects to a given address and port.
  133. -- Returns 0 on success, and an error number on error.
  134. connect : Socket -> SocketAddress -> Port -> IO Int
  135. connect sock addr port = do
  136. conn_res <- (mkForeign (FFun "idrnet_connect" [FInt, FInt, FInt, FString, FInt] FInt)
  137. (descriptor sock) (toCode $ family sock) (toCode $ socketType sock) (show addr) port)
  138. if conn_res == (-1) then
  139. getErrno
  140. else return 0
  141. -- Listens on a bound socket.
  142. listen : Socket -> IO Int
  143. listen sock = do
  144. listen_res <- mkForeign (FFun "listen" [FInt, FInt] FInt) (descriptor sock) BACKLOG
  145. if listen_res == (-1) then
  146. getErrno
  147. else return 0
  148. -- Parses a textual representation of an IPv4 address into a SocketAddress
  149. parseIPv4 : String -> SocketAddress
  150. parseIPv4 str = case splitted of
  151. (i1 :: i2 :: i3 :: i4 :: _) => IPv4Addr i1 i2 i3 i4
  152. _ => InvalidAddress
  153. where toInt' : String -> Integer
  154. toInt' = cast
  155. toInt : String -> Int
  156. toInt s = fromInteger $ toInt' s
  157. splitted : List Int
  158. splitted = map toInt (Prelude.Strings.split (\c => c == '.') str)
  159. -- Retrieves a socket address from a sockaddr pointer
  160. getSockAddr : SockaddrPtr -> IO SocketAddress
  161. getSockAddr (SAPtr ptr) = do
  162. addr_family_int <- mkForeign (FFun "idrnet_sockaddr_family" [FPtr] FInt) ptr
  163. -- putStrLn $ "Addr family int: " ++ (show addr_family_int)
  164. -- ASSUMPTION: Foreign call returns a valid int
  165. assert_total (case getSocketFamily addr_family_int of
  166. Just AF_INET => do
  167. ipv4_addr <- mkForeign (FFun "idrnet_sockaddr_ipv4" [FPtr] FString) ptr
  168. return $ parseIPv4 ipv4_addr
  169. Just AF_INET6 => return IPv6Addr
  170. Just AF_UNSPEC => return InvalidAddress)
  171. accept : Socket -> IO (Either SocketError (Socket, SocketAddress))
  172. accept sock = do
  173. -- We need a pointer to a sockaddr structure. This is then passed into
  174. -- idrnet_accept and populated. We can then query it for the SocketAddr and free it.
  175. sockaddr_ptr <- mkForeign (FFun "idrnet_create_sockaddr" [] FPtr)
  176. accept_res <- mkForeign (FFun "idrnet_accept" [FInt, FPtr] FInt) (descriptor sock) sockaddr_ptr
  177. if accept_res == (-1) then
  178. map Left getErrno
  179. else do
  180. let (MkSocket _ fam ty p_num) = sock
  181. sockaddr <- getSockAddr (SAPtr sockaddr_ptr)
  182. sockaddr_free (SAPtr sockaddr_ptr)
  183. return $ Right ((MkSocket accept_res fam ty p_num), sockaddr)
  184. send : Socket -> String -> IO (Either SocketError ByteLength)
  185. send sock dat = do
  186. send_res <- mkForeign (FFun "idrnet_send" [FInt, FString] FInt) (descriptor sock) dat
  187. if send_res == (-1) then
  188. map Left getErrno
  189. else
  190. return $ Right send_res
  191. freeRecvStruct : RecvStructPtr -> IO ()
  192. freeRecvStruct (RSPtr p) = mkForeign (FFun "idrnet_free_recv_struct" [FPtr] FUnit) p
  193. freeRecvfromStruct : RecvfromStructPtr -> IO ()
  194. freeRecvfromStruct (RFPtr p) = mkForeign (FFun "idrnet_free_recvfrom_struct" [FPtr] FUnit) p
  195. recv : Socket -> Int -> IO (Either SocketError (String, ByteLength))
  196. recv sock len = do
  197. -- Firstly make the request, get some kind of recv structure which
  198. -- contains the result of the recv and possibly the retrieved payload
  199. recv_struct_ptr <- mkForeign (FFun "idrnet_recv" [FInt, FInt] FPtr) (descriptor sock) len
  200. recv_res <- mkForeign (FFun "idrnet_get_recv_res" [FPtr] FInt) recv_struct_ptr
  201. if recv_res == (-1) then do
  202. errno <- getErrno
  203. freeRecvStruct (RSPtr recv_struct_ptr)
  204. return $ Left errno
  205. else
  206. if recv_res == 0 then do
  207. freeRecvStruct (RSPtr recv_struct_ptr)
  208. return $ Left 0
  209. else do
  210. payload <- mkForeign (FFun "idrnet_get_recv_payload" [FPtr] FString) recv_struct_ptr
  211. freeRecvStruct (RSPtr recv_struct_ptr)
  212. return $ Right (payload, recv_res)
  213. -- Sends the data in a given memory location
  214. sendBuf : Socket -> BufPtr -> ByteLength -> IO (Either SocketError ByteLength)
  215. sendBuf sock (BPtr ptr) len = do
  216. send_res <- mkForeign (FFun "idrnet_send_buf" [FInt, FPtr, FInt] FInt) (descriptor sock) ptr len
  217. if send_res == (-1) then
  218. map Left getErrno
  219. else
  220. return $ Right send_res
  221. recvBuf : Socket -> BufPtr -> ByteLength -> IO (Either SocketError ByteLength)
  222. recvBuf sock (BPtr ptr) len = do
  223. recv_res <- mkForeign (FFun "idrnet_recv_buf" [FInt, FPtr, FInt] FInt) (descriptor sock) ptr len
  224. if (recv_res == (-1)) then
  225. map Left getErrno
  226. else
  227. return $ Right recv_res
  228. sendTo : Socket -> SocketAddress -> Port -> String -> IO (Either SocketError ByteLength)
  229. sendTo sock addr p dat = do
  230. sendto_res <- mkForeign (FFun "idrnet_sendto" [FInt, FString, FString, FInt, FInt] FInt)
  231. (descriptor sock) dat (show addr) p (toCode $ family sock)
  232. if sendto_res == (-1) then
  233. map Left getErrno
  234. else
  235. return $ Right sendto_res
  236. sendToBuf : Socket -> SocketAddress -> Port -> BufPtr -> ByteLength -> IO (Either SocketError ByteLength)
  237. sendToBuf sock addr p (BPtr dat) len = do
  238. sendto_res <- mkForeign (FFun "idrnet_sendto_buf" [FInt, FPtr, FInt, FString, FInt, FInt] FInt)
  239. (descriptor sock) dat len (show addr) p (toCode $ family sock)
  240. if sendto_res == (-1) then
  241. map Left getErrno
  242. else
  243. return $ Right sendto_res
  244. foreignGetRecvfromPayload : RecvfromStructPtr -> IO String
  245. foreignGetRecvfromPayload (RFPtr p) = mkForeign (FFun "idrnet_get_recvfrom_payload" [FPtr] FString) p
  246. foreignGetRecvfromAddr : RecvfromStructPtr -> IO SocketAddress
  247. foreignGetRecvfromAddr (RFPtr p) = do
  248. sockaddr_ptr <- map SAPtr $ mkForeign (FFun "idrnet_get_recvfrom_sockaddr" [FPtr] FPtr) p
  249. getSockAddr sockaddr_ptr
  250. foreignGetRecvfromPort : RecvfromStructPtr -> IO Port
  251. foreignGetRecvfromPort (RFPtr p) = do
  252. sockaddr_ptr <- mkForeign (FFun "idrnet_get_recvfrom_sockaddr" [FPtr] FPtr) p
  253. port <- mkForeign (FFun "idrnet_sockaddr_ipv4_port" [FPtr] FInt) sockaddr_ptr
  254. return port
  255. recvFrom : Socket -> ByteLength -> IO (Either SocketError (UDPAddrInfo, String, ByteLength))
  256. recvFrom sock bl = do
  257. recv_ptr <- mkForeign (FFun "idrnet_recvfrom" [FInt, FInt] FPtr)
  258. (descriptor sock) bl
  259. let recv_ptr' = RFPtr recv_ptr
  260. if !(nullPtr recv_ptr) then -- ! notation = monadic bind shortcut, not "not"
  261. map Left getErrno
  262. else do
  263. result <- mkForeign (FFun "idrnet_get_recvfrom_res" [FPtr] FInt) recv_ptr
  264. if result == -1 then do
  265. freeRecvfromStruct recv_ptr'
  266. map Left getErrno
  267. else do
  268. payload <- foreignGetRecvfromPayload recv_ptr'
  269. port <- foreignGetRecvfromPort recv_ptr'
  270. addr <- foreignGetRecvfromAddr recv_ptr'
  271. freeRecvfromStruct recv_ptr'
  272. return $ Right (MkUDPAddrInfo addr port, payload, result)
  273. recvFromBuf : Socket -> BufPtr -> ByteLength -> IO (Either SocketError (UDPAddrInfo, ByteLength))
  274. recvFromBuf sock (BPtr ptr) bl = do
  275. recv_ptr <- mkForeign (FFun "idrnet_recvfrom_buf" [FInt, FPtr, FInt] FPtr) (descriptor sock) ptr bl
  276. let recv_ptr' = RFPtr recv_ptr
  277. if !(nullPtr recv_ptr) then -- ! notation = monadic bind shortcut, not "not"
  278. map Left getErrno
  279. else do
  280. result <- mkForeign (FFun "idrnet_get_recvfrom_res" [FPtr] FInt) recv_ptr
  281. if result == -1 then do
  282. freeRecvfromStruct recv_ptr'
  283. map Left getErrno
  284. else do
  285. port <- foreignGetRecvfromPort recv_ptr'
  286. addr <- foreignGetRecvfromAddr recv_ptr'
  287. freeRecvfromStruct recv_ptr'
  288. return $ Right (MkUDPAddrInfo addr port, result + 1)