Idris2/libs/network/Network/Socket/Raw.idr

205 lines
6.1 KiB
Idris
Raw Normal View History

2020-05-18 15:59:07 +03:00
||| Low-Level C Sockets bindings for Idris. Used by higher-level, cleverer things.
||| Type-unsafe parts. Use Network.Socket for a safe variant.
|||
||| Original (C) SimonJF, MIT Licensed, 2014
||| Modified (C) The Idris Community, 2015, 2016
module Network.Socket.Raw
import public Network.Socket.Data
import Network.FFI
2020-05-18 15:59:07 +03:00
-- ---------------------------------------------------------------- [ Pointers ]
public export
data RecvStructPtr = RSPtr AnyPtr
public export
data RecvfromStructPtr = RFPtr AnyPtr
public export
data BufPtr = BPtr AnyPtr
public export
data SockaddrPtr = SAPtr AnyPtr
-- ---------------------------------------------------------- [ Socket Utilies ]
||| Put a value in a buffer
export
sock_poke : BufPtr -> Int -> Int -> IO ()
sock_poke (BPtr ptr) offset val = primIO $ idrnet_poke ptr offset val
2020-05-18 15:59:07 +03:00
||| Take a value from a buffer
export
sock_peek : BufPtr -> Int -> IO Int
sock_peek (BPtr ptr) offset = primIO $ idrnet_peek ptr offset
2020-05-18 15:59:07 +03:00
||| Frees a given pointer
export
sock_free : BufPtr -> IO ()
sock_free (BPtr ptr) = primIO $ idrnet_free ptr
2020-05-18 15:59:07 +03:00
export
sockaddr_free : SockaddrPtr -> IO ()
sockaddr_free (SAPtr ptr) = primIO $ idrnet_free ptr
2020-05-18 15:59:07 +03:00
||| Allocates an amount of memory given by the ByteLength parameter.
|||
||| Used to allocate a mutable pointer to be given to the Recv functions.
export
sock_alloc : ByteLength -> IO BufPtr
sock_alloc bl = map BPtr $ primIO $ idrnet_malloc bl
2020-05-18 15:59:07 +03:00
||| Retrieves the port the given socket is bound to
export
getSockPort : Socket -> IO Port
getSockPort sock = primIO $ idrnet_sockaddr_port $ descriptor sock
2020-05-18 15:59:07 +03:00
||| Retrieves a socket address from a sockaddr pointer
export
getSockAddr : SockaddrPtr -> IO SocketAddress
getSockAddr (SAPtr ptr) = do
addr_family_int <- primIO $ idrnet_sockaddr_family ptr
2020-05-18 15:59:07 +03:00
-- ASSUMPTION: Foreign call returns a valid int
assert_total (case getSocketFamily addr_family_int of
Just AF_INET => do
ipv4_addr <- primIO $ idrnet_sockaddr_ipv4 ptr
2020-05-18 15:59:07 +03:00
pure $ parseIPv4 ipv4_addr
Just AF_INET6 => pure IPv6Addr
Just AF_UNSPEC => pure InvalidAddress)
export
freeRecvStruct : RecvStructPtr -> IO ()
freeRecvStruct (RSPtr p) = primIO $ idrnet_free_recv_struct p
2020-05-18 15:59:07 +03:00
||| Utility to extract data.
export
freeRecvfromStruct : RecvfromStructPtr -> IO ()
freeRecvfromStruct (RFPtr p) = primIO $ idrnet_free_recvfrom_struct p
2020-05-18 15:59:07 +03:00
||| Sends the data in a given memory location
|||
||| Returns on failure a `SocketError`
||| Returns on success the `ResultCode`
|||
||| @sock The socket on which to send the message.
||| @ptr The location containing the data to send.
||| @len How much of the data to send.
export
sendBuf : (sock : Socket)
-> (ptr : BufPtr)
-> (len : ByteLength)
-> IO (Either SocketError ResultCode)
sendBuf sock (BPtr ptr) len = do
send_res <- primIO $ idrnet_send_buf (descriptor sock) ptr len
2020-05-18 15:59:07 +03:00
if send_res == (-1)
then map Left getErrno
else pure $ Right send_res
||| Receive data from a given memory location.
|||
||| Returns on failure a `SocketError`
||| Returns on success the `ResultCode`
|||
||| @sock The socket on which to receive the message.
||| @ptr The location containing the data to receive.
||| @len How much of the data to receive.
export
recvBuf : (sock : Socket)
-> (ptr : BufPtr)
-> (len : ByteLength)
-> IO (Either SocketError ResultCode)
recvBuf sock (BPtr ptr) len = do
recv_res <- primIO $ idrnet_recv_buf (descriptor sock) ptr len
2020-05-18 15:59:07 +03:00
if (recv_res == (-1))
then map Left getErrno
else pure $ Right recv_res
||| Send a message stored in some buffer.
|||
||| Returns on failure a `SocketError`
||| Returns on success the `ResultCode`
|||
||| @sock The socket on which to send the message.
||| @addr Address of the recipient.
||| @port The port on which to send the message.
||| @ptr A Pointer to the buffer containing the message.
||| @len The size of the message.
export
sendToBuf : (sock : Socket)
-> (addr : SocketAddress)
-> (port : Port)
-> (ptr : BufPtr)
-> (len : ByteLength)
-> IO (Either SocketError ResultCode)
sendToBuf sock addr p (BPtr dat) len = do
sendto_res <- primIO $ idrnet_sendto_buf
(descriptor sock) dat len (show addr) p (toCode $ family sock)
2020-05-18 15:59:07 +03:00
if sendto_res == (-1)
then map Left getErrno
else pure $ Right sendto_res
||| Utility function to get the payload of the sent message as a `String`.
export
foreignGetRecvfromPayload : RecvfromStructPtr -> IO String
foreignGetRecvfromPayload (RFPtr p) = primIO $ idrnet_get_recvfrom_payload p
2020-05-18 15:59:07 +03:00
||| Utility function to return senders socket address.
export
foreignGetRecvfromAddr : RecvfromStructPtr -> IO SocketAddress
foreignGetRecvfromAddr (RFPtr p) = do
sockaddr_ptr <- map SAPtr $ primIO $ idrnet_get_recvfrom_sockaddr p
2020-05-18 15:59:07 +03:00
getSockAddr sockaddr_ptr
||| Utility function to return sender's IPV4 port.
export
foreignGetRecvfromPort : RecvfromStructPtr -> IO Port
foreignGetRecvfromPort (RFPtr p) = do
sockaddr_ptr <- primIO $ idrnet_get_recvfrom_sockaddr p
port <- primIO $ idrnet_sockaddr_ipv4_port sockaddr_ptr
2020-05-18 15:59:07 +03:00
pure port
||| Receive a message placed on a 'known' buffer.
|||
||| Returns on failure a `SocketError`.
||| Returns on success a pair of
||| + `UDPAddrInfo` :: The address of the sender.
||| + `Int` :: Result value from underlying function.
|||
||| @sock The channel on which to receive.
||| @ptr Pointer to the buffer to place the message.
||| @len Size of the expected message.
|||
export
recvFromBuf : (sock : Socket)
-> (ptr : BufPtr)
-> (len : ByteLength)
-> IO (Either SocketError (UDPAddrInfo, ResultCode))
recvFromBuf sock (BPtr ptr) bl = do
recv_ptr <- primIO $ idrnet_recvfrom_buf (descriptor sock) ptr bl
2020-05-18 15:59:07 +03:00
let recv_ptr' = RFPtr recv_ptr
isnull <- nullPtr recv_ptr
if isnull
then map Left getErrno
else do
result <- primIO $ idrnet_get_recvfrom_res recv_ptr
2020-05-18 15:59:07 +03:00
if result == -1
then do
freeRecvfromStruct recv_ptr'
map Left getErrno
else do
port <- foreignGetRecvfromPort recv_ptr'
addr <- foreignGetRecvfromAddr recv_ptr'
freeRecvfromStruct recv_ptr'
pure $ Right (MkUDPAddrInfo addr port, result + 1)