0 | ||| Low-Level C Sockets bindings for Idris. Used by higher-level, cleverer things.
  1 | ||| Type-unsafe parts. Use Network.Socket for a safe variant.
  2 | |||
  3 | ||| Original (C) SimonJF, MIT Licensed, 2014
  4 | ||| Modified (C) The Idris Community, 2015, 2016
  5 | module Network.Socket.Raw
  6 |
  7 | import public Network.Socket.Data
  8 |
  9 | import Network.FFI
 10 | import System.FFI
 11 |
 12 | -- ---------------------------------------------------------------- [ Pointers ]
 13 |
 14 | public export
 15 | data RecvStructPtr     = RSPtr AnyPtr
 16 |
 17 | public export
 18 | data RecvfromStructPtr = RFPtr AnyPtr
 19 |
 20 | public export
 21 | data BufPtr = BPtr AnyPtr
 22 |
 23 | public export
 24 | data SockaddrPtr = SAPtr AnyPtr
 25 |
 26 | -- ---------------------------------------------------------- [ Socket Utilies ]
 27 |
 28 | ||| Put a value in a buffer
 29 | export
 30 | sock_poke : HasIO io => BufPtr -> Int -> Int -> io ()
 31 | sock_poke (BPtr ptr) offset val = primIO $ prim__idrnet_poke ptr offset val
 32 |
 33 | ||| Take a value from a buffer
 34 | export
 35 | sock_peek : HasIO io => BufPtr -> Int -> io Int
 36 | sock_peek (BPtr ptr) offset = primIO $ prim__idrnet_peek ptr offset
 37 |
 38 | ||| Frees a given pointer
 39 | export
 40 | sock_free : HasIO io => BufPtr -> io ()
 41 | sock_free (BPtr ptr) = free ptr
 42 |
 43 | export
 44 | sockaddr_free : HasIO io => SockaddrPtr -> io ()
 45 | sockaddr_free (SAPtr ptr) = free ptr
 46 |
 47 | ||| Allocates an amount of memory given by the ByteLength parameter.
 48 | |||
 49 | ||| Used to allocate a mutable pointer to be given to the Recv functions.
 50 | export
 51 | sock_alloc : HasIO io => ByteLength -> io BufPtr
 52 | sock_alloc bl = map BPtr $ malloc bl
 53 |
 54 | ||| Retrieves the port the given socket is bound to
 55 | export
 56 | getSockPort : HasIO io => Socket -> io Port
 57 | getSockPort sock = primIO $ prim__idrnet_sockaddr_port $ descriptor sock
 58 |
 59 |
 60 | ||| Retrieves a socket address from a sockaddr pointer
 61 | export
 62 | getSockAddr : HasIO io => SockaddrPtr -> io SocketAddress
 63 | getSockAddr (SAPtr ptr) = do
 64 |   addr_family_int <- primIO $ prim__idrnet_sockaddr_family ptr
 65 |
 66 |   -- ASSUMPTION: Foreign call returns a valid int
 67 |   assert_total (case getSocketFamily addr_family_int of
 68 |     Just AF_INET => do
 69 |       ipv4_addr <- primIO $ prim__idrnet_sockaddr_ipv4 ptr
 70 |
 71 |       pure $ parseIPv4 ipv4_addr
 72 |     Just AF_INET6 => pure IPv6Addr
 73 |     Just AF_UNIX => map Hostname $ primIO (prim__idrnet_sockaddr_unix ptr)
 74 |     Just AF_UNSPEC => pure InvalidAddress)
 75 |
 76 | export
 77 | freeRecvStruct : HasIO io => RecvStructPtr -> io ()
 78 | freeRecvStruct (RSPtr p) = primIO $ prim__idrnet_free_recv_struct p
 79 |
 80 | ||| Utility to extract data.
 81 | export
 82 | freeRecvfromStruct : HasIO io => RecvfromStructPtr -> io ()
 83 | freeRecvfromStruct (RFPtr p) = primIO $ prim__idrnet_free_recvfrom_struct p
 84 |
 85 | ||| Sends the data in a given memory location
 86 | |||
 87 | ||| Returns on failure a `SocketError`
 88 | ||| Returns on success the `ResultCode`
 89 | |||
 90 | ||| @sock The socket on which to send the message.
 91 | ||| @ptr  The location containing the data to send.
 92 | ||| @len  How much of the data to send.
 93 | export
 94 | sendBuf : HasIO io
 95 |        => (sock : Socket)
 96 |        -> (ptr  : BufPtr)
 97 |        -> (len  : ByteLength)
 98 |        -> io (Either SocketError ResultCode)
 99 | sendBuf sock (BPtr ptr) len = do
