Idris2/libs/network/Network/Socket/Raw.idr
Edwin Brady dbdf7dab3d Back to HasIO, remove MonadIO
Following a fairly detailed discussion on slack, the feeling is
generally that it's better to have a single interface. While precision
is nice, it doesn't appear to buy us anything here. If that turns out to
be wrong, or limiting somehow, we can revisit it later. Also:

- it's easier for backend authors if the type of IO operations is
  slightly less restrictive. For example, if it's in HasIO, that limits
  alternative implementations, which might be awkward for some
  alternative back ends.
- it's one less extra detail to learn. This is minor, but there needs to
  be a clear advantage if there's more detail to learn.
- It is difficult to think of an underlying type that can't have a Monad
  instance (I have personally never encountered one - if they turns out
  to exist, again, we can revisit!)
2020-06-21 19:21:22 +01:00

209 lines
6.3 KiB
Idris

||| 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
-- ---------------------------------------------------------------- [ 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 : HasIO io => BufPtr -> Int -> Int -> io ()
sock_poke (BPtr ptr) offset val = primIO $ idrnet_poke ptr offset val
||| Take a value from a buffer
export
sock_peek : HasIO io => BufPtr -> Int -> io Int
sock_peek (BPtr ptr) offset = primIO $ idrnet_peek ptr offset
||| Frees a given pointer
export
sock_free : HasIO io => BufPtr -> io ()
sock_free (BPtr ptr) = primIO $ idrnet_free ptr
export
sockaddr_free : HasIO io => SockaddrPtr -> io ()
sockaddr_free (SAPtr ptr) = primIO $ idrnet_free ptr
||| 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 : HasIO io => ByteLength -> io BufPtr
sock_alloc bl = map BPtr $ primIO $ idrnet_malloc bl
||| Retrieves the port the given socket is bound to
export
getSockPort : HasIO io => Socket -> io Port
getSockPort sock = primIO $ idrnet_sockaddr_port $ descriptor sock
||| Retrieves a socket address from a sockaddr pointer
export
getSockAddr : HasIO io => SockaddrPtr -> io SocketAddress
getSockAddr (SAPtr ptr) = do
addr_family_int <- primIO $ idrnet_sockaddr_family ptr
-- 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
pure $ parseIPv4 ipv4_addr
Just AF_INET6 => pure IPv6Addr
Just AF_UNSPEC => pure InvalidAddress)
export
freeRecvStruct : HasIO io => RecvStructPtr -> io ()
freeRecvStruct (RSPtr p) = primIO $ idrnet_free_recv_struct p
||| Utility to extract data.
export
freeRecvfromStruct : HasIO io => RecvfromStructPtr -> io ()
freeRecvfromStruct (RFPtr p) = primIO $ idrnet_free_recvfrom_struct p
||| 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 : HasIO io
=> (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
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 : HasIO io
=> (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
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 : HasIO io
=> (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)
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 : HasIO io => RecvfromStructPtr -> io String
foreignGetRecvfromPayload (RFPtr p) = primIO $ idrnet_get_recvfrom_payload p
||| Utility function to return senders socket address.
export
foreignGetRecvfromAddr : HasIO io => RecvfromStructPtr -> io SocketAddress
foreignGetRecvfromAddr (RFPtr p) = do
sockaddr_ptr <- map SAPtr $ primIO $ idrnet_get_recvfrom_sockaddr p
getSockAddr sockaddr_ptr
||| Utility function to return sender's IPV4 port.
export
foreignGetRecvfromPort : HasIO io => RecvfromStructPtr -> io Port
foreignGetRecvfromPort (RFPtr p) = do
sockaddr_ptr <- primIO $ idrnet_get_recvfrom_sockaddr p
port <- primIO $ idrnet_sockaddr_ipv4_port sockaddr_ptr
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 : HasIO io
=> (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
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
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)