100 |   send_res <- primIO $ prim__idrnet_send_buf (descriptor sock) ptr len
101 |
102 |   if send_res == (-1)
103 |    then map Left getErrno
104 |    else pure $ Right send_res
105 |
106 | ||| Receive data from a given memory location.
107 | |||
108 | ||| Returns on failure a `SocketError`
109 | ||| Returns on success the `ResultCode`
110 | |||
111 | ||| @sock The socket on which to receive the message.
112 | ||| @ptr  The location containing the data to receive.
113 | ||| @len  How much of the data to receive.
114 | export
115 | recvBuf : HasIO io
116 |        => (sock : Socket)
117 |        -> (ptr  : BufPtr)
118 |        -> (len  : ByteLength)
119 |        -> io (Either SocketError ResultCode)
120 | recvBuf sock (BPtr ptr) len = do
121 |   recv_res <- primIO $ prim__idrnet_recv_buf (descriptor sock) ptr len
122 |
123 |   if (recv_res == (-1))
124 |     then map Left getErrno
125 |     else pure $ Right recv_res
126 |
127 | ||| Send a message stored in some buffer.
128 | |||
129 | ||| Returns on failure a `SocketError`
130 | ||| Returns on success the `ResultCode`
131 | |||
132 | ||| @sock The socket on which to send the message.
133 | ||| @addr Address of the recipient.
134 | ||| @port The port on which to send the message.
135 | ||| @ptr  A Pointer to the buffer containing the message.
136 | ||| @len  The size of the message.
137 | export
138 | sendToBuf : HasIO io
139 |          => (sock : Socket)
140 |          -> (addr : SocketAddress)
141 |          -> (port : Port)
142 |          -> (ptr  : BufPtr)
143 |          -> (len  : ByteLength)
144 |          -> io (Either SocketError ResultCode)
145 | sendToBuf sock addr p (BPtr dat) len = do
146 |   sendto_res <- primIO $ prim__idrnet_sendto_buf
147 |                 (descriptor sock) dat len (show addr) p (toCode $ family sock)
148 |
149 |   if sendto_res == (-1)
150 |     then map Left getErrno
151 |     else pure $ Right sendto_res
152 |
153 | ||| Utility function to get the payload of the sent message as a `String`.
154 | export
155 | foreignGetRecvfromPayload : HasIO io => RecvfromStructPtr -> io String
156 | foreignGetRecvfromPayload (RFPtr p) = primIO $ prim__idrnet_get_recvfrom_payload p
157 |
158 | ||| Utility function to return senders socket address.
159 | export
160 | foreignGetRecvfromAddr : HasIO io => RecvfromStructPtr -> io SocketAddress
161 | foreignGetRecvfromAddr (RFPtr p) = do
162 |   sockaddr_ptr <- map SAPtr $ primIO $ prim__idrnet_get_recvfrom_sockaddr p
163 |   getSockAddr sockaddr_ptr
164 |
165 | ||| Utility function to return sender's IPV4 port.
166 | export
167 | foreignGetRecvfromPort : HasIO io => RecvfromStructPtr -> io Port
168 | foreignGetRecvfromPort (RFPtr p) = do
169 |   sockaddr_ptr <- primIO $ prim__idrnet_get_recvfrom_sockaddr p
170 |   port         <- primIO $ prim__idrnet_sockaddr_ipv4_port sockaddr_ptr
171 |   pure port
172 |
173 | ||| Receive a message placed on a 'known' buffer.
174 | |||
175 | ||| Returns on failure a `SocketError`.
176 | ||| Returns on success a pair of
177 | ||| + `UDPAddrInfo` :: The address of the sender.
178 | ||| + `Int`         :: Result value from underlying function.
179 | |||
180 | ||| @sock The channel on which to receive.
181 | ||| @ptr  Pointer to the buffer to place the message.
182 | ||| @len  Size of the expected message.
183 | |||
184 | export
185 | recvFromBuf : HasIO io
186 |            => (sock : Socket)
187 |            -> (ptr  : BufPtr)
188 |            -> (len  : ByteLength)
189 |            -> io (Either SocketError (UDPAddrInfo, ResultCode))
190 | recvFromBuf sock (BPtr ptr) bl = do
191 |   recv_ptr <- primIO $ prim__idrnet_recvfrom_buf (descriptor sock) ptr bl
192 |
193 |   let recv_ptr' = RFPtr recv_ptr
194 |
195 |   isnull <- nullPtr recv_ptr
196 |
197 |   if isnull
198 |     then map Left getErrno
199 |     else do
200 |       result <- primIO $ prim__idrnet_get_recvfrom_res recv_ptr
201 |       if result == -1
202 |         then do
203 |           freeRecvfromStruct recv_ptr'
204 |           map Left getErrno
205 |         else do
206 |           port <- foreignGetRecvfromPort recv_ptr'
207 |           addr <- foreignGetRecvfromAddr recv_ptr'
208 |           freeRecvfromStruct recv_ptr'
209 |           pure $ Right (MkUDPAddrInfo addr port, result + 1)
210 